我在Qt上很新,我在创建新对话框时遇到了问题。这是它的类:
void MyQObject::initialize_m_Dialog(QMainWindow* p) { //slot
m_Dialog = new MyDialog(p);
}
...
QMetaObject::invokeMethod(this, "initialize_m_Dialog", Qt::BlockingQueuedConnection, Q_ARG(QMainWindow*, parent));
在另一个主题而非主要主题中,我这样做:
<UniformGrid Columns="###">
但它说我无法在另一个线程中创建新的小部件。所以我尝试了:
ItemsPanelTemplate
但我不确定我在做什么...... :) 我对此感到失望。
我怎样才能做到这一点?
答案 0 :(得分:3)
现有代码的设计很糟糕。让我们从对话框开始修复它。
Ui::
类不应该是公共基础。它们是一个实现细节,永远不应该暴露在对话框之外的任何地方。该对话框是一个抽象:您可以对其执行一些操作。它们应该是方法,并在内部访问Ui::
对象。该对话框也应该不依赖于任何特定类型的父窗口。如果对话框应该与其他一些对象相互作用,它应该发出信号然后连接到例如到主窗口。
为了演示它,假设对话框具有QLineEdit edit
元素。可以设置文本,其他人通知文本中的更改。它的设计应如下:
class ConnectToSource : public QDialog {
Q_OBJECT
public:
ConnectToSource(QWidget *parent = {}) : QDialog(parent) {
ui.setupUi(this);
connect(ui.edit, &QLineEdit::textChanged,
this, &ConnectToSource::textChanged); // forward the signal
}
Q_SLOT void setText(const QString & text) {
ui.edit->setText(text);
}
QString text() const {
return ui.edit->text();
}
Q_SIGNAL void textChanged(const QString &);
protected:
void keyPressEvent(QKeyEvent *) override { ... }
private:
Ui::ConnectToSource ui;
};
现在,让我们看看我们如何从任何线程访问它。关键是发送一些代码在主线程中执行。有关详细信息,请参阅this answer。这段代码 - 一个仿函数 - 应该包含设置对话框所需的所有数据。
然后:
// https://stackoverflow.com/a/21653558/1329652
template <typename F>
static void postToThread(F && fun, QThread * thread = qApp->thread());
void setupDialog(MainWindow *parent, const QString &text) {
postToThread([=]{ // the functor captures parent and text by value
auto dialog = new ConnectToSource(parent);
dialog->setText(text);
connect(dialog, &ConnectToSource::textChanged, parent, &MainWindow::useText);
dialog->show();
dialog->setAttribute(Qt::WA_DeleteOnClose); // don't leak the dialog
});
}
setupDialog
函数是线程安全的,只要线程不比parent
更长,就可以在任何线程中执行。
请注意,上面的代码基本上是非阻塞的。仿函数包含在一个事件中,并传递给主线程的事件调度程序,然后执行仿函数。执行setupDialog
的线程可能只会在互斥锁上满足主线程的事件队列。这个互斥锁只是偶尔持有很短的时间。
答案 1 :(得分:1)
我在下面的不同主题中创建了一个用于创建UI组件的演示,
MyThread
在启动后会发出信号。如果UI线程收到信号,则会创建并显示dialog
。
<强> MyThread的:强>
#include <QThread>
#include <QDebug>
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject* parent = 0) :
QThread(parent){}
protected:
void run(){
qDebug()<<"Current thread:"<<QThread::currentThread();
emit somethingHappened();
}
signals:
void somethingHappened();
};
Qt用户界面
#include <QMainWindow>
#include <QDialog>
class MyDialog : public QDialog
{
public:
MyDialog(QWidget *parent = 0) :
QDialog(parent)
{ show(); }
void keyPressEvent(QKeyEvent* /*e*/){
close();
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0) :
QMainWindow(parent),
mDialog(Q_NULLPTR)
{
qDebug()<<"Current thread:"<<QThread::currentThread();
MyThread* myThread = new MyThread(this);
connect(myThread, &MyThread::somethingHappened,
this, &MainWindow::createDialog, Qt::QueuedConnection);
myThread->start();
}
private slots:
void createDialog(){
qDebug()<<"Current thread:"<<QThread::currentThread();
if(mDialog == Q_NULLPTR)
mDialog = new MyDialog(this);
}
private:
MyDialog* mDialog;
};
有关您的代码的一些建议:
void MyQObject :: initialize_m_Dialog(QMainWindow * p){ m_Dialog = new MyDialog(p); }
dialog
的创建应该在UI线程中进行,因此UI组件(QMainWindow * p)不能作为参数。
注意内存泄漏。