Qt信号连接到特定对象的槽函数

4
我想知道在Qt中使用信号/槽的以下哪种方式是正确的。
我需要有多个对话框实例,即AB。我需要从不同的线程告诉A打印“A”,告诉B打印“B”。所以我认为我需要像下面这样的东西: 选项1)A->print("A")B->print("B") 或者更好的做法是: 选项2)emit print("A")emit print("B") 并使用一种我不知道的方式,只有A捕获“A”,只有B捕获“B”。
我已经使选项1按以下方式工作:
class myClass : public QMainWindow
{
    Q_OBJECT

public:
    myClass (QWidget *parent = 0, Qt::WFlags flags = 0);
    ~myClass ();
    void doPrint(char* text)
    {
         emit mySignal(text);
    }
private:
    Ui::myClass ui;

public slots:
    void newLog(char* msg);

signals:
     void mySignal(char* msg);
};

myClass::myClass(QWidget *parent, Qt::WFlags flags) : QMainWindow(parent, flags)
{
    ui.setupUi(this);
    connect(this, SIGNAL(mySignal(char*)), this, SLOT(newLog(char*)));
}

void myClass::newLog(char* msg)
{
    ui.textEdit->append(msg);
}

然后我所需要做的就是:

myClass* instanceA = new myClass();
myClass* instanceB = new myClass();
instanceA->doPrint("A");
instanceB->doPrint("B");

这个对吗?

谢谢!


让我们从您的问题的反面开始。在哪种情况下,您可能会对选项2感到舒适?能否举个例子,说明第二种情况有什么优势,如果您只想打印"A"和"B"的话? - Narek
2个回答

3

由于您的插槽在另一个线程中,您必须使用元对象系统以异步方式调用该方法。正确的方法是使用QMetaObject::invokeMethod

请勿子类化QThread并覆盖run方法。有关详细信息,请参见:https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong

void otherClass::printTo(myClass* instance, char* text)
{
    QMetaObject::invokeMethod(instance,        // pointer to a QObject
                              "doPrint",       // member name (no parameters here)
                              Qt::QueuedConnection,     // connection type
                              Q_ARG(char*, text));     // parameters
}

void myClass::doPrint(char* text)
{
    ui.textEdit->append(text);
}

myClass* instanceA = new myClass();
myClass* instanceB = new myClass();
printTo(instanceA, "A");
printTo(instanceB, "B");

如果char*类型还未在元对象系统中注册,请使用Q_DECLARE_METATYPE(char*)进行注册。然后:
qRegisterMetaType<char*>("charPtr");

0
在这个简化的例子中,我认为你选择了正确的路径,即选项1。但是,如果您不需要doPrint()方法,那将更好,这也将消除对mySignal信号的需求(至少在myClass中)。相反,我建议从QThread继承您的线程(如果还没有),并执行以下操作:
class myThread : public QThread
{
    Q_OBJECT

public:
    myThread (QWidget *parent = 0 ) : QThread(parent) {}
    ~myThread () {}
    void run(char* text)
    {
         emit mySignal(text);
    }

signals:
     void mySignal(char* msg);
};

然后你需要做类似这样的事情:

myClass* instanceA = new myClass();
myThread* threadA = new myThread();
connect(threadA, SIGNAL(mySignal(char*)), instanceA, SLOT(newLog(char*)), Qt::QueuedConnection);
threadA->run( "A" );

显然,在大多数非示例代码中,您不会将字符串传递给run,而是生成要在threadA运行的字符串。优点是这使线程考虑因素不涉及myClass,并且只需要在它们连接的地方考虑它们。另一方面,您引入了更少的依赖项到线程中,因为它们不需要知道myClass就能够记录。


1
请停止散布关于在这种情况下需要Qt :: QueuedConnection的错误信息。自最近三个Qt版本以来,它已经不再需要了。 - Marc Mutz - mmutz
1
@mmutz,我在我的示例中这样做是为了确保连接的类型,基于http://doc.trolltech.com/4.5/threads.html#signals-and-slots-across-threads,其中部分内容指出:“QThread对象存在于创建对象的线程中--而不是在调用QThread::run()时创建的线程中。在您的QThread子类中提供插槽通常是不安全的。”然而,我现在看到“您可以安全地从QThread::run()实现中发出信号...”,这是我之前错过的。无论如何,指定连接类型不会损害任何我能看到的东西。 - Caleb Huitt - cjhuitt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接