БлогNot. QT: делаем метку, реагирующую на клик мышью и динамический список меток

QT: делаем метку, реагирующую на клик мышью и динамический список меток

Механизм сигналов и слотов позволяет не только пользоваться готовыми возможностями, но и легко добавлять обработку событий, отсутствующих у оригинальных компонент QT. Рассмотрим эту ситуацию на примере. Оригинальная текстовая метка QLabel не испускает сигнала clicked(), соответственно, не имеет возможности реагировать на нажатия кнопки мыши. Можно использовать другую компоненту, например, QPushButton, но тогда придётся повозиться с её геометрией, чтобы сделать кнопку похожей на текстовую метку. Гораздо проще и логичней написать класс-наследник QLabel, который реагирует на нажатия кнопки мыши.

Добавим в пустой виджет класс ClickableLabel, сделаем его наследником QLabel и предусмотрим в нём обработку сигнала clicked() и события мыши mousePressEvent(). Также предусмотрим ситуацию, когда меток много и надо различать их между собой по какому-то дополнительному свойству, как сделано в C++ Builder, где все компоненты имеют "пользовательское" свойство tag. Вот листинг файла clickablelabel.h:

#ifndef CLICKABLELABEL_H
#define CLICKABLELABEL_H

#include <QLabel>
#include <QMouseEvent>

class ClickableLabel : public QLabel {
 Q_OBJECT
public:
 explicit ClickableLabel (const QString& text="", QWidget* parent=0);
 ~ClickableLabel();
signals:
 void clicked(QMouseEvent *); //объект класса будет испускать этот сигнал
protected:
 void mousePressEvent (QMouseEvent *event); //при нажатии кнопки мыши
private:
 int t; //"номер" кнопки , будем их различать по номерам,
        //если их много
public:
 void setTag(int); //метод для установки номера кнопки
 int tag(void);    //и для получения
};

#endif // CLICKABLELABEL_H

В файле clickablelabel.cpp примечательна только та строчка, где при нажатии кнопки мыши искусственно посылается наш сигнал clicked(). Кто будет его принимать - это уже забота программиста, использующего наш класс. Мы будем делать это в основном виджете. Существенно, что сигнал передаёт всю информацию о клике с помощью объекта QMouseEvent *:

#include "clickablelabel.h"

ClickableLabel::ClickableLabel (const QString& text, QWidget* parent) : QLabel(parent) {
 setText (text);
 this->setTag(0);
}

ClickableLabel::~ClickableLabel() {}

void ClickableLabel::mousePressEvent (QMouseEvent *event) {
 emit clicked(event); //испускаем сигнал при нажатии кнопки мыши!
}

void ClickableLabel::setTag(int tag) {
 this->t = tag;
}
int ClickableLabel::tag(void) {
 return this->t;
}

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

QList <ClickableLabel *> labels;

Для Visual Studio нечто подобное делалось в этой заметке.

Также добавим в виджет свойство "длина списка" (в реальности необязательно, так как можно узнавать количество элементов из QList) и слот, который будет принимать сигналы от наших ClickableLabel:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtWidgets>
#include <QVBoxLayout>
#include "clickablelabel.h"

class Widget : public QWidget {
Q_OBJECT
public:
 explicit Widget(QWidget *parent = 0);
 ~Widget();
private:
 int length; //длина списка меток
 QList <ClickableLabel *> labels; //динамический список меток
private slots:
 void labelClicked(QMouseEvent*); //слот для приёма сигналов от метки
};
#endif // WIDGET_H

Это был файл widget.h, а в widget.cpp всю работу по созданию и размещению наших "кликабельных меток" выполнит конструктор виджета, на долю общего для всех меток обработчика сигнала labelClicked() останутся лишь определение того, на какой именно метке была нажата мышь и демонстрация, что сигнал обработан (увеличим на единицу число, написанное на метке).

В данном случае слоту не понадобилась дополнительная информация о событии, переданная через параметр event (например, координаты клика мыши), но в других проектах это может быть не так.

#include "widget.h"

Widget::Widget(QWidget *parent) : QWidget(parent) {
 this->length = 10;
 this->setGeometry(QRect(0,0,200,this->length*20));
 //добавить метки в список:
 for (int i = 0; i < this->length; ++i) {
  labels << new ClickableLabel (QString::number(i+1),this);
 }
 QVBoxLayout *myLayout = new QVBoxLayout(this);
 //настроить свойства меток:
 for (int i = 0; i < this->length; i++) {
  labels.at(i)->setGeometry(QRect(0, i*20, this->size().width(), 200));
  labels.at(i)->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
  labels.at(i)->setTag(i);
  //...обработку сигнала:
  connect(labels.at(i), SIGNAL(clicked(QMouseEvent*)),this, SLOT(labelClicked(QMouseEvent*)));
  //...и разместить в контейнере:
  myLayout->addWidget(labels.at(i));
 }
}

Widget::~Widget() { }

void Widget::labelClicked (QMouseEvent *event) {
 //узнать, от какой из меток пришёл сигнал:
 int i=((ClickableLabel *)sender())->tag();
 //и увеличить число на ней на единицу:
 labels.at(i)->setText(
  QString::number(1+labels.at(i)->text().toInt())
 );
}

Главный файл проекта main.cpp будет иметь обычный вид, за исключением того, что принудительно передвигает окно виджета в центр экрана десктопа:

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[]) {
 QApplication a(argc, argv);
 Widget w;
 w.move( QApplication::desktop()->screen()->rect().center() - w.rect().center() );
 w.show();
 return a.exec();
}

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

скриншот
скриншот

 Скачать папку проекта QT5 ClickableLabel в архиве .zip (3 Кб)

10.03.2017, 14:15 [8051 просмотр]


теги: c++ программирование учебное список qt

К этой статье пока нет комментариев, Ваш будет первым