我一直在尝试制作一个程序,根据窗口的宽度有 3 种布局(使用 Tkinter)。
问题出在中等布局上。每当我将鼠标放在左侧画布上并使用 MouseWheel 时,右侧画布就会滚动,而左侧画布应该滚动。当我的鼠标放在右侧画布上并使用鼠标滚轮时,右侧画布会滚动。我找不到任何解决方案。
import tkinter as tk
from tkinter import ttk
import random as rand
class App(tk.Tk):
def init(self, Geometry, Icon, MinimumSizesDictionary):
super().init()
self.title("Practise")
self.geometry(f"{Geometry[0]}x{Geometry[1]}")
self.minsize(Geometry[0], Geometry[1])
self.iconbitmap(Icon)
# variables
self.MinimumSizesDictionary = MinimumSizesDictionary
# widgets
self.LayoutNSizeChecker = LayoutNSizeChecker(self, self.MinimumSizesDictionary)
# run
self.mainloop()
class LayoutNSizeChecker(ttk.Frame):
def __init__(self, Parent, MinimumSizesDictionary):
super ().__init__(Parent)
self.pack(expand = True, fill = "both")
self.MinimumSizesDictionary = MinimumSizesDictionary
self.MinimumSize = self.CurrentMinimumSize = None
self.Layout = None
# binding an event to the main frame
self.bind("<Configure>", lambda event : self.Update())
# binding an event to the window
Parent.bind("<Configure>", lambda event : self.CheckScreenSize(event, Parent))
def CheckScreenSize(self, event, Window):
if event.widget == Window:
for Key, Value in self.MinimumSizesDictionary.items():
self.Delta = event.width - Key
if self.Delta >= 0: self.MinimumSize = Key
if self.CurrentMinimumSize != self.MinimumSize:
self.CurrentMinimumSize = self.MinimumSize
if self.CurrentMinimumSize == 300:
for Widget in self.winfo_children():
Widget.destroy()
self.Destroyed = True
print("small layout")
self.Layout = "Small"
# variables
self.NumberOfButtons = 20
self.SpaceEachRowTakes = 100
self.TotalHeight = self.NumberOfButtons * self.SpaceEachRowTakes
# canvas
self.Canvas = tk.Canvas(self, scrollregion = (0, 0, self.winfo_width(), self.TotalHeight))
self.Canvas.pack(expand = True, fill = "both")
# canvas frame
self.CanvasFrame = ttk.Frame(self)
# scroll bar
self.ScrollBar = ttk.Scrollbar(self, orient = "vertical", command = self.Canvas.yview)
self.ScrollBar.place(relx = 1, rely = 0, relheight = 1, anchor = "ne")
self.Canvas.config(yscrollcommand = self.ScrollBar.set)
# scroll using the mousewheel
self.Canvas.bind_all("<MouseWheel>", lambda event : self.Scroll(event))
for i in range(1, 21):
self.FramePortion = ttk.Frame(self.CanvasFrame)
# variables
self.TextData = ["hello!", "goodbye!", "cya", "innit", "lol", "xd", "alr", "bro", "dude", "wsg"]
# making the grid
self.FramePortion.rowconfigure(0, weight = 1, uniform = "z")
self.FramePortion.columnconfigure((0, 1, 2), weight = 1, uniform = "z")
# the widgets
self.Button1 = ttk.Button(self.FramePortion, text = i)
self.Button2 = ttk.Button(self.FramePortion, text = rand.choice(self.TextData))
self.Button3 = ttk.Button(self.FramePortion, text = rand.choice(self.TextData))
# placing the widgets onto the grid we created earlier
self.Button1.grid(row = 0, column = 0, sticky = "nesw", padx = 5, pady = 5)
self.Button2.grid(row = 0, column = 1, sticky = "nesw", padx = 5, pady = 5)
self.Button3.grid(row = 0, column = 2, sticky = "nesw", padx = 5, pady = 5)
self.FramePortion.pack(expand = True, fill = "both")
self.Destroyed = False
elif self.CurrentMinimumSize == 600:
print("medium layout")
self.Layout = "Medium"
for Widget in self.winfo_children():
Widget.destroy()
self.Destroyed = True
# grid for main frame
self.rowconfigure(0, weight = 1, uniform = "z")
self.columnconfigure((0, 2), weight = 6, uniform = "z")
self.columnconfigure((1, 3), weight = 1, uniform = "z")
# left side layout
self.LeftFrame = ttk.Frame(self)
self.LeftFrame.grid(row = 0, column = 0, sticky = "nesw")
self.LeftCanvas = tk.Canvas(self.LeftFrame, scrollregion = (0, 0, self.LeftFrame.winfo_width(), self.TotalHeight / 2))
self.LeftCanvas.pack(side = "left", pady = 10, expand = True, fill = "both")
self.LeftCanvasFrame = ttk.Frame(self.LeftCanvas)
# scroll bar
self.LeftScrollBar = ttk.Scrollbar(self, orient = "vertical", command = self.LeftCanvas.yview)
self.LeftScrollBar.grid(row = 0, column = 1, sticky = "ns")
self.LeftCanvas.config(yscrollcommand = self.LeftScrollBar.set)
# scroll using the mouse wheel
self.LeftCanvas.bind_all("<MouseWheel>", lambda event : self.LeftScroll(event))
#
for i in range(1, 11):
self.LeftFramePortion = ttk.Frame(self.LeftCanvasFrame)
self.Button4 = ttk.Button(self.LeftFramePortion, text = i)
self.Button5 = ttk.Button(self.LeftFramePortion, text = rand.choice(self.TextData))
self.Button6 = ttk.Button(self.LeftFramePortion, text = rand.choice(self.TextData))
self.Button4.pack(expand = True, fill = "both", padx = 2.5, pady = 2.5)
self.Button5.pack(expand = True, fill = "both", padx = 2.5, pady = 2.5)
self.Button6.pack(expand = True, fill = "both", padx = 2.5, pady = 2.5)
self.LeftFramePortion.pack(expand = True, fill = "both")
# right side layout
self.RightFrame = ttk.Frame(self)
self.RightFrame.grid(row = 0, column = 2, sticky = "nesw")
self.RightCanvas = tk.Canvas(self.RightFrame, scrollregion=(0, 0, self.RightFrame.winfo_width(), self.TotalHeight / 2))
self.RightCanvas.pack(side = "left", pady = 10, expand = True, fill = "both")
self.RightCanvasFrame = ttk.Frame(self.RightCanvas)
# scroll bar
self.RightScrollBar = ttk.Scrollbar(self, orient = "vertical", command = self.RightCanvas.yview)
self.RightScrollBar.grid(row = 0, column = 3, sticky = "ns")
self.RightCanvas.config(yscrollcommand = self.RightScrollBar.set)
# scroll using the mouse wheel
self.RightCanvas.bind_all("<MouseWheel>", lambda event : self.RightScroll(event))
for i in range(11, 21):
self.RightFramePortion = ttk.Frame(self.RightCanvasFrame)
self.Button7 = ttk.Button(self.RightFramePortion, text = i)
self.Button8 = ttk.Button(self.RightFramePortion, text = rand.choice(self.TextData))
self.Button9 = ttk.Button(self.RightFramePortion, text = rand.choice(self.TextData))
self.Button7.pack(expand = True, fill = "both", padx = 2.5, pady = 2.5)
self.Button8.pack(expand = True, fill = "both", padx = 2.5, pady = 2.5)
self.Button9.pack(expand = True, fill = "both", padx = 2.5, pady = 2.5)
self.RightFramePortion.pack(expand = True, fill = "both")
self.Destroyed = False
elif self.CurrentMinimumSize == 1000:
print("large layout")
self.Layout = "Large"
for Widget in self.winfo_children():
Widget.destroy()
self.Destroyed = True
def Update(self):
if not self.Destroyed and self.Layout == "Small":
self.Canvas.create_window(
(0, 0),
anchor = "nw",
window = self.CanvasFrame,
width = self.winfo_width(),
height = self.TotalHeight
)
elif not self.Destroyed and self.Layout == "Medium":
self.LeftCanvas.create_window(
(0, 0),
anchor = "nw",
window = self.LeftCanvasFrame,
width = self.LeftFrame.winfo_width(),
height = self.TotalHeight / 2
)
self.RightCanvas.create_window(
(0, 0),
anchor = "nw",
window = self.RightCanvasFrame,
width = self.RightFrame.winfo_width(),
height = self.TotalHeight / 2
)
def Scroll(self, event):
if not self.Destroyed and self.Layout == "Small":
self.Canvas.yview_scroll(-int(event.delta / 60), "units")
def LeftScroll(self, event): # left scroll bar function for medium layout
self.LeftCanvas.yview_scroll(-int(event.delta / 60), "units")
def RightScroll(self, event): # right scroll bar function for medium layout
self.RightCanvas.yview_scroll(-int(event.delta / 60), "units")
App = App([300, 300], "icon.ico", {300 : "Small", 600 : "Medium", 1000 : "Large"})
一如既往地在中等布局中,每当我使用鼠标滚轮时,无论如何它都会滚动右侧画布。
这是因为您在两个画布上都使用了
bind_all()
作为事件<MouseWheel>
,所以最后一个绑定将覆盖前一个。
您可以
bind_all()
到公共回调并找到相应的要滚动的画布小部件:
...
self.LeftCanvas.bind_all("<MouseWheel>", self.scroll_canvas) # bind to common callback
...
self.RightCanvas.bind_all("<MouseWheel>", self.scroll_canvas) # bind to common callback
...
以及常见的回调:
def scroll_canvas(self, event):
# find the canvas widget
widget = event.widget
root = widget.winfo_toplevel()
while widget != root and not isinstance(widget, tk.Canvas):
widget = widget.master
# if it is a canvas widget, do the scrolling
if isinstance(widget, tk.Canvas):
widget.yview_scroll(-event.delta//60, 'units')