如何解决Qt信号链接到最新对象的插槽
我在运行时创建了多个 QWidget_WindowContact 类型的对象。当我点击 Increment 或 decrement 按钮时,最后生成的对象中的值会更新,而不是同一对象中的值。
我正在努力正确链接信号和插槽,以便在运行时生成多个相同类型的对象时,按钮发出的信号会与正确对象中的信号产生核心响应。
QWidget_WindowContact.h:
#ifndef QWIDGET_WINDOWCONTACT_H
#define QWIDGET_WINDOWCONTACT_H
#include "qwidget.h"
class QWidget_WindowContact : public QWidget
{
Q_OBJECT
public:
explicit QWidget_WindowContact(QWidget* parent = nullptr);
private slots:
void on_btnInc_clicked();
void on_btnDec_clicked();
void on_btnDel_clicked();
private:
QPoint offset;
protected:
};
#endif // QWIDGETTEMP_H
QWidget_WindowContact.h:
#include <QtWidgets>
#include "QWidget_WindowContact.h"
QSpinBox* counter;
QWidget_WindowContact::QWidget_WindowContact(QWidget* parent) : QWidget(parent)
{
this->setobjectName("");
setMinimumSize(100,100);
this->setStyleSheet("border: 1px solid gray");
QVBoxLayout* layout = new QVBoxLayout(this);
QLabel* name = new QLabel("WINDOWCONTACT",this);
QPushButton* btnInc = new QPushButton("Increment",this);
btnInc->setobjectName("btnInc");
QPushButton* btnDec = new QPushButton("Decrement",this);
btnDec->setobjectName("btnDec");
QPushButton* btnDel = new QPushButton("Delete Widget",this);
btnDel->setobjectName("btnDel");
counter = new QSpinBox(this);
counter->setobjectName("counter");
connect(this->findChild<QPushButton*>("btnInc"),SIGNAL(clicked()),this,SLOT(on_btnInc_clicked()));
connect(this->findChild<QPushButton*>("btnDec"),SLOT(on_btnDec_clicked()));
connect(this->findChild<QPushButton*>("btnDel"),SLOT(on_btnDel_clicked()));
layout->addWidget(name);
layout->addWidget(counter);
layout->addWidget(btnInc);
layout->addWidget(btnDec);
layout->addWidget(btnDel);
}
void QWidget_WindowContact::on_btnInc_clicked()
{
counter->setValue(counter->value() + 1);
}
void QWidget_WindowContact::on_btnDec_clicked()
{
counter->setValue(counter->value() - 1);
}
void QWidget_WindowContact::on_btnDel_clicked()
{
close();
}
解决方法
你有
QSpinBox* counter;
作为全局变量,每次创建新的 QWidget_WindowContact
时都会更改其值。这段代码:
void QWidget_WindowContact::on_btnInc_clicked()
{
counter->setValue(counter->value() + 1);
}
正在使用这个全局变量,它总是指向最近创建的 QSpinBox
的指针。删除全局变量并将此 QSpinBox*
ptr 保留为类的私有成员,或者使用 findChild
QT 功能:
void QWidget_WindowContact::on_btnInc_clicked()
{
QSpinBox* counter = this->findChild<QSpinBox*>("counter");
counter->setValue(counter->value() + 1);
}
附带说明,findChild
在您的 CTor 中实际上并不需要,因为您仍然拥有指向作用域中相应对象的指针。
一般来说,除非您需要调试工具或自省,否则不需要设置小部件名称。命名可以在构建时使用模板类轻松添加,也可以插入到布局中。这减少了冗长。请注意,WQ
是一个您将编写一次并广泛使用的库类,因此不必担心它的“复杂”程度 - 尽管它当然应该使用实际的标签调度头库来
#include <QtWidgets>
static inline constexpr auto name_ = [] {};
static inline constexpr auto layout_ = [] {};
static inline constexpr auto text_ = [] {};
template<class Base>
class WQ : public Base
{
public:
WQ() = default;
template<class... Args>
WQ(decltype(layout_),QLayout *layout,Args &&...args) : WQ(std::forward<Args>(args)...)
{
layout->addWidget(this);
}
template<class... Args>
WQ(decltype(name_),QStringView name,Args &&...args) : WQ(std::forward<Args>(args)...)
{
this->setObjectName(name.toString());
}
template<class... Args>
WQ(decltype(text_),QStringView text,Args &&...args) : WQ(std::forward<Args>(args)...)
{
this->setText(text.toString());
}
};
小部件可以非常紧凑,并以声明方式构建 - 不要受到 Qt 示例代码的启发,它不必要地冗长。我们可以在类的主体中设置大多数“短”小部件属性:
class QWidget_WindowContact : public QWidget
{
Q_OBJECT
using Self = QWidget_WindowContact;
public:
explicit QWidget_WindowContact(QWidget *parent = nullptr);
private:
void inc_clicked();
void dec_clicked();
QPoint offset;
QVBoxLayout layout{this};
WQ<QLabel> name{::layout_,&layout,::text_,tr("WINDOWCONTACT")};
WQ<QPushButton> btnInc{::layout_,tr("Increment")};
WQ<QPushButton> btnDec{::layout_,tr("Decrement")};
WQ<QPushButton> btnDel{::layout_,tr("Delete Widget")};
WQ<QSpinBox> counter{::layout_,::name_,L"counter"};
};
那么,构造函数也很简单:
QWidget_WindowContact::QWidget_WindowContact(QWidget *parent) : QWidget(parent)
{
setMinimumSize(100,100);
setStyleSheet("border: 1px solid gray");
connect(&btnInc,&QPushButton::clicked,this,&Self::inc_clicked);
connect(&btnDec,&Self::dec_clicked);
connect(&btnDel,&Self::close);
}
插槽保持原样,除非您需要进行内省,否则无需实际制作它们。不要将方法包装在“什么都不做”而只是转发调用的插槽中 - 这是不必要的。还要注意使用现代 (Qt 5) 风格的信号槽连接。另请注意,当前的 Qt 版本是 Qt 6.1 - 所以现代信号槽语法几乎不是“现代”的。
void QWidget_WindowContact::inc_clicked()
{
counter.setValue(counter.value() + 1);
}
void QWidget_WindowContact::dec_clicked()
{
counter.setValue(counter.value() - 1);
}
该示例在外部窗口内设置了一个 3x3 的小部件网格:
int main(int argc,char *argv[])
{
QApplication a(argc,argv);
QWidget w;
QGridLayout layout{&w};
constexpr int rows = 3,cols = 3;
QWidget_WindowContact wc[rows * cols];
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; ++j)
layout.addWidget(&wc[i * cols + j],i,j);
w.show();
return a.exec();
}
#include "main.moc"
可编译示例到此结束。它看起来像这样:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。