Часть 57. Битовая графика

Содержание
57.1. Пример
57.2. Shaped Windows (the Wheelbarrow)
57.3. Кнопка с картинкой

Иерархия наследования

Object
   +--- Widget
         +--- Misc
               +--- Pixmap
         

Битовая графика - структуры данных, которые содержат изображения. Эти изображения могут использоваться где угодно, но, как правило, обычно используются как иконки на рабочем столе X, или как курсоры.

Битовая графика, состоящая из 2-х цветов, называется битовым массивом (битмапом - bitmap) и для его использования существует несколько дополнительных фукций

Для понимания того, что такое битовая графика, необходимо понимать, как работает система XWindow. Приложения под X-ами не обязательно могут быть запущены на томже компьютере, на котором работает пользователь. Различные приложения, называемые клиентами, общаются с одной программой, которая показывает графику и работает с клавиатурой или мышью. Эту программу, которая работает непосредственно с пользователем, называют сервером или X-сервером. Так как общение возможно и по сети, то необходимо держать соединение с X-сервером. Это означает, что битовое изображение содержится в памяти X-сервера и основные свойства изображения для его показа не должны постоянно передаваться клиенту, вместо этого передается команда "по координатам XYZ показывается такой-то цвет". В случае даже если Вы не будете использовать X-сервер, то нижеприведенные конструкции без проблем могут быть перенесены под X.

Для использования битовой графики в GTK, мы в первую очередь должны построить битовую структуру используя методы GDK. Битовое изображение может быть создано либо прямо из памяти, либо прочитано из файла. Мы сделаем тем и другим способом.

$gdkpixmap = Gtk::Gdk::Pixmap->create_from_data( $window,
                                                 $data,
                                                 $width,
                                                 $height );

Этот метод используется для создания двухцветного изображения прямо из памяти. Каждый бит данных предстявляет из себя флаг, согласно которому каждый пиксел либо включен либо выключен. Ширина и высота определяют число пикселей. Указатель GdkWindow привязывает изображение к конкретному окну, т.к. битовая графика имеет смысл только в контексте панели, на которой она должна быть отображена.

$gdkpixmap = Gtk::Gdk::Pixmap->create_from_data( $window,
                                                 $data,
                                                 $width,
                                                 $height,
                                                 $depth,
                                                 $foreground,
                                                 $background );

Этот метод используется для того, чтобы создать изображение заданной глубины (числа цветов) от определенного двуцветного массива. $foreground и $background являются соответственно нижним и верхним цветами (в смысле каждый охотник желает знать где сидит фазан)

$gdkpixmap = Gtk::Gdk::Pixmap->create_from_xpm( $window,
                                                $mask,
                                                $transparant_color,
                                                $filename );

Формат XPM - удбочитаемое представление для оконной системы Unix XWindow. Он широко используется и файлы в этом формате можно создать многочисленными утилитами. Файл *.xpm должен содержать изображение в том формате, которое соответствует расширению, в этом случае он будет прочитан и загружен как битовая графика. Маска определяет, какие биты изображения являются непрозрачными. Все другие биты будет окрашены в соответствии с цветом, определенным в переменной $transparent_color.

$gdkpixmap = Gtk::Gdk::Pixmap->create_from_xpm_d( $window,
                                                  $mask,
                                                  $transparent_color,
                                                  $data );

Маленькие изображения могут быть включены в прямо в программу. В этом случае битовая графика будет создана без открытия дополнительных файлов. Пример такого включения:

my @xpm_data = ( "16 16 3 1",
                 "       c None",
                 ".      c #000000000000",
                 "X      c #FFFFFFFFFFFF",
                 "                ",
                 "   ......       ",
                 "   .XXX.X.      ",
                 "   .XXX.XX.     ",
                 "   .XXX.XXX.    ",
                 "   .XXX.....    ",
                 "   .XXXXXXX.    ",
                 "   .XXXXXXX.    ",
                 "   .XXXXXXX.    ",
                 "   .XXXXXXX.    ",
                 "   .XXXXXXX.    ",
                 "   .XXXXXXX.    ",
                 "   .XXXXXXX.    ",
                 "   .........    ",
                 "                ",
                 "                " );

Как только мы создали битовое изображение, мы можем его показать как виждет GTK. Для этого необходимо создать специальный GTK виджет, который бы содержал битовое изображение GDK. Это можно сделать так:

$pixmap = new Gtk::Pixmap( $gdkpixmap, $mask );

Другие вызовы этого типа виждетов:

$pixmap->get_type();

$pixmap->set( $val, $mask );

$pixmap->get( $val, $mask );

set() используется для смены виджета с битовой графикой, используемого в данным момент. $val является битовой графикой, созданной при помощи GDK.

57.1. Пример

Нижеследующий пример показывает использование кнопки с картинкой:

Исходник

      
#!/usr/bin/perl -w


use 
Gtk
;
use 
strict
;

set_locale Gtk;
init Gtk;


my
 $false = 0;

my
 $true = 1;


# XPM данные для иконки "Открыть файл"

my
 @xpm_data = ( "16 16 4
1",
		 "       c None s None",
		 ".      c black",
		 "X      c #808080",
		 "o      c white",
		 "                ",
		 "  ..            ",
		 " .Xo.    ...    ",
		 " .Xoo. ..oo.    ",
		 " .Xooo.Xooo...  ",
		 " .Xooo.oooo.X.  ",
		 " .Xooo.Xooo.X.  ",
		 " .Xooo.oooo.X.  ",
		 " .Xooo.Xooo.X.  ",
		 " .Xooo.oooo.X.  ",
		 "  .Xoo.Xoo..X.  ",
		 "   .Xo.o..ooX.  ",
		 "    .X..XXXXX.  ",
		 "    ..X.......  ",
		 "     ..         ",
		 "                ");


my
 $window;

my
 $pixmapwid;

my
 $button;

my
 $pixmap;

my
 $mask;

my
 $style;

$window = new Gtk::Window( "toplevel" );
$window->signal_connect( "delete_event",

sub
 { Gtk->
exit
( 0 ); } );
$window->border_width( 10 );
$window->show();

# закачиваем данные из gdk
$style = $window->get_style()->bg( 'normal' );
( $pixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d(
$window->window,
							  $style,
							  @xpm_data );

#битовая графика присваивается виждету
$pixmapwid = new Gtk::Pixmap( $pixmap, $mask );
$pixmapwid->show();

# кнопка содержит виджет с битовой графикой
$button = new Gtk::Button();
$button->add( $pixmapwid );
$button->show();
$button->signal_connect( "clicked",

sub
 { 
print
( "button clicked\n" ); } );
$window->add( $button );

main Gtk;

exit
( 0 );


# конец примера
      
   

скриншот

Подгружая данные из файла в текущей директории, названного icon.xpm, мы создали бы изображение таким образом:

( $pixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm( $window->window,
                                                        $style;
                                                        "./icon.xpm" );
$pixmapwid = new Gtk::Pixmap( $pixmap, $mask );
$pixmapwid->show();

57.2. Контурные панели(на примере изображения тачки)

Неудобство использования пиксмапов в том, что они отображаются всегда как квадратные, независимо от того, что на них нарисовано. Т.е. необходимо создать более приемлемые формы изображения. Например для создания игры хочетсяч иметь круглые кнопки. Один из путей использования - контурные панели.

Контурные панели это обычная графика, в которой фоновые пикселы являются прозрачными. В этом смысле если фоновое изображение многоцветно, то мы не переписываем его как квадратное... Нижеследующий пример показывает изображение огородной одноколесной тачки (без перегноя :) на рабочем столе:

Исходник

      
#!/usr/bin/perl -w

use 
Gtk
;
use 
strict
;

set_locale Gtk;
init Gtk;


my
 $false = 0;

my
 $true = 1;


my
 @wheelbarrow = ( '48 48 64 1',
		    '       c None',
		    '.      c #DF7DCF3CC71B',
		    'X      c #965875D669A6',
		    'o      c #71C671C671C6',
		    'O      c #A699A289A699',
		    '+      c #965892489658',
		    '@      c #8E38410330C2',
		    '#      c #D75C7DF769A6',
		    '$      c #F7DECF3CC71B',
		    '%      c #96588A288E38',
		    '&      c #A69992489E79',
		    '*      c #8E3886178E38',
		    '=      c #104008200820',
		    '-      c #596510401040',
		    ';      c #C71B30C230C2',
		    ':      c #C71B9A699658',
		    '>      c #618561856185',
		    ',      c #20811C712081',
		    '<      c #104000000000',
		    '1      c #861720812081',
		    '2      c #DF7D4D344103',
		    '3      c #79E769A671C6',
		    '4      c #861782078617',
		    '5      c #41033CF34103',
		    '6      c #000000000000',
		    '7      c #49241C711040',
		    '8      c #492445144924',
		    '9      c #082008200820',
		    '0      c #69A618611861',
		    'q      c #B6DA71C65144',
		    'w      c #410330C238E3',
		    'e      c #CF3CBAEAB6DA',
		    'r      c #71C6451430C2',
		    't      c #EFBEDB6CD75C',
		    'y      c #28A208200820',
		    'u      c #186110401040',
		    'i      c #596528A21861',
		    'p      c #71C661855965',
		    'a      c #A69996589658',
		    's      c #30C228A230C2',
		    'd      c #BEFBA289AEBA',
		    'f      c #596545145144',
		    'g      c #30C230C230C2',
		    'h      c #8E3882078617',
		    'j      c #208118612081',
		    'k      c #38E30C300820',
		    'l      c #30C2208128A2',
		    'z      c #38E328A238E3',
		    'x      c #514438E34924',
		    'c      c #618555555965',
		    'v      c #30C2208130C2',
		    'b      c #38E328A230C2',
		    'n      c #28A228A228A2',
		    'm      c #41032CB228A2',
		    'M      c #104010401040',
		    'N      c #492438E34103',
		    'B      c #28A2208128A2',
		    'V      c #A699596538E3',
		    'C      c #30C21C711040',
		    'Z      c #30C218611040',
		    'A      c #965865955965',
		    'S      c #618534D32081',
		    'D      c #38E31C711040',
		    'F      c #082000000820',
		    '                                                ',
		    '          .XoO                                  ',
		    '         +@#$%o&                                ',
		    '         *=-;#::o+                              ',
		    '           >,<12#:34                            ',
		    '             45671#:X3                          ',
		    '               +89<02qwo                        ',
		    'e*                >,67;ro                       ',
		    'ty>                 459@>+&&                    ',
		    '$2u+                  ><ipas8*                  ',
		    '%$;=*                *3:.Xa.dfg>                ',
		    'Oh$;ya             *3d.a8j,Xe.d3g8+             ',
		    ' Oh$;ka          *3d$a8lz,,xxc:.e3g54           ',
		    '  Oh$;kO       *pd$%svbzz,sxxxxfX..&wn>         ',
		    '   Oh$@mO    *3dthwlsslszjzxxxxxxx3:td8M4       ',
		    '    Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B*     ',
		    '     Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&   ',
		    '      Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM*  ',
		    '       OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ',
		    '        2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ',
		    '        :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo',
		    '         +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g',
		    '          *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en',
		    '           p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>',
		    '           OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ',
		    '            3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ',
		    '             @26MvzxNzvlbwfpdettttttttttt.c,n&  ',
		    '             *;16=lsNwwNwgsvslbwwvccc3pcfu<o    ',
		    '              p;<69BvwwsszslllbBlllllllu<5+     ',
		    '              OS0y6FBlvvvzvzss,u=Blllj=54       ',
		    '               c1-699Blvlllllu7k96MMMg4         ',
		    '               *10y8n6FjvllllB<166668           ',
		    '                S-kg+>666<M<996-y6n<8*          ',
		    '                p71=4 m69996kD8Z-66698&&        ',
		    '                &i0ycm6n4 ogk17,0<6666g         ',
		    '                 N-k-<>     >=01-kuu666>        ',
		    '                 ,6ky&      &46-10ul,66,        ',
		    '                 Ou0<>       o66y<ulw<66&       ',
		    '                  *kk5       >66By7=xu664       ',
		    '                   <<M4      466lj<Mxu66o       ',
		    '                   *>>       +66uv,zN666*       ',
		    '                              566,xxj669        ',
		    '                              4666FF666>        ',
		    '                               >966666M         ',
		    '                                oM6668+         ',
		    '                                  *4            ',
		    '                                                ',
		    '                                                ' );


my
 $window;

my
 $pixmap;

my
 $fixed;

my
 $gdkpixmap;

my
 $mask;

my
 $style;

my
 $normal;

my
 $gc;

# Создаем основное окно и вешаем на него обработчик сигнала delete_event для закрытия
# приложения. Заметим, что основное окное не имеет заголовка наверху
# так как мы его делаем popup окном.
$window = new Gtk::Window( "popup" );
$window->signal_connect( "delete_event",

sub
 { Gtk->
exit
( 0 ); } );
$window->show();

# Строчки для битовой графики и виджета битовой графики
$style = Gtk::Widget->get_default_style();
$normal = $style->bg( 'normal' );
$gc = $style->black_gc;
( $gdkpixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d(
$window->window,
							     $normal,
							     @wheelbarrow );
$pixmap = new Gtk::Pixmap( $gdkpixmap, $mask );
$pixmap->show();

# Показывая битовую графику мы используем фиксированный виджет
$fixed = new Gtk::Fixed();
$fixed->set_usize( 200, 200 );
$fixed->put( $pixmap, 0, 0 );
$window->add( $fixed );
$fixed->show();

# маскируется все, кроме изображения
$window->shape_combine_mask( $mask, 0, 0 );

# показываем окно
$window->set_uposition( 400, 400 );
$window->show();
main Gtk;

exit
( 0 );


# конец примера
      
   

Скриншот

Чтобы сделать виджет отзывающимся на сигнал, мы присваиваем ему свойство откликаться на нажатие кнопки. несколько строк кода показывают, как сделать так, чтобы нажатием мышшиной кнопки по картинке виджет закончил свою работу.

$window->set_events( [ $window->get_events(), 'button_press_mask' ] );

$window->signal_connect( "button_press_event", sub { Gtk->exit( 0 ); } );
         

57.3. Пример кнопки с картинкой

Исходник

      
#!/usr/bin/perl -w

use 
Gtk
;
use 
strict
;

set_locale Gtk;
init Gtk;


my
 $false = 0;

my
 $true = 1;


my
 $window;

my
 $box;

my
 $button;


# Create the window
$window = new Gtk::Window( "Пример кнопки" );
$window->set_title( "Кнопка с картинкой!"
);
$window->border_width( 10 );
$window->realize();
$window->signal_connect( "destroy",

sub
 { Gtk->
exit
( 0 ); } );
$window->signal_connect( "delete_event",

sub
 { Gtk->
exit
( 0 ); } );

# этот вызов нашего бокса создает функцию - ? (This
calls our box creating function)
$box = xpm_label_box( $window, "info.xpm",
"Замерзни!" );

$button = new Gtk::Button();
$button->signal_connect( "clicked",

sub
 {
			    
print
( "Замер-р-р-р-з-з-з-заю!!!\n" ); } );

# Показываем все наши виджеты
$box->show();
$button->add( $box );
$button->show();
$window->add( $button );
$window->show();

# расслабляемся и готовимся отдохнуть!
main Gtk;

exit
( 0 );



# Создаем новый горизонтальный бокс с лейблом и помещаем туда картинку
# и засовываем в бокс.

sub
 
xpm_label_box

{
   
my
 ( $parent,
$xpm_filename, $label_text ) = @_;

   
my
 $box;
   
my
 $style;
   
my
 $pixmap;
   
my
 $mask;
   
my
 $pixmapwid;
   
my
 $label;

   # Создаем область отрисовки для xpm и лейбл
   $box = new Gtk::HBox( $false, 0 );

   # берем цвет кнопки как фоновый для картинки.
   $style = $parent->get_style()->bg( 'normal' );

   # теперь для xpm
   ( $pixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm(
$parent->window,
							   $style,
							   $xpm_filename );
   $pixmapwid = new Gtk::Pixmap( $pixmap, $mask );

   # создаем лейбл для кнопки
   $label = new Gtk::Label( $label_text );

   # передаем картинку и лейбл в горизонтальный виджет
   $box->pack_start( $pixmapwid, $false, $false, 3 );
   $box->pack_start( $label, $false, $false, 3 );

   $box->border_width( 2 );
   $pixmapwid->show();
   $label->show();

   
return
 ( $box );
}


# конец примера
      
   

Прежде чем запстить программу, необходимо загрузить изображение image и сохранить его в директорию с кодом приведенной программы. Если изображения не будет, то $pixmap не будет содержать информации и программа не будет работать корректно.

Как только вы произвели все вышеприведенные операции, то появится следующая панель, яб даже сказал, панелька: Once you have done this, the above program should look like this:

скриншот

Функция xpm_label_box() может использоваться любым виджетом, являющимся контейнером.

Вызов в Notice in xpm_label_box() подобен вызову get_style(). Каждый виджет имеет стиль, содержащий теневой и основной цвета для различных ситуаций, выбора фона и пр. Эти значения установлены по умолчанию в каждом виджете и являются необходимыми для вызова многих функций GDK, например $pixmap->create_from_xpm(), которая создает нормальный фоновый цвет. Использя ресурс-файлы стиль виджетов может быть переопределен.

Также отметим, что realize() вызывается после установления ширины границы окна. Эта функция использует GDK для создания панелей XWindow, связаных с виджетом. Она автоматически вызывается когда вы вызываете функцию show() для виджета. Но вызов функции create_from_xpm() требдует, чтобы аргумент $window обратился к реальной панели X.т.к. это необходимо виджету до вызова GDK (туманно и неясно, что это за зверь)