質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
ウィンドウ

コンピューター用語において、ウィンドウとはユーザとプログラムのやり取りを可能にするGUIの枠組みのことをいいます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

4189閲覧

[Python] データ読み込み中にwxPythonで待機ウィンドウを表示させ、読み込み完了後に閉じたい…

Lups20

総合スコア2

ウィンドウ

コンピューター用語において、ウィンドウとはユーザとプログラムのやり取りを可能にするGUIの枠組みのことをいいます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

2グッド

3クリップ

投稿2021/06/03 14:17

編集2021/06/03 17:14

前提・実現したいこと

Python初心者です。
GUIはwxpythonを使っています。
データの読み込みをするプログラムなのですが、読み込むファイル(xlsx)が重いため時間が少しかかります。
そこで読み込んでいる間、読み込み中のウィンドウを表示して読み込みをバックグラウンドで進めたいと思ったのですが、どのようにしたらいいのかで躓いています。

やりたいこととしては

import pandas as pd import wx #ここで待機ウィンドウを表示したい。 #####エクセルデータ読み込み##### E1=pd.read_excel('E1.xlsx') E2=pd.read_excel('E2.xlsx') . . #読み込みが終わったら待機ウィンドウを閉じたい。

ウィンドウの表示自体は

if __name__ == '__main__': app = wx.App() frame = Project() frame.Show() app.MainLoop()

などでできるのは分かるのですが、当然ながらこれをデータ読み込み前に書いてしまうとMainLoopが閉じるまで処理が進みません。
Messsageウィンドウでも同様で、OKボタンを押すまで結局先に進まないため、バックグラウンドで処理が進みません。。

何かのソフトを立ち上げる際に、待機ウィンドウがぽっと出てきて終わったら本ウィンドウが立ち上がる。というソフトを良く見かけるため、不可能ではないとは思うのですが、なかなか似たような疑問や方法を見つけられず躓いております。

###自身で試したコード

import wx from time import sleep app = wx.App() frame = wx.Frame(None, wx.ID_ANY, 'Test', size=(300, 200)) frame.Show() #####エクセルデータ読み込み##### E1=pd.read_excel('E1.xlsx') E2=pd.read_excel('E2.xlsx') frame.Close() frame.Destroy() app.Destroy() print('wait') sleep(5)

この場合、ウィンドウを起動直後に立ち上げることはできますが、閉じることはできません。
app.MainLoop()を記入しないと、Close()が機能しないのか閉じられません。
sleepで5秒経ってプログラム自体が終了すれば当然ウィンドウごとプログラムは閉じられますが、それでは意味がなく…プログラムが起動している状態でウィンドウの表示と処理完了後に閉じる操作をするにはどうするのが良いのでしょうか。

##まとめ
main.pyを実行した直後に「読み込み中」のウィンドウを表示させ、読み込みが終わったらそのウィンドウを閉じる。(プログラムは稼働継続)
ということがやりたいことになります。

補足

Anacondaで構築しています。
Pythonは3.8.10
WxPythonは4.1.1です。

glyzinieh, teamikl👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

前提として、

  • GUI はイベントループが稼働している メイン・スレッド側で動かす必要があります。
  • 時間の掛かる処理は、バックグラウンド(別スレッド)で行います。

バックグラウンドの処理が終わり次第、ウィンドウを閉じるのですが、
この時、別スレッド側から直接 GUI を操作してはいけなくて、
スレッドセーフな手段、同期キュー等の仕組みを通じて、
メインスレッドのイベントループに通知します。

他スレッド⇒メイン(GUI)スレッドへの簡単な通知方法は、
wx.CallAfter に関数を渡す方法です。

※ wx.CallAfter はスレッドセーフな関数で、内部で同期キュー相当の仕組みが使われてます。

python

1 2from threading import Thread 3import time 4import wx 5 6app = wx.App() 7frame = wx.Frame(None, wx.ID_ANY, 'Test', size=(300, 200)) 8frame.Show() 9 10subframe = wx.Frame(frame, wx.ID_ANY, 'loading ...') 11subframe.Show() 12 13def finished(): 14 subframe.Close() 15 subframe.Destroy() 16 17def worker(): 18 time.sleep(5) # ここで時間の掛かる処理 19 20 wx.CallAfter(finished) # メインスレッドから呼び出す (MainLoop内で呼び出される) 21 22thread = Thread(target=worker, daemon=True) 23wx.CallAfter(thread.start) 24app.MainLoop() 25

質問のコードでは app.Destory を呼び出してますが、
通常はアプリケーションのMainLoopは稼働させておいて、ウィンドウのみを閉じるようにし、
GUIと関係のない処理は別スレッドで行うようにした方が良いです。


関連

Thread/CallAfter のサンプルコード有。

It is a safe way of requesting action of a Window from another thread.

投稿2021/06/03 21:35

編集2021/06/03 21:42
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Lups20

2021/06/04 02:15 編集

ご丁寧に教えて頂きありがとうございます。大変参考&勉強になります。 是非ベストアンサーにさせて頂きたいのですが、その前に一点だけ伺っても宜しいでしょうか。 <質問> 頂いたリンク(wxPython wiki - CallAfter)の方では、wx.CallAfter(thread.start)はなく、単純にthread.start()としています。試しに頂いたコードをwx.CallAfter(thread.start)からthread.start()に変更しても問題なく作動したように思えました。どういった違いがあるのでしょうか。前者の方が良い理由があるのでしょうか。 <余談> 最初ご回答が理解できずthreadingやキューについても色々と調べ、 「並列処理をする必要があるため別スレッドで処理をするが、スレッドセーフで行わなければならず、そのやり取りに簡単で便利な方法としてwx.CallAfterがある。」という所まで理解できました。 また、wx.CallAfterについては、頂いたリンクや他のページも読んだのですが、プログラムやPythonの基本的な理解が足りないためかはっきりとは理解できず、今の段階では「別スレッドで処理したい関数があり、その中でGUI側の操作をしたい場合やGUI側に値を渡したい場合はスレッドセーフにするためwx.CallAfterを使うのが良い。」という所に留まっています。今後頑張って参ります。。
teamikl

2021/06/04 03:43 編集

> どういった違いがあるのでしょうか。 thread.start の呼び出しはメインスレッドで良いので、 ここのwx.CallAfter(thread.start) は、殆どの場合、大した違いはありません。 重要な CallAfter は、サブスレッドから使う方です。 (要点が解り難くなるので、例として提示するなら thread.start() で良かったかも知れません) 違いは、 CallAfter で登録した関数は、wxPythonのイベントループにスケジュールされ 「MainLoop 内のイベント処理として」呼び出されます。 frame.Show() の時点では GUI は、実際には表示は保証されておらず MainLoop の時点で確実に描画が更新されるので、ここでの wx.CallAfter(thread.start) は、 実際に画面に描画されてからスレッドが開始されるのを期待します。 稀なケースですが、読み込むファイルが小さく「時間が掛からない処理」だった場合 thread.start() ~例えば、この間に他の処理があり、 app.MainLoop() とした場合、MainLoop が実行されるまでに読み込みが完了してると ウィンドウが表示されることなく閉じてしまい、(もしくは一瞬で閉じてちらつきのような現象) 読込が始まったのか、呼ばれてないのか判断が付きにくく成ります。 他の解決策: wx.CallLater(1000, finished) # 1秒 (1000ms) 後に閉じる で、ある程度、遅延に感じない程度にウィンドウを表示してる時間に猶予を持たせる。 (wx.CallAfter を説明をしたかったので避けましたが、同様にメインスレッドへの通知として使えます) ==== >余談について 概ねその通りです。 「CallLater(指定時間後に実行)」「CallAfter(直ぐに実行)」は、 イベントループ上でのタイマー機能を提供するもので、 そのスケジュール管理の為に、優先度付きキューや同期キューを用いてます。 キューの内容を読み出して実行するのは、元々提供されている挙動なので、 簡易的なケースでは CallAfter のみで済みます。 注意点は、CallAfter は MainLoop 内のイベントループから呼び出されるので 「他スレッド → メインスレッド」の一方通行な通知です。 「メインスレッド→他スレッド」や、双方向の通信は他の手段が必要です。 例えば、GUI からの操作で読み込み処理を中断したいといった場合は、 → 変数を変更し変化を伝える、キューを使う、等。 スレッド間通信としては「threadingやキュー」が汎用的な解決策なので wxPython 以外でも通用します。 wxPython でのスレッド間通信の手段 - CallAfter (他スレッド → メインスレッド) - PostEvent/CommandEvent (※ 他スレッド → メインスレッド の場合のみ) - QueueEvent/ThreadEvent (メインスレッド <=> 他スレッド)  双方向でスレッドセーフなイベント - wx.lib.pubsub (双方向)
teamikl

2021/06/04 04:01

訂正: > frame.Show() の時点では GUI は、実際には表示は保証されておらず >MainLoop の時点で確実に描画が更新されるので、 試してみると、Show() の時点で表示されたので、 正確な説明ではなかったです。(他のライブラリと勘違いしてたかもしれない)
Lups20

2021/06/05 03:01

度々詳細にご回答頂きありがとうございます。。 簡単に言えば、違いとしてはMainloop()前か後どちらでthread.startさせるかどうかで、意図としては処理が極端に早く終わってしまった場合を考えて後の方がより確実。ということですね。 wx.CallLaterについての解決策も参考になりました。ありがとうございます。 メインスレッド→他スレッドを実装するについてはまだまだ勉強が必要そうですが、また取り組んでみたいと思います。その他もまとめて頂き大変勉強になります。 (何気に当たり前に見ることのあるインストール作業や読み込み中のキャンセルボタンは、簡単に見えて色々と考えられている機能なのですね。。) 本当に助かりました!ありがとうございました!
teamikl

2021/06/05 05:26

意図はその通りです。実際は、コメントを書いていた時点で想定した挙動と違ったので 今回のケースでは影響さそうなので無視して貰って構いません。 MainLoop後だと MainLoop() が終わった後と混同しそうな為、説明としては 「MainLoop 外(前)」か「MainLoop 内 … イベントループ内からの呼出」がより適切かな。 違い自体はあり、使い分けも場合によっては存在します。 ---- 読込のキャンセルは難しそうですね。 強制中断であれば「プロセス」にしたり「シグナル」を使う方法がありますが、 普通に中断処理を実装しようとすると、read_excel は処理が完了する迄ブロック状態なので 読込中、定期的に呼ばれる内部処理の中で、フラグを判定して中断するようにする必要があります。 read_excel の実装次第ですが、(対象がCで書かれた処理だと面倒) Pythonでは動的にメソッドを差し替える等の方法で対応可能です。 tqdm で pd.read_excel の進捗を表示する方法があるので、 もし読込の進捗表示やキャンセルを実装する場合は、その辺りの情報を参考にしてみてください。
Lups20

2021/06/07 17:57

御返事遅くなってしまいすみません。 >「MainLoop 外(前)」か「MainLoop 内 … イベントループ内からの呼出」がより適切 すみません。。仰る通りですね。 こちらもご丁寧にアドバイス頂きありがとうございます。 中断の実装はもっと勉強してからになりそうです。。 進捗を表示する方法があるのですね!表示だけでもやってみようかと思います。 ありがとうございました!!
guest

0

wxPython-複数ドキュメントインターフェイス

というのを使えば複数のウインドウを表示できると書いてあります。
これで別のウインドウを開いて「読み込み中」と表示させれば良いのではないですか。

投稿2021/06/03 15:20

ppaul

総合スコア24666

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Lups20

2021/06/03 15:57 編集

ご回答ありがとうございます。 ただ、伺いたかったこととしては複数ウィンドウを出すことではなく、実行直後に「読み込み中」というウィンドウを一つだけ表示させて読み込みが終わったら閉じる、ということは可能かということだったのですが、分かり難くてすみません。。そういったことは難しいのでしょうか。 (誤解を招いてしまったため、質問内容を編集いたしました。)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問