QThread 的复杂使用——

Posted

技术标签:

【中文标题】QThread 的复杂使用——【英文标题】:Complex use of QThread - 【发布时间】:2015-05-08 18:52:33 【问题描述】:

我用 QT/C++ 编写了一个应用程序。这个应用程序有多个类来管理窗口、treewidget..和一个自定义类。

应用程序的目标是在 MacOSx 上像 QT/c++ 中的 android 文件传输。

目前整个应用程序都在一个线程中工作,其中包括 UI 管理和 android 设备管理。 android 设备访问由一个名为 DeviceManager 的类管理。这个类主要会打开设备,读取它,添加/删除文件....

我想做的是创建一个线程来处理 DeviceManager 中定义的所有方法。我希望 UI 在一个线程中,而 devicemngr 在一个单独的线程中。

这是我当前的代码:

main.cpp

int main(int argc, char *argv[])

    PULS_mtp_error_t error = ERROR_GENERAL;
    QThread PulsDeviceThread;

    QApplication app(argc, argv);

    DeviceMngr *MyMtp = new DeviceMngr;
    error = MyMtp->OpenDevice();
    ...
    MainUI MyWindow(*MyMtp);
    MyWindow.show();
    return app.exec();


MainUI 类定义如下

MainUI::MainUI(DeviceMngr& device) :
    m_device(device)


    m_closing = false;

    setWindowTitle(QString::fromUtf8("PULS"));
    resize(800,600);
    setUnifiedTitleAndToolBarOnMac(true);
    /* Creation of the Top bar section */
    createBackForwardButton();
    createLogoSection();
    createAddFolder();

    QWidget *TopBarWidget = new QWidget();
    TopBarWidget->setFixedHeight(61);
    QHBoxLayout *TopBarLayout = new QHBoxLayout(TopBarWidget);
    TopBarLayout->addWidget(BackForwardSection);
    TopBarLayout->addWidget(LogoSection);
    TopBarLayout->addWidget(AddFolderSection);

    /* Creation of Tree View Section */
    createTreeView();

    /* Creation of the bottom bar section */
    createInfoSection();

    /*about*/
    aboutAction = new QAction(tr("&About"),this);
    connect(aboutAction, SIGNAL(triggered()),this ,SLOT(aboutPuls()));

    QMenu *helpMenu = new QMenu("Help", this);
    helpMenu->addAction(aboutAction);
    menuBar()->addMenu(helpMenu);

    /*Overall Layout*/
    QWidget *MainWindowWidget = new QWidget();
    QVBoxLayout *MainWindowLayout = new QVBoxLayout(MainWindowWidget);

    MainWindowLayout->setSpacing(0);
    MainWindowLayout->addWidget(TopBarWidget);
    MainWindowLayout->addWidget(TreeViewSection);
    MainWindowLayout->addWidget(CreateInfoSection);

    setCentralWidget(MainWindowWidget);

    PulsUnplugged = false;
#if 1
    activeTimer = new QTimer(this);
    activeTimer->setSingleShot(false);
    activeTimer->setInterval(200);
    connect(activeTimer, SIGNAL(timeout()), this, SLOT(PulsDetection()));
    activeTimer->start();
#endif
    show();

createTreeView 等一些方法也会使用 m_device 来访问设备。

void MainUI::createTreeView()


    TreeViewSection = new QWidget();

    QVBoxLayout *TreeViewLayout = new QVBoxLayout(TreeViewSection);

    MyTreeWidget = new MyNewTreeWidget(m_device, *this);

    TreeViewLayout->addWidget(MyTreeWidget);

MyNewTreeWidget 还需要访问 DeviceMngr 类。

大部分用于 UI 管理的类都可以访问 DeviceMngr 类。我不知道如何使用 QThread 确保所有 UI 类都可以访问 DeviceMngr 类。

我想在 main.cpp 中创建一个 Qthread,但我看不到如何在 main.cpp 中添加插槽/信号,并且 DeviceMngr 将有信号/插槽与所有其他线程讨论。主要需要例如打开设备并接收结果。

我是否需要在主中创建所有信号/插槽连接,或者我可以在不同的类中添加我需要的内容并在需要时创建连接。

有什么想法吗?我已经尝试了第一个实现,但效果并不好。

谢谢

【问题讨论】:

【参考方案1】:

我建议创建工作线程并将您的DeviceMngr 移动到它。它的槽(和整个事件循环)将在线程的上下文中运行,您必须使用 Qt 的信号/槽机制,以确保线程安全地从其他 QObject 访问DeviceMngr

int main(...) 
    // ...
    QThread MtpThread;
    DeviceMngr MyMtp;
    MyMtp.moveToThread(&MtpThread);

    // connect signals/slots of DeviceMngr
    // ...

    // launch the thread
    MtpThread.start();

    // should you need to call slots of DeviceMngr from main use metacalls
    QMetaObject::invokeMethod(&MyMtp, "nameOfSlot");

    // run application

    // in the end join
    MtpThread.quit(); // stop event queue
    MtpThread.wait(); // join the thread

我希望你能明白。密钥是moveToThread() 和元调用。

【讨论】:

但是当 MainUI 或 MyTreeWidget 类需要访问时,我需要把所有的连接放在哪里?我可以在 main 中声明所有内容吗?例如 connect(this, SIGNAL(ckjskjgkls), MyTreeWidget, SLOT(MyTreeWidget::nameOfApi)) 或类似的东西? 例如在 MyTreeWidget 我有一个动作来创建一个调用 DeviceMngr 方法创建文件夹的文件夹。我在我的代码中使用 m_device 作为 ref 来访问实例化类。 MyTreeWidget 的上下文中,您可以使用上述元调用或者只是声明一个信号并发出它而不是调用。实际的连接是运行时的事情。我建议从您可以访问两个连接对象的地方调用它。 main 是个不错的选择。或者你可以创建一些“builder”类来完成这一切。

以上是关于QThread 的复杂使用——的主要内容,如果未能解决你的问题,请参考以下文章

Qt系列文章之二十八(基于QThread多线程概述)

等待QThread时UI会阻塞/如何正确使用QThread

重点:怎样正确的使用QThread类(注:包括推荐使用QThread线程的新方法QObject::moveToThread)

QThread与多线程(比较清楚)

我啥时候应该使用 QThread::HighestPriority

如何从 GUI 停止 QThread