passing data from worker thread to the main

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
cadol_wx
Earned a small fee
Earned a small fee
Posts: 17
Joined: Thu Jan 30, 2020 3:27 pm

passing data from worker thread to the main

Post by cadol_wx »

Hello everybody,

I have a newbie question about passing data from a worker thread to the main.

In the examples on-line I found how to attach an integer to an event in a worker thread and how to get it in the event handler of the main thread:

Code: Select all


//in worker thread:
wxThread::ExitCode wrkThr::Entry()
 {
  for (int n=0; n<100; n++)
   {
    this->Sleep(500);

    //notify the main thread
    wxCommandEvent e( wxEVT_COMMAND_TEXT_UPDATED, NUMBER_UPDATE_ID );
   
    //pass some data along the event, a number in this case
    e.SetInt(n);
   
    frmParent->GetEventHandler()->AddPendingEvent(e);
   }
   
  return 0;
 }

...

//in main thread:
void frmMain::OnNumberUpdate(wxCommandEvent& e)
 {
  //setting value in text box
  tbDisp->SetValue(std::to_string(e.GetInt()));
 }


So the main work done by two (probably standard) functions: SetInt(n) and GetInt().

But what if I need to pass two integers? Or an integer and a float? Or even more than two values? I do not need a variable size data, it always be the same type.

Say, if, for example, I use a struct:

Code: Select all

struct strData
 {
  int int1;
  int int2;
  float f1;
 }
How do I pass such a struct from the worker and read it in the main? What's the syntax in the example above?


Thank you in advance!
Kvaz1r
Super wx Problem Solver
Super wx Problem Solver
Posts: 357
Joined: Tue Jun 07, 2016 1:07 pm

Re: passing data from worker thread to the main

Post by Kvaz1r »

cadol_wx
Earned a small fee
Earned a small fee
Posts: 17
Joined: Thu Jan 30, 2020 3:27 pm

Re: passing data from worker thread to the main

Post by cadol_wx »

Hi Kvaz1r,

Thank you for a quick response. Could you please show how to use SetPayload(), GetPayload() in the example of my post? What's the syntax?
Kvaz1r
Super wx Problem Solver
Super wx Problem Solver
Posts: 357
Joined: Tue Jun 07, 2016 1:07 pm

Re: passing data from worker thread to the main

Post by Kvaz1r »

Code: Select all

#include "wx/wx.h"
#include <wx/thread.h>

struct strData
{
    int int1;
    int int2;
    float f1;

    wxString asString() const {
        return wxString::Format("{%d %d %lf}", int1, int2, f1);
    }
};

class MyThread : public wxThread
{
public:
    MyThread(wxEvtHandler* parent) : wxThread(), m_parent(parent) {}

    ExitCode Entry()
    {
        for (unsigned n = 0; n < 10; n++)
        {
            wxThreadEvent *evt = new wxThreadEvent(wxEVT_THREAD);
            strData var;
            var.int1 = n;
            var.int2 = n * n;
            var.f1 = std::sqrt(n);
            evt->SetPayload(var);
            m_parent->QueueEvent(evt);
            this->Sleep(1000);
        }
        return NULL;
    }
protected:
    wxEvtHandler* m_parent;
};

// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame
{
public:
    // ctor(s)
    MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title)
    {
        m_label = new wxStaticText(this, wxID_ANY, "", wxPoint(10, 100), wxSize(80, -1));

        MyThread* thread = new MyThread(this);
        if (thread->Create() == wxTHREAD_NO_ERROR)
        {
            thread->Run();
        }

        Bind(wxEVT_THREAD, [this](wxThreadEvent& ev)
            {
                m_label->SetLabel(ev.GetPayload<strData>().asString());
            });
    }

    wxStaticText* m_label;
};

class MyApp : public wxApp
{
public:
    virtual bool OnInit() wxOVERRIDE
    {
        if (!wxApp::OnInit())
            return false;

        (new MyFrame("Minimal wxWidgets App"))->Show(true);

        return true;
    }
};
wxIMPLEMENT_APP(MyApp);
cadol_wx
Earned a small fee
Earned a small fee
Posts: 17
Joined: Thu Jan 30, 2020 3:27 pm

Re: passing data from worker thread to the main

Post by cadol_wx »

I am trying this:

Code: Select all

struct strData
 {
  int int1;
  int int2;
  float f1;
 }

...

//in worker thread:
wxThread::ExitCode wrkThr::Entry()
 {
  strData d1;

 
  for (int n=0; n<100; n++)
   {
    this->Sleep(500);

    //notify the main thread
    wxCommandEvent e( wxEVT_COMMAND_TEXT_UPDATED, NUMBER_UPDATE_ID );

    d1.int1 = n;
    d1.int2 = n*2;
    d1.f1 = n + 0.14;
    
    //pass struct
    e.SetPayload(d1);  
   
    frmParent->GetEventHandler()->AddPendingEvent(e);
   }
   
  return 0;
 }

...

//in main thread:
void frmMain::OnNumberUpdate(wxCommandEvent& e)
 {
  strData d2;

  d2 = e.GetPayload<strData>();
 
  //setting value in text box
  tbDisp->SetValue(std::to_string(d2.f1))
 }

But I am getting the following compiler errors (CodeLite):

expected primary-expression before '>' token d2 = e.GetPayload<strData>();

expected primary-expression before ')' token d2 = e.GetPayload<strData>();

'class wxCommandEvent' has no member named 'SetPayload' e.SetPayload(strData::d1);

Any idea what's wrong?..
cadol_wx
Earned a small fee
Earned a small fee
Posts: 17
Joined: Thu Jan 30, 2020 3:27 pm

Re: passing data from worker thread to the main

Post by cadol_wx »

Hi Kvaz1r,

Thank you! - Your example works perfectly, I am trying to figure out why I am getting those compiler errors when trying to compile mine...
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7459
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: passing data from worker thread to the main

Post by ONEEYEMAN »

Hi,
You need to use wxThreadEvent and not wxCommandEvent.

After all - this is a threads communication. ;-)

Thank you.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: passing data from worker thread to the main

Post by PB »

And you are supposed to use wxQueueEvent() instead of AddPendingEvent() as well.

Using an existing command event is a bad idea either way, as this one is supposed to be generated by a control and having its fields such as event object set accordingly. It is better to create a new event, it is simple, see e.g. this
viewtopic.php?f=1&t=46485#p194840
cadol_wx
Earned a small fee
Earned a small fee
Posts: 17
Joined: Thu Jan 30, 2020 3:27 pm

Re: passing data from worker thread to the main

Post by cadol_wx »

ONEEYEMAN, PB,

Thank you for pointing out at these things. I am in process of adjusting my app accordingly, I will let you know about the result when done.
cadol_wx
Earned a small fee
Earned a small fee
Posts: 17
Joined: Thu Jan 30, 2020 3:27 pm

Re: passing data from worker thread to the main

Post by cadol_wx »

Hello Everybody,

Taking your suggestions I've ended up with these two functions which do what I need:

Code: Select all

//in main frame:
void frmMain::OnWorkerThrEvent(wxThreadEvent& e)
 {
  strData d1;
  
  d1 = e.GetPayload<strData>();
  tbDisp->SetValue(std::to_string(d1.f1));
 }


//in worker thread
wxThread::ExitCode wrkThr::Entry()
 {
  int i;
  strData d1;

  wxThreadEvent e(wxEVT_THREAD,ID_WORKER_THREAD);

  for(i = 0; i < 10; i++)
   {
    d1.i1 = i;
    d1.i2 = i * i;
    d1.f1 = i + 0.11;
    
    e.SetPayload(d1);
    wxQueueEvent(frmParent, e.Clone());

    wxMilliSleep(500);
   }//for(i = 0; i < 10; i++)

  //on completion
  d1.f1 = 3.14;
  e.SetPayload(d1);
  wxQueueEvent(frmParent, e.Clone());

  return NULL;
 }//wxThread::ExitCode wrkThr::Entry()
       


Thank you all for your help!
Post Reply