[PyQt] QTabWidget's tab bar behavior?

Maurizio Berti maurizio.berti at gmail.com
Sat May 25 21:53:01 BST 2019


Il giorno sab 25 mag 2019 alle ore 20:18 Matic Kukovec <
kukovecmatic at hotmail.com> ha scritto:

> I made a QTabWidget with a custom tabBar in which I manually add a
> QGroupBox with QLabel
> that acts as a close button with *self.setTabButton(index,
> QTabBar.RightSide, groupbox)*.
>

First of all, why do you use a QGroupBox? As far as I understand, you are
using it as a close button, but it doesn't seem to add any major advantage
or implementation. If it's for look purposes, just go with stylesheets or
custom painting. If it's to extend the TabBar capabilities, extend them in
the TabBar; assigning functionality to a button that doesn't seem to belong
to it doesn't make much sense.


I don't exactly know what caused this? I wish to get the standard behaviour
> back, can anyone help?
>

I feel like you've being overshooting, as your code seems too complicated
than it should.
For example: why do you need to constantly change the close "buttons"
everytime the tab order is changed? As far as I can tell, the only thing
that changes is their index, and you seem to want to hide/show them
accordingly; why not just hide the button and/or change its index property?

I believe that the issue you're facing comes from the fact that you're
constantly setting a new widget as the tabButton, which might interfere
with widget size and positioning that occur while moving tabs (hence the
odd graphical and mouse interaction behavior). As soon as I disable
the _tab_moved_slot and _current_tab_changed connection slots, the QTabBar
behaves as expected.

You might want to think about a different and easier approach, and possibly
rethink the whole concept you use while keeping it as simple as possible.


If I may, I also have some (unrequested) suggestions.

First of all: why the unnecessary int() for every QSize declaration? You
don't even need to use QSize for set[*]Size, as it also accepts numeric
values. Moreover, you can even use floats, as they're automatically
transformed to integers.

Then, If you need button-style behavior, just use a button :-)
You can use stylesheets to set icons, I also just discovered (thanks to
you!) that you can set widgets graphical properties within the stylesheets
like this:

        button.setStyleSheet('''
            QPushButton {
                border: none;
                qproperty-icon: url(up-icon.svg) off,
                url(down-icon.svg) on;
            }
        ''')

This allows you to set icons for both down and up states. Unfortunately,
there are some bugs with this. First of all, a still unsolved bug prevents
to set (some) properties for pseudo states, which means that you can't set
an icon for the :hover state. Also, it seems that setting (some) other
properties in the stylesheet (such as the border) prevents the successful
update of the state icon. Nonetheless, since you only need a button with an
icon and without text, you can use the simpler image syntax:

        button.setStyleSheet('''
            QPushButton {
                border: 1px solid red;
                image: url(up-icon.svg);
            }
            QPushButton:hover {
                border: 1px solid green;
                image: url(down-icon.svg);
            }
        ''')

This works as long as there's no button text: whenever you set it, you'll
see that the text is overimposed on the icon, and there's no known method
of setting the background alignment according to the button's text (or vice
versa), since no margin or padding setting will correctly affect the result.
If you need to do more complex interaction, set an alternate stylesheet for
enterEvent and set it back on leaveEvent, but at this point you could
obviously use setIcon() as well.

Somehow trivial, but it's a good practice: as PEP8 suggests, it's always
better to use "if cond is [not]" comparison whenever a singleton comes in
place; as far as I know of, None is the most important one to keep in mind,
as it's an actual singleton in both Python 2 and 3 (you can't create an
instance of None), while technically you could create a new "True" variable
in Python 2. While "cond == None" works, it's not a preferred pattern, not
from a programming perspective, but mostly from a readability one (which
also means better and easier debugging). That said, the problem also comes
whenever you're using comparisons in a "light" fashion: obviously, a simple
"if not cond" doesn't work as expected if you're expecting None and you get
False, 0, or an empty string.

Also, remember that if you use external resources (like the button pixmaps
in your case) you should attach them. While it wasn't much of an issue in
your case, it might be a problem in other situations: QPixmaps that are
init-ed with a non existing file are null pixmaps, so their size is
invalid, which might become a problem if, for example, the issue at hand is
about painting or sizing.

Finally, when attaching an example, it's always better to make it as
minimal as possible. When people see hundreds of lines of code and find
that it's overly complicated, they might be discouraged in trying to help
you, since they'd lose much of their time trying to understand what you're
really trying to achieve while being lost in dozens of unnecessary
declarations, functions and methods. I know it can be annoying, but it
really does help: first of all, in my experience I've learnt that
recreating the example usually results in being able to find the solution
on your own (see https://en.wikipedia.org/wiki/Rubber_duck_debugging ),
but, anyway, it's always better to lose 15-20 minutes of your time
"minimizing" your example (with the possibility of even finding the
solution on your own) than losing the opportunity of having somebody who
could actually help you but didn't want (or wasn't able) to find the time
to "filter out" your code.

Good luck,
Maurizio


-- 
È difficile avere una convinzione precisa quando si parla delle ragioni del
cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20190525/a28489f1/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 2019-05-25_20-05-15.gif
Type: image/gif
Size: 54169 bytes
Desc: not available
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20190525/a28489f1/attachment-0002.gif>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 2019-05-25_20-02-47.gif
Type: image/gif
Size: 85414 bytes
Desc: not available
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20190525/a28489f1/attachment-0003.gif>


More information about the PyQt mailing list