Особенности Qt: слоты и сигналы, описание QObject и QApplication, виды окон и т.д.

Вводная часть:

Qt – это не только элементы графического интерфейса. Этот фреймворк представляет собой взаимосвязанную систему. Родственность Qt-объектов осуществляется через наследование класса QObject. А связи между ними через сигнально-слотовую систему.В этой статье будут описаны основные классы и полезные особенности этой библиотеки.


QObject

Это базовый класс для всех объектов Qt. Его наследует любой класс использующий сигналы и слоты. Он обеспечивает возможность соединения объектов друг с другом. А также предоставляет к этому полезную функциональность. Во введение этого базового класса находиться:

  • Прослеживание за потомками и родителями и возвращение указателей на них (Наследственная информация).
  • Программная реализация таймера
  • Динамические свойства
  • Интернационализация приложения

QApplication

Это сердце любого приложения использующего графические элементы.Объект этого класса должен быть создан в единственном экземпляре перед основным кодом программы. В его сферу ответственности входит:

  • Инициализирует приложение в соответствии с пользовательскими настройками.
  • Выполняет обработку сообщений и передачу их соответствующим виджетам.
  • Анализирует аргументы командной строки и соответствующим образом устанавливает свой внутреннее состояние.
  • Определяет внешний вид приложения через установку стиля и изменение разрешенных цветов.
  • Предоставляет ссылки на глобальный буфер обмена(clipboard()) и другие полезные объекты.
  • Следит за открытыми окнами приложения и может выдать информацию о них.
  • Управляет видом иконки курсора.
  • И другие полезные функции.

Слоты и сигналы:

Для того чтобы объекты могли общаться, был введён механизм сигналов и слотов. С помощью этого объекты сообщают друг другу о произошедших событиях и отсылают нужные данные. Слот, как и функция-член, может быть публичной, приватной, защищённой или виртуальной. Он вызывается, если приходит подсоединённый к нему сигнал. Он в свою очередь генерируется, если в коде обработки события имеется вызов(макрос) emit signal().

Для себя можно представить определенную схему:

Событие Генерация сигналаПередача параметров в слот.

Как выглядит в программе:

«Щелчок по кнопке»программная обработкаsignal(clicked()) slot(myslot()).

Основа для создания:

В классе, который должен будет иметь свои сигналы и слоты, нужно произвести наследование от QObject или другого класса библиотеки, наследующего его, и определить макрос Q_Object, с помощью этого объявления компилятор понимает, что необходимо сгенерировать из написанного qt-кода, который использует свои нововведения, в равнозначный шаблонный код стандартного C++. Начиная с Qt 5.0 появился новый способ соединения:

1)Ранняя версия:

QObject::connect(&Отправитель,SIGNAL(mysignal()),&Адресат,SLOT(myslot()));

В шаблонной версии выглядит так:

bool QObject::connect (const QObject * sender, const char * signal, const QObject * receiver, const char * methodtype Qt::ConnectionType = Qt::AutoConnection)

Для создания связи необходимо прописать:

  1. Отправителя и его сигнал.
  2. Получателя и его слот.
  3. А также тип соединения, который зависит от того используете вы соединение в потоке или нет. По умолчанию тип соединения определяется автоматически.

Этот старый способ соединения использует механизм обработки строк. Если не существует сигнала или слота с данными именами, то соединения не происходит, а в консоль не отсылается сообщение об ошибке. Разъединение происходит при уничтожении объекта автоматически. В редких случаях используется такая форма для отключения сигналов:

QObject::disconnect(&Отправитель,SIGNAL(mysignal()),&Адресат,SLOT(myslot()));
Шаблон: bool QObject::disconnect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method )

2)connect(&Отправитель,&Class_1::mysignal,&Адресат,&Class_2::myslot);

Шаблон:connect(sender, &Sender::valueChanged,  receiver, &Receiver::updateValue);

Достоинства:

  1. Проверка существования сигналов и слота, типов, или если Q_OBJECT отсутствует(выдаёт ошибки).
  2. Параметр может быть определением типа или с различными спецификаторами пространства имен.
  3. Возможность автоматического приведения типов, если есть неявное преобразование (например, от QString до QVariant)
  4. Возможно соединиться с любой функцией-членом класса(произведённого от QObject),а не только со слотами.

Недостатки:

  1.    Более сложный синтаксис(Вы должны определить тип своего объекта)
  2.    Очень сложный синтаксис в случаях перегрузок
  3.    Параметры по умолчанию в слоте теперь не поддерживаются.

Нововведение: возможность соединения с простой функцией.

Благодаря новому синтаксису, появилась возможность соединения с функциями не являющимися слотами. Пример:connect(sender, &Sender::valueChanged, saveFunction);

При создании такого вида соединения не происходит автоматического разъединения при уничтожении получателя! Это нужно делать вручную.

Такая форма используется для разъединения:

Шаблон:QObject::disconnect(sender, &Sender::valueChanged,  receiver, &Receiver::updateValue );

Оба способа соединения разрешены для применения в программе. Так-как у них имеются различия нужно выбирать тот способ, который лучше подходит под ваши нужды.

Разберём на примере использование слотов и сигналов в программе.

Пример:

Добавьте в созданный на предыдущем уроке "sppstudio" новый подпроект lesson_2(Qt Widget).

Очистите его от посторонних файлов, оставив в нём main.cpp.Создайте в нем файлы myclass.h и myclass.cpp. Давайте придумаем функциональность для нашей программы. Пусть она при нажатии на кнопку создаёт окна и если число созданных окон превысит 5, то она закончит выполнение.

Перед вами реализация её класса:

//myclass.h
#ifndef MYCLASS
#define MYCLASS
#include <QLabel>
#include <QPushButton>
#include <QDialog>
#include <QHBoxLayout>
#include <QDebug>

//отображение числа нажатий
class MyClass:public QObject
{
Q_OBJECT
private:
    QPushButton * butt;
    QHBoxLayout * hbox;
    QWidget *window;
    static int counter;//счетчик объектов
public:
    MyClass();
    ~MyClass()
    {
        qDebug()<<"delete object MyClass";
        delete butt;
        delete hbox;
        delete window;
    }

private slots:
    void incCounter();//добавляет единицу к counter при вызове
    void newWindow();//создаёт новое окно
signals:
    s_transmitter();//сигнал передатчик
};
#endif // MYCLASS

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

Обратите внимания на макросы:

Q_Object  — нужно прописывать в любом классе использующего свои слоты и сигналы.

private slots: и signals:  — здесь собственно они прописываются.

Определения методов у нас находятся в файле .cpp

Перейдём к его  детальному рассмотрению:

//myclass.cpp
#include "myclass.h"
#include <QString>
#include <QApplication>

MyClass::MyClass()
{
    butt = new QPushButton(" Создать ещё окно ");

    QString str;
    str = "Экземпляр №"+QString::number(counter);

    hbox = new QHBoxLayout;
    hbox->addWidget(butt);

    connect(butt,SIGNAL(clicked()),this,SLOT(incCounter()));
    connect(this,SIGNAL(s_transmitter()),this,SLOT(newWindow()));
    /*
    *Это старая версия соединения сигнала и слота
    *А ниже распологается новая:
    *connect(butt,&QPushButton::clicked,this,&MyClass::incCounter);
    *Вы можете выбрать эту версию
    *Для этой маленькой программы не будет большой разницы какой тип вы используете
    */

    window = new QWidget;
    window->setWindowTitle(str);
    window->setLayout(hbox);
    window->resize(230,45);
    window->move(200,80*counter);
    window->show();
}
void MyClass::incCounter()
{
    ++counter;
    if(counter>5)
    {
        QCoreApplication::quit();
        return;
    }
    emit s_transmitter();
}

void MyClass::newWindow()
{
    MyClass * m = new MyClass();
    m->setParent(this);
}
int MyClass::counter = 1;

Со строки 6 начинается описание конструктора MyClass. В нём создаётся окно с кнопкой и соединяются слоты с  сигналами. В 16-17 происходит соединение старого типа. За настройки вида приложения отвечают строки 27-31.

27: Установка названия окна

29: Размеры окна по x и y

30: Перемещает каждое новое окно вниз чтобы не нагромождать их на одном месте.

Последняя строка отвечает за появление окна на дисплее.

Функция incCounter отвечает за инкриминирование счётчика, вызов сигнала(строка 41) и выход из программы(36-40).

Функция newWindow создаёт новое окно и устанавливает ему родителя.Им становится объект у которого была нажата кнопка.

 Установка родителя — это полезная возможность не следить за удалением объектов в динамической памяти, так как класс QObject берёт на себя все обязательства по удалению объектов-потомков при уничтожении родителя.
//main.cpp
#include <QApplication>
#include "myclass.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyClass myclass;
    a.exec();
    return 0;
}

И наконец main.cpp.

В нём прописывается автоматическая переменная-объект, которая удаляется при завершении программы.

Резюме:

Собственно в следующих статьях будет произведён упор не на описании базовых функций, а об использовании полезных особенностей библиотеки на практике. Здесь вы можете найти русский перевод документации по Qt 4.3 – 4.8. Описываются классы библиотеки, основные свойства и инструменты разработки.

Автор: tenebris
Дата: 25.08.2015
Поделиться:

Оставить комментарий

Вы должны войти, чтобы оставить комментарий.