2011-06-27 73 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() 

問題は、スクロールの動きがマウスの動きは、(あなたがマウスを動かす停止した場合、スクロールがあまりにも停止)がある場合にのみ動作モーション機能であるということです。私はそれがマウスが動いていなくても(しかし、 "スクロール領域"にいても)ウィンドウが最後までスクロールし続けるようにしたいと思います。

while x < 30: 

しかし、その後、マウスがこの位置プログラムがフリーズ(待機中に到達したとき:

私は明白な方法は、このように、while文に(例えばライン30)からif文を変更することだろうと思いましたwhileループが終わると思う)。

提案がありますか?

ありがとうございます。

UPDATE

は、ここで(または可能性の1)の答えと作業コードです。私は、質問自体を答えで更新するのが正しいかどうか分かりませんが、他人には役立つと思います。

正しいのかどうか教えてください。誰もが議論と提案に感謝します。 Theselinkswereも便利です。

+0

私はTkinterの専門家ではありませんが、[this](http://old.nabble.com/Cursor-coordinates-td26644652.html)は役に立ちます – inspectorG4dget

+0

変数 'ws 'は定義されていません。文脈では、私はそれが座標であると仮定します –

答えて

0

まず、ウィンドウをわずかな量だけスクロールし、マウスがその領域内にある場合は一定の時間(たとえば100ms)後に再び呼び出すメソッドを設定します。あなたはこれをやるために "after"メソッドを使うことができます。これにより、マウスがスクロール領域にある限り、キャンバスは連続的にスクロールします。

次に、カーソルが最初にスクロールゾーンに入るときにこの関数を呼び出すバインディングを作成します。

これだけです。一度に1つのスクロールジョブしか実行していないことを確認してください。

+0

私はあなたの提案を理解したと思いますが、このコンセプト(と他のもの)では、私はまだそれを行う方法を理解することができません。自分自身を呼び出す関数を作ったが、マウスがスクロール領域を離れると、それ自身を呼び出す。私はまだそれを試していますが、あなたは私に例またはいくつかの疑似コードを表示できますか?前もって感謝します。 –

+0

私は 'after'メソッドを知りませんでした。これは、' Timer'クラスにスレッドが含まれているので、このケースではより適していると思います。 –

+0

私は少なくとも、それが働いているとわかったと思います。質問を作業コードで更新しました。私がそれを正しく行ったかどうかを教えてください(コードが良い場合)。 –

-1

あなたのプログラムが立ち往生する明らかな理由は、ループに入る瞬間であり、ループから抜け出すまでは、マウスを制御することはできません。また、マウスを制御できなければ位置は常に< 30になるので、ループは永遠に満足され、決して終わらないでしょう。

このチェックをwhile x < 30の別のスレッドで実行する必要があります。スレッドx becomes less than 30を初期化し、そのスレッドからスクロールを制御することができます。瞬間x becomes more than or equal to 30、あなたはスレッドを殺す。

+0

いいえ、スレッドはこの問題のためにあまりにも重すぎます。 –

+0

なぜdownvote?それは重量のある解決策かもしれないが、間違っていない。私は彼のプログラムがなぜ詰まっているのか理解しました。 –

+0

@Guanidene、実際に私はあなたの提案を理解できませんでした。なぜなら、私はマウスを制御することができたからです。プログラムがもはや応答しないことです(まだ、あなたは永遠に満足している状態について正しいと思います)。そして提案のために、私はこれまでに聞いたことのない「スレッド」について検索しました。 –

0

これは、マウスが動いている場合にのみ機能します。それ以外の場合は<Motion>イベントは発生しません。マウスがスクロール領域にある場合に限り、タイムアウト後にトリガーされるタイマーを使用できます。私は、コードをチェックしませんでしたし、おそらく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 

と:以下は、私はActiveState社で見つかったresettable timerを使用してちょうど擬似コードでありますロックを使用する必要があります。

関連する問題