Seite 1 von 1
Widgetbeladenne Fensterfläche anklicken
Verfasst: Dienstag 24. Februar 2015, 17:58
von Goswin
Ich möchte jedesmal dann ein Event aktivieren, wenn ich mit der Maus auf eine Teilfläche meines Fensters klicke, die durch einen Frame begrenzt ist.
Rein theoretisch könnte ich den definierenden Frame an das Mausevent binden, aber praktisch funktioniert das nicht, weil dieser Frame Labels und andere Kind-Widgets enthält, und es dann nicht zur Kenntnis nimmt, wenn ich zufälligerweise auf eines dieser Kindwidgets klicke. So etwas ist *nicht erwünscht*: das Event soll immer aktiviert werden, egal ob ich auf den Frame oder irgendeines der Kind- oder Enkelwidgets klicke.
Wenn ich zuoberst auf sämtliche Kindevents ein transparentes Label oder Canvas mit den Dimensionen des Frames legen könnte, dann könnte ich dieses transparente Label oder Canvas an das Event binden. Aber das funktioniert ja schon deshalb nicht, weil (1)_es in tkinter anscheinend keine transparenten Widgets gibt, und (2)_ich nicht wüsste, was ich als Elternwidget für das transparente Widget nehmen kann, ohne die Kindwidgets von ihrem Ort zu verdrängen.
Gibt es dafür eine Lösung? Oder besteht die beste Lösung darin, tkinter zu vergessen und ein anderes GUI-Tool zu benutzen?
Re: Widgetbeladenne Fensterfläche anklicken
Verfasst: Dienstag 24. Februar 2015, 20:37
von wuf
Hi Goswin
Was hältst du von dieser Lösung?:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
try:
#~~ For Python 2.x
import Tkinter as tk
except ImportError:
#~~ For Python 3.x
import tkinter as tk
def event_callback(event):
if str(main_frame) in str(event.widget):
print('Bingo! MainFrame-Event')
app_win = tk.Tk()
app_win.geometry('300x300')
app_win.bind('<Button-1>', event_callback)
main_frame = tk.Frame(app_win, width=200, height=200, bg='yellow')
main_frame.propagate(False)
main_frame.pack(expand=True)
sub_frame = tk.Frame(main_frame, width=100, height=100, bg='green')
sub_frame.propagate(False)
sub_frame.pack(expand=True)
canvas = tk.Canvas(sub_frame, width=50, height=50, bg='blue')
canvas.pack(expand=True)
app_win.mainloop()
Gruss wuf

Re: Widgetbeladenne Fensterfläche anklicken
Verfasst: Donnerstag 26. Februar 2015, 15:48
von Goswin
@wuf:
Vielen Dank, deine Lösung funktioniert offenbar - zumindest auf meinem Rechner!
Ich weiß aber immer noch nicht, wo nun der feine Unterschied zwischen einem Tk-Objekt, Toplevel-Objekt, und einem beliebigen Frame besteht, mein Python-Handbuch ist veraltet und sagt nichts darüber. Reagiert das Tk-Objekt auf sämtliche Events, die für irgendein Widget stattfinden? Gilt das für beliebige Toplevel-Objekte?
Und nicht zuletzt: Ist die verwendete str-Darstellung der Subframes und Widgets Standard oder implementierungsabhängig? Wird sie irgendwo offiziell beschrieben?
Re: Widgetbeladenne Fensterfläche anklicken
Verfasst: Donnerstag 26. Februar 2015, 17:44
von BlackJack
@Goswin: Das Tkinter-Buch vom Effbot ist immer wieder hilfreich:
http://effbot.org/tkinterbook/tkinter-e ... ndings.htm
Da stehen die vier Ebenen für die man `bind()` verwenden kann beschrieben und in welcher Reihenfolge die aufgerufen werden.
Die Zeichenkettendarstellung eines Widget-Objekts ist laut Docstring der Tk-Pfadname. Widgets sind in Tk in einer Hierarchie angeordnet mit '.' als Wurzel/Pfadtrenner. Die Tk-Namen für die Widgets würfelt Tkinter irgendwie aus aber ansonsten ist das Format schon (Tk-)Standard.
Tkinter ist ja nur eine *sehr* dünne Schicht über dem Tk-Interpreter. Oft hilft es sich in Tk-Dokumentation einzulesen wenn man mehr Informationen benötigt als die Tkinter-Dokumentation hergibt.
Re: Widgetbeladenne Fensterfläche anklicken
Verfasst: Donnerstag 26. Februar 2015, 22:10
von Goswin
@wuf:
Für das was ich anstrebe, funktioniert deine Lösung leider doch nicht oder ich verstehe sie nicht richtig.
Genauer gesagt: sie funktioniert anscheinend nicht, wenn ich zwischen mehrere verschiedenen Frames unterscheiden muss; im unteren Beispiel ordnet sie das ButtonRelease-Event
demselben Frame zu wie das letztaktivierte Button-Event, obwohl ich die Maustaste über
einem anderen Frame loslasse:
Code: Alles auswählen
#!/usr/bin/python3
#coding=utf8
import tkinter as tk
def main():
def click_callback(event):
print(str(mainframe1),str(mainframe2),str(event.widget))
if str(mainframe1) in str(event.widget):
print(str(mainframe1),str(event.widget))
print('MainFrame1-Click')
if str(mainframe2) in str(event.widget):
print('MainFrame2-Click')
def release_callback(event):
print(str(mainframe1),str(mainframe2),str(event.widget))
if str(mainframe1) in str(event.widget):
print('MainFrame1-Release')
if str(mainframe2) in str(event.widget):
print('MainFrame2-Release')
app_win = tk.Tk()
app_win.geometry('600x300')
app_win.resizable(False,False)
app_win.bind('<Button-1>', click_callback)
app_win.bind('<ButtonRelease-1>', release_callback)
mainframe1 = tk.Frame(app_win, width=200, height=200, bg='cyan')
mainframe1.propagate(False)
mainframe1.pack(expand=True,side=tk.LEFT)
#
subframe1 = tk.Frame(mainframe1, width=100, height=100, bg='green')
subframe1.propagate(False)
subframe1.pack(expand=True)
mainframe2 = tk.Frame(app_win, width=200, height=200, bg='yellow')
mainframe2.propagate(False)
mainframe2.pack(expand=True,side=tk.LEFT)
#
subframe2 = tk.Frame(mainframe2, width=100, height=100, bg='red')
subframe2.propagate(False)
subframe2.pack(expand=True)
app_win.mainloop()
if __name__ == '__main__': main()
Auf
welches Widget wird 'event.widget' beim Aufreten von 'event' gesetzt? Offenbar
nicht auf das Widget, wo das Event 'event' stattfindet!

Re: Widgetbeladenne Fensterfläche anklicken
Verfasst: Donnerstag 26. Februar 2015, 23:22
von BlackJack
@Goswin: Alle Mausereignisse zwischen 'ButtonPress' und 'ButtonRelease' (inklusive) werden an das gleiche Widget gesendet welches das 'ButtonRelease'-Ereignis empfangen hat. Steht auch so beim Effbot dokumentiert. Du könntest Dir bei der Behandlung vom 'ButtonRelease' die `x_root`- und `y_root`-Attribute vom Ereignisobjekt nehmen und mit `Widget.winfo_containing()` das Widget an dieser Position geben lassen. Ungetestet.
Re: Widgetbeladenne Fensterfläche anklicken
Verfasst: Donnerstag 26. Februar 2015, 23:51
von wuf
Hi Goswin
Dank dem Tipp von BlackJack habe ich noch folgendes ausprobiert:
Code: Alles auswählen
#!/usr/bin/python3
#coding=utf8
import tkinter as tk
def main():
def click_callback(event):
event_path = event.widget.winfo_containing(event.x_root, event.y_root)
if str(mainframe1) in str(event_path):
print(str(mainframe1),str(event.widget))
print('MainFrame1-Click')
if str(mainframe2) in str(event_path):
print(str(mainframe2),str(event.widget))
print('MainFrame2-Click')
def release_callback(event):
event_path = event.widget.winfo_containing(event.x_root, event.y_root)
print(event_path)
if str(mainframe1) in str(event_path):
print(str(mainframe1),str(event.widget))
print('MainFrame1-Release')
if str(mainframe2) in str(event_path):
print(str(mainframe2),str(event.widget))
print('MainFrame2-Release')
app_win = tk.Tk()
app_win.geometry('600x300')
app_win.resizable(False,False)
app_win.bind('<Button-1>', click_callback)
app_win.bind('<ButtonRelease-1>', release_callback)
mainframe1 = tk.Frame(app_win, width=200, height=200, bg='cyan')
mainframe1.propagate(False)
mainframe1.pack(expand=True,side=tk.LEFT)
#
subframe1 = tk.Frame(mainframe1, width=100, height=100, bg='green')
subframe1.propagate(False)
subframe1.pack(expand=True)
mainframe2 = tk.Frame(app_win, width=200, height=200, bg='yellow')
mainframe2.propagate(False)
mainframe2.pack(expand=True,side=tk.LEFT)
#
subframe2 = tk.Frame(mainframe2, width=100, height=100, bg='red')
subframe2.propagate(False)
subframe2.pack(expand=True)
app_win.mainloop()
if __name__ == '__main__': main()
Gruss wuf

Re: Widgetbeladenne Fensterfläche anklicken
Verfasst: Freitag 27. Februar 2015, 00:17
von Goswin
Danke euch beiden! Ich benutze das jetzt einmal so wie es ist und verschiebe das Verstehen auf später.
