gpt4 book ai didi

c++ - 使用 repaint() 刷新停止工作

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:57:13 26 4
gpt4 key购买 nike

今天我遇到了 repaint() 的问题来自 QT 库的函数。长话短说,我得到了一个使用 BP 算法训练我的神经网络的位置。我已经在控制台中测试了整个算法,然后想将它移到 GUI 应用程序中。除了刷新,一切正常。神经网络的训练是一个包含大量计算的过程,这些计算在bp_alg中进行。函数(训练)和licz_mse功能(计算当前错误)。变量 ilosc_epok可以设置为 1e10。因此整个过程可能持续数小时。这就是为什么我想在每 100000 个时期(最后一个 if 条件)后显示当前进度。 wyniki是用于显示进度的 QTextEdit 类的对象。不幸的是,repaint()没有按预期工作。一开始它刷新wyniki在 GUI 中,但在随机时间后它停止工作。外部循环完成后,它会再次刷新显示所有更改。

我尝试改变刷新频率,但迟早总是会停止(除非整个训练过程因满足中断条件而足够早地停止)。看起来应用程序在某个时刻决定停止刷新,因为计算太多。我不应该发生。我在旧问题中寻找解决方案,并在使用 qApp->processEvents(QEventLoop::ExcludeUserInputEvents); 时设法解决了问题。而不是 wyniki->repaint(); .不过,我还是很好奇为什么repaint()就这样停止工作。

下面我粘贴了一部分有问题的代码。如果有帮助,我会使用 QT Creator 2.4.1 和 QT Libraries 4.8.1。

unsigned long int ile_epok;
double mse_w_epoce;
for (ile_epok=0; ile_epok<ilosc_epok; ile_epok++) { //external loop of training
mse_w_epoce = 0;
for (int i=0; i<zbior_uczacy_rozmiary[0]; i++) { //internal loop of training
alg_bp(zbior_uczacy[i], &zbior_uczacy[i][zbior_uczacy_rozmiary[1]]);
mse_w_epoce += licz_mse(&zbior_uczacy[i][zbior_uczacy_rozmiary[1]]);
}
//checking break condition
if (mse_w_epoce < warunek_stopu) {
wyniki->append("Zakończono uczenie po " + QString::number(ile_epok) + " epokach, osiągając MSE: " + QString::number(mse_w_epoce));
break;
}
//problematic part
if ((ile_epok+1)%(100000) == 0) {
wyniki->append("Uczenie w toku, po " + QString::number(ile_epok+1) + " epokach MSE wynosi: " + QString::number(mse_w_epoce));
wyniki->repaint();
}
}

最佳答案

您正在阻塞您的 GUI 线程,因此重绘将不起作用,这显然是糟糕的设计。您永远不应该阻塞 GUI 线程。

如果您坚持在 GUI 线程中完成工作,则必须强行将工作分成小块,并在每个小块之后返回主事件循环。嵌套的事件循环是邪恶的,所以别以为您会想要一个。所有这些都有不好的代码味道,所以请远离。

或者,只需将您的计算 QObject 移动到工作线程并在那里完成工作。

下面的代码展示了这两种技术。很容易注意到,工作的分割需要在工作对象内部维护循环状态,而不仅仅是在循环本地。它更加困惑,代码闻起来很糟糕 - 再次避免它。

代码在 Qt 4.8 和 5.1 下均有效。

Screenshot

//main.cpp
#include <QApplication>
#include <QThread>
#include <QWidget>
#include <QBasicTimer>
#include <QElapsedTimer>
#include <QGridLayout>
#include <QPlainTextEdit>
#include <QPushButton>

class Helper : private QThread {
public:
using QThread::usleep;
};

class Trainer : public QObject {
Q_OBJECT
Q_PROPERTY(float stopMSE READ stopMSE WRITE setStopMSE)
float m_stopMSE;
int m_epochCounter;
QBasicTimer m_timer;
void timerEvent(QTimerEvent * ev);
public:
Trainer(QObject *parent = 0) : QObject(parent), m_stopMSE(1.0) {}
Q_SLOT void startTraining() {
m_epochCounter = 0;
m_timer.start(0, this);
}
Q_SLOT void moveToGUIThread() { moveToThread(qApp->thread()); }
Q_SIGNAL void hasNews(const QString &);
float stopMSE() const { return m_stopMSE; }
void setStopMSE(float m) { m_stopMSE = m; }
};

void Trainer::timerEvent(QTimerEvent * ev)
{
const int updateTime = 50; //ms
const int maxEpochs = 5000000;
if (ev->timerId() != m_timer.timerId()) return;

QElapsedTimer t;
t.start();
while (1) {
// do the work here
float currentMSE;
#if 0
for (int i=0; i<zbior_uczacy_rozmiary[0]; i++) { //internal loop of training
alg_bp(zbior_uczacy[i], &zbior_uczacy[i][zbior_uczacy_rozmiary[1]]);
currentMSE += licz_mse(&zbior_uczacy[i][zbior_uczacy_rozmiary[1]]);
}
#else
Helper::usleep(100); // pretend we're busy doing some work
currentMSE = 2E4/m_epochCounter;
#endif
// bail out if we're done
if (currentMSE <= m_stopMSE || m_epochCounter >= maxEpochs) {
QString s = QString::fromUtf8("Zakończono uczenie po %1 epokach, osiągając MSE: %2")
.arg(m_epochCounter).arg(currentMSE);
emit hasNews(s);
m_timer.stop();
break;
}
// send out periodic updates
// Note: QElapsedTimer::elapsed() may be expensive, so we don't call it all the time
if ((m_epochCounter % 128) == 1 && t.elapsed() > updateTime) {
QString s = QString::fromUtf8("Uczenie w toku, po %1 epokach MSE wynosi: %2")
.arg(m_epochCounter).arg(currentMSE);
emit hasNews(s);
// return to the event loop if we're in the GUI thread
if (QThread::currentThread() == qApp->thread()) break; else t.restart();
}
m_epochCounter++;
}
}

class Window : public QWidget {
Q_OBJECT
QPlainTextEdit *m_log;
QThread *m_worker;
Trainer *m_trainer;
Q_SIGNAL void startTraining();
Q_SLOT void showNews(const QString & s) { m_log->appendPlainText(s); }
Q_SLOT void on_startGUI_clicked() {
QMetaObject::invokeMethod(m_trainer, "moveToGUIThread");
emit startTraining();
}
Q_SLOT void on_startWorker_clicked() {
m_trainer->moveToThread(m_worker);
emit startTraining();
}
public:
Window(QWidget *parent = 0, Qt::WindowFlags f = 0) :
QWidget(parent, f), m_log(new QPlainTextEdit), m_worker(new QThread(this)), m_trainer(new Trainer)
{
QGridLayout * l = new QGridLayout(this);
QPushButton * btn;
btn = new QPushButton("Start in GUI Thread");
btn->setObjectName("startGUI");
l->addWidget(btn, 0, 0, 1, 1);
btn = new QPushButton("Start in Worker Thread");
btn->setObjectName("startWorker");
l->addWidget(btn, 0, 1, 1, 1);
l->addWidget(m_log, 1, 0, 1, 2);
connect(m_trainer, SIGNAL(hasNews(QString)), SLOT(showNews(QString)));
m_trainer->connect(this, SIGNAL(startTraining()), SLOT(startTraining()));
m_worker->start();
QMetaObject::connectSlotsByName(this);
}
~Window() {
m_worker->quit();
m_worker->wait();
delete m_trainer;
}
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}

#include "main.moc"

关于c++ - 使用 repaint() 刷新停止工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16749583/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com