2003-12-09 01:23:28 +00:00
|
|
|
|
|
|
|
import random
|
|
|
|
import time
|
|
|
|
import thread
|
|
|
|
|
|
|
|
import wx
|
|
|
|
import wx.lib.newevent
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
2003-10-02 00:58:06 +00:00
|
|
|
# This creates a new Event class and a EVT binder function
|
2003-12-09 01:23:28 +00:00
|
|
|
(UpdateBarEvent, EVT_UPDATE_BARGRAPH) = wx.lib.newevent.NewEvent()
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
|
|
class CalcBarThread:
|
|
|
|
def __init__(self, win, barNum, val):
|
|
|
|
self.win = win
|
|
|
|
self.barNum = barNum
|
|
|
|
self.val = val
|
|
|
|
|
|
|
|
def Start(self):
|
2003-03-25 06:35:27 +00:00
|
|
|
self.keepGoing = self.running = True
|
1999-11-13 05:52:53 +00:00
|
|
|
thread.start_new_thread(self.Run, ())
|
|
|
|
|
|
|
|
def Stop(self):
|
2003-03-25 06:35:27 +00:00
|
|
|
self.keepGoing = False
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
def IsRunning(self):
|
|
|
|
return self.running
|
|
|
|
|
|
|
|
def Run(self):
|
|
|
|
while self.keepGoing:
|
2003-10-02 00:58:06 +00:00
|
|
|
evt = UpdateBarEvent(barNum = self.barNum, value = int(self.val))
|
2003-12-09 01:23:28 +00:00
|
|
|
wx.PostEvent(self.win.GetEventHandler(), evt)
|
2001-12-19 21:25:11 +00:00
|
|
|
#del evt
|
1999-11-13 05:52:53 +00:00
|
|
|
|
2003-12-09 01:23:28 +00:00
|
|
|
sleeptime = (random.random() * 2) + 0.5
|
2001-12-19 21:25:11 +00:00
|
|
|
time.sleep(sleeptime/4)
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
sleeptime = sleeptime * 5
|
2003-12-09 01:23:28 +00:00
|
|
|
if int(random.random() * 2):
|
1999-11-13 05:52:53 +00:00
|
|
|
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
|
|
|
|
|
2003-03-25 06:35:27 +00:00
|
|
|
self.running = False
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
2003-12-09 01:23:28 +00:00
|
|
|
class GraphWindow(wx.Window):
|
1999-11-13 05:52:53 +00:00
|
|
|
def __init__(self, parent, labels):
|
2003-12-09 01:23:28 +00:00
|
|
|
wx.Window.__init__(self, parent, -1)
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
self.values = []
|
|
|
|
for label in labels:
|
|
|
|
self.values.append((label, 0))
|
|
|
|
|
2003-12-09 01:23:28 +00:00
|
|
|
font = wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD)
|
2001-11-20 06:04:42 +00:00
|
|
|
self.SetFont(font)
|
1999-11-13 05:52:53 +00:00
|
|
|
|
2003-12-09 01:23:28 +00:00
|
|
|
self.colors = [ wx.RED, wx.GREEN, wx.BLUE, wx.CYAN,
|
2001-11-20 06:04:42 +00:00
|
|
|
"Yellow", "Navy" ]
|
1999-11-13 05:52:53 +00:00
|
|
|
|
2003-12-09 01:23:28 +00:00
|
|
|
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
|
|
|
|
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
2000-07-15 19:51:35 +00:00
|
|
|
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
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):
|
2003-12-09 01:23:28 +00:00
|
|
|
wx.Window.SetFont(self, font)
|
1999-11-13 05:52:53 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2001-11-20 06:04:42 +00:00
|
|
|
def GetBestHeight(self):
|
|
|
|
return 2 * (self.barHeight + 1) * len(self.values)
|
|
|
|
|
|
|
|
|
1999-11-13 07:33:29 +00:00
|
|
|
def Draw(self, dc, size):
|
2001-11-20 06:04:42 +00:00
|
|
|
dc.SetFont(self.GetFont())
|
2003-12-09 01:23:28 +00:00
|
|
|
dc.SetTextForeground(wx.BLUE)
|
|
|
|
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
|
1999-11-13 07:33:29 +00:00
|
|
|
dc.Clear()
|
2003-12-09 02:27:08 +00:00
|
|
|
dc.SetPen(wx.Pen(wx.BLACK, 3, wx.SOLID))
|
2003-11-22 22:57:49 +00:00
|
|
|
dc.DrawLine((self.linePos, 0), (self.linePos, size.height-10))
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
bh = ypos = self.barHeight
|
|
|
|
for x in range(len(self.values)):
|
|
|
|
label, val = self.values[x]
|
2003-11-22 22:57:49 +00:00
|
|
|
dc.DrawText(label, (5, ypos))
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
if val:
|
|
|
|
color = self.colors[ x % len(self.colors) ]
|
2003-12-09 02:27:08 +00:00
|
|
|
dc.SetPen(wx.Pen(color))
|
|
|
|
dc.SetBrush(wx.Brush(color))
|
2003-11-22 22:57:49 +00:00
|
|
|
dc.DrawRectangle((self.linePos+3, ypos), (val, bh))
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
ypos = ypos + 2*bh
|
2003-12-09 01:23:28 +00:00
|
|
|
if ypos > size[1]-10:
|
1999-11-13 05:52:53 +00:00
|
|
|
break
|
|
|
|
|
1999-11-13 07:33:29 +00:00
|
|
|
|
|
|
|
def OnPaint(self, evt):
|
2003-12-09 02:27:08 +00:00
|
|
|
width, height = size =self.GetSize()
|
2003-12-09 01:23:28 +00:00
|
|
|
bmp = wx.EmptyBitmap(width, height)
|
|
|
|
|
|
|
|
dc = wx.MemoryDC()
|
1999-11-13 07:33:29 +00:00
|
|
|
dc.SelectObject(bmp)
|
|
|
|
|
2003-12-09 02:27:08 +00:00
|
|
|
|
|
|
|
self.Draw(dc, size)
|
2003-12-09 01:23:28 +00:00
|
|
|
|
|
|
|
wdc = wx.PaintDC(self)
|
1999-11-13 07:33:29 +00:00
|
|
|
wdc.BeginDrawing()
|
2003-11-22 22:57:49 +00:00
|
|
|
wdc.Blit((0,0), size, dc, (0,0))
|
1999-11-13 07:33:29 +00:00
|
|
|
wdc.EndDrawing()
|
|
|
|
|
2003-12-09 01:23:28 +00:00
|
|
|
dc.SelectObject(wx.NullBitmap)
|
1999-12-29 20:07:27 +00:00
|
|
|
|
1999-11-13 07:33:29 +00:00
|
|
|
|
|
|
|
def OnEraseBackground(self, evt):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
2003-12-09 01:23:28 +00:00
|
|
|
class TestFrame(wx.Frame):
|
1999-11-13 05:52:53 +00:00
|
|
|
def __init__(self, parent, log):
|
2003-12-09 01:23:28 +00:00
|
|
|
wx.Frame.__init__(self, parent, -1, "Thread Test", size=(450,300))
|
1999-11-13 05:52:53 +00:00
|
|
|
self.log = log
|
|
|
|
|
|
|
|
#self.CenterOnParent()
|
|
|
|
|
2003-12-09 01:23:28 +00:00
|
|
|
panel = wx.Panel(self, -1)
|
|
|
|
panel.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD))
|
|
|
|
wx.StaticText(panel, -1,
|
1999-11-13 05:52:53 +00:00
|
|
|
"This demo shows multiple threads interacting with this\n"
|
2001-12-19 21:25:11 +00:00
|
|
|
"window by sending events to it, one thread for each bar.",
|
2003-12-09 01:23:28 +00:00
|
|
|
(5,5))
|
1999-11-13 05:52:53 +00:00
|
|
|
panel.Fit()
|
|
|
|
|
1999-11-13 07:33:29 +00:00
|
|
|
self.graph = GraphWindow(self, ['Zero', 'One', 'Two', 'Three', 'Four',
|
|
|
|
'Five', 'Six', 'Seven'])
|
2001-11-20 06:19:02 +00:00
|
|
|
self.graph.SetSize((450, self.graph.GetBestHeight()))
|
1999-11-13 05:52:53 +00:00
|
|
|
|
2003-12-09 01:23:28 +00:00
|
|
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
sizer.Add(panel, 0, wx.EXPAND)
|
|
|
|
sizer.Add(self.graph, 1, wx.EXPAND)
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
self.SetSizer(sizer)
|
2003-03-25 06:35:27 +00:00
|
|
|
self.SetAutoLayout(True)
|
2001-11-20 06:04:42 +00:00
|
|
|
sizer.Fit(self)
|
1999-11-13 05:52:53 +00:00
|
|
|
|
2003-12-09 01:23:28 +00:00
|
|
|
self.Bind(EVT_UPDATE_BARGRAPH, self.OnUpdate)
|
|
|
|
|
1999-11-13 05:52:53 +00:00
|
|
|
self.threads = []
|
1999-11-13 07:33:29 +00:00
|
|
|
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))
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
for t in self.threads:
|
|
|
|
t.Start()
|
|
|
|
|
2003-12-09 01:23:28 +00:00
|
|
|
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
def OnUpdate(self, evt):
|
|
|
|
self.graph.SetValue(evt.barNum, evt.value)
|
2003-03-25 06:35:27 +00:00
|
|
|
self.graph.Refresh(False)
|
1999-11-13 05:52:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
def OnCloseWindow(self, evt):
|
2003-12-09 01:23:28 +00:00
|
|
|
busy = wx.BusyInfo("One moment please, waiting for threads to die...")
|
|
|
|
wx.Yield()
|
|
|
|
|
1999-11-13 05:52:53 +00:00
|
|
|
for t in self.threads:
|
|
|
|
t.Stop()
|
2003-12-09 01:23:28 +00:00
|
|
|
|
1999-11-13 05:52:53 +00:00
|
|
|
running = 1
|
2003-12-09 01:23:28 +00:00
|
|
|
|
1999-11-13 05:52:53 +00:00
|
|
|
while running:
|
|
|
|
running = 0
|
2003-12-09 01:23:28 +00:00
|
|
|
|
1999-11-13 05:52:53 +00:00
|
|
|
for t in self.threads:
|
|
|
|
running = running + t.IsRunning()
|
2003-12-09 01:23:28 +00:00
|
|
|
|
1999-11-13 05:52:53 +00:00
|
|
|
time.sleep(0.1)
|
2003-12-09 01:23:28 +00:00
|
|
|
|
1999-11-13 05:52:53 +00:00
|
|
|
self.Destroy()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
|
|
def runTest(frame, nb, log):
|
|
|
|
win = TestFrame(frame, log)
|
|
|
|
frame.otherWin = win
|
2003-03-25 06:35:27 +00:00
|
|
|
win.Show(True)
|
1999-11-13 05:52:53 +00:00
|
|
|
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
|
2003-10-02 00:58:06 +00:00
|
|
|
thread, but this often severely limits what can be done in an
|
1999-11-13 05:52:53 +00:00
|
|
|
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
|
2003-12-09 01:23:28 +00:00
|
|
|
alternate threads. A function called wx.PostEvent allows you to do
|
1999-11-13 05:52:53 +00:00
|
|
|
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.
|
|
|
|
|
|
|
|
"""
|
2003-07-02 23:13:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
import sys,os
|
|
|
|
import run
|
|
|
|
run.main(['', os.path.basename(sys.argv[0])])
|
|
|
|
|