Sl-Alex домашняя лаборатория

Руководство по WxWidgets: 11. Самодельные виджеты

Просмотров: 2316Комментарии: 0
Статьи
Руководство по WxWidgets: 11. Самодельные виджеты

Вы когда-нибудь задумывались, глядя на интерфейс приложения, как был создан данный GUI объект? Вероятно каждый, кто увлечен программированием задумывался. Затем он смотрел на список виджетов, предоставляемых его любимой GUI библиотекой. Но не мог найти ничего подобного. Обычно тулкиты предоставляют только самые общие виджеты, такие как кнопки, текстовые виджеты, слайдеры и т.д. Ни один тулкит не может предоставить всех возможных виджетов.

Вообще существует два типа тулкитов: спартанские и тяжеловесные. FLTK тулкит является спартанским. Он предоставляет только базовые виджеты и подразумевает, что программист создаст недостающие сам. wxWidget является тяжеловесным тулкитом. Он предоставляет большое количество виджетов. Однако, даже он не предоставляет более специализированных виджетов. Например, виджет измерения скорости, или виджет, который измеряет вместимость CD для записи (см. Nero). Тулкит также не содержит графиков и диаграмм.

Программисты должны создавать такие виджеты сами. Это делается с помощью инструментов для рисования, предоставляемых тулкитом. Существует два варианта. Программист может изменить или дополнить существующий виджет. Или же он может создать самодельный виджет с нуля.

Здесь я предполагаю, что вы прочитали предыдущую главу, о контекстах устройств.

Виджет прожига

Это пример виджета созданного с нуля. Этот виджет встречается в различных приложения для прожига дисков, таких как Nero Burning ROM.

widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <wx/wx.h>
class Widget : public wxPanel
{
public:
	Widget(wxPanel *parent, int id );
	wxPanel *m_parent;
	void OnSize(wxSizeEvent& event);
	void OnPaint(wxPaintEvent& event);  
};
#endif
widget.cpp
#include <wx/wx.h>
#include "widget.h"
#include "burning.h"
int num[] = { 75, 150, 225, 300, 375, 450, 525, 600, 675 };
int asize = sizeof(num)/sizeof(num[1]);
Widget::Widget(wxPanel *parent, int id)
	: wxPanel(parent, id, wxDefaultPosition, wxSize(-1, 30), wxSUNKEN_BORDER)
{
	m_parent = parent;
	Connect(wxEVT_PAINT, wxPaintEventHandler(Widget::OnPaint));
	Connect(wxEVT_SIZE, wxSizeEventHandler(Widget::OnSize));
}
void Widget::OnPaint(wxPaintEvent& event)
{
	wxFont font(9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
		wxFONTWEIGHT_NORMAL, false, wxT("Courier 10 Pitch"));
	wxPaintDC dc(this);
	dc.SetFont(font);
	wxSize size = GetSize();
	int width = size.GetWidth();
	Burning *burn = (Burning *) m_parent->GetParent();
	int cur_width = burn->GetCurWidth();
	int step = (int) round(width / 10.0);
	int till = (int) ((width / 750.0) * cur_width);
	int full = (int) ((width / 750.0) * 700);
	if (cur_width >= 700) {
		dc.SetPen(wxPen(wxColour(255, 255, 184))); 
		dc.SetBrush(wxBrush(wxColour(255, 255, 184)));
		dc.DrawRectangle(0, 0, full, 30);
		dc.SetPen(wxPen(wxColour(255, 175, 175)));
		dc.SetBrush(wxBrush(wxColour(255, 175, 175)));
		dc.DrawRectangle(full, 0, till-full, 30);
	} else { 
		dc.SetPen(wxPen(wxColour(255, 255, 184)));
		dc.SetBrush(wxBrush(wxColour(255, 255, 184)));
		dc.DrawRectangle(0, 0, till, 30);
	}
	dc.SetPen(wxPen(wxColour(90, 80, 60)));
	for ( int i=1; i <= asize; i++ ) {
		dc.DrawLine(i*step, 0, i*step, 6);
		wxSize size = dc.GetTextExtent(wxString::Format(wxT("%d"), num[i-1]));
		dc.DrawText(wxString::Format(wxT("%d"), num[i-1]), 
		i*step-size.GetWidth()/2, 8);
	}
}
void Widget::OnSize(wxSizeEvent& event)
{
	Refresh();
}
burning.h
#ifndef BURNING_H
#define BURNING_H
#include <wx/wx.h>
#include "widget.h"
class Burning : public wxFrame
{
public:
	Burning(const wxString& title);
	void OnScroll(wxScrollEvent& event);
	int GetCurWidth();
  
	wxSlider *m_slider;
	Widget *m_wid;
	int cur_width;
};
#endif
burning.cpp
#include "burning.h"
#include "widget.h"
int ID_SLIDER = 1;
Burning::Burning(const wxString& title)
	: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(350, 200))
{
	cur_width = 75;
	wxPanel *panel = new wxPanel(this, wxID_ANY);
	wxPanel *centerPanel = new wxPanel(panel, wxID_ANY);
	m_slider = new wxSlider(centerPanel, ID_SLIDER, 75, 0, 750, wxPoint(-1, -1), 
		wxSize(150, -1), wxSL_LABELS);
	wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
	wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
	wxBoxSizer *hbox2 = new wxBoxSizer(wxHORIZONTAL);
	wxBoxSizer *hbox3 = new wxBoxSizer(wxHORIZONTAL);
	m_wid = new Widget(panel, wxID_ANY);
	hbox->Add(m_wid, 1, wxEXPAND);
	hbox2->Add(centerPanel, 1, wxEXPAND);
	hbox3->Add(m_slider, 0, wxTOP | wxLEFT, 35);
	centerPanel->SetSizer(hbox3);
	vbox->Add(hbox2, 1, wxEXPAND);
	vbox->Add(hbox, 0, wxEXPAND);
	panel->SetSizer(vbox);
	m_slider->SetFocus();
	Connect(ID_SLIDER, wxEVT_COMMAND_SLIDER_UPDATED, 
		wxScrollEventHandler(Burning::OnScroll)); 
	Centre();
}
void Burning::OnScroll(wxScrollEvent& WXUNUSED(event))
{
	cur_width = m_slider->GetValue();
	m_wid->Refresh();
}
int Burning::GetCurWidth() 
{
	return cur_width;
}
main.h
#include <wx/wx.h>
class MyApp : public wxApp
{
	public:
		virtual bool OnInit();
};
main.cpp
#include "main.h"
#include "burning.h"
IMPLEMENT_APP(MyApp)
bool MyApp::OnInit()
{
	Burning *burning = new Burning(wxT("The Burning Widget"));
	burning->Show(true);
	return true;
}

Мы помещаем wxPanel внизу окна и рисуем чистый виджет вручную. Весь важный код находится в методе OnPaint() класса widget. Этот виджет визуально показывает общую ёмкость носителя и свободное место, доступное на нём. Виджет контролируется слайдером. Минимальное значение нашего виджета 0, максимальное - 750. Если мы достигнем значения 700, мы начнем рисовать красным цветом. Так обычно обозначается перепрожиг.

wxSize size = GetSize();
int width = size.GetWidth();
... 
int till = (int) ((width / 750.0) * cur_width);
int full = (int) ((width / 750.0) * 700);

Мы рисуем виджет динамическим. Чем больше окно, тем больше виджет прожига и наоборот. Поэтому мы должны вычислять размер wxPanel, в которой мы рисуем самодельный виджет. Параметр till определяет итоговую величину для рисования. Это значение получаем от слайдера. Это пропорция от всей области. Параметр full определяет точку, где мы начинаем рисовать красным цветом. Помните, пользуйтесь арифметикой для чисел с плавающей точкой. Это позволит достичь большей точности.

Фактически рисование состоит из трёх шагов. Мы рисуем жёлтый, или красный и жёлтый прямоугольники. Далее мы рисуем вертикальные линии, которые делят виджет на несколько частей. Наконец, мы рисуем числа, указывающие ёмкость носителя.

void Widget::OnSize(wxSizeEvent& event)
{
	Refresh();
}

Каждый раз, когда окно меняет свои размеры, мы обновляем виджет. Т.е. виджет перерисовывает себя.

void Burning::OnScroll(wxScrollEvent& WXUNUSED(event))
{
	cur_width = m_slider->GetValue();
	m_wid->Refresh();
}

При перемещении ползунка слайдера, мы получаем некоторое значение и сохраняем его в переменной cur_width. Эта переменная используется, при рисовании виджета прожига. После этого мы перерисовываем виджет.

Рисунок 11.1: Виджет прожига

Рисунок 11.1: Виджет прожига

Остальные материалы курса:

Вступление

1. Вспомогательные классы

2. Первые программы

3. Меню и панели инструментов

4. Управление компоновкой

5. События

6. Диалоги

7. Виджеты часть 1

8. Виджеты часть 2

9. Перетаскивание

10. Контексты устройств

11. Самодельные виджеты

12. Тетрис в wxWidgets

Примечание

Оригинал руководства расположен здесь. Автор оригинала - Jan Bodnar.

Перевод (без разметки и картинок) был выполнен пользователем ber113 с сайта translated.by. К сожалению, сайт сейчас не работает.

Данный документ представляет собой компиляцию указанных документов с сохранением оригинальной разметки и иллюстраций.