Evgenii Legotckoi
Evgenii Legotckoi15. September 2015 11:44

Qt/C++ - Lektion 024. Signale und Slot in Qt5

Signale und Slots werden für die Kommunikation zwischen Objekten in Qt verwendet. Der Signal- und Slot-Mechanismus ist ein zentrales Feature in Qt und unterscheidet Qt wahrscheinlich in seiner Funktionalität von anderen Frameworks. Signale und Slots werden ermöglicht durch das meta-object system Qt.

Einführung

Bei der GUI-Programmierung möchten wir oft, dass die anderen Widgets benachrichtigt werden, wenn sich eines der Widgets ändert. Im Allgemeinen möchten wir, dass Objekte miteinander interagieren können. Wenn der Benutzer beispielsweise die Schaltfläche Schließen gedrückt hat, möchten wir wahrscheinlich, dass das Fenster Objekt close () aufruft.

Andere Entwicklungstools bieten ähnliche Funktionen mit Rückruf. callback ist ein Zeiger auf eine Funktion, und wenn Sie eine Funktion ausführen möchten, die Sie über ein Ereignis benachrichtigt, übergeben Sie einen Zeiger an eine andere Funktion, dh callback. Die laufende Funktion ruft callback . auf dann ggf. Es gibt zwar Frameworks, die Rückruffunktionen erfolgreich verwenden, Rückruf ist jedoch eine nicht intuitive Methode, die Probleme verursachen kann, um sicherzustellen, dass die zurückgegebenen Argumente korrekt sind.


Signale und Slots

Qt verwendet eine alternative Technik, das heißt, es verwendet Slots und Signale. Das Signal wird ausgeführt, wenn ein bestimmtes Ereignis eintritt. Qt-Widgets haben viele vordefinierte Signale, aber wir können immer vom Widget erben und unsere eigenen Signale dafür definieren. Ein Slot ist eine Funktion, die als Reaktion auf ein bestimmtes Signal aufgerufen wird. Qt-Widgets haben auch viele vordefinierte Slots, aber es ist gängige Praxis, von Widgets zu erben und eigene Slots hinzuzufügen, damit Sie alle Signale verarbeiten können, an denen Sie interessiert sind.

Signale und Slots in Qt Signale und Slots sind typsichere Mechanismen. Die Signatur des Signals muss mit der Signatur des empfangenden Slots übereinstimmen (obwohl der Slot tatsächlich eine Signatur haben kann, die kürzer als das Signal ist, akzeptiert der Slot das Signal, da er die zusätzlichen Argumente ignoriert). Da die Signaturen kompatibel sind, kann der Compiler helfen, Inkonsistenzen bei der Verwendung einer zeigerbasierten Syntax zu erkennen. Mit der auf den Makros SIGNAL und SLOT basierenden Syntax ist es hingegen nur im Laufzeitprozess möglich, den Typ-Mismatch zu ermitteln. Signale und Slots sind lose gekoppelt: Die Klasse, die das Signal aufruft, kennt nur den Slot, der das Signal empfängt. Der Signal- und Slot-Mechanismus in Qt wird bereitgestellt, wenn Sie ein Signal an einen Slot anschließen, der zum richtigen Zeitpunkt mit den Signalparametern aufgerufen wird. Signale und Slots können mehrere Argumente und Typen haben. Und sie sind absolut typsicher.

Alle Klassen, die von QObject oder seinen Unterklassen (wie QWidget) kann Signale und Slots enthalten. Signale werden von Objekten ausgelöst, die ihren Zustand ändern, was für andere Objekte von Interesse sein kann. Dies ist alles, was das Objekt für die Kommunikation tut. Und dem Objekt ist es egal, wer die Signale empfängt, die es aussendet. Dies ist eine faire Kapselung von Informationen und stellt sicher, dass das Objekt als Softwarekomponente verwendet werden kann.

Slots können zum Empfangen von Signalen verwendet werden, sind aber auch gängige Funktionen. So wie das Objekt nicht weiß, was sein Signal empfangen hat, weiß der Slot nicht, welches Signal an ihm angeschlossen ist. Dies bietet echte Unabhängigkeit für die Komponenten, die mit Qt gebaut werden können.

Sie können mehrere Signale an einen Steckplatz anschließen, oder ein Signal kann an mehrere Steckplätze angeschlossen werden. Und es ist sogar möglich, ein Signal direkt mit einem anderen Signal zu verbinden. (Dies löst einen zweiten Piepton aus, wenn der erste aufgerufen wurde)

Signale und Slots bilden zusammen einen leistungsstarken Mechanismus zur Komponentenprogrammierung.

Signale

Signale werden von einem Objekt ausgesendet, wenn sich sein interner Zustand in eine bestimmte Richtung geändert hat, die für andere Objekte von Interesse sein kann. Signale sind öffentlich zugängliche Funktionen und können überall aufgerufen werden, es wird jedoch empfohlen, sie nur in der Klasse aufzurufen, in der sie definiert wurden und auch in deren Unterklassen.

Beim Auslösen eines Signals wird der damit verbundene Slot in der Regel sofort ausgeführt, wie eine normale Funktion. Dies liegt daran, dass der Signal- und Slot-Mechanismus unabhängig von Schleifen in der GUI ist. Die Ausführung des Codes sollte mit der Direktive emit aufgerufen werden, die alle Slots aufruft. In Situationen, in denen Verbindungswarteschlangen verwendet werden, löst der Code das Signal aus und die Slots werden etwas später ausgeführt.

Wenn mehrere Slots mit dem gleichen Signal verbunden sind, werden die Slots beim Aufruf des Signals nacheinander in der Reihenfolge aufgerufen, in der sie verbunden sind.

Signale werden automatisch in moc generiert und müssen weder in der .cpp-Datei definiert werden, noch geben sie jemals ein Ergebnis zurück.

  • Hinweis: * Unserer Erfahrung nach werden Signale und Slots häufiger verwendet, wenn keine speziellen Typen verwendet werden. Wenn QScrollBar :: valueChanged () ein spezieller Typ wie der hypothetische QScrollBar :: Range ist, kann er nur mit dem Slot verbunden werden speziell für [QScrollBar] entwickelt (http://doc.qt.io/qt-5/qscrollbar.html). Das Verbinden verschiedener Widgets ist möglicherweise nicht möglich.

Schlüssel

Ein Slot wird aufgerufen, wenn ein daran angeschlossenes Signal getriggert wurde. Slots sind eine normale C++-Funktion und können aufgerufen werden; sie sind nur dadurch besonders, dass Signale mit ihnen verbunden sind.

Slots können auch wie normale Funktionen ausgeführt werden, sie gehorchen beim direkten Aufruf den üblichen C++-Regeln. Als Slots können sie jedoch von anderen Komponenten unabhängig von ihrer Zugriffsebene über eine Signal-to-Slot-Verbindung aufgerufen werden. Dies bedeutet, dass ein Signal von einer der Klassen ausgesendet wird und an einen privaten Slot übergeben werden kann, der von dieser nicht verwandten Klasse aufgerufen wird.

Sie können Slots auch als virtuell definieren, was wir in der Praxis sehr nützlich finden.

Im Vergleich zu Callback sind Signale und Slots aufgrund der Flexibilität, die sie bieten, etwas langsamer, obwohl die Unterschiede in einer realen Anwendung geringfügig sind. Grundsätzlich ist das Aufrufen eines Signals, das mit mehreren Slots verbunden ist, etwa zehnmal langsamer als das Aufrufen einer nicht virtuellen Funktion. Dies ist ein Overhead, der das Auffinden des Verbindungsobjekts durch Iterieren über alle Slots und Signale und Vergleichen der Signaturen erfordert, um die Slotfunktion sicher aufzurufen. Während zehn nicht-virtuelle Funktionen mit weniger Overhead aufgerufen werden als mehrere Neu- und Löschoperationen. Wenn Sie im Hintergrund eine String-, Vektor- oder Listenoperation ausführen, die neue und Löschoperationen erfordert, ist der Signal- und Slot-Overhead im Vergleich zu den vollen Kosten eines Funktionsaufrufs sehr gering. Dies ist der Fall, wenn Sie einen Systemaufruf an einen Slot tätigen oder mehr als zehn Funktionen indirekt aufrufen. Die Einfachheit und Flexibilität des Signal- und Slot-Mechanismus ist ein unwichtiger Overhead, den Ihre Benutzer nicht bemerken werden.

Beachten Sie, dass andere Bibliotheken Variablen namens Signale und Slots definieren und beim Kompilieren einer Qt-basierten Anwendung Fehler und Warnungen ausgeben können. Die Lösung dieser Probleme ist die Verwendung der #undef-Direktive für den Präprozessor.

Anschließen eines Signals an einen Steckplatz

Vor der fünften Version von Qt wurde das Verbinden eines Signals mit einem Slot mithilfe von Makros geschrieben, während in Version fünf eine zeigerbasierte Notation verwendet wurde.

Aufnahme mit Makros:

connect(button, SIGNAL(clicked()), this, SLOT(slotButton()));

Zeigerbasierte Notation:

connect(button, &QPushButton::clicked, this, &MainWindow::slotButton);

Der Vorteil der zweiten Option besteht darin, dass die Nichtübereinstimmung von Signaturen und der falsche Name des Slots oder Signals bereits während der Kompilierung des Projekts und nicht während des Testens der Anwendung festgestellt werden können.

Beispiel für die Verwendung von Signalen und Slots

Als Beispiel für die Verwendung von Signalen und Slots wurde ein Projekt erstellt, bei dem das Hauptfenster drei Schaltflächen enthält, an die jeweils ein Steckplatz angeschlossen ist und diese Steckplätze das Signal an einen einzelnen Steckplatz mit der Nummer der gedrückten Schaltfläche übertragen.

Projektstruktur

Projektstruktur Der etablierten Unterrichtstradition entsprechend füge ich die Projektstruktur bei, die absolut trivial ist und bis zur Schande vorschwebt, dass ich die darin enthaltenen Klassen und Dateien nicht einmal beschreibe.

mainwindow.h

Die Aktion ist also wie folgt: drei Buttons - drei Slots, ein Signal für alle drei Buttons, das an die Button-Slots gesendet wird und die Button-Nummer an einen gemeinsamen Slot überträgt, der eine Nachricht mit der Button-Nummer ausgibt.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPushButton>
#include <QMessageBox>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

signals:
    void signalFromButton(int buttonID);    // Сигнал для передачи номер нажатой кнопки

private:
    Ui::MainWindow *ui;

private slots:
    void slotButton1();     // Слоты-обработчики нажатий кнопок
    void slotButton2();
    void slotButton3();

    // Слоты вызывающий сообщение с номеро нажатой кнопки
    void slotMessage(int buttonID);
};

#endif // MAINWINDOW_H

mainwindow.cpp

Und in dieser Datei ist die in den vorherigen Absätzen beschriebene Logik konfiguriert. Überprüfen Sie einfach den Programmcode und fahren Sie mit dem Ansehen des Videos fort. Es zeigt den gesamten Prozess im Detail, demonstriert die Anwendung und zeigt auch, was passiert, wenn Sie Code mit verschiedenen Fehlern schreiben.

#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    /* Объявляем и инициализируем кнопки
     * */
    QPushButton *but_1 = new QPushButton(this);
    QPushButton *but_2 = new QPushButton(this);
    QPushButton *but_3 = new QPushButton(this);

    /* Устанавливаем номера кнопок
     * */
    but_1->setText("1");
    but_2->setText("2");
    but_3->setText("3");

    /* Добавляем кнопки на слой с вертикальной ориентацией
     * */
    ui->verticalLayout->addWidget(but_1);
    ui->verticalLayout->addWidget(but_2);
    ui->verticalLayout->addWidget(but_3);

    /* Подключаем к кнопкам индивидуальные слоты
     * */
    connect(but_1, SIGNAL(clicked()), this, SLOT(slotButton1()));
    connect(but_2, SIGNAL(clicked()), this, SLOT(slotButton2()));
    connect(but_3, SIGNAL(clicked()), this, SLOT(slotButton3()));

    /* Подключаем сигнал с передачей номера кнопки к слоту вывода сообщения
     * */
    connect(this, &MainWindow::signalFromButton, this, &MainWindow::slotMessage);
}

MainWindow::~MainWindow()
{
    delete ui;
}

/* Слоты для обработки нажатия кнопок
 * */
void MainWindow::slotButton1()
{
    emit signalFromButton(1);
}

void MainWindow::slotButton2()
{
    emit signalFromButton(2);
}

void MainWindow::slotButton3()
{
    emit signalFromButton(3);
}

/* Слоты вывода сообщения
 * */
void MainWindow::slotMessage(int buttonID)
{
    QMessageBox::information(this,
                             "Уведомление о нажатой кнопке",
                             "Нажата кнопка под номером " + QString::number(buttonID));
}

Videoanleitung

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

ИВ
  • 29. April 2020 10:04

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

connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::slotButton1);

Я так понял что сиглалы виджетов заранее предопределены в библиотеке и в данном случае &QPushButton::clicked это событие из стандартной библиотеки Qt. В справке сказано, что всего есть четыре сигнала clicked, pressed, released,toggled. Может я не ту справку читаю, от того и странные вопросы.

Правильно всё читаете. Так и есть.

Если хотите отслеживать правую кнопку мыши, то вам следует наследоваться от QPushButton и переопределять методы

virtual void mouseDoubleClickEvent(QMouseEvent *event)
virtual void mouseMoveEvent(QMouseEvent *event)
virtual void mousePressEvent(QMouseEvent *event)
virtual void mouseReleaseEvent(QMouseEvent *event)

И переопределять поведение кнопки в случае правой, или левой кнопки мыши.

ИВ
  • 29. April 2020 12:39

Извините, последний вопрос.
Написал маленький "проект", хочу стобы сцена ловила события мыши. Особенно когда мышь "отпустили". По сути это нужно для манипулирования с картинками на сцене.
Согласно справке за события на сцене отвечает QGraphicsSceneMouseEvent, однако попытка подружить слот и сигнал со сценой провалились, вот такой код привязки

connect (scene, &MainWindow::mousePressEvent, this, &MainWindow::Slot_mousePressEvent);

не работает, компилятор ругается на объект scene, если поставить this то все конечно компилируется, но это же и не должно работать как надо
Вот весь код проекта.
mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtCore>
#include <QtGui>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void mousePressEvent(QGraphicsSceneMouseEvent *e);
    void mouseMoveEvent(QGraphicsSceneMouseEvent *e);
    void mouseReleasEvent(QGraphicsSceneMouseEvent *e);
private:
    Ui::MainWindow *ui;
    QGraphicsScene *scene;
    QGraphicsEllipseItem *ellips;
private slots:
    void Slot_mousePressEvent();     // Слоты-обработчики нажатий кнопок
    void Slot_mouseMoveEvent();
    void Slot_mouseReleasEvent();
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsItem>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    scene = new QGraphicsScene(this);  //вот они наши сцены
    ui->graphicsView->setScene(scene);
    QBrush redBrash (Qt::red);
    QPen blackpen(Qt::black);
    blackpen.setWidth(6);
    ellips = scene->addEllipse(10,10,100,100,blackpen,redBrash);
    ellips->setFlag(QGraphicsItem::ItemIsMovable);

    //вот тут ошибки
    connect (this, &MainWindow::mousePressEvent, this, &MainWindow::Slot_mousePressEvent);
    connect (this, &MainWindow::mouseMoveEvent, this, &MainWindow::Slot_mouseMoveEvent);
    connect (this, &MainWindow::mouseReleasEvent, this, &MainWindow::Slot_mouseReleasEvent);
}
MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::mousePressEvent(QGraphicsSceneMouseEvent *e)
{
    emit Slot_mousePressEvent();
    Q_UNUSED(e);
}
void MainWindow::mouseMoveEvent(QGraphicsSceneMouseEvent *e)
{
    emit Slot_mouseMoveEvent();
    Q_UNUSED(e);
}
void MainWindow::mouseReleasEvent(QGraphicsSceneMouseEvent *e)
{
    emit Slot_mouseReleasEvent();
    Q_UNUSED(e);
}
void MainWindow::Slot_mousePressEvent()
{
    QMessageBox msgBox;
    msgBox.setText("Hello Here");
    msgBox.exec();
}
void MainWindow::Slot_mouseMoveEvent()
{
    QMessageBox msgBox;
    msgBox.setText("Hello Here");
    msgBox.exec();
}
void MainWindow::Slot_mouseReleasEvent()
{
    QMessageBox msgBox;
    msgBox.setText("Hello Here");
    msgBox.exec();
}

Как можно привязать слот к сцене или либому другому виджету ? Может быть у вас есть видеоурок на этой теме? Особенно интересует обработка событий при столкновении двух пиксельных рисунков на сцене.

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

ИВ
  • 29. April 2020 13:09

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

Дмитрий
  • 8. Juni 2022 15:24

Возник такой вопрос.
Разбираюсь с одной библиотекой. В ней применен паттерн Pimpl. И в коде есть вызовы метода connect(), но с помощью макросов:

Q_Q(QAmqpClient);
initSocket();
heartbeatTimer = new QTimer(q);
QObject::connect(heartbeatTimer, SIGNAL(timeout()), q, SLOT(_q_heartbeat()));
...

Q_Q(QAmqpClient); - это, как понял, получение q-указателя на основной класс.
В нем сигнал таймера, который находится в приватном классе коннектится к слоту, который находится в основном классе.
Как этот коннект записать без макросов?
В основном классе _q_heartbeat() объявлен как Q_PRIVATE_SLOT(d_func(), void _q_heartbeat()). Т.е., на сколько понял, вызывается приватный метод _q_heartbeat приватного класса. Причем он находится в секции public, а не public slots.

Попытки написать что-то типа

QObject::connect(heartbeatTimer, &QTimer::timeout, q, &QAmqpClient::_q_heartbeat);
или
QObject::connect(heartbeatTimer, &QTimer::timeout, q, &QAmqpClientPrivate::_q_heartbeat);

приводят к ошибке компиляции.
ошибка: '_q_heartbeat' is not a member of 'QAmqpClient'
или
ошибка: expected primary-expression before '/' token

Evgenii Legotckoi
  • 24. August 2022 07:38

Без макросов никак. Приватные методы через указатели не коннектятся извне, что правильно, а вот макросы болт кладут на private и protected модификаторы.

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
ИМ
Игорь Максимов5. Oktober 2024 07:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 11:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr8. Februar 2024 18:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25. Dezember 2023 10:30
Boost - statisches Verknüpfen im CMake-Projekt unter Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken