Exemple #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)
Exemple #2
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)