Начинаем программировать на Qt

39 комментариев

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

Hello Notepad

В первом примере, мы создадим простое окно для редактирования текста. Это самая элементарная программа с графическим интерфейсом Qt.

TextEdit - пример программы на 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 под полем редактирования текста. При клике на нее, программа будет завершаться.

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

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

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(), которые были добавлены в предыдущем примере.

QFileDialog

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.

Пишем адресную книгу на Qt

Комментарии к статье: 39

Подождите, загружаются комментарии...

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

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