Reducing flicker with QBoxLayout - qt

Whenever a displayed QBoxLayout is populated, there's some flicker on the screen as widgets get added to the layout. How do I stop this flicker?
setUpdatesEnabled did not do the trick.

show() the widget only after you've finished populating it/laying it out.
Or don't attach your layout to it's widget before you're done adding things to it. (i.e. only call setLayout(your_layout) when you've finished adding things to your_layout).
Alternatively, look into the updatesEnabled QWidget property. You can use that to temporarily disable the widget's updates to prevent flicker. (This is most useful on the more complex widgets like QTableWidget and similar when you are making "massive" changes to the underlying data.)
Quote from the doc above:
setUpdatesEnabled() is normally used to disable updates for a short period of time, for instance to avoid screen flicker during large changes. In Qt, widgets normally do not generate screen flicker, but on X11 the server might erase regions on the screen when widgets get hidden before they can be replaced by other widgets. Disabling updates solves this.

Related

Using QT 4.7 - dialog is busy loading a BIG thing; how to disable all controls?

[EDIT] - wasn't getting to the disable code the way I thought I was; works fine with any of the solutions below.
I have a modeless QT dialog, in which all kinds of user settings can be manipulated. In addition, it can save and load large data sets, which can take several seconds. Lots going on in the main window underneath (realtime app) and that's fine, and interaction there is okay, but in the dialog itself, I need to block user interaction.
Essentially, during the load, I don't want the user to be able to change / affect any of the controls in the modeless dialog (and there are a lot of them.)
Rather than disable each one individually (or even that way if that's the only way), is there a straightforward means I can use to disable input to the dialog entirely until the load is complete?
There's a progress bar in it that shows what's going on, too, which is constantly updated by the load process, so that needs to keep on working.
I tried this...
On dialog open:
QDialog *window = this;
Then around load:
window->setDisabled(true);
....
window->setDisabled(false);
...which compiles fine, and runs without complaint, but the window did not disable.
I also tried:
window->setEnabled(false);
....
window->setEnabled(true);
...that doesn't seem to do anything either.
I tried this too:
QList<QWidget*> list = window->findChildren<QWidget *>();
foreach(QWidget *qw, list)
{
qw->setEnabled(false);
}
....
QList<QWidget*> list = window->findChildren<QWidget *>();
foreach(QWidget *qw, list)
{
qw->setEnabled(true);
}
..also does nothing.
Based on Qt documentation (http://doc.qt.io/archives/qt-4.8/qwidget.html#enabled-prop):
Disabling a widget implicitly disables all its children. Enabling
respectively enables all child widgets unless they have been
explicitly disabled.
So your snippets are essentially doing same thing in different ways so that doesn't seem to be a problem and modality of dialog shouldn't have any effect on this as well. How are you loading / saving the data? If you are doing it in e.g. click slot then you are basically blocking the UI event loop which means that UI doesn't have cycles to react to your changes. If you want to isolate UI from background action so it will properly react even that you are doing heavy lifting in the background and you should consider using QRunnable or QTread to offload the work away from UI thread.

Is there a way to prevent updates (painting) to a `WebView` and then re-enable them?

Is there a way to prevent updates (painting) to a WebView and then re-enable them?
e.g.
disable painting
set new html content
re-enable painting
Why I'd like to do this
because my html is jumpy the elements will all be in the same positions on the HTML page after as before but during the update of the HTML they will move around a bit. Since I'm refreshing the HTML on a periodic basis, it is quite annoying to have it jump around often.
There is no real painting on/off switch either in WebView or in general for JavaFX (as far as I know), but perhaps the workaround below might help.
You could always:
snapshot the WebView, to create an image.
Replace the WebView with the snapshot image.
Set new html content.
Replace the snapshot image with the WebView.
To do this, you could put the snapshot image and the WebView in a StackPane, with the WebView on top and trigger setVisible(false) on the WebView when you want to hide it and set new html content and trigger setVisible(true) once you want the new content to be seen.
You might want to use some synchronization to know when the newly set html content has finished rendering either by a message from the JavaScript to Java or by monitoring the html events in Java (doing such is beyond the scope of this post). There might also be some issues with the WebView detecting it is not actually visible and then deciding to be lazy because of an optimization and not repaint itself for updated html content until it is made visible again (not sure I haven't tried it).
Anyway, perhaps give this technique a try and see if it works for you.

How can I disable a Qt widget without changing its appearence

I have a Qt widget which has a layout and there are more widgets inside it. When I disable the widget the whole widget becomes little faded and no modifications are possible anymore. I want the features that comes with disabling a widget but I do not want it's appearance to change. Please let me know how this can be done.
Few ideas that comes to my mind:
Rather disabling widget, capture all the events on the widget and do nothing
Update style sheet for disabled state (not sure if possible)
1. Capture events
Use QObject::installEventFilter() and QObject::eventfilter().
Keep in mind the way Qt dispatch GUI events, in particular children get events first. So you need to install the event filter recursively on all widgets and watch for QEvent::ChildAdded.
2. Using stylesheets
This is a not a good solution. Stylesheets tend to break QStyle mechanisms which may lead to side effects.
3. Use a QPixmap
Hide all the child widgets, render them to a QPixmap and draw the pixmap in the paintEvent.

How to update a QLayout and get the new dimensions before returning?

This is driving me nuts. I have a custom menu class that, when set visible, shows a list of items located in a particular folder. When a hardware button is pressed, my application gets the latest list of items, populates the menu with them, and returns.
The menu displaying these items uses a QListWidget filled with custom widgets. Each of the widgets contains one or more QLabels in a horizontal layout, and is created at the time the menu is shown. In order to adjust the text displayed based on the menu width available, I need to get the size of the QLabel AFTER it has been resized according to the layout, but before the menu becomes visible to the user. The problem is, my layout does not get updated until all of the functions constructing my list return.
I have tried QApplication::ProcessEvents() and the layout update functions, but none of them have updated the values of my QLabels before returning. I can set a QTimer when the button is initially pressed, and have it show the menu, update the items, and stop itself, but that seems like a terrible solution.
Any help would really be appreciated! I've spent most of a day on this.
Marlon
I had this exact problem and could not find an answer anywhere on the Internet. Calling Layout.update(), Layout.activate(), or widget.adjustSize() (all suggested in various places) all did not work.
I had a widget with a vertical layout that I wanted to add a QLabel to and then immediately use the size of the QLabel.
The only thing that worked reliably was
layout->addWidget(myLabel);
myLabel->show();
size = myLabel->size();
It would seem that layouts will just not recalculate until you either return from a function and allow the Qt event loop to progress or manually call show() yourself.
How to update a QLayout and get the new dimensions before returning?
Don't. You're not meant to do that. It'll drive you "nuts" because you're doing it backwards. Layout updates are handled asynchronously from the event loop. Instead of getting layout dimensions right away, set yourself up to be part of the system. Some options are:
Implement a custom widget that will interact properly with the layout, growing to fill the available width of the layout. Perhaps all you need is a size policy and a way to elide text?
Make a custom layout that takes the special properties of your use case into account.
You want to call QWidget::adjustSize() on your parent widget. This will force the layout recalculations.
Have you tried using layout()->update(); ?
I've tried many but nothing works for me on Qt 5.15.
Only invented little patch - create timer and get size after 20 msec:
QTimer::singleShot(20, this, [this]
{
const auto height = myLayout->contentsRect().height();
// ...
});

How to force calling of QWidget::paintEvent() when its hovered by other window?

I have a problem:
I'm creating a widget which displays current date's day number. It's like a button, but it's not derived from QPushButton class. Just from QWidget. So I've reimplemented enterEvent(), leaveEvent(), mousePressEvent(), mouseReleaseEvent(). I do call update() inside these methods and widget has realistic button behavior (paintEvent() is reimplemented also).
But when I change system date and hover that widget with other window, my widget doesn't get paintEvent() and the old date is displayed. Only when I place mouse over it, widget repaints it's contents.
I guess there is a feature (like buffering) which paints old contents on hovering with other window to avoid unnecessary recalculations. But I need to disable it. Tried to set many attributes (the Qt::WidgetAttribute enum). But it doesn't work.
I think you should find a way to detect that the system time has changed and call update() when that happens. Any other method (like detecting the "hovering" of a window or waiting for a mouse event) will cause the update to occur too late.

Resources