The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]

Каталог документации / Раздел "Программирование, языки" / Оглавление документа

Программирование графического интерфейса с помощью Qt 4, Часть 1

Знакомство с Qt 4, Interview Framework

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

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

Знакомство с Qt 4 мы начнем с вопросов лицензирования, которые многим, возможно, покажутся слишком скучными. В Qt 4 наконец-то реализована полноценная схема двойного лицензирования для всех платформ (Windows, X11 и Mac), причем открытый вариант Qt распространяется на условиях GPL 2.0. Сами представители Qt именуют свой подход Quid Pro Quo, что в вольном переводе с древней латыни означает баш на баш. Если вы хотите пользоваться средствами Qt бесплатно, взамен вы должны предоставить сообществу свой код (открытая модель). Если вы не желаете делиться кодом, вы должны заплатить деньги (своего рода выкуп, который, естественно, будет потрачен на дальнейшее совершенствование Qt). Интересно отметить, что лицензионная политика Qt не позволяет лицензиатам применять тот же принцип двойного лицензирования к своим продуктам. Если вы ведете разработку с помощью Qt, ваш проект должен быть либо открытым на условиях GPL, либо коммерческим. То есть, вам, конечно, никто не мешает раздавать ваш код бесплатно, но если это делается не на основе GPL, то для разработки этого кода необходимо использовать (и, естественно, оплатить) коммерческую версию Qt. У открытой версии Qt для Windows есть одна неприятная особенность согласно намерениям разработчиков ее можно использовать только совместно со средой компиляции MinGW, которая нравится не всем (мне, например, не нравится). Для того чтобы работать с MS Visual Studio (а это, все-таки, лучшая платформа разработки для Windows), придется заплатить за коммерческую версию Qt. Причины такого ограничения непонятны и упомянутый выше принцип quid pro quo здесь не срабатывает, поскольку сама Microsoft распространяет бесплатный вариант Visual Studio Express Edition. Кроме того, по сведениям, полученным с различных форумов, после небольшой обработки напильником открытую Qt 4 можно заставить работать с компиляторами С++ Microsoft и CodeGear, предназначенными для командной строки. По моему мнению, ограничив поддержку компиляторов для открытой версии Qt, разработчики из TrollTech сделали глупость, которая, я надеюсь, будет исправлена в дальнейших релизах Qt 4.x. 

Перейдем теперь к техническим новшествам Qt 4. Похоже, старым добрым контейнерам STL так и не суждено стать настоящим стандартом. У каждого набора визуальных компонентов, основанного на C++, есть свой набор контейнеров (что, вообще говоря, не очень хорошо, так как делает невизуальный код, который особенно часто использует абстрактные типы данных, труднопереносимым между разными платформами разработки). В Qt 4 появилась система контейнеров Tulip (тюльпан), призванная заменить старые контейнеры Qt 3. Tulip предоставляет в наше распоряжение стандартный список контейнеров последовательного доступа (список, связный список, очередь, вектор, стек) и несколько ассоциативных контейнеров: отображения (классы QMap и QMultiMap), хеш-таблицы (классы QHash и QMultiHash) и набор (класс QSet). Теоретически разница между классами QMap и QMultiMap и классами QHash и QMultiHash заключается в том, что первый класс из каждой пары позволяет связать с ключом только одно значение, тогда как второй класс позволяет назначать по несколько значений одному ключу. Напомню, что в ассоциативных контейнерах хранимым значениям сопоставляются ключи, которые позволяют организовать произвольный доступ к данным контейнера. Разделение классов QHash и QMultiHash вызывает некоторые вопросы. При использовании хеш-таблиц нередко возникают коллизии (когда двум хранимым значениям соответствует один и тот же ключ). Придумать хеш-функцию, которая бы гарантированно не вызывала коллизий, очень трудно (а иногда и нереально). Фактически, возможность связывать несколько значений с одним ключом является неотъемлемым свойством хеш-таблиц. Разработчики контейнеров Qt, разумеется, об этом знают, и в классе QHash реализована возможность добавления нескольких значений с одним и тем же ключом. В результате различие между контейнерами QHash и QMultiHash (второй, кстати, является потомком первого) выглядит скорее косметическим. 

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

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

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

Классы QAbstractTableModel и QAbstractListModel могут служить основой для ваших собственных классов-моделей, предполагающих представление данных в виде таблиц и списков соответственно. Такие классы как QTreeView и QListView предназначены для работы с моделями, но использовать их в качестве самостоятельных виджетов затруднительно. Для решения этой проблемы на базе классов QTableView, QTreeView и QListView созданы классы QTableWidget, QTreeWidget и QListWidget. Объекты этих классов представляют собой обычные визуальные компоненты, при работе с которыми пользователь может добавлять и удалять данные, не заботясь о моделях и делегатах. На самом деле, эти классы просто реализуют свои собственные модели данных, незаметные для пользователя. Как видим, система Interview Framework играет в Qt большую роль, чем может показаться на первый взгляд!

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

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

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

createdb testdb

теперь войдите в консольный клиент PostrgeSQL:

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>, который содержит полезные средства для вывода отладочных сообщений. Вследствие некоторых особенностей структуры заголовочных файлов Qt 4.x, если вы решили включить в текст программы этот файл, вы должны включить его прежде всех других заголовочных файлов. Нарушение этого правила приводит к тому, что при определенном сочетании версий Qt 4.x, набора используемых заголовочных файлов и фазы Луны компилятор начинает выдавать довольно странные сообщения об ошибках. Далее следует заголовочный файл <QtGui>. Этот файл содержит объявления классов и функций, реализованных в двух базовых модулях Qt QtCore и 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).

Программа просмотра БД

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

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

Исходные тексты примеров


Статья впервые опубликована в журнале Linux Format

© 2008  Андрей Боровский <anb @ symmetrica.net>



Спонсоры:
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2020 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру