Qt信号槽按钮
Qt signal slot button
我每次单击时都需要更改 QLabel 中的文本,以便我的信息更新。
当我改变 A 的值时,B 的值也必须改变。
我有两个按钮可以更改两个 QLabel 中的值(A 的值,B 的值)。
main.cpp:
Counter A, B;
QObject::connect(&A, &Counter::changeValue, &B, &Counter::setValue);
QObject::connect(&A, &Counter::changeValue, &B, &Counter::Increment);
QObject::connect(&A, &Counter::changeValue, &B, &Counter::Decrement );
QObject::connect(Add, &QPushButton::clicked, &A, &Counter::clickedAdd(QLabel* obj));
QObject::connect(Sub, &QPushButton::clicked, &B, &Counter::clickedSub(QLabel* obj));
class Counter: public QObject{
private:
int count;
public slots:
int Increment () {
count++;
emit changeValue(count);
}
int Decrement () {
count--;
emit changeValue(count);
}
void clickedAdd(QLabel* obj){
int new_count = Increment();
obj_label->setText(QString::number(new_count));_
}
void clickedSub(QLabel* obj){
int new_count = Deccrement();
obj_label->setText(QString::number(new_count));_
}
void setValue(int new_count){
m_count = new_count;
emit changeValue(new_count);
}
public signals:
void changeValue(int);
如何更改两个 QLabel 中的文本?因为以这种方式它保持 const - 0 ....
当我尝试连接时:
QObject::connect(Add, &QPushButton::clicked, &A, &Counter::clickedAdd(QLabel* obj));
写错了:
调用不带对象参数的非静态成员函数。
但是我向函数传递了一个参数 - QLabel*。
假设 OP 需要
的应用程序
- 两个可以递增和递减的计数器
- 一个图形用户界面
- 显示计数器值
- 按钮到 increment/decrement 交互式计数器。
根据这个要求,我会推导出程序的结构:
- a class 用于
Counter
(已由 OP 公开)
- 一个 GUI。
对于后者,我经常在许多示例代码中看到 classes,但我相信:对于这样一个最小的 GUI / 应用程序,这甚至可以直接在 main()
中完成。
testQCounter.cc
:
#include <iostream>
#include <string>
// Qt header:
#include <QtWidgets>
// OPs Counter Class
class Counter : public QObject {
Q_OBJECT
private:
int count = 0;
public slots:
int Increment() {
count++;
emit changeValue(count);
return count;
}
int Decrement() {
count--;
emit changeValue(count);
return count;
}
int getValue() const { return count; }
void setValue(int new_count) {
count = new_count;
emit changeValue(new_count);
}
signals:
void changeValue(int);
};
#include "testQCounter.moc"
void setLabelValue(QLabel& qLbl, int value)
{
qLbl.setText(QString::number(value));
}
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup data
Counter a, b;
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("Counter Sample");
QGridLayout qGrid;
QLabel qLblATitle("Counter A");
qGrid.addWidget(&qLblATitle, 0, 0);
QPushButton qBtnIncA("+");
qGrid.addWidget(&qBtnIncA, 1, 0);
QLabel qLblA;
qLblA.setAlignment(Qt::AlignRight);
qLblA.setFrameStyle(QLabel::Box);
qGrid.addWidget(&qLblA, 2, 0);
QPushButton qBtnDecA("-");
qGrid.addWidget(&qBtnDecA, 3, 0);
QLabel qLblBTitle("Counter B");
qGrid.addWidget(&qLblBTitle, 0, 1);
QPushButton qBtnIncB("+");
qGrid.addWidget(&qBtnIncB, 1, 1);
QLabel qLblB("");
qLblB.setAlignment(Qt::AlignRight);
qLblB.setFrameStyle(QLabel::Box);
qGrid.addWidget(&qLblB, 2, 1);
QPushButton qBtnDecB("-");
qGrid.addWidget(&qBtnDecB, 3, 1);
qWinMain.setLayout(&qGrid);
qWinMain.show();
setLabelValue(qLblA, a.getValue());
setLabelValue(qLblB, b.getValue());
// install signal handlers
// connect clicked signal of buttons to counter a
QObject::connect(&qBtnDecA, &QPushButton::clicked, &a, &Counter::Decrement);
QObject::connect(&qBtnIncA, &QPushButton::clicked, &a, &Counter::Increment);
// connect changeValue signal of counter a to a function
QObject::connect(&a, &Counter::changeValue,
[&](int value) { setLabelValue(qLblA, value); });
// connect clicked signal of buttons to counter b
QObject::connect(&qBtnDecB, &QPushButton::clicked, &b, &Counter::Decrement);
QObject::connect(&qBtnIncB, &QPushButton::clicked, &b, &Counter::Increment);
// connect changeValue signal of counter b to b function
QObject::connect(&b, &Counter::changeValue,
[&](int value) { setLabelValue(qLblB, value); });
// runtime loop
return app.exec();
}
输出:
Qt Version: 5.15.1
备注:
关于计数器值标签的更新:
QLabel::setText()
(潜在槽)的签名是void QLabel::setText(const QString&)
.
要连接的信号的签名是 void Counter::changeValue(int)
.
显然,这些签名不兼容。
为了方便,我引入了一个函数
void setLabelValue(QLabel& qLbl, int value)
{
qLbl.setText(QString::number(value));
}
但这并不能解决不兼容问题,因为该函数还有另一个参数 QLabel&
,它不在发出的信号中。
这是一个非常常见的情况,非常常见的解决方案是绑定 resp。 QLabel
参考信号。
使用 lambda.
最容易完成
当我第一次看到 C++ 中的 lambda 时,我发现语法 non-intuitive 并且不知何故吓到了。
但是,在阅读了文档之后。和教程我已经习惯了,今天,我无法想象没有它会怎样。
我必须承认,在我了解 lambda 之前,我不得不 fiddle 与 bind()
和 hide()
(在 gtkmm
与 sigc++
中)。这真是一场噩梦……
Counter
定义了一个signal
。 (我修复了 OP 的错误语法。)
为了正确建立这个链接,我必须添加一些东西:
Q_OBJECT
#include "testQCounter.moc"
- 在我的构建脚本中支持 Qt moc。
我的构建脚本是一个 Visual Studio 项目,我用 CMake
脚本准备了它。
我不得不为 moc 扩展我的 CMakeLists.txt
(因为我通常在没有 moc 的情况下构建)。
CMakeLists.txt
用于为 testQCounter.cc
构建构建脚本:
project(QCounter)
cmake_minimum_required(VERSION 3.10.0)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(Qt5 COMPONENTS Widgets REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories("${CMAKE_SOURCE_DIR}")
add_executable(testQCounter testQCounter.cc)
target_link_libraries(testQCounter Qt5::Widgets)
对于我的作品,我也会使用单独的 class
es 作为 GUI 的东西,当然。
因此,考虑到该示例有两个具有几乎相同 GUI 的计数器,引入一个计数器小部件可能是有意义的 – CounterEdit
。
testQCounter2.cc
:
#include <iostream>
#include <string>
// Qt header:
#include <QtWidgets>
// OPs Counter Class
class Counter : public QObject {
Q_OBJECT
private:
int count = 0;
public slots:
int Increment() {
count++;
emit changeValue(count);
return count;
}
int Decrement() {
count--;
emit changeValue(count);
return count;
}
int value() const { return count; }
void setValue(int new_count) {
count = new_count;
emit changeValue(new_count);
}
signals:
void changeValue(int);
};
#include "testQCounter2.moc"
class CounterEdit : public QWidget {
private:
Counter* pCounter = nullptr;
QVBoxLayout qVBox;
QLabel qLblTitle;
QPushButton qBtnInc;
QLabel qLblValue;
QPushButton qBtnDec;
QMetaObject::Connection connectionInc;
QMetaObject::Connection connectionDec;
QMetaObject::Connection connectionValue;
public:
CounterEdit(const QString& title, QWidget* pQParent = nullptr) :
QWidget(pQParent),
qLblTitle(title),
qBtnInc("+"),
qLblValue(""),
qBtnDec("-")
{
qLblTitle.setAlignment(Qt::AlignCenter);
qVBox.addWidget(&qLblTitle);
qVBox.addWidget(&qBtnInc);
qLblValue.setAlignment(Qt::AlignRight);
qLblValue.setFrameStyle(QLabel::Box);
qVBox.addWidget(&qLblValue);
qVBox.addWidget(&qBtnDec);
setLayout(&qVBox);
}
virtual ~CounterEdit()
{
QObject::disconnect(connectionInc);
QObject::disconnect(connectionDec);
QObject::disconnect(connectionValue);
}
CounterEdit(const CounterEdit&) = delete;
CounterEdit& operator=(const CounterEdit&) = delete;
Counter* counter() { return pCounter; }
const Counter* counter() const { return pCounter; }
void updateValue();
void updatevalue(int) { updateValue(); }
void setCounter(Counter* pCounter);
};
void CounterEdit::updateValue()
{
if (pCounter) {
qLblValue.setText(QString::number(pCounter->value()));
} else {
qLblValue.setText(QString());
}
}
void CounterEdit::setCounter(Counter* pCounter)
{
QObject::disconnect(connectionInc);
QObject::disconnect(connectionDec);
QObject::disconnect(connectionValue);
this->pCounter = pCounter;
if (pCounter) {
qLblValue.setText(QString::number(pCounter->value()));
connectionInc
= QObject::connect(&qBtnInc, &QPushButton::clicked, pCounter, &Counter::Increment);
connectionDec
= QObject::connect(&qBtnDec, &QPushButton::clicked, pCounter, &Counter::Decrement);
connectionValue
= QObject::connect(pCounter, &Counter::changeValue, this, &CounterEdit::updateValue);
}
}
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup data
Counter a, b;
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("Counter Sample");
QHBoxLayout qHBox;
CounterEdit editA("Counter A:");
qHBox.addWidget(&editA);
CounterEdit editB("Counter B:");
qHBox.addWidget(&editB);
qWinMain.setLayout(&qHBox);
qWinMain.show();
editA.setCounter(&a);
editB.setCounter(&b);
// runtime loop
return app.exec();
}
输出:
Qt Version: 5.15.1
备注:
由于数据模型和 GUI 不再是硬连线设计,我稍微更改了 signal-slot 连接的管理:
- 连接是按需完成的(在
CounterEdit::setCounter()
中)。
- 不再需要时断开连接。
并不一定要像我在示例中那样存储连接。
在 Qt 中,也可以通过提供信号和插槽来断开连接,例如 connect()
。
我不喜欢这个有两个原因:
- 我很偏执。
- 这不适用于 lambda。
虽然更新的实际成员函数 (CounterEdit::updateValue()
) 是 parameter-less,但我提供了第二种风格 (CounterEdit::updateValue(int)
),它只是一个包装。
但是,这个 2nd flavor 与 Counter::changeValue()
具有相同的签名,因此可以在没有适配器的情况下用作插槽。
我每次单击时都需要更改 QLabel 中的文本,以便我的信息更新。 当我改变 A 的值时,B 的值也必须改变。 我有两个按钮可以更改两个 QLabel 中的值(A 的值,B 的值)。
main.cpp:
Counter A, B;
QObject::connect(&A, &Counter::changeValue, &B, &Counter::setValue);
QObject::connect(&A, &Counter::changeValue, &B, &Counter::Increment);
QObject::connect(&A, &Counter::changeValue, &B, &Counter::Decrement );
QObject::connect(Add, &QPushButton::clicked, &A, &Counter::clickedAdd(QLabel* obj));
QObject::connect(Sub, &QPushButton::clicked, &B, &Counter::clickedSub(QLabel* obj));
class Counter: public QObject{
private:
int count;
public slots:
int Increment () {
count++;
emit changeValue(count);
}
int Decrement () {
count--;
emit changeValue(count);
}
void clickedAdd(QLabel* obj){
int new_count = Increment();
obj_label->setText(QString::number(new_count));_
}
void clickedSub(QLabel* obj){
int new_count = Deccrement();
obj_label->setText(QString::number(new_count));_
}
void setValue(int new_count){
m_count = new_count;
emit changeValue(new_count);
}
public signals:
void changeValue(int);
如何更改两个 QLabel 中的文本?因为以这种方式它保持 const - 0 .... 当我尝试连接时:
QObject::connect(Add, &QPushButton::clicked, &A, &Counter::clickedAdd(QLabel* obj));
写错了: 调用不带对象参数的非静态成员函数。
但是我向函数传递了一个参数 - QLabel*。
假设 OP 需要
的应用程序- 两个可以递增和递减的计数器
- 一个图形用户界面
- 显示计数器值
- 按钮到 increment/decrement 交互式计数器。
根据这个要求,我会推导出程序的结构:
- a class 用于
Counter
(已由 OP 公开) - 一个 GUI。
对于后者,我经常在许多示例代码中看到 classes,但我相信:对于这样一个最小的 GUI / 应用程序,这甚至可以直接在 main()
中完成。
testQCounter.cc
:
#include <iostream>
#include <string>
// Qt header:
#include <QtWidgets>
// OPs Counter Class
class Counter : public QObject {
Q_OBJECT
private:
int count = 0;
public slots:
int Increment() {
count++;
emit changeValue(count);
return count;
}
int Decrement() {
count--;
emit changeValue(count);
return count;
}
int getValue() const { return count; }
void setValue(int new_count) {
count = new_count;
emit changeValue(new_count);
}
signals:
void changeValue(int);
};
#include "testQCounter.moc"
void setLabelValue(QLabel& qLbl, int value)
{
qLbl.setText(QString::number(value));
}
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup data
Counter a, b;
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("Counter Sample");
QGridLayout qGrid;
QLabel qLblATitle("Counter A");
qGrid.addWidget(&qLblATitle, 0, 0);
QPushButton qBtnIncA("+");
qGrid.addWidget(&qBtnIncA, 1, 0);
QLabel qLblA;
qLblA.setAlignment(Qt::AlignRight);
qLblA.setFrameStyle(QLabel::Box);
qGrid.addWidget(&qLblA, 2, 0);
QPushButton qBtnDecA("-");
qGrid.addWidget(&qBtnDecA, 3, 0);
QLabel qLblBTitle("Counter B");
qGrid.addWidget(&qLblBTitle, 0, 1);
QPushButton qBtnIncB("+");
qGrid.addWidget(&qBtnIncB, 1, 1);
QLabel qLblB("");
qLblB.setAlignment(Qt::AlignRight);
qLblB.setFrameStyle(QLabel::Box);
qGrid.addWidget(&qLblB, 2, 1);
QPushButton qBtnDecB("-");
qGrid.addWidget(&qBtnDecB, 3, 1);
qWinMain.setLayout(&qGrid);
qWinMain.show();
setLabelValue(qLblA, a.getValue());
setLabelValue(qLblB, b.getValue());
// install signal handlers
// connect clicked signal of buttons to counter a
QObject::connect(&qBtnDecA, &QPushButton::clicked, &a, &Counter::Decrement);
QObject::connect(&qBtnIncA, &QPushButton::clicked, &a, &Counter::Increment);
// connect changeValue signal of counter a to a function
QObject::connect(&a, &Counter::changeValue,
[&](int value) { setLabelValue(qLblA, value); });
// connect clicked signal of buttons to counter b
QObject::connect(&qBtnDecB, &QPushButton::clicked, &b, &Counter::Decrement);
QObject::connect(&qBtnIncB, &QPushButton::clicked, &b, &Counter::Increment);
// connect changeValue signal of counter b to b function
QObject::connect(&b, &Counter::changeValue,
[&](int value) { setLabelValue(qLblB, value); });
// runtime loop
return app.exec();
}
输出:
Qt Version: 5.15.1
备注:
关于计数器值标签的更新:
QLabel::setText()
(潜在槽)的签名是void QLabel::setText(const QString&)
.
要连接的信号的签名是void Counter::changeValue(int)
.
显然,这些签名不兼容。为了方便,我引入了一个函数
void setLabelValue(QLabel& qLbl, int value) { qLbl.setText(QString::number(value)); }
但这并不能解决不兼容问题,因为该函数还有另一个参数
QLabel&
,它不在发出的信号中。这是一个非常常见的情况,非常常见的解决方案是绑定 resp。
最容易完成QLabel
参考信号。 使用 lambda.当我第一次看到 C++ 中的 lambda 时,我发现语法 non-intuitive 并且不知何故吓到了。 但是,在阅读了文档之后。和教程我已经习惯了,今天,我无法想象没有它会怎样。 我必须承认,在我了解 lambda 之前,我不得不 fiddle 与
bind()
和hide()
(在gtkmm
与sigc++
中)。这真是一场噩梦……Counter
定义了一个signal
。 (我修复了 OP 的错误语法。)为了正确建立这个链接,我必须添加一些东西:
Q_OBJECT
#include "testQCounter.moc"
- 在我的构建脚本中支持 Qt moc。
我的构建脚本是一个 Visual Studio 项目,我用
CMake
脚本准备了它。 我不得不为 moc 扩展我的CMakeLists.txt
(因为我通常在没有 moc 的情况下构建)。CMakeLists.txt
用于为testQCounter.cc
构建构建脚本:project(QCounter) cmake_minimum_required(VERSION 3.10.0) set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) find_package(Qt5 COMPONENTS Widgets REQUIRED) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories("${CMAKE_SOURCE_DIR}") add_executable(testQCounter testQCounter.cc) target_link_libraries(testQCounter Qt5::Widgets)
对于我的作品,我也会使用单独的 class
es 作为 GUI 的东西,当然。
因此,考虑到该示例有两个具有几乎相同 GUI 的计数器,引入一个计数器小部件可能是有意义的 – CounterEdit
。
testQCounter2.cc
:
#include <iostream>
#include <string>
// Qt header:
#include <QtWidgets>
// OPs Counter Class
class Counter : public QObject {
Q_OBJECT
private:
int count = 0;
public slots:
int Increment() {
count++;
emit changeValue(count);
return count;
}
int Decrement() {
count--;
emit changeValue(count);
return count;
}
int value() const { return count; }
void setValue(int new_count) {
count = new_count;
emit changeValue(new_count);
}
signals:
void changeValue(int);
};
#include "testQCounter2.moc"
class CounterEdit : public QWidget {
private:
Counter* pCounter = nullptr;
QVBoxLayout qVBox;
QLabel qLblTitle;
QPushButton qBtnInc;
QLabel qLblValue;
QPushButton qBtnDec;
QMetaObject::Connection connectionInc;
QMetaObject::Connection connectionDec;
QMetaObject::Connection connectionValue;
public:
CounterEdit(const QString& title, QWidget* pQParent = nullptr) :
QWidget(pQParent),
qLblTitle(title),
qBtnInc("+"),
qLblValue(""),
qBtnDec("-")
{
qLblTitle.setAlignment(Qt::AlignCenter);
qVBox.addWidget(&qLblTitle);
qVBox.addWidget(&qBtnInc);
qLblValue.setAlignment(Qt::AlignRight);
qLblValue.setFrameStyle(QLabel::Box);
qVBox.addWidget(&qLblValue);
qVBox.addWidget(&qBtnDec);
setLayout(&qVBox);
}
virtual ~CounterEdit()
{
QObject::disconnect(connectionInc);
QObject::disconnect(connectionDec);
QObject::disconnect(connectionValue);
}
CounterEdit(const CounterEdit&) = delete;
CounterEdit& operator=(const CounterEdit&) = delete;
Counter* counter() { return pCounter; }
const Counter* counter() const { return pCounter; }
void updateValue();
void updatevalue(int) { updateValue(); }
void setCounter(Counter* pCounter);
};
void CounterEdit::updateValue()
{
if (pCounter) {
qLblValue.setText(QString::number(pCounter->value()));
} else {
qLblValue.setText(QString());
}
}
void CounterEdit::setCounter(Counter* pCounter)
{
QObject::disconnect(connectionInc);
QObject::disconnect(connectionDec);
QObject::disconnect(connectionValue);
this->pCounter = pCounter;
if (pCounter) {
qLblValue.setText(QString::number(pCounter->value()));
connectionInc
= QObject::connect(&qBtnInc, &QPushButton::clicked, pCounter, &Counter::Increment);
connectionDec
= QObject::connect(&qBtnDec, &QPushButton::clicked, pCounter, &Counter::Decrement);
connectionValue
= QObject::connect(pCounter, &Counter::changeValue, this, &CounterEdit::updateValue);
}
}
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup data
Counter a, b;
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("Counter Sample");
QHBoxLayout qHBox;
CounterEdit editA("Counter A:");
qHBox.addWidget(&editA);
CounterEdit editB("Counter B:");
qHBox.addWidget(&editB);
qWinMain.setLayout(&qHBox);
qWinMain.show();
editA.setCounter(&a);
editB.setCounter(&b);
// runtime loop
return app.exec();
}
输出:
Qt Version: 5.15.1
备注:
由于数据模型和 GUI 不再是硬连线设计,我稍微更改了 signal-slot 连接的管理:
- 连接是按需完成的(在
CounterEdit::setCounter()
中)。 - 不再需要时断开连接。
并不一定要像我在示例中那样存储连接。 在 Qt 中,也可以通过提供信号和插槽来断开连接,例如
connect()
。 我不喜欢这个有两个原因:- 我很偏执。
- 这不适用于 lambda。
- 连接是按需完成的(在
虽然更新的实际成员函数 (
CounterEdit::updateValue()
) 是 parameter-less,但我提供了第二种风格 (CounterEdit::updateValue(int)
),它只是一个包装。但是,这个 2nd flavor 与
Counter::changeValue()
具有相同的签名,因此可以在没有适配器的情况下用作插槽。