Beispiel #1
0
class EditableTabBar(QTabBar, object):
    """
    Basic implementation of an editable tab bar
    """

    addTabClicked = Signal()
    tabRenamed = Signal(object, object, object)

    def __init__(self, parent=None):
        super(EditableTabBar, self).__init__(parent=parent)

        self._is_editable = True

        self._editor = QLineEdit(self)
        self._editor.setWindowFlags(Qt.Popup)
        self._editor.setFocusProxy(self)
        self._editor.editingFinished.connect(self.handle_editing_finished)
        self._editor.installEventFilter(self)

        self.add_tab_btn = EditableAddButton(parent=self)
        self._move_add_tab_btn()

        self.setDrawBase(False)

        self.add_tab_btn.clicked.connect(self.addTabClicked.emit)

    def is_editable(self):
        """
        Returns whether the tab bar enables rename mode when the user double clicks on the tab
        :return: bool
        """

        return self._is_editable

    def set_is_editable(self, flag):
        """
        Sets whether the tabs are editable or not
        :param flag: bool
        """

        self._is_editable = bool(flag)

    def edit_tab(self, index):
        """
        Function that is called when the tab is going to be edited
        :param index:
        :return:
        """

        if not self._is_editable:
            return

        rect = self.tabRect(index)
        self._editor.setFixedSize(rect.size())
        self._editor.move(self.parent().mapToGlobal(rect.topLeft()))
        self._editor.setText(self.tabText(index))
        self._editor.selectAll()
        if not self._editor.isVisible():
            self._editor.show()

    def handle_editing_finished(self):
        """
        Function that is called when the tab edit has been completed
        """

        index = self.currentIndex()
        if index >= 0:
            self._editor.hide()
            for i in range(self.count()):
                if self.tabText(i) == self._editor.text():
                    LOGGER.warning(
                        'Impossible to rename category because exists a tab with the same name!'
                    )
                    return
            old_name = self.tabText(index)
            self.setTabText(index, self._editor.text())
            self.tabRenamed.emit(index, self.tabText(index), old_name)

    def sizeHint(self):
        """
        Return the size of the tab bar with increased width for the add tab button
        :return: QSize, size of the tab bar
        """

        size_hint = super(EditableTabBar, self).sizeHint()
        return QSize(size_hint.width() + 25, size_hint.height())

    def resizeEvent(self, event):
        """
        Resize the widget and make sure the add tab button is in the correct location
        """

        super(EditableTabBar, self).resizeEvent(event)
        self._move_add_tab_btn()

    def tabLayoutChange(self):
        """
        This virtual handler is called whenever the tab layout changes.
        If anything changes make sure the add char btn is in the correct location
        """

        super(EditableTabBar, self).tabLayoutChange()
        self._move_add_tab_btn()

    def eventFilter(self, widget, event):
        if ((event.type() == QEvent.MouseButtonPress
             and not self._editor.geometry().contains(event.globalPos()))
                or (event.type() == QEvent.KeyPress
                    and event.key() == Qt.Key_Escape)):
            self._editor.hide()
            return True
        return QTabBar.eventFilter(self, widget, event)

    def mouseDoubleClickEvent(self, event):
        index = self.tabAt(event.pos())
        if index >= 0:
            if not self._is_editable:
                return
            self.edit_tab(index)

    def _move_add_tab_btn(self):
        """
        Move the add tab button to the correct location
        """

        # Find the width of all of the tabs
        size = sum([self.tabRect(i).width() for i in range(self.count())])
        # Set the add tab button location in a visible area
        h = self.geometry().top()
        w = self.width()
        if size > w:
            self.add_tab_btn.move(w - 50, h)
        else:
            self.add_tab_btn.move(size, h)
Beispiel #2
0
class DockTitleBar(QWidget, object):
    def __init__(self, dock_widget, renamable=False):
        super(DockTitleBar, self).__init__(dock_widget)

        self._renamable = renamable

        main_layout = QHBoxLayout()
        main_layout.setContentsMargins(0, 0, 0, 1)
        self.setLayout(main_layout)

        self._buttons_box = QGroupBox('')
        self._buttons_box.setObjectName('Docked')
        self._buttons_layout = QHBoxLayout()
        self._buttons_layout.setSpacing(1)
        self._buttons_layout.setMargin(2)
        self._buttons_box.setLayout(self._buttons_layout)
        main_layout.addWidget(self._buttons_box)

        self._title_label = QLabel(self)
        self._title_label.setStyleSheet('background: transparent')
        self._title_edit = QLineEdit(self)
        self._title_edit.setVisible(False)

        self._button_size = QSize(14, 14)

        self._dock_btn = QToolButton(self)
        self._dock_btn.setIcon(resources.icon('restore_window', theme='color'))
        self._dock_btn.setMaximumSize(self._button_size)
        self._dock_btn.setAutoRaise(True)
        self._close_btn = QToolButton(self)
        self._close_btn.setIcon(resources.icon('close_window', theme='color'))
        self._close_btn.setMaximumSize(self._button_size)
        self._close_btn.setAutoRaise(True)

        self._buttons_layout.addSpacing(2)
        self._buttons_layout.addWidget(self._title_label)
        self._buttons_layout.addWidget(self._title_edit)
        self._buttons_layout.addStretch()
        self._buttons_layout.addSpacing(5)
        self._buttons_layout.addWidget(self._dock_btn)
        self._buttons_layout.addWidget(self._close_btn)

        self._buttons_box.mouseDoubleClickEvent = self.mouseDoubleClickEvent
        self._buttons_box.mousePressEvent = self.mousePressEvent
        self._buttons_box.mouseMoveEvent = self.mouseMoveEvent
        self._buttons_box.mouseReleaseEvent = self.mouseReleaseEvent

        dock_widget.featuresChanged.connect(self._on_dock_features_changed)
        self._title_edit.editingFinished.connect(self._on_finish_edit)
        self._dock_btn.clicked.connect(self._on_dock_btn_clicked)
        self._close_btn.clicked.connect(self._on_close_btn_clicked)

        self._on_dock_features_changed(dock_widget.features())
        self.set_title(dock_widget.windowTitle())
        dock_widget.installEventFilter(self)
        dock_widget.topLevelChanged.connect(self._on_change_floating_style)

    @property
    def renamable(self):
        return self._renamable

    @renamable.setter
    def renamable(self, flag):
        self._renamable = flag

    def eventFilter(self, obj, event):
        if event.type() == QEvent.WindowTitleChange:
            self.set_title(obj.windowTitle())
        return super(DockTitleBar, self).eventFilter(obj, event)

    def mouseMoveEvent(self, event):
        event.ignore()

    def mousePressEvent(self, event):
        event.ignore()

    def mouseReleaseEvent(self, event):
        event.ignore()

    def mouseDoubleClickEvent(self, event):
        if event.pos().x() <= self._title_label.width() and self._renamable:
            self._start_edit()
        else:
            super(DockTitleBar, self).mouseDoubleClickEvent(event)

    def update(self, *args, **kwargs):
        self._on_change_floating_style(self.parent().isFloating())
        super(DockTitleBar, self).update(*args, **kwargs)

    def set_title(self, title):
        self._title_label.setText(title)
        self._title_edit.setText(title)

    def add_button(self, button):
        button.setAutoRaise(True)
        button.setMaximumSize(self._button_size)
        self._buttons_layout.insertWidget(5, button)

    def _start_edit(self):
        self._title_label.hide()
        self._title_edit.show()
        self._title_edit.setFocus()

    def _finish_edit(self):
        self._title_edit.hide()
        self._title_label.show()
        self.parent().setWindowTitle(self._title_edit.text())

    def _on_dock_features_changed(self, features):
        if not features & QDockWidget.DockWidgetVerticalTitleBar:
            self._close_btn.setVisible(features
                                       & QDockWidget.DockWidgetClosable)
            self._dock_btn.setVisible(features
                                      & QDockWidget.DockWidgetFloatable)
        else:
            raise ValueError('Vertical title bar is not supported!')

    def _on_finish_edit(self):
        self._finish_edit()

    def _on_dock_btn_clicked(self):
        self.parent().setFloating(not self.parent().isFloating())

    def _on_close_btn_clicked(self):
        self.parent().toggleViewAction().setChecked(False)
        self.parent().close()

    def _on_change_floating_style(self, state):
        pass
Beispiel #3
0
class EditableTearOffTabBar(TearOffTabBar, object):
    """
    Extended implementation of an editable tab bar with:
        - Rename functionality
        - Tear off functionality
    """

    tab_label_renamed = Signal(str, str)
    request_remove = Signal(int)
    tab_changed = Signal(int)

    def __init__(self, parent=None):
        super(EditableTearOffTabBar, self).__init__(parent=parent)

        self._editor = QLineEdit(self)
        self._editor.setWindowFlags(Qt.Popup)
        self._editor.setFocusProxy(self)
        self._editor.setFocusPolicy(Qt.StrongFocus)
        self._editor.editingFinished.connect(self.handle_editing_finished)
        self._editor.installEventFilter(self)
        self._editor.setValidator(QRegExpValidator(nameRegExp))
        self._edit_index = -1

    def edit_tab(self, index):
        """
        This set the tab in edit mode
        This method is called when double click on the tab
        :param index: int, Index of the tab to be renamed
        """

        # Show the editable line and position it on top of the selected tab
        rect = self.tabRect(index)
        self._editor.setFixedSize(rect.size())
        self._editor.move(self.parent().mapToGlobal(rect.topLeft()))
        self._editor.setText(self.tabText(index))
        if not self._editor.isVisible():
            self._editor.show()
            self._edit_index = index

    def handle_editing_finished(self):
        """
        This finish the edit of the tab name
        """

        # This method only works of we are editing any tab
        if self._edit_index >= 0:
            # Hide the text and update tab text
            self._editor.hide()
            old_text = self.tabText(self.__editIndex)
            new_text = self._editor.text()
            if old_text != new_text:
                names = [self.tabText(i) for i in range(self.count())]
                new_text = naming.get_numeric_name(new_text, names)
                self.setTabText(self._edit_index, new_text)
                self.tab_label_renamed.emit(old_text, new_text)
                self._edit_index = -1

    def eventFilter(self, widget, event):
        """
        If we click (with mouse or keyboard) on registered widgets we hide the editor
        """

        if event.type(
        ) == QEvent.MouseButtonPress and not self._editor.geometry().contains(
                event.globalPos()) or event.type(
                ) == QEvent.KeyPress and event.key() == Qt.Key_Escape:
            self._editor.hide()
            return False
        return QTabBar.eventFilter(self, widget, event)