PyQt4: How to color each tab in QTabWidget separately?
Asked Answered
C

1

0

I'm working on a project with a GUI, for which I am using Python with PyQt4 module.

Here is my demo code:

import sys
from PyQt4 import QtGui, QtCore

class Window(QtGui.QMainWindow):

    def __init__(self):
        super(Window, self).__init__()
        self.setWindowTitle('PyQt4 demo')
        self.setGeometry(50, 50, 1000, 1000)
        self.createTabs()
        self.styleTabs()
        self.show()

    def createTabs(self):
        '''Creates a QTabWidget with 5 tabs,
        named 1, 2, 3, 4, 5
        '''

        self.tabs = QtGui.QTabWidget(self)
        self.tabs.resize(1000, 1000)

        contents1 = QtGui.QWidget()
        contents2 = QtGui.QWidget()
        contents3 = QtGui.QWidget()
        contents4 = QtGui.QWidget()
        contents5 = QtGui.QWidget()

        self.tabs.addTab(contents1, '1')
        self.tabs.addTab(contents2, '2')
        self.tabs.addTab(contents3, '3')
        self.tabs.addTab(contents4, '4')
        self.tabs.addTab(contents5, '5')

    def styleTabs(self):
        #Would like to add some code here which colors
        #each tab with a different color.
        pass


def run():
    app = QtGui.QApplication(sys.argv)
    GUI = Window()
    sys.exit(app.exec_())

run()

Most objects (including QtabWidget and QTabBar) support styling with CSS using .setStyleSheet(str) method. But with this I could only achieve coloring all tabs with the same color. I've also found a way to color selected, first, last tab, but could never achieve coloring a tab for ex.: with an index of 2.

For example:

self.tabs.setStyleSheet('''
    QTabBar::tab {background-color: green;}
    QTabBar::tab:selected {background-color: red;}
    QTabBar::tab:first {background-color: red;}
    QTabBar::tab:last {background-color: red;}
    ''')

I've also tried applying color to current QTabBar. This works with Qt, but not with PyQt apparently:

tab = self.tabs.tabBar()
tab.setStyleSheet('background-color: grey;')

The PyQt4 coloring methods didn't work either:

plt = QtGui.QPalette()
clr = QtGui.QColor()
clr.setRgb(100, 100, 100)
plt.setColor(10, clr)
tab.setPalette(plt)

I've been searching on web a lot, but haven't found any solutions for this problem. At this point, I'm not even sure an easy solution exists.

Is there a way to modify PyQt4 source code, so one of the above techniques could be applied?

Additional info:

  • Python version 3.4

  • PyQt version 4.12

Causeway answered 16/12, 2017 at 10:36 Comment(2)
What platform are you on? There's an easy solution for linux, but I don't think there's any way to do it on windows or mac, because they use pixmaps to render the background.Hayner
@Hayner I am using windows, forgot to mention that. I’ll try what I can do with QPainter. Thank you for reply anyway.Causeway
H
1

Unfortunally, QTabBar doesn't expose all its properties, since its contents are not children widgets layed out in a normal fashion, but are drawn internally using private methods.

There are two possibilities, though.

  1. Draw the tabbar by hand, using its paintEvent. Using QStyle draw* methods, it is possible to customize it as you want, while keeping consistency with the current theme; that's not an easy task, but can be done.

  2. Customize the background of the current selected tab only: using QTabBar's currentChanged signal, you can easily reset the stylesheet everytime the current index is changed

This is an example:

def createTabs(self):
    #[create your tabs, then...]
    self.tabColors = {
        0: 'green', 
        1: 'red', 
        2: 'yellow', 
        3: 'orange', 
        4: 'blue', 
        }
    self.tabs.tabBar().currentChanged.connect(self.styleTabs)

[...]

def styleTabs(self, index):
    self.tabs.setStyleSheet('''
        QTabBar::tab {{}}
        QTabBar::tab:selected {{background-color: {color};}}
        '''.format(color=self.tabColors[index]))

You might want to "initialize" it when the widget is first shown, by calling styleTabs(0), since the colouring is applied when the signal is fired only.

Hairless answered 16/12, 2017 at 14:43 Comment(5)
Thank you for your answer! I think QPaintEvent will be right for my customization needs.Causeway
@ZedsZen. As I already pointed out, the QPainter approach works on linux, but does not work on windows or mac.Hayner
@ekhumoro: The "basic" paintEvent implementationo won't work, but I think that it could be achieved by using QStyle primitive control drawing. It might not look as a standard tab, but I've not deepen enough into it, since I've only used basic drawing only ( doc.qt.io/qt-5/qdrawutil-h.html ): QStyle's drawing might achieve this, but it's a bit more complicated and requires a deeper knowledge on how every widget is actually drawn. Apart from that, one could draw its own version of the control, without caring about system graphical consistency.Hairless
@musicamante. No, you are just plain wrong about this - it is simply not possible to use QStyle to do this on windows/mac. If you don't believe me, read the Qt FAQ (but note that QProxyStyle is only available in PyQt5, not PyQt4). I have already provided a solution using QStyle drawing - but it only works on linux. I think the only way to do this is to bypass the current style and draw everything from scratch.Hayner
@Hayner As I wrote, "I think" that you could try to draw a very "similar" QTabBar widget (not a "native" one): what I was talking about was drawing the bar using primitives and controls, not QStyleOptionTab - sorry, I wasn't clear. But, as you pointed out too, at this point, it would be much easier to write draw from scratch :-)Hairless

© 2022 - 2024 — McMap. All rights reserved.