QObject является базовым классом для почти всех классов Qt. Исключением являются только классы, которые должны быть достаточно "лёгкими" (экземпляры которых должны занимать как можно меньше памяти) и классы, объекты которых должны копироваться (QObject не поддерживает копирования), а также контейнерные классы. Все виджеты Qt наследуют QObject (класс QWidget является потомком QObject). QObject реализует все базовые особенности, которыми обладают классы Qt:
Каждый объект типа QObject обладает метаданными, которые хранятся внутри специального метаобъекта. Этот метаобъект создаётся для каждого потомка QObject и сохраняет различную информацию об объекте (так называемые метаданные). Среди доступных для программиста метаданных есть:
Метаданные собираются во время компиляции (предварительной обработки проекта с помощью qmake) метаобъектным компилятором moc, который анализирует содержание заголовочных файлов программы. Эти метаданные позволяют получить информацию о любом потомке QObject. Эта информация может быть полезна как для отладки программы, так и для создания различных механизмов взаимодействия между объектами в программе.
При разработке с использованием Qt часто возникает необходимость наследовать класс QObject непосредственно или его потомка. Объекты, которые наследуют QObject:
Рассмотрим создание собственного класса на примере создания виждета, который можно будет многократно использовать в разных программах. Разработаем поле ввода с пиктограммой, которая может реагировать на действия пользователя. Такая пиктограмма:
Создадим новый проект и добавим к нему новый класс, который будет наследовать от QLineEdit (создание новых классов с использованием мастера описано в разделе 13.1). Назовём новый класс IconizedLineEdit. После создания нового класса откроем файл описания (IconizedLineEdit.h).
Для того, чтобы корректно наследовать класс от QObject необходимо выполнить несколько условий:
Созданный класс выполняет эти условия (наследует от QLineEdit, который наследует от QWidget, а тот в свою очередь от QObject и содержит макрос Q_OBJECT в частной секции класса).
#ifndef QRCLEARABLELINEEDIT_H #define QRCLEARABLELINEEDIT_H #include <QLineEdit> class QLabel; class IconizedLineEdit : public QLineEdit { Q_OBJECT public : //Режимы отображения пиктограммы, которые определяют её поведение enum IconVisibilItyMode { //Всегда отображать пиктограмму IconAlwaysVisible =0, //Отображать пиктограмму после наведения курсора на поле ввода IconVisibleOnHover, //Отображать пиктограмму при присутствии текста IconVisibleOnTextPresence, //Отображать пиктограмму при отсутствии текста IconVisibleOnEmptyText, //Всегда прятать пиктограмму IconA lwaysH idden }; explicit IconizedLineEdit ( QWidget * parent = 0 ); bool isIconVisible ( ) const; void setIconPixmap ( const QPixmap &pPixmap ); void setIconTooltip ( const QString &pToolTip ); private : void updateIconPositionAndsize ( ); private : QLabel *mIconLabel; //Указатель на метку, которая отображает пиктограмму }; #end if // QRCLEARABLELINEEDIT_H
В конструкторе класса создадим метку mIconLabel с помощью которой мы будем отображать значок. Также добавим реализацию для нескольких вспомогательных методов.
#include " IconizedLineEdit .h " #include <QSty le> #include <QLabel> //Конструктор класса IconizedLineEdit::IconizedLineEdit ( QWidget * parent ) : QLineEdit ( parent ) { mIconLabel = new QLabel ( this ); //Создаём метку для того, чтобы показать пиктограмму } //Возвращает true, если пиктограмма видима bool IconizedLineEdit::isIconVisible ( ) const { return mIconLabel-> isVisible ( ); } //Устанавливает пиктограмму void IconizedLineEdit::setIconPixmap ( const QPixmap &pPixmap ) { //Устанавливаем пиктограмму для метки mIconLabel->setP ixmap ( pPixmap ); //Обновляем позицию и размеры updateIconPositionAndsize ( ); } //Устанавливаем подсказку для пиктограммы void IconizedLineEdit::setIconTooltip ( const QString &pToolTip ) { //Подсказка будет видимой после наведения курсора на метку с пиктограммой mIconLabel->setToolTip ( pToolTip ); } void IconizedLineEdit::updateIconPositionAndsize ( ) { //Обновить размер пиктограммы mIconLabel->setScaledContents ( true ); mIconLabel->setFixedsize ( height ( ), height ( ) ); //Обновить размещение пиктограммы QSizelsize = mIconLabel->size ( ); mIconLabel->move ( rect ( ).right ( ) - lsize.width ( ), ( rect ( ).bottom ( ) + 1 - lsize .height ( ) ) / 2 ); //Изменить отступы текста внутри поля ввода в зависимости от видимости if ( mIconLabel-> isVisible ( ) ) { //Добавить отступ справа чтобы текст не накладывался на пиктограмму QMarginslMargins = textMargins ( ); lMargins.setright ( mIconLabel->size ( ).width ( ) + 1 ); setTextMargins ( lMargins ); } else { //Убрать отступы setTextMargins ( QMargins ( 0, 0, 0, 0 ) ); } }
Чтобы показать значок, мы используем метку, изображения для которой можно передать с помощью метода setPixmap(). Этот метод принимает экземпляр QPixmap класса для работы с растровыми изображениями.
Метод updateIconPositionAndSize() обновляет размер и размещение для метки. Для того, чтобы растянуть\сжать изображения мы передаём true методу метки setScaledContents(). Это позволяет игнорировать размеры изображения и изменить размер для значков. Далее мы устанавливаем фиксированный размер для метки, таким образом, чтобы её пропорции подходили для размещения в поле. Затем размещаем метку в правом конце текстового поля.
Для того, чтобы установить тот или иной режим отображения значка запрограммируем метод setIconVisibility(). Добавим объявление метода в файл описания, а также добавим описание перечисления IconVisibilityMode, содержащее режимы отображения пиктограммы.
public : //Режимы отображения пиктограммы, которые определяют её поведение enum IconVisibilItyMode { //Всегда отображать пиктограмму IconAlwaysVisible =0, //Отображать пиктограмму после наведения на поле ввода IconVisibleOnHover, //Отображать пиктограмму при присутствии текста IconVisibleOnTextPresence, //Отображать пиктограмму при отсутствии текста IconVisibleOnEmptyText, //Всегда прятать пиктограмму IconA lwaysH idden }; void setIconVisibilIt y ( IconVisibilItyMode pIconVisibilItyMode ); ... . private slots : void slotTextChanged ( const QString &pText ); private : void updateIconPositionAndsize ( ); void setIconVisible ( bool pisVisible ); private : IconVisibilItyMode mIconVisibilItyMode; //Режим отображения
Добавим к файлу iconizedlineedit.cpp реализацию для этих методов.
//Устанавливает режим отображения для пиктограммы void IconizedLineEdit::setIconVisibilIty ( IconVisibilItyMode pIconVisibilItyMode ) { //Сохранение режима mIconVisibilItyMode = pIconVisibilItyMode; //Выполняем изменения соответсвенно к установленому значению switch ( pIconVisibilItyMode ) { case IconAlwaysVisible : setIconVisible ( true ); break; case IconVisibleOnEmptyText : case IconVisibleOnTextPresence : slotTextChanged ( Text( ) ); break; default : setIconVisible ( false ); break; } } //Реализует реакцию на изменение текста в поле для режимов IconVisibleOnEmptyText //и IconVisibleOnNotEmptyText void IconizedLineEdit::slotTextChanged ( const QString &pText ) { if ( IconVisibleOnEmptyText == mIconVisibilItyMode ) { setIconVisible ( pText.isEmpty ( ) ); } else if ( IconVisibleOnTextPresence == mIconVisibilItyMode ) { setIconVisible ( ! pText.isEmpty ( ) ); } } //Сделать пиктограмму видимой или спрятать void IconizedLineEdit::setIconVisible ( bool pisVisible ) { //Показать/скрыть метку с пиктограммой mIconLabel->setVisible ( pisVisible ); }
Для того, чтобы слот slotTextChanged(QString) работал, добавим в конструктор сигнально-слотовое соединение. Также добавим начальную инициализацию для поля mIconVisibilityMode.
//Конструктор класса IconizedLineEdit::IconizedLineEdit ( QWidget * parent ) : QLineEdit ( parent ), mIconVisibilItyMode ( IconAlwaysVisible ) //Инициализация { mIconLabel = new QLabel ( this ); //Создаём метку для того, чтобы показать пиктограмму //Обработка изменения текста в поле connect ( this, SIGNAL( textChanged ( QString ) ), this,SLOT( slotTextChanged ( QString ) ), Qt::UniqueConnection ); }
Чтобы использовать наш виджет в программе, добавим файл описания класса и создадим несколько экземпляров, которые разместим на форме. Ответственным за создание интерфейса окна будет отдельный метод createUi().
В файле описания класса главного окна напишем:
#include <QWidget> class IconizedLineEdit; class MainWindow : public QWidget { Q_OBJECT public : explicit MainWindow ( QWidget * parent = 0 ); private : void createUi ( ); private : IconizedLineEdit * IconizedLineEdit; IconizedLineEdit * IconizedLineEdit_ 2; IconizedLineEdit * IconizedLineEdit_ 3; IconizedLineEdit * IconizedLineEdit_ 4; IconizedLineEdit * IconizedLineEdit_ 5; };
В файле реализации главного окна разместим код:
#include " mainwindow .h " #include " IconizedLineEdit .h " #include <QVBoxLayout> MainWindow::MainWindow ( QWidget * parent ) : QWidget ( parent ) { createUi ( ); } void MainWindow::createUi ( ) { QVBoxLayout * lMa inLayout = new QVBoxLayout; set Layout ( lMa inLayout ); IconizedLineEdit = new IconizedLineEdit; IconizedLineEdit ->setPlaceholderText( " Click to open File " ); IconizedLineEdit ->setIconPixmap (QPixmap ( " Folder.png " ) ); IconizedLineEdit ->setIconVisibilIt y ( IconizedLineEdit::IconAlwaysVisible ); lMa inLayout->addWidget ( IconizedLineEdit ); IconizedLineEdit_2 = new IconizedLineEdit; iconizedLineEdit_2 ->setPlaceholderText( " Enter IP address " ); iconizedLineEdit_2 ->setIconPixmap (QPixmap ( " Checkmark.png " ) ); iconizedLineEdit_2 ->setIconVisibilIt y ( IconizedLineEdit::IconAlwaysVisible ); lMa inLayout->addWidget ( IconizedLineEdit_2 ); IconizedLineEdit_3 = new IconizedLineEdit; iconizedLineEdit_3 ->setPlaceholderText( " " ); iconizedLineEdit_3 ->setIconPixmap (QPixmap ( " Questions.png " ) ); iconizedLineEdit_3 ->setIconVisibilIt y ( IconizedLineEdit : : IconVisibleOnTextPresence ); lMa inLayout->addWidget ( IconizedLineEdit_3 ); IconizedLineEdit_4 = new IconizedLineEdit; iconizedLineEdit_4 ->setPlaceholderText( " Cannot be empty..." ); iconizedLineEdit_4 ->setIconPixmap (QPixmap ( " Warning.png " ) ); iconizedLineEdit_4 ->setIconVisibilIt y ( IconizedLineEdit : : IconVisibleOnEmptyText ); lMa inLayout->addWidget ( IconizedLineEdit_4 ); IconizedLineEdit_5 = new IconizedLineEdit; iconizedLineEdit_5 ->setPlaceholderText( " Clearable " ); iconizedLineEdit_5 ->setIconPixmap (QPixmap ( " X.png " ) ); iconizedLineEdit_5 ->setIconVisibilIt y ( IconizedLineEdit : : IconVisibleOnTextPresence ); lMa inLayout->addWidget ( IconizedLineEdit_5 ); }
Пути к файлам изображений для значков мы передаём в конструктор QPixmap. Изображения должны находиться в текущей папке (папке, в которой расположен файл проекта).
После запуска программы мы увидим главное окно и пять полей с пиктограммами. Нашему виджету ещё не хватает нескольких важных возможностей. Значки не двигаются при изменении размера текстовых полей. Также значок не реагирует на нажатие мышкой. Совершенствование этого примера, а также дальнейшее изучение свойств объектов и виджетов мы продолжим в следующих параграфах, посвящённых обработке событий.