Use QEventLoop for wxEventLoop implementation in wxQt to fix several
issues with wxEventLoop, notably avoid "QApplication::exec is already
running" errors.

See https://github.com/wxWidgets/wxWidgets/pull/1165
This commit is contained in:
Vadim Zeitlin
2019-01-22 19:05:53 +01:00
2 changed files with 32 additions and 30 deletions

View File

@@ -9,6 +9,7 @@
#define _WX_QT_EVTLOOP_H_ #define _WX_QT_EVTLOOP_H_
class QTimer; class QTimer;
class QEventLoop;
class WXDLLIMPEXP_CORE wxQtEventLoopBase : public wxEventLoopBase class WXDLLIMPEXP_CORE wxQtEventLoopBase : public wxEventLoopBase
{ {
@@ -16,13 +17,15 @@ public:
wxQtEventLoopBase(); wxQtEventLoopBase();
~wxQtEventLoopBase(); ~wxQtEventLoopBase();
virtual int DoRun(); virtual int DoRun() wxOVERRIDE;
virtual void ScheduleExit(int rc = 0); virtual void ScheduleExit(int rc = 0) wxOVERRIDE;
virtual bool Pending() const; virtual bool Pending() const wxOVERRIDE;
virtual bool Dispatch(); virtual bool Dispatch() wxOVERRIDE;
virtual int DispatchTimeout(unsigned long timeout); virtual int DispatchTimeout(unsigned long timeout) wxOVERRIDE;
virtual void WakeUp(); virtual void WakeUp() wxOVERRIDE;
virtual void DoYieldFor(long eventsToProcess); virtual void DoYieldFor(long eventsToProcess) wxOVERRIDE;
void ScheduleIdleCheck();
#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);
@@ -30,6 +33,7 @@ public:
protected: protected:
private: private:
QEventLoop *m_qtEventLoop;
QTimer *m_qtIdleTimer; QTimer *m_qtIdleTimer;
wxDECLARE_NO_COPY_CLASS(wxQtEventLoopBase); wxDECLARE_NO_COPY_CLASS(wxQtEventLoopBase);

View File

@@ -17,6 +17,7 @@
#include <QtCore/QAbstractEventDispatcher> #include <QtCore/QAbstractEventDispatcher>
#include <QtCore/QSocketNotifier> #include <QtCore/QSocketNotifier>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtCore/QEventLoop>
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
@@ -40,14 +41,13 @@ wxQtIdleTimer::wxQtIdleTimer( wxQtEventLoopBase *eventLoop )
connect( this, &QTimer::timeout, this, &wxQtIdleTimer::idle ); connect( this, &QTimer::timeout, this, &wxQtIdleTimer::idle );
setSingleShot( true ); setSingleShot( true );
start( 0 );
} }
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() )
start( 0 ); m_eventLoop->ScheduleIdleCheck();
return false; // Continue handling the event return false; // Continue handling the event
} }
@@ -57,10 +57,10 @@ void wxQtIdleTimer::idle()
// Process pending events // Process pending events
if ( wxTheApp ) if ( wxTheApp )
wxTheApp->ProcessPendingEvents(); wxTheApp->ProcessPendingEvents();
// Send idle event // Send idle event
if ( m_eventLoop->ProcessIdle() ) if ( m_eventLoop->ProcessIdle() )
start( 0 ); m_eventLoop->ScheduleIdleCheck();
} }
wxQtEventLoopBase::wxQtEventLoopBase() wxQtEventLoopBase::wxQtEventLoopBase()
@@ -78,10 +78,15 @@ wxQtEventLoopBase::wxQtEventLoopBase()
// Pass all events to the idle timer, so it can be restarted each time // Pass all events to the idle timer, so it can be restarted each time
// an event is received // an event is received
qApp->installEventFilter( m_qtIdleTimer ); qApp->installEventFilter( m_qtIdleTimer );
m_qtEventLoop = new QEventLoop;
} }
wxQtEventLoopBase::~wxQtEventLoopBase() wxQtEventLoopBase::~wxQtEventLoopBase()
{ {
qApp->removeEventFilter(m_qtIdleTimer);
delete m_qtEventLoop;
delete m_qtIdleTimer; delete m_qtIdleTimer;
} }
@@ -89,44 +94,31 @@ void wxQtEventLoopBase::ScheduleExit(int rc)
{ {
wxCHECK_RET( IsInsideRun(), wxT("can't call ScheduleExit() if not started") ); wxCHECK_RET( IsInsideRun(), wxT("can't call ScheduleExit() if not started") );
m_shouldExit = true; m_shouldExit = true;
QCoreApplication::exit( rc ); m_qtEventLoop->exit(rc);
} }
int wxQtEventLoopBase::DoRun() int wxQtEventLoopBase::DoRun()
{ {
int ret; const int ret = m_qtEventLoop->exec();
// This is placed inside of a loop to take into account nested event loops
while ( !m_shouldExit )
{
// This will print Qt warnins if app already started:
// "QCoreApplication::exec: The event loop is already running"
// TODO: check the loopLevel (nested) like in wxGTK
ret = QCoreApplication::exec();
// process pending events (if exec was started previously)
// TODO: use a real new QEventLoop() ?
QCoreApplication::processEvents();
}
OnExit(); OnExit();
return ret; return ret;
} }
bool wxQtEventLoopBase::Pending() const bool wxQtEventLoopBase::Pending() const
{ {
return QCoreApplication::hasPendingEvents(); QAbstractEventDispatcher *instance = QAbstractEventDispatcher::instance();
return instance->hasPendingEvents();
} }
bool wxQtEventLoopBase::Dispatch() bool wxQtEventLoopBase::Dispatch()
{ {
QCoreApplication::processEvents(); m_qtEventLoop->processEvents();
return true; return true;
} }
int wxQtEventLoopBase::DispatchTimeout(unsigned long timeout) int wxQtEventLoopBase::DispatchTimeout(unsigned long timeout)
{ {
QCoreApplication::processEvents( QEventLoop::AllEvents, timeout ); m_qtEventLoop->processEvents(QEventLoop::AllEvents, timeout);
return true; return true;
} }
@@ -146,6 +138,12 @@ void wxQtEventLoopBase::DoYieldFor(long eventsToProcess)
wxEventLoopBase::DoYieldFor(eventsToProcess); wxEventLoopBase::DoYieldFor(eventsToProcess);
} }
void wxQtEventLoopBase::ScheduleIdleCheck()
{
if ( IsInsideRun() )
m_qtIdleTimer->start(0);
}
#if wxUSE_EVENTLOOP_SOURCE #if wxUSE_EVENTLOOP_SOURCE
template <void (wxEventLoopSourceHandler::*function)()> template <void (wxEventLoopSourceHandler::*function)()>