QThread blockt GUI-Thread

Alles rund um die Programmierung mit Qt
Antworten
thereapman
Beiträge: 36
Registriert: 6. Juni 2007 15:39

QThread blockt GUI-Thread

Beitrag von thereapman »

Hi!

Hab hier folgendes Problem:
Eine guianwendung und einen QThread der aus einer Datenbank Daten hohlt und dann per signal-Slot verbindung an den GUI-thread übergibt.
Sinn der Sache soll sein das der GUI-Thread nicht mehr blockiert wenn Zeug aus der Datenbank abgerufen werden muss.

Allerdings sobald der Thread anfängt (durch einen Qtimer ausgelöst) seine Daten aus der Datenbank zu hohlen blockiert die GUI.

Hier mal noch bissl Code:
GUI-anwendung:

Code: Alles auswählen

#include <QMainWindow>
#include <QTimer>

#include "ui_mainwindow.h"
#include "tourprio.h"
#include "TableModel.h"
#include "refreshthread.h"

class MDE_Main : public QMainWindow, public Ui::MDE_Main
{
        Q_OBJECT

        public:
                MDE_Main(bool);
                void connectActions();
                QString readSettings(QString);


        private slots:

                ...
                void getAuslagerdata(QList<QStringList>);
        public:
                void printArbeitsauftrag(QList<QString>, bool = false);
                TableModel *auslagermodel;
                TableModel *einlagermodel;
                TableModel *freigegebenmodel;

        private:
               ...

                RefreshThread rft;
               ...


};

#endif

mainwindow.cpp

Code: Alles auswählen


MDE_Main::MDE_Main(bool db_ready)
{

	setupUi(this);
	//Buldnummer setzen
	buildnr = "200812";


	//Timer Initzialisieren
	refresh_timer = new QTimer(this);
	inRefresh = false;

	//User setzen
	chgusr = qgetenv("USERNAME");
	qDebug() << "Initialisiere Programm für Nutzer:" + chgusr;

	//Modelle Laden und erstellen
...

	if(db_ready == true)
	{
		//Timer starten
                //refresh_timer->start(80000);

		//statusleiste schreiben
		connect_status_line->setText("DB2 Verbunden");
		refreshView(false);
	}
	else
	{
		connect_status_line->setText("DB2 Getrennt");
	}

	auslager_rfcount = 0;
	einlager_rfcount = 0;

	this->setWindowTitle(this->windowTitle() + " - Nutzer:" + chgusr);

	//Auf erstes Tab wechseln
	tab_widget->setCurrentIndex(0);

        //refreshthread starten
        qRegisterMetaType< QList<QStringList> >("QList<QStringList>");
        connect(&rft,SIGNAL(dataRefreshed(QList<QStringList>)),this,SLOT(getAuslagerdata(QList<QStringList>)),Qt::QueuedConnection);
       rft.start();



}

void MDE_Main::getAuslagerdata(QList<QStringList> input)
{
    qDebug() << "Get new Data";
    auslagermodel->setTableData(input);
    auslager_table->setModel(auslagermodel);
    auslager_table->reset();
    qDebug() << "fertig";
}


Der Thread:

Code: Alles auswählen

#ifndef REFRESHTHREAD_H
#define REFRESHTHREAD_H

#include <QThread>
#include <QtCore>
#include <QtSql>

class RefreshThread : public QThread
{
    Q_OBJECT

public:
    RefreshThread();

protected:
    void run();

private:
    QTimer *refresh_timer;
    QSqlDatabase db2;
    bool connectDB2();

private slots:
    void initRefresh();
    void refreshAuslagern();

signals:
    void dataRefreshed(QList<QStringList>);

};

#endif // REFRESHTHREAD_H

Code: Alles auswählen

#include <QtCore>
#include <QtSql>
#include "refreshthread.h"

RefreshThread::RefreshThread()
{

}


bool RefreshThread::connectDB2()
{
        db2 = QSqlDatabase::addDatabase("QODBC","THREADDB");
        //Use global Settings

        QSettings settings("settings.ini", QSettings::IniFormat);
        settings.beginGroup("global");

        //DB ODBC Alias
        QString dbname = settings.value("db_name", "").toString();
        QString username = settings.value("db_user", "").toString();
        QString pass = settings.value("db_pass", "").toString();
        settings.endGroup();


      //  db2.setDatabaseName(dbname);
       // db2.setUserName(username);
        //db2.setPassword(pass);

        db2.setDatabaseName("WISATEST");
        db2.setUserName("VAGen");
        db2.setPassword("VAGen");
        if(db2.open())
            return true;
        else
            return false;
}



void RefreshThread::run()
{
    qDebug() << "AUTO-THREAD --> strated";
    qDebug() << this->currentThreadId();

    qDebug() << "AUTO_THREAD --> DB connect";
    if(!connectDB2())
    {
        qDebug() << "AUTO-THREAD --> DBERROR --> " << db2.lastError().text();
        return;
    }

    qDebug() << "AUTO-THREAD --> Set timer";
    //RefreshTimer
    refresh_timer = new QTimer();
    refresh_timer->setInterval(20000);
    connect(refresh_timer, SIGNAL(timeout()),this, SLOT(initRefresh()));

    qDebug() << "AUTO-THREAD --> Start timer";
    refresh_timer->start();

    qDebug() << "AUTO-THREAD --> Starting Eventloop";
    exec();

}

void RefreshThread::initRefresh()
{
    qDebug() << "AUTO-THREAD --> Executing refresh";
    refreshAuslagern();


    qDebug() << "AUTO-THREAD --> Refresh END";
}

void RefreshThread::refreshAuslagern()
{
  [DATENBANK ABFRAGEZEUGS]
 emit dataRefreshed(auslager_data);
}

EDIT: titel geändert
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

2 Punkte fallen mir beim Überfliegen auf:

1) die QSqlDatabase Objekte werden statisch verwaltet. D.h. ein einmaliges ::addDatabase sollte reichen (in der GUI-Klasse), damit du per QSqlQuery von überall auf die Datenbank kommst (es sei denn du brauchst andere/mehrere Verbindungen). Ob das auch in Threads funktioniert, weiß ich allerdings nicht wirklich (habe DBs bisher nur singlethreaded programmiert)

2) Wieso triggerst du dir das Refresh in deinem Thread mit einem Timer, wenn der common way ist, eine Schleife in der run-Methode zu haben die das erledigt?

Code: Alles auswählen

::run()
{
   while (isRunning())
   {
      refresh();
      QWaitCond::sleep(1000);  //pseudo
   }
}
?

[edit]

3) wozu ist der refresh timer im GUI thread?
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag von upsala »

Ob eine Datenbank-Verbindung Multi-Thread fähig ist, steht in der Doku zur jeweiligen Datenbank. Normerlweise ist dies nicht der Fall.
thereapman
Beiträge: 36
Registriert: 6. Juni 2007 15:39

Beitrag von thereapman »

Das mit dem Refresh hab ich so aus der single-thread Variante meines Programms.


Das Problem mit dem blockenden GUI hab ich gelöst.
Alles was ausserhalb des Scopes von run() liegt wird nicht im neuen Thread ausgeführt. Nachdem ich die eigentlichen Arbeitsfunktionen in seperate Classen ausgelagert habe die in run instanziert werden gehts.

EDIT: der Refreshtimer im GUI-Thread ist der alte Timer der den Refresh im singlethread betrieb getriggert hat. Muss den Code dann ersmtal noch ausräumen. Die hauptklasse sieht aus wien Rübenfeld *gg*
Antworten