Примери за използване на wxWidgets, Бележки на програмиста
Примери за използване на wxW >
Както обещах, пиша продължение на моята бележка за wxW >
1. Инсталирайте wxWidgets и Code::Blocks
Тъй като се занимаваме с разработка на различни платформи, всичко зависи от операционната система, която използваме.За Windows трябва да изтеглите wxPack и компилатора MinGW от тук, последната версия на Code::Blocksбез MinGW от тук и помощната програма wx-config.exe от тук. Инсталирайте wxPack, след това MinGW, след това Code::Blocks. Копираме помощната програма wx-config.exe в “C:\Windows\” или където и да е инсталиран Windows.
Допълнение: Както се оказа, можете също да инсталирате по-нова версия на MinGW, да речем, ако искате да използвате функциите на C++0x във вашия проект. Дори не се нуждаете от wxPack, просто инсталирайте пакета wxMSW от тук. Само имайте предвид, че някъде в wxW >C++0x, така че в Project → Build Options → Compiler Settings → Other Options трябва да добавите флага -std=gnu++0x. Флагът -std=c++0x, който Code::Blocks ви позволява да зададете с отметка в следващия раздел Флагове на компилатора, не е добър.
В Ubuntu/Debian инсталационният процес е както следва:
Инсталирането нана FreeBSD е много по-лесно, защото всички зависимости се изтеглят автоматично:
Стартираме IDE, отидете в менюто „Настройки → Компилатор и програма за отстраняване на грешки ...“. В „Седения на компилатора → Други опции“ пишем:
В „Настройки на линкер → Други опции на линкер“ пишем:
Когато пренасяте проект от *nix към Windows, трябва да се уверите, че в „Проект → Опции за изграждане → Настройки на компилатор/линкер“ wx-config се извиква с опцията --static=yes.
Допълнение: Има доказателства, че хранилището на Ubuntu съдържа остаряла и бъгова версия на Code::Blocks, така че се препоръчва да се използват вечерни компилации или компилиране на Code::Blocks от източника.Подробности можете да намерите тук.
2. Създаване на GUI
За мен най-трудно беше да разбера, че в 90% от случаите цялото оформление е базирано на wxBoxSizer. Например, помислете за моите скици на интерфейса на mp3 плейър:

Червените правоъгълници са границите на елементите wxBoxSizer. Най-големият правоъгълник има ориентация wxVERTICAL. Всички елементи, добавени към него, ще бъдат разположени един под друг. Вложените размери имат wxХОРИЗОНТАЛНА ориентация. Елементите, разположени в тях, са подредени в един ред (виж долния ред бутони).
Ето как изглежда контролното дърво:

Използвайки свойствата Proportion, Shaped, Expand, Placement и Border, можете да постигнете почти всяко въобразимо подреждане на елементи. Можете да конфигурирате тези свойства в панела за бързи свойства (долния бутон с буквата Q от дясната страна на wxSmith). С думи е доста трудно да се обясни на кои свойства трябва да се присвоят какви стойности, за да се постигне определен резултат. Експериментирайте малко и сами ще разберете всичко.
3. Използване на регулярни изрази
Най-накрая стигнахме до примерите за код. Ще започна с любимите си регулярни изрази. Класическата ситуация е, когато потребителят въведе нещо в полето за въвеждане и щракне върху бутона OK. Програмата трябва по някакъв начин да обработи въведените данни, но първо трябва да провери тяхната коректност.
// получаване на име на функция (от wxTextCtrl) wxString funcName = txtFuncName - > GetValue();
// създаване на обект с регулярен израз wxRegEx reFuncName ( _T ( "^[a-z_][a-z0-9_]+$") , wxRE_ICASE ) ; // проверка дали е валиден (само във версия за отстраняване на грешки) wxASSERT ( reFuncName. IsValid ( ) ) ;
// входът съответства ли на регулярния израз? ако ( !reFuncName. Съвпада ( funcName ) ) < // показване на съобщение за грешка wxMessageBox ( _ ("Невалидно име на функция '" ) + funcName + _T ( "'!" ) ); // изчистете полето за въвеждане txtFuncName - > изчистване(); // излизане от събитието OnClick return ; > // .
За повече относно регулярните изрази в wxWidgets вижте точка 5 по-долу.
4. Интернационализация в wxWidgets
Интернационализация (съкратено като i18n, от „интернационализация“) е, когато напишем програма веднъж, след което просто трябва да й предоставим файлове за превод, за да я преведем на друг език. Тук основният трик е, че не е необходимо да прекомпилираме програмата за всеки език. Традиционно преводът се извършва с помощта на програмата PoEdit.
PoEdit анализира изходния код на нашата програма, извличайки редовете, заобиколени от макроса _(), който всъщност е извикване на функцията gettext(). За всеки ред въведете превода. Таблицата с низове се записва в .po файл (оттук и името на програмата). Когато преводът приключи, .po файлът трябва да бъде "компилиран" във формат .mo. Това е преводният файл.
Остава да добавим няколко реда в програмния код:
class ExampleApp : public wxApp < публичен: виртуален bool OnInit (); private: wxLocale m_Locale; // > ;
bool ExampleApp :: OnInit() < //(*AppInitialize bool wxsOK = true ; wxInitAllImageHandlers () ; if ( wxsOK) < // vvv добави това vvv
// добавяне на директория за търсене на .mo файлове m_Locale. AddCatalogLookupPathPrefix ( _T ( "lang" ) ); // инициализация на обект m_Locale. в него(); // заредете .mo файла, съответстващ на текущия локал // (ако има такъв файл) // например, акотекущият локал е ru_RU и файлът ru_RU.mo е в директорията ./lang //, той ще се използва за превод на m_Locale. AddCatalog (m_Locale. GetCanonicalName ()); // за локала ru_RU е подобно на извикването на // m_Locale.AddCatalog(_T("ru_RU"));
// ^^^ добави това ^^^
Сега хвърляме файлове с преведени низове в директорията ./lang, за български език - ru_RU.mo, за немски - de_DE.mo и т.н. Програмата автоматично ще покаже езика, на който говори потребителят. В света на UNIX, например, този език се дефинира с помощта на променливата на средата $LANG.
Можете също така да разпространявате .po файла заедно с програмата. Чисто в случай, че има желаещи да го преведат на родния си език. За да ги улесните, използвайте английски първоначално. Едва ли много хора в Япония или Египет говорят български.
5. Пример за използване на wxHTTP
wxWidgets има много полезни класове, сред които специално бих искал да спомена wxHTTP. Както лесно се досещате от името, това е готов HTTP клиент. Пример за употреба:
// изтегляне на уеб страница (в UTF-8!) и съхраняване в rslt // вярно при успех, невярно при грешка bool ExampleFrame :: HttpGet ( const wxString url, wxString & rslt, size_t maxLen ) < const unsigned int DEFAULT_MAX_LEN = 128 * 1024; const unsigned int HTTP_TIMEOUT = 10; ако ( maxLen == 0 ) < // невалиден аргумент или стойност по подразбиране maxLen = DEFAULT_MAX_LEN ; >
// регулярен израз за url wxRegEx reUrl ( wxT ( "^(?:http://)?([^/]+)(/.*)$") , wxRE_ADVANCED ) ; wxASSERT( reUrl. IsValid ( ) );
if ( ! reUrl. Съвпада с ( url ) ) < // невалиден URL формат // вероятно липсва наклонена черта след името на домейна връща false ; >
// получаване на име на домейн и получаване на заявка wxString server = reUrl. GetMatch(url, 1); wxString заявка = reUrl. GetMatch(url, 2);
// създаване на http клиент wxHTTP http ; http. SetTimeout( HTTP_TIMEOUT ); http. SetHeader ( wxT ("Content-type") , wxT ( "text/html; charset=utf-8") ) ; http. SetHeader ( wxT ( "Потребителски агент") , wxT ( "Mozilla/5.0 (X11; U; FreeBSD i386; ru-RU; " "rv:1.9.2.13) Gecko/20110202 Firefox/3.6.13" ) ) ;
// свързване към сървър ако ( ! http. Свързване ( сървър ) ) < // this->LogAdd(_("Връзката е неуспешна.")); връща невярно; >
// получаване на поток от данни wxInputStream * searchRslt = http. GetInputStream (заявка); ако ( http. GetError ( ) != wxPROTO_NOERR ) < // това->LogAdd(_("Неуспешно изтегляне, get.GetError() == ") // + http.GetError()); // нещо като if(x) < изтрий x; x = NULL; > wxDELETE (търсенеRslt); връща невярно; >
// заделяне на памет за временен буфер char * binaryData ; опитайте < binaryData = new char[ maxLen] ; > catch ( bad_alloc & error ) < // няма памет wxDELETE ( searchRslt ); връща невярно; >
// четене на данни от поток в буфер searchRslt - > Четене (binaryData, maxLen); wxDELETE (търсенеRslt);
// конвертиране на данни от буфер в низ rslt = wxString :: FromUTF8 (binaryData, maxLen) ; // нещо като if(x) < изтрий[]x; x = NULL; > wxDELETEA (двоични данни); връща истина; >
Обърнете внимание как се разпределя паметта за временния буфер. В съвременния C++, в случаябез памет, новият оператор хвърля изключение std::bad_alloc. Аз, например, не знаех това от много дълго време и след като извиках new, направих проверка в стила на if(ptr != 0) .
Сред недостатъците на класа wxHTTP бих искал да отбележа предупрежденията на компилатора при изграждането на Release версията на програмата. Надяваме се, че wxWidgets 2.9 ще поправи това.
6. Многопоточност в wxWidgets
Нека разгледаме най-простия пример. Има нишка, която увеличава стойността на брояча в цикъл, докато стойността на определен флаг не бъде зададена на true. Когато това се случи, нишката излиза. Достъпът до флага се контролира от mutex. Според мен не може да бъде по-лесно.
Сега как да го използвате:
// ---- файл wxThreadsMain.h ----- // . клас wxThreadsFrame : публичен wxFrame < публично: //. private: wxMutex m_Mutex; bool m_ExitFlag; CounterThread * m_CounterThreads [ 8 ] ; // . > ; // . // ---- файл wxThreadsMain.cpp ---- // . void wxThreadsFrame :: OnButton1Click ( wxCommandEvent & събитие ) < // брой нишки const int ThreadsNumber = sizeof ( m_CounterThreads ) / sizeof ( m_CounterThreads [ 0 ] ); // флаг за излизане от нишка m_ExitFlag = false; // създаване на нишки за ( int i = 0 ; i ThreadsNumber ; i ++ ) < опитайте < m_CounterThreads [ i ] = нов CounterThread ( wxTHREAD_JOINABLE ); > catch ( bad_alloc & error ) < // не успя да създаде нишка поради недостатъчна памет if ( i > 0 ) < за (j = 0; j i; j ++) < wxDELETE (m_CounterThreads [j]); > > wxMessageBox ( _ ( "Неуспешно стартиране на нишка #" ) + wxString :: Format ( _T ( "%d") , i ) ) ; връщане; >
m_CounterThreads [ i ] - > Създавайте(& m_Mutex, & m_ExitFlag) ; > // всички нишки са създадени, сега ги стартираме for ( int i = 0 ; i ThreadsNumber ; i ++ ) < m_CounterThreads [ i ] - > тичам(); > // изчакайте една секунда wxSleep ( 1 ) ; // задайте флаг за изход m_Mutex. ключалка(); m_ExitFlag = true; m_Mutex. отключване(); // стойностите на броячите ще бъдат запазени тук unsigned int counters [ ThreadsNumber ] ; // и тук - конкатенацията на техните най-малко значими битове wxString randomNumber ; // изчакайте нишките да завършат, освобождаване на памет за ( int i = 0 ; i ThreadsNumber ; i ++ ) < // изчакване за завършване m_CounterThreads [ i ] - > изчакайте(); // вземете стойност на брояча counters [ i ] = m_CounterThreads [ i ] - > GetCounter(); // нишката вече не е необходима wxDELETE ( m_CounterThreads [ i ] ); // записва стойността на най-малкия бит на брояча randomNumber + = wxString :: Format ( _T ( "%d") , counters [ i ] & 1 ) ; > // показване на резултат ListBox1 - > Вмъкване (произволно Число, 0); > // .
Примерът може да изглежда малко безсмислен, но всъщност не е съвсем верен. Стойностите на броячите са силно зависими от натоварването на системата. Ако добавите някои действия с RAM (например сортиране на масив с „балон“) и с твърд диск (четене / запис на временен файл) към метода CounterThread::Entry(), броячът ще се държи още по-непредсказуемо. А случайните числа са много полезни (например в криптографията) и често не са толкова лесни за получаване.
Оптималният брой нишки е равен на броя на процесорните ядра на борда, който може да се определи с помощта на функцията wxThread::GetCPUCount(). Първо трябва да зададете достатъчно голямm_CounterThreads размер на масива или използвайте вектор.
7. Къде мога да получа допълнителна информация?
Демонстрирах само някои от функциите на wxWidgets. Вижте официалната документация на Doxygen и wiki за повече информация.
От българоезичните сайтове бих препоръчал блога на Николай Тюшков и сайта wxWidgets.info. Първият се поддържа и редовно актуализира от практикуващ програмист, който използва, наред с други неща, wxWidgets в работата си. Вторият не е актуализиран от 2009 г., но съдържа много подходящи статии по темата.
В LiveJournal е открита общност, посветена на wxWidgets. Участниците в него са малко, но някои от тях от време на време дават признаци на живот.
За да намерите примерни кодове, препоръчвам да използвате koders.com. Всичко останало може да се намери чрез Google.