Qt QWidget::minimumSizeHint delay (#2) - c++

When i hide a widget in my application, the minimumSizeHint isn't updated immediatly.
I tried the QLayout::activate() solution suggested at this post, but it doesn't work for me because QLayout::activate() returns false.
I try to do it at MainWindow class like this:
ui.groupBox->setVisible(!ui.groupBox->isVisible());
qDebug() << this->layout()->activate();
qDebug() << this->minimumSizeHint();
this->resize(this->minimumSizeHint());
Any ideas why it's not working?
My current workaround is:
QTimer::singleShot(10, this, SLOT(on_resizeMin()));
but i noticed 10ms may not be enough on a slow system. Nasty workaround.

The size hints are meant to be used at appropriate moments by the layout system. You're not supposed to be using them elsewhere - that's why they appear to "not work".
You appear to want to constrain the size of the main widget (the window) to one of the size hints. You'll need to set an appropriate sizeConstrainton that widget's layout. There are two approaches:
Use the QLayout::SetFixedSize constraint. Reimplement the layout's sizeHint to return minimumSize, such that the QLayout::SetFixedSize will set the window's size to the minimum size hint, not the default size hint.
Patch Qt to implement QLayout::SetMinAndMaxToMinSize, meaning "The main widget's minimum and maximum size are set to minimumSize()"
The patch is small :)

Related

How can I adjust the size of a QDialog according to its title length?

One of my dialog window's title is shortened (like "My Dialogt..."). If the dialog was slightly wider, the whole title would be completely displayed, which would look nicer.
It seems as if there is no setting in Qt to do that. I have found a hack for a QMessageBox here: Can QMessageBox::about adjust size to title length?, but it is not general. For example it would have to take also the sizes of the icons to the left and to the right of the window title into account to compute a really good minimal size where still the title is completely shown.
Is there a general way to accomplish that? Is there also a simple way to do that? Or is this overengineering?
Not only this goal is questionable (see vahanco comment) but it is hard to achieve, because the window title bar is not Qt territory at all: apart from being able to set its text and manage to show or hide close/min/max button using window flags, there is little else in control, there.
By the way, a very raw way to set a dialog minimum width which could (could) make room to the whole text is the following:
const QString text = "Very very very very very very very very very very very very very long window title";
setWindowTitle(text);
QFontMetrics metrics(font(), this);
setMinimumWidth( metrics.horizontalAdvance(text));
This won't work out of the box, and it's very likely that the text stay cut, because the font used is supposed to be the same used in the title bar (which usually isn't) and we're not taking into account the frame width, the icon width, the title bar buttons width, and everything else which is owned by the window manager and is totally unknown to Qt.
So, you can figure out how much extra space is needed by all these stuff, and adjust the width with a totally arbitrary extra padding like
setMinimumWidth( metrics.horizontalAdvance(text) + 256);
and maybe get what you wanted in the first place (if you still really want it).
The accepted answer did not work for me.
The below code works in QT 5.15. According to the documentation after you call setMinumumWidth() you must call updateGeometry() update geometry docs. Setting minimumWidth should update the sizeHint. That was not happening for me. Also QFontMetrics::horizontalAdvance was not returning the width of the text. I had to use QFontMetrics::boundingRect({title_string}).width().
Calling resize on the dialog is what finally got it working for me. If the accepted answer doesn't work for you give this a try.
QString message = "Message for the user";
QInputDialog dialog = QInputDialog(this);
dialog.setLabelText(message);
QString longTitle = QString("Super long test title for making sure the widget will show all of the stupid long title.");
dialog.setWindowTitle(longTitle);
dialog.setInputMode(QInputDialog::TextInput);
auto fontMetrics = dialog.fontMetrics();
auto width = fontMetrics.boundingRect(longTitle).width();
dialog.resize(width + 200, dialog.rect().height());
const int ret = dialog.exec();

Qt QGraphicsScene does not fitInView() with scrollbars

QGraphicsView::fitInView() seems to ignore the presence of scrollbars, that apparently are overlaid. (It also uses a hardcoded 2 pixel margin.)
There is a related bug report (https://bugreports.qt-project.org/browse/QTBUG-1047) stating that calling fitInView() twice would resolve the problem.
In my case, it does not. Only triggering it twice manually fits regarding the scrollbars. This does not work:
void myGraphicsView::mousePressEvent(QMouseEvent *event) {
if( event->button() == Qt::LeftButton ) {
QGraphicsItem* clicked = scene()->itemAt( mapToScene( event->pos() ) );
qDebug() << clicked << clicked->boundingRect();
fitInView( clicked, Qt::KeepAspectRatio);
fitInView( clicked, Qt::KeepAspectRatio); // doesn't work for me
QGraphicsView::mousePressEvent(event);
return;
}
}
Is there another workaround?
Qt 4.8.1 with MSVC2010
Calling fitInView() twice does work, but you have to let Qt process its events between the two calls.
This also means you end up redrawing the graphicsview twice.
To avoid this, what I do is:
Disable updates
Call fitInView
Call QApplication::processEvents()
Call fitInView again
Enable updates
In your code it would look like this:
bool updateState = updatesEnabled();
setUpdatesEnabled(false);
fitInView(clicked, Qt::KeepAspectRatio);
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
fitInView(clicked, Qt::KeepAspectRatio);
setUpdatesEnabled(updateState);
The cause could be this (at least it looks like it is in the bug report): https://bugreports.qt.io/browse/QTBUG-42331 - please vote on it on the qt bug tracker to up its priority.
In short, fitInView has hardcoded margins and this can cause all kinds of havoc - the least of which is that now you lose a few pixels of display area and might also force unnecessary rescaling. It can cause runaway resizing and weird instabilities such as what the QTBUG-1047 bug submitter described.
You can fix the problem by reimplementing fitInView, based on the existing implementation but removing it's ridiculous margins. An example of that is available here in python, based on the original C++ version:
https://github.com/nevion/pyqimageview/blob/master/qimageview/widget.py#L276
Let me know if it fixes your problem! That way we can close out that since-2007 bug too.

qt resize window after widget remove

I'm adding widget in layout
ui->horizontalLayout->addWidget(tabwidget);
and qmainwindow resizes itself. But then I do
tabwidget->setVisible(false);
qs = sizeHint();
resize(qs);
I get the size like tabwidget was not removed from window.
I've made new button
void MainWindow::on_pushButton_2_clicked()
{
qs = sizeHint();
resize(qs);
}
and it gives correct size.
Seems I need some update function but I can't find it. Please advice
This is caused by a long-time internal Qt issue (I remember experiencing it first with Qt3). The top widget needs to receive an event to truly update its geometry, and know its boundaries. This event seems to be always received after the resize event generated by the layout, and therefore it is always too late to shrink the widget.
The solution posted in the accepted answer works. However a simpler solution posted below also works, when added after the layout :
layout()->removeWidget( widget );
QApplication::processEvents( QEventLoop::ExcludeUserInputEvents );
resize( sizeHint() );
Basically all we need is to let the event loop run and deliver the necessary events so the top widget geometry is updated before resize() is run.
Note that this code might have side effects if you have multiple threads running, or there are events delivered to your slots. Hence it is safer not to have any code in this function after resize().
If the button slot gives you the correct result then you can always call the sizeHint() and subsequent resize() in a slot which is called by a single shot timer:
void MainWindow::fixSize()
{
QSize size = sizeHint();
resize(size);
}
void MainWindow::methodWhereIHideTheTabWidget()
{
tabwidget->setVisible(false);
QTimer::singleShot(0, this, SLOT(fixSize()));
}
This timer is set to zero delay. This means that the slot will be called immediatelly when the program returns to the main loop and hopefully after the internal widget state gets updated. If this doesn't resolve your problem you may try replacing zero with 1.

CListBox's Item size changed when changing the size of the list box even if I specify the size in MeasureItem() method?

I used a class which derives from CListBox, and create it with following:
style:WS_CHILD|WS_VISIBLE |LBS_OWNERDRAWFIXED | WS_VSCROLL | WS_HSCROLL
I expect the ListBox's item to be have a fixed size, not affected by the size of the list box. So I override the MeasureItem() method, in which I specify the item's size like below:
void CMyListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMIS)
{
lpMIS->itemHeight = ALBUM_ITEM_HEIGHT;
lpMIS->itemWidth = ALBUM_ITEM_WIDTH;
}
But the item's size changes according to the List box's size changing. is there anything wrong with my approach?
What's not mentioned in the reference is that WM_MEASUREITEM is called every time the *_OWNERDRAWFIXED control is resized.
I don't know however, how official this behavior is and whether it should be relied on, but it has been verified at CodeGuru and several forum posts found on the Google thing.
If you don't want to process the message, then just set a private flag somewhere in the first OnMeasureItem() call and return from it as soon as you check that it's set next time.
If you look at the MSDN entry for CListBox::MeasureItem you'll see that it's only called once unless the LBS_OWNERDRAWVARIABLE (not LBS_OWNERDRAWFIXED) style is set. If I understand correctly then this would explain the behaviour you're seeing because MeasureItem would need to be called each time the control's size changes.
Also, have you considered the points made in MFC Technical Note 14 : Custom Controls?

QDockWidget initial width

How do I set the initial width of a QDockWidget?
I have implemented the sizeHint function but what next?
The documentation for QDockWidget says:
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.
So the size hint is taken from whatever you put in the dock widget. Have you tried setting the size of the QDockWidget's child?
But, I agree with Marius, the best thing to do is probably to use QSettings to save and restore the widths of all the dock windows when the application starts. Have a look at QMainWindow::saveState Apart from getting the data from saveState rather than from individual functions my save function looks very similar to the one given by Marius.
If you want it to have the same width as the same last time the program was running, you should look into settings. The Qt 4.4 documentation has an example on how to use settings here.
This is how I have done it:
void Applicotion::readSettings() {
QSettings settings("Company Name", "Application Name");
settings.beginGroup("LibraryDock");
libraryDock->setFloating(settings.value("docked").toBool());
libraryDock->resize(settings.value("size", QSize(1, 1)).toSize());
libraryDock->move(settings.value("pos", QPoint(200, 200)).toPoint());
addDockWidget((Qt::DockWidgetArea)settings.value("dockarea", Qt::RightDockWidgetArea).toInt(), libraryDock);
settings.endGroup();
}
void Applicotion::writeSettings() {
QSettings settings("Company Name", "Application Name");
settings.beginGroup("LibraryDock");
settings.setValue("dockarea", dockWidgetArea(libraryDock));
settings.setValue("docked", libraryDock->isFloating());
settings.setValue("size", libraryDock->size());
settings.setValue("pos", libraryDock->pos());
settings.endGroup();
}