Python Tkinter画布滚动鼠标位置

2024-04-26 03:02:18 发布

您现在位置:Python中文网/ 问答频道 /正文

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

我试图创建一个根据鼠标位置滚动的窗口:如果鼠标靠近屏幕顶部,它将滚动到顶部;如果它靠近右侧边框,则会滚动到右侧,依此类推。代码如下:

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语句,如下所示:

^{pr2}$

但是当鼠标到达这个位置时,程序就会冻结(我想是等待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)

请告诉我是否正确。感谢大家的讨论和建议。Theselinkswere也很有用。在


Tags: eventifcolumnroot鼠标gridrowcanvas
3条回答

正如您所说,只有在鼠标移动的情况下,这才有效,否则不会触发<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变量可能存在竞争条件,您应该使用锁。在

首先,设置一个方法,滚动窗口很小的量,然后在一段固定的时间后(例如100毫秒)再次调用它自己(如果鼠标在该区域)。您可以使用“after”方法来完成此操作。这样,只要鼠标处于滚动区域,画布就会持续滚动。在

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

这就是你所需要的。只要确保您在任何时候只运行一个tbese滚动作业。在

程序陷入困境的明显原因是x is less than 30它进入循环的那一刻,除非它脱离循环,否则你将无法控制鼠标,除非你能控制鼠标,否则位置将始终是<;30,这样循环将永远得到满足,并且永远不会结束。在

所以,您需要做的是在一个单独的线程中运行这个检查while x < 30。您可以立即初始化线程x becomes less than 30,并从该线程控制滚动。一旦x becomes more than or equal to 30,你就杀死了线程。在

相关问题 更多 >

    热门问题