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

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

新規登録して質問してみよう
ただいま回答率
85.46%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

1回答

3075閲覧

pythonのwxで監視アプリをつくり、タスクバーの右クリックメニューを出して処理を実行させたい

toccipc

総合スコア15

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2021/11/20 08:45

編集2021/11/20 21:14

python 3.8 で開発
windows 8.1 で起動させたいアプリです。

ttray_test_before.py

python

1import wx #wxPython のモジュール 2import wx.adv 3# ファイル変更イベント検出のため、watchdogをインポート 4from watchdog.events import PatternMatchingEventHandler 5from watchdog.observers import Observer 6 7# ファイルアクセス 8import os 9# 時間処理 10import datetime 11 12# 監視対象ディレクトリ 13target_dir = r'C:\My Documents\watch' 14 15#------------------------------------------------------------------------------- 16# 質問用のダミー処理 17#------------------------------------------------------------------------------- 18def dummy_fnc(): 19 # 現在時刻 20 nowtime = datetime.datetime.now().strftime("%Y%m%d%H%M%S") 21 filepath = os.path.join(target_dir , nowtime + '.txt') 22 with open(filepath, 'a', encoding='utf-8') as file: 23 file.write(nowtime + '\n') 24 25#------------------------------------------------------------------------------- 26# タスクトレイのクラス (TaskBarIconを継承) 27#------------------------------------------------------------------------------- 28class SysTray(wx.adv.TaskBarIcon): 29 30 def __init__(self): 31 32 # 初期起動 33 wx.adv.TaskBarIcon.__init__(self) # 継承元の初期化を呼び出す 34 35 # カレントディレクトリの変更 36 os.chdir(os.getcwd()) 37 38 # アイコン設定 39 icon = wx.Icon('icon.png', wx.BITMAP_TYPE_ANY ) 40 self.SetIcon(icon, "filewatcher") 41 42 # 左クリック時の関数登録 43 self.Bind(wx.adv.EVT_TASKBAR_LEFT_UP, self.LeftClick) 44 45 #--------------------------------------------------------------------------------------------------- 46 # タスクトレイでアイコンをクリックしたときにメニューを表示させ、メニュー内をクリックしたら処理を行う 47 #--------------------------------------------------------------------------------------------------- 48 def CreatePopupMenu(self): 49 # タスクトレイメニューの作成 50 menu = wx.Menu() 51 menu.Append(1, "手動処理") 52 menu.Append(2, "終了") 53 self.Bind(wx.EVT_MENU, self.click_item) 54 return menu 55 #------------------------------------------------------------------------------- 56 # 左マウスボタンクリック 57 #------------------------------------------------------------------------------- 58 def LeftClick(self, even): 59 # クローズイベントでフレーム重複作成を防止 60 # ・・・出来てないが、質問内容とは関係ない不具合 61 wx.Frame(None).Show() 62 63 #------------------------------------------------------------------------------- 64 # メニュー項目をクリック 65 #------------------------------------------------------------------------------- 66 def click_item(self, event): 67 event_id = event.GetId() # IDを取得する 68 69 if event_id == 1: 70 # 本当はメイン処理が別途あるが質問対象外の内容の為非公開とし、ダミー処理とする 71 dummy_fnc() 72 73 elif event_id == 2: 74 self.exitTray( event ) 75 76 #------------------------------------------------------------------------------- 77 # 終了処理 78 #------------------------------------------------------------------------------- 79 def exitTray(self, event): 80 self.Destroy() 81 wx.Exit() 82 83#------------------------------------------------------------------------------- 84# メイン 85#------------------------------------------------------------------------------- 86if __name__ == "__main__": 87 app = wx.App() 88 # タスクトレイクラスの呼出 89 taskttr = SysTray() 90 # 右クリックでメニュー呼び出し 91 SysTray.CreatePopupMenu(taskttr) 92 93 app.MainLoop() 94

上記にてタスクトレイにアプリを常駐させ、
アイコンを右クリックすると
「手動処理」
「終了」
が表示されます。

「手動処理」を押すとメイン処理(本処理は公開不可の為、ダミー処理です)
「終了」でアプリを終了させます。

上記は正常に動作しております。
(※最終的にはpy2exeでexe化しタスクスケジューラ起動させるようにする)

今度、特定のディレクトリ内にファイルが作成された時、
「手動処理」と同じ関数を実行させたいです。

ttray_test_main.py

python

1import wx #wxPython のモジュール 2import wx.adv 3# ファイル変更イベント検出のため、watchdogをインポート 4from watchdog.events import PatternMatchingEventHandler 5from watchdog.observers import Observer 6 7# osをインポート 8import os 9 10import datetime 11# 時間処理 12# 監視対象ディレクトリ 13target_dir = r'C:\My Documents\watch' 14 15#------------------------------------------------------------------------------- 16# 質問用のダミー処理 17#------------------------------------------------------------------------------- 18def dummy_fnc(): 19 # 現在時刻 20 nowtime = datetime.datetime.now().strftime("%Y%m%d%H%M%S") 21 filepath = os.path.join(target_dir , nowtime + '.txt') 22 with open(filepath, 'a', encoding='utf-8') as file: 23 file.write(nowtime + '\n') 24 25#------------------------------------------------------------------------------- 26# ファイル監視のクラス ( PatternMatchingEventHandlerを継承) 27#------------------------------------------------------------------------------- 28class FileChangeHandler(PatternMatchingEventHandler): 29 # クラス初期化 30 def __init__(self, patterns): 31 super(FileChangeHandler, self).__init__(patterns=patterns) 32 33 34 # ファイル作成時のイベント 35 def on_created(self, event): 36 # 本当はメイン処理が別途あるが質問対象外の内容の為非公開とし、ダミー処理とする 37 dummy_fnc() 38 39#------------------------------------------------------------------------------- 40# 監視処理 41#------------------------------------------------------------------------------- 42def watchfile(): 43 44 # ファイル監視の開始 45 event_handler = FileChangeHandler(['*.*']) 46 observer = Observer() 47 # ↓ recursive = True ← サブディレクトリも対象 48 observer.schedule(event_handler, target_dir, recursive=True) 49 observer.start() 50 51 # 処理が終了しないようスリープを挟んで無限ループ 52 try: 53 while True: 54 pass 55 56 # Ctri + C で止める 57 except KeyboardInterrupt: 58 observer.stop() 59 observer.join() 60 61#------------------------------------------------------------------------------- 62# タスクトレイのクラス (TaskBarIconを継承) 63#------------------------------------------------------------------------------- 64class SysTray(wx.adv.TaskBarIcon): 65 66 def __init__(self): 67 68 # 初期起動 69 wx.adv.TaskBarIcon.__init__(self) # 継承元の初期化を呼び出す 70 71 # カレントディレクトリの変更 72 os.chdir(os.getcwd()) 73 74 # アイコン設定 75 icon = wx.Icon('icon.png', wx.BITMAP_TYPE_ANY ) 76 self.SetIcon(icon, "filewatcher") 77 78 # 左クリック時の関数登録 79 self.Bind(wx.adv.EVT_TASKBAR_LEFT_UP, self.LeftClick) 80 #--------------------------------------------------------------------------------------------------- 81 # タスクトレイでアイコンをクリックしたときにメニューを表示させ、メニュー内をクリックしたら処理を行う 82 #--------------------------------------------------------------------------------------------------- 83 def CreatePopupMenu(self): 84 # タスクトレイメニューの作成 85 menu = wx.Menu() 86 menu.Append(1, "手動処理") 87 menu.Append(2, "終了") 88 self.Bind(wx.EVT_MENU, self.click_item) 89 return menu 90 #------------------------------------------------------------------------------- 91 # 左マウスボタンクリック 92 #------------------------------------------------------------------------------- 93 def LeftClick(self, even): 94 # クローズイベントでフレーム重複作成を防止 95 # ・・・出来てないが、質問内容とは関係ない不具合 96 wx.Frame(None).Show() 97 98 #------------------------------------------------------------------------------- 99 # メニュー項目をクリック 100 #------------------------------------------------------------------------------- 101 def click_item(self, event): 102 event_id = event.GetId() # IDを取得する 103 104 if event_id == 1: 105 # 本当はメイン処理が別途あるが質問対象外の内容の為非公開とし、ダミー処理とする 106 dummy_fnc() 107 108 elif event_id == 2: 109 self.exitTray( event ) 110 111 #------------------------------------------------------------------------------- 112 # 終了処理 113 #------------------------------------------------------------------------------- 114 def exitTray(self, event): 115 self.Destroy() 116 wx.Exit() 117 118#------------------------------------------------------------------------------- 119# メイン 120#------------------------------------------------------------------------------- 121if __name__ == "__main__": 122 app = wx.App() 123 # タスクトレイクラスの呼出 124 taskttr = SysTray() 125 # 右クリックでメニュー呼び出し 126 SysTray.CreatePopupMenu(taskttr) 127 128 # ファイル監視の開始 129 watchfile() 130 131 app.MainLoop() 132

とコーディングしました。

出来ている事
ファイル追加時にdummy_fncは正常に処理をする
出来ていなくて出来るようにしたいこと
タスクトレイを右クリックした時に、ttray_test_before.pyの時と同様
メニュー表示し、
「手動処理」を押すとメイン処理の実行、「終了」でアプリを終了させたい。

どのように変更すれば良いか教えてください。

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

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

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

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

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

guest

回答1

0

ベストアンサー

問題点: ファイルの監視が終わるまで GUI が開始しない

# ファイル監視の開始 watchfile() app.MainLoop()

となっていますが、watchfile 関数が無限ループを持つブロッキング処理になってます

python

1 2 # 処理が終了しないようスリープを挟んで無限ループ 3 try: 4 while True: 5 pass 6 7 # Ctri + C で止める 8 except KeyboardInterrupt: 9 observer.stop() 10 observer.join()

これではwatchfile が終わらないと app.MainLoop() が呼ばれないので、GUIが稼働しません。

observer はスレッドで実行されるので、observer.start() の後は関数を抜けても大丈夫です。
observer.stop() をアプリケーション終了時に呼び出す為に、
observerのインスタンス自体は何処かに保持してください。

解決策: MainLoop を呼び出す為に、無限ループでブロックしない

python

1observer.start() 2 3app.MainLoop() 4 5observer.stop() # 終了をリクエスト 6observer.join() # 処理中のタスクが完了するまで待機

投稿2021/11/21 02:34

teamikl

総合スコア8664

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

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

toccipc

2021/11/21 05:16 編集

teamikl 様 無限ループが問題だったんですね。 joinはそもそも実装してたのでそこで待てば良かったのに、ってとこなんですね。 記載のコードにて修正したところ、稼働の確認が取れました。 ```python def watchfile(): # ファイル監視の開始 event_handler = FileChangeHandler(['*.*']) observer = Observer() # ↓ recursive = True ← サブディレクトリも対象 observer.schedule(event_handler, target_dir, recursive=True) observer.start() # 監視開始 app.MainLoop() observer.stop() observer.join() ``` ※他、修正無し ご回答いただきありがとうございました。
teamikl

2021/11/21 07:04

>joinはそもそも実装してたのでそこで待てば良かったのに、ってとこなんですね 細かい点なのですが、若干ニュアンスが違って、 認識としては join ではなく 「MainLoopで待てば良かった」 とした方が適切です。 (コメントに記載のコード自体は問題ありません) join でも待機できるのですが、observer.start ではバックグラウンドのスレッドでの実行なので app.MainLoop が稼働してる間は、そもそも observer に待機を指示する必要がありません。 app.MainLoop と observer.join が「ブロッキング処理」なので、どちらも 無限ループに期待されるような待機ポイントとして用いることができます。 GUIと併用する場合に join で待つ場合は注意が必要になり GUI のイベントハンドラ内で Thread.join (observer.join) や無限ループのような ブロッキング処理をすると、GUIが応答なしになってしまいます。 上記のコードでは、MainLoop を抜けて GUI が終了してから observer を stop し join でその完了迄待つという構成にしてます。 # 監視開始 <-- 監視の開始はこちら observer.start() # アプリケーションの終了を待つ app.MainLoop() # 監視停止 observer.stop() observer.join()
toccipc

2021/12/18 00:06 編集

修正したところ(上記とは別で)の記載失念 #------------------------------------------------------------------------------- # メイン #------------------------------------------------------------------------------- if __name__ == "__main__": app = wx.App() # タスクトレイクラスの呼出 taskttr = SysTray() # 右クリックでメニュー呼び出し SysTray.CreatePopupMenu(taskttr) app.MainLoop() の最後の app.MainLoop() を消しました。
toccipc

2022/07/24 00:04

これ、よくよく見ると 終了してもpythonのプロセスは消えて無いですね。 終了を押して、タスクトレイから消えますが・・・タスクマネージャからpythonは消えていない・・・
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問