微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Qt信号链接到最新对象的插槽

如何解决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"

可编译示例到此结束。它看起来像这样:

Screenshot of the example

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。