Eine Funktion für mehrere Widgets

Fragen zu Tkinter.
Antworten
Bl3nder
User
Beiträge: 139
Registriert: Freitag 3. Januar 2020, 17:07

Guten Morgen,


Ich habe folgende Frage bzw Problem Ich möchte gerne eine Funktion für unterschiedliche Widgets.

Beispiel:

Ich habe 2 Entrys bei beiden öffnet sich per klick ein Filedialog danach möchte Ich die dazugehörige Textvariable setzen.Da der Grundsatz dont repeat yourself im Kopf rumschwiert weiß Ich leider nicht wie Ich das schaffe diese Funktion so zu stricken das sie mit mehreren events gekoppelt werden kann.


Code: Alles auswählen

import configparser
from pathlib import Path
import tkinter as tk
from tkinter import filedialog


def config_datei_laden(default_pfad):
    """
    Falls Configdatei vorhanden ist ( je nach dem wo das skript liegt) wird diese ausgelesen
    :return: dictonary mit pfaden
    """
    if default_pfad.exists():
        config = configparser.ConfigParser()
        config.read(default_pfad)
        pfade_dict = {}
        for section in config.sections():
            for (key, value) in config.items(section):
                pfade_dict[key] = value
        return pfade_dict


def gui(config_pfade):
   

    def file_dialog():
        ausgewaehlter_ordner = tk.filedialog.askdirectory()
        textvariable.set(ausgewaehlter_ordner)

    # Hauptfenster
    window = tk.Tk()
    window.title("Konfigurator")
    window.geometry("300x300")

    config_frame = tk.Frame()
    textvariable = tk.StringVar()
    config_label = tk.Label(master=config_frame, text="Pfad zur Konfigurationsdatei:")
    if not list(config_pfade.values())[0] == "test":
        textvariable.set(str(list(config_pfade.values())[0]))
    config_entry = tk.Entry(master=config_frame, text=textvariable)
    config_entry.bind("<1>", file_dialog)    #<--------------------------------------- hier geht es drum später möchte Ich mehrere solcher pfade eingeben und das dafür nur eine Funktion haben 
    config_label.grid(row=1, column=1, padx=5, pady=5)
    config_entry.grid(row=1, column=2, padx=5, pady=5)

    # Frame positionieren
    config_frame.pack(fill="both", expand=True)

    window.mainloop()




def main():
    config_pfaden = config_datei_laden((Path(__file__).parent / "config.ini"))
    gui(config_pfaden)


if __name__ == "__main__":
    main()


p.s Ich habe es schon mit lamda versucht aber dann wird immer ( mehrere funktionen hintereinander aufgerufen lambda x: [filedialog(),textvariable.set(x)] nur x ist das quasi einfach nur das event objekt)

Jemand eine Idee ?
Eine Vision ohne Aktion bleibe eine Illusion
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@Bl3nder,

es gibt bei bind() noch einen dritten Parameter: 'add'
Damit kann man weitere Funktionen verbinden:

Code: Alles auswählen

config_entry.bind("<1>", function1)
config_entry.bind("<1>", function2, "+")
Wenn 'add' ein leerer String ist "", werden alle existierenden Bindungen aufgelöst und die neue Funktion als erste und einzige verbunden.
Wenn 'add' ein '+' enthält bleiben die bestehenden Verbindungen bestehen und die neue Funktion wird hinzugefügt.

https://docs.python.org/3/library/tkint ... and-events
Bl3nder
User
Beiträge: 139
Registriert: Freitag 3. Januar 2020, 17:07

Ok danke ich probiere es gleich mal aus :)
Eine Vision ohne Aktion bleibe eine Illusion
Bl3nder
User
Beiträge: 139
Registriert: Freitag 3. Januar 2020, 17:07

Ok vill war die Frage nicht ganz klar gestellt von mir also mal angenommen ich habe 10 entry Widgets die alle einen filedialog haben den ich dann in das Feld setzten möchte muss ich dann 10 einzelne callback Funktion machen oder bekommt man sowas auch mit 1 Funktion hin ?
Eine Vision ohne Aktion bleibe eine Illusion
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@Bl3nder,

dann würde ich eine Funktionschreiben, die diese 10 oder meinetwegen 100 Widgets in einer Schleife erstellt und auch das binding übernimmt.
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ob man das mit 10 oder 1 Funktion hinbekommt hängt doch davon ab, was die machen. Machen die 10 unterschiedliche Dinge? Dann nicht. Machen die 10 mal das gleiche? Dann ja. Und im kontinuum dazwischen kann es sein, dass zb eine Bewertung (von 1 bis 10) abgegeben werden soll, dann kann man natürlich eine Funktion mit dem Wert als Parameter erstellen. Oder Namen oder was auch immer da an Variablität zugrunde liegt, weswegen es mehrere Knöpfe sind.
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@__deets__,
Ob man das mit 10 oder 1 Funktion hinbekommt hängt doch davon ab, was die machen. Machen die 10 unterschiedliche Dinge?
Kann sein, dass ich es falsch verstanden habe, aber ich dachte es *ist* nur eine Funktion aber mehrere Widgets, die alle ein binding zu *dieser* Funktion haben sollen.
Bl3nder
User
Beiträge: 139
Registriert: Freitag 3. Januar 2020, 17:07

OK Ich mache dann mal einen Pseudocode damit ihr das besser versteht.

Code: Alles auswählen

from pathlib import Path
import tkinter as tk
from tkinter import filedialog



def filedialog():
    ausgewaehlter_ordner = tk.filedialog.askdirectory()
    #textvariable.set(ausgewahlter_ordner)

window = tk.Tk()
config1_entry.bind("<1>", file_dialog)
config2_entry.bind("<1>", file_dialog)
config3_entry.bind("<1>", file_dialog)
config4_entry.bind("<1>", file_dialog)
config5_entry.bind("<1>", file_dialog)
config6_entry.bind("<1>", file_dialog)
config7_entry.bind("<1>", file_dialog)


window.mainloop()

Der Code dient wirklich nur der verdeutlichung ... also Ich habe 10 entry die Ich immer an die gleiche funktion binde ( es soll ein filedialog geöffnet werden) und später setzte Ich dann jedes mal den ausgewählten file_pfad als text in das entry . Und Ich weiß leider nicht wie Ich in dem Fall die Argumente übergeben kann das Ich definiert sagen kann :"OK config3_entry wurde gedrückt" also setze Ich dann auch später die Textvariable in config3 .Ich hoffe ihr könnt mich verstehen .
Eine Vision ohne Aktion bleibe eine Illusion
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mit functools partial oder einem lambda. Wie immer.
Bl3nder
User
Beiträge: 139
Registriert: Freitag 3. Januar 2020, 17:07

also so in der art ?

lambda event, a=1 :file_dialog(a) dann einfach in dem filedialog if a = 1 ... setze die textvariable etc ? macht man das so also ist das so ein gute Umsetzung


Code: Alles auswählen

import configparser
from pathlib import Path
import tkinter as tk
from tkinter import filedialog


def config_datei_laden(default_pfad):
    """
    Falls Configdatei vorhanden ist ( je nach dem wo das skript liegt) wird diese ausgelesen
    :return: dictonary mit pfaden
    """
    if default_pfad.exists():
        config = configparser.ConfigParser()
        config.read(default_pfad)
        pfade_dict = {}
        for section in config.sections():
            for (key, value) in config.items(section):
                pfade_dict[key] = value
        return pfade_dict


def gui(config_pfade):
    # erstellt configdatei
    def configdatei_erstellen():
        config = configparser.ConfigParser()
        config['PATH_DEFAULT'] = {"wetterstation_pfad": r"C:\Users\Marcel\Desktop\WETTERSTATION"}
        with open('config.ini', 'w') as configfile:
            config.write(configfile)

    def file_dialog(test):

        ausgewaehlter_ordner = tk.filedialog.askdirectory()
        if test == 1:
            textvariable.set(ausgewaehlter_ordner)

    # Hauptfenster
    window = tk.Tk()
    window.title("Konfigurator")
    window.geometry("300x300")

    config_frame = tk.Frame()
    textvariable = tk.StringVar()
    config_label = tk.Label(master=config_frame, text="Pfad zur Konfigurationsdatei:")
    if not list(config_pfade.values())[0] == "test":
        textvariable.set(str(list(config_pfade.values())[0]))
    config_entry = tk.Entry(master=config_frame, text=textvariable)
    config_entry.bind("<1>", lambda event, a=1 :file_dialog(a))
    config_label.grid(row=1, column=1, padx=5, pady=5)
    config_entry.grid(row=1, column=2, padx=5, pady=5)

    # Frame positionieren
    config_frame.pack(fill="both", expand=True)

    window.mainloop()


def main():
    config_pfaden = config_datei_laden((Path(__file__).parent / "config.ini"))
    gui(config_pfaden)


if __name__ == "__main__":
    main()


Eine Vision ohne Aktion bleibe eine Illusion
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist der Weg, Argumente reinzubekommen, ja.
Bl3nder
User
Beiträge: 139
Registriert: Freitag 3. Januar 2020, 17:07

ok alles klar vielen dank euch beiden
Eine Vision ohne Aktion bleibe eine Illusion
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Statt lambda benutzt man functools.partial.
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Sirius3: das fliegt mir regelmäßig um die Ohren, weil der tkinter wrapper das nicht schluckt. Leider.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Was schluckt denn der nicht?
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ein Partial Objekt. Bin ich schon diverse Male drüber gestolpert. Und defaulte darum bei tkinter immer auf lambdas 🤷‍♂️
Antworten