Python GUIプログラミング tkinter Canvas state Mod3

 見に来てくださってありがとうございます。石川さんです。

 先日書いた記事に修正を加えようと思って、ソースコードを探し出して実行してみたら、なぜかうまく動かなくてずいぶんと悩んでしまいました。今回はそのお悩みを解決した話です。

 修正自体はスクロールバーが必要な時には表示されて、不要な時には非表示になる、という修正をしようと思いたちました(その記事は次回の予定に。。。)。スクロールバー使ったソースはどれかしら、と、探し出して、実行してみたところなんと、ホイールを使ったスクロールがなぜか動作しません。先日、少なくともブログを書いた今年の4月頃までは動作していたのに!
 これはもしや知らないうちにPythonの仕様が変更されたのでは、時々バージョンアップしてるからねぇ、と、Pythonの仕様変更を疑ってしまいました。でも、いやいや待て待て、そんな影響が起きるような仕様変更がそんなに頻繁にあるわけはなかろう、と、気を取り直しまして、、、とりあえず、ソースコードをよく読みこんでみました。
 ええと、ちゃんとホイールが利用されたときのコーディングもされていますね。と、よく見ると、if文の条件が「event.state == 5」となっていますね。このstateは何かのキーが同時に押されると特定のビットを立てていくようになっていたはずです。ちょっと怪しいのでprintして確認してみました。

mouse_wheel: <MouseWheel event state=Mod3 delta=-120 x=231 y=103> 32

 なんと、stateに「Mod3(32 = 2**5)」がセットされている。。。何だこりゃ?!そりゃ、動きませんなぁ。しかし、何でしょうこのMod3は。Google先生に聞いてもなかなか見つけられなかったのですが、、、ありました。ここです。なるほど、ScrollLockですね。値としては、2**5でした。他にもCapsLockでは、2**1が確認できました。ScrollLockとCapsLockが同時にセットされた場合は、2**1 | 2**5となるわけですね。(2進数だと、00100010というように下位から1番目と5番目のビットがセットされてくる。(0オリジンです。))こんな風に、いろいろとセットされてくる可能性があるので、「event.state == 5」でShiftとCtrlが押されていることを判断するのではちょっと足りなかった、ということだったのですね。
 と、いうことでstateをチェックしているところはすべて以下のように修正しました。

if event.state & (2**0 | 2**2) == 5: # Shift|Control
    ...
elif event.state & 2**2 == 4: # Control
    ...
elif event.state & 2**0 == 1: # Shift
    ...
elif event.state & (2**0 | 2**2) == 0: # None
    ...

 ちょっと美しくないので、なんとかしたいところですが、ま、そのうち、おいおいと。

 調べた結果をまとめますと、以下の通りです。マウスクリックしたときの<ButtonPress>イベントについて記載します。

state数値WindowsLinuxMac OSX
Shift2**0 = 1Shiftキー
Lock2**1 = 2Caps Lock
Control2**2 = 4Ctrlキー
Mod12**3 = 8NumLockAltCommand(=Meta)
Mod22**4 = 16NumLockOption(=Alt)
Mod32**5 = 32ScrollLockNumLock
Mod42**6 = 64WinFn
Mod52**7 = 128
0x200002**17 = 131,072Alt
マウスクリックとキー入力状態によるstateの違い(Windows10は実験結果、Linux、MaOSXは未検証、※は不明)

 わからないところが結構あって、埋められていません。どなたかわかる方、教えていただけるとうれしいです。以下のスクリプトを実行すると、イベントがprintされますので、確認してみてください。

import tkinter
c = tkinter.Canvas()
c.pack()
c.bind("<ButtonPress>", print)
c.mainloop()

まとめ

 CtrlキーやShiftキーが押された状態を扱う処理を作る場合は、bit演算が必要です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です


reCaptcha の認証期間が終了しました。ページを再読み込みしてください。