Button Event in Programm zurück geben

Fragen zu Tkinter.
Antworten
DerSp83
User
Beiträge: 5
Registriert: Dienstag 19. April 2016, 09:10

Hi,

Mein Name ist Jan und ich bin neu hier.

Ich habe so eben mein Interesse für Python entdeckt und direkt mit einem kleinen Projekt begonnen.

Hänge allerdings gerade an einer Stelle die mit Sicherheit nicht schwer zu lösen ist nur komme ich wohl einfach nicht drauf.

Ich generiere einen Button über tkinter. Ich möchte das wenn dieser Button betätigt wird mir eine Information an mein im Hintergrund laufenden Programm weiter gegeben wird.
Beispiel:

Wenn ich den Button drücke soll z.B. ein True ans Programm übergene werden und erst dann soll etwas geschehen.

Kann mir vielleicht jemand einen Lösungsansatz bieten?

Gruß

Jan
BlackJack

@DerSp83: Wie hat man sich denn Dein im Hintergrund laufendes Programm vorzustellen? Ist das ein eigener Prozess? Oder läuft das in einem Thread? Kannst Du ein minimales, aber lauffähiges Beispiel zeigen, welches Dein Problem repräsentiert?

Objektorientierte Programmierung beherrschst Du schon? Und wie ereignisorientierte Programmierung bei GUIs funktioniert, ist auch klar?
DerSp83
User
Beiträge: 5
Registriert: Dienstag 19. April 2016, 09:10

Hi,

Also wie gesagt habe Interesse an Python muss also noch viel lernen.

Ich arbeite als Programmierer für CNC-Anlage (Logik-Programme) für Kunden Projekte.

Nur ist die CNC Seite etwas anders aufgebaut genauso wie die Fanuc Robotic Seite die wir verwenden.

CNC seitig würde ich es wohl so schreiben:

If Button == True
X = True

If x == True
G0 z100

Gruß

Jan
BlackJack

@DerSp83: Es ist nicht wirklich möglich Deine Frage zu beantworten ohne das konkrete Problem zu kennen. :-)

Die Rückfragen zu objektorientierter und ereignisorientierter Programmierung habe ich gestellt, weil man beides für GUI-Programmierung braucht.
DerSp83
User
Beiträge: 5
Registriert: Dienstag 19. April 2016, 09:10

Hi,

Also ich möchte einen Button haben.

Wenn ich diesen Drücke soll über einen Raspberry Pi ein Schrittmotor ausgelöst werden bzw eine Schrittkette.

Ist diese Schrittkette beendet so soll es möglich sein einen anderen Button zu drücken welcher eine andere Schrittkette auslöst.

Hoffe das ist die richtige Antwort auf deine Frage;-)

Achja Befasse mich zur Zeit mit einem Buch: Einstieg in Python ;-)

Nochmal von oben kopiert:
CNC seitig würde ich es wohl so schreiben:

If Button == True
X = True

If x == True
G0 z100

Gruß

Jan
DerSp83
User
Beiträge: 5
Registriert: Dienstag 19. April 2016, 09:10

Mein erste Versuch in Python also bitte nachsicht;-)

Code: Alles auswählen

from tkinter import *
x = 0
#Generieren eines Anzeigefensters
fenster = Tk()
fenster.title("Wählen Sie Ihr Getränk")
fenster.geometry("500x500")

                 
#Generieren von Buttons für die Getränkeauswahl
#if Getränk1da == True
Cola = Button (fenster, text="Cola",)
Cola.pack()
def cola (event):
    x = True
print(x)    
#if Getränk2da == True
Fanta = Button (fenster, text="Fanta")
Fanta.pack()
#if Getränk3da == True
MezzoMix = Button (fenster, text="MezzoMix")
MezzoMix.pack()
#if Getränk4da == True
ColaLight = Button (fenster, text="ColaLight")
ColaLight.pack()

Getränk1 = "Cola"
Getränk2 = "Fanta"
Getränk3 = "Mezzo mix"
Getränk4 = "Öttinger"
Getränk1da = True
Getränk2da = True
Getränk3da = False
Getränk4da = True
# Hier soll den einzelnen Buttons ein Wert zugewiesen werden
#welchen diesen Wert setzt und dem entsprechend das ereignis
#auslöst
Getränkeanwahl = 1


if Getränk1da == True and Getränkeanwahl == 1:
    print(Getränk1,"wird ausgegeben")
if Getränk1da == False and Getränkeanwahl == 1:
    print(Getränk1,"Steht zur zeit leider nicht zur verfügung. Bitte Auffüllen")


if Getränk2da == True and Getränkeanwahl == 2:
    print(Getränk2,"wird ausgegeben")
if Getränk2da == False and Getränkeanwahl == 2:
    print(Getränk2,"Steht zur zeit leider nicht zur verfügung. Bitte Auffüllen")

if Getränk3da == True and Getränkeanwahl == 3:
    print(Getränk3,"wird ausgegeben")
if Getränk3da == False and Getränkeanwahl == 3:
    print(Getränk3,"Steht zur zeit leider nicht zur verfügung. Bitte Auffüllen")


if Getränk4da == True and Getränkeanwahl == 4:
    print(Getränk4,"wird ausgegeben")
if Getränk4da == False and Getränkeanwahl == 4:
    print(Getränk4,"Steht zur zeit leider nicht zur verfügung. Bitte Auffüllen")

if cola == True:
    print(x)
    
mainloop()
Zuletzt geändert von Anonymous am Samstag 23. April 2016, 18:12, insgesamt 1-mal geändert.
Grund: Quelltext in Code-Tags gesetzt.
BlackJack

@DerSp83: Das sieht noch ziemlich weit von einem funktionierenden, sauberen Programm aus. Das Programm ist ja nicht einmal in Funktionen aufgeteilt, sondern das Hauptprogramm steht einfach so auf Modulebene. Mit den ganzen Variablen.

Zu den Variablen aus Deinem Programm kommen dann noch die knapp 190 Namen die Du durch den Sternchen-Import aus `tkinter` in den Namensraum holst.

Die Programmlogik ist mit der GUI vermischt. Üblicherweise schreibt man erst die Logik und testet die, und wenn das funktioniert, setzt man die GUI darauf auf. Das vermeidet die Gefahr beides zu vermischen.

`x` ist kein guter Name für — ja, wofür eigentlich?

Die Namensschreibweise entspricht nicht immer dem Style Guide for Python Code.

Daten und Code sollte man nicht wiederholen. Bei den Daten sieht man was für Probleme dadurch entstehen können wenn man sich ColaLight anschaut. Das steht auf der Schaltfläche, aber später im Code wird 'Öttinger' für die Ausgabe bei ColaLight verwendet.

Durchnummerieren von Namen ist ein „code smell“. In der Regel möchte man sich da bessere, passendere Namen ausdenken oder eine Datenstruktur verwenden. Meistens eine Liste. Und falls man dann Daten in Listen hat, möchte man *nicht* mehrere Listen haben, bei denen Werte am gleichen Index jeweils zusammengehören. Informationen wie Getränkename und ob das Getränk vorrätig ist oder nicht, gehören zusammen in ein Objekt und nicht in parallele Listen.

Der lokale Name `x` in der (schlecht benannten) `cola()`-Funktion hat nichts mit dem `x` auf Modulebene zu tun. Du willst in einer Funktion keinen Namen ausserhalb der Funktion Werte zuweisen. Und auch nicht auf Namen (ausser Konstanten) zugreifen, die nicht als Argument übergeben wurden. Für ”Funktionen” bei denen sich Zustand über verschiedene Aufrufe hinweg gemerkt oder verändert werden muss, wurde objektorientierte Programmierung (OOP) erfunden.

Im konkreten Fall sehe ich die Notwendigkeit nicht über so eine Getränkeauswahl-Variable zu gehen, statt die jeweilige Aktion direkt über den Druck der jeweiligen Schaltfläche auszulösen. Da alle Schaltflächen für die Getränke das gleiche machen, nur mit jeweils einem oder mehreren unterschiedlichen Parametern, braucht man am Ende auch nur eine Methode dafür. Für das `command`-Argument kann man dann mit `functools.partial()` dafür sorgen, dass dort für jede Schaltfläche ein spezifisches Argument übergeben wird.

Explizites vergleichen mit boole'schen Literalen ist unnötig. Da kommt am Ende ja nur wieder ein Wahrheitswert heraus. Wenn man mit ``== True`` vergleicht ist das der gleiche Wert mit dem man vergleicht. Und statt ``== False`` kann man einen Test mit ``not`` verwenden. Wobei die ganzen Abfragen sowieso eigenartig strukturiert sind. Es würde mehr Sinn machen erst die Getränkeauswahl zu prüfen und darin dann mit ``if`` ob es davon noch etwas gibt und dann einen ``else``-Zweig für den Fall das nicht. Da nur ein Getränk zu gleichen Zeit ausgewählt sein kann, würden für die Folgefälle ``elif``-Zweige Sinn machen. Und nur zur Sicherheit würde ich ans Ende noch ein ``else`` setzen, mit einer Fehlermeldung, weil dieser Fall nie erreicht werden dürfte wenn der Programmierer keinen Fehler gemacht hat. Da Programmierer das aber machen… :-)

Man bräuchte die ganzen Wiederholungen dieser ``if``-Strukturen nicht, wenn die Daten in einer Datenstruktur abgelegt wären. Zum Beispiel in einer Liste. Wenn die Getränkeauswahl-Werte bei 0 statt bei 1 anfangen würden, könnte man das einfach als Index in die Liste verwenden und kann viel Code einsparen und kann die Getränkeauswahl vergrössern oder verkleinern ohne Code zu ändern, in dem man ganz einfach nur die Liste verkürzt oder durch zusätzliche Getränke erweitert.

``if cola == True:`` wird niemals zutreffen, denn `cola` ist in dem Programm an eine Funktion gebunden und eine Funktion ist niemals der Wert `True`.

IMHO fängst Du am falschen Ende an. Mit dem Entwurf einer GUI kann man ein Projekt anfangen um sich ein Bild von den Aktionen machen zu können, die von der Programmlogik bereitgestellt werden müssen, aber dann sollte man anfangen das Programm von der Logik her aufzubauen.  Da man an dem Code nicht sieht was am Ende tatsächlich gemacht werden muss wenn der Benutzer eine Schaltfläche drückt, lässt sich an dieser Stelle nicht wirklich gut sagen, wie dass im Zusammenspiel mit einer GUI am besten funktionieren wird.

Entweder brauchen die Aktionen nicht lange, dann ist das kein Problem. Oder man kann die Logik so aufbauen, dass sie in Schritten in bestimmten Zeitabschnitten von aussen ”getrieben” wird, dann kann man zum Beispiel von Tk aus die `after()`-Methode dafür verwenden. Oder es muss tatsächlich im Hintergrund etwas laufen, also zu OOP und ereignisorientierter Programmierung käme dann auch noch das Thema nebenläufige Programmierung. Und das für ein Programm das noch nicht einmal Listen oder Funktionen verwendet und alles einfach so auf Modulebene stehen hat.
DerSp83
User
Beiträge: 5
Registriert: Dienstag 19. April 2016, 09:10

Hi,

Ich sagte ja ich habe noch viel zu lernen.
Allerdings ist das was ich hier eingefügt habe ein, sagen wir mal Test (erstes mal Python) um zu testen was geht.
Bin Dir sehr dankbar für Deine Antwort auch wenn ich vieles davon nachlesen muss.
Ich werde mich wohl dann erst wieder melden wenn ich etwas professioneller vorgehen kann.
Da ich kompletter Neuling in dieser Thematik bin. Muss ich wohl dann etwas mehr lesen.
Wie gesagt die Programmstruktur an einer cnc anlage btw an einem Roboter ist da etwas anders.
Hier merkt man wohl das ich in alte Gewohnheiten falle.
Nochmals vielen Dank für deine Unterstützung.

Gruß

Jan
BlackJack

Hier mal ein Ansatz das zumindest das bisherige die erwartete Ausgabe macht:

Code: Alles auswählen

import tkinter as tk
from functools import partial


class Potable(object):

    def __init__(self, name, available):
        self.name = name
        self.available = available


class VendingMachine(object):

    def __init__(self, potables):
        self.potables = potables

    def emit(self, index):
        potable = self.potables[index]
        if potable.available:
            print(potable.name, 'wird ausgegeben')
        else:
            print(
                potable.name,
                'steht zur Zeit nicht zur Verfügung. Bitte auffüllen.'
            )


def main():
    vending_machine = VendingMachine(
        [
            Potable('Cola', True),
            Potable('Fanta', True),
            Potable('Mezzo Mix', False),
            Potable('Cola Light', True),
        ]
    )

    root = tk.Tk()
    root.title('Wählen Sie Ihr Getränk')
    root.geometry('500x500')

    for i, potable in enumerate(vending_machine.potables):
        tk.Button(
            root, text=potable.name, command=partial(vending_machine.emit, i)
        ).pack(expand=True, fill=tk.BOTH)
       
    root.mainloop()


if __name__ == '__main__':
    main()
Antworten