Beispiel #1
0
class AlarmTreeEditorDisplay(Display):
    def __init__(self, parent=None):
        super(AlarmTreeEditorDisplay, self).__init__(parent=parent)

        self.app = QApplication.instance()

        # set up the ui
        self.setup_ui()

        # allow add and remove row
        self.add_button.clicked.connect(self.insertChild)
        self.remove_button.clicked.connect(self.removeItem)
        self.remove_button.setEnabled(True)

        # connect save changes
        self.button_box.accepted.connect(self.save_property_changes)

        # upon tree view selection, change the item view
        self.tree_view.selectionModel().selectionChanged.connect(
            self.handle_selection)
        self.tree_view.tree_model.dataChanged.connect(self.item_change)

        self.file_dialog = QFileDialog()
        self.open_config_action = QAction("Open", self)
        self.open_config_action.triggered.connect(self.open_file)
        self.toolbar.addAction(self.open_config_action)

        self.save_config_action = QAction("Save", self)
        self.save_config_action.triggered.connect(self.save_configuration)
        self.toolbar.addAction(self.save_config_action)

        # update configuration name
        self.tree_label.editingFinished.connect(self._update_config_name)

        # default open size
        self.resize(800, 600)

        self.config_tool = PhoebusConfigTool()

    def setup_ui(self):
        self.main_layout = QGridLayout()
        self.setLayout(self.main_layout)

        # add toolbar
        self.toolbar = QToolBar()
        self.main_layout.setMenuBar(self.toolbar)

        # create the tree view layout and add/remove buttons
        self.tree_view_layout = QVBoxLayout()
        self.tree_view = PyDMAlarmTree(self,
                                       config_name="UNITITLED",
                                       edit_mode=True)
        self.tree_view.setEditTriggers(QAbstractItemView.DoubleClicked)
        self.tree_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.tree_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.tree_view.setHeaderHidden(True)

        # Drag/drop
        self.tree_view.setDragDropMode(QAbstractItemView.InternalMove)
        self.tree_view.setDragEnabled(True)
        self.tree_view.setAcceptDrops(True)

        # view sizing
        self.tree_view.setColumnWidth(0, 160)
        self.tree_view.setColumnWidth(1, 160)
        self.tree_view.setColumnWidth(2, 160)

        # lable for tree view
        configuration_indicator = QLabel("Configuration:")
        self.tree_label = QLineEdit("Untitled")

        self.tree_label_layout = QHBoxLayout()
        self.tree_label_layout.addWidget(configuration_indicator)
        self.tree_label_layout.addWidget(self.tree_label)

        self.tree_view_layout.addLayout(self.tree_label_layout)
        self.tree_view_layout.addWidget(self.tree_view)

        # add/ remove buttons
        self.add_remove_layout = QHBoxLayout()
        spacer = QSpacerItem(40, 20, QSizePolicy.Expanding,
                             QSizePolicy.Minimum)
        self.add_remove_layout.addItem(spacer)
        self.add_button = QPushButton("New", self)
        self.add_remove_layout.addWidget(self.add_button)
        self.remove_button = QPushButton("Remove", self)
        self.add_remove_layout.addWidget(self.remove_button)
        self.tree_view_layout.addLayout(self.add_remove_layout)

        # add the tree view to the window
        self.main_layout.addLayout(self.tree_view_layout, 0, 0)

        self.property_layout = QVBoxLayout()
        self.property_layout.addWidget(QLabel("Alarm Properties"))

        # crate property view
        self.property_data_layout = QStackedLayout()
        self.property_layout.addLayout(self.property_data_layout)

        self.property_widget_config = QWidget()
        self.property_widget_config.setWindowTitle("config")

        # create group widget
        self.property_widget_group = QWidget()
        self.property_widget_group.setWindowTitle("group")

        self.property_view_layout_group = QGridLayout()

        # add label
        self.label_edit_group = QLineEdit()
        self.label_label_group = QLabel("NAME")

        # add guidance
        self.guidance_edit_group = QLineEdit()
        self.guidance_label_group = QLabel("GUIDANCE")

        self.property_view_layout_group.addWidget(self.label_label_group, 1, 0)
        self.property_view_layout_group.addWidget(self.label_edit_group, 1, 1)

        self.property_view_layout_group.addWidget(self.guidance_label_group, 2,
                                                  0)
        self.property_view_layout_group.addWidget(self.guidance_edit_group, 2,
                                                  1)

        spacer = QSpacerItem(40, 200, QSizePolicy.Expanding,
                             QSizePolicy.Minimum)

        self.property_view_layout_group.addItem(spacer, 3, 0)
        self.property_view_layout_group.addItem(spacer, 4, 0)
        self.property_view_layout_group.addItem(spacer, 5, 0)
        self.property_view_layout_group.addItem(spacer, 6, 0)
        self.property_view_layout_group.addItem(spacer, 7, 0)
        self.property_view_layout_group.addItem(spacer, 8, 0)

        # create pv widget
        self.property_widget_pv = QWidget()
        self.property_widget_pv.setWindowTitle("pv")

        self.property_view_layout_pv = QGridLayout()

        # add label
        self.label_edit_pv = QLineEdit()
        self.label_label_pv = QLabel("NAME")

        # add guidance
        self.guidance_edit_pv = QLineEdit()
        self.guidance_label_pv = QLabel("GUIDANCE")

        self.property_view_layout_pv.addWidget(self.label_label_pv, 1, 0)
        self.property_view_layout_pv.addWidget(self.label_edit_pv, 1, 1, 1, 3)

        self.property_view_layout_pv.addWidget(self.guidance_label_pv, 2, 0)
        self.property_view_layout_pv.addWidget(self.guidance_edit_pv, 2, 1, 1,
                                               3)

        # add description
        self.description_edit = QLineEdit()
        self.description_label = QLabel("DESCRIPTION")
        self.property_view_layout_pv.addWidget(self.description_label, 3, 0)
        self.property_view_layout_pv.addWidget(self.description_edit, 3, 1, 1,
                                               3)

        # add delay
        self.delay_edit = QLineEdit()
        self.delay_label = QLabel("DELAY")
        self.property_view_layout_pv.addWidget(self.delay_label, 4, 0)
        self.property_view_layout_pv.addWidget(self.delay_edit, 4, 1, 1, 3)
        self.delay_edit.setValidator(QIntValidator())

        # add count
        self.count_edit = QLineEdit()
        self.count_label = QLabel("COUNT")
        self.property_view_layout_pv.addWidget(self.count_label, 5, 0)
        self.property_view_layout_pv.addWidget(self.count_edit, 5, 1, 1, 3)
        self.count_edit.setValidator(QIntValidator())

        # add filter/force pv
        self.filter_edit = QLineEdit()
        self.filter_label = QLabel("ENABLING FILTER")
        self.property_view_layout_pv.addWidget(self.filter_label, 6, 0)
        self.property_view_layout_pv.addWidget(self.filter_edit, 6, 1, 1, 3)

        # enabled, latching, annunciating
        self.enabled_check = QCheckBox("ENABLED")
        self.annunciating_check = QCheckBox("ANNUNCIATING")
        self.latching_check = QCheckBox("LATCHING")
        self.property_view_layout_pv.addWidget(self.enabled_check, 7, 0)
        self.property_view_layout_pv.addWidget(self.annunciating_check, 7, 1)
        self.property_view_layout_pv.addWidget(self.latching_check, 7, 2)

        self.property_view_layout_pv.addItem(spacer, 8, 0)

        # create save button
        self.button_box = QDialogButtonBox(self)
        self.button_box.setOrientation(Qt.Horizontal)
        self.button_box.addButton("Save Properties",
                                  QDialogButtonBox.AcceptRole)

        self.property_layout.addWidget(self.button_box)
        # self.property_layout.addLayout(self.property_view_layout)

        self.property_widget_pv.setLayout(self.property_view_layout_pv)
        self.property_widget_group.setLayout(self.property_view_layout_group)

        self.property_data_layout.addWidget(self.property_widget_config)
        self.property_data_layout.addWidget(self.property_widget_pv)
        self.property_data_layout.addWidget(self.property_widget_group)

        self.main_layout.addLayout(self.property_layout, 0, 1)

        self.setWindowTitle("Alarm Tree Editor")
        self.tree_view.expandAll()

    def minimumSizeHint(self):
        # This is the default recommended size
        # for this screen
        return QSize(400, 200)

    def insertChild(self):
        index = self.tree_view.selectionModel().currentIndex()
        model = self.tree_view.model()

        if model.columnCount(index) == 0:
            if not model.insertColumn(0, index):
                return

        if not model.insertRow(0, index):
            return

        for column in range(model.columnCount(index)):
            child = model.index(0, column, index)
            model.set_data(child, label="NEW_ITEM", role=Qt.EditRole)

    def removeItem(self):
        index = self.tree_view.selectionModel().currentIndex()
        self.tree_view.model().removeRow(index.row(), index.parent())

    @Slot()
    def save_property_changes(self):
        index = self.tree_view.selectionModel().currentIndex()
        item = self.tree_view.model().getItem(index)
        if item.is_group:
            guidance = self.guidance_edit_group.text()
            label = self.label_edit_group.text()
        else:
            guidance = self.guidance_edit_pv.text()
            label = self.label_edit_pv.text()

        self.tree_view.model().set_data(
            index,
            label=label,
            description=self.description_edit.text(),
            delay=self.delay_edit.text(),
            count=self.count_edit.text(),
            enabled=self.enabled_check.isChecked(),
            annunciating=self.annunciating_check.isChecked(),
            latching=self.latching_check.isChecked(),
            alarm_filter=self.filter_edit.text(),
            guidance=guidance,
            role=Qt.EditRole,
        )

    @Slot()
    def handle_selection(self):
        self.remove_button.setEnabled(
            self.tree_view.selectionModel().hasSelection())

        index = self.tree_view.selectionModel().currentIndex()
        item = self.tree_view.model().getItem(index)

        if item.is_group:
            self.guidance_edit_group.setText(item.guidance)
            self.label_edit_group.setText(item.label)
        else:
            self.guidance_edit_pv.setText(item.guidance)
            self.label_edit_pv.setText(item.label)

        if item.is_group:
            # black for configuration screen
            if not item.parent:
                self.property_data_layout.setCurrentWidget(
                    self.property_widget_config)
            # otherwise show group screen and set all disables
            else:
                self.property_data_layout.setCurrentWidget(
                    self.property_widget_group)
                self.description_edit.setEnabled(False)
                self.description_edit.setVisible(False)
                self.description_label.setVisible(False)

                self.count_edit.setEnabled(False)
                self.count_edit.setVisible(False)
                self.count_label.setVisible(False)

                self.delay_edit.setEnabled(False)
                self.delay_edit.setVisible(False)
                self.delay_label.setVisible(False)

                self.latching_check.setEnabled(False)
                self.latching_check.setVisible(False)

                self.annunciating_check.setEnabled(False)
                self.annunciating_check.setVisible(False)

                self.filter_edit.setEnabled(False)
                self.filter_edit.setVisible(False)
                self.filter_label.setVisible(False)

        # set pv enabled
        else:
            self.property_data_layout.setCurrentWidget(self.property_widget_pv)
            self.description_edit.setEnabled(True)
            self.description_edit.setVisible(True)
            self.description_label.setVisible(True)

            self.count_edit.setEnabled(True)
            self.count_edit.setVisible(True)
            self.count_label.setVisible(True)

            self.delay_edit.setEnabled(True)
            self.delay_edit.setVisible(True)
            self.delay_label.setVisible(True)

            self.latching_check.setEnabled(True)
            self.latching_check.setVisible(True)

            self.annunciating_check.setEnabled(True)
            self.annunciating_check.setVisible(True)

            self.filter_edit.setEnabled(True)
            self.filter_edit.setVisible(True)
            self.filter_label.setVisible(True)

            if item.enabled:
                self.enabled_check.setChecked(True)
            else:
                self.enabled_check.setChecked(False)

            if item.latching:
                self.latching_check.setChecked(True)
            else:
                self.latching_check.setChecked(False)

            if item.annunciating:
                self.annunciating_check.setChecked(True)
            else:
                self.annunciating_check.setChecked(False)

    @Slot()
    def item_change(self):
        index = self.tree_view.selectionModel().currentIndex()
        item = self.tree_view.model().getItem(index)

        if item.is_group:
            self.guidance_edit_group.setText(item.guidance)
            self.label_edit_group.setText(item.label)
        else:
            self.guidance_edit_pv.setText(item.guidance)
            self.label_edit_pv.setText(item.label)

        if item.is_group:
            if not item.parent():
                self.property_data_layout.setCurrentWidget(
                    self.property_widget_config)

            else:
                self.property_data_layout.setCurrentWidget(
                    self.property_widget_group)

            self.description_edit.setEnabled(False)
            self.description_edit.setVisible(False)
            self.description_label.setVisible(False)

            self.count_edit.setEnabled(False)
            self.count_edit.setVisible(False)
            self.count_label.setVisible(False)

            self.delay_edit.setEnabled(False)
            self.delay_edit.setVisible(False)
            self.delay_label.setVisible(False)

            self.latching_check.setEnabled(False)
            self.latching_check.setVisible(False)

            self.annunciating_check.setEnabled(False)
            self.annunciating_check.setVisible(False)

            self.filter_edit.setEnabled(False)
            self.filter_edit.setVisible(False)
            self.filter_label.setVisible(False)

        else:
            self.delay_edit.setText(item.delay)
            self.count_edit.setText(item.count)

            if item.enabled:
                self.enabled_check.setChecked(True)
            else:
                self.enabled_check.setChecked(False)

            self.property_data_layout.setCurrentWidget(self.property_widget_pv)
            self.description_edit.setEnabled(True)
            self.description_edit.setVisible(True)
            self.description_label.setVisible(True)

            self.count_edit.setEnabled(True)
            self.count_edit.setVisible(True)
            self.count_label.setVisible(True)

            self.delay_edit.setEnabled(True)
            self.delay_edit.setVisible(True)
            self.delay_label.setVisible(True)

            self.latching_check.setEnabled(True)
            self.latching_check.setVisible(True)

            self.annunciating_check.setEnabled(True)
            self.annunciating_check.setVisible(True)

            self.filter_edit.setEnabled(True)
            self.filter_edit.setVisible(True)
            self.filter_label.setVisible(True)

            if item.latching:
                self.latching_check.setChecked(True)
            else:
                self.latching_check.setChecked(False)

            if item.annunciating:
                self.annunciating_check.setChecked(True)
            else:
                self.annunciating_check.setChecked(False)

    def ui_filepath(self):
        # No UI file is being used
        return None

    @Slot(bool)
    def open_file(self, checked):
        modifiers = QApplication.keyboardModifiers()
        try:
            curr_file = self.current_file()
            folder = os.path.dirname(curr_file)
        except Exception:
            folder = os.getcwd()

        filename = QFileDialog.getOpenFileName(
            self, "Open File...", folder,
            "XML (*.xml);; ALH Config (*.alhConfig)")
        filename = filename[0] if isinstance(filename,
                                             (list, tuple)) else filename

        if filename:
            filename = str(filename)

            # if alh file selected, open conversion prompt
            if filename[-9:] == "alhConfig":
                self.legacy_window = LegacyWindow(filename)
                self.legacy_window.exec_()

                if self.legacy_window.converted_filename:
                    self.import_configuration(
                        self.legacy_window.converted_filename)

            else:
                self.import_configuration(filename)

    def import_configuration(self, filename):
        nodes = self.config_tool.parse_config(filename)
        self.tree_view.model().import_hierarchy(nodes)
        self.tree_label.setText(self.tree_view.model()._nodes[0].label)

    @Slot()
    def save_configuration(self):
        modifiers = QApplication.keyboardModifiers()
        try:
            curr_file = self.current_file()
            folder = os.path.dirname(curr_file)
        except Exception:
            folder = os.getcwd()

        filename = QFileDialog.getSaveFileName(self, "Save File...", folder,
                                               "Configration files (*.xml)")
        filename = filename[0] if isinstance(filename,
                                             (list, tuple)) else filename

        self.config_tool.save_configuration(self.tree_view.model()._root_item,
                                            filename)

    def _update_config_name(self):
        name = self.tree_label.text()
        self.tree_view.model()._nodes[0].label = name

    def _import_legacy_file(self):
        convert_alh_to_phoebus()
Beispiel #2
0
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()
Beispiel #3
0
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)