我试图根据Manual创建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;
}
我不知道如何编写代码,可以编译,因为代码很多。
答案 0 :(得分:5)
Qt提供的QWidget
(和派生类)方法是线程安全的。因此,您无法从GUI线程以外的任何线程访问QProgressBar
或任何其他小部件。
experimentFunction
在非GUI线程中运行,因此不能访问小部件。你必须弄清楚其他一些沟通方式,例如:使用信号和插槽。回想一下,您可以在experimentFunction
中自由发出信号,因为信号实现是通过合约线程安全的。
这一切都非常简单,你不需要未来的观察者。在您尝试“修复”问题时,您无可救药地将代码组合在一起。
有关跨线程安全地调用方法的其他方法,请参阅this question和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"