Tartalomjegyzék

< wxWidgets kézikönyv

Események

Az események minden grafikus felhasználói felülettel (GUI-val) rendelkező program része. Minden GUI program eseményeken keresztül van vezérelve. Egy alkalmazás reagál a különböző eseménytípusokra, amely a futása során generálódnak. Az eseményeket főleg a felhasználó generálja. De az események máshonnan is származhatnak. Például a internet kapcsolat, ablakkezelő, időzítő, stb. Amikor egy alkalmazás elindul egy fő eseményhurok keletkezik. Az alkalmazások a fő eseményhurkon ülnek és várják a létrejövő eseményeket. A fő eseményhurok megszűnik, amikor kilépünk az alkalmazásból.

Definíciók

Az események az alkalmazások szintjén az információcsere alapvető eszközei. A GUI alkalmazás tipikusan ilyen. Az eseményhurok egy programozói eszköz az események és üzenetek figyelésére egy programban. Az esemény hurok folyamatosan, újra és újra feldolgozza az eseményeket. A program diszpécsere összeköti az eseményeket az eseménykezelőkkel. Az eseménykezelők metódusok, amelyek reagálnak az eseményekre.

Az esemény objektum az eseményt kiváltó objektum. Ez alap szinten egy ablak. Az esemény típusa elárulja, hogyan keletkezett.

Egy egyszerű esemény példa

Az események kezelésének hagyományos módja a wxWidgetsben a statikus eseménytáblák használata. Ez az MFC hatása volt. A sok rugalmasságot nyújtó modern módszer a Connect() metódus használata. Mivel ez a módszer jobb mint az eseménytábla, ezért wxWidgets oktatóanyagban ezt fogjuk használni.

Eseménytábla

A következő példában az eseménytábla használatát mutatjuk be.

button.h
#include <wx/wx.h>
 
class MyButton : public wxFrame
{
  public:
      MyButton(const wxString& title);
 
      void OnQuit(wxCommandEvent& event);
 
  private:
      DECLARE_EVENT_TABLE()
 
};
button.cpp
#include "button.h"
 
MyButton::MyButton(const wxString& title)
         : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(270, 150))
{
 
    wxPanel *panel = new wxPanel(this, wxID_ANY);
    wxButton *button = new wxButton(panel, wxID_EXIT, 
        wxT("Quit"), wxPoint(20, 20));
 
    Centre();
 }
 
void MyButton::OnQuit(wxCommandEvent& WXUNUSED(event))
{
    Close(true);
}
 
BEGIN_EVENT_TABLE(MyButton, wxFrame)
      EVT_BUTTON(wxID_EXIT,  MyButton::OnQuit)
END_EVENT_TABLE()
main.h
#include <wx/wx.h>
 
class MyApp : public wxApp
{
    public:
      virtual bool OnInit();
};
main.cpp
#include "main.h"
#include "button.h"
 
IMPLEMENT_APP(MyApp)
 
bool MyApp::OnInit()
{
 
      MyButton *button = new MyButton(wxT("Button"));
      button->Show(true);
 
      return true;
}

A példánkban készítünk egy egyszerű nyomógombot. A nyomgombra klikkelés bezárja az alkalmazást.

  private:
      DECLARE_EVENT_TABLE()

A header fájlba deklaráljuk egy eseménytáblát a DECLARE_EVENT_TABLE() makróval.

  BEGIN_EVENT_TABLE(MyButton, wxFrame)
      EVT_BUTTON(wxID_EXIT,  MyButton::OnQuit)
  END_EVENT_TABLE()

Meghatároztunk egy eseménytáblát minden tagfüggvény számára.

Példa a Connect() használatára

Ebben a részben a mozgásos eseményekről lesz szó. A mozgásos események információkat tartalmaznak a változásokról. Egy mozgásos esemény akkor generálódik, amikor egy ablakot pozicionálunk. A mozgásos eseményeket a wxMoveEvent osztály tartalmazza. A wxEVT_MOVE egy eseménytípus.

move.h
#include <wx/wx.h>
 
class Move : public wxFrame
{
public:
    Move(const wxString& title);
 
    void OnMove(wxMoveEvent & event);
 
    wxStaticText *st1;
    wxStaticText *st2;
 
};
move.cpp
#include "move.h"
 
Move::Move(const wxString& title)
       : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
{
    wxPanel *panel = new wxPanel(this, -1);
 
    st1 = new wxStaticText(panel, -1, wxT(""), wxPoint(10, 10));
    st2 = new wxStaticText(panel, -1, wxT(""), wxPoint(10, 30));
 
    Connect(wxEVT_MOVE, wxMoveEventHandler(Move::OnMove));
 
    Centre();
}
 
void Move::OnMove(wxMoveEvent& event)
{
    wxPoint size = event.GetPosition();
    st1->SetLabel(wxString::Format(wxT("x: %d"), size.x ));
    st2->SetLabel(wxString::Format(wxT("y: %d"), size.y ));
}
main.h
#include <wx/wx.h>
 
class MyApp : public wxApp
{
    public:
      virtual bool OnInit();
};
main.cpp
#include "main.h"
#include "move.h"
 
IMPLEMENT_APP(MyApp)
 
bool MyApp::OnInit()
{
      Move *move = new Move(wxT("Move event"));
      move->Show(true);
 
      return true;
}

A példában az aktuális ablakpozíciót látjuk.

Connect(wxEVT_MOVE, wxMoveEventHandler(Move::OnMove));

Itt kapcsolódunk egy wxEVT_MOVE eseménytípushoz az OnMove() metódussal.

wxPoint size = event.GetPosition();

Az esemény paraméter az OnMove() metódusban egy objektum az aktuális eseményhez. Esetünkben ez a wxMoveEvent osztály. Ez az objektum tartalmaz információkat az eseményről. Megkaphatjuk az aktuális kurzorpozíciót az esemény GetPosition() metódusával.

Move event

Figure: Move event

Eseményszaporítás

Kétféle esemény létezik. Az alapesemények és a parancsesemények. A parancsesemények gyermek, szülő és nagyszülő komponensek között is terjednek, egymáshoz eljutnak. Az alapesemények, mint például a wxCloseEvent nem. Ezek nem jutnak el más komponensek felé.

Alapértelmezésként ha egy eseménykezelő elkap egy eseményt, az eseményfigyelés leáll. Az eseményfigyelés újraindítása a Skip() metódus hívásával lehetséges.

propagate.h
#include <wx/wx.h>
 
class Propagate : public wxFrame
{
public:
    Propagate(const wxString& title);
 
    void OnClick(wxCommandEvent& event);
 
};
 
 
class MyPanel : public wxPanel
{
public: 
    MyPanel(wxFrame *frame, int id);
 
    void OnClick(wxCommandEvent& event);
};
 
 
class MyButton : wxButton
{
public:
    MyButton(MyPanel *panel, int id, const wxString &label);
 
    void OnClick(wxCommandEvent& event);
 
};
propagate.cpp
#include <iostream>
#include "propagate.h"
 
const int ID_BUTTON = 1;
 
Propagate::Propagate(const wxString& title)
      : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
{
    MyPanel *panel = new MyPanel(this, -1);
 
    new MyButton(panel, ID_BUTTON, wxT("Ok"));
 
    Connect(ID_BUTTON, wxEVT_COMMAND_BUTTON_CLICKED, 
        wxCommandEventHandler(Propagate::OnClick));
 
    Centre();
}
 
 
void Propagate::OnClick(wxCommandEvent& event) 
{
    std::cout << "event reached frame class" << std::endl;
    event.Skip();
}
 
 
MyPanel::MyPanel(wxFrame *frame, int id)
      : wxPanel(frame, id)
{
    Connect(ID_BUTTON, wxEVT_COMMAND_BUTTON_CLICKED, 
        wxCommandEventHandler(MyPanel::OnClick));
} 
 
void MyPanel::OnClick(wxCommandEvent& event) 
{
    std::cout << "event reached panel class" << std::endl;
    event.Skip();
}
 
 
MyButton::MyButton(MyPanel *mypanel, int id, const wxString& label)
      : wxButton(mypanel, id, label, wxPoint(15, 15))
{
    Connect(ID_BUTTON, wxEVT_COMMAND_BUTTON_CLICKED, 
        wxCommandEventHandler(MyButton::OnClick));
} 
 
 
void MyButton::OnClick(wxCommandEvent& event) 
{
    std::cout << "event reached button class" << std::endl;
    event.Skip();
}
main.h
#include <wx/wx.h>
 
class MyApp : public wxApp
{
    public:
      virtual bool OnInit();
};
main.cpp
#include "main.h"
#include "propagate.h"
 
IMPLEMENT_APP(MyApp)
 
bool MyApp::OnInit()
{
 
      Propagate *prop = new Propagate(wxT("Propagate"));
      prop->Show(true);
 
      return true;
}

A példánkban van egy gomb, egy panelon. A panel a frame komponensen van. Minden komponens részére definiálunk egy kezelőt.

event reached button class
event reached panel class
event reached frame class

Ezt kapjuk, amikor kattintunk egy gombra. Az esemény elindul a gombtól a panelhez és a frame-hez. Próbáld meg kihagyni több Skip() metódust és nézd, mi történik.

Vetoing events

Néha szükség lehet egy eseményfeldolgozás leállítására. Erre a célra használjuk a Veto() metódust.

veto.h
#include <wx/wx.h>
 
class Veto : public wxFrame
{
public:
    Veto(const wxString& title);
 
    void OnClose(wxCloseEvent& event);
 
};
veto.cpp
#include "veto.h"
 
 
Veto::Veto(const wxString& title)
      : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
{
 
    Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(Veto::OnClose));
    Centre();
}
 
void Veto::OnClose(wxCloseEvent& event) 
{
    wxMessageDialog *dial = new wxMessageDialog(NULL,
        wxT("Are you sure to quit?"), wxT("Question"),
        wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
 
    int ret = dial->ShowModal();
    dial->Destroy();
 
    if (ret == wxID_YES) {
        Destroy();
    } else {
        event.Veto();
    }
 
}
main.h
#include <wx/wx.h>
 
class MyApp : public wxApp
{
    public:
      virtual bool OnInit();
};
main.cpp
#include "main.h"
#include "veto.h"
 
IMPLEMENT_APP(MyApp)
 
bool MyApp::OnInit()
{
 
      Veto *veto = new Veto(wxT("Veto"));
      veto->Show(true);
 
      return true;
}

A példánkban a wxCloseEvent eseményt dolgozzuk fel. Ez az esemény akkor következik be, amikor kattintunk az X gombra címsorban, vagy Alt+F4, vagy a Rendszermenüből a Bezárás menüpontot választjuk. Néhány alkalmazásban szeretnénk megakadályozni az ablak véletlen bezárását. Ehhez a wxEVT_CLOSE_WINDOW eseménytípushoz fogunk kapcsolódni.

wxMessageDialog *dial = new wxMessageDialog(NULL, 
    wxT("Are you sure to quit?"), wxT("Question"),
    wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);

A close esemény bekövetkezésére feldobunk egy üzenetablakot.

if (ret == wxID_YES) {
    Destroy();
} else {
    event.Veto();
}

A visszatérési értéktől függően vagy megsemmisítjük az ablakot, vagy elvetjük az eseményt. Jegyezd meg, hogy az ablak bezárásához a Destroy() metódust kell meghívnunk. A Close() metódus meghívásával befejezhetjük egy végtelen ciklusban.

Ablak azonosítók

Az ablakazonosítók egyedileg meghatározott egész számok, amelyek egy ablakot azonosítanak az események rendszerében. Az ablakazonosítók három módon hozhatók létre.

Minden widget rendelkezik egy azonosító paraméterrel. Ez egy egyedi azonosító az események rendszerében. Ha több widget-el dolgozunk, különbözniük kell.

wxButton(parent, -1)
wxButton(parent, wxID_ANY)

Ha -1-t vagy wxID_ANY-t adunk meg azonosítónak paraméterként, akkor a wxWidgets automatikusan készít egy azonosítót. Az automatikusan kreált azonosítók mindig negatív számok, így a felhasználó által megadott számoknak mindig pozitívnak kell lenniük. Ezt a lehetőséget rendszeresen használhatjuk, ha a widget állapota nem változik. Például egy statikus szöveg (StaticText), általában sosem változik az alkalmazás élete alatt. Az azonosítót ha szeretnénk megkaphatjuk. Erre a GetId() metódus áll rendelkezésre.

Ha csak lehetséges használjunk szabványos azonosítót. Az azonosítók bizonyos szabványos grafikákat és viselkedéseket biztosítanak egyes platformokon.

ident.h
include <wx/wx.h>
 
class Ident : public wxFrame
{  
  public:
    Ident(const wxString& title);
 
};
ident.cpp
#include "ident.h"
 
Ident::Ident(const wxString& title)
      : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(200, 150))
{
 
    wxPanel *panel = new wxPanel(this, -1);
 
    wxGridSizer *grid = new wxGridSizer(2, 3);
 
    grid->Add(new wxButton(panel, wxID_CANCEL), 0, wxTOP | wxLEFT, 9);
    grid->Add(new wxButton(panel, wxID_DELETE), 0, wxTOP, 9);
    grid->Add(new wxButton(panel, wxID_SAVE), 0, wxLEFT, 9);
    grid->Add(new wxButton(panel, wxID_EXIT));
    grid->Add(new wxButton(panel, wxID_STOP), 0, wxLEFT, 9);
    grid->Add(new wxButton(panel, wxID_NEW));
 
    panel->SetSizer(grid);
    Centre();
}
main.h
#include <wx/wx.h>
 
class MyApp : public wxApp
{
    public:
      virtual bool OnInit();
};
main.cpp
#include "main.h"
#include "ident.h"
 
IMPLEMENT_APP(MyApp)
 
bool MyApp::OnInit()
{
 
      Ident *ident = new Ident(wxT("Identifiers"));
      ident->Show(true);
 
      return true;
}

A példánkban szabványos azonosítókat használunk a gombok számára. Linuxon, a gombok kis ikonokkal rendelkeznek.