Як створити портабельну GUI додаток на wxPerl, Записки програміста
Як створити портабельну GUI додаток на wxPerl
Одна з проблем мов, що інтерпретуються, полягає в залежності скриптів від наявності у користувача відповідного інтерпретатора і модулів, що використовуються в коді скрипта. У світі UNIX ці проблеми нікого не турбують завдяки менеджерам пакетів. На жаль, 90% наших потенційних користувачів сидять не під UNIX.
Постає питання — чи є спосіб писати скриптовими мовами програми, розраховані на широку аудиторію? Тобто, аудиторію, більшість якої неспроможна завантажити Strawberry Perl і поставити необхідні модулі з CPAN (якщо мова про Perl). Очевидно, є як мінімум два можливі рішення.
Перше рішення полягає в тому, щоб транслювати скрипт у програму на якомусь C/C++, а потім зібрати її за допомогою GCC. Тільки в цьому випадку доведеться відмовитись від деяких можливостей мови. Perl, наприклад, цими можливостями будуть eval() , а також модулі типу Moose або Inline::* . Однак постає питання — якщо ми все одно не використовуємо всієї гнучкості скриптових мов, може тоді краще одразу писати на C++ чи, скажімо, Haskell? В останнього, до речі, є компілятор, і інтерпретатор.
Наскільки мені відомо, описаний вище підхід Perl взагалі не використовується. У Python навпаки, відповідних проектів удосталь - див PyPy, Shedskin і Cython. Кожен із них заслуговує на окрему замітку, так що в рамках цієї посади вони не розглядаються.
Друге можливе вирішення проблеми — тягати разом із програмою інтерпретатор та всі необхідні модулі. Незважаючи на свою незграбність, це рішення є досить популярним і цілком робочим. Зокрема, програми типу perl2exe і py2exe використовують саме його. Ось про це рішення у контекстімови Perl та буде розказано далі за текстом. Забігаючи вперед, скажу, що програма вийде цілком прийнятного розміру і стартуватиме без жодних затримок.
Як нам з вами добре відомо, користувачі Windows не дуже шанують консольні програми. У зв'язку з цим нам знадобляться біндінги якоїсь бібліотеки для створення GUI. Питання вибору GUI бібліотеки свого часу я приділив багато уваги і зупинився на wxWidgets. Її і використовуватимемо.
Відповідна бібліотека Perl називається wxPerl. Де її взяти? Найпростіший спосіб це використовувати збірку Perl під назвою Citrus Perl. Ця збірка відрізняється від Active Perl і Strawberry Perl тим, що вона «з коробки» має багато готових модулів, включаючи wxPerl. Я користуюсь Citrus Perl вже не перший місяць і повністю ним задоволений.
Встановлюється Citrus Perl не зовсім простим чином. Завантажуємо установник звідси, запускаємо cmd.exe і кажемо:
Тут d: \ coding-stuff - це директорія, куди ви хочете поставити Citrus Perl. Після розпакування всіх файлів кажемо:
Потім йдемо в «Комп'ютер → Додаткові параметри системи → Змінні середовища» і дописуємо «d: coding-stuff Citrus Perl x 86 5-12 bin» в змінну оточення PATH. Згоден, нормальний інсталятор не завадив би, але ми з вами програмісти чи де? Зрештою, запускаємо новий екземпляр cmd.exe і перевіряємо, чи все працює:
Особисто я також скачав Padre та бібліотеку GD:
Додаток: У недавно вийшов Citrus Perl Release 8 бібліотеки GD і GD::Graph йдуть «з коробки».
Щоб не писати код GUI вручну, можна використовувати wxGlade або wxFormBuilder. Останній не вміє генерувати Perl-код, але на CPAN доступний модуль FBP::Perl, який компенсує цей недолік. У wxGlade є генератор Perl-коду, правда самапрограма вимагає Python.
Для цієї замітки я написав невеликий скрипт, що прибирає «сміття» з дизассемблерного лістингу, який отримується за допомогою IDA.

# ida2code.pl v 0.1 # (c) Alexandr A Alexeev 2011 http://eax.me/
use Wx 0.15 qw [: all >] ; use strict;
use Wx qw[:everything]; use base qw (Wx :: Frame); use strict;
sub new < my ($self, $parent, $id, $title, $pos, $size, $style, $name) = @_; $parent = undef unless defined $parent ; $id = - 1 unless defined $id ; $title = "" unless defined $title ; $pos = wxDefaultPosition unless defined $pos ; $size = wxDefaultSize unless defined $size ; $name = "" unless defined $name ;
# begin wxGlade: MyFrame::new
$style = wxDEFAULT_FRAME_STYLE unless defined $style ;
$self = $self -> SUPER :: new ($parent, $id, $title, $pos, $size, $style, $name); $self -> < textedit >= Wx :: TextCtrl -> new ( $self , - 1 , " " , wxDefaultPosition , wxDefaultSize , wxTE_MULTILINE wxHSCROLL ); $self -> < button_1 >= Wx :: Button -> new ( $self , - 1 , "Remove /.text: [0-9a-f] + /") ;
$self -> __set_properties(); $self -> __do_layout();
Wx :: Event :: EVT_BUTTON ($self, $self -> < button_1 >-> GetId, \&onPressButton);
# end wxGlade return $self;
sub __set_properties < my $self = shift;
# begin wxGlade: MyFrame::__set_properties
$self -> SetTitle ("Ida2code v 0.1 http://eax.me/"); $self -> SetSize (Wx :: Size-> new (400, 300));
sub __do_layout < my $self = shift;
# begin wxGlade: MyFrame::__do_layout
$self -> <sizer_1 >= Wx :: BoxSizer -> новий (wxVERTICAL); $self -> < розмір_1 >-> Додати ($self -> < textedit >, 1, wxEXPAND, 0) ; $self -> < розмір_1 >-> Додати ( $self -> < button_1 >, 0 , wxALIGN_CENTER_HORIZONTAL , 0 ) ; $self -> SetSizer ( $self -> < sizer_1 >) ; $self -> Макет ( ) ;
sub onPressButton < мій ($self, $event) = @_; # wxGlade: MyFrame::onPressButton
# попередження "Обробник подій (onPressButton) не реалізовано"; my $text = $self -> < texteded >-> GetValue (); $текст =
# кінець класу MyFrame
якщо ( абонент ) < local *Wx :: App :: OnInit = sub < 1 >; my $app = Wx :: App -> новий ( ); Wx :: InitAllImageHandlers ( ) ;
мій $frame_1 = MyFrame -> новий ( );
$app -> SetTopWindow ($frame_1) ; $frame_1 -> Показати (1); $app -> MainLoop ( ); >
Вся функція сосредоточена у функціях onPressButton — взяти текст з поля введення, прогнати через регулярні виведення і запихнути навпаки. Останній код був згенерований у wxGlade.
Демонстраційний скрипт, створений за допомогою FBP::Perl, можна завантажити тут. Його я виклав так, на всякий випадок.
У мене не знайшлося зайвих 150$ на perl2exe і більше 300$ на PerlApp, так що я вирішив спробувати частину з модулем PAR::Packer. Програма Cava Packager теж виглядає неплохо, але до мене в руки не дійшло.
Зі звичайними консольними програмами він справляється відмінно, але при роботі зі скриптами, які використовують wxPerl, потрібна невелика підтримка:
Відповідно до документації до Wx::Perl::Packager, від нас потрібно просто написати в потрібному місці
… після чого команда:
… магічним способом створити exe-шник.На жаль, практично все виявилося не так просто. У мене програма, створена за допомогою wxpar, видавала таку помилку:
Вирішення проблеми вдалося нагуглити. Виявляється, досить просто замінити в скрипті рядок:
І справді, exe'шник, створений із пропатченого скрипту, вже не виводить повідомлень про помилки. Він нічого не виводить. Взагалі. У сенсі навіть вікон.
Поколупав exe'шник і трохи погугливши, я так і не знайшов вирішення проблеми. Як я зрозумів, вона полягає в тому, що упакований скрипт не може знайти модуль Win32.pm, хоча він є у пакеті. Ну, якщо проблему не вдається вирішити, спробуємо її оминути.
Виявляється, отриманий exe'шник є архівом, що саморозпаковується, і його можна відкрити в будь-якому сучасному архіваторі. Розпаковуємо вміст архіву в окрему директорію. Динамічні бібліотеки з підкаталогу shlib кладемо в корінь. Туди ж копіюємо perl.exe, perl512.dll і libgcc_s_sjlj-1.dll з CitrusPerl x86 5-12 bin. Відкриваємо файл script/ida2code.pl і видаляємо з нього рядок:
Потім створюємо run.bat наступного змісту:
Запускаємо і… воно працює! Неважко перевірити, що тепер скрипт працюватиме з флешки навіть на комп'ютері без встановленого Perl. Взагалі ми вбили відразу двох зайців — скрипт тепер не тільки працює, а й запускається моментально, а не протягом декількох секунд.
Для зручності користувача замість bat-файлу можна покласти спеціальну невелику програму з гарною іконкою або зробити інсталятор. Що стосується розміру програми, що вийшла, в zip-архіві воно важить 9 Мб, а в 7z - 5 Мб, що за нинішніми мірками не так вже й багато. Я використовував параметри стандартного стиснення, якщо комп'ютер не має оперативної пам'яті. Слід врахувати, що миможемо поширювати додаток у двох варіантах - "все в одному" і "поставте Perl і потрібні CPAN модулі сам".
PS. Варто спробувати написати архіватор, ув'язнений для стиснення портабельних Perl-скриптів. Цікаво, чи дасть обфускація CPAN-модулів відчутне покращення коефіцієнта стиснення?