Getting QTabWidget to look more like Maya's native

Hey folks,
anyone know how to get rid of this thin line under all the tabs in a QTabWidget so it can be more like a maya native bit of UI? I tried a stylesheet of ("QTabWidget::pane {border: 0px solid #000000;}") which does remove it but breaks more things for some reason (second image), making the unselected tabs as light as the selected tabs, no idea why, guess my stylesheet experience is failing me. thanks

from PySide2 import QtWidgets
        
class Tab1(QtWidgets.QWidget):
    
    def __init__(self, parent=None, ):
        super(Tab1, self).__init__(parent)
        
        main_layout = QtWidgets.QVBoxLayout(self)
        main_layout.addWidget(QtWidgets.QLabel("Tab:"))
        main_layout.addStretch()

        
class TabWidgetTesting(QtWidgets.QDialog):

    def __init__(self, parent=None):
        super(TabWidgetTesting, self).__init__(parent=parent)
        self.setMinimumSize(240, 320)

        self.create_widgets()
        self.create_layouts()
    
    def create_widgets(self):
        self.Tab1 = Tab1()
        self.Tab2 = Tab1()
        
        self.tab_widget = QtWidgets.QTabWidget()
        #self.tab_widget.setStyleSheet("QTabWidget::pane {border: 0px solid #000000;}") 
        self.tab_widget.addTab(Tab1(), "Tab 1")
        self.tab_widget.addTab(Tab1(), "Tab 2")
        
        
    def create_layouts(self):
        main_layout = QtWidgets.QVBoxLayout(self)
        main_layout.addWidget(self.tab_widget)
    

if __name__ == "__main__":
    test_dialog = TabWidgetTesting()
    test_dialog.show()
    

01
02

2 Likes

As far as I know, this is simply tedious. If somebody else knows a good workaround, please share.

Maya doesn’t use stylesheets, it uses QStyle. And unfortunately QStyle and stylesheets don’t mix well. So if you do set a stylesheet on a widget, it overrides a bunch of other values that you didn’t explicitly set in your stylesheet. That’s why it looks bad when you set the stylesheet.

You could, of course, make a stylesheet that explicitly sets all of those values. But there’s no easy way to know which values. And there is no way to extract an equivalent stylesheet from Maya. It’s just tedious guess-n-check work. (I couldn’t find anywhere that somebody has done this and shared their code. Certainly worth double checking, though. Maybe your Google-fu is stronger)

Another way I know of that could work (but I have NOT yet tried) is using a QtWidgets.QProxyStyle. From what I understand, you would have to subclass it and override some methods to change how the QTabWidget pane is drawn. Then you wrap the current style in that proxy, and set that proxy style on your dialog. I don’t know how much work it is, but I think it is the “Technically Correct™” way to do this.

1 Like

Thanks, this was driving me up the wall but that answer makes terrible, horrible sense.

Annoying this hack, moving the tab bar down seems to make it look the same, or 1 pixel off or something. self.tab_widget.setStyleSheet("QTabWidget:tab-bar:top {top: 1px;}")

assuming MayaUSD developers work in the same team as Maya developers (at least in the same company), we can use their QSS :wink:

setStyleSheet("""
QTabWidget::tab-bar:top {
    left: 10px; /* move to the right by 5px */
    top: 1px;
}
QTabWidget::tab-bar:left {
    right: 1px;
}
QTabWidget::top:pane {
    border: none;
    border-top: 1px solid rgb(42, 42, 42);
}
""")

thanks, yeh its similar to the uncommented code i put though, it’s still not conformant with a native maya one if you put them side by side

Not sure if I got this right, kinda deep for me, but I learn maya GUI programing from Chris Zurbriggs video, and that’s what he does, I don’t actually fully understand those deep OOP, but it came out looks very maya like.

from PySide2 import QtWidgets
from shiboken2 import wrapInstance
import maya.OpenMayaUI as omui


def maya_main_window():
      main_window_ptr = omui.MQtUtil.mainWindow()
      return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
      

class TestDialog(QtWidgets.QDialog):
      
      def __init__(self, parent=maya_main_window()):
            super(TestDialog, self).__init__(parent)
            
            self.setWindowTitle("Test Dialog")

I didn’t use any stylesheet to make it look like maya native windows, it just look like it, and I vaguely remember the wrapInstance() somehow works with the super() and voila, maya native dialogue.

I might be getting it totally wrong here, take it with a ton of salt

Indeed you can do this but the trade off of native widget functionality vs slightly aesthetic conformity isnt really worth it, I just wondered if there was a straightforward way of getting it to look naitve

I wonder if that may be a place to start, though.
You could use maya_main_window().findChild(...) to find a widget of your required type. Then grab the style from that widget using widget.style() and use it in your own ui using .setStyle()

I seem to recall trying to get the style from a built in maya widget and nothing comes back? As you said above isnt there a Qstyle on the whole ilibrary that we cant unpack?

We can’t unpack the QStyle into css, but we can access and use the style object that maya uses.

mw = maya_main_window()
style = mw.style()
print(style)
# prints <PySide2.QtWidgets.QProxyStyle(0x2557fa47ea0, name = "adskdarkflatui") at 0x000002559D04D980>

However, setting this style to the widget from your example doesn’t appear to change anything about how the UI draws. Oh well, it was an interesting attempt

1 Like

Thanks for your input here @tfox_TD - appreciate it

@arondurkin , I tried looking into this and I think if you turn document mode, it will bring you close to the look that you are looking for:
https://doc.qt.io/qt-6/qtabwidget.html#documentMode-prop

It will basically get rid of the frames around the tab but it still wouldn’t look exactly as the native tabs for Maya.

Another thing that I noticed is the line below the tab name is only visible when QT follows the style for Maya. The tabs look fine if it follows my OS style (Linux in my case but I think it’s the same for Windows). It’s worth looking at the source code of QTabWidget and see how it’s handling the painting of the tab frame for the OS and go from there:
https://codebrowser.dev/qt5/qtbase/src/widgets/widgets/qtabwidget.cpp.html#_ZN10QTabWidget10paintEventEP11QPaintEvent

The code itself is in C++ but it’s fairly simple to understand how you can use the same idea in Python :slight_smile: