最近寫python需要程式一直在迴圈裡面跑
如果用while
true的話會當機,沒有辦法按下停止鍵
看了下面兩個非常好的reference來練習一下怎麼寫wxpython
thread
Reference
:
在wxpython下有三個最安全的方式來寫thread
wx.PostEvent
wx.CallAfter
wx.CallLater
wx.PostEvent是最低階的方式,那那那那就先用它來寫寫看吧!!!!!
這個練習是使用者按下start鍵,表示開始執行另一個thread,然後右邊會反覆出現紅色與綠色的按鍵還有說明,最下面顯示這個迴圈已經被跑了幾次,每次跑的時候先從txt中讀出上次已經跑的次數,然後再繼續累加(所以要先在同一個資料夾下面準備一個test_cnt.txt的檔案),順便練習讀寫file還有把file放在textctrl中顯示,按下stop則停止這個thread
用wxglade建立好視窗之後要加入
from
threading import Thread
這邊寫thread最主要的關鍵就是EVT_RESULT_ID
在wx.frame中再用ResultEvent來跟thread溝通
在wx.Frame中如果使用者按下start,則會連到thread
class來執行run
function
在run
function中,先把txt中的值讀出來,int()把string轉成int來累加,利用wx.PostEvent(self._notify_window,ResultEvent("Green"))
來告訴wx.Frame
class在GUI上要做甚麼變化,如果使用者按下STOP則會傳到abort中,然後傳到run中停止執行緒
test.py
import wx # begin wxGlade: dependencies import gettext # end wxGlade # begin wxGlade: extracode # end wxGlade ############### Definitions ############### from threading import Thread import time import os ########################################### ############### Threads ####################### # Define notification event for thread completion EVT_RESULT_ID = wx.NewId() def EVT_RESULT(win, func): """Define Result Event.""" win.Connect(-1, -1, EVT_RESULT_ID, func) class ResultEvent(wx.PyEvent): """Simple event to carry arbitrary result data.""" def __init__(self, data): wx.PyEvent.__init__(self) self.SetEventType(EVT_RESULT_ID) self.data = data class WorkerThread(Thread): def __init__(self,notify_window): Thread.__init__(self) self._notify_window = notify_window self._want_abort = 0 self.start() def run(self): print("Read test_cnt.txt ") fread = file('test_cnt.txt','r') while True: text = fread.readline() if len(text) == 0: break; print text cnt = int(text) fread.close() while True: time.sleep(1) if self._want_abort: wx.PostEvent(self._notify_window,ResultEvent(None)) return wx.PostEvent(self._notify_window,ResultEvent("Green")) time.sleep(1) wx.PostEvent(self._notify_window,ResultEvent("Red")) time.sleep(1) fwrite = file('test_cnt.txt','w') cnt += 1 fwrite.write("%s"%cnt) fwrite.close() time.sleep(1) wx.PostEvent(self._notify_window,ResultEvent("cnt")) def abort(self): self._want_abort = 1 ################################################ class MyFrame(wx.Frame): def __init__(self, *args, **kwds): # begin wxGlade: MyFrame.__init__ kwds["style"] = wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, *args, **kwds) self.window_1 = wx.SplitterWindow(self, wx.ID_ANY, style=wx.SP_3D | wx.SP_BORDER) self.window_1_pane_1 = wx.Panel(self.window_1, wx.ID_ANY) self.start = wx.Button(self.window_1_pane_1, wx.ID_ANY, _("Start")) self.stop = wx.Button(self.window_1_pane_1, wx.ID_ANY, _("Stop")) self.window_1_pane_2 = wx.Panel(self.window_1, wx.ID_ANY) self.light = wx.Button(self.window_1_pane_2, wx.ID_ANY, _("")) self.status = wx.StaticText(self.window_1_pane_2, wx.ID_ANY, _("Status\n")) self.cnt = wx.TextCtrl(self.window_1_pane_2, wx.ID_ANY, "") self.__set_properties() self.__do_layout() # end wxGlade ################# init function ################## rd_log = open(os.path.join('', 'test_cnt.txt'), 'r') self.cnt.SetValue(rd_log.read()) rd_log.close() ################# function binding ############### self.Bind(wx.EVT_BUTTON,self.EVTSTART,self.start) self.Bind(wx.EVT_BUTTON,self.EVTSTOP,self.stop) EVT_RESULT(self,self.updateDisplay) self.worker = None ################################################### ################# function binding ############### def EVTSTART(self,event): print("Start testing") self.worker = WorkerThread(self) def EVTSTOP(self,event): print("End testing") self.worker.abort() def updateDisplay(self,event): if event.data is "Green": self.light.SetBackgroundColour(wx.Colour(0, 255, 127)) self.status.SetLabel('Green') if event.data is "Red": self.light.SetBackgroundColour(wx.Colour(255, 0, 61)) self.status.SetLabel('Red') if event.data is "cnt": rd_log = open(os.path.join('', 'test_cnt.txt'), 'r') self.cnt.SetValue(rd_log.read()) rd_log.close() ################################################### def __set_properties(self): # begin wxGlade: MyFrame.__set_properties self.SetTitle(_("Test")) self.start.SetMinSize((100, 80)) self.stop.SetMinSize((100, 80)) self.window_1_pane_1.SetMinSize((192, 257)) self.light.SetMinSize((100, 40)) self.window_1_pane_2.SetMinSize((188, 257)) # end wxGlade def __do_layout(self): # begin wxGlade: MyFrame.__do_layout sizer_2 = wx.BoxSizer(wx.VERTICAL) sizer_5 = wx.BoxSizer(wx.VERTICAL) sizer_3 = wx.BoxSizer(wx.HORIZONTAL) sizer_4 = wx.BoxSizer(wx.VERTICAL) sizer_4.Add(self.start, 0, wx.LEFT | wx.TOP | wx.BOTTOM, 20) sizer_4.Add(self.stop, 0, wx.LEFT | wx.TOP, 20) sizer_3.Add(sizer_4, 1, wx.EXPAND, 0) self.window_1_pane_1.SetSizer(sizer_3) sizer_5.Add(self.light, 0, wx.LEFT | wx.TOP, 40) sizer_5.Add(self.status, 0, wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_CENTER_HORIZONTAL, 40) sizer_5.Add(self.cnt, 0, wx.TOP | wx.ALIGN_CENTER_HORIZONTAL, 30) self.window_1_pane_2.SetSizer(sizer_5) self.window_1.SplitVertically(self.window_1_pane_1, self.window_1_pane_2) sizer_2.Add(self.window_1, 1, wx.EXPAND, 0) self.SetSizer(sizer_2) sizer_2.Fit(self) self.Layout() # end wxGlade # end of class MyFrame if __name__ == "__main__": gettext.install("app") # replace with the appropriate catalog name app = wx.PySimpleApp(0) wx.InitAllImageHandlers() Test = MyFrame(None, wx.ID_ANY, "") app.SetTopWindow(Test) Test.Show() app.MainLoop() |
如果用callafter則如下
import wx # begin wxGlade: dependencies import gettext # end wxGlade # begin wxGlade: extracode # end wxGlade ############### Definitions ############### from threading import Thread from wx.lib.pubsub import Publisher import time import os ########################################### ############### Threads ####################### class WorkerThread(Thread): def __init__(self,notify_window): Thread.__init__(self) self._notify_window = notify_window self._want_abort = 0 self.start() def run(self): print("Read test_cnt.txt ") fread = file('test_cnt.txt','r') while True: text = fread.readline() if len(text) == 0: break; print text cnt = int(text) fread.close() while True: time.sleep(1) if self._want_abort: wx.CallAfter(Publisher().sendMessage, "update", None) return wx.CallAfter(Publisher().sendMessage, "update", "Green") time.sleep(1) wx.CallAfter(Publisher().sendMessage, "update", "Red") time.sleep(1) fwrite = file('test_cnt.txt','w') cnt += 1 fwrite.write("%s"%cnt) fwrite.close() time.sleep(1) wx.CallAfter(Publisher().sendMessage, "update", "cnt") def abort(self): self._want_abort = 1 ################################################ class MyFrame(wx.Frame): def __init__(self, *args, **kwds): # begin wxGlade: MyFrame.__init__ kwds["style"] = wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, *args, **kwds) self.window_1 = wx.SplitterWindow(self, wx.ID_ANY, style=wx.SP_3D | wx.SP_BORDER) self.window_1_pane_1 = wx.Panel(self.window_1, wx.ID_ANY) self.start = wx.Button(self.window_1_pane_1, wx.ID_ANY, _("Start")) self.stop = wx.Button(self.window_1_pane_1, wx.ID_ANY, _("Stop")) self.window_1_pane_2 = wx.Panel(self.window_1, wx.ID_ANY) self.light = wx.Button(self.window_1_pane_2, wx.ID_ANY, _("")) self.status = wx.StaticText(self.window_1_pane_2, wx.ID_ANY, _("Status\n")) self.cnt = wx.TextCtrl(self.window_1_pane_2, wx.ID_ANY, "") self.__set_properties() self.__do_layout() # end wxGlade ################# init function ################## rd_log = open(os.path.join('', 'test_cnt.txt'), 'r') self.cnt.SetValue(rd_log.read()) rd_log.close() ################# function binding ############### self.Bind(wx.EVT_BUTTON,self.EVTSTART,self.start) self.Bind(wx.EVT_BUTTON,self.EVTSTOP,self.stop) Publisher().subscribe(self.updateDisplay, "update") self.worker = None ################################################### ################# function binding ############### def EVTSTART(self,event): print("Start testing") self.worker = WorkerThread(self) def EVTSTOP(self,event): print("End testing") self.worker.abort() def updateDisplay(self,event): if event.data is "Green": self.light.SetBackgroundColour(wx.Colour(0, 255, 127)) self.status.SetLabel('Green') if event.data is "Red": self.light.SetBackgroundColour(wx.Colour(255, 0, 61)) self.status.SetLabel('Red') if event.data is "cnt": rd_log = open(os.path.join('', 'test_cnt.txt'), 'r') self.cnt.SetValue(rd_log.read()) rd_log.close() ################################################### def __set_properties(self): # begin wxGlade: MyFrame.__set_properties self.SetTitle(_("Test")) self.start.SetMinSize((100, 80)) self.stop.SetMinSize((100, 80)) self.window_1_pane_1.SetMinSize((192, 257)) self.light.SetMinSize((100, 40)) self.window_1_pane_2.SetMinSize((188, 257)) # end wxGlade def __do_layout(self): # begin wxGlade: MyFrame.__do_layout sizer_2 = wx.BoxSizer(wx.VERTICAL) sizer_5 = wx.BoxSizer(wx.VERTICAL) sizer_3 = wx.BoxSizer(wx.HORIZONTAL) sizer_4 = wx.BoxSizer(wx.VERTICAL) sizer_4.Add(self.start, 0, wx.LEFT | wx.TOP | wx.BOTTOM, 20) sizer_4.Add(self.stop, 0, wx.LEFT | wx.TOP, 20) sizer_3.Add(sizer_4, 1, wx.EXPAND, 0) self.window_1_pane_1.SetSizer(sizer_3) sizer_5.Add(self.light, 0, wx.LEFT | wx.TOP, 40) sizer_5.Add(self.status, 0, wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_CENTER_HORIZONTAL, 40) sizer_5.Add(self.cnt, 0, wx.TOP | wx.ALIGN_CENTER_HORIZONTAL, 30) self.window_1_pane_2.SetSizer(sizer_5) self.window_1.SplitVertically(self.window_1_pane_1, self.window_1_pane_2) sizer_2.Add(self.window_1, 1, wx.EXPAND, 0) self.SetSizer(sizer_2) sizer_2.Fit(self) self.Layout() # end wxGlade # end of class MyFrame if __name__ == "__main__": gettext.install("app") # replace with the appropriate catalog name app = wx.PySimpleApp(0) wx.InitAllImageHandlers() Test = MyFrame(None, wx.ID_ANY, "") app.SetTopWindow(Test) Test.Show() app.MainLoop() |
沒有留言:
張貼留言