X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8a693e6e0460b6b3c32e4b6f114a3ab7b7cd24ea..f6bcfd974ef26faf6f91a62cac09827e09463fd1:/wxPython/demo/Threads.py?ds=sidebyside diff --git a/wxPython/demo/Threads.py b/wxPython/demo/Threads.py new file mode 100644 index 0000000000..19b759aece --- /dev/null +++ b/wxPython/demo/Threads.py @@ -0,0 +1,244 @@ + +from wxPython.wx import * + +import thread +import time +from whrandom import random + +#---------------------------------------------------------------------- + +wxEVT_UPDATE_BARGRAPH = 25015 + +def EVT_UPDATE_BARGRAPH(win, func): + win.Connect(-1, -1, wxEVT_UPDATE_BARGRAPH, func) + + +class UpdateBarEvent(wxPyEvent): + def __init__(self, barNum, value): + wxPyEvent.__init__(self) + self.SetEventType(wxEVT_UPDATE_BARGRAPH) + self.barNum = barNum + self.value = value + + +#---------------------------------------------------------------------- + +class CalcBarThread: + def __init__(self, win, barNum, val): + self.win = win + self.barNum = barNum + self.val = val + + def Start(self): + self.keepGoing = self.running = true + thread.start_new_thread(self.Run, ()) + + def Stop(self): + self.keepGoing = false + + def IsRunning(self): + return self.running + + def Run(self): + while self.keepGoing: + evt = UpdateBarEvent(self.barNum, int(self.val)) + wxPostEvent(self.win, evt) + del evt + + sleeptime = (random() * 2) + 0.5 + #print self.barNum, 'sleeping for', sleeptime + time.sleep(sleeptime) + + sleeptime = sleeptime * 5 + if int(random() * 2): + self.val = self.val + sleeptime + else: + self.val = self.val - sleeptime + + if self.val < 0: self.val = 0 + if self.val > 300: self.val = 300 + + self.running = false + +#---------------------------------------------------------------------- + + +class GraphWindow(wxWindow): + def __init__(self, parent, labels): + wxWindow.__init__(self, parent, -1) + + self.values = [] + for label in labels: + self.values.append((label, 0)) + + self.font = wxFont(12, wxSWISS, wxNORMAL, wxBOLD) + self.SetFont(self.font) + + self.colors = [ wxRED, wxGREEN, wxBLUE, wxCYAN, + wxNamedColour("Yellow"), wxNamedColor("Navy") ] + + EVT_ERASE_BACKGROUND(self, self.OnEraseBackground) + EVT_PAINT(self, self.OnPaint) + + + def SetValue(self, index, value): + assert index < len(self.values) + cur = self.values[index] + self.values[index:index+1] = [(cur[0], value)] + + + def SetFont(self, font): + wxWindow.SetFont(self, font) + wmax = hmax = 0 + for label, val in self.values: + w,h = self.GetTextExtent(label) + if w > wmax: wmax = w + if h > hmax: hmax = h + self.linePos = wmax + 10 + self.barHeight = hmax + + + def Draw(self, dc, size): + dc.SetFont(self.font) + dc.SetTextForeground(wxBLUE) + dc.SetBackground(wxBrush(self.GetBackgroundColour())) + dc.Clear() + dc.SetPen(wxPen(wxBLACK, 3, wxSOLID)) + dc.DrawLine(self.linePos, 0, self.linePos, size.height-10) + + bh = ypos = self.barHeight + for x in range(len(self.values)): + label, val = self.values[x] + dc.DrawText(label, 5, ypos) + + if val: + color = self.colors[ x % len(self.colors) ] + dc.SetPen(wxPen(color)) + dc.SetBrush(wxBrush(color)) + dc.DrawRectangle(self.linePos+3, ypos, val, bh) + + ypos = ypos + 2*bh + if ypos > size.height-10: + break + + + def OnPaint(self, evt): + size = self.GetSize() + bmp = wxEmptyBitmap(size.width, size.height) + dc = wxMemoryDC() + dc.SelectObject(bmp) + self.Draw(dc, size) + + wdc = wxPaintDC(self) + wdc.BeginDrawing() + wdc.Blit(0,0, size.width, size.height, dc, 0,0) + wdc.EndDrawing() + + dc.SelectObject(wxNullBitmap) + + + def OnEraseBackground(self, evt): + pass + + + + +#---------------------------------------------------------------------- + +class TestFrame(wxFrame): + def __init__(self, parent, log): + wxFrame.__init__(self, parent, -1, "Thread Test", size=(450,300)) + self.log = log + + #self.CenterOnParent() + + panel = wxPanel(self, -1) + panel.SetFont(wxFont(10, wxSWISS, wxNORMAL, wxBOLD)) + wxStaticText(panel, -1, + "This demo shows multiple threads interacting with this\n" + "window by sending events to it.", wxPoint(5,5)) + panel.Fit() + + self.graph = GraphWindow(self, ['Zero', 'One', 'Two', 'Three', 'Four', + 'Five', 'Six', 'Seven']) + + sizer = wxBoxSizer(wxVERTICAL) + sizer.Add(panel, 0, wxEXPAND) + sizer.Add(self.graph, 1, wxEXPAND) + + self.SetSizer(sizer) + self.SetAutoLayout(true) + + #self.graph.SetValue(0, 25) + #self.graph.SetValue(1, 50) + #self.graph.SetValue(2, 75) + #self.graph.SetValue(3, 100) + + EVT_UPDATE_BARGRAPH(self, self.OnUpdate) + self.threads = [] + self.threads.append(CalcBarThread(self, 0, 50)) + self.threads.append(CalcBarThread(self, 1, 75)) + self.threads.append(CalcBarThread(self, 2, 100)) + self.threads.append(CalcBarThread(self, 3, 150)) + self.threads.append(CalcBarThread(self, 4, 225)) + self.threads.append(CalcBarThread(self, 5, 300)) + self.threads.append(CalcBarThread(self, 6, 250)) + self.threads.append(CalcBarThread(self, 7, 175)) + + for t in self.threads: + t.Start() + + EVT_CLOSE(self, self.OnCloseWindow) + + + def OnUpdate(self, evt): + self.graph.SetValue(evt.barNum, evt.value) + self.graph.Refresh(false) + + + def OnCloseWindow(self, evt): + busy = wxBusyInfo("One moment please, waiting for threads to die...") + for t in self.threads: + t.Stop() + running = 1 + while running: + running = 0 + for t in self.threads: + running = running + t.IsRunning() + time.sleep(0.1) + self.Destroy() + + + +#---------------------------------------------------------------------- + +def runTest(frame, nb, log): + win = TestFrame(frame, log) + frame.otherWin = win + win.Show(true) + return None + +#---------------------------------------------------------------------- + + + + +overview = """\ +The main issue with multi-threaded GUI programming is the thread safty +of the GUI itself. On most platforms the GUI is not thread safe and +so any cross platform GUI Toolkit and applications written with it +need to take that into account. + +The solution is to only allow interaction with the GUI from a single +thread, but this often severly limits what can be done in an +application and makes it difficult to use additional threads at all. + +Since wxPython already makes extensive use of event handlers, it is a +logical extension to allow events to be sent to GUI objects from +alternate threads. A function called wxPostEvent allows you to do +this. It accepts an event and an event handler (window) and instead +of sending the event immediately in the current context like +ProcessEvent does, it processes it later from the context of the GUI +thread. + +"""