4

When I do multiple updates on a widget in wxPython (like loading content of tables, trees, un-hiding widgets etc.) I often use Freeze to disable the widget re-drawing and when I am done, I call Thaw so the new content appears all at once. Is there a similar way in Qt?

Related question: Python/Tkinter: Turn on/off screen updates like wxPython Freeze/Thaw?

Community
  • 1
  • 1
Fenikso
  • 9,251
  • 5
  • 44
  • 72

2 Answers2

3

I know that this is an old post, but I found this at Turn off PyQt Event Loop While Editing Table

There is a way to do this:

blockSignals(bool) is intended for suppressing QObjects and their subclasses from emitting signals, thus preventing any other objects from receiving them in slots. But this is a QObject method. If you are specifically trying to prevent one object from emitting signals in response to changes that you are making, which might trigger calculations or some other expensive processing in a slot, then this is what you want.

But if your situation is that making repeated changes is causing expensive paint operations over and over (or other expensive events being generated on the widget), then you have the ability to disable updates with updatesEnabled(bool). A benefit of this method is that it recursively disables the children of the target widget, preventing them from being updated as well. So nothing in the hierarchy will receive updates until you enable again.

mainWidget.setUpdatesEnabled(False)
# do a bunch of operations that would trigger expensive events
# like repaints
mainWidget.setUpdatesEnabled(True)
Community
  • 1
  • 1
Jim Provan
  • 56
  • 1
2

In Qt it shouldn't be necessary to do it, since the update events are compressed and won't be delivered until the control returns to the event loop. For example, the following will only result in one repaint of the label:

void test(QLabel * label) {
  label->setText("foo");
  // update is called internally by setText, but the extra call is harmless here
  label->update(); 
  label->setText("bar");
  label->setText("baz");
}

If you are certain that your updates are interspersed with returns to the event loop, then you can implement the freeze/thaw as follows by filtering the update events. The below is in C++, feel free to translate it to Python :)

class UpdateFilter : public QObject {
  Q_OBJECT
  QSet<QObject> m_deferredUpdates;
  Q_SLOT void isDestroyed(QObject * obj) {
    m_deferredUpdates.remove(obj);
  }
  Q_OBJECT bool eventFilter(QObject * obj, QEvent * ev) {
    if (ev->type() == QEvent::UpdateRequest) {
      if (! m_deferredUpdates.contains(obj) {
        m_deferredUpdates.insert(obj);
        connect(obj, SIGNAL(destroyed(QObject*)), SLOT(isDestroyed(QObject*)));
      }
      return true;
    }
    return false;
  }
public:
  void thaw(QWidget * w) {
    if (m_deferredUpdates.contains(w)) {
      w->update();
      m_deferredUpdates.remove(w);
    }
};

// This instance is only created on the first call to freeze.
Q_GLOBAL_STATIC(UpdateFilter, updateFilter)

void freeze(QWidget * w) { w->installEventFilter(&updateFilter()); }
void thaw(QWidget * w) { updateFilter().thaw(w); }
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Sorry for not looking into this for a long time. I have an example where your first part of the answer does not seem to apply. Do you have any reference for "update events are compressed"? My case is where I try to resize a window (due to unhiding some elements) and then moving the window so it is still centered on the screen. However I can see it flick for a moment, so the events do not seem to be compressed. – Fenikso Apr 02 '14 at 07:39
  • @Fenikso The update events are compressed, as are resize events, but this may not be enough to prevent flicker in some scenarios - especially during resize, when the event loop is often starved by Windows itself. – Kuba hasn't forgotten Monica Apr 02 '14 at 07:50
  • Oh, so you say, that events are compressed, but OS Window Manager is slow to do the resize and move? – Fenikso Apr 02 '14 at 07:53