class AlgorithmChoose(QWidget): finished = Signal() started = Signal() result = Signal(SegmentationResult) value_changed = Signal() progress_signal = Signal(str, int) algorithm_changed = Signal(str) def __init__( self, settings: BaseSettings, algorithms: typing.Dict[str, typing.Type[SegmentationAlgorithm]], parent=None ): super().__init__(parent) self.settings = settings self.algorithms = algorithms settings.algorithm_changed.connect(self.updated_algorithm) self.stack_layout = QStackedLayout() self.algorithm_choose = QComboBox() self.algorithm_dict: typing.Dict[str, BaseAlgorithmSettingsWidget] = {} self.algorithm_choose.currentTextChanged.connect(self.change_algorithm) self.add_widgets_to_algorithm() self.settings.image_changed.connect(self.image_changed) # self.setMinimumWidth(370) self.setContentsMargins(0, 0, 0, 0) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.algorithm_choose) layout.addLayout(self.stack_layout) self.setLayout(layout) def add_widgets_to_algorithm(self): self.algorithm_choose.blockSignals(True) self.algorithm_choose.clear() for name, val in self.algorithms.items(): self.algorithm_choose.addItem(name) widget = InteractiveAlgorithmSettingsWidget(self.settings, name, val, []) self.algorithm_dict[name] = widget widget.algorithm_thread.execution_done.connect(self.result.emit) widget.algorithm_thread.finished.connect(self.finished.emit) widget.algorithm_thread.started.connect(self.started.emit) widget.algorithm_thread.progress_signal.connect(self.progress_signal.emit) widget.values_changed.connect(self.value_changed.emit) self.stack_layout.addWidget(widget) name = self.settings.get("current_algorithm", "") self.algorithm_choose.blockSignals(False) if name: self.algorithm_choose.setCurrentText(name) def reload(self, algorithms=None): if algorithms is not None: self.algorithms = algorithms for _ in range(self.stack_layout.count()): widget: InteractiveAlgorithmSettingsWidget = self.stack_layout.takeAt(0).widget() widget.algorithm_thread.execution_done.disconnect() widget.algorithm_thread.finished.disconnect() widget.algorithm_thread.started.disconnect() widget.algorithm_thread.progress_signal.disconnect() widget.values_changed.disconnect() self.algorithm_dict = {} self.add_widgets_to_algorithm() def updated_algorithm(self): self.change_algorithm( self.settings.last_executed_algorithm, self.settings.get(f"algorithms.{self.settings.last_executed_algorithm}"), ) def recursive_get_values(self): result = {} for key, widget in self.algorithm_dict.items(): result[key] = widget.recursive_get_values() self.settings.set("algorithm_widget_state", update(self.settings.get("algorithm_widget_state", dict), result)) return result def change_algorithm(self, name, values: dict = None): self.settings.set("current_algorithm", name) widget = self.stack_layout.currentWidget() self.blockSignals(True) if name != widget.name: widget = self.algorithm_dict[name] self.stack_layout.setCurrentWidget(widget) widget.image_changed(self.settings.image) if hasattr(widget, "set_mask") and hasattr(self.settings, "mask"): widget.set_mask(self.settings.mask) elif values is None: self.blockSignals(False) return if values is not None: widget.set_values(values) self.algorithm_choose.setCurrentText(name) self.blockSignals(False) self.algorithm_changed.emit(name) def image_changed(self): current_widget: InteractiveAlgorithmSettingsWidget = self.stack_layout.currentWidget() if hasattr(self.settings, "mask") and hasattr(current_widget, "change_mask"): current_widget.change_mask() current_widget.image_changed(self.settings.image) def mask_changed(self): current_widget: InteractiveAlgorithmSettingsWidget = self.stack_layout.currentWidget() if hasattr(self.settings, "mask") and hasattr(current_widget, "change_mask"): current_widget.change_mask() def current_widget(self) -> InteractiveAlgorithmSettingsWidget: return self.stack_layout.currentWidget() def current_parameters(self) -> SegmentationProfile: widget = self.current_widget() return SegmentationProfile("", widget.name, widget.get_values()) def get_info_text(self): return self.current_widget().algorithm_thread.get_info_text()
class TabContainer(AbstractContainer): dockMove = Signal(object, object, object) class Point(object): def __init__(self, x, y): self.__x = x self.__y = y def x(self): return self.__x def y(self): return self.__y def __init__(self, bench, parent_container): """ Container which layouts its child items on stacked layout and provides a tab bar. :param bench: :param parent_container: """ super(TabContainer, self).__init__(bench, parent_container) self.setAcceptDrops(True) self.refDropRegions = None self.absDropRegions = None self.overlay = DropOverlay(self) self.layout = QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) if CONFIG.debug_layout: self.layout.setContentsMargins(2, 16, 2, 2) self.setLayout(self.layout) self._tabbar = TabHeader() self.layout.addWidget(self._tabbar) self._dockstack = QStackedLayout() self._dockstack.setContentsMargins(0, 0, 0, 0) self._dockstack.setSpacing(0) self.layout.addLayout(self._dockstack) def activateTab(self, uid): _log.debug("Activating tab for dock: {}".format(uid)) for d in self.flatDockList: if d.uid == uid: self._dockstack.setCurrentWidget(d) d.tab.setActive(True) else: d.tab.setActive(False) def closeChild(self, uid): _log.debug("Closing dock: {}".format(uid)) was_active = False for d in self.flatDockList: if d.uid == uid: _log.debug("Removing dock: {}".format(d)) was_active = d.tab.active d.tab.setParent(None) d.tab._dock = None d.parentContainer = None d.close() d.deleteLater() d.setParent(None) self.contentModified.emit() if self._dockstack.count() == 0: # Container is empty, close and propagate self.closing.emit(self._uid) elif was_active: dock = self.flatDockList[0] self.activateTab(dock.uid) @property def flatDockList(self): return [ self._dockstack.itemAt(k).widget() for k in range(self._dockstack.count()) ] @property def docks(self): return self.flatDockList def addItem(self, index, item): # _config.debug check if not isinstance(item, Dock): raise BenchException("Misuse") item.parentContainer = self item.closing.connect(self.closeChild) item.activated.connect(self.activateTab) self._tabbar.addTab(index, item) self._dockstack.insertWidget(index, item) self.activateTab(item.uid) item.setVisible(True) item.tab.setVisible(True) @classmethod def __checkEventMimeTypeData(cls, event): """ Checks the drag events MIME type, and that at least two dockbench items on the area. :param event: drag event / drop event :return: true when the MIME type can be handled """ if not event.mimeData().hasFormat(MIME_TYPE): event.ignore() return False event.accept() return True def dragEnterEvent(self, event): _log.debug("TabContainer: Drag enter event") if not self.__checkEventMimeTypeData(event): return data = event.mimeData().data(MIME_TYPE).data() dock_uid = pickle.loads(data) _log.debug("ETID: {}".format(dock_uid)) for d in self.flatDockList: if d.uid == dock_uid and len(self.flatDockList) == 1: _log.debug("Tab in container") event.ignore() return if self.rect().width() < 200 and self.rect().height() < 200: _log.debug("To less widget left...") event.ignore() return if self.overlay.isHidden(): _log.debug("Drop overlay") self.overlay.raise_() self.overlay.show() w = self._dockstack.currentWidget() pos = w.mapTo(self, QPoint(0, 0)) rect = QRect(pos.x(), pos.y(), w.width(), w.height()) self.overlay.setGeometry(rect) xc = pos.x() + w.width() / 2.0 yc = pos.y() + w.height() / 2.0 self.refDropRegions = { TabContainer.Point(xc - 34, yc): Placement.LEFT, TabContainer.Point(xc + 34, yc): Placement.RIGHT, TabContainer.Point(xc, yc - 34): Placement.TOP, TabContainer.Point(xc, yc + 34): Placement.BOTTOM, TabContainer.Point(xc, yc): Placement.TAB, } self.absDropRegions = { TabContainer.Point(xc - 68, yc): Placement.LEFT, TabContainer.Point(xc + 68, yc): Placement.RIGHT, TabContainer.Point(xc, yc - 68): Placement.TOP, TabContainer.Point(xc, yc + 68): Placement.BOTTOM, } def dragMoveEvent(self, event): if not self.__checkEventMimeTypeData(event): return pos = event.pos() x = pos.x() y = pos.y() for region in self.refDropRegions: if abs(region.x() - x) <= 12 and abs(region.y() - y) <= 12: _log.debug("Drop ref over: {}".format( self.refDropRegions[region])) event.accept() self.overlay.setActiveDropRegion(self.refDropRegions[region]) return for region in self.absDropRegions: if abs(region.x() - x) <= 12 and abs(region.y() - y) <= 12: _log.debug("Drop abs over: {}".format( self.absDropRegions[region])) event.accept() self.overlay.setActiveDropRegion(self.absDropRegions[region], True) return self.overlay.setActiveDropRegion(None) event.ignore() def dragLeaveEvent(self, event): _log.debug("TabContainer: Drag leave event") if not self.overlay.isHidden(): self.overlay.hide() self.refDropRegions = None self.absDropRegions = None def dropEvent(self, event): _log.debug("TabContainer: Drop event") if not self.__checkEventMimeTypeData(event): event.ignore() return data = event.mimeData().data(MIME_TYPE).data() dock_uid = pickle.loads(data) _log.debug("ETID: {}".format(dock_uid)) pos = event.pos() x = pos.x() y = pos.y() for region in self.refDropRegions: if abs(region.x() - x) <= 12 and abs(region.y() - y) <= 12: _log.debug("Drop ref over: {}".format( self.refDropRegions[region])) ref = self._dockstack.currentWidget() # self.dockMove.emit(dock_uid, self.refDropRegions[region], ref.uid) self._bench.dockMove(dock_uid, self.refDropRegions[region], ref.uid) break if self.absDropRegions is not None: for region in self.absDropRegions: if abs(region.x() - x) <= 12 and abs(region.y() - y) <= 12: _log.debug("Drop abs over: {}".format( self.absDropRegions[region])) # self.dockMove.emit(dock_uid, self.absDropRegions[region], None) self._bench.dockMove(dock_uid, self.absDropRegions[region], None) break if not self.overlay.isHidden(): self.overlay.hide() self.refDropRegions = None self.absDropRegions = None def saveLayout(self): layout = super(TabContainer, self).saveLayout() children = [] for dock in self.flatDockList: children.append(dock.saveLayout()) layout["children"] = children return layout def loadLayout(self, layout): for k, child in enumerate(layout["children"]): module_str = child["module"] class_str = child["class"] # Bootstrap part II: Make the child containers mod = __import__(module_str, fromlist=[class_str]) klass = getattr(mod, class_str) child_obj = klass() self.addItem(k, child_obj) child_obj.loadLayout(child)