-1

In reference to this answer on adding a new tab button to QTabWidget, I am unsure where the QPushButton is added to the QTabBar. I assume the setParent method on the pushButton adds it to the tab bar. But when I try to implement it, the pushButton doesnt seem to appear anywhere on the tab bar even if I add hard values to the move operation.

Here is a minimum reproducible example,

from PyQt5 import QtGui, QtCore, QtWidgets
class TabBarPlus(QtWidgets.QTabBar):
    """Tab bar that has a plus button floating to the right of the tabs."""

    plusClicked = QtCore.pyqtSignal()

    def __init__(self):
        super().__init__()

        # Plus Button
        self.plusButton = QtWidgets.QPushButton("+")
        self.plusButton.setParent(self)
        self.plusButton.setFixedSize(20, 20)  # Small Fixed size
        self.plusButton.clicked.connect(self.plusClicked.emit)
        self.movePlusButton() # Move to the correct location
    # end Constructor

    def sizeHint(self):
        """Return the size of the TabBar with increased width for the plus button."""
        sizeHint = QtWidgets.QTabBar.sizeHint(self) 
        width = sizeHint.width()
        height = sizeHint.height()
        return QtCore.QSize(width+25, height)
    # end tabSizeHint

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

        self.movePlusButton()
    # end resizeEvent

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

        self.movePlusButton()
    # end tabLayoutChange

    def movePlusButton(self):
        """Move the plus 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())])
        # size = 0
        # for i in range(self.count()):
        #     size += self.tabRect(i).width()

        # Set the plus button location in a visible area
        h = self.geometry().top()
        w = self.width()
        if size > w: # Show just to the left of the scroll buttons
            self.plusButton.move(w-54, h)
        else:
            self.plusButton.move(size, h)
    # end movePlusButton
# end class MyClass

class CustomTabWidget(QtWidgets.QTabWidget):
    """Tab Widget that that can have new tabs easily added to it."""

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

        # Tab Bar
        self.tab = TabBarPlus()
        self.setTabBar(self.tab)

        # Properties
        self.setMovable(True)
        self.setTabsClosable(True)

        # Signals
        self.tab.plusClicked.connect(self.addTab)
        # self.tab.tabMoved.connect(self.moveTab)
        # self.tabCloseRequested.connect(self.removeTab)
    # end Constructor
# end class CustomTabWidget
class AppDemo(QtWidgets.QMainWindow):
    def __init__(self):
        super(AppDemo, self).__init__()
        self.centralwidget = QtWidgets.QWidget(self)
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setContentsMargins(0, -1, 0, -1)

        self.playlist_manager = CustomTabWidget(self.centralwidget)

        self.horizontalLayout.addWidget(self.playlist_manager)

        blankWidget = QtWidgets.QWidget(self.playlist_manager)
        self.playlist_manager.addTab(blankWidget, "New")
        self.setCentralWidget(self.centralwidget)

        self.show()
# end class AppDemo


def main():
    import sys
    app = QtWidgets.QApplication(sys.argv)

    w = AppDemo()
    w.setWindowTitle('AppDemo')
    w.show()

    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

Expected behvaiour is that a "+" button appears at the right of all the tabs, but no such button appears.

Community
  • 1
  • 1
Blaine
  • 576
  • 9
  • 30
  • I understand that addTab method exists, but I want to add a custom button that calls the add tab method so that whenever the button is pressed a new tab is added. In the other solutions listed in the answer I linked a blank tab is used, but that would cause issues with a lot of things, and since the above solution has worked, I was interested in why it wasnt working now. – Blaine Apr 24 '20 at 08:57
  • I am unsure if you understand what my problem is. I will add a minimum reproducible example in accordance to the answer I referred in the original question – Blaine Apr 24 '20 at 09:05
  • I'm sorry, but I think I can't help you out; I'm coding in `c++` and the syntax of python has been too long ago. The functions being called, or the approach, are things I can help with. I would have connected signal of the tab button being pressed with a function that either creates or shows the buttons using the signals and slots mechanism. Since I've misunderstood, I've deleted my previous comments. –  Apr 24 '20 at 09:11
  • That is also what i thought, but that would mess with custom tab close behaviour I have along with some things where I use the tab count. I had hoped to subclass QTabBar and add a custom QPushButton that would stay at the right of all open tabs and would emit a signal when pressed that i could then catch and add tabs. – Blaine Apr 24 '20 at 09:21

1 Answers1

0

Okay so after some brainstorming, I figured the issue out. Unlike PyQt4. The QTabBar width does not span the entire width of the QTabWidget, and as such the PlusButton when moved to the right of all the tabs will exceed the width of the parent widget and disappear.

The Solution to this is to add the QPushButton in the QTabWidget itself and emit layoutchange from the QTabBar.

Here is a working example, I have modified values to fit my use case

class tabBarPlus(QTabBar):
    layoutChanged = pyqtSignal()
    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.layoutChanged.emit()

    def tabLayoutChange(self):
        super().tabLayoutChange()
        self.layoutChanged.emit()


class customTabWidget(QTabWidget):
    plusClicked = pyqtSignal()
    def __init__(self, parent=None):
        super(customTabWidget, self).__init__(parent)

        self.tab = tabBarPlus()
        self.setTabBar(self.tab)

        self.plusButton = QPushButton('+', self)
        self.plusButton.setFixedSize(35, 25)
        self.plusButton.clicked.connect(self.plusClicked.emit)
        self.setMovable(True)
        self.setTabsClosable(True)

        self.tab.layoutChanged.connect(self.movePlusButton)

    def movePlusButton(self):
        size = sum([self.tab.tabRect(i).width() for i in range(self.tab.count())])
        h = max(self.tab.geometry().bottom() - 24, 0)
        w = self.tab.width()
        print(size, w, h)
        if size > w:
            self.plusButton.move(w-self.plusButton.width(), h)
        else:
            self.plusButton.move(size-2, h)
Blaine
  • 576
  • 9
  • 30