2011-06-27 134 views
0

我认为这是一个很常见的问题,但我找不到答案。Python Tkinter - 滚动鼠标位置的画布

我试图做一个窗口,根据鼠标位置滚动:如果鼠标靠近屏幕顶部,它滚动到顶部,如果它靠近右边框,它滚动到对等等。下面是代码:

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循环来完成我认为)。

有什么建议吗?

在此先感谢。

UPDATE

这里是与工作码(或可能的一个)的答案。我不知道用答案更新问题本身是否正确,但我认为这对其他人有用。

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也有用。

+0

我不是Tkinter的专家,但[this](http://old.nabble.com/Cursor-coordinates-td26644652.html)可能有帮助 – inspectorG4dget

+0

我可以找到这个帮助,除了变量'ws '没有定义。根据上下文,我假设它是坐标 –

回答

0

首先,建立一个滚动窗口少量的方法,然后在一段固定时间后(如100ms)再次调用自己,如果鼠标在该区域。你可以使用“之后”的方法来解决这个问题。有了这个,只要鼠标在滚动区域内,画布就会连续滚动。

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

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

+0

我想我理解你的建议,但玩完后有了这个概念(和其他),我仍然无法弄清楚如何去做。我做了一个自己调用的函数,但是它在鼠标离开滚动区域后继续调用它自己。我仍然在尝试,但是,你可以给我看一个例子或一些伪代码吗?提前致谢。 –

+0

我不知道'after'方法,在这种情况下我认为更合适,因为'Timer'类包含线程。 –

+0

我想我已经想通了,至少它工作正常。我已经用工作代码更新了这个问题。请让我知道我是否做得对(如果代码是好的)。 –

-1

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

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

+0

不,线程对于这个问题太重了。 –

+0

为什么downvote?它可能是重量级的解决方案,但并不正确。至少我让他弄清楚为什么他的程序卡住了。 –

+0

@Guanidene,实际上我不明白你的建议,因为我可以控制鼠标,它只是程序不再响应(仍然,我认为你是正确的,而条件永远满足)。由于这个建议,我搜索了关于“线程”的内容,这是我以前从未听说过的。 –

0

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