繁体   English   中英

Python Tkinter-使用鼠标位置滚动画布

[英]Python Tkinter - canvas scrolling with mouse position

我认为这是一个非常常见的问题,但我找不到答案。

我正在尝试创建一个根据鼠标位置滚动的窗口:如果鼠标靠近屏幕顶部,则滚动到顶部,如果靠近右侧边框,则滚动到右边,依此类推。 这是代码:

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)

请让我知道是否正确。 感谢大家的讨论和建议。 这些 链接 很有用。

如您所说,这仅在鼠标移动时有效,否则不会触发<Motion>事件。 您可以使用仅在鼠标位于滚动区域中时才在超时后触发的计时器。 以下只是使用我在ActiveState中找到的可重置计时器的伪代码:

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变量上存在竞争条件,您应该使用锁。

首先,设置一种将窗口滚动很小的方法,然后如果鼠标在该区域中,则经过一定的固定时间(例如100ms)后再次调用自身。 您可以使用“之后”方法来执行此操作。 这样,只要鼠标位于滚动区域中,画布就会连续滚动。

接下来,创建一个绑定,当光标第一次进入滚动区域时,调用此函数。

这就是您所需要的。 只要确保您一次只能运行tbese滚动作业之一即可。

程序卡住的明显原因是,当x is less than 30它进入循环,除非它退出循环,否则您将无法控制鼠标,除非您能够控制鼠标,位置将始终为<30,因此您的循环将永远得到满足,并且永远不会结束。

因此,您需要做的是在单独线程中while x < 30运行此检查。 您可以在x becomes less than 30初始化线程,并从该线程控制滚动。 x becomes more than or equal to 30的那一刻,您杀死了线程。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM