Sources.RU Magazine Поиск по журналу
 

TopList

Введение в 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 можно создавать приложения, пользуясь достаточно удобной интегрированной средой разработки.



 Design by Шишкин Алексей (Лёха)  ©2004-2008 by sources.ru