]> git.saurik.com Git - wxWidgets.git/blob - wxPython/demo/Threads.py
Fixed a bunch of leaking references in how the callbacks deal with
[wxWidgets.git] / wxPython / demo / Threads.py
1
2 from wxPython.wx import *
3
4 import thread
5 import time
6 from whrandom import random
7
8 #----------------------------------------------------------------------
9
10 wxEVT_UPDATE_BARGRAPH = 25015
11
12 def EVT_UPDATE_BARGRAPH(win, func):
13 win.Connect(-1, -1, wxEVT_UPDATE_BARGRAPH, func)
14
15
16 class UpdateBarEvent(wxPyEvent):
17 def __init__(self, barNum, value):
18 wxPyEvent.__init__(self)
19 self.SetEventType(wxEVT_UPDATE_BARGRAPH)
20 self.barNum = barNum
21 self.value = value
22
23
24 #----------------------------------------------------------------------
25
26 class CalcBarThread:
27 def __init__(self, win, barNum, val):
28 self.win = win
29 self.barNum = barNum
30 self.val = val
31
32 def Start(self):
33 self.keepGoing = self.running = true
34 thread.start_new_thread(self.Run, ())
35
36 def Stop(self):
37 self.keepGoing = false
38
39 def IsRunning(self):
40 return self.running
41
42 def Run(self):
43 while self.keepGoing:
44 evt = UpdateBarEvent(self.barNum, int(self.val))
45 wxPostEvent(self.win, evt)
46 del evt
47
48 sleeptime = (random() * 2) + 0.5
49 #print self.barNum, 'sleeping for', sleeptime
50 time.sleep(sleeptime)
51
52 sleeptime = sleeptime * 5
53 if int(random() * 2):
54 self.val = self.val + sleeptime
55 else:
56 self.val = self.val - sleeptime
57
58 if self.val < 0: self.val = 0
59 if self.val > 300: self.val = 300
60
61 self.running = false
62
63 #----------------------------------------------------------------------
64
65
66 class GraphWindow(wxWindow):
67 def __init__(self, parent, labels):
68 wxWindow.__init__(self, parent, -1)
69
70 self.values = []
71 for label in labels:
72 self.values.append((label, 0))
73
74 self.font = wxFont(12, wxSWISS, wxNORMAL, wxBOLD)
75 self.SetFont(self.font)
76
77 self.colors = [ wxRED, wxGREEN, wxBLUE, wxCYAN,
78 wxNamedColour("Yellow"), wxNamedColor("Navy") ]
79
80 EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
81 EVT_PAINT(self, self.OnPaint)
82
83
84 def SetValue(self, index, value):
85 assert index < len(self.values)
86 cur = self.values[index]
87 self.values[index:index+1] = [(cur[0], value)]
88
89
90 def SetFont(self, font):
91 wxWindow.SetFont(self, font)
92 wmax = hmax = 0
93 for label, val in self.values:
94 w,h = self.GetTextExtent(label)
95 if w > wmax: wmax = w
96 if h > hmax: hmax = h
97 self.linePos = wmax + 10
98 self.barHeight = hmax
99
100
101 def Draw(self, dc, size):
102 dc.SetFont(self.font)
103 dc.SetTextForeground(wxBLUE)
104 dc.SetBackground(wxBrush(self.GetBackgroundColour()))
105 dc.Clear()
106 dc.SetPen(wxPen(wxBLACK, 3, wxSOLID))
107 dc.DrawLine(self.linePos, 0, self.linePos, size.height-10)
108
109 bh = ypos = self.barHeight
110 for x in range(len(self.values)):
111 label, val = self.values[x]
112 dc.DrawText(label, 5, ypos)
113
114 if val:
115 color = self.colors[ x % len(self.colors) ]
116 dc.SetPen(wxPen(color))
117 dc.SetBrush(wxBrush(color))
118 dc.DrawRectangle(self.linePos+3, ypos, val, bh)
119
120 ypos = ypos + 2*bh
121 if ypos > size.height-10:
122 break
123
124
125 def OnPaint(self, evt):
126 size = self.GetSize()
127 bmp = wxEmptyBitmap(size.width, size.height)
128 dc = wxMemoryDC()
129 dc.SelectObject(bmp)
130 self.Draw(dc, size)
131
132 wdc = wxPaintDC(self)
133 wdc.BeginDrawing()
134 wdc.Blit(0,0, size.width, size.height, dc, 0,0)
135 wdc.EndDrawing()
136
137 dc.SelectObject(wxNullBitmap)
138
139
140 def OnEraseBackground(self, evt):
141 pass
142
143
144
145
146 #----------------------------------------------------------------------
147
148 class TestFrame(wxFrame):
149 def __init__(self, parent, log):
150 wxFrame.__init__(self, parent, -1, "Thread Test", size=(450,300))
151 self.log = log
152
153 #self.CenterOnParent()
154
155 panel = wxPanel(self, -1)
156 panel.SetFont(wxFont(10, wxSWISS, wxNORMAL, wxBOLD))
157 wxStaticText(panel, -1,
158 "This demo shows multiple threads interacting with this\n"
159 "window by sending events to it.", wxPoint(5,5))
160 panel.Fit()
161
162 self.graph = GraphWindow(self, ['Zero', 'One', 'Two', 'Three', 'Four',
163 'Five', 'Six', 'Seven'])
164
165 sizer = wxBoxSizer(wxVERTICAL)
166 sizer.Add(panel, 0, wxEXPAND)
167 sizer.Add(self.graph, 1, wxEXPAND)
168
169 self.SetSizer(sizer)
170 self.SetAutoLayout(true)
171
172 #self.graph.SetValue(0, 25)
173 #self.graph.SetValue(1, 50)
174 #self.graph.SetValue(2, 75)
175 #self.graph.SetValue(3, 100)
176
177 EVT_UPDATE_BARGRAPH(self, self.OnUpdate)
178 self.threads = []
179 self.threads.append(CalcBarThread(self, 0, 50))
180 self.threads.append(CalcBarThread(self, 1, 75))
181 self.threads.append(CalcBarThread(self, 2, 100))
182 self.threads.append(CalcBarThread(self, 3, 150))
183 self.threads.append(CalcBarThread(self, 4, 225))
184 self.threads.append(CalcBarThread(self, 5, 300))
185 self.threads.append(CalcBarThread(self, 6, 250))
186 self.threads.append(CalcBarThread(self, 7, 175))
187
188 for t in self.threads:
189 t.Start()
190
191 EVT_CLOSE(self, self.OnCloseWindow)
192
193
194 def OnUpdate(self, evt):
195 self.graph.SetValue(evt.barNum, evt.value)
196 self.graph.Refresh(false)
197
198
199 def OnCloseWindow(self, evt):
200 busy = wxBusyInfo("One moment please, waiting for threads to die...")
201 for t in self.threads:
202 t.Stop()
203 running = 1
204 while running:
205 running = 0
206 for t in self.threads:
207 running = running + t.IsRunning()
208 time.sleep(0.1)
209 self.Destroy()
210
211
212
213 #----------------------------------------------------------------------
214
215 def runTest(frame, nb, log):
216 win = TestFrame(frame, log)
217 frame.otherWin = win
218 win.Show(true)
219 return None
220
221 #----------------------------------------------------------------------
222
223
224
225
226 overview = """\
227 The main issue with multi-threaded GUI programming is the thread safty
228 of the GUI itself. On most platforms the GUI is not thread safe and
229 so any cross platform GUI Toolkit and applications written with it
230 need to take that into account.
231
232 The solution is to only allow interaction with the GUI from a single
233 thread, but this often severly limits what can be done in an
234 application and makes it difficult to use additional threads at all.
235
236 Since wxPython already makes extensive use of event handlers, it is a
237 logical extension to allow events to be sent to GUI objects from
238 alternate threads. A function called wxPostEvent allows you to do
239 this. It accepts an event and an event handler (window) and instead
240 of sending the event immediately in the current context like
241 ProcessEvent does, it processes it later from the context of the GUI
242 thread.
243
244 """