我试图根据手册创建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"更多推荐
发布评论