26
votes

I've just started working on a new C++/Qt project. It's going to be an MDI-based IDE with docked widgets for things like the file tree, object browser, compiler output, etc. One thing is bugging me so far though: I can't figure out how to programmatically make a QDockWidget smaller. For example, this snippet creates my bottom dock window, "Build Information":

m_compilerOutput = new QTextEdit;
m_compilerOutput->setReadOnly(true);
dock = new QDockWidget(tr("Build Information"), this);
dock->setWidget(m_compilerOutput);
addDockWidget(Qt::BottomDockWidgetArea, dock);

When launched, my program looks like this (bear in mind the early stage of development):

Actual

However, I want it to appear like this:

Expected

I can't seem to get this to happen. The Qt Reference on QDockWidget says this:

Custom size hints, minimum and maximum sizes and size policies should be implemented in the child widget. QDockWidget will respect them, adjusting its own constraints to include the frame and title. Size constraints should not be set on the QDockWidget itself, because they change depending on whether it is docked

Now, this suggests that one method of going about doing this would be to sub-class QTextEdit and override the sizeHint() method. However, I would prefer not to do this just for that purpose, nor have I tried it to find that to be a working solution.

I have tried calling dock->resize(m_compilerOutput->width(), m_compilerOutput->minimumHeight()), calling m_compilerOutput->setSizePolicy() with each of its options... Nothing so far has affected the size. Like I said, I would prefer a simple solution in a few lines of code to having to create a sub-class just to change sizeHint(). All suggestions are appreciated.

9

9 Answers

9
votes

I made it easy: HEADER:

private void setDockSize(QDockWidget *dock, int, int);
  public slots:
  void returnToOldMaxMinSizes();

SOURCE:

QSize oldMaxSize, oldMinSize;

void MainWindow::setDockSize(QDockWidget* dock, int setWidth,int setHeight)
{

    oldMaxSize=dock->maximumSize();
    oldMinSize=dock->minimumSize();

  if (setWidth>=0)
    if (dock->width()<setWidth)
        dock->setMinimumWidth(setWidth);
    else dock->setMaximumWidth(setWidth);
  if (setHeight>=0)
    if (dock->height()<setHeight)
        dock->setMinimumHeight(setHeight);
    else dock->setMaximumHeight(setHeight);

    QTimer::singleShot(1, this, SLOT(returnToOldMaxMinSizes()));
}

void MainWindow::returnToOldMaxMinSizes()
{
    ui->dockWidget->setMinimumSize(oldMinSize);
    ui->dockWidget->setMaximumSize(oldMaxSize);
}
6
votes

It sounds like the dock widget re-sizes itself to the proper size, considering its child widget. From the QDockWidget documentation (emphasis mine):

A QDockWidget acts as a wrapper for its child widget, set with setWidget(). Custom size hints, minimum and maximum sizes and size policies should be implemented in the child widget. QDockWidget will respect them, adjusting its own constraints to include the frame and title. Size constraints should not be set on the QDockWidget itself, because they change depending on wether it is docked; a docked QDockWidget has no frame and a smaller title bar.

In order to change the size, then, you must re-size the child widget.

EDIT: The Qt documentation can sometimes be misleading when it discusses size hints. Often, it's referring to any kind of resizing, whether performed automatically by the widget or programatically.

6
votes

This is an old question, but I wanted to chime in to mention that Qt 5.6 introduced the QMainWindow::resizeDocks function to handle this.

Unfortunately, it doesn't work for my use case (moving separator between two QDockWidgets that have been split with QMainWindows::splitDockWidget)

5
votes

I just went through this same process. After trying far too many permutations of resize(), adjustSize() and friends on dock widgets and their contained widget, none of which worked, I ended up subclassing QListView and adding that sizeHint() method.

Now it works like a charm.

2
votes

Have you tried calling resize() on the QTextEdit inside your dock widget? You could also try temporarily setting the dock widget's maximum & minimum sizes to the size you want it to be, then restore the original values.

2
votes

You could do this:

Set a maximum height for your QTextEdit:

m_compilerOutput = new QTextEdit;
m_compilerOutput->setMaximumHeight(100);

And then in the show event of your main window set it back to the old size or something high:

void MainWindow::showEvent(QShowEvent *)
{
   m_compilerOutput->setMaximumHeight(10000);
}

That is all you should need.

1
votes

Tests using resize on the QDockWidget::widget() (i.e. the widget that the QDockWidget is managing) do not consistently work as expected.

With a QDockWidget subclass (DW) in which a QWidget with a QHBoxLayout that has two widgets (left-panel and right-panel) added, all of which have had their size policies set to QSizePolicy::Minimum, the DW normally has both panel widgets visible. When the DW is positioned in a side dock an application (QMainWindow) slot handling the DW's dockLocationChanged signal hides left-panel and re-sizes the DW->widget() to the size right-panel. When the DW is programmatically moved to the bottom dock area leftPanel is set visible and the DW fills the full width of the main window (of course). When the DW is then programmatically moved to a side dock area the left-panel is hidden and the DW is re-sized down. This works as intended. However, if the DW is dragged from the bottom dock area to a side dock area, though the left-panel is hidden and the re-size applied as before, the DW is not re-sized down as when the repositioning is done programatically. The DW can be manually re-sized down by dragging the splitter handele between the DW and main window central area. Note that the main window central area is a QWidget having a QHBoxLayout with size polices QSizePolicy::Expanding.

Calling adjustSize on the main window after the DW has been re-sized has no effect. This despite the DW having reimplemented sizeHint to return its minimum size depending on whether left-panel is visible or not.

Either I am missing something in how to control the size of QDockWidget (which, given the difficulty I've had understanding all the interactions amongst the parts of the layout management system, is quite likely), or the QMainWindow is ignoring or overriding the layout instructions that it is being given. Closely examining the event stream during the QDockWidget repositioning operations suggests the latter: After the slot handling the dockLocationChanged signal has done its resizing work and returned to the event loop I can see that the QMainWindow, when user repositioning is done, applies additional re-size operations on the affected QDockWidget thus undoing the application logic that attempts to control the dock size. Something seems amiss in the QMainWindow ....

1
votes

If the dockWidgets are docked, the sizes are control by their parent. In such cases you can use the QMainWindow::resizeDocks function.

If floating, sizes are determined by their children.Resize children to achieve your purpose.

0
votes

The resize of dock widgets problem when MainWindow is maximized is described in QTBUG-16252 (https://bugreports.qt.io/browse/QTBUG-16252)

I've found another workaround on it. Works for me on QT 5.4.1 minGW on Windows7. It looks like some widget state restore operations are closely related to QApplication event loop.

DockWidget sizes are restored correctly ONLY when following conditions are met:

  1. restoreGeometry() is called BEFORE entering QApplication::exec() (e.g. in constructor of your MainWindow class)

  2. restoreState() is called AFTER exec() (e.g. via QTimer)

Here is my code:

int main(int argc, char *argv[])
{
    QApplication application(argc, argv);

    //...

    MainWindow mainWindow;
    mainWindow.show();

    return application.exec();
}

MainWindow::MainWindow(...) 
{
    ui->setupUi(this);

    //...
    QTimer* nt = new QTimer(this);

    nt->setSingleShot(true);
    nt->setInterval( 100 );

    connect(nt, SIGNAL(timeout()), SLOT(restoreWidgetSettings()));
    nt->connect(nt, SIGNAL(timeout()), SLOT(deleteLater()));

    nt->start();

    restoreWidgetSettings(true);
}

void MainWindow::restoreWidgetSettings(bool geometryOnly) {

    //...
    QByteArray geometry = getSettings();

    restoreGeometry(geometry);

    if(geometryOnly)
        return;

    //... //create dock widgets here

    restoreState(mainWindowState);

}