Иерархия объектов Qt часть 1 - Механизм сигналов и слотов, приведение типов и свойства объектов.

    Вступление.


    В Qt все построено на объектах, главным базовым классом является QObject. Все классы, имеющие сигналы и слоты в Qt унаследованы от него. При множественном наследовании, если в иерархии планируется наличие класса QObject, его имя должно стоять первым в списке базовых классов, иначе метаобъектный компилятор Qt moc (Meta object compiler) будет ругаться. Например:

class Foo : public QObject, public Bar {
    //…
};

    Так же нужно проследить за тем, что бы только один класс в иерархии наследовал QObject.
    QObject содержит в себе поддержку сигналов и слотов, таймера, механизма фильтрации событий, мета-информации, приведений типов и объединения объектов в иерархии.
    Поддержка таймера позволяет каждому из производных от QObject  классов создавать свои таймеры.  Объединение в иерархии позволяет упростить управление памятью, так как родительские объекты автоматически удаляют дочерние, механизм слотов и сигналов позволяет наладить взаимодействие объектов с внешним миром.
    Если нужно что бы класс мог посылать сигналы и иметь слоты, в его объявлении должен находится макрос Q_OBJECT, после макроса не нужно ставить точку с запятой. Его наличие сообщает moc о том, что в тело класса нужно включить мета-информацию необходимую для функциональности сигналов и слотов.

    Например:

class Foo : public QObject {
    Q_OBJECT
public:
    //…
    //…
};

    Препроцессор moc анализирует объявления классов содержащих макрос Q_OBJECT и добавляет в них нужную мета-информацию. Механизм сигналов и слотов объектно ориентирован и является главным при программировании в Qt.

    Сигналы.


    Начнем с них. Сигнал может иметь произвольное количество аргументов. Определять сигнал нельзя, это делает moc. Описать сигнал можно следующим образом:


class Foo : public QObject {
    Q_OBJECT
    //…
    //…
signals:
    void error(const QString& message);
};

    В примере выше мы объявили сигнал error имеющий аргумент message. Послать сигнал можно оператором emit. Вообще говоря, называть emit оператором неверно, это ключевое слово сообщает moc о том, что нужно послать сигнал error. Мы можем послать сигнал и передать ему аргумент так:

//….
emit error(“Ошибка”);
//….

    Напоминаю, что сигналы реализовывать не надо, это делает moc, он порождает код на стандартном С++, который и заставляет это работать.

    Слоты.


    Это обработчики сигналов. Объявить слот можно так:

class Bar : public QObject {
    Q_OBJECT
    //….
    public slots:
        void handleError(const QString& message)
        {
            qDebug()<<message;
        }
    //….
};

    Примечание для пользователей Windows: Для того, что бы видеть в консоли вывод функции qDebug() вам нужно добавить в файл проекта следующую строку: CONFIG += console.

    В коде выше мы реализовали слот handleError с аргументом message. В отличие от сигналов слоты нужно реализовывать самому. Слоты могут быть public, protected и private. Они так же могут быть вызваны как обычные функции, вообще говоря, с точки зрения программирования слоты это обычные методы класса. Для того, что бы наладить взаимодействие между сигналами и слотами их нужно соединить. Для этого в классе QObject существует функция connect.

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

    Где sender это объект который посылает сигнал, signal это сигнал, receiver это объект обработчик, slot это слот, type это тип соединения.

    Тип соединения может принимать три значения:
    Qt::DirectConnection – сигнал обрабатывается сразу соответствующим слотом.
    Qt::QueuedConnection – сигнал преобразуется в событие и ставится в очередь.
    Qt::AutoConnection – если sender и receiver в одном потоке то Qt::QueuedConnection, если нет, то Qt::DirectConnection.
    Вообще, вряд ли вам придется изменять значение этого параметра, но знать, что это возможно будет полезно.

    И так соединение может выглядеть так:

//…
connect(mySender,SIGNAL(error(const QString&)),myReceiver,SLOT(handleError(const QString&)));
//…

    Как видно в коде, сигнал должен обрамляться макросом SIGNAL, а слот макросом SLOT. В списке параметров нужно указывать только типы аргументов, имена должны отсутствовать, иначе соединение может не состоятся, о чем Qt выведет предупреждение на консоль. Если на месте myReceiver должно стоять слово this, его можно опустить и передать функции connect три аргумента. Так же существует возможность отменить соединение сигнала и слота путем вызова QObject::disconnect.

bool QObject::disconnect ( const QObject * sender, const char * signal, const QObject *receiver, const char * method )

    Она полностью аналогична функции connect, за исключением того, что вместо соединения эта функция выполняет разъединение. И так пора уже написать пример.

    У нас будет класс со слотом showMessage() который будет выводить сообщение по нажатию на кнопку.

    Файл Foo.h

#ifndef FOO_H_
#define FOO_H_

#include <QObject>

//Для поддержки сигналов и слотов наследуем QObject
class Foo: public QObject {
	//Наличе макроса обязательно
	Q_OBJECT
public:
	//Конструктор
	Foo(QObject* parent = 0);

public slots:
	//Наш слот
	void showMessage();
};

#endif /* FOO_H_ */

    Файл Foo.cpp

#include "Foo.h"
#include <QMessageBox>

Foo::Foo(QObject* parent)
:QObject(parent)
{

}

void Foo::showMessage()
{

//О том, как работает класс QMessageBox см. QtAssistaint

	QMessageBox::information(0,"Message","You press button",QMessageBox::Yes);
}

#include <QtGui>
#include <QApplication>
#include <QPushButton>
#include "Foo.h"

    Файл main.cpp

int main(int argc, char *argv[]) {
    //Инициализируем каркас приложения,это нужно сделать
    //ДО манипуляций с виджетами
    QApplication a(argc, argv);
    //Создаем кнопку
    QPushButton button("Press me!");
    //Наш класс
    Foo foo;
    //Соединяем сигнал кнопки clicked(),который посылатся кнопкой
    //когда на нее нажимают
    QObject::connect(&button,SIGNAL(clicked()),&foo,SLOT(showMessage()));
    //Отображаем кнопку
    button.show();
    return a.exec();
}

    Разобраться в том, что тут происходит должны помочь комментарии. При запуске приложения вы должны увидеть окно с кнопкой, нажав на нее вы увидите сообщение.

    Свойства.


    Так же QObject обеспечивает поддержку свойств. Свойства это поля объекта которые можно читать и писать. Этот механизм используется QtDesigner и QtScript. Свойство можно определить при помощи макроса Q_PROPERTY();

    Например:

// Как обычно в квадратных скобках указаны необязательные поля.

Q_PROPERTY (
    // Тип и имя свойства
    Type name

    // Функция чтения
    READ getFunc

    // Функция записи
    [WRITE setFunc]


    // Функция сброса свойства
    [RESET resetFunc]

    // Указывает отображается ли это свойство в QtDesigner
    [DESIGNABLE bool]

    // Указывает доступно ли это свойство из QtScript
    [SCRIPTABLE bool]

    // Указывает запоминается ли свойство при сохранении объекта
    [STORED bool]
)

    Теперь напишем класс обладающий поддержкой сигналов и слотов, и имеющий так же свойство good – хороший.


class Foo : public QObject {
    Q_OBJECT

    Q_PROPERTY(bool good, READ getName(), WRITE setName())

    // После макросов НЕТ точки с запятой
public:
    Foo (QObject* parent  = 0) : QObject(parent),
                    good(true)
    {

    }

    void setGood(bool flag)
    {
        good = flag;
    }

bool getGood() const
    {
        return good;
    }

private:
bool good;
};

    Выше мы определили класс имеющий свойство good. Как видно из кода функция чтения свойства это getGood и поэтому она объявлена в макросе Q_PROPERTY с квалификатором READ, функция записи setGood и соответственно она объявляется  с квалификатором WRITE.  Теперь мы программно можем изменять свойства объекта например так:


//….
if (!foo->getProperty(“good”).toBool())
    foo->setProperty(“good”,true);

    Заключение.


    Приведения типов и объединение объектов в иерархии мы рассмотрим в следующей статье. Остальные темы слишком объемны и будут рассматриваться отдельно.




Добавлена: 06-02-2009 | Изменена: 06-02-2009 | Пользователем: gnudimarik | Просмотров: 3901


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



Капча *

Captcha

Комментарий будет опубликован после проверки модератором

Для подсветки синтаксиса исходный код следует обрамлять следующими тэгами
<pre><code class="синтаксис" >код</code></pre>
Подерживаются следующие: cpp php javascript sql html-xml css ini