我会尽量明确。我创建了一个 Qt 应用程序,它有一些按钮和一个 QTextEdit。接下来我创建一个 pthread。并提供指向 MainWindow 的指针作为参数。像这样:
MainWindow w;
pthread_create(&rThread,NULL,treat,&w);
treat是创建线程时执行的函数。现在,如果我有一个名为 myButton 的按钮,并且我在 treat 函数中做了类似这样的事情:
void *treat(void *arg)
{
MainWindow *win = (MainWindow*)arg;
win->ui->myButton->setEnabled(false);
close(pthread_self());
}
它将正常工作,我的应用程序中的 myButton 将被禁用。但是,如果我这样做:
void *treat(void *arg)
{
MainWindow *win = (MainWindow*arg;
win->ui->editText->setText("random string");
close(pthread_self());
}
我的应用程序将因以下错误而崩溃:
QObject: Cannot create children for a parent that is in a different thread. (Parent is QTextDocument(0x23af2e0), parent's thread is QThread(0x209a290), current thread is QThread(0x7f7eec000af0) The program has unexpectedly finished.
据我所知,Ui 存在于主线程中,并且可能无法在我创建的线程中访问,尽管我已将主窗口的指针提供给该线程。但是为什么禁用按钮会起作用?我很困扰。我不使用 QThread 的原因是因为我们的老师告诉我不要这样做。我必须使用 pthreads。我如何将 pthread 中的这种更改应用到 editText ? 我如何将信号从 pthread 发送到 Ui 处于“事件状态”的另一个线程。提前谢谢大家。
最佳答案
一般来说,从 object->thread()< 以外的线程调用任何
- 除非它们被设计和记录为线程安全的。 Qt 中有一些方法被明确记录为线程安全的,例如 QObject
(或派生类)方法都是错误的QCoreApplication::postEvent
。
您遇到的行为是由于从非 gui 线程访问 QWidget
方法所致。这是未定义的行为,所以有些方法可能会崩溃,有些不会,但即使它们不会崩溃,它仍然是您不能指望的未定义行为。据我们所知,这可能取决于月相。
从另一个线程做的唯一安全的事情是向对象发布一个事件。当您在另一个线程中的对象上使用 QMetaMethod::invoke
或 QMetaObject::invokeMethod
时,Qt 将在内部向该对象发送一个 QMetaCallEvent
。由于发布事件是线程安全的(可以从其他线程完成),因此可以从其他线程使用这些调用方法中的任何一个。 QObject::event()
通过执行适当的方法调用对此类事件作出 react 。
因此,您可以从另一个线程做的唯一事情是:
QMetaObject::invokeMethod(win->ui->editText, "setText", Q_ARG(QString, "random string"));
唉,这是糟糕的设计,因为您将 MainWindow 的内部细节(如 ui
指针)暴露给外部。你应该做的是在窗口上有一个 setEditText
槽:
MainWindow : public QWidget {
...
public:
Q_SLOT void setEditText(const QString & str) {
ui->editText->setText(str);
}
...
};
然后,从另一个线程,你做:
QMetaObject::invokeMethod(win, "setEditText", Q_ARG(QString, "random string"));
我完全同意 Marek R 的建议,当您有可用的 QThread
时不要使用 pthread。
关于c - 将信号从 pthread 发送到 QObject,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21134655/