在主线程中创建 QDialog
Create a QDialog in main thread
我是Qt的新手,在创建新对话框时遇到了问题。这是它的类:
class MyDialog : public QDialog, public Ui::ConnectToSource
{
public:
MyDialog(QMainWindow *p_Parent = 0);
void keyPressEvent(QKeyEvent* e);
};
MyDialog::MyDialog(QMainWindow *p_Parent) : QDialog(p_Parent) {
setupUi(this);
}
在另一个线程而不是主线程中,我正在这样做:
m_Dialog = new MyDialog(parent);
但这是说我无法在另一个线程中创建新的小部件。所以我尝试了:
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));
但我不太确定我在做什么... :) 我对此陷入了僵局。
我怎样才能做到这一点?
现有代码的设计很烂。让我们修复它,从对话框开始。
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;
};
现在,让我们看看如何从任何线程访问它。关键是发送一些代码在主线程中执行。有关详细信息,请参阅此答案。该段代码(函子)应携带设置对话框所需的所有数据。
然后:
// 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
的线程只能在主线程的事件队列的互斥锁上获得内容。这种互斥锁只是偶尔持有,持续时间很短。
我在下面的不同线程中创建了一个用于创建 UI 组件的演示,MyThread
启动后会发出信号。如果 UI 线程收到信号,将创建并显示dialog
。
我的线程:
#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 UI:
#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 = 新的 MyDialog(p);}
dialog
的创建应该在 UI 线程中完成,因此 UI 组件 (QMainWindow* p) 不能作为参数。注意内存泄漏。
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 使用std::multimap迭代器创建std::list
- 在全局变量中保存类的实例以重新创建类(创建"backup")
- 使用CMake创建QML插件
- 如何在c++中为模板函数实例创建快捷方式
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- OpenCV EqualizeHist()从彩色图像创建黑白图像
- 试图在visual studio上用C++创建一个桌面应用程序
- std::threads可以从Windows DLL中的全局变量创建/销毁吗?
- 如何在C++20中创建模板别名的推导指南
- 如何为模板化对象创建模板向量?VS正在投掷C3203
- 如何创建一个空的全局类并在启动时实例化它
- Qt5自定义QDialog,不使用qt创建器
- 在主线程中创建 QDialog
- 创建多个带有通过阀的QDialog
- 在Qt中创建类,其中QDialog内置特定对话框
- Qt:当主窗口被模式QDialog阻止时,如何聚焦从主窗口创建的无模式QDialog
- 显示QtDesigner中创建的QDialog
- QDialog创建后闪回