Forum: PC-Programmierung QLabel kontinuierlich aktuelisieren


von David S. (eledave)


Lesenswert?

Hallo zusammen!

Ich möchte in meinem QT Projekt ein QLabel verwenden.
Dazu ist zu sagen, dass ich über ein Modul digitale Ein- und Ausgänge 
schalte, bzw. einlese.
Wenn ich meinen pushButton drücke, läuft ein Stück Programm ab, in dem 
mehrmals die digitalen Eingänge eingelesen werden. Die Bitmaske der 8 
digitalen Eingänge muss ich zwischendurch einlesen, da ich somit den 
Schaltungszustand eines Relais abfrage. Nun habe ich das QLabel soweit 
konfiguriert, jedoch aktualisiert er das Label nicht nach jedem Step 
(mehrere Steps im pushButton, wo jedes mal die Bitmaske abgefragt wird), 
sondern erst wenn das Programm des pushButtons einmal durchgelaufen ist. 
Das Programm aktualisiert nur am Ende das QLabel und zeigt auch nur nur 
den letzten abgefragten Wert an.
Frage ist also, wie ich den Wert der in das QLabel geschrieben wird 
kontinuierlich aktualisiere?

Im Voraus schonmal vielen dank!!

von Sven B. (scummos)


Lesenswert?

Du hast nur einen Thread und solange der mit Programmlogik beschäftigt 
ist, kann er nicht gleichzeitig das Label neu malen.

Du musst entweder zwischendurch zur Event Loop zurückkehren und das 
zweite Einlesen der Eingänge später machen (z.B. mit einem Timer) oder 
einen zweiten Thread starten, der mit der Hardware redet.

von Rolf M. (rmagnus)


Lesenswert?

Ich verstehe nicht ganz, wie dein Programm abläuft. Wozu musst du alle 
Zwischenwerte ansehen können? Das dürfte sich doch so schnell ändern, 
dass man die eh nicht erkennen kann. Oder hast du irgendwo eine 
Warteschleife oder sowas, das dein Programm verzögert? Dann auf eine 
Timer-Steuerung umstellen. Notfalls wie Sven schon geschrieben hat, in 
einen separaten Thread stecken. Der GUI-Thread darf nicht durch lang 
andauernde Dinge blockiert werden.

von David S. (eledave)


Lesenswert?

Auszug aus dem Sourcecode der mainwindow.cpp:

Alle STEPs sind ähnlich aufgebaut wie STEP17:

void MainWindow::STEP17()
{
    mycard.get_RELAIS_NO();
    showRelais();
    Sleep(1700);        //vorher 1800
    elfkiloHz();
    //return;
}
void MainWindow::on_WEITER_clicked()
{
    STEP3();
    STEP4();
    STEP5();
    STEP6();
    STEP7();
    STEP8();
    STEP9();
    STEP10();
    STEP11();
    STEP12();
    STEP13();
    STEP14();
    STEP15();
    STEP16();
    STEP17();
    STEP18();
    STEP19();
    return;
}
void MainWindow::showRelais()
{
    ui->relaisLabel->setText(tr("Relaiszustand:%1")
                   .arg(QString::number(digitIN)));
}

Der mycard.get_RELAIS_NO(); holt sich aus dem Sourcecode des Moduls die 
Bitmaske der digitalen Eingänge, hier: digitIN (mögliche Werte die vom 
Modul gegeben werden sind 0-255).
Und in jedem Step hole ich mir den "aktuellen" Wert digitIN und möchte 
den in meinem QLabel relaisLabel anzeigen, um so ständig den Zustand des 
Relais anzeigen zu lassen.

von David S. (eledave)


Lesenswert?

Sehe grade, dass ich nen Tippfehler im Betreff habe, muss natürlich 
aktualisieren heißen ;-)

von MaWin (Gast)


Lesenswert?

David S. schrieb:
> Sleep(1700);

Tödlich für ein GUI-Programm.
Mache es raus und implementiere die ganze Geschichte mit einer 
Statemachine.

von Rolf M. (rmagnus)


Lesenswert?

David S. schrieb:
> Alle STEPs sind ähnlich aufgebaut wie STEP17:
>
> void MainWindow::STEP17()
> {
>     mycard.get_RELAIS_NO();
>     showRelais();
>     Sleep(1700);        //vorher 1800

Und da haben wir ja den Übeltäter. Genau dieses Sleep() muss weg! Es 
blockiert dir jedesmal die komplette GUI. Sowas darf nicht in einem 
Thread stehen, der eine Eventloop ausführt. Das ist ähnlich wie bei den 
ISRs auf einem µC. Nimm einen Timer (In diesem Fall die Klasse QTimer) 
und steuere dein Timing damit.

>     elfkiloHz();
>     //return;
> }

von Mathias O. (m-obi)


Lesenswert?

processEvents();

von Rolf M. (rmagnus)


Lesenswert?

Mathias O. schrieb:
> processEvents();

Kann manchmal sinnvoll sein. Hier ist aber die Vermeidung von 
blockierenden Wartefunktionen besser.

von Bernd K. (prof7bit)


Lesenswert?

Rolf M. schrieb:
> Mathias O. schrieb:
>> processEvents();
>
> Kann manchmal sinnvoll sein. Hier ist aber die Vermeidung von
> blockierenden Wartefunktionen besser.

Wenn processEvents() eintrittsinvariant ist (was es vermutlich ist) dann 
kann man mit dieser Methode oftmals einen ganzen Haufen Code und 
unübersichtliche Statemachines sparen, das erhöht meist die 
Leserlichkeit. Allerdings muss man genau wissen was man tut wenn man 
sich in der x-ten Ebene geschachtelter processEvents()-Auifrufe befindet 
und wie man da unter allen Umständen sauber wieder rauskommt ohne sich 
dabei aufzuhängen.

Ich mach das gerne in Lazarus (LCL), dort heißt das 
Application.ProcessMessages() und hat mir schon so manchen Timer, so 
manche häßliche unhandliche Statemachine und auch schon so manchen 
Thread erspart.

Wenn man weiß was das für Implikationen hat ist es im Sinne der 
Lesbarkeit oftmals die zu empfehlende Vorgehensweise.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Bernd K. schrieb:
> Rolf M. schrieb:
>> Mathias O. schrieb:
>>> processEvents();
>>
>> Kann manchmal sinnvoll sein. Hier ist aber die Vermeidung von
>> blockierenden Wartefunktionen besser.
>
> Wenn processEvents() eintrittsinvariant ist (was es vermutlich ist) dann
> kann man mit dieser Methode oftmals einen ganzen Haufen Code und
> unübersichtliche Statemachines sparen, das erhöht meist die
> Leserlichkeit.

Hier geht es um ein Programm, dass im obigen Beispiel X mal 
hintereinander komplett schlafen gelegt wird, in der beispielhaft 
gezeigen Funktion für 1,7 Sekunden. Was ist dein Vorschlag? Alle 1,7 
Sekunden mal processEvents() aufrufen? Oder aus dem einen Aufruf eine 
Schleife machen und 100 kurze Sleeps mit jeweils einem processEvents()? 
Das ist doch Murks.
Viel Statemachine braucht man ja auch nicht, wenn einfach eine Reihe von 
Aktionen nacheinander ausgeführt werden soll - immer in gleicher Anzahl 
und Reihenfolge.

von Bernd K. (prof7bit)


Lesenswert?

Rolf M. schrieb:
> Das ist doch Murks.

Definiere Murks.

von Sven B. (scummos)


Lesenswert?

Rolf M. schrieb:
> Hier geht es um ein Programm, dass im obigen Beispiel X mal
> hintereinander komplett schlafen gelegt wird, in der beispielhaft
> gezeigen Funktion für 1,7 Sekunden. Was ist dein Vorschlag? Alle 1,7
> Sekunden mal processEvents() aufrufen? Oder aus dem einen Aufruf eine
> Schleife machen und 100 kurze Sleeps mit jeweils einem processEvents()?
> Das ist doch Murks.

QEventLoop benutzen. Ist aber trotzdem Murks.

von Rolf M. (rmagnus)


Lesenswert?

Bernd K. schrieb:
> Rolf M. schrieb:
>> Das ist doch Murks.
>
> Definiere Murks.

Schrieb ich doch schon: Sleep-Funktionen haben in einem Thread, der eine 
GUI-Eventloop enthält, nichts verloren. Das macht das Programm nur 
unnötig träge. processEvents() ist da nur eine Krücke, die das etwas 
abmildert. Das grundlegende Problem bleibt aber bestehen. Murks eben...

von Georg A. (georga)


Lesenswert?

Es gibt für Qt "empfohlene" Emulationen von u/m/sleep. Entweder indirekt 
über QThread:
1
class Sleeper : public QThread
2
{
3
public:
4
    static void usleep(unsigned long usecs){QThread::usleep(usecs);}
5
    static void msleep(unsigned long msecs){QThread::msleep(msecs);}
6
    static void sleep(unsigned long secs){QThread::sleep(secs);}
7
};
8
...
9
Sleeper::sleep(5);
10
...
Oder von Hand mit Mutex:
1
        QWaitCondition sleep;
2
        QMutex mtx;
3
        mtx.lock();
4
        sleep.wait(&mtx,milisecs);

Nur vorsicht: Unter Windows nur(!) mit Qt4.8.6 scheint trotzdem die 
Eventloop zumindest teilsweise nicht ausgeführt zu werden (mit beiden 
Methoden). Da hängen Programme, die DDE-Anfragen an alle anderen 
broadcasten genau für die Dauer des sleeps.

von BobbyX (Gast)


Lesenswert?

Murks hoch 2 ist es :-)

Wie ich solche "Probleme" löse:

1. Die STEP1-19 ( auch Murks ) Funktionen müssen vom separetem Thread 
aufgerufen werden. Also: Meinen Thread von QThread ableiten und die STEP 
aufrufe in seine run() Methode Packen. Dort kann man auch problemloss 
sleep() benutyen, die GUI wird nicht geblockt...

2. IN dem Thread Objekt Metoden und Variablen deklarieren und 
definieren, die den Zustand von dem, was der Thread macht abfragen. 
MUtex ist nicht notwending, da wir nur lesen. Also diese Mothoden sind 
const Methoden...

3. Im GUI THread einen QTimer benutzen, durch den der Zustand des 
Threads regelmässig abgefragt wird, ZB. alle 100ms....

4. Den Thread starten und Tee Trinken... ;-)

von Sven B. (scummos)


Lesenswert?

Georg A. schrieb:
> Es gibt für Qt "empfohlene" Emulationen von u/m/sleep. Entweder indirekt
> über QThread:


Diese Antwort ist m.E. komplett wirr. Zum einen macht die Helper-Klasse 
keinerlei Sinn, du kannst auch direkt QThread::sleep() schreiben statt 
Sleeper::sleep() (???). Zum anderen ist ein sleep eben ein sleep, das 
macht i.d.R. einen Syscall der dem Betriebssystem sagt, dass jetzt ein 
anderer Thread geschedult werden soll und dieser hier nach n µs wieder 
aufgeweckt. Events werden während einem Sleep nie prozessiert und das 
hat auch mit der Qt-Version nichts zu tun.

von Rolf M. (rmagnus)


Lesenswert?

Sven B. schrieb:
> Diese Antwort ist m.E. komplett wirr. Zum einen macht die Helper-Klasse
> keinerlei Sinn, du kannst auch direkt QThread::sleep() schreiben statt
> Sleeper::sleep() (???).

Das geht wohl erst in neueren Qt-Versionen, da früher aus 
unverständlichen Gründen diese Funktionen protected waren und somit nur 
direkt aus abgeleiteten Klassen heraus und nicht von überalls aus 
aufrufbar waren.

> Events werden während einem Sleep nie prozessiert und das
> hat auch mit der Qt-Version nichts zu tun.

Der Grund für die Existenz dieser Funktionen dürfte auch nicht sein, 
dass da auf irgendeine Weise Events bearbeitet würden, sondern einfach 
damit man auf betriebssystemspezifische sleep-Funktionen verzichten 
kann.

von Sven B. (scummos)


Lesenswert?

Rolf M. schrieb:
> Sven B. schrieb:
>> Diese Antwort ist m.E. komplett wirr. Zum einen macht die Helper-Klasse
>> keinerlei Sinn, du kannst auch direkt QThread::sleep() schreiben statt
>> Sleeper::sleep() (???).
>
> Das geht wohl erst in neueren Qt-Versionen, da früher aus
> unverständlichen Gründen diese Funktionen protected waren und somit nur
> direkt aus abgeleiteten Klassen heraus und nicht von überalls aus
> aufrufbar waren.

Ok, das war mir nicht bewusst.

von Georg A. (georga)


Lesenswert?

Sven B. schrieb:
> Diese Antwort ist m.E. komplett wirr.

Tja, jedem seine Erfahrungen.

> Zum einen macht die Helper-Klasse
> keinerlei Sinn, du kannst auch direkt QThread::sleep() schreiben statt
> Sleeper::sleep() (???).

Die Antwort hast du ja schon bekommen... Und es ist nicht so super-hipp, 
das neueste Qt zu benutzen, wenn man "alte" Qt4-Anwendungen hat und der 
Kunde keine Lust hat, die Zeit für die Portierung zu bezahlen, auch wenn 
das an sich nicht so schlimm ist. Aber man muss das Zeug ja auch 
testen...

> Zum anderen ist ein sleep eben ein sleep, das
> macht i.d.R. einen Syscall der dem Betriebssystem sagt, dass jetzt ein
> anderer Thread geschedult werden soll und dieser hier nach n µs wieder
> aufgeweckt. Events werden während einem Sleep nie prozessiert und das
> hat auch mit der Qt-Version nichts zu tun.

Es gibt durchaus sinnvolle Beispiele, wo ein (non-GUI)Thread in seinem 
Ablauf einfach mal eine kurze(!) Zeit warten soll und das mit einer Art 
von sleep am schnellsten zu machen ist, statt den Ablauf mit 
Timer-Events komplett zu zerstückeln. Und das geht auch super ohne 
Nebenwirkungen. Ausser eben bei genau 4.8.6 (nicht bei 4.8.5 oder 5*) 
mit dem Windows-Kram. Fällt sonst überhaupt nicht auf, ausser jemand 
macht noch DDE-Broadcasts. In meinem Fall war das eine andere 
Nicht-Standard-Anwendung, die jedesmal beim Öffnen des Fileselektors 
kurz blockiert hat.

von Sven B. (scummos)


Lesenswert?

Georg A. schrieb:
> Die Antwort hast du ja schon bekommen... Und es ist nicht so super-hipp,
> das neueste Qt zu benutzen, wenn man "alte" Qt4-Anwendungen hat und der
> Kunde keine Lust hat, die Zeit für die Portierung zu bezahlen, auch wenn
> das an sich nicht so schlimm ist. Aber man muss das Zeug ja auch
> testen...
Naja, Dezember 2012 ist jetzt nicht gerade gestern. Aber ja.

> Es gibt durchaus sinnvolle Beispiele, wo ein (non-GUI)Thread in seinem
> Ablauf einfach mal eine kurze(!) Zeit warten soll und das mit einer Art
> von sleep am schnellsten zu machen ist, statt den Ablauf mit
> Timer-Events komplett zu zerstückeln. Und das geht auch super ohne
> Nebenwirkungen.
Außer dass eben der Thread seine Event-Loop nicht durchläuft. Und genau 
darum ging es ja in diesem Thema hier ...

von BobbyX (Gast)


Lesenswert?

Sven B. schrieb:
tt zu zerstückeln. Und das geht auch super ohne
>> Nebenwirkungen.
> Außer dass eben der Thread seine Event-Loop nicht durchläuft. Und genau
> darum ging es ja in diesem Thema hier ...

Ein nicht GUI Thread braucht normalerweise gar keinen Event Loop.... 
Enzige frage ist ob die Funktionen die in dem anderen Thrad laufen 
blocken oder nicht. Falls nicht ist sleep notwendig, sonst sind wir bei 
100% COU Last....

von BobbyX (Gast)


Lesenswert?

Heir mein Ansatz nochmal zum Mitschreiben:

#include <QWidget>
#include <QTimer>
#include <QLabel>
#include <QThread>

class MyThread: public QThread
{
     Q_OBJECT
public:
     MyThread(QObject *parent=0): QThread(parent) : someStatus(0) { }
     int getStatus() const { return someStatus; }
protected:
    void run()
    {
        while(1)
        {
           someStatus = CallStepOrWhatever();
           //Sleep if CallStepOrWhatever() does not block or whatever
           msleep(1000);
        }
    }
private:
    int someStatus;
}

class MyWidget: public QWidget
{
    Q_OBJECT
public:
    MyWidget(QWidget *parent=0): QWidget(parent)
    {
        mylabel=new QLabel(this);

        timer=new QTimer();
        connect(timer, SIGNAL(timeout()), this, 
SLOT(guiRefreshTimerSlot()));
        timer->start(100);

        theThread=new QThread(this);
        thread->start();

    }
private slots:
    void guiRefreshTimerSlot()
    {
        mylabel->setText( QString::number(theThread->getStatus()));
    }
private:
    QThread *theThread;
    QTimer *timer;
    QLabel *mylabel;
};

von Sven B. (scummos)


Lesenswert?

BobbyX schrieb:
> Sven B. schrieb:
> tt zu zerstückeln. Und das geht auch super ohne
>>> Nebenwirkungen.
>> Außer dass eben der Thread seine Event-Loop nicht durchläuft. Und genau
>> darum ging es ja in diesem Thema hier ...
>
> Ein nicht GUI Thread braucht normalerweise gar keinen Event Loop....

In Qt kommuniziert man normalerweise mit Signals & Slots mit Threads, 
und dann brauchen die eine Event Loop. Das macht auch Sinn.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.