От GTK к PyQt
Материал из Wiki.crossplatform.ru
Автор: Philippe Fremy
Перевод: Andi Peredri
Содержание |
[править] Введение
Все началось с того, что я захотел написать игру Klotski. Однако в сети я нашел Gnotski и решил провести испытание: сколько уйдет времени на то, чтобы перенести программу с GTK на Qt, и как трудно это сделать? В конечном счете, я нашел это занимательным и решил сделать несколько вариантов программы, чтобы проанализировать каждый инструментарий.
Для ознакомления с этим исследованием, пожалуйста, загрузите исходный код.
[править] Gnotski
Gnotski - это оригинальная игра, которую вы можете найти в Gnome CVS. Это небольшая и простая программа. Она может быть изменена, улучшена и оптимизирована, но не это является целью. Цель состоит в том, чтобы выяснить, насколько инструментарии похожи и насколько они отличаются.
Вот небольшой пример кода, написанный с поочередным использованием Gtk, Qt и PyQt. В нем нет ничего особо интересного, просто я хочу подчеркнуть, насколько Gtk и Qt имеют похожую структуру.
void gui_draw_pixmap(char *target, gint x, gint y){ GdkRectangle area; int value; gdk_draw_pixmap(buffer, space->style->black_gc, tiles_pixmap, get_piece_nr(target,x,y)*TILE_SIZE, 0, x*TILE_SIZE, y*TILE_SIZE, TILE_SIZE, TILE_SIZE); if(get_piece_id(target,x,y)=='*'){ if(get_piece_id(orig_map,x,y)=='.') value = 20; else value = 22; gdk_draw_pixmap(buffer, space->style->black_gc, tiles_pixmap, value*TILE_SIZE+10,10, x*TILE_SIZE+10, y*TILE_SIZE+10,8,8); } area.x = x*TILE_SIZE; area.y = y*TILE_SIZE; area.width = TILE_SIZE; area.height = TILE_SIZE; gtk_widget_draw (space, &area); }
[править] C-Klotski
Для переноса Gnotski с Gtk/C на Qt/C++ мне потребовалось 3 часа. Это был первый этап: я попробовал максимизировать общий код и структуру. Я не использовал настоящий C++, а лишь обернул C++ вокруг C-функций ( отсюда название C-Klotski ). Перенос оказался чрезвычайно простым. Gtk и Qt имеют похожую структуру виджетов и механизм сигналов и слотов.
void Klotski::gui_draw_pixmap(char *target, int x, int y) { int value; bitBlt( buffer, 0, 0, tiles_pixmap, get_piece_nr(target,x,y)*TILE_SIZE, 0, TILE_SIZE, TILE_SIZE, Qt::CopyROP); if(get_piece_id(target,x,y)=='*'){ if(get_piece_id(orig_map,x,y)=='.') value = 20; else value = 22; bitBlt( buffer, 8, 8, tiles_pixmap, value*TILE_SIZE+10,10, 8, 8, Qt::CopyROP); } QPainter p( space ); p.drawPixmap( x*TILE_SIZE, y*TILE_SIZE, *buffer ); p.end(); }
[править] Cpp-Klotski
Затем я попробовал переписать программу в стиле С++ с максимизацией объема общего кода. Мне пришлось назначить каждую функцию определенному классу. Это было труднее сделать, потому что мне пришлось реструктуризировать программу. Но реструктуризация была единственной трудностью. В результате проделанной работы код оказался более кратким, но все еще имел много общего с Gnotski.
Вот тот же самый фрагмент кода:
void Board::gui_draw_pixmap(int x, int y) { int value; bitBlt( buffer, 0, 0, tiles_pixmap, map->piece_nr(x,y)*TILE_SIZE, 0, TILE_SIZE, TILE_SIZE, Qt::CopyROP); if(map->piece_id(x,y)=='*'){ if(map->is_goal(x,y)) value = 20; else value = 22; bitBlt( buffer, 8, 8, tiles_pixmap, value*TILE_SIZE+10,10, 8, 8, Qt::CopyROP); } QPainter p( this ); p.drawPixmap( x*TILE_SIZE, y*TILE_SIZE, *buffer ); p.end(); }
[править] PyQt-Klotski
Третьим этапом был перенос кода на PyQt. Я начал изучать Python недавно и нахожу его великолепным языком. Привязки PyQt выглядят превосходно и имеют очень активного разработчика, поэтому свой выбор я остановил на них. Перенос с Cpp-Klotski оказался тривиальным: 90% работы заключалось лишь в смене синтаксиса ( повсеместное добавление 'self.', удаление ';' и т.д. ). Эта работа могла быть автоматизирована с помощью сценария. Для переноса мне потребовалось 2 часа. Я думаю, что если бы пришлось начинать непосредственно с Gnotski, потребовалось бы менее 4-ех часов.
def gui_draw_pixmap(self,x, y) : bitBlt( self.buffer, 0, 0, self.tiles_pixmap, self.map.piece_nr(x,y)*TILE_SIZE, 0, TILE_SIZE, TILE_SIZE, Qt.CopyROP) if self.map.piece_id(x,y)=='*' : if self.map.is_goal(x,y) : value = 20 else: value = 22 bitBlt( self.buffer, 8, 8, self.tiles_pixmap, value*TILE_SIZE+10, 10, 8, 8, Qt.CopyROP) p = QPainter( self ) p.drawPixmap( x*TILE_SIZE, y*TILE_SIZE, self.buffer ); p.end()
[править] Система подсчета результатов
Gnotski использует несколько Gnome-функций для обеспечения подсчета результатов. Так как Qt и KDE не имеют ничего подобного, я не мог перенести эту часть программы. Чтобы обеспечить объективность, я удалил из своих расчетов строки, имевшие отношение к подсчету результатов. Вот они:
83:void game_score(); 90:void score_cb(GtkWidget *, gpointer); 453: GNOMEUIINFO_MENU_SCORES_ITEM (score_cb, NULL), 482: gnome_score_init(APPNAME); 571: game_score(); 617:void score_cb(GtkWidget *widget, gpointer data){ 618: gnome_scores_display (_(APPNAME_LONG), APPNAME, current_level, 0); 619:} 620: 621:void game_score(){ 622: gint pos; 623: pos = gnome_score_log(moves,current_level,FALSE); 624: gnome_scores_display(_(APPNAME_LONG), APPNAME, current_level, pos); 625:}
В итоге это составляет:
Lines | Characters |
14 | 435 |
[править] Сравнение размеров
Анализируемые файлы я поместил в каталог stat каждой из программ. Gnotski смешивает в одной и той же структуре определения уровней и меню. Я не смог использовать ту же структуру непосредственно в Qt, потому что она была слишком характерной для Gnome. Поэтому я использовал почти тот же тип структуры, но отделил код меню. Это объясняет наличие отдельной позиции Level+Menu. Также я поместил в отдельный файл объявления ( Declarations ). И, наконец, позиция Total является суммой Declarations, Level+Menu и Pure code.
Вот результаты:
Files | Lines | Characters | ||
Gnotski | Declarations | Gnotski-declaration.h | 91 | 2454 |
Level+Menu | gnotski-levels.h | 381 | 8978 | |
Pure code | 568 | 13829 | ||
Total | pieces.h gnotski-without-score.c | 1040 | 25266 |
Files | Lines | Characters | ||
C-Klotski | Declarations | klotski.h | 126 | 3019 |
Level+Menu | klotski-levels-menu.h | 327 | 6528 | |
Pure code | 505 | 10453 | ||
Total | pieces.h klotski.h levels.h klotski.cpp | 958 | 19990 |
Files | Lines | Characters | ||
Cpp-Klotski | Declarations | klotski.h | 157 | 3136 |
Level+Menu | levels-menu.h | 319 | 6502 | |
Pure code | 492 | 9394 | ||
Total | pieces.h klotski.h levels.h klotski.cpp | 968 | 19032 |
Files | Lines | Characters | ||
PyQt-Klotski | Declarations | 0 | 0 | |
Level+Menu | levels-menu.py | 315 | 6270 | |
Pure code | 391 | 9357 | ||
Total | levels.py klotski.py pieces.py | 706 | 15633 |
[править] Выводы
Во-первых, размер кода - это еще не все. Чтобы получить верное представление о различиях между языками и инструментариями, пожалуйста, просмотрите исходный код. Программа достаточно коротка и проста, поэтому для ее понимания Вам не понадобится много времени. Единственная сложная функция - это get_piece_nr(), которая определяет, какое должно быть выведено изображение (pixmap). Вы можете ее проигнорировать, так как для нашего сравнения это не существенно.
Во-вторых, эта программа слишком коротка и проста. Поэтому наши выводы не определяют лучший инструментарий, а лишь являются первым впечатлением от их сравнения.
[править] Menu+Level
Так как я использовал отличную от Gnotski структуру уровней и меню, составляющая Level+Menu несущественна. Это можно объяснить тем, что моя сруктура более эффективна, чем структура в Gnotski. Заметьте, для определения всех уровней и меню в Gnotski используется 380 строк и 8980 символов, тогда как в Cpp-Klotski - около 320 строк и 6500 символов. В PyQt-Klotski для этих же целей используется всего 315 строк и 6270 символов.
[править] Declarations
Gnotski нуждается лишь в нескольких объявлениях, что составляет 91 строку. C++ требует объявления всех классов и методов, в результате это составляет 126 строк для C-Klotski и 157 строк для Cpp-klotksi. Python не использует объявления.
Различия между C-Klotski и Cpp-Klotski не вызывают удивления. Все объекты C-Klotski имеют неограниченный доступ друг к другу. Это не настоящая объектно-ориентированная программа. В Cpp-Klotski я определил, насколько мне это удалось, объекты с методами для доступа к другим объектам. То есть, объекты не могут получить свободный доступ к другим. Это более безопасно, но это также означает, что для связи объектов Вам потребуется больше методов. Это моя личная интерпретация.
[править] Pure code
Результат таков:
- Gnotski содержит 568 строк и 13829 символов.
- C-Klotski содержит 505 строк и 10453 символа.
- Cpp-Klotski содержит 492 строки и 9394 символа.
- PyQt-Klotski содержит 391 строку и 9357 символов.
C-Klotski является более эффективным, чем Gnotski по числу строк и символов. Я считаю, что это благодаря использованию более эффективного инструментария. При создании одной и той же программы с использованием C/Gtk и C++/Qt Вам потребуется меньшее количество строк и символов, чтобы сделать это с помощью C++/Qt.
Я был немного удивлен столь относительно большим различием между C-Klotski и Cpp-Klotski. Это можно объяснить тем, что при более чистом коде Вы можете делать то же самое проще.
Без сомнения, лидирует PyQt-Klotski. На 100 строк меньше, чем в C-Klotski или Cpp-Klotski. Но почти то же самое число символов, что и в Cpp-Klotski. Я думаю, что это из-за необходимости использования 'self.' для доступа к членам класса, что немного неудобно.
[править] Total
В итоге Gnotski - самая большая программа: она насчитывает 1040 строк и 25000 символов. Это обусловлено используемым в ней языком. C++ и Qt способствуют уменьшению объема вводимого кода, в то время как Gtk/Gdk увеличивают его из-за своей объектной реализации на не объектно-ориентированном языке:
- Во всех функциях используется префикс gtk_ или gdk_ . Для избежания этого в C++ используется пространство имен классов.
- Для формирования имен функций используется символ подчеркивания. Для этих же целей в Qt применяется слияние слов с прописной буквы.
- Для похожих функций с отличающимися параметрами используются различные имена. C++ лишен этого недостатка и упрощает код благодаря использованию механизма перегрузки имен функций.
- Для определения типов ( typecasting ) используются сложные макросы. В C++ для этого имеется встроенная поддержка.
Cpp-Klotski и C-Klotski почти эквивалентны: по 960 строк и 19000/19900 символов. PyQt-Klotski подтверждает свое превосходство 700 строками и 15600 символами.
[править] Забавный материал
Давайте еще немного проанализируем:
[править] Избавление от gtk_, gdk_ и _ в Gnotski
toolkits-comparison > cat gnotski/*.h gnotski/*.c | wc 1054 3011 25696 > cat gnotski/*.h gnotski/*.c | sed -e 's/gtk_//g' -e 's/gdk_//g' -e 's/_//g' | wc 1054 3011 24645
Меньше лишь на 1000 символов. Я разочарован, поскольку ожидал большего. По-видимому, одного лишь сокращения имен функций не достаточно для того, чтобы приблизить размер Gtk-программ к размеру Qt-программ.
[править] Избавление от self в PyQt-Klotski
toolkits-comparison > cat pyqt-klotski/*.py | wc 706 2156 15633 toolkits-comparison > cat pyqt-klotski/*.py | sed -e 's/self//g' | wc 706 2151 14737
Снова меньше лишь на 1000 символов. Мои ожидания опять не оправдались. Но на самом деле это не так уж мало, поскольку составляет 1/15 программы. Я добавлю алиас для 'self' в моем редакторе!
[править] Другие примеры
Klotski - не единственный пример, подтверждающий эти выводы.
[править] KVim
Я занимаюсь переносом gvim в KDE. В основном я лишь копирую код из Gtk и вставляю его в KDE/Qt. Почти всегда он становится проще и короче.
[править] VeePee: Поддержка Python в приложениях GNOME и KDE
В настоящий момент VeePee является набором компонентов, позволяющих разработчикам легко добавлять в приложения GNOME и KDE возможности создания сценариев.
VeePee содержит эквивалентный код для Gnome и KDE. Ответ автора из FAQ :
Разве не тяжело разрабатывать пользовательский графический интерфейс одновременно для нескольких инструментариев? В действительности все инструментарии GUI обеспечивают похожий набор возможностей, однако реализуют их каждый по-своему. В настоящей версии VeePee 74% кода не зависит от GUI, 15% кода специфичны для Gnome и 11% - для KDE. Таким образом, Gnome-код на 33% больше эквивалентного KDE-кода.
[править] Основной вывод
В ходе этого анализа Вы не должны упустить основной вывод:
[править] Перенос программы с Gtk на Qt тривиален.
Вам необходимо лишь обернуть C-функции классами и методами C++, а также заменить Gtk-код на эквивалентный Qt-код. Этот простой процесс я выполнил несколько раз, например, для kvim.
[править] Личная интерпретация
Инструментарий - это набор виджетов, который обрабатывает асинхронные события. Поэтому, по сути, он объектно-ориентирован. Gtk использует для этого C. Я думаю, что хорошим выбором является использование в Qt такого объектно-ориентированного языка как C++, более подходящего для подобных целей.
Одним из преимуществ Qt является более краткий синтаксис. Для Gtk приходится вводить на 30% больше символов, чем для Qt/C++. Это может занять довольно большой отрезок времени.
Теперь перейдем к PyQt. Python - отличный язык, очень простой и мощный одновременно. Переход от Qt/C++ к PyQt достаточно прост, поскольку все имена сохраняются. Он может быть осуществлен даже Python-сценарием. К тому же Вы можете использовать всю обширную документацию Qt.
[править] Личные рекомендации
Если Вы начинаете разработку нового приложения, пожалуйста, учтите следующие рекомендации:
Если Вы желаете использовать чистый C, используйте Gtk. Он был сделан именно для Вас. Но разве Gtk - это настоящий C ? Это C с конструкторами, деструкторами, объектами, наследованием и т.д. Это C с объектной реализацией. Вы не думаете, что истинный объектно-ориентированный язык мог бы упростить разработку и отладку вашей программы?
Если Вы согласны использовать C++, я настоятельно рекомендую Qt. Доступна обширная превосходная документация, а Ваша программа будет короче. C++ является родным языком для Qt, поэтому отсутствуют задержки в выходе новых версий и проблемы, связанные с обертками ( wrappers ). Один из бывших разработчиков Gtk-- ( привязок C++ для Gtk ) Гуиллаум Лаурент ( Guillaume Laurent ) сейчас с успехом использует Qt/KDE. И напоследок, Qt более развит, чем Gtk. Qt уже не один год обладает теми возможностями, которые были реализованы в последних версиях Gtk: уникод, двумерная графика ( canvas ), XML, потоки и т.д.
Если для Вашего приложения не критично время его выполнения, я рекомендую PyQt даже больше чем Qt. PyQt очень тесно интегрирован с Qt. К тому же Python - великолепный язык! Вы выиграете в размере и читаемости кода, во времени компиляции ( его нет ) и в эффективности. Поскольку язык изначально очень мощный, Вы можете без проблем использовать более сложные конструкции. И не забывайте, что PyQt можно непосредственно использовать в Windows и Linux.
[править] Обратная связь
Вам понравилось это исследование? Или оно Вам не понравилось? Я допустил какие-либо ошибки? Тогда, пожалуйста, напишите мне pfremy@chez.com.
Я признаю, что предпочитаю использовать Qt, но на протяжении этого исследования я старался оставаться нейтральным, насколько это было возможным, за исключением разделов, помеченных как "Личные".
Вам понравилась игра? Тогда посетите написанные мной настоящие klotski. Они созданы с нуля с использованием QCanvas, обладают множеством возможностей и не имеют почти ничего общего с этими klotski.
Было бы великолепно перенести эту же программу на другие инструментарии или языки. Для более интересного исследования мне действительно хотелось бы иметь, например, PyGtk и GtkAda версии.
Источник: From Gtk to PyQt