Tipps für Programmaufbau

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
knautschkissen
User
Beiträge: 7
Registriert: Montag 23. September 2019, 20:57

Hallo Leute
ich Programmiere zur Zeit eine Fernsteuerunng für ein voll funktionsfähiges Modell.
Ich habe eine Klasse welche die Grafische Oberfläche beinhaltet und die "Signals" der Buttons, Slider etc. beherbergt,
eine Klasse welche das mathematische Modell beinhaltet und eine Klasse welche sich um das Protokoll und die 2 Wege Kommunikation mit dem Physischen Modell kümmert.
der Übersicht halber alles einzelne .py dateien.

Ich stehe allerdings gerade auf dem schlauch wie ich jetzt alles "Zusammen baue"
mit Threading habe ich mich schon auseinander gesetz und denke dass ich garnicht drum herum komme das einzusetzen.
Meine erste Idee war Sternförmig aus dem Mainprogram heraus die Gui, die Simulation und die Kommunikation zu starten wobei die Simulation und die Kommunikation in extra threads laufen. Das Main Programm sollte die Events und Analogen Werte von der Gui abfragen, an die Simulation übergeben, und die Ergebnisse dann an die Kommunikation zum übermitteln weiter geben. Schließlich die Empfangenen Daten vom Modell wieder an die Gui übergibt zum darstellen.
Problem... Die Main läuft bis "app.exec_()" Damit läuft die Gui aber alles andere wird erst ausgeführt wenn die Gui geschlossen wird bzw. hat nur einen Programmdurchlauf bis zu dem Punkt.
Ich habe gelesen man soll die Gui im Mainthread laufen lassen damit die funktioniert...

Jetzt stehe ich wie bereits erwähnt erstmal auf dem schlauch...
Wie baut man am besten ein solchen Programm auf?

Ich würde ungerne alles in die Gui klasse integrieren und daraus aufrufen um die einzelnen Programmteile schön getrennt behandeln zu können.

Über Tipps und vll sogar Literatur/Tutorial Hinweise würde ich mich sehr freuen.
Danke schonmal.

ps. Das Programm ist im Prinzip "nur" die Umsetzung meiner auf einem Arduino geschriebenen Fernbedienung welche aber nicht sonderlich Benutzerfreundlich ist Deshalb will ich das ganze mit einem Raspi und Touchscreen umsetzen. Dachte mir kann ja nicht soo schwer sein... Pustekuchen :( die einzelnen Klassen für sich funktionieren soweit schonmal.
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

So wie du das beschreibst, passt das ales von der Theorie her. Wenn es in der Praxis nicht funkrioniert, dann gibt es Fehler im Code, den wir nicht kennen. Daher zeig her das Program.
knautschkissen
User
Beiträge: 7
Registriert: Montag 23. September 2019, 20:57

Hallo..
sorry dass ich jetzt erst antworte... musste kurzfristig auf Dienstreise und dort hab ich meinen stick wo alles drauf war verloren... :evil:

naja bin wieder da und hab versucht neu anzufangen. momentan ist das programm mehr ein test, wie ich alles zum funktionieren bekomme. UART und Simulation ist noch nicht mit drin aber wenn ich einmal weiß wie es geht kann ich das entsprechend übertragen.
Tipps zum Organisieren des Programms nehme ich sehr gerne entgegen. Momentane Hauptfrage betrifft die Queus die ich zur Kommunikation nutzen will. über

Code: Alles auswählen

try:
	queue.get()
except:
	none

die Abfrage zu machen ohne zu schauen ob was im queue ist funktioniert zwar, ist aber bestimmt nicht im sinne des Erfinders...
wie macht man das denn richtig?

und (der gedanke kommt mir jetzt gerade) könnte ich ein array oder eine struktur als queue übergeben? Wenn das geht muss ich nicht warten bis 3 queues eingegangen sind und schauen ob die in der reihenfolge sinn ergeben.

und ich muss noch einen weg finden, wie die threads sauber beendet werden wenn ich die Gui beende.

hier noch meine derzeitiger test des Programmcodes.

Code: Alles auswählen

from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal
from threading import Thread
from queue import Queue
import sys
import GUI
import time

class Gui (QtWidgets.QMainWindow, GUI.Ui_Form):
    
    def __init__(self,Befehl,Value):
        self.Befehl=Befehl
        self.Value=Value
        super(self.__class__, self).__init__()
        self.setupUi(self)
        self.M_Azi_Left.clicked.connect(self.M_Azi_Left_clicked)
        self.M_Azi_Right.clicked.connect(self.M_Azi_Right_clicked)
        self.M_Azi_Zero.clicked.connect(self.M_Azi_Zero_clicked)
        self.M_Pitch_Up.clicked.connect(self.M_Pitch_Up_clicked)
        self.M_Pitch_Down.clicked.connect(self.M_Pitch_Down_clicked)
        self.M_Pitch_Zero.clicked.connect(self.M_Pitch_Zero_clicked)
        self.Azi_MinMax_Fast_Low.clicked.connect(self.Azi_MinMax_Fast_Low_clicked)
        self.Azi_MinMax_Low.clicked.connect(self.Azi_MinMax_Low_clicked)
        self.Azi_MinMax_Fast_Up.clicked.connect(self.Azi_MinMax_Fast_Up_clicked)
        self.Azi_MinMax_Up.clicked.connect(self.Azi_MinMax_Up_clicked)
        self.RotSpeed_Min_Fast_Low.clicked.connect(self.RotSpeed_Min_Fast_Low_clicked)
        self.RotSpeed_Min_Low.clicked.connect(self.RotSpeed_Min_Low_clicked)
        self.RotSpeed_Min_Fast_Up.clicked.connect(self.RotSpeed_Min_Fast_Up_clicked)
        self.RotSpeed_Min_Up.clicked.connect(self.RotSpeed_Min_Up_clicked)
        self.RotSpeed_Max_Fast_Low.clicked.connect(self.RotSpeed_Max_Fast_Low_clicked)
        self.RotSpeed_Max_Low.clicked.connect(self.RotSpeed_Max_Low_clicked)
        self.RotSpeed_Max_Fast_Up.clicked.connect(self.RotSpeed_Max_Fast_Up_clicked)
        self.RotSpeed_Max_Up.clicked.connect(self.RotSpeed_Max_Up_clicked)
        self.Btn_Start.clicked.connect(self.Btn_Start_clicked)
        self.Btn_Stop.clicked.connect(self.Btn_Stop_clicked)
        
        self.ScrollBar_Pitch.valueChanged.connect(self.ScrollBar_Pitch_value)
        self.ScrollBar_Azi.valueChanged.connect(self.ScrollBar_Azi_value)
        self.ScrollBar_Speed.valueChanged.connect(self.ScrollBar_Speed_value)
        
        self.tabWidget.currentChanged.connect(self.tabWidget_changed)
        

    def TuWas (self): 
        print ("hab was getahn")
        self.lcdNumber_Azi_MinMax.display(self.ScrollBar_Pitch.value())
    def M_Azi_Left_clicked (self): 
        self.Befehl.put("M_Azi_Left_clicked")
    def M_Azi_Right_clicked (self): 
        self.Befehl.put("M_Azi_Right_clicked")
    def M_Azi_Zero_clicked (self): 
        self.Befehl.put("M_Azi_Zero_clicked")
    
    def M_Pitch_Up_clicked (self): 
        self.Befehl.put("M_Pitch_Up_clicked")
    def M_Pitch_Down_clicked (self): 
        self.Befehl.put("M_Pitch_Down_clicked")
    def M_Pitch_Zero_clicked (self): 
        self.Befehl.put("M_Pitch_Zero_clicked")
    
    def Azi_MinMax_Fast_Low_clicked (self): 
        self.Befehl.put("Azi_MinMax_Fast_Low_clicked")
    def Azi_MinMax_Low_clicked (self): 
        self.Befehl.put("Azi_MinMax_Low_clicked")
    def Azi_MinMax_Fast_Up_clicked (self):
        self.Befehl.put("Azi_MinMax_Fast_Up_clicked")    
    def Azi_MinMax_Up_clicked (self): 
        self.Befehl.put("Azi_MinMax_Up_clicked")
    
    def RotSpeed_Min_Fast_Low_clicked (self): 
        self.Befehl.put("RotSpeed_Min_Fast_Low_clicked")
    def RotSpeed_Min_Low_clicked (self): 
        self.Befehl.put("RotSpeed_Min_Low_clicked")    
    def RotSpeed_Min_Fast_Up_clicked (self): 
        self.Befehl.put("RotSpeed_Min_Fast_Up_clicked")    
    def RotSpeed_Min_Up_clicked (self): 
        self.Befehl.put("RotSpeed_Min_Up_clicked")
    
    def RotSpeed_Max_Fast_Low_clicked (self):   
        self.Befehl.put("RotSpeed_Max_Fast_Low_clicked")
    def RotSpeed_Max_Low_clicked (self):   
        self.Befehl.put("RotSpeed_Max_Low_clicked")
    def RotSpeed_Max_Fast_Up_clicked (self):  
        self.Befehl.put("RotSpeed_Max_Fast_Up_clicked")
    def RotSpeed_Max_Up_clicked (self): 
        self.Befehl.put("RotSpeed_Max_Up_clicked")
    
    def Btn_Start_clicked (self): 
        self.Befehl.put("Btn_Start_clicked")
    def Btn_Stop_clicked (self):
        self.Befehl.put("Btn_Stop_clicked")
    
    def ScrollBar_Pitch_value (self):   
        self.Befehl.put("ScrollBar_Pitch_value")
        self.Value.put(self.ScrollBar_Pitch.value())
    def ScrollBar_Azi_value (self):  
        self.Befehl.put("ScrollBar_Azi_value")
        self.Value.put(self.ScrollBar_Azi.value())    
    def ScrollBar_Speed_value (self): 
        self.Befehl.put("ScrollBar_Speed_value")
        self.Value.put(self.ScrollBar_Speed.value())
    
    def tabWidget_changed (self):
        self.Befehl.put("tabWidget_changed")
        
    
    

class Task (Thread):

    def __init__(self,Addresse,Befehl,Value):
        self.Addresse=Addresse
        self.Befehl=Befehl
        self.Value=Value
        self.dauer500=500
        self.jetzt=0
        self.nächste=0
        
        super(Task, self).__init__()
        
    def zeitstempel(self,jetzt, dauer):
        if self.jetzt >= self.nächste:
            self.nächste=self.jetzt+self.dauer
            return (True)
        else:
            return (False)
        
    def run(self):
        self.task500ms=self.zeitstempel
        self.dauer=500
        self.jetzt=0
        while 1:
            self.jetzt = time.time_ns()/1000000
            if self.task500ms(jetzt = self.jetzt, dauer=self.dauer):
                print("500ms sind vergangen")
                try:
                    print( self.Befehl.get(block=False, timeout=1))
                    print( self.Value.get(block=False, timeout=1))
                except:
                    None
        

def main():
    q_Adresse=Queue()
    q_Befehl=Queue()
    q_Value=Queue()
    tasks=Task(q_Adresse,q_Befehl,q_Value)
    tasks.start()
 
    q_Adresse.put("putAdresse")
    q_Befehl.put("putBefehl")
    q_Value.put("putValue")   
    
    
    app = QtWidgets.QApplication(sys.argv)
    form = Gui(q_Befehl,q_Value)
    form.show()
    
    app.exec_()

    
    
    
if __name__ == '__main__':
    main()
    
        
    
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da ist eine Menge im Argen. Fangen wir mal mit ein paar offensichtlichen Dingen an:

- den Wert einer Variable in ihren Namen zu kodieren habe ich glaube bis dato noch nicht gesehen. Ausser in "The daily WTF" wo so Perlen wie #define 3 4 vorkamen. dauer500 sollte einfach dauer sein, sonst musst du das ueberall umbenennen wenn's mal zu 450 wird.
- man macht keine busy-loops um Zeit zu verballern. Man kann sleep aufrufen, oder einen Timer aufsetzen. Und da Python keine echte Parallelitaet kann, wird dich das auch deutlich Performance kosten, das so zu machen, denn deine GUI wird dann ruckelig trotz Threading.
- ich sehen noch nicht, warum du Threads brauchst. Sofern das Programm nicht unter Windows laeuft. Kann ich nicht erkennen. Wenn nicht, dann ist es voellig problemlos moeglich, mit Timern und QSocketNotifier zu arbeiten, um Modell und Kommunikation ueber UART abzuhandeln. Wenn das Windows ist, wuerde ich auch erstmal NUR die Kommunikation in einen Therad auslagern (bzw. nur das lesen). Alles andere immer noch Timer-getrieben machen.
- das loest dann auch das Problem, einen ganzen Zoo von Threads zu haben, deren Ergebnisse dann aber eine intrinsische Abhaengigkeit haben, die du erstmal wieder kuenstlich herstellen musst. So wie du das als Problem beschreibst.
- bitte mach dich mal mit den Namenskonventionen in PEP8 vertraut. Da steht, wie Klassen, attribute_und_variablen, KONNSTANTEN etc. bennannt werden. Es ist verwirrend, wenn das so wie bei dir komplett anders und durcheinander ist. Python kennt keine Typen, so dass dem Verstaendnis sehr zutraeglich ist, sich da an die Konventionen zu halten.
- return ist keine Funktion. return(...) ist also ueberfluessig, return ... reicht.
- wenn du mit Qt arbeitest, und auf Threading setzen musst (was ich wie gesagt in Frage stelle, aber falls doch) - dann ist es SEHR ratsam, QThread zu benutzen. Damit sich die Multi-Threading-Mechanismen von Qt nutzen lassen. Wie zB queued connections.
knautschkissen
User
Beiträge: 7
Registriert: Montag 23. September 2019, 20:57

Oh danke für die schnelle Antwort.
mit der Namenskonvention habe ich schon öffters auch in anderen foren gelesen und hab mir vorgenommen das mal zu übernehmen.
-mit dauer500 ist erstmal nur als test. damit ich gleich weiß welche dauer bzw. alle wie viel s der thread etwas tun soll. Im fertigen Programm hab ich wahrscheinlich 3 verschiedene. einen 200ms einen 500ms und einen 2s task.
Laufen soll das Programm wenn es fertig ist auf einem Raspberry mit (so hatte ich das zumundest geplant) 3 Threads. Dem Main thread in dem die Gui läuft, einer Simulation die ein mathematisches Modell enthält und anhand der Grafischen Oberfläche oder der gemessenen Daten die Steuerbefehle für das Modell erzeugt. Als 3. Thread hatte ich die Kommunikation geplant.
Und ja die Grafische Oberfläche ist mit dem QTdesigner erstellt.
Momentan besteht es nur aus ein paar buttons und slidern. Will aber später wenn ich das hinbekomme eine Animation des Modells mit einfügen.
ich werd mich jedenfalls erstmal anlesen wie das mit den vorgeschlagenen Timern und QSocketNotifier funktioniert.
Benutzeravatar
__blackjack__
User
Beiträge: 14047
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@knautschkissen: In Queues kannst Du beliebige Objekte tun, diese Aufteilung auf drei Queues scheint also nicht sinnvoll zu sein.

Nackte ``except``\s ohne konkrete Ausnahemen anzugeben ist eine ganz schlechte Idee. Du erwartest da nur ein `queue.Empty` bei der nicht-blockierenden Variante, und auch nur das sollte dann durch totales ignorieren behandelt werden, falls das eine sinnvolle Behandlung dafür ist. Alle anderen Ausnahmen sollten weiterhin ganz normale Folgen haben. Sonst macht man sich die Fehlersuche nur unnötig schwer.

Wenn man in einem Zweig nichts machen will, da aber aus syntaktischen Gründen etwas stehen muss, verwendet man das Schlüsselwort ``pass`` und nicht irgendeinen Wert. Ich würde da auch immer einen Kommentar dran schreiben warum das okay ist da nichts zu tun, oder zumindest deutlich machen, dass das Absicht ist, und da nicht einfach nur Code fehlt der noch nicht geschrieben wurde.

`QtGui`, `QtCore`, `QThread` und `pyqtSignal` werden importiert, aber nirgends verwendet.

Code generieren ist eigentlich nicht mehr nötig/üblich, man kann die `*.ui`-Datei die der Qt-Designer speichert zur Laufzeit mit Hilfe des `PyQt5.uic`-Moduls laden.

`super()` braucht *gar keine* Argumente, aber auf jeden Fall eher nicht `self.__class__`, denn das funktioniert bei Vererbung nicht richtig.

Namenskonvention bei Python ist klein_mit_unterstrichen für alles ausser Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Bibliotheken wie `PyQt5` verstossen dagegen weil die darunterliegende Bibliothek in einer anderen Programmiersprache geschrieben ist, und deshalb die Namen von dort übernommen wurden, statt alles umzubennenen, was arbeit macht, zu Fehlern/Problemen führen kann, und dann auch von der Qt-Dokumentation abweichen würde. Man kann sich dann überlegen ob man beim reinen GUI-Code auch diese Konvention übernimmt, aber `Befehl` wäre als Name für einen Wert in beiden Namenskonventionen falsch. Und man sollte kryptische Abkürzungen und Prä-/und Suffixe vermeiden. Also anstatt `q_Befehl` besser `befehls_queue` schreiben. Insbesondere kann das `q_` auch im Zusammenhang mit Qt beim Leser zu Verwirrung führen.

*Eine* Sprache bei den Namen wäre gut. Mir ist zum Beispiel nicht ersichtlich warum es ausgerechnet `befehl` und `value` heisst und nicht `command` und `value` oder `command` und `wert`.

Das mit dem Thread wird so nicht funktionieren. In einer Schleife dauerhaft den Prozessor damit zu beschäftigen auf einen Zeitpunkt zu warten ist auch keine gute Idee. Neben dem unnötigen Verbrauch von Rechenzeit ist das auch sehr unpräzise. Zudem gibt es keine `time_ns()`-Funktion.

Man sollte keine Zahlen als literale Wahrheitswerte missbrauchen. Python hat dafür extra `True` und `False`.

Bei `Queue.get()` die Argumente `block` *und* `timeout` anzugeben ist überflüssig. Wenn man einen Wert für die Zeitüberschreitung übergibt, dann macht es keinen Sinn `block` auf `False` zu setzen. Oder eben umgekehrt, wenn man den Aufruf nicht blockierend haven will, macht es keinen Sinn zu sagen wie lange maximal blockiert werden soll.

Wenn man alles aus der Klasse raus wirft was keinen Sinn macht, bleibt die `__init__()` wo die Queue übergeben wird, und die `run()`. Also eigentlich nicht mehr wirklich eine Klasse, sondern eine Funktion.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
from functools import partial
from queue import Empty, Queue
import sys
from threading import Thread

from attr import attrib, attrs
from PyQt5 import QtWidgets
import GUI


@attrs(frozen=True)
class Command:
    command = attrib()
    value = attrib(default=None)
    address = attrib(default=None)


class Gui(QtWidgets.QMainWindow, GUI.Ui_Form):
    def __init__(self, command_queue):
        super().__init__()
        self.setupUi(self)
        self.command_queue = command_queue

        self.M_Azi_Left.clicked.connect(
            partial(self.put_command, "M_Azi_Left_clicked")
        )
        self.M_Azi_Right.clicked.connect(
            partial(self.put_command, "M_Azi_Right_clicked")
        )
        self.M_Azi_Zero.clicked.connect(
            partial(self.put_command, "M_Azi_Zero_clicked")
        )
        self.M_Pitch_Up.clicked.connect(
            partial(self.put_command, "M_Pitch_Up_clicked")
        )
        self.M_Pitch_Down.clicked.connect(
            partial(self.put_command, "M_Pitch_Down_clicked")
        )
        self.M_Pitch_Zero.clicked.connect(
            partial(self.put_command, "M_Pitch_Zero_clicked")
        )
        self.Azi_MinMax_Fast_Low.clicked.connect(
            partial(self.put_command, "Azi_MinMax_Fast_Low_clicked")
        )
        self.Azi_MinMax_Low.clicked.connect(
            partial(self.put_command, "Azi_MinMax_Low_clicked")
        )
        self.Azi_MinMax_Fast_Up.clicked.connect(
            partial(self.put_command, "Azi_MinMax_Fast_Up_clicked")
        )
        self.Azi_MinMax_Up.clicked.connect(
            partial(self.put_command, "Azi_MinMax_Up_clicked")
        )
        self.RotSpeed_Min_Fast_Low.clicked.connect(
            partial(self.put_command, "RotSpeed_Min_Fast_Low_clicked")
        )
        self.RotSpeed_Min_Low.clicked.connect(
            partial(self.put_command, "RotSpeed_Min_Low_clicked")
        )
        self.RotSpeed_Min_Fast_Up.clicked.connect(
            partial(self.put_command, "RotSpeed_Min_Fast_Up_clicked")
        )
        self.RotSpeed_Min_Up.clicked.connect(
            partial(self.put_command, "RotSpeed_Min_Up_clicked")
        )
        self.RotSpeed_Max_Fast_Low.clicked.connect(
            partial(self.put_command, "RotSpeed_Max_Fast_Low_clicked")
        )
        self.RotSpeed_Max_Low.clicked.connect(
            partial(self.put_command, "RotSpeed_Max_Low_clicked")
        )
        self.RotSpeed_Max_Fast_Up.clicked.connect(
            partial(self.put_command, "RotSpeed_Max_Fast_Up_clicked")
        )
        self.RotSpeed_Max_Up.clicked.connect(
            partial(self.put_command, "RotSpeed_Max_Up_clicked")
        )

        self.Btn_Start.clicked.connect(
            partial(self.put_command, "Btn_Start_clicked")
        )
        self.Btn_Stop.clicked.connect(
            partial(self.put_command, "Btn_Stop_clicked")
        )

        self.ScrollBar_Pitch.valueChanged.connect(
            partial(self._put_command, "ScrollBar_Pitch_value")
        )
        self.ScrollBar_Azi.valueChanged.connect(
            partial(self._put_command, "ScrollBar_Azi_value")
        )
        self.ScrollBar_Speed.valueChanged.connect(
            partial(self._put_command, "ScrollBar_Speed_value")
        )

        self.tabWidget.currentChanged.connect(
            partial(self.put_command, "tabWidget_changed")
        )

    def do_something(self):
        print("hab was getan")
        self.lcdNumber_Azi_MinMax.display(self.ScrollBar_Pitch.value())

    def put_command(self, command, value=None, address=None):
        self.command_queue.put(Command(command, value, address))


def process_commands(command_queue):
    while True:
        try:
            try:
                print(command_queue.get(timeout=0.5))
            finally:
                command_queue.task_done()
        except Empty:
            print("500ms sind vergangen")


def main():
    command_queue = Queue()

    command_thread = Thread(
        target=process_commands, args=[command_queue], daemon=True
    )
    command_thread.start()

    command_queue.put(Command("putBefehl", "putValuev", "putAdresse"))

    app = QtWidgets.QApplication(sys.argv)
    gui = Gui(command_queue)
    gui.show()
    app.exec_()
    
    command_thread.join()


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es ist unerheblich ob es "erstmal nur als Test" ist oder nicht. Genauso wenig wie man in CSS eine Klasse "rot" nennt, sondern semantisch zB "alarm", ist es nicht sinnvoll, einem Namen den Wert einzubacken. Das ist dann halt die "dauer_simulationsschritt" oder was auch immer. Welchen *wert* die dann hat, ist zweitrangig.

Und ich stelle nochmal in Frage, dass deine Thread-Planung richtig ist. Jeder Thread den man hat ist einer zu viel. Das ist gerade im PI-Umfeld oft ohne Ahnung die Antwort auf alle Fragen - bis es dann scheppernd kracht. Man muss bei mulit-threading wirklich aufpassen, es nicht zu verbocken, und wenn man sich einen Thread sparen kann, ist das sehr, sehr viel wert. Mit generatoren und asyncio hat Python auch andere Loesungsmoeglichkeiten, die es erlauben, den linearen Code eines Teilmoduls beizubehalten, ohne auf Threading zurueckfallen zu muessen. Und da du unter Linux unterwegs bist, ist zB fuer den UART auch kein Threading notwendig.
knautschkissen
User
Beiträge: 7
Registriert: Montag 23. September 2019, 20:57

Hallo
Danke für euren Input

eine Frage,

Code: Alles auswählen

 self.M_Azi_Right.clicked.connect(
            partial(self.put_command, "M_Azi_Right_clicked")
        )
        
tut das partial() quasi das gleiche wie

Code: Alles auswählen

 self.M_Azi_Right.clicked.connect(self.Befehl.put("M_Azi_Right_clicked"))
tun würde nur dass das nicht funktioniert?

...programmieren macht mir spaß aber meine versuche es mir selbst durch Youtubevideos und in Foren lesen beizubringen sind zunehmend frustrierend... am ende stell ich immer dumme fragen und nerve euch damit^^
könnt ihr mir ein Buch am besten auf Deutsch empfehlen in dem man das alles mal ordentlich lernen und üben kann? Eines dass sich nicht nur auf die richtige syntax in schleifen und wie man etwas in eine datenbank schreibt und liest usw beschränkt. Ich hoffe ihr wisst was ich meine.
Benutzeravatar
__blackjack__
User
Beiträge: 14047
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@knautschkissen: `connect()` muss man etwas aufrufbares übergeben was dann aufgerufen werden kann wenn wie in dem gezeigten Beispiel die Schaltfläche geklickt wurde. Wenn Du da ``self.Befehl.put("M_Azi_Right_clicked")`` übergibst, dann wird das ja *einmal* und *vor* dem `connect()`-Aufruf ausgeführt und bei jedem Klick wird dann versucht den Rückgabewert von `Queue.put()` auszuführen, nur das der `None` ist und man `None` gar nicht aufrufen kann.

`partial()` übergibt man etwas aufrufbares und dann Argumente mit denen das später aufgerufen werden soll wenn man den Rückgabewert von `partial()` aufruft. Das ist funktionale Programmierung. Da sind Funktionen auch Werte und die können an andere Funktionen übergeben werden und es kann auch Funktionen als Ergebnis/Rückgabewert von Aufrufen geben.

Code: Alles auswählen

In [1]: from functools import partial                                           

In [2]: f = partial(print, "Hallo")                                             

In [3]: f                                                                       
Out[3]: functools.partial(<built-in function print>, 'Hallo')

In [4]: f()  # äquivalent zu ``print("Hallo")``                                                    
Hallo

In [5]: f("Welt")  # äquivalent zu ``print("Hallo", "Welt")``
Hallo Welt
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten