пятница, июля 03, 2009

Свобода!

Чёрт побери,наконец-то конец сессии. Один экзамен не сдан, второй сдан на 3, ещё два на 4.
Осенью буду (наверняка частично) исправлять положение, а сейчас - отдыхать и заниматься действительно интересными делами.

Третья программа на wxWidgets

В прошлый раз мы остановились на простенькой программке в одну кнопку и один текстовый блок. Пора добавить одно текстовое поле.

1 #include
2
3 class MainApp: public wxApp{
4 public:
5 virtual bool OnInit();
6 };
7
8 enum{
9 BUTTON_Pimpa = 1,
10 Question_ID = 2
11 };
12
13 class MainFrame: public wxFrame{
14 public:
15 MainFrame( const wxString &title, const wxPoint &pos, const wxSize &size );
16 wxButton *Pimpa;
17 wxStaticText * Warning;
18 wxTextCtrl *Question;
19 void changeText(wxCommandEvent& event){Warning->SetLabel(_T("А вы нажали на кнопку."));}
20 private:
21 DECLARE_EVENT_TABLE()
22 };
23
24 BEGIN_EVENT_TABLE ( MainFrame, wxFrame )
25 EVT_BUTTON ( BUTTON_Pimpa, MainFrame::changeText)
26 END_EVENT_TABLE()
27
28 DECLARE_APP(MainApp)
29 IMPLEMENT_APP(MainApp)
30 bool MainApp::OnInit(){
31 MainFrame *MainWin = new MainFrame(_("Заголовок окна"), wxDefaultPosition, wxSize(580, 200));
32 MainWin->Show(true);
33 SetTopWindow(MainWin);
34 return true;
35 }
36
37 MainFrame::MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size):wxFrame((wxFrame *) NULL, -1, title, pos, size) {
38 Pimpa = new wxButton(this, BUTTON_Pimpa, _T("Произвести вычисления"),wxDefaultPosition, wxDefaultSize, 0);
39 Warning = new wxStaticText (this, BUTTON_Pimpa + 1, _T("Я написал что-то на wxWidgets!!"), wxPoint(10,100), wxDefaultSize,0,wxStaticTextNameStr);
40 Question = new wxTextCtrl(this, Question_ID, wxEmptyString, wxPoint(390,40), wxSize(25,20));
41 }

Пример не идеальный, поэтому мы ещё будем его исправлять. Но сначала я объясню, где что значит.
Из действительно новых элементов интересна только строка 40.
wxEmptyString - это строка, которая стоит в текстовом поле по умолчанию - при запуске программы и при вызове метода Question->Clear().
wxSize(25,20) - это размер текстового поля.

Теперь - к усовершенствованиям.Добавьте после 40й строки ещё две:

Question -> SetMaxLength(2);
Question -> SetValidator(wxTextValidator(wxFILTER_NUMERIC));

Что это даёт? Это иллюстрирует ограничения на ввод. Первое ограничение говорит о том,что ввести можно лишь 2 символа. Второе ограничение - то,что эти символы могут быть только цифрами. Здесь необходимо сделать замечание: при обработке методом Question->GetValue() библиотека возвращает wxString - значение поля. Но числа можно задать разными способами: это может быть обычная нотация,как 1, 2, 3 и так далее, это может быть 3.14, или 3,14, или 3e+13. Таким образом, на деле в нашем текстовом поле может оказаться не только цифра.

Чтобы считать число из поля и записать его в переменную, удобно писать такую конструкцию:
if (!(Question->GetValue()).ToLong(&variable,10)){...}
, где на месте ... обрабатывается случай, когда значение Question не удалось преобразовать в десятичное число.

Но я отвлёкся к частному случаю. Если с maxLength всё более или менее понятно, про валидаторы можно рассказать подробнее.
Валидаторы, как вы уже поняли, служат для разнообразных ограничений ввода.В базовом варианте они проверяют нажатия клавиш и блокируют недопустимые. Есть много стандартных валидаторов, которые используются в самых простых случаях; также можно описать свой, если, например, вам нужны только буквы А,В,С,Е,Н,Й,М и Ψ. Это уже отдельная тема, не буду отвлекаться настолько сильно.

Что же мы получили сейчас? Программу с одним текстовым блоком, одним текстовым полем и одной кнопкой. Помнится, я именно из такого набора составлял графическую версию "Магического Шара". Более того: до фига моих лабораторок за 3й курс сводились к приёму и обработке параметров, это реализуется так же просто. Ну, или этот набор годится для кроссплатформенного кейгена (ни разу не встречал ничего подобного, но это просто фантазии).

Поэтому пока что закончу серию. Простейшая программа готова и работоспособна.Что ещё осталось сказать?
"Ура!"

четверг, июля 02, 2009

Вторая программа на wxWidgets

Blogger с утра не пускал меня, поэтому пишу пост немного позже ожидаемого.
Продолжаем tutorial, теперь пора добавить немного активности.
Прежде всего - добавить кнопку. Назовём её Pimpa.
Пусть при нажатии на эту кнопку меняется значение нашего текстового поля. Но тогда это поле должно тоже приобрести имя - пусть будет Warning.

1 #include <wx/wx.h>
2
3 class MainApp: public wxApp{
4 public:
5 virtual bool OnInit();
6 };
7
8 enum{
9 BUTTON_Pimpa = 1
10 };
11
12 class MainFrame: public wxFrame{
13 public:
14 MainFrame( const wxString &title, const wxPoint &pos, const wxSize &size );
15 wxButton *Pimpa;
16 wxStaticText * Warning;
17 void changeText(wxCommandEvent& event){Warning->SetLabel(_T("А вы нажали на кнопку."));}
18 private:
19 DECLARE_EVENT_TABLE()
20 };
21
22 BEGIN_EVENT_TABLE ( MainFrame, wxFrame )
23 EVT_BUTTON ( BUTTON_Pimpa, MainFrame::changeText)
24 END_EVENT_TABLE()
25
26 DECLARE_APP(MainApp)
27 IMPLEMENT_APP(MainApp)
28 bool MainApp::OnInit(){
29 MainFrame *MainWin = new MainFrame(_("Заголовок окна"), wxDefaultPosition, wxSize(580, 200));
30 MainWin->Show(true);
31 SetTopWindow(MainWin);
32 return true;
33 }
34
35 MainFrame::MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size):wxFrame((wxFrame *) NULL, -1, title, pos, size) {
36 Pimpa = new wxButton(this, BUTTON_Pimpa, _T("Произвести вычисления"),wxDefaultPosition, wxDefaultSize, 0);
37 Warning = new wxStaticText (this, BUTTON_Pimpa + 1, _T("Я написал что-то на wxWidgets!!"), wxPoint(10,100), wxDefaultSize,0,wxStaticTextNameStr);
38 }

Скомпилируйте и зацените.А теперь по строкам.
Строки 8-10 задают идентификатор для кнопки. Зачем нужны эти ID, я упоминал уже ранее; теперь же можете посмотреть на строку 23.У этой строки такой смысл: при вызове контрола с ID Button_Pimpa надо звать метод changeText. Обработка событий происходит в закрытой части класса, что логично,а вот метод закрывать необязательно.
Строка 37 даёт ещё одно понятие арифметики идентификаторов. В самом деле, мы же можем взять следующий после кнопки. Есть ещё один трюк - это когда вы пишете wxID_HIGHEST + 1,, это даёт наибольший свободный ID. Ну да не суть важно.
Возможно,у вас ещё давным-давно возник вопрос, почему строковые константы заключены в интересные _T("") и _(""). Это макросы wxWidgets, которые конвертируют строки во внутренний тип wxString. Различие _T() и _ в том,что _T() позволяет включать интернационализацию строки - если вы вздумаете использовать gettext, то вам оно понадобится.
Кстати - здесь я также играю с позициями. Можно запросто поставить в тупик стандартный компоновщик, если прописать обоим контролам wxDefaultPosition. Но чем усложнять программу хаками, лучше сосредоточимся на действительно нужных вещах.

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

среда, июля 01, 2009

Первая программа на wxWidgets

От нечего делать решил написать маленький тьюториал. В своё время меня по-настоящему достало отсутствие нормальных обучалок по wxWidgets.
Прежде всего, обговорю два момента. Во-первых, я надеюсь,что мой читатель знает,что такое библиотека wxWidgets, что он умеет программировать на C++, что он уже установил себе всё необходимое и настроил компилятор (самый простой способ на Windows - это поставить IDE wxDev-Cpp, на Linux все способы одинаково простые) и что он горит желанием начать.
Второе - это то,что я хочу сейчас сделать. Я хочу создать минимальное рабочее GUI-приложение на wxWidgets, не обращая пристального внимания на оптимизации и трюки. Из чего состоит минимальное приложение? Дайте подумать...кусочек текста на сером фоне подойдёт.
Сперва даю код,после этого - объясняю,что он делает, договорились?

1 #include <wx/wx.h>
2
3 class MainApp: public wxApp{
4 public:
5 virtual bool OnInit();
6 };
7
8 class MainFrame: public wxFrame{
9 public:
10 MainFrame( const wxString &title, const wxPoint &pos, const wxSize &size );
12 };
13
14 DECLARE_APP(MainApp)
15 IMPLEMENT_APP(MainApp)
16 bool MainApp::OnInit(){
17 MainFrame *MainWin = new MainFrame(_("Заголовок окна"), wxDefaultPosition, wxSize(580, 200));
18 MainWin->Show(true);
19 SetTopWindow(MainWin);
20 return true;
21 }
22
23 MainFrame::MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size):wxFrame((wxFrame *) NULL, -1, title, pos, size) {
24 new wxStaticText (this, wxID_ANY, _T("Я написал что-то на wxWidgets!!"), wxPoint(10,100), wxDefaultSize,0,wxStaticTextNameStr);
25 }


За этими 25 строчками кроется глубинный смысл. Пока что скомпилируйте, а я расскажу,что да как. Напоминаю,что на Linux это делается командой:


g++ `wx-config --cxxflags` <ЗДЕСЬ_ИМЯ_ФАЙЛА> `wx-config --libs` -o <ИМЯ_БИНАРНИКА>


Начнём,пожалуй. Строка 1 подключает собственно wxWidgets. Для случая прекомпиляции предусмотрен иной подход, но что я говорил насчет оптимизаций?
Итак, на строке 3 мы создаём класс MainApp. Это будет основной класс нашей программы,о чём напоминают строчки 14 и 15.В строках 8-12 создаётся фрейм программы - собственно окошечко, в котором будет виден текст. Позднее туда ещё добавится пара контролов.

Если вы заметили,то программа не содержит функции main(). Вместо неё первой выполняется функция OnInit(),это строки 16-21. В них всё довольно топорно: пичкаем фрейм нужными параметрами (заголовок, позиция на экране и размер), выводим на экран, и даём команду сделать его основным окном программы.

В строках 23-25 описывается содержимое фрейма. Собственно, что и будет выведено после выполнения строки 18. У нас это безымянный текстовый блок. немного о параметрах конструктора:

new wxStaticText (this, wxID_ANY, _T("Я написал что-то на wxWidgets!!"), wxPoint(10,100), wxDefaultSize,0,wxStaticTextNameStr);

this - это указатель на окно,в котором выводится текстовый блок. У нас, собственно,и нет выбора.
Значение wxID_ANY нужно объяснять долго. wxWidgets использует систему ID - то есть,каждый объект на фрейме имеет собственный целочисленный номер. Это понадобится чуть позже, когда придётся обрабатывать события. Пока что обработка событий в наши планы не входит, поэтому и ID мы задаём произвольным образом.
wxPoint(10,100) - это положение тестового блока на фрейме. Объект класса wxPoint задаёт его левый верхний угол - здесь только две координаты, которые считаются от левого верхнего угла.Вместо этого можно написать wxDefaultPosition - размещение по умолчанию, но ведь лёгкими путями не достигнуть просветления.
Здесь хотелось бы сделать замечание. При ручной расстановке я заметил,что в разных осях разные шрифты и размеры блоков. Так, в одной операционке мне не хватает ширины на фрейме,в другой - высоты. Разница в шрифтах, разница в размере границ полей..всё это даёт сильные перекосы, поэтому старайтесь придерживаться всё-таки стандартных компоновщиков,это сильно сэкономит нервы и время.
wxDefaultSize - размер блока. Рекомендую не вычислять вручную, библиотека это превосходно сделает за вас.
0 - отвечает за стиль текста. Если интересно - смотреть здесь. Но в большинстве случаев вам это смотреть не понадобится.
wxStaticTextNameStr - имя окна. Играть с этим не требуется,но если есть желание - почитайте маны и попробуйте.

Итак, вроде всё. У нас получилось одинокое окошко с текстовым блоком.Попробуем чуть-чуть усложнить задачу. За что я не люблю остальные руководства - они думают, что статусбар и меню - это обязательные элементы интерфейса. Чушь собачья! Интерфейс должен быть максимально простым и требовать не больше одного клика мышки.Что требует всего лишь одного нажатия? Кнопка.

Так, на сегодня пока всё. В следующий раз попробуем сделать ещё одну бессмысленную программу, в которой значение текстового поля будет меняться с щелчком кнопки. При желании, кнопку можно будет сделать красной :-)

воскресенье, июня 28, 2009

Небольшой скрипт

Вчера была очередная игра, но я не об этом.
Я написал скрипт для того,чтобы обновлять внешний IP'шник в своём DC++. Предполагается linuxdcpp последних версий. Сильно, думаю, упростит жизнь людям с динамическим IP. Перед использованием проверьте переменные $path и $url

#!/usr/bin/perl
use strict;
use warnings;
use LWP::Simple; #получение IP
my $path = ".dc++/DCPlusPlus.xml";#местонахождение собственно конфига
my $url = "http://p2p.kuzbass.net/getip.php";#адрес, с которого берётся IP. Подойдёт любой стабильный и простой по выводу, как http://whatismyip.ru или http://whatismyip.org. Основной критерий, конечно же, стабильность.
my $ip = get($url) or die($!);
$ip =~ /(\d+.\d+.\d+.\d+)/; $ip = $1;
print "Your IP is: $1\n";
my $CONFIG, my $BACKUP;
open (CONFIG,$path) or die($!);
open (BACKUP,">",$path."~~") or die($!);
while (){
s{\d+.\d+.\d+.\d+}{$ip};
print BACKUP;
}
close(CONFIG);
close BACKUP;
exec ("mv $path~~ $path");

Постоянные читатели

Архив