7

Im using wxWidgets and I call function which takes a long time to proceed. I would like to do it in background.

How can I do that?

Thanks for help

gruber
  • 28,739
  • 35
  • 124
  • 216

4 Answers4

8

I've worked with threads in wxWidgets in pretty much all of the ways described here, and I can say that using custom events, while initially a bit more complex, saves you some headache in the long run. (The wxMessageQueue class is quite nice, but when I used it I found it to leak; I haven't checked it in about a year though.)

A basic example:

MyFrm.cpp

#include "MyThread.h"

BEGIN_EVENT_TABLE(MyFrm,wxFrame)
    EVT_COMMAND(wxID_ANY, wxEVT_MYTHREAD, MyFrm::OnMyThread)
END_EVENT_TABLE()
void MyFrm::PerformCalculation(int someParameter){
    //create the thread
    MyThread *thread = new Mythread(this, someParameter);
    thread->Create();
    thread->Run();
    //Don't worry about deleting the thread, there are two types of wxThreads 
    //and this kind deletes itself when it's finished.
}
void MyFrm::OnMyThread(wxCommandEvent& event)
{
    unsigned char* temp = (unsigned char*)event.GetClientData();
    //do something with temp, which holds unsigned char* data from the thread
    //GetClientData() can return any kind of data you want, but you have to cast it.
    delete[] temp; 
}    

MyThread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

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

BEGIN_DECLARE_EVENT_TYPES()
    DECLARE_EVENT_TYPE(wxEVT_MYTHREAD, -1)
END_DECLARE_EVENT_TYPES()

class MyThread : public wxThread
{
    public:
        MyThread(wxEvtHandler* pParent, int param);
    private:
        int m_param;
        void* Entry();
    protected:
        wxEvtHandler* m_pParent;
};
#endif 

MyThread.cpp

#include "MyThread.h"
DEFINE_EVENT_TYPE(wxEVT_MYTHREAD)
MyThread::MyThread(wxEvtHandler* pParent, int param) : wxThread(wxTHREAD_DETACHED), m_pParent(pParent)
{
    //pass parameters into the thread
m_param = param;
}
void* MyThread::Entry()
{
    wxCommandEvent evt(wxEVT_MYTHREAD, GetId());
    //can be used to set some identifier for the data
    evt.SetInt(r); 
    //whatever data your thread calculated, to be returned to GUI
    evt.SetClientData(data); 
    wxPostEvent(m_pParent, evt);
    return 0;
}

I feel like this is much more clear, concise example than the one the wiki offers. Obviously I left out code concerning actually launching the app (wx convention would make that MyApp.cpp) and any other non-thread-related code.

Evan Cordell
  • 4,108
  • 2
  • 31
  • 47
2

If you simply need to have something work in the background until it's finished -- fire and forget if you will, something like this:

// warning: off the top of my head ;-)
class MyThread
  : public wxThread
{
public:
  MyThread() : wxThread(wxTHREAD_DETACHED)
  {
    if(wxTHREAD_NO_ERROR == Create()) {
      Run();
    }
  }
protected:
  virtual ExitCode Entry()
  {
    // do something here that takes a long time
    // it's a good idea to periodically check TestDestroy()
    while(!TestDestroy() && MoreWorkToDo()) {
      DoSaidWork();
    }
    return static_cast<ExitCode>(NULL);
  }
};

MyThread* thd = new MyThread(); // auto runs & deletes itself when finished
NuSkooler
  • 5,391
  • 1
  • 34
  • 58
1

A few tips from implementing the above:

  1. Using MingW32 and Codeblocks I've had the following warning: EVENT redeclared without dllimport attribute: previous dllimport ignored. The soultion to this is that if you do not need to export your events, use DEFINE_LOCAL_EVENT_TYPE and DECLARE_LOCAL_EVENT_TYPE (instead of DEFINE_EVENT_TYPE and DECLARE_EVENT_TYPE).

  2. If you want to pass objects via SetClientData(), make sure you create the data using the new operator in the detachable thread. The calling application will then have to delete the data once it's copied.

For example:

BEGIN_DECLARE_EVENT_TYPES()
    DECLARE_LOCAL_EVENT_TYPE(wxEVT_CALC_THREAD, -1)
END_DECLARE_EVENT_TYPES()

void* MyThread::Entry()
{
    wxCommandEvent evt(wxEVT_CALC_THREAD, GetId());
    // do some work
    vector<map<int, int> > *vm = new vector<map<int, int> >();
    // perform operations with the object vm ...
    evt.SetClientData((void*)vm);
    wxPostEvent(m_pParent, evt);
}

and in the calling application:

DEFINE_LOCAL_EVENT_TYPE(wxEVT_CALC_THREAD)

// change this to your event table
BEGIN_EVENT_TABLE(..., ...)
    EVT_COMMAND(wxID_ANY, wxEVT_CALC_THREAD, ThreadDone)
END_EVENT_TABLE()

void ThreadDone(wxCommandEvent& event)
{

   vector<map<int, int> > *temp = (vector<map<int, int> > *)event.GetClientData();
   // store the data in *temp 
   delete temp;
}
im2graph
  • 11
  • 1
0

If your program is simple and you don't want to mess around with threads, you may consider calling wxWindow::Update() periodically in your long function.

streeto
  • 123
  • 1
  • 4