Menüauswahl abfragen

Fragen zu Tkinter.
Benutzeravatar
wuf
User
Beiträge: 1366
Registriert: Sonntag 8. Juni 2003, 09:50

Re: Menüauswahl abfragen

Beitragvon wuf » Samstag 23. Dezember 2017, 10:55

Hier eine etwas entflochtene und ausführbare Variante des Beitrages:
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. from functools import partial
  5.  
  6. try:
  7.     # Tkinter for Python 2.xx
  8.     import Tkinter as tk
  9. except ImportError:
  10.     # Tkinter for Python 3.xx
  11.     import tkinter as tk
  12.  
  13. APP_TITLE = "Menu Abfragen"
  14. APP_XPOS = 100
  15. APP_YPOS = 100
  16. APP_WIDTH = 400
  17. APP_HEIGHT = 300
  18.  
  19.  
  20. class Application(tk.Frame):
  21.  
  22.     def __init__(self, master):
  23.         self.master = master
  24.         tk.Frame.__init__(self, master)
  25.  
  26.         self.obj_canvas_master = tk.Canvas(master, width=500, height=300,
  27.             relief="sunken", bd=3)
  28.         self.obj_canvas_master.pack()
  29.  
  30.         self.obj_frame = tk.Frame(self.obj_canvas_master, background="blue",
  31.             bd=6, relief="groove", width=300, height=200, padx=3, pady=3)
  32.            
  33.         self.obj_canvas_master.create_window(0, 0, window=self.obj_frame,
  34.             anchor="nw", tags="self.frame")
  35.  
  36.         # attach popup to canvas
  37.         self.obj_frame.bind("<Button-1>", self.popup)
  38.        
  39.         # create a popup menu
  40.         mainmenu = tk.Menu(self.obj_frame)
  41.         menu = tk.Menu(mainmenu, tearoff=0, relief="raised", bd=3,
  42.             activeforeground="red", activebackground="yellow")
  43.         mainmenu.add_command(label="Spieler neu", command=self.hello)
  44.        
  45.         submenu_player_edit = tk.Menu(menu, tearoff=0)
  46.         submenu_player_edit.add_command(label="Name ändern", command=self.hello)
  47.         submenu_team_choice = tk.Menu(submenu_player_edit, tearoff=0)
  48.  
  49.         for i in range(1, 17):
  50.             '''
  51.            #~~ Variante OP
  52.            #   Funktioniert: Typischer gewöhnungsbedürftige 'lambda' Syntax
  53.            submenu_team_choice.add_command(label="Team " + str(i),
  54.                command=lambda i=i: self.auswahl(str(i)))
  55.                
  56.            #~~ Variante sanfu
  57.            #   Funktioniert nicht: Hier wird immer der letzte Team-Index
  58.            #   als Argument übergeben
  59.            submenu_team_choice.add_command(label="Team {}".format(i),
  60.                command=lambda: self.auswahl(i))
  61.            '''
  62.             #~~ Variante Sirius3, __deets__ (und wuf)
  63.             #   Funktioniert: Ein etwas mehr verständlicherer 'partial' Syntax
  64.             submenu_team_choice.add_command(label="Team {}".format(i),
  65.             command=partial(self.auswahl, i))
  66.            
  67.         submenu_player_edit.add_cascade(label="Team wählen", menu=submenu_team_choice)
  68.         submenu_player_edit.add_command(label="Handycap wählen", command=self.hello)
  69.         menu.add_cascade(label="Spieler anpassen", menu=submenu_player_edit)
  70.  
  71.         submenu_player_move = tk.Menu(menu, tearoff=0)
  72.         submenu_player_move.add_command(label="hoch", command=self.hello)
  73.         submenu_player_move.add_command(label="runter", command=self.hello)
  74.         menu.add_cascade(label="Spieler verschieben", menu=submenu_player_move,
  75.             state=tk.DISABLED)
  76.  
  77.         submenu_player_delete = tk.Menu(menu, tearoff=0)
  78.         submenu_player_delete.add_command(label="ja", command=self.hello)
  79.         submenu_player_delete.add_command(label="nein", command=self.hello)
  80.         menu.add_cascade(label="Spieler löschen", menu=submenu_player_delete)
  81.  
  82.         menu.add_separator()
  83.         menu.add_command(label="Statistik", command=self.hello)
  84.         menu.add_separator()
  85.         menu.add_command(label="abbrechen")
  86.  
  87.         self.menu = menu
  88.        
  89.     def popup(self, event):
  90.         self.menu.post(event.x_root, event.y_root)
  91.         #self.menu.post(100, 100)
  92.    
  93.     def hello(self):
  94.         print("hello!")
  95.        
  96.     def auswahl(self, number):
  97.         print("Dies ist Team {}".format(number))
  98.  
  99.            
  100. def main():
  101.     app_win = tk.Tk()
  102.     app_win.title(APP_TITLE)
  103.     app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
  104.     app_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
  105.        
  106.     Application(app_win).pack(fill='both', expand=True)
  107.    
  108.     app_win.mainloop()
  109.  
  110.  
  111. if __name__ == '__main__':
  112.     main()      
Gruss wuf :wink:
Take it easy Mates!
Sirius3
User
Beiträge: 7043
Registriert: Sonntag 21. Oktober 2012, 17:20

Re: Menüauswahl abfragen

Beitragvon Sirius3 » Samstag 23. Dezember 2017, 10:56

@snafu: hier noch mal das Problem als Programm, mit dem es hoffentlich klar wird:
  1. from functools import partial
  2.  
  3. def define_functions1():
  4.     result = []
  5.     for i in range(5):
  6.         result.append(lambda: print(i))
  7.     return result
  8.  
  9. def define_functions2():
  10.     result = []
  11.     for i in range(5):
  12.         result.append(partial(print, i))
  13.     return result
  14.  
  15. def main():
  16.     func1 = define_functions1()
  17.     func2 = define_functions2()
  18.     print("Ergebnis 1: ")
  19.     for f in func1: f()
  20.     print("Ergebnis 2: ")
  21.     for f in func2: f()
  22.    
  23. if __name__ == '__main__':
  24.     main()


und der Ausgabe:
  1. Ergebnis 1:
  2. 4
  3. 4
  4. 4
  5. 4
  6. 4
  7. Ergebnis 2:
  8. 0
  9. 1
  10. 2
  11. 3
  12. 4
Benutzeravatar
snafu
User
Beiträge: 5384
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Re: Menüauswahl abfragen

Beitragvon snafu » Samstag 23. Dezember 2017, 12:57

Habe es jetzt begriffen, denke ich. Die GUI führt die Lambdas nicht unmittelbar aus. Dadurch gilt bei einem nachträglichen Aufruf nur der letzte Wert von i. Ähnlich wie es wäre, wenn man die Lambdas zuvor in eine Liste ablegt und die Liste anschließend durchläuft.
shcol (Repo | Doc | PyPi)
Benutzeravatar
snafu
User
Beiträge: 5384
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Re: Menüauswahl abfragen

Beitragvon snafu » Samstag 23. Dezember 2017, 13:11

__deets__ hat geschrieben:Das Problem ist, das der Closure/ABschluss über die Namen passiert. Nicht über die Werte.

Jetzt nochmal gelesen und nun auch verstanden. Hatte etwas länger gedauert bei mir.
shcol (Repo | Doc | PyPi)
suk
User
Beiträge: 17
Registriert: Sonntag 17. Dezember 2017, 01:18

Re: Menüauswahl abfragen

Beitragvon suk » Sonntag 24. Dezember 2017, 13:10

Das war bis hier recht ausführlich. Danke, konnte einiges Neues mitnehmen.
Als Fazit für mich ... beide Varianten sind möglich.
Vorteil lambda: direkter Zusammenbau der erforderlichen Funktion
Vorteil partial: deutlich einfacher zu verstehen, jedoch Import einer zusätzlichen Funktion erforderlich

Habe allerdings noch ein zweites Problem. Wie bekomme ich es hin, dass das Menü nicht gleich bei Auswahl geschlossen wird. Die Menüpunkte hoch und runter sollen ggf. mehrmals hintereinander ausgeführt werden. Da wäre es nervig, jedesmal den Menüpunkt neu aufrufen zu müssen.
Mein Ziel wäre es bei konsequenter Menüführung, das Menü bei einigen Auswahlpunkten offen zu halten oder nach Ausführung genau so wieder aufzumachen, dass der Benutzer sofort auf dem selben Menüpunkt steht.
Die Alternative wäre natürlich, die Mehrfachwiederholung in der Funktion selber einzubauen.
Hätte jemand eine Idee?
Benutzeravatar
__deets__
User
Beiträge: 2141
Registriert: Mittwoch 14. Oktober 2015, 14:29

Re: Menüauswahl abfragen

Beitragvon __deets__ » Sonntag 24. Dezember 2017, 16:02

Das geht denke ich nicht. Du hast dir da eine Interaktion überlegt für die Menüs nicht gedacht sind. So etwas baut man in einen normalen Dialog.
suk
User
Beiträge: 17
Registriert: Sonntag 17. Dezember 2017, 01:18

Re: Menüauswahl abfragen

Beitragvon suk » Dienstag 26. Dezember 2017, 01:42

wuf hat geschrieben:Hier eine etwas entflochtene und ausführbare Variante des Beitrages:

...
class Application(tk.Frame):
...



@wuf: Deine version sieht sehr aufgeräumt aus. Ich habe aber anscheinend noch nicht ganz verstanden, warum Du in die Klassendefinition tk.Frame aufgenommen hast. Ich hätte ja vermutet, dass an der Übergabe app_win auch die Funktion tk.Frame mit dranhängt.
Welche Aufgabe hat der Teil in der Klassendefinition?
Sirius3
User
Beiträge: 7043
Registriert: Sonntag 21. Oktober 2012, 17:20

Re: Menüauswahl abfragen

Beitragvon Sirius3 » Sonntag 31. Dezember 2017, 09:55

@suk: das nennt sich Vererbung und hat was mit Objektorientierung zu tun. Application erbt alle Eigenschaften von tk.Frame und bekommt noch ein paar mehr.
Benutzeravatar
wuf
User
Beiträge: 1366
Registriert: Sonntag 8. Juni 2003, 09:50

Re: Menüauswahl abfragen

Beitragvon wuf » Sonntag 31. Dezember 2017, 12:29

suk hat geschrieben:@wuf: Deine version sieht sehr aufgeräumt aus. Ich habe aber anscheinend noch nicht ganz verstanden, warum Du in die Klassendefinition tk.Frame aufgenommen hast. Ich hätte ja vermutet, dass an der Übergabe app_win auch die Funktion tk.Frame mit dranhängt:

Als Basis meiner Experimentierskripte verwende ich folgendes Template um einzelne im Beitrag gesammelte Codefetzen in eine ausführbare Struktur einzufügen. Der Klassenname 'Application' müsste eventuell eher in 'Container' umbenannt werden. Übergeben wird nur das Hauptfenstersobjekt ohne ein Frameobjekt übergeben.
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. from functools import partial
  5.  
  6. try:
  7.     # Tkinter for Python 2.xx
  8.     import Tkinter as tk
  9. except ImportError:
  10.     # Tkinter for Python 3.xx
  11.     import tkinter as tk
  12.  
  13. APP_TITLE = "Template"
  14. APP_XPOS = 100
  15. APP_YPOS = 100
  16. APP_WIDTH = 400
  17. APP_HEIGHT = 300
  18.  
  19.  
  20. class Application(tk.Frame):
  21.  
  22.     def __init__(self, master):
  23.         self.master = master
  24.         tk.Frame.__init__(self, master)
  25.  
  26.            
  27. def main():
  28.     app_win = tk.Tk()
  29.     app_win.title(APP_TITLE)
  30.     app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
  31.     app_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
  32.        
  33.     Application(app_win).pack(fill='both', expand=True)
  34.    
  35.     app_win.mainloop()
  36.  
  37.  
  38. if __name__ == '__main__':
  39.     main()      
Gruss wuf :wink:
Take it easy Mates!
Benutzeravatar
__deets__
User
Beiträge: 2141
Registriert: Mittwoch 14. Oktober 2015, 14:29

Re: Menüauswahl abfragen

Beitragvon __deets__ » Sonntag 31. Dezember 2017, 12:51

Ich würde die die Vererbung von Frame loswerden. Sie ist unnötig und schlimmstenfalls verwirrend. Sie würde nur dann Sinn ergeben, wenn durch die Ableitung die eigentliche Darstellung innerhalb tkinters verändert würde. So wie man zb in Qt ein QWidget mit überladen der Paint Methode machen kann.

Tkinter kennt so etwas meiner Kenntnis nach nicht, höchstens könnte man ein Styling vornehmen - das kann man aber immer auch in dem umliegenden Code machen.
Benutzeravatar
wuf
User
Beiträge: 1366
Registriert: Sonntag 8. Juni 2003, 09:50

Re: Menüauswahl abfragen

Beitragvon wuf » Sonntag 31. Dezember 2017, 17:40

__deets__ hat geschrieben:Ich würde die die Vererbung von Frame loswerden. Sie ist unnötig und schlimmstenfalls verwirrend. Sie würde nur dann Sinn ergeben, wenn durch die Ableitung die eigentliche Darstellung innerhalb tkinters verändert würde. So wie man zb in Qt ein QWidget mit überladen der Paint Methode machen kann.
Wie würdest du dann das obige Template abändern, dass es deinen Vorstellungen entspricht?
Gruss wuf :wink:
Take it easy Mates!
Benutzeravatar
__deets__
User
Beiträge: 2141
Registriert: Mittwoch 14. Oktober 2015, 14:29

Re: Menüauswahl abfragen

Beitragvon __deets__ » Sonntag 31. Dezember 2017, 19:13

Einfach von object erben, und den Frame im Konstruktor erzeugen. Grob (auf dem Telefon getippt):

  1. class Application(object):
  2.  
  3.      der __init__(self, ...):
  4.            self._frame = tk.Frame(....)
Benutzeravatar
wuf
User
Beiträge: 1366
Registriert: Sonntag 8. Juni 2003, 09:50

Re: Menüauswahl abfragen

Beitragvon wuf » Sonntag 31. Dezember 2017, 23:09

OK __deets__

Danke für deine Rückantwort. Habe mein Templates für Tkinter-Versuche wie folgt auf meine Bedürfnisse abgeändert:
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. from functools import partial
  5.  
  6. try:
  7.     # Tkinter for Python 2.xx
  8.     import Tkinter as tk
  9. except ImportError:
  10.     # Tkinter for Python 3.xx
  11.     import tkinter as tk
  12.  
  13. APP_TITLE = "NewTemplate_01"
  14. APP_XPOS = 100
  15. APP_YPOS = 100
  16. APP_WIDTH = 300
  17. APP_HEIGHT = 200
  18.  
  19.        
  20. class Application(tk.Tk):
  21.  
  22.     def __init__(self):
  23.        
  24.         tk.Tk.__init__(self)
  25.         self.title(APP_TITLE)
  26.         self.protocol("WM_DELETE_WINDOW", self.close_app)
  27.         self.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
  28.         self.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
  29.  
  30.         self.option_add("*Button.highlightThickness", 0)
  31.        
  32.         # Trial container-01
  33.         self.app_container_01 = tk.Frame(self, bg='yellow', bd=0)
  34.         self.app_container_01.pack(side='left', fill='both', expand=True)
  35.         tk.Button(self.app_container_01, text='Container-01').pack(expand=True)
  36.  
  37.         # Trial container-02
  38.         self.app_container_02 = tk.Frame(self, bg='steelblue', bd=0)
  39.         self.app_container_02.pack(side='left', fill='both', expand=True)
  40.         tk.Button(self.app_container_02, text='Container-02').pack(expand=True)
  41.        
  42.     def close_app(self):
  43.         # Here do something before apps shutdown
  44.         print("Good Bye!")
  45.         self.destroy()
  46.        
  47. Application().mainloop()
P.S. Nicht auf der Telefon-Konsole getippt. Wünsche dir und allen Forum Mitgliedern noch alles Gute fürs 2018!

Gruss wuf :wink:
Take it easy Mates!
Benutzeravatar
__deets__
User
Beiträge: 2141
Registriert: Mittwoch 14. Oktober 2015, 14:29

Re: Menüauswahl abfragen

Beitragvon __deets__ » Montag 1. Januar 2018, 12:23

Frohes neues zurück.

Auch hier hast du ja aber eine Vererbung drin, diesmal halt zu tk.Tk. Wozu? Einen solchen Schritt sollte man nur machen, wenn es notwendig ist. nicht nur, weil es zulässig ist.
Benutzeravatar
wuf
User
Beiträge: 1366
Registriert: Sonntag 8. Juni 2003, 09:50

Re: Menüauswahl abfragen

Beitragvon wuf » Montag 1. Januar 2018, 19:18

OK __deets__

Dann hältst du diese Variante für besser?
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. from functools import partial
  5.  
  6. try:
  7.     # Tkinter for Python 2.xx
  8.     import Tkinter as tk
  9. except ImportError:
  10.     # Tkinter for Python 3.xx
  11.     import tkinter as tk
  12.  
  13. APP_TITLE = "NewTemplate_01"
  14. APP_XPOS = 100
  15. APP_YPOS = 100
  16. APP_WIDTH = 300
  17. APP_HEIGHT = 200
  18.  
  19.        
  20. class Application(object):
  21.  
  22.     def __init__(self, app_win):
  23.         self.app_win = app_win
  24.         app_win.protocol("WM_DELETE_WINDOW", self.close_app)
  25.        
  26.         # Trial container-01
  27.         self.app_container_01 = tk.Frame(app_win, bg='yellow', bd=0)
  28.         self.app_container_01.pack(side='left', fill='both', expand=True)
  29.         tk.Button(self.app_container_01, text='Container-01').pack(expand=True)
  30.  
  31.         # Trial container-02
  32.         self.app_container_02 = tk.Frame(app_win, bg='steelblue', bd=0)
  33.         self.app_container_02.pack(side='left', fill='both', expand=True)
  34.         tk.Button(self.app_container_02, text='Container-02').pack(expand=True)
  35.        
  36.     def close_app(self):
  37.         # Here do something before apps shutdown
  38.         print("Good Bye!")
  39.         self.app_win.destroy()
  40.  
  41.        
  42. def main():
  43.     app_win = tk.Tk()
  44.     app_win.title(APP_TITLE)
  45.     app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
  46.     app_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
  47.     app_win.option_add("*Button.highlightThickness", 0)
  48.    
  49.     Application(app_win)
  50.    
  51.     app_win.mainloop()
  52.  
  53.  
  54. if __name__ == '__main__':
  55.     main()
Gruss wuf :wink:
Take it easy Mates!

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder