Добро пожаловать в мир Qt — кроссплаформенного инструмента для создания графических интерфейсов. В этом руководстве мы изучим базовые аспекты работы с Qt, на примере написания простого текстового редактора. После прочтения этого руководства, вы сможете углубиться в изучение документации API и искать другую необходимую информацию для разработки приложений.
Hello Notepad
В первом примере, мы создадим простое окно для редактирования текста. Это самая элементарная программа с графическим интерфейсом Qt.
Исходный код примера:
#include <QApplication>
#include <QTextEdit>
int main(int argv, char **args)
{
QApplication app(argv, args);
QTextEdit textEdit;
textEdit.show();
return app.exec();
}
Рассмотрим подробно каждую строчку кода. В первых двух, мы подключаем заголовочные файлы классов QApplication и QTextEdit, которые необходимы для работы этого примера. Каждому классу в Qt соответствует заголовочный файл с таким же названием.
В строке 6 создается объект класса QApplication, который управляет основными
ресурсами, необходимыми для запуска любой программы с графическим
интерфейсом Qt. Ему необходимо передать аргументы argv
и args
функции
main()
, т.к. Qt использует некоторые параметры командной строки,
передаваемые при запуске программы.
В восьмой строке кода создается объект класса QTextEdit. QTextEdit — это визуальный элемент графического интерфейса. В Qt используются определенные виджеты — например, полосы прокрутки, метки и кнопки radio. Виджет может быть контейнером для хранения других виджетов. Наглядным примером является главное диалоговое окно программы.
В строке 9, окно редактирования текста выводится на экран в главном фрейме
программы. Поскольку виджеты могут работать как контейнеры (В экземпляре класса
QMainWindow находятся
полосы панели инструментов, меню, строка состояния и несколько других
виджетов), мы может отобразить только один виджет в окне нашей программы. По
умолчанию, виджеты не видны. Функция show()
используется для их отображения.
В строке 11, создается объект QApplication, который генерирует цикл событий в процессе работы приложения. События генерируются и передаются на обработку виджетам. Примерами событий могут являться клики мыши, нажатия клавиш на клавиатуре и т.п. Когда вы вводите текст в окне редактирования виджета QTextEdit, нажатия клавиш обрабатываются средствами виджета, и вводимый текст отображается в процессе набора.
Для запуска программы, откройте командную строку и зайдите в директорию с .cpp файлом программы. Выполните следующие команды shell-интерпретатора, чтобы скомпилировать пример.
qmake -project
qmake
make
После успешного выполнения предыдущих команд, скомпилированная программа будет
размещена в директории текущего проекта (в Windows вы можете использовать
nmake
вместо make
. Исполняемые файлы будут размещены в директориях debug
или release, которые создадутся командой make
. qmake
— это утилита,
которая создает файлы конфигурации Qt-проекта, если ей передан аргумент
-project
. После создания файла конфигурации (.pro), qmake
генерирует
Makefile, который используется утилитой make
для сборки приложения. Позже,
мы рассмотрим процесс написания собственных .pro файлов.
Добавления кнопки выхода
В реальных проектах, обычно используется более одного виджета. Сегодня мы добавим кнопку QPushButton под полем редактирования текста. При клике на нее, программа будет завершаться.
Рассмотрим исходный код программы.
#include <QtGui>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QTextEdit textEdit;
QPushButton quitButton("Quit");
/* 10 */ QObject::connect(&quitButton, SIGNAL(clicked()), qApp, SLOT(quit()));
/* 12 */ QVBoxLayout layout;
/* 13 */ layout.addWidget(&textEdit);
/* 14 */ layout.addWidget(&quitButton);
QWidget window;
/* 17 */ window.setLayout(&layout);
window.show();
return app.exec();
}
Сначала подключается заголовочный файл QtGui. В нем содержатся все классы графического интерфейса Qt.
В строке 10 применяется механизм сигналов и слотов, для того, чтобы закрыть программу после нажатия на кнопку выхода. Слот — это функция, которая может быть вызвана в процессе выполнения программы. Сигнал — функция, вызывающая функции-слоты, которые с ним посредством QObject::connect.
quit()
— слот класса QApplication, завершающий работу приложения. clicked()
— сигнал, который передает нажатая кнопка QPushButton. Статическая функция QObject::connect связывает определенный слот и сигнал. SIGNAL()
и SLOT()
— макросы, которые принимают сигнатуры сигналов и слотов, для их связи между собой. Также, функции connect()
необходимо передать указатели на объекты, которые будут принимать и рассылать сигналы.
В строке 12 создается объект класса QVBoxLayout. Как уже было сказано, виджеты могут содержать в себе другие виджеты. Можно задавать координаты и размер вложенных виджетов непосредственно, но обычно для этого используют слои (layouts). Слой управляет границами вложенных виджетов. Например, объект QVBoxLayout размещает виджеты в одну вертикальную колонку.
В строках 13 и 14, мы добавляем поле редактирования текста и кнопку выхода к слою layout
. В строке 17 задается главный слой для всего приложения.
Наследование QWidget
Когда пользователь хочет закрыть приложение, ему можно показать всплывающее окно, в котором программа спросит подтверждение данного действия. В этом примере мы создадим наследник QWidget и добавим в него слот, который будет привязан к кнопке выхода.
Рассмотрим следующий код.
class Notepad : public QWidget
{
Q_OBJECT
public:
Notepad();
private slots:
/* 13 */ void quit();
private:
QTextEdit *textEdit;
QPushButton *quitButton;
};
Макрос Q_OBJECT объявляет наш класс как QObject. Он должен находиться в самом начале определения класса. QObject добавляет несколько расширенных возможностей к обычному классу C++. Например, имена классов и слотов можно запросить в процессе выполнения. Также, мы можем узнать типы параметров слота и вызвать его.
В строке 13 объявляется слот quit()
. В последствии, мы присоединим этот слот к сигналу.
В предыдущих примерах, создание графики и присоединение слотов осуществлялось внутри функции main()
. Сейчас мы будем использовать конструктор Notepad.
Notepad::Notepad()
{
textEdit = new QTextEdit;
quitButton = new QPushButton(tr("Quit"));
connect(quitButton, SIGNAL(clicked()), this, SLOT(quit()));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(textEdit);
layout->addWidget(quitButton);
setLayout(layout);
setWindowTitle(tr("Notepad"));
}
Как видно из определения класса, мы используем указатели на объекты textEdit
и quitButton
. Для объектов QObject, почти всегда рациональнее выделять память в куче, вместо их копирования.
Мы используем функцию tr()
для обработки всех строк, которые видны пользователю. Эта функция применяется, когда приложение нужно перевести на несколько языков. Здесь мы не будем углубляться в детали, но вы можете найти нужную информацию в описании библиотеки Qt Linguist.
Создание файлов .pro
Ранее, мы использовали команду qmake -project
для создания файлов .pro. В следующем примере, мы создадим этот файл вручную.
HEADERS = hotepad.h
SOURCES = notepad.cpp \
main.cpp
Теперь для сборки программ на Qt, будут использоваться следующие команды.
qmake
make
Использование QMainWindow
Во многих приложениях удобно использовать QMainWindow — слой, в который можно добавлять контекстные меню, встроенные виджеты, панели инструментов и строку состояния. В QMainWindow есть центральная область, куда можно добавлять любые дочерние виджеты. В этом примере мы разместим там поле для редактирования текста.
Рассмотрим определение класса Notepad
.
#include <QtGui>
class Notepad : public QMainWindow
{
Q_OBJECT;
public:
Notepad();
private slots:
void open();
void save();
void quit();
private:
QTextEdit *textEdit;
QAction *saveAction;
QAction *exitAction;
QMenu *fileMenu;
};
Мы создали два дополнительных слота — open()
и save()
. Они будут открывать и сохранять документ. Пример их реализации будет представлен позже.
Очень часто, один и тот же слот используется одновременно несколькими виджетами. Например, в пунктах меню и соответственных кнопках на панели инструментов. Чтобы упростить этот процесс, в Qt существует класс QAction, который можно передавать сразу нескольким виджетам и присоединять к слоту. Например, QMenu и QToolBar могут создавать пункты меню, используя одно действие QAction. Скоро вы убедитесь, как это облегчает процессе разработки.
Сначала, мы используем конструктор класса Notepad
для создания графического интерфейса программы.
Notepad::Notepad()
{
openAction = new QAction(tr("&Open"), this);
saveAction = new QAction(tr("&Save"), this);
exitAction = new QAction(tr("E&xit"), this);
connect(openAction, SIGNAL(triggered()), this, SLOT(open()));
connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(openAction);
fileMenu->addAction(saveAction);
fileMenu->addSeparator();
fileMenu->addAction(exitAction);
textEdit = new QTextEdit;
setCentralWidget(textEdit);
setWindowTitle(tr("Notepad"));
}
При создании действий, задается текст, отображаемый внутри виджетов, в которые будут добавлены эти действия (в нашем случае, пункты меню). Если нам нужно добавить кнопки для этих действий в панель инструментов, мы можем передать иконку для каждого действия.
Когда пользователь кликает на пункте меню, рассылается сигнал действия и вызывается соответствующий слот.
Сохранение и открытие
В этом примере, мы реализуем функционал слотов open()
и save()
, которые были добавлены в предыдущем примере.
void Notepad::open()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "",
tr("Text Files (*.txt);;C++ Files (*.cpp *.h)"));
if (fileName != "") {
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::critical(this, tr("Error"), tr("Could not open file"));
return;
}
QTextStream in(&file);
textEdit->setText(in.readAll());
file.close();
}
}
Первый шаг — это запрос у пользователя имени файла для открытия. В состав Qt входит класс QFileDialog — диалоговое окно, в котором пользователь может выбрать файл. Статическая функция getOpenFileName() открывает модальное окно для выбора файла и возвращает путь к выбранном файлу. Если пользователь отменил действие, функция возвращает пустую строку.
После того, как было получено имя файла, мы пробуем открыть его с помощью функции open(). Она возвращает логическое true
в случае успешного завершения. Если файл открыть не удалось, мы используем класс QMessageBox для того, чтобы показать пользователю всплывающее окно с сообщением об ошибке (смотрите описание класса QMessageBox, для получения более подробной информации).
Чтение данных становится тривиальной задачей, благодаря классу QTextStream — оберткой над объектом QFile. Функция readAll() возвращает содержимое файла, как объект QString. Это содержимое мы поместим в поле для редактирования текста. Затем, мы закрываем файл с помощью функции close(), чтобы вернуть файловый дескриптор операционной системе.
Перейдем к реализации слота save()
.
void Notepad::save()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "",
tr("Text Files (*.txt);;C++ Files (*.cpp *.h)"));
if (fileName != "") {
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
// error message
} else {
QTextStream stream(&file);
stream << textEdit->toPlainText();
stream.flush();
file.close();
}
}
}
Для сохранения данных мы снова используем класс QTextStream. При помощи него, можно записывать строки QString в файл, используя оператор <<
.
Оригинал перевода: http://doc.qt.nokia.com/4.7/gettingstartedqt.html.
Комментарии к статье: 39
Возможность комментировать эту статью отключена автором. Возможно, во всем виновата её провокационная тематика или большое обилие флейма от предыдущих комментаторов.
Если у вас есть вопросы по содержанию статьи, рекомендуем вам обратиться за помощью на наш форум.