QLabel kontinuierlich aktuelisieren

OP #4966970
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!!
#4966976
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.
#4967007
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.
OP #4967042
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.
#4967136
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;
> }
#4967411
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.
#4967437
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.
#4967537
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.
#4968037
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.
Gast #4968764
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... ;-)
#4972263
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.
#4972322
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.
#4972625
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.
#4972693
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.
#4973038
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 ...
Gast #4973183
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....
Gast #4973202
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;
};
#4973257
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren