Commit 58501ed77f5937cac4036858ce0c7bcce62b9c13
1 parent
8ffbc4ad
Exists in
master
Split class plot into separate module, as part of attempt to declutter main
Showing
2 changed files
with
131 additions
and
122 deletions
Show diff stats
robots/little_john/telemetry/code/monitor/version1/main.py
| ... | ... | @@ -22,6 +22,7 @@ import logging |
| 22 | 22 | from serialselect import selectserial |
| 23 | 23 | from colours import * |
| 24 | 24 | from series import Series |
| 25 | +from plot import Plot | |
| 25 | 26 | |
| 26 | 27 | logging.basicConfig(format='%(levelname)s:\t%(message)s', level=logging.DEBUG) |
| 27 | 28 | logging.info("Logging system active") |
| ... | ... | @@ -39,128 +40,6 @@ else: |
| 39 | 40 | os='Other' |
| 40 | 41 | logging.info('OS = ' + os) |
| 41 | 42 | |
| 42 | -class Plot(pyglet.window.Window): | |
| 43 | - def __init__(self, series): | |
| 44 | - """Setup a the details of a plot, and create a corresponding window""" | |
| 45 | - pyglet.window.Window.__init__(self, resizable=True) | |
| 46 | - self.set_icon(pyglet.image.load('32x32.png')) | |
| 47 | - self.set_minimum_size(320,320) | |
| 48 | - self.series = series | |
| 49 | - self.font = 'Arkhip' | |
| 50 | - self.margins = (0.09, 0.08) # Fractions of window size | |
| 51 | - self.lines = (10, 8) | |
| 52 | - #self.resizable = True | |
| 53 | - self.set_caption(self.series.title) | |
| 54 | - | |
| 55 | - def on_resize(self, width, height): | |
| 56 | - """Handle a resize event from the pyglet event loop""" | |
| 57 | - try: | |
| 58 | - self.bounds = ((int(self.width * self.margins[0]), int(self.width * (1 - self.margins[0]))), | |
| 59 | - (int(self.height * self.margins[1]), int(self.height * (1 - self.margins[1])))) | |
| 60 | - except Exception as e: | |
| 61 | - logging.critical(str(e)) | |
| 62 | - self.close() | |
| 63 | - logging.critical('Instance closed') | |
| 64 | - sys.exit() | |
| 65 | - self.tag_size = min(self.height*self.margins[1]*0.3,self.width*self.margins[0]*0.3) | |
| 66 | - # This sometimes seems to throw an error ('AttributeError: 'Plot' object has no attribute 'margins') when started for a second time from the same instance. Interesting. Causes the plot windows to freeze | |
| 67 | - pyglet.window.Window.on_resize(self, width, height) | |
| 68 | - | |
| 69 | - def on_draw(self): | |
| 70 | - """Draw all the components of the graph""" | |
| 71 | - self.drawBackground() | |
| 72 | - self.drawHeading() | |
| 73 | - self.drawAxis(0) | |
| 74 | - self.drawAxis(1) | |
| 75 | - self.drawLine(self.series) | |
| 76 | - | |
| 77 | - def drawBackground(self): | |
| 78 | - """Draw the graph background, currently a plain colour""" | |
| 79 | - pyglet.image.SolidColorImagePattern(WHITE).create_image(self.width, self.height).blit(0, 0) | |
| 80 | - | |
| 81 | - def drawHeading(self): | |
| 82 | - """Draw a title for the graph (duplicated in the window titlebar, if present""" | |
| 83 | - heading = pyglet.text.Label(self.series.title, color=BLACK, | |
| 84 | - font_name=self.font, font_size=self.height*self.margins[0]*0.5, | |
| 85 | - x=self.width/2, y=self.height-(self.margins[1]), | |
| 86 | - anchor_x='center', anchor_y='top') | |
| 87 | - heading.draw() | |
| 88 | - | |
| 89 | - def drawLine(self, series): | |
| 90 | - xscale = float(self.series.xlimits[1]-self.series.xlimits[0])/(self.bounds[0][1]-self.bounds[0][0]) | |
| 91 | - yscale = float(self.series.ylimits[1]-self.series.ylimits[0])/(self.bounds[1][1]-self.bounds[1][0]) | |
| 92 | - logging.debug("xscale = " + str(xscale) + ", yscale = " + str(yscale)) | |
| 93 | - lmar = int(self.width * self.margins[0]) | |
| 94 | - rmar = int(self.width * self.margins[1]) | |
| 95 | - tmar = int(self.height * self.margins[0]) | |
| 96 | - bmar = int(self.height * self.margins[1]) | |
| 97 | - | |
| 98 | - pyglet.gl.glLineWidth(2) | |
| 99 | - | |
| 100 | - for n in range(len(series.data) - 1): | |
| 101 | - x1, y1 = series.data[n][0]-series.xlimits[0], series.data[n][1]-series.ylimits[0] | |
| 102 | - x2, y2 = series.data[n+1][0]-series.xlimits[0], series.data[n+1][1]-series.ylimits[0] | |
| 103 | - x1 = int((x1/xscale)+lmar) | |
| 104 | - y1 = int((y1/yscale)+bmar) | |
| 105 | - x2 = int((x2/xscale)+lmar) | |
| 106 | - y2 = int((y2/yscale)+bmar) | |
| 107 | - pyglet.graphics.draw(2, pyglet.gl.GL_LINES, | |
| 108 | - ('v2i', (x1, y1, x2, y2)), | |
| 109 | - ('c3B', (255, 0, 0, 255, 0, 0))) | |
| 110 | - pyglet.gl.glLineWidth(1) | |
| 111 | - | |
| 112 | - | |
| 113 | - | |
| 114 | - def drawAxis(self, axis): # axis=0 is x, 1 is y | |
| 115 | - """Draw the gridlines and labels for one axis, specified in the last argument""" | |
| 116 | - limita = self.bounds[1-axis][1] | |
| 117 | - limitb = self.bounds[1-axis][0] | |
| 118 | - start = self.bounds[axis][0] | |
| 119 | - stop = self.bounds[axis][1] | |
| 120 | - increment = float(stop-start)/self.lines[axis] | |
| 121 | - for pos in numpy.arange(start, stop+1, increment): | |
| 122 | - # Using fp arithmetic to avoid intermittent fencepost errors | |
| 123 | - pos = int(pos) | |
| 124 | - if axis==0: # x axis, vertical lines | |
| 125 | - scale = float(self.series.xlimits[1]-self.series.xlimits[0])/(stop-start) | |
| 126 | - tagvalue = ((pos-start) * scale) + self.series.xlimits[0] | |
| 127 | - tagtext = str(int(tagvalue)) | |
| 128 | - pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (pos, limita, pos, limitb)), | |
| 129 | - ('c3B', (0, 0, 0, 0, 0, 0))) | |
| 130 | - tag = pyglet.text.Label(tagtext, color=BLACK, | |
| 131 | - font_name=self.font, font_size=self.tag_size, | |
| 132 | - x=pos, y=self.height*self.margins[1], | |
| 133 | - anchor_x='left', anchor_y='top') | |
| 134 | - axistitle = pyglet.text.Label(self.series.xname, color=BLACK, | |
| 135 | - font_name=self.font, font_size=self.tag_size, | |
| 136 | - x=self.width/2, y=0, | |
| 137 | - anchor_x='center', anchor_y='bottom') | |
| 138 | - axistitle.draw() | |
| 139 | - if axis==1: # y axis, horizontal lines | |
| 140 | - scale = float(self.series.ylimits[1]-self.series.ylimits[0])/(stop-start) | |
| 141 | - tagvalue = ((pos-start) * scale) + self.series.ylimits[0] | |
| 142 | - tagtext = str(int(tagvalue)) | |
| 143 | - pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (limita, pos, limitb, pos)), | |
| 144 | - ('c3B', (0, 0, 0, 0, 0, 0))) | |
| 145 | - tag = pyglet.text.Label(tagtext, color=BLACK, | |
| 146 | - font_name=self.font, font_size=self.tag_size, | |
| 147 | - x=self.width*self.margins[0]*0.9, y=pos, | |
| 148 | - anchor_x='right', anchor_y='center') | |
| 149 | - axistitle = pyglet.text.Label(self.series.yname, color=BLACK, | |
| 150 | - font_name=self.font, font_size=self.tag_size, | |
| 151 | - x=0, y=self.height/2, | |
| 152 | - anchor_x='center', anchor_y='top') | |
| 153 | - pyglet.gl.glPushMatrix() # Set up a new context to avoid confusing the main one | |
| 154 | - # Tranformation to rotate label, and ensure it ends up in the right place | |
| 155 | - pyglet.gl.glTranslatef(self.height//2, self.height//2, 0.0) | |
| 156 | - pyglet.gl.glRotatef(90.0, 0.0, 0.0, 1.0) | |
| 157 | - # Draw the axis title using the rotated coordinate system | |
| 158 | - axistitle.draw() | |
| 159 | - # Return everything to its previous state | |
| 160 | - pyglet.gl.glPopMatrix() | |
| 161 | - | |
| 162 | - tag.draw() | |
| 163 | - | |
| 164 | 43 | testseries = Series() |
| 165 | 44 | logging.info("Series created") |
| 166 | 45 | plots = [] | ... | ... |
robots/little_john/telemetry/code/monitor/version1/plot.py
0 โ 100644
| ... | ... | @@ -0,0 +1,130 @@ |
| 1 | +# Class representing a single 2D plot window and its properties | |
| 2 | +# Written as a telemetry tool by: | |
| 3 | +# The UoN Robot Wars Project, 2018 | |
| 4 | + | |
| 5 | +import pyglet | |
| 6 | +import numpy | |
| 7 | +import logging | |
| 8 | +from colours import * | |
| 9 | + | |
| 10 | +class Plot(pyglet.window.Window): | |
| 11 | + def __init__(self, series): | |
| 12 | + """Setup a the details of a plot, and create a corresponding window""" | |
| 13 | + pyglet.window.Window.__init__(self, resizable=True) | |
| 14 | + self.set_icon(pyglet.image.load('32x32.png')) | |
| 15 | + self.set_minimum_size(320,320) | |
| 16 | + self.series = series | |
| 17 | + self.font = 'Arkhip' | |
| 18 | + self.margins = (0.09, 0.08) # Fractions of window size | |
| 19 | + self.lines = (10, 8) | |
| 20 | + #self.resizable = True | |
| 21 | + self.set_caption(self.series.title) | |
| 22 | + | |
| 23 | + def on_resize(self, width, height): | |
| 24 | + """Handle a resize event from the pyglet event loop""" | |
| 25 | + try: | |
| 26 | + self.bounds = ((int(self.width * self.margins[0]), int(self.width * (1 - self.margins[0]))), | |
| 27 | + (int(self.height * self.margins[1]), int(self.height * (1 - self.margins[1])))) | |
| 28 | + except Exception as e: | |
| 29 | + logging.critical(str(e)) | |
| 30 | + self.close() | |
| 31 | + logging.critical('Instance closed') | |
| 32 | + sys.exit() | |
| 33 | + self.tag_size = min(self.height*self.margins[1]*0.3,self.width*self.margins[0]*0.3) | |
| 34 | + # This sometimes seems to throw an error ('AttributeError: 'Plot' object has no attribute 'margins') when started for a second time from the same instance. Interesting. Causes the plot windows to freeze | |
| 35 | + pyglet.window.Window.on_resize(self, width, height) | |
| 36 | + | |
| 37 | + def on_draw(self): | |
| 38 | + """Draw all the components of the graph""" | |
| 39 | + self.drawBackground() | |
| 40 | + self.drawHeading() | |
| 41 | + self.drawAxis(0) | |
| 42 | + self.drawAxis(1) | |
| 43 | + self.drawLine(self.series) | |
| 44 | + | |
| 45 | + def drawBackground(self): | |
| 46 | + """Draw the graph background, currently a plain colour""" | |
| 47 | + pyglet.image.SolidColorImagePattern(WHITE).create_image(self.width, self.height).blit(0, 0) | |
| 48 | + | |
| 49 | + def drawHeading(self): | |
| 50 | + """Draw a title for the graph (duplicated in the window titlebar, if present""" | |
| 51 | + heading = pyglet.text.Label(self.series.title, color=BLACK, | |
| 52 | + font_name=self.font, font_size=self.height*self.margins[0]*0.5, | |
| 53 | + x=self.width/2, y=self.height-(self.margins[1]), | |
| 54 | + anchor_x='center', anchor_y='top') | |
| 55 | + heading.draw() | |
| 56 | + | |
| 57 | + def drawLine(self, series): | |
| 58 | + xscale = float(self.series.xlimits[1]-self.series.xlimits[0])/(self.bounds[0][1]-self.bounds[0][0]) | |
| 59 | + yscale = float(self.series.ylimits[1]-self.series.ylimits[0])/(self.bounds[1][1]-self.bounds[1][0]) | |
| 60 | + logging.debug("xscale = " + str(xscale) + ", yscale = " + str(yscale)) | |
| 61 | + lmar = int(self.width * self.margins[0]) | |
| 62 | + rmar = int(self.width * self.margins[1]) | |
| 63 | + tmar = int(self.height * self.margins[0]) | |
| 64 | + bmar = int(self.height * self.margins[1]) | |
| 65 | + | |
| 66 | + pyglet.gl.glLineWidth(2) | |
| 67 | + | |
| 68 | + for n in range(len(series.data) - 1): | |
| 69 | + x1, y1 = series.data[n][0]-series.xlimits[0], series.data[n][1]-series.ylimits[0] | |
| 70 | + x2, y2 = series.data[n+1][0]-series.xlimits[0], series.data[n+1][1]-series.ylimits[0] | |
| 71 | + x1 = int((x1/xscale)+lmar) | |
| 72 | + y1 = int((y1/yscale)+bmar) | |
| 73 | + x2 = int((x2/xscale)+lmar) | |
| 74 | + y2 = int((y2/yscale)+bmar) | |
| 75 | + pyglet.graphics.draw(2, pyglet.gl.GL_LINES, | |
| 76 | + ('v2i', (x1, y1, x2, y2)), | |
| 77 | + ('c3B', (255, 0, 0, 255, 0, 0))) | |
| 78 | + pyglet.gl.glLineWidth(1) | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + def drawAxis(self, axis): # axis=0 is x, 1 is y | |
| 83 | + """Draw the gridlines and labels for one axis, specified in the last argument""" | |
| 84 | + limita = self.bounds[1-axis][1] | |
| 85 | + limitb = self.bounds[1-axis][0] | |
| 86 | + start = self.bounds[axis][0] | |
| 87 | + stop = self.bounds[axis][1] | |
| 88 | + increment = float(stop-start)/self.lines[axis] | |
| 89 | + for pos in numpy.arange(start, stop+1, increment): | |
| 90 | + # Using fp arithmetic to avoid intermittent fencepost errors | |
| 91 | + pos = int(pos) | |
| 92 | + if axis==0: # x axis, vertical lines | |
| 93 | + scale = float(self.series.xlimits[1]-self.series.xlimits[0])/(stop-start) | |
| 94 | + tagvalue = ((pos-start) * scale) + self.series.xlimits[0] | |
| 95 | + tagtext = str(int(tagvalue)) | |
| 96 | + pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (pos, limita, pos, limitb)), | |
| 97 | + ('c3B', (0, 0, 0, 0, 0, 0))) | |
| 98 | + tag = pyglet.text.Label(tagtext, color=BLACK, | |
| 99 | + font_name=self.font, font_size=self.tag_size, | |
| 100 | + x=pos, y=self.height*self.margins[1], | |
| 101 | + anchor_x='left', anchor_y='top') | |
| 102 | + axistitle = pyglet.text.Label(self.series.xname, color=BLACK, | |
| 103 | + font_name=self.font, font_size=self.tag_size, | |
| 104 | + x=self.width/2, y=0, | |
| 105 | + anchor_x='center', anchor_y='bottom') | |
| 106 | + axistitle.draw() | |
| 107 | + if axis==1: # y axis, horizontal lines | |
| 108 | + scale = float(self.series.ylimits[1]-self.series.ylimits[0])/(stop-start) | |
| 109 | + tagvalue = ((pos-start) * scale) + self.series.ylimits[0] | |
| 110 | + tagtext = str(int(tagvalue)) | |
| 111 | + pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (limita, pos, limitb, pos)), | |
| 112 | + ('c3B', (0, 0, 0, 0, 0, 0))) | |
| 113 | + tag = pyglet.text.Label(tagtext, color=BLACK, | |
| 114 | + font_name=self.font, font_size=self.tag_size, | |
| 115 | + x=self.width*self.margins[0]*0.9, y=pos, | |
| 116 | + anchor_x='right', anchor_y='center') | |
| 117 | + axistitle = pyglet.text.Label(self.series.yname, color=BLACK, | |
| 118 | + font_name=self.font, font_size=self.tag_size, | |
| 119 | + x=0, y=self.height/2, | |
| 120 | + anchor_x='center', anchor_y='top') | |
| 121 | + pyglet.gl.glPushMatrix() # Set up a new context to avoid confusing the main one | |
| 122 | + # Tranformation to rotate label, and ensure it ends up in the right place | |
| 123 | + pyglet.gl.glTranslatef(self.height//2, self.height//2, 0.0) | |
| 124 | + pyglet.gl.glRotatef(90.0, 0.0, 0.0, 1.0) | |
| 125 | + # Draw the axis title using the rotated coordinate system | |
| 126 | + axistitle.draw() | |
| 127 | + # Return everything to its previous state | |
| 128 | + pyglet.gl.glPopMatrix() | |
| 129 | + | |
| 130 | + tag.draw() | ... | ... |