【问题标题】:Qt. signal linked to slot of latest objectQt。链接到最新对象插槽的信号
【发布时间】:2021-08-28 06:21:09
【问题描述】:

我正在运行时创建多个 QWidget_WindowContact 类型的对象。当我单击增量或减量按钮时,最后生成的对象中的值会更新,而不是同一对象中的值。

我正在努力正确链接信号和插槽,以便在运行时生成多个相同类型的对象时,按钮发出的信号与正确对象中的信号相对应。

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"), SIGNAL(clicked()), this, SLOT(on_btnDec_clicked()));
    connect(this->findChild<QPushButton*>("btnDel"), SIGNAL(clicked()), this, 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();
}

【问题讨论】:

    标签: c++ qt signals-slots


    【解决方案1】:

    你有

    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);
    }
    

    附带说明,您的 CTor 中实际上不需要 findChild,因为您仍然拥有指向范围内相应对象的指针。

    【讨论】:

    • 非常感谢,没想到
    【解决方案2】:

    一般来说,设置小部件名称不是必需的,除非您需要它用于调试工具或自省。命名可以在构建时使用模板类轻松添加,也可以插入到布局中。这减少了冗长。请注意,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_, &layout, ::text_, tr("Increment")};
        WQ<QPushButton> btnDec{::layout_, &layout, ::text_, tr("Decrement")};
        WQ<QPushButton> btnDel{::layout_, &layout, ::text_, tr("Delete Widget")};
        WQ<QSpinBox> counter{::layout_, &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, &QPushButton::clicked, this, &Self::dec_clicked);
        connect(&btnDel, &QPushButton::clicked, this, &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"
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-15
      • 1970-01-01
      • 2015-03-14
      • 2016-11-10
      相关资源
      最近更新 更多