rcmdnk's blog

20171110_ahktooltip_200_200

AutoHotkeyでキー入力したときや Gui上のオブジェクトの上にマウスを持っていったときとかに ToolTipを出す方法について。

Sponsored Links

ToolTip

1
ToolTip [, Text, X, Y, WhichToolTip]

な感じでToolTipを出現させられます。

ToolTip

Textに出現させたい文字列を入れればその文字列が出ます。 (`nで改行もいれられる。)

Textに何も入れないで実行すると表示されているToolTipを消しせます。

XYは出現位置で CoordModeToolTipの値によってスタート地点が変わりますが、 初期値ではWindowになっていて、 0, 0とすれば現在のウィンドウの左上にひっつくような表示になります。

1
2
WinGetPos, , , W, H, A
Tooltip, right bottom, W, H

とかすればウィンドウの右下から外側にくっつく様なToolTipに。

X, Yが省略されるとマウスの所に出現します。

WhichToolTipはToolTipに振られる番号で、1から20を指定できます。 省略すると1番になります。複数のToolTipを表示させたい場合には 違う番号を指定します。(そうしないと前のものを上書きしてしまいます。)

キー入力イベントで表示させる簡単な例

簡単に、Aを押すと1秒だけToolTipを出すやり方としては

tooltip.ahk
1
2
3
4
5
a::
  ToolTip, tool tip A
  Sleep, 1000
  ToolTip
Return

こんな方法。ToolTipを出して、 Sleep で一秒待ってからText無しのToolTipコマンドで削除します。

Sleepに与える引数はミリ秒なので1000で一秒です。

次に複数のToolTipを出してみます。

tooltip_multi.ahk
1
2
3
4
5
6
7
8
9
a::
  ToolTip, tool tip 1
  ToolTip, tool tip 2, 100, 100
  ToolTip, tool tip 3, 200, 200, 2
  Sleep, 1000
  ToolTip
  Sleep, 1000
  ToolTip, , , , 2
Return

これを実行してAを押すと、 tool tip 2tool tip 3が出ますが、 ToolTip番号1はtool tip 2に上書きされるので tool tip 1は出ません。

その後、1秒たってtool tip 2が消え、さらに1秒経ってからtool tip 3が消えます。

ただし、このSleepを使った方法はちょっと問題があって、 1秒経つ前にAを押した場合、 再びtool tip 2が表示される命令が出てこれはそのまま表示され続けるままですが、 以前のSleep後のToolTipが生きているので前にAを押したときから 1秒のタイミングで消えてしまいます。

この様な事を避けるため、通常は次のSetTimerを使った方法を使います。

SetTimerを使った例

SetTimer を使うと指定時間の途中で消したりすることが可能になります。

tooltip_settimer.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
a::
  ToolTip, tool tip
  SetTimer, RemoveToolTip, 10000
Return

RemoveToolTip:
  SetTimer, RemoveToolTip, Off
  ToolTip
Return

b::
  Gosub, RemoveToolTip
Return

AでToolTipを出した後、SetTimerを使っています。 SetTimerでは最初の引数でラベルを指定し、2つめに数字があると その時間(ミリ秒)のタイマーを作動し、その時間分待った後にラベルで指定された動作を実行します。

RemoveToolTipというラベルの中では Text無しのToolTipで削除しています。 最初のタイマーが10000ミリ秒(10秒)で作動するのでここではツールチップが10秒間表示されることになります。

また、表示中にAをもう一度押すと ToolTipがもう一度実行されますがこの際は同じメッセージなのでそのまま。 その後SetTimerによってタイマーがリセットされるので Sleepを使ったときと違い、最後に押したときから10秒経ってから消えることになります。

RemoveToolTipではさらにToolTipの直前でSetTimerOffをしています。 2つ目にOffがあるとそのタイマーは停止されますが、 最初のSetTimerからラベルを伝って来る場合には既にタイマーが終了した後なので 意味はありません。

一方、これをいれておくと、Bでこのラベルを呼出し、 途中で消すことが出来ます。

Gosub はラベルにジャンプしてReturnで戻ってくる機能です。 Goto という似たような機能がありますが、こちらは行ったまま戻ってきません。 ここでは直後にReturnしてるのでどちらでも同じですが、 Goto的なものは混乱の元なのでGosubで関数的に呼び出して戻ってくる、 と言った感じにした方が分かりやすくなります。

ここで、Bに対して単に

1
2
3
b::
  ToolTip
Return

の様にToolTipを直接消す様に定義しても全く同じことが出来ます。 ただこの場合は裏でタイマーは動き続けていて3秒後にToolTipを実行します。 実行してもTextが無いので結局何も起こりませんが。

これだけの例だとそこまで気にする必要はありませんが、 与えたラベルの中で色々やる場合に、複数回起動されると困ることもあるでしょうし、 単に裏で無駄なプロセスが残っているのも気持ち悪いので きちんと消すようにした方が良いです。

同様に、Sleepを使ってる場合でもBToolTipを実行させて 無理やり消すことも出来ますが、 この場合もSleep後の実行命令は残りますし、 上にも書いたようにSleepでの実行には問題があるので SetTimerを使った方が良いです。

GuiのオブジェクトにマウスオーバーでToolTipを表示させる

Tooltipを直接表示させることは出来ましたが、 Webや他のアプリで良くあるのは何らかのものの上にマウスを持っていった時に 表示されるToolTipです。

これを実現するためには以下の様な事をやります。

tooltip_gui.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
; Global variables
CurrControl := ""
PrevControl := ""

; Prepare Gui
Gui, Add, Edit, vMyEdit
Gui, Add, DropDownList, Y+10 vMyDDL, Red|Green|Blue
Gui, Add, Checkbox, Y+10 vMyCheck, This control has no tooltip.
Gui, Add, Text, Y+10 vMyTextNoGLabel, No tooltip for text without g-label.
Gui, Add, Text, gMyTextGLabel vMyTextGLabel, Tooltip can be assigned for text with g-label.
Gui, Show

; Prepare tooltip messages
MyEdit_TT := "This is a tooltip for the control whose variable is MyEdit."
MyDDL_TT := "Choose a color from the drop-down list."
MyTextNoGLabel_TT := "This text cannot be shown."
MyTextGLabel_TT := "This is a tooltip for text with g-label."

; Set function on WM_MOUSEMOVE
OnMessage(0x200, "MyMouseMove")

; End of auto-execute section
Return

; Dummy label to assign Gui Control to the text
MyTextGLabel:
Return

MyMouseMove(){
  global CurrControl, PrevControl
  CurrControl := A_GuiControl

  ; Do only when GuiControl is changed.
  If(CurrControl != PrevControl){
    PrevControl := CurrControl

    ; Delete current tooltip
    ToolTip

    ; No space check (InStr(...)) is also needed,, to avoid error at %CurrControl%_TT.
    if(CurrControl != "" && InStr(CurrControl, " ") == 0){
      ; Show tooltip if mouse stays more than 1 sec.
      SetTimer, DisplayToolTip, 1000
    }
  }

  return
}

DisplayToolTip:
  SetTimer, DisplayToolTip, Off
  ; First, interpret CurrControl to string, then interpret %CurrControl%_TT to string (done by the first %)
  ToolTip % %CurrControl%_TT
  SetTimer, RemoveToolTip, 60000
return

RemoveToolTip:
  SetTimer, RemoveToolTip, Off
  ToolTip
return

GuiClose:
  ExitApp
Return

ahktooltip

以下のページで出てる解答にちょっと手を加えたものになっています。

(SOLVED) gui control tooltip on hover? - Page 2 - Ask for Help - AutoHotkey Community

Guiに関しては以下参照。

まず、いくつかのGuiのコンテンツをいれたウィンドウを表示させています。

Showした後に、 OnMessage という関数がありますが、 これは特定のメッセージ番号を受け取った時に実行する関数を指定できる 関数です。

最初の引数がメッセージ番号で2つめに関数名を文字列で書きます。

メッセージはSendMessage, 0x200の様にスクリプト内で送ることも出来ますが、 それ以外にもGui上の操作などでも特定の番号でAutoHotkeyに送られてきます。

List of Windows Messages

ここに

...
WM_MOUSEFIRST = 0x200
WM_MOUSEMOVE = 0x200
WM_LBUTTONDOWN = 0x201
WM_LBUTTONUP = 0x202
WM_LBUTTONDBLCLK = 0x203
...

とありますが、0x200WM_MOUSEFIRST及びWM_MOUSEMOVEにあてられています。 これはGuiウィンドウの上でマウスが動いた時にメッセージを送ることになります。

その後のWM_LBUTTONDOWNCLK(0x203)なら左クリックした時にメッセージを送ることになります。

これを使うことでGuiウィンドウ上でマウスが動いた時に常に MyMouseMoveが実行される事になります。

MyMouseMoveでは、 CurrControlPrevControlという2つのグローバル変数を使っています。

上の参考URLの中ではstaticを使って関数内で次回も使える変数にして いますが、これによって DisplayToolTipも関数内に書く様な形になっています。

2つの値を他の余計な所で書き換えられない、という点はstaticの方が便利ですが、 ラベルは外に出したほうがすっきりするかな、ということで 上の様な書き方をしてみました。

その上でまずA_GuiControlという値を取ってきています。 A_GuiControlは命令を実行させたGui Controlが入っていますが、 v-labelが指定されてる時はv-labelが、 されてない時はテキストが入ります。

この場合はマウスの下にあるオブジェクトのものが入ることになります。

Gui, Add, Edit, vMyEdit

ならMyEdit

Gui, Add, Edit, , MyEditText

ならMyEditTextの部分がA_Controlに入ります。

両方ない場合は空。

ただし、Textはちょっと注意が必要で そのままv-labelを与えてもテキストを与えてもGui Controlではないので A_GuiControlに何も入りません。 (上の参考URLの最後の質問にこれに関すると思われるものもありますが。)

コントロールオブジェクトにするためにはg-labelを付けます。 これで押した時にする動作を付けられるわけですが、 今回はこれに何もしない動作をDummyラベルを割り当てています、 これにより、押しても何もしないテキストですが Gui Controlとなり、A_GuiControlで値が取得できる様になります。

値を取得したら、前回と変わってなければ何もせずに終了。

上のURLの例だと!=の代わりに< >を使っていますが同じ意味です。 また、InStr(...)" "と比べ、空白を含まないチェックをしていますが、 これは空白を含むと、あとで

1
ToolTip % %CurrControl%_TT

の所で、%CurrControl%の値と_TTをくっつけた名前の変数の中身を取り出す時に 変数名に空白があるとエラーが出てしまうからです。

ただ、その様な別の要素に移った際にもTooltipを消してリセットするのが正しいので 変更後はまずA_GuiControlが変わったかどうかだけ見てPrevControlをセットし ToolTipで一度ToolTipを削除しています。

その次に、A_GuiControlが空白(何も無いスペースやTextなどでGui Controlオブジェクトじゃないもの、v-labelもテキストも無いもの)だったり、 スペースを含むような値(大概はTextでg-labelを設定してないもの) の場合には何もせずに終了します。

この部分に関してもう少し。参考URLの方では …and not InStr(CurrControl, " ")みたいなことをしていますがこれは上手く動きませんでした。

InStrは2つ目の引数が最初に現れる位置を返しますが、なければ0を返します。 0がfalseなのでnotにしてandでつなげれば同じことなはずなのですが。 これの&&だけ変更したり...==0だけ変更したりしても上手く行きませんでした。 エラーになるわけではなく、CurrControl != ""の真偽だけで全て決まってしまうような形になっていました。 何か勘違いしてる気がしますが、とりあえず上に書いた形だと期待通りに動いてくれました。

いずれにせよ、もし使えるA_GuiControlだった場合、 DisplayToolTipを1秒のタイマーでセットします。

これによっていきなり出すのではなく、1秒以上マウスが同じオブジェクトの上に居た場合にToolTip を出すことが出来ます。

DisplayToolTipでは上でも説明したとおり%CurrControl%_TTを ToolTipに出していますが、 この値は Prepare tooltip messagesのところで作っています。 この様にGui Control名+_TT (_TTでなくてもいいけど適当な共通のもの) をToolTipの内容として作っておくことで こんな感じの動作を実現することが出来ます。

これとは別の方法で実現しようとする以下のような解答もありました。

ToolTip in Gui - Ask for Help - AutoHotkey Community

ここでは MouseGetPos を使ってGui Controlの情報を取ってきていますが、 ここで取れるものはButton1とかButton2とかで、 ClassNNと呼ばれるコントロール群の名前+番号となっています。

従って配置した順序によって取れる値が変わるためちょっと使いづらいものになっています。

Sponsored Links
Sponsored Links

« AutoHotkeyで設定ファイルの読み書きをする vim_ahkでメニューから設定変更出来る様にした »