Witam,
Wydawało mi się, że juz mniej wiecej rozumiem, jak działa mechanizm wątków i eventów w wxWidgets, ale jak widać nie do końca.
Celem moim było napisanie najprostszej aplikacji korzystającej z jednego wątku. Wątek powinien informować o postępie okno aplikacji poprzez mechanizm eventów.
Najpierw więc utworzyłem najprostszą klasę wątku:
MyThread.h
#pragma once
#include <wx/wx.h>
#include <wx/thread.h>
#include <wx/url.h>
#include <wx/file.h>
#include "MyEvent.h"
class MyThread : public wxThread
{
private:
wxWindow *m_pParent;
public:
MyThread(wxWindow *pParent);
virtual ~MyThread(void);
virtual void *Entry();
virtual void OnExit();
};
MyThread.cpp
#include "MyThread.h"
///---------------------------------------------------------------------------------------------
MyThread::MyThread(wxWindow *pParent): wxThread(wxTHREAD_DETACHED), m_pParent(pParent)
{
if ( this->Create() != wxTHREAD_NO_ERROR )
{
wxLogError(wxT("Nie można utworzyć wątku!"));
}
else
{
if ( this->Run() != wxTHREAD_NO_ERROR )
{
wxLogError(wxT("Nie można uruchomić wątku!"));
}
}
}
///---------------------------------------------------------------------------------------------
MyThread::~MyThread(void)
{
}
///---------------------------------------------------------------------------------------------
void *MyThread::Entry(void)
{
MyEvent event( wxEVT_TEST,GetId() );
event.SetEventObject( (wxObject *)this->This() );
int p=0;
/*Operacje wykonywane przez wątek*/
while(1)
{
event.SetProgress(p++);
wxSleep(1);
m_pParent->GetEventHandler()->AddPendingEvent( event );
}
return 0;
}
///---------------------------------------------------------------------------------------------
void MyThread::OnExit(void)
{
/*Operacje po zakończeniu wątku*/
}
///---------------------------------------------------------------------------------------------
... a następnie klasę do obsługi eventów:
MyEvent.h
#pragma once
#include <wx/event.h>
#include "MyThread.h"
class MyThread;
class MyEvent: public wxNotifyEvent
{
public:
MyEvent(wxEventType commandType = wxEVT_NULL, int id = 0 );
MyEvent(const MyEvent& event);
virtual ~MyEvent(void);
virtual wxEvent *Clone() const
{ return new MyEvent(*this); }
private:
DECLARE_DYNAMIC_CLASS(MyEvent);
int m_progress;
void SetProgress(int);
int GetProgress(void);
};
typedef void (wxEvtHandler::*MyEventFunction)(MyEvent&);
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE(wxEVT_TEST, wxID_EVENT_TEST)
END_DECLARE_EVENT_TYPES()
#define EVT_TEST(fn) DECLARE_EVENT_TABLE_ENTRY( \
wxEVT_TEST, wxID_ANY, wxID_ANY, (wxObjectEventFunction) (wxEventFunction) \
(MyEventFunction) & fn, (wxObject *) NULL ),
MyEvent.cpp
#include "MyEvent.h"
DEFINE_EVENT_TYPE( wxEVT_TEST )
IMPLEMENT_DYNAMIC_CLASS(MyEvent, wxNotifyEvent)
MyEvent::MyEvent(wxEventType commandType, int id): wxNotifyEvent(commandType, id)
{
}
MyEvent::MyEvent(const MyEvent& event) : wxNotifyEvent(event)
{
}
MyEvent::~MyEvent(void)
{
}
void MyEvent::SetProgress(int progress)
{
m_progress = progress;
}
int MyEvent::GetProgress(void)
{
return m_progress;
}
W oknie głównym rejestruję event z wątku w taki sposób:
BEGIN_EVENT_TABLE(threadsDialog,wxDialog)
EVT_TEST(threadsDialog::OnThreadEvent)
END_EVENT_TABLE()
Start wątku po kliknięciu przycisku - wątek od razu startuje, bo metody startujące wątek są w jego konstruktorze:
this->testThread = new ::MyThread(this);
No i sama funkcja OnThreadEvent:
int progress = event.GetProgress();
wxString p="";
p.sprintf("%d",progress);
this->progressLabel->SetLabel(p);
Co się dzieje? Ano zawsze po 4 iteracjach w wątku jest błąd Acces Violation i program natychmiastowo się wywala. Co tutaj jest nie tak? Jeżeli nie uzywam eventów, to wątek chodzi i wszystko jest OK. Potrzeba mi jednak przekazywac eventy do GUI i jest to jak się zdaje zalecana metoda...
Kompilator to Visual C++ 2008. Próbując skompilować to samo pod GCC na windows (przy użyciu Code::Blocks), mam z kolei błąd:
obj\Release\MyThread.o:MyThread.cpp:(.text+0x2d7)||undefined reference to
__imp__wxEVT_TEST'| obj\Release\threadsMain.o:threadsMain.cpp:(.text+0x210c)||undefined reference to
__imp__wxEVT_TEST'|