Joseph Quinsey Joseph Quinsey - 4 months ago 84
C++ Question

How to determine the correct size of a QTableWidget?

Is there any way to set the "correct" size of a QTableWidget? (I'm a newbie) This test code is only 25 lines long, in two files, with the file Test.h:

#include <QtGui>
class Test : public QMainWindow {
Q_OBJECT
public:
Test();
};


and the file Test.cpp:

#include "Test.h"
Test::Test() : QMainWindow() {
QVBoxLayout *vbox = new QVBoxLayout;
QPushButton *btn = new QPushButton("Hello World etc etc etc etc etc");
QTableWidget *tbl = new QTableWidget(2, 2);
vbox->addWidget(btn);
vbox->addWidget(tbl);
QWidget *w = new QWidget;
setCentralWidget(w);
w->setLayout(vbox);
resize(1, 1);
}

int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Test test;
test.show();
app.exec();
}


Then the command:

qmake -project && qmake && make && ./Test


gives the window:

Unwanted window

But what we want of course is something more like:

Wanted window

Using
tbl->width()
seems to be useless, as it gives a default of
640
before
test.show()
, and the unwanted value of
195
after. I've looked at the Qt Size Hints and Policies until my head spun, and I've tried
setResizeMode(QHeaderView::Fixed)
and
setStretchLastSection(false)
. Maybe I'm missing something obvious? This is with Qt 4.7.4 on CentOS 5, if this matters. Thank you for any help.

Edit: In response to DK, if the line
resize(1, 1);
is not present, there is the equal and opposite problem: the window is too large.

And in response to Donotalo, adding:

tbl->setMaximumWidth(222);
tbl->setMinimumWidth(222);
tbl->setMaximumHeight(88);
tbl->setMinimumHeight(88);


will give the desired window size (at least on my machine), but not in the desired way. How should we calculate the 'constants'
222
and
88?


And Ton's answer to Qt: How to force a hidden widget to calculate its layout? doesn't seem to work here: the addition of
tbl->setAttribute(Qt::WA_DontShowOnScreen); tbl->show();
left the value of
tbl->width()
unchanged at 640.

Answer

The thread How to set a precise size of QTableWidget to prevent from having scroll bars? (Qt-interest Archive, June 2007) between Lingfa Yang and Susan Macchia seems to resolve my question. I will post more details shortly, if my testing works.

Update #1: My test now generates the nice-looking window:

successful test window

The complete test code for this, with Test.h unchanged, is:

#include "Test.h"

static QSize myGetQTableWidgetSize(QTableWidget *t) {
   int w = t->verticalHeader()->width() + 4; // +4 seems to be needed
   for (int i = 0; i < t->columnCount(); i++)
      w += t->columnWidth(i); // seems to include gridline (on my machine)
   int h = t->horizontalHeader()->height() + 4;
   for (int i = 0; i < t->rowCount(); i++)
      h += t->rowHeight(i);
   return QSize(w, h);
}

static void myRedoGeometry(QWidget *w) {
   const bool vis = w->isVisible();
   const QPoint pos = w->pos();
   w->hide();
   w->show();
   w->setVisible(vis);
   if (vis && !pos.isNull())
      w->move(pos);
}

Test::Test() : QMainWindow() {
   QVBoxLayout *vbox = new QVBoxLayout;
   QPushButton *btn  = new QPushButton("Hello World etc etc etc etc etc");
   QTableWidget *tbl = new QTableWidget(2, 2);
   vbox->addWidget(btn);
   vbox->addWidget(tbl);
   setCentralWidget(new QWidget);
   centralWidget()->setLayout(vbox);
   layout()->setSizeConstraint(QLayout::SetMinimumSize); // or SetFixedSize

   tbl->setVerticalHeaderItem(1, new QTableWidgetItem("two")); // change size
   myRedoGeometry(this);
   tbl->setMaximumSize(myGetQTableWidgetSize(tbl));
   tbl->setMinimumSize(tbl->maximumSize()); // optional
}

int main(int argc, char *argv[]) {
   QApplication app(argc, argv);
   Test test;
   test.show();
   app.exec();
}

Some notes:

  • The above thread's inclusion of verticalScrollBar()->width() seems to be wrong. And, in my testing, this was always either a default value of 100, or the value 15, if the scrollbar had been displayed.

  • Applying the show(); hide(); sequence just to the QTableWidget was not sufficient to force Qt to recalculate the geometry, in this test. I needed to apply it to the whole window.

  • Any suggestions for improvements would be welome. (I'll wait a bit before accepting my own answer, in case there are better solutions.)

Update #2:

  • The Qt-interest thread may be wrong (or, at least, it disagrees with my version of Qt running my machine) regarding details on how to calculate the size: the +1 for each gridline is unnecessary, but an overall +4 is needed.

  • I'm still working through layout()->invalidate() vs. e.g. QT: How to preview sizes of widgets in layout BEFORE a show().

Update #3:

  • This is my final version--but I'm not very happy with it. Any improvements would be very welcome.

  • Things like adjustSize() and layout()->invalidate() and Qt::WA_DontShowOnScreen don't seem to help.

  • The blog Shrinking Qt widgets to minimum needed size is interesting, but using setSizeConstraint() is just as good.

  • The setSizeConstraint() and move() methods are needed for subsequent changes to the table, not shown here.

  • There was an odd thing in my testing, done on CentOS 5 with Qt 4.6.3 and 4.7.4. For the hide(); show(); sequence, the window position is saved/restored. But about 25% of the time (the patterning was irregular) the restored position would be 24 pixels higher on the screen, presumably the height of the window title. And about 10% of the time the value returned by pos() would be null. The Qt site says for X11 it needs nifty heuristics and clever code for this, but something seems to be broken somewhere.