Qt
Señales y Slots
Buscar..
Introducción
Observaciones
La documentación oficial sobre este tema se puede encontrar aquí .
Un pequeño ejemplo
Las señales y las ranuras se utilizan para la comunicación entre objetos. El mecanismo de señales y ranuras es una característica central de Qt y probablemente la parte que más se diferencia de las características proporcionadas por otros marcos.
El ejemplo mínimo requiere una clase con una señal, una ranura y una conexión:
contador.h
#ifndef COUNTER_H
#define COUNTER_H
#include <QWidget>
#include <QDebug>
class Counter : public QWidget
{
/*
* All classes that contain signals or slots must mention Q_OBJECT
* at the top of their declaration.
* They must also derive (directly or indirectly) from QObject.
*/
Q_OBJECT
public:
Counter (QWidget *parent = 0): QWidget(parent)
{
m_value = 0;
/*
* The most important line: connect the signal to the slot.
*/
connect(this, &Counter::valueChanged, this, &Counter::printvalue);
}
void setValue(int value)
{
if (value != m_value) {
m_value = value;
/*
* The emit line emits the signal valueChanged() from
* the object, with the new value as argument.
*/
emit valueChanged(m_value);
}
}
public slots:
void printValue(int value)
{
qDebug() << "new value: " << value;
}
signals:
void valueChanged(int newValue);
private:
int m_value;
};
#endif
El main
establece un nuevo valor. Podemos comprobar cómo se llama la ranura, imprimiendo el valor.
#include <QtGui>
#include "counter.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Counter counter;
counter.setValue(10);
counter.show();
return app.exec();
}
Finalmente, nuestro archivo de proyecto:
SOURCES = \
main.cpp
HEADERS = \
counter.h
La nueva sintaxis de conexión Qt5.
La sintaxis de connect
convencional que usa las macros SIGNAL
y SLOT
funciona completamente en tiempo de ejecución, que tiene dos inconvenientes: tiene una sobrecarga de tiempo de ejecución (que también produce una sobrecarga de tamaño binario), y no hay verificación de corrección en tiempo de compilación. La nueva sintaxis aborda ambos problemas. Antes de verificar la sintaxis en un ejemplo, deberíamos saber qué sucede en particular.
Digamos que estamos construyendo una casa y queremos conectar los cables. Esto es exactamente lo que hace la función de conexión. Las señales y las ranuras son las que necesitan esta conexión. El punto es que si realiza una conexión, debe tener cuidado con las conexiones superpuestas. Cada vez que conectas una señal a una ranura, estás tratando de decirle al compilador que cada vez que se emitió la señal, simplemente invoca la función de ranura. Esto es lo que sucede exactamente.
Aquí hay un ejemplo de main.cpp :
#include <QApplication>
#include <QDebug>
#include <QTimer>
inline void onTick()
{
qDebug() << "onTick()";
}
struct OnTimerTickListener {
void onTimerTick()
{
qDebug() << "OnTimerTickListener::onTimerTick()";
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
OnTimerTickListener listenerObject;
QTimer timer;
// Connecting to a non-member function
QObject::connect(&timer, &QTimer::timeout, onTick);
// Connecting to an object member method
QObject::connect(&timer, &QTimer::timeout, &listenerObject, &OnTimerTickListener::onTimerTick);
// Connecting to a lambda
QObject::connect(&timer, &QTimer::timeout, [](){
qDebug() << "lambda-onTick";
});
return app.exec();
}
Sugerencia: la sintaxis antigua (macros de SIGNAL
/ SLOT
) requiere que el metacompilador Qt (MOC) se ejecute para cualquier clase que tenga ranuras o señales. Desde el punto de vista de la codificación, eso significa que dichas clases deben tener la macro Q_OBJECT
(lo que indica la necesidad de ejecutar MOC en esta clase).
La nueva sintaxis, por otro lado, aún requiere MOC para que las señales funcionen, pero no para las ranuras. Si una clase solo tiene slots y ninguna señal, no necesita tener la macro Q_OBJECT
y, por lo tanto, no puede invocar el MOC, lo que no solo reduce el tamaño binario final sino que también reduce el tiempo de compilación (no hay una llamada MOC ni una llamada de compilador posterior para el *_moc.cpp
archivo *_moc.cpp
).
Conexión de señales / slots sobrecargados
Aunque es mejor en muchos aspectos, la nueva sintaxis de conexión en Qt5 tiene una gran debilidad: la conexión de señales y ranuras sobrecargadas. Para permitir que el compilador resuelva las sobrecargas, necesitamos usar static_cast
s para los punteros de función miembro, o (comenzando en Qt 5.7) qOverload
y amigos:
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = nullptr) : QObject(parent) {}
public slots:
void slot(const QString &string) {}
void slot(const int integer) {}
signals:
void signal(const QString &string) {}
void signal(const int integer) {}
};
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
// using pointers to make connect calls just a little simpler
MyObject *a = new MyObject;
MyObject *b = new MyObject;
// COMPILE ERROR! the compiler does not know which overloads to pick :(
QObject::connect(a, &MyObject::signal, b, &MyObject::slot);
// this works, now the compiler knows which overload to pick, it is very ugly and hard to remember though...
QObject::connect(
a,
static_cast<void(MyObject::*)(int)>(&MyObject::signal),
b,
static_cast<void(MyObject::*)(int)>(&MyObject::slot));
// ...so starting in Qt 5.7 we can use qOverload and friends:
// this requires C++14 enabled:
QObject::connect(
a,
qOverload<int>(&MyObject::signal),
b,
qOverload<int>(&MyObject::slot));
// this is slightly longer, but works in C++11:
QObject::connect(
a,
QOverload<int>::of(&MyObject::signal),
b,
QOverload<int>::of(&MyObject::slot));
// there are also qConstOverload/qNonConstOverload and QConstOverload/QNonConstOverload, the names should be self-explanatory
}
Conexión de ranura de señal de ventana múltiple
Un ejemplo simple de ventanas múltiples que usa señales y ranuras.
Hay una clase de MainWindow que controla la vista de la ventana principal. Una segunda ventana controlada por clase de sitio web.
Las dos clases están conectadas para que cuando haga clic en un botón en la ventana del sitio web ocurra algo en la ventana principal (se cambia una etiqueta de texto).
Hice un ejemplo simple que también está en GitHub :
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "website.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void changeText();
private slots:
void on_openButton_clicked();
private:
Ui::MainWindow *ui;
//You want to keep a pointer to a new Website window
Website* webWindow;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::changeText()
{
ui->text->setText("New Text");
delete webWindow;
}
void MainWindow::on_openButton_clicked()
{
webWindow = new Website();
QObject::connect(webWindow, SIGNAL(buttonPressed()), this, SLOT(changeText()));
webWindow->show();
}
website.h
#ifndef WEBSITE_H
#define WEBSITE_H
#include <QDialog>
namespace Ui {
class Website;
}
class Website : public QDialog
{
Q_OBJECT
public:
explicit Website(QWidget *parent = 0);
~Website();
signals:
void buttonPressed();
private slots:
void on_changeButton_clicked();
private:
Ui::Website *ui;
};
#endif // WEBSITE_H
website.cpp
#include "website.h"
#include "ui_website.h"
Website::Website(QWidget *parent) :
QDialog(parent),
ui(new Ui::Website)
{
ui->setupUi(this);
}
Website::~Website()
{
delete ui;
}
void Website::on_changeButton_clicked()
{
emit buttonPressed();
}
Composición del proyecto:
SOURCES += main.cpp\
mainwindow.cpp \
website.cpp
HEADERS += mainwindow.h \
website.h
FORMS += mainwindow.ui \
website.ui
Considere la posibilidad de componer la UIS:
- Ventana principal: una etiqueta llamada "texto" y un botón llamado "openButton"
- Ventana del sitio web: un botón llamado "changeButton"
Por lo tanto, los puntos clave son las conexiones entre señales y ranuras y la gestión de los punteros o referencias de Windows.