class CollapsibleFilterComponent(OWComponent, QObject): options_changed = pyqtSignal() filter_by_full_text: str = settings.Setting('', schema_only=True) filter_by_name: str = settings.Setting('', schema_only=True) filter_by_contrib: str = settings.Setting('', schema_only=True) filter_by_owner: str = settings.Setting('', schema_only=True) filter_by_modified: int = settings.Setting(FilterByDateModified.any_time, schema_only=True) sort_by: int = settings.Setting(SortBy.newest_first, schema_only=True) FILTER_FULL_TEXT_LABEL = 'Search' TOGGLE_BTN_LABEL = 'Narrow your search' FILTER_NAME_LABEL = 'Filter by name' FILTER_CONTRIB_LABEL = 'Filter by contributor' FILTER_OWNER_LABEL = 'Filter by owner' FILTER_MODIFIED_LABEL = 'Filter by date modified' SORTING_LABEL = 'Sorting' def __init__(self, parent_widget, parent_component): QObject.__init__(self) OWComponent.__init__(self, widget=parent_widget) box = gui.widgetBox(parent_component, margin=0) self.filter_full_text = gui.lineEdit( box, self, 'filter_by_full_text', label=self.FILTER_FULL_TEXT_LABEL, callback=self.on_filter_full_text_changed, ) self.toggle_animation = QParallelAnimationGroup() self.toggle_button = QToolButton() self.toggle_button.setCheckable(True) self.toggle_button.setChecked(False) self.toggle_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.toggle_button.setArrowType(Qt.RightArrow) self.toggle_button.setText(self.TOGGLE_BTN_LABEL) self.toggle_button.setStyleSheet('QToolButton {border: none; padding-top: 5px; }') self.toggle_button.setIconSize(QSize(15, 15)) self.toggle_button.pressed.connect(self.on_toggle) self.collapsible_components = QScrollArea() self.collapsible_components.setMaximumHeight(0) self.collapsible_components.setMinimumHeight(0) self.collapsible_components.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.collapsible_components.setFrameShape(QFrame.NoFrame) box = gui.widgetBox(parent_component, margin=0) box.layout().addWidget(self.toggle_button) box.layout().addWidget(self.collapsible_components) self.toggle_animation.addAnimation(QPropertyAnimation(box, b"minimumHeight")) self.toggle_animation.addAnimation(QPropertyAnimation(box, b"maximumHeight")) self.toggle_animation.addAnimation(QPropertyAnimation(self.collapsible_components, b"maximumHeight")) layout = QHBoxLayout() left_box = gui.widgetBox(None, self, margin=0, flat=True) mid_box = gui.widgetBox(None, self, margin=0, flat=True) right_box = gui.widgetBox(None, self, margin=0, flat=True) self.filter_name = gui.lineEdit( left_box, self, 'filter_by_name', label=self.FILTER_NAME_LABEL, callback=self.on_filter_changed, addSpace=5 ) self.filter_contrib = gui.lineEdit( mid_box, self, 'filter_by_contrib', label=self.FILTER_CONTRIB_LABEL, callback=self.on_filter_changed, addSpace=5, ) self.filter_owner = gui.lineEdit( right_box, self, 'filter_by_owner', label=self.FILTER_OWNER_LABEL, callback=self.on_filter_changed, addSpace=5, ) self.filter_modified = gui.comboBox( left_box, self, 'filter_by_modified', label=self.FILTER_MODIFIED_LABEL, callback=self.on_filter_changed, items=FilterByDateModified.labels(), ) self.sorting = gui.comboBox( mid_box, self, 'sort_by', label=self.SORTING_LABEL, callback=self.on_filter_changed, items=SortBy.labels() ) gui.rubber(left_box) gui.rubber(mid_box) gui.rubber(right_box) layout.addWidget(left_box) layout.addWidget(mid_box) layout.addWidget(right_box) self.collapsible_components.setLayout(layout) collapsed_height = box.layout().sizeHint().height() - self.collapsible_components.maximumHeight() content_height = layout.sizeHint().height() for i in range(self.toggle_animation.animationCount()): animation = self.toggle_animation.animationAt(i) animation.setDuration(100) animation.setStartValue(collapsed_height) animation.setEndValue(collapsed_height + content_height) content_animation = self.toggle_animation.animationAt(self.toggle_animation.animationCount() - 1) content_animation.setDuration(100) content_animation.setStartValue(0) content_animation.setEndValue(content_height) def on_toggle(self): """ Start animation """ checked = self.toggle_button.isChecked() self.toggle_button.setArrowType(Qt.DownArrow if not checked else Qt.RightArrow) self.toggle_animation.setDirection(QAbstractAnimation.Forward if not checked else QAbstractAnimation.Backward) self.toggle_animation.start() def on_filter_full_text_changed(self): self.sort_by = SortBy.relevance if self.filter_full_text else SortBy.newest_first self.on_filter_changed() def on_filter_changed(self): self.options_changed.emit()
class ControlToolButton(ControlBase): def __init__(self, *args, **kwargs): self._checkable = kwargs.get('checkable', False) self._maxheight = kwargs.get('maxheight', None) self._maxwidth = kwargs.get('maxwidth', None) super(ControlToolButton, self).__init__(*args, **kwargs) default = kwargs.get('default', None) if default: self.value = default icon = kwargs.get('icon', None) if icon: self.icon = icon def init_form(self): self._form = QToolButton() if self._maxwidth: self._form.setMaximumWidth(self._maxwidth) if self._maxheight: self._form.setMaximumHeight(self._maxheight) self._form.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self._form.setCheckable(self._checkable) self.label = self._label self._form.setToolTip(self.help) def click(self): self._form.click() def load_form(self, data, path=None): pass def save_form(self, data, path=None): pass ########################################################################## @property def label(self): return ControlBase.label.fget(self) @label.setter def label(self, value): ControlBase.label.fset(self, value) if value is None: self._form.setToolButtonStyle(Qt.ToolButtonIconOnly) else: self._form.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self._form.setText(self._label) @property def icon(self): return self._form.icon() @icon.setter def icon(self, value): if isinstance(value, (str, bytes)): self._form.setIcon(QIcon(value)) else: self._form.setIcon(value) ########################################################################## @property def value(self): return None @value.setter def value(self, value): try: self._form.clicked.disconnect() # ignore previous signals if any except TypeError as err: # http://stackoverflow.com/questions/21586643/pyqt-widget-connect-and-disconnect pass self._form.clicked[bool].connect(value) @property def checked(self): return self._form.isChecked() @checked.setter def checked(self, value): self._form.setChecked(value)