Funktion ständig aufrufen trotz app.exec_()

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Wolflkoder
User
Beiträge: 28
Registriert: Sonntag 7. Dezember 2014, 15:57

Hallo,


ich frage in einer Funktion einen PIN vom Raspberry ab, dessen Zustand ist verantwortlich für eine "INFO BOX" Ausschreibung in QT4.

leider wird meine Funktion nur einmal aufgerufen, da am Ende meines Codes app.exec_() steht.

durch app.exec() wird anscheinend jegliche Schleife ignoriert.

wie kann ich dieses Problem am besten lösen?

Wer kann mir hier einen Lösungsansatz geben ???

wäre es möglich mit Threads zu arbeiten ?

( _thread.start_new_thread)

oder gibst einen slot im QT4 der mir eine Funktion startet, wenn nix geklickt wird ?

Danke Wolflkoder
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@Wolflkoder: Ja, das geht mit Threads. Allerdings solltest du nicht das _thread-Modul verwenden. Dessen Name beiginnt mit einem Unterstrich, was in Python bedeutet: Implementierungsdetail, bitte nur verwenden, wenn du genau weißt, was du tust. Wenn man das Verzeichnis der Python Standard Library von oben nach unten durchgeht, findet man das threading-Modul vor dem _thread-Modul. Ich kann nur annehmen, dass noobs gerne von unten nach oben lesen, denn Fragen nach dem _thread-Modul werden eigentlich nur von noobs gestellt. Oder vielleicht denken sich noobs "Ah, da steht 'Low-level threading API', das ist sicher einfacher zu verwenden als threading, weil ich mich bei _thread um den ganzen low-level-Kram selber kümmern darf. Yippie."

Oder, weil du ja ohnehin QT benutzt, verwende gleich QThreads.
In specifications, Murphy's Law supersedes Ohm's.
BlackJack

@pillmuncher: So ein gewisses OpenBook vom (ehemals) Galileo-Verlag hat für die erste Auflage (Python 2) das `thread`-Modul verwendet und für die Python 3-Auflage (Papierform) die Texte und Quelltexte dann nicht auf `threading` geändert sondern einfach nur den Unterstrich vor `thread` geschrieben ohne sich darum zu kümmern was das eigentlich bedeutet.

@Wolflkoder: Bei Threads muss man darauf achten das die GUI nur aus dem Hauptthread manipuliert werden darf. Signale und Threads sind allerdings thread-sicher und Qt hat eine eigene `QThread`-Klasse.
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@BlackJack: Nur gut, dass es das Python Forum gibt, in dem wir den Leuten diesen Unsinn wieder austreiben. Wir haben ja sonst nichts zu tun...
In specifications, Murphy's Law supersedes Ohm's.
Wolflkoder
User
Beiträge: 28
Registriert: Sonntag 7. Dezember 2014, 15:57

Hab jetzt mal bisschen probiert, und steh nun vor folgendem Problem.

Wenn ich meinen Code ausführe, dann erscheint mir die GUI, allerdings nach den 5 Sekunden (am Anfang der While schleife) verschwindet diese wieder.

Code: Alles auswählen

#!/usr/bin/env python3
from time import sleep                 
import urllib
import urllib.request
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.uic import *
import os
import threading

app = QApplication(sys.argv)   
a = loadUi("anfahren.ui")



def S1a():
        print ("Vorlauf")         
def S2a():
        print("Rücklauf")
        
class parallel(threading.Thread):
       
        def run(self):

                while 1:
                        sleep(5)
                        os.system("gpio mode 22 in")
                        wert = os.popen('gpio read 22',"r")
                        line = wert.readline()
                        str1 = (line)
                        str2 = "1"
                        sicherheit = str1.find(str2);
                        print (sicherheit)
                        if sicherheit == 0:
                                a.info.setText ("Freigabe erhalten, neue Eingabe möglich")
                                print ("Wert = 0")
                        if sicherheit != 0:
                                a.info.setText ("keine Freigabe, keine Eingabe möglich")
                                print ("Wert = 1")
                        print ("Freigabe ausgeführt")
                        
    


 
           
# Buttons
a.connect(a.S1a, SIGNAL("clicked()"),S1a)
a.connect(a.S2a, SIGNAL("clicked()"),S2a)


thread1 = parallel()
thread1.start()
a.show()
app.exec_()
Wolflkoder
User
Beiträge: 28
Registriert: Sonntag 7. Dezember 2014, 15:57

Hab den Fehler gefunden,

liegt an

Code: Alles auswählen

a.info.setText ("Freigabe erhalten, neue Eingabe möglich")
aber wie schaff ich es nun, den ermittelten Wert in der Variable sicherheit zu verarbeiten ohne daß mir das Programm abstürzt?
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Wolfcoder:
Du darfst im Subthread nicht auf GUI-Elementen agieren. In Qt ist die GUI zwingend an den Hauptthread gebunden, Zugriffe aus anderen Threads sind nicht sicher. Du musst Dir einen Benachrichtigungsmechanismus zwischen den Threads überlegen und die anzuzeigenden Daten im Hauptthread an die GUI übergeben. Dafür gibts in Python als auch in Qt fertige Lösungen. Damit Du diese nutzen kannst, musst Du aber zunächst Deinen Code aufräumen und besser (ereignisgesteuert) strukturieren. Das Dein Hauptwidget 'a' als globale Variable rumfliegt, ist mehr als dirty.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Wolflkoder: wie schon im zweiten Beitrag erwähnt, darf ein Thread die GUI nicht verändern. Aber dafür bietet Dir Qt ja das Signal-Slot-Konzept an.

Zum Code:
Sternchenimporte sind zu vermeiden, da man keine Kontrolle hat, was da in den Namensraum geladen wird. Eingerückt wird mit 4 Leerzeichen pro Ebene. Auf Modulebene sollte kein Anweisungen stehen, die gehören alle in eine Funktion (main).

Wenn Du nur eine run-Methode hast, bietet sich das target-Argument von Thread an, damit man keine Klasse schreiben muß.

Python kennt True und False als Wahrheitswerte. Statt os.system und os.popen solltest Du die Funktionen aus subprocess nutzen.

Für was sind str1 und str2 da? Zu "if" gibt es auch noch "else".
Wolflkoder
User
Beiträge: 28
Registriert: Sonntag 7. Dezember 2014, 15:57

Erst mal Danke, das war viel INPUT

zu dem Signal-Slot-Konzept

besteht eine Möglichkeit dass ich diese "Abfrage" direkt mit einer Variable starten kann?
z.B. wenn sich eine Variable ändert?

Gedankengang:

a.connect(HIER MEINE VARIABLE, SIGNAL('valueChanged'),MEINE FUNKTION ???

oder ist dieses Signal-Slot-Konzept nur gedacht zwischen GUI und Programm zu kommunizieren?
BlackJack

@Wolflkoder: Nein, so direkt geht das nicht. Man könnte ein Property schreiben das bei einer Änderung des Wertes ein Signal auslöst.

Signale/Slots sind zur Kommunikation zwischen beliebigen (Qt)-Objekten gedacht. Also zumindest das Signal muss auf einer von `QObject` abgeleiteten Klasse definiert werden. Als Slot funktioniert in Python ja jedes aufraufbare Objekt, auch ohne dass das irgendwie als Slot definiert oder registriert werden müsste. Das kann man zwar machen, aber ich fand das bisher noch nicht nötig weil das bei meinen (wenigen) Qt-Oberflächen noch nie ein Flaschenhals war.
Wolflkoder
User
Beiträge: 28
Registriert: Sonntag 7. Dezember 2014, 15:57

bin hier jetzt langsam am verzweifeln, ich bekomme einfach die Variable nicht in die haupt Klasse

Code: Alles auswählen

#!/usr/bin/env python3
from time import sleep                 
import urllib
import urllib.request
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.uic import *
import os
import threading


def S1a():
        print ("Vorlauf")
                        
def S2a():
        print("Rücklauf")   



class haupt(threading.Thread):
    def run(sicherheit):
        while 1:
            app = QApplication(sys.argv)
            a = loadUi("anfahren.ui")
            a.show()
            a.connect(a.S1a, SIGNAL("clicked()"),S1a)
            a.connect(a.S2a, SIGNAL("clicked()"),S2a)                        

            print (sicherheit)
            if sicherheit == 0:
                a.info.setText ("Freigabe erhalten, neue Eingabe möglich")                       

            if sicherheit == -1:
                a.info.setText ("keine Freigabe, keine Eingabe möglich")

            else :
                a.info.setText ("?????????????????") 
            app.exec_()
       
                
                        
                
                                      
class parallel(threading.Thread):
    def run(sicherheit):
        while 1:
            sleep(1)
            os.system("gpio mode 22 in")
            wert = os.popen('gpio read 22',"r")
            line = wert.readline()
            str1 = (line)
            str2 = "1"
            sicherheit = str1.find(str2);
            print (sicherheit)
            if sicherheit == 0:                                
                print ("Wert = 0")
            if sicherheit == -1:
                print ("Wert = 1")                                                  
                        
                                        
                                                
thread1 = parallel()
thread1.start()
thread2 = haupt()
thread2.start()

wo muss ich ansetzen um den Inhalt der Variable "sicherheit" der Klasse parallel in die Klasse "haupt" zu bekommen?

Gruß

Wolflkoder
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Wolflkoder: hast Du irgendetwas von dem, was bisher geschrieben wurde, umgesetzt? Warum gibt es jetzt plötzlich einen "haupt"-Thread? Wie kommst Du auf die Idee, das einzige Argument von "run" "sicherheit" zu nennen?

Es bringt ja nichts, nochmal das selbe zu wiederholen. Was von dem was wir bisher geschrieben haben, hast Du versucht umzusetzen und was war Dir dabei noch unklar?
Antworten