PythonのGUIライブラリ「wxPython」でデスクトップアプリを開発している時、マウスカーソルの見た目を変更したいと思ったことはありませんか?
例えば、
- クリックできるボタンやリンクの上に来たら「手の形」にしたい。
- 重い処理が実行されている間は「砂時計」にして、処理中だと伝えたい。
- テキスト入力欄では「Iビーム(縦線)」にしたい。
このように、状況に応じてマウスカーソルの形状を変えることは、ユーザーが次に何をすべきかを直感的に理解する手助けとなり、アプリの「使いやすさ=UX(ユーザー体験)」を大きく向上させます。
「カーソルの変更」と聞くと、なんだかOSの設定をいじるようで難しそうに聞こえるかもしれません。
しかし、ご安心ください。wxPythonには**wx.StockCursor**という、非常に簡単な方法が用意されています。これを使えば、OSが標準で用意しているお馴染みのカーソルを、たった1行のコードで呼び出すことができます。
この記事では、wxPython初心者から中級者の方に向けて、wx.StockCursorの基本的な使い方と、代表的なカーソルの種類、そして実際のウィジェットに設定する方法までを、分かりやすく解説します。
(バージョン情報) この記事で紹介するwx.StockCursorは、wxPythonの基本的な機能であり、wxPython 4系など、現在広く使われているバージョンで標準的にサポートされています。
本記事で解説するwx.StockCursorの公式ドキュメントはこちらです。
wx.Cursorとwx.StockCursorとは?
まず、カーソルを扱う上で基本となる2つの要素、「wx.Cursor」と「wx.StockCursor」の違いを理解しましょう。
wx.Cursorクラスの基本的な役割
**wx.Cursor**は、マウスカーソルの画像や形状そのものを表す「オブジェクト(モノ)」です。
アプリのカーソルを変更するということは、このwx.Cursorオブジェクトを(自分で作るか、どこかから持ってくるかして)準備し、それを「このウィンドウ(またはボタン)のカーソルとして使ってください」と設定する作業を指します。
wx.Cursorは、自作の画像ファイル(.pngや.bmpなど)からカスタムカーソルを作ることもできますが、今回はもっと簡単な方法を使います。
wx.StockCursorとは?(今回の主役)
**wx.StockCursor**は、wx.Cursorオブジェクトを簡単に作成するための便利な「関数」です。
“Stock”とは「在庫品」や「標準品」といった意味があります。その名の通り、wx.StockCursorは、WindowsやmacOS、LinuxといったOSが標準で用意している(=在庫している)カーソルを呼び出すためのものです。
例えば、「手の形のカーソルが欲しい」と思った時、画像ファイルを自分で探してくる必要はありません。 wx.StockCursor(wx.CURSOR_HAND)と書くだけで、OS標準の「手の形」のwx.Cursorオブジェクトを自動で取得してきてくれます。
この手軽さがwx.StockCursorの最大の魅力です。
wx.StockCursorで使える!代表的なカーソルID一覧
wx.StockCursorに渡すことができる「カーソルID」はたくさん定義されています。 ここでは、UI開発で特によく使う代表的なものをピックアップして紹介します。
これだけ知っていれば、日常的な開発で困ることはほとんどないでしょう。
1. wx.CURSOR_ARROW (標準の矢印)
これがデフォルト(初期状態)のカーソルです。お馴染みの白い矢印ですね。 何かのカーソルに変更した後、標準に戻したい時にも使えます。
2. wx.CURSOR_HAND (手の形)
今回のタイトルにもある「手のカーソル」です。 主にハイパーリンクや、ボタンのように「ここがクリックできる」ということをユーザーに明示したいウィジェットの上で使います。
3. wx.CURSOR_WAIT (砂時計・待機中)
タイトルにある「砂時計」のカーソルです。(OSによっては砂時計ではなく、回転する円などになります) ユーザーに「現在処理中なので待ってください」という状態を示すために使います。
ただし、このカーソルの使い方には重要な注意点があります。これは後のセクション「注意点: wx.CURSOR_WAIT と wx.BusyCursor の使い分け」で詳しく解説します。
4. wx.CURSOR_CROSS (十字)
十字のカーソルです。 主に、画像編集ソフトの描画キャンバス上や、スクリーンショットアプリで範囲選択をする時などに使われます。
5. wx.CURSOR_IBEAM (Iビーム)
テキスト入力フィールド(wx.TextCtrlなど)の上で自動的に表示される、あの縦線のカーソルです。 通常、wx.TextCtrlを使えば自動で設定されますが、自作のテキストエディタなどを作る際には明示的に指定することもあります。
6. その他(サイズ変更など)
上記以外にも、ウィンドウの境界線でよく見る「サイズ変更」用のカーソルなどもwx.StockCursorで取得できます。
wx.CURSOR_SIZEWE: 左右のサイズ変更(West-East)wx.CURSOR_SIZENS: 上下のサイズ変更(North-South)wx.CURSOR_SIZENWSE: 左上と右下のサイズ変更(NorthWest-SouthEast)wx.CURSOR_SIZENESW: 右上と左下のサイズ変更(NorthEast-SouthWest)
実際にカーソルを変更する2つの方法
wx.StockCursorでwx.Cursorオブジェクトを取得する方法がわかったところで、次にそれをウィジェットに設定する具体的な方法をコードで見ていきましょう。
準備: wxPythonアプリの雛形
まずは、カーソルを変更する対象となるウィンドウとパネルを用意します。 以下のコードをベースとして使います。
import wx
import time
class MyFrame(wx.Frame):
def __init__(self, parent, title):
super(MyFrame, self).__init__(parent, title=title, size=(400, 300))
# メインパネル
main_panel = wx.Panel(self)
main_panel.SetBackgroundColour(wx.Colour(240, 240, 240)) # 背景色を灰色に
# このパネルのカーソルを変更していきます
self.target_panel = wx.Panel(main_panel, size=(200, 100), pos=(50, 50))
self.target_panel.SetBackgroundColour(wx.Colour(150, 200, 255)) # 背景色を水色に
# 説明用のテキスト
wx.StaticText(main_panel, label="↑この水色のパネルの上で\nカーソルが変わります", pos=(50, 160))
# --- ここにカーソル設定コードを追加していく ---
# ----------------------------------------
self.Centre()
self.Show(True)
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame(None, 'wx.StockCursor サンプル')
app.MainLoop()この時点では、水色のパネルの上にマウスを乗せても、カーソルは矢印のままです。
方法1: ウィジェット(wx.Panelなど)のカーソルを変更する
これが最も一般的でよく使う方法です。 特定のウィジェット(パネル、ボタン、テキストなど)の上にマウスが来た時だけ、カーソルを変更します。
設定には、すべてのウィジェットが持っている SetCursor() メソッドを使います。
先ほどの雛形コードの「--- ここにカーソル設定コードを追加していく ---」の部分に、以下の1行を追加してみましょう。
# --- ここにカーソル設定コードを追加していく ---
# 1. wx.StockCursorで「手の形」のカーソルオブジェクトを取得
hand_cursor = wx.StockCursor(wx.CURSOR_HAND)
# 2. target_panelのカーソルとして設定
self.target_panel.SetCursor(hand_cursor)
# ----------------------------------------実行して、マウスカーソルを水色のパネルの上に移動させてみてください。 パネルの外では「矢印」ですが、パネルの上に乗った瞬間「手の形」に変わるのが確認できるはずです。
方法2: ウィンドウ全体(wx.Frame)のカーソルを変更する
SetCursor()メソッドは、wx.Frame(ウィンドウ全体)に対しても使うことができます。 この場合、アプリのウィンドウ領域内であれば、どこでも指定したカーソルがデフォルトになります。
# --- ここにカーソル設定コードを追加していく ---
# フレーム全体(自分自身=self)のカーソルを十字に変更
cross_cursor = wx.StockCursor(wx.CURSOR_CROSS)
self.SetCursor(cross_cursor) # self は MyFrame のこと
# ----------------------------------------これを実行すると、ウィンドウ全体(水色のパネルの上も含む)でカーソルが「十字」になります。
ただし、この方法はあまり使いません。 なぜなら、ボタンやテキスト入力欄など、本来カーソルが変わるべき場所でも強制的に十字になってしまい、ユーザーを混乱させる可能性があるからです。
お絵かきアプリなど、ウィンドウ全体がキャンバスであるような特殊なアプリ以外では、方法1(ウィジェット単位での設定)を使うのが基本です。
カーソルを元に戻す方法
一度設定したカーソルを、デフォルト(標準の矢印、または親ウィジェットの設定)に戻したい場合はどうすればよいでしょうか。
SetCursor()メソッドに wx.NullCursor を渡します。
wx.NullCursorは「カーソル設定を解除する」という意味の特別なオブジェクトです。
# 手の形に設定した後...
hand_cursor = wx.StockCursor(wx.CURSOR_HAND)
self.target_panel.SetCursor(hand_cursor)
# ...何かの処理の後、カーソル設定を解除して元に戻す
# (この例だと一瞬で実行されるので意味はありませんが、文法として)
self.target_panel.SetCursor(wx.NullCursor) SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) としても見た目上は矢印に戻りますが、親ウィジェットが別のカーソル(例: 十字)に設定されていた場合に意図した動作になりません。そのため、設定を解除する wx.NullCursor を使うのが正確です。
注意点: wx.CURSOR_WAIT と wx.BusyCursor の使い分け
さて、wx.CURSOR_WAIT(砂時計)に関して、初心者が最も陥りやすい罠について解説します。これは非常に重要なポイントです。
SetCursorで「砂時計」を設定する問題点
「ボタンを押したら重い処理が始まるから、処理中は砂時計にしたい」と考え、以下のようなコードを書いたとします。
# ※これは良くない例です!
def on_button_click(self, event):
# (1) カーソルを砂時計に設定
wait_cursor = wx.StockCursor(wx.CURSOR_WAIT)
self.SetCursor(wait_cursor) # フレーム全体を砂時計に
# (2) 重い処理(のフリ)
time.sleep(5) # 5秒間フリーズ
# (3) カーソルを元に戻す
self.SetCursor(wx.NullCursor)
print("処理完了!")このコードには2つの大きな問題があります。
- GUIがフリーズする:
time.sleep(5)の間、GUIのスレッドが完全に停止するため、カーソルが砂時計に変わる描画処理すら実行されず、アプリ全体が5秒間固まります。(これはwx.Cursorとは別の問題ですが、よく同時に発生します) - エラー時に戻らない: もし「(2) 重い処理」の途中でエラー(例外)が発生したらどうなるでしょう? プログラムは「(3) カーソルを元に戻す」の行に到達しません。その結果、アプリのカーソルが砂時計のまま戻らなくなってしまいます。
SetCursor(wx.StockCursor(wx.CURSOR_WAIT))は、あくまで「そのウィジェットの上にいる間は常に砂時計にする」という設定であり、一時的な処理待機に使うものではありません。
処理中待機はwx.BusyCursorを使おう
「ボタンを押してから処理が終わるまで」の間だけ一時的にカーソルを砂時計(待機状態)にしたい場合、使うべきはwx.StockCursorではなく、**wx.BusyCursor**クラスです。
wx.BusyCursorは、Pythonのwith文と組み合わせて使うために設計されています。
with文のブロックに入った瞬間にカーソルが自動で砂時計になり、with文のブロックを抜けた瞬間(処理が正常に終わっても、エラーで中断しても)に、カーソルが自動で元に戻ります。
先ほどの良くない例を、wx.BusyCursorを使って書き換えてみましょう。 (※GUIのフリーズ問題は別でスレッド処理が必要ですが、ここではカーソルの管理に絞って解説します)
# これが「処理中」のカーソル変更の正しい方法です
def on_button_click(self, event):
# withブロックに入ると自動で砂時計になる
with wx.BusyCursor():
# (2) 重い処理(のフリ)
# ※実際のアプリでは、ここは別スレッドで実行すべきです
time.sleep(5)
# もしここでエラーが発生しても...
# raise ValueError("エラー発生!")
# withブロックを抜けると、エラーがあってもなくても
# 自動でカーソルが元に戻る!
print("処理完了!")SetCursorやwx.NullCursorを手動で管理する必要が一切なく、非常に安全でコードもスッキリします。
まとめると、以下のように使い分けましょう。
wx.StockCursor(wx.CURSOR_WAIT): (ほぼ使わない)wx.BusyCursor: 一時的な重い処理の間だけ、カーソルを待機状態にしたい時(推奨)wx.StockCursor(wx.CURSOR_HAND)など: 特定のウィジェットの上にいる間、常にカーソルの形状を変えておきたい時
まとめ
今回は、wxPythonでマウスカーソルを簡単に変更できるwx.StockCursorについて解説しました。
wx.Cursorは、カーソルの形状そのものを表す「オブジェクト」です。wx.StockCursor(ID)は、OS標準のカーソル(矢印、手、砂時計など)を簡単に取得できる便利な「関数」です。widget.SetCursor(cursor_obj)メソッドで、特定のウィジェットのカーソルを変更できます。widget.SetCursor(wx.NullCursor)で、カーソル設定を解除し元に戻せます。- 最重要: 「処理中」の待機カーソルには
wx.StockCursor(wx.CURSOR_WAIT)を手動で使うのではなく、with wx.BusyCursor():構文を使うのが、はるかに安全でスマートです。
wx.StockCursorを適切に使いこなすことで、ユーザーは「今何をすべきか」「アプリがどういう状態か」を直感的に理解できるようになります。 ぜひこのテクニックをあなたのアプリに取り入れて、ワンランク上のユーザー体験を目指してみてください。


コメント