Rambler's Top100
Sources.RU Magazine
Декабрь 2004

· От редактора

· Создание сайта

· Программирование на IAR

· Обзор UPS

· mIRC + Delphi = WinAmp Plugin

· Многоязыковое приложение

· Передача файлов через сокеты

· H.323: Обзор архитектуры

· Введение в KDevelop

· Linux Live CD - Основы

· Microsoft Speech API — Синтез речи

 
· On-line версия выпуска

Введение в KDevelop

Автор: RaD

Введение

KDevelop является интегрированной средой разработки приложений под KDE/Qt. KDevelop (http://www.kdevelop.org) обладает необходимой функциональностью для удобной разработки программ. Среда включает в себя:

  • поддержку всех средств разработки на языке C++ (компилятор, линкер, automake, autoconf);
  • мастер генерирования основных типов приложений;
  • генератор классов для создания новых классов;
  • менеджер исходных текстов и документации проектов;
  • средства создания документации пользователя в формате SGML/HTML;
  • поддержку интернационализации приложений;
  • поддержку создания интерфейса приложений с помощью Qt Designer;
  • средства поддержки внешних отладчиков.

В данной среде разработки мы создадим небольшое приложение для просмотра свойств WAV файлов.

Операционная система и железо

Разработка приложения производилась на компьютере Intel Celeron 900 (1795 bogomips) / 256 MB RAM / 20 GB HDD.

Программное обеспечение: ASPLinux 7.3 2.96-112 / gcc version 2.96 20000731 / KDevelop 2.1.2 / Qt 3.0.4.

Проект

Для изучения процесса разработки приложений с помощью KDevelop создадим небольшую утилиту просмотра свойств файлов формата WAV. Приложение управляется с помощью меню и отображает основные свойства звукового файла.

Приложение должно:

  • иметь простой интерфейс;
  • позволять последовательно открывать файлы;
  • определять формат файла, отсеивать неподходящие файлы;
  • отображать свойства файла.

Создание проекта

Запустим KDevelop и создадим новый проект с помощью меню Project->New. Появится форма (см. рисунок) на которой надо выбрать тип создаваемого приложения. В нашем случае надо выбрать тип KDE Mini.

Создание нового проекта, выбор типа.

На следующей странице надо заполнить все поля формы. В поле Project Name надо вписать KWaveInfo. Поле Project Directory автоматически дополнится. Нажимаем на [Next].

На следующей странице можно включить поддержку CVS для вашего проекта. В нашем случае ничего включать не надо. Нажимаем на [Next].

На следующих двух страницах можно отредактировать текст, который будет отображаться в каждом заголовочном файле и в каждом исходнике проекта. Нажимаем на [Next].

Теперь можно сгенерировать файлы проекта. Нажимаем на [Create] и дожидаемся завершения процесса генерации. Затем нажимаем на [Exit].

Теперь можно заниматься программированием...

Главная форма приложения

Создаём форму

Для создания главной формы приложения необходимо воспользоваться утилитой Qt Designer. Для этого в KDevelop с помощью меню File->New открываем диалог создания нового файла. В диалоге на вкладке General выбираем Qt Designer File (*.ui) и в поле Filename вводим mainform.ui. При нажатии на [OK] среда предложит открыть файл в ASCII режиме, от этого надо отказаться. В открывшемся Qt Designer'е выбираем тип создаваемой формы. В нашем варианте это будет Main Window.

Уберём все галочки в мастере создания формы. Таким образом мы получаем пустую панель. Если редактора свойств нет на экране, то вызываем его с помощью меню Window->Views->Property Editor/Signal Handler. Теперь необходимо заменить в свойствах формы её наименование (name) на MainForm и отображаемый заголовок (caption) на KWaveInfo.

Добавляем меню

Наше приложение будет управляться с помощью меню. Для создания меню надо нажать на правую кнопку мыши на форме и выбрать из контекстного меню элемент Add Menu Item. Переименуем появившийся элемент меню в &File. Также создадим элемент &Help. Для создания подэлементов элемента File необходимо воспользоваться так называемыми "действиями" (actions).

Если окна Actions нет на экране, то это исправляется с помощью меню Window->Views->Action Editor. В этом окне в контекстном меню выбираем элемент New Action, в окне появляется действие с именем Action. Выделяем его мышью и в редакторе свойств производим следующие действия:

  • изменяем свойство name на fileExitAction;
  • изменяем свойство text на Exit;
  • изменияем свойство menuText на E&xit.

Аналогично создаем действия fileOpenAction, fileCloseAction и helpAboutAction.

Сигналы и слоты

Термины "сигнал" и "слот" в данном случае означают в терминах программиста под Win32 "событие" и "обработчик" соответственно.

Для создания слота у формы надо вызвать её контекстное меню и выбрать элемент Slots..., это приводит к появлению окна редактирования слотов. С помощью кнопки [New Slot] создаём новый слот. В свойствах слота меняем newSlot() на ExitSlot() и нажимаем на кнопку [OK]. Аналогично создаём слоты OpenSlot(), CloseSlot() и AboutSlot().

Теперь надо соединить действие fileExitAction с созданным слотом ExitSlot(). Для этого выделяем действие в окне Actions и нажимаем на синекрасную кнопку расположенную выше списка действий. В открывшемся окне редактирования соединений в левом верхнем углу расположен список сигналов, которые может генерировать объект; в правом верхнем - список слотов главной формы; внизу окна отображаются созданные соединения.

Выделим сигнал activated() и слот ExitSlot(), нажмём на активировавшуюся кнопку [Connect]. В списке созданных соединений появится новая строка.

Делаем аналогичные действия для действий fileOpenAction, fileCloseAction и helpAboutAction.

Qt Designer

Сохраняем разработанную форму

Теперь необходимо созранить форму в каталоге проекта, например, ~/kwaveinfo/kwaveinfo/mainform.ui. При этом создаваемый файл перезапишет файл-пустышку созданный KDevelop. Закрываем Qt Designer и переходим в KDevelop.

Использование формы

Проект созданный мастером использует QWidget для отображения пустой формы. Чтобы подставить созданную форму надо сделать небольшие изменения в исходном тексте файлов kwaveinfo.h и kwaveinfo.cpp.

kwaveinfo.h, до:

#include [kapp.h]


#include [qwidget.h] 


/** KWaveInfo is the base class of the project */ 


class KWaveInfo : public QWidget


kwaveinfo.h, после:

#include [kapp.h] 


#include [qwidget.h] 


#include "mainform.h"


/** KWaveInfo is the base class of the project */ 


class KWaveInfo : public MainForm


kwaveinfo.cpp, до:

KWaveInfo::KWaveInfo(QWidget *parent, const char *name) : QWidget(parent, name)


kwaveinfo.cpp, после:

KWaveInfo::KWaveInfo(QWidget *parent, const char *name) : MainForm(parent, name)


Жирным шрифтом выделены изменения внесённые в код.

Использование слотов формы

Ранее в форме был определён слот ExitSlot().

В коде на языке C++ объявление слота выглядит так:

virtual void ExitSlot();


Таким образом, его можно переопределить в класс KWaveInfo, так как данный класс был порождён от класса MainForm. Переопределяем:

// Закрываем приложение 


void ExitSlot(void) 


{ 


	exit(0); 


}


Теперь напишем код для слота OpenSlot():

#include [qfiledialog.h] 


... 


// Отображаем диалог открытия файла 


QString Filename; 


Filename = QFileDialog::getOpenFileName( 


	"/mnt/winxp",               // Стартовый каталог 


	"Wave Files (*.wav)",       // Фильтр для файлов 


	this,                       // Указатель на форму 


	"openfiledialog",           // Имя диалога 


	"Choose a file to open...");// Заголовок диалога 


// Проверяем выбор пользователя 


if (Filename.isEmpty()) return; 


// Изменияем заголовок формы приложения 


setCaption(Filename + " - KWaveInfo"); 


// Вызываем функцию для отбработки указанного файла 


GetInfoFromFile(Filename);


Код для слота CloseSlot() будет выглядеть так:

// Удаляем информацию с формы 


DisableAllElements(); 


edDataSize->setText(""); 


edCompression->setText(""); 


edChannels->setText(""); 


edFrequency->setText(""); 


// Восстанавливаем заголовок формы 


setCaption("KWaveInfo");


Информация об авторе

Теперь напишем код, который будет выполняться при выборе меню Help->About:

void KWaveInfo::AboutSlot(void)


{


    QMessageBox::about(this, "About KWaveInfo",


        "KWaveInfo is a simple RIFF file format viewer\n\n"


        "Copyright 2003 Ruslan Popov aka RaD.\n"


        "This application is freeware. Use it as is. No warranty!\n\n"


        "For technical support, call 99-00-00 or see http://ts.kmc.ru/\n");


}


Управление элементами

Нижеприведённый код включает и отключает элементы на форме приложения:

void KWaveInfo::DisableAllElements(void)


{


    edDataSize->setEnabled(FALSE);


    edCompression->setEnabled(FALSE);


    edChannels->setEnabled(FALSE);


    edFrequency->setEnabled(FALSE);


}


void KWaveInfo::EnableAllElements(void)


{


    edDataSize->setEnabled(TRUE);


    edCompression->setEnabled(TRUE);


    edChannels->setEnabled(TRUE);


    edFrequency->setEnabled(TRUE);


}


Чтение заголовка WAV файла

Теперь можно прочитать заголовок WAV файла. Для этого создаётся структура описывающая формат заголовка и заполняется с помощью стандартных функций файловой системы. Полученные данные отображаются на форме приложения.

void KWaveInfo::GetInfoFromFile(QString Filename)


{


    // Variables


    RIFF sRiff;


    FILE *hF;


    QString Temp;


    // Get header of wave file


    if ((hF = fopen(Filename, "rb")) == NULL) return;


    if (fgets((char *)&sRiff, sizeof(RIFF), hF) == NULL) return;


    if (hF != NULL) fclose(hF);


    // Check file format


    if (strncmp(sRiff.Riff, "RIFF", 4) != 0) return;


    // Show information


    EnableAllElements();


    Temp = Temp.setNum(sRiff.DataSize);


    edDataSize->setText(Temp + " bytes");


    Temp = Temp.setNum(sRiff.CompressType);


    switch(sRiff.CompressType)


    {


        case 0x01: Temp += " / PCM"; break;


        case 0x02: Temp += " / MS ADPCM"; break;


        case 0x03: Temp += " / Float PCM"; break;


        case 0x06: Temp += " / a-law"; break;


        case 0x07: Temp += " / u-law"; break;


        case 0x11: Temp += " / IMA ADPCM"; break;


        case 0x12: Temp += " / Videologic"; break;


        case 0x13: Temp += " / Sierra ADPCM"; break;


        case 0x20: Temp += " / Yamaha ADPCM"; break;


        case 0x55: Temp += " / MPEG"; break;


        default: Temp += " / Unknown"; break;


    }


    edCompression->setText(Temp);


    Temp = Temp.setNum(sRiff.Channels);


    switch (sRiff.Channels)


    {


        case 1: Temp += " / Mono"; break;


        case 2: Temp += " / Stereo"; break;


        case 4: Temp += " / Quadro"; break;


        default: Temp += " / Unknown"; break;


    }


    edChannels->setText(Temp);


    Temp = Temp.setNum(sRiff.Frequency);


    edFrequency->setText(Temp + " Hz");


}


Внешний вид приложения

Вот так выглядит созданное приложение:

Приложение KWaveInfo

Исходный код приложения

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

Заголовочный файл

/***************************************************************************


                          kwaveinfo.h  -  description


                             -------------------


    begin                : Суб Янв 11 02:30:19 KRAT 2003


    copyright            : (C) 2003 by Ruslan Popov aka RaD


    email                : rad@kmc.ru


 ***************************************************************************/





/***************************************************************************


 *                                                                         *


 *   This program is free software; you can redistribute it and/or modify  *


 *   it under the terms of the GNU General Public License as published by  *


 *   the Free Software Foundation; either version 2 of the License, or     *


 *   (at your option) any later version.                                   *


 *                                                                         *


 ***************************************************************************/





#ifndef KWAVEINFO_H


#define KWAVEINFO_H





#ifdef HAVE_CONFIG_H


#include [config.h]


#endif





#include [kapp.h]


#include [qwidget.h]


#include "mainform.h"





#define UINT unsigned int


#define WORD unsigned short int


#define BYTE unsigned char





// RIFF WAVE header's structure


typedef struct {


    char    Riff[4];


    UINT    FileSize;


    char    Wave[4];


    char    Fmt[4];


    UINT    FormatSize;


    WORD    CompressType;


    WORD    Channels;


    UINT    Frequency;


    UINT    AvgBytesPerSec;


    WORD    BytesPerSample;


    WORD    BitsPerSample;


    char    Data[4];


    UINT    DataSize;


} RIFF, *PRIFF;





/** KWaveInfo is the base class of the project */


class KWaveInfo : public MainForm


{


  Q_OBJECT 


  public:


    /** construtor */


    KWaveInfo(QWidget* parent=0, const char *name=0);


    /** destructor */


    ~KWaveInfo();


    // Define form's slots


    void OpenSlot(void);


    void CloseSlot(void);


    void ExitSlot(void);


    void AboutSlot(void);


    // Define functions


    void DisableAllElements(void);


    void EnableAllElements(void);


    void GetInfoFromFile(QString Filename);


};


#endif


Исходный файл

/***************************************************************************


                          kwaveinfo.cpp  -  description


                             -------------------


    begin                : Суб Янв 11 02:30:19 KRAT 2003


    copyright            : (C) 2003 by Ruslan Popov aka RaD


    email                : rad@kmc.ru


 ***************************************************************************/





/***************************************************************************


 *                                                                         *


 *   This program is free software; you can redistribute it and/or modify  *


 *   it under the terms of the GNU General Public License as published by  *


 *   the Free Software Foundation; either version 2 of the License, or     *


 *   (at your option) any later version.                                   *


 *                                                                         *


 ***************************************************************************/





#include [qfiledialog.h]


#include [qlineedit.h]


#include [qmessagebox.h]





#include [stdio.h]


#include [string.h]





#include "kwaveinfo.h"





#define CAPTION "KWaveInfo"





KWaveInfo::KWaveInfo(QWidget *parent, const char *name) : MainForm(parent, name)


{


    DisableAllElements();


}





KWaveInfo::~KWaveInfo()


{


}





void KWaveInfo::OpenSlot(void)


{


    // Open File Dialog


    QString Filename;


    Filename = QFileDialog::getOpenFileName(


                            "/mnt/winxp",               // Start path


                            "Wave Files (*.wav)",       // Filter


                            this,                       // Pointer to parent window


                            "openfiledialog",           // Dialog's name


                            "Choose a file to open...");// Dialog's caption


    // Check existence of filename


    if (Filename.isEmpty()) return;


    // Set caption


    setCaption(Filename + " - " + CAPTION);


    // Get WAV format information


    GetInfoFromFile(Filename);


}





void KWaveInfo::CloseSlot(void)


{


    // Remove information


    DisableAllElements();


    edDataSize->setText("");


    edCompression->setText("");


    edChannels->setText("");


    edFrequency->setText("");


    // Set caption


    setCaption(CAPTION);


}





void KWaveInfo::ExitSlot(void)


{


    // Exit application


    exit(0);


}





void KWaveInfo::AboutSlot(void)


{


    QMessageBox::about(this, "About KWaveInfo",


        "KWaveInfo is a simple RIFF file format viewer\n\n"


        "Copyright 2003 Ruslan Popov aka RaD.\n"


        "This application is freeware. Use it as is. No warranty!\n\n"


        "For technical support, call 99-00-00 or see http://ts.kmc.ru/\n");


}





void KWaveInfo::DisableAllElements(void)


{


    edDataSize->setEnabled(FALSE);


    edCompression->setEnabled(FALSE);


    edChannels->setEnabled(FALSE);


    edFrequency->setEnabled(FALSE);


}





void KWaveInfo::EnableAllElements(void)


{


    edDataSize->setEnabled(TRUE);


    edCompression->setEnabled(TRUE);


    edChannels->setEnabled(TRUE);


    edFrequency->setEnabled(TRUE);


}





void KWaveInfo::GetInfoFromFile(QString Filename)


{


    // Variables


    RIFF sRiff;


    FILE *hF;


    QString Temp;


    // Get header of wave file


    if ((hF = fopen(Filename, "rb")) == NULL) return;


    if (fgets((char *)&sRiff, sizeof(RIFF), hF) == NULL) return;


    if (hF != NULL) fclose(hF);


    // Check file format


    if (strncmp(sRiff.Riff, "RIFF", 4) != 0) return;


    // Show information


    EnableAllElements();


    Temp = Temp.setNum(sRiff.DataSize);


    edDataSize->setText(Temp + " bytes");


    Temp = Temp.setNum(sRiff.CompressType);


    switch(sRiff.CompressType)


    {


        case 0x01: Temp += " / PCM"; break;


        case 0x02: Temp += " / MS ADPCM"; break;


        case 0x03: Temp += " / Float PCM"; break;


        case 0x06: Temp += " / a-law"; break;


        case 0x07: Temp += " / u-law"; break;


        case 0x11: Temp += " / IMA ADPCM"; break;


        case 0x12: Temp += " / Videologic"; break;


        case 0x13: Temp += " / Sierra ADPCM"; break;


        case 0x20: Temp += " / Yamaha ADPCM"; break;


        case 0x55: Temp += " / MPEG"; break;


        default: Temp += " / Unknown"; break;


    }


    edCompression->setText(Temp);


    Temp = Temp.setNum(sRiff.Channels);


    switch (sRiff.Channels)


    {


        case 1: Temp += " / Mono"; break;


        case 2: Temp += " / Stereo"; break;


        case 4: Temp += " / Quadro"; break;


        default: Temp += " / Unknown"; break;


    }


    edChannels->setText(Temp);


    Temp = Temp.setNum(sRiff.Frequency);


    edFrequency->setText(Temp + " Hz");


}


Интернационализация приложения

Что такое i18n

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

Как KDE поддерживает интернационализацию

Одной из задач KDE является предоставление приложений пользователю на его родном языке и упростить для разработчика процесс перевода своего приложения.

Технически, это реализовано в стандарте KDE File System, который включает в себя поддержку локализации и предоставляет приложениям поддержку интернационализации через использование класса KLocale библиотеки KDE-core.

Со стороны разработчика надо сделать следующее:

  • Подключить kapp.h к исходному коду;
  • При работе с отображаемым текстом заключать его в i18n() макрос;
  • При использовании локали использовать klocale() макрос.

Это всё, что надо будет помнить при программировании. Отметьте, что не следует интернационализировать конфигурационные строки, которые используются KConfig.

Добавляем язык в проект

KDevelop облегчает жизнь разработчика. При создании проекта в главный каталог проекта добавляется каталог po. Затем в этот каталог добавляется kwaveinfo.pot (что-то я не нашёл такого у себя). Этот файл должен содержать все строки для которых был использован i18n макрос, теперь вы должны писать свой код, не забывая использовать i18n макрос при работе с отображаемыми строками. Добавлять строки в файл надо время от времени с помощью меню Project->Make messages and merge.

Добавить новый язык для приложения можно с помощью меню Project->Add new Translation File, в появившемся окне надо выбрать требующийся язык. Соответствующий, в нашем случае русский, файл ru.po появится в каталоге po проекта.

После этого в дереве KWaveInfo, которое находится на вкладке Groups, в элементе Translations появится элемент ru.po. Кликнем на него, на вопрос открывать файл как текст, скажем нет. Откроется приложение KBabel...

Но вся проблема в том, что при создании файла перевода у меня выскакивает следующая ошибка:

gmake -f admin/Makefile.common package-messages 
gmake[1]: Вход в каталог `/root/ProjectsQt/kwaveinfo'
./kwaveinfo
./kwaveinfo has *.rc or *.ui files, but not correct messages line
gmake[2]: Вход в каталог `/root/ProjectsQt/kwaveinfo/kwaveinfo'
xgettext: ошибка открытия файла "/include/kde.pot" для чтения: Нет такого файла или каталога
gmake[2]: *** [messages] Ошибка 1
gmake[2]: Выход из каталог `/root/ProjectsQt/kwaveinfo/kwaveinfo'
gmake[1]: Выход из каталог `/root/ProjectsQt/kwaveinfo'
gmake -C po merge
gmake[1]: Вход в каталог `/root/ProjectsQt/kwaveinfo/po'
gmake -f ../admin/Makefile.common package-merge POFILES="ru.po" PACKAGE=kwaveinfo
gmake[2]: Вход в каталог `/root/ProjectsQt/kwaveinfo/po'
admin/cvs.sh: admin/cvs.sh: Нет такого файла или каталога
gmake[2]: *** [package-merge] Ошибка 127
gmake[2]: Выход из каталог `/root/ProjectsQt/kwaveinfo/po'
gmake[1]: *** [merge] Ошибка 2
gmake[1]: Выход из каталог `/root/ProjectsQt/kwaveinfo/po'
gmake: *** [package-messages] Ошибка 2
*** failed ***

Надо как-то решить проблему! Наверное, я просто дождусь выхода следующей версии KDevelop.

Заключение

Надеюсь эта обзорная статья показала, что под Linux/Qt/KDE можно создавать приложения, пользуясь достаточно удобной интегрированной средой разработки.