使用QProgressBar时:无法为位于不同线程中的父级创建子级

时间:2017-06-08 20:45:31

标签: c++ qt

我试图根据Manual创建QProgressBar。但它的工作非常糟糕(例如,如果我在构造函数中创建QProgressDialog,它会在app运行时出现,所以我决定使用QProgressBar)。但是有一个问题:

enter image description here

虽然我使用了互联网上的建议。我的代码:

UPD![2]

// StudentAbsenceTableApp.h
using Job = std::function<void ()>;
Q_DECLARE_METATYPE(Job)
class StudentAbsenceTableApp{
public:
    StudentAbsenceTableApp(QWidget *parent = 0);

private:
    Q_SIGNAL void reqLoadFile(const QString& fileName);
    Q_SIGNAL void reqSaveFile(const QString& fileName);
    Q_SIGNAL void reqGui(const Job&);

    bool documentModified;
    QProgressBar *progressBar;
};

// StudentAbsenceTableApp.cpp
StudentAbsenceTableApp::StudentAbsenceTableApp(QWidget *parent)
   : QMainWindow(parent)
{
    // ...

    setStatusBar(new QStatusBar(this));

    qRegisterMetaType<Job>();
    progressBar = new QProgressBar(statusBar());
    progressBar->setMinimum(0);
    progressBar->setMaximum(0);
    progressBar->setMaximumWidth(150);
    progressBar->hide();
    statusBar()->addPermanentWidget(progressBar);

    connect(this, &StudentAbsenceTableApp::reqLoadFile, this, [this] (const QString& fileName){
        QtConcurrent::run(this, &StudentAbsenceTableApp::loadFile, fileName);
    });
    connect(this, &StudentAbsenceTableApp::reqGui, [this](const Job & job){
        job();
    });
}

// funtion that emit reqLoadFile(fileName)

bool StudentAbsenceTableApp::loadFile(const QString& fileName)
{
    reqGui([=] () { progressBar->show(); });
    auto xmlParser = XMLParser(model);

    try
    {
        reqGui([&] () {
            xmlParser.read(fileName);
            setCurrentFileName(fileName);
            statusBar()->showMessage(tr("Файл загружен"), 2000);
            documentModified = false;
        });
    }
    catch(FileOpenException)
    {
        reqGui([=] () {
            QMessageBox::warning(this, "Ошибка!", "Ошибка открытия файла!", QMessageBox::Ok);
            statusBar()->showMessage(tr("Загрузка отменена"), 2000);
        });
        return false;
    }
    catch(FileReadException)
    {
        reqGui([=] () {
            QMessageBox::warning(this, "Ошибка!", "Ошибка чтения файла!", QMessageBox::Ok);
            statusBar()->showMessage(tr("Загрузка отменена"), 2000);
        });
        return false;
    }

    reqGui([=] () { progressBar->hide(); });
    return true;
}

我不知道如何编写代码,可以编译,因为代码很多。

1 个答案:

答案 0 :(得分:5)

Qt提供的QWidget(和派生类)方法是线程安全的。因此,您无法从GUI线程以外的任何线程访问QProgressBar或任何其他小部件。

experimentFunction在非GUI线程中运行,因此不能访问小部件。你必须弄清楚其他一些沟通方式,例如:使用信号和插槽。回想一下,您可以在experimentFunction中自由发出信号,因为信号实现是通过合约线程安全的。

这一切都非常简单,你不需要未来的观察者。在您尝试“修复”问题时,您无可救药地将代码组合在一起。

有关跨线程安全地调用方法的其他方法,请参阅this questionthat question

// https://github.com/KubaO/stackoverflown/tree/master/questions/thread-progress-future-44445248
#include <QtConcurrent>
#include <QtWidgets>
#include <exception>
#include <functional>

struct FileOpenException : std::exception {};
struct FileReadException : std::exception {};
struct Model {};
struct XMLParser {
   XMLParser(Model &) {}
   void read(const QString &) {
      static int outcome;
      QThread::sleep(3);
      switch (outcome++ % 3) {
      case 0: return;
      case 1: throw FileOpenException();
      case 2: throw FileReadException();
      }
   }
};

using Job = std::function<void()>;
Q_DECLARE_METATYPE(Job)

class StudentAbsenceTable : public QMainWindow {
   Q_OBJECT
   QStatusBar m_statusBar;
   QProgressBar m_progress;
   QPushButton m_start{"Start Concurrent Task"};
   Model m_model;
   bool m_documentModified = {};
public:
   StudentAbsenceTable() {
      qRegisterMetaType<Job>();
      m_statusBar.addPermanentWidget(&m_progress);
      m_progress.setMinimum(0);
      m_progress.setMaximum(0);
      m_progress.setMaximumWidth(150);
      m_progress.hide();
      setStatusBar(&m_statusBar);
      setCentralWidget(&m_start);
      connect(&m_start, &QPushButton::clicked, this, [this]{
         m_start.setEnabled(false);
         QtConcurrent::run(this, &StudentAbsenceTable::loadFile);
      });
      connect(this, &StudentAbsenceTable::reqGui, this, [this](const Job & job){
         job();
      });
   }
private:
   bool loadFile() {
      reqGui([=]{ m_progress.show(); });
      auto fileName = QStringLiteral("/media/bsuir/data.xml");
      auto xmlParser = XMLParser(m_model);
      try {
         xmlParser.read(fileName);
         reqGui([=]{
            setCurrentFileName(fileName);
            statusBar()->showMessage(tr("Файл загружен"), 2000);
            m_documentModified = false;
         });
      }
      catch(FileOpenException&) {
         reqGui([=]{
            QMessageBox::warning(this, "Ошибка!", "Ошибка открытия файла!", QMessageBox::Ok);
            statusBar()->showMessage(tr("Загрузка отменена"), 2000);
         });
      }
      catch(FileReadException&) {
         reqGui([=]{
            QMessageBox::warning(this, "Ошибка!", "Ошибка чтения файла!", QMessageBox::Ok);
            statusBar()->showMessage(tr("Загрузка отменена"), 2000);
         });
      }
      reqGui([=]{ m_progress.hide(); m_start.setEnabled(true); });
      return false;
   }
   Q_SIGNAL void reqGui(const Job &);
   void setCurrentFileName(const QString &) {}
};

int main(int argc, char ** argv) {
   QApplication app(argc, argv);
   StudentAbsenceTable ui;
   ui.setMinimumSize(350, 350);
   ui.show();
   return app.exec();
}
#include "main.moc"