Butons die über Zeit Relais ansteuern

Fragen zu Tkinter.
Antworten
Oggy
User
Beiträge: 3
Registriert: Donnerstag 20. September 2018, 14:27

Hallo würde mir gerne eine Weinmischmanschine bauen und Programmieren. Habe 2 Schlauchpumpen die über 2 Relais mit Zeit angesteuert sollen und so das Mischverhältnis hinbekommen sollen. Das ganze soll dann über ein Touchdisplay funk­ti­o­nie­ren.

Hier mal meine erste Versuche mit einem Programm.
Bitte nicht gleich Steinigen bin noch kein Nerd.

import tkinter
#import tkinter
import time
#import Time


def wein1():# 2 Relais über Zeit ansteuern
command=ende
def wein2():# 2 Relais über Zeit ansteuern
command=ende
def wasser1():# 2 Relais über Zeit ansteuern
command=ende
def wasser2():# 2 Relais über Zeit ansteuern
command=ende
def weinschorle1():# 2 Relais über Zeit ansteuern
command=ende
def weinschorle2():# 2 Relais über Zeit ansteuern
command=ende

def ende():#Defination ende
main.destroy()

main = tkinter.Tk()

main.wm_title("Weinschorlemischmaschine")#Fenstertitel

main.config(background = "#7fe31f")#Hintergrundfarbe


b1 = tkinter.Button(main, text="Wein 0,2",background="#2e5dae", command=wein1, height = 7, width = 15)
b1.place(relx=0.8, rely=0.2,anchor="n")
##e31f1f
b2 = tkinter.Button(main, text="Wein 0,4",background="#2e5dae", command=wein2, height = 7, width = 15)
b2.place(relx=0.8, rely=0.4,anchor="n")

b3 = tkinter.Button(main, text="Wasser 0,2",background="#2e5dae", command=wasser1, height = 7, width = 15)
b3.place(relx=0.1, rely=0.2,anchor="n")

b4 = tkinter.Button(main, text="Wasser 0,4",background="#2e5dae", command=wasser2, height = 7, width = 15)
b4.place(relx=0.1, rely=0.4,anchor="n")

b5 = tkinter.Button(main, text="Weinschorle 0,2",background="#2e5dae", command=weinschorle1, height = 7, width = 15)
b5.place(relx=0.5, rely=0.2, anchor= "n")

b6 = tkinter.Button(main, text="Weinschorle 0,4",background="#2e5dae", command=weinschorle2, height = 7, width = 15)
b6.place(relx=0.5, rely=0.4, anchor= "n")

b7 = tkinter.Button(main, text="Ende",background="#2e5dae", command=ende, height = 7, width = 15)
b7.place(relx=0.5, rely=0.6, anchor= "n")

b8 = tkinter.Button(main, text="Reinigung",background="#2e5dae", command=ende, height = 7, width = 15)
b8.place(relx=0.8, rely=0.6, anchor= "n")
main.mainlopp()
heiner88
User
Beiträge: 64
Registriert: Donnerstag 20. Oktober 2016, 07:29

Was ist jetzt deine Frage?
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Oggy: Du importierst `time`, nutzt das dann aber gar nicht. Falls Du das für `sleep()` verwenden wolltest: Das geht in GUI-Rückrufen nicht, weil die immer nur kurz etwas machen dürfen und so schnell wie möglich die Kontrolle an die GUI-Hauptschleife zurückgeben müssen von der sie aufgerufen wurden. Tun sie das nicht, blockiert die GUI.

Kommentare stehen in Python nicht unter, sondern über dem Code den sie kommentieren. Und Kommentare sollten auch etwas zu sagen haben. Faustregel: Kommentare beschreiben nicht *was* getan wird, denn das steht da ja bereits als Code, sondern *warum* der Code das (so) tut. Sofern das nicht offensichtlich ist. Die Kommentare im gezeigten Quelltext sind alle überflüssig.

Die Zuweisung an `command` in den vorhandenen Funktionen macht keinen Sinn. Man sollte auch nicht so viele sinnlose Dummy-Funktionen schreiben.

Einbuchstabige Namen sind selten gute Namen. Nummerierte Namen sind so gut wie nie gute Namen. Namen die aus einem Buchstaben + Durchnummerierung bestehen sind mit an sicherheit grenzender Wahrscheinlichkeit total besch…eiden. Ein Name soll dem Leser die Bedeutung des Wertes dahinter vermitteln, ohne das der gross rumrätseln muss das `b` für `button` steht und was der mit der Nummer 4 wohl auslösen mag.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Funktionen und Methoden sollten alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen. `ende()` darf beispielsweise nicht einfach so magisch auf ein `main` zugreifen (was man nicht so nennen sollte, denn so heisst die Funktion mit dem Hauptprogramm), sondern sollte das als Argument übergeben bekommen.

Damit ergibt sich bei jeder nicht-trivialen GUI, dass man objektorientierte Programmierung (OOP) braucht. Oder mindestens `functools.partial()` – womit man aber nur begrenzt weit kommt, bis man dann doch OOP *braucht*.

`ende` braucht man hier aber auch nicht wirklich, weil man direkt `main.destroy` als `command`-Argument übergeben kann.

Da in dem Programm bisher keines der `Button`-Objekte wirklich noch gebraucht wird, braucht man denen auch keine Namen geben.

Bein zeigen von Programmen wäre es auch gut wenn die funktionieren würden. `mainlopp()` gibt's nicht, das heisst `mainloop()`.

`place()` ist eine blöde Idee – auch mit relativen Angaben, weil das nicht das Problem mit dem benötigten Platz löst. Das Programm sieht nach dem Start bei mir so aus:
Bild

Ich muss das erst manuell auf folgende Grösse ziehen, damit alle Schaltflächen sichtbar sind:
Bild
Und diese Grösse gilt erst einmal auch nur für mein System, mit meiner Monitorauflösung und meinen Systemeinstellungen.

Wenn man Anzeigeelemente in einem Gitter anordnen möchte, dann nimmt man `grid()` und nicht `place()`

Man erkennt auf dem Bild auch ein Muster, das sich nicht im Code wiederspiegelt. Nämlich das die Zeilen für die Gesamtmenge stehen und die Spalten für das Mischverhältnis. Da braucht man letztlich also auch nicht für jede Schaltfläche eine eigene Funktion, sondern nur *eine* die Menge und Verhältnis als Argument(e) übergeben bekommt. Und die kann man dann in zwei verschachtelten Schleifen erstellen – die äussere über die Gesamtmenge und die innere über Namen und Mischverhältnisse.

Die allen Schaltflächen gemeinsamen Optionen sollte man nicht jedes mal neu hinschreiben sondern in einem Wörterbuch zusammenfassen.

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
from functools import partial


def main():
    root = tk.Tk()
    root.title('Weinschorlemischmaschine')
    root.config(background='#7fe31f')

    common_arguments = {'background': '#2e5dae', 'width': 15, 'height': 7}
    for row, amount in enumerate([0.2, 0.4]):
        for column, (name, water_ratio) in enumerate(
            [('Wasser', 1), ('Weinschorle', 0.5), ('Wein', 0)]
        ):
            tk.Button(
                root,
                text=f'{name} {str(amount).replace(".", ",")}',
                command=partial(
                    print,
                    f'Mische {amount}L {name}'
                    f' mit {water_ratio * amount}L Wasser'
                    f' und {(1 - water_ratio) * amount}L Wein.'
                ),
                **common_arguments
            ).grid(row=row, column=column)

    tk.Button(
        root, text='Ende', command=root.destroy, **common_arguments
    ).grid(row=row+1, column=1)
    
    tk.Button(
        root, text='Reinigung', command=root.destroy, **common_arguments
    ).grid(row=row+1, column=2)
    
    root.mainloop()


if __name__ == '__main__':
    main()
Für eine sinnvolle GUI muss man sich dann aber wohl doch alle `Button`-Objekte in einer Liste merken, denn wenn man eine Mischung zubereiten lässt, müssen die Schaltflächen ja deaktiviert und am Ende wieder aktiviert werden, damit man nicht während einer Mischung eine weitere anstossen kann. Eventuell möchte man den Vorgang auch abbrechen können.

Du gehst das IMHO aber auch von der falschen Seite an. Ich würde erst einmal die Geschäftslogik implementieren und testen, also den Teil der unabhängig von der GUI ist. Und wenn der läuft, dann die GUI da drauf setzen.

Ich habe eben tatsächlich auch mal wieder etwas gelernt: f-Zeichenkettenliterale bilden ein Closure über die verwendeten Namen. Cool. 😀
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten