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 [8458 просмотров]