Воскресенье, 03 января 2021 12:57

Мышь - Слоты и сигналы - Виджеты (компоненты) в Qt5.

Россия
Оцените материал
(0 голосов)

Сегодня мы расширим функционал работы с нажатиями клавиши мыши и вращением её колесика, с помощью слотов и сигналов.

Так же мы напишем универсальный метод слота, функционал которого позволит использовать его с любой лампочкой. Для того чтобы отличить одну лампочку от другой мы будем использовать свойства виджетов (property).

Мы будем использовать проект из предыдущей статьи.

Добавляем сигналы 

Добавим сигналы в класс виджета 

signals:
    void clicked();
    void released();
    void pressed();
    void doubleClicked();

Изменим реализацию методов для работы с мышью:

void QLampWidget::mousePressEvent(QMouseEvent *event)
{
    switch (event->button()) {
        case Qt::LeftButton:
        {
            qDebug() << "Left Mouse button pressed";
            break;
        }
        case Qt::RightButton:
        {
            qDebug() << "Right Mouse button pressed";
            break;
        }
        case Qt::MiddleButton:
        {
            qDebug() << "Middle Mouse button pressed";
            break;
        }
        default:
        {
            qDebug() << "Other button pressed, id = "+QString::number(event->button());
            break;
        }
    }

    repaint();
    emit pressed();
}

void QLampWidget::mouseReleaseEvent(QMouseEvent *event)
{
    switch (event->button()) {
        case Qt::LeftButton:
        {
            if (this->status == QLampWidget::on)
            {
                this->status = QLampWidget::off;
            }
            else
            {
                this->status = QLampWidget::on;
            }

            qDebug() << "Left Mouse button released";
            break;
        }
        default:
        {
            qDebug() << "Other button released, id = "+QString::number(event->button());
            break;
        }
    }

    repaint();
    emit released();
    emit clicked();

}

void QLampWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
    qDebug() << "Double clicked";
    emit doubleClicked();
}

Здесь мы просто добавили отправку сигнала каждый раз, когда происходит нужное событие.

Обратите внимание, в методе mouseReleaseEvent() мы отправляем сразу два сигнала:

    emit released();
    emit clicked();

Так как они, по сути, происходят одновременно. 

Переключаем состояние лампочки 

Давайте напишем метод, который переключает состояние нашей лампочки: 

void QLampWidget::toggle()
{
    if (this->status == QLampWidget::on)
    {
        this->status = QLampWidget::off;
    }
    else
    {
        this->status = QLampWidget::on;
    }
    this->update();
}

Проверим его работу – добавим в конструктор главной формы строку:

lamp->toggle();

Теперь при запуске обе лампочки будут выключены.

Вызываем событие при изменении статуса лампочки 

Каждый раз, когда состояние лампочки изменяется, нам нужно уведомить об этом другие виджеты, для этого существуют сигналы.

Добавим сигнал: 

signals:
    void toggled(int status);

Внесем изменения в метод: 

void QLampWidget::toggle()
{
    if (this->status == QLampWidget::on)
    {
        this->status = QLampWidget::off;
    }
    else
    {
        this->status = QLampWidget::on;
    }
    this->update();
    emit this->toggled(this->getStatus());
}

После переключения статуса мы отправляем сигнал, а в качестве параметра передаём текущий статус виджета.

Теперь осталось добавить слот в класс главной формы и подключить его к сигналу:

private slots:
    void lampToggled(int status);

 Реализация

void MainWindow::lampToggled(int status)
{
    qDebug() << "Lamp toggled: " + QString::number(status);
}

Добавим в конструктор формы:

    QObject::connect(lamp, SIGNAL(toggled(int)),this,SLOT(lampToggled(int)));
    lamp->toggle();

Запустим:

При запуске первая кнопка отключается, а в консоли мы видим:

"Lamp toggled: 0" 

Это означает, что сигнал был обработан. 

Обработка щелчка по виджету через слот 

Давайте реализуем переключение лампочки по щелчку, но через слот. 

Внесем изменения в метод: 

void QLampWidget::mouseReleaseEvent(QMouseEvent *event)
{
    switch (event->button()) {
        case Qt::LeftButton:
        {
            qDebug() << "Left Mouse button released";
            emit clicked();
            break;
        }
        default:
        {
            qDebug() << "Other button released, id = "+QString::number(event->button());
            break;
        }
    }

    repaint();
    emit released();
}

Теперь только при щелчке левой кнопкой мыши будет отправляться сигнал clicked()

 Добавим в главной форме слот:

private slots:
    void clicked();

Реализация: 

void MainWindow::clicked()
{
    lamp->toggle();
}

Соединим с сигналом:

QObject::connect(lamp, SIGNAL(clicked()),this,SLOT(clicked()));

 Запустим, теперь при щелчке по лампе, в консоли мы увидим: 

"Lamp toggled: 0"
Left Mouse button pressed
Left Mouse button released
"Lamp toggled: 1"
Left Mouse button pressed
Left Mouse button released
"Lamp toggled: 0"

И лампочка будем включаться/выключаться.

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

void MainWindow::clicked()
{
    QLampWidget *lamp = qobject_cast<QLampWidget*>(sender());
    if( lamp != NULL )
    {
        lamp->toggle();
    }
    else
    {
        qFatal("Lamp pointer is NULL!");
    }
}

Здесь мы используем механизм приведения типов. С помощью метода sender() получаем ссылку на виджет – источник события и приводим её к типу нашего виджета. Обязательно проверяем удачно ли прошло приведение типа, если вы попробуете использовать этот слот с другим виджетом – программа завершиться с ошибкой: 

Lamp pointer is NULL!
хх:хх:хх: Программа неожиданно завершилась.
хх:хх:хх: Процесс был завершён принудительно.

Запустим – левая лампочка переключается, а правая нет, давайте это исправим.

Добавим для lamp2 следующий код: 

   QObject::connect(lamp2, SIGNAL(clicked()),this,SLOT(clicked()));

Запустим – теперь обе лампочки переключаются.

Как отличить одну лампочку от другой

Мы создали универсальный метод слота, который нам позволяет переключать разные лампочки. Но, если у нас 10 лампочек, для каждой придется создавать отдельный слот, это очень неудобно и не практично.

Есть более простой способ – для любого виджета можно назначить любое количество свойств, которые уникальны для данного экземпляра виджета.

Добавление свойства производится с помощью метода setProperty()

Например:

lamp->setProperty("id",1);

Создаст свойство id со значением 1, при этом lamp2 не будет иметь этого свойства!

Добавим для виджета конструктор, который будет добавлять id для наших ламп.

QLampWidget::QLampWidget(QString color, QString offcolor, int status, int id)
{
    this->setFixedWidth(32);
    this->setFixedHeight(32);
    this->color = color;
    this->offColor = offcolor;
    this->setStatus(status);
    this->setProperty("id",id);
}

Изменим код в конструкторе главной формы 

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QVBoxLayout *vlay = new QVBoxLayout();

    QHBoxLayout *hlay1 = new QHBoxLayout();

    lamp = new QLampWidget("#557d00","#F00",QLampWidget::on, 1);
    hlay1->addWidget(lamp);

    QObject::connect(lamp, SIGNAL(toggled(int)),this,SLOT(lampToggled(int)));
    QObject::connect(lamp, SIGNAL(clicked()),this,SLOT(clicked()));

    lamp->toggle();

    lamp2 = new QLampWidget("#557d00","#F00",QLampWidget::off, 2);
    lamp2->toggle();
    QObject::connect(lamp2, SIGNAL(toggled(int)),this,SLOT(lampToggled(int)));
    QObject::connect(lamp2, SIGNAL(clicked()),this,SLOT(clicked()));
    hlay1->addWidget(lamp2);


    vlay->addLayout(hlay1);

    hlay1->addStretch(1);

    vlay->addStretch(1);

    ui->centralwidget->setLayout(vlay);
}

Изменим метод: 

void MainWindow::lampToggled(int status)
{
    QLampWidget *lamp = qobject_cast<QLampWidget*>(sender());
    if ( lamp != NULL )
    {
        qDebug() << "Lamp toggled: " + QString::number(status) + " id: " + lamp->property("id").toString();
    }
    else
    {
        qFatal("Lamp pointer is NULL!");
    }
}

Запустим, в консоли мы теперь можем видеть id переключенной лампочки: 

"Lamp toggled: 0 id: 1"
Left Mouse button pressed
Left Mouse button released
"Lamp toggled: 1 id: 1"
Left Mouse button pressed
Left Mouse button released
"Lamp toggled: 0 id: 1"
Left Mouse button pressed
Left Mouse button released
"Lamp toggled: 0 id: 2"
Left Mouse button pressed
Left Mouse button released
"Lamp toggled: 1 id: 2"
Left Mouse button pressed
Left Mouse button released
"Lamp toggled: 1 id: 1"

Определяем вращение колесика мыши

Добавим в класс виджета два новых сигнала: 

signals:
    void wheelUp(int count);
    void wheelDown(int count);

Изменим метод wheelEvent()  

void QLampWidget::wheelEvent(QWheelEvent *event)
{
    QPoint numDegrees = event->angleDelta() / 8;

    if (numDegrees.y() > 0)
    {
        emit wheelUp(numDegrees.y());
    }
    else
    {
        emit wheelDown(numDegrees.y());
    }
}

Добавим слоты в класс главной формы

private slots:
    void wheelUp(int count);
    void wheelDown(int count);

Реализация:

void MainWindow::wheelUp(int count)
{
    qDebug() << "Wheel up - count = " +QString::number(count);
}

void MainWindow::wheelDown(int count)
{
    qDebug() << "Wheel down - count = " +QString::number(count);
}

Свяжем сигналы со слотами:

    QObject::connect(lamp, SIGNAL(wheelUp(int)),this,SLOT(wheelUp(int)));
    QObject::connect(lamp, SIGNAL(wheelDown(int)),this,SLOT(wheelDown(int)));

    QObject::connect(lamp2, SIGNAL(wheelUp(int)),this,SLOT(wheelUp(int)));
    QObject::connect(lamp2, SIGNAL(wheelDown(int)),this,SLOT(wheelDown(int)));

Запустим.

Теперь при вращении колесика мыши в консоли мы увидим: 

"Wheel up - count = 15"
"Wheel up - count = 15"
"Wheel down - count = -15"
"Wheel down - count = -15"

Доработаем методы слотов, таким образом, чтобы у нас изменялся и цвет лампочки:

void MainWindow::wheelUp(int count)
{
    QLampWidget *lamp = qobject_cast<QLampWidget*>(sender());
    if( lamp != NULL )
    {
        lamp->setActiveColor(count);
    }
    else
    {
        qFatal("Lamp pointer is NULL!");
    }

    qDebug() << "Wheel up - count = " +QString::number(count) + " id: " + lamp->property("id").toString();;

}

void MainWindow::wheelDown(int count)
{
    QLampWidget *lamp = qobject_cast<QLampWidget*>(sender());
    if( lamp != NULL )
    {
        lamp->setActiveColor(count);
    }
    else
    {
        qFatal("Lamp pointer is NULL!");
    }

    qDebug() << "Wheel down - count = " +QString::number(count) + " id: " + lamp->property("id").toString();;
}

 Теперь колесико мыши контролирует цвет зажжённой лампочки.

Заключение 

Сегодня мы рассмотрели создание слотов и сигналов для нашего виджета.

Были добавлены сигналы, отправляемые при нажатии и отпускании кнопки мыши, её одиночного и двойного клика.

Мы добавили метод для переключения состояния лампочки и сигнал об изменении статуса лампочки.

Для всех сигналов были добавлены и связаны слоты.

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

Так же мы реализовали механизм, позволяющий с помощью свойств (property) задавать для лампочек id.

Созданы сигналы и универсальные слоты для вращения колесика мыши – вверх и вниз.

Исходный код проекта вы можете найти в Github.

Прочитано 898 раз Последнее изменение Среда, 13 января 2021 12:11