Приложение 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;
}
Задачи
Задача 1
В окне размером 400x400 нарисовать 9 клеток как для игры в крестики-нолики
Загрузить файл и нарисовать все
Задача 2.
Для предыдущей задачи написать функции
Coss - крестик и
Null - нолик, которые рисуют в окне крестик и нолик. Крестик и нолик должны помещаться в клетку (предыдущая задача)
Задача 3*
Крестик и нолик рисуются по-очереди в той клетке, куда ткнули мышкой
Задача 4*
Написать игру крестики-нолики.
Документация
wxWidgwets.pdf,
docs.wxwidgets.org,
zetcode.com
--
TatyanaOvsyannikova2011 - 22 Apr 2016