Sending events from worker thread while a modal dialog is open.
Sending events from worker thread while a modal dialog is open.
In my little app I have a worker thread that communicates to some hardware and sends its status data to the main window for display. It does this 10 times per second. The worker thread queues these status messages to one of wxPanel-derived panels via wxQueueEvent(new wxThreadEvent(...)). Everything seems to work OK until a modal dialog (my custom dialog or even the standard wxFileOpenDialog) is opened via ShowModal. After the dialog is shown, my messages are not processed in my panel anymore. I understand that modal dialogs set their custom message loops which suspend other window's loops. But due to this, I not only lose up-to-date hardware status data for display while the dialog is open but also have a big problem: since my worker thread keeps sending these status messages, it seems that something bad happens to GUI - it becomes nearly unresponsive after a modal dialog is open for about 10 seconds. Probably the main window's message queue is gettin overfilled with the status messages from the worker thread. So what is the best way to avoid this situation? I could set some boolean flag visible to the worker thread to signal it to not send the status message after each ShowModal and clear the flag after each EndModal, but I have many dialogs in my app and this approach seems to be inconvenient and error-prone. I could probably somehow check from within a thread that the window's message loop is currently suspended by dialog's custom loop and to not make the wxQueueThread calls if so, but I do not see the way check this condition. I could probably call ProcessPendingEvents periodically and even have the correct hardware status displayed while a dialog is open (perfect), but while I could probably do this from my custom dialogs, it seems not possible for standard dialogs (like wxFileOpenDialog). Can somebody please suggest me a bright idea because to be honest, i'm a bit desperate.
Re: Sending events from worker thread while a modal dialog is open.
What platform is that? On Windows, I still get thread events even when a modal dialog is open.
I have just tested it with this simple program
I am not familiar with platforms besides Windows, so...
I have just tested it with this simple program
Code: Select all
#include <wx/wx.h>
class MyThread : public wxThread
{
public:
MyThread(wxEvtHandler* sink) : wxThread(wxTHREAD_JOINABLE), m_sink(sink)
{}
protected:
wxEvtHandler* m_sink;
ExitCode Entry() override
{
int eventCount = 0;
while ( !TestDestroy() )
{
wxThreadEvent evt;
evt.SetInt(++eventCount);
wxQueueEvent(m_sink, evt.Clone());
Sleep(500); // send twice a second
}
return static_cast<wxThread::ExitCode>(nullptr);
}
};
class MyDialog : public wxDialog
{
public:
MyDialog(wxWindow* parent) : wxDialog(parent, wxID_ANY, "MyDialog")
{
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(new wxStaticText(this, wxID_ANY, "Empty dialog"));
sizer->Add(CreateSeparatedButtonSizer(wxOK | wxCANCEL), wxSizerFlags().Expand());
SetSizerAndFit(sizer);
}
};
class MyFrame : public wxFrame
{
public:
MyFrame() : wxFrame(nullptr, wxID_ANY, "Test")
{
wxPanel* mainPanel = new wxPanel(this);
wxSizer* mainPanelSizer = new wxBoxSizer(wxVERTICAL);
mainPanelSizer->Add(new wxButton(mainPanel, wxID_ANY, "Show Dialog..."), wxSizerFlags().Expand().Border());
Bind(wxEVT_BUTTON, &MyFrame::OnShowDialog, this);
wxTextCtrl* logCtrl = new wxTextCtrl(mainPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);
mainPanelSizer->Add(logCtrl, wxSizerFlags(1).Expand().DoubleBorder());
wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl));
wxLog::DisableTimestamp();
mainPanel->SetSizer(mainPanelSizer);
SetClientSize(FromDIP(wxSize(400, 400)));
Bind(wxEVT_THREAD, &MyFrame::OnThreadEvent, this);
StartMyThread();
}
~MyFrame() { StopMyThread(); }
private:
MyThread* m_thread{nullptr};
bool StartMyThread()
{
StopMyThread();
m_thread = new MyThread(this);
if ( m_thread->Run() != wxTHREAD_NO_ERROR )
{
wxDELETE(m_thread);
wxLogError(_("Could not create the thread needed to load the data."));
return false;
}
return true;
}
void StopMyThread()
{
if ( m_thread )
{
m_thread->Delete();
wxDELETE(m_thread);
}
}
void OnThreadEvent(wxThreadEvent& evt)
{
wxLogMessage("MyThread event no %d", evt.GetInt());
}
void OnShowDialog(wxCommandEvent&)
{
MyDialog dlg(this);
wxLogMessage("Showing dialog...");
dlg.ShowModal();
wxLogMessage("Dialog closed.");
}
};
class MyApp : public wxApp
{
public:
bool OnInit() override
{
(new MyFrame)->Show();
return true;
}
}; wxIMPLEMENT_APP(MyApp);
FWIW, there is https://docs.wxwidgets.org/trunk/classw ... _hook.htmlSlowspeed wrote: ↑Sun Jan 23, 2022 4:47 am I could set some boolean flag visible to the worker thread to signal it to not send the status message after each ShowModal and clear the flag after each EndModal, but I have many dialogs in my app and this approach seems to be inconvenient and error-prone.
Re: Sending events from worker thread while a modal dialog is open.
Thanks, man! But please try to replace the dialog creation call in your OnShowDialog with the following:
then press "Show Dialog" and wait for ther first 20-30 messages. Do not move the mouse cursor! What I observe is that events start to come with increasing delays and finally stop coming at all.
Thanks again.
Code: Select all
//MyDialog dlg(this);
wxString sWildcard{ wxString::Format(wxT("Gerber files (*.gbr)|*.gbr|All files(%s)|%s"), wxFileSelectorDefaultWildcardStr,
wxFileSelectorDefaultWildcardStr) };
wxFileDialog dlg(this, wxT("Choose a file"), wxEmptyString, wxEmptyString, sWildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
Thanks again.
Re: Sending events from worker thread while a modal dialog is open.
Even with a wxFileDialog it works fine for me under Windows.
Which platform, compiler and wxWidgets version are you using?
And if it's not the latest wxWidgets version, try with that one please.
Which platform, compiler and wxWidgets version are you using?
And if it's not the latest wxWidgets version, try with that one please.
Use the source, Luke!
Re: Sending events from worker thread while a modal dialog is open.
I can confirm that it does NOT work with wxFileDialolg (which on MSW is not a real dialog but basically a wrapped call to GetOpenFileName()).
After the file dialog is being shown, I get few messages but they soon stop coming. I can always somehow get few more after moving a mouse.
No idea why this does not happen with any custom wxDialog, according to the docs, it actually should, as the main even queue is not being processed?
Tested on current wxWidgets master, Win10.
I also have no idea what to do, perhaps asking in wx-users and hoping Vadim will know, as this is not a an uncommon scenario...
After the file dialog is being shown, I get few messages but they soon stop coming. I can always somehow get few more after moving a mouse.
No idea why this does not happen with any custom wxDialog, according to the docs, it actually should, as the main even queue is not being processed?
Tested on current wxWidgets master, Win10.
I also have no idea what to do, perhaps asking in wx-users and hoping Vadim will know, as this is not a an uncommon scenario...
Re: Sending events from worker thread while a modal dialog is open.
I develop under MS Windows 10 64 bit, using MS VS 2019 16.11.3 Community with Windows SDK 10.0.19041.0 installed. wxWidgets is of the version 3.1.5 built in its default vcpkg config. I link my program statically with both wxWidgets and the multithreaded runtime (/MT(d)). My CPU is AMD Ryzen TR 2950X if this may somehow affect the threading behaviour.
Re: Sending events from worker thread while a modal dialog is open.
Did you try to run your event loop manually from within your dialog?
https://docs.wxwidgets.org/3.0/classwx_ ... 0dd305e6f2
https://docs.wxwidgets.org/3.0/classwx_ ... 0dd305e6f2