Як створити портабельну 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-модулів відчутне покращення коефіцієнта стиснення?