Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Создание графических приложений

Цилюрик О.И.

Настоящая статья является дополнением к книге «Инструменты Linux для Windows-программистов». Это не описание как делать GUI приложения в Linux, это описание того, как ПРИСТУПИТЬ к созданию графических приложений в Linux, и, хотелось бы надеяться что это прозвучит - чем принципиально программирование графики в Linux отличается от того же занятия в Windows. Главным требованием здесь была простота. Сделав простейший шаблон GUI прложения, дальше двигаться уже гораздо проще. Кроме того, все эти простейшие приёмы программирования показаны сравнительно: на основе основных графических технологий (библиотек), используемых в UNIX.

Все примеры к тексту вы можете скачать в виде общего архива.


Создание приложений, взаимодействующих с пользователем посредством графического интерфейса (GUI приложений), является частным классом задач, отдельной областью программирования. Из числа других подобных областей приложения можно было бы привести, как примеры:

  • реализация алгоритмов цифровой обработки сигналов (DSP): быстрые спектральные преобразования (FFT и другие), вэйвлеты, авторегрессионные разложения...;
  • обработка аудио-потоков (пакеты: sox, ogg, speex и другие);
  • задачи IP-телефонии, SIP протокола, реализация разнообразных программных SoftSwitch;

Это сравнительный ряд автономных областей развития приведен как пример таких частных классов, одним из которых является и разработка GUI приложений. И как частный класс, со своей спецификой инструментов и средств, он не заслуживал бы отдельного упоминания, если бы не одно обстоятельство — принципиально отличающееся, диаметрально противоположное отношение к GUI в операционных системах семейства Windows и в UNIX (и в Linux, как его частный вид):

  • В Windows каждое приложение является принципиально GUI, неотъемлемым атрибутом любого приложения в Win32 API (низкого уровня) является главное окно приложения, уже само приложение «вяжется» вокруг его главного окна. Операционная система регистрирует классы окон и уже далее к ним соотносит конкретные приложения. Не может существовать приложения (взаимодействующего с пользователем, не системные службы) без окна, с этим были связаны и первоначальные сложности Windows в реализации консольных (терминальных) приложений.
  • в UNIX картина принципиально обратная: первичным является приложение, которое, по умолчанию, является консольным, текстовым, вся графическая система не является составной частью операционной системы, а является надстройкой пользовательского уровня. Чаще всего такой графической надстройкой является X11 (в реализации Xorg или X11R5), но и это не обязательно: практиковались и другие графические системы, хороший пример тому графические системы Qwindow, а затем Photon в операционной системе QNX, сосуществующие там одновременно с X11.
  • Показательно в этом смысле то, что вся оригинальная часть реализации X11 работает в пространстве пользователя, не в привилегированном режиме ядра (супервизора): работа с аппаратурой видеоадаптеров, устройствами ввода и другое. Отдельные реализации (видеосистемы NVIDIA или ATI Radeon) могут быть реализованы в режиме ядра (модули), но это а) сторонние относительно X11 разработки, и б) решение вопросов только производительности.

Из-за обозначенной специфики, разработка GUI приложений в UNIX (Linux) принципиально отличается:

  • вся работа GUI приложений ведётся через промежуточные слои (библиотеки) пользовательского уровня;
  • из-за того, что это ординарный пользовательский уровень, для разработчика предлагается широкий спектр альтернативных инструментов (библиотек), практически равнозначных, и конкурирующих друг с другом: Xlib, GTK+, Qt, wxWorks и многие другие.
  • базовый API работы с X11 предоставляет Xlib, все другие используют уже её функционал, как это показано на рисунке.

  • разработчик имеет возможность широкого выбора тех уровня и инструментов, которые он предполагает использовать, начиная от Xlib и выше (хотя уровень Xlib и слишком низок и работа с ним громоздкая).

Из-за названной специфики GUI приложений в Linux, все они, независимо от используемых средств создания, имеют абсолютно сходную структуру. Рассмотрим, для сравнения, код нескольких простейших GUI приложений, подготовленных с помощью различных инструментов. Важнейшей задачей такой экспозиции будут команды компиляции и сборки, чтобы, исходя из таких примеров, показать возможность начать создавать свои собственные GUI приложения.

Средства Xlib (архив Xlib.tgz):

Xlib.c :

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <errno.h> 
#include <X11/Xlib.h> 

extern int errno; 

int main( void ) { 
   Display *d; 
   Window w; 
   XEvent e; 
   char *msg = "Hello, World!"; 
   int s; 

   if( ( d = XOpenDisplay( getenv("DISPLAY" ) ) ) == NULL ) {  // Соединиться с X сервером, 
      printf( "Can't connect X server: %s\n", strerror( errno ) ); 
      exit( 1 ); 
   } 
   s = DefaultScreen( d ); 
   w = XCreateSimpleWindow( d, RootWindow( d, s ),     // Создать окно 
                            10, 10, 200, 200, 1, 
                            BlackPixel( d, s ), WhitePixel( d, s ) ); 
   XSelectInput( d, w, ExposureMask | KeyPressMask );  // На какие события будем реагировать? 
   XMapWindow( d, w );                                 // Вывести окно на экран 
   while( 1 ) {                                        // Бесконечный цикл обработки событий 
      XNextEvent( d, &e ); 
      if( e.type == Expose ) {                         // Перерисовать окно 
         XFillRectangle( d, w, DefaultGC( d, s ), 20, 20, 10, 10 ); 
         XDrawString( d, w, DefaultGC( d, s ), 50, 50, msg, strlen( msg ) ); 
      } 
      if( e.type == KeyPress )                         // При нажатии кнопки - выход 
         break; 
   } 
   XCloseDisplay( d );                                 // Закрыть соединение с X сервером 
   return 0; 
} 

Сборка приложения:

$ gcc Xlib.c -o Xlib -lX11 
  ...

Запуск приложения:

$ ./Xlib


Средства GTK+ (архив GTK+.tgz):

gtk.c :

# include <gtk/gtk.h>   /* Подключаем библиотеку GTK+ */ 

int main( int argc, char *argv[] ) { 
                               //Объявляем виджеты: 
   GtkWidget *label;                                          // Метка 
   GtkWidget *window;                                         // Главное окно 
   gtk_init( &argc, &argv );                           // Инициализируем GTK+
   window = gtk_window_new( GTK_WINDOW_TOPLEVEL );            // Создаем главное окно
   gtk_window_set_title( GTK_WINDOW( window ),                // Устанавливаем заголовок окна
                        "Здравствуй, мир!"); 
   label = gtk_label_new( "Здравствуй, мир!" );      // Создаем метку с текстом

   gtk_container_add( GTK_CONTAINER( window ), label );       // Вставляем метку в главное окно 

   gtk_widget_show_all( window );                             // Показываем окно вместе с виджетами
   g_signal_connect( G_OBJECT( window ), "destroy", // Соединяем сигнал завершения с выходом
                     G_CALLBACK( gtk_main_quit ), NULL ); 
   gtk_main();                                                // Приложение переходит в цикл ожидания 
   return 0; 
}

Сборка приложения:

$ gcc gtk.c -o gtk `pkg-config --cflags --libs gtk+-2.0`

...

Выполнение приложения:

$ ./gtk


Средства Qt (архив Qt.tgz):

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

index.cc :

#include <qapplication.h>
#include <qdialog.h> 

int main( int argc, char** argv ) { 
  QApplication app( argc, argv ); 
  QDialog  startDialog; 
  startDialog.setMinimumWidth( 200 );
  startDialog.show(); 
  return app.exec(); 
}

Теперь проделываем последовательно:

$ ls 
index.cc
$ qmake -project 
$ ls 
index.cc  Qt.pro 
$ qmake 
$ ls 
index.cc  Makefile  Qt.pro 

Исходя из «подручных» файлов исходных кодов, у нас сгенерировался файл проекта и, далее, сценарий сборки (Makefile). Далее проделываем традиционную сборку, а заодно и посмотрим опции компиляции и сборки, которые нам сгенерировал проект:

$ make

g++ -c -pipe -Wall -W -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables

g++ -o Qt index.o -L/usr/lib/qt-3.3/lib -lqt-mt -lXext -lX11 -lm

$ ls

index.cc index.o Makefile Qt Qt.pro

Выполнение приложения:

$ ./Qt


Средства wxWidgets (архив wxWidgets.tgz):

simple.cc :

#include <wx/wx.h> 
class Simple : public wxFrame { 
public:
     Simple( const wxString& title
};

Simple::Simple( const wxString& title ) :
  wxFrame( NULL, wxID_ANY, title, wxDefaultPosition, wxSize( 250, 150 ) ) { 
  Centre(); 
}

class MyApp : public wxApp { 
public:
     virtual bool OnInit(); 
};

bool MyApp::OnInit() { 
  Simple *simple = new Simple( wxT( "Simple" ) ); 
  simple->Show(true);   // старт петли обработки событий
  return true; 
}

IMPLEMENT_APP(MyApp )   // этот макрос реализует функцию main() в приложениях wxWidgets,
                        // скрывая подробности главного цикла ожидания события.

Сборка приложения:

$ g++ simple.cc `wx-config —cxxflags` `wx-config --libs` -o simple

...

Выполнение приложения:

$ ./simple


Средства GLUT (архив glut.tgz):

OpenGL Utility Toolkit, как и следует из названия, это средства использования технологии OpenGL в приложениях, которая требует определённой поддержки со стороны видео оборудования.

glut.c :

#include <GL/gl.h>

void Draw( void ) {
   glClear( GL_COLOR_BUFFER_BIT ); 
   glColor3f( 0.0f, 0.0f, 1.0f );
   glLineWidth( 1 ); 
   glBegin( GL_LINES ); 
   glVertex2f( 0, 0.5f ); 
   glVertex2f( 0, -0.5f ); 
   glEnd();
   glFlush();
}

int main( int argc, char *argv[] ) { 
   glutInit( &argc, argv );
   glutInitWindowSize( 400, 300 );
   glutInitWindowPosition( 100, 100 );
   glutCreateWindow( "GL Demo" );
   glutDisplayFunc( Draw );
   glClearColor( 0, 0, 0, 0 );
   glutMainLoop();
   return 0;
}

Сборка приложения:

$ gcc glut.c -o glut -lX11 -lglut

...

Выполнение приложения:

$ ./glut


То, что показано выше, это фактически не приложения, а скелеты приложений, но они позволяют: а) сравнить подобие всех GUI технологий в X11, и б) быть отправной точкой для сборки более содержательных GUI приложений. Показано только несколько GUI технологий, применяемых в X11 (большинство из них являются кросс-платформенными, и применимы в большинстве существующих операционных систем). Каждая из этих технологий, а названы только немногие из значительно большего числа, присутствующих в UNIX, могут быть полной альтернативой любой другой из этого же ряда, они взаимно заменимы, и даже взаимно дополняемые.

В данной статье были показаны образцы кода GUI приложений. Естественно, визуальные образы таких приложений строятся не путём непосредственного кодирования, а при использовании некоторых визуальных построителей, в составе тех или иных интегрированных средств разработки (IDE).