4

After lots of research, I managed to customize the QTabWidget in PyQt5 (Python 3.6) such that I can assign a different color to an arbitrary tab:

enter image description here

Yes, I know that one can manipulate certain tabs using CSS-selectors like:

  • QTabBar::tab:selected
  • QTabBar::tab:hover
  • QTabBar::tab:selected
  • QTabBar::tab:!selected

But none of these selectors solves the actual problem I have. If I want to highlight the second tab - no matter if it is selected, hovered, ... - neither of these CSS-selectors help me.

I will now explain how I got it eventually working. After that, I'll show where the computation-intensive part is, and why I can't get that out. Hopefully you can help me to improve the efficiency.


The code

Below you can find the source code of my solution. To try it out yourself, just copy-paste the code into a new file (like tab_test.py) and run it. Below the code you find more explanations.

import sys

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *


#########################################################
#             STYLESHEET FOR QTABWIDGET                 #
#########################################################
def get_QTabWidget_style():
    styleStr = str("""
        QTabWidget::pane {             
            border-width: 2px;         
            border-style: solid;       
            border-color: #0000ff;         
            border-radius: 6px;        
        }                              
        QTabWidget::tab-bar {          
            left: 5px;                 
        }                              
    """)
    return styleStr

#########################################################
#               STYLESHEET FOR QTABBAR                  #
#########################################################
def get_QTabBar_style():
    styleStr = str("""
        QTabBar {                                          
            background: #00ffffff;                         
            color: #ff000000;                              
            font-family: Courier;                          
            font-size: 12pt;                               
        }                                                  
        QTabBar::tab {                  
            background: #00ff00;                         
            color: #000000;                              
            border-width: 2px;                             
            border-style: solid;                           
            border-color: #0000ff;                             
            border-bottom-color: #00ffffff;                
            border-top-left-radius: 6px;                   
            border-top-right-radius: 6px;                  
            min-height: 40px;                              
            padding: 2px;                                  
        }                                                  
        QTabBar::tab:selected {                            
            border-color: #0000ff;                             
            border-bottom-color: #00ffffff;                
        }                                                  
        QTabBar::tab:!selected {                           
            margin-top: 2px;                               
        }                                                  
        QTabBar[colorToggle=true]::tab {                   
            background: #ff0000;                         
        }                                                  
    """)

    return styleStr


#########################################################
#                  SUBCLASS QTABBAR                     #
#########################################################
class MyTabBar(QTabBar):
    def __init__(self, *args, **kwargs):
        super(MyTabBar, self).__init__(*args, **kwargs)
        self.__coloredTabs = []
        self.setProperty("colorToggle", False)

    def colorTab(self, index):
        if (index >= self.count()) or (index < 0) or (index in self.__coloredTabs):
            return
        self.__coloredTabs.append(index)
        self.update()

    def uncolorTab(self, index):
        if index in self.__coloredTabs:
            self.__coloredTabs.remove(index)
            self.update()

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

        for i in range(self.count()):
            self.initStyleOption(opt, i)
            if i in self.__coloredTabs:
                self.setProperty("colorToggle", True)
                self.style().unpolish(self)
                self.style().polish(self)

                painter.drawControl(QStyle.CE_TabBarTabShape, opt)
                painter.drawControl(QStyle.CE_TabBarTabLabel, opt)
            else:
                self.setProperty("colorToggle", False)
                self.style().unpolish(self)
                self.style().polish(self)

                painter.drawControl(QStyle.CE_TabBarTabShape, opt)
                painter.drawControl(QStyle.CE_TabBarTabLabel, opt)

        painter.restore()

#########################################################
#                SUBCLASS QTABWIDGET                    #
#########################################################
class MyTabWidget(QTabWidget):
    def __init__(self, *args, **kwargs):
        super(MyTabWidget, self).__init__(*args, **kwargs)
        self.myTabBar = MyTabBar()
        self.setTabBar(self.myTabBar)
        self.setTabsClosable(True)

        self.setStyleSheet(get_QTabWidget_style())
        self.tabBar().setStyleSheet(get_QTabBar_style())

    def colorTab(self, index):
        self.myTabBar.colorTab(index)

    def uncolorTab(self, index):
        self.myTabBar.uncolorTab(index)




'''=========================================================='''
'''|                  CUSTOM MAIN WINDOW                    |'''
'''=========================================================='''
class CustomMainWindow(QMainWindow):

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

        # -------------------------------- #
        #           Window setup           #
        # -------------------------------- #

        # 1. Define the geometry of the main window
        # ------------------------------------------
        self.setGeometry(100, 100, 800, 800)
        self.setWindowTitle("Custom TabBar test")

        # 2. Create frame and layout
        # ---------------------------
        self.__frm = QFrame(self)
        self.__frm.setStyleSheet("QWidget { background-color: #efefef }")
        self.__lyt = QVBoxLayout()
        self.__frm.setLayout(self.__lyt)
        self.setCentralWidget(self.__frm)

        # 3. Insert the TabMaster
        # ------------------------
        self.__tabMaster = MyTabWidget()
        self.__lyt.addWidget(self.__tabMaster)

        # 4. Add some dummy tabs
        # -----------------------
        self.__tabMaster.addTab(QFrame(), "first")
        self.__tabMaster.addTab(QFrame(), "second")
        self.__tabMaster.addTab(QFrame(), "third")
        self.__tabMaster.addTab(QFrame(), "fourth")

        # 5. Color a specific tab
        # ------------------------
        self.__tabMaster.colorTab(1)


        # 6. Show window
        # ---------------
        self.show()

    ''''''

'''=== end Class ==='''


if __name__ == '__main__':
    app = QApplication(sys.argv)
    QApplication.setStyle(QStyleFactory.create('Fusion'))
    myGUI = CustomMainWindow()
    sys.exit(app.exec_())

''''''

The code explained

1. Dynamic stylesheets
I've got a stylesheet for the QTabWidget and one for the QTabBar. The magic is in the last one. The background color of the tab (denoted by the CSS-selector QTabBar::tab) is generally green #00ff00. But when the colorToggle property is on, the color is set to red #ff0000.

 
2. class MyTabBar
I subclass QTabBar into a new class MyTabBar. In this way, I can do two things:

  • I add a function colorTab(index) such that external code can call it to color an arbitrary tab.

  • I override the paintEvent(event) function such that I can apply the color on the chosen tabs.

The colorTab(index) function simply takes an index and adds it to a list. That's it. The list will be checked in the overridden paintEvent(event) function.

After checking the list, the paintEvent(event) function decides whether it should set or clear the property "colorToggle":

    self.setProperty("colorToggle", True)

After setting (or clearing) this property, the paintEvent(event) function proceeds to paint the actual tab:

    self.style().unpolish(self)
    self.style().polish(self)

    painter.drawControl(QStyle.CE_TabBarTabShape, opt)
    painter.drawControl(QStyle.CE_TabBarTabLabel, opt)

 

I have noticed that self.style().unpolish(self) and self.style().polish(self) consume a lot of processing power. But deleting them results in failure. I don't know any (less computational-intensive) alternative.

 
3. class MyTabWidget
I've also subclassed the QTabWidget class. In its constructor, I replace the default QTabBar by my own subclassed MyTabBar. After that, I apply my stylesheets.

 
4. class CustomMainWindow
I create a main window (subclassed from QMainWindow) to simply test the new Tab Widget. That's very simple. I instantiate MyTabWidget() and insert some dummy tabs into it.
Then I color the second one (note: tab counting starts from 0).


The problem explained

The problem is all in the lines:

    self.style().unpolish(self)
    self.style().polish(self)

inside the overridden paintEvent(event) function. They take some execution time, which is a problem because the paintEvent function gets called very regularly. My processor runs at 14% for this simple example (I have a 4Ghz watercooled i7 processor). Such processor load is simply unacceptable.


The platform/environment

I'm running on:

  • Python 3.6.3
  • PyQt5
  • Windows 10 (but please feel free to post your solution if it works on Linux)

Apparently the widget-style seems to be important. On the last lines of the sample code, you can see:

    QApplication.setStyle(QStyleFactory.create('Fusion'))

That widget-style should be consistently the same - both on Windows and Linux. But again - please feel free to post your solution if it works on another non-Fusion style.


First proposed solution

I was recommended to take a look here: Qt TabWidget Each tab Title Background Color

A solution is proposed: Subclass QTabBar and override the paintEvent(event) function. That's quite similar to the solution I already have above, but the code inside the paintEvent(event) function is different. So I give it a try.

First, I translate the given C++ code into Python:

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

        for i in range(self.count()):
            self.initStyleOption(opt, i)
            if i in self.__coloredTabs:
                opt.palette.setColor(QPalette.Button, QColor("#ff0000"))
            painter.drawControl(QStyle.CE_TabBarTabShape, opt)
            painter.drawControl(QStyle.CE_TabBarTabLabel, opt)

Now I replace my previous paintEvent(event) function with this code. I run the file ... but all tabs are green :-(

There must be something I'm doing wrong?

EDIT :
Apparently the tab didn't color because I was mixing stylesheets with QPalette changes. I was suggested to comment out all calls to setStyleSheet(..) and try again. Indeed, the intended tab gets the new color. But I lose all my styles... So this won't really help me.


Second proposed solution

Musicamante has proposed a solution based on QStyleOption helper classes. Please look below to see his answer. I've inserted his solution into my own sample code:

import sys

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *


#########################################################
#             STYLESHEET FOR QTABWIDGET                 #
#########################################################
def get_QTabWidget_style():
    styleStr = str("""
        QTabWidget::pane {             
            border-width: 2px;         
            border-style: solid;       
            border-color: #0000ff;         
            border-radius: 6px;        
        }                              
        QTabWidget::tab-bar {          
            left: 5px;                 
        }                              
    """)
    return styleStr

#########################################################
#               STYLESHEET FOR QTABBAR                  #
#########################################################
def get_QTabBar_style():
    styleStr = str("""
        QTabBar {                                          
            background: #00ffffff;                         
            color: #ff000000;                              
            font-family: Courier;                          
            font-size: 12pt;                               
        }                                                  
        QTabBar::tab {                  
            background: #00ff00;                         
            color: #000000;                              
            border-width: 2px;                             
            border-style: solid;                           
            border-color: #0000ff;                             
            border-bottom-color: #00ffffff;                
            border-top-left-radius: 6px;                   
            border-top-right-radius: 6px;                  
            min-height: 40px;                              
            padding: 2px 12px;                                  
        }                                                  
        QTabBar::tab:selected {                            
            border-color: #0000ff;                             
            border-bottom-color: #00ffffff;                
        }                                                  
        QTabBar::tab:!selected {                           
            margin-top: 2px;                               
        }                                                  
        QTabBar[colorToggle=true]::tab {                   
            background: #ff0000;                         
        }                                                  
    """)

    return styleStr


#########################################################
#                  SUBCLASS QTABBAR                     #
#########################################################
class MyTabBar(QTabBar):
    def __init__(self, parent):
        QTabBar.__init__(self, parent)
        self.colorIndexes = parent.colorIndexes

    def paintEvent(self, event):
        qp = QPainter(self)
        qp.setRenderHints(qp.Antialiasing)
        option = QStyleOptionTab()
        option.features |= option.HasFrame
        palette = option.palette
        for index in range(self.count()):
            self.initStyleOption(option, index)
            palette.setColor(palette.Button, self.colorIndexes.get(index, QColor(Qt.green)))
            palette.setColor(palette.Window, QColor(Qt.blue))
            option.palette = palette
            self.style().drawControl(QStyle.CE_TabBarTab, option, qp)


#########################################################
#                SUBCLASS QTABWIDGET                    #
#########################################################
class MyTabWidget(QTabWidget):
    def __init__(self):
        QTabWidget.__init__(self)
        self.colorIndexes = {
            1: QColor(Qt.red), 
            3: QColor(Qt.blue), 
            }
        self.setTabBar(MyTabBar(self))

        self.tabBar().setStyleSheet(get_QTabBar_style())
        self.setStyleSheet(get_QTabWidget_style())
        self.setTabsClosable(True)



'''=========================================================='''
'''|                  CUSTOM MAIN WINDOW                    |'''
'''=========================================================='''
class CustomMainWindow(QMainWindow):

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

        # -------------------------------- #
        #           Window setup           #
        # -------------------------------- #

        # 1. Define the geometry of the main window
        # ------------------------------------------
        self.setGeometry(100, 100, 800, 800)
        self.setWindowTitle("Custom TabBar test")

        # 2. Create frame and layout
        # ---------------------------
        self.__frm = QFrame(self)
        self.__frm.setStyleSheet("QWidget { background-color: #efefef }")
        self.__lyt = QVBoxLayout()
        self.__frm.setLayout(self.__lyt)
        self.setCentralWidget(self.__frm)

        # 3. Insert the TabMaster
        # ------------------------
        self.__tabMaster = MyTabWidget()
        self.__lyt.addWidget(self.__tabMaster)

        # 4. Add some dummy tabs
        # -----------------------
        self.__tabMaster.addTab(QFrame(), "first")
        self.__tabMaster.addTab(QFrame(), "second")
        self.__tabMaster.addTab(QFrame(), "third")
        self.__tabMaster.addTab(QFrame(), "fourth")

        # 5. Show window
        # ---------------
        self.show()

    ''''''

'''=== end Class ==='''


if __name__ == '__main__':
    app = QApplication(sys.argv)
    QApplication.setStyle(QStyleFactory.create('Fusion'))
    myGUI = CustomMainWindow()
    sys.exit(app.exec_())

''''''

The result gets pretty close to the desired outcome:

enter image description here

Musicamante says:

The only issue here is that the tab border does not use stylesheets (I wasn't able to find how QStyle draws them), so the radius is smaller and the pen width is thinner.

Thank you very much @musicamante! There is still one issue (the borders) but the result is the closest we ever got to the solution.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
K.Mulier
  • 8,069
  • 15
  • 79
  • 141
  • Both `polish` and `unpolish` will more than likely cause a new paint event to be posted. So calling either of these from within `paintEvent` is almost certainly going to result in a "busy-painting" loop as you've discovered. You might want to look at [the answer to this question](https://stackoverflow.com/questions/46137500/qt-tabwidget-each-tab-title-background-color) -- albeit for `C++` rather than `python`. – G.M. Mar 24 '18 at 13:42
  • Hi @G.M. , thank you very much. I have tried the link you sent me, but without success. I've documented my trial (you can see the details if you refresh this page, there is a new little chapter "Proposed solutions"). What am I doing wrong? – K.Mulier Mar 24 '18 at 13:58
  • I think the problem now is that you're mixing style sheets with `QPalette` changes. From what I recall the style sheet will be used in such cases. By way of a quick check try removing (commenting out) all calls to `setStyleSheet` and see what happens. – G.M. Mar 24 '18 at 14:20
  • Hi @G.M. , when I comment out the `setStyleSheet(...)` functions, it works indeed. But then I lose all my styles. Which is annoying (I need them for several reasons). Any other solution that keeps the styles intact? – K.Mulier Mar 24 '18 at 14:30
  • What platforms does this have to work on? This is going to matter *a lot*, because some solutions won't work at all, depending on the specifics of the platform and/or the current widget style in use (see this [Qt FAQ](https://wiki.qt.io/Qt_project_org_faq#Why_does_nothing_happen_when_I_set_the_palette_background_color_on_a_QPushButton_in_some_styles.3F) for more details). You will probably have to enforce the fusion widget-style in order to overcome some of the issues. – ekhumoro Apr 10 '18 at 17:41
  • Hi @ekhumoro , thank you very much for pointing this out. The operating system is Windows 10. The widget-style is `fusion` indeed (see last lines of the code). – K.Mulier Apr 10 '18 at 17:44

1 Answers1

6

EDIT: After a lot of experience I've gained with QStyle, I suddenly remembered about this question due to another one that was recently posted, and realized why the "first proposed solution" linked in the question didn't work and mine either (for the same reasons, but using different implementation). Scroll down for the alternate solution.

First (accepted) answer

I stumbled upon similar issues some weeks ago, and then I studied a bit about how QStyle works. The concept is that you will let Qt draw the whole widget, but using QStyleOption helper classes (there's almost one for every kind of widget).

screenshot of the proposed solution

Here's a simple example (I updated the code), using part of the stylesheets you used. The only issue here is that the tab border does not properly use stylesheets (I wasn't able to find how QStyle draws them), so the radius is smaller and the pen width is thinner.

I tested it and it works without consuming resources. I hope it helps.

class TabBar(QtWidgets.QTabBar):
    def __init__(self, parent):
        QtWidgets.QTabBar.__init__(self, parent)
        self.colorIndexes = parent.colorIndexes
        self.setStyleSheet('''
            QTabBar {
                font-family: Courier;
                font-size: 12pt;
            }
            QTabBar::tab {
                min-height: 40px;
                padding: 2px 8px;
            }
            ''')

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        qp.setRenderHints(qp.Antialiasing)
        option = QtWidgets.QStyleOptionTab()
        option.features |= option.HasFrame
        palette = option.palette
        for index in range(self.count()):
            self.initStyleOption(option, index)
            palette.setColor(palette.Button, self.colorIndexes.get(index, QtGui.QColor(QtCore.Qt.green)))
            palette.setColor(palette.Window, QtGui.QColor(QtCore.Qt.blue))
            option.palette = palette
            self.style().drawControl(QtWidgets.QStyle.CE_TabBarTab, option, qp)


class TabWidget(QtWidgets.QTabWidget):
    def __init__(self):
        QtWidgets.QTabWidget.__init__(self)
        self.setStyleSheet('''
            QTabWidget::pane {
                border: 2px solid blue;
                border-radius: 6px;
            }
            QTabWidget::tab-bar {
                left: 5px;
            }
            ''')
        self.colorIndexes = {
            1: QtGui.QColor(QtCore.Qt.red), 
            3: QtGui.QColor(QtCore.Qt.blue), 
            }
        self.setTabBar(TabBar(self))
        for i in range(5):
            w = QtWidgets.QWidget()
            self.addTab(w, 'tab {}'.format(i))


app = QtWidgets.QApplication(sys.argv)
QtWidgets.QApplication.setStyle('Fusion')
w = TabWidget()
w.show()
sys.exit(app.exec_())

Note: this example only works using Fusion style. Breeze doesn't use palette.Button but palette.Window instead; this means that you might be able to find other palette role combinations in other styles, which might result in a result that better meets your requirements. I don't know if it's actually possible to draw the tab borders through QStyle; if you absolutely need the borders the alternative is to draw them yourself, taking the various content sizes from QStyle.subElementRect().

Alternate (updated and improved) solution

The problem is that, when working with Qt's stylesheets, the optional widget argument of the QStyle functions is really important, because they rely almost completely on the widget's stylesheet to draw its shapes and colors (and compute its metrics), while usually ignoring the palette.

I'd like to add an alternate answer, a workaround which actually is a small "hack", but that, most importantly, solves the inconsistence with the tab border by painting the tab bar exactly as expected.
Also, it seems to be style independent: I've tried it with Breeze, Oxygen, Windows and Fusion styles, and it always gives the same, expected result.

The trick is to create a "private" QTabBar widget (with no parent, to ensure that it won't be shown) that acts as a "proxy", and apply a custom stylesheet to it, which has a default background set; then, if the tab that is going to be painted is one of the "colored" tabs, it uses that internal QTabBar widget as an argument of the drawControl function. I've created an example that can colorize each tab with different colors, but you can just use one if you don't need that level of complexity, obviously.
The important difference here is that we're using a plain QPainter instead of QStylePainter, whose functions wouldn't allow us to set another widget as argument.

def get_QTabBar_style(background='#00ff00'):
    styleStr = str('''
        QTabBar {{
            background: #00ffffff;
            color: #ff000000;
            font-family: Courier;
            font-size: 12pt;
        }}
        QTabBar::tab {{
            background: {};
            color: #000000;
            border-width: 2px;
            border-style: solid;
            border-color: #0000ff;
            border-bottom-color: #00ffffff;
            border-top-left-radius: 6px;
            border-top-right-radius: 6px;
            min-height: 40px;
            padding: 2px;
        }}
        QTabBar::tab:selected {{
            border-color: #0000ff;
            border-bottom-color: #00ffffff;
        }}
        QTabBar::tab:!selected {{
            margin-top: 2px;
        }}
    '''.format(background))

    return styleStr


class MyTabBar(QtWidgets.QTabBar):
    def __init__(self, parent):
        QtWidgets.QTabBar.__init__(self, parent)
        self.setStyleSheet(get_QTabBar_style())
        self.__coloredTabs = {}

    def colorTab(self, index, color='#ff0000'):
        if not 0 <= index < self.count():
            return
        proxy = self.__coloredTabs.get(index)
        if not proxy:
            proxy = self.__coloredTabs[index] = QtWidgets.QTabBar()
        proxy.setStyleSheet(get_QTabBar_style(color))
        self.update()

    def uncolorTab(self, index):
        try:
            self.__coloredTabs.pop(index)
            self.update()
        except:
            return

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

        for i in range(self.count()):
            self.initStyleOption(opt, i)
            self.style().drawControl(
                QtWidgets.QStyle.CE_TabBarTabShape, opt, painter, 
                self.__coloredTabs.get(i, self))
            self.style().drawControl(
                QtWidgets.QStyle.CE_TabBarTabLabel, opt, painter, self)


class MyTabWidget(QtWidgets.QTabWidget):
    def __init__(self):
        QtWidgets.QTabWidget.__init__(self)
        self.setStyleSheet(get_QTabWidget_style())
        tabBar = MyTabBar(self)
        self.setTabBar(tabBar)
        self.colorTab = tabBar.colorTab
        self.uncolorTab = tabBar.uncolorTab

As you can see, the result is almost perfect (except for the small margin between tab bar and tab contents, which I'm afraid is style and OS dependent). screenshot of the improved solution

musicamante
  • 41,230
  • 6
  • 33
  • 58
  • I just realized that the drawing issues are due to the fact that I'm drawing the tabs only, not the full widget. This also means that you'd need to manually draw corner and tab buttons (including the close button) if you need them. As soon as I get home I'll update the answer. – musicamante Apr 11 '18 at 10:30
  • Hi Musicamante, thank you very much for your answer. I've inserted your code into my own sample code, but apparently my stylesheet is causing trouble. Please take a look at `Second proposed solution` - a new chapter I added to my question, completely focuses on your answer :-) – K.Mulier Apr 11 '18 at 12:18
  • Ok, I didn't get that you wanted to "restrict" the style used. It seems like Fusion QStyle uses buttons as "factory" to draw tabs. You can just change the first setColor() argument to `palette.Button` and it works exactly as you need. :-) Btw, remember that QPalette doesn't usually play very well with stylesheets. – musicamante Apr 11 '18 at 18:00
  • Hi @musicamente. Thank you very much. I've updated the sample code (in the chapter `Second proposed solution`). I've run it, but still get problems. I need to deactivate my stylesheet to get it properly coloring the tabs... – K.Mulier Apr 11 '18 at 18:13
  • Hi, I updated the answer, the result is much more similar to your request, but has still some small issues (for instance, the tab border width). – musicamante Apr 11 '18 at 21:14
  • Hi @musicamante. Thank you very much. I've updated the section `Second proposed solution` in my question too. Despite the issue with the tab borders, your approach is the closest we ever got to a solution! You definitely deserve the 50 bonus points. I will give them to you at the end of the week. But for now, I leave the question (and the bonus points) open, still hoping for the golden bullet to come in (a solution without any issues - if it exists at all). – K.Mulier Apr 12 '18 at 07:40
  • Hi @musicamante. Did you try out a few more things to get the tab border fixed? I would like to try myself also, but I lack the deeper knowledge of QStyle etc... I wish I could do something in return for you. At the moment, all I can do is give you the 50 bonus points. Please let me know if there is anything else I can do :-) – K.Mulier Apr 14 '18 at 10:58
  • Hi @musicamante. I gave you the bounty - as you provided the best answer possible. If however you find a fix someday for the tab-border-issue, please share it with us ;-) – K.Mulier Apr 16 '18 at 12:27
  • Hi, @K.Mulier, and thanks for the bounty. Sorry for not answering before, I was busy. The solution I proposed still seems the better yet to me, even if not perfect. If, someday, I'll find better and more complete ways of painting custom tab widgets, I will update the answer. – musicamante Apr 19 '18 at 19:04
  • Hi @musicamante. Thank you very much. That would be awesome :-) – K.Mulier Apr 20 '18 at 07:17
  • @musicamante In your edition you point to my answer, my answer only works if you use the fusion style, if you use another style it probably does not work since it uses many times what the OS provides, and you use Qt Style Sheet it will not work that it cancels any other style – eyllanesc Aug 05 '19 at 01:20
  • @eyllanesc I linked your answer according to the same link used in this same question, but it was just for reference, since the OP used it as a starting point for his own implementation. But, if you think that it's better that I review and rephrase that paragraph, I'll do it. – musicamante Aug 05 '19 at 01:30