X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/bb2775b9e8bc1b882d9a5c7ffe68371717b9e5dc..be05b434519887d3e8c1634120d52e052b0530c1:/wxPython/samples/wxPIA_book/Chapter-12/radargraph.py diff --git a/wxPython/samples/wxPIA_book/Chapter-12/radargraph.py b/wxPython/samples/wxPIA_book/Chapter-12/radargraph.py new file mode 100644 index 0000000000..89908fdf84 --- /dev/null +++ b/wxPython/samples/wxPIA_book/Chapter-12/radargraph.py @@ -0,0 +1,170 @@ +import wx +import math +import random + +class RadarGraph(wx.Window): + """ + A simple radar graph that plots a collection of values in the + range of 0-100 onto a polar coordinate system designed to easily + show outliers, etc. You might use this kind of graph to monitor + some sort of resource allocation metrics, and a quick glance at + the graph can tell you when conditions are good (within some + accepted tolerance level) or approaching critical levels (total + resource consumption). + """ + def __init__(self, parent, title, labels): + wx.Window.__init__(self, parent) + self.title = title + self.labels = labels + self.data = [0.0] * len(labels) + self.titleFont = wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD) + self.labelFont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL) + + self.InitBuffer() + + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_PAINT, self.OnPaint) + + + def OnSize(self, evt): + # When the window size changes we need a new buffer. + self.InitBuffer() + + + def OnPaint(self, evt): + # This automatically Blits self.buffer to a wx.PaintDC when + # the dc is destroyed, and so nothing else needs done. + dc = wx.BufferedPaintDC(self, self.buffer) + + + def InitBuffer(self): + # Create the buffer bitmap to be the same size as the window, + # then draw our graph to it. Since we use wx.BufferedDC + # whatever is drawn to the buffer is also drawn to the window. + w, h = self.GetClientSize() + self.buffer = wx.EmptyBitmap(w, h) + dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) + self.DrawGraph(dc) + + + def GetData(self): + return self.data + + def SetData(self, newData): + assert len(newData) == len(self.data) + self.data = newData[:] + + # The data has changed, so update the buffer and the window + dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) + self.DrawGraph(dc) + + + def PolarToCartesian(self, radius, angle, cx, cy): + x = radius * math.cos(math.radians(angle)) + y = radius * math.sin(math.radians(angle)) + return (cx+x, cy-y) + + + def DrawGraph(self, dc): + spacer = 10 + scaledmax = 150.0 + + dc.SetBackground(wx.Brush(self.GetBackgroundColour())) + dc.Clear() + dw, dh = dc.GetSize() + + # Find out where to draw the title and do it + dc.SetFont(self.titleFont) + tw, th = dc.GetTextExtent(self.title) + dc.DrawText(self.title, (dw-tw)/2, spacer) + + # find the center of the space below the title + th = th + 2*spacer + cx = dw/2 + cy = (dh-th)/2 + th + + # calculate a scale factor to use for drawing the graph based + # on the minimum available width or height + mindim = min(cx, (dh-th)/2) + scale = mindim/scaledmax + + # draw the graph axis and "bulls-eye" with rings at scaled 25, + # 50, 75 and 100 positions + dc.SetPen(wx.Pen("black", 1)) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.DrawCircle(cx,cy, 25*scale) + dc.DrawCircle(cx,cy, 50*scale) + dc.DrawCircle(cx,cy, 75*scale) + dc.DrawCircle(cx,cy, 100*scale) + + dc.SetPen(wx.Pen("black", 2)) + dc.DrawLine(cx-110*scale, cy, cx+110*scale, cy) + dc.DrawLine(cx, cy-110*scale, cx, cy+110*scale) + + # Now find the coordinates for each data point, draw the + # labels, and find the max data point + dc.SetFont(self.labelFont) + maxval = 0 + angle = 0 + polypoints = [] + for i, label in enumerate(self.labels): + val = self.data[i] + point = self.PolarToCartesian(val*scale, angle, cx, cy) + polypoints.append(point) + x, y = self.PolarToCartesian(125*scale, angle, cx,cy) + dc.DrawText(label, x, y) + if val > maxval: + maxval = val + angle = angle + 360/len(self.labels) + + # Set the brush color based on the max value (green is good, + # red is bad) + c = "forest green" + if maxval > 70: + c = "yellow" + if maxval > 95: + c = "red" + + # Finally, draw the plot data as a filled polygon + dc.SetBrush(wx.Brush(c)) + dc.SetPen(wx.Pen("navy", 3)) + dc.DrawPolygon(polypoints) + + + +class TestFrame(wx.Frame): + def __init__(self): + wx.Frame.__init__(self, None, title="Double Buffered Drawing", + size=(480,480)) + self.plot = RadarGraph(self, "Sample 'Radar' Plot", + ["A", "B", "C", "D", "E", "F", "G", "H"]) + + # Set some random initial data values + data = [] + for d in self.plot.GetData(): + data.append(random.randint(0, 75)) + self.plot.SetData(data) + + # Create a timer to update the data values + self.Bind(wx.EVT_TIMER, self.OnTimeout) + self.timer = wx.Timer(self) + self.timer.Start(500) + + + def OnTimeout(self, evt): + # simulate the positive or negative growth of each data value + data = [] + for d in self.plot.GetData(): + val = d + random.uniform(-5, 5) + if val < 0: + val = 0 + if val > 110: + val = 110 + data.append(val) + self.plot.SetData(data) + + +app = wx.PySimpleApp() +frm = TestFrame() +frm.Show() +app.MainLoop()