【问题标题】:Python Tkinter - canvas scrolling with mouse positionPython Tkinter - 使用鼠标位置滚动画布
【发布时间】:2011-06-27 15:35:39
【问题描述】:

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

我正在尝试制作一个根据鼠标位置滚动的窗口:如果鼠标靠近屏幕顶部,它会滚动到顶部,如果它靠近右边框,它会向右滚动等等。代码如下:

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)

请让我知道它是否正确。感谢大家的讨论和建议。 Theselinkswere也很有用。

【问题讨论】:

  • 我不是 Tkinter 专家,但 this 可能会有所帮助
  • 我觉得这很有帮助,除了变量 ws 没有定义。根据上下文,我假设它是坐标

标签: python scroll mouse tkinter


【解决方案1】:

首先,设置一个将窗口滚动一小部分的方法,然后如果鼠标在该区域中,则在某个固定时间段(例如 100 毫秒)后再次调用自身。您可以使用“之后”的方法来执行此操作。这样,只要鼠标在滚动区域,画布就会不断滚动。

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

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

【讨论】:

  • 我想我理解了你的建议,但是在玩过这个概念(和其他)之后,我仍然不知道该怎么做。我做了一个调用自己的函数,但是在鼠标离开滚动区域后它会一直调用自己。我还在尝试,但是,你能给我看一个例子或一些伪代码吗?提前致谢。
  • 我不知道after 方法,我认为它更适合这种情况,因为Timer 类涉及线程。
  • 我想我已经知道,至少,它正在工作。我已经用工作代码更新了这个问题。如果我做对了,请告诉我(如果代码是好的)。
【解决方案2】:

正如您所说,这仅在鼠标移动时才有效,否则不会触发 &lt;Motion&gt; 事件。您可以使用超时后触发的计时器,并且仅当鼠标位于滚动区域时。以下只是一个伪代码,它使用了我在 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 变量可能存在竞争条件,您应该使用锁。

【讨论】:

    【解决方案3】:

    您的程序卡住的明显原因是x is less than 30 进入循环的那一刻,除非它退出循环,否则您将无法控制鼠标,除非您能够控制鼠标鼠标,位置将始终

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

    【讨论】:

    • 为什么投反对票?它可能是重量级的解决方案,但它不是不正确的。至少我让他弄清楚了为什么他的程序卡住了。
    • @Guanidene,其实我不明白你的建议,因为我可以控制鼠标,只是程序不再响应(不过,我认为你是对的 while 条件是永远满意)。由于这个建议,我搜索了以前从未听说过的“线程”。
    • @Marcos:如果您以前从未听说过线程,那么此解决方案绝对不适合您。线程增加了相当多的复杂性,其中一些非常微妙。
    • @Guanidene:我投反对票的原因与我反对使用割草机剪头发的建议相同。我觉得答案不太有用,因为它把一个人送错了路。线程对线程有益的事情有好处,但对其他一切都很糟糕。这个问题属于“其他所有”类别恕我直言。
    • @Macros - 我的意思是你不能控制鼠标,你的鼠标移动事件不会被你的程序接受,除非它退出 while 循环......
    猜你喜欢
    • 2014-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-05
    • 1970-01-01
    • 2014-06-04
    • 1970-01-01
    • 2014-01-20
    相关资源
    最近更新 更多