
from wxPython.wx import *

from threading import *
import Queue
import time
from   whrandom import random

#----------------------------------------------------------------------

class CalcBarThread(Thread):
    def __init__(self, queue, barNum, val):
        Thread.__init__(self)
        self.queue = queue
        self.barNum = barNum
        self.val = val

    def Start(self):
        self.keepGoing = Event()
        self.keepGoing.set()
        self.start()

    def Stop(self):
        self.keepGoing.clear()

    def run(self):
        while self.keepGoing.isSet():
            self.queue.put((self.barNum, int(self.val)))

            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

#----------------------------------------------------------------------


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 UpdateTimer(wxTimer):
    def __init__(self, cb):
        wxTimer.__init__(self)
        self.cb = cb

    def Notify(self):
        self.cb()


class TestFrame(wxFrame):
    def __init__(self, parent, log):
        wxFrame.__init__(self, parent, -1, "Thread Test", size=(450,300))
        self.timer = UpdateTimer(self.OnTimer)
        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.queue = Queue.Queue(100)
        self.threads = []
        self.threads.append(CalcBarThread(self.queue, 0, 50))
        self.threads.append(CalcBarThread(self.queue, 1, 75))
        self.threads.append(CalcBarThread(self.queue, 2, 100))
        self.threads.append(CalcBarThread(self.queue, 3, 150))
        self.threads.append(CalcBarThread(self.queue, 4, 225))
        self.threads.append(CalcBarThread(self.queue, 5, 300))
        self.threads.append(CalcBarThread(self.queue, 6, 250))
        self.threads.append(CalcBarThread(self.queue, 7, 175))

        for t in self.threads:
            t.Start()

        self.timer.Start(100)  # Start the update timer (frequency in ms)
        EVT_CLOSE(self, self.OnCloseWindow)

    def OnCloseWindow(self, evt):
        if self.timer:  # True only once, might be called twice
            self.timer.Stop()
            self.timer = None
            busy = wxBusyInfo("One moment please, waiting for threads to die...")
            for t in self.threads:
                t.Stop()
            self.Destroy()

    def OnTimer(self):
        try:
            barNum, value = self.queue.get(0)
            self.graph.SetValue(barNum, value)
            self.graph.Refresh(false)
        except Queue.Empty:
            pass
            
        
        
        

#----------------------------------------------------------------------

def runTest(frame, nb, log):
    global win
    win = TestFrame(frame, log)
    frame.otherWin = win
    win.Show(true)
    return None

def cleanUp():
    # Make sure the threads are cleaned up, otherwise the program does not
    # terminate
    win.OnCloseWindow(None)


