]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/demo/Threads.py
07910dc4d75fabebba3a18b8ad12d841414cc61e
9 #----------------------------------------------------------------------
11 # This creates a new Event class and a EVT binder function
12 (UpdateBarEvent
, EVT_UPDATE_BARGRAPH
) = wx
.lib
.newevent
.NewEvent()
15 #----------------------------------------------------------------------
18 def __init__(self
, win
, barNum
, val
):
24 self
.keepGoing
= self
.running
= True
25 thread
.start_new_thread(self
.Run
, ())
28 self
.keepGoing
= False
35 evt
= UpdateBarEvent(barNum
= self
.barNum
, value
= int(self
.val
))
36 wx
.PostEvent(self
.win
, evt
)
38 sleeptime
= (random
.random() * 2) + 0.5
39 time
.sleep(sleeptime
/4)
41 sleeptime
= sleeptime
* 5
42 if int(random
.random() * 2):
43 self
.val
= self
.val
+ sleeptime
45 self
.val
= self
.val
- sleeptime
47 if self
.val
< 0: self
.val
= 0
48 if self
.val
> 300: self
.val
= 300
52 #----------------------------------------------------------------------
55 class GraphWindow(wx
.Window
):
56 def __init__(self
, parent
, labels
):
57 wx
.Window
.__init
__(self
, parent
, -1)
61 self
.values
.append((label
, 0))
63 font
= wx
.Font(12, wx
.SWISS
, wx
.NORMAL
, wx
.BOLD
)
66 self
.colors
= [ wx
.RED
, wx
.GREEN
, wx
.BLUE
, wx
.CYAN
,
69 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
70 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
73 def SetValue(self
, index
, value
):
74 assert index
< len(self
.values
)
75 cur
= self
.values
[index
]
76 self
.values
[index
:index
+1] = [(cur
[0], value
)]
79 def SetFont(self
, font
):
80 wx
.Window
.SetFont(self
, font
)
82 for label
, val
in self
.values
:
83 w
,h
= self
.GetTextExtent(label
)
86 self
.linePos
= wmax
+ 10
90 def GetBestHeight(self
):
91 return 2 * (self
.barHeight
+ 1) * len(self
.values
)
94 def Draw(self
, dc
, size
):
95 dc
.SetFont(self
.GetFont())
96 dc
.SetTextForeground(wx
.BLUE
)
97 dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour()))
99 dc
.SetPen(wx
.Pen(wx
.BLACK
, 3, wx
.SOLID
))
100 dc
.DrawLine(self
.linePos
, 0, self
.linePos
, size
.height
-10)
102 bh
= ypos
= self
.barHeight
103 for x
in range(len(self
.values
)):
104 label
, val
= self
.values
[x
]
105 dc
.DrawText(label
, 5, ypos
)
108 color
= self
.colors
[ x
% len(self
.colors
) ]
109 dc
.SetPen(wx
.Pen(color
))
110 dc
.SetBrush(wx
.Brush(color
))
111 dc
.DrawRectangle(self
.linePos
+3, ypos
, val
, bh
)
114 if ypos
> size
[1]-10:
118 def OnPaint(self
, evt
):
119 width
, height
= size
=self
.GetSize()
120 bmp
= wx
.EmptyBitmap(width
, height
)
128 wdc
= wx
.PaintDC(self
)
130 wdc
.Blit(0,0, size
[0], size
[1], dc
, 0,0)
133 dc
.SelectObject(wx
.NullBitmap
)
136 def OnEraseBackground(self
, evt
):
142 #----------------------------------------------------------------------
144 class TestFrame(wx
.Frame
):
145 def __init__(self
, parent
, log
):
146 wx
.Frame
.__init
__(self
, parent
, -1, "Thread Test", size
=(450,300))
149 #self.CenterOnParent()
151 panel
= wx
.Panel(self
, -1)
152 panel
.SetFont(wx
.Font(10, wx
.SWISS
, wx
.NORMAL
, wx
.BOLD
))
153 wx
.StaticText(panel
, -1,
154 "This demo shows multiple threads interacting with this\n"
155 "window by sending events to it, one thread for each bar.",
159 self
.graph
= GraphWindow(self
, ['Zero', 'One', 'Two', 'Three', 'Four',
160 'Five', 'Six', 'Seven'])
161 self
.graph
.SetSize((450, self
.graph
.GetBestHeight()))
163 sizer
= wx
.BoxSizer(wx
.VERTICAL
)
164 sizer
.Add(panel
, 0, wx
.EXPAND
)
165 sizer
.Add(self
.graph
, 1, wx
.EXPAND
)
168 self
.SetAutoLayout(True)
171 self
.Bind(EVT_UPDATE_BARGRAPH
, self
.OnUpdate
)
174 self
.threads
.append(CalcBarThread(self
, 0, 50))
175 self
.threads
.append(CalcBarThread(self
, 1, 75))
176 self
.threads
.append(CalcBarThread(self
, 2, 100))
177 self
.threads
.append(CalcBarThread(self
, 3, 150))
178 self
.threads
.append(CalcBarThread(self
, 4, 225))
179 self
.threads
.append(CalcBarThread(self
, 5, 300))
180 self
.threads
.append(CalcBarThread(self
, 6, 250))
181 self
.threads
.append(CalcBarThread(self
, 7, 175))
183 for t
in self
.threads
:
186 self
.Bind(wx
.EVT_CLOSE
, self
.OnCloseWindow
)
189 def OnUpdate(self
, evt
):
190 self
.graph
.SetValue(evt
.barNum
, evt
.value
)
191 self
.graph
.Refresh(False)
194 def OnCloseWindow(self
, evt
):
195 busy
= wx
.BusyInfo("One moment please, waiting for threads to die...")
198 for t
in self
.threads
:
206 for t
in self
.threads
:
207 running
= running
+ t
.IsRunning()
215 #---------------------------------------------------------------------------
217 class TestPanel(wx
.Panel
):
218 def __init__(self
, parent
, log
):
220 wx
.Panel
.__init
__(self
, parent
, -1)
222 b
= wx
.Button(self
, -1, "Show Threads sample", (50,50))
223 self
.Bind(wx
.EVT_BUTTON
, self
.OnButton
, b
)
226 def OnButton(self
, evt
):
227 win
= TestFrame(self
, self
.log
)
231 #---------------------------------------------------------------------------
234 def runTest(frame
, nb
, log
):
235 win
= TestPanel(nb
, log
)
238 #----------------------------------------------------------------------
245 The main issue with multi-threaded GUI programming is the thread safty
246 of the GUI itself. On most platforms the GUI is not thread safe and
247 so any cross platform GUI Toolkit and applications written with it
248 need to take that into account.
250 The solution is to only allow interaction with the GUI from a single
251 thread, but this often severely limits what can be done in an
252 application and makes it difficult to use additional threads at all.
254 Since wxPython already makes extensive use of event handlers, it is a
255 logical extension to allow events to be sent to GUI objects from
256 alternate threads. A function called wx.PostEvent allows you to do
257 this. It accepts an event and an event handler (window) and instead
258 of sending the event immediately in the current context like
259 ProcessEvent does, it processes it later from the context of the GUI
266 if __name__
== '__main__':
269 run
.main(['', os
.path
.basename(sys
.argv
[0])] + sys
.argv
[1:])