Qt → Учим Qt вместе. Part 2
Ну вот и продолжение цикла о QT. Сори что так долго. В прошлой статье мы создали минимальное приложение с одной кнопкой на форме. В этой статье разберем более сложный пример в котом мы будем использоваться меню, строка состояния а также научимся создавать свои слоты.
Создаем консольный проект demo02 в QT Creator`е, добавляем в него два файла(mainwindow.h & mainwindow.cpp).
demo02.pro
main.cpp
mainwindow.h
mainwindow.cpp
Сигналы и слоты
Сигналы и слоты — это фундаментальный механизм Qt, позволяющий связывать объекты друг с другом. Cвязанным объектам нет необходимости что-либо «знать» друг о друге. Сигналы и слоты гораздо удобнее механизма функций обратного вызова (callbacks) и четко вписываются в концепцию ООП.
Для использования этого механизма объявление класса должно содержать специальный макрос Q_OBJECT на следующей строке после ключевого слова class:
После макроса Q_OBJECT не нужно ставить точку с запятой. Перед выполнением компиляции, Meta Object Compiler (MOC) анализирует такие классы и автоматически внедряет в них всю необходимую информацию (используются отдельные файлы, разработчик может их игнорировать).
Наследники QObject могут иметь любое количество сигналов и слотов.
Сигналы
Сигнал может быть определен следующим образом:
Для того чтобы инициировать сигнал (выслать сигнал) нужно ипользовать ключевое слово emit.
Сигналы могут использовать параметры для передачи дополнительной информации.
Слоты
Слоты практически идентичны обычным членам-методам C++, при их объявлении можно использовать стандартные спецификаторы доступа public, protected или private. Слоты можно вызывать напрямую, как обычные члены-функции класса C++. Главная особенность слотов — это возможность связывания с сигналами, в этом случае слоты будут вызваться автоматически при каждом возникновении соответствующих сигналов. В слотах нельзя использовать параметры по умолчанию.
Слот может быть определен следующим образом:
В теле слота можно узнать, какой объект выслал сигнал.
Соединение сигналов и слотов
Для соединения сигналов и слотов можно использовать статический метод connect, определенный в классе QObject. В общем виде соединение выглядит следующим образом:
signal и slot — сигнатуры сигнала и слота.
Пример соединения:
В приведенном выше примере, сигнал, возникающий при каждом изменении объекта spinbox, связывается с соответствующим слотом объекта slider. Вызов слота slider.setValue(int) происходит автоматически при каждом возникновении сигнала spinBox.valueChanged(int).
Существует множество различных вариантов соединения сигналов и слотов:
* Один сигнал может быть соединен со многими слотами:
* Множество сигналов могут быть соединены с единственным слотом:
* Сигналы могут быть соединены между собой:
* Метод disconnect можно использовать для того, чтобы удалить соединение между сигналом и слотом.
Локализация
В QT существует три стандартных способа локализации приложения, но мы будем использовать наиболее предпочтительный. Этот способ предлагает использовани специальной функции перевода tr, с помощью которой осуществляется интернационализация приложений. Эта статическая функция является членом всех классов Qt, порождённых от базового класса QObject, но если, как сейчас, мы собираемся вызвать её в главной программе, а не в каком-либо методе класса, то приходится указывать какой-нибудь подходящий объект, например, QObject::tr. Для указания кодировки, используемой функцией перевода, надо создать соответствующий кодек и передать его в качестве аргумента методу setCodecForTr.
Строка состояния
Строка состояния QStatusBar создаётся автоматически в нижней части главного окна приложения, если в программе хоть раз вызвается метод MainWindow::statusBar. При наведении указателя мыши на кнопку панели инструментов или пункт меню в строке состояния на время появляется текст подсказки, если этот текст определён для данной кнопки или данного пункта.
По умолчанию строка состояния представляется в виде одной панели, располагаемой по всей ширине родительского окна. Но её можно разбить по ширине на отдельные поля, если вставить в неё другие элементы, например, QLabel. Для этого предназначены методы addWidget, addPermanentWidget и insertWidget.
Элементы, добавляемые с помощью метода addPermanentWidget, располагаются в правой части строки состояния и не затираются сообщениями, выводимыми с помощью showMessage.
В нижней правой части строки состояния по умолчанию отображается специальный маркер, который можно «зацепить» указателем мыши для изменения размеров окна. Его показ можно запретить, вызвав QStatusBar::setSizeGripEnabled(false). При этом возможность изменять размеры окна по-прежнему остаётся.
Меню
Горизонтальная панель меню QMenuBar создаётся автоматически, если мы обращаемся к ней для добавления хотя бы одного вертикального меню QMenu.
Размещение элементов в окне
«Ручное» размещение
С помощью метода setGeometry(int x, int y, int w, int h) или setGeometry(const QRect&) можно задать положение и размер любого визуального элемента в пикселах. Для установки размеров без изменения положения может использоваться метод resize(int w, int h) или resize(const QSize&). Наоборот, для перемещения элемента в нужную позицию с сохранением прежних размеров служит метод move(int x, int y) или move(const QPoint&). Недостатком жёсткого варианта размещения элементов интерфейса является то, что пользователь не может изменить размер окна диалога (или изменение размеров окна не влияет на взаимное положение и размеры всех его элементов). В результате при низком разрешении монитора всё выглядит слишком крупно, а то и вовсе не помещается на экран, при высоком — наоборот, слишком мелко. Кроме того, в различных операционных системах используются разные шрифты, поэтому надписи и поля ввода, прекрасно смотревшиеся в одной системе, при переносе на другую платформу могут не уместиться в прежних границах. К тому же в солидных программных продуктах принято давать пользователю возможность настраивать интерфейс программы по своему вкусу, в частности, изменять гарнитуру и размер шрифта. А при локализации (переводе интерфейса программы на другой язык) всё ещё больше усложняется.
Менеджеры размещения
Менеджер размещения (layout manager) — это объект, который управляет размерами и положением виджетов. В Qt имеются классы QHBoxLayout, QVBoxLayout и QGridLayout, которые специально предназначены для управления положением и размерами элементов в окне. Первый позволяет располагать элементы друг за другом по горизонтали, второй — по вертикали, а третий размещает виджеты в ячейках воображаемой таблицы, причём каждый элемент может занимать несколько смежных ячеек по вертикали и/или горизонтали.
Ну вот на сегодня все.
Если какие то моменты остались не понятными пишите добавлю в описание статьи.
Создаем консольный проект demo02 в QT Creator`е, добавляем в него два файла(mainwindow.h & mainwindow.cpp).
demo02.pro
QT += gui
TARGET = demo02
#CONFIG += console
#CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp \
mainwindow.cpp
HEADERS += mainwindow.h
main.cpp
#include <QApplication>
#include <QMainWindow>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow *mw = new MainWindow();
mw->setGeometry(200,200,600,400);
mw->show();
return app.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTextCodec>
#include <QAction>
#include <QMenu>
#include <QMenuBar>
#include <QStatusBar>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QCheckBox>
#include <QFileDialog>
#include <QWidget>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
private:
QAction *actionOpen;
QAction *actionExit;
QAction *actionAbout;
QMenu *menuFile;
QMenu *menuHelp;
QLabel *labelMenu;
QLabel *labelFile;
QLabel *labelImage;
QPushButton *butOpen;
QCheckBox *cbSize;
QVBoxLayout *vlayout;
QHBoxLayout *hlayout;
QWidget *mainWidget;
private slots:
void slotOpen();
void slotAbout();
void slotImageSize(bool on);
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow()
{
QTextCodec *codec = QTextCodec::codecForName("UTF8");
QTextCodec::setCodecForTr(codec);
actionOpen = new QAction(tr("О&ткрыть"), this);
actionOpen->setStatusTip(tr("Открыть рисунок"));
connect(actionOpen, SIGNAL(triggered()), this, SLOT(slotOpen()));
actionExit = new QAction(tr("В&ыход"), this);
actionExit->setStatusTip(tr("Выход из программы"));
actionExit->setShortcut(tr("Ctrl+Q"));
connect(actionExit, SIGNAL(triggered()), this, SLOT(quit()));
actionAbout = new QAction(tr("&О программе"), this);
actionAbout->setStatusTip(tr("Сведения о программе"));
connect(actionAbout, SIGNAL(triggered()), this, SLOT(slotAbout()));
menuFile = menuBar()->addMenu(tr("&Файл"));
menuFile->addAction(actionOpen);
menuFile->addSeparator();
menuFile->addAction(actionExit);
menuFile = menuBar()->addMenu(tr("&Справка"));
menuFile->addAction(actionAbout);
labelMenu = new QLabel(statusBar());
labelFile = new QLabel();
statusBar()->setSizeGripEnabled(false);
statusBar()->addWidget(labelMenu, 1);
statusBar()->addWidget(labelFile, 2);
mainWidget = new QWidget();
setCentralWidget(mainWidget);
labelImage = new QLabel;
butOpen = new QPushButton(tr("Открыть"));
butOpen->setStatusTip(tr("Открыть рисунок"));
connect(butOpen, SIGNAL(clicked()), this, SLOT(slotOpen()));
cbSize = new QCheckBox(tr("Вписать рисунок в окно"));
connect(cbSize, SIGNAL(toggled(bool)), this, SLOT(slotImageSize(bool)));
vlayout = new QVBoxLayout;
hlayout = new QHBoxLayout;
vlayout->addWidget(labelImage);
hlayout->addWidget(butOpen);
hlayout->addWidget(cbSize);
hlayout->addStretch(1);
vlayout->addLayout(hlayout);
mainWidget->setLayout(vlayout);
}
void MainWindow::slotOpen()
{
labelImage->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
QString fileName = QFileDialog::getOpenFileName(0, tr("Открыть файл"), QDir::currentPath());
if (!fileName.isEmpty())
{
QImage image(fileName);
labelImage->setPixmap(QPixmap::fromImage(image));
labelFile->setText(fileName);
}
}
void MainWindow::slotAbout()
{
QMessageBox::about( 0 , tr("О программе..."), tr("©2009 Dmitry <a href='http://example.com'>http://example.com</a>< br>Специально для <a href='http://open-life.org'>OpenLife</a>"));
}
void MainWindow::slotImageSize(bool b)
{
labelImage->setScaledContents(b);
}
Сигналы и слоты
Сигналы и слоты — это фундаментальный механизм Qt, позволяющий связывать объекты друг с другом. Cвязанным объектам нет необходимости что-либо «знать» друг о друге. Сигналы и слоты гораздо удобнее механизма функций обратного вызова (callbacks) и четко вписываются в концепцию ООП.
Для использования этого механизма объявление класса должно содержать специальный макрос Q_OBJECT на следующей строке после ключевого слова class:
class MyClass {
Q_OBJECT
public:
...
};
После макроса Q_OBJECT не нужно ставить точку с запятой. Перед выполнением компиляции, Meta Object Compiler (MOC) анализирует такие классы и автоматически внедряет в них всю необходимую информацию (используются отдельные файлы, разработчик может их игнорировать).
Наследники QObject могут иметь любое количество сигналов и слотов.
Сигналы
Сигнал может быть определен следующим образом:
class MyClass: public QObject {
Q_OBJECT
public:
...
signals:
void mySignal();
};
Для того чтобы инициировать сигнал (выслать сигнал) нужно ипользовать ключевое слово emit.
void MyClass ::sendMySignal()
{
emit mySignal();
}
Сигналы могут использовать параметры для передачи дополнительной информации.
Слоты
Слоты практически идентичны обычным членам-методам C++, при их объявлении можно использовать стандартные спецификаторы доступа public, protected или private. Слоты можно вызывать напрямую, как обычные члены-функции класса C++. Главная особенность слотов — это возможность связывания с сигналами, в этом случае слоты будут вызваться автоматически при каждом возникновении соответствующих сигналов. В слотах нельзя использовать параметры по умолчанию.
Слот может быть определен следующим образом:
class MyClass: public QObject {
Q_OBJECT
public slots:
void mySlot()
{
...
}
};
В теле слота можно узнать, какой объект выслал сигнал.
Соединение сигналов и слотов
Для соединения сигналов и слотов можно использовать статический метод connect, определенный в классе QObject. В общем виде соединение выглядит следующим образом:
connect(sender, SIGNAL(signal), receiver, SLOT(slot));
sender и receiver — это указатели на QObject.signal и slot — сигнатуры сигнала и слота.
Пример соединения:
QObject::connect(spinBox, SIGNAL(valueChanged(int)),slider, SLOT(setValue(int)));
В приведенном выше примере, сигнал, возникающий при каждом изменении объекта spinbox, связывается с соответствующим слотом объекта slider. Вызов слота slider.setValue(int) происходит автоматически при каждом возникновении сигнала spinBox.valueChanged(int).
Существует множество различных вариантов соединения сигналов и слотов:
* Один сигнал может быть соединен со многими слотами:
connect(slider, SIGNAL(valueChanged(int)),spinBox, SLOT(setValue(int)));
connect(slider, SIGNAL(valueChanged(int)),this, SLOT(updateStatusBarIndicator(int)));
При возникновении сигнала, слоты вызываются один за другим, порядок не определен.* Множество сигналов могут быть соединены с единственным слотом:
connect(sender0, SIGNAL(overflow()),receiver1, SLOT(handleMathError()));
connect(sender1, SIGNAL(divisionByZero()),receiver1, SLOT(handleMathError()));
* Сигналы могут быть соединены между собой:
connect(sender1, SIGNAL(function1()),receiver, SIGNAL(function2()));
При возникновении первого сигнала, автоматически генерируются все сязанные сигналы. Не считая этого, соединение сигнал-сигнал неотличимо от соединения сигнал-слот.* Метод disconnect можно использовать для того, чтобы удалить соединение между сигналом и слотом.
disconnect(sender0, SIGNAL(overflow()),receiver1, SLOT(handleMathError()));
На практике прямой вызов disconnect используется редко, так как Qt автоматически удаляет все соединения при удалении объектов. Локализация
В QT существует три стандартных способа локализации приложения, но мы будем использовать наиболее предпочтительный. Этот способ предлагает использовани специальной функции перевода tr, с помощью которой осуществляется интернационализация приложений. Эта статическая функция является членом всех классов Qt, порождённых от базового класса QObject, но если, как сейчас, мы собираемся вызвать её в главной программе, а не в каком-либо методе класса, то приходится указывать какой-нибудь подходящий объект, например, QObject::tr. Для указания кодировки, используемой функцией перевода, надо создать соответствующий кодек и передать его в качестве аргумента методу setCodecForTr.
QTextCodec *codec = QTextCodec::codecForName("UTF8");
QTextCodec::setCodecForTr(codec);
Строка состояния
Строка состояния QStatusBar создаётся автоматически в нижней части главного окна приложения, если в программе хоть раз вызвается метод MainWindow::statusBar. При наведении указателя мыши на кнопку панели инструментов или пункт меню в строке состояния на время появляется текст подсказки, если этот текст определён для данной кнопки или данного пункта.
По умолчанию строка состояния представляется в виде одной панели, располагаемой по всей ширине родительского окна. Но её можно разбить по ширине на отдельные поля, если вставить в неё другие элементы, например, QLabel. Для этого предназначены методы addWidget, addPermanentWidget и insertWidget.
Элементы, добавляемые с помощью метода addPermanentWidget, располагаются в правой части строки состояния и не затираются сообщениями, выводимыми с помощью showMessage.
В нижней правой части строки состояния по умолчанию отображается специальный маркер, который можно «зацепить» указателем мыши для изменения размеров окна. Его показ можно запретить, вызвав QStatusBar::setSizeGripEnabled(false). При этом возможность изменять размеры окна по-прежнему остаётся.
statusBar()->setSizeGripEnabled(false);
statusBar()->addWidget(labelMenu, 1);
statusBar()->addWidget(labelFile, 2);
Меню
Горизонтальная панель меню QMenuBar создаётся автоматически, если мы обращаемся к ней для добавления хотя бы одного вертикального меню QMenu.
actionOpen = new QAction(tr("О&ткрыть"), this);
actionOpen->setStatusTip(tr("Открыть рисунок"));
connect(actionOpen, SIGNAL(triggered()), this, SLOT(slotOpen()));
actionExit = new QAction(tr("В&ыход"), this);
actionExit->setStatusTip(tr("Выход из программы"));
actionExit->setShortcut(tr("Ctrl+Q"));
connect(actionExit, SIGNAL(triggered()), this, SLOT(quit()));
menuFile = menuBar()->addMenu(tr("&Файл"));
menuFile->addAction(actionOpen);
menuFile->addSeparator();
menuFile->addAction(actionExit);
Размещение элементов в окне
«Ручное» размещение
С помощью метода setGeometry(int x, int y, int w, int h) или setGeometry(const QRect&) можно задать положение и размер любого визуального элемента в пикселах. Для установки размеров без изменения положения может использоваться метод resize(int w, int h) или resize(const QSize&). Наоборот, для перемещения элемента в нужную позицию с сохранением прежних размеров служит метод move(int x, int y) или move(const QPoint&). Недостатком жёсткого варианта размещения элементов интерфейса является то, что пользователь не может изменить размер окна диалога (или изменение размеров окна не влияет на взаимное положение и размеры всех его элементов). В результате при низком разрешении монитора всё выглядит слишком крупно, а то и вовсе не помещается на экран, при высоком — наоборот, слишком мелко. Кроме того, в различных операционных системах используются разные шрифты, поэтому надписи и поля ввода, прекрасно смотревшиеся в одной системе, при переносе на другую платформу могут не уместиться в прежних границах. К тому же в солидных программных продуктах принято давать пользователю возможность настраивать интерфейс программы по своему вкусу, в частности, изменять гарнитуру и размер шрифта. А при локализации (переводе интерфейса программы на другой язык) всё ещё больше усложняется.
Менеджеры размещения
Менеджер размещения (layout manager) — это объект, который управляет размерами и положением виджетов. В Qt имеются классы QHBoxLayout, QVBoxLayout и QGridLayout, которые специально предназначены для управления положением и размерами элементов в окне. Первый позволяет располагать элементы друг за другом по горизонтали, второй — по вертикали, а третий размещает виджеты в ячейках воображаемой таблицы, причём каждый элемент может занимать несколько смежных ячеек по вертикали и/или горизонтали.
mainWidget = new QWidget();
setCentralWidget(mainWidget);
labelImage = new QLabel;
butOpen = new QPushButton(tr("Открыть"));
butOpen->setStatusTip(tr("Открыть рисунок"));
connect(butOpen, SIGNAL(clicked()), this, SLOT(slotOpen()));
cbSize = new QCheckBox(tr("Вписать рисунок в окно"));
connect(cbSize, SIGNAL(toggled(bool)), this, SLOT(slotImageSize(bool)));
vlayout = new QVBoxLayout;
hlayout = new QHBoxLayout;
vlayout->addWidget(labelImage);
hlayout->addWidget(butOpen);
hlayout->addWidget(cbSize);
hlayout->addStretch(1);
vlayout->addLayout(hlayout);
mainWidget->setLayout(vlayout);
Ну вот на сегодня все.
Если какие то моменты остались не понятными пишите добавлю в описание статьи.
- +16
- DmitryG
- 13 июля 2009, 12:35
Очень хотелось бы увидеть что-нибудь подобное с использованием Qt Designer.
За статью спасибо, ждём продолжение.
Парсер съел! :(
Да я знаю. Это для общего развития, что бы лучше понимать как это все работает! Всегда надо начинать с основ!!!(или желательно :) )