📜 ⬆️ ⬇️

Idle event in qt

Hi, habra people!

Embedding


Not so long ago, I started working with Qt under Windows. My task is to develop a graphical application with a real-time drawing of the user interface with animation and other buns. All this beauty works through DirectX, and Qt really helps with animation, windows, signals and other useful things.

Why is this necessary?


If anyone has been involved in game development, he knows that the screen is redrawn during the “application downtime” - i.e. those moments when the message queue to the window is empty. This allows the window to be redrawn fairly quickly, in particular, an empty window that can draw several thousand frames per second (with vertical synchronization turned off).

How not to do?

I worked with MFC a long time ago and remembered that there was an OnIdle () function that could be overloaded. Having rummaged on the Internet and in source codes, I and did not find something similar in Qt. The only solution that seems to work is to create a timer that starts at an interval of 0 ms, i.e. looked like this:
  1. class QMyWindow : public QWidget
  2. {
  3. Q_OBJECT
  4. int m_idleTimerId ;
  5. public :
  6. QMyWindow ( QObject * parent ) : QWidget ( parent ) : m_idleTimerId ( - 1 )
  7. {
  8. m_idleTimerId = startTimer ( 0 ) ;
  9. }
  10. void timerEvent ( QTimerEvent * event )
  11. {
  12. if ( event - > timerId ( ) == m_idleTimerId )
  13. {
  14. // your idle handling code is here ...
  15. }
  16. }
  17. } ;

According to the author, the timer message is activated when we have an empty message queue. But the problem is that these same timer messages “clog up” the queue so that even QResizeEvent does not immediately reach the handler.
')

Solution found


The solution came unexpectedly - to use the window drawing scheme in WinAPI:
  1. while ( msg. message ! = WM_QUIT )
  2. {
  3. if ( PeekMessage ( & msg, NULL , 0U, 0U, PM_REMOVE ) )
  4. {
  5. TranslateMessage ( & msg ) ;
  6. DispatchMessage ( & msg ) ;
  7. }
  8. else
  9. Render ( ) ;
  10. }

The code shows that we first receive the message, via PeekMessage, then we process the message and send it to the window handler, and if the queue is empty, we draw our elements. On Qt, it looks like this:
  1. WinPlatform w ;
  2. MwApplication a ( & w, argc, argv, true ) ;
  3. a. installEventFilter ( & w ) ;
  4. a. postEvent ( & w, new FLoadingEvent ( ) , Qt :: LowEventPriority ) ;
  5. while ( w. isRunning ( ) || QApplication :: hasPendingEvents ( ) )
  6. {
  7. // QApplication :: sendPostedEvents ();
  8. if ( QApplication :: hasPendingEvents ( ) )
  9. QApplication :: processEvents ( ) ;
  10. else
  11. w. renderObjects ( ) ;
  12. }

In short, a couple of points:
  1. set a filter for QCloseEvent messages, well, or use the lastWindowClosed signal so that w.isRunning returns false if the window is closed
  2. process messages as long as they are in the queue, or draw our objects

It is worth telling separately about why the while condition is hasPendingEvents (). The fact is that when the window is closed, we can queue objects for deletion by calling deleteLater, which are deleted when returning to eventLoop. To avoid possible problems, you must process the queue to the end.

Also, you probably noticed the commented out line calling sendPostedEvents (). The documentation says that the absence of a call to this procedure can cause problems with deferred deletion (deleteLater), although everything seems to work in my application.

Conclusion


It turned out, of course, a bit of a crutch, but it still works. If you have any suggestions, you are welcome in the comments. Thank you all for your attention!

Source: https://habr.com/ru/post/114392/


All Articles