PythonのGUIライブラリであるwxPythonを使ってデスクトップアプリを開発していると、ファイルの読み込みやデータの書き出し、ネットワーク通信など、少し時間のかかる処理を実装する場面が必ず出てきます。
そんな時、処理中にアプリの画面が応答しなくなると、ユーザーは「アプリが固まった(フリーズした)のでは?」と不安になってしまいます。最悪の場合、強制終了されてしまうかもしれません。
そうした事態を防ぐために重要なのが、「今、ちゃんと処理中ですよ」とユーザーに伝えることです。
wxPythonには、この「処理中」メッセージを非常に簡単に表示できるwx.BusyInfoという便利な機能が用意されています。
さらに、wx.BusyInfoはPythonのwith文と組み合わせることで、驚くほどコードがスッキリし、安全に実装できます。
この記事では、wxPython初心者から中級者の方に向けて、wx.BusyInfoの基本的な使い方と、with文を使ったスマートな実装方法を、具体的なコード例を交えて詳しく解説します。
この記事を読み終える頃には、あなたのwxPythonアプリのユーザー体験(UX)を一段階アップさせるテクニックが身についているはずです。
wx.BusyInfoとは?
まずはwx.BusyInfoがどのようなものかを知りましょう。
ユーザーに「処理中」を伝えるシンプルな方法
wx.BusyInfoは、その名の通り「ビジー状態(取り込み中)」であることをユーザーに通知するための、一時的なメッセージウィンドウを表示するクラスです。
「データをロード中です…」や「処理を実行しています…」といった短いメッセージを表示するのに特化しています。
似たような機能にwx.ProgressDialog(進捗バー)がありますが、あちらは処理の進捗度(例: 0%〜100%)を視覚的に示したい場合に適しています。
一方、wx.BusyInfoは「処理にかかる時間はわからないけど、とにかく少し待ってほしい」という、比較的短い待機時間(数秒〜十数秒程度)を想定した、よりシンプルな機能です。
(バージョン情報) wx.BusyInfoはwxPythonの基本的なUIコンポーネントの一つであり、wxPython 4系など、現在広く利用されているバージョンで標準的に使用できます。
本記事で解説するwx.BusyInfoの公式ドキュメントはこちらです。
wx.BusyInfoが表示されるタイミングと消えるタイミング
wx.BusyInfoの動作原理は非常にシンプルです。
- 表示:
wx.BusyInfo("メッセージ")のように、wx.BusyInfoクラスのオブジェクト(インスタンス)が作成された瞬間に、メッセージウィンドウが表示されます。 - 非表示: そのオブジェクトが破棄(削除)された瞬間に、メッセージウィンドウが自動的に消えます。
この「オブジェクトの生存期間 = メッセージの表示期間」という仕組みが、後ほど解説するPythonのwith文と非常に相性が良いのです。
wx.BusyInfoの基本的な使い方(with文なしの場合)
with文の便利さを実感していただくために、まずはwith文を使わない基本的な実装方法を見ていきましょう。
ここでは、ボタンを押すと3秒間だけ「処理中」と表示する簡単なサンプルアプリを作成します。
1. インポートとアプリケーションの準備
まずは、wxPythonアプリケーションの最小限の雛形(ウィンドウとボタンを一つ配置)を用意します。
import wx
import time
class MyFrame(wx.Frame):
def __init__(self, parent, title):
super(MyFrame, self).__init__(parent, title=title, size=(300, 200))
panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
self.button = wx.Button(panel, label="重い処理を実行")
self.button.Bind(wx.EVT_BUTTON, self.on_button_click) # ボタンイベントを紐付け
vbox.Add(self.button, proportion=0, flag=wx.ALL | wx.CENTER, border=10)
panel.SetSizer(vbox)
self.Centre()
self.Show(True)
def on_button_click(self, event):
# ここに処理を書きます
print("ボタンが押されました。")
# TODO: BusyInfoを使った処理
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame(None, 'wx.BusyInfo サンプル')
app.MainLoop()このコードを実行すると、ボタンが一つあるだけのシンプルなウィンドウが表示されます。
2. wx.BusyInfoの表示と非表示(手動)
次に、on_button_clickメソッドを修正し、ボタンが押されたらwx.BusyInfoを表示し、重い処理(のフリ)を行い、最後に非表示にする処理を追加します。
def on_button_click(self, event):
print("処理開始...")
# 1. BusyInfoのインスタンスを作成(この瞬間に表示される)
busy_info = wx.BusyInfo("重い処理を実行中です、お待ちください...")
# 2. 時間のかかる処理のシミュレーション
time.sleep(3) # 3秒間待機
# 3. BusyInfoのインスタンスを破棄(この瞬間に消える)
del busy_info
# もしくは busy_info = None でもOK
print("処理完了!")ボタンを押すと、「重い処理を実行中です、お待ちください…」というメッセージウィンドウが表示され、3秒後に自動で消えるはずです。
4. エラー発生時に問題となるケース
上記の方法はシンプルですが、一つ大きな問題点があります。 もし「2. 時間のかかる処理」の途中でエラー(例外)が発生したらどうなるでしょうか?
del busy_infoの行に到達する前にプログラムが停止してしまうため、wx.BusyInfoのオブジェクトが破棄されず、メッセージウィンドウが表示されたまま消えなくなってしまいます。
この問題を避けるため、with文を使わない場合はtry...finally構文を使うのが一般的です。
def on_button_click(self, event):
print("処理開始...")
busy_info = None # 先にNoneで初期化
try:
# 1. BusyInfoのインスタンスを作成(表示)
busy_info = wx.BusyInfo("重い処理を実行中です、お待ちください...")
# 2. 時間のかかる処理のシミュレーション
time.sleep(3)
# わざとエラーを発生させてみる
# raise ValueError("何らかのエラーが発生しました!")
print("処理が正常に完了しました。")
except Exception as e:
print(f"エラーが発生しました: {e}")
finally:
# 3. 処理が成功しようがエラーになろうが、必ず実行される
if busy_info:
del busy_info # インスタンスを破棄(非表示)
print("finallyブロックが実行され、BusyInfoが閉じられました。")try...finallyを使うことで、途中でエラーが発生してもfinallyブロックが必ず実行されるため、メッセージウィンドウが残り続ける事態を防げます。
しかし、コードが冗長になり、busy_info変数の管理も少し面倒に感じませんか?
with文でスマートに!wx.BusyInfoの実装
ここからが本題です。先ほどのtry...finallyを使った冗長なコードは、Pythonのwith文を使うことで、劇的にシンプルかつ安全になります。
なぜwith文が使えるのか?
wx.BusyInfoクラスは、Pythonの「コンテキストマネージャ」という仕組みに対応するように設計されています。
難しい用語はさておき、with文で使えるオブジェクトは、「ブロック(字下げされた範囲)に入る時に特定の前処理を行い、ブロックを抜ける時に(エラーがあってもなくても)必ず後処理を行う」 という動作を保証してくれます。
wx.BusyInfoの場合、これが以下のようにマッピングされます。
withブロックに入る時:wx.BusyInfoが作成され、メッセージが表示される。withブロックを抜ける時:wx.BusyInfoが自動的に破棄され、メッセージが非表示になる。
これは、先ほどのtry...finally構文の動作と全く同じですが、with文がその面倒な処理をすべて裏側で引き受けてくれるのです。
with文を使った具体的なコード例
on_button_clickメソッドをwith文を使って書き換えてみましょう。
def on_button_click(self, event):
print("処理開始...")
# with文を使うと、ブロックを抜ける時に自動で消える
try:
with wx.BusyInfo("データを処理中です。しばらくお待ちください..."):
# 2. 時間のかかる処理のシミュレーション
time.sleep(3)
# わざとエラーを発生させてみる
# raise ValueError("何らかのエラーが発生しました!")
print("処理が正常に完了しました。")
except Exception as e:
print(f"エラーが発生しました: {e}")
# エラーが発生しても、withブロックを抜けているのでBusyInfoは消える
print("withブロックを抜けました。")どうでしょうか? try...finallyを使ったコードと比べて、busy_info変数の管理やdelの呼び出しが一切不要になり、非常にスッキリしました。
withブロックの中に時間のかかる処理を書くだけで、処理の開始時に自動で表示され、処理の終了時(またはエラー発生時)に自動で消えてくれます。これこそがwx.BusyInfoの最もスマートな使い方です。
メッセージを途中で変更する方法
処理が複数のステップに分かれている場合、進捗に合わせてメッセージを変更したいこともあります。 with文のasキーワードを使えば、wx.BusyInfoのインスタンスを受け取ることができ、UpdateMessageメソッドでメッセージを更新できます。
def on_button_click(self, event):
print("処理開始...")
# 'as info' でインスタンスを受け取る
with wx.BusyInfo("ステップ1/3: データの準備中...", parent=self) as info:
time.sleep(2) # ステップ1の処理
# メッセージを更新
info.UpdateMessage("ステップ2/3: データの処理中...")
time.sleep(2) # ステップ2の処理
# メッセージを更新
info.UpdateMessage("ステップ3/3: データの保存中...")
time.sleep(2) # ステップ3の処理
print("全ての処理が完了しました。")これにより、ユーザーは処理が止まっているのではなく、順調に進んでいることを視覚的に理解でき、より安心して待つことができます。
wx.BusyInfoの便利なオプション
最後に、wx.BusyInfoをさらに便利に使うための代表的なオプションを2つ紹介します。
親ウィンドウを指定する
wx.BusyInfoのコンストラクタ(オブジェクトを作るところ)でparent引数を指定すると、そのウィンドウの中央にメッセージが表示されるようになります。
# parent を指定しない場合(デフォルト)
# 画面(スクリーン)の中央に表示される
wx.BusyInfo("処理中です...")
# parent を指定する場合
# self.frame (MyFrameインスタンス) の中央に表示される
with wx.BusyInfo("処理中です...", parent=self): # self は MyFrame のこと
time.sleep(2)親ウィンドウを指定することで、ユーザーは「どのアプリが処理中なのか」を明確に認識できるため、特別な理由がなければ指定することが推奨されます。
wx.BusyCursorとの違い
wx.BusyInfoとよく似た名前にwx.BusyCursorという機能があります。
wx.BusyInfo: メッセージウィンドウを表示します(この記事で解説したもの)。wx.BusyCursor: マウスカーソルの形状を、砂時計や回転する円などの「ビジー状態」のアイコンに変更します。
wx.BusyCursorは、処理中であることを示しつつも、ユーザーが他の操作(ウィンドウの移動など)をできる(場合もある)ことを示唆します。一方、wx.BusyInfoは「操作を止めて待ってほしい」という意図がより強いです。
wx.BusyCursorもwith文に対応しているため、使い方は非常に似ています。
# 0.5秒程度の本当に一瞬の処理の場合
with wx.BusyCursor():
# 何か非常に軽い処理
time.sleep(0.5)数秒以上かかる処理の場合はwx.BusyInfoでメッセージを明示し、1秒未満のほんの一瞬だけ待たせる場合はwx.BusyCursorでカーソルを変えるだけにする、といった使い分けが考えられます。
まとめ: wx.BusyInfoはwith文で使おう!
今回は、wxPythonで時間のかかる処理中にユーザーを待たせるためのwx.BusyInfoの使い方を解説しました。
wx.BusyInfoは、処理中メッセージを簡単に表示できる機能です。- インスタンスの作成で表示され、破棄で消えます。
try...finallyでも実装できますが、コードが冗長になりがちです。wx.BusyInfoはコンテキストマネージャに対応しているため、with文が使えます。with wx.BusyInfo(...)構文を使うのが、最もシンプルで、安全(閉じ忘れがない)、かつスマートな実装方法です。
重い処理を実装する際は、ユーザーを不安にさせない「ひと工夫」として、ぜひwith wx.BusyInfo(...)を活用してみてください。あなたのwxPythonアプリの品質がきっと向上するはずです。


コメント