Журнал LinuxFormat - перейти на главную

LXF102:Qt4

Материал из Linuxformat
Перейти к: навигация, поиск
Новая серия! Осваиваем технологии, лежащие в основе нашумевшего KDE4

Содержание

Интервью с троллем

Qt4
ЧАСТЬ 1 Qt4 предоставляет в распоряжение программиста целый букет нововведений – но Андрей Боровский решил начать с того, что не сразу бросается в глаза.

А вы уже пишете программы под Qt 4.x? Хотя на рабочих столах Linux все еще господствует KDE 3.x, а следовательно, и Qt 3.x, все новые версии дистрибутивов Linux поставляются также и с Qt 4.x, причем во многих из них по умолчанию используются именно инструменты Qt четвертой версии. К этому следует добавить, что и переход на KDE4 – дело ближайшего будущего, а значит, если вы еще не портировали свою программу, использующую Qt3, на новую версию, самое время заняться этим сейчас. Если же вы только собираетесь создавать Qt-проект, Qt 3.x не стоит даже рассматривать.

В общем и целом можно сказать, что по сравнению с предыдущей версией система Qt4 улучшилась, обогатилась возможностями и усложнилась. Давно прошли те времена, когда вся Qt умещалась в одной-единственной разделяемой библиотеке. Что касается системы Qt4, то ее основные классы вольготно расположились в 12 библиотеках (тринадцатая библиотека, libQt3Support.so, предназначена для поддержки кода Qt3.x в тех случаях, когда перенос кода на новую версию представляется совсем уж трудным делом). Мы надеемся, что программисты Linux, привыкшие управлять демонами и убивать зомби, люди не суеверные, и число 13 их не испугает. Заголовочные файлы Qt4 также умножились в числе и распределены теперь по нескольким поддиректориям. Впрочем, как мы увидим дальше, управлять всем этим «зверинцем» не так уж и трудно.

Дела лицензионные

Знакомство с Qt4 мы начнем с вопросов лицензирования, которые многим, возможно, покажутся слишком скучными. В Qt4 наконец-то реализована полноценная схема двойного лицензирования для всех платформ (Windows, X11 и Mac), причем открытый вариант Qt распространяется на условиях GPL 2.0 [во время подготовки этой статьи к печати появилось сообщение, что Trolltech перевела Qt на GPLv3, см. Новости, – прим. ред.]. Сами представители Qt именуют свой подход «Quid Pro Quo», что в вольном переводе с древней латыни означает «баш на баш» ['qui pro quo' – лат. путаница, букв. «кто за кого», а англ. 'quid' в разговоре означает «фунт» (стерлингов)]. Если вы хотите пользоваться средствами Qt бесплатно, взамен вы должны предоставить сообществу свой код (открытая модель). Если вы не желаете делиться кодом, вы должны заплатить деньги (своего рода «выкуп», который, естественно, будет потрачен на дальнейшее совершенствование Qt).

Интересно отметить, что лицензионная политика Qt не позволяет лицензиатам применять тот же принцип двойного лицензирования к своим продуктам. Если вы ведете разработку с помощью Qt, ваш проект должен быть либо открытым на условиях GPL, либо коммерческим. То есть, вам, конечно, никто не мешает раздавать ваш код бесплатно, но если это делается не на основе GPL, то для разработки этого кода необходимо использовать (и, естественно, оплатить) коммерческую версию Qt [возможны исключения; полный список свободных лицензий, по которым может распространяться программа, скомпонованная с Qt, можно найти на сайте компании, – прим. ред.]. У открытой версии Qt для Windows есть одна неприятная особенность: согласно намерениям разработчиков, ее можно использовать только совместно со средой компиляции MinGW, которая нравится не всем (мне, например, не нравится). Чтобы работать с MS Visual Studio (а это все-таки лучшая платформа разработки для Windows), придется заплатить за коммерческую версию Qt. Причины такого ограничения непонятны, и упомянутый выше принцип quid pro quo здесь не срабатывает, поскольку даже сама Microsoft распространяет бесплатный вариант Visual Studio Express Edition. Кроме того, по сведениям, полученным с различных форумов, после небольшой «обработки напильником» открытую Qt4 можно заставить работать с компиляторами С++ от Microsoft и CodeGear, предназначенными для командной строки. По моему мнению, ограничив поддержку компиляторов для открытой версии Qt, разработчики из TrollTech сделали глупость, которая, я надеюсь, будет исправлена в дальнейших релизах Qt4.x.

А что же внутри?

Перейдем теперь к техническим новшествам Qt4. Похоже, старым добрым контейнерам STL так и не суждено стать настоящим стандартом. У каждого набора визуальных компонентов, основанного на C++, есть свой набор контейнеров (что, вообще говоря, не очень хорошо, так как делает невизуальный код, который особенно часто использует абстрактные типы данных, труднопереносимым между разными платформами разработки).

В Qt4 появилась система контейнеров Tulip (тюльпан), призванная заменить старые контейнеры Qt3. Tulip предоставляет в наше распоряжение стандартный набор контейнеров последовательного доступа (список, связный список, очередь, вектор, стек) и несколько ассоциативных контейнеров: отображения (классы QMap и QMultiMap), хэш-таблицы (классы QHash и QMultiHash) и множество (класс QSet). Теоретически разница между классами QMap и QMultiMap и классами QHash и QMultiHash заключается в том, что первый класс из каждой пары позволяет связать с ключом только одно значение, тогда как второй класс позволяет назначать несколько значений одному ключу. Напомню, что в ассоциативных контейнерах хранимым значениям сопоставляются ключи, которые позволяют организовать произвольный доступ к данным контейнера.

Разделение классов QHash и QMultiHash вызывает некоторые вопросы. При использовании хэш-таблиц нередко возникают коллизии (когда двум хранимым значениям соответствует один и тот же ключ). Придумать хэш-функцию, которая бы гарантированно не вызывала коллизий, очень трудно (обычно просто нереально). Фактически, возможность связывать несколько значений с одним ключом является неотъемлемым свойством хэш-таблиц. Разработчики контейнеров Qt, разумеется, об этом знают, и в классе QHash реализована возможность добавления нескольких значений с одним и тем же ключом. В результате различие между контейнерами QHash и QMultiHash (второй, кстати, является потомком первого) выглядит скорее косметическим.

Системные требования

Настоящая серия учебников ориентирована на читателей, знакомых с Qt3, но даже если вы только начинаете программировать с Qt, не расстраивайтесь – мы публиковали учебник по Qt3/KDE3 в номерах – LXF78-85.

Еще одно новшество Qt4 – среда Interview. Система Interview Framework представляет собой вариант реализации парадигмы «модель-контроллер–вид». В ее основе лежит старая и плодотворная идея разделения «движка» программы и интерфейса. В рамках парадигмы «модель-контроллер-вид» (подробно описанной в многочисленной литературе по «правильному» программированию) модель представляет собой, по сути, «движок» приложения. Именно модель определяет, что и как может делать программа. Термином «вид» (представление) фактически описывается все, что имеет непосредственное отношение к интерфейсу пользователя. Вид позволяет пользователю получать информацию о состоянии модели и передавать программе команды. Команды пользователя обрабатывает контроллер, который вносит соответствующие изменения в состояние модели или вида и, в частности, не позволяет пользователю нарушить целостность модели в результате введения неправильных команд. Как и многие другие парадигмы, призванные формализовать процесс создания программ, парадигма «модель-контроллер-вид» редко применяется на практике в чистом виде. В частности, отдельные элементы парадигмы нередко объединяются друг с другом. Система Interview превращает парадигму «модель-контроллер-вид» в парадигму «модель-вид», объединяя контроллер и вид в одно целое. Хотя парадигма «модель-контроллервид» (а, следовательно, и Interview) может применяться при написании множества типов программ, разработчики Interview, судя по всему, ориентировались в основном на создание клиентских приложений для работы с базами данных. Именно на примере клиентского приложения БД проще всего понять, как работает среда Interview. В клиентском приложении БД, использующем Interview, модель играет роль посредника между БД и интерфейсом пользователя. Именно модель определяет логику представления данных. Когда пользователь хочет получить информацию о текущем состоянии БД, пользовательский интерфейc (компонент «вид») обращается к модели напрямую. Для работы с отдельными элементами данных служат делегаты, которые передают команды пользователя модели.

Играем в классики

Переходя от абстрактного изложения принципов системы Interview к изложению более конкретному, мы должны познакомиться (простите за каламбур) с тремя абстрактными классами QAbstractItemModel, QAbstractItemView и QAbstractItemDelegate. Они являются предками всех классов, реализующих, соответственно, модели, представления (виды) и делегаты. Классы QtableView, QtreeView и QListView реализуют три наиболее популярные формы представления данных: таблицу, дерево и простой список. Если вам требуется более сложный компонент отображения модели, вам придется создавать собственный класс, основанный на QAbstractItemView.

Класс QStandardItemModel, являющийся потомком QAbstractItemModel, представляет собой реализацию модели в самом общем смысле. Помимо прочего, этот класс реализует ряд методов, предназначенных для работы с индексами. Индексы используются в среде Interview для указания элементов данных, с которыми работает модель. Класс QDirModel реализует модель для работы с директориями. Этот класс пригодится вам, если вы надумаете писать собственный файл-менеджер или свою версию диалоговых окон открытия и сохранения файла. Следует отметить, что один и тот же объект, реализующий модель, может взаимодействовать (в том числе, одновременно) с объектами нескольких разных классов, отвечающими за представление данных. Например, уже упомянутый класс QDirModel может использовать для представления информации о директориях классы QtableView, QtreeView, и QListView. Класс QStringListModel, как можно догадаться по его названию, реализует модель, основой которой является список строк.

Классы QAbstractTableModel и QAbstractListModel могут служить основой для ваших собственных классов-моделей, предполагающих представление данных в виде таблиц и списков соответственно

Такие классы, как QtreeView и QListView, предназначены для работы с моделями, но использовать их в качестве самостоятельных виджетов затруднительно. Для решения этой проблемы на базе классов QtableView, QtreeView и QListView созданы классы QtableWidget, QtreeWidget и QListWidget. Объекты этих классов представляют собой обычные визуальные компоненты, при работе с которыми пользователь может добавлять и удалять данные, не заботясь о моделях и делегатах. На самом деле, эти классы просто реализуют свои собственные модели данных, незаметные для пользователя. Как видим, система Interview играет в Qt большую роль, чем может показаться на первый взгляд!

Клиент на базе

Как отмечалось выше, наиболее удобное средство демонстрации возможностей Interview – клиентские приложения баз данных. Такое приложение мы и напишем. Прежде всего, рассмотрим арсенал специальных классов, которые среда Interview предоставляет нам для работы с базами данных. Их три: QSqlQueryModel, QSqlTableModel и QsqlRelationalTableModel, причем QSqlQueryModel – самый простой. Его возможности, фактически, ограничиваются передачей результата запроса к базе данных. При этом, правда, стоит отметить, что класс QSqlQueryModel обладает определенными возможностями, позволяющими изменить структуру отображения данных перед передачей ее на уровень представления. Класс QSqlTableModel гораздо функциональнее. Этот класс логически организует результаты SQL-запросов как таблицы и предусматривает функции редактирования данных. Наконец, класс QSqlRelationalTableModel позволяет задействовать в приложении основные возможности реляционной модели баз данных – работу с данными из нескольких таблиц, связанных внешними ключами. Для представления данных моделей SQL наиболее логично использовать объекты класса QtableView (хотя унифицированная структура среды Interview позволяет использовать совместно с SQL-моделями и другие стандартные «виды», они, как правило, менее удобны или информативны при работе с данными БД).

Для нашего первого приложения InterviewQt4!) воспользуемся самой простой SQL-моделью QSqlQueryModel. Мы создадим программу, позволяющую просматривать некий каталог музыкальных произведений (на самом деле – крошечный фрагмент моего музыкального каталога). Для нашего приложения БД нам понадобится база данных, содержащая хотя бы одну таблицу. При написании программы для примера я воспользовался СУБД PostrgeSQL, которая присутствует в любом дистрибутиве Linux. Если захотите, вы сможете адаптировать этот пример к любой другой СУБД.

Для любителей точных инструкций, привожу последовательность действий по созданию базы данных testdb. Настройте и запустите сервер PostrgeSQL на вашем компьютере (см. LXF85). Далее скомандуйте

createdb testdb

войдите в консольный клиент PostgreSQL:

psql testdb

и скомандуйте

\i createtable.sql

Файл createtable.sql вы найдете на прилагаемом диске. Теперь можете выйти из клиента с помощью команды \q.

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

 #include <QtDebug>
 #include <QtGui>
 #include <QSqlDatabase>
 #include <QSqlQueryModel>
 #include <QSqlError>
 #include <QTableView>
 int main(int argc, char *argv[])
 {
     QApplication app(argc, argv);
      QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
      db.setHostName("localhost");
      db.setDatabaseName("testdb");
     db.setUserName("user");
     db.setPassword("password");
     if (!db.open())
             {
                         qDebug() << QObject::trUtf8("Не смогла я открыть базу данных") << db.lastError().text();
                         return -1;
             }
             QSqlQueryModel * model = new QSqlQueryModel(0);
             model->setQuery("SELECT * FROM music");
             model->setHeaderData(0, Qt::Horizontal, QObject::trUtf8("Автор"));
             model->setHeaderData(1, Qt::Horizontal, QObject::trUtf8("Альбом"));
             model->setHeaderData(2, Qt::Horizontal, QObject::trUtf8("Произведение"));
             model->setHeaderData(3, Qt::Horizontal, QObject::trUtf8("Год выхода"));
             QTableView * view = new QTableView(0);
             view->setModel(model);
             view->setWindowTitle(QObject::trUtf8("Музыкальный каталог"));
             view->show();
             return app.exec();
  }

Исходный текст программы начинается, естественно, с заголовочных файлов. Первым следует <QtDebug>, он содержит полезные средства для вывода отладочных сообщений. Вследствие некоторых особенностей структуры заголовочных файлов Qt4.x, если вы решили включить в текст программы этот файл, вы должны включить его прежде всех других заголовочных файлов. Нарушение этого правила приводит к тому, что при определенном сочетании версий Qt4.x, набора используемых заголовочных файлов и фазы Луны компилятор начинает выдавать довольно странные сообщения об ошибках. Далее следует заголовочный файл <QtGui>. Этот файл содержит объявления классов и функций, реализованных в двух базовых модулях QtQtCore и QtGui. Включив этот файл в текст нашей программы, мы избавляем себя от необходимости добавлять по отдельности заголовочные файлы для таких классов, как QApplication. Файл <QSqlDatabase> содержит объявление класса QSqlDatabase, который мы используем для создания соединения с сервером БД. Мы включаем в текст программы определения классов QSqlQueryModel и QtableView.

Нашей первой задачей, как всегда, является создание объекта класса QApplication. Затем мы создаем соединение с сервером баз данных. Объект QSqlDatabase, инкапсулирующий соединение с сервером, создается с помощью статического метода QSqlDatabase::addDatabase() Этот метод существует в нескольких вариантах, каждому из которых при вызове можно передать несколько аргументов. Мы передаем методу addDatabase() один аргумент (значения остальных заданы по умолчанию), представляющий собой имя драйвера СУБД. Поскольку я воспользовался PostgreSQL, в программе указывается драйвер QPSQL. Если вы захотите использовать MySQL, вам понадобится драйвер QMYSQL. По умолчанию драйверы скомпилированы в виде отдельных модулей, но если хотите, можете встроить код драйвера в свое приложение. Для этого вам понадобятся исходные тексты Qt. Исходные тексты всех драйверов вы найдете в поддиректориях директории QTDIR/src/sql/drivers/.

Теперь, когда у нас есть объект db класса QSqlDatabase, реализующий соединение с сервером БД, мы должны настроить это соединение. С помощью соответствующих методов объекта db мы указываем имена узла, базы данных, имя пользователя и его пароль (если вы создавали базу данных так, как описано выше, имя пользователя и пароль для доступа к ней совпадают с именем пользователя и паролем вашеучетной записи Linux). Настроенное соединение открывается с помощью метода open(). Этот метод возвращает значение типа bool, которое указывает, удалось ли установить соединение с сервером БД. Если open() возвращает false, наша программа выводит жалобное сообщение, затем более подробное описание ошибки (с помощью метода db.lastError().text()), и завершает работу.

Успешно установив соединение с БД, мы можем приступить к созданию модели (объект model класса QSqlQueryModel). Главным методом объекта QSqlQueryModel является метод setQuery(), который позволяет указать текст SQL-запроса с БД. В нашем примере запрос выбирает все данные из таблицы music. Внимательно глядя на этот код, вы можете спросить: а откуда объект model узнает, из какой базы данных, иначе говоря, из какого объекта QSqlDatabase, он должен получить данные? Ответ на этот вопрос прост. При вызове статического метода addDatabase(), которым был создан объект db, мы могли бы указать уникальное имя соединения. Поскольку мы этого не сделали, созданное нами соединение с БД стало соединением по умолчанию (вполне логично: ведь наша программа использует только одну базу данных). Получить объект, реализующий соединение по умолчанию, можно с помощью статического метода QSqlDatabase::database(), вызванного без параметров. Именно так метод setQuery() объекта model определяет нужное соединение с БД. Если бы мы работали с несколькими соединениями, мы могли бы воспользоваться перегруженным вариантом метода setQuery(), которому помимо текста запроса передается ссылка на объект QSqlDatabase.

Метод setHeaderData(), который мы далее вызываем, позволяет назначить произвольные заголовки столбцам таблицы. Это один из немногих «косметических» методов, с помощью которых модель QSqlQueryModel может внести свою лепту в обработку данных (еще один метод – removeColumns() – позволяет сделать невидимыми отдельные столбцы таблицы). Мы используем метод setHeaderData() для присвоения русских названий столбцам таблицы.

Теперь мы переходим к созданию вида (объект view). Метод setModel() связывает вид с моделью, а метод setWindowTitle() устанавливает заголовок окна. Нам остается вызывать метод show(), что- бы сделать вид действительно видимым, и запустить цикл обработки сообщений приложения Qt.

Процедура сборки приложения выглядит несколько сложнее, чем в стандартном случае. После того как мы скомандовали qmake -project, нам необходимо «доработать» содержимое созданного файла .pro. Как уже отмечалось, различные компоненты Qt расположены в разных файлах библиотек, а заголовочные файлы – в разных директориях. К сожалению, сама утилита qmake не может определить, какие модули Qt использует наша программа, и придется указать их явным образом. Впрочем, сделать это несложно. Добавим в файл .pro строку

QT += sql

Таким образом мы указываем, что нашей программе потребуется модуль QtSql. В результате в создаваемый make-файл будут включены директивы, подключающие к проекту необходимые библиотеки и указывающие расположение заголовочных файлов, необходимых для работы с базами данных. Мы могли бы подключить компоненты модуля SQL и «вручную». Для этого в файл .pro надо было бы добавить строку

LIBS += -lQtSql

которая трансформировалась бы в make-файле в директиву компоновщика на связывание проекта с разделяемой библиотекой libQtSql.so. Заголовочные файлы модуля QtSql расположены по умолчанию в директории /usr/include/QtSql/. Если бы мы делали все вручную, нам бы следовало либо добавить эту директорию в переменную INCLUDEPATH файла .pro, либо заменить в исходных текстах програм- мы директивы типа

#include <QSqlDatabase>

на

#include <QtSql/QSqlDatabase>

Как видите, добавление модуля sql в переменную Qt избавило нас от многих хлопот. Теперь наша программа готова к сборке. Командуем:

qmake
make

В результате получаем простенькую программу просмотра содержимого таблицы БД (Рис. 1).

То ли еще будет

Система Qt4 подготовила для нас много нового. В следующей статье мы продолжим знакомство со средой Interview и созданными на ее основе компонентами, а также рассмотрим новые вспомогательные средства разработчика Qt. LXF

LXF102 89 1.jpg

Рис. 1. Программа для просмотра музыкального каталога.

Персональные инструменты
купить
подписаться
Яндекс.Метрика