Source code for qudi.util.widgets.collapsible

# -*- coding: utf-8 -*-

"""
This file contains a custom QWidget to wrap a collapsible widget with expand/collapse animation.

Copyright (c) 2021, the qudi developers. See the AUTHORS.md file at the top-level directory of this
distribution and on <https://github.com/Ulm-IQO/qudi-core/>

This file is part of qudi.

Qudi is free software: you can redistribute it and/or modify it under the terms of
the GNU Lesser General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.

Qudi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with qudi.
If not, see <https://www.gnu.org/licenses/>.
"""

__all__ = ['CollapsibleWidget']


from PySide2 import QtCore, QtWidgets
from typing import Optional


[docs] class CollapsibleWidget(QtWidgets.QWidget): """ ToDo: Document """ sigCollapsedChanged = QtCore.Signal(bool) # True: Collapsed, False: Expanded
[docs] def __init__(self, widget: QtWidgets.QWidget, title: Optional[str] = '', animation_duration: Optional[float] = 0.2, parent: Optional[QtWidgets.QWidget] = None): super().__init__(parent=parent) if animation_duration is None: animation_duration = 0.2 self._last_collapsed = True self.expand_collapse_button = QtWidgets.QToolButton() self.expand_collapse_button.setStyleSheet( 'QToolButton { border: none; background-color: none; }' ) self.expand_collapse_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) self.expand_collapse_button.setArrowType(QtCore.Qt.RightArrow) self.expand_collapse_button.setText(title if title else '') self.expand_collapse_button.setCheckable(True) widget.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) widget.setMaximumHeight(0) widget.setMinimumHeight(0) layout = QtWidgets.QVBoxLayout() layout.addWidget(self.expand_collapse_button) layout.addWidget(widget) layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) # Animations to adjust max and min height of this widget and content widget self._min_height_animation = QtCore.QPropertyAnimation(self, b"minimumHeight") self._max_height_animation = QtCore.QPropertyAnimation(self, b"maximumHeight") self._widget_max_height_animation = QtCore.QPropertyAnimation(widget, b"maximumHeight") collapsed_height = self.sizeHint().height() - widget.maximumHeight() widget_height = widget.sizeHint().height() self._min_height_animation.setStartValue(collapsed_height) self._min_height_animation.setEndValue(collapsed_height + widget_height) self._max_height_animation.setStartValue(collapsed_height) self._max_height_animation.setEndValue(collapsed_height + widget_height) self._widget_max_height_animation.setStartValue(0) self._widget_max_height_animation.setEndValue(widget_height) self._animation_group = QtCore.QParallelAnimationGroup() self._animation_group.addAnimation(self._min_height_animation) self._animation_group.addAnimation(self._max_height_animation) self._animation_group.addAnimation(self._widget_max_height_animation) self.set_animation_duration(animation_duration) self.expand_collapse_button.toggled[bool].connect(self._start_animation) self._animation_group.finished.connect(self._animation_finished)
@property def collapsed(self) -> bool: if self._min_height_animation.startValue() == self.minimumHeight(): return True if self._min_height_animation.endValue() == self.minimumHeight(): return False return self._last_collapsed @property def animation_duration(self) -> float: return self._min_height_animation.duration() / 1000. def set_collapsed(self, collapse: bool) -> None: self.expand_collapse_button.setChecked(not collapse) def set_animation_duration(self, duration: float) -> None: duration_ms = int(round(1000 * duration)) self._min_height_animation.setDuration(duration_ms) self._max_height_animation.setDuration(duration_ms) self._widget_max_height_animation.setDuration(duration_ms) @QtCore.Slot(bool) def _start_animation(self, checked: bool) -> None: if checked: arrow_type = QtCore.Qt.DownArrow direction = QtCore.QAbstractAnimation.Forward else: arrow_type = QtCore.Qt.RightArrow direction = QtCore.QAbstractAnimation.Backward self.expand_collapse_button.setArrowType(arrow_type) self._animation_group.setDirection(direction) self._animation_group.start() @QtCore.Slot() def _animation_finished(self) -> None: collapsed = self.collapsed if collapsed != self._last_collapsed: self._last_collapsed = collapsed self.sigCollapsedChanged.emit(collapsed)