在主线程中创建一个QDialog

时间:2018-03-22 20:49:51

标签: c++ qt

我在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

但我不确定我在做什么...... :) 我对此感到失望。

我怎样才能做到这一点?

2 个答案:

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

  1. dialog的创建应该在UI线程中进行,因此UI组件(QMainWindow * p)不能作为参数。

  2. 注意内存泄漏。