2011-06-27 130 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變量,你應該使用一個鎖。