3

When calculating the height that is needed for a QTableWidget so that all rows are visible and no scroll bar is added, it's not enough to take the number of rows, multiply them by the row height and add the header's height. One has to add a few more pixels, which seem to depend on the style used.

I asked for a solution to this very problem some years ago, and the hints given in the respective post worked for Qt 5: The additional pixels could be calculated by taking the widget's height and subtracting the viewport height and the header height.

Using Qt 6 and Plasma 6, this doesn't work anymore for all styles.

It still works for Fusion, where e.g. the widget height is 480 pixels, the viewport height is 456 and the header 22, resulting in the 2 pixels I need:

Example using the Fusion style

But using Breeze, the heights are different: 480 pixels for the widget, 450 for the viewport and 30 for the header, resulting in 0 (and we would need 4 pixels). Thus, I get scrollbars again when using Breeze:

Example using the Breeze style

So the question is the same: How can I reliably calculate the additional pixels I need to add?

10
  • Using the size of the widget as soon it's constructed is pointless (children always are 100x30, parentless 640x480, unless there are size constraints created with setMinimum/Maximum/Fixed size functions). Use the horizontalHeader()->height() plus the verticalHeader()->length() (which is what viewportSizeHint() returns), and add twice the frameWidth(). Note that if you use sizeAdjustPolicy this is done automatically, but for both dimensions. Commented Jul 9 at 8:20
  • The frameWidth() actually does the trick. After the QTableWidget being added to a layout and show() is called (it does not work without), one can calculate the needed height using the widget->horizontalHeader()->height() plus the height of all rows (e.g. widget->rowCount() * widget->rowHeight(0) if all rows have the same height) plus 2 * widget->frameWidth(). This works for all combinations of Qt 5, Qt 6, Plasma 5, Plasma 5, Fusion and Breeze. Would you post an answer so that I can accept it? Commented Jul 9 at 11:13
  • @TobiasLeupold yes, please post your answer and accept it. It will be easier for future readers to find it.
    – Pamputt
    Commented Jul 9 at 13:02
  • I'd like @musicamante to earn the compliments for that ;-) Commented Jul 9 at 13:35
  • 1
    @TobiasLeupold I will only be able to provide a Python based example, I hope it will be enough. In any case, it shouldn't be necessary to call show() in order to get the proper sizes, unless you set a stylesheet on some parent and it affects the view. If that's the case, you need to ensure that: 1. the widget is properly parented; 2. it's polished (ensurePolished()). Commented Jul 9 at 18:26

1 Answer 1

1

tl;dr

For QTableView based classes, you should consider the height of their vertical header (even if it's not visible), the possible height of the horizontal header (if visible) and the view's margins.

Ensuring that the widget is also polished by the style is also mandatory.

Explanation

First of all, we can almost never rely on the initial size of a widget.

By default, all standard Qt widgets will return the following size() as soon as they're being created:

  • 640x480 for any widget created without a parent QWidget in its constructor; the assumption is that it may be a top level widget (a window);
  • 100x30 for any widget created with a parent QWidget in its constructor;

The above is hard coded in the common QWidget source code and the only exception is for widgets that explicitly set a size restraint in their own constructors (AFAIR, no standard Qt widget does that): for instance, a call to setMinimumHeight(100) may override the height of a widget created without a parent, or setMaximumWidth(500) could do the same for a widget created with a parent.
Still, the result of the widget's size() will consider the above only after they've been applied.

That said, it's important to consider the inheritance path of a widget; in the case at point:

  1. QTableWidget
  2. QTableView
  3. QAbstractItemView
  4. QAbstractScrollArea
  5. QFrame
  6. QWidget
  7. QObject

This means that each inheritance level will possibly override or inherit (to some level) the behavior of the class on which it's based.

In this specific case, the QFrame frameWidth property must be considered, which is the width of the border of the frame used for the view.

Note, though, that that value may be unreliable as the style (specifically, using QSS with variable border widths) may use inconsistent borders.

The above point is also indirectly important: the widget has to be polished in order to retrieve a proper frame width; the QStyle of the widget must be applied to the widget, which is always important: the widget may inherit the style, or behave differently if it's a top level widget (a window) or not. The style itself may choose to "polish" aspects differently, based on the parents. Some internal values may be set upon creation and only reset upon reparenting/restyling, and size hints may be cached too.

So, in order to get a (possible) proper height, you need to:

  1. ensure that the widget is already part of the definitive widget structure (parenthood has to be properly set at least to the top level window) so that the style is accurately set/adjusted;
  2. call ensurePolished() so that any possible cached value (including indirect values and size hints) is reset;
  3. get the proper height of the view items, using the vertical header's length() (which is normally more accurate than just multiplying the rowHeight() of the first row by the row count, and faster than iterating through all rows, since the header has probably already done that);
  4. add the frameWidth(), but doubled (since you have to consider both top and bottom margins), while hoping that the style/QSS use the same values for all sides;

Note that a more appropriate approach should do the above in the sizeHint() and minimumSizeHint() overrides of the view, also consistently with the possible model layout changes and ensuring that a proper sizeAdjustPolicy property value is set.

Not the answer you're looking for? Browse other questions tagged or ask your own question.