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

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

新規登録して質問してみよう
ただいま回答率
85.35%
Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

スレッドセーフ

マルチスレッド環境において、複数のスレッド上で常に正常に実行する事が可能なコードを、スレッドセーフなコードと呼びます。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

Python

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

Q&A

解決済

1回答

3264閲覧

wxpythonで10秒後にgif動画再生を別のgifへ切り替えたい。

dendenmushi

総合スコア98

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

スレッドセーフ

マルチスレッド環境において、複数のスレッド上で常に正常に実行する事が可能なコードを、スレッドセーフなコードと呼びます。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

Python

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

0グッド

0クリップ

投稿2020/04/26 11:29

編集2020/04/26 12:22

前提・実現したいこと

wxpythonでgif動画を再生するデスクトップアプリを作っています。
10秒後にgif動画を切り替えたいだけなのですが、かなり苦戦しています。

Architectureと発生している問題・エラーメッセージ

wxのメイン画面を開き

GIFクラスを呼び

指定の動画(著作権フリーのgif)をwx.advで再生

MyThreadEventとEVT_MY_THREADをバインド

print("四.一")のwhileループ箇所(秒カウント箇所)でMyThreadEventを呼び
(ここで2重スレッド発生)

EVT_MY_THREADに紐づくOnUpdateメソッドの実行
(この時、10秒経過しているか呼ばれたときに毎回チェック)
(if evt.msg>10 and self.status=='vacant' ここは常にvacantですので無視して下さい。)
(10秒経過したかどうかだけ検知しています。)

10秒経過した時点でsomeiyoshino1.gifがDestroyもしくはSTOPされ、次のsomeiyoshino2.gifが再生されるはず…
チカチカとsomeiyoshino1.gifとsomeiyoshino2.gifが重なって表示されるだけでした。(Qモジュールの箇所をコメントアウトすればその現象を再現できます。)

python

1q = queue.Queue(1) # あってもなくても変わらない 2q.put('go') 3if(q.get() == 'stop')

上記の箇所をコメントアウトすれば、2つの動画が10秒後に重なってチカチカとなるところまではできました。
(4/26 21:15 一番下にそのソースコードを追記しました。)

あとは前動画を削除さえできればいいのではと思ってはいます。

queueモジュールを使ってしまうと、毎回('stop')であるか調べて、Destoroyをしようと思いましたが、なぜかコンソール上で停止してしまいました。

該当のソースコード

python

1import smtplib 2from email.mime.text import MIMEText 3import time 4import _thread 5import wx 6import wx.lib.newevent 7import queue 8import pdb 9import os 10import sys 11import cv2 12import numpy as np 13import wx.adv 14 15q = queue.Queue(1) # あってもなくても変わらない 16q.put('go') 17 18# 新しいイベントクラスとイベントを定義する 19(MyThreadEvent, EVT_MY_THREAD) = wx.lib.newevent.NewEvent() 20class MyThread: 21 def __init__(self, win): 22 self.win = win 23 self.i=0 24 print("一") 25 def Start(self): 26 self.keepGoing = True 27 self.running = True 28 print("MyThread.Start.running") 29 _thread.start_new_thread(self.Run, ()) 30 def Stop(self): 31 self.keepGoing = False 32 def IsRunning(self): 33 return self.running 34 def Run(self): 35 print("四.一") 36 while self.keepGoing: 37 38 # メッセージを格納したイベントを作る 39 #データ受信 40 41 self.i=self.i+1 42 print(self.i) 43 44 # (MyThreadEvent, EVT_MY_THREAD) = wx.lib.newevent.NewEvent() 45 # ↓  46 # self.Bind(EVT_MY_THREAD, self.OnUpdate) 47 # この紐づきでMyThreadEventが呼ばれたときにEVT_MY_THREADイベントが実行されOnUpdateが呼ばれる 48 49 # つまりMyThreadEvent→EVT_MY_THREADイベント→OnUpdateという10秒経過したかチェックメソッドへ 50 evt = MyThreadEvent(msg=self.i) 51 52 # イベントを投げる 53 wx.PostEvent(self.win, evt) 54 time.sleep(1) 55 56 self.running = False 57 print("二") 58class dataserver: 59 def Start(self): 60 _thread.start_new_thread(self.Run, ()) 61 def Run(self): 62 print("八") 63 # self.lock=q.get() # あってもなくても変わらない 64 65class GIF(object): 66 def __init__(self,anime,id,gifname): 67 self.counter = 0 68 self.anime = anime 69 self.file = gifname 70 self.anime = anime 71 self.ani = wx.adv.Animation(self.file) 72 self.ctrl = wx.adv.AnimationCtrl(anime, -1, self.ani, pos=(10, 10)) 73 self.ctrl.Play() 74class GIF_Stop(object): 75 def __init__(self,anime,id,gifname): 76 self.counter = 0 77 self.anime = anime 78 self.file = gifname 79 self.anime = anime 80 self.ani = wx.adv.Animation(self.file) 81 self.ctrl = wx.adv.AnimationCtrl(anime, -1, self.ani, pos=(10, 10)) 82 self.ctrl.Stop() 83 time.sleep(5) 84class GIF_Destroy(object): 85 def __init__(self,anime,id,gifname): 86 self.counter = 0 87 self.anime = anime 88 self.file = gifname 89 self.anime = anime 90 self.ani = wx.adv.Animation(self.file) 91 self.ctrl = wx.adv.AnimationCtrl(anime, -1, self.ani, pos=(10, 10)) 92 self.ctrl.Stop() 93 self.ctrl.DestroyChildren() 94 95 96class MainWindow(wx.Frame): 97 def __init__(self, parent, id, title): 98 wx.Frame.__init__(self, parent, id, title, size=(300, 200)) 99 self.Center() 100 101 # パネル 102 self.display = wx.Panel(self, size=(300, 100), pos=(0, 0)) 103 self.display.SetBackgroundColour('#FFFAF0') 104 self.status = 'vacant' 105 106 # gif 107 self.MainPanel = wx.Panel(self, size=(1300, 900))#メイン画面の大きさ 108 self.anime = wx.Panel(self.MainPanel, pos=(0, 0),size=(1250, 850)) 109 self.anime.SetBackgroundColour("WHITE") 110 gifname = 'someiyoshino2.gif' 111 self.Motion = GIF(self.anime, id, gifname) 112 self.Fit() 113 114 print(q.get()) 115 if(q.get() == 'stop'): 116 gifname = 'someiyoshino2.gif' 117 self.Motion = GIF_Destroy(self.anime, id, gifname) 118 self.Fit() 119 gifname = 'someiyoshino1.gif' 120 self.Motion = GIF(self.anime, id, gifname) 121 self.Fit() 122 123 124 #テキスト 125 self.text1 = wx.StaticText(self.display,id, self.status) 126 font = wx.Font(20, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL) 127 self.text1.SetFont(font) 128 self.text1.Center() 129 130 self.reservation = wx.Panel(self,size=(300, 100), pos=(0, 100)) 131 self.button_1 = wx.Button(self.reservation,id, 'reserve', size=(100, 50)) 132 self.button_1.SetFont(font) 133 self.button_1.Bind(wx.EVT_BUTTON, self.click) 134 135 136 # 新しく定義したイベントに対応する処理関数をバインドする 137 print("三") 138 139 # つまりMyThreadEvent→EVT_MY_THREADイベント→OnUpdateという10秒経過したかチェックメソッドへ 140 self.Bind(EVT_MY_THREAD, self.OnUpdate) 141 print("四") 142 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) 143 # スレッドのインスタンスを作り、起動する 144 # pdb.set_trace() 145 self.my_thread = MyThread(self) 146 self.my_thread.Start() 147 148 # ここから2重スレッド↓↓↓ 149 150 self.Centre() 151 self.Show(True) 152 print("五") 153 154 def click(self, event): 155 # mail1=mail() 156 if self.status == 'vacant': 157 print("六のあとにクリックしたらここにくる 七") 158 self.status='reserved' 159 self.text1.SetLabel(self.status) 160 self.data=dataserver() # あってもなくてもかわらない クラスの呼び方(インスタンス) 161 self.data.Start() 162 # q.put('locked') 163 self.button_1.Disable() 164 # mail1.message() 165 166 def OnCloseWindow(self, evt): 167 print("OnCloseWindow") 168 # スレッドが停止するまで待ってから、ウィンドウを閉じる 169 self.my_thread.Stop() 170 171 while self.my_thread.IsRunning(): 172 time.sleep(0.1) 173 self.Destroy() 174 175 # MyThreadEvent→EVT_MY_THREADイベント→OnUpdateという10秒経過したかチェックメソッドへ 176 def OnUpdate(self, evt): 177 print("六") 178 print("OnUpdate") 179 # スレッドからイベントを受信したときの処理 180 if evt.msg>10 and self.status=='vacant': 181 self.status='occupied' 182 self.text1.SetLabel(self.status) 183 # 10秒後にGIFをSTOP 184 q.put("stop") 185 186 187app = wx.App() 188MainWindow(None, wx.ID_ANY, 'TOTO') 189app.MainLoop()

試したこと

sqliteで変数をselectしてチェックする方法や、スレッド同時並行ではなく、プロセスを2つ作る方法なども試しましたが、いずれも次のgif再生はできませんでした。ただ動画の再生の切り替えだけなのですが、どなたかご存じの方教えて頂けないでしょうか。何か単純なミスをしているか、決定的な基礎知識が抜けている気がしてなりません。こんな需要はすぐに見つかるものだと思っていたのですが、調べてもわからなく困っています。どうかよろしくお願い致します。

補足情報(FW/ツールのバージョンなど)

win10
python3.7
wxpython

参考サイト

https://teratail.com/questions/158458
https://teratail.com/questions/233764
https://qiita.com/wikipediia/items/2919362de582a7d8de9e
https://www.python-beginners.com/entry/20191125/1574687207
https://qiita.com/asakbiz/items/5a34cae7b6c00c87a7e6
https://dev.classmethod.jp/articles/python-asyncio/
https://www.rhoboro.com/2019/02/09/coroutine-abstract.html
https://qiita.com/satsukiya/items/f7a3c7fdae566ed96306
https://www.yoheim.net/blog.php?q=20170601

2020/4/26 追記

Qモジュールを使わず、Onupdateメソッド内に動画切り替えメソッドを置いた場合のソースコードは以下になります。

python

1 2# q = queue.Queue(1) # あってもなくても変わらない 3# q.put('go') 4 56途中省略 78 9 # gif 10 self.MainPanel = wx.Panel(self, size=(1300, 900))#メイン画面の大きさ 11 self.anime = wx.Panel(self.MainPanel, pos=(0, 0),size=(1250, 850)) 12 self.anime.SetBackgroundColour("WHITE") 13 gifname = 'someiyoshino2.gif' 14 self.Motion = GIF(self.anime, id, gifname) 15 self.Fit() 16 17 18 #テキスト 19 self.text1 = wx.StaticText(self.display,id, self.status) 20 font = wx.Font(20, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL) 21 2223途中省略 2425 26 27 # MyThreadEvent→EVT_MY_THREADイベント→OnUpdateという10秒経過したかチェックメソッドへ 28 def OnUpdate(self, evt): 29 print("六") 30 print("OnUpdate") 31 # スレッドからイベントを受信したときの処理 32 if evt.msg>10 and self.status=='vacant': 33 self.status='occupied' 34 self.text1.SetLabel(self.status) 35 # 10秒後にGIFをSTOP 36 gifname = 'someiyoshino2.gif' 37 self.Motion = GIF_Destroy(self.anime, id, gifname) 38 self.Fit() 39 gifname = 'someiyoshino1.gif' 40 self.Motion = GIF(self.anime, id, gifname) 41 self.Fit() 42 43 44app = wx.App() 45MainWindow(None, wx.ID_ANY, 'TOTO') 46app.MainLoop()

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

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

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

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

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

guest

回答1

0

ベストアンサー

短い回答: 10秒ごとの変更でしたら wx.CallLater を使ってはどうでしょう?

スレッドを使う場合、別スレッド内から描画処理等のGUI操作は出来ないので注意です。
スレッドセーフな操作ではない為、大抵のGUIライブラリでは禁止されていたり誤動作を起こします。

もしスレッドで実装する場合は、自分で作成したスレッド側ではデータ変更や通知のみに留め、
描画はメインループに委ねるようにします。(コードは今から拝見します)


CallLater利用周りのみになりますが、該当部分周辺のコードとデモ。
スレッドやキューは不要になります。

python

1from glob import glob 2from itertools import cycle 3from functools import partial 4 5getNextImageFile = partial(next, cycle(glob("*.gif"))) 6 7# getNextImageFile 関数は用途に合わせて実装してください。 8 9# 上記は、カレントディレクトリ内の gif ファイルを繰り返す例 10# next() で次の要素を取り出せるので、上のように定義が出来ます。 11# itertools.cycle で末尾まで到達すると、次は先頭から繰り返します。 12 13def showNextImage(): 14 filepath = getNextImageFile() 15 # NOTE: self.ctrl は wx.adv.AnimationCtrlのインスタンス。適切なスコープにおいてください。 16 self.ctrl.SetAnimation(wx.adv.Animation(filepath)) 17 self.ctrl.Play() 18 wx.CallLater(1000*10, showNextImage) 19 20showNextImage()

デモ用(3秒切り替え)

イメージ説明


追記: コードを追加する場所が解り難かったと思うので、
実行可能なコードを追加します。読み込む gif はカレントディレクトリが対象です。

python

1import wx 2import wx.adv 3 4 5class MainWindow(wx.Frame): 6 def __init__(self, parent, id, title): 7 wx.Frame.__init__(self, parent, id, title, size=(400, 400)) 8 9 self.ctrl = wx.adv.AnimationCtrl(self) 10 self.ctrl.Stop() 11 12 from glob import glob 13 from itertools import cycle 14 from functools import partial 15 16 getNextImageFile = partial(next, cycle(glob("*.gif"))) 17 18 def showNextImage(): 19 filepath = getNextImageFile() 20 self.ctrl.SetAnimation(wx.adv.Animation(filepath)) 21 self.ctrl.Play() 22 23 wx.CallLater(1000*10, showNextImage) 24 showNextImage() 25 26 27if __name__ == '__main__': 28 app = wx.App() 29 win = MainWindow(None, wx.ID_ANY, 'TOTO') 30 win.Centre() 31 win.Show() 32 app.MainLoop()

投稿2020/04/26 15:29

編集2020/04/26 23:31
teamikl

総合スコア8760

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

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

dendenmushi

2020/04/26 23:19 編集

本当にありがとうございます。ぱっと実装したらなぜか動かなかったので今日夜じっくり見させて頂きます。(GWがまだ始まっていません…) 別質問窓にしようか迷っているのですが、同じメモリ内のAスレッドで引数を持たせて、何秒後かにフラグを立たせて、 ''' if __name__ == "__main__": a = Value('d', 0) func1_proc = Process(target=func1, args=(a,)) func2_proc = Process(target=func2, args=(a,)) func1_proc.start() func2_proc.start() func1_proc.join() func2_proc.join() print(a.value) ''' 同じメモリ内のBスレッドでwhileで共有の変数a.Valueが変わったか(フラグが立ったか)、によって動画をチェンジするという方法も考えています。これはwxpythonでは難しいことなのでしょうか。 というのも、秒カウント以外に、何か条件(メールを受信したら等々)で動画をチェンジしたいというのもあります。 (こちらベストアンサーにして再度質問立ち上げた方がいいでしょうかね…)
teamikl

2020/04/26 23:28

コードを置く場所が解り難かったかもしれないので、 スレッド等を省いた、そのまま動くコードを追加しておきます。 >動画をチェンジするという方法も考えています。これはwxpythonでは難しいことなのでしょうか。 同じアプローチで可能です。AnimationCtrl の SetAnimation や Play() のみを CallAfter や CallLater を使ってメインスレッドから呼び出すようにします。 まずはタイマー抜きに、画像や動画を変更するメソッドを実装して、 スレッドからはそのメソッドを CallLater で呼び出す形にすると良いです。 (CallLater は、wxのイベント キューに呼び出す関数を追加します。 MainLoop()内部から順番に実行されるので、上記のコードでも再帰呼び出しにはなりません)
dendenmushi

2020/04/26 23:33 編集

>そのまま動くコードを追加しておきます。 ありがとうございます。 ちょっとその追加の件についても非常に興味があり今日の夜にでも詳しく質問立ち上げますので教えて頂けないでしょうか。よろしくお願い致します。
teamikl

2020/04/27 22:15 編集

スレッド先にも書きましたが、訂正です 誤: CallAfter や CallLater を使ってメインスレッドから呼び出すようにします。 結果として解決策がメインスレッドから呼ばれるようにする事には変わりないのですが、 CallLater/CallAfterでスレッドからのタイマー生成に制限がありました。 この説明が誤り不正確でした→: (CallLater は、wxのイベント キューに呼び出す関数を追加します。 メインスレッドにコードを実行させるには、 ここのコードのコメントアウトされている部分でやろうとしてる、イベント通知の方が安全です。 キューによりスレッド安全が保障されます。 ※ このコメント欄内での説明の訂正です。回答内の説明およびコードは大丈夫です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問