I'm using a QMainWindow with dock widgets. My central widget is a QScrollArea containing a custom widget of fixed size, call it "canvas". I'd like to be able to set the initial size of canvas at application startup time such that the scroll area will not pop up scroll bars given the initial docked dock widgets i.e. such that canvas is exactly the size of the central area of the main window, given the initial docked widgets and the initial main window size.
Currently I set the canvas size in the first window resize event that happens after the first window show event. (Because in the very first resize event the window has incorrect geometry when it is initially shown maximized: see this thread). In this second resize event I try to calculate the correct size for the canvas by trying to calculate the "client area" of the main window and then subtracting the width of dock widgets docked on the left or right sides and the height of docked widgets docked on the top or bottom. Relavent code below:
my_main_window::my_main_window(QWidget* parent) :
QMainWindow(parent), was_shown_(false) {
// ... create menubar etc.
setDockNestingEnabled(true);
addDockWidget(Qt::LeftDockWidgetArea, new some_docking_widget1(this));
addDockWidget(Qt::TopDockWidgetArea, new some_docking_widget2(this));
//etc.
QScrollArea* scroller = new QScrollArea();
scroller->setWidget(canvas_ = new canvas());
scroller->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
setCentralWidget(scroller);
}
void my_main_window::showEvent(QShowEvent* event) {
was_shown_ = true;
}
void my_main_window::resizeEvent(QResizeEvent* event) {
if (was_shown_ && canvas_->size() == QSize(0, 0)) {
QSize size = this->size();
QLayout* layout = this->layout();
QMargins margins = layout->contentsMargins();
int width = size.width() - margins.left() - margins.right();
int height = size.height() - margins.top() - margins.bottom();
QSize client_size = QSize(width, height);
for (QDockWidget* dw : findChildren<QDockWidget*>()) {
if (dw->isVisible() && !dw->isFloating()) {
auto area = dockWidgetArea(dw);
if (area == Qt::LeftDockWidgetArea || area == Qt::RightDockWidgetArea) {
client_size.setWidth( client_size.width() - dw->size().width());
} else {
client_size.setHeight(client_size.height() - dw->size().height());
}
}
}
canvas_->setFixedSize(client_size);
}
}
This almost works. The horizontal dimension is correct but the vertical dimension is off by a small amount -- possibly because of the height of the menu bar? I am wondering if there is some much easier or less verbose way to do this?
I've found a much simpler way to get the same result as the method in the question (which did work once I accounted for the height of the menu bar)
You can control whether the widget contained in a QScrollArea is resized when the QScrollArea is resized with its member function setWidgetResizable
. This means you can let the widget in the scroll area to be resized with the scroll area at creation time but set it to not be resizable once the application's initial state and geometry has been completely determined.
So the new code would look like this
my_main_window::my_main_window(QWidget* parent) :
QMainWindow(parent), was_shown_(false) {
// ... create menubar etc.
setDockNestingEnabled(true);
addDockWidget(Qt::LeftDockWidgetArea, new some_docking_widget1(this));
addDockWidget(Qt::TopDockWidgetArea, new some_docking_widget2(this));
//etc.
scroller_ = new QScrollArea();
scroller_->setWidget(canvas_ = new canvas());
// let canvas, which we have *not* given a size to, be resized
// with scroller_
scroller_->setWidgetResizable(true);
scroller_->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
setCentralWidget(scroller_);
}
void my_main_window::showEvent(QShowEvent* event) {
QMainWindow::showEvent(event);
was_shown_ = true;
}
void my_main_window::resizeEvent(QResizeEvent* event) {
QMainWindow::resizeEvent(event);
if (was_shown_ && scroller_->widgetResizable()) {
// let the size of canvas now be independent
// of the size of scroller_
scroller_->setWidgetResizable(false);
}
}