Search code examples
pythonpyqt5qwidgetqtabwidgetqtabbar

Left alignment of tab names


In PyQt5, it is not possible to create tabs on the left and arrange them horizontally by aligning them vertically. You can place it only on the left, but then the tabs will be vertically arranged. I found a code that allows you to place tabs horizontally on the left, but the names of the tabs are aligned in the middle. Is it possible to align tabs to the left without the ProxyStyle class?

Image:

enter image description here

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QWidget
import sys


class TabWidget(QtWidgets.QTabWidget):
    def __init__(self):
        QtWidgets.QTabWidget.__init__(self)
        self.setTabBar(self.TabBar(self))
        self.setTabPosition(QTabWidget.TabPosition.West)

    class TabBar(QtWidgets.QTabBar):
        def tabSizeHint(self, index):
            s = QtWidgets.QTabBar.tabSizeHint(self, index)
            s.transpose()
            return s

        def paintEvent(self, event):
            painter = QtWidgets.QStylePainter(self)
            opt = QtWidgets.QStyleOptionTab()

            for i in range(self.count()):
                self.initStyleOption(opt, i)
                painter.drawControl(QStyle.ControlElement.CE_TabBarTabShape, opt)
                painter.save()

                s = opt.rect.size()
                s.transpose()
                r = QtCore.QRect(QtCore.QPoint(), s)
                r.moveCenter(opt.rect.center())
                opt.rect = r

                c = self.tabRect(i).center()
                painter.translate(c)
                painter.rotate(90)
                painter.translate(-c)
                painter.drawControl(QStyle.ControlElement.CE_TabBarTabLabel, opt);
                painter.restore()


# class ProxyStyle(QtWidgets.QProxyStyle):
#     def drawControl(self, element, opt, painter, widget):
#         if element == QStyle.ControlElement.CE_TabBarTabLabel:
#             r = QtCore.QRect(opt.rect)
#             r.setHeight(opt.fontMetrics.horizontalAdvance(opt.text))
#             r.moveBottom(opt.rect.bottom())
#             opt.rect = r
#         # QtWidgets.QProxyStyle.drawControl(self, element, opt, painter, widget)
#         super().drawControl(element, opt, painter, widget)


if __name__ == '__main__':
    app = QApplication([])
    # app.setStyle(ProxyStyle())  # Is it possible to align the names without this class?

    tab = TabWidget()
    tab.setGeometry(0, 0, 5000, 5000)
    tab.addTab(QWidget(), "Tab with a long name")
    tab.addTab(QWidget(), "Tab 2")
    tab.show()

    sys.exit(app.exec())

Solution

  • I solved my problem (get rid of the custom ProxyStyle class and register the left alignment in the TabWidget.

    class TabWidget(QtWidgets.QTabWidget):
        def __init__(self):
            QtWidgets.QTabWidget.__init__(self)
            self.setTabBar(self.TabBar(self))
            self.setTabPosition(QTabWidget.TabPosition.West)
    
        class TabBar(QtWidgets.QTabBar):
            def tabSizeHint(self, index):
                s = QtWidgets.QTabBar.tabSizeHint(self, index)
                s.transpose()
                s.setHeight(int(s.height() * 1.3))  # We increase the height of each panel in which the tab name is placed
                return s
    
            def paintEvent(self, event):
                painter = QtWidgets.QStylePainter(self)
                opt = QtWidgets.QStyleOptionTab()
    
                for i in range(self.count()):
                    self.initStyleOption(opt, i)
                    painter.drawControl(QStyle.ControlElement.CE_TabBarTabShape, opt)
                    painter.save()
    
                    r = self.tabRect(i)
                    r.moveLeft(r.left() + 20)  # Move the rectangle to the left to align to the left edge
                    opt.rect = r
    
                    painter.translate(r.topLeft())
                    painter.drawText(QtCore.QRect(0, 0, r.width(), r.height()), QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, opt.text)
                    painter.restore()