Merge branch 'qt_more_event_loop_improvements' of https://github.com/GeoTeric/wxWidgets

Event loop and idle events improvements for wxQt

See https://github.com/wxWidgets/wxWidgets/pull/1171
This commit is contained in:
Vadim Zeitlin
2019-01-27 03:55:38 +01:00
2 changed files with 61 additions and 35 deletions

View File

@@ -8,7 +8,7 @@
#ifndef _WX_QT_EVTLOOP_H_ #ifndef _WX_QT_EVTLOOP_H_
#define _WX_QT_EVTLOOP_H_ #define _WX_QT_EVTLOOP_H_
class QTimer; class wxQtIdleTimer;
class QEventLoop; class QEventLoop;
class WXDLLIMPEXP_CORE wxQtEventLoopBase : public wxEventLoopBase class WXDLLIMPEXP_CORE wxQtEventLoopBase : public wxEventLoopBase
@@ -30,11 +30,10 @@ public:
#if wxUSE_EVENTLOOP_SOURCE #if wxUSE_EVENTLOOP_SOURCE
virtual wxEventLoopSource *AddSourceForFD(int fd, wxEventLoopSourceHandler *handler, int flags); virtual wxEventLoopSource *AddSourceForFD(int fd, wxEventLoopSourceHandler *handler, int flags);
#endif // wxUSE_EVENTLOOP_SOURCE #endif // wxUSE_EVENTLOOP_SOURCE
protected:
private: private:
QEventLoop *m_qtEventLoop; QEventLoop *m_qtEventLoop;
QTimer *m_qtIdleTimer; wxObjectDataPtr<wxQtIdleTimer> m_qtIdleTimer;
wxDECLARE_NO_COPY_CLASS(wxQtEventLoopBase); wxDECLARE_NO_COPY_CLASS(wxQtEventLoopBase);
}; };

View File

@@ -21,33 +21,48 @@
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
class wxQtIdleTimer : public QTimer class wxQtIdleTimer : public QTimer, public wxRefCounter
{ {
public: public:
wxQtIdleTimer( wxQtEventLoopBase *eventLoop ); wxQtIdleTimer();
~wxQtIdleTimer();
virtual bool eventFilter( QObject * watched, QEvent * event ); virtual bool eventFilter( QObject * watched, QEvent * event );
private: private:
void idle(); void idle();
void ScheduleIdleCheck();
private:
wxQtEventLoopBase *m_eventLoop;
}; };
wxQtIdleTimer::wxQtIdleTimer( wxQtEventLoopBase *eventLoop ) wxQtIdleTimer::wxQtIdleTimer()
{ {
m_eventLoop = eventLoop; // We need a QCoreApplication for event loops, create it here if it doesn't
// already exist as we can't modify wxAppConsole
if ( !QCoreApplication::instance() )
{
new QApplication(wxAppConsole::GetInstance()->argc, wxAppConsole::GetInstance()->argv);
}
connect( this, &QTimer::timeout, this, &wxQtIdleTimer::idle );
setSingleShot( true ); // Pass all events to the idle timer, so it can be restarted each time
// an event is received
qApp->installEventFilter(this);
connect(this, &QTimer::timeout, this, &wxQtIdleTimer::idle);
setSingleShot(true);
}
wxQtIdleTimer::~wxQtIdleTimer()
{
qApp->removeEventFilter(this);
} }
bool wxQtIdleTimer::eventFilter( QObject *WXUNUSED( watched ), QEvent *WXUNUSED( event ) ) bool wxQtIdleTimer::eventFilter( QObject *WXUNUSED( watched ), QEvent *WXUNUSED( event ) )
{ {
// Called for each Qt event, start with timeout 0 (run as soon as idle) // Called for each Qt event, start with timeout 0 (run as soon as idle)
if ( !isActive() ) if ( !isActive() )
m_eventLoop->ScheduleIdleCheck(); ScheduleIdleCheck();
return false; // Continue handling the event return false; // Continue handling the event
} }
@@ -59,35 +74,40 @@ void wxQtIdleTimer::idle()
wxTheApp->ProcessPendingEvents(); wxTheApp->ProcessPendingEvents();
// Send idle event // Send idle event
if ( m_eventLoop->ProcessIdle() ) if (wxTheApp->ProcessIdle())
m_eventLoop->ScheduleIdleCheck(); ScheduleIdleCheck();
}
namespace
{
wxObjectDataPtr<wxQtIdleTimer> gs_idleTimer;
}
void wxQtIdleTimer::ScheduleIdleCheck()
{
wxQtEventLoopBase *eventLoop = static_cast<wxQtEventLoopBase*>(wxEventLoop::GetActive());
if ( eventLoop )
eventLoop->ScheduleIdleCheck();
} }
wxQtEventLoopBase::wxQtEventLoopBase() wxQtEventLoopBase::wxQtEventLoopBase()
{ {
// We need a QCoreApplication for event loops, create it here if it doesn't
// already exist as we can't modify wxAppConsole
if ( !QCoreApplication::instance() )
{
new QApplication( wxAppConsole::GetInstance()->argc, wxAppConsole::GetInstance()->argv );
}
// Create an idle timer to run each time there are no events (timeout = 0) // Create an idle timer to run each time there are no events (timeout = 0)
m_qtIdleTimer = new wxQtIdleTimer( this ); if ( !gs_idleTimer )
gs_idleTimer.reset(new wxQtIdleTimer());
// Pass all events to the idle timer, so it can be restarted each time
// an event is received
qApp->installEventFilter( m_qtIdleTimer );
m_qtIdleTimer = gs_idleTimer;
m_qtEventLoop = new QEventLoop; m_qtEventLoop = new QEventLoop;
} }
wxQtEventLoopBase::~wxQtEventLoopBase() wxQtEventLoopBase::~wxQtEventLoopBase()
{ {
qApp->removeEventFilter(m_qtIdleTimer); //Clear the shared timer if this is the only external reference to it
if ( gs_idleTimer->GetRefCount() <= 2 )
gs_idleTimer.reset(NULL);
delete m_qtEventLoop; delete m_qtEventLoop;
delete m_qtIdleTimer;
} }
void wxQtEventLoopBase::ScheduleExit(int rc) void wxQtEventLoopBase::ScheduleExit(int rc)
@@ -131,16 +151,23 @@ void wxQtEventLoopBase::WakeUp()
void wxQtEventLoopBase::DoYieldFor(long eventsToProcess) void wxQtEventLoopBase::DoYieldFor(long eventsToProcess)
{ {
while (wxTheApp && wxTheApp->Pending())
// TODO: implement event filtering using the eventsToProcess mask QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents;
wxTheApp->Dispatch();
if ( !(eventsToProcess & wxEVT_CATEGORY_USER_INPUT) )
flags |= QEventLoop::ExcludeUserInputEvents;
if ( !(eventsToProcess & wxEVT_CATEGORY_SOCKET) )
flags |= QEventLoop::ExcludeSocketNotifiers;
m_qtEventLoop->processEvents(flags);
wxEventLoopBase::DoYieldFor(eventsToProcess); wxEventLoopBase::DoYieldFor(eventsToProcess);
} }
void wxQtEventLoopBase::ScheduleIdleCheck() void wxQtEventLoopBase::ScheduleIdleCheck()
{ {
if ( IsInsideRun() ) if ( IsInsideRun() && !m_shouldExit )
m_qtIdleTimer->start(0); m_qtIdleTimer->start(0);
} }