Button per Rechtsklick umbenennen

Fragen zu Tkinter.
Antworten
Sven1234
User
Beiträge: 1
Registriert: Donnerstag 10. Februar 2022, 15:48

Hallo zusammen,

geht hier um eine kleine GUI zum Schalten eines 8-Relay-USB-Board.
Jedes Relais hat zum Schalten einen Button

Ich möchte die Buttons aber nicht nur mit Relay_x beschriften sondern...
1. die Beschriftung soll sich je nach Schaltzustand ändern
2. die Beschriftung soll individuell per Rechtklick veränderbar sein

Sprich, wenn ich per Rechtsklick auf den Button Relay_x drücke, soll sich ein Popup öffnen in dem ich für den Schaltzustand ON und OFF jeweils einen beliebigen Freitext eingeben kann.
Beim Schließen des Popups soll diese Benennung dann für den Button übernommen werden.

Ich habe diese Umstetzung bei folgendem Tool schon gesehen, hab aber kein Plan wie ich sowas umsetzen kann.
http://denkovi.com/usb-8-relay-manager

Durch meine begrentzen py Kenntnisse, hab ich leider kein Plan wie ich sowas angehen kann.
Für den Schaltzustand gibt es genügend Beispiele, aber eben nicht in Verbindung mit dem Freitext.
Wer sich jetzt fragt wie ich dann das untern geschafft habe... das hab ich so bekommen :oops:
Anpassungen / Änderung gehen noch, aber mit "grüner Wiese" hab ich Proleme (geht gar nicht :cry: )

Code: Alles auswählen

import tkinter
import sys
import serial
import time
from tkinter import Label

window = tkinter.Tk()
state = [0, 0, 0, 0, 0, 0, 0, 0]
relay = []
serPort = serial.Serial(timeout=0)
portnamev = ""
def clear():
    portnamev = portname.get()
    print(portnamev)
    portname.delete(0, tkinter.END)
    serPort.baudrate = 9600
    serPort.port = portnamev
    serPort.open()
    portname.destroy()
    label1.destroy()
    insert.destroy()
    for i in range(0, 8):
        relayIndex = str(i)
        string2 = ("relay read " + relayIndex + "\n\r")
        string2 = string2.encode('utf-8')
        serPort.write(string2)
        response = serPort.read(25)
        response = str(response)
        if (response.find("off") > 0):
            state[i] = 0
        elif (response.find("on") > 0):
            state[i] = 1

        for i in range(0, 8):
            if state[i]:
                relay[i].configure(bg="green3")

            else:
                relay[i].configure(bg="grey55")

def Relaycont(x):

    relayIndex = str(x)
    print(x)
    string1 = ("relay " + "on" + " " + relayIndex + "\n\r")
    string2 = ("relay " + "off" + " " + relayIndex + "\n\r")
    string2 = string2.encode('utf-8')
    string1 = string1.encode('utf-8')
    if(state[x]):
        serPort.write(string2)
#        tkinter.Label(window, text=string2).grid(row=4, column=2)
        state[x] = 0
        relay[x]["bg"] = "grey55"
    else:
        serPort.write(string1)
#        tkinter.Label(window, text=string1).grid(row=4, column=2)
        state[x] = 1
        relay[x].configure(bg="green3")


def All_is_on():
    for i in range(0, 8):
        relayIndex = str(i)
        string1 = ("relay " + "on" + " " + relayIndex + "\n\r")
        string1 = string1.encode('utf-8')
        serPort.write(string1)
        state[i]=1
        relay[i].configure(bg="green3")

def All_is_off():
    string = "reset\n\r"
    string = string.encode('utf-8')
    serPort.write(string)
    for i in range(0, 8):
        relay[i].configure(bg="grey55")
        state[i] = 0


window.title("8-Relay GUI")
window.labelfont = ('Calibri', 30, 'bold')
window.geometry ('250x270')
window.resizable(width=0, height=0)


#initialchek()
relay.append(1)

relay[0]=tkinter.Button(window,
                        text="Relay 0",
                        anchor="center",
                        foreground="black",
                        font="Calibri 12",
                        relief="raised",
                        width="10",
                        command=lambda: Relaycont(0))
relay[0].place(x=15, y=10, width=100, height=30)

relay.append(1)

relay[1]=tkinter.Button(window,
                        text="Relay 1",
                        anchor="center",
                        foreground="black",
                        font="Calibri 12",
                        relief="raised",
                        width="10",
                        command=lambda: Relaycont(1))
relay[1].place(x=15, y=50, width=100, height=30)

relay.append(1)

relay[2]=tkinter.Button(window,
                        text="Relay 2",
                        anchor="center",
                        foreground="black",
                        font="Calibri 12",
                        relief="raised",
                        width="10",
                        command=lambda: Relaycont(2))
relay[2].place(x=15, y=90, width=100, height=30)

relay.append(1)

relay[3]=tkinter.Button(window,
                        text="Relay 3",
                        anchor="center",
                        foreground="black",
                        font="Calibri 12",
                        relief="raised",
                        width="10",
                        command=lambda: Relaycont(3))
relay[3].place(x=15, y=130, width=100, height=30)

relay.append(1)

relay[4]=tkinter.Button(window,
                        text="Relay 4",
                        anchor="center",
                        foreground="black",
                        font="Calibri 12",
                        relief="raised",
                        width="10",
                        command=lambda: Relaycont(4))
relay[4].place(x=130, y=10, width=100, height=30)

relay.append(1)

relay[5]=tkinter.Button(window,
                        text="Relay 5",
                        anchor="center",
                        foreground="black",
                        font="Calibri 12",
                        relief="raised",
                        width="10",
                        command=lambda: Relaycont(5))
relay[5].place(x=130, y=50, width=100, height=30)

relay.append(1)

relay[6]=tkinter.Button(window,
                        text="Relay 6",
                        anchor="center",
                        foreground="black",
                        font="Calibri 12",
                        relief="raised",
                        width="10",
                        command=lambda: Relaycont(6))
relay[6].place(x=130, y=90, width=100, height=30)

relay.append(1)

relay[7]=tkinter.Button(window,
                        text="Relay 7",
                        anchor="center",
                        foreground="black",
                        font="Calibri 12",
                        relief="raised",
                        width="10",
                        command=lambda: Relaycont(7))
relay[7].place(x=130, y=130, width=100, height=30)

relay.append(1)

for i in range(0, 8):
    if state[i]:
       relay[i].configure(bg="green3")

    else:
        relay[i].configure(bg="grey55")


relay[8]=tkinter.Button(window,
                        text="all Relays on",
                        anchor="center",
                        foreground="black",
                        font="Calibri 12",
                        relief="raised",
                        width="10",
                        background="grey75",
                        command=lambda: All_is_on())
relay[8].place(x=15, y=170, width=100, height=30)

relay.append(1)

relay[9]=tkinter.Button(window,
                        text="all Relays off",
                        anchor="center",
                        foreground="black",
                        font="Calibri 12",
                        relief="raised",
                        width="10",
                        background="grey75",
                        command=lambda: All_is_off())
relay[9].place(x=130, y=170, width=100, height=30)

label1=tkinter.Label(window,
                     text="enter COM-Port",
                     anchor="center",
                     font="Calibri 12",
                     fg="black")
label1.place(x=15, y=220, width=100, height=30)

portname=tkinter.Entry(window)
portname.place(x=120, y=220, width=50, height=30)

insert=tkinter.Button(window,
                      text='Insert',
                      anchor="center",
                      foreground="black",
                      font="Calibri 12",
                      relief="raised",
                      width="10",
                      command=lambda: clear())
insert.place(x=180, y=220, width=50, height=30)

window.mainloop()
Grüße
Sven
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Globale Variablen darf man nicht benutzen. Alles was eine Funktion braucht, muß sie über ihre Argumente übergeben bekommen.
Bei `clear` fehlen `portname`, `serPort`, `label1`, `insert`, state` und `relay`. Es fehlt eine main-Funktion, die das Hauptprogramm enthält. Eine main-Funktion verhindert auch effektiv, dass man versehentlich globale Variablen benutzt.
Variablennamen schreibt man generell komplett klein.
Statt `find` kann man in Deinem Fall einfach `in` verwenden. Was sind das eigentlich für 25 Bytes die da zurückkommen? Da utf8 keine fixe Länge hat, ist das ungünstig, wenn statt 25 auch mal 100 Bytes geschrieben werden. Bei seriellen Protokollen sollte man sich, wenn möglich, auf den ASCII-Bereich beschränken.
Benutzt keine Abkürzungen, was soll `Relaycont` sein?
Statt über einen Index iteriert man in Python über die Elemente direkt. Strings stückelt man nicht per + zusammen, sondern nutzt f-Strings.
Warum hängst Du an `relay` immer erst eine 1 an, um dieses Element sofort danach mit einem Button zu überschreiben? Der direkte Weg wäre doch, gleich append mit dem Button zu benutzen?
`place` benutzt man nicht, weil das die Programme auf anderen Systemen mit anderen Bildschirmauflösungen unbenutzbar macht. Benutzt grid oder pack.
Statt 8 mal fast den selben Code zu kopieren, benutzt man eine for-Schleife, um die Buttons zu erzeugen.
Warum sind "all Relays on" und "all Relays off" auch in der `relay`-Liste?
Für Wahrheitswerte gibt es True und False, da benutzt man nicht 1 oder 0.

Das ganze sollte ungefähr so aussehen:

Code: Alles auswählen

import tkinter
import serial


class StateButton(tkinter.Button):
    def __init__(self, *args, **kw):
        tkinter.Button.__init__(self, *args, **kw)
        self._state = False
    
    @property
    def state(self):
        return self._state
        
    @state.setter
    def state(self, state):
        self.state = state
        self['bg'] = "green3" if state else "grey55"


def clear(serial_port, portname, label, button, relays):
    serial_port.baudrate = 9600
    serial_port.port = portname.get()
    serial_port.open()

    portname.destroy()
    label.destroy()
    button.destroy()
    for index, relay in enumerate(relays):
        seriel_port.write(f"relay read {index}\n\r".encode('utf-8'))
        response = seriel_port.read(25).decode('utf-8')
        if b"off" in response:
            relay.state = False
        elif b"on" in response:
            relay.state = True


def set_state(relay, serial_port, index, state):
    text_state = "on" if state else "off"
    relay.state = state
    seriel_port.write(f"relay {text_state} {index}\n\r".encode('utf-8'))


def relay_switch(relay, serial_port, index):
    relay.state = not relay.state
    set_state(relay, serial_port, index)


def all_relays_on(relays, seriel_port):
    for index, relay in enumerate(relays):
        set_state(relay, seriel_port, index, True)


def all_relays_off(relays, seriel_port):
    for index, relay in enumerate(relays):
        set_state(relay, seriel_port, index, False)


def main():
    serial_port = serial.Serial(timeout=0)

    window = tkinter.Tk()
    window.title("8-Relay GUI")

    relays = []
    for index in range(8):
        relay = StateButton(window,
                        text=f"Relay {index}",
                        anchor="center",
                        foreground="black",
                        background="grey55"
                        font="Calibri 12",
                        relief="raised",
                        width="10")
        relay['command'] = partial(relay_switch, relay, serial_port, index)
        relay.pack()
        relays.append(relay)

    tkinter.Button(window,
        text="all Relays on",
        anchor="center",
        foreground="black",
        font="Calibri 12",
        relief="raised",
        width="10",
        background="grey75",
        command=partial(all_relays_on, relays, serial_port)).pack()

    tkinter.Button(window,
        text="all Relays off",
        anchor="center",
        foreground="black",
        font="Calibri 12",
        relief="raised",
        width="10",
        background="grey75",
        command=partial(all_relays_on, relays, serial_port)).pack()

    label = tkinter.Label(window,
         text="enter COM-Port",
         anchor="center",
         font="Calibri 12",
         fg="black")
    label.pack()
    portname = tkinter.Entry(window)
    portname.pack()
    insert_button = tkinter.Button(window,
                      text='Insert',
                      anchor="center",
                      foreground="black",
                      font="Calibri 12",
                      relief="raised",
                      width="10")
    insert_button['command'] = partial(clear, serial_port, portname, label, insert_button, relays)
    insert_button.pack()
    window.mainloop()
    
if __name__ == "__main__":
    main()
Für Aktionen mit dem rechten Button muß man `bind` benutzen. Für das, was Du vorhast, ist dann meine Klasse schon ein guter Ausgangspunkt.
derElch
User
Beiträge: 33
Registriert: Sonntag 25. Februar 2018, 13:14

Hallo Sirius3,

danke für dein Codebeispiel, das sind einige Ansätze die sehr interessant sind. Ich habe eine Frage zu den Übergabewerten im command:

Code: Alles auswählen

relay['command'] = partial(relay_switch, relay, serial_port, index)
Wieso arbeitest du hier mit partial? Welcher Vorteil ist hier gegenüber einer einer lamda Funktion mit der ich ebenfalls Werte an die Funktion übergeben kann?
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der Hauptvorteil liegt darin, dass es keine impliziten captures by name gibt. Eine der großen Klassiker bei lambdas.

Das partial Objekt ist auch besser inspizierbar als ein lambda. Aber gebraucht habe ich das noch nie.
derElch
User
Beiträge: 33
Registriert: Sonntag 25. Februar 2018, 13:14

Hallo __deets__,

danke für die schnelle Antwort. Für mich persönlich erhöht es auch die Lesbarkeit mit partial.

Anbei noch einen Beitrag aus einem anderen Forum, bei dem der Grund genauer erklärt wird:
https://discourse.techart.online/t/pyth ... rtial/4752
Antworten