• Введение в KDE и Qt
  • Установка Qt
  • Сигналы и слоты
  • Виджеты Qt
  • QLineEdit
  • Кнопки Qt
  • QComboBox
  • QListView
  • Диалоговые окна
  • QDialog
  • QMessageBox
  • QInputDialog
  • Применение qmake для упрощения написания make-файлов
  • Создание меню и панелей инструментов с помощью KDE
  • Приложение для работы с базой данных компакт-дисков с использованием KDE/Qt
  • MainWindow
  • AddCdDialog
  • LogonDialog
  • main.cpp
  • Резюме 
  • Глава 17

    Программирование в KDE с помощью Qt

    В главе 16 вы познакомились с библиотеками GUI графической среды GNOME/GTK+, предназначенными для создания пользовательского графического интерфейса под управлением системы X. Эти библиотеки — лишь половина истории, другой крупный игрок на поле GUI в системе Linux — графическая среда KDE/Qt, и в этой главе мы рассмотрим ее библиотеки и увидим, как они развиваются в условиях конкуренции.

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

    В этой главе мы обсудим следующие темы:

    □ знакомство с комплектом инструментов Qt;

    □ установка Qt;

    □ практическое применение;

    □ механизм "сигнал/слот";

    □ виджеты Qt;

    □ диалоговые окна;

    □ создание меню и панелей инструментов с помощью KDE;

    □ разработка средствами KDE/Qt вашего приложения для работы с базой данных компакт-дисков.

    Введение в KDE и Qt

    KDE (Desktop Environment, K-среда рабочего стола) — графическая среда рабочего стола с открытым программным кодом, основанная на библиотеке графического пользовательского интерфейса Qt. В состав KDE входит множество приложений и утилит, включая полный офисный пакет, Web-обозреватель и даже полнофункциональную IDE (интегрированная среда разработки) для программирования приложений KDE/Qt (KDevelop обсуждалась в главе 9). Профессиональное признание функциональных возможностей развитых приложений KDE пришло, когда компания Apple выбрала Web-обозреватель KDE в качестве ядра основного Web-обозревателя для системы Mac OS X, названного Safari и известного как очень быстрый обозреватель.

    Главная страница проекта KDE находится по адресу http://www.kde.org, на ней вы найдете подробные сведения, файлы загрузки среды KDE и ее приложения, документацию, сможете присоединиться к списку адресатов файлов рассылок и получить другую информацию для разработчиков.

    Примечание

    Во время написания книги последней версией KDE была версия 3.5.7, и поскольку эта версия включена в современные дистрибутивы Linux, мы считаем, что у вас установлена версия KDE 3.5 или более свежая. Продолжается работа над крупным обновлением — KDE 4.0. Вы можете также загрузить из Интернета предварительные версии KDE 4.0. Точно так же самая свежая версия Qt — 4.3, но в большинстве дистрибутивов Linux установлена версия Qt 3, например версия 3.3, как стандартная версия Qt. В этой главе обсуждается Qt 3.3, потому что она встречается чаще других.

    С точки зрения программиста, KDE предлагает десятки виджетов KDE, обычно унаследованных от их аналогов Qt, но улучшенных и облегчающих использование. Виджеты KDE обеспечивают более тесную связь с рабочим столом KDE, чем сам по себе комплект инструментов Qt; у вас появляется, например, возможность управления сеансами.

    Qt — тщательно продуманный межплатформный комплект инструментов GUI, написанный на языке С++. Это детище норвежской компании Trolltech, разрабатывающей, продающей и осуществляющей техническую поддержку Qt и сопутствующего программного обеспечения для промышленного рынка. Trolltech во всеуслышание рекламирует межплатформные возможности комплекта Qt, которые бесспорно впечатляющи, Qt поддерживается в ОС Linux и модификациях UNIX, Windows, Mac OS X и даже на встроенных платформах, явно отдающих предпочтение комплекту Qt по сравнению с его конкурентами.

    Примечание

    Специализированная версия Qt выполняется на сотовых телефонах. Еще одна версия работает на PDA (Personal Digital Assistant, электронный секретарь) Sharp Zaurus и аналогичных платформах. Qt Jambi представляет собой версию комплекта инструментов для языка Java.

    В настоящее время компания Trolltech продает коммерческие версии Qt случайным пользователям и любителям по завышенным ценам. К счастью, Trolltech понимает важность бесплатной версии для сообщества, распространяющего свободное программное обеспечение, и предлагает свободно распространяемую версию Qt Open Source Edition для. ОС Linux, Windows и Mac OS X. В ответ Trolltech получает большую базу пользовательских установок, обширное сообщество программистов и высокое реноме своего продукта.

    Qt Open Source Edition распространяется на условиях лицензии GPL, т.е. вы можете программировать, используя библиотеки-Qt, и распространять бесплатно собственное программное обеспечение, отвечающее требованиям лицензии GPL. Насколько мы можем судить, у свободно распространяемой версии есть два основных отличия от коммерческих версий: отсутствие технической поддержки и запрет на применение программного обеспечения Qt в коммерческих приложениях. Вся необходимая вам документация по API есть на Web-сайте Trolltech по адресу http://www.trolltech.com.

    Установка Qt

    Если у вас нет особых причин для компиляции из исходного программного кода, самый простой путь — найти для вашего дистрибутива двоичный пакет или пакет RPM. Дистрибутив Fedora Linux 7 поставляется с пакетом qt-3.3.8-4.i386.rpm, который можно установить с помощью следующей команды.

    $ rpm -Uvh qt-3.3.3-4.i386.rpm

    Комплект Qt и библиотеки программирования KDE можно также установить с помощью приложения Package Manager (Диспетчер пакетов) — рис. 17.1.

    Рис. 17.1 


    Если вы хотите загрузить из Интернета исходный программный код и сформировать Qt самостоятельно, самый свежий программный код можно получить с FTP-сайта Trolltech по адресу ftp://ftp.trolltech.com/qt/source/. Пакет исходного программного кода приходит с подробнейшими инструкциями, касающимися компиляции и установки Qt и хранящимися в файле INSTALL, упакованном программой tar.

    $ сd /usr/local

    $ tar -xvzf qt-x11-free-3.3.8.tar.gz

    $ ./configure

    $ make

    Также следует добавить в файл /etc/ld.so.conf следующую строку:

    /usr/lib/qt-3.3/lib

    Вставить ее можно в любое место файла.

    Примечание

    В системах Linux Fedora и Red Hat эту строку нужно сохранить в файле /etc/ld.so.conf.d/qt-i386.conf. Если вы устанавливали Qt, как показано на рис. 17.1, этот этап уже будет пройден.

    Если комплект Qt установлен корректно, переменная окружения

    QTDIR
    будет содержать каталог установки. Проверить это можно следующим образом:

    $ echo $QTDIR

    /usr/lib/qt-3.3

    Убедитесь также в том, что каталог lib добавлен в файл /etc/ld.so.conf. Затем выполните как суперпользователь следующую команду:

    # ldconfig

    Испытайте простейшую программу с применением Qt и убедитесь в том, что ваша установка функционирует должным образом (упражнение 17.1).

    Упражнение 17.1. Окно
    QMain

    Введите (или скопируйте и вставьте программный код из загруженного файла) приведенную далее программу и назовите ее qt1.cpp:

    #include <qapplication.h>

    #include <qmainwindow.h>


    int main(int argc, char **argv) {

     QApplication app(argc, argv);

     QMainWindow* window = new QMainWindow();

     app.setMainWidget(window);

     window->show();

     return app.exec();

    }

    При компиляции вам необходимо указать Qt-каталоги include и lib:

    $ g++ -о qt1 qt1.cpp -I$QTDIR/include -L$QTDIR/lib -lqui

    Примечание

    На некоторых платформах в конце строки указывается библиотека

    -lqt
    . В версии Qt 3.3, тем не менее, используйте
    -lqui
    .

    Выполнив приложение, вы должны получить окно Qt (рис. 17.2).

    $ ./qtl

    Рис. 17.2 


    Как это работает

    В отличие от GTK+ здесь нет вмещающего в себя все заголовочного файла qt.h, поэтому вы должны явно включать заголовочные файлы всех используемых объектов.

    Первый объект, с которым вы встречаетесь, —

    QApplication
    . Это главный объект Qt, который вы должны сформировать, передав ему в самом начале аргументы командной строки. У каждого приложения Qt должен быть один и только один объект типа
    QApplication
    , который вы должны создать перед тем, как делать что-то еще.
    QApplication
    имеет дело с внутренними встроенными операциями Qt, такими как обработка событий, размещение строк и управление внешним представлением.

    Вы будете применять два метода

    QApplication
    :
    setMainWidget
    , который создает главный виджет вашего приложения, и exec, который запускает выполнение цикла отслеживания событий. Метод exec не возвращает управление до тех пор, пока либо не будет вызван метод
    QApplication::quit()
    , либо не будет закрыт главный виджет.

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

    Далее мы обсудим механизм программирования, управляемого событиями, и вы вставите в приложение виджет

    PushButton
    .

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

    Как вы видели в главе 16, сигналы и их обработка — главные механизмы, используемые приложениями GUI для реагирования на ввод пользователя, и ключевые функции библиотек GUI. Механизм обработки сигналов комплекта Qt состоит из сигналов и слотов или приемников, называемых сигналами и функциями обратного вызова в комплекте инструментов GTK+ или событиями и обработчиками событий в языке программирования Java.

    Примечание

    Имейте в виду, что сигналы Qt отличаются от сигналов UNIX, обсуждавшихся в главе 11.

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

    clicked
    ,
    text_changed
    или
    key_pressed
    . Как правило, вам захочется сделать что-то в ответ на действие пользователя, например, сохранить документ или выйти из приложения, и вы выполняете это, связав сигнал с функцией обратного вызова или слотом на языке Qt.

    Применение сигналов и слотов довольно специфично — Qt определяет два новых соответствующим образом описанных псевдоключевых слова,

    signals
    и
    slots
    для обозначения в вашем программном коде классов сигналов и слотов. Это замечательно с точки зрения читаемости и сопровождения программного кода, но вы вынуждены пропускать свой код через отдельный этап препроцессорной обработки для поиска и замены этих псевдоключевых слов дополнительным кодом на языке С++.

    Примечание

    Таким образом, программный код с использованием Qt — не настоящий программный код на С++. Порой это становится проблемой для некоторых разработчиков. См. документацию Qt на Web-сайте http://doc.trolltech.com/, чтобы понять причину применения этих новых псевдоключевых слов в С++. Более того, применение сигналов и слотов не так уж отличается от Microsoft Foundation Classes (MFC, библиотека базовых классов Microsoft) в ОС Windows, в которой также используется модифицированное определение языка С++.

    На способы применения сигналов и слотов в Qt есть несколько ограничений, но они не слишком существенные:

    □ сигналы и слоты должны быть функциями-методами класса-потомка

    QObject
    ;

    □ при использовании множественного наследования

    QObject
    должен быть первым в списке класса;

    □ оператор

    Q_OBJECT
    должен появляться первым в объявлении класса;

    □ сигналы нельзя применять в шаблонах;

    □ указатели на функцию не могут использоваться как аргументы в сигналах и слотах;

    □ сигналы и слоты не могут переопределяться или обновляться до статуса

    public
    (общедоступный).

    Поскольку вы должны писать ваши сигналы и слоты как потомков объекта

    QObject
    , логично создавать ваш интерфейс, расширяя и настраивая виджет, начиная с
    QWidget
    , базового виджета Qt, потомка виджета
    QObject
    . В комплекте Qt вы почти всегда будете создавать интерфейсы, расширяя такие виджеты, как
    QMainWindow
    .

    Типичное определение класса в файле MyWindow.h для вашего GUI будет напоминать приведенное далее:

    class MyWindow : public QMainWindow {

     Q_OBJECT

    public:

     MyWindow();

     virtual ~MyWindow();

    signals:

     void aSignal();

    private slots:

     void doSomething();

    }

    Ваш класс — наследник объекта

    QMainWindow
    , который определяет функциональные возможности главного окна в приложении. Аналогичным образом при создании диалогового окна вы определите подкласс
    QDialog
    . Первым указан оператор
    Q_OBJECT
    , действующий как метка для препроцессора, за которым следуют обычные объявления конструктора и деструктора. Далее даны определения сигнала и слота.

    У вас есть один сигнал и один слот, оба без параметров. Для порождения сигнала

    aSignal()
    вам нужно всего лишь в любом месте программы вызвать функцию
    emit
    :

    emit aSignal();

    Это означает, что все остальное обрабатывается Qt. Вам даже не потребуется реализация

    aSignal()
    .

    Для применения слотов их нужно связать с сигналом. Делается это соответствующим образом с помощью названного статического метода

    connect
    класса
    QObject
    :

    bool QObject::connect(const QObject * sender, const char* signal,

     const QObject * receiver, const char * member);

    Просто передайте объект, владеющий сигналом (отправитель), функцию сигнала, объект, владеющий слотом (приемником), и в завершение укажите имя слота.

    В примере MyWindow, если бы вы захотели связать сигнал

    clicked
    виджета
    QPushButton
    с вашим слотом
    doSomething
    , вы бы написали:

    connect(button, SIGNAL(clicked()), this, SLOT(doSomething()));

    Учтите, что необходимо применять макросы

    SIGNAL
    и
    SLOT
    для выделения функций сигналов и слотов. Как и в комплекте GTK+, вы можете связать ряд слотов с заданным сигналом и также связать слот с любым количеством сигналов с помощью множественных вызовов функции connect. Если она завершается аварийно, то возвращает
    FALSE
    .

    Остается реализовать ваш слот в виде обычной функции-метода:

    void MyWindow::doSomething() {

     // Код слота

    }

    Выполните упражнение 17.2.

    Упражнение 17.2. Сигналы и слоты

    Теперь, зная основы использования сигналов и слотов, применим их в примере. Усовершенствуйте

    QMainWindow
    , вставьте в него кнопку и свяжите сигнал кнопки
    clicked
    со слотом.

    1. Введите следующее объявление класса и назовите файл ButtonWindow.h:

    #include <qmainwindow.h>


    class ButtonWindow : public QMainWindow {

     Q_OBJECT

    public:

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

     virtual ~ButtonWindow();

    private slots:

     void Clicked();

    };

    2. Далее следует реализация класса в файле ButtonWindow.cpp:

    #include "ButtonWindow.moc"

    #include <qpushbutton.h>

    #include <qapplication.h>

    #include <iostream>

    3. В конструкторе вы задаете заголовок окна, создаете кнопку и связываете сигнал нажатия кнопки с вашим слотом.

    setCaption
    — метод объектов типа
    QMainWindow
    , который, что неудивительно, задает заголовок окна:

    ButtonWindow::ButtonWindow(QWidget *parent, const char* name) : QMainWindow(parent, name) {

     this->setCaption("This is the window Title");

     QPushButton *button = new QPushButton("Click Me!", this, "Button1");

     button->setGeometry(50, 30, 70, 20);

     connect(button, SIGNAL(clicked()), this, SLOT(Clicked()));

    }

    4. Qt автоматически удаляет виджеты, поэтому ваш деструктор пуст:

    ButtonWindow::~ButtonWindow() {}

    5. Затем реализация слота:

    void ButtonWindow::Clicked(void) {

     std::cout << "clicked!\n";

    }

    6. И наконец, в функции

    main
    вы просто создаете экземпляр типа
    ButtonWindow
    , делаете его главным окном вашего приложения и отображаете окно на экране:

    int main(int argc, char **argv) {

     QApplication app(argc, argv);

     ButtonWindow *window = new ButtonWindow();

     app.setMainWidget(window);

     window->show();

     return app.exec();

    }

    7. Прежде чем вы сможете откомпилировать данный пример, необходимо запустить препроцессор для заголовочного файла. Программа этого препроцессора называется Meta Object Compiler (moc, компилятор метаобъекта) и должна быть включена в пакет комплекта Qt. Выполните

    moc
    для файла ButtonWindow.h, сохранив результат в файле ButtonWindow.moc:

    $ moc ButtonWindow.h -о ButtonWindow.moc

    Теперь можно компилировать как обычно, скомпоновав с результатом команды

    moc
    .

    $ g++ -о button ButtonWindow.срр -I$QTDIR/include -L$QTDIR/lib -lqui

    Выполнив программу, вы получите пример, показанный на рис. 17.3.

    Рис. 17.3 


    Как это работает

    В этом примере мы ввели новый виджет и некоторые новые функции, поэтому давайте их рассмотрим.

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

    Конструктор объекта

    QPushButton
    очень прост.

    QPushButton::QPushButton(const QString &text, QWidget *parent,

     const char* name=0);

    Первый аргумент — текст метки кнопки, далее родительский виджет и последний аргумент — имя кнопки, обычно применяемое Qt для внутренних операций.

    Параметр родительского виджета, общий для всех объектов, —

    QWidget
    , он управляет отображением и уничтожением и разными другими свойствами. Передача
    NULL
    в качестве родительского объекта означает виджет верхнего уровня, при этом создается содержащее его пустое окно. В примере вы передаете текущий объект
    ButtonWindow
    с помощью ключевого слова
    this
    , что приводит к вставке кнопки в основную область окна
    ButtonWindow
    .

    Аргумент

    name
    задает имя виджета для внутреннего использования Qt. Если комплект Qt обнаружит ошибку, имя виджета будет выведено в сообщении об ошибке, поэтому неплохо выбирать подходящие имена виджетов, поскольку при отладке это сбережет массу времени.

    Вы могли заметить, что объект

    QPushButton
    очень примитивно вставляется в окно
    ButtonWindow
    , с помощью параметра parent конструктора
    QPushButton
    , без указания положения кнопки, ее размера, рамки или чего-либо еще. Если вы хотите управлять внешним видом кнопки, что очень важно для создания привлекательного интерфейса, следует применять виджеты компоновки комплекта Qt. Давайте их сейчас рассмотрим,

    В Qt есть целый ряд способов размещения и компоновки виджетов. Вы уже видели использование абсолютных координат с помощью вызова

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

    Предпочтительный метод компоновки виджетов — применение классов

    QLayout
    или виджетов-контейнеров, которые изменяют свои размеры соответствующим образом после задания им подсказок, касающихся отступов и расстояний между виджетами.

    Ключевое различие между классами

    QLayout
    и упаковочными контейнерами заключается в том, что объекты класса
    QLayout
    не являются виджетами.

    Классы компоновки — потомки объектов, типа

    QObject
    , а не
    QWidget
    , поэтому их применение ограничено. Например, вы не можете создать объект
    QVBoxLayout
    — основной виджет объекта
    QMainWindow
    .

    Виджеты упаковочных контейнеров (такие, как

    QHBox
    и
    QVBox
    ) напротив — потомки объекта типа
    QWidget
    следовательно, вы можете применять их как обычные виджеты. Возможно, вас удивляет, что в Qt есть и классы
    QLayout
    , и виджеты
    QBox
    с дублирующимися функциональными возможностями. На самом деле виджеты
    QBox
    существуют только для удобства и по существу служат оболочкой классов
    QLayout
    в типе
    QWidget
    . Объекты
    QLayout
    обладают возможностью автоматического изменения размеров, в то время как размеры виджетов нужно изменять вручную с помощью вызова метода
    QWidget::resizeEvent()
    .

    Подклассы

    QLayout
    :
    QVBoxLayout
    и
    QHBoxLayout
    , — самый распространенный способ создания интерфейса, и именно их вы будете чаще всего встречать в программном коде с применением Qt.

    QVBoxLayout
    и
    QHBoxLayout
    — невидимые объекты-контейнеры, хранящие другие виджеты и схемы размещения с вертикальной и горизонтальной ориентациями соответственно. Вы сможете создавать сколь угодно сложные компоновки виджетов, поскольку допускается использование вложенных компоновок, например, за счет вставки как элемента горизонтальной схемы размещения внутрь вертикального упаковочного контейнера.

    Есть три конструктора

    QVBoxLayout
    , заслуживающих внимания (у объектов
    QHBoxLayout
    идентичный API).

    QVBoxLayout::QVBoxLayout(QWidget *parent, int margin, int spacing,

     const char *name)

    QVBoxLayout::QVBoxLayout(QLayout *parentLayout, int spacing,

     const char * name)

    QVBoxLayout::QVBoxLayout(int spacing, const char *name)

    Родителем объекта

    QLayout
    может быть либо виджет, либо другой объект типа
    QLayout
    . Если не задавать родительский объект, вы сможете только вставить позже данную схему размещения в другой объект
    QLayout
    с помощью метода
    addLayout
    .

    Параметры margin и spacing задают пустое пространство в пикселах вокруг схемы размещения

    QLayout
    и между отдельными виджетами в ней.

    После создания вашей схемы размещения

    QLayout
    вы можете вставлять дочерние виджеты или схемы с помощью следующей пары методов:

    QBoxLayout::addWidget(QWidget *widget, int stretch = 0, int alignment = 0);

    QBoxLayout::addLayout(QLayout *layout, int stretch = 0);

    Выполните упражнение 17.3.

    Упражнение 17.3. Применение классов
    QBoxLayout

    В этом примере вы увидите в действии классы

    QBoxLayout
    при размещении виджетов
    QLabel
    в окне
    QMainWindow
    .

    1. Сначала введите заголовочный файл LayoutWindow.h:

    #include <qmainwindow.h>


    class LayoutWindow : public QMainWindow {

     QOBJECT

    public:

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

    virtual ~LayoutWindow();

    };

    2. Теперь введите реализацию в файл LayoutWindow.cpp:

    #include <qapplication.h>

    #include <qlabel.h>

    #include <qlayout.h>


    #include "LayoutWindow.moc"

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

     QMainWindow(parent, name) {

     this->setCaption("Layouts");

    3. Необходимо создать фиктивный

    QWidget
    для хранения объекта
    QHBoxLayout
    , поскольку его нельзя напрямую вставить в объект
    QMainWindow
    :

     QWidget *widget = new QWidget(this);

     setCentralWidget(widget);

     QHBoxLayout *horizontal = new QHBoxLayout(widget, 5, 10, "horizontal");

     QVBoxLayout *vertical = new QVBoxLayout();

     QLabel* label1 = new QLabel("Top", widget, "textLabel1");

     QLabel* label2 = new QLabel("Bottom", widget, "textLabel2");

     QLabel* label3 = new QLabel("Right", widget, "textLabel3");

     vertical->addwidget(label1);

     vertical->addwidget(label2);

     horizontal->addLayout(vertical);

     horizontal->addWidget(label3);

     resize(150, 100);

    }


    LayoutWindow::~LayoutWindow() { }


    int main(int argc, char **argv) {

     QApplication app(argc, argv);

     LayoutWindow *window = new LayoutWindow();

     app.setMainWidget(window);

     window->show();

     return app.exec();

    }

    Как и прежде, перед компиляцией нужно выполнить

    moc
    для заголовочного файла:

    $ moc LayoutWindow.h -о LayoutWindow.moc

    $ g++ -о layout LayoutWindow.cpp -I$QTDIR/include -L$QTDIR/lib -lqui

    Выполнив эту программу, вы получите схему размещения ваших меток QLabel (рис. 17.4). Попробуйте изменить величину окна и посмотрите, как расширяются и сжимаются метки, заполняя все доступное пространство.

    Рис. 17.4


    Как это работает

    Программа LayoutWindow.cpp создает два виджета упаковочных контейнеров, горизонтальный и вертикальный контейнер для размещения виджетов. Вертикальный контейнер получает две метки, описанные, соответственно, как

    Top
    и
    Bottom
    . Горизонтальный контейнер также содержит два виджета, метку, обозначенную
    Right
    , и вертикальный контейнер. Вы можете помещать компоновочные виджеты внутрь других компоновочных виджетов, как показано в данном примере.

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

    Мы рассмотрели основы применения Qt — сигналы и слоты, команду moc и средства компоновки. Теперь пора более внимательно изучить виджеты.

    Виджеты Qt

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

    QLineEdit

    QLineEdit
    — виджет для ввода однострочного текста. Применяйте его для ввода коротких фрагментов текста, таких как имя пользователя. В виджете
    QLineEdit
    можно ограничить длину вводимого текста с помощью маски ввода, предлагающей заполнить шаблон, или для дополнительного контроля можно применить функцию проверки допустимости, например, чтобы убедиться в том, что пользователь вводит корректные дату, номер телефона или подобные величины. У виджета
    QLineEdit
    есть функции редактирования, позволяющие выбирать части текста, вырезать и вставлять, отменять и повторять изменения, как командами пользователя, так и средствами API.

    Далее перечислены конструкторы и наиболее полезные методы.

    #include <qlineedit.h>

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

    QLineEdit::QLineEdit(const QString& contents, QWidget *parent,

     const char *name = 0);

    QLineEdit::QLineEdit(const QString& contents, const QString& inputMask,

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

    void setInputMask(const QString& inputMask);

    void insert(const QString& newText);

    bool isModified(void);

    void setMaxLength(int length);

    void setReadOnly(bool read);

    void setText(const QString &text);

    QString text(void);

    void setEchoMode(EchoMode mode);

    В конструкторах вы задаете как обычно родительский виджет и имя виджета с помощью параметров

    parent
    и
    name
    .

    Интересно свойство

    EchoMode
    , определяющее способ отображения текста в виджете. Оно может принимать одно из трех значений:

    QLineEdit::Normal
    — отображать вводимые символы (по умолчанию);

    □ 

    QLineEdit::Password
    — отображать звездочки на месте символов;

    □ 

    QLineEdit::NoEcho
    — ничего не отображать. Задается режим отображения с помощью метода
    setEchoMode
    :

    lineEdit->setEchoMode(QLineEdit::Password);

    Усовершенствование, внесенное в версию Qt 3.2, — свойство

    inputMask
    , ограничивающее ввод в соответствии с правилом маски.

    inputMask
    — это строка, сформированная из символов, каждый из которых соответствует правилу, принимающему диапазон определенных символов. Если вы знакомы с регулярными выражениями,
    inputMask
    использует во многом тот же самый принцип.

    Есть два сорта символов, формирующих

    inputMask
    : первые указывают на необходимость присутствия определенного символа, вторые при наличии символа добиваются его соответствия заданному правилу. В табл. 17.1 приведены примеры таких символов и их значения.


    Таблица 17.1

    Обязательный символ Символы, которые разрешены, но не обязательны Значение
    A a Символы ASCII А–Z, а–z
    N n Символы ASCII A–Z, a–z, 0–9
    X x Любой символ
    9 0 Цифры 0–9
    D d Цифры 1–9

    Наша

    inputMask
    — это строка, сформированная комбинацией этих символов и необязательно завершающаяся точкой с запятой. Существуют дополнительные специальные символы, у которых также есть значения (табл. 17.2).


    Таблица 17.2

    Символ Значение
    #
    Разрешен, но не обязателен знак
    +/-
    >
    Преобразует все последующие введенные символы в символы верхнего регистра.
    <
    Преобразует все последующие введенные символы в символы нижнего регистра
    !
    Останавливает преобразование регистра
    \
    Символ управляющей последовательности для применения специальных символов в качестве разделителей

    Все остальные символы в

    inputMask
    действуют как разделители в поле ввода
    QLineEdit
    .

    В табл. 17.3 приведены примеры масок ввода и соответствующий им текст для ввода.


    Таблица 17.3

    Пример Допустимый ввод
    "AAAAAA-999D"
    Допустимо Athens-2004, но не Sydney-2000 или Atlanta-1996
    "ААААnn-99-99;"
    Допустимо March-03-12, но не Мау-03-12 или September-03-12
    "000.000.000.000"
    Допустим IP-адрес, например, 192.168.0.1

    Выполните упражнение 17.4.

    Упражнение 17.4. Виджет
    QLineEdit

    Посмотрим, как действует виджет

    QLineEdit
    .

    1. Сначала — заголовочный файл LineEdit.h:

    #include <qmainwindow.h>

    #include <qlineedit.h>

    #include <qstring.h>


    class LineEdit : public QMainWindow {

     Q_OBJECT

    public:

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

     QLineEdit *password_entry;

    private slots:

     void Clicked();

    };

    2. LineEdit.cpp — уже знакомый файл реализации класса:

    #include "LineEdit.moc"

    #include <qpushbutton.h>

    #include <qapplication.h>

    #include <qlabel.h>

    #include <qlayout.h>

    #include <iostream>


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

     QMainWindow(parent, name) {

     QWidget *widget = new QWidget(this);

     setCentralWidget(widget);

    3. Для компоновки виджетов примените

    QGridLayout
    . Задайте число строк и столбцов, величины отступов и расстояния между виджетами:

     QGridLayout *grid = new QGridLayout(widget, 3, 2, 10, 10, "grid");

     QLineEdit *username_entry = new QLineEdit(widget, "username_entry");

     password_entry = new QLineEdit(widget, "password_entry");

     password_entry->setEchoMode(QLineEdit::Password);

     grid->addWidget(new QLabel("Username", widget, "userlabel"), 0, 0, 0);

     grid->addwidget(new QLabel("Password", widget, "passwordlabel"), 1, 0, 0);

     grid->addWidget(username_entry, 0, 1, 0);

     grid->addWidget(password_entry, 1, 1, 0);

     QPushButton *button = new QPushButton("Ok", widget, "button");

     grid->addWidget(button, 2, 1, Qt::AlignRight);

     resize(350, 200);

     connect(button, SIGNAL(clicked()), this, SLOT(Clicked()));

    }


    void LineEdit::Clicked(void) {

     std::cout << password_entry->text() << "\n";

    }


    int main(int argc, char **argv) {

     QApplication app(argc, argv);

     LineEdit *window = new LineEdit();

     app.setMainWidget(window);

     window->show();

     return app.exec();

    }

    Выполнив эту программу, вы должны получить результат, показанный на рис. 17.5.

    Рис. 17.5


    Как это работает

    Вы создали два виджета

    QLineEdit
    , один подготовили для ввода пароля, задав
    EchoMode
    , и заставили его выводить содержимое при щелчке мышью кнопки
    PushButton
    . Обратите внимание на виджет
    QGridLayout
    , который очень полезен для размещения виджетов в табличной сетке. Когда виджет вставляется в сетку таблицы, вы передаете номер строки и столбца, нумерация начинается с 0, нулевые номера строки и столбца у верхней левой ячейки.

    Кнопки Qt

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

    PushButton
    , флажки
    CheckBox
    и радиокнопки (или зависимые переключатели)
    RadioButton
    .

    QButton: базовый класс кнопок

    Все виджеты кнопок в комплекте Qt — потомки абстрактного класса

    QButton
    . У этого класса есть методы для опроса и переключения включенного/выключенного состояния кнопки и задания текста кнопки или ее графического представления.

    Вам никогда не придется обрабатывать виджет типа

    QButton
    (не путайте с виджетом
    QPushButton
    !), поэтому нет смысла приводить конструкторы. Далее перечислено несколько полезных функций-методов этого класса:

    #include <qbutton.h>

    virtual void QButton::setText(const QString&);

    virtual void QButton::setPixmap(const QPixmap&);

    bool QButton::isToggleButton() const;

    virtual void QButton::setDown(bool);

    bool QButton::isDown() const;

    bool QButton::isOn() const;

    enum QButton::ToggleState { Off, NoChange, On }

    ToggleState QButton::state() const;

    У функций

    isDown
    и
    isOn
    одно назначение. Обе они возвращают
    TRUE
    , если кнопка была нажата или активизирована.

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

    QWidget::setEnable(FALSE)
    .

    У 

    QButton
    есть три подкласса, заслуживающие внимания:

    □ 

    QPushButton
    — виджет простой кнопки, выполняющий некоторое действие при щелчке кнопкой мыши;

    □ 

    QCheckBox
    — виджет кнопки, способный изменять состояние с включенного на выключенное для обозначения некоторого выбора;

    □ 

    QRadioButton
    — виджет кнопки, обычно применяемый в группе таких же кнопок, только одна из которых может быть активна в любой момент времени.

    QPushButton

    QPushButton
    — стандартная кнопка общего вида, содержащая текст, такой как "OK" или "Cancel" и/или пиксельную пиктограмму. Как все кнопки класса
    QButton
    , она порождает при активизации сигнал
    clicked
    и обычно используется для связи со слотом и выполнения некоторого действия.

    Вы уже применяли кнопку

    QPushButton
    в примерах, и есть лишь еще одна интересная деталь, касающаяся этого простейшего из виджетов Qt. Кнопку
    QPushButton
    можно превратить из кнопки, не помнящей своего состояния, в кнопку-выключатель (т.е. способную быть включенной и выключенной), вызвав метод
    setToggleButton
    . (Если помните, у комплекта GTK+ из предыдущей главы есть для этих целей разные виджеты.)

    Далее для полноты описания приведены конструкторы и полезные методы.

    #include <qpushbutton.h>

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

    QPushButton(const QString& text, QWidget *parent, const char *name = 0);

    QPushButton(const QIconSet& icon, const QString& text,

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

    void QPushButton::setToggleButton(bool);

    QCheckBox

    QCheckBox
    — это кнопка, у которой есть состояние, ее можно включить и выключить (или установить и сбросить). Внешний вид
    QCheckBox
    зависит от стиля отображения окон текущей системы (Motif, Windows и т.д.), но обычно она отображается как флажок с сопроводительным текстом справа.

    Вы можете также перевести кнопку

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

    #include <qcheckbox.h>

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

    QCheckBox(const QString& text, QWidget *parent, const char *name = 0);

    bool QCheckBox::isChecked();

    void QCheckBox::setTristate(bool y = TRUE);

    bool QCheckBox::isTristate();

    QRadioButton

    Радиокнопки — кнопки-переключатели, применяемые для отображения исключающего выбора, когда можно выбрать только один вариант из группы представленных (вспомните снова старые автомобильные радиоприемники, в которых можно было нажать только одну кнопку блока). Сами по себе кнопки

    QRadioButton
    не многим отличаются от кнопок
    QCheckBox
    , поскольку группировка и исключительный выбор обрабатываются классом
    QButtonGroup
    , главное же их отличие заключается в том, что они отображаются как круглые кнопки, а не как флажки.

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

    #include <qbuttongroup.h>

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

    QButtonGroup(const QString& title, QWidget* parent = 0,

     const char * name = 0);

    int insert (QButton *button, int id = -1);

    void remove(QButton *button);

    int id(QButton *button) const;

    int count() const;

    int selectedId() const;

    Применять виджет

    QButtonGroup
    проще простого: он даже предлагает необязательную рамку вокруг кнопок, если используется конструктор
    title
    .

    Добавить кнопку в

    QButtonGroup
    можно с помощью метода
    insert
    или заданием
    QButtonGroup
    в качестве родительского виджета кнопки. Для уникального обозначения каждой кнопки в группе можно задать
    id
    в методе
    insert
    . Это особенно полезно при определении выбранной кнопки, т.к. функция
    selectedId
    возвращает
    id
    выбранной кнопки.

    Все кнопки

    QRadioButton
    , добавляемые в группу, автоматически становятся кнопками с исключающим выбором.

    Далее приведены прототипы конструкторов

    QRadioButton
    и одного уникального метода, который не вызовет большого удивления:

    #include <qradiobutton.h>

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

    QRadioButton(const QString& text, QWidget *parent, const char *name = 0);

    bool QRadioButton::isChecked();

    Выполните упражнение 17.5.

    Упражнение 17.5. Виджет
    QButton

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

    1. Введите файл Buttons.h:

    #include <qmainwindow.h>

    #include <qcheckbox.h>

    #include <qbutton.h>

    #include <qradiobutton.h>

    class Buttons : public CMainWindow {

     Q_OBJECT

    public:

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

    2. Вы запросите состояние ваших кнопок позже, в функции слота, поэтому объявите указатели кнопок и вспомогательную функцию

    PrintActive
    с атрибутом private в объявлении класса:

    private:

     void PrintActive(QButton *button);

     QCheckBox *checkbox;

     QRadioButton *radiobutton1, *radiobutton2;

    private slots:

     void Clicked();

    }

    3. Далее следует файл Buttons.срр:

    #include "Buttons.moc"

    #include <qbuttongroup.h>

    #include <qpushbutton.h>

    #include <qapplication.h>

    #include <qlabel.h>

    #include <qlayout.h>

    #include <iostream>


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

     QMainWindow(parent, name) {

     QWidget* widget = new QWidget(this);

     setCentralWidget(widget);

     QVBoxLayout *vbox = new QVBoxLayout(widget, 5, 10, "vbox");

     checkbox = new QCheckBox("CheckButton", widget, "check");

     vbox->addWidget(checkbox);

    4. Затем вы создаете

    QButtonGroup
    для двух ваших радиокнопок (переключателей).

     QButtonGroup *buttongroup = new QButtonGroup(0);

     radiobutton1 = new QRadioButton("RadioButton1", widget, "radio1");

     buttongroup->insert(radiobutton1);

     vbox->addWidget(radiobutton1);

     radiobutton2 = new QRadioButton("RadioButton2", widget, "radio2");

     buttongroup->insert(radiobutton2);

     vbox->addWidget(radiobutton2);

     QPushButton* button = new QPushButton("Ok", widget, "button");

     vbox->addWidget(button);

     resize(350, 200);

     connect(button, SIGNAL(clicked()), this, SLOT(Clicked()));

    }

    5. Затем приведен удобный метод для вывода состояния заданной кнопки

    QButton
    :

    void Buttons::PrintActive(QButton *button) {

     if (button->isOn())

      std::cout << button->name() << " is checked\n";

     else

      std::cout" << button->name() << " is not checked\n";

    }


    void Buttons::Clicked(void) {

     PrintActive(checkbox);

     PrintActive(radiobutton1);

     PrintActive(radiobutton2);

     std::cout << "\n";

    }


    int main(int argc, char **argv) {

     QApplication app(argc, argv);

     Buttons *window = new Buttons();

     app.setMainWidget(window);

     window->show();

     return app.exec();

    }

    Как это работает

    Этот простой пример показывает, как опрашивать виджеты кнопок Qt разных типов. После создания все они по большей части действуют одинаково. Например, функция

    PrintActive
    демонстрирует, как получить состояние кнопки (включена или выключена). Обратите внимание на то, как она действует в случае запоминающих состояние кнопок разных типов, таких как флажки и переключатели (радиокнопки). В основном отличаются только вызовы для создания виджета кнопки. Радиокнопки, наиболее сложные (т.к. только одна в группе может быть включена), при создании требуют больше всего работы. В случае радиокнопок вы должны создать
    QButtonGroup
    для того, чтобы гарантировать активность только одной радиокнопки в группе в любой момент времени.

    QComboBox

    Переключатели (радиокнопки) — отличный способ, позволяющий пользователю выбрать из небольшого числа вариантов, скажем шести или меньше. Если вариантов больше шести, ситуация начинает выходить из-под контроля и становится еще более напряженной, когда количество вариантов растет, что приводит к ощутимому увеличению размера окна. В этом случае прекрасным решением может быть использование поля ввода с раскрывающимся меню, также называемое раскрывающимся списком (combo box). Варианты выводятся, когда вы щелкаете кнопкой мыши и открываете меню и количество вариантов при этом ограничено только удобством поиска в списке.

    В виджете

    QComboBox
    сочетаются функциональные возможности виджетов
    QLineEdit
    и
    QPushButton
    и раскрывающихся меню, позволяя выбрать один вариант из неограниченного набора вариантов.

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

    При создании виджета

    QComboBox
    можно указать, открыт ли он для чтения и записи или только для чтения, задавая логическое значение в конструкторе:

    QComboBox *combo = new QComboBox(TRUE, parent, "widgetname");

    Передача значения

    TRUE
    переводит
    QComboBox
    в режим "чтение/запись". Остальные параметры — обычный указатель на родительский виджет и имя создаваемого виджета.

    Как все виджеты Qt,

    QComboBox
    обладает гибкостью и предлагает широкий набор функциональных возможностей.

    Вы можете добавлять варианты по одному или набором, как тип

    QString
    или в стандартном формате
    char*
    .

    Для вставки одного варианта вызовите функцию

    insertItem
    :

    combo->insertItem(QString("An Item"), 1);

    Приведенная функция принимает объект типа

    QString
    и номер позиции в списке. В данном случае 1 вставляет вариант в список первым.

    Для добавления в конец списка задайте любое отрицательное целое число.

    Гораздо чаще вы будете вставлять несколько элементов списка одновременно, для этого можно применить класс

    QStrList
    или, как показано далее, массив
    char*
    :

    char* weather[] = {"Thunder", "Lightning", "Rain", 0};

    combo->insertStrList(weather, 3);

    И снова вы можете задать номер позиции вставляемых в список элементов.

    Если в виджете

    QComboBox
    задан режим "чтение/запись", вводимые пользователем варианты могут автоматически вставляться в список. Это очень полезное, экономящее время свойство, избавляющее пользователя от повторного набора варианта, если он хочет уже введенный вариант использовать несколько раз.

    Метод

    InsertionPolicy
    управляет позицией вводимого в список элемента. Вы можете выбрать одно из значений, приведенных в табл. 17.4.


    Таблица 17.4

    Значение Действие
    QComboBox::AtTop
    Вставляет вводимый в список элемент первым
    QComboBox::AtBottom
    Вставляет вводимый в список элемент последним
    QComboBox::AtCurrent
    Заменяет предварительно выбранный вариант в списке
    QComboBox::BeforeCurrent
    Вставляет вводимый элемент перед предварительно выбранным вариантом из списка
    QComboBox::AfterCurrent
    Вставляет вводимый элемент после предварительно выбранного варианта из списка
    QComboBox::NoInsertion
    Новый элемент не вставляется в список вариантов

    Для задания политики вызовите метод

    InsertionPolicy
    виджета
    QComboBox
    :

    combo->setInsertionPolicy(QComboBox::AtTop);

    Давайте бросим взгляд на конструкторы и методы выбора варианта виджета

    QComboBox
    :

    #include <qcombobox.h>

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

    QComboBox(bool readwrite, QWidget *parent = 0, const char *name = 0);

    int count();

    void insertStringList(const QStringList& list, int index = -1);

    void insertStrList(const QStrList& list, int index = -1);

    void insertStrList(const QStrList *list, int index = -1);

    void insertStrList (const char **strings, int numStrings = -1, int index = -1);

    void insertItem(const QString &t, int index = -1);

    void removeItem(int index);

    virtual void setCurrentItem(int index);

    QString currentText();

    virtual void setCurrentText(const QString &);

    void setEditable(bool);

    Функция

    count
    возвращает количество вариантов в списке.
    QStringList
    и
    QStrList
    — классы коллекций, которые можно применять для вставки вариантов. Удалить варианты можно с помощью метода removeItem, извлечь и задать текущий вариант можно, с помощью методов
    currentText
    и
    setCurrentText
    , а перейти в редактируемый режим — с помощью метода
    setEditable
    .

    QComboBox
    порождает сигнал
    textChanged(QString&)
    при каждом новом выборе варианта, передавая вновь выбранный элемент как аргумент.

    Выполните упражнение 17.6.

    Упражнение 17.6. Виджет
    QComboBox

    В этом примере вы сделаете попытку применить виджет

    QComboBox
    и посмотрите, как ведут себя сигналы и слоты с параметрами. Вы создадите класс
    ComboBox
    , потомка
    QMainWindow
    . В нем будут два виджета
    QComboBox
    : один для чтения/записи, другой только для чтения. Вы установите связь с сигналом
    textChanged
    для того, чтобы получать текущее значение при каждом его изменении.

    1. Введите следующий программный код и назовите файл ComboBox.h:

    #include <qmainwindow.h>

    #include <qcombobox.h>


    class ComboBox : public QMainWindow {

     Q_OBJECT

    public:

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

    private slots:

     void Changed(const QString& s);

    };

    2. Интерфейс состоит из двух виджетов

    QComboBox
    : один редактируемый, а другой предназначен только для чтения. Вы заполните оба списка одними и теми же вариантами:

    #include "ComboBox.moс"

    #include <qlayout.h>

    #include <iostream>


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

     QMainWindow(parent, name) {

     QWidget *widget = new QWidget(this);

     setCentralWidget(widget);

     QVBoxLayout *vbox = new QVBoxLayout(widget, 5, 10, "vbox");

     QComboBox *editablecombo = new QComboBox(TRUE, widget, "editable");

     vbox->addWidget(editablecombo);

     QComboBox *readonlycombo = new QComboBox(FALSE, widget, "readonly");

     vbox->addWidget(readonlycombo);

     static const char* items[] = {"Macbeth", "Twelfth Night", "Othello", 0};

     editablecombo->insertStrList(items);

     readonlycombo->insertStrList(items);

     connect(editablecombo, SIGNAL(textchanged(const QString&),

      this, SLOT(Changed(const QString&)));

     resize(350, 200);

    }

    3. Далее приведена функция слота. Обратите внимание на параметр

    s
    типа
    QString
    , передаваемый сигналом:

    void ComboBox::Changed(const QString& s) {

     std::cout << s << "\n";

    }


    int main(int argc, char **argv) {

     QApplication app(argc, argv);

     ComboBox* window = new ComboBox();

     app.setMainWidget(window);

     window->show();

     return app.exec();

    }

    Вы сможете видеть вновь выбранные из редактируемого

    QComboBox
    варианты в командной строке на рис. 17.6.

    Рис. 17.6


    Как это работает

    Создаются виджеты раскрывающегося списка во многом так же, как и другие виджеты. Главная новая деталь — вызов функции

    insertStrList
    для сохранения списка вариантов в виджете.

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

    QListView

    Списки и деревья в комплекте Qt формируются виджетом

    QListView
    . Этот виджет представляет как простые списки, так и иерархические данные, разделенные на строки и столбцы. Он очень подходит для вывода структур каталогов или чего-то подобного, потому что дочерние элементы можно раскрыть и свернуть, щелкнув кнопкой мыши знак "плюс" или "минус", так же как в файловом обозревателе.

    В отличие от виджета GTK+

    ListView
    виджет
    QListView
    обрабатывает и данные, и их представление, что сделано для облегчения использования, если не для исключительной гибкости.

    В виджете

    QListView
    можно выбрать строки или отдельные ячейки и затем вырезать и вставить данные, отсортировать их по столбцу и вы получите виджеты
    QCheckBox
    , отображенные в ячейках. В этот виджет встроено множество функциональных возможностей — как программисту вам достаточно лишь вставить данные и задать некоторые правила форматирования.

    Создается виджет

    QListView
    обычным образом, заданием родительского виджета и собственного имени виджета:

    QListView *view = new QListView(parent, "name");

    Для задания заголовков столбцов используйте соответствующим образом названный метод

    addColumn
    :

    view->addColumn("Left Column", width1); // фиксированной ширины

    view->addColumn("Right Column"); // с автоматически задаваемым размером

    Ширина столбца задается в пикселах или, если пропущена, приравнивается к величине самого длинного элемента в столбце. В дальнейшем при вставке и удалении элементов ширина столбца автоматически меняется.

    Данные вставляются в

    QListView
    с помощью объекта
    QListViewItem
    , представляющего строку данных. Вы должны лишь передать в конструктор объект
    QListView
    и элементы строки, и она добавится в конец представления:

    QListViewItem *toplevel = new QListViewItem(view, "Left Data", "Right Data");

    Первый параметр — либо объект

    QListView
    , как в данном случае, либо еще один объект типа
    QListViewItem
    . Если передается
    QListViewItem
    , строка создается как дочерняя по отношению к этому объекту
    QListViewItem
    . Таким образом, структура дерева формируется передачей объекта
    QListView
    для узлов верхнего уровня и затем последующих объектов типа
    QListViewItem
    для дочерних или подчиненных узлов.

    Остальные параметры — данные каждого столбца, по умолчанию равные

    NULL
    , если не заданы.

    Добавление дочернего узла — это просто вариант передачи в функцию указателя верхнего уровня. Если вы не добавляете последующие дочерние узлы в объект

    QListViewItem
    , нет необходимости сохранять возвращаемый указатель:

    new QListViewItem(toplevel, "Left Data", "Right Data");

    // Дочерний по отношению к верхнему уровню

    В API

    QListViewItem
    можно найти методы обхода узлов дерева на случай корректировки конкретных строк:

    #include <qlistview.h>

    virtual void insertItem(QListviewitem* newChild);

    virtual void setText(int column, const QString& text);

    virtual QString text(int column) const;

    QListViewItem* firstChild() const;

    QListViewItem* nextSibling() const;

    QListViewItem* parent() const;

    QListViewItem* itemAbove();

    QListViewItem *itemBelow();

    Получить первую строку в дереве можно, вызвав метод

    firstChild
    для самого объекта
    QListView
    . Затем можно многократно вызывать
    firstChild
    и
    nextSibling
    для возврата фрагментов или целого дерева.

    Приведенный далее фрагмент программного кода выводит первый столбец всех узлов верхнего уровня:

    QListViewItem *child = view->firstChild();

    while(child) {

     cout << myChild->text(1) << "\n";

     myChild = myChild->nextSibling();

    }

    Все подробности, касающиеся

    QListView
    ,
    QListViewItem
    и
    QCheckListView
    , см. в документации API комплекта Qt.

    Выполните упражнение 17.7.

    Упражнение 17.7 Виджет
    QListView

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

    Давайте для краткости пропустим заголовочный файл и рассмотрим реализацию класса, файл ListView.cpp.

    #include "Listview.moc"


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

     QMainWindow(parent, name) {

     listview = new QListView(this, "listview1");

     listview->addColumn("Artist");

     listview->addColumn("Title");

     listview->addColumn("Catalogue");

     listview->setRootIsDecorated(TRUE);

     QListViewItem* toplevel = new QListViewItem(listview, "Avril Lavigne",

      "Let Go", "AVCD01");

     new QListViewItem(toplevel, "Complicated");

     new QListViewItem(toplevel, "Sk8er Boi");

     setCentralWidget(listview);

    }


    int main(int argc, char **argv) {

     QApplication app(argc, argv);

     ListView *window = new ListView();

     app.setMainWidget(window);

     window->show();

     return app.exec();

    }

    Как это работает

    Виджет

    QListView
    кажется сложным, потому что он действует и как список элементов, и как дерево элементов. В вашем программном коде необходимо создать экземпляры
    QListViewItem
    для каждого элемента, включаемого вами в список. У каждого экземпляра типа
    QListViewItem
    есть родитель. Эти элементы с самим виджетом в качестве родителя отображаются как элементы верхнего уровня. Элементы с другим элементом типа
    QListViewItem
    в качестве родителя выводятся на экран как дочерние элементы. В этом примере показаны экземпляры
    QListViewItem
    со всего одним уровнем глубины, но можно создавать и деревья элементов с гораздо большей глубиной.

    После компиляции и выполнения примера ListView вы увидите виджет

    QListView
    в действии, как показано на рис. 17.7.

    Обратите внимание на то, как дочерние строки почтительно отступают от своих "родителей". Знаки "плюс" и "минус", указывающие на наличие скрытых или сворачивающихся строк, не представлены по умолчанию; в этом примере они задаются с помощью

    setRootIsDecorated
    .

    Рис. 17.7


    Диалоговые окна

    До сих пор вы создавали подклассы

    QMainWindow
    для построения своих интерфейсов. Объекты
    QMainWindow
    предназначены для создания главного окна в вашем приложении, но для кратковременных диалоговых окон следует рассмотреть виджет
    QDialog
    .

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

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

    Наряду с обычными модальными и немодальными (или безмодальными на языке Qt) диалоговыми окнами комплект Qt также предлагает полумодальное диалоговое окно. В следующем перечне приведены отличия модальных и немодальных диалоговых окон, в него также включены полумодальные окна.

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

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

    □ Полумодальное диалоговое окно — это модальное окно, не имеющее своего цикла событий. Это позволяет возвращать управление приложению, но сохранять блокировку ввода для других окон. Полумодальные окна бывают полезны в редких случаях, когда у вас есть индикатор выполнения процесса важной, требующей значительного времени операции, и вы хотите дать пользователю возможность отменить ее при необходимости. Поскольку у такого окна нет собственного цикла событий, для его обновления вы должны периодически вызывать метод

    QApplication::processEvents
    .

    QDialog

    QDialog
    — базовый класс диалоговых окон в Qt, предоставляющий методы
    exec
    и
    show
    для обработки модальных и немодальных диалоговых окон, у него есть встроенный класс
    QLayout
    , который можно использовать, и несколько сигналов и слотов, полезных для формирования откликов на нажатие кнопки.

    Обычно вы будете создавать для своих диалоговых окон класс-потомок

    QDialog
    и вставлять в него виджеты для создания интерфейса диалогового окна:

    #include <qdialog.h>


    MyDialog::MyDialog(QWidget *parent, const char *name) : QDialog(parent, name) {

     QHBoxLayout *hbox = new QHBoxLayout(this);

     hbox->addWidget(new Qlabel("Enter your name"));

     hbox->addWidget(new QLineEdit());

     hbox->addWidget(ok_pushbutton);

     hbox->addWidget(cancel_pushbutton);

     connect(ok_pushbutton, SIGNAL(clicked()), this, SLOT(accept()));

     connect(cancel_pushbutton, SIGNAL(clicked()), this, SLOT(reject()));

    }

    В отличие от виджета типа

    QMainWindow
    вы можете задать объект
    MyDialog
    как родительский для своего объекта
    QLayout
    без создания пустого
    QWidget
    в качестве родительского.

    Примечание

    Имейте в виду, что в этом примере пропущен программный код для создания виджетов

    ok_pushbutton
    и
    cancel_pushbutton
    .

    У объекта

    QDialog
    есть два слота —
    accept
    и
    reject
    , которые применяются для обозначения результата, полученного в диалоговом окне. Этот результат возвращается методом exec. Как правило, вы будете связывать кнопки OK и Cancel со слотами, как в
    MyDialog
    .

    Модальные диалоговые окна

    Для применения диалогового окна как модального вы вызываете метод

    exec
    , который открывает диалоговое окно и возвращает
    QDialog::Accepted
    или
    QDialog::Rejected
    в зависимости от того, какой слот был активизирован:

    MyDialog* dialog = new MyDialog(this, "mydialog");

    if (dialog->exec() == QDialog::Accepted) {

     // Пользователь щелкнул мышью кнопку OK

     doSomething();

    } else {

     // Пользователь щелкнул мышью кнопку Cancel или

     // диалоговое окно уничтожено

     doSomethingElse();

    }

    delete dialog;

    Когда метод

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

    Учтите, что когда вызывается

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

    Немодальные диалоговые окна

    Немодальные диалоговые окна слегка отличаются от обычных основных окон прежде всего тем, что располагаются поверх своего родительского окна, совместно используют их элемент на панели задач и автоматически скрываются, когда вызван слот

    accept
    или
    reject
    .

    Для отображения немодального диалогового окна вызывайте метод

    show
    , как вы сделали бы для окна
    QMainWindow
    :

    MyDialog *dialog = new MyDialog(this, "mydialog");

    dialog->show();

    Функция

    show
    выводит диалоговое окно на экран и немедленно возвращается в приложение для продолжения цикла выполнения. Для обработки нажатой кнопки вы должны написать слоты и установить с ними связь:

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

     QDialog(parent, name) {

     ...

     connect(ok_pushbutton, SIGNAL(clicked()), this, SLOT(OkClicked()));

     connect(cancel_pushbutton, SIGNAL(clicked()), this, SLOT(CancelClicked()));

    }


    MyDialog::OkClicked() {

     // Выполните что-либо

    }


    MyDialog::CancelClicked() {

     // Выполните что-либо другое

    }

    Как и в случае модального окна, диалоговое окно автоматически скрывается при нажатии кнопки.

    Полумодальное диалоговое окно

    Для создания полумодального диалогового окна вы должны задать флаг модального режима в конструкторе

    QDialog
    и применить метод
    show
    :

    QDialog(QWidget *parent=0, const char *name=0, bool modal=FALSE, WFlags f=0);

    Вы не задаете в модальном диалоговом окне флаг модального режима равным

    TRUE
    , потому что вызов
    exec
    заставляет диалоговое окно перейти в модальный режим независимо от значения этого флага.

    Конструктор вашего диалогового окна будет выглядеть примерно следующим образом:

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

     QDialog(parent, name, TRUE) {

     ...

    }

    После того как вы определили ваше диалоговое окно, вызовите функцию

    show
    как обычно и затем продолжите свою обработку, периодически вызывая
    QApplication::processEvents
    для обновления вашего диалогового окна:

    MySMDialog *dialog = MySMDialog(this, "semimodal");

    dialog->show();

    while (processing) {

     doSomeProcessing();

     app->processEvents();

     if (dialog->wasCancelled()) break;

    }

    Перед продолжением выполнения проверьте, не уничтожено ли диалоговое окно. Имейте в виду, что функция

    wasCancelled
    не является частью класса
    QDialog
    — вы должны написать ее самостоятельно.

    Комплект Qt предоставляет готовые подклассы класса

    QDialog
    , предназначенные для конкретных задач, таких как выбор файлов, ввод текста, индикация процесса выполнения и вывод окна сообщения. Применение этих виджетов в любых приложениях убережет вас от множества неприятностей и проблем.

    QMessageBox

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

    У класса

    QMessageBox
    есть статические методы создания и отображения окон всех трех перечисленных типов:

    #include <qmessagebox.h>

    int information(QWidget *parent, const QString& caption,

     const QString&text, int button0, int button1=0, int button2=0);

    int warning(QWidget *parent, const QString& caption,

     const QString& text, int button0, int button1, int button2=0);

    int critical(QWidget *parent, const QString& caption,

     const QString& text, int button0, int button1, int button2=0);

    Можно выбрать кнопки из списка готовых кнопок

    QMessageBox
    , соответствующих значениям, возвращаемым статическими методами:

    QMessageBox::Ok
    ;

    QMessageBox::Cancel
    ;

    QMessageBox::Yes
    ;

    QMessageBox::No
    ;

    QMessageBox::Abort
    ;

    QMessageBox::Retry
    ;

    QMessageBox::Ignore
    .

    Типичный пример использования окна

    QMessageBox
    будет похож на приведенный далее фрагмент программного кода:

    int result = QMessageBox::information(this,

     "Engine Room Query",

     "Do you wish to engage the HyperDrive?",

     QMessageBox::Yes | QMessageBox::Default,

     QMessageBox::No | QMessageBox::Escape);

    switch (result) {

    case QMessageBox::Yes:

     hyperdrive->engage();

     break;

    case QMessageBox::No:

     // сделайте что-нибудь еще

     break;

    }

    Вы соединили операцией

    OR
    (
    |
    ) коды кнопок с вариантами
    Default
    и
    Escape
    , чтобы задать стандартные действия, при нажатии клавиш <Enter> (или <Return>) и <Esc>. Результирующее диалоговое окно показано на рис. 17.8.

    Рис. 17.8 

    QInputDialog

    Окно

    QInputDialog
    полезно для ввода пользователем отдельных значений, будь то текст, вариант раскрывающегося списка, целочисленное или действительное значение. У класса
    QInputDialog
    есть статические методы, например
    QMessageBox
    , создающие некоторые проблемы, поскольку у них слишком много параметров, к счастью, у большинства из них есть значения по умолчанию:

    #include <qinputdialog.h>

    QString getText(const QString& caption, const QString& label,

     QLineEdit::EchoMode mode=QLineEdit::Normal,

     const QString& text=QString::null,

     bool* ok = 0, QWidget* parent = 0, const char * name = 0);

    QString getItem(const QString& caption, const QString& label,

     const QStringList& list, int current=0, bool editable=TRUE,

     bool* ok=0, QWidget* parent = 0, const char* name=0)

    int getInteger(const QString& caption, const QString& label,

     int num=0, int from = -2147483647, int to = 2147483647,

     int step = 1, bool* ok = 0, QWidget* parent = 0, const char* name = 0);

    double getDouble(const QString& caption, const QString& label,

     double num = 0, double from = -2147483647, double to = 2147483647,

     int decimals = 1, bool* ok = 0, QWidget* parent = 0, const char* name = 0);

    Для ввода строки текста напишите следующий фрагмент кода:

    bool result;

    QString text = QInputDialog::getText("Question", "What is your Quest?:",

     QLineEdit::Normal, QString::null, &result, this, "input");

    if (result) {

     doSomething(text);

    } else {

     // Пользователь нажал Cancel

    }

    Как видно из рис. 17.9, окно

    QInputDialog
    создано с помощью виджета QLi
    n
    eEdit и кнопок OK и Cancel.

    Рис. 17.9 


    Диалоговое окно, созданное методом

    QInputDialog::getText
    , применяет виджет
    QLineEdit
    . Параметр режима редактирования, передаваемый в функцию
    getText
    , управляет способом отображения набираемого текста точно так же, как аналогичный параметр режима виджета
    QLineEdit
    . Вы можете также задать текст, выводимый по умолчанию, или оставить поле пустым, как показано на рис. 17.9. У всех окон
    QInputDialog
    есть кнопки OK и Cancel, и в метод передается указатель типа
    bool
    для обозначения нажатой кнопки — результат равен
    TRUE
    , если пользователь щелкает мышью кнопку OK.

    Метод

    getItem
    с помощью раскрывающегося списка
    QComboBox
    предлагает пользователю список вариантов:

    bool result;

    QStringList options;

    options << "London" << "New York" << "Paris";

    QString city = QInputDialog::getItem("Holiday", "Please select a

     destination:", options, 1, TRUE, &result, this, "combo");

    if (result) selectDestination(city);

    Созданное диалоговое окно показано на рис. 17.10.

    Рис. 17.10 


    Функции

    getInteger
    и
    getDouble
    действуют во многом аналогично, поэтому мы не будем на них останавливаться.

    Применение qmake для упрощения написания make-файлов

    Компиляция приложения с библиотеками KDE и Qt становится утомительным занятием, поскольку ваш make-файл получается очень сложным из-за необходимости использовать

    moc
    и иметь библиотеки здесь, там и везде. К счастью, Qt поставляется с утилитой
    qmake
    для создания ваших make-файлов.

    Примечание

    Если вы уже пользовались комплектом Qt, вам, возможно, знакома утилита

    tmake
    — более раннее (и теперь устаревшее) воплощение
    qmake
    , поставлявшееся с предыдущими версиями Qt.

    Утилита

    qmake
    принимает в качестве входного файл .pro. Этот файл содержит самые существенные сведения, необходимые для компиляции, такие как исходные тексты, заголовочные файлы, результирующий двоичный файл и местонахождения библиотек KDE/Qt.

    Типичный pro-файл среды KDE выглядит следующим образом:

    TARGET = app

    MOC_DIR = moc

    OBJECTS_DIR = obj

    INCLUDEPATH = /usr/include/kde

    QMAKE_LIBDIR_X11 += /usr/lib

    QMAKE_LIBS_X11 += -lkdeui -lkdecore

    SOURCES = main.cpp window.cpp

    HEADERS = window.h

    Вы задаете результирующий двоичный файл, временные каталоги moc и объектных файлов, путь к библиотеке KDE и исходные тексты, и заголовочные файлы, из которых формируется приложение. Учтите, что местонахождение файлов библиотек и заголовочных файлов KDE зависит от вашего дистрибутива. Пользователи SUSE должны приравнять

    INCLUDEPATH
    путь /opt/kde3/include и
    QMAKE_LIBS_X11
    путь /opt/kde3/lib.

    $ qmake file.pro -о Makefile

    Затем вы можете выполнить команду

    make
    как обычно, что не вызовет затруднений. Для упрощения процедуры построения приложения следует использовать
    qmake
    с программами любой сложности, применяющими KDE/Qt.

    Создание меню и панелей инструментов с помощью KDE

    Для того чтобы продемонстрировать мощь виджетов KDE, мы оставили меню и панели инструментов напоследок, поскольку они — уж очень наглядные примеры того, как библиотеки KDE экономят время и усилия по сравнению с применением только Qt или любых других комплектов с элементами графического пользовательского интерфейса.

    Обычно в библиотеках GUI элементы меню и панелей инструментов — отличающиеся элементы, каждый со своим собственным виджетом. Вы должны создавать отдельные объекты для каждого элемента и отслеживать изменения, например, делая недоступными определенные варианты, каждый отдельно.

    У программистов KDE появилось лучшее решение. Вместо такого обособленного подхода в KDE определен виджет

    KAction
    для представления действия, которое может выполнить приложение. Это действие может открыть новый документ, сохранить файл или вывести окно справки.

    KAction
    присваивается текст, клавиатурный акселератор, пиктограмма и слот, который вызывается при активизации действия:

    KAction *new_file = new KAction("New", "filenew",

     KstdAccel::shortcut(KstdAccel::New), this,

     SLOT(newFile()), this, "newaction");

    Затем

    KAction
    может быть вставлено в меню и панель инструментов без дополнительного описания:

    new_file->plug(a_menu);

    new_file->plug(a_toolbar);

    Таким образом, вы создали пункт меню New и кнопку панели инструментов, которые вызывают

    newFile
    при щелчке кнопкой мыши.

    Теперь если вам нужно отменить

    KAction
    — скажем, вы не хотите, чтобы пользователь мог создать новый файл, — вызов централизован:

    new_file->setEnabled(FALSE);

    Это все, что касается меню и панелей инструментов в среде KDE — на самом деле очень легко и просто. Далее приведен конструктор виджета

    KAction
    :

    #include <kde/kaction.h>

    KAction(const QString& text, const KShortcut& cut,

     const QObject* receiver, const char* slot,

     QObject *parent, const char* name = 0);

    KDE предоставляет стандартные объекты

    KAction
    для унификации текста, клавиатурных акселераторов и пиктограмм в разных приложениях KDE:

    #include <kde/kaction.h>

    KAction* openNew(const QObject* recvr, const char *slot,

     KActionCollection* parent, const char* name = 0)ж

    KAction* save ...

    KAction* saveAs ...

    KAction* revert ...

    KAction* close ...

    KAction* print ...

    И т.д.

    Любое стандартное действие принимает одни и те же параметры; слот-приемник и функцию,

    KActionCollection
    и имя
    KAction
    . Объект
    KActionCollection
    управляет в окне объектами
    KAction
    , и вы можете получить текущий объект с помощью метода
    actionCollection
    окна
    KMainWindow
    :

    KAction *saveas = KStdAction::saveAs(this, SLOT(saveAs()) ,

     actionCollection(), "saveas");

    Выполните упражнение 17.8.

    Упражнение 17.8. Приложение в KDE с меню и панелями инструментов

    В приведенном далее примере вы опробуете объекты

    KAction
    в приложении среды KDE.

    1. Начните с заголовочного файла KDEMenu.h.

    KDEMenu
    — это подкласс
    KMainWindow
    , являющегося подклассом класса
    QMainWindow
    .
    KMainWindow
    управляет сеансом в среде KDE и обладает встроенными панелью инструментов и строкой состояния.

    #include <kde/kmainwindow.h>


    class KDEMenu : public KMainWindow {

     Q_OBJECT

    public:

     KDEMenu(const char * name = 0);

    private slots:

     void newFile();

     void aboutApp();

    };

    2. Файл KDEMenu.cpp начните с директив

    #include
    для виджетов, которые будете применять:

    #include "KDEMenu.h"

    #include <kde/kapp.h>

    #include <kde/kaction.h>

    #include <kde/kstdaccel.h>

    #include <kde/kmenubar.h>

    #include <kde/kaboutdialog.h>

    3. В конструкторах, создающих три виджета

    KAction
    ,
    new_file
    определяется вручную, a
    quit_action
    и
    help_action
    используют стандартные определения
    KAction
    :

    KDEMenu::KDEMenu(const char *name = 0) : KMainWindow (0L, name) {

     KAction *new_file = new KAction("New", "filenew",

      KstdAccel::shortcut(KstdAccel::New), this, SLOT(newFile()),

      this, "newaction");

     KAction *quit_action = KStdAction::quit(KApplication::kApplication(),

      SLOT(quit()), actionCollection());

     KAction *help_action = KStdAction::aboutApp(this, SLOT(aboutApp()),

      actionCollection());

    4. Создайте два меню верхнего уровня и включите их в строку меню

    KApplication
    :

    QPopupMenu *file_menu = new QPopupMenu;

    QPopupMenu *help_menu = new QPopupMenu;

    menuBar()->insertItem("&File", file_menu);

    menuBar()->insertItem("&Help", help_menu);

    5. Теперь вставьте действия в меню и панель инструментов, добавив разделительную линию между

    new_file
    и
    quit_action
    :

     new_file->plug(file_menu);

     file_menu->insertSeparator();

     quit_action->plug(file_menu);

     help_action->plug(help_menu);

     new_file->plug(toolBar());

     quit_action->plug(toolBar());

    }

    6. В заключение несколько определений слотов:

    aboutApp
    создает диалоговое окно
    KAbout
    для отображения сведений о программе. Учтите, что слот
    quit
    определен как часть
    KApplication
    :

    void KDEMenu::newFile() {

     // Создание нового файла

    }


    void KDEMenu::aboutApp() {

     KAboutDialog *about = new KAboutDialog(this, "dialog");

      about->setAuthor(QString("A. N. Author"),

      QString("an@email.net"), QString("http://url.com"),

      QString("work"));

     about->setVersion("1.0");

     about->show();

    }


    int main(int argc, char **argv) {

     KApplication app(argc, argv, "cdapp");

     KDEMenu* window = new KDEMenu("kdemenu");

     app.setMainWidget(window);

     window->show();

     return app.exec();

    }
     

    7. Далее вам нужен файл menu.pro для утилиты

    qmake
    :

    TARGET = kdemenu

    MOC_DIR = moc

    OBJECTS_DIR = obj

    INCLUDEPATH = /usr/include/kde

    QMAKE_LIBDIR_X11 += -L$KDEDIR/lib

    QMAKE_LIBS_X11 += -lkdeui -lkdecore

    SOURCES = KDEMenu.cpp

    HEADERS = KDEMenu.h

    8. Теперь запустите

    qmake
    для создания make-файла, откомпилируйте и выполните программу:

    $ qmake menu.pro -о Makefile

    $ make

    $ ./kdemenu

    Как это работает

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

    KAction
    — возможность использования каждого из них в разных частях программы, таких как панель инструментов и меню в строке меню, все упомянутые возможности показаны в данном примере.

    Построение приложений KDE требует больше работы, чем создание большинства программ, по крайней мере, на первый взгляд. В действительности файл menu.pro и команда

    qmake
    скрывают большой набор параметров, которые в противном случае вам пришлось бы вставлять вручную в ваш make-файл.

    На рис. 17.11 и 17.12 показано, как появляются в окне меню и кнопки панели инструментов.

    Рис. 17.11 



    Рис. 17.12 


    И вот оно! Мы закончили наш тур по Qt и KDE, рассмотрев базовые элементы, всех приложений GUI, окна, схемы размещения, кнопки, диалоговые окна и меню. Существует бесчисленное множество виджетов Qt и KDE, о которых мы не упоминали, начиная с

    QColorDialog
    — диалогового окна для выбора цвета — и заканчивая
    KHTML
    — виджетом Web-обозревателя — все они подробно описаны на Web-сайтах компании Trolltech и графической среды KDE. 

    Приложение для работы с базой данных компакт-дисков с использованием KDE/Qt

    Теперь, когда вы можете использовать силу и мощь KDE/Qt, пришло время снова обратить внимание на приложение для работы с компакт-дисками, чтобы привести его в чувство.

    Напоминаем, чего вы хотите добиться от вашего приложения для работы с базой данных компакт-дисков:

    □ регистрация в базе данных из графического пользовательского интерфейса;

    □ поиск компакт-диска в базе данных;

    □ вывод информации о компакт-диске и его дорожках;

    □ добавление компакт-диска в базу данных;

    □ отображение окна About (О программе).

    MainWindow

    Начнем обсуждение с программного кода для главного окна приложения, которое содержит виджет поля поиска и список для отображения результатов поиска.

    1. Начните с ввода программного кода в файл MainWindow.h (или загрузите его с Web-сайта книги). Поскольку окно содержит виджет

    QLineEdit
    для поиска компакт-дисков и виджет
    QListView
    для вывода результатов поиска, вы должны вставить в программный код заголовочные файлы qlistview.h и qlineedit.h:

    #include <kde/kmainwindow.h>

    #include <qlistview.h>

    #include <qlineedit.h>


    class MainWindow : public KMainWindow {

     Q_OBJECT

    public:

     MainWindow(const char *name);

    public slots:

     void doSearch();

     void Added();

    private:

     QListView *list;

     QLineEdit *search_entry;

    };

    2. MainWindow.срр — самая сложная часть программы. В конструкторе вы создаете интерфейс главного окна и связываете необходимые сигналы с вашими слотами. Как обычно, начните программу с файлов в директивах

    #include
    :

    #include "MainWindow.h"

    #include "AddCdDialog.h"

    #include "app_mysql.h"

    #include <qvbox.h>

    #include <qlineedit.h>

    #include <qpushbutton.h>

    #include <qlabel.h>

    #include <qlistview.h>

    #include <kde/kapp.h>

    #include <kde/kmenubar.h>

    #include <kde/klocale.h>

    #include <kde/kpopupmenu.h>

    #include <kde/kstatusbar.h>

    #include <kde/kaction.h>

    #include <kde/kstdaccel.h>

    #include <string.h>


    MainWindow::MainWindow(const char * name) : KMainWindow(0L, name) {

     setCaption("CD Database");

    3. Теперь создайте элементы меню и панели инструментов с помощью виджета

    KAction
    .

    KAction *addcd_action = new KAction("&Add CD", "filenew",

     KStdAccel::shortcut(KStdAccel::New), this, SLOT(AddCd()), this);

     KAction *quit_action = KStdAction::quit(KApplication::kApplication(),

      SLOT(quit()), actionCollection());

     QPopupMenu* filemenu = new QPopupMenu;

     QString about = ("CD App\n\n"

      "(C) 2007 Wrox Press\n" "email@email.com\n");

     QPopupMenu* helpmenu = helpMenu(about);

     menuBar()->insertItem("&File", filemenu);

     menuBar()->insertltem(i18n("&Help"), helpmenu);

     addcd_action->plug(filemenu);

     filemenu->insertSeparator();

     quit_action->plug(filemenu);

     addcd_action->plug(toolBar());

     quit_action->plug(toolBar());

    4. Для разнообразия примените виджеты

    QBoxLayout
    вместо обычных классов
    QLayout
    :

     QVBox *vbox = new QVBox(this);

     QHBox *hbox = new QHBox(vbox);

     QLabel* label = new QLabel(hbox);

     label->setText("Search Text: ");

     search_entry = new QLineEdit(hbox);

     QPushButton *button = new QPushButton("Search", hbox);

    5. Далее следует виджет

    QListView
    , занимающий основную часть рабочей области окна. После этого для поиска компакт-диска в базе данных вы связываете необходимые сигналы с вашим слотом
    doSearch
    . Строка состояния
    KMainWindow
    становится видимой за счет вставки пустого сообщения:

     list = new QListView(vbox, "name", 0L);

     list->setRootIsDecorated(TRUE);

     list->addColumn("Title");

     list->addColumn("Artist");

     list->addColumn("Catalogue");

     connect(button, SIGNAL(clicked()), this, SLOT(doSearch()));

     connect(search_entry, SIGNAL(returnPressed()), this, SLOT(doSearch()));

     statusBar()->message("");

     setCentralWidget(vbox);

     resize(300, 400);

    }

    6. Слот

    doSearch
    — рабочее завершение приложения. В нем считывается строка поиска и выбираются все соответствующие ей компакт-диски и их дорожки. Логика слота такая же, как в функции
    doSearch
    GNOME/GTK+ в главе 16.

    void MainWindow::doSearch() {

     cd_search_st *cd_res = new cd_search_st;

     current_cd_st *cd = new current_cd_st;

     struct current_tracks_st ct;

     int res1, i, j, res2, res3;

     char track_title[110];

     char search_text[100];

     char statusBar_text[200];

     QListViewItem *cd_item;

     strcpy(search_text, search_entry->text());

    7. Извлеките

    id
    соответствующих компакт-дисков и обновите строку состояния, чтобы отобразить результаты поиска:

     res1 = find_cds(search_text, cd_res);

     sprintf(statusBar_text,

      " Displaying %d result(s) for search string ' %s'",

      res1, search_text);

     statusBar()->message(statusBar_text);

     i = 0;

     list->clear();

    8. Для каждого

    id
    извлеките сведения о компакт-диске в виджет
    QListView
    и информацию обо всех дорожках данного CD:

     while (i < res1) {

      res2 = get_cd(cd_res->cd_id[i], cd);

      cd_item = new QListViewItem(list, cd->title, cd->artist_name,

       cd->catalogue);

      res3 = get_cd_tracks(cd_res->cd_id[i++], &ct);

      j = 0;

      /* Заполните дерево дорожками текущего компакт-диска */

      while (j < res3) {

       sprintf(track_title, " Track %d. ", j+1);

       strcat(track_title, ct.track[j++]);

       new QListViewItem(cd_item, track_title);

      }

     }

    }

    Рис. 17.13


    9. Слот

    AddCd
    вызывается, когда активизирован пункт меню или кнопка панели инструментов
    addcd_action
    :

    void MainWindow::AddCd()

     AddCdDialog* dialog = new AddCdDialog(this);

     dialog->show();

    }

    Результат показан на рис. 17.13.

    AddCdDialog

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

    1. Введите следующий программный код в файл AddCdDialog.h. Имейте в виду, что класс

    AddCdDialog
    — потомок
    KDialogBase
    , виджета диалогового окна в среде KDE.

    #include <kde/kdialogbase.h>

    #include <qlineedit.h>


    class AddCdDialog : public KDialogBase {

     Q_OBJECT

    public:

     AddCdDialog(QWidget* parent);

    private:

     QLineEdit* artist_entry, *title_entry, *catalogue_entry;

    public slots:

     void okClicked();

    };

    2. Далее следует файл AddCdDialog.cpp, в котором в слоте

    okClicked
    вызывается функция
    add_cd
    из интерфейса MySQL:

    #include "AddCdDialog.h"

    #include "app_mysql.h"

    #include <qlayout.h>

    #include <qlabel.h>

    AddCdDialog::AddCdDialog(QWidget* parent)

     : KDialogBase(parent, "AddCD", false, "Add CD",

     KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true) {

     QWidget *widget = new QWidget(this);

     setMainWidget(widget);

     QGridLayout *grid = new QGridLayout(widget, 3, 2, 10, 5, "grid");

     grid->addWidget(new QLabel("Artist", widget, "artistlabel"), 0, 0, 0);

     grid->addWidget(new QLabel("Title", widget, "titlelabel"), 1, 0, 0);

     grid->addwidget(new QLabel("Catalogue", widget, "cataloguelabel"), 2, 0, 0);

     artist_entry = new QLineEdit(widget, "artist_entry");

     title_entry = new QLineEdit(widget, "titleentry");

     catalogue_entry = new QLineEdit(widget, "catalogue_entry");

     grid->addWidget(artist_entry, 0, 1, 0);

     grid->addWidget(title_entry, 1, 1, 0);

     grid->addWidget(catalogue_entry, 2, 1, 0);

     connect(this, SIGNAL(okClicked()), this, SLOT(okClicked()));

    }


    void AddCdDialog::okClicked() {

     char artist[200];

     char title[200];

     char catalogue[200];

     int cd_id = 0;

     strcpy(artist, artist_entry->text());

     strcpy(title, title_entry->text());

     strcpy(catalogue, catalogue_entry->text());

     add_cd(artist, title, catalogue, &cd_id);

    }

    На рис. 17.14 показано работающее диалоговое окно

    AddCdDialog
    .

    Рис. 17.14 

    LogonDialog

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

    LogonDialog
    . (Да, еще один пример остроумного и образного имени!)

    1. Начнем с заголовочного файла. Введите приведенный далее программный код и назовите файл LogonDialog.h. Обратите внимание на то, что для разнообразия этот класс описан в данном случае как потомок класса

    QDialog
    , а не
    KDialogBase
    .

    #include <qdialog.h>

    #include <qlineedit.h>


    class LogonDialog : public QDialog {

     Q_OBJECT

    public:

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

     QString getUsername();

    QString getPassword();

    private:

     QLineEdit *username_entry, *password_entry;

    };

    2. У вас есть более удобные методы для имени пользователя и пароля, чем инкапсуляция в файле LogonDialog.cpp вызова

    database_start
    . Далее приведен файл LogonDialog.cpp:

    #include "LogonDialog.h"

    #include "appmysql.h"

    #include <qpushbutton.h>

    #include <qlayout.h>

    #include <qlabel.h>


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

     QDialog(parent, name) {

     QGridLayout *grid = new QGridLayout(this, 3, 2, 10, 5, "grid");

     grid->addWidget(new QLabel("Username", this, "usernamelabel"), 0, 0, 0);

     grid->addWidget(new QLabel("Password", this, "passwordlabel"), 1, 0, 0);

     username_entry = new QLineEdit(this, "username entry");

     password_entry = new QLineEdit(this, "password_entry");

     password_entry->setEchoMode(QLineEdit::Password);

     grid->addWidget(username_entry, 0, 1, 0);

     grid->addWidget(passwordentry, 1, 1, 0);

     QPushButton* button = new QPushButton("Ok", this, "button");

     grid->addWidget(button, 2, 1, Qt::AlignRight);

     connect (button, SIGNAL(clicked()), this, SLOT(accept()));

    }


    QString LogonDialog::getUsername() {

     if (username_entry == NULL) return NULL;

     return username_entry->text();

    }


    QString LogonDialog::getPassword() {

     if (password_entry == NULL) return NULL;

     return password_entry->text();

    }

    На рис. 17.15 показано, как будет выглядеть диалоговое окно.

    Рис. 17.15 

    main.cpp

    Единственный оставшийся программный код — функция

    main
    , которую вы помещаете в отдельный файл main.cpp.

    1. В файле main.cpp вы открываете окно

    LogonDialog
    и получаете успешную регистрацию из функции
    database_start
    . Если регистрация оказалась неудачной, вы выводите окно
    QMessageBox
    или при попытке закрыть
    LogonDialog
    просите у пользователя подтверждения его выхода.

    #include "MainWindow.h"

    #include "app_mysql.h"

    #include "LogonDialog.h"

    #include <kde/kapp.h>

    #include <qmessagebox.h>


    int main(int argc, char **argv) {

     char username[100];

     char password[100];

     KApplication a(argc, argv, "cdapp");

     LogonDialog *dialog = new LogonDialog();

     while (1) {

      if (dialog->exec() == QDialog::Accepted) {

       strcpy(username, dialog->getUsername());

       strcpy(password, dialog->getPassword());

       if (database_start(username, password)) break;

       QMessageBox::information(0, "Title",

        "Could not Logon: Check username and/or password",

        QMessageBox::Ok);

       continue;

      } else {

       if (QMessageBox:information(0, "Title",

        "Are you sure you want to quit?", QMessageBox::Yes,

        QMessageBox::No) == QMessageBox::Yes) {

        return 0;

       }

      }

     }

     delete dialog;

     MainWindow *window = new MainWindow("Cd App");

     window->resize(600, 400);

     a.setMainWidget(window);

     window->show();

     return a.exec();

    }

    2. Осталось только написать pro-файл для утилиты

    qmake
    . Назовите его cdapp.pro:

    TARGET = app

    MOC_DIR = moc

    OBJECTS_DIR = obj

    INCLUDEPATH = /usr/include/kde /usr/include/mysql

    QMAKE_LIBDIR_X11 += -/usr/lib

    QMAKE_LIBDIR_X11 += /usr/lib/mysql

    QMAKE_LIBS_X11 += -lkdeui -lkdecore -lmysqlclient

    SOURCES = MainWindow.cpp main.cpp app_mysql.cpp AddCdDialog.cpp LogonDialog.cpp

    HEADERS = MainWindow.h app_mysql.h AddCdDialog.h LogonDialog.h

    Примечание

    Обратите внимание на то, что приведенный программный код позволяет вам немного схитрить, просто переименовав файл app_mysql.c в файл app_mysql.cpp; таким образом, вы сможете использовать его как обычный исходный файл на языке С++. Это устраняет небольшое усложнение, необходимость редактирования связей или компоновки объектного файла на языке С и объектного файла на языке С++,

    $ qmake cdapp.pro -о Makefile

    $ make

    $ ./арр

    Если все нормально, вы должны получить работающую базу данных компакт-дисков!

    Для того чтобы глубже понять KDE/Qt, можно попробовать реализовать другие функции в интерфейсе MySQL, такие как добавление дорожек в компакт-диски или удаление компакт-дисков. Вам понадобятся диалоговые окна, новые элементы меню и панели инструментов, придется также запрограммировать внутреннюю логику. Сделайте такую попытку!

    Резюме 

    В этой главе вы научились применять библиотеку графического пользовательского интерфейса Qt и рассмотрели виджеты графической среды KDE в действии. Вы узнали, что Qt — это библиотека на языке С++, применяющая механизм "сигнал/слот" для реализации программирования, управляемого событиями. Вы познакомились с основными виджетами Qt и написали несколько программ-примеров для демонстрации их практического применения. В заключение средствами KDE/Qt реализован графический пользовательский интерфейс вашего приложения для работы с компакт-дисками. 








    Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх