c++ Qt QObject::连接不同类的接收器和插槽[重复]

qfe3c7zg  于 2023-06-07  发布在  其他
关注(0)|答案(1)|浏览(124)

此问题已在此处有答案

Unable to connect signal to slot in another class(2个答案)
3天前关闭。
截至3天前,社区正在审查是否重新讨论此问题。
我有两个班:第一个是主QMainWindow类,第二个是我的自定义类。例如,我想在我的自定义类的构造函数中建立一个连接,当我按下TestButton(它是main类的ui的一部分)时,它会从我的自定义类中调用一个函数。代码如下:
程序h:

class Custom;

class Program : public QMainWindow
{
    Q_OBJECT

    friend class Custom;

public:
    Program(QWidget *parent = nullptr);
    ~Program();

private:
    Ui::ProgramClass ui;
}

Program.cpp:

#include "Program.h"
#include "Custom.h"

Program::Program(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi

    Custom custom = Custom(this);
}

自定义h:

#include "Program.h"

class Custom : public QObject {
    Q_OBJECT
public:
    Custom(Program* program);
    ~Custom();

public slots:
    void foo();

private:
    Program* m_program;
}

最后是Custom.cpp:

#include "Custom.h"
#include "Program.h"

Custom::Custom(Program* program) {
    m_program = program;

    /* Here's the main problem */
    QObject::connect(m_program->ui.TestButton, &QPushButton::clicked, m_program, &Custom::foo);
}

/* Here just changing text of the button and set flat true or false every time button is clicked */
void Custom::foo() {
    QPushButton* button = m_program->ui.TestButton;

    button->setFlat(!button->isFlat());
    button->setText(button->isFlat() ?
        "If you see the text changing when clicking the button, it means test is working correctly" :
        "Text changed");
}

主要部分在自定义构造函数中,我在其中键入了connect函数。
错误:无法将参数3从“Program *”转换为“const Custom *”。因此,指向receiver和函数foo的指针必须是相同的类。
所以我试了这个:QObject::connect(m_program->ui.TestButton, &QPushButton::clicked, this, &Custom::foo);
没有错误,但实际上没有连接到主程序,当我点击按钮-没有什么变化。
唯一的工作变量是:

  • foo函数作为Program类的方法,但我不想让Program类有很多实际上应该是独立类的方法的函数。如果我想修改这些分离类的其他字段,这个变体将不起作用;
  • 在QObject::connection中输入lambda-function,但是我有一些大的函数,我需要经常在QObject::connect中调用其中的一些函数。

那么,我怎样才能正确地连接到整个程序,而不把foo函数作为自定义类的方法呢?

baubqpgj

baubqpgj1#

Custom custom = Custom(this);创建了一个本地对象,它在退出Program构造函数后“立即死亡”,这不是你想要做的。下面是要持久化Custom的示例所必须做的:

Custom *custom = new Custom(this);

你甚至可以让名为custom的指针成为一个成员变量,如果你想以后访问它的话。Custom的构造函数必须是:

Custom::Custom(Program* program) : QObject(program) 
 {
     QObject::connect( m_program->ui.TestButton, &QPushButton::clicked,
                       this, &Custom::foo );
 }

该构造函数将指针传递给QObject的构造函数,该构造函数将Custom注册为所提供对象的“子”,在我们的示例中为Program。在Qt术语中,Program将负责Custom示例的销毁。
是你想做的吗?要将按钮连接到Custom的示例,请执行以下操作:坦率地说,在这里使用m_program->ui.TestButton侵犯了Program的个人空间,并依赖于实现,但这在这里是一个题外话。
但让我们退一步来看看,为什么你所做的,即使是完全错误的,也没有成功?
让我们把函数和插槽放在一边。如果必须对“普通”类这样做,并使用一个不同的class A指针调用类B的方法foo(),你会怎么做?
正确,类B应该派生自Afoo()应该是在A中首先声明的虚方法。THis允许一种类型擦除,其中指向B的指针或引用可以作为指向A的指针传递。函数也可以通过指针传递,但如果它们是类的成员则不行。为此,存在一种特殊的指针。

#include <iostream>

class A {
public:
   virtual void foo() = 0;
};

class B : public A {
public:
   virtual void foo() { std::cout << "Hello from foo()!\n"; }
};

// A registering\calling class
class C {
public:

   void connect(A* p) {  
         cptr = p;
         f_ptr = &A::foo;
   }

   void call() {  (cptr->*f_ptr)(); }
private:
   A    *cptr;
   void (A::*f_ptr)();
};

int main() {
    B  b;
    C  c;
    c.connect(&b);
    c.call();
}

现在,您必须了解C::connect在那里做了什么:它保存指向对象A* p的指针的值和指向成员的指针。表达式&A::foo,一个指向A的成员的指针,如果我们将它与指向B的指针一起使用,那么对于被重写的&B::foo来说是法律的和正确的。
指向成员的指针可以像Qt中那样作为C::connect的参数,但要使其与任何成员函数一起工作,我们需要创建一个模板和另一个级别的类型擦除来保存这些值。我为了简洁起见把它省略了。
这几乎和Qt信号\插槽系统一样,至少如果你使用直接连接和新的连接语法。你需要一个类示例来调用它的成员,这就是为什么connect有这样的语法。即使您成功地执行了连接,您也会通过单击连接按钮来调用未定义的行为。值得庆幸的是,Qt通过断开被破坏的对象来优雅地处理它,* 因此实际上什么也没有发生。
这就是为什么所有使用signal\slot的类都必须是QObject的后代。信号和插槽只是函数。它们之间的区别在于 meta对象编译器生成信号的实现。
要使类型擦除工作,可以执行以下操作之一:
1.必须将类型为QObject*的指针传递给Custom的示例,并将其转换为class Custom,以便QObject::connect工作。如果在QObject中声明了signal或 virtual slot,则不需要强制转换。
1.或者你必须传递一个指向某个基类的指针,这个基类已经声明了插槽void foo()

  • 您可以将插槽声明为虚拟 *,而对于信号,您 * 不需要 * 这样做。
public slots:
    virtual void foo();  // can be pure in abstract class

在我看来,将QMainWindow作为基类是一个坏主意,你需要一些第三类。在最简单的情况下,它创建了过于冗长的代码,这就是为什么引入了另一个重载,它允许lambda或functor对象。

相关问题