2

repaint()今日、QT ライブラリの関数で問題が発生しました。簡単に言うと、BP アルゴリズムを使用してニューラル ネットワークをトレーニングするスロットを取得しました。コンソールでアルゴリズム全体をテストしてから、それを GUI アプリケーションに移動したいと考えました。リフレッシュ以外はすべて正常に動作します。bp_algニューラル ネットワークのトレーニングは、関数 (トレーニング) とlicz_mse関数 (現在のエラーのカウント)で行われる多くの計算を含むプロセスです。変数ilosc_epokは 1e10 まで設定できます。したがって、プロセス全体が数時間続くこともあります。そのため、100000 エポック (最後のif条件) ごとに現在の進行状況を表示したかったのです。wyniki進行状況を表示するために使用される QTextEdit クラスのオブジェクトです。不運にも、repaint()意図したとおりに動作しません。最初は GUIで更新さwynikiれますが、ランダムな時間が経過すると動作しなくなります。外部ループが終了すると、再び更新され、すべての変更が表示されます。

リフレッシュの頻度を変更しようとしましたが、遅かれ早かれ常に停止します (ブレーク条件を満たすためにトレーニング プロセス全体が十分に早く停止しない限り)。計算が多すぎるために、ある時点でアプリケーションが更新を停止することを決定したようです。私もそれは起こるべきではありません。古い質問の中から解決策を探していましたが、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();
    }
}
4

1 に答える 1

0

GUI スレッドをブロックしているため、再描画は機能しません。これは明らかに悪い設計です。GUI スレッドをブロックしてはいけません。

GUI スレッドで作業を行うことに固執する場合は、作業を強制的に小さなチャンクに分割し、各チャンクの後にメイン イベント ループに戻る必要があります。ネストされたイベント ループは悪なので、必要だとは思わないでください。これらはすべて悪臭を放つコードなので、近づかないようにしてください。

または、単純に計算QObjectをワーカー スレッドに移動し、そこで作業を行います。

以下のコードは、両方の手法を示しています。作業の細分化では、ループ内でローカルにだけでなく、ワーカー オブジェクト内でループ状態を維持する必要があることに気付くのは簡単です。それは厄介です、コードはまた悪臭を放ちます-それを避けてください.

コードは Qt 4.8 と 5.1 の両方で動作します。

スクリーンショット

//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"
于 2013-08-26T20:41:32.673 に答える