Package dics :: Module gui
[hide private]
[frames] | no frames]

Source Code for Module dics.gui

  1  #!/usr/bin/env python 
  2   
  3  import datetime 
  4  import dics 
  5  import gamin 
  6  import logging 
  7  import re 
  8  import tc08 
  9  import time 
 10   
 11  import matplotlib 
 12  matplotlib.use('GTKAgg') 
 13  from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as \ 
 14       FigureCanvas 
 15  from matplotlib.backends.backend_gtk import NavigationToolbar2GTK as \ 
 16       NavigationToolbar 
 17  from matplotlib.axes import Subplot 
 18  from matplotlib.figure import Figure 
 19   
 20  import pygtk 
 21  pygtk.require('2.0') 
 22  import gobject 
 23  import gtk 
 24   
25 -class gui:
26 """ 27 First stab at a gui element. To begin with it's just a temperature 28 monitor display. The GTK main loop will block, so to retain 29 ability to use the python console we'll want to make this a seperate 30 thread at some point. 31 """ 32 colours = ('b', 'r', 'c', 'm', 'y', 'k') 33 styles = ('-', '--', '-.', ':') 34 35 # This callback quits the program
36 - def delete_event(self, widget, event, data=None):
37 gtk.main_quit() 38 return False
39
40 - def __init__(self, instrument):
41 """ 42 Initialises the gui. Takes a dics.instrument object as an argument, 43 which it scans through for tc08 instances from which it polls 44 temperatures for display. 45 """ 46 if not isinstance(instrument, dics.instrument): 47 raise TypeError('Expected dics.instrument, got %s!' % \ 48 type(instrument)) 49 50 self.logger = logging.getLogger('gui') 51 52 # Need these in order to parse the date/times. 53 self.pattern = re.compile(\ 54 r'(?:(?<=:\d{2})\s+)|(?:(?<=\.\d{2})\s+(?!$))') 55 self.timefmt = '%d%b%y %H:%M:%S' 56 57 # Now, look through instrument collecting references to tc08's. 58 self.tc08s = [value for value in instrument.__dict__.values() if \ 59 isinstance(value, tc08.tc08)] 60 61 # Create a window and set the title. 62 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 63 self.window.set_title('DICS - Temperature monitor') 64 self.window.set_default_size(700,500) 65 66 # Set a handler for delete_event signal that immediately exits GTK. 67 self.window.connect("delete_event", self.delete_event) 68 69 # Create an hbox, and add it to the window. 70 self.hbox = gtk.HBox(homogeneous=False, spacing=10) 71 self.window.add(self.hbox) 72 73 # Create a table to hold the live data. 74 num_rows = sum([len(temp_logger.getNames()) for temp_logger in \ 75 self.tc08s]) 76 self.table = gtk.Table(num_rows, 4, homogeneous=False) 77 self.table.set_row_spacings(0) 78 self.table.set_col_spacings(0) 79 self.hbox.pack_start(self.table, expand=False, fill=False, \ 80 padding=10) 81 82 # Now run through the tc08 objects, adding name labels, 83 # temperature labels and unit labels. 84 self.labels_name = [] 85 self.labels_temp = [] 86 self.labels_unit = [] 87 self.checks_plot = [] 88 row = 0 89 90 for temp_logger in self.tc08s: 91 for name in temp_logger.getNames(): 92 93 label_name = gtk.Label(name) 94 label_temp = gtk.Label('****.**') 95 label_unit = gtk.Label(temp_logger.units) 96 check_plot = gtk.CheckButton() 97 98 self.table.attach(label_name, 0, 1, row, row + 1, \ 99 xoptions=0, yoptions=0, \ 100 xpadding=0, ypadding=0) 101 self.table.attach(label_temp, 1, 2, row, row + 1, \ 102 xoptions=0, yoptions=0, \ 103 xpadding=0, ypadding=0) 104 self.table.attach(label_unit, 2, 3, row, row + 1, \ 105 xoptions=0, yoptions=0, \ 106 xpadding=0, ypadding=0) 107 self.table.attach(check_plot, 3, 4, row, row + 1, \ 108 xoptions=0, yoptions=0, \ 109 xpadding=0, ypadding=0) 110 111 label_name.set_alignment(0,0.5) 112 check_plot.set_active(True) 113 114 label_name.show() 115 label_temp.show() 116 label_unit.show() 117 check_plot.show() 118 119 self.labels_name += [label_name] 120 self.labels_temp += [label_temp] 121 self.labels_unit += [label_unit] 122 self.checks_plot += [check_plot] 123 row += 1 124 125 # We also want a vbox for the plot and its toolbar to go into. 126 self.vbox_plot = gtk.VBox(homogeneous=False, spacing=0) 127 self.hbox.pack_start(self.vbox_plot, expand=True, fill=True, \ 128 padding=0) 129 130 # Get all teh data. 131 self.logfiles = [] 132 self.logs = [] 133 for temp_logger in self.tc08s: 134 filename = temp_logger.filename 135 self.logfiles.append(filename) 136 try: 137 logfile = file(filename, 'r') 138 except IOError: 139 self.logger.warning("Couldn't open temperature logfile %s!" % \ 140 filename) 141 continue 142 log = logfile.readlines() 143 logfile.close() 144 self.logs.append(self.parse_log(log)) 145 146 # Create the initial plot based on the data in the log file 147 self.figure = Figure(figsize=(5,4), dpi=100) 148 self.axes = self.figure.add_subplot(111) 149 150 # Create the plot in the self.axes object. 151 self.make_plot() 152 153 # Put the top level matplotlib bits in. 154 self.canvas = FigureCanvas(self.figure) 155 self.vbox_plot.pack_start(self.canvas, expand=True, fill=True, \ 156 padding=0) 157 158 self.toolbar = NavigationToolbar(self.canvas, self.window) 159 self.vbox_plot.pack_start(self.toolbar, expand=False, fill=False, \ 160 padding=0) 161 162 self.canvas.show() 163 self.toolbar.show() 164 165 # Now show everything remaining. 166 self.table.show() 167 self.vbox_plot.show() 168 self.hbox.show() 169 self.window.show()
170
171 - def line_colour(self, number):
172 """ 173 Given an integer, returns a matplotlib line colour character. As the 174 number is increased the line colour cycles through blue, green, 175 red, cyan, magenta, yellow, black. 176 """ 177 index = int(number) % len(gui.colours) 178 return gui.colours[index]
179
180 - def line_style(self, number):
181 """ 182 Given an integer, returns a matplotlib line style character. As the 183 number is increased the line style cycles through solid line, dashed 184 line, dot-dashed line and dotted line. 185 """ 186 index = int(number) % len(gui.styles) 187 return gui.styles[index]
188
189 - def make_plot(self):
190 # Plots the stuff. 191 self.axes.clear() 192 193 for index1 in range(0, len(self.tc08s)): 194 log = self.logs[index1] 195 temp_logger = self.tc08s[index1] 196 names = temp_logger.getNames() 197 style = self.line_style(index1) 198 for index2 in range(1, len(log)): 199 channel = log[index2] 200 name = names[index2-1] 201 colour = self.line_colour(index2-2) 202 self.axes.plot_date(matplotlib.dates.date2num(log[0]), \ 203 channel, colour + style, label=name) 204 205 self.axes.legend(numpoints=2) 206 self.axes.set_xlabel('Date/time') 207 self.axes.set_ylabel('Temperature')
208
209 - def parse_log(self, temp_log):
210 """ 211 Parses a temperature log file, and returns a tuple containing a list 212 of datetime.datetime objects and a one list of temperatures per 213 channel. 214 """ 215 # Run the lines of the log through the regex 216 splits = [self.pattern.split(line) for line in temp_log if \ 217 line[0] != '#'] 218 # Converting from a string to a datetime.datetime object is stupidly 219 # awkward, have to use time.strptime to convert into a time.struct_time 220 # but even that's not usable for constructing a datetime, first convert 221 # to a POSIX timestamp using time.mktime. 222 data = [\ 223 [datetime.datetime.fromtimestamp(\ 224 time.mktime(time.strptime(split[0], self.timefmt)))] + \ 225 [float(temp) for temp in split[2:]] 226 for split in splits] 227 # Rearrange 228 data = zip(*data) 229 # Return the result 230 return data
231
232 - def updateDisplay(self):
233 """ 234 Callback function that gets the current temperatures from the tc08 235 objects in self.tc08s and updates the temperature display. 236 """ 237 # Collect all the current temperature values. 238 newTemps = [] 239 for temp_logger in self.tc08s: 240 newTemps += temp_logger.getTemps() 241 # Now set the text of the corresponding labels. 242 for label, temp in zip(self.labels_temp, newTemps): 243 label.set_text('%7.2f' % temp) 244 # return True so the timer continues. 245 return True
246
247 - def main(self):
248 249 # Set up the timeout to update the display. 250 self.timer_id = gobject.timeout_add(5000, self.updateDisplay) 251 252 gtk.main() 253 # Need to do this so that the tc08's aren't multiply referenced, 254 # and so will get their __del__ methods called when the program 255 # is exited. 256 del self.tc08s 257 return 0
258