wxPythonでsleepすると応答なしになる件

2020-08-13

プログラミング言語Python(パイソン)に関する話です。

wxPythonでsleepを使うと応答なし

先日、wxPythonでsleep()を使うというイレギュラーなプログラムを作ったときに、プログラムが応答なし(フリーズする)になるという現象に遭遇しました。

この問題と解決方法について記載します。

確認環境

  • Windows 10 Home(64bit)
  • Python 3.8.2(64bit)
  • wxPython 4.0.7.post2

一応Python 3.8の32bit版と、Python 3.7の64bit版も試しましたが同じ状況でした。

現象について

当方の環境では、

  • メイン処理の中のtime.sleep()を3回以上使用すると「応答なし」
  • 現象は発生したり、しなかったり
  • 1度発生すると以降は確実に発生
  • PCの再起動で発生しなくなることがある
  • 応答なしになると、以降の処理がまともに動作しない
  • sleep()の時間を変えても改善せず

ちなみに当方の場合、sleep()を使用する目的は「定期実行」というよりは「処理の遅延」「待機」が目的になります。

当初、現象が100%発生していたので「何か対策が必要なのだろうな」と考えているときに、再起動で現象が発生しなくなり「ん?とりあえず大丈夫そうか?」となってしまい、そのまま進めると数日後にまた応答なしになるという状況…

100%動かなければもっと早く解決できたのですが。

ちなみに試してたこと

いろいろ調べて「wx.Sleep()」というものを見つけました。

「wxのSleep関数だし、これで解決じゃん!」

と思って意気揚々と試してみましたが、全然ダメでした。wx.MilliSleep()もダメでした。wx.MicroSleep()はUNIX用なので試していません。

調べていると「定期実行したい」という質問に対する回答はいくつか見かけたのですが「処理を遅延させたい」「待機したい」という質問と回答はなかなか見つけられませんでした。

原因

そもそもの原因について以下のような記載がありました。

質問に挙げたコードのように記述しますと、途中のループ処理が抜けるまで application.MainLoop() が呼ばれませんので、アプリケーションがまっとうに動作しません。

application.MainLoop() はイベントループなどとも呼ばる「ユーザやシステムからのイベントを待ちうけて、イベントの振り分け処理をする関数」ですので、この関数が呼ばれない限り「アプリケーションが応答しません」となってしまいます。

初心者です。sleep文を入れたら、アプリが正常に作動しなくなりました。

「一時停止して待機」と「イベント駆動型GUIプログラミング」は一緒に使用できません。メインGUIスレッドが何かを待ってブロックされている限り、他のイベントは処理できず、プログラムはフリーズしているように見えます。オプションは、(実際には待機しないことで)「待機」する方法を変更するか、別のスレッドを使用することです。

ある時点でコードを一時停止する

要するに、イベントドリブンのGUIプログラムのメインループで基本的にsleep()は使用できない、メインループは待機させちゃダメということなのでしょう。

対処法・解決策

別スレッドを作成して、その中でSleep()することにしました。

(私がやりたかったことは)それで解決しました。

サンプルコード

大体、以下のような感じです。

import wx
import time
from threading import Thread

################################################################################
#sleep()を含めた処理
################################################################################
def sleep_function():
    time.sleep(1)
  #その他処理
    time.sleep(1)
  #その他処理
    time.sleep(1)

################################################################################
#実行ボタンをクリック時の動作
################################################################################
def run_button_function(event):
    thread1 = Thread(target=sleep_function)					#sleep_function()を別スレッドにして
    thread1.start()								#スレッド開始

################################################################################
#メイン処理
################################################################################
if __name__ == '__main__':
    window = MyApp(0)								# wxPythonのGUI Appインスタンスを生成 (= GUI Appを初期化)
    frame = MyFrame1(None)		                                        # フレーム作成(ウィンドウタイトルとウィンドウサイズを指定)
    panel = wx.Panel(frame, -1)							# ボタンやテキストボックスを配置するためのパネルを作成

    #コントロール配置等

    run_button = wx.Button(panel, wx.ID_ANY, label='実行')		        #実行ボタンの作成
    run_button.Bind(wx.EVT_BUTTON, run_button_function)  		        #実行ボタン押下と関数をひもづける

    frame.Show()								#フレーム表示
    window.MainLoop()								#イベント待機

参考サイト

以上。

後記

最後までお読みくださりありがとうございます。

こんなに更新したのに、現在はほぼ誰も見ていないゴミカスクズ無価値ブログです。

よければ(はてな)ブックマークや拡散をお願いいたします。

更新の原動力として励ましが欲しいのです…<(_ _)>

当ブログ内お薦め記事