Раздел «Язык Си».ClassGUI:

Приложение wxWidgets

Оконные приложения несколько отличаются от консольных программ.

Каждое оконное приложение работает с событиями, которые могут происходить со всеми его частями и устройствами.

Для обработки этих событий включается цикл обработки событий, который работает все время пока запущено приложение. Некоторые события приложение может отслеживать и обрабатывать.

Для работы с приложениями уже имеется достаточно большое количество классов, описывающее работу тех или иных устройств окна.

Рассмотрим пример самого простого приложения

#include <wx/wx.h>

// Уже имеется абстрактный класс wxApp для создания  
// приложения
// наше приложение будет наследником этого wxApp
// виртуальную функцию OnInit необходимо определить в нашем классе
class Begin: public wxApp{
    public:
        virtual bool OnInit();
};

// Макрос для запуска приложения (вместо main)
// В скобках указыаем как называется наш класс-приложение
IMPLEMENT_APP(Begin)

// переопределяем виртуальную функцию OnInit
// именно она и запускает приложение
bool Begin::OnInit()
{
// создаем динамический объект класса wxFrame (наше окно)
//окно будет "пустое" размером 200х200 пикселов
  wxFrame *wind=new wxFrame(NULL,wxID_ANY,wxT("Begin.."),wxDefaultPosition,wxSize(200,200));
// запуск. Окно будет видимое
  wind->Show(true);
  return true;
};

Расширим возможности нашего окна. Добавим в него меню.

#include <wx/wx.h>
// Класс нашео приложения
class Begin: public wxApp{
    public:
        virtual bool OnInit();
};

// Окошко было пустое. Поэтому расширим фрейм.
// Для этого создадим класс-наследник MyWin

class MyWin:public wxFrame{
// Добавим элементы:

    wxMenuBar *menubar; // полоска для меню
    wxMenu *file; // менюшка  на полоске
    wxStatusBar *sb; // статус-бар 
// Пункты меню для открытия файла и 
// закрытия приложения  
    wxMenuItem *load,*quit;   
    wxString ss; // строка (пригодится)

 public:
// конструктор с заголовком окна
    MyWin(const wxString& title);

// Функции, которые будут вызываться при выборе пунктов меню
    // Функция закрытия окна
    void OnQuit(wxCommandEvent& event);
// функция загрузки файла
    void OnLoad(wxCommandEvent& event);
}; 
// Идентификаторы нужны всем элементам, которые будут обрабатываться обработчиками событий 
// идентификатор пунткта меню
const int ID_MENU_LOAD =1002;

MyWin::MyWin(const wxString& title):wxFrame(NULL,wxID_ANY,title,wxDefaultPosition,wxSize(200,200)){
  // создали полоску для менюшки
    menubar = new wxMenuBar;
 // создали менюшку
    file = new wxMenu;
    quit = new wxMenuItem(file, wxID_EXIT, wxT("&Quit"));
    load = new wxMenuItem(file, ID_MENU_LOAD, wxT("&Load"));
    file->Append(load);
    file->Append(quit);
// закинули менюшку на полоску
    menubar->Append(file, wxT("&File"));
 // установили полоску в окно
    SetMenuBar(menubar);

// Connect служит для соединения элемента приложения с обработчиком событий
// подключили менюшку
// wxID_EXIT - стандартный идентификатор для выключения приложения,
// wxEVT_COMMAND_MENU_SELECTED - идентификатор действия (выбор пункта меню)
// wxCommandEventHandler - обработчик событий, связанных с командами: меню, кнопки...
// MyWin::OnQuit - функция, которая будет вызываться при этом событиии
  Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyWin::OnQuit));
// ID_MENU_LOAD - не стандартный идентификатор (сами определяли)
// MyWin::OnLoad - функция, которая будет вызываться при этом событиии
   Connect(ID_MENU_LOAD, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyWin::OnLoad));

    sb=CreateStatusBar();
    sb->SetStatusText(wxString(wxT("что-то напишем и здесь")));
};

// Функция выключения окна
// Параметр - объект класса wxCommandEvent
// Можно использовать его методы
// Но здесь не нужно
void MyWin::OnQuit(wxCommandEvent& event){
  Close(true);
};

// Функция для загрузки
void MyWin::OnLoad(wxCommandEvent& event){
// Ничего пока не происходит 
// просто будет сообщение
   wxMessageBox(wxT("Нажали"),wxT("Load"));
 };

IMPLEMENT_APP(Begin)

bool Begin::OnInit()
{
  MyWin *wind=new MyWin(wxT("Begin.."));
  wind->Show(true);
  return true;
};

А теперь попробуем написать что-то разумное

Это приложение будет открывать файл с координатами прямоугольника, показывать их в текстовом окне и рисовать этот прямоугольник

#include <wx/wx.h>
#include <wx/file.h>
#include <wx/wfstream.h>
#include <wx/txtstrm.h>
#include <wx/sstream.h>
#include <wx/string.h>


class Begin: public wxApp{
    public:
        virtual bool OnInit();
};
// Нужен еще класс Draw
class Draw;
class MyWin;

class MyWin:public wxFrame{
    wxMenuBar *menubar; // полоска для меню
    wxMenu *file,*im; // менюшка  на полоске
    wxMenuItem *load,*quit; // открывалка  файла
    wxTextCtrl *tc; // текстовое окошко
// Указатель на объект Draw  
    Draw *dp;
// панель 
// если элемнты помещать сразу на фрейм, то первый же объект
// займет весь фрейм
// для различных элементов есть wxPanel 
    wxPanel *m_pan;
// Это в "подвале" окошка
    wxStatusBar *sb; // статус бар
// строка (для всех типов кодировки)
    wxString ss;
 public:
//  конструктор
    MyWin(const wxString& title);
// две координаты для рисования     
     wxPoint a;
    wxPoint b;
    // Функция закрытия окна
    void OnQuit(wxCommandEvent& event);
// Загрузка файла с данными
    void OnLoad(wxCommandEvent& event);
}; 

// класс для рисования
// наследник wxPanel
class Draw: public wxPanel{
// указатель на верхнее окно
// это нужно для доступа к элементам (к a и b)
    MyWin *mn; 
  public:
// в конструкторе указывается адрес объекта, который ее содержит
        Draw(wxPanel *parent, MyWin *main);
 // рисовалка
        void OnPaint(wxPaintEvent & event);
};
//иднтификаторы 
// загрузка
const int ID_MENU_LOAD =1002;
// редактирование
const int ID_MENU_EDIT =1003;

// Конструктор фрейма
MyWin::MyWin(const wxString& title):wxFrame(NULL,wxID_ANY,title,wxDefaultPosition,wxSize(400,400)){
  // создали полоску для менюшки
    menubar = new wxMenuBar;
// создали менюшку
    file = new wxMenu;

// закинули менюшку на полоску
// пока не работает (для "красоты")
   
    file->Append(wxID_ANY, wxT("&New"));
    
    file->AppendSeparator();
// Для всех пунктор меню указывем идентификатор чтобы
// связать обработчик событие с конкретным элементом
// добавили к менюшке раздел quit
    quit = new wxMenuItem(file, wxID_EXIT, wxT("&Quit"));
// добавили раздел load
    load = new wxMenuItem(file, ID_MENU_LOAD, wxT("&Load"));
    file->Append(load);
    file->Append(quit);
// Это еще один пункт меню
// пока тоже не работает
     im = new wxMenu;
     im->Append(wxID_ANY, wxT("Edit figure"));
     im->Append(wxID_ANY, wxT("Rotate 90"));
// добавили оба пункта меню на полску меню    
     menubar->Append(file, wxT("&File"));
     menubar->Append(im, wxT("&Edit"));

 // установили полоску в окно
    SetMenuBar(menubar);
// подключили менюшку exit
  Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyWin::OnQuit));
// подключили load (идентификаторы такие же как и при создании каждого элемента)
   Connect(ID_MENU_LOAD, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyWin::OnLoad));

// создание панельки для текста, кнопок и рисовалки
    m_pan= new wxPanel(this,wxID_ANY);
// это тоже панель, но наша.
// помещаем ее нна панель m_pan и задаем указатель на главный фрейм
    dp=new Draw(m_pan,this);
// это окошко для текста. Тоже помещаем на m_pan
    tc =  new wxTextCtrl(m_pan, -1, wxT(""), wxPoint(200, 10),wxSize(200,50)); 
// статус-бар будет внизу окна
    sb=CreateStatusBar();
    sb->SetStatusText(wxString(wxT("что-то напишем и здесь")));
};


void MyWin::OnLoad(wxCommandEvent& event){
// специальный класс для листания файлов 
  wxFileDialog * openFileDialog = new wxFileDialog(this);
// если все открывается, выберем имя файла
// Только имя!!!
  if (openFileDialog->ShowModal() == wxID_OK){
// Что выбрали, то и будет именем файла
// Запоминаем в строку
      wxString fileName = openFileDialog->GetPath();
// Загружаем содержимое в окно текста
      tc->LoadFile(fileName);
// Теперь нужно получить данные из файла
// Создаем объект - файловый поток
      wxFileInputStream input(fileName);
// Чтобы он работал как текстовый файл, превращаем его в поток-текст
      wxTextInputStream intext(input);
    int  x1,y1,x2,y2; 
// "обычным" образом считываем данные
      intext>>x1>>y1>>x2>>y2;
// Это были координаты
      a.x = x1;
      a.y = y1;
      b.x = x2;
      b.y = y2;
// А еще можно сделать поток из строки
// но к работе окна это отношения не имеет
      wxString a;
// В строку записали текст
      a<<wxT("123 17 89 12.5");
// превратили в поток
     wxStringInputStream st(a);
// превратили в поток-текст
     wxTextInputStream in(st);
      int a1,a2,a3;
      float w;
// получили данные
      in>>a1>>a2>>a3>>w;
// Это нужно для перерисвки окна, когда будем рисовать фигуру
      dp->Refresh();
  }

};

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

// Конструктор нашего Draw
Draw::Draw(wxPanel *parent, MyWin *fr):wxPanel(parent, -1,wxPoint(50,50),wxSize(100,100),wxBORDER_SUNKEN){
// подключили панель к событиям рисования
    Connect(wxEVT_PAINT,wxPaintEventHandler(Draw::OnPaint));
    mn = fr;
};

// Свободная функция рисования прямоугольника
// Для рисования есть абстрактный класс wxDC
// У него много разных наследников
// Самый простой wxPaintDC
// Но мы сделаем функцию для всех наследников wxDC
void DrRec(wxPoint a, wxPoint b, wxDC * dc){
    dc->DrawRectangle(a, wxSize (abs(a.x-b.x),abs(a.y-b.y)));
};

// Функция OnPaint() сработает при любом рисовании окна:
// первое рисование, сложил-разложили
// или вызов функции Refresh()

void Draw::OnPaint(wxPaintEvent& event){
// положили планшет wxPaintDC на нашу панель
   wxPaintDC dc(this);
// Установили цвет заливки
   dc.SetBrush(wxBrush(wxColour(255,0,0)));
// передали свободной функции координаты и указатель на планшет
   DrRec(mn->a,mn->b,&dc);
};

// запуск окна
IMPLEMENT_APP(Begin)

bool Begin::OnInit()
{
  MyWin *wind=new MyWin(wxT("Begin.."));
  wind->Show(true);
  return true;
};

События времени и мышь

#include <wx/wx.h>
#include <wx/dcbuffer.h>
//#include <fstream.h>
#include <cstdlib>
#include <string>


class MyApp : public wxApp
{
  public:
    virtual bool OnInit();
};
/*

Класс описывает окно, которое появится и будет демонстрироваться в приложении

В окне будет двигаться кружок, в поле статус будут отображаться координаты 
центра кружка.
Щелчок мыши перемещает кружок на новую позицию в окне.


*/

class Move : public wxFrame
{
     int x,y,mx,my,z,np; // координаты центра шарика
     wxTimer *tm;        // объект таймер
     wxStatusBar *m_stsbar; //часть окна "статус-бар"
          // файл для вывода log-информации
     wxString sz,st;
   // string st;
public:
  Move(const wxString& title);  // конструктор окна

  void OnPaint(wxPaintEvent & event);  // рисование содержимого окна
                                       // вызывается, когда происходит перерисовка 
  void OnTimer(wxCommandEvent& event); // изменение параметров при истечении 
                                       // момента времени
  void OnMouse(wxMouseEvent& event);  // вызывается при обработке сигнала от мыши
  
  void PaintBackground(wxDC& dc);   // рисование фона
  void OnEraseBackground(wxEraseEvent& event);  // очистка фона
  
};

// конструктор окна

Move::Move(const wxString& title)
       : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(1000, 1000))
{
 // объявление функции, с помощью которой будет обрабатываться событие отрисовки  
  this->Connect(wxEVT_PAINT, wxPaintEventHandler(Move::OnPaint));
 // объявление функции, с помощью которой будет обрабатываться событие таймера 
  this->Connect(wxEVT_TIMER, wxCommandEventHandler(Move::OnTimer));
 // функция, с помощью которой будет обрабатываться событие мыши 
 // (нажатие на левую кнопку)
 
  this->Connect(wxEVT_LEFT_DOWN,wxMouseEventHandler(Move::OnMouse));
  this->Centre();
 // f.open("t.dat");
//  f1.open("log.dat",std::ios::out);

  x=40;
  y=40;
  z=40;
  np=1;
  mx=0;
  my=0;
  // установка таймера
  tm=new wxTimer(this, 1);
 m_stsbar = CreateStatusBar();
  
  m_stsbar->SetStatusText(wxT("0"));
  };
// рисование линии 
// передаем ссылку на планшет
// и используем функции класса wxDC
void Line(wxPoint a, wxPoint b, wxDC& d){
    d.DrawLine(a,b);
}  
// рисование (происходит каждый раз, когда нужно перерисовать окно)

void Move::OnPaint(wxPaintEvent& event)
{
  // локальный объект - панель для рисования 
 wxPaintDC dc(this);
 // установка промежутка времени
  tm->Start(70);
   Line(wxPoint(20,0),wxPoint(20,100),dc);
// установка параметров кисточки    
 
    dc.SetBrush(wxBrush(wxColour(255,100,0)));
 // установка параметров обводки   
    dc.SetPen(wxPen(wxColor(100,0,100),6));

 // рисование шарика   (функции класса wxDC) 
    dc.DrawCircle(x,y,z);
  // организация надписи для помещения на статус-бар (просто строка текста)    
     wxString str;
     str<<wxT("n=")<<np<<wxT("x=")<<x<<wxT("y=")<<y<<wxT("mx=")<<mx<<wxT("my=")<<my;
  // вывод строки в файл - loc  
//     f1<<(str.c_str());
   // помещение на статус бар  
     m_stsbar->SetStatusText(str); 
   
}

void Move::PaintBackground(wxDC& dc)
{
    wxColour backgroundColour = GetBackgroundColour();
    if (!backgroundColour.Ok())
        backgroundColour =
            wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);

    dc.SetBrush(wxBrush(backgroundColour));
    dc.SetPen(wxPen(backgroundColour, 1));

    wxRect windowRect(wxPoint(0, 0), GetClientSize());    

     dc.DrawRectangle(windowRect);
};

// Empty implementation, to prevent flicker
void Move::OnEraseBackground(wxEraseEvent& event)
{
    wxMemoryDC mdc;
};

// таймер. происходит каждый раз, когда истекло установленное время
void Move::OnTimer(wxCommandEvent& event){
 // смена направления движения   
    if (x>=960)  np=0;   
    if (x<=20)    np=1;
   // изменение координаты кружка 
    if(np)x+=20;
     if(!np) x-=20;
  // остановка таймера (при перерисовке запустится снова)   
    tm->Stop();
   // директивная перерисовка 
      Refresh();
      Update();
};

// мышь. вызывается каждый раз, когда происходит события мыши
void Move::OnMouse(wxMouseEvent &event){
    // получение координат мыши
    mx=event.GetX();
    my=event.GetY();
    x=mx;
    y=my;
};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{

    Move *mv = new Move(wxT("Move on Test"));
    mv->Show(true);

    return true;
}

REFACTOR Задачи

REFACTOR Задача 1

В окне размером 400x400 нарисовать 9 клеток как для игры в крестики-нолики Загрузить файл и нарисовать все

REFACTOR Задача 2.

Для предыдущей задачи написать функции Coss - крестик и Null - нолик, которые рисуют в окне крестик и нолик. Крестик и нолик должны помещаться в клетку (предыдущая задача)

REFACTOR Задача 3*

Крестик и нолик рисуются по-очереди в той клетке, куда ткнули мышкой

REFACTOR Задача 4*

Написать игру крестики-нолики.

Документация wxWidgwets.pdf, docs.wxwidgets.org, zetcode.com

-- TatyanaOvsyannikova2011 - 22 Apr 2016