class Reader(object): def __init__(self,win_size, win_loc, title,subplot_dim=[1,1]): self.top = gtk.Window() self.top.connect('delete-event', gtk.main_quit) self.top.set_title(title) self.top.set_position(gtk.WIN_POS_CENTER) self.top.set_default_size(*win_size) self.fig = Figure() self.axs = [self.fig.add_subplot(subplot_dim[0], subplot_dim[1], i+1) for i in range(np.prod(subplot_dim))] self.canvas = FigureCanvas(self.fig) self.top.add(self.canvas) self.top.show_all() self.update_background() if len(self.axs) == 1: self.ax = self.axs[0] def update_background(self): self.canvas.draw() self.backgrounds = [self.canvas.copy_from_bbox(ax.bbox) for ax in self.axs] if len(self.backgrounds) == 1: self.background = self.backgrounds[0] return def draw(self): raise Exception('Not implemented.') def read(self): raise Exception('Not implemented yet.')
class Reader(object): def __init__(self, win_size, win_loc, title, subplot_dim=[1, 1]): self.top = gtk.Window() self.top.connect('delete-event', gtk.main_quit) self.top.set_title(title) self.top.set_position(gtk.WIN_POS_CENTER) self.top.set_default_size(*win_size) self.fig = Figure() self.axs = [ self.fig.add_subplot(subplot_dim[0], subplot_dim[1], i + 1) for i in range(np.prod(subplot_dim)) ] self.canvas = FigureCanvas(self.fig) self.top.add(self.canvas) self.top.show_all() self.update_background() if len(self.axs) == 1: self.ax = self.axs[0] def update_background(self): self.canvas.draw() self.backgrounds = [ self.canvas.copy_from_bbox(ax.bbox) for ax in self.axs ] if len(self.backgrounds) == 1: self.background = self.backgrounds[0] return def draw(self): raise Exception('Not implemented.') def read(self): raise Exception('Not implemented yet.')
class SensorWindow(object): def __init__(self, mainThread, gladefile='sensor_window.glade'): self.builder = gtk.Builder() self.builder.add_from_file(gladefile) self.builder.connect_signals(self) self._stopped = False self.mainThread = mainThread self.fig = plt.figure() self.numLines = 1 # lines plot self.ax = self.fig.add_subplot(111) self.ax.set_xlabel('Time') self.ax.set_ylabel('Power') self.ax.xaxis.set_animated(True) self.ax.yaxis.set_animated(True) self.ax.set_title('Light Intensity') self.ax.grid(True) self.start = time.time() self.background1 = None self.prev_time = self.start self.prev_pixel_offset = 0 self.x0 = 0 self.value = [0] * self.numLines self.ax.set_ylim(-1, 256) self.lines = [] for i in range(self.numLines): line, = self.ax.plot([], [], animated=True, lw=2) self.lines.append(line) self.canvas = FigureCanvas(self.fig) self.graphview = self.builder.get_object("box2") self.graphview.pack_start(self.canvas) self.graphview.reorder_child(self.canvas, 0) self.img = self.builder.get_object("image1") self.img.set_from_file("off.svg") self.lamp = False self.canvas.show() gobject.idle_add(self.update_line) self.canvas.mpl_connect('draw_event', self.on_draw) self.barpath = [] def close_window(self, obj): print "closing window" self.builder.get_object("window1").destroy() def destroy_callback(self, obj): print "destroying window" self.mainThread.stop() self._stopped = True def close_from_mainthread(self): print "close from mainthread" self.builder.get_object("window1").destroy() def toggle_lamp(self): print "toggle lamp!!" self.img = self.builder.get_object("image1") if (self.lamp): self.lamp = False self.img.set_from_file("off.svg") else: self.lamp = True self.img.set_from_file("on.svg") def update_line(self, *args): if self._stopped: self.destroy_callback(None) return False if self.background1 is None: return True cur_time = time.time() pixel_offset = int((cur_time - self.start) * 40.) dx_pixel = pixel_offset - self.prev_pixel_offset self.prev_pixel_offset = pixel_offset dx_data = self.get_dx_data(dx_pixel) #cur_time - self.prev_time) x0 = self.x0 self.x0 += dx_data self.prev_time = cur_time self.ax.set_xlim(self.x0 - 2, self.x0 + 0.1) # restore background which will plot lines from previous plots self.restore_background_shifted(dx_pixel) #x0, self.x0) # now plot line segment within [x0, x0+dx_data], # Note that we're only plotting a line between [x0, x0+dx_data]. xx = np.array([x0, self.x0]) for i in range(len(self.lines)): line = self.lines[i] line.set_xdata(xx) # the for loop below could be improved by using collection. line.set_ydata(np.array([self.value[i], self.value[i]])) self.ax.draw_artist(line) self.background2 = self.canvas.copy_from_bbox(self.get_bg_bbox()) self.ax.draw_artist(self.ax.xaxis) self.ax.draw_artist(self.ax.yaxis) self.canvas.blit(self.ax.get_figure().bbox) return True def get_dx_data(self, dx_pixel): tp = self.ax.transData.inverted().transform_point x0, y0 = tp((0, 0)) x1, y1 = tp((dx_pixel, 0)) return (x1 - x0) def get_bg_bbox(self): return self.ax.bbox.padded(-3) def save_bg(self): self.background1 = self.canvas.copy_from_bbox( self.ax.get_figure().bbox) self.background2 = self.canvas.copy_from_bbox(self.get_bg_bbox()) def on_draw(self, *args): self.save_bg() return False def restore_background_shifted(self, dx_pixel): """ restore bacground shifted by dx in data coordinate. This only works if the data coordinate system is linear. """ # restore the clean slate background self.canvas.restore_region(self.background1) # restore subregion (x1+dx, y1, x2, y2) of the second bg # in a offset position (x1-dx, y1) x1, y1, x2, y2 = self.background2.get_extents() self.canvas.restore_region(self.background2, bbox=(x1 + dx_pixel, y1, x2, y2), xy=(x1 - dx_pixel, y1)) return dx_pixel def update(self, data): if type(data) == ListType: assert (len(self.lines) == len(data)) self.value = data else: assert (len(self.lines) == 1) self.value = [data]
class XratersWindow(gtk.Window): __gtype_name__ = "XratersWindow" def __init__(self): """__init__ - This function is typically not called directly. Creation a XratersWindow requires redeading the associated ui file and parsing the ui definition extrenally, and then calling XratersWindow.finish_initializing(). Use the convenience function NewXratersWindow to create XratersWindow object. """ self._acc_cal = ((128, 128, 128), (255, 255, 255)) self._acc = [0, 0, 0] self._connected = False self._wiiMote = None self._resetData() self._dataLock = threading.Lock() isConnected = property(lambda self: self._connected) def callback(funct): """A decorator used to require connection to the Wii Remote This decorator is used to implement the precondition that the Wii Remote must be connected. """ def _callback(cls, *args, **kwds): if cls.isConnected: funct(cls, *args, **kwds) return True else: return False return _callback def _connectCallback(self, connectionMaker): """Callback function called upon successful connection to the Wiimote """ if connectionMaker.connected: self._connected = True self._wiiMote = connectionMaker.wiiMote self._resetData() gobject.timeout_add(45, self._drawAcc) self.widget('actionDisconnect').set_sensitive(True) self.widget('actionSave').set_sensitive(True) self.widget('actionReset').set_sensitive(True) self.widget('actionPause').set_sensitive(True) self.widget('toolbutton1').set_related_action( self.widget('actionDisconnect')) self._acc_cal = connectionMaker.acc_cal self._wiiMote.mesg_callback = self._getAcc self._updBatteryLevel() gobject.timeout_add_seconds(60, self._updBatteryLevel) else: self.widget('actionWiiConnect').set_sensitive(True) @callback def _upd_background(self, event): """Keep a copy of the figure background """ self.__background = self._accCanvas.copy_from_bbox(self._accAxis.bbox) def _getAcc(self, messages, theTime=0): """Process acceleration messages from the Wiimote This function is intended to be set as cwiid.mesg_callback """ if self._Paused: return for msg in messages: if msg[0] == cwiid.MESG_ACC: # Normalize data using calibration info for i, axisAcc in enumerate(msg[1]): self._acc[i] = float(axisAcc - self._acc_cal[0][i]) self._acc[i] /=(self._acc_cal[1][i]\ -self._acc_cal[0][i]) with self._dataLock: # Store time and acceleration in the respective arrays self._time.append(theTime - self._startTime) [self._accData[i].append(self._acc[i]) for i in threeAxes] # We only keep about 6 seconds worth of data if (self._time[-1] - self._time[0] > 6): with self._dataLock: self._time.pop(0) [self._accData[i].pop(0) for i in threeAxes] @callback def _drawAcc(self): """Update the acceleration graph """ # Do nothing while paused or there's no data available if self._Paused or len(self._time) == 0: return draw_flag = False # Update axes limits if the data fall out of range lims = self._accAxis.get_xlim() if self._time[-1] > lims[1]: self._accAxis.set_xlim(lims[0], lims[1] + 2) lims = self._accAxis.get_xlim() draw_flag = True if (self._time[-1] - lims[0] > 6): self._accAxis.set_xlim(lims[0] + 2, lims[1]) draw_flag = True if draw_flag: gobject.idle_add(self._accCanvas.draw) # Do the actual update of the background if self.__background != None: self._accCanvas.restore_region(self.__background) # Do the actual update of the lines with self._dataLock: [ self._lines[i].set_data(self._time, self._accData[i]) for i in threeAxes ] [self._accAxis.draw_artist(self._lines[i]) for i in threeAxes] self._accCanvas.blit(self._accAxis.bbox) @callback def _updBatteryLevel(self): """Callback to update the battery indicator in the status bar """ self._wiiMote.request_status() self._setBatteryIndicator( float(self._wiiMote.state['battery']) / cwiid.BATTERY_MAX) def _setBatteryIndicator(self, level): """Actually update the battery indicator in the status bar """ progressBar = self.widget("progressbarBattery") progressBar.set_fraction(level) progressBar.set_text("Battery: %.0f%%" % (level * 100)) def _resetData(self): """Reset stored data and status flags to their defaults """ self._accData = [list(), list(), list()] self._time = list() self._startTime = time.time() self._moveTime = self._startTime self._Paused = False def widget(self, name): """Helper function to retrieve widget handlers """ return self.builder.get_object(name) def finish_initializing(self, builder): """finish_initalizing should be called after parsing the ui definition and creating a XratersWindow object with it in order to finish initializing the start of the new XratersWindow instance. """ #get a reference to the builder and set up the signals self.builder = builder self.builder.connect_signals(self) #uncomment the following code to read in preferences at start up dlg = PreferencesXratersDialog.NewPreferencesXratersDialog() self.preferences = dlg.get_preferences() #code for other initialization actions should be added here self._accFigure = Figure(figsize=(8, 6), dpi=72) self._accAxis = self._accFigure.add_subplot(111) self._accAxis.set_xlabel("time (s)") self._accAxis.set_ylabel("acceleration (g)") self._lines = self._accAxis.plot(self._time, self._accData[X], self._time, self._accData[Y], self._time, self._accData[Z], animated=True) self._accFigure.legend(self._lines, ("X", "Y", "Z"), 'upper center', ncol=3) self._accAxis.set_xlim(0, 2) self._accAxis.set_ylim(-3, 3) self._accCanvas = FigureCanvas(self._accFigure) self._accCanvas.mpl_connect("draw_event", self._upd_background) self.__background = self._accCanvas.copy_from_bbox(self._accAxis.bbox) self._accCanvas.show() self._accCanvas.set_size_request(600, 400) vbMain = self.widget("vboxMain") vbMain.pack_start(self._accCanvas, True, True) vbMain.show() vbMain.reorder_child(self._accCanvas, 2) self._setBatteryIndicator(0) def about(self, widget, data=None): """about - display the about box for xraters """ about = AboutXratersDialog.NewAboutXratersDialog() response = about.run() about.destroy() def preferences(self, widget, data=None): """preferences - display the preferences window for xraters """ prefs = PreferencesXratersDialog.NewPreferencesXratersDialog() response = prefs.run() if response == gtk.RESPONSE_OK: #make any updates based on changed preferences here self.preferences = prefs.get_preferences() prefs.destroy() def quit(self, widget, data=None): """quit - signal handler for closing the XratersWindow""" self.destroy() def on_destroy(self, widget, data=None): """on_destroy - called when the XratersWindow is close. """ #clean up code for saving application state should be added here if self.isConnected: self.on_wiiDisconnect(widget, data) gtk.main_quit() def on_wiiConnect(self, widget, data=None): """Signal handler for the WiiConnect action """ self.widget('actionWiiConnect').set_sensitive(False) connectionMaker = WiiConnectionMaker(self.preferences['wiiAddress'], self.widget("statusbar"), self._connectCallback) self._accAxis.set_xlim(0, 2) gobject.idle_add(self._accCanvas.draw) connectionMaker.start() def on_wiiDisconnect(self, widget, data=None): """Signal handler for the WiiDisconnect action """ self._wiiMote.close() self._connected = False self.widget('actionDisconnect').set_sensitive(False) self.widget('actionWiiConnect').set_sensitive(True) self.widget('actionReset').set_sensitive(False) self.widget('actionPause').set_sensitive(False) self.widget('toolbutton1').set_related_action( self.widget('actionWiiConnect')) self.widget('actionSave').set_sensitive(True) self.widget('statusbar').pop( self.widget("statusbar").get_context_id('')) self._setBatteryIndicator(0) def on_Reset(self, widget, data=None): """Signal handler for the reset action """ self._resetData() self._accAxis.set_xlim(0, 2) gobject.idle_add(self._accCanvas.draw) def on_Pause(self, widge, data=None): """Signal handler for the pause action """ if not self._Paused: self.widget('actionPause').set_short_label("Un_pause") else: self.widget('actionPause').set_short_label("_Pause") self._Paused = not (self._Paused) def save(self, widget, data=None): """Signal handler for the save action """ fileName = os.sep.join([ self.preferences['outputDir'], "acceleration_" + time.strftime("%Y-%m-%d_%H-%M-%S") + ".dat" ]) try: with open(fileName, 'wb') as outFile: writer = csv.writer(outFile, 'excel-tab') outFile.write( writer.dialect.delimiter.join(("#time", "Ax", "Ay", "Az"))) outFile.write(writer.dialect.lineterminator) outFile.write( writer.dialect.delimiter.join(("#s", "g", "g", "g"))) outFile.write(writer.dialect.lineterminator) with self._dataLock: writer.writerows(zip(self._time, *self._accData)) except IOError as error: dialog = gtk.MessageDialog(parent=None, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK, message_format=str(error)) dialog.set_title(error[1]) dialog.connect('response', lambda dialog, response: dialog.destroy()) dialog.show()
class SensorWindow(object): def __init__(self, mainThread, gladefile = 'sensor_window.glade'): self.builder = gtk.Builder() self.builder.add_from_file(gladefile) self.builder.connect_signals(self) self._stopped = False self.mainThread = mainThread self.fig = plt.figure() self.numLines = 1 # lines plot self.ax = self.fig.add_subplot(111) self.ax.set_xlabel('Time') self.ax.set_ylabel('Power') self.ax.xaxis.set_animated(True) self.ax.yaxis.set_animated(True) self.ax.set_title('Light Intensity') self.ax.grid(True) self.start = time.time() self.background1 = None self.prev_time = self.start self.prev_pixel_offset = 0 self.x0 = 0 self.value = [0] * self.numLines self.ax.set_ylim(-1, 256) self.lines = [] for i in range(self.numLines): line, = self.ax.plot([], [], animated = True, lw = 2) self.lines.append(line) self.canvas = FigureCanvas(self.fig) self.graphview = self.builder.get_object("box2") self.graphview.pack_start(self.canvas) self.graphview.reorder_child(self.canvas, 0) self.img = self.builder.get_object("image1") self.img.set_from_file("off.svg") self.lamp = False self.canvas.show() gobject.idle_add(self.update_line) self.canvas.mpl_connect('draw_event', self.on_draw) self.barpath = [] def close_window(self, obj): print "closing window" self.builder.get_object("window1").destroy() def destroy_callback(self, obj): print "destroying window" self.mainThread.stop() self._stopped = True def close_from_mainthread(self): print "close from mainthread" self.builder.get_object("window1").destroy() def toggle_lamp(self): print "toggle lamp!!" self.img = self.builder.get_object("image1") if(self.lamp): self.lamp = False self.img.set_from_file("off.svg") else: self.lamp = True self.img.set_from_file("on.svg") def update_line(self, *args): if self._stopped: self.destroy_callback(None) return False if self.background1 is None: return True cur_time = time.time() pixel_offset = int((cur_time - self.start) * 40.) dx_pixel = pixel_offset - self.prev_pixel_offset self.prev_pixel_offset = pixel_offset dx_data = self.get_dx_data(dx_pixel) #cur_time - self.prev_time) x0 = self.x0 self.x0 += dx_data self.prev_time = cur_time self.ax.set_xlim(self.x0-2, self.x0+0.1) # restore background which will plot lines from previous plots self.restore_background_shifted(dx_pixel) #x0, self.x0) # now plot line segment within [x0, x0+dx_data], # Note that we're only plotting a line between [x0, x0+dx_data]. xx = np.array([x0, self.x0]) for i in range(len(self.lines)): line = self.lines[i] line.set_xdata(xx) # the for loop below could be improved by using collection. line.set_ydata(np.array([self.value[i], self.value[i]])) self.ax.draw_artist(line) self.background2 = self.canvas.copy_from_bbox(self.get_bg_bbox()) self.ax.draw_artist(self.ax.xaxis) self.ax.draw_artist(self.ax.yaxis) self.canvas.blit(self.ax.get_figure().bbox) return True def get_dx_data(self, dx_pixel): tp = self.ax.transData.inverted().transform_point x0, y0 = tp((0, 0)) x1, y1 = tp((dx_pixel, 0)) return (x1 - x0) def get_bg_bbox(self): return self.ax.bbox.padded(-3) def save_bg(self): self.background1 = self.canvas.copy_from_bbox(self.ax.get_figure().bbox) self.background2 = self.canvas.copy_from_bbox(self.get_bg_bbox()) def on_draw(self, *args): self.save_bg() return False def restore_background_shifted(self, dx_pixel): """ restore bacground shifted by dx in data coordinate. This only works if the data coordinate system is linear. """ # restore the clean slate background self.canvas.restore_region(self.background1) # restore subregion (x1+dx, y1, x2, y2) of the second bg # in a offset position (x1-dx, y1) x1, y1, x2, y2 = self.background2.get_extents() self.canvas.restore_region(self.background2, bbox=(x1+dx_pixel, y1, x2, y2), xy=(x1-dx_pixel, y1)) return dx_pixel def update(self, data): if type(data) == ListType: assert(len(self.lines) == len(data)) self.value = data else: assert(len(self.lines) == 1) self.value = [data]
class Plot(gtk.HBox): """ Base class for LivePlots. This class is an abstract class. In addition to inheriting from this class it is also required to take the following actions to achieve a functioning plot: Bla bla bla """ def __init__(self, dpi=100, x_pixel_size=500, y_pixel_size=400): gtk.HBox.__init__(self) # If the class is being reinitialized, we need to remove the old plot [self.remove(child) for child in self.get_children()] self.vbox = gtk.VBox() self.pack_start(self.vbox) # this is bad with labels, I have to figure out why self.connect('size_allocate', self._full_update) self.fig = Figure(figsize=(float(x_pixel_size)/dpi, float(y_pixel_size)/dpi), dpi=dpi) self.fig.set_facecolor('white') self.canvas = FigureCanvasGTKAgg(self.fig) self.ax = self.fig.add_subplot(111) self.vbox.pack_end(self.canvas) self.canvas.connect('button_press_event', self._on_mouse_click) self.first_update = True self.settings = {} self.n_lines = None #self.line_styles = None #self.line_colors = None self.lines = None self.saved_window_size = None self.background = None self.auto_x_scale = False self.auto_y_scale = False def _change_settings_common(self, number_of_lines, **kw): """ Change the subset of settings that are common for all plots """ self.settings.update(kw) self.n_lines = number_of_lines self.line_styles = kw['line_styles'] if kw.has_key('line_styles')\ else ['']*self.n_lines self.line_colors = kw['line_colors'] if kw.has_key('line_colors')\ else self._get_colors(self.n_lines) self.lines = None self.background = None if self.settings['legends'] is not None: c = matplotlib.colors.ColorConverter() colors = [matplotlib.colors.rgb2hex(c.to_rgb(color)) for color in self.line_colors] self.legends = Legends(self.settings['legends'], colors, self.settings['legend_placement'], self.settings['legend_cols']) if self.settings['legend_placement'] == 'top': self.vbox.pack_end(self.legends, expand=False) else: self.pack_end(self.legends, expand=False) def _on_mouse_click(self, widget, event): if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3: b1 = gtk.MenuItem("A button") b2 = gtk.MenuItem("Another") menu = gtk.Menu() menu.append(b1) menu.append(b2) b1.show() b2.show() menu.popup(None, None, None, event.button, event.time) def _update_bounds(self): return self._update_x_bounds() or self._update_y_bounds() def _update_x_bounds(self): if not self.auto_x_scale: return False def _update_y_bounds(self): modified = False if not self.auto_y_scale: return modified # Combine data (double loop), sort out None and get min/max y_min = min([inner for outer in self.y for inner in outer\ if inner is not None]) y_max = max([inner for outer in self.y for inner in outer\ if inner is not None]) delta = y_max - y_min if delta < 1E-10: return modified br = 0.2 if y_min < self.settings['y_bounds'][0]: self.settings['y_bounds'] =\ (y_min-br*delta, self.settings['y_bounds'][1]) modified = True elif y_min > self.settings['y_bounds'][0] + br*delta: self.settings['y_bounds'] =\ (y_min, self.settings['y_bounds'][1]) modified = True if y_max > self.settings['y_bounds'][1]: self.settings['y_bounds'] =\ (self.settings['y_bounds'][0], y_max+br*delta) modified = True elif y_max < self.settings['y_bounds'][1] - br*delta: self.settings['y_bounds'] =\ (self.settings['y_bounds'][0], y_max) modified = True return modified def update_legends(self): if self.settings['legends'] is not None: for n in range(self.n_lines): point = self.y[n][-1] self.legends.set_legend_text(n, point) def _quick_update(self): if self.first_update: self._full_update() self.first_update = False self.canvas.restore_region(self.background) [line.set_ydata(y_data) for line, y_data in zip(self.lines, self.y)] [self.ax.draw_artist(line) for line in self.lines] # just redraw the axes rectangle self.canvas.blit(self.ax.bbox) def _full_update(self, widget=None, size=None): # This does not work properly, FIXME if widget is not None: if [size.width, size.height] != self.saved_window_size: self.saved_window_size = [size.width, size.height] else: return # Plot if self.first_update: plot = self.ax.semilogy if self.settings['logscale'] else self.ax.plot self.lines = [plot(x, y, style, color=color, animated=True)[0] for x, y, color, style in zip(self.x, self.y, self.line_colors, self.line_styles)] # Title and axis labels if self.settings['title'] is not None: self.ax.set_title(self.settings['title']) if self.settings['x_label'] is not None: self.ax.set_xlabel(self.settings['x_label']) if self.settings['y_label'] is not None: self.ax.set_ylabel(self.settings['y_label']) self.fig.subplots_adjust(left=0.25, bottom=0.15) else: [line.set_ydata(y_data) for line, y_data in zip(self.lines, self.y)] # Get or set boundaries if self.first_update: if self.settings['x_bounds'] is None: self.settings['x_bounds'] = self.ax.get_xlim() if self.settings['y_bounds'] is None: self.settings['y_bounds'] = self.ax.get_ylim() else: self.ax.set_xlim(*self.settings['x_bounds']) self.ax.set_ylim(*self.settings['y_bounds']) # Get the background for later use self.canvas.draw() self.background = self.canvas.copy_from_bbox(self.ax.bbox) # First draw update [line.set_animated(False) for line in self.lines] self.canvas.draw() [line.set_animated(True) for line in self.lines] def _get_colors(self, n): """ Generate colors for the lines if they are not provided. First use 6 of the standard colors and then generate random colors Parameters: n -- the number of colors requested """ standard = ['r', 'g', 'b', 'c', 'm', 'k'] if n <= len(standard): out = standard[:n] else: out = standard if n > len(standard): for i in range(len(standard), n): out.append((random.random(), random.random(), random.random())) return out
class XratersWindow(gtk.Window): __gtype_name__ = "XratersWindow" def __init__(self): """__init__ - This function is typically not called directly. Creation a XratersWindow requires redeading the associated ui file and parsing the ui definition extrenally, and then calling XratersWindow.finish_initializing(). Use the convenience function NewXratersWindow to create XratersWindow object. """ self._acc_cal = ((128, 128, 128), (255, 255, 255)) self._acc = [0, 0, 0] self._connected = False self._wiiMote = None self._resetData() self._dataLock = threading.Lock() isConnected = property(lambda self: self._connected) def callback(funct): """A decorator used to require connection to the Wii Remote This decorator is used to implement the precondition that the Wii Remote must be connected. """ def _callback(cls, *args, **kwds): if cls.isConnected: funct(cls, *args, **kwds) return True else: return False return _callback def _connectCallback(self, connectionMaker): """Callback function called upon successful connection to the Wiimote """ if connectionMaker.connected: self._connected = True self._wiiMote = connectionMaker.wiiMote self._resetData() gobject.timeout_add(45, self._drawAcc) self.widget('actionDisconnect').set_sensitive(True) self.widget('actionSave').set_sensitive(True) self.widget('actionReset').set_sensitive(True) self.widget('actionPause').set_sensitive(True) self.widget('toolbutton1').set_related_action(self.widget('actionDisconnect')) self._acc_cal = connectionMaker.acc_cal self._wiiMote.mesg_callback = self._getAcc self._updBatteryLevel() gobject.timeout_add_seconds(60, self._updBatteryLevel) else: self.widget('actionWiiConnect').set_sensitive(True) @callback def _upd_background(self, event): """Keep a copy of the figure background """ self.__background = self._accCanvas.copy_from_bbox(self._accAxis.bbox) def _getAcc(self, messages, theTime=0): """Process acceleration messages from the Wiimote This function is intended to be set as cwiid.mesg_callback """ if self._Paused: return for msg in messages: if msg[0] == cwiid.MESG_ACC: # Normalize data using calibration info for i, axisAcc in enumerate(msg[1]): self._acc[i] = float(axisAcc-self._acc_cal[0][i]) self._acc[i] /=(self._acc_cal[1][i]\ -self._acc_cal[0][i]) with self._dataLock: # Store time and acceleration in the respective arrays self._time.append(theTime-self._startTime) [self._accData[i].append(self._acc[i]) for i in threeAxes] # We only keep about 6 seconds worth of data if (self._time[-1] - self._time[0] > 6): with self._dataLock: self._time.pop(0) [self._accData[i].pop(0) for i in threeAxes] @callback def _drawAcc(self): """Update the acceleration graph """ # Do nothing while paused or there's no data available if self._Paused or len(self._time)==0: return draw_flag = False # Update axes limits if the data fall out of range lims = self._accAxis.get_xlim() if self._time[-1] > lims[1]: self._accAxis.set_xlim(lims[0], lims[1]+2) lims = self._accAxis.get_xlim() draw_flag = True if (self._time[-1] - lims[0] > 6): self._accAxis.set_xlim(lims[0]+2, lims[1]) draw_flag = True if draw_flag: gobject.idle_add(self._accCanvas.draw) # Do the actual update of the background if self.__background != None: self._accCanvas.restore_region(self.__background) # Do the actual update of the lines with self._dataLock: [self._lines[i].set_data(self._time, self._accData[i]) for i in threeAxes] [self._accAxis.draw_artist(self._lines[i]) for i in threeAxes] self._accCanvas.blit(self._accAxis.bbox) @callback def _updBatteryLevel(self): """Callback to update the battery indicator in the status bar """ self._wiiMote.request_status() self._setBatteryIndicator(float(self._wiiMote.state['battery']) / cwiid.BATTERY_MAX) def _setBatteryIndicator(self, level): """Actually update the battery indicator in the status bar """ progressBar = self.widget("progressbarBattery") progressBar.set_fraction(level) progressBar.set_text("Battery: %.0f%%" % (level * 100)) def _resetData(self): """Reset stored data and status flags to their defaults """ self._accData = [list(), list(), list()] self._time = list() self._startTime = time.time() self._moveTime = self._startTime self._Paused = False def widget(self, name): """Helper function to retrieve widget handlers """ return self.builder.get_object(name) def finish_initializing(self, builder): """finish_initalizing should be called after parsing the ui definition and creating a XratersWindow object with it in order to finish initializing the start of the new XratersWindow instance. """ #get a reference to the builder and set up the signals self.builder = builder self.builder.connect_signals(self) #uncomment the following code to read in preferences at start up dlg = PreferencesXratersDialog.NewPreferencesXratersDialog() self.preferences = dlg.get_preferences() #code for other initialization actions should be added here self._accFigure = Figure(figsize=(8,6), dpi=72) self._accAxis = self._accFigure.add_subplot(111) self._accAxis.set_xlabel("time (s)") self._accAxis.set_ylabel("acceleration (g)") self._lines = self._accAxis.plot(self._time, self._accData[X], self._time, self._accData[Y], self._time, self._accData[Z], animated=True) self._accFigure.legend(self._lines, ("X", "Y", "Z"), 'upper center', ncol=3) self._accAxis.set_xlim(0, 2) self._accAxis.set_ylim(-3, 3) self._accCanvas = FigureCanvas(self._accFigure) self._accCanvas.mpl_connect("draw_event", self._upd_background) self.__background = self._accCanvas.copy_from_bbox(self._accAxis.bbox) self._accCanvas.show() self._accCanvas.set_size_request(600, 400) vbMain = self.widget("vboxMain") vbMain.pack_start(self._accCanvas, True, True) vbMain.show() vbMain.reorder_child(self._accCanvas, 2) self._setBatteryIndicator(0) def about(self, widget, data=None): """about - display the about box for xraters """ about = AboutXratersDialog.NewAboutXratersDialog() response = about.run() about.destroy() def preferences(self, widget, data=None): """preferences - display the preferences window for xraters """ prefs = PreferencesXratersDialog.NewPreferencesXratersDialog() response = prefs.run() if response == gtk.RESPONSE_OK: #make any updates based on changed preferences here self.preferences = prefs.get_preferences() prefs.destroy() def quit(self, widget, data=None): """quit - signal handler for closing the XratersWindow""" self.destroy() def on_destroy(self, widget, data=None): """on_destroy - called when the XratersWindow is close. """ #clean up code for saving application state should be added here if self.isConnected: self.on_wiiDisconnect(widget, data) gtk.main_quit() def on_wiiConnect(self, widget, data=None): """Signal handler for the WiiConnect action """ self.widget('actionWiiConnect').set_sensitive(False) connectionMaker = WiiConnectionMaker(self.preferences['wiiAddress'], self.widget("statusbar"), self._connectCallback) self._accAxis.set_xlim(0, 2) gobject.idle_add(self._accCanvas.draw) connectionMaker.start() def on_wiiDisconnect(self, widget, data=None): """Signal handler for the WiiDisconnect action """ self._wiiMote.close() self._connected = False self.widget('actionDisconnect').set_sensitive(False) self.widget('actionWiiConnect').set_sensitive(True) self.widget('actionReset').set_sensitive(False) self.widget('actionPause').set_sensitive(False) self.widget('toolbutton1').set_related_action(self.widget('actionWiiConnect')) self.widget('actionSave').set_sensitive(True) self.widget('statusbar').pop(self.widget("statusbar").get_context_id('')) self._setBatteryIndicator(0) def on_Reset(self, widget, data=None): """Signal handler for the reset action """ self._resetData() self._accAxis.set_xlim(0, 2) gobject.idle_add(self._accCanvas.draw) def on_Pause(self, widge, data=None): """Signal handler for the pause action """ if not self._Paused: self.widget('actionPause').set_short_label("Un_pause") else: self.widget('actionPause').set_short_label("_Pause") self._Paused = not (self._Paused) def save(self, widget, data=None): """Signal handler for the save action """ fileName = os.sep.join([self.preferences['outputDir'], "acceleration_" + time.strftime("%Y-%m-%d_%H-%M-%S") + ".dat"]) try: with open(fileName, 'wb') as outFile: writer = csv.writer(outFile, 'excel-tab') outFile.write(writer.dialect.delimiter.join(("#time", "Ax", "Ay", "Az"))) outFile.write(writer.dialect.lineterminator) outFile.write(writer.dialect.delimiter.join(("#s", "g", "g", "g"))) outFile.write(writer.dialect.lineterminator) with self._dataLock: writer.writerows(zip(self._time, *self._accData)) except IOError as error: dialog = gtk.MessageDialog(parent = None, flags = gtk.DIALOG_DESTROY_WITH_PARENT, type = gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_OK, message_format = str(error)) dialog.set_title(error[1]) dialog.connect('response', lambda dialog, response: dialog.destroy()) dialog.show()
class plot_data(object): ''' Creates a object with a widget ready to be inserted in a GTK window, in wich we can plot the current robot variables. calling the object will update the plot with the data passed calling the method set_values you can change the color, units a/o length ''' def __init__(self, units=None, colors=None, length=None): ''' Modifiable attributes: colors: color of the lines - list of strings: len = 4 default: ['r', 'g', 'b', 'y'] units: units of the plots - list of strings: len = 2 default: [rad, N*m] length: maximum period of time to show - int or float default: 100 Accessible attributes: main_widget: widget to be inserted in a GTK window ''' self.set_values(units, colors, length) # Define axis and lines labels self.__xlabel = 'Time - s' self.__ylabel = ['Angular position - '+self.__units[0], 'Angular velocity - '+self.__units[0]+'/s', 'Angular acceleration - '+self.__units[0]+'/s**2', 'Torque - '+self.__units[1]] self.__lines_labels = ["Shoulder_X", "Shoulder_Y", "Shoulder_Z", "Elbow_Z"] # Define font size for the legend self.__font = font_manager.FontProperties(size = 8) # Create the Figure and the plot self.__figure = figure() self.__sub_figures = [] self.__backgrounds = [] self.__lines = [[], [], [], []] # Create the widget, a FigureCanvas containing our Figure self.main_widget = FigureCanvas(self.__figure) # Create and configure the subplots for index in xrange(4): self.__sub_figures.append(self.__figure.add_subplot(221+index)) self.__sub_figures[index].grid(True) self.__sub_figures[index].set_xlabel(self.__xlabel, fontsize = 9) self.__sub_figures[index].set_ylabel(self.__ylabel[index], fontsize = 9) #FIXME: change the y limits, the currents are for test only self.__sub_figures[index].set_ylim(-256, 256) self.__sub_figures[index].set_xlim(0, self.__lenght) self.__sub_figures[index].tick_params(axis='both', which = 'major', labelsize = 10) self.__sub_figures[index].tick_params(axis='both', which = 'minor', labelsize = 8) for l_index in xrange(4): self.__lines[index].append(self.__sub_figures[index].plot([], [], self.__colors[l_index], animated = True)[0]) #Saving the firsts background to redraw self.__backgrounds.append(self.main_widget.copy_from_bbox(self.__sub_figures[index].bbox)) #Setting up the legend box figlegend(self.__lines[0], self.__lines_labels, loc = "upper center", prop= self.__font) #Show and set initial data self.main_widget.draw() self.__reset_data() return def __call__(self, values, d_time): ''' values: object with the current values to plot, in the form of attributes like: self.position = [] self.velocity = [] self.acceleration = [] self.torque = [] d_time: current time, since the plot starts - int or float ''' self.__time.append(d_time) if (d_time >= (self.__time[0] + self.__lenght)): self.__reset_data() self.__time.append(d_time) for index in xrange(4): self.__sub_figures[index].set_xlim(d_time, d_time+self.__lenght) self.main_widget.draw() for index in xrange(4): self.__backgrounds[index] = self.main_widget.copy_from_bbox(self.__sub_figures[index].bbox) for index in xrange(4): self.main_widget.restore_region(self.__backgrounds[index]) self.__position[index].append(values.position[index]) self.__velocity[index].append(values.velocity[index]) self.__acceleration[index].append(values.acceleration[index]) self.__torque[index].append(values.torque[index]) for index in xrange(4): for l_index in xrange(4): if index == 0: self.__lines[index][l_index].set_data(self.__time, self.__position[l_index]) elif index == 1: self.__lines[index][l_index].set_data(self.__time, self.__velocity[l_index]) elif index == 2: self.__lines[index][l_index].set_data(self.__time, self.__acceleration[l_index]) elif index == 3: self.__lines[index][l_index].set_data(self.__time, self.__torque[l_index]) self.__sub_figures[index].draw_artist(self.__lines[index][l_index]) self.main_widget.blit(self.__sub_figures[index].bbox) return def __reset_data(self): #Create the vectors for the variables try: type(self.__time) except: self.__time = [] self.__position = [[], [], [], []] self.__velocity = [[], [], [], []] self.__acceleration = [[], [], [], []] self.__torque = [[], [], [], []] return #If the vectors are already created, then only leave the last value del(self.__time[-2:0:-1]) self.__time.pop(0) for index in xrange(4): del(self.__position[index][-2:0:-1]) del(self.__velocity[index][-2:0:-1]) del(self.__acceleration[index][-2:0:-1]) del(self.__torque[index][-2:0:-1]) self.__position[index].pop(0) self.__velocity[index].pop(0) self.__acceleration[index].pop(0) self.__torque[index].pop(0) return def set_values(self, units=None, colors=None, length=None): self.__units = ["rad", "N*m"] if type(units) is NoneType else units self.__colors = ['r', 'g', 'b', 'y'] if type(colors) is NoneType else colors self.__lenght = 100 if type(length) is NoneType else length return