我认为这是一个非常常见的问题,但我找不到答案。
我正在尝试制作一个根据鼠标位置滚动的窗口:如果鼠标靠近屏幕顶部,它会滚动到顶部,如果它靠近右边框,则滚动到右边等等。这是代码:
from tkinter import *
from tkinter import ttk
root = Tk()
h = ttk.Scrollbar(root, orient = HORIZONTAL)
v = ttk.Scrollbar(root, orient = VERTICAL)
canvas = Canvas(root, scrollregion = (0, 0, 2000, 2000), width = 600, height = 600, yscrollcommand = v.set, xscrollcommand = h.set)
h['command'] = canvas.xview
v['command'] = canvas.yview
ttk.Sizegrip(root).grid(column=1, row=1, sticky=(S,E))
canvas.grid(column = 0, row = 0, sticky = (N,W,E,S))
h.grid(column = 0, row = 1, sticky = (W,E))
v.grid(column = 1, row = 0, sticky = (N,S))
root.grid_columnconfigure(0, weight = 1)
root.grid_rowconfigure(0, weight = 1)
canvas.create_rectangle((0, 0, 50, 50), fill = 'black')
canvas.create_rectangle((500, 500, 550, 550), fill = 'black')
canvas.create_rectangle((1500, 1500, 1550, 1550), fill = 'black')
canvas.create_rectangle((1000, 1000, 1050, 1050), fill = 'black')
def xy_motion(event):
x, y = event.x, event.y
if x < 30:
delta = -1
canvas.xview('scroll', delta, 'units')
if x > (600 - 30):
delta = 1
canvas.xview('scroll', delta, 'units')
if y < 30:
delta = -1
canvas.yview('scroll', delta, 'units')
if y > (600 - 30):
delta = 1
canvas.yview('scroll', delta, 'units')
canvas.bind('<Motion>', xy_motion)
root.mainloop()
问题在于滚动运动在运动功能中,只有在鼠标移动时才有效(如果停止移动鼠标,滚动也会停止)。我想让它成为一种方式,即使鼠标没有移动(但仍然在“滚动区域”),窗口将继续滚动直到它到达终点。
我认为显而易见的方法是将if语句(例如从第30行)更改为while语句,如下所示:
while x < 30:
但是当鼠标到达此位置时,程序会冻结(等待while循环完成,我认为)。
有什么建议吗?
提前致谢。
更新
这是带有(或可能的)答案的工作代码。我不知道用答案更新问题本身是否正确,但我认为这对其他人有用。
x, y = 0, 0
def scroll():
global x, y
if x < 30:
delta = - 1
canvas.xview('scroll', delta, 'units')
elif x > (ws - 30):
delta = 1
canvas.xview('scroll', delta, 'units')
elif y < 30:
delta = -1
canvas.yview('scroll', delta, 'units')
elif y > (ws - 30):
delta = 1
canvas.yview('scroll', delta, 'units')
canvas.after(100, scroll)
def xy_motion(event):
global x, y
x, y = event.x, event.y
scroll()
canvas.bind('<Motion>', xy_motion)
答案 0 :(得分:0)
正如您所说,只有在鼠标移动时才有效,否则不会触发<Motion>
事件。您可以使用在超时后触发的计时器,并且仅当鼠标位于滚动区域时才使用。以下只是一个伪代码,它使用了我在ActiveState中找到的resettable timer:
TIMEOUT = 0.5
timer = None
def _on_timeout(event):
global timer
scroll_xy(event)
timer = TimerReset(TIMEOUT, _on_timeout, [event])
timer.start()
def xy_motion(event):
global timer
if is_in_scrollable_area(event):
if timer is None:
timer = TimerReset(TIMEOUT, _on_timeout, [event])
timer.start()
else:
timer.reset()
scroll_xy(event)
elif timer is not None:
timer.cancel()
timer = None
请注意这些只是想法,我没有检查代码,可能在timer
变量上有竞争条件,你应该使用锁。
答案 1 :(得分:0)
首先,设置一个滚动窗口少量的方法,然后在一段固定的时间(例如100ms)后再次调用自身,如果鼠标在该区域中。您可以使用“之后”方法来解决此问题。这样,只要鼠标位于滚动区域,画布就会不断滚动。
接下来,创建一个在光标第一次进入滚动区时调用此函数的绑定。
这就是你所需要的。只需确保您在任何时候只运行一个tbese滚动作业。
答案 2 :(得分:-1)
程序卡住的明显原因是它进入循环的那一刻x is less than 30
除非它离开循环,否则你将无法控制鼠标,除非你能够控制鼠标,位置将始终<&lt; 30所以你的循环将永远满足,永远不会结束。
因此,您需要做的是在一个单独的线程中运行此检查while x < 30
。您可以在x becomes less than 30
时刻初始化线程,并从该线程控制滚动。那一刻x becomes more than or equal to 30
,你杀了线程。