使用QProgressBar时:无法为位于不同线程中的父级创建子级(While working with QProgressBar: Cannot create children for a pare

编程入门 行业动态 更新时间:2024-10-10 19:19:04
使用QProgressBar时:无法为位于不同线程中的父级创建子级(While working with QProgressBar: Cannot create children for a parent that is in a different thread)

我试图根据手册创建QProgressBar。 然而它确实很糟糕(例如,如果我在构造函数中创建QProgressDialog,它会在app运行时出现,所以我决定使用QProgressBar)。 但有个问题:

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

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; }

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

I tried to create QProgressBar according to Manual. Yet it works really bad (for instance, if I create QProgressDialog in the constructor, it will appear as soon as app is running, so I decided to use QProgressBar). But there is a problem:

Although I used advices from the internet. My code:

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; }

I don't know how to write code, that is possible to compile, because there is a lot of code.

最满意答案

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

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

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

有关在线程之间安全地调用方法的其他方法,请参阅此问题和该问题 。

// 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"

No QWidget (and derived classes) methods provided by Qt are thread-safe. Thus you can't access QProgressBar nor any other widgets from any thread other then the GUI thread.

The experimentFunction runs in a non-GUI thread and thus must not access widgets. You must figure out some other means of communication, e.g. using signals and slots. Recall that you're free to emit signals in experimentFunction, since the signal implementations are by contract thread-safe.

It's all really simple, and you don't need the future watcher. In your attempts to "fix" the issue, you've hopelessly combobulated your code.

For other ways of invoking methods safely across threads, see this question and that 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"

更多推荐

本文发布于:2023-07-17 15:06:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1146368.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:线程   working   QProgressBar   create   thread

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!