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:
- QTableWidget
- QTableView
- QAbstractItemView
- QAbstractScrollArea
- QFrame
- QWidget
- 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:
- 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;
- call
ensurePolished()
so that any possible cached value (including indirect values and size hints) is reset;
- 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);
- 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.