Sending events from worker thread while a modal dialog is open.

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
Slowspeed
In need of some credit
In need of some credit
Posts: 7
Joined: Tue May 05, 2009 2:37 pm

Sending events from worker thread while a modal dialog is open.

Post by Slowspeed »

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.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Sending events from worker thread while a modal dialog is open.

Post by PB »

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
wxthread-dlg.png
wxthread-dlg.png (8.87 KiB) Viewed 551 times

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);
I am not familiar with platforms besides Windows, so...
Slowspeed 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.
FWIW, there is https://docs.wxwidgets.org/trunk/classw ... _hook.html
Slowspeed
In need of some credit
In need of some credit
Posts: 7
Joined: Tue May 05, 2009 2:37 pm

Re: Sending events from worker thread while a modal dialog is open.

Post by Slowspeed »

Thanks, man! But please try to replace the dialog creation call in your OnShowDialog with the following:

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);
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.
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Sending events from worker thread while a modal dialog is open.

Post by doublemax »

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.
Use the source, Luke!
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Sending events from worker thread while a modal dialog is open.

Post by PB »

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...
Slowspeed
In need of some credit
In need of some credit
Posts: 7
Joined: Tue May 05, 2009 2:37 pm

Re: Sending events from worker thread while a modal dialog is open.

Post by Slowspeed »

doublemax wrote: Sun Jan 23, 2022 1:02 pm Which platform, compiler and wxWidgets version are you using?
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.
sparhawk
Experienced Solver
Experienced Solver
Posts: 81
Joined: Tue May 21, 2013 8:08 am

Re: Sending events from worker thread while a modal dialog is open.

Post by sparhawk »

Did you try to run your event loop manually from within your dialog?

https://docs.wxwidgets.org/3.0/classwx_ ... 0dd305e6f2
Post Reply