Set 102010
 

Quando ho cominciato a utilizzare le wxwidgets per convertire il software che gestsce il mio impianto domotico, precedentemente scritto con il Borland C++ Builder, ho avuto non poche problematiche nel gestire Thread, Socket, ed eventi che permettessero l’interscambio di informazioni fra le varie componenti del programma. Questo breve articolo mostra come creare eventi custom da gestire in un Thread per comunicare con la GUI.

Allo scopo ho preparato un apposito programmino semplice semplice. Esso definisce una basilare interfaccia grafica con soli due pulsanti. Il primo crea un’istanza ad un Thread ed il secondo la rilascia. Il thread stesso è molto semplice in quanto non fà altro che mandare ogni due secondi un messaggio tramite evento custom alla GUI principale che si occuperà di stamparlo in un TextBox. In realtà manda un messaggio iniziale anche all’inizio del codice del thread stesso. Ho preparato anche un archivio con i files in modo che li abbiate già pronti. Sicuramente il progetto completo non funzionerà così com’è, se non altro perchè il percorso delle wxWidgets del mio PC non sarà uguale al vostro, ma affronteremo questa problematica in un prossimo articolo. Per questo motivo vi conviene creare un progetto nuovo e fare “copia e incolla” per ripristinare il rpogetto come all’orignine.

Il pacchetto completo è comunque disponibile qui:Custom events and wxthread (241)

L’esempio è composto da 4 files che sono EventsMain.cpp, EventsMain.h, Thread.cpp e Thread.h oltre a EventssApp.cpp, EventsApp.h e Eventsframe.wxs che gestiscono l’iniziazione dell’applicazione e la descrizione dell’interfaccia grafica. Benchè intuibile, EventsMain rappresenta il thread principale del programma, mentre Thread rappresenta il thread secondario con il relativo evento custom. Come potete vedere di seguito i files Thead.cpp e .h sono davvero ridotti ai minimi termini per cui la comprensione ed eventuale modifica dovrebbero essere molto semplici.

La GUI credo sia altrettanto semplice. Ci sono tutta una serie di linee di codice deputate solo alla gestione dell’interfaccia mentre le due reali funzioni importanti sono la OnButton2Click che crea l’istanza e lancia il thread secondario e la DoMyEvent che riceve i testi da stampare dal thread secondario e li riporta sul TextBox. Si noti che la DoMyEvent riceve due argomenti, uno wxstring ed uno numerico. Se date uno sguardo attento ai listati capirete facilmente come modificare il numero e tipologia di argomenti in base alle vostre esigenze. Faccio notare anche l’importanza dell’istruzione Connect che associa la funzione DoMyEvent alla ricezione degli eventi da parte del Thread secondario. Spero che i listati qui sotto siano sufficientemente chiari da non richedere ulteriori approfondimenti. Nel caso lasciate pure i vostri commenti / domande.

Thread.h

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

DECLARE_EVENT_TYPE( E_MyEvent, -1 )

class C_MyEvent: public wxCommandEvent
{
public:
C_MyEvent( wxEventType commandType = E_MyEvent, int id = 0 ) : wxCommandEvent(commandType, id) { }
wxEvent* Clone() const { return new C_MyEvent(*this); }

wxString GetText() const { return m_Text; }
void SetText( const wxString& text ) { m_Text = text;}

private:
wxString m_Text;
};

typedef void (wxEvtHandler::*MyEventFunction)(C_MyEvent &);
#define MyEventHandler(func) (wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction) wxStaticCastEvent(MyEventFunction, &func)

enum
{
ThreadUserMessage = 1, ThreadError, ThreadDebug, ThreadVerbose, ThreadFailure
};

class CustomThread : public wxThread
{
public:
CustomThread(wxEvtHandler *);
~CustomThread();
void SendThreadMessage(wxString,int);
virtual void * Entry();
protected:
private:
wxEvtHandler * EH;
};

Thread.cpp

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

#include "Thread.h"

DEFINE_EVENT_TYPE(E_MyEvent)

void CustomThread::SendThreadMessage(wxString str,int type)
{
 if(EH)
 {
 C_MyEvent event(E_MyEvent,type);
 event.SetText(str);
 wxPostEvent( EH, event );
 }
}

CustomThread::CustomThread(wxEvtHandler *ev): wxThread(wxTHREAD_JOINABLE)
{
 EH=ev;
}

CustomThread::~CustomThread()
{

}

void* CustomThread::Entry()
{
 SendThreadMessage(_("this is a message from a thread"),ThreadDebug);
 while(1)
 {
 if (TestDestroy())
 {
 break;
 }
 wxThread::Sleep(2000);
 SendThreadMessage(_("this is a message from a thread after 2 seconds"),ThreadUserMessage);
 }
 return(NULL);
}

EventsMain.h

#ifndef EVENTSMAIN_H
#define EVENTSMAIN_H

//(*Headers(EventsFrame)
#include <wx/sizer.h>
#include <wx/textctrl.h>
#include <wx/button.h>
#include <wx/frame.h>
//*)

#include "Thread.h"
class EventsFrame: public wxFrame
{
 public:

 EventsFrame(wxWindow* parent,wxWindowID id = -1);
 virtual ~EventsFrame();

 private:

 //(*Handlers(EventsFrame)
 void OnQuit(wxCommandEvent& event);
 void OnAbout(wxCommandEvent& event);
 void OnButton2Click(wxCommandEvent& event);
 void OnButton1Click(wxCommandEvent& event);
 //*)

 //(*Identifiers(EventsFrame)
 static const long ID_BUTTON2;
 static const long ID_BUTTON1;
 static const long ID_TEXTCTRL1;
 //*)

 //(*Declarations(EventsFrame)
 wxButton* Button1;
 wxButton* Button2;
 wxTextCtrl* TextCtrl1;
 //*)
 void DoMyEvent(C_MyEvent &event );

 DECLARE_EVENT_TABLE()
};

#endif // EVENTSMAIN_H

EventsMain.cpp

#include "EventsMain.h"
//(*InternalHeaders(EventsFrame)
#include <wx/intl.h>
#include <wx/string.h>
//*)

//helper functions
enum wxbuildinfoformat {
short_f, long_f };

wxString wxbuildinfo(wxbuildinfoformat format)
{
wxString wxbuild(wxVERSION_STRING);

if (format == long_f )
{
#if defined(__WXMSW__)
wxbuild << _T("-Windows");
#elif defined(__UNIX__)
wxbuild << _T("-Linux");
#endif

#if wxUSE_UNICODE
wxbuild << _T("-Unicode build");
#else
wxbuild << _T("-ANSI build");
#endif // wxUSE_UNICODE
}

return wxbuild;
}

//(*IdInit(EventsFrame)
const long EventsFrame::ID_BUTTON2 = wxNewId();
const long EventsFrame::ID_BUTTON1 = wxNewId();
const long EventsFrame::ID_TEXTCTRL1 = wxNewId();
//*)

CustomThread * MyThread=NULL;

BEGIN_EVENT_TABLE(EventsFrame,wxFrame)
//(*EventTable(EventsFrame)
//*)
END_EVENT_TABLE()

EventsFrame::EventsFrame(wxWindow* parent,wxWindowID id)
{
//(*Initialize(EventsFrame)
wxFlexGridSizer* FlexGridSizer2;
wxBoxSizer* BoxSizer1;
wxFlexGridSizer* FlexGridSizer1;

Create(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("id"));
SetClientSize(wxSize(219,176));
BoxSizer1 = new wxBoxSizer(wxVERTICAL);
FlexGridSizer2 = new wxFlexGridSizer(2, 1, 0, 0);
FlexGridSizer1 = new wxFlexGridSizer(0, 2, 0, 0);
Button2 = new wxButton(this, ID_BUTTON2, _("START"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON2"));
FlexGridSizer1->Add(Button2, 1, wxALL|wxALIGN_LEFT|wxALIGN_BOTTOM, 5);
Button1 = new wxButton(this, ID_BUTTON1, _("STOP"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON1"));
Button1->Disable();
FlexGridSizer1->Add(Button1, 1, wxALL|wxALIGN_LEFT|wxALIGN_BOTTOM, 5);
FlexGridSizer2->Add(FlexGridSizer1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5);
TextCtrl1 = new wxTextCtrl(this, ID_TEXTCTRL1, wxEmptyString, wxDefaultPosition, wxSize(284,155), wxTE_MULTILINE, wxDefaultValidator, _T("ID_TEXTCTRL1"));
FlexGridSizer2->Add(TextCtrl1, 1, wxALL|wxALIGN_LEFT|wxALIGN_BOTTOM, 5);
BoxSizer1->Add(FlexGridSizer2, 1, wxALL|wxALIGN_LEFT|wxALIGN_BOTTOM, 5);
SetSizer(BoxSizer1);
BoxSizer1->SetSizeHints(this);

Connect(ID_BUTTON2,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&EventsFrame::OnButton2Click);
Connect(ID_BUTTON1,wxEVT_COMMAND_BUTTON_CLICKED,(wxObjectEventFunction)&EventsFrame::OnButton1Click);
//*)

Connect( wxID_ANY, E_MyEvent,MyEventHandler(EventsFrame::DoMyEvent), NULL, this );
}

EventsFrame::~EventsFrame()
{
if(MyThread) MyThread->Delete();
}

void EventsFrame::OnQuit(wxCommandEvent& event)
{
Close();
}

void EventsFrame::OnButton2Click(wxCommandEvent& event)
{
MyThread=new CustomThread(GetEventHandler());
if(!MyThread) {TextCtrl1->AppendText(_("Error creating thread"));return;}
if(MyThread->Create((unsigned int)0)!=wxTHREAD_NO_ERROR) // lancia il thread che parte in modalità “suspended”. Lo 0 indica di usare lo stack di default
{
delete MyThread;
return;
}
MyThread->SetPriority(75); // setta la priorità del thread  (da 0 a 100 dove lo 0 indica la priorità minore.
MyThread->Run(); // questo lancia il thread*/
TextCtrl1->AppendText(_("Thread is running...\n"));
Button2->Enable(false);
Button1->Enable(true);
}

void EventsFrame::DoMyEvent(C_MyEvent &event )
{
int ID=event.GetId();
wxString MsgID=wxString::Format(wxT("%d"),ID);
wxString Msg=event.GetText();
TextCtrl1->AppendText(_("ID :")+MsgID+_(" - Message: ")+Msg+_("\n"));
if(ID==ThreadFailure) TextCtrl1->AppendText(_("F A I L U R E\n"));
}

void EventsFrame::OnButton1Click(wxCommandEvent& event)
{
if(MyThread) MyThread->Delete();
delete MyThread;
MyThread=NULL;
Button2->Enable(true);
Button1->Enable(false);
}

  No Responses to “Creare eventi personalizzati in un thread”