簡體   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