Search code examples
c++qtresizeqsplitter

Qt Resizing a QMainWindow with a Splitter


I have a QMainWindow with:

  • Two widgets in a horizontal splitter. "m_liner" is on the right side
    • Both widgets have a minimum size of say, 300 pixels.
  • A checkbox to hide/show the right-side widget m_liner.

I want the overall QMainWindow to expand when showing the widget, and shrink when hiding. The code below does this except:

  • If both widgets are shown, the minimum window size is 600 pixels.
  • Shrink the window to this smallest size.
  • Uncheck the box to hide the right-side widget.
  • Program hides the right-side widget.
  • Program calls this->resize(300, height);
  • The window ends up being 600 pixels wide (the minimum size with both widgets visible), instead of around 300 (the minimum size with only the left widget).
  • Later, I can resize the window down to 300 pixels with the mouse or another button. But it won't resize to 300 in the checkbox event, even if I call resize several times.

Does anyone have an idea how to solve this?

Critical bit of code follows, I have a full project available if you need it:

void MainWindow::on_checkBox_stateChanged(int val)
{
std::cout << "-------------------- Checkbox clicked "  << val << std::endl;
bool visible = val;
QWidget * m_liner = ui->textEdit_2;
QSplitter * m_splitter = ui->splitter;

int linerWidth = m_liner->width();
if (linerWidth <= 0) linerWidth = m_lastLinerWidth;
if (linerWidth <= 0) linerWidth = m_liner->sizeHint().width();
// Account for the splitter handle
linerWidth += m_splitter->handleWidth() - 4;

std::cout << "Frame width starts at " << this->width() << std::endl;
std::cout << "Right Panel width is " << m_liner->width() << std::endl;

//  this->setUpdatesEnabled(false);
if (visible && !m_liner->isVisible())
{
  // Expand the window to include the Right Panel
  int w = this->width() + linerWidth;
  m_liner->setVisible(true);
  QList<int> sizes = m_splitter->sizes();
  if (sizes[1] == 0)
  {
    sizes[1] = linerWidth;
    m_splitter->setSizes(sizes);
  }
  this->resize(w, this->height());
}
else if (!visible && m_liner->isVisible())
{
  // Shrink the window to exclude the Right Panel
  int w = this->width() - linerWidth;
  std::cout << "Shrinking to " << w << std::endl;
  m_lastLinerWidth = m_liner->width();
  m_liner->setVisible(false);
  m_splitter->setStretchFactor(1, 0);
  this->resize(w, this->height());
  m_splitter->resize(w, this->height());
  this->update();
  this->resize(w, this->height());
}
else
{
  // Toggle the visibility of the liner
  m_liner->setVisible(visible);
}
this->setUpdatesEnabled(true);
std::cout << "Frame width of " << this->width() << std::endl;
}

Solution

  • Sounds to me like there are some internal Qt events that need to get propagated before it recognizes that you can resize the main window. If this is the case then I can think of two potential solutions:

    Use a queued single shot timer to call the code that resizes your window down to 300px:

    m_liner->hide();
    QTimer::singleShot( 0, this, SLOT(resizeTo300px()) );
    

    or, after you hide your widget you can try a call to processEvents() (this function has potentially dangerous side effects, so use with caution):

    m_liner->hide();
    QApplication::processEvents();
    resize( w, height() );
    

    Another potential solution would be to set the horizontal size policy of your widget to ignored when hiding it:

    m_liner->hide();
    m_liner->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Preferred );
    resize( w, height() );
    

    When showing your widget again, you'd need to adjust the size policy again.