class PlotPanel(wx.Panel): """The PlotPanel has a Figure and a Canvas. OnSize events simply set a flag, and the actual resizing of the figure is triggered by an Idle event.""" def __init__(self, parent, color=None, dpi=None, **kwargs): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure self.parent = parent # initialize Panel if 'id' not in kwargs.keys(): kwargs['id'] = wx.ID_ANY if 'style' not in kwargs.keys(): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__(self, parent, **kwargs) # initialize matplotlib stuff self.figure = Figure(None, dpi) self.canvas = FigureCanvasWxAgg(self, -1, self.figure) self.SetColor(color) self._SetSize() self.initial_draw() self._resizeflag = False self._redrawflag = False self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) def SetColor(self, rgbtuple=None): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) def _onSize(self, event): self._resizeflag = True def _onIdle(self, evt): with draw_lock: if self._resizeflag: self._resizeflag = False self._SetSize() if self._redrawflag: self._redrawflag = False self.canvas.draw() def _SetSize(self): # When drawing from another thread, I think this may need a lock pixels = tuple(self.parent.GetClientSize()) self.SetSize(pixels) self.canvas.SetSize(pixels) self.figure.set_size_inches(float(pixels[0])/self.figure.get_dpi(), float(pixels[1])/self.figure.get_dpi()) def initial_draw(self): pass # abstract, to be overridden by child classes
def __init__(self, parent=None, width=5, height=4, dpi=100): fig = Figure(figsize=(width, height), dpi=dpi) # We want the axes cleared every time plot() is called self.axes = fig.add_subplot(1, 1, 1) self.axes.hold(False) FigureCanvas.__init__(self, fig) # self.figure self.setParent(parent) FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) self._title = '' self.title_font = {'family': 'serif', 'fontsize': 10} self._title_size = 0 self.figure.subplots_adjust(top=0.95, bottom=0.15) window_brush = self.window().palette().window() fig.set_facecolor(brush_to_color_tuple(window_brush)) fig.set_edgecolor(brush_to_color_tuple(window_brush)) self._active = False
class PlotPanel(wx.Panel): """ The PlotPanel has a Figure and a Canvas. OnSize events simply set a flag, and the actual resizing of the figure is triggered by an Idle event. """ def __init__(self, parent, obj_id): # initialize Panel wx.Panel.__init__(self, parent, obj_id) # initialize matplotlib stuff self.figure = Figure(None, None) self.canvas = FigureCanvasWxAgg(self, wx.ID_ANY, self.figure) rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255.0 for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) self.Bind(wx.EVT_SIZE, self._on_size) def _on_size(self, event): self._set_size() def _set_size(self): pixels = tuple(self.GetClientSize()) self.SetSize(pixels) self.canvas.SetSize(pixels) self.figure.set_size_inches(float(pixels[0]) / self.figure.get_dpi(), float(pixels[1]) / self.figure.get_dpi()) def draw(self): pass # abstract, to be overridden by child classes
class PlotPanel (wx.Panel): """ PlotPanel class taken from http://www.scipy.org/Matplotlib_figure_in_a_wx_panel The PlotPanel has a Figure and a Canvas. OnSize events simply set a flag, and the actual resizing of the figure is triggered by an Idle event.""" def __init__(self, parent, color=None, dpi=None, **kwargs): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure self.parent = parent # initialize Panel if 'id' not in list(kwargs.keys()): kwargs['id'] = wx.ID_ANY if 'style' not in list(kwargs.keys()): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__(self, parent, **kwargs) # initialize matplotlib stuff self.figure = Figure(None, dpi) self.canvas = FigureCanvasWxAgg(self, -1, self.figure) self.SetColor(color) self._SetSize() self.draw() self._resizeflag = False self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) def SetColor(self, rgbtuple=None): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) def _onSize(self, event): self._resizeflag = True def _onIdle(self, evt): if self._resizeflag: self._resizeflag = False self._SetSize() def _SetSize(self): pixels = tuple(self.parent.GetClientSize()) self.SetSize(pixels) self.canvas.SetSize(pixels) self.figure.set_size_inches(float(pixels[0]) / self.figure.get_dpi(), float(pixels[1]) / self.figure.get_dpi()) def draw(self): # abstract, to be overridden by child classes raise NotImplementedError( "Abstract method - must be defined in subclass")
class WXMatPlotLibPanel( wx.Panel ): def __init__( self, parent, color=None, dpi=None, **kwargs ): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure # initialize Panel if 'id' not in kwargs.keys(): kwargs['id'] = wx.ID_ANY if 'style' not in kwargs.keys(): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__( self, parent, **kwargs ) # initialize matplotlib stuff self.figure = Figure( None, dpi ) self.canvas = FigureCanvasWxAgg( self, -1, self.figure ) #self.SetColor( color ) self._SetSize() self.draw() self._resizeflag = False self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) def SetColor( self, rgbtuple=None ): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor( clr ) self.figure.set_edgecolor( clr ) self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) def _onSize( self, event ): self._resizeflag = True def _onIdle( self, evt ): if self._resizeflag: self._resizeflag = False self._SetSize() def _SetSize( self ): pixels = tuple( self.parent.GetClientSize() ) self.SetSize( pixels ) self.canvas.SetSize( pixels ) self.figure.set_size_inches( float( pixels[0] )/self.figure.get_dpi(), float( pixels[1] )/self.figure.get_dpi() ) def draw(self): pass # abstract, to be overridden by child classes
class PlotPanel (wx.Panel): """The PlotPanel has a Figure and a Canvas. OnSize events simply set a flag, and the actual resizing of the figure is triggered by an Idle event.""" def __init__( self, parent, color=None, dpi=None, **kwargs ): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure # initialize Panel if 'id' not in kwargs.keys(): kwargs['id'] = wx.ID_ANY if 'style' not in kwargs.keys(): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__( self, parent, **kwargs ) # initialize matplotlib stuff self.figure = Figure( None, dpi ) self.canvas = FigureCanvasWxAgg( self, -1, self.figure ) self.SetColor( color ) self._resizeflag = False self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) def SetColor( self, rgbtuple=None ): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor( clr ) self.figure.set_edgecolor( clr ) self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) def _onSize( self, event ): self._resizeflag = True def _onIdle( self, evt ): if self._resizeflag: self._resizeflag = False self.canvas.SetSize( self.GetSize() ) def draw(self, *args, **kwargs): pass # abstract, to be overridden by child classes if not hasattr( self, 'subplot' ): self.subplot = self.figure.add_subplot( 111 ) clr = [0.5, 0.5, 0.5] self.subplot.plot( [1,2,3,4], [-5,12,3,45], color=clr )
def marker_image(path, size, trans, edgecolor='k', facecolor = 'b', edgewidth= 1.0): fig = Figure(figsize=((size*2+1)/72., (size*2+1)/72.), dpi = 72) #fig.set_edgecolor([1,1,1,0]) #fig.set_facecolor([1,1,1,0]) fig.set_edgecolor([0,0,0,0]) fig.set_facecolor([0,0,0,0]) fig.patch.set_alpha(0.0) fig.clf() fig.frameon = False ax = fig.add_subplot(111) ax.set_position((0,0,1,1)) ed=ax.transAxes.transform([(0,0), (1,1)]) path = trans.transform_path(path) patch = patches.PathPatch(path, facecolor=facecolor, edgecolor=edgecolor, lw=edgewidth) ax.patch.set_facecolor([0,0,0,0]) ax.patch.set_edgecolor([0,0,0,0]) ax.patch.set_alpha(0.0) ax.add_patch(patch) ax.set_xlim(-1,1) ax.set_ylim(-1,1) ax.tick_params(length=0) ax.set_axis_off() canvas = FigureCanvasAgg(fig) buff, size = canvas.print_to_buffer() im = np.fromstring(buff, np.uint8).reshape(size[1], size[0], -1) #idx = np.where(im[:,:,-1] == 0) #im[:,:,0][idx] = 0 #im[:,:,1][idx] = 0 #im[:,:,2][idx] = 0 return im
class Grafico(wx.Panel): altura_grafico = 340 largura_grafico = 550 def __init__(self, pai, posicao, labely, labelx): wx.Panel.__init__(self, pai, -1, posicao, size=(Grafico.largura_grafico, Grafico.altura_grafico)) self.labelx = labelx self.labely = labely self.inicia() #funcao que inicia os gráficos def inicia(self): #cria uma nova figura que vai conter o grafico self.figura = Figure() #cria um canvas para imagem self.canvas = FigureCanvas(self, -1, self.figura) #cria um só plot self.eixos = self.figura.add_subplot(111) self.eixos.set_ylabel(self.labely) self.eixos.set_xlabel(self.labelx) self.canvas.SetSize((Grafico.largura_grafico,Grafico.altura_grafico)) self.figura.set_edgecolor("m") #desenha os pontos x e y. Dois vetores que devem ter o mesmo tamanho #os vertices serão (pontosX[n], pontosY[n]) def desenha(self, pontosX, pontosY): #adiciona os pontos x e y no grafico self.eixos.plot(pontosX, pontosY) self.canvas.draw() #limpa o grafico def limpa(self): self.inicia()
class PlotPanel(wx.Panel): """ The PlotPanel has a Figure and a Canvas. """ def __init__(self,parent,obj_id): # initialize Panel wx.Panel.__init__(self,parent,obj_id) # initialize matplotlib stuff self.figure = Figure(None,None,tight_layout=True) self.canvas = FigureCanvasWxAgg(self,wx.ID_ANY,self.figure) rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor( clr ) self.figure.set_edgecolor( clr ) self.sizer = wx.BoxSizer(wx.HORIZONTAL) self.sizer.Add(self.canvas,1,wx.EXPAND) self.SetSizer(self.sizer) def draw(self): pass # abstract, to be overridden by child classes
class TransformCanvasPanel(FigureCanvas): """A panel that extends the matplotlib class FigureCanvas for plotting the corresponding points from the two coordinate systems, and their transforms""" def __init__(self, parent, **kwargs): """keyword the same as standard init function for a FigureCanvas""" self.figure = Figure(figsize=(6,4)) FigureCanvas.__init__(self, parent, -1, self.figure, **kwargs) self.canvas = self.figure.canvas #format the appearance self.figure.set_facecolor((1,1,1)) self.figure.set_edgecolor((1,1,1)) self.canvas.SetBackgroundColour('black') #add subplots for various things self.fromplot = self.figure.add_axes([.05,.05,.45,.92]) self.toplot = self.figure.add_axes([.55,.05,.45,.92]) self.fromplot.set_axis_bgcolor('black') self.toplot.set_axis_bgcolor('black') self.fromplot.yaxis.set_major_locator(LooseMaxNLocator(nbins=5,margin=.125)) self.fromplot.xaxis.set_major_locator(LooseMaxNLocator(nbins=5,margin=.125)) #self.toplot.yaxis.set_major_locator(LooseMaxNLocator(nbins=5,margin=.0125)) #self.toplot.xaxis.set_major_locator(LooseMaxNLocator(nbins=5,margin=.0125)) def plot_points_in_from(self,xx,yy,color='black'): self.from_points=Line2D(xx,yy,marker='x',markersize=7,markeredgewidth=1.5,markeredgecolor=color) self.fromplot.add_line(self.pointLine2D) def plot_trans_points(self,xx,yy,color='black'): self.trans_points=Line2D(xx,yy,marker='x',markersize=7,markeredgewidth=1.5,markeredgecolor=color) self.toplot.add_line(self.trans_points) for i in range(len(xx)): numTxt = self.toplot.text(xx[i],yy[i],str(i)+" ",color='g',weight='bold') numTxt.set_visible(True) numTxt.set_horizontalalignment('left') self.toplot.relim() self.toplot.autoscale_view() self.draw()
class ScatterPanel(FigureCanvasWxAgg): ''' Contains the guts for drawing scatter plots. ''' def __init__(self, parent, **kwargs): self.figure = Figure() FigureCanvasWxAgg.__init__(self, parent, -1, self.figure, **kwargs) self.canvas = self.figure.canvas self.SetMinSize((100,100)) self.figure.set_facecolor((1,1,1)) self.figure.set_edgecolor((1,1,1)) self.canvas.SetBackgroundColour('white') self.subplot = self.figure.add_subplot(111) self.gate_helper = GatingHelper(self.subplot, self) self.x_column = None self.y_column = None self.navtoolbar = None self.x_points = [] self.y_points = [] self.key_lists = None self.colors = [] self.x_scale = LINEAR_SCALE self.y_scale = LINEAR_SCALE self.x_label = '' self.y_label = '' self.selection = {} self.mouse_mode = 'gate' self.legend = None self.lasso = None self.redraw() self.canvas.mpl_connect('button_press_event', self.on_press) self.canvas.mpl_connect('button_release_event', self.on_release) def set_configpanel(self,configpanel): '''Allow access of the control panel from the plotting panel''' self.configpanel = configpanel def get_current_x_measurement_name(self): '''Return the x measurement column currently selected in the UI panel''' return str(self.configpanel.x_choice.Value) def get_current_y_measurement_name(self): '''Return the x measurement column currently selected in the UI panel''' return str(self.configpanel.y_choice.Value) def is_per_object_data(self): '''return whether points in the current plot represent objects''' if p.object_table is None: return False for kl in self.key_lists: try: if len(kl[0]) == len(object_key_columns()): return True except KeyError: pass return False def is_per_image_data(self): '''return whether points in the current plot represent images''' # FIXME: still don't support per-well data return not self.is_per_object_data() def selection_is_empty(self): return self.selection == {} or all([len(s)==0 for s in list(self.selection.values())]) def lasso_callback(self, verts): # Note: If the mouse is released outside of the canvas, (None,None) is # returned as the last coordinate pair. # Cancel selection if user releases outside the canvas. if None in verts[-1]: return for c, collection in enumerate(self.subplot.collections): # Build the selection if len(self.xys[c]) > 0: from matplotlib.path import Path new_sel = np.nonzero(Path(verts).contains_points(self.xys[c]))[0] else: new_sel = [] if self.selection_key == None: self.selection[c] = new_sel elif self.selection_key == 'shift': self.selection[c] = list(set(self.selection.get(c,[])).union(new_sel)) elif self.selection_key == 'alt': self.selection[c] = list(set(self.selection.get(c,[])).difference(new_sel)) # outline the points edgecolors = collection.get_edgecolors() for i in range(len(self.xys[c])): if i in self.selection[c]: edgecolors[i] = SELECTED_OUTLINE_COLOR else: edgecolors[i] = UNSELECTED_OUTLINE_COLOR logging.info('Selected %s points.'%(np.sum([len(sel) for sel in list(self.selection.values())]))) self.canvas.draw_idle() def on_press(self, evt): if self.legend and self.legend.hit_test(evt): return if evt.button == 1: self.selection_key = evt.key if self.canvas.widgetlock.locked(): return if evt.inaxes is None: return if self.navtoolbar.get_mode() == 'lasso': self.lasso = Lasso(evt.inaxes, (evt.xdata, evt.ydata), self.lasso_callback) # acquire a lock on the widget drawing self.canvas.widgetlock(self.lasso) def on_release(self, evt): # Note: lasso_callback is not called on click without drag so we release # the lock here to handle this case as well. if evt.button == 1: if self.lasso: self.canvas.draw_idle() self.canvas.widgetlock.release(self.lasso) self.lasso = None elif evt.button == 3: # right click self.show_popup_menu((evt.x, self.canvas.GetSize()[1]-evt.y), None) def show_objects_from_selection(self, evt=None): '''Callback for "Show objects in selection" popup item.''' show_keys = [] for i, sel in list(self.selection.items()): keys = self.key_lists[i][sel] show_keys += list(set([tuple(k) for k in keys])) if len(show_keys[0]) == len(image_key_columns()): from . import datamodel dm = datamodel.DataModel() obkeys = [] for key in show_keys: obkeys += dm.GetObjectsFromImage(key) show_keys = obkeys if len(show_keys) > 100: te = wx.TextEntryDialog(self, 'You have selected %s %s. How many ' 'would you like to show at random?'%(len(show_keys), p.object_name[1]), 'Choose # of %s'% (p.object_name[1]), defaultValue='100') te.ShowModal() try: res = int(te.Value) np.random.shuffle(show_keys) show_keys = show_keys[:res] except ValueError: wx.MessageDialog('You have entered an invalid number', 'Error').ShowModal() return from . import sortbin f = sortbin.CellMontageFrame(None) f.Show() f.add_objects(show_keys) def show_objects_from_gate(self, evt=None): '''Callback for "Show objects in gate" popup item.''' gatename = self.configpanel.gate_choice.get_gatename_or_none() if gatename: ui.show_objects_from_gate(gatename) def show_images_from_gate(self, evt=None): '''Callback for "Show images in gate" popup item.''' gatename = self.configpanel.gate_choice.get_gatename_or_none() if gatename: ui.show_images_from_gate(gatename) def show_images_from_selection(self, evt=None): '''Callback for "Show images in selection" popup item.''' show_keys = set() for i, sel in list(self.selection.items()): keys = self.key_lists[i][sel] show_keys.update([tuple(k) for k in keys]) if len(show_keys)>10: dlg = wx.MessageDialog(self, 'You are about to open %s images. ' 'This may take some time depending on your ' 'settings. Continue?'%(len(show_keys)), 'Warning', wx.YES_NO|wx.ICON_QUESTION) response = dlg.ShowModal() if response != wx.ID_YES: return logging.info('Opening %s images.'%(len(show_keys))) for key in sorted(show_keys): if len(key) == len(image_key_columns()): imagetools.ShowImage(key, p.image_channel_colors, parent=self) else: imview = imagetools.ShowImage(key[:-1], p.image_channel_colors, parent=self) imview.SelectObject(key) def show_selection_in_table(self, evt=None): '''Callback for "Show selection in a table" popup item.''' for i, sel in list(self.selection.items()): keys = self.key_lists[i][sel].T.astype('object') if len(keys) > 0: xpoints = self.x_points[i][sel] ypoints = self.y_points[i][sel] table_data = np.vstack((keys, xpoints, ypoints)).T column_labels = [] if self.is_per_image_data(): column_labels = list(image_key_columns()) group = 'Image' elif self.is_per_object_data(): column_labels = list(object_key_columns()) group = 'Object' key_col_indices = list(range(len(column_labels))) column_labels += [self.get_current_x_measurement_name(), self.get_current_y_measurement_name()] grid = tableviewer.TableViewer(self, title='Selection from collection %d in scatter'%(i)) grid.table_from_array(table_data, column_labels, group, key_col_indices) grid.set_fitted_col_widths() grid.Show() else: logging.info('No points were selected in collection %d'%(i)) def on_new_collection_from_filter(self, evt): '''Callback for "Collection from filter" popup menu options.''' assert self.key_lists, 'Can not create a collection from a filter since image keys have not been set for this plot.' filter = self.popup_menu_filters[evt.Id] keys = sorted(db.GetFilteredImages(filter)) key_lists = [] xpoints = [] ypoints = [] sel_keys = [] sel_xs = [] sel_ys = [] for c, col in enumerate(self.subplot.collections): sel_indices = [] unsel_indices = [] # Find indices of keys that fall in the filter. # Improved performance: |N|log(|F|) N = data points, F = filterd points # Assumes that the filtered image keys are in order if self.is_per_object_data(): collection_keys = [tuple(k[:-1]) for k in self.key_lists[c]] else: collection_keys = [tuple(k) for k in self.key_lists[c]] for i, key in enumerate(collection_keys): idx = bisect(keys, key) - 1 if keys[idx] == key: sel_indices += [i] else: unsel_indices += [i] # Build the new collections if len(sel_indices) > 0: if self.key_lists: sel_keys += list(self.key_lists[c][sel_indices]) sel_xs += list(self.x_points[c][sel_indices]) sel_ys += list(self.y_points[c][sel_indices]) if len(unsel_indices) > 0: if self.key_lists: key_lists += [self.key_lists[c][unsel_indices]] xpoints += [np.array(self.x_points[c][unsel_indices])] ypoints += [np.array(self.y_points[c][unsel_indices])] xpoints += [np.array(sel_xs)] ypoints += [np.array(sel_ys)] if self.key_lists: key_lists += [np.array(sel_keys)] self.set_points(xpoints, ypoints) if self.key_lists: self.set_keys(key_lists) # reset scale (this is so the user is warned of masked non-positive values) self.set_x_scale(self.x_scale) self.set_y_scale(self.y_scale) self.redraw() self.figure.canvas.draw_idle() def on_collection_from_selection(self, evt): '''Callback for "Collection from selection" popup menu option.''' key_lists = [] xpoints = [] ypoints = [] sel_keys = [] sel_xs = [] sel_ys = [] for c, col in enumerate(self.subplot.collections): indices = list(range(len(col.get_offsets()))) sel_indices = self.selection[c] unsel_indices = list(set(indices).difference(sel_indices)) if len(sel_indices) > 0: if self.key_lists: sel_keys += list(self.key_lists[c][sel_indices]) sel_xs += list(self.x_points[c][sel_indices]) sel_ys += list(self.y_points[c][sel_indices]) if len(unsel_indices) > 0: if self.key_lists: key_lists += [self.key_lists[c][unsel_indices]] xpoints += [np.array(self.x_points[c][unsel_indices])] ypoints += [np.array(self.y_points[c][unsel_indices])] xpoints += [np.array(sel_xs)] ypoints += [np.array(sel_ys)] if self.key_lists: key_lists += [np.array(sel_keys)] self.set_points(xpoints, ypoints) if self.key_lists: self.set_keys(key_lists) # reset scale (this is so the user is warned of masked non-positive values) self.set_x_scale(self.x_scale) self.set_y_scale(self.y_scale) self.redraw() self.figure.canvas.draw_idle() def show_popup_menu(self, xxx_todo_changeme1, data): (x,y) = xxx_todo_changeme1 self.popup_menu_filters = {} popup = wx.Menu() loadimages_table_item = popup.Append(-1, 'Create gated table for CellProfiler LoadImages') selected_gate = self.configpanel.gate_choice.get_gatename_or_none() selected_gates = [] if selected_gate: selected_gates = [selected_gate] self.Bind(wx.EVT_MENU, lambda e:ui.prompt_user_to_create_loadimages_table(self, selected_gates), loadimages_table_item) show_images_in_gate_item = popup.Append(-1, 'Show images in gate') show_images_in_gate_item.Enable(selected_gate is not None) self.Bind(wx.EVT_MENU, self.show_images_from_gate, show_images_in_gate_item) if p.object_table: show_objects_in_gate_item = popup.Append(-1, 'Show %s in gate (montage)'%(p.object_name[1])) show_objects_in_gate_item.Enable(selected_gate is not None) self.Bind(wx.EVT_MENU, self.show_objects_from_gate, show_objects_in_gate_item) popup.AppendSeparator() show_images_item = popup.Append(-1, 'Show images in selection') show_images_item.Enable(not self.selection_is_empty()) self.Bind(wx.EVT_MENU, self.show_images_from_selection, show_images_item) if p.object_table: show_objects_item = popup.Append(-1, 'Show %s in selection'%(p.object_name[1])) if self.selection_is_empty(): show_objects_item.Enable(False) self.Bind(wx.EVT_MENU, self.show_objects_from_selection, show_objects_item) show_imagelist_item = popup.Append(-1, 'Show selection in table') if self.selection_is_empty(): show_imagelist_item.Enable(False) self.Bind(wx.EVT_MENU, self.show_selection_in_table, show_imagelist_item) collection_from_selection_item = popup.Append(-1, 'Create collection from selection') if self.selection_is_empty(): collection_from_selection_item.Enable(False) self.Bind(wx.EVT_MENU, self.on_collection_from_selection, collection_from_selection_item) # Color points by filter submenu submenu = wx.Menu() for f in p._filters_ordered: id = wx.NewId() item = submenu.Append(id, f) self.popup_menu_filters[id] = f self.Bind(wx.EVT_MENU, self.on_new_collection_from_filter, item) popup.Append(-1, 'Create collection from filter', submenu) self.PopupMenu(popup, (x,y)) def get_key_lists(self): return self.key_lists def get_colors(self): if self.colors: colors = self.colors elif max(list(map(len, self.x_points)))==0: colors = [] else: # Choose colors from jet colormap starting with light blue (0.28) vals = np.arange(0.28, 1.28, 1. / len(self.x_points)) % 1. colors = [colorConverter.to_rgba(cm.jet(val), alpha=0.75) for val in vals] return colors def set_keys(self, keys): if len(keys) == 0: self.key_lists = None if type(keys) != list: assert len(keys) == len(self.x_points[0]) assert len(self.x_points) == 1 self.key_lists = [keys] else: assert len(keys) == len(self.x_points) for ks, xs in zip(keys, self.x_points): assert len(ks) == len(xs) self.key_lists = keys def set_points(self, xpoints, ypoints): ''' xpoints - an array or a list of arrays containing points ypoints - an array or a list of arrays containing points xpoints and ypoints must be of equal size and shape each array will be interpreted as a separate collection ''' assert len(xpoints) == len(ypoints) if len(xpoints) == 0: self.x_points = [] self.y_points = [] elif type(xpoints[0]) != np.ndarray: self.x_points = [xpoints] self.y_points = [ypoints] else: self.x_points = xpoints self.y_points = ypoints def get_x_points(self): return self.x_points def get_y_points(self): return self.y_points def redraw(self): t0 = time() # XXX: maybe attempt to maintain selection based on keys self.selection = {} self.subplot.clear() # XXX: move to setters? self.subplot.set_xlabel(self.x_label) self.subplot.set_ylabel(self.y_label) xpoints = self.get_x_points() ypoints = self.get_y_points() # Stop if there is no data in any of the point lists if len(xpoints) == 0: logging.warn('No data to plot.') if self.navtoolbar: self.navtoolbar.reset_history() return # Gather all categorical data to be plotted so we can populate # the axis the same regardless of which collections the categories # fall in. xvalmap = {} yvalmap = {} if not issubclass(self.x_points[0].dtype.type, np.number): x_categories = sorted(set(np.hstack(self.x_points))) # Map all categorical values to integer values from 0..N for i, category in enumerate(x_categories): xvalmap[category] = i if not issubclass(self.y_points[0].dtype.type, np.number): y_categories = sorted(set(np.hstack(self.y_points))) # Map all categorical values to integer values from 0..N for i, category in enumerate(y_categories): yvalmap[category] = i # Each point list is converted to a separate point collection by # subplot.scatter self.xys = [] xx = [] yy = [] for c, (xs, ys, color) in enumerate(zip(self.x_points, self.y_points, self.get_colors())): if len(xs) > 0: xx = xs yy = ys # Map categorical values to integers 0..N if xvalmap: xx = [xvalmap[l] for l in xx] # Map categorical values to integers 0..N if yvalmap: yy = [yvalmap[l] for l in yy] data = [Datum(xy, color) for xy in zip(xx, yy)] facecolors = [d.color for d in data] self.xys.append(np.array([(d.x, d.y) for d in data])) self.subplot.scatter(xx, yy, s = 30, facecolors = facecolors, edgecolors = ['none' for f in facecolors], alpha = 0.75) # Set ticks and ticklabels if data is categorical if xvalmap: self.subplot.set_xticks(list(range(len(x_categories)))) self.subplot.set_xticklabels(sorted(x_categories)) self.figure.autofmt_xdate() # rotates and shifts xtick-labels so they look nice if yvalmap: self.subplot.set_yticks(list(range(len(y_categories)))) self.subplot.set_yticklabels(sorted(y_categories)) if len(self.x_points) > 1: if self.legend: self.legend.disconnect_bindings() self.legend = self.subplot.legend(fancybox=True) try: self.legend.draggable(True) except: self.legend = DraggableLegend(self.legend) # Set axis scales if self.x_scale == LOG_SCALE: self.subplot.set_xscale('log', base=2.1) if self.y_scale == LOG_SCALE: self.subplot.set_yscale('log', base=2.1) # Set axis bounds. Clip non-positive values if in log space # Must be done after scatter. xmin = min([np.nanmin(pts[:,0]) for pts in self.xys if len(pts)>0]) xmax = max([np.nanmax(pts[:,0]) for pts in self.xys if len(pts)>0]) ymin = min([np.nanmin(pts[:,1]) for pts in self.xys if len(pts)>0]) ymax = max([np.nanmax(pts[:,1]) for pts in self.xys if len(pts)>0]) if self.x_scale == LOG_SCALE: xmin = min([np.nanmin(pts[:,0][pts[:,0].flatten() > 0]) for pts in self.xys if len(pts)>0]) xmin = xmin / 1.5 xmax = xmax * 1.5 else: xmin = xmin - (xmax - xmin) / 20. xmax = xmax + (xmax - xmin) / 20. if self.y_scale == LOG_SCALE: ymin = min([np.nanmin(pts[:,1][pts[:,1].flatten() > 0]) for pts in self.xys if len(pts)>0]) ymin = ymin / 1.5 ymax = ymax * 1.5 else: ymin = ymin - (ymax - ymin) / 20. ymax = ymax + (ymax - ymin) / 20. self.subplot.axis([xmin, xmax, ymin, ymax]) logging.debug('Scatter: Plotted %s points in %.3f seconds.' %(sum(map(len, self.x_points)), time() - t0)) if self.navtoolbar: self.navtoolbar.reset_history() def get_toolbar(self): if not self.navtoolbar: self.navtoolbar = CustomNavToolbar(self.canvas) self.navtoolbar.Realize() return self.navtoolbar def set_x_scale(self, scale): self.x_scale = scale # Set log axes and print warning if any values will be masked out ignored = 0 if self.x_scale == LOG_SCALE: self.subplot.set_xscale('log', base=2.1, nonpositive='mask') ignored = sum([len(self.x_points[i][xs <= 0]) for i, xs in enumerate(self.x_points) if len(xs) > 0]) if ignored > 0: logging.warn('Scatter masked out %s points with non-positive X values.'%(ignored)) def set_y_scale(self, scale): self.y_scale = scale # Set log axes and print warning if any values will be masked out ignored = 0 if self.y_scale == LOG_SCALE: self.subplot.set_yscale('log', base=2.1, nonpositive='mask') ignored += sum([len(self.y_points[i][ys <= 0]) for i, ys in enumerate(self.y_points) if len(ys) > 0]) if ignored > 0: logging.warn('Scatter masked out %s points with non-positive Y values.'%(ignored)) def set_x_label(self, label): self.x_label = label if self.x_scale == LOG_SCALE: self.x_label = 'Log(%s)'%(self.x_label) def set_y_label(self, label): self.y_label = label if self.y_scale == LOG_SCALE: self.y_label = 'Log(%s)'%(self.y_label)
class PlottingWindow(wx.Window): """ Class for wx window with embedded matplotlib plots """ def __init__(self, *args, **kwargs): """ Initialize plot window parameters. Optional keywords: figsize: size of figure in inches integrate: 0 or 1 of whether the plot should send the integrate pubsub when a right click is activated. smash: 0 or 1 of whether the plot should send the integrate pubsub when a right click is activated. :param args: Arguments :param kwargs: Keywords :return: """ self.displaysize = wx.GetDisplaySize() if "figsize" in kwargs: figsize = kwargs["figsize"] del kwargs["figsize"] else: figsize = (6. * 0.9, 5. * 0.9) if "axes" in kwargs: self._axes = kwargs["axes"] del kwargs["axes"] else: if figsize[0] < 5: self._axes = [0.2, 0.2, 0.7, 0.7] else: self._axes = [0.11, 0.11, 0.8, 0.8] self.figsize = figsize if "integrate" in kwargs: self.int = kwargs["integrate"] del kwargs["integrate"] else: self.int = 0 if "smash" in kwargs: self.smash = kwargs["smash"] del kwargs["smash"] else: self.smash = 0 wx.Window.__init__(self, *args, **kwargs) self.figure = Figure(figsize=figsize) # , dpi= self.subplot1 = None self.zoom = None self.subplot1 = None self.canvas = FigureCanvasWxAgg(self, -1, self.figure) self.Bind(wx.EVT_SIZE, self.size_handler) self.resize = 1 self.flag = False self.kda = False self.kdnorm = 1. self.normalticks = True self.nativez = [] self.text = [] self.lines = [] self.cbar = None self.datalims = None self.cmap = None self.set_color() self.xlabel = "" self.ylabel = "" self.zoomtype = "box" self.tickcolor = "black" self.canvas.mpl_connect('button_release_event', self.on_release) self.canvas.mpl_connect('key_press_event', self.on_key) def on_release(self, event): """ Function triggered on button release event from plot. Currently wired to trigger on_save_figure_dialog on middle button. :param event: wx.Event :return: None """ if event.button == 1: if wx.GetKeyState(wx.WXK_ALT): try: self.zoom.switch_label() except: print("Could not switch on labels") if event.button == 2: if wx.GetKeyState(wx.WXK_CONTROL): dlg = DoubleInputDialog(self) dlg.initialize_interface("Matplotlib RC Parameters", "RC Param Name:", 'lines.markersize', "Value:", "6") dlg.ShowModal() rcname = dlg.value rcval = dlg.value2 print(rcname, rcval) rcParams[rcname] = rcval elif wx.GetKeyState(wx.WXK_ALT): dlg = DoubleInputDialog(self) dlg.initialize_interface("Set Plot X Range", "Min:", '', "Max:", "") dlg.ShowModal() minval = dlg.value maxval = dlg.value2 try: minval = float(minval) maxval = float(maxval) self.zoom.set_manual(minval, maxval) print("Manually Set Zoom:", minval, maxval) except: print("Error converting string to float:", minval, maxval) elif wx.GetKeyState(wx.WXK_SHIFT): dlg = DoubleInputDialog(self) dlg.initialize_interface("Set Plot Y Range", "Min:", '', "Max:", "") dlg.ShowModal() minval = dlg.value maxval = dlg.value2 try: minval = float(minval) maxval = float(maxval) self.zoom.set_manual_y(minval, maxval) print("Manually Set Zoom:", minval, maxval) except: print("Error converting string to float:", minval, maxval) elif wx.GetKeyState(wx.WXK_SPACE): try: self.zoom.switch_label() except: print("Could not switch on labels") else: self.on_save_fig_dialog(event) def on_key(self, evt): # print("you pressed", evt.key) if evt.key == "ctrl+c": self.copy_to_clipboard() def on_save_fig_dialog(self, evt): """ Open a save figure dialog for specified plot. :param evt: wx.Event (unused) :return: None """ path = FileDialogs.save_file_dialog() if path is not None: self.save_figure(path) def on_save_fig(self, evt, path, **kwargs): """ Save figure to path. :param evt: wx.Event (unused) :param path: Path to save figure to :param kwargs: keywords passed to save_figure :return: None """ if path is not None: self.save_figure(path, **kwargs) def save_figure(self, path, **kwargs): """ Saves Figure to path. :param path: Path to save figure at. :param kwargs: Keywords passed to matplotlib.figure.savefig (note only specific ones are passed) :return: None """ if "transparent" in kwargs: t = kwargs["transparent"] else: t = True if "dpi" in kwargs: dpi = kwargs["dpi"] else: dpi = None self.figure.savefig(path, transparent=t, dpi=dpi) print("Saved Figure: ", path) def kda_test(self, xvals): """ Test whether the axis should be normalized to convert mass units from Da to kDa. Will use kDa if: xvals[int(len(xvals) / 2)] > 100000 or xvals[len(xvals) - 1] > 1000000 If kDa is used, self.kda=True and self.kdnorm=1000. Otherwise, self.kda=False and self.kdnorm=1. :param xvals: mass axis :return: None """ try: if xvals[int(len(xvals) / 2)] > 20000 or xvals[len(xvals) - 1] > 150000: self.kdnorm = 1000. self.xlabel = "Mass (kDa)" self.kda = True else: self.xlabel = "Mass (Da)" self.kda = False self.kdnorm = 1. except (TypeError, ValueError): self.xlabel = "Mass (Da)" self.kdnorm = 1. self.kda = False def plotadddot(self, x, y, colval, markval, label=""): """ Adds a scatter plot to the figure. May be one or more. :param x: x values :param y: y values :param colval: Color :param markval: Marker :return: None """ self.subplot1.plot(np.array(x) / self.kdnorm, y, color=colval, marker=markval, linestyle='None', clip_on=True , markeredgecolor="k", label=label) def repaint(self, setupzoom=False): """ Redraw and refresh the plot. :return: None """ if setupzoom: self.setup_zoom([self.subplot1], self.zoomtype) self.canvas.draw() def clear_plot(self, *args): """ Clear the plot and rest some of the parameters. :param args: Arguments :return: """ self.figure.clear() self.flag = False self.nativez = [] self.text = [] self.lines = [] self.kda = False self.kdnorm = 1. if "nopaint" not in args: self.repaint() def set_nticks(self, bins): """ Set the number of ticks in the x-axis. :param bins: Number of ticks in the x-axis :return: None """ if self.normalticks: self.subplot1.tick_params(axis="x", labelsize=12) self.subplot1.tick_params(axis="y", labelsize=12) self.subplot1.xaxis.set_major_locator(MaxNLocator(nbins=bins)) self.repaint() def add_legend(self, location=1, anchor=None): """ Adds a legend to the plot. :param location: Integer code for location :return: None """ handles, labels = self.subplot1.get_legend_handles_labels() if anchor is None: anchor = (1, 1) if location == 1: self.subplot1.legend(handles, labels, loc=location, bbox_to_anchor=anchor) else: self.subplot1.legend(handles, labels, loc=location) self.repaint() def add_title(self, title=""): self.subplot1.set_title(title) self.repaint() def set_color(self, rgbtuple=None): """ Sets background color :param rgbtuple: background color :return: """ # Set figure and canvas colours to be the same if not rgbtuple: rgbtuple = [255., 255., 255.] col = [c / 255.0 for c in rgbtuple] self.figure.set_facecolor(col) self.figure.set_edgecolor(col) # self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) def set_tickcolor(self): """ Sets tick colors based on the colormap set at self.cmap :return: None """ if self.cmap[:2] == "b'": self.cmap = self.cmap[2:-1] try: self.cmap = str(self.cmap, encoding="utf-8") except: pass output = cm.ScalarMappable(norm=None, cmap=str(self.cmap)).to_rgba(0) if sum(output[:2]) > 0.9: self.tickcolor = u"black" else: self.tickcolor = u"white" ''' if self.cmap[-1] == "r": self.tickcolor = "black" else: self.tickcolor = "white" ''' def size_handler(self, *args, **kwargs): """ Resizes the plots :param args: :param kwargs: :return: None """ if self.resize == 1: self.canvas.SetSize(self.GetSize()) def copy_to_clipboard(self, *args, **kwargs): obj = tempfile.NamedTemporaryFile(delete=False) self.canvas.print_figure(obj, format="png", dpi=300) obj.close() img = wx.Image(obj.name) btm = wx.Bitmap(img) bobj = wx.BitmapDataObject(btm) if wx.TheClipboard.Open(): wx.TheClipboard.SetData(bobj) #wx.TheClipboard.SetData(wx.TextDataObject("Test")) wx.TheClipboard.Close() print("Image Copied") os.remove(obj.name) def setup_zoom(self, plots, zoom, data_lims=None, pad=0): """ Set up zoom on axes. :param plots: Axes objects to setup :param zoom: Type of zoom ('span' or 'box') :param data_lims: Optional manual description of the data limits (where to go when fully zoomed out) :return: None """ # setup for zoom box if zoom == 'span': self.zoom = ZoomSpan( plots, None, useblit=True, onmove_callback=None, rectprops=dict(alpha=0.2, facecolor='yellow')) if zoom == 'box': self.zoom = ZoomBox( plots, None, drawtype='box', useblit=True, button=1, # so zoombox is left button onmove_callback=None, spancoords='data', rectprops=dict(alpha=0.2, facecolor='yellow'), data_lims=data_lims, integrate=self.int, smash=self.smash, pad=pad) if zoom == "fixed_span": self.zoom = NoZoomSpan( plots, None, minspan=0, useblit=True, onmove_callback=None, rectprops=dict(alpha=0.2, facecolor='yellow')) '''
class GraphFrame(wx.Frame): def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): global graphFrame_enabled global mplImported global mpl_version self.legendFix = False if not graphFrame_enabled: pyfalog.warning("Matplotlib is not enabled. Skipping initialization.") return try: cache_dir = mpl._get_cachedir() except: cache_dir = os.path.expanduser(os.path.join("~", ".matplotlib")) cache_file = os.path.join(cache_dir, 'fontList.cache') if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): # remove matplotlib font cache, see #234 os.remove(cache_file) if not mplImported: mpl.use('wxagg') graphFrame_enabled = True if int(mpl.__version__[0]) < 1: print("pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds") print("pyfa: Recommended minimum matplotlib version is 1.0.0") self.legendFix = True mplImported = True wx.Frame.__init__(self, parent, title=u"pyfa: Graph Generator", style=style, size=(520, 390)) i = wx.IconFromBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) self.SetIcon(i) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.CreateStatusBar() self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.mainSizer) sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) self.fits = [fit] if fit is not None else [] self.fitList = FitList(self) self.fitList.SetMinSize((270, -1)) self.fitList.fitList.update(self.fits) self.graphSelection = wx.Choice(self, wx.ID_ANY, style=0) self.mainSizer.Add(self.graphSelection, 0, wx.EXPAND) self.figure = Figure(figsize=(4, 3)) rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas = Canvas(self, -1, self.figure) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) self.subplot = self.figure.add_subplot(111) self.subplot.grid(True) self.mainSizer.Add(self.canvas, 1, wx.EXPAND) self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND) self.gridPanel = wx.Panel(self) self.mainSizer.Add(self.gridPanel, 0, wx.EXPAND) dummyBox = wx.BoxSizer(wx.VERTICAL) self.gridPanel.SetSizer(dummyBox) self.gridSizer = wx.FlexGridSizer(0, 4) self.gridSizer.AddGrowableCol(1) dummyBox.Add(self.gridSizer, 0, wx.EXPAND) for view in Graph.views: view = view() self.graphSelection.Append(view.name, view) self.graphSelection.SetSelection(0) self.fields = {} self.select(0) self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) self.mainSizer.Add(self.sl1, 0, wx.EXPAND) self.mainSizer.Add(self.fitList, 0, wx.EXPAND) self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.mainFrame.Bind(GE.FIT_CHANGED, self.draw) self.Bind(wx.EVT_CLOSE, self.close) self.Fit() self.SetMinSize(self.GetSize()) def handleDrag(self, type, fitID): if type == "fit": self.AppendFitToList(fitID) def close(self, event): self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem) self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw) event.Skip() def getView(self): return self.graphSelection.GetClientData(self.graphSelection.GetSelection()) def getValues(self): values = {} for fieldName, field in self.fields.iteritems(): values[fieldName] = field.GetValue() return values def select(self, index): view = self.getView() icons = view.getIcons() labels = view.getLabels() sizer = self.gridSizer self.gridPanel.DestroyChildren() self.fields.clear() # Setup textboxes for field, defaultVal in view.getFields().iteritems(): textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) self.fields[field] = textBox textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) if defaultVal is not None: if not isinstance(defaultVal, basestring): defaultVal = ("%f" % defaultVal).rstrip("0") if defaultVal[-1:] == ".": defaultVal += "0" textBox.ChangeValue(defaultVal) imgLabelSizer = wx.BoxSizer(wx.HORIZONTAL) if icons: icon = icons.get(field) if icon is not None: static = wx.StaticBitmap(self.gridPanel) static.SetBitmap(icon) imgLabelSizer.Add(static, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 1) if labels: label = labels.get(field) label = label if label is not None else field else: label = field imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) sizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) self.draw() def draw(self, event=None): global mpl_version values = self.getValues() view = self.getView() self.subplot.clear() self.subplot.grid(True) legend = [] for fit in self.fits: try: success, status = view.getPoints(fit, values) if not success: # TODO: Add a pwetty statys bar to report errors with self.SetStatusText(status) return x, y = success, status self.subplot.plot(x, y) legend.append(fit.name) except: pyfalog.warning("Invalid values in '{0}'", fit.name) self.SetStatusText("Invalid values in '%s'" % fit.name) self.canvas.draw() return if mpl_version < 2: if self.legendFix and len(legend) > 0: leg = self.subplot.legend(tuple(legend), "upper right", shadow=False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) elif not self.legendFix and len(legend) > 0: leg = self.subplot.legend(tuple(legend), "upper right", shadow=False, frameon=False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) elif mpl_version >= 2: legend2 = [] legend_colors = { 0: "blue", 1: "orange", 2: "green", 3: "red", 4: "purple", 5: "brown", 6: "pink", 7: "grey", } for i, i_name in enumerate(legend): try: selected_color = legend_colors[i] except: selected_color = None legend2.append(Patch(color=selected_color, label=i_name), ) if len(legend2) > 0: leg = self.subplot.legend(handles=legend2) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) self.canvas.draw() self.SetStatusText("") if event is not None: event.Skip() def onFieldChanged(self, event): self.draw() def AppendFitToList(self, fitID): sFit = Fit.getInstance() fit = sFit.getFit(fitID) if fit not in self.fits: self.fits.append(fit) self.fitList.fitList.update(self.fits) self.draw() def removeItem(self, event): row, _ = self.fitList.fitList.HitTest(event.Position) if row != -1: del self.fits[row] self.fitList.fitList.update(self.fits) self.draw()
class PlotPanel(wx.Panel): """Base class for embedding matplotlib in wx. The PlotPanel has a Figure and a Canvas. OnSize events simply set a flag, and the actual resizing of the figure is triggered by an Idle event. See: http://wiki.scipy.org/Matplotlib_figure_in_a_wx_panel """ # TODO: look into this implementation: http://fides.fe.uni-lj.si/pyopus/doc/wxmplplot.html # http://sukhbinder.wordpress.com/2013/12/19/matplotlib-with-wxpython-example-with-panzoom-functionality/ def __init__(self, parent, color=None, dpi=None, **kwargs): # initialize Panel if 'id' not in kwargs.keys(): kwargs['id'] = wx.ID_ANY if 'style' not in kwargs.keys(): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE self.parent = parent wx.Panel.__init__(self, parent, **kwargs) # initialize matplotlib stuff self.figure = Figure(None, dpi, frameon=True, tight_layout=False) self.canvas = FigureCanvasWxAgg(self, -1, self.figure) self.SetColor(color) # Wire up mouse event #self.canvas.mpl_connect('motion_notify_event', self.on_mouse_motion) #self.canvas.Bind(wx.EVT_ENTER_WINDOW, self.ChangeCursor) self._SetSize() self.draw() self._resizeflag = False self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) def SetColor(self, rgbtuple=None): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) def _onSize(self, event): self._resizeflag = True def _onIdle(self, event): if self._resizeflag: self._resizeflag = False self._SetSize() def _SetSize(self): pixels = tuple(self.parent.GetClientSize()) inches = float(pixels[0])/self.figure.get_dpi(), float(pixels[1])/self.figure.get_dpi() self.SetSize(pixels) self.canvas.SetSize(pixels) self.figure.set_size_inches(inches) log.debug('_SetSize DPI: %s pixels: %s inches: %s', self.figure.get_dpi(), pixels, inches) def on_mouse_motion(self, event): pass # abstract, to be overridden by child classes def draw(self): pass # abstract, to be overridden by child classes
class WxScatter(wx.Panel): def __init__(self, *args, **kwargs): self.mapwidget = kwargs.pop('map', None) self.timewidget = kwargs.pop('time', None) wx.Panel.__init__(self, *args, **kwargs) self.id = wx.NewId() self.gpxfig = Figure() self.ax = self.gpxfig.add_subplot(111) self.gpxfig.subplots_adjust(right=0.9, left=0.06) # canvas and events self.gpxcanvas = FigureCanvas(self, -1, self.gpxfig) self.gpxcanvas.mpl_connect('draw_event', self.OnDraw) self.gpxcanvas.mpl_connect('scroll_event', self.OnMouseWheel) self.gpxcanvas.mpl_connect('button_press_event', self.OnLeftMouseDown) self.gpxcanvas.mpl_connect('button_release_event', self.OnLeftMouseUp) self.gpxcanvas.mpl_connect('motion_notify_event', self.OnMouseMotion) self.gpxcanvas.mpl_connect('resize_event', self.OnSize) self.gpxcanvas.mpl_connect('figure_enter_event', self.OnMouseEnter) self.gpxcanvas.mpl_connect('figure_leave_event', self.OnMouseLeave) self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.gpxcanvas, 1, wx.LEFT | wx.TOP | wx.GROW | wx.EXPAND) self.SetSizer(self.sizer) msgwrap.register(self.OnSigCurChanged, "CurChanged") msgwrap.register(self.OnSigSelChanged, "SelChanged") msgwrap.register(self.OnSigValChanged, "ValChanged") #that code does not work on linux... color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) self.gpxfig.set_facecolor( (color.red / 255.0, color.green / 255.0, color.blue / 255.0)) self.gpxfig.set_edgecolor( (color.red / 255.0, color.green / 255.0, color.blue / 255.0)) self.gpxcanvas.SetBackgroundColour(color) self.dragging = False #plugin specific initialization self.xsrc = 'speed' self.ysrc = 'speed' self.autoscale = True self.grid = False self.kwargs={'color':'#0000FF',\ 'marker':'o'} self.grid = False def Plot(self, xrange=None, yrange=None): self.ax.cla() self.ax.scatter(self.gpx[(self.xsrc,1,1)], \ self.gpx[(self.ysrc,1,1)], **self.kwargs) self.ax.set_xlabel(self.xsrc + " (" + self.gpx.get_unit(self.xsrc)[0] + ")") self.ax.set_ylabel(self.ysrc + " (" + self.gpx.get_unit(self.ysrc)[0] + ")") if xrange != None: self.ax.set_xlim(xrange) if yrange != None: self.ax.set_ylim(yrange) self.ax.grid(self.grid) self.gpxcanvas.draw() self.OnSize(None) def AttachGpx(self, data): self.gpx = data self.Plot() self.OnSize(None) def DetachGpx(self): self.gpx = None def OnSigSelChanged(self, arg1, arg2, arg3): if arg1 == self.id: return def OnSigValChanged(self, arg1): if arg1 == self.id: return self.Plot() def OnSigCurChanged(self, arg1, arg2): if arg1 == self.id: return def OnDraw(self, event): pass def OnSize(self, event): pixels = self.GetClientSize() if pixels[0] < 20 or pixels[1] < 20: return self.gpxfig.set_size_inches( float(pixels[0]) / self.gpxfig.get_dpi(), float(pixels[1]) / self.gpxfig.get_dpi()) leg = self.ax.xaxis.get_tightbbox(self.gpxcanvas.get_renderer()) leg1 = self.ax.yaxis.get_tightbbox(self.gpxcanvas.get_renderer()) bottomalign = (leg.height + 15) / pixels[1] leftalign = (leg1.width + 15) / pixels[0] rightalign = (1 - (15.0) / pixels[0]) if (leftalign < rightalign): self.gpxfig.subplots_adjust(bottom=bottomalign, left=leftalign, right=rightalign) def OnLeftMouseDown(self, event): if event.button == 1: if event.dblclick: try: event.guiEvent.GetEventObject().ReleaseMouse() except: pass self.OnLeftMouseDblClick(event) return else: if self.get_axis(event, 25) == 'bottom' or self.get_axis( event, 25) == 'left': self.dragging = True (self.x0, self.y0) = (event.xdata, event.ydata) def OnLeftMouseDblClick(self, event): (dummy,xlo,xhi,ylo,yhi,self.autoscale,self.grid,\ dummy,self.xsrc,self.ysrc,self.kwargs['marker'],self.kwargs['color'],extra)=\ WxQuery("Graph Settings",\ [('wxnotebook','Axes',None,None,None), ('wxentry','X Low',None,self.ax.get_xlim()[0],'float'), ('wxentry','X High',None,self.ax.get_xlim()[1],'float'), ('wxentry','Y Low',None,self.ax.get_ylim()[0],'float'), ('wxentry','Y High',None,self.ax.get_ylim()[1],'float'), ('wxcheck','Autoscale',None,self.autoscale,'bool'), ('wxcheck','Show Grid',None,self.grid,'bool'), ('wxnotebook','Scatter plot',None,None,None), ('wxcombo','X axis',self.XAxisAllowed(),self.xsrc,'str'), ('wxcombo','Y axis',self.XAxisAllowed(),self.ysrc,'str'), ('wxcombo','Symbol','.|o|+|x|^|4|s|*|D',self.kwargs['marker'],'str'), ('wxcolor','Color',None,self.kwargs['color'],'str'), ('wxentry','Extra arguments',None,"{}",'str') ]) self.kwargs.update(ast.literal_eval(extra)) if self.autoscale: self.Plot() else: self.Plot((xlo, xhi), (ylo, yhi)) def get_axis(self, event, tolerance): bbox = self.ax.get_window_extent().transformed( self.gpxfig.dpi_scale_trans.inverted()) l = bbox.bounds[0] * self.gpxfig.dpi b = bbox.bounds[1] * self.gpxfig.dpi r = l + bbox.bounds[2] * self.gpxfig.dpi t = b + bbox.bounds[3] * self.gpxfig.dpi #convert screen coordinates to graph coordinates xlo = self.ax.get_xlim()[0] xhi = self.ax.get_xlim()[1] event.xdata = (event.x - l) / (r - l) * (xhi - xlo) + xlo if ptinrect(l - tolerance, t, l, b, event.x, event.y): ylo, yhi = self.ax.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'left' if ptinrect(r, t, r + tolerance, b, event.x, event.y): ylo, yhi = self.ax.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'right' if ptinrect(l, t, r, t + tolerance, event.x, event.y): ylo, yhi = self.ax.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'top' if ptinrect(l, b - tolerance, r, b, event.x, event.y): ylo, yhi = self.ax.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'bottom' if ptinrect(l, t, r, b, event.x, event.y): ylo, yhi = self.ax.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'main' def OnMouseWheel(self, event): scale_factor = 1.2 if event.button == 'down' else (1.0 / 1.2) where = self.get_axis(event, 25) if where == 'bottom': xlo, xhi = self.ax.get_xlim() nxhi = event.xdata + (scale_factor * (xhi - event.xdata)) nxlo = event.xdata - (scale_factor * (event.xdata - xlo)) self.ax.set_xlim([min(nxlo, nxhi), max(nxlo, nxhi)]) elif where == 'left': ylo, yhi = self.ax.get_ylim() nyhi = event.ydata + (scale_factor * (yhi - event.ydata)) nylo = event.ydata - (scale_factor * (event.ydata - ylo)) self.ax.set_ylim([min(nylo, nyhi), max(nylo, nyhi)]) self.gpxcanvas.draw() def OnLeftMouseUp(self, event): self.dragging = False def OnMouseMotion(self, event): where = self.get_axis(event, 25) if where == 'bottom' or where == 'left': wx.SetCursor(wx.Cursor(wx.CURSOR_MAGNIFIER)) else: wx.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) if where == 'bottom' and self.dragging: dx = event.xdata - self.x0 self.ax.set_xlim(self.ax.get_xlim()[0] - dx, self.ax.get_xlim()[1] - dx) if where == 'left' and self.dragging: dy = event.ydata - self.y0 self.ax.set_ylim(self.ax.get_ylim()[0] - dy, self.ax.get_ylim()[1] - dy) self.gpxcanvas.draw() def OnMouseEnter(self, event): pass def OnMouseLeave(self, event): pass def XAxisAllowed(self): l = '' for name in self.gpx.get_header_names(): if name not in ['time', 'ok'] and name[0] != '_': l += '|' + name return l[1:]
class DpsmapFrame(wx.Frame): def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): global enabled global mplImported self.legendFix = False if not enabled: return try: import matplotlib as mpl if not mplImported: mpl.use('wxagg') from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas from matplotlib.figure import Figure import matplotlib.pyplot as pyplot; enabled = True if mpl.__version__[0] != "1": print "pyfa: Found matplotlib version ",mpl.__version__, " - activating OVER9000 workarounds" print "pyfa: Recommended minimum matplotlib version is 1.0.0" self.legendFix = True except: print "Problems importing matplotlib; continuing without graphs" enabled = False return mplImported = True wx.Frame.__init__(self, parent, title=u"pyfa: DPS map Generator", style=style, size=(520, 390)) i = wx.IconFromBitmap(bitmapLoader.getBitmap("graphs_small", "icons")) self.SetIcon(i) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.CreateStatusBar() self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.mainSizer) sFit = service.Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) self.fits = [fit] if fit is not None else [] self.fitList = FitList(self) self.fitList.SetMinSize((270, -1)) self.fitList.fitList.update(self.fits) self.graphSelection = wx.Choice(self, wx.ID_ANY, style=0) self.mainSizer.Add(self.graphSelection, 0, wx.EXPAND) self.figure = Figure(figsize=(4, 3)) self.colorBar = None; rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor( clr ) self.figure.set_edgecolor( clr ) self.canvas = Canvas(self, -1, self.figure) self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) self.subplot = self.figure.add_subplot(111) self.mainSizer.Add(self.canvas, 1, wx.EXPAND) self.mainSizer.Add(wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0 , wx.EXPAND) self.gridPanel = wx.Panel(self) self.mainSizer.Add(self.gridPanel, 0, wx.EXPAND) dummyBox = wx.BoxSizer(wx.VERTICAL) self.gridPanel.SetSizer(dummyBox) self.gridSizer = wx.FlexGridSizer(0, 4) self.gridSizer.AddGrowableCol(1) dummyBox.Add(self.gridSizer, 0, wx.EXPAND) for view in Graph.views: view = view() self.graphSelection.Append(view.name, view) self.graphSelection.SetSelection(0) self.fields = {} self.select(0) self.sl1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) self.mainSizer.Add(self.sl1,0, wx.EXPAND) self.mainSizer.Add(self.fitList, 0, wx.EXPAND) self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.mainFrame.Bind(GE.FIT_CHANGED, self.draw) self.Bind(wx.EVT_CLOSE, self.close) self.Fit() self.SetMinSize(self.GetSize()) def handleDrag(self, type, fitID): if type == "fit": self.AppendFitToList(fitID) def close(self, event): self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem) self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw) event.Skip() def getView(self): return self.graphSelection.GetClientData(self.graphSelection.GetSelection()) def getValues(self): values = {} for fieldName, field in self.fields.iteritems(): if fieldName == 'signatureRadius': try: tmp = float(field.GetValue()); except: tmp = 0; values[fieldName] = tmp; else: values[fieldName] = field.GetValue() return values def select(self, index): view = self.getView() icons = view.getIcons() labels = view.getLabels() sizer = self.gridSizer self.gridPanel.DestroyChildren() self.fields.clear() #Setup textboxes for field, defaultVal in view.getFields().iteritems(): textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) self.fields[field] = textBox textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) if defaultVal is not None: if not isinstance(defaultVal, basestring): defaultVal = ("%f" % defaultVal).rstrip("0") if defaultVal[-1:] == ".": defaultVal = defaultVal + "0" textBox.ChangeValue(defaultVal) imgLabelSizer = wx.BoxSizer(wx.HORIZONTAL) if icons: icon = icons.get(field) if icon is not None: static = wx.StaticBitmap(self.gridPanel) static.SetBitmap(icon) imgLabelSizer.Add(static, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 1) if labels: label = labels.get(field) label = label if label is not None else field else: label = field imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) sizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) self.draw() def draw(self, event=None): values = self.getValues() view = self.getView() # self.subplot.clear() # self.subplot.grid(True) legend = [] import numpy; print "draw" first = True; for fit in self.fits: # try: signature = values['signatureRadius']; transMax = 500; transStep = 10; distanceMax = 20000; distStep = 200; dmgGridArray = []; if (first): dpsMatrix = view.getDpsMatrix(fit, signature, distanceMax, distStep, transMax, transStep ) else: substractMatrix = view.getDpsMatrix(fit, signature, distanceMax, distStep, transMax, transStep ) newDpsMatrix = []; for row1, row2 in zip(dpsMatrix, substractMatrix): newRow = []; print len(row1); for i in xrange(0, len(row1) - 1): newRow.append(row1[i] - row2[i]); newDpsMatrix.append(newRow); dpsMatrix = newDpsMatrix; first = False; # except: # print "exception"; # self.SetStatusText("Invalid values in '%s'" % fit.name) import matplotlib.pyplot as pyplot; imgraph = self.subplot.imshow(dpsMatrix, aspect=25, extent=[0,transMax,0,distanceMax/1000], interpolation=None, origin='lower', ); if self.colorBar is None: self.colorBar = self.figure.colorbar(imgraph); else: self.colorBar.update_bruteforce(imgraph); legend.append(fit.name) if self.legendFix and len(legend) > 0: leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) elif not self.legendFix and len(legend) >0: leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False, frameon = False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) self.canvas.draw() self.SetStatusText("") if event is not None: event.Skip() def onFieldChanged(self, event): self.draw() def AppendFitToList(self, fitID): sFit = service.Fit.getInstance() fit = sFit.getFit(fitID) if fit not in self.fits: self.fits.append(fit) self.fitList.fitList.update(self.fits) self.draw() def removeItem(self, event): row, _ = self.fitList.fitList.HitTest(event.Position) if row != -1: del self.fits[row] self.fitList.fitList.update(self.fits) self.draw()
class t_C_MatPlot(My_Control_Class): def __init__(self, *args, **kwargs): My_Control_Class.__init__(self, *args, **kwargs) # ************************************************************* # Create the panel with the plot controls # ************************************************************* self.Panel = wx.Panel(self.Dock) self.CB_Grid = wx.CheckBox(self.Panel, -1, "Grid", pos=(0, 5)) self.CP_Grid = wx.ColourPickerCtrl(self.Panel, -1, pos=(40, 1)) wx.StaticText(self.Panel, -1, "BackGround-", pos=(70, 5)) self.CP_BG = wx.ColourPickerCtrl(self.Panel, -1, pos=(130, 1)) self.CB_Axis = wx.CheckBox(self.Panel, -1, "Axis", pos=(160, 5)) self.CB_Legend = wx.CheckBox(self.Panel, -1, "Legend", pos=(210, 5)) self.CB_Polar = wx.CheckBox(self.Panel, -1, "Low Res", pos=(275, 5)) self.CB_FFT = wx.CheckBox(self.Panel, -1, "FFT", pos=(335, 5)) bmp = wx.ArtProvider.GetBitmap(wx.ART_COPY, wx.ART_BUTTON, (16, 16)) self.Button_ClipBoard = wx.BitmapButton(self.Panel, -1, bmp, pos=(375, 0)) self.Button_ClipBoard.SetToolTipString('Copy to ClipBoard') bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_BUTTON, (16, 16)) self.Button_Image = wx.BitmapButton(self.Panel, -1, bmp, pos=(405, 0)) self.Button_Image.SetToolTipString('Save as PNG-image') if self.Test: self.Spin = wx.SpinCtrl(self.Panel, wx.ID_ANY, min=1, max=5, pos=(435, 2), size=(40, 20)) self.Spin.SetToolTipString('Select Demo') # background color of the not used part of the button bar self.Dock.SetBackgroundColour(self.Panel.GetBackgroundColour()) # ************************************************************* # ************************************************************* # ************************************************************* # These parameters must be set before the figure is created # EASIER to set these parameters from rcParams, than from rc, # because you can use deep nested properties #rc ('figure.subplot', 'left' = 0.5) ## not accepted rcParams['figure.subplot.top'] = 0.95 rcParams['figure.subplot.bottom'] = 0.05 rcParams['figure.subplot.left'] = 0.1 rcParams['figure.subplot.right'] = 0.97 self.figure = Figure() self.Canvas = FigureCanvas(self.Dock, -1, self.figure) self.axes = self.figure.add_subplot(111) self.axes_2 = None self.lx = None # ************************************************************* # ************************************************************* # Try to reload the settings, otherwise set defaults # ************************************************************* self.Legends = ('signal 1', 'signal 2') self.SignalsX = [] self.SignalsY = [] self.Pseudo_Color = False if self.Test and self.Ini: print('piep1') self.Ini.Section = 'MatPlot' #line = self.Ini.Read ( 'Pars', '' ) self.Load_Settings(self.Ini) if self.Test: self.MatPlot_Example(self.Spin.GetValue()) else: self.MatPlot_Redraw() # ************************************************************* # ************************************************************* # ************************************************************* Sizer = wx.BoxSizer(wx.VERTICAL) Sizer.Add(self.Canvas, 1, wx.EXPAND) Sizer.Add(self.Panel, 0) self.Dock.SetSizer(Sizer) #Dock.Fit() # We need this for Ubuntu, and even then it works limited self.Dock.Bind(wx.EVT_SIZE, self._OnSize) # ************************************************************* # ************************************************************* # ************************************************************* self.Dock.Bind(wx.EVT_COLOURPICKER_CHANGED, self.MatPlot_OnPolar, self.CP_Grid) self.Dock.Bind(wx.EVT_COLOURPICKER_CHANGED, self.MatPlot_OnSet_CBs, self.CP_BG) self.Dock.Bind(wx.EVT_CHECKBOX, self.MatPlot_OnSet_CBs, self.CB_Grid) self.Dock.Bind(wx.EVT_CHECKBOX, self.MatPlot_OnSet_CBs, self.CB_Axis) self.Dock.Bind(wx.EVT_CHECKBOX, self.MatPlot_OnSet_CBs, self.CB_Legend) self.Dock.Bind(wx.EVT_CHECKBOX, self.MatPlot_OnPolar, self.CB_Polar) self.Dock.Bind(wx.EVT_CHECKBOX, self.MatPlot_OnFFT, self.CB_FFT) self.Button_Image.Bind(wx.EVT_BUTTON, self.MatPlot_OnSaveImage, self.Button_Image) self.Button_ClipBoard.Bind(wx.EVT_BUTTON, self.MatPlot_OnCopyClipBoard, self.Button_ClipBoard) if self.Test: self.Spin.Bind(wx.EVT_SPINCTRL, self.MatPlot_OnSpinEvent, self.Spin) self.Dock.Bind(wx.EVT_CLOSE, self.MatPlot_OnClose) #if not ( self.connect ) : self.Dock.connect = self.Canvas.mpl_connect('motion_notify_event', self.MatPlot_OnMotion) # ************************************************************* # ************************************************************* # We need this for Ubuntu, and even then it works limited # ************************************************************* def _OnSize(self, event): event.Skip() wx.CallAfter(self.MatPlot_Redraw) # ************************************************************* # ************************************************************* def Calculate(self): # input might be one of the following: # 1: <array> # 2: list = [ <array>, [ <signal name>, <signal color>, <linewidth> ] ] # # array might be one of the following: # A: a 1-dimensional array ==> y(x), x=equidistant # B: a 2-dimensional array, with second dimension = 2 ==> y = f(x) # C: a 2-dimensional array, with second dimension >> 6 ==> z = f(x,y) # we can either use signal, which is the input channel that has changed # or use the Bricks input channels, so we can use all of them at once self.SignalsX = [] self.SignalsY = [] self.Legends = [] # for test purposes, we don't have a brick but Test_Inputs try: Inputs = self.Brick.In except: Inputs = self.Test_Inputs for s, IVV in enumerate(Inputs[1:]): if IVV != None: #print 'MatPlot',s,len(IVV) if not ( operator.isSequenceType ( IVV ) ) or \ isinstance ( IVV, ndarray ): IVV = eval('[IVV]') #print 'no sequence' # determine the number of arrays # if exactly 2, special case: x,y pairs NA = 0 for IV in IVV: if isinstance(IV, ndarray): NA += 1 else: break #print 'NA',NA,IVV[0].shape # process all signals self.Pseudo_Color = False if IVV[0].ndim == 2: self.Pseudo_Color = True self.SignalsX.append(IVV[0]) elif NA == 1: L = len(IVV[0]) self.SignalsX.append(linspace(0, L - 1, L)) self.SignalsY.append(IVV[0]) elif NA == 2: self.SignalsX.append(IVV[0]) self.SignalsY.append(IVV[1]) else: self.SignalsX.append(IVV[0]) for i, IV in enumerate(IVV[1:NA]): self.SignalsY.append(IV) # add legends if NA == 1: if (len(IVV) > NA) and (len(IVV[NA]) > 0): self.Legends.append(IVV[NA][0]) else: self.Legends.append('Signal 1') for i, IV in enumerate(IVV[1:NA]): if (len(IVV) > NA) and (len(IVV[NA]) > i): self.Legends.append(IVV[NA][i]) else: self.Legends.append('Signal ' + str(i + 1)) #print 'Legends',self.Legends self.MatPlot_ReCreate_Plot() #print 'MatPlot recreated' # ************************************************************* # ************************************************************* def MatPlot_ReCreate_Plot(self): # BUG, "hold" doesn't work in polar, therefore create a new figure # helps sometimes .... for polar plot ## LUKT NIET MEER rcParams [ 'grid.color' ] = self.Color_2_MatPlot ( self.CP_Grid.GetColour () ) self.figure.clear() self.lx = None if not (self.Pseudo_Color): self.CB_Polar.SetLabel('-Polar') if self.CB_FFT.GetValue(): config = 212 else: config = 111 self.axes = self.figure.add_subplot(config, polar=self.CB_Polar.GetValue()) for i, SX in enumerate(self.SignalsY): self.axes.plot( self.SignalsX[0], self.SignalsY[i], ) if self.CB_FFT.GetValue(): self.axes_2 = self.figure.add_subplot( 211, polar=self.CB_Polar.GetValue()) for i, SX in enumerate(self.SignalsY): self.axes_2.psd( self.SignalsY[i], 512, 1, #detrend = mlab.detrend_linear, #doesn't work #detrend = mlab.detrend_mean, #doesn't work detrend=mlab.detrend_none, #window = mlab.window_none ) #weird scaling window=mlab.window_hanning) # doesn't work: # self.axes_2.xlabel = 'aap' # self.axes_2.axis ( 0, 0, 50, -50) else: self.axes_2 = None else: # Pseudo color self.CB_FFT.Hide() self.CB_Polar.SetLabel('-Low Res') self.axes = self.figure.add_subplot(111) if self.CB_Polar.GetValue(): cmap = cm.get_cmap('jet', 10) # 10 discrete colors else: cmap = cm.jet #cmap = cm.gray # IMPORTANT, setting the size explictly, # prevents rescaling which sometimes occurs when crosshair is drawn s = self.SignalsX[0].shape try: # SignalsX might not be available yet im = self.axes.imshow(self.SignalsX[0], cmap=cmap, extent=(0, s[0], 0, s[1])) #im.set_interpolation ( 'nearest' ) # and there are a lot more !! #im.set_interpolation ( 'bicubic' ) im.set_interpolation('bilinear') self.figure.colorbar(im) except: pass self.axes.hold(True) # needed for measurement cursor if self.axes_2: self.axes_2.hold(True) self.MatPlot_Redraw() # ************************************************************* # ************************************************************* def MatPlot_Redraw(self): color = self.CP_BG.GetColour() color = self.Color_2_MatPlot(color) self.axes.set_axis_bgcolor(color) if self.axes_2: self.axes_2.set_axis_bgcolor(color) self.figure.set_facecolor(color) self.figure.set_edgecolor(color) if self.CB_Axis.GetValue(): self.axes.set_axis_on() if self.axes_2: self.axes_2.set_axis_on() else: self.axes.set_axis_off() if self.axes_2: self.axes_2.set_axis_off() color = self.CP_Grid.GetColour() color = self.Color_2_MatPlot(color) self.axes.grid(self.CB_Grid.GetValue()) if self.axes_2: self.axes_2.grid(self.CB_Grid.GetValue()) # setting the grid color sometimes generates an exception # this seems to happen completely random ???? try: if self.CB_Grid.GetValue(): self.axes.grid(color=color) if self.axes_2: self.axes_2.grid(color=color) except: pass # Polar doesn't support legend (use figlegend) if self.Pseudo_Color or not (self.CB_Polar.GetValue()): if self.CB_Legend.GetValue(): self.axes.legend(self.Legends) else: self.axes.legend_ = None # FFT plot: no legend if self.axes_2: self.axes_2.legend_ = None self.Canvas.draw() # ************************************************************* # create an example image # ************************************************************* def MatPlot_Example(self, Example): self.Test_Inputs = [None] if Example == 1: # Sine x = arange(0.0, 3.0, 0.01) y = sin(2 * pi * x) elif Example == 2: # SPIRAL t = arange(0, 10 * pi, 0.1) x = t * sin(t) y = t * cos(t) elif Example == 3: # CARDIOID t = arange(0, 2 * pi, 0.1) x = (1 + cos(t)) * cos(t) y = (1 + cos(t)) * sin(t) elif Example == 4: # SPIROGRAPH phi = linspace(0, 4, 100) #r=sin(phi*pi) # r = sin(cos(tan(phi))) x = phi y = 20 * r elif Example == 5: # Pseudo Color def _func(x, y): return (1 - x / 2 + x**5 + y**3) * exp(-x**2 - y**2) dx, dy = 0.05, 0.05 x = arange(-3.0, 3.0, dx) y = arange(-3.0, 3.0, dy) X, Y = meshgrid(x, y) self.Test_Inputs.append(_func(X, Y)) if len(self.Test_Inputs) == 1: temp = [] temp.append(x) temp.append(y) self.Test_Inputs.append(temp) self.Calculate() # ************************************************************* # ************************************************************* def MatPlot_OnMotion(self, event): if self.CB_Polar.GetValue() and not (self.Pseudo_Color): return x, y = event.x, event.y # check if within an "axes" but not in a "bar-axes" if event.inaxes and isinstance(event.inaxes, matplotlib.axes.Subplot): x, y = event.xdata, event.ydata if not (self.lx): self.minx, self.maxx = event.inaxes.get_xlim() self.miny, self.maxy = event.inaxes.get_ylim() """ self.lx, = self.axes.plot ( ( self.minx, self.maxx ), ( y, y ), 'k-' ) # the horiz line self.ly, = self.axes.plot ( ( x, x ), ( self.miny, self.maxy ), 'k-' ) # the vert line self.meas_txt = self.axes.text ( 0.02, 0.02, 'x=%1.2f, y=%1.2f'% ( x, y ), transform = self.axes.transAxes ) """ self.lx, = event.inaxes.plot((self.minx, self.maxx), (y, y), 'k-') # the horiz line self.ly, = event.inaxes.plot((x, x), (self.miny, self.maxy), 'k-') # the vert line self.meas_txt = event.inaxes.text( 0.02, 0.02, 'x=%1.2f, y=%1.2f' % (x, y), transform=event.inaxes.transAxes) else: # update the crosshair positions self.lx.set_data((self.minx, self.maxx), (y, y)) self.ly.set_data((x, x), (self.miny, self.maxy)) self.meas_txt.set_text('x=%1.2f, y=%1.2f' % (x, y)) else: # Hide the cross hair if self.lx: self.lx.set_data((x, x), (y, y)) self.ly.set_data((x, x), (y, y)) self.meas_txt.set_text('') self.lx = None self.Canvas.draw() # ************************************************************* # ************************************************************* def MatPlot_OnSet_CBs(self, event): self.MatPlot_Redraw() # ************************************************************* # ************************************************************* def MatPlot_OnPolar(self, event): if self.CB_Polar.GetValue(): self.CB_FFT.Hide() else: self.CB_FFT.Show() self.MatPlot_ReCreate_Plot() # ************************************************************* # ************************************************************* def MatPlot_OnFFT(self, event): if self.CB_FFT.GetValue(): self.CB_Polar.Hide() else: self.CB_Polar.Show() self.MatPlot_ReCreate_Plot() # ************************************************************* # ************************************************************* def MatPlot_OnSpinEvent(self, event): Example = event.GetInt() self.MatPlot_Example(Example) # ************************************************************* # ************************************************************* def MatPlot_OnCopyClipBoard(self, event): #canvas = FigureCanvasWxAgg(...) self.Canvas.Copy_to_Clipboard(event=event) # ************************************************************* # ************************************************************* def MatPlot_OnSaveImage(self, event): file = Ask_File_For_Save(os.getcwd(), FileTypes='*.png', Title='Save Plot as PNG-image') if file: self.figure.savefig(file) # ************************************************************* # Set Grid color of all backgrounds # Doing this gives huges problems, # therefor we accept that in the polar # ************************************************************* """ def OnGridColor ( self, event ) : rcParams [ 'grid.color' ] = self.Color_2_MatPlot ( self.CP_Grid.GetColour () ) # Because we need to reload the resources, # we force it by recreating to the total plot self.Sizer.Remove ( self.Canvas ) self.figure = Figure () self.Canvas = FigureCanvas ( self.Dock, -1, self.figure ) self.lx = None self.Sizer.Prepend ( self.Canvas, 1, wx.EXPAND ) self.Sizer.Layout () self.MatPlot_Example ( self.Spin.GetValue () ) """ # ************************************************************* # MatPlot accepts RGB colors in relative range 0..1, # alpha blend is also not accepted # ************************************************************* def Color_2_MatPlot(self, color): # if already in MatPlot format, just return the same value if isinstance(color, float) or isinstance(color[0], float): return color # else limit to 3 elements in the range 0.0 ... 1.0 kleur = [] for c in color[:3]: kleur.append(c / 255.0) return kleur # ************************************************************* # MatPlot accepts RGB colors in relative range 0..1, # alpha blend is also not accepted # ************************************************************* def MatPlot_2_Color(self, color): # if already in normal format, just return the same value if isinstance(color, wx.Color): return color if isinstance(color, basestring): try: color = float(color) except: return color # named color probably if isinstance(color, float): i = int(color * 255) kleur = [i, i, i] else: kleur = [] if isinstance(color[0], float): for c in color[:3]: kleur.append(int(255 * c)) else: kleur = color[:3] kleur = wx.Color(*kleur) return kleur # ************************************************************* # ************************************************************* def MatPlot_OnClose(self, event): if self.Ini and self.Test: self.Ini.Section = 'MatPlot' self.Ini.Write('Pos', self.Dock.GetPosition()) self.Ini.Write('Size', self.Dock.GetSize()) self.Save_Settings(self.Ini) event.Skip() # ************************************************************* # ************************************************************* def Save_Settings(self, ini, key=None): if ini: line = [] line.append(tuple(self.CP_BG.GetColour())) line.append(tuple(self.CP_Grid.GetColour())) line.append(self.CB_Grid.GetValue()) line.append(self.CB_Axis.GetValue()) line.append(self.CB_Legend.GetValue()) line.append(self.CB_Polar.GetValue()) line.append(self.CB_FFT.GetValue()) if self.Test: line.append(self.Spin.GetValue()) if not (key): key = 'CS_' v3print('MatPlot SAVE', key, '=', line) line = ini.Write(key, line) # ************************************************************* # ************************************************************* def Load_Settings(self, ini, key=None): #print 'llkwp',line if not (key): key = 'CS_' line = ini.Read(key, '') if line: self.CP_BG.SetColour(line[0]) self.CP_Grid.SetColour(line[1]) self.CB_Grid.SetValue(line[2]) self.CB_Axis.SetValue(line[3]) self.CB_Legend.SetValue(line[4]) self.CB_Polar.SetValue(line[5]) self.CB_FFT.SetValue(line[6]) if self.Test: self.Spin.SetValue(line[7]) self.MatPlot_ReCreate_Plot()
class PlotWidget(custom_result.ResultWidget): __gsignals__ = { 'button-press-event': 'override', 'button-release-event': 'override', 'expose-event': 'override', 'size-allocate': 'override', 'unrealize': 'override' } def __init__(self, result): custom_result.ResultWidget.__init__(self) figsize = (DEFAULT_FIGURE_WIDTH, DEFAULT_FIGURE_HEIGHT) self.figure = Figure(facecolor='white', figsize=figsize) self.canvas = _PlotResultCanvas(self.figure) self.axes = self.figure.add_subplot(111) self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE) self.cached_contents = None self.sidebar_width = -1 def do_expose_event(self, event): cr = self.window.cairo_create() if not self.cached_contents: self.cached_contents = cr.get_target().create_similar( cairo.CONTENT_COLOR, self.allocation.width, self.allocation.height) renderer = RendererCairo(self.figure.dpi) renderer.set_width_height(self.allocation.width, self.allocation.height) renderer.set_ctx_from_surface(self.cached_contents) self.figure.draw(renderer) # event.region is not bound: http://bugzilla.gnome.org/show_bug.cgi?id=487158 # gdk_context = gtk.gdk.CairoContext(renderer.ctx) # gdk_context.region(event.region) # gdk_context.clip() cr.set_source_surface(self.cached_contents, 0, 0) cr.paint() def do_size_allocate(self, allocation): if allocation.width != self.allocation.width or allocation.height != self.allocation.height: self.cached_contents = None gtk.DrawingArea.do_size_allocate(self, allocation) def do_unrealize(self): gtk.DrawingArea.do_unrealize(self) self.cached_contents = None def do_button_press_event(self, event): if event.button == 3: custom_result.show_menu(self, event, save_callback=self.__save) return True else: return True def do_button_release_event(self, event): return True def do_realize(self): gtk.DrawingArea.do_realize(self) cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR) self.window.set_cursor(cursor) def do_size_request(self, requisition): try: # matplotlib < 0.98 requisition.width = self.figure.bbox.width() requisition.height = self.figure.bbox.height() except TypeError: # matplotlib >= 0.98 requisition.width = self.figure.bbox.width requisition.height = self.figure.bbox.height def recompute_figure_size(self): width = (self.sidebar_width / self.figure.dpi) height = width / DEFAULT_ASPECT_RATIO self.figure.set_figwidth(width) self.figure.set_figheight(height) self.queue_resize() def sync_dpi(self, dpi): self.figure.set_dpi(dpi) if self.sidebar_width >= 0: self.recompute_figure_size() def set_sidebar_width(self, width): if self.sidebar_width == width: return self.sidebar_width = width if self.sidebar_width >= 0: self.recompute_figure_size() def sync_style(self, style): self.cached_contents = None matplotlib.rcParams[ 'font.size'] = self.parent.style.font_desc.get_size() / pango.SCALE def __save(self, filename): # The save/restore here was added to matplotlib's after 0.90. We duplicate # it for compatibility with older versions. (The code would need modification # for 0.98 and newer, which is the reason for the particular version in the # check) version = [int(x) for x in matplotlib.__version__.split('.')] need_save = version[:2] < [0, 98] if need_save: orig_dpi = self.figure.dpi.get() orig_facecolor = self.figure.get_facecolor() orig_edgecolor = self.figure.get_edgecolor() try: self.canvas.print_figure(filename) finally: if need_save: self.figure.dpi.set(orig_dpi) self.figure.set_facecolor(orig_facecolor) self.figure.set_edgecolor(orig_edgecolor) self.figure.set_canvas(self.canvas)
class PlotPanel(wx.Panel): '''A wx panel with a matplotlib canvas. The PlotPanel extends the wx.Panel base class and has a matplotlib Figure and Canvas to draw/plot interesting things on the panel. OnSize events simply set a flag, and the actual resizing of the figure is triggered by an Idle event.''' def __init__(self, parent, color=None, dpi=None, **kwargs): '''Initialisation of the PlotPanel instance.''' from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas ##from matplotlib.backends.backend_agg import NavigationToolbar2Agg as NavigationToolbar # from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas # from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar # from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas from matplotlib.figure import Figure # initialize Panel if 'id' not in kwargs.keys(): kwargs['id'] = wx.ID_ANY if 'style' not in kwargs.keys(): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__(self, parent, **kwargs) # initialize matplotlib stuff dpi = 72 print 'dpi=', dpi self.figure = Figure((2.0, 2.0), dpi) self.canvas = FigureCanvas(self) ##, -1, self.figure ) self.SetColor(color) ##self.toolbar = NavigationToolbar(self.canvas) self.vbox = wx.BoxSizer(wx.VERTICAL) self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) ##self.vbox.Add(self.toolbar, 0, wx.EXPAND) self.SetSizer(self.vbox) self.vbox.Fit(self) # self._SetSize() self.draw() # self._resizeflag = False # self.Bind(wx.EVT_IDLE, self._onIdle) # self.Bind(wx.EVT_SIZE, self._onSize) def SetColor(self, rgbtuple=None): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) def _onSize(self, event): self._resizeflag = True def _onIdle(self, evt): if self._resizeflag: self._resizeflag = False self._SetSize() def _SetSize(self): pixels = tuple(self.parent.GetClientSize()) self.SetSize(pixels) self.canvas.SetSize(pixels) self.figure.set_size_inches(float(pixels[0]) / self.figure.get_dpi(), float(pixels[1]) / self.figure.get_dpi()) def draw(self): pass # abstract, to be overridden by child classes
class PlotPanel(wx.Panel): """The PlotPanel has a Figure and a Canvas""" def __init__(self, parent, color=(255, 255, 255), dpi=None, **kwargs): # initialize Panel if 'id' not in kwargs.keys(): kwargs['id'] = wx.ID_ANY if 'style' not in kwargs.keys(): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__(self, parent, **kwargs) # subplotparams = SubplotParams(0.02, 0.02, 0.98, 0.98, 0.1, 0.1) # initialize matplotlib stuff self.figure = Figure((2.1, 2.97), dpi) self.canvas = FigureCanvasWxAgg(self, -1, self.figure) self.SetColor(color) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, 1, wx.EXPAND | wx.SHAPED, 0) self.SetSizer(sizer) self.Bind(wx.EVT_SIZE, self.set_size) self.draw() self.Refresh() def SetColor(self, rgbtuple=None): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) def set_size(self, evt=None): if self.ClientSize[0] > 0 and self.ClientSize[1] > 0: self.canvas.SetSize(self.ClientSize) self.canvas.draw() def draw(self): raise NoImplementedError # abstract, to be overridden by child classes def image_grid(self, num_rows, num_cols): return ImageGrid(self.figure, 111, share_all=True, nrows_ncols=(num_rows, num_cols), cbar_size="3%", cbar_pad=0.02, cbar_mode='single') def get_norm(self, vmin, vmax): return matplotlib.colors.normalize(vmax=vmax, vmin=vmin) def save_to_pdf(self, pdfpages): old_fig = self.figure self.figure = Figure((8.5, 11), dpi=300) canvas = matplotlib.backends.backend_pdf.FigureCanvasPdf(self.figure) self.draw() pdfpages.savefig(self.figure) self.figure = old_fig def align_subplots(self): xmin = matplotlib.numpy.inf xmax = -matplotlib.numpy.inf for subplot in self.figure.get_axes(): xmin = min(xmin, subplot.get_xlim()[0]) xmax = max(xmax, subplot.get_xlim()[1]) for subplot in self.figure.get_axes(): subplot.set_xlim(xmin, xmax)
class wxMatplotPanel(scrolled.ScrolledPanel): """ The PlotPanel has a Figure and a Canvas. OnSize events simply set a flag, and the actually redrawing of the figure is triggered by an Idle event. """ def __init__(self, renderPanel, color=None, dpi=None, **kwargs): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure # initialize Panel if 'id' not in list(kwargs.keys()): kwargs['id'] = wx.ID_ANY if 'style' not in list(kwargs.keys()): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE scrolled.ScrolledPanel.__init__(self, renderPanel, **kwargs) self.renderPanel = renderPanel # initialize matplotlib stuff self.figure = Figure(None, dpi) #self.canvas = NoRepaintCanvas( self, -1, self.figure ) self.canvas = FigureCanvasWxAgg(self, -1, self.figure) self.canvas.mpl_connect('button_press_event', self.onMousePress) self.canvas.mpl_connect('pick_event', self.onPick) sizer = wx.BoxSizer() sizer.Add(self.canvas, 1, wx.EXPAND) self.SetSizer(sizer) # self.SetAutoLayout(1) # self.SetupScrolling() self.SetColor(color) self._refresh = False self._updateDraw = False self.toolBar_ = None self.canvasZoomWidth = 1.0 self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) self.resfreshCounter = 0 self.needUpdateHack_ = False self.needDrawing = False self.refresh() def onPick(self, event): pass def onMousePress(self, event): pass def onZoomChanged(self): pass def onPanChanged(self): pass def getToolBar(self, parent=None): if not self.toolBar_: self.toolBar_ = wxAUIMatplotPanelToolbar(self, self.canvas, parent) return self.toolBar_ def SetColor(self, rgbtuple=None): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) def _onSize(self, event): self._refresh = True def _onIdle(self, evt): if self.IsShownOnScreen(): if self.needDrawing: self.redraw() if self._refresh: self.refresh() self._refresh = False if self._updateDraw: swatch = Stopwatch(True) self.canvas.draw() if self.needUpdateHack_: self.needUpdateHack() self.needUpdateHack_ = False self._updateDraw = False if self.canvasZoomWidth == 1.0: self.SetupScrolling(False, False) print("draw: ", swatch.duration()) def updateDrawOnIdle(self): self._updateDraw = True def resizeOnIdle(self): self._refresh = True def refresh(self): swatch = Stopwatch(True) #pixels = tuple( self.GetParent().GetClientSize() ) self.resfreshCounter += 1 pixels = tuple([ int(self.GetSize()[0] * self.canvasZoomWidth), int(self.GetSize()[1] * 1.0) ]) # print self, self.resfreshCounter, pixels if self.canvas.GetMinSize( )[0] != pixels[0] \ or self.canvas.GetMinSize()[1] != pixels[1]: # print "resize canvas" # print "parent-size", self.renderPanel.GetSize() # print "self-size", self.GetSize() # print "tupel-size", pixels # to avoid _onSize loop under linux # if self.GetSize() != self.parent.GetClientSize(): # if self.GetSize() != pixels: #self.SetSize( pixels ) #self.canvas.SetSize( pixels ) self.canvas.SetMinSize(pixels) self.figure.set_size_inches( float(pixels[0]) / self.figure.get_dpi(), float(pixels[1]) / self.figure.get_dpi()) adjust = True if hasattr(self, 'cbar'): if self.cbar.active: adjust = False if pixels[0] > 50 and adjust: self.figure.subplotpars.update(left=50.0 / pixels[0], right=(pixels[0] - 20.0) / pixels[0]) for a in self.figure.axes: if hasattr(a, "update_params"): a.update_params() #a.set_position( a.figbox, which = 'original' ) a.set_position(a.figbox, which='both') #self.figure.subplots_adjust( left = 50.0 / pixels[ 0 ] ) #self.figure.subplots_adjust( right = ( pixels[ 0 ] - 20.0 ) / pixels[ 0 ] ) # self.canvas.draw() self.updateDrawOnIdle() self.needUpdateHack_ = True # print "refresh: ", swatch.duration() def draw(self): pass # abstract, to be overridden by child classes def needUpdateHack(self): pass
def plot_stdleads_matrix(rhythm_data: pd.DataFrame, anns_df: pd.DataFrame = None, fig: figure.Figure = None, dpi: int = 300, textsize: int = 6, ecg_linewidth: float = 0.6, plot_grid: bool = True, grid_color: str = "#a88332", v_offset: float = 1.5, xmin: float = 0.0, xmax: float = 10000.0, ymin: float = -1.5, ymax: float = 1.5, x_margin: float = 280, for_gui: bool = True) -> figure.Figure: """Plots the waveform and annotations in a 3x4 + lead II layout Args: rhythm_data (pd.DataFrame): aECG waveform as returned by :any:`Aecg.rhythm_as_df` or :any:`Aecg.derived_as_df`. anns_df (pd.DataFrame, optional): aECG annotations. For example, as returned by pd.DataFrame(the_aecg.DERIVEDANNS[0].anns) where the_aecg is an :any:`Aecg` object. Defaults to None. fig (figure.Figure, optional): Figure containing the plot. Defaults to None. dpi (int, optional): Plot resolution in dots per inch (dpi). Defaults to 300. textsize (int, optional): Default text fontsize. Defaults to 6. ecg_linewidth (float, optional): Line width for the ECG waveform. Defaults to 0.3. plot_grid (bool, optional): Indicates whether to plot the standard ECG grid. Defaults to True. grid_color (str, optional): Color of the ECG grid. Defaults to "#a88332". v_offset (float, optional): Vertical offset between leads in mV. Defaults to 1.5. xmin (float, optional): X axis minimum value in ms. Defaults to 0.0. xmax (float, optional): X axis maximum value in ms. This value may be adjusted automatically when maintaining aspect ratio. Defaults to 10000.0. ymin (float, optional): Y axis minimum value in mV. Defaults to -1.5. ymax (float, optional): Y axis maximum value in mV. This value may be adjusted automatically when maintaining aspect ratio. Defaults to 1.5. x_margin (float, optional): Margin on the X axis in ms. Defaults to 280. for_gui (bool, optional): Indicates whether to plot is generated for a graphical user interface. If true, the figure will be closed before returning the object so a canvas will be needed to render it . Otherwise, the figure will be return immediately. Defaults to True Returns: figure.Figure: Plot of the aECG waveforms and its annotations """ # Add offsets to the leads to match desired 3x4 layout h_offset = 2500 column_padding = 50 # Check if standard leads are present and, if not, populate with np.nan for lead in [ "I", "II", "III", "aVR", "aVL", "aVF", "V1", "V2", "V3", "V4", "V5", "V6" ]: if lead not in rhythm_data.columns: rhythm_data[lead] = np.nan beat_plot_col1 = rhythm_data[rhythm_data.TIME < (h_offset - column_padding)][[ "TIME", "I", "II", "III" ]].copy() beat_plot_col2 = rhythm_data[(rhythm_data.TIME >= h_offset) & ( rhythm_data.TIME < (2 * h_offset - column_padding))][[ "TIME", "aVR", "aVF", "aVL" ]].copy() beat_plot_col3 = rhythm_data[(rhythm_data.TIME >= (2 * h_offset)) & ( rhythm_data.TIME < (3 * h_offset - column_padding))][[ "TIME", "V1", "V2", "V3" ]].copy() beat_plot_col4 = rhythm_data[(rhythm_data.TIME >= (3 * h_offset)) & ( rhythm_data.TIME < (4 * h_offset - column_padding))][[ "TIME", "V4", "V5", "V6" ]].copy() beat_plot = pd.concat( [beat_plot_col1, beat_plot_col2, beat_plot_col3, beat_plot_col4]) anns_matrix = None anns_matrix_col1 = None anns_matrix_col2 = None anns_matrix_col3 = None anns_matrix_col4 = None if anns_df is not None: if anns_df.shape[0] > 0: anns_matrix_col1 = anns_df[anns_df.TIME < (h_offset - column_padding)].copy() anns_matrix_col2 = anns_df[(anns_df.TIME >= h_offset) & ( anns_df.TIME < (2 * h_offset - column_padding))].copy() anns_matrix_col3 = anns_df[(anns_df.TIME >= (2 * h_offset)) & ( anns_df.TIME < (3 * h_offset - column_padding))].copy() anns_matrix_col4 = anns_df[(anns_df.TIME >= (3 * h_offset)) & ( anns_df.TIME < (4 * h_offset - column_padding))].copy() anns_matrix = pd.concat([ anns_matrix_col1, anns_matrix_col2, anns_matrix_col3, anns_matrix_col4 ]) # Compute maximum height range based on number of leads ecg_ymin = min(ymin, -4 * v_offset) ecg_ymax = max(v_offset, ymax) # Compute image size ecg_width = (xmax - xmin + x_margin) / 40.0 # mm (25 mm/s -> 1 mm x 0.04s) # mm ( 10 mm/mV -> 1 mm x 0.1 mV) ecg_height = (ecg_ymax - ecg_ymin) * 10.0 ecg_w_in = ecg_width / 25.4 # inches ecg_h_in = ecg_height / 25.4 # inches # Figure size if fig is None: fig = plt.figure(dpi=dpi) else: fig.clear() fig.set_size_inches(ecg_w_in, ecg_h_in) fig.set_dpi(dpi) fig.set_facecolor('w') fig.set_edgecolor('k') ax1 = fig.add_axes([0, 0, 1, 1], frameon=False) # ecg grid if plot_grid: grid_major_x = np.arange(0, xmax + x_margin, 200) grid_minor_x = np.arange(0, xmax + x_margin, 40) for xc in grid_major_x: ax1.axvline(x=xc, color=grid_color, linewidth=0.5) for xc in grid_minor_x: ax1.axvline(x=xc, color=grid_color, linewidth=0.2) grid_major_y = np.arange(-4 * v_offset, v_offset, 0.5) grid_minor_y = np.arange(-4 * v_offset, v_offset, 0.1) for yc in grid_major_y: ax1.axhline(y=yc, color=grid_color, linewidth=0.5) for yc in grid_minor_y: ax1.axhline(y=yc, color=grid_color, linewidth=0.2) ecglibann_voffset = { "RPEAK": 1.0, "PON": 0.7, "QON": 0.4, "QOFF": 0.7, "TOFF": 0.4 } # First column # ecg calibration pulse lead_zero = 0.0 ax1.plot([40, 80, 80, 280, 280, 320], [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead I tmp = plt.text(x_margin + 80, 0.55, 'I', size=textsize) if "I" in rhythm_data.columns: ax1.plot(beat_plot.TIME[beat_plot.I.notna()] + x_margin, beat_plot.I[beat_plot.I.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.I.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.I.notna()].values[-1] + x_margin if anns_matrix_col1 is not None: for idx, ann in anns_matrix_col1[anns_matrix_col1["LEADNAM"] == "I"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # ecg calibration pulse lead_zero = -v_offset ax1.plot([40, 80, 80, 280, 280, 320], [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead II ax1.text(x_margin + 80, 0.55 + lead_zero, 'II', size=textsize) if "II" in rhythm_data.columns: beat_plot.II = beat_plot.II + lead_zero ax1.plot(beat_plot.TIME[beat_plot.II.notna()] + x_margin, beat_plot.II[beat_plot.II.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.II.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.II.notna()].values[-1] + x_margin if anns_matrix_col1 is not None: for idx, ann in anns_matrix_col1[anns_matrix_col1["LEADNAM"] == "II"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x < col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # ecg calibration pulse lead_zero = -2 * v_offset ax1.plot([40, 80, 80, 280, 280, 320], [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead III ax1.text(x_margin + 80, 0.55 + lead_zero, 'III', size=textsize) if "III" in rhythm_data.columns: beat_plot.III = beat_plot.III + lead_zero ax1.plot(beat_plot.TIME[beat_plot.III.notna()] + x_margin, beat_plot.III[beat_plot.III.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.III.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.III.notna()].values[-1] + x_margin if anns_matrix_col1 is not None: for idx, ann in anns_matrix_col1[anns_matrix_col1["LEADNAM"] == "III"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # Second column # ecg calibration pulse lead_zero = 0 ax1.plot(np.array([40, 80, 80, 280, 280, 320]) + h_offset, [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead aVR ax1.text(h_offset + x_margin + 80, 0.55, 'aVR', size=textsize) if "aVR" in rhythm_data.columns and\ len(beat_plot.TIME[beat_plot.aVR.notna()]) > 0: ax1.plot(beat_plot.TIME[beat_plot.aVR.notna()] + x_margin, beat_plot.aVR[beat_plot.aVR.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.aVR.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.aVR.notna()].values[-1] + x_margin if anns_matrix_col2 is not None: for idx, ann in anns_matrix_col2[anns_matrix_col2["LEADNAM"] == "aVR"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # ecg calibration pulse lead_zero = -v_offset ax1.plot(np.array([40, 80, 80, 280, 280, 320]) + h_offset, [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead aVL ax1.text(h_offset + x_margin + 80, 0.55 + lead_zero, 'aVL', size=textsize) if "aVL" in rhythm_data.columns and\ len(beat_plot.TIME[beat_plot.aVL.notna()]) > 0: beat_plot.aVL = beat_plot.aVL + lead_zero ax1.plot(beat_plot.TIME[beat_plot.aVL.notna()] + x_margin, beat_plot.aVL[beat_plot.aVL.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.aVL.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.aVL.notna()].values[-1] + x_margin if anns_matrix_col2 is not None: for idx, ann in anns_matrix_col2[anns_matrix_col2["LEADNAM"] == "aVL"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") lead_zero = -2 * v_offset ax1.plot(np.array([40, 80, 80, 280, 280, 320]) + h_offset, [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead aVF ax1.text(h_offset + x_margin + 80, 0.55 + lead_zero, 'aVF', size=textsize) if "aVF" in rhythm_data.columns and\ len(beat_plot.TIME[beat_plot.aVF.notna()]) > 0: beat_plot.aVF = beat_plot.aVF + lead_zero ax1.plot(beat_plot.TIME[beat_plot.aVF.notna()] + x_margin, beat_plot.aVF[beat_plot.aVF.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.aVF.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.aVF.notna()].values[-1] + x_margin if anns_matrix_col2 is not None: for idx, ann in anns_matrix_col2[anns_matrix_col2["LEADNAM"] == "aVF"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # Third column # ecg calibration pulse lead_zero = 0 ax1.plot(np.array([40, 80, 80, 280, 280, 320]) + 2 * h_offset, [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead V1 ax1.text(2 * h_offset + x_margin + 80, 0.55, 'V1', size=textsize) if "V1" in rhythm_data.columns and\ len(beat_plot.TIME[beat_plot.V1.notna()]) > 0: ax1.plot(beat_plot.TIME[beat_plot.V1.notna()] + x_margin, beat_plot.V1[beat_plot.V1.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.V1.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.V1.notna()].values[-1] + x_margin if anns_matrix_col3 is not None: for idx, ann in anns_matrix_col3[anns_matrix_col3["LEADNAM"] == "V1"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # ecg calibration pulse lead_zero = -v_offset ax1.plot(np.array([40, 80, 80, 280, 280, 320]) + 2 * h_offset, [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead V2 ax1.text(2 * h_offset + x_margin + 80, 0.55 + lead_zero, 'V2', size=textsize) if "V2" in rhythm_data.columns and\ len(beat_plot.TIME[beat_plot.V2.notna()]) > 0: beat_plot.V2 = beat_plot.V2 + lead_zero ax1.plot(beat_plot.TIME[beat_plot.V2.notna()] + x_margin, beat_plot.V2[beat_plot.V2.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.V2.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.V2.notna()].values[-1] + x_margin if anns_matrix_col3 is not None: for idx, ann in anns_matrix_col3[anns_matrix_col3["LEADNAM"] == "V2"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # ecg calibration pulse lead_zero = -2 * v_offset ax1.plot(np.array([40, 80, 80, 280, 280, 320]) + 2 * h_offset, [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead V3 ax1.text(2 * h_offset + x_margin + 80, 0.55 + lead_zero, 'V3', size=textsize) if "V3" in rhythm_data.columns and\ len(beat_plot.TIME[beat_plot.V3.notna()]) > 0: beat_plot.V3 = beat_plot.V3 + lead_zero ax1.plot(beat_plot.TIME[beat_plot.V3.notna()] + x_margin, beat_plot.V3[beat_plot.V3.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.V3.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.V3.notna()].values[-1] + x_margin if anns_matrix_col3 is not None: for idx, ann in anns_matrix_col3[anns_matrix_col3["LEADNAM"] == "V3"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # Fourth column # ecg calibration pulse lead_zero = 0 ax1.plot(np.array([40, 80, 80, 280, 280, 320]) + 3 * h_offset, [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead V4 ax1.text(3 * h_offset + x_margin + 80, 0.55, 'V4', size=textsize) if "V4" in rhythm_data.columns and\ len(beat_plot.TIME[beat_plot.V4.notna()]) > 0: ax1.plot(beat_plot.TIME[beat_plot.V4.notna()] + x_margin, beat_plot.V4[beat_plot.V4.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.V4.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.V4.notna()].values[-1] + x_margin if anns_matrix_col4 is not None: for idx, ann in anns_matrix_col4[anns_matrix_col4["LEADNAM"] == "V4"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # ecg calibration pulse lead_zero = -v_offset ax1.plot(np.array([40, 80, 80, 280, 280, 320]) + 3 * h_offset, [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead V5 ax1.text(3 * h_offset + x_margin + 80, 0.55 + lead_zero, 'V5', size=textsize) if "V5" in rhythm_data.columns and\ len(beat_plot.TIME[beat_plot.V5.notna()]) > 0: beat_plot.V5 = beat_plot.V5 + lead_zero ax1.plot(beat_plot.TIME[beat_plot.V5.notna()] + x_margin, beat_plot.V5[beat_plot.V5.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.V5.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.V5.notna()].values[-1] + x_margin if anns_matrix_col4 is not None: for idx, ann in anns_matrix_col4[anns_matrix_col4["LEADNAM"] == "V5"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # ecg calibration pulse lead_zero = -2 * v_offset ax1.plot(np.array([40, 80, 80, 280, 280, 320]) + 3 * h_offset, [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # Lead V6 ax1.text(3 * h_offset + x_margin + 80, 0.55 + lead_zero, 'V6', size=textsize) if "V6" in rhythm_data.columns and\ len(beat_plot.TIME[beat_plot.V6.notna()]) > 0: beat_plot.V6 = beat_plot.V6 + lead_zero ax1.plot(beat_plot.TIME[beat_plot.V6.notna()] + x_margin, beat_plot.V6[beat_plot.V6.notna()], color='black', linewidth=ecg_linewidth) lead_start_time = beat_plot.TIME[ beat_plot.V6.notna()].values[0] + x_margin col_end = beat_plot.TIME[beat_plot.V6.notna()].values[-1] + x_margin if anns_matrix_col4 is not None: for idx, ann in anns_matrix_col4[anns_matrix_col4["LEADNAM"] == "V6"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # Rhythm strip # ecg calibration pulse lead_zero = -0.5 - 3 * v_offset ax1.plot(np.array([40, 80, 80, 280, 280, 320]), [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) ax1.text(x_margin + 80, lead_zero + 0.55, 'II', size=textsize) if "II" in rhythm_data.columns: ax1.plot(rhythm_data.TIME + x_margin, rhythm_data.II + lead_zero, color='black', linewidth=ecg_linewidth) lead_start_time = rhythm_data.TIME[ rhythm_data.II.notna()].values[0] + x_margin col_end = rhythm_data.TIME[ rhythm_data.II.notna()].values[-1] + x_margin if anns_df is not None: if anns_df.shape[0] > 0: for idx, ann in anns_df[anns_df["LEADNAM"] == "II"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ann_x = ann["TIME"] + lead_start_time if ann_x <= col_end: ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) ax1.text(ann_x, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # Plot global annotations if anns_matrix is not None: if anns_matrix.shape[0] > 0: for idx, ann in anns_matrix[anns_matrix["LEADNAM"] == "GLOBAL"].iterrows(): ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] # Columns ann_x = ann["TIME"] + xmin + x_margin ax1.plot([ann_x, ann_x], [-0.5 - 2 * v_offset, ymax - 0.5], color="red", linewidth=0.5, linestyle=":") # Lead II strip at the bottom ax1.plot([ann_x, ann_x], [-1.0 - 3 * v_offset, 1.0 - 0.5 - 3 * v_offset], color="red", linewidth=0.5, linestyle=":") ax1.text(ann_x, -3 * v_offset - ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="red") # Turn off tick labels ax1.set_xticks([]) ax1.set_yticks([]) # Set figure width and height ax1.set_xlim(xmin, xmax + x_margin) ax1.set_ylim(ecg_ymin, ecg_ymax) if for_gui: # Close plt tmp = plt.close() return fig
class DensityPanel(FigureCanvasWxAgg): def __init__(self, parent, **kwargs): self.figure = Figure() FigureCanvasWxAgg.__init__(self, parent, -1, self.figure, **kwargs) self.canvas = self.figure.canvas self.SetMinSize((100, 100)) self.figure.set_facecolor((1, 1, 1)) self.figure.set_edgecolor((1, 1, 1)) self.canvas.SetBackgroundColour('white') self.subplot = self.figure.add_subplot(111) self.gate_helper = GatingHelper(self.subplot, self) self.navtoolbar = None self.point_list = [] self.gridsize = 50 self.cb = None self.x_scale = LINEAR_SCALE self.y_scale = LINEAR_SCALE self.color_scale = None self.x_label = '' self.y_label = '' self.cmap = 'jet' self.canvas.mpl_connect('button_release_event', self.on_release) def setpointslists(self, points): self.subplot.clear() self.point_list = points plot_pts = np.array(points).astype(float) if self.x_scale == LOG_SCALE: plot_pts = plot_pts[(plot_pts[:, 0] > 0)] if self.y_scale == LOG_SCALE: plot_pts = plot_pts[(plot_pts[:, 1] > 0)] hb = self.subplot.hexbin(plot_pts[:, 0], plot_pts[:, 1], gridsize=self.gridsize, xscale=self.x_scale, yscale=self.y_scale, bins=self.color_scale, cmap=matplotlib.cm.get_cmap(self.cmap)) if self.cb: # Remove the existing colorbar and reclaim the space so when we add # a colorbar to the new hexbin subplot, it doesn't get indented. #self.figure.delaxes(self.figure.axes[1]) self.cb.remove() self.figure.subplots_adjust(right=0.90) self.cb = self.figure.colorbar(hb, fraction=0.046, pad=0.04) if self.color_scale == LOG_SCALE: self.cb.set_label('log10(N)') self.subplot.set_xlabel(self.x_label) self.subplot.set_ylabel(self.y_label) xmin = np.nanmin(plot_pts[:, 0]) xmax = np.nanmax(plot_pts[:, 0]) ymin = np.nanmin(plot_pts[:, 1]) ymax = np.nanmax(plot_pts[:, 1]) # Pad all sides if self.x_scale == LOG_SCALE: xmin = xmin / 1.5 xmax = xmax * 1.5 else: xmin = xmin - (xmax - xmin) / 20. xmax = xmax + (xmax - xmin) / 20. if self.y_scale == LOG_SCALE: ymin = ymin / 1.5 ymax = ymax * 1.5 else: ymin = ymin - (ymax - ymin) / 20. ymax = ymax + (ymax - ymin) / 20. self.subplot.axis([xmin, xmax, ymin, ymax]) self.reset_toolbar() def getpointslists(self): return self.point_list def setgridsize(self, gridsize): self.gridsize = gridsize def set_x_scale(self, scale): self.x_scale = scale def set_y_scale(self, scale): self.y_scale = scale def set_color_scale(self, scale): if scale == LINEAR_SCALE: scale = None self.color_scale = scale def set_x_label(self, label): self.x_label = label def set_y_label(self, label): self.y_label = label def set_colormap(self, cmap): self.cmap = cmap self.draw() def get_toolbar(self): if not self.navtoolbar: self.navtoolbar = NavigationToolbar(self.canvas) return self.navtoolbar def reset_toolbar(self): # Cheat since there is no way reset if self.navtoolbar: self.navtoolbar._views.clear() self.navtoolbar._positions.clear() self.navtoolbar.push_current() def set_configpanel(self, configpanel): '''Allow access of the control panel from the plotting panel''' self.configpanel = configpanel def on_release(self, evt): if evt.button == 3: # right click self.show_popup_menu((evt.x, self.canvas.GetSize()[1] - evt.y), None) def show_popup_menu(self, (x, y), data): self.popup_menu_filters = {} popup = wx.Menu() loadimages_table_item = popup.Append( -1, 'Create gated table for CellProfiler LoadImages') selected_gate = self.configpanel.gate_choice.get_gatename_or_none() selected_gates = [] if selected_gate: selected_gates = [selected_gate] self.Bind( wx.EVT_MENU, lambda (e): ui.prompt_user_to_create_loadimages_table( self, selected_gates), loadimages_table_item) show_images_in_gate_item = popup.Append(-1, 'Show images in gate') show_images_in_gate_item.Enable(selected_gate is not None) self.Bind(wx.EVT_MENU, self.show_images_from_gate, show_images_in_gate_item) if p.object_table: show_objects_in_gate_item = popup.Append( -1, 'Show %s in gate' % (p.object_name[1])) show_objects_in_gate_item.Enable(selected_gate is not None) self.Bind(wx.EVT_MENU, self.show_objects_from_gate, show_objects_in_gate_item) self.PopupMenu(popup, (x, y))
class GraphFrame(wx.Frame): def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): global graphFrame_enabled global mplImported global mpl_version self.legendFix = False if not graphFrame_enabled: pyfalog.warning("Matplotlib is not enabled. Skipping initialization.") return try: cache_dir = mpl._get_cachedir() except: cache_dir = os.path.expanduser(os.path.join("~", ".matplotlib")) cache_file = os.path.join(cache_dir, 'fontList.cache') if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): # remove matplotlib font cache, see #234 os.remove(cache_file) if not mplImported: mpl.use('wxagg') graphFrame_enabled = True if int(mpl.__version__[0]) < 1: pyfalog.warning("pyfa: Found matplotlib version {} - activating OVER9000 workarounds".format(mpl.__version__)) pyfalog.warning("pyfa: Recommended minimum matplotlib version is 1.0.0") self.legendFix = True mplImported = True wx.Frame.__init__(self, parent, title="pyfa: Graph Generator", style=style, size=(520, 390)) i = wx.Icon(BitmapLoader.getBitmap("graphs_small", "gui")) self.SetIcon(i) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.CreateStatusBar() self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.mainSizer) sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) self.fits = [fit] if fit is not None else [] self.fitList = FitList(self) self.fitList.SetMinSize((270, -1)) self.fitList.fitList.update(self.fits) self.targets = [] # self.targetList = TargetList(self) # self.targetList.SetMinSize((270, -1)) # self.targetList.targetList.update(self.targets) self.graphSelection = wx.Choice(self, wx.ID_ANY, style=0) self.mainSizer.Add(self.graphSelection, 0, wx.EXPAND) self.figure = Figure(figsize=(5, 3), tight_layout={'pad': 1.08}) rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas = Canvas(self, -1, self.figure) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) self.subplot = self.figure.add_subplot(111) self.subplot.grid(True) self.mainSizer.Add(self.canvas, 1, wx.EXPAND) self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND) self.graphCtrlPanel = wx.Panel(self) self.mainSizer.Add(self.graphCtrlPanel, 0, wx.EXPAND | wx.ALL, 0) self.showY0 = True self.selectedY = None self.selectedYRbMap = {} ctrlPanelSizer = wx.BoxSizer(wx.HORIZONTAL) viewOptSizer = wx.BoxSizer(wx.VERTICAL) self.showY0Cb = wx.CheckBox(self.graphCtrlPanel, wx.ID_ANY, "Always show Y = 0", wx.DefaultPosition, wx.DefaultSize, 0) self.showY0Cb.SetValue(self.showY0) self.showY0Cb.Bind(wx.EVT_CHECKBOX, self.OnShowY0Update) viewOptSizer.Add(self.showY0Cb, 0, wx.LEFT | wx.TOP | wx.RIGHT | wx.EXPAND, 5) self.graphSubselSizer = wx.BoxSizer(wx.VERTICAL) viewOptSizer.Add(self.graphSubselSizer, 0, wx.ALL | wx.EXPAND, 5) ctrlPanelSizer.Add(viewOptSizer, 0, wx.EXPAND | wx.LEFT | wx.TOP | wx.BOTTOM, 5) self.inputsSizer = wx.FlexGridSizer(0, 4, 0, 0) self.inputsSizer.AddGrowableCol(1) ctrlPanelSizer.Add(self.inputsSizer, 1, wx.EXPAND | wx.RIGHT | wx.TOP | wx.BOTTOM, 5) self.graphCtrlPanel.SetSizer(ctrlPanelSizer) self.drawTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.draw, self.drawTimer) for view in Graph.views: view = view() self.graphSelection.Append(view.name, view) self.graphSelection.SetSelection(0) self.fields = {} self.updateGraphWidgets() self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) self.mainSizer.Add(self.sl1, 0, wx.EXPAND) fitSizer = wx.BoxSizer(wx.HORIZONTAL) fitSizer.Add(self.fitList, 1, wx.EXPAND) #fitSizer.Add(self.targetList, 1, wx.EXPAND) self.mainSizer.Add(fitSizer, 0, wx.EXPAND) self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick) self.fitList.fitList.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu) self.mainFrame.Bind(GE.FIT_CHANGED, self.OnFitChanged) self.mainFrame.Bind(GE.FIT_REMOVED, self.OnFitRemoved) self.Bind(wx.EVT_CLOSE, self.closeEvent) self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent) self.Bind(wx.EVT_CHOICE, self.graphChanged) from gui.builtinStatsViews.resistancesViewFull import EFFECTIVE_HP_TOGGLED # Grr crclar gons self.mainFrame.Bind(EFFECTIVE_HP_TOGGLED, self.OnEhpToggled) self.contextMenu = wx.Menu() removeItem = wx.MenuItem(self.contextMenu, 1, 'Remove Fit') self.contextMenu.Append(removeItem) self.contextMenu.Bind(wx.EVT_MENU, self.ContextMenuHandler, removeItem) self.Fit() self.SetMinSize(self.GetSize()) def handleDrag(self, type, fitID): if type == "fit": self.AppendFitToList(fitID) def closeEvent(self, event): self.closeWindow() event.Skip() def kbEvent(self, event): keycode = event.GetKeyCode() mstate = wx.GetMouseState() if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE: self.closeWindow() return elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL: self.fitList.fitList.selectAll() elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE: self.removeFits(self.getSelectedFits()) event.Skip() def OnContextMenu(self, event): if self.getSelectedFits(): self.PopupMenu(self.contextMenu) def ContextMenuHandler(self, event): selectedMenuItem = event.GetId() if selectedMenuItem == 1: # Copy was chosen fits = self.getSelectedFits() self.removeFits(fits) def OnEhpToggled(self, event): event.Skip() view = self.getView() if view.redrawOnEffectiveChange: view.clearCache() self.draw() def OnFitChanged(self, event): event.Skip() view = self.getView() view.clearCache(key=event.fitID) self.draw() def OnFitRemoved(self, event): event.Skip() fit = next((f for f in self.fits if f.ID == event.fitID), None) if fit is not None: self.removeFits([fit]) def graphChanged(self, event): self.selectedY = None self.updateGraphWidgets() event.Skip() def closeWindow(self): from gui.builtinStatsViews.resistancesViewFull import EFFECTIVE_HP_TOGGLED # Grr gons self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.OnLeftDClick) self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.OnFitChanged) self.mainFrame.Unbind(GE.FIT_REMOVED, handler=self.OnFitRemoved) self.mainFrame.Unbind(EFFECTIVE_HP_TOGGLED, handler=self.OnEhpToggled) self.Destroy() def getView(self): return self.graphSelection.GetClientData(self.graphSelection.GetSelection()) def getValues(self): values = {} for fieldHandle, field in self.fields.items(): values[fieldHandle] = field.GetValue() return values def OnShowY0Update(self, event): event.Skip() self.showY0 = self.showY0Cb.GetValue() self.draw() def OnYTypeUpdate(self, event): event.Skip() obj = event.GetEventObject() formatName = obj.GetLabel() self.selectedY = self.selectedYRbMap[formatName] self.draw() def updateGraphWidgets(self): view = self.getView() view.clearCache() self.graphSubselSizer.Clear() self.inputsSizer.Clear() for child in self.graphCtrlPanel.Children: if child is not self.showY0Cb: child.Destroy() self.fields.clear() # Setup view options self.selectedYRbMap.clear() if len(view.yDefs) > 1: i = 0 for yAlias, yDef in view.yDefs.items(): if i == 0: rdo = wx.RadioButton(self.graphCtrlPanel, wx.ID_ANY, yDef.switchLabel, style=wx.RB_GROUP) else: rdo = wx.RadioButton(self.graphCtrlPanel, wx.ID_ANY, yDef.switchLabel) rdo.Bind(wx.EVT_RADIOBUTTON, self.OnYTypeUpdate) if i == (self.selectedY or 0): rdo.SetValue(True) self.graphSubselSizer.Add(rdo, 0, wx.ALL | wx.EXPAND, 0) self.selectedYRbMap[yDef.switchLabel] = i i += 1 # Setup inputs for fieldHandle, fieldDef in (('x', view.xDef), *view.extraInputs.items()): textBox = wx.TextCtrl(self.graphCtrlPanel, wx.ID_ANY, style=0) self.fields[fieldHandle] = textBox textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) self.inputsSizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) if fieldDef.inputDefault is not None: inputDefault = fieldDef.inputDefault if not isinstance(inputDefault, str): inputDefault = ("%f" % inputDefault).rstrip("0") if inputDefault[-1:] == ".": inputDefault += "0" textBox.ChangeValue(inputDefault) imgLabelSizer = wx.BoxSizer(wx.HORIZONTAL) if fieldDef.inputIconID: icon = BitmapLoader.getBitmap(fieldDef.inputIconID, "icons") if icon is not None: static = wx.StaticBitmap(self.graphCtrlPanel) static.SetBitmap(icon) imgLabelSizer.Add(static, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 1) imgLabelSizer.Add(wx.StaticText(self.graphCtrlPanel, wx.ID_ANY, fieldDef.inputLabel), 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) self.inputsSizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) self.Layout() self.draw() def delayedDraw(self, event=None): self.drawTimer.Stop() self.drawTimer.Start(Fit.getInstance().serviceFittingOptions["marketSearchDelay"], True) def draw(self, event=None): global mpl_version if event is not None: event.Skip() self.drawTimer.Stop() # todo: FIX THIS, see #1430. draw() is not being unbound properly when the window closes, this is an easy fix, # but not a proper solution if not self: pyfalog.warning("GraphFrame handled event, however GraphFrame no longer exists. Ignoring event") return values = self.getValues() view = self.getView() self.subplot.clear() self.subplot.grid(True) legend = [] min_y = 0 if self.showY0 else None max_y = 0 if self.showY0 else None xRange = values['x'] extraInputs = {ih: values[ih] for ih in view.extraInputs} try: chosenY = [i for i in view.yDefs.keys()][self.selectedY or 0] except IndexError: chosenY = [i for i in view.yDefs.keys()][0] self.subplot.set(xlabel=view.xDef.axisLabel, ylabel=view.yDefs[chosenY].axisLabel) for fit in self.fits: try: xs, ys = view.getPlotPoints(fit, extraInputs, xRange, 100, chosenY) # Figure out min and max Y min_y_this = min(ys, default=None) if min_y is None: min_y = min_y_this elif min_y_this is not None: min_y = min(min_y, min_y_this) max_y_this = max(ys, default=None) if max_y is None: max_y = max_y_this elif max_y_this is not None: max_y = max(max_y, max_y_this) self.subplot.plot(xs, ys) legend.append('{} ({})'.format(fit.name, fit.ship.item.getShortName())) except Exception as ex: pyfalog.warning("Invalid values in '{0}'", fit.name) self.SetStatusText("Invalid values in '%s'" % fit.name) self.canvas.draw() return y_range = max_y - min_y min_y -= y_range * 0.05 max_y += y_range * 0.05 if min_y == max_y: min_y -= min_y * 0.05 max_y += min_y * 0.05 if min_y == max_y: min_y -= 5 max_y += 5 self.subplot.set_ylim(bottom=min_y, top=max_y) if mpl_version < 2: if self.legendFix and len(legend) > 0: leg = self.subplot.legend(tuple(legend), "upper right", shadow=False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) elif not self.legendFix and len(legend) > 0: leg = self.subplot.legend(tuple(legend), "upper right", shadow=False, frameon=False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) elif mpl_version >= 2: legend2 = [] legend_colors = { 0: "blue", 1: "orange", 2: "green", 3: "red", 4: "purple", 5: "brown", 6: "pink", 7: "grey", } for i, i_name in enumerate(legend): try: selected_color = legend_colors[i] except: selected_color = None legend2.append(Patch(color=selected_color, label=i_name), ) if len(legend2) > 0: leg = self.subplot.legend(handles=legend2) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) self.canvas.draw() self.SetStatusText("") self.Refresh() def onFieldChanged(self, event): view = self.getView() view.clearCache() self.delayedDraw() def AppendFitToList(self, fitID): sFit = Fit.getInstance() fit = sFit.getFit(fitID) if fit not in self.fits: self.fits.append(fit) self.fitList.fitList.update(self.fits) self.draw() def OnLeftDClick(self, event): row, _ = self.fitList.fitList.HitTest(event.Position) if row != -1: try: fit = self.fits[row] except IndexError: pass else: self.removeFits([fit]) def removeFits(self, fits): toRemove = [f for f in fits if f in self.fits] if not toRemove: return for fit in toRemove: self.fits.remove(fit) self.fitList.fitList.update(self.fits) view = self.getView() for fit in fits: view.clearCache(key=fit.ID) self.draw() def getSelectedFits(self): fits = [] for row in self.fitList.fitList.getSelectedRows(): try: fit = self.fits[row] except IndexError: continue fits.append(fit) return fits
class PlotPanel(wx.Panel): """ The PlotPanel has a Figure and a Canvas. OnSize events simply set a flag, and the actual resizing of the figure is triggered by an Idle event. See: http://www.scipy.org/Matplotlib_figure_in_a_wx_panel """ def __init__(self, parent, dataframes, color=None, dpi=None, **kwargs): # initialize Panel if 'id' not in kwargs.keys(): kwargs['id'] = wx.ID_ANY if 'style' not in kwargs.keys(): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__(self, parent, **kwargs) self.parent = parent self.dataframes = dataframes # initialize matplotlib stuff self.figure = Figure(None, dpi) self.figure.autofmt_xdate() self.canvas = FigureCanvasWxAgg(self, -1, self.figure) self.SetColor(color) #self._SetSize((800, 600)) self.draw() self._resizeflag = False self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) def SetColor(self, rgbtuple=None): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) def _onSize(self, event): self._resizeflag = True def _onIdle(self, evt): if self._resizeflag: self._resizeflag = False self._SetSize() def _SetSize(self, size=None): if size is None: size = tuple(self.GetClientSize()) self.SetSize(size) self.canvas.SetSize(size) self.figure.set_size_inches( float(size[0]) / self.figure.get_dpi(), float(size[1]) / self.figure.get_dpi()) def draw(self): ax = self.figure.add_subplot(111) for dataframe in self.dataframes: x = dataframe.index for col in dataframe.columns: empty = dataframe[col].count() == 0 y = dataframe[col].values if not empty else np.zeros(x.shape) ax.plot(x, y, label=col) try: self.figure.autofmt_xdate() except: pass ax.legend(loc="best") ax.grid()
class BoxPlotPanel(FigureCanvasWxAgg): def __init__(self, parent, points, **kwargs): ''' points -- a dictionary mapping x axis values to lists of values to plot ''' self.figure = Figure() FigureCanvasWxAgg.__init__(self, parent, -1, self.figure, **kwargs) self.canvas = self.figure.canvas self.SetMinSize((100,100)) self.figure.set_facecolor((1,1,1)) self.figure.set_edgecolor((1,1,1)) self.canvas.SetBackgroundColour('white') self.navtoolbar = None self.setpoints(points) def setpoints(self, points): ''' Updates the data to be plotted and redraws the plot. points - list of array samples, where each sample will be plotted as a separate box plot against the same y axis ''' self.xlabels = [] self.points = [] ignored = 0 for label, values in sorted(points.items()): if type(label) in [tuple, list]: self.xlabels += [','.join([str(l) for l in label])] else: self.xlabels += [label] self.points += [np.array(values).astype('f')[~ np.isnan(values)]] ignored += len(np.array(values)[np.isnan(values)]) if not hasattr(self, 'subplot'): self.subplot = self.figure.add_subplot(111) self.subplot.clear() # nothing to plot? if len(self.points)==0: logging.warn('No data to plot.') return self.subplot.boxplot(self.points, sym='k.') if len(self.points) > 1: self.figure.autofmt_xdate() self.subplot.set_xticklabels(self.xlabels) self.reset_toolbar() if ignored == 0: logging.info('Boxplot: Plotted %s points.'%(sum(map(len, self.points)))) else: logging.warn('Boxplot: Plotted %s points. Ignored %s NaNs.' %(sum(map(len, self.points)), ignored)) def set_x_axis_label(self, label): self.subplot.set_xlabel(label) def set_y_axis_label(self, label): self.subplot.set_ylabel(label) def get_point_lists(self): return self.points def get_xlabels(self): return self.xlabels def get_toolbar(self): if not self.navtoolbar: self.navtoolbar = NavigationToolbar(self.canvas) self.navtoolbar.DeleteToolByPos(6) return self.navtoolbar def reset_toolbar(self): # Cheat since there is no way reset if self.navtoolbar: self.navtoolbar._views.clear() self.navtoolbar._positions.clear() self.navtoolbar.push_current()
class WxLineScatterWidget(wx.Panel): axis_width = 20 axis_offset = 1.05 def __init__(self, *args, **kwargs): kwargs['style'] = kwargs.setdefault( 'style', wx.NO_FULL_REPAINT_ON_RESIZE) | wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__(self, *args, **kwargs) self.id = wx.NewId() self.plot1 = 'none' self.plot2 = 'none' self.plot3 = 'none' self.lineprops1 = wxLineProps({'color': '#990000', 'fill': True}) self.lineprops2 = wxLineProps({'color': '#009900', 'fill': True}) self.lineprops3 = wxLineProps({'color': '#000099', 'fill': True}) self.autoy1 = True self.autoy2 = True self.autoy3 = True self.smooth1 = 1 self.smooth2 = 1 self.smooth3 = 1 self.xaxis = '' self.press = False self.cursor = None self.span = None self.selstart = 0 self.selstop = 0 self.enablecursor = True self.enablespan = True self.cursorcolor = '#FF0000' self.cursorwidth = 1 self.gpxfig = Figure() self.ax1 = self.gpxfig.add_subplot( 1, 1, 1 ) # create a grid of 1 row, 1 col and put a subplot in the first cell of this grid self.gpxfig.subplots_adjust(right=0.9, left=0.06) self.ax2 = self.ax1.twinx() #self.ax2.spines["left"].set_visible(False) self.ax3 = self.ax1.twinx() self.ax3.spines["right"].set_position(("axes", self.axis_offset)) #self.ax3.spines["left"].set_visible(False) # canvas and events self.gpxcanvas = FigureCanvas(self, -1, self.gpxfig) self.gpxcanvas.mpl_connect('scroll_event', self.OnMouseWheel) self.gpxcanvas.mpl_connect('button_press_event', self.OnLeftMouseDown) self.gpxcanvas.mpl_connect('button_release_event', self.OnLeftMouseUp) self.gpxcanvas.mpl_connect('motion_notify_event', self.OnMouseMotion) self.gpxcanvas.mpl_connect('resize_event', self.OnSize) self.gpxcanvas.mpl_connect('figure_enter_event', self.OnMouseEnter) self.gpxcanvas.mpl_connect('figure_leave_event', self.OnMouseLeave) self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightMouseDown) self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.gpxcanvas, 1, wx.LEFT | wx.TOP | wx.GROW | wx.EXPAND) self.SetSizer(self.sizer) #self.OnSize(None) msgwrap.register(self.OnSigCurChanged, signal="CurChanged") msgwrap.register(self.OnSigSelChanged, signal="SelChanged") msgwrap.register(self.OnSigValChanged, signal="ValChanged") #set background color to pure white #that code does not work on linux... #color = wx.SystemSettings.GetColour(wx.wx.SYS_COLOUR_BTNFACE) color = wx.Colour(255, 255, 255) self.gpxfig.set_facecolor( (color.red / 255.0, color.green / 255.0, color.blue / 255.0)) self.gpxfig.set_edgecolor( (color.red / 255.0, color.green / 255.0, color.blue / 255.0)) self.gpxfig.set_edgecolor((0.0, 0.0, 0.0)) self.gpxcanvas.SetBackgroundColour(color) # create right now the popup menu self.select_menu = wx.Menu() for text in ["Disable selected",\ "Enable selected",\ "Delete selected",\ "Disable non selected",\ "Enable non selected",\ "Delete non selected",\ "Toggle points"]: item = self.select_menu.Append(wx.NewId(), text) self.Bind(wx.EVT_MENU, self.OnPopup, item) def x_to_num(self, value, scaled=True): if self.xaxis == 'time': return dates.date2num(dateutil.parser.parse(value)) else: if scaled: #return float(value)/self.gpx.get_scale(self.xaxis) return float(value) * self.gpx.get_scale(self.xaxis) else: return float(value) def num_to_x(self, value, scaled=True): if self.xaxis == 'time': return dates.num2date(value) else: if scaled: #return value*self.gpx.get_scale(self.xaxis) return value / self.gpx.get_scale(self.xaxis) else: return value def x_max(self): if self.xaxis == 'time': return self.x_to_num( self.gpx[self.xaxis][self.gpx.get_row_count() - 1]) else: return self.x_to_num(np.nanmax(self.gpx[self.xaxis])) def x_min(self): if self.xaxis == 'time': return self.x_to_num(self.gpx[self.xaxis][0]) else: return self.x_to_num(np.nanmin(self.gpx[self.xaxis])) def format_x_axis(self): if self.xaxis == 'time': xlo = self.ax1.get_xlim()[0] xhi = self.ax1.get_xlim()[1] if (xhi - xlo) > 0.003: self.ax1.xaxis.set_major_formatter( dates.DateFormatter("%H:%M")) else: self.ax1.xaxis.set_major_formatter( dates.DateFormatter("%H:%M:%S")) self.ax1.set_xlabel('Time (HH:MM:SS)') else: #self.ax1.set_xlabel('Distance (m)') self.ax1.set_xlabel(self.xaxis + " (" + self.gpx.get_unit(self.xaxis)[0] + ")") #self.ax1.xaxis.set_major_formatter(mpl.ticker.FormatStrFormatter('%.0f') ) self.ax1.xaxis.set_major_formatter(mpl.ticker.ScalarFormatter()) pass def get_axis(self, event, tolerance): bbox = self.ax1.get_window_extent().transformed( self.gpxfig.dpi_scale_trans.inverted()) l = bbox.bounds[0] * self.gpxfig.dpi b = bbox.bounds[1] * self.gpxfig.dpi r = l + bbox.bounds[2] * self.gpxfig.dpi t = b + bbox.bounds[3] * self.gpxfig.dpi #convert screen coordinates to graph coordinates xlo = self.ax1.get_xlim()[0] xhi = self.ax1.get_xlim()[1] event.xdata = (event.x - l) / (r - l) * (xhi - xlo) + xlo if ptinrect(l - tolerance, t, l, b, event.x, event.y): ylo, yhi = self.ax1.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'left' if ptinrect(r, t, r + tolerance, b, event.x, event.y): ylo, yhi = self.ax2.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'right' if ptinrect(l, t, r, t + tolerance, event.x, event.y): ylo, yhi = self.ax1.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'top' if ptinrect(l, b - tolerance, r, b, event.x, event.y): ylo, yhi = self.ax1.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'bottom' #we need a small adjustment here, but this hack gives good results if ptinrect(r * self.axis_offset * 0.985, t, r * self.axis_offset * 0.985 + tolerance, b, event.x, event.y): ylo, yhi = self.ax3.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return "3rd" if ptinrect(l, t, r, b, event.x, event.y): ylo, yhi = self.ax1.get_ylim() event.ydata = (event.y - b) / (t - b) * (yhi - ylo) + ylo return 'main' def update_axis(self, ax, plot, ylo, yhi, yauto, lineprops, smooth): if plot != 'none': ## process data!! N = smooth #data=(1.0)*np.convolve(self.gpx[plot]*self.gpx.scale[plot], np.ones((N,))/N)[(N-1):] data = (1.0) * np.convolve(self.gpx[(plot, True)], np.ones((N, )) / N)[(N - 1):] data[self.gpx['ok'] == False] = np.NAN ##end of data processing #remove fill_between collection for coll in ax.collections: ax.collections.remove(coll) #need to rebuild dates array in case something was deleted self.xvalues = [] for x in self.gpx[self.xaxis]: self.xvalues.append(self.x_to_num(x)) ax.get_lines()[0].set_data(self.xvalues, data) self.format_x_axis() if lineprops['fill']: ax.fill_between(self.xvalues, 0, data, facecolor=lineprops['color'], alpha=0.2) ax.get_lines()[0].set_color(lineprops['color']) ax.get_lines()[0].set_linewidth(lineprops['linewidth']) ax.get_lines()[0].set_marker(lineprops['marker']) ax.get_lines()[0].set_markersize(lineprops['markersize']) ax.set_autoscaley_on(yauto) ##now using legends instead of labels #ax.set_ylabel(plot+" ("+str(self.gpx.get_unit(plot)[0])+")") #ax.yaxis.label.set_color(lineprops['color']) lines = self.line1 + self.line2 + self.line3 labs = [ p for p in [self.plot1, self.plot2, self.plot3] if p != 'none' ] self.ax1.legend( lines, labs, loc='best' ) #,bbox_to_anchor=(0.5, 1.3), ncol=3, fancybox=False, shadow=False) if not yauto: ax.set_ylim(ylo, yhi) else: ax.set_ylim(np.min(self.gpx[plot] * self.gpx.scale[plot]), np.max(self.gpx[plot] * self.gpx.scale[plot])) ax.set_visible(True) for tick in ax.get_yticklabels(): tick.set_color(lineprops['color']) ax.spines["right"].set_edgecolor(lineprops['color']) ax.tick_params(axis='y', colors=lineprops['color']) else: ax.get_lines()[0].set_data(self.xvalues, np.zeros(self.gpx.get_row_count())) ax.set_visible(False) self.cursor.set_color(self.cursorcolor) self.cursor.set_linewidth(self.cursorwidth) self.format_x_axis() self.Draw(False) self.OnSize(None) def AttachGpx(self, data): self.gpx = data self.xvalues = [] self.xaxis = self.gpx.get_header_names()[0] for x in self.gpx[self.xaxis]: self.xvalues.append(self.x_to_num(x)) self.ax1.set_xlabel('') self.line1 = self.ax1.plot(self.xvalues, np.zeros(self.gpx.get_row_count()), picker=5, label='ax1') self.line2 = self.ax2.plot(self.xvalues, np.zeros(self.gpx.get_row_count()), picker=5, label='ax2') self.line3 = self.ax3.plot(self.xvalues, np.zeros(self.gpx.get_row_count()), picker=5, label='ax3') xlo = self.x_to_num(self.gpx[self.xaxis][0]) xhi = self.x_to_num(self.gpx[self.xaxis][self.gpx.get_row_count() - 1]) if xlo != xhi: self.ax1.set_xlim([xlo, xhi]) if self.enablecursor == True: self.cursor = self.ax1.axvline(color='r', animated=True) mid = (self.ax1.get_xlim()[0] + self.ax1.get_xlim()[1]) / 2 self.cursor.set_xdata(mid) #self.cursor.set_color('k') #self.cursor.set_linewidth(4) if self.enablespan == True: self.span = patches.Rectangle( (self.ax1.get_xlim()[0], 0), (self.ax1.get_xlim()[1] - self.ax1.get_xlim()[0]) / 3, 200, color='k', alpha=0.3, animated=True) self.ax1.add_patch(self.span) self.span.set_visible(False) self.SetDefaultPlots() self.OnSize(None) def DetachGpx(self): self.gpx = None self.plot1 = 'none' self.plot2 = 'none' self.plot3 = 'none' self.autoy1 = True self.autoy2 = True self.autoy3 = True self.fill1 = True self.fill2 = True self.fill3 = True self.xaxis = '' self.press = False if self.cursor != None: self.cursor.remove() self.cursor = None if self.span != None: self.span.remove() self.span = None def OnSigSelChanged(self, arg1, arg2, arg3): if arg1 == self.id: return if self.span != None: xlo = self.x_to_num(self.gpx[self.xaxis][arg2]) xhi = self.x_to_num(self.gpx[self.xaxis][arg3]) self.span.set_bounds( xlo, self.ax1.get_ylim()[0], xhi - xlo, self.ax1.get_ylim()[1] - self.ax1.get_ylim()[0]) self.span.set_visible(True) def OnSigValChanged(self, arg1): if arg1 == self.id: return self.update_axis(self.ax1, self.plot1, self.ax1.get_xlim()[0], self.ax1.get_xlim()[1], self.autoy1, self.lineprops1, self.smooth1) self.update_axis(self.ax2, self.plot2, self.ax2.get_xlim()[0], self.ax2.get_xlim()[1], self.autoy2, self.lineprops2, self.smooth2) self.update_axis(self.ax3, self.plot3, self.ax2.get_xlim()[0], self.ax2.get_xlim()[1], self.autoy3, self.lineprops3, self.smooth3) def OnSigCurChanged(self, arg1, arg2): if arg1 == self.id: return if self.gpx != None: xval = self.gpx[self.xaxis][arg2] self.gpxcanvas.restore_region(self.background) if self.cursor != None: self.cursor.set_xdata(self.x_to_num(xval)) self.ax1.draw_artist(self.cursor) if self.span != None and self.span.get_visible(): self.ax1.draw_artist(self.span) self.gpxcanvas.blit() self.UpdateStatusBar(arg2) def SetDefaultPlots(self): self.xaxis = self.gpx.get_header_names()[0] self.plot1 = self.gpx.get_header_names()[1] self.plot2 = 'none' self.plot3 = 'none' self.update_axis(self.ax1, self.plot1, 0, 1, True, self.lineprops1, self.smooth1) self.update_axis(self.ax2, self.plot2, 0, 1, True, self.lineprops2, self.smooth2) self.update_axis(self.ax3, self.plot3, 0, 1, True, self.lineprops3, self.smooth3) def XAxisAllowed(self): l = '' for name in self.gpx.get_header_names(): l += '|' + name return l[1:] def YAxisAllowed(self): l = '' for name in self.gpx.get_header_names(): l += '|' + name return l[1:] def Draw(self, blit): if blit: self.gpxcanvas.restore_region(self.background) else: self.gpxcanvas.draw() self.background = self.gpxcanvas.copy_from_bbox(self.ax1.bbox) if self.span != None and self.span.get_visible(): self.ax1.draw_artist(self.span) if self.cursor != None: self.ax1.draw_artist(self.cursor) self.gpxcanvas.blit() def OnSize(self, event): pixels = self.GetClientSize() if pixels[0] < 20 or pixels[1] < 20: return self.SetSize(pixels) self.gpxcanvas.SetSize(pixels) self.gpxfig.set_size_inches( float(pixels[0]) / self.gpxfig.get_dpi(), float(pixels[1]) / self.gpxfig.get_dpi()) leg = self.ax1.xaxis.get_tightbbox(self.gpxcanvas.get_renderer()) leg1 = self.ax1.yaxis.get_tightbbox(self.gpxcanvas.get_renderer()) leg2 = self.ax2.yaxis.get_tightbbox(self.gpxcanvas.get_renderer()) leg3 = self.ax3.yaxis.get_tightbbox(self.gpxcanvas.get_renderer( )) #leg2 and leg3 are exactly the same!! bottomalign = (leg.height + 5) / pixels[1] leftalign = (leg1.width + 5) / pixels[0] if self.plot2 == 'none' and self.plot3 == 'none': rightalign = (1 - (5.0) / pixels[0]) / self.axis_offset else: rightalign = (1 - (leg2.width + 5) / pixels[0]) / self.axis_offset if pixels[1] > 32: self.gpxfig.subplots_adjust(bottom=bottomalign) if pixels[0] > 32: self.gpxfig.subplots_adjust(left=leftalign, right=rightalign) ##PYTHON3 self.gpxfig.subplots_adjust(right=0.9, left=0.06, bottom=0.2) self.Draw(False) def OnLeftMouseDblClick(self, event): #dble click. Let's get prepared xlo = self.num_to_x(self.ax1.get_xlim()[0], False) xhi = self.num_to_x(self.ax1.get_xlim()[1], False) y1lo = self.ax1.get_ylim()[0] y1hi = self.ax1.get_ylim()[1] y2lo = self.ax2.get_ylim()[0] y2hi = self.ax2.get_ylim()[1] y3lo = self.ax3.get_ylim()[0] y3hi = self.ax3.get_ylim()[1] (dummy,xaxis,xlo,xhi,self.cursorcolor,self.cursorwidth, dummy,self.plot1,y1lo,y1hi,self.autoy1,self.smooth1, self.lineprops1['color'],self.lineprops1['linewidth'],self.lineprops1['marker'],self.lineprops1['markersize'],self.lineprops1['fill'],\ dummy,self.plot2,y2lo,y2hi,self.autoy2,self.smooth2, self.lineprops2['color'],self.lineprops2['linewidth'],self.lineprops2['marker'],self.lineprops2['markersize'],self.lineprops2['fill'],\ dummy,self.plot3,y3lo,y3hi,self.autoy3,self.smooth3, self.lineprops3['color'],self.lineprops3['linewidth'],self.lineprops3['marker'],self.lineprops3['markersize'],self.lineprops3['fill'])=\ WxQuery("Graph Settings",\ [('wxnotebook','X Axis',None,None,None), ('wxcombo','X axis',self.XAxisAllowed(),self.xaxis,'str'), ("wxentry","Start",None,str(xlo),'str'), ("wxentry","End",None,str(xhi),'str'), ('wxcolor','Cursor color',None,self.cursorcolor,'str'), ('wxspin','Cursor width','0|6|1',self.cursorwidth,'int'), ('wxnotebook','Y1Axis',None,None,None), ('wxcombo','Channel 1',self.YAxisAllowed(),self.plot1,'str'), ('wxentry','Bottom',None,y1lo,'float'), ('wxentry','Top',None,y1hi,'float'), ('wxcheck','Auto Scale','-9|-8', self.autoy1,'bool'), #8 ('wxhscale','Smooth','1|12|1|1',self.smooth1,'int'), ('wxcolor','Color',None,self.lineprops1['color'],'str'), ('wxspin','Line width','0|12|1',self.lineprops1['linewidth'],'int'), ('wxcombo','Marker','.|o|+|x|^|4|s|*|D',self.lineprops1['marker'],'str'), ('wxspin','Marker size','0|12|1',self.lineprops1['markersize'],'int'), ('wxcheck','Fill area',None,self.lineprops1['fill'],'bool'), ('wxnotebook','Y2 Axis',None,None,None), ('wxcombo','Channel 2',self.YAxisAllowed(),self.plot2,'str'), ('wxentry','Bottom',None,y2lo,'float'), ('wxentry','Top',None,y2hi,'float'), ('wxcheck','Auto Scale','-20|-19', self.autoy2,'bool'), ('wxhscale','Smooth','1|12|1|1',self.smooth2,'int'), ('wxcolor','Color',None,self.lineprops2['color'],'str'), ('wxspin','Line width','0|12|1',self.lineprops2['linewidth'],'int'), ('wxcombo','Marker','.|o|+|x|^|4|s|*|D',self.lineprops2['marker'],'str'), ('wxspin','Marker size','0|12|1',self.lineprops2['markersize'],'int'), ('wxcheck','Fill area',None,self.lineprops2['fill'],'bool'), ('wxnotebook','Y3 Axis',None,None,None), ('wxcombo','Channel 3',self.YAxisAllowed(),self.plot3,'str'), ('wxentry','Bottom',None,y3lo,'float'), ('wxentry','Top',None,y3hi,'float'), ('wxcheck','Auto Scale','-31|-30', self.autoy3,'bool'), ('wxhscale','Smooth','1|12|1|1',self.smooth3,'int'), ('wxcolor','Color',None,self.lineprops3['color'],'str'), ('wxspin','Line width','0|12|1',self.lineprops3['linewidth'],'int'), ('wxcombo','Marker','.|o|+|x|^|4|s|*|D',self.lineprops3['marker'],'str'), ('wxspin','Marker size','0|12|1',self.lineprops3['markersize'],'int'), ('wxcheck','Fill area',None,self.lineprops3['fill'],'bool') ]) if self.xaxis == xaxis: xlo = max(self.x_to_num(xlo, False), self.x_min()) xhi = min(self.x_to_num(xhi, False), self.x_max()) self.ax1.set_xlim([xlo, xhi]) else: #time units have changed... don't bother and set to full x range self.xaxis = xaxis self.ax1.set_xlim([self.x_min(), self.x_max()]) self.update_axis(self.ax1, self.plot1, y1lo, y1hi, self.autoy1, self.lineprops1, self.smooth1) self.update_axis(self.ax2, self.plot2, y2lo, y2hi, self.autoy2, self.lineprops2, self.smooth2) self.update_axis(self.ax3, self.plot3, y3lo, y3hi, self.autoy3, self.lineprops3, self.smooth3) def OnLeftMouseDown(self, event): where = self.get_axis(event, self.axis_width) #if hasattr(event, 'guiEvent') and int(event.guiEvent.type)==5: #calling direcly the dialog may freeze on unix (linux-osX systems) under wx backend #workaround is to release mouse #see http://stackoverflow.com/questions/16815695/modal-dialog-freezes-the-whole-application #event.guiEvent.GetEventObject().ReleaseMouse() for pick_event if event.button == 1: if event.dblclick: try: event.guiEvent.GetEventObject().ReleaseMouse() except: pass self.OnLeftMouseDblClick(event) return if where == 'bottom': (self.x0, self.y0) = (event.xdata, event.ydata) self.press = True if where == 'main' and self.span != None: self.span.set_visible(True) (self.x0, self.y0) = (event.xdata, event.ydata) self.selstart = self.x0 self.selstop = self.x0 self.span.set_bounds( event.xdata, self.ax1.get_ylim()[0], 0, self.ax1.get_ylim()[1] - self.ax1.get_ylim()[0]) self.press = True elif event.button == 3: if where == 'main': self.OnRightMouseDown(event) def OnLeftMouseUp(self, event): where = self.get_axis(event, self.axis_width) self.press = False if event.button == 1 and self.span != None: if where == 'main': idx1 = np.searchsorted(self.ax1.get_lines()[0].get_data()[0], self.x0) idx2 = np.searchsorted(self.ax1.get_lines()[0].get_data()[0], event.xdata) self.selstart = min(idx1, idx2) self.selstop = max(idx1, idx2) if self.selstart == self.selstop: self.span.set_visible(False) msgwrap.message("SelChanged", arg1=self.id, arg2=self.selstart, arg3=self.selstop) self.press = False def OnRightMouseDown(self, event): #may be necessary in some OSes event.guiEvent.GetEventObject().ReleaseMouse() if self.selstart == self.selstop: self.select_menu.Enable( self.select_menu.FindItem("Disable selected"), False) self.select_menu.Enable( self.select_menu.FindItem("Enable selected"), False) self.select_menu.Enable( self.select_menu.FindItem("Delete selected"), False) else: self.select_menu.Enable( self.select_menu.FindItem("Disable selected"), True) self.select_menu.Enable( self.select_menu.FindItem("Enable selected"), True) self.select_menu.Enable( self.select_menu.FindItem("Delete selected"), True) self.select_menu.Enable(self.select_menu.FindItem("Toggle points"), True) # on some OS (and depending on wxPython/wxWidgets version, calling # wx.PopupMenu will fail unless it is called after matplotlib handler has returned # for some magic reason, we do not need to specify wx.Point(event.x, event.y) in parameterss #self.PopupMenu(self.select_menus) wx.CallAfter(self.PopupMenu, self.select_menu) def OnMouseMotion(self, event): where = self.get_axis(event, self.axis_width) if where == 'bottom' or where == 'right' or where == 'left' or where == '3rd': wx.SetCursor(wx.Cursor(wx.CURSOR_MAGNIFIER)) else: wx.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) if where == 'bottom' and self.press: dx = event.xdata - self.x0 dy = event.ydata - self.y0 self.ax1.set_xlim(self.ax1.get_xlim()[0] - dx, self.ax1.get_xlim()[1] - dx) self.Draw(False) if where == 'main' and self.press: self.span.set_bounds(self.x0,\ self.ax1.get_ylim()[0],\ event.xdata-self.x0,\ self.ax1.get_ylim()[1]-self.ax1.get_ylim()[0]) self.Draw(True) if where == 'main' and self.cursor != None: self.cursor.set_xdata(event.xdata) xval = event.xdata idx = np.searchsorted(self.ax1.get_lines()[0].get_data()[0], xval) while self.gpx['ok'][ idx] == False and idx >= 0: #look for nearest enabled point idx -= 1 idx = clamp(idx, 0, self.gpx.get_row_count() - 1) self.cursor.set_xdata(self.x_to_num(self.gpx[self.xaxis][idx])) msgwrap.message("CurChanged", arg1=self.id, arg2=idx) ##send a message for the status bar self.UpdateStatusBar(idx) self.Draw(True) def OnMouseWheel(self, event): where = self.get_axis(event, self.axis_width) if where == 'bottom': xmax = self.x_max() xmin = self.x_min() xlo, xhi = self.ax1.get_xlim() if event.button == 'down': scale_factor = 1.2 else: scale_factor = 1 / 1.2 nxhi = event.xdata + (scale_factor * (xhi - event.xdata)) nxlo = event.xdata - (scale_factor * (event.xdata - xlo)) nxhi = min(nxhi, xmax) nxlo = max(nxlo, xmin) self.ax1.set_xlim([nxlo, nxhi]) self.format_x_axis() elif where == 'left' or where == 'right' or where == '3rd': if where == 'left': ax = self.ax1 plot = self.plot1 elif where == 'right': ax = self.ax2 plot = self.plot2 elif where == '3rd': ax = self.ax3 plot = self.plot3 ymax = np.max(self.gpx[plot] * self.gpx.scale[plot]) ymin = np.min(self.gpx[plot] * self.gpx.scale[plot]) ylo, yhi = ax.get_ylim() if event.button == 'down': scale_factor = 1.2 else: scale_factor = 1 / 1.2 nyhi = event.ydata + (scale_factor * (yhi - event.ydata)) nylo = event.ydata - (scale_factor * (event.ydata - ylo)) nyhi = min(nyhi, ymax) nylo = max(nylo, ymin) ax.set_ylim([nylo, nyhi]) self.Draw(False) def OnMouseEnter(self, event): self.SetFocus( ) # stupid bug in wxSplitterWindow, mouse wheel is always send to the same panel in wxSplittedWIndow def OnMouseLeave(self, event): wx.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) pass def OnPopup(self, event): item = self.select_menu.FindItemById(event.GetId()) text = item.GetText() if text == "Disable selected": self.gpx['ok'][self.selstart:self.selstop] = False if text == "Enable selected": self.gpx['ok'][self.selstart:self.selstop] = True if text == "Disable non selected": self.gpx['ok'][:self.selstart] = False self.gpx['ok'][self.selstop:] = False if text == "Enable non selected": self.gpx['ok'][:self.selstart] = True self.gpx['ok'][self.selstop:] = True if text == "Delete selected": if wx.MessageDialog(None, "Delete Points...?",\ 'Are you sure you want to delete these points',\ wx.YES_NO | wx.ICON_QUESTION).ShowModal()==wx.ID_YES: for _ in range(self.selstart, self.selstop): self.gpx.drop_row( self.selstart ) #each time we delete, the rest of the array is shifted. so we have to delete always the same index if text == "Delete non selected": if wx.MessageDialog(None, "Delete Points...?",\ 'Are you sure you want to delete these points',\ wx.YES_NO | wx.ICON_QUESTION).ShowModal()==wx.ID_YES: for _ in range(self.selstop, self.gpx.get_row_count()): self.gpx.drop_row( self.selstop ) #delete first end of range, to avoid shifting selstop for _ in range(0, self.selstart): self.gpx.drop_row(0) if text == "Toggle points": self.gpx['ok'] = np.invert(self.gpx['ok']) msgwrap.message("ValChanged", arg1=self.id) self.update_axis(self.ax1, self.plot1, self.ax1.get_ylim()[0], self.ax1.get_ylim()[1], self.autoy1, self.lineprops1, self.smooth1) self.update_axis(self.ax2, self.plot2, self.ax2.get_ylim()[0], self.ax2.get_ylim()[1], self.autoy2, self.lineprops2, self.smooth2) self.update_axis(self.ax3, self.plot3, self.ax3.get_ylim()[0], self.ax3.get_ylim()[1], self.autoy3, self.lineprops3, self.smooth3) def UpdateStatusBar(self, idx): if self.plot1 != "none": msg1=self.plot1+\ " ("+str(self.gpx.get_unit(self.plot1)[0])+"): "\ +str(self.gpx[self.plot1][idx]*self.gpx.scale[self.plot1]) else: msg1 = "" if self.plot2 != "none": msg2=self.plot2+\ " ("+str(self.gpx.get_unit(self.plot2)[0])+"): "\ +str(self.gpx[self.plot2][idx]*self.gpx.scale[self.plot2]) else: msg2 = "" if self.plot3 != "none": msg3=self.plot3+\ " ("+str(self.gpx.get_unit(self.plot3)[0])+"): "\ +str(self.gpx[self.plot3][idx]*self.gpx.scale[self.plot3]) else: msg3 = "" msgwrap.message("StatusChanged",arg1=self.id,\ arg2=self.gpx['time'][idx],\ arg3=msg1,\ arg4=msg2,\ arg5=msg3 )
def make_colorbar(width, height, dpi, grid, orientation, transparent, norm, cmap, extend, paletted, add_ticks, center_labels): dpi = 2*dpi figsize = width/dpi, height/dpi fig = Figure(figsize=figsize, dpi=dpi, frameon=False) fig.set_facecolor('white') fig.set_edgecolor('none') if orientation == 'vertical': if add_ticks: ax = fig.add_axes([0.05, 0.05, 0.60, 0.90]) else: ax = fig.add_axes([0.0, 0.0, 1.0, 1.0]) else: if add_ticks: cbwidth = 0.95 # ratio of width to figsize cbratio = 0.10 # ratio of width to height cbheight= cbratio*cbwidth*width/float(height) ax = fig.add_axes([(1-cbwidth)/2.0, 1-cbheight, cbwidth, cbheight]) else: ax = fig.add_axes([0.0, 0.0, 1.0, 1.0]) if transparent: fig.figurePatch.set_alpha(0.0) ax.axesPatch.set_alpha(0.5) if len(norm.boundaries) <= 14: # label all colors nb = norm.boundaries if center_labels: nb_pos = nb + (nb[1]-nb[0])*0.5 else: nb_pos = nb else: nb_pos = None cb = ColorbarBase(ax, cmap=cmap, norm=norm, ticks=nb_pos, drawedges=True, orientation=orientation, extend=extend, extendfrac='auto') if center_labels: cb.ax.set_xticklabels(nb) cb.ax.tick_params('both', length=0, width=1) fontsize = 0 #cb.solids.set_antialiased(False) #cb.patch.set_antialiased(False) #cb.outline.set_antialiased(False) if add_ticks: # ticks.set_antialiased(False) if orientation == 'vertical': for tick in cb.ax.get_yticklabels(): txt = tick.get_text() ntxt = len(txt) fontsize = max(int(0.50*width/ntxt), fontsize) fontsize = min(14, fontsize) for tick in cb.ax.get_yticklabels(): tick.set_fontsize(fontsize) tick.set_color('black') else: #cb.ax.xaxis.set_ticks() ticks = cb.ax.get_xticklabels() stxt = 0 # get total ticks text newticks = [] for tick in ticks: # Substitute m-minus with hyphen-minus (n-minus) newtick = tick.get_text().replace(u'\u2212', u'\u002D') stxt += len(newtick) newticks.append(newtick) cb.ax.set_xticklabels(newticks) fontsize = width/(2*(stxt+5.0)) fontsize = max(min(12, fontsize),6) for tick in cb.ax.get_xticklabels(): tick.set_fontsize(fontsize) tick.set_color('black') else: cb.ax.xaxis.set_major_locator(NullLocator()) # Save to buffer. canvas = FigureCanvas(fig) output = StringIO() canvas.print_png(output) """ if paletted: # The text on colorbars look bad when we disable antialiasing # entirely. We therefore just force the number of paletted # colors to a reasonable number try: # We reduce the number of colors to the number of colors # in the color scale plus 20 to allow for some # text antialiasing. nother = 20 ncolors = len(norm.boundaries) + nother except AttributeError: ncolors = None output = convert_paletted(canvas, ncolors=ncolors) else: output = StringIO() canvas.print_png(output) """ return output
class GraphFrame(wx.Frame): def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): global graphFrame_enabled global mplImported global mpl_version self.legendFix = False if not graphFrame_enabled: pyfalog.warning( "Matplotlib is not enabled. Skipping initialization.") return try: cache_dir = mpl._get_cachedir() except: cache_dir = os.path.expanduser(os.path.join("~", ".matplotlib")) cache_file = os.path.join(cache_dir, 'fontList.cache') if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): # remove matplotlib font cache, see #234 os.remove(cache_file) if not mplImported: mpl.use('wxagg') graphFrame_enabled = True if int(mpl.__version__[0]) < 1: pyfalog.warning( "pyfa: Found matplotlib version {} - activating OVER9000 workarounds" .format(mpl.__version__)) pyfalog.warning( "pyfa: Recommended minimum matplotlib version is 1.0.0") self.legendFix = True mplImported = True wx.Frame.__init__(self, parent, title="pyfa: Graph Generator", style=style, size=(520, 390)) i = wx.Icon(BitmapLoader.getBitmap("graphs_small", "gui")) self.SetIcon(i) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.CreateStatusBar() self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.mainSizer) sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) self.fits = [fit] if fit is not None else [] self.fitList = FitList(self) self.fitList.SetMinSize((270, -1)) self.fitList.fitList.update(self.fits) self.targets = [] # self.targetList = TargetList(self) # self.targetList.SetMinSize((270, -1)) # self.targetList.targetList.update(self.targets) self.graphSelection = wx.Choice(self, wx.ID_ANY, style=0) self.mainSizer.Add(self.graphSelection, 0, wx.EXPAND) self.figure = Figure(figsize=(5, 3), tight_layout={'pad': 1.08}) rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas = Canvas(self, -1, self.figure) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) self.subplot = self.figure.add_subplot(111) self.subplot.grid(True) self.mainSizer.Add(self.canvas, 1, wx.EXPAND) self.mainSizer.Add( wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND) self.graphCtrlPanel = wx.Panel(self) self.mainSizer.Add(self.graphCtrlPanel, 0, wx.EXPAND | wx.ALL, 0) self.showY0 = True self.selectedY = None self.selectedYRbMap = {} ctrlPanelSizer = wx.BoxSizer(wx.HORIZONTAL) viewOptSizer = wx.BoxSizer(wx.VERTICAL) self.showY0Cb = wx.CheckBox(self.graphCtrlPanel, wx.ID_ANY, "Always show Y = 0", wx.DefaultPosition, wx.DefaultSize, 0) self.showY0Cb.SetValue(self.showY0) self.showY0Cb.Bind(wx.EVT_CHECKBOX, self.OnShowY0Update) viewOptSizer.Add(self.showY0Cb, 0, wx.LEFT | wx.TOP | wx.RIGHT | wx.EXPAND, 5) self.graphSubselSizer = wx.BoxSizer(wx.VERTICAL) viewOptSizer.Add(self.graphSubselSizer, 0, wx.ALL | wx.EXPAND, 5) ctrlPanelSizer.Add(viewOptSizer, 0, wx.EXPAND | wx.LEFT | wx.TOP | wx.BOTTOM, 5) self.inputsSizer = wx.FlexGridSizer(0, 4, 0, 0) self.inputsSizer.AddGrowableCol(1) ctrlPanelSizer.Add(self.inputsSizer, 1, wx.EXPAND | wx.RIGHT | wx.TOP | wx.BOTTOM, 5) self.graphCtrlPanel.SetSizer(ctrlPanelSizer) self.drawTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.draw, self.drawTimer) for view in Graph.views: view = view() self.graphSelection.Append(view.name, view) self.graphSelection.SetSelection(0) self.fields = {} self.updateGraphWidgets() self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) self.mainSizer.Add(self.sl1, 0, wx.EXPAND) fitSizer = wx.BoxSizer(wx.HORIZONTAL) fitSizer.Add(self.fitList, 1, wx.EXPAND) #fitSizer.Add(self.targetList, 1, wx.EXPAND) self.mainSizer.Add(fitSizer, 0, wx.EXPAND) self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick) self.fitList.fitList.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu) self.mainFrame.Bind(GE.FIT_CHANGED, self.OnFitChanged) self.mainFrame.Bind(GE.FIT_REMOVED, self.OnFitRemoved) self.Bind(wx.EVT_CLOSE, self.closeEvent) self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent) self.Bind(wx.EVT_CHOICE, self.graphChanged) from gui.builtinStatsViews.resistancesViewFull import EFFECTIVE_HP_TOGGLED # Grr crclar gons self.mainFrame.Bind(EFFECTIVE_HP_TOGGLED, self.OnEhpToggled) self.contextMenu = wx.Menu() removeItem = wx.MenuItem(self.contextMenu, 1, 'Remove Fit') self.contextMenu.Append(removeItem) self.contextMenu.Bind(wx.EVT_MENU, self.ContextMenuHandler, removeItem) self.Fit() self.SetMinSize(self.GetSize()) def handleDrag(self, type, fitID): if type == "fit": self.AppendFitToList(fitID) def closeEvent(self, event): self.closeWindow() event.Skip() def kbEvent(self, event): keycode = event.GetKeyCode() mstate = wx.GetMouseState() if keycode == wx.WXK_ESCAPE and mstate.GetModifiers() == wx.MOD_NONE: self.closeWindow() return elif keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL: self.fitList.fitList.selectAll() elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE ) and mstate.GetModifiers() == wx.MOD_NONE: self.removeFits(self.getSelectedFits()) event.Skip() def OnContextMenu(self, event): if self.getSelectedFits(): self.PopupMenu(self.contextMenu) def ContextMenuHandler(self, event): selectedMenuItem = event.GetId() if selectedMenuItem == 1: # Copy was chosen fits = self.getSelectedFits() self.removeFits(fits) def OnEhpToggled(self, event): event.Skip() view = self.getView() if view.redrawOnEffectiveChange: view.clearCache() self.draw() def OnFitChanged(self, event): event.Skip() view = self.getView() view.clearCache(key=event.fitID) self.draw() def OnFitRemoved(self, event): event.Skip() fit = next((f for f in self.fits if f.ID == event.fitID), None) if fit is not None: self.removeFits([fit]) def graphChanged(self, event): self.selectedY = None self.updateGraphWidgets() event.Skip() def closeWindow(self): from gui.builtinStatsViews.resistancesViewFull import EFFECTIVE_HP_TOGGLED # Grr gons self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.OnLeftDClick) self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.OnFitChanged) self.mainFrame.Unbind(GE.FIT_REMOVED, handler=self.OnFitRemoved) self.mainFrame.Unbind(EFFECTIVE_HP_TOGGLED, handler=self.OnEhpToggled) self.Destroy() def getView(self): return self.graphSelection.GetClientData( self.graphSelection.GetSelection()) def getValues(self): values = {} for fieldHandle, field in self.fields.items(): values[fieldHandle] = field.GetValue() return values def OnShowY0Update(self, event): event.Skip() self.showY0 = self.showY0Cb.GetValue() self.draw() def OnYTypeUpdate(self, event): event.Skip() obj = event.GetEventObject() formatName = obj.GetLabel() self.selectedY = self.selectedYRbMap[formatName] self.draw() def updateGraphWidgets(self): view = self.getView() view.clearCache() self.graphSubselSizer.Clear() self.inputsSizer.Clear() for child in self.graphCtrlPanel.Children: if child is not self.showY0Cb: child.Destroy() self.fields.clear() # Setup view options self.selectedYRbMap.clear() if len(view.yDefs) > 1: i = 0 for yAlias, yDef in view.yDefs.items(): if i == 0: rdo = wx.RadioButton(self.graphCtrlPanel, wx.ID_ANY, yDef.switchLabel, style=wx.RB_GROUP) else: rdo = wx.RadioButton(self.graphCtrlPanel, wx.ID_ANY, yDef.switchLabel) rdo.Bind(wx.EVT_RADIOBUTTON, self.OnYTypeUpdate) if i == (self.selectedY or 0): rdo.SetValue(True) self.graphSubselSizer.Add(rdo, 0, wx.ALL | wx.EXPAND, 0) self.selectedYRbMap[yDef.switchLabel] = i i += 1 # Setup inputs for fieldHandle, fieldDef in (('x', view.xDef), *view.extraInputs.items()): textBox = wx.TextCtrl(self.graphCtrlPanel, wx.ID_ANY, style=0) self.fields[fieldHandle] = textBox textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) self.inputsSizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) if fieldDef.inputDefault is not None: inputDefault = fieldDef.inputDefault if not isinstance(inputDefault, str): inputDefault = ("%f" % inputDefault).rstrip("0") if inputDefault[-1:] == ".": inputDefault += "0" textBox.ChangeValue(inputDefault) imgLabelSizer = wx.BoxSizer(wx.HORIZONTAL) if fieldDef.inputIconID: icon = BitmapLoader.getBitmap(fieldDef.inputIconID, "icons") if icon is not None: static = wx.StaticBitmap(self.graphCtrlPanel) static.SetBitmap(icon) imgLabelSizer.Add(static, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 1) imgLabelSizer.Add( wx.StaticText(self.graphCtrlPanel, wx.ID_ANY, fieldDef.inputLabel), 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) self.inputsSizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) self.Layout() self.draw() def delayedDraw(self, event=None): self.drawTimer.Stop() self.drawTimer.Start( Fit.getInstance().serviceFittingOptions["marketSearchDelay"], True) def draw(self, event=None): global mpl_version if event is not None: event.Skip() self.drawTimer.Stop() # todo: FIX THIS, see #1430. draw() is not being unbound properly when the window closes, this is an easy fix, # but not a proper solution if not self: pyfalog.warning( "GraphFrame handled event, however GraphFrame no longer exists. Ignoring event" ) return values = self.getValues() view = self.getView() self.subplot.clear() self.subplot.grid(True) legend = [] min_y = 0 if self.showY0 else None max_y = 0 if self.showY0 else None xRange = values['x'] extraInputs = {ih: values[ih] for ih in view.extraInputs} try: chosenY = [i for i in view.yDefs.keys()][self.selectedY or 0] except IndexError: chosenY = [i for i in view.yDefs.keys()][0] self.subplot.set(xlabel=view.xDef.axisLabel, ylabel=view.yDefs[chosenY].axisLabel) for fit in self.fits: try: xs, ys = view.getPlotPoints(fit, extraInputs, xRange, 100, chosenY) # Figure out min and max Y min_y_this = min(ys, default=None) if min_y is None: min_y = min_y_this elif min_y_this is not None: min_y = min(min_y, min_y_this) max_y_this = max(ys, default=None) if max_y is None: max_y = max_y_this elif max_y_this is not None: max_y = max(max_y, max_y_this) self.subplot.plot(xs, ys) legend.append('{} ({})'.format(fit.name, fit.ship.item.getShortName())) except Exception as ex: pyfalog.warning("Invalid values in '{0}'", fit.name) self.SetStatusText("Invalid values in '%s'" % fit.name) self.canvas.draw() return y_range = max_y - min_y min_y -= y_range * 0.05 max_y += y_range * 0.05 if min_y == max_y: min_y -= min_y * 0.05 max_y += min_y * 0.05 if min_y == max_y: min_y -= 5 max_y += 5 self.subplot.set_ylim(bottom=min_y, top=max_y) if mpl_version < 2: if self.legendFix and len(legend) > 0: leg = self.subplot.legend(tuple(legend), "upper right", shadow=False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) elif not self.legendFix and len(legend) > 0: leg = self.subplot.legend(tuple(legend), "upper right", shadow=False, frameon=False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) elif mpl_version >= 2: legend2 = [] legend_colors = { 0: "blue", 1: "orange", 2: "green", 3: "red", 4: "purple", 5: "brown", 6: "pink", 7: "grey", } for i, i_name in enumerate(legend): try: selected_color = legend_colors[i] except: selected_color = None legend2.append(Patch(color=selected_color, label=i_name), ) if len(legend2) > 0: leg = self.subplot.legend(handles=legend2) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) self.canvas.draw() self.SetStatusText("") self.Refresh() def onFieldChanged(self, event): view = self.getView() view.clearCache() self.delayedDraw() def AppendFitToList(self, fitID): sFit = Fit.getInstance() fit = sFit.getFit(fitID) if fit not in self.fits: self.fits.append(fit) self.fitList.fitList.update(self.fits) self.draw() def OnLeftDClick(self, event): row, _ = self.fitList.fitList.HitTest(event.Position) if row != -1: try: fit = self.fits[row] except IndexError: pass else: self.removeFits([fit]) def removeFits(self, fits): toRemove = [f for f in fits if f in self.fits] if not toRemove: return for fit in toRemove: self.fits.remove(fit) self.fitList.fitList.update(self.fits) view = self.getView() for fit in fits: view.clearCache(key=fit.ID) self.draw() def getSelectedFits(self): fits = [] for row in self.fitList.fitList.getSelectedRows(): try: fit = self.fits[row] except IndexError: continue fits.append(fit) return fits
class DispCheckModifyWidget(FigureCanvas): selectChanged = QtCore.pyqtSignal(object, bool) stateChanged = QtCore.pyqtSignal(object, object) def __init__(self, parent=None, width=5, height=4, dpi=100): self.fig = Figure(figsize=(width, height), dpi=dpi, facecolor='none', frameon=True, linewidth=5) self.ax = self.fig.add_axes([0.1, 0.1, 0.8, 0.8], facecolor='none') FigureCanvas.__init__(self, self.fig) self.setParent(parent) FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding, #QtGui.QSizePolicy.Minimum, #QtGui.QSizePolicy.Minimum, ) FigureCanvas.updateGeometry(self) def set_EGF_file(self, EGF_file): self.EGFfile = EGF_file def set_Disp_file(self, Disp_file): self.dispfile = Disp_file def set_sta_pair(self, sta_pair): self.sta_pair = sta_pair def get_EGF_file(self): return self.EGFfile def get_Disp_file(self): return self.dispfile def get_sta_pair(self): return self.sta_pair def set_gv_para(self, gvmin, gvmax, gvdelta): self.gvmin = gvmin self.gvmax = gvmax self.gvdelta = gvdelta def set_T_para(self, Tmin, Tmax, Tdelta): self.Tmin = Tmin self.Tmax = Tmax self.Tdelta = Tdelta def set_cfile_format(self, cfile_format): self.cfile_format = cfile_format def set_accept_state(self, state): assert(state in ['P', 'A', 'R']) self.accept = state def get_gv_para(self): return self.gvmin, self.gvmax, self.gvdelta def get_T_para(self): return self.Tmin, self.Tmax, self.Tdelta def get_cfile_format(self): return self.cfile_format def get_accept_state(self): return self.accept def save_disp(self, folder, filename=None): if filename is None: filename = self.disp.filename self.disp.save(os.path.join(folder, filename)) def plot_init(self): self.disp = Disp(self.dispfile) self.gvi = GrpVelImg(self.EGFfile, cfile_format=self.cfile_format, gvmin = self.gvmin, gvmax = self.gvmax, gvdelta = self.gvdelta, Tmin = self.Tmin, Tmax = self.Tmax, Tdelta = self.Tdelta) mpl.rcParams['xtick.labelsize'] = 8 mpl.rcParams['ytick.labelsize'] = 8 self.gvimage = self.ax.imshow(self.gvi.img, origin='lower', extent=self.gvi.extent, cmap='jet', aspect=self.gvi.aspect) self.mask_origin = self.disp.masked_disp.mask.copy() self.displine_origin, = self.ax.plot(self.disp.T, self.disp.masked_disp, '.', color='gray', lw=2) self.displine, = self.ax.plot(self.disp.T, self.disp.masked_disp, '.w', lw=2) for vg, c in zip([1.0, 2.0, 3.0], ['y--', 'g--', 'b--']): x = self.gvi.dist / (2.0*vg) self.ax.plot([x,x],[0,1], c, transform=self.ax.get_xaxis_transform()) if DEBUG: print(self.disp.masked_disp) #self.accept = 'P' self.accept_dict = dict(P='Pending', A='Accept', R='Reject') self.accept_color_dict = dict(P='none', A='green', R='red') self.accept_text = self.ax.text(1,1, self.accept_dict[self.accept], ha='right', va='top', transform=self.ax.transAxes) self.fig.set_facecolor(self.accept_color_dict[self.accept]) self.sta_pair_text = self.ax.text(0.5, 1, self.sta_pair, ha='center', va = 'top', transform=self.ax.transAxes) self.ax.set_xlim(*self.gvi.extent[:2]) self.ax.set_ylim(*self.gvi.extent[2:]) self.ax.set_xlabel('Period (s)', fontsize=10) self.ax.set_ylabel('Velocity (km/s)', fontsize=10) self.bline = None self.eline = None self.b = 2*self.gvi.T[0] - self.gvi.T[1] self.e = 2*self.gvi.T[-1] - self.gvi.T[-2] self.modify_state = False self.shift_state = False self.alt_state = False self.selected = False self.track_disp_line = None def connect(self): self.fig.canvas.mpl_connect('button_press_event', self.onclick) self.fig.canvas.mpl_connect('figure_enter_event', self.mouse_enter) self.fig.canvas.mpl_connect('figure_leave_event', self.mouse_leave) def onclick(self, event): if DEBUG: if event.xdata is None: print("Out of axes") else: if event.button == 1: print(" Click on (%.2f,%.2f)" % (event.xdata, event.ydata)) elif event.button == 3: print("Right Click on (%.2f,%.2f)" % (event.xdata, event.ydata)) if event.button == 1: if self.modify_state: x, y = event.xdata, event.ydata xi = np.argmin(np.abs(x - self.disp.T)) self.disp.masked_disp.data[xi] = y elif self.shift_state: T0, gv0 = event.xdata, event.ydata gvmin, gvmax, gvdelta = self.get_gv_para() Tmin, Tmax, Tdelta = self.get_T_para() i0 = int((T0-Tmin)/Tdelta) j0 = int((gv0-gvmin)/gvdelta) self.tracked_disp = track_peak(i0, j0, self.gvi.img) self.tracked_disp = self.tracked_disp * gvdelta + gvmin self.disp.masked_disp.data = self.tracked_disp.copy() #if self.track_disp_line is None: # self.track_disp_line, = \ # self.ax.plot(self.gvi.T, self.tracked_disp, 'w+') #else: # self.track_disp_line.set_ydata(self.tracked_disp) elif self.alt_state: pass else: self.b = event.xdata if self.b is None: # out of axes self.b = 2*self.gvi.T[0]-self.gvi.T[1] if self.bline is None: self.bline, = self.ax.plot([self.b, self.b], [0, 1], 'w', lw=2, transform=self.ax.get_xaxis_transform()) else: self.bline.set_data([self.b,self.b], [0,1]) elif event.button == 3: if self.modify_state: if not self.shift_state: T0, gv0 = event.xdata, event.ydata gvmin, gvmax, gvdelta = self.get_gv_para() Tmin, Tmax, Tdelta = self.get_T_para() i0 = int((T0-Tmin)/Tdelta) j0 = int((gv0-gvmin)/gvdelta) tracked_disp_f = track_peak_forward(i0, j0, self.gvi.img) tracked_disp_f = tracked_disp_f * gvdelta + gvmin self.disp.masked_disp.data[i0:] = tracked_disp_f[i0:] else: T0, gv0 = event.xdata, event.ydata gvmin, gvmax, gvdelta = self.get_gv_para() Tmin, Tmax, Tdelta = self.get_T_para() i0 = int((T0-Tmin)/Tdelta) j0 = int((gv0-gvmin)/gvdelta) tracked_disp_b = track_peak_backward(i0, j0, self.gvi.img) tracked_disp_b = tracked_disp_b * gvdelta + gvmin self.disp.masked_disp.data[:i0+1] = tracked_disp_b[:i0+1] elif self.shift_state: T0, gv0 = event.xdata, event.ydata gvmin, gvmax, gvdelta = self.get_gv_para() Tmin, Tmax, Tdelta = self.get_T_para() i0 = int((T0-Tmin)/Tdelta) j0 = int((gv0-gvmin)/gvdelta) self.tracked_disp = track_peak(i0, j0, self.gvi.img) self.tracked_disp = self.tracked_disp * gvdelta + gvmin self.disp.masked_disp.data[:] = self.tracked_disp[:] elif self.alt_state: pass else: self.e = event.xdata if self.e is None: # out of axes self.e = 2*self.gvi.T[-1]-self.gvi.T[-2] if self.eline is None: self.eline, = self.ax.plot([self.e, self.e], [0, 1], 'y', lw=2, transform=self.ax.get_xaxis_transform()) else: self.eline.set_data([self.e,self.e], [0,1]) self.newmask = (self.disp.T < self.b) | (self.disp.T > self.e) \ | self.mask_origin if DEBUG: print self.disp.T print self.b, self.e print self.mask_origin print self.newmask self.disp.masked_disp.mask = self.newmask self.displine.set_data(self.disp.T, self.disp.masked_disp) self.accept = 'A' self.stateChanged.emit(self.sta_pair, 'A') self.fig.set_facecolor('green') self.accept_text.set_text('Accept') self.fig.canvas.draw() def onmotion(self, event): if DEBUG: print('Mouse position (matplotlib): {}, {}'.format(\ event.xdata, event.ydata)) def mouse_enter(self, event): self.selected = True self.selectChanged.emit(self.sta_pair, self.selected) print('mouse_enter (emit): {} {}'.format(self.sta_pair, self.selected)) self.fig.set_edgecolor('red') self.fig.canvas.draw() if DEBUG: print('Mouse Enter:') def mouse_leave(self, event): self.selected = False self.selectChanged.emit(self.sta_pair, self.selected) print('mouse_enter (emit): {} {}'.format(self.sta_pair, self.selected)) self.fig.set_edgecolor('white') self.fig.canvas.draw() if DEBUG: print('Mouse Leave:') def onkeypress(self, event): if DEBUG: print('Key pressed: %s' % event.key()) if event.key() == QtCore.Qt.Key_R: if DEBUG: print('Key pressed: R') self.accept = 'R' self.stateChanged.emit(self.sta_pair, 'R') self.fig.set_facecolor('red') self.accept_text.set_text('Reject') self.fig.canvas.draw() elif event.key() == QtCore.Qt.Key_A: if DEBUG: print('Key pressed: A') self.accept = 'A' self.stateChanged.emit(self.sta_pair, 'A') self.fig.set_facecolor('green') self.accept_text.set_text('Accept') self.fig.canvas.draw() elif event.key() == QtCore.Qt.Key_P: self.accept = 'P' self.stateChanged.emit(self.sta_pair, 'P') self.fig.set_facecolor('none') self.accept_text.set_text('Pending') self.fig.canvas.draw() elif event.key() == QtCore.Qt.Key_Control: if DEBUG: print('Key pressed: Control') self.modify_state = True elif event.key() == QtCore.Qt.Key_Shift: self.shift_state = True if DEBUG: print('Key pressed: Shift') print('Shift_state: {}'.format(self.shift_state)) elif event.key() == QtCore.Qt.Key_Alt: self.alt_state = True elif event.key() == QtCore.Qt.Key_F1: self.pop_help_window() #elif event.key() == QtCore.Qt.Key_F: # self.forward_tracking = True #elif event.key() == QtCore.Qt.Key_B: # self.backward_tracking = True def onkeyrelease(self, event): if DEBUG: print('Key released: %s' % event.key()) if event.key() == QtCore.Qt.Key_Control: if DEBUG: print('Key released: Control') self.modify_state = False elif event.key() == QtCore.Qt.Key_Shift: self.shift_state = False if DEBUG: print('Key released: Shift') print('Shift_state: {}'.format(self.shift_state)) elif event.key() == QtCore.Qt.Key_Alt: self.alt_state = False # self.forward_tracking = False #elif event.key() == QtCore.Qt.Key_B: # self.backward_tracking = False def pop_help_window(self): Help_message = """Keys shortcut: Left click: L Limit T Right click: L Limit T Shift: click : pick (automatic) Control: click : pick (manually) right click: pick (auto, leftwards) Shift + right click: pick (auto, rightwards) """ reply = QtGui.QMessageBox.information(self, "Help", Help_message, QtGui.QMessageBox.Ok)
def make_chart_seaborn(model_name, chart_id, options, data_filename=''): points = [] if model_name == "FileData" and data_filename and chart_id == -1: xx, yy = get_data_from_file(data_filename) else: points = make_points(model_name, chart_id) xx = [x[0] for x in points] yy = [y[1] for y in points] if options.get('color') == options.get('bg_color'): color_to_contrast = options.get('color', '#292928') options['bg_color'], options['color'] = get_contrasted_colors( color_to_contrast) seaborn_style = dict() scatter_plot = options.get('flag_scatter_plot', False) show_grid = options.get('flag_show_grid', True) logscale_x = options.get('flag_logscale_x', False) logscale_y = options.get('flag_logscale_y', False) x_label = options.get('x_label', 'x') y_label = options.get('y_label', 'y') title = options.get('title', 'Seaborn') fig = Figure(figsize=(5.0, 5.0), facecolor='#ebebeb') axis = fig.add_subplot(1, 1, 1) axis.set_facecolor(options.get('bg_color', 'white')) axis.set_xlabel(x_label, labelpad=5, fontsize=15) axis.set_ylabel(y_label, labelpad=-5, fontsize=15) axis.set_title(title, fontsize=17) fig.set_edgecolor('red') if logscale_x: axis.set_xscale('log') if logscale_y: axis.set_yscale('log') if show_grid: axis.grid(True) seaborn_style['style'] = 'darkgrid' else: axis.grid(False) plot_kwargs = dict() plot_kwargs['legend'] = False plot_kwargs['color'] = options.get('color', 'black') plot_kwargs['linewidth'] = options.get('line_width', 3) plot_kwargs['markers'] = [options.get('marker', 'dot'), ''] if scatter_plot: Ax = sb.scatterplot(xx, yy, ax=axis, **plot_kwargs) else: Ax = sb.lineplot(xx, yy, ax=axis, **plot_kwargs) for line in Ax.lines: line.set_linestyle(options.get('line_style', 'solid')) return fig
class WxScatter(wx.Panel): def __init__(self, *args, **kwargs): self.mapwidget = kwargs.pop('map', None) self.timewidget = kwargs.pop('time', None) wx.Panel.__init__(self, *args, **kwargs) self.id = wx.NewId() self.gpxfig = Figure() self.ax = self.gpxfig.add_subplot(111, polar=True) self.ax.set_theta_zero_location("N") self.gpxfig.subplots_adjust(right=0.9, left=0.1) # canvas and events self.gpxcanvas = FigureCanvas(self, -1, self.gpxfig) self.gpxcanvas.mpl_connect('draw_event', self.OnDraw) self.gpxcanvas.mpl_connect('scroll_event', self.OnMouseWheel) self.gpxcanvas.mpl_connect('button_press_event', self.OnLeftMouseDown) self.gpxcanvas.mpl_connect('button_release_event', self.OnLeftMouseUp) self.gpxcanvas.mpl_connect('motion_notify_event', self.OnMouseMotion) self.gpxcanvas.mpl_connect('resize_event', self.OnSize) self.gpxcanvas.mpl_connect('figure_enter_event', self.OnMouseEnter) self.gpxcanvas.mpl_connect('figure_leave_event', self.OnMouseLeave) self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.gpxcanvas, 1, wx.LEFT | wx.TOP | wx.GROW | wx.EXPAND) self.SetSizer(self.sizer) msgwrap.register(self.OnSigCurChanged, "CurChanged") msgwrap.register(self.OnSigSelChanged, "SelChanged") msgwrap.register(self.OnSigValChanged, "ValChanged") #that code does not work on linux... color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) self.gpxfig.set_facecolor( (color.red / 255.0, color.green / 255.0, color.blue / 255.0)) self.gpxfig.set_edgecolor( (color.red / 255.0, color.green / 255.0, color.blue / 255.0)) self.gpxcanvas.SetBackgroundColour(color) #plugin specific initialization self.thetasrc = 'course' self.radiussrc = 'speed' self.autoscale = True self.grid = False self.kwargs = {'color': '#0000FF'} self.grid = False def Plot(self, xrange=None, yrange=None): self.ax.cla() self.ax.scatter(self.gpx[(self.thetasrc,1,1)]/360*2*np.pi,\ self.gpx[(self.radiussrc,1,1)],\ c=self.gpx[(self.radiussrc,1,1)],\ marker='o', cmap=cm.jet) self.ax.set_theta_zero_location("N") self.ax.set_theta_direction(-1) self.ax.grid(self.grid) self.gpxcanvas.draw() self.OnSize(None) def AttachGpx(self, data): self.gpx = data self.Plot() self.OnSize(None) def DetachGpx(self): self.gpx = None def OnSigSelChanged(self, arg1, arg2, arg3): if arg1 == self.id: return def OnSigValChanged(self, arg1): if arg1 == self.id: return self.Plot() def OnSigCurChanged(self, arg1, arg2): if arg1 == self.id: return def OnDraw(self, event): pass def OnSize(self, event): pixels = self.GetClientSize() if pixels[0] < 20 or pixels[1] < 20: return self.gpxfig.set_size_inches( float(pixels[0]) / self.gpxfig.get_dpi(), float(pixels[1]) / self.gpxfig.get_dpi()) self.gpxfig.subplots_adjust(right=0.85, left=0.15, top=0.85, bottom=0.15) def OnLeftMouseDown(self, event): if event.button == 1: if event.dblclick: try: event.guiEvent.GetEventObject().ReleaseMouse() except: pass self.OnLeftMouseDblClick(event) return def OnLeftMouseDblClick(self, event): (dummy,xlo,xhi,ylo,yhi,self.autoscale,self.grid,\ dummy,self.thetasrc,self.radiussrc,extra)=\ WxQuery("Graph Settings",\ [('wxnotebook','Axes',None,None,None), ('wxentry','Theta Low',None,self.ax.get_xlim()[0],'float'), ('wxentry','Theta High',None,self.ax.get_xlim()[1],'float'), ('wxentry','Radius Low',None,self.ax.get_ylim()[0],'float'), ('wxentry','Radius High',None,self.ax.get_ylim()[1],'float'), ('wxcheck','Autoscale',None,self.autoscale,'bool'), ('wxcheck','Show Grid',None,self.grid,'bool'), ('wxnotebook','Polar plot',None,None,None), ('wxcombo','Theta',self.XAxisAllowed(),self.thetasrc,'str'), ('wxcombo','Radius',self.XAxisAllowed(),self.radiussrc,'str'), ('wxentry','Extra arguments',None,{},'str') ]) self.kwargs.update(ast.literal_eval(extra)) if self.autoscale: self.Plot() else: self.Plot((xlo, xhi), (ylo, yhi)) def OnMouseWheel(self, event): scale_factor = 1.2 if event.button == 'down' else (1.0 / 1.2) rmin, rmax = self.ax.get_ylim() self.ax.set_ylim(rmin * scale_factor, rmax * scale_factor) self.gpxcanvas.draw() def OnLeftMouseUp(self, event): pass def OnMouseMotion(self, event): pass def OnMouseEnter(self, event): pass def OnMouseLeave(self, event): pass def XAxisAllowed(self): l = '' for name in self.gpx.get_header_names(): if name not in ['time', 'ok'] and name[0] != '_': l += '|' + name return l[1:]
class PlotPanel(wx.Panel): """ グラフ描画クラス(派生させて利用すること) """ def __init__(self, parent, id = -1, color = None,\ dpi = None, style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs): """ 初期化 """ wx.Panel.__init__(self, parent, id = id, style = style, **kwargs) self._figure = Figure(None, dpi) self.__canvas = NoRepaintCanvas(self, -1, self._figure) self.SetColor(color) self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) self.__resizeflag = True self.setSize() def SetColor(self, rgbtuple): """ 図形とキャンバスの色を設定する """ if not rgbtuple: rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() col = [c/255.0 for c in rgbtuple] self._figure.set_facecolor(col) self._figure.set_edgecolor(col) self.__canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) def _onSize(self, event): """ サイズイベント発生時の処理 """ self.__resizeflag = True def _onIdle(self, evt): """ アイドルイベント発生時の処理 """ if self.__resizeflag: self.__resizeflag = False self.setSize() self.draw() def setSize(self, pixels = None): """ サイズを変更したい場合に呼び出す。 強制的に描画させたい場合にも有効 """ if not pixels: pixels = self.GetClientSize() self.__canvas.SetSize(pixels) self._figure.set_size_inches(pixels[0]/self._figure.get_dpi(), pixels[1]/self._figure.get_dpi()) def draw(self): """ グラフ描画を行う """ pass
class AbstractPlot(wx.Panel): def __init__(self, parent, color=None, dpi=None, **kwargs): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure # set default matplotlib parameters import matplotlib as mpl fontSize = 10 mpl.rcParams['font.size'] = fontSize mpl.rcParams['axes.titlesize'] = fontSize mpl.rcParams['axes.labelsize'] = fontSize mpl.rcParams['xtick.labelsize'] = fontSize mpl.rcParams['ytick.labelsize'] = fontSize mpl.rcParams['legend.fontsize'] = fontSize # initialize Panel if 'id' not in kwargs.keys(): kwargs['id'] = wx.ID_ANY if 'style' not in kwargs.keys(): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__(self, parent, **kwargs) # initialize matplotlib stuff self.figure = Figure(None, dpi) self.canvas = FigureCanvasWxAgg(self, -1, self.figure) self.SetColor( color ) self.SetPlotSize() self._resizeFlag = False parent.Bind(wx.EVT_SIZE, self._OnSize) self.Bind(wx.EVT_IDLE, self._OnIdle) def _OnSize(self, event): self._resizeFlag = True def _OnIdle(self, event): if self._resizeFlag: self._resizeFlag = False self.SetPlotSize() def SetColor( self, rgbtuple=None ): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor( clr ) self.figure.set_edgecolor( clr ) self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) def SetPlotSize( self ): """ Set plot to fill entire frame. """ pixels = tuple( self.GetParent().GetClientSize() ) self.SetSize( pixels ) self.canvas.SetSize( pixels ) self.figure.set_size_inches( float( pixels[0] )/self.figure.get_dpi(), float( pixels[1] )/self.figure.get_dpi() ) def Draw(self): """Function to be overridden in derived class.""" pass def DrawEmptyPlot(self): """Draw empty plot.""" self.figure.clf() emptyAxis = self.figure.add_axes([0.1,0.1,0.85,0.85]) emptyAxis.set_ylabel('y axis') emptyAxis.set_xlabel('x axis') self.canvas.draw() def Clear(self): """ Clear current figure. """ self.figure.clf() def SavePlot(self, dpi=300): """Save plot to file.""" saveImageDlg = SaveImageDlg(self) if saveImageDlg.ShowModal() == wx.ID_OK: if saveImageDlg.filename != None: self.figure.savefig(saveImageDlg.filename, format=saveImageDlg.format, dpi=saveImageDlg.DPI, facecolor='white', edgecolor='white') saveImageDlg.Destroy()
class wxMatplotPanel( scrolled.ScrolledPanel ): """ The PlotPanel has a Figure and a Canvas. OnSize events simply set a flag, and the actually redrawing of the figure is triggered by an Idle event. """ def __init__( self, renderPanel, color=None, dpi=None, **kwargs ): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure # initialize Panel if 'id' not in list(kwargs.keys()): kwargs['id'] = wx.ID_ANY if 'style' not in list(kwargs.keys()): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE scrolled.ScrolledPanel.__init__( self, renderPanel, **kwargs ) self.renderPanel = renderPanel # initialize matplotlib stuff self.figure = Figure( None, dpi ) #self.canvas = NoRepaintCanvas( self, -1, self.figure ) self.canvas = FigureCanvasWxAgg( self, -1, self.figure ) self.canvas.mpl_connect('button_press_event', self.onMousePress ) self.canvas.mpl_connect('pick_event', self.onPick ) sizer = wx.BoxSizer(); sizer.Add( self.canvas, 1, wx.EXPAND ) self.SetSizer( sizer ) #self.SetAutoLayout(1) #self.SetupScrolling() self.SetColor( color ) self._refresh = False self._updateDraw = False self.toolBar_ = None self.canvasZoomWidth = 1.0 self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) self.resfreshCounter = 0 self.needUpdateHack_ = False self.needDrawing = False self.refresh() def onPick( self, event ): pass def onMousePress( self, event ): pass def onZoomChanged( self ): pass def onPanChanged( self ): pass def getToolBar( self, parent = None ): if not self.toolBar_: self.toolBar_ = wxAUIMatplotPanelToolbar( self, self.canvas, parent ) return self.toolBar_ def SetColor( self, rgbtuple=None ): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor( clr ) self.figure.set_edgecolor( clr ) self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) def _onSize( self, event ): self._refresh = True def _onIdle( self, evt ): if self.IsShownOnScreen(): if self.needDrawing: self.redraw() if self._refresh: self.refresh() self._refresh = False if self._updateDraw: swatch = Stopwatch( True ) self.canvas.draw() if self.needUpdateHack_: self.needUpdateHack() self.needUpdateHack_ = False self._updateDraw = False if self.canvasZoomWidth == 1.0: self.SetupScrolling( False, False ) print("draw: ", swatch.duration()) def updateDrawOnIdle( self ): self._updateDraw = True def resizeOnIdle( self ): self._refresh = True; def refresh( self ): swatch = Stopwatch( True ) #pixels = tuple( self.GetParent().GetClientSize() ) self.resfreshCounter += 1; pixels = tuple( [ int( self.GetSize()[0] * self.canvasZoomWidth ), int( self.GetSize()[1] * 1.0 ) ] ) # print self, self.resfreshCounter, pixels if self.canvas.GetMinSize( )[0] != pixels[0] \ or self.canvas.GetMinSize( )[1] != pixels[1] : #print "resize canvas" #print "parent-size", self.renderPanel.GetSize() #print "self-size", self.GetSize() #print "tupel-size", pixels # to avoid _onSize loop under linux #if self.GetSize() != self.parent.GetClientSize(): #if self.GetSize() != pixels: #self.SetSize( pixels ) #self.canvas.SetSize( pixels ) self.canvas.SetMinSize( pixels ) self.figure.set_size_inches( float( pixels[ 0 ] )/self.figure.get_dpi(), float( pixels[ 1 ] )/self.figure.get_dpi() ) adjust = True if hasattr( self, 'cbar' ): if self.cbar.active: adjust = False; if pixels[ 0 ] > 50 and adjust: self.figure.subplotpars.update( left = 50.0 / pixels[ 0 ] , right = ( pixels[ 0 ] - 20.0 ) / pixels[ 0 ] ) for a in self.figure.axes: if hasattr( a, "update_params" ): a.update_params() #a.set_position( a.figbox, which = 'original' ) a.set_position( a.figbox, which = 'both' ) #self.figure.subplots_adjust( left = 50.0 / pixels[ 0 ] ) #self.figure.subplots_adjust( right = ( pixels[ 0 ] - 20.0 ) / pixels[ 0 ] ) #self.canvas.draw() self.updateDrawOnIdle( ) self.needUpdateHack_ = True; #print "refresh: ", swatch.duration() def draw(self): pass # abstract, to be overridden by child classes def needUpdateHack( self ): pass
def plot_stdleads_stacked(rhythm_data: pd.DataFrame, anns_df: pd.DataFrame = None, fig: figure.Figure = None, dpi: int = 300, textsize: int = 6, ecg_linewidth: float = 0.3, plot_grid: bool = True, grid_color: str = "#a88332", v_offset: float = 1.5, xmin: float = 0.0, xmax: float = 10000.0, ymin: float = -1.5, ymax: float = 1.5, x_margin: float = 280, for_gui: bool = True) -> figure.Figure: """Plots the waveform and annotations in a stacked or superimposed layout Args: rhythm_data (pd.DataFrame): aECG waveform as returned by :any:`Aecg.rhythm_as_df` or :any:`Aecg.derived_as_df`. anns_df (pd.DataFrame, optional): aECG annotations. For example, as returned by pd.DataFrame(the_aecg.DERIVEDANNS[0].anns) where the_aecg is an :any:`Aecg` object. Defaults to None. fig (figure.Figure, optional): Figure containing the plot. Defaults to None. dpi (int, optional): Plot resolution in dots per inch (dpi). Defaults to 300. textsize (int, optional): Default text fontsize. Defaults to 6. ecg_linewidth (float, optional): Line width for the ECG waveform. Defaults to 0.3. plot_grid (bool, optional): Indicates whether to plot the standard ECG grid. Defaults to True. grid_color (str, optional): Color of the ECG grid. Defaults to "#a88332". v_offset (float, optional): Vertical offset between leads in mV. Set to 0 For a superimposed layout. Defaults to 1.5. xmin (float, optional): X axis minimum value in ms. Defaults to 0.0. xmax (float, optional): X axis maximum value in ms. This value may be adjusted automatically when maintaining aspect ratio. Defaults to 10000.0. ymin (float, optional): Y axis minimum value in mV. Defaults to -1.5. ymax (float, optional): Y axis maximum value in mV. This value may be adjusted automatically when maintaining aspect ratio. Defaults to 1.5. x_margin (float, optional): Margin on the X axis in ms. Defaults to 280. for_gui (bool, optional): Indicates whether to plot is generated for a graphical user interface. If true, the figure will be closed before returning the object so a canvas will be needed to render it . Otherwise, the figure will be return immediately. Defaults to True. Returns: figure.Figure: Plot of the aECG waveforms and its annotations """ # Compute maximum height range based on number of leads ecg_ymin = min(ymin, -min(12, (rhythm_data.shape[1] - 1)) * v_offset) ecg_ymax = max(v_offset, ymax) # Compute image size ecg_width = (xmax - xmin + x_margin) / 40.0 # mm (25 mm/s -> 1 mm x 0.04s) ecg_height = (ecg_ymax - ecg_ymin) * 10.0 # mm ( 10 mm/mV -> 1 mm x 0.1 mV) ecg_w_in = ecg_width / 25.4 # inches ecg_h_in = ecg_height / 25.4 # inches # Figure size if fig is None: fig = plt.figure() else: fig.clear() fig.set_size_inches(ecg_w_in, ecg_h_in) fig.set_dpi(dpi) fig.set_facecolor('w') fig.set_edgecolor('k') ax1 = fig.add_axes([0, 0, 1, 1], frameon=False) # ecg grid if plot_grid: grid_major_x = np.arange(xmin, xmax + x_margin, 200.0) grid_minor_x = np.arange(xmin, xmax + x_margin, 40.0) for xc in grid_major_x: ax1.axvline(x=xc, color=grid_color, linewidth=0.5) for xc in grid_minor_x: ax1.axvline(x=xc, color=grid_color, linewidth=0.2) numleads = min(12, len(rhythm_data.columns) - 1) grid_major_y = np.arange(min(ymin, -numleads * v_offset), max(v_offset, ymax), 0.5) grid_minor_y = np.arange(min(ymin, -numleads * v_offset), max(v_offset, ymax), 0.1) for yc in grid_major_y: ax1.axhline(y=yc, color=grid_color, linewidth=0.5) for yc in grid_minor_y: ax1.axhline(y=yc, color=grid_color, linewidth=0.1) # Plot leads stacked with lead I on top and V6 at the bottom idx = 0 lead_zero = 0 ecglibann_voffset = { "RPEAK": 1.0, "PON": 0.7, "QON": 0.4, "QOFF": 0.7, "TOFF": 0.4 } for lead in [ "I", "II", "III", "aVR", "aVL", "aVF", "V1", "V2", "V3", "V4", "V5", "V6" ]: if lead in rhythm_data.columns: lead_zero = -idx * v_offset # ecg calibration pulse ax1.plot([40, 80, 80, 280, 280, 320], [ lead_zero, lead_zero, lead_zero + 1, lead_zero + 1, lead_zero, lead_zero ], color='black', linewidth=0.5) # lead name ax1.text(x_margin + 80, lead_zero + 0.55, lead, size=textsize) ax1.plot(rhythm_data.TIME[rhythm_data[lead].notna()] + x_margin, rhythm_data[lead][rhythm_data[lead].notna()].values + lead_zero, color='black', linewidth=ecg_linewidth) lead_start_time = rhythm_data.TIME[ rhythm_data[lead].notna()].values[0] + x_margin # Plot global annotations if anns_df is not None: if anns_df.shape[0] > 0: ann_voffset = 1.0 for j, ann in anns_df[anns_df["LEADNAM"] == lead].iterrows(): # Annotation type if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ ann["ECGLIBANNTYPE"]] else: ann_voffset = ann_voffset - 0.3 if ann_voffset < 0.0: ann_voffset = 1.0 ax1.text(ann["TIME"] + lead_start_time, lead_zero + ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="blue") # Annotation vertical line ann_x = ann["TIME"] + lead_start_time, ax1.plot([ann_x, ann_x], [lead_zero - 1.0, lead_zero + 1.0], color="blue", linewidth=0.5) idx = idx + 1 # Plot global if anns_df is not None: if anns_df.shape[0] > 0: for idx, ann in anns_df[anns_df["LEADNAM"] == "GLOBAL"].iterrows(): # Annotation type ann_voffset = 1.0 if ann["ECGLIBANNTYPE"] in ecglibann_voffset.keys(): ann_voffset = ecglibann_voffset[ann["ECGLIBANNTYPE"]] ax1.text(ann["TIME"] + xmin + x_margin, lead_zero - ann_voffset, ann["ECGLIBANNTYPE"], size=textsize - 1, color="red") # Annotation vertical line ax1.axvline(x=ann["TIME"] + x_margin, color="red", linewidth=0.5, linestyle=":") # Turn off tick labels ax1.set_xticks([]) ax1.set_yticks([]) # Set figure width and height ax1.set_xlim(xmin, xmax + x_margin) ax1.set_ylim(ecg_ymin, ecg_ymax) if for_gui: # Close plt plt.close() return fig
class DBVisualization(FigureCanvas): def __init__(self, lang: dict, parent=None, width: float = 10, height: float = 10, dpi: int = 100): self.figure = Figure(figsize=(width, height), dpi=dpi) self.database_management = DBManagement() self._lang = lang self.ax = self.figure.add_subplot(111) super(DBVisualization, self).__init__(self.figure) self._theme = None # self._customization() @property def theme(self): return self._theme @theme.setter def theme(self, value): if isinstance(value, dict): self._theme = value def _customization(self): self.figure.set_facecolor('#272c36') self.figure.set_edgecolor('#55aaff') self.ax.set_facecolor('#272c36') self.ax.spines['top'].set_visible(False) self.ax.spines['right'].set_visible(False) self.ax.spines['left'].set_color('r') self.ax.spines['bottom'].set_color('r') self.ax.spines['left'].set_linewidth(3) self.ax.set_title('Title', fontsize=20) # self.ax.grid(color='#55aaff', linestyle='--', linewidth=2) def create_pie_chart_temperatures( self, threshold: int = 37.5, stat_time: tuple = None, temperature: tuple = None, name: str = None, identifiers: list = None, devices: list = None, title: str = 'All time passage of people' # Passage of people for the current day ): self.ax.clear() number_passages = self.database_management.get_number_statistics( time=stat_time, temperature=temperature, name=name, identifiers=identifiers, devices=devices) number_all_persons = np.array(number_passages).sum() if number_all_persons == 0: data = (1, 1) number_all_persons = 2 else: data = number_passages number_normal_temp_stats, number_normal_temp_stranger_stats = \ self.database_management.get_number_normal_temperature_statistics( threshold=threshold, time=stat_time, temperature=temperature, name=name, identifiers=identifiers, devices=devices ) number_temperatures = [ number_normal_temp_stats, number_passages[0] - number_normal_temp_stats, number_normal_temp_stranger_stats, number_passages[1] - number_normal_temp_stranger_stats ] if np.array(number_temperatures).sum() == 0: data_temperatures = [1, 1, 1, 1] else: data_temperatures = number_temperatures colors_temperatures = ['#84F9BD', '#F0939D', '#84F9BD', '#F0939D'] colors = ['#87B7E3', '#91D1EE'] wedges, texts = self.ax.pie(data, colors=colors, startangle=45, frame=False, pctdistance=0.7, wedgeprops=dict(width=0.5)) wedges2, texts2 = self.ax.pie(data_temperatures, colors=colors_temperatures, radius=0.75, startangle=45) centre_circle = plt.Circle((0, 0), 0.5, color='white', fc='white', linewidth=0) self.ax.add_artist(centre_circle) bbox_props = dict(boxstyle='square,pad=0.3', fc='white', ec='#09376B', lw=1.5) kw = dict(arrowprops=dict(arrowstyle='-', color='#09376B'), bbox=bbox_props, zorder=0, va='center', color='#09376B') j = 0 for i, p in enumerate(wedges): ang = (p.theta2 - p.theta1) / 2. + p.theta1 y = np.sin(np.deg2rad(ang)) x = np.cos(np.deg2rad(ang)) connectionstyle = 'angle,angleA=0,angleB={}'.format(ang) kw['arrowprops'].update({'connectionstyle': connectionstyle}) self.ax.annotate( f'{np.round(100 * data[i] / number_all_persons, 2)}% ' f'({number_passages[i]})\n' f'[{self._lang["pie"]["short_norm_temp"]} - {number_temperatures[j]};' f' {self._lang["pie"]["short_heat_temp"]} - {number_temperatures[j + 1]}]', xy=(x, y), xytext=(2 * np.sign(x), 1.5 * y), horizontalalignment='center', fontsize=16, **kw) j += 2 self.ax.set_title(title, color='#09376B', fontsize=20, weight='regular') self.ax.legend([*wedges, *wedges2], [ self._lang['pie']['legend_profile'], self._lang['pie']['legend_stranger'], self._lang['pie']['legend_norm_temp'], self._lang['pie']['legend_heat_temp'] ], loc='best', bbox_to_anchor=(0.1, 0.7), fontsize=14, framealpha=0, labelcolor='#09376B') def create_pie_chart_number_persons(self, low: str = None, high: str = None): pass def create_histogram_temperatures(self, identifier: int, time: tuple = None, temperature: tuple = None): pass def create_line_graph_temperatures(self, identifier: int, time: tuple = None, temperature: tuple = None): name = self.database_management.get_profile_name(identifier) statistics = self.database_management.get_statistics( identifiers=[identifier], time=time, temperature=temperature) statistics.sort(key=lambda x: x.time) temperatures = [] times = [] for statistic in statistics: temperatures.append(float(statistic.temperature)) times.append(statistic.time.timestamp()) self.ax.clear() temperatures = np.array(temperatures) times = np.array(times) self.ax.plot(times, temperatures, '#87B7E3', linewidth=3, marker='o') self.ax.grid(True, color='#09376B') self.ax.spines['top'].set_visible(False) self.ax.spines['right'].set_visible(False) self.ax.spines['left'].set_visible(False) self.ax.spines['bottom'].set_visible(False) self.ax.set_ylabel(self._lang['chart1']['y'], color='#09376B', fontsize=20) self.ax.set_title(name, color='#09376B', fontsize=24, weight='regular') labels = [ datetime.datetime.fromtimestamp(item).strftime('%m/%d %H:%M') for item in self.ax.get_xticks().tolist() ] self.ax.set_xticklabels(labels, color='#09376B') self.ax.tick_params(colors='#09376B', which='both') def create_map_temperatures(self, identifier: int, time: tuple = None, temperature: tuple = None): name = self.database_management.get_profile_name(identifier) statistics = self.database_management.get_statistics( identifiers=[identifier], time=time, temperature=temperature) statistics.sort(key=lambda x: x.time) temperatures = [] times = [] dates = [] for statistic in statistics: temperatures.append(float(statistic.temperature)) time = statistic.time.time() times.append( datetime.timedelta(hours=time.hour, minutes=time.minute, seconds=0).total_seconds()) dates.append(statistic.time.date()) dates = mdates.date2num(dates) self.ax.clear() hb = self.ax.hexbin(x=dates, y=times, C=temperatures, cmap='viridis', gridsize=25) cb = self.figure.colorbar(hb, ax=self.ax, pad=0) cb.outline.set_visible(False) cb.set_label(self._lang['chart2']['bar'], fontsize=20, color='#09376B') cb.ax.tick_params(color='#09376B', labelcolor='#09376B') self.ax.spines['top'].set_visible(False) self.ax.spines['right'].set_visible(False) self.ax.spines['left'].set_visible(False) self.ax.spines['bottom'].set_visible(False) labels = [ str(datetime.timedelta(seconds=item)) for item in self.ax.get_yticks().tolist() ] self.ax.set_yticklabels(labels, color='#09376B') labels = [ mdates.num2date(item).strftime('%m-%d') for item in self.ax.get_xticks().tolist() ] self.ax.set_xticklabels(labels, color='#09376B') self.ax.tick_params(colors='#09376B', which='both') self.ax.set_title(name, color='#09376B', fontsize=24) def create_map_similar(self, statistics: list): pass def save(self, filename): self.figure.savefig(filename)
class HistogramPanel(FigureCanvasWxAgg): def __init__(self, parent, points, bins=100, **kwargs): self.figure = Figure() FigureCanvasWxAgg.__init__(self, parent, -1, self.figure, **kwargs) self.canvas = self.figure.canvas self.SetMinSize((100, 100)) self.figure.set_facecolor((1, 1, 1)) self.figure.set_edgecolor((1, 1, 1)) self.canvas.SetBackgroundColour('white') self.subplot = self.figure.add_subplot(111) self.gate_helper = GatingHelper(self.subplot, self) self.navtoolbar = NavigationToolbar(self.canvas) self.navtoolbar.Realize() self.x_label = '' self.log_y = False self.x_scale = LINEAR_SCALE self.setpoints(points, bins) self.canvas.mpl_connect('button_release_event', self.on_release) def setpoints(self, points, bins): ''' Updates the data to be plotted and redraws the plot. points - array of samples bins - number of bins to aggregate points in ''' points = np.array(points).astype('f') self.bins = bins x_label = self.x_label self.subplot.clear() # log xform the data, ignoring non-positives # XXX: This will not work for selection since the data is changed if self.x_scale in [LOG_SCALE, LOG2_SCALE]: if self.x_scale == LOG_SCALE: points = np.log(points[points > 0]) x_label = 'Log(%s)' % (self.x_label) elif self.x_scale == LOG2_SCALE: points = np.log2(points[points > 0]) x_label = 'Log2(%s)' % (self.x_label) ignored = len(points[points <= 0]) if ignored > 0: logging.warn('Histogram ignored %s negative value%s.' % (ignored, (ignored != 1 and 's' or ''))) # hist apparently doesn't like nans, need to preen them out first points = points[~np.isnan(points)] # nothing to plot? if len(points) == 0: logging.warn('No data to plot.') return self.subplot.hist(points, self.bins, facecolor=[0.0, 0.62, 1.0], edgecolor='none', log=self.log_y, alpha=0.75) self.subplot.set_xlabel(x_label) self.reset_toolbar() def set_x_label(self, label): self.x_label = label def set_x_scale(self, scale): '''scale -- LINEAR_SCALE, LOG_SCALE, or LOG2_SCALE''' self.x_scale = scale def set_y_scale(self, scale): '''scale -- LINEAR_SCALE or LOG_SCALE''' if scale == LINEAR_SCALE: self.log_y = False elif scale == LOG_SCALE: self.log_y = True else: raise 'Unsupported y-axis scale.' def get_toolbar(self): return self.navtoolbar def reset_toolbar(self): '''Clears the navigation toolbar history. Called after setpoints.''' # Cheat since there is no way reset if self.navtoolbar: self.navtoolbar._views.clear() self.navtoolbar._positions.clear() self.navtoolbar.push_current() def set_configpanel(self, configpanel): '''Allow access of the control panel from the plotting panel''' self.configpanel = configpanel def on_release(self, evt): '''click handler''' if evt.button == 3: # right click self.show_popup_menu((evt.x, self.canvas.GetSize()[1] - evt.y), None) def show_popup_menu(self, (x, y), data): '''Show context sensitive popup menu.''' self.popup_menu_filters = {} popup = wx.Menu() loadimages_table_item = popup.Append( -1, 'Create gated table for CellProfiler LoadImages') selected_gate = self.configpanel.gate_choice.get_gatename_or_none() selected_gates = [] if selected_gate: selected_gates = [selected_gate] self.Bind( wx.EVT_MENU, lambda (e): ui.prompt_user_to_create_loadimages_table( self, selected_gates), loadimages_table_item) show_images_in_gate_item = popup.Append(-1, 'Show images in gate') show_images_in_gate_item.Enable(selected_gate is not None) self.Bind(wx.EVT_MENU, self.show_images_from_gate, show_images_in_gate_item) if p.object_table: show_objects_in_gate_item = popup.Append( -1, 'Show %s in gate' % (p.object_name[1])) show_objects_in_gate_item.Enable(selected_gate is not None) self.Bind(wx.EVT_MENU, self.show_objects_from_gate, show_objects_in_gate_item) self.PopupMenu(popup, (x, y))
class PlotPanel(wx.Panel): ''' Base class for the plotting in GenX - all the basic functionallity should be implemented in this class. The plots should be derived from this class. These classes should implement an update method to update the plots. ''' def __init__(self, parent, id = -1, color = None, dpi = None , style = wx.NO_FULL_REPAINT_ON_RESIZE|wx.EXPAND|wx.ALL , config = None, config_name = '', **kwargs): wx.Panel.__init__(self,parent, id = id, style = style, **kwargs) self.parent = parent self.callback_window = self self.config = config self.config_name = config_name self.figure = Figure(None,dpi) self.canvas = FigureCanvasWxAgg(self, -1, self.figure) self.canvas.SetExtraStyle(wx.EXPAND) self.SetColor(color) self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) self._resizeflag = True self.print_size = (15./2.54, 12./2.54) #self._SetSize() # Flags and bindings for zooming self.zoom = False self.zooming = False self.scale = 'linear' self.autoscale = True self.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnLeftMouseButtonDown) self.canvas.Bind(wx.EVT_LEFT_UP, self.OnLeftMouseButtonUp) self.canvas.Bind(wx.EVT_MOTION, self.OnMouseMove) self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDblClick) self.canvas.Bind(wx.EVT_RIGHT_UP, self.OnContextMenu) cursor = wx.StockCursor(wx.CURSOR_CROSS) self.canvas.SetCursor(cursor) self.old_scale_state = True self.ax = None # Init printout stuff self.fig_printer = FigurePrinter(self) # Create the drawing bitmap self.bitmap =wx.EmptyBitmap(1, 1) # DEBUG_MSG("__init__() - bitmap w:%d h:%d" % (w,h), 2, self) self._isDrawn = False def SetColor(self, rgbtuple=None): ''' Set the figure and canvas color to be the same ''' if not rgbtuple: rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() col = [c/255. for c in rgbtuple] self.figure.set_facecolor(col) self.figure.set_edgecolor(col) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) def _onSize(self, evt): self._resizeflag = True self._SetSize() #self.canvas.draw(repaint = False) def _onIdle(self, evt): if self._resizeflag: self._resizeflag = False self._SetSize() #self.canvas.gui_repaint(drawDC = wx.PaintDC(self)) def _SetSize(self, pixels = None): ''' This method can be called to force the Plot to be a desired size which defaults to the ClientSize of the Panel. ''' if not pixels: pixels = self.GetClientSize() self.canvas.SetSize(pixels) #self.figure.set_size_inches(pixels[0]/self.figure.get_dpi() #, pixels[1]/self.figure.get_dpi()) def ReadConfig(self): '''ReadConfig(self) --> None Reads in the config file ''' bool_items = ['zoom', 'autoscale'] bool_func = [self.SetZoom, self.SetAutoScale] if not self.config: return vals = [] for index in range(len(bool_items)): try: val = self.config.get_boolean(self.config_name,\ bool_items[index]) except io.OptionError, e: print 'Could not locate option %s.%s'\ %(self.config_name, bool_items[index]) vals.append(None) else: vals.append(val) try: scale = self.config.get(self.config_name, 'y scale') string_sucess = True except io.OptionError, e: string_sucess = False print 'Could not locate option %s.%s'\ %(self.config_name, 'scale')
class plotHist(wx.Panel): def _init_coll_boxSizer1_Items(self, parent): # generated method, don't edit parent.AddWindow(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) parent.AddWindow(self.toolbar, 0, wx.EXPAND) def _init_sizers(self): # generated method, don't edit self.boxSizer1 = wx.BoxSizer(orient=wx.VERTICAL) self._init_coll_boxSizer1_Items(self.boxSizer1) self.SetSizer(self.boxSizer1) def _init_ctrls(self, prnt): # matplotlib.figure.Figure.__init__(self) wx.Panel.__init__(self, prnt, -1) self.figure = Figure() plot = self.figure.add_subplot(111) plot.set_title("No Data To Plot") self.canvas = FigCanvas(self, -1, self.figure) # Create the navigation toolbar, tied to the canvas self.toolbar = NavigationToolbar(self.canvas, True) self.toolbar.Realize() self.figure.tight_layout() self.setColor("WHITE") self.canvas.SetFont( wx.Font(20, wx.SWISS, wx.NORMAL, wx.NORMAL, False, u'Tahoma')) self.canvas.draw() self._init_sizers() self.bins = 50 def changeNumOfBins(self, bins): self.bins = bins self.updatePlot() def clear(self): self.figure.clear() # plt.clear() def gridSize(self, cells): rows = 1 cols = 1 while rows * cols < cells: if rows == cols: cols = cols + 1 else: rows = rows + 1 return rows, cols def textSize(self, cells): wrap = 50 wrap = wrap - (cells * 3) text = 20 - cells return wrap, text def Plot(self, seriesPlotInfo): self.seriesPlotInfo = seriesPlotInfo self.updatePlot() def updatePlot(self): self.clear() rows, cols = self.gridSize(self.seriesPlotInfo.count()) logger.debug("Rows: %s, cols: %s" % (rows, cols)) i = 1 for oneSeries in self.seriesPlotInfo.getAllSeries(): if len(oneSeries.dataTable) > 0: self._createPlot(oneSeries, rows, cols, i) i += 1 #self.figure.tight_layout() self.canvas.draw() def _createPlot(self, oneSeries, rows, cols, index): ax = self.figure.add_subplot(repr(rows) + repr(cols) + repr(index)) logger.debug("HISTOGRAM: %s" % ax) #logger.debut("rows: %s cols: %s, index: %s"%(rows, cols, index)) # oneSeries.filteredData.hist(ax= ax, color='k', alpha=0.5, bins=50) his = oneSeries.dataTable.hist(column="DataValue", ax=ax, bins=self.bins, facecolor=oneSeries.color, label=oneSeries.siteName + " " + oneSeries.variableName, grid=False) wrap, text = self.textSize(self.seriesPlotInfo.count()) ax.set_xlabel("\n".join(textwrap.wrap(oneSeries.variableName, wrap))) ax.set_ylabel("Number of Observations") self.canvas.SetFont( wx.Font(text, wx.SWISS, wx.NORMAL, wx.NORMAL, False, u'Tahoma')) ax.set_title("\n".join(textwrap.wrap(oneSeries.siteName, wrap))) def setColor(self, color): """Set figure and canvas colours to be the same.""" self.figure.set_facecolor(color) self.figure.set_edgecolor(color) self.canvas.SetBackgroundColour(color) def __init__(self, parent, id, pos, size, style, name): self._init_ctrls(parent)
class plotHist(wx.Panel): def _init_coll_boxSizer1_Items(self, parent): # generated method, don't edit parent.AddWindow(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) parent.AddWindow(self.toolbar, 0, wx.EXPAND) def _init_sizers(self): # generated method, don't edit self.boxSizer1 = wx.BoxSizer(orient=wx.VERTICAL) self._init_coll_boxSizer1_Items(self.boxSizer1) self.SetSizer(self.boxSizer1) def _init_ctrls(self, prnt): #matplotlib.figure.Figure.__init__(self) wx.Panel.__init__(self, prnt, -1) self.figure = Figure() self.plot = self.figure.add_subplot(111) self.plot.set_title("No Data To Plot") self.canvas = FigCanvas(self, -1, self.figure) # Create the navigation toolbar, tied to the canvas self.toolbar = NavigationToolbar(self.canvas, True) self.toolbar.Realize() self.figure.tight_layout() self.setColor("WHITE") self.canvas.SetFont(wx.Font(20, wx.SWISS, wx.NORMAL, wx.NORMAL, False, u'Tahoma')) self.canvas.draw() self._init_sizers() self.hist = [] self.bins = 50 def changeNumOfBins(self, bins): self.bins = bins self.updatePlot() def clear(self): self.figure.clear() self.hist = [] def gridSize(self, cells): rows = 1 cols = 1 while rows * cols < cells: if rows == cols: cols = cols + 1 else: rows = rows + 1 return rows, cols def textSize(self, cells): wrap = 50 wrap = wrap - (cells * 3) text = 20 - cells return wrap, text def Plot(self, seriesPlotInfo): self.seriesPlotInfo = seriesPlotInfo self.updatePlot() def updatePlot(self): self.clear() count = self.seriesPlotInfo.count() rows, cols = self.gridSize(count) self.plots = [] i = 1 for oneSeries in self.seriesPlotInfo.getAllSeries(): self.plots.append(self.figure.add_subplot(repr(rows) + repr(cols) + repr(i))) wrap, text = self.textSize(count) self.plots[i - 1].set_xlabel("\n".join(textwrap.wrap(oneSeries.variableName, wrap))) self.plots[i - 1].set_ylabel("Number of Observations") self.canvas.SetFont(wx.Font(text, wx.SWISS, wx.NORMAL, wx.NORMAL, False, u'Tahoma')) #self.plots[i - 1].set_title( # "\n".join(textwrap.wrap(oneSeries.siteName + " " + oneSeries.variableName, wrap))) self.plots[i - 1].set_title("\n".join(textwrap.wrap(oneSeries.siteName, wrap))) #print "oneSeries.dataTable:", oneSeries.dataTable if len(oneSeries.dataTable) >0: self.hist.append(self.plots[i - 1].hist( [x[0] for x in oneSeries.dataTable if x[0] <> oneSeries.noDataValue], bins=self.bins, normed=False, facecolor=oneSeries.color, label=oneSeries.siteName + " " + oneSeries.variableName ) ) i += 1 left = 0.125 # the left side of the subplots of the figure right = 0.9 # the right side of the subplots of the figure bottom = 0.51 # the bottom of the subplots of the figure top = 1.2 # the top of the subplots of the figure wspace = .8 # the amount of width reserved for blank space between subplots hspace = .8 # the amount of height reserved for white space between subplots self.figure.subplots_adjust( left=left, bottom=bottom, right=right, top=top, wspace=wspace, hspace=hspace ) if len(self.hist)>0: self.figure.tight_layout() self.canvas.draw() def setColor(self, color): """Set figure and canvas colours to be the same.""" self.figure.set_facecolor(color) self.figure.set_edgecolor(color) self.canvas.SetBackgroundColour(color) def __init__(self, parent, id, pos, size, style, name): self._init_ctrls(parent)
class HistogramPanel(FigureCanvasWxAgg): def __init__(self, parent, points, bins=100, **kwargs): self.figure = Figure() FigureCanvasWxAgg.__init__(self, parent, -1, self.figure, **kwargs) self.canvas = self.figure.canvas self.SetMinSize((100,100)) self.figure.set_facecolor((1,1,1)) self.figure.set_edgecolor((1,1,1)) self.canvas.SetBackgroundColour('white') self.subplot = self.figure.add_subplot(111) self.gate_helper = GatingHelper(self.subplot, self) self.navtoolbar = NavigationToolbar(self.canvas) self.navtoolbar.Realize() self.x_label = '' self.log_y = False self.x_scale = LINEAR_SCALE self.setpoints(points, bins) self.canvas.mpl_connect('button_release_event', self.on_release) def setpoints(self, points, bins): ''' Updates the data to be plotted and redraws the plot. points - array of samples bins - number of bins to aggregate points in ''' points = np.array(points).astype('f') self.bins = bins x_label = self.x_label self.subplot.clear() # log xform the data, ignoring non-positives # XXX: This will not work for selection since the data is changed if self.x_scale in [LOG_SCALE, LOG2_SCALE]: if self.x_scale == LOG_SCALE: points = np.log(points[points>0]) x_label = 'Log(%s)'%(self.x_label) elif self.x_scale == LOG2_SCALE: points = np.log2(points[points>0]) x_label = 'Log2(%s)'%(self.x_label) ignored = len(points[points<=0]) if ignored>0: logging.warn('Histogram ignored %s negative value%s.'% (ignored, (ignored!=1 and's' or ''))) # hist apparently doesn't like nans, need to preen them out first points = points[~ np.isnan(points)] # nothing to plot? if len(points)==0: logging.warn('No data to plot.') return self.subplot.hist(points, self.bins, facecolor=[0.0,0.62,1.0], edgecolor='none', log=self.log_y, alpha=0.75) self.subplot.set_xlabel(x_label) self.reset_toolbar() def set_x_label(self, label): self.x_label = label def set_x_scale(self, scale): '''scale -- LINEAR_SCALE, LOG_SCALE, or LOG2_SCALE''' self.x_scale = scale def set_y_scale(self, scale): '''scale -- LINEAR_SCALE or LOG_SCALE''' if scale == LINEAR_SCALE: self.log_y = False elif scale == LOG_SCALE: self.log_y = True else: raise ValueError('Unsupported y-axis scale.') def get_toolbar(self): return self.navtoolbar def reset_toolbar(self): '''Clears the navigation toolbar history. Called after setpoints.''' # Cheat since there is no way reset if self.navtoolbar: self.navtoolbar._views.clear() self.navtoolbar._positions.clear() self.navtoolbar.push_current() def set_configpanel(self,configpanel): '''Allow access of the control panel from the plotting panel''' self.configpanel = configpanel def on_release(self, evt): '''click handler''' if evt.button == 3: # right click self.show_popup_menu((evt.x, self.canvas.GetSize()[1]-evt.y), None) def show_popup_menu(self, (x,y), data): '''Show context sensitive popup menu.''' self.popup_menu_filters = {} popup = wx.Menu() loadimages_table_item = popup.Append(-1, 'Create gated table for CellProfiler LoadImages') selected_gate = self.configpanel.gate_choice.get_gatename_or_none() selected_gates = [] if selected_gate: selected_gates = [selected_gate] self.Bind(wx.EVT_MENU, lambda(e):ui.prompt_user_to_create_loadimages_table(self, selected_gates), loadimages_table_item) show_images_in_gate_item = popup.Append(-1, 'Show images in gate') show_images_in_gate_item.Enable(selected_gate is not None) self.Bind(wx.EVT_MENU, self.show_images_from_gate, show_images_in_gate_item) if p.object_table: show_objects_in_gate_item = popup.Append(-1, 'Show %s in gate'%(p.object_name[1])) show_objects_in_gate_item.Enable(selected_gate is not None) self.Bind(wx.EVT_MENU, self.show_objects_from_gate, show_objects_in_gate_item) self.PopupMenu(popup, (x,y))
class GraphFrame(wx.Frame): def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): global graphFrame_enabled global mplImported global mpl_version self.legendFix = False if not graphFrame_enabled: pyfalog.warning("Matplotlib is not enabled. Skipping initialization.") return try: cache_dir = mpl._get_cachedir() except: cache_dir = os.path.expanduser(os.path.join("~", ".matplotlib")) cache_file = os.path.join(cache_dir, 'fontList.cache') if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): # remove matplotlib font cache, see #234 os.remove(cache_file) if not mplImported: mpl.use('wxagg') graphFrame_enabled = True if int(mpl.__version__[0]) < 1: print(("pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds")) print("pyfa: Recommended minimum matplotlib version is 1.0.0") self.legendFix = True mplImported = True wx.Frame.__init__(self, parent, title="pyfa: Graph Generator", style=style, size=(520, 390)) i = wx.Icon(BitmapLoader.getBitmap("graphs_small", "gui")) self.SetIcon(i) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.CreateStatusBar() self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.mainSizer) sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) self.fits = [fit] if fit is not None else [] self.fitList = FitList(self) self.fitList.SetMinSize((270, -1)) self.fitList.fitList.update(self.fits) self.graphSelection = wx.Choice(self, wx.ID_ANY, style=0) self.mainSizer.Add(self.graphSelection, 0, wx.EXPAND) self.figure = Figure(figsize=(4, 3)) rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas = Canvas(self, -1, self.figure) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) self.subplot = self.figure.add_subplot(111) self.subplot.grid(True) self.mainSizer.Add(self.canvas, 1, wx.EXPAND) self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND) self.gridPanel = wx.Panel(self) self.mainSizer.Add(self.gridPanel, 0, wx.EXPAND) dummyBox = wx.BoxSizer(wx.VERTICAL) self.gridPanel.SetSizer(dummyBox) self.gridSizer = wx.FlexGridSizer(0, 4, 0, 0) self.gridSizer.AddGrowableCol(1) dummyBox.Add(self.gridSizer, 0, wx.EXPAND) for view in Graph.views: view = view() self.graphSelection.Append(view.name, view) self.graphSelection.SetSelection(0) self.fields = {} self.select(0) self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) self.mainSizer.Add(self.sl1, 0, wx.EXPAND) self.mainSizer.Add(self.fitList, 0, wx.EXPAND) self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.mainFrame.Bind(GE.FIT_CHANGED, self.draw) self.Bind(wx.EVT_CLOSE, self.close) self.Fit() self.SetMinSize(self.GetSize()) def handleDrag(self, type, fitID): if type == "fit": self.AppendFitToList(fitID) def close(self, event): self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem) self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw) event.Skip() def getView(self): return self.graphSelection.GetClientData(self.graphSelection.GetSelection()) def getValues(self): values = {} for fieldName, field in self.fields.items(): values[fieldName] = field.GetValue() return values def select(self, index): view = self.getView() icons = view.getIcons() labels = view.getLabels() sizer = self.gridSizer self.gridPanel.DestroyChildren() self.fields.clear() # Setup textboxes for field, defaultVal in view.getFields().items(): textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) self.fields[field] = textBox textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) if defaultVal is not None: if not isinstance(defaultVal, str): defaultVal = ("%f" % defaultVal).rstrip("0") if defaultVal[-1:] == ".": defaultVal += "0" textBox.ChangeValue(defaultVal) imgLabelSizer = wx.BoxSizer(wx.HORIZONTAL) if icons: icon = icons.get(field) if icon is not None: static = wx.StaticBitmap(self.gridPanel) static.SetBitmap(icon) imgLabelSizer.Add(static, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 1) if labels: label = labels.get(field) label = label if label is not None else field else: label = field imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) sizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) self.draw() def draw(self, event=None): global mpl_version if event is not None: event.Skip() # todo: FIX THIS, see #1430. draw() is not being unbound properly when the window closes, this is an easy fix, # but not a proper solution if not self: pyfalog.warning("GraphFrame handled event, however GraphFrame no longer exists. Ignoring event") return values = self.getValues() view = self.getView() self.subplot.clear() self.subplot.grid(True) legend = [] for fit in self.fits: try: success, status = view.getPoints(fit, values) if not success: # TODO: Add a pwetty statys bar to report errors with self.SetStatusText(status) return x, y = success, status self.subplot.plot(x, y) legend.append(fit.name) except Exception as ex: pyfalog.warning("Invalid values in '{0}'", fit.name) self.SetStatusText("Invalid values in '%s'" % fit.name) self.canvas.draw() return if mpl_version < 2: if self.legendFix and len(legend) > 0: leg = self.subplot.legend(tuple(legend), "upper right", shadow=False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) elif not self.legendFix and len(legend) > 0: leg = self.subplot.legend(tuple(legend), "upper right", shadow=False, frameon=False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) elif mpl_version >= 2: legend2 = [] legend_colors = { 0: "blue", 1: "orange", 2: "green", 3: "red", 4: "purple", 5: "brown", 6: "pink", 7: "grey", } for i, i_name in enumerate(legend): try: selected_color = legend_colors[i] except: selected_color = None legend2.append(Patch(color=selected_color, label=i_name), ) if len(legend2) > 0: leg = self.subplot.legend(handles=legend2) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) self.canvas.draw() self.SetStatusText("") def onFieldChanged(self, event): self.draw() def AppendFitToList(self, fitID): sFit = Fit.getInstance() fit = sFit.getFit(fitID) if fit not in self.fits: self.fits.append(fit) self.fitList.fitList.update(self.fits) self.draw() def removeItem(self, event): row, _ = self.fitList.fitList.HitTest(event.Position) if row != -1: del self.fits[row] self.fitList.fitList.update(self.fits) self.draw()
class PlotPanel(wx.Panel): """The PlotPanel has a Figure and a Canvas. OnSize events simply set a flag, and the actual resizing of the figure is triggered by an Idle event.""" def __init__(self, parent, color=None, dpi=None, **kwargs): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure self.parent = parent # initialize Panel if 'id' not in kwargs.keys(): kwargs['id'] = wx.ID_ANY if 'style' not in kwargs.keys(): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__(self, parent, **kwargs) # initialize matplotlib stuff self.figure = Figure(None, dpi) self.canvas = FigureCanvasWxAgg(self, -1, self.figure) self.SetColor(color) self._SetSize() #self.draw() self._resizeflag = False self._vis_flag = False self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) self.Bind(wx.EVT_SET_FOCUS, lambda e: self.draw()) def SetColor(self, rgbtuple=None): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) def _onSize(self, event): self._resizeflag = True #print 'Resizing Plot' #self._SetSize() def _onIdle(self, evt): if self._resizeflag: self._resizeflag = False self._SetSize() #print(self.IsShown()) if self.IsShownOnScreen(): if not self._vis_flag: #print('s') self._vis_flag = True self.draw() else: self._vis_flag = False def _SetSize(self): pixels = tuple(self.GetClientSize()) if not tuple(self.canvas.GetSize()) == pixels: self.SetSize(pixels) self.canvas.SetSize(pixels) self.figure.set_size_inches( float(pixels[0]) / self.figure.get_dpi(), float(pixels[1]) / self.figure.get_dpi()) try: if self.IsShownOnScreen(): self.draw() except: pass def draw(self): pass # abstract, to be overridden by child classes
class BarPanel (wx.Panel): """The BarPanel has a Figure and a Canvas. OnSize events simply set a flag, and the actual resizing of the figure is triggered by an Idle event.""" def __init__( self, parent, color=None, dpi=None, **kwargs ): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure self.parent=parent wx.Panel.__init__( self, parent, **kwargs ) # initialize matplotlib stuf self.figure = Figure( None, dpi ) self.axes = self.figure.add_subplot(111) self.canvas = FigureCanvas(self, -1, self.figure) self.SetColor( color ) self._SetSize() self.speedup1=[1.,1.,1.,1.,1.,1.,1.] #used for rolling average self.speedup2=[1.,1.,1.,1.,1.,1.,1.] self._resizeflag = False self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) def SetColor( self, rgbtuple=None ): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor( clr ) self.figure.set_edgecolor( clr ) self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) def _onSize( self, event ): self._resizeflag = True #print 'told to resize' self._SetSize() def _onIdle( self, evt ): if self._resizeflag: self._resizeflag = False #print 'told to resize' self._SetSize() def _SetSize( self ): pixels = self.GetClientSize() #tuple( self.parent.GetClientSize() ) self.SetSize( pixels ) self.canvas.SetSize( pixels ) self.figure.set_size_inches( float( pixels[0] )/self.figure.get_dpi(), float( pixels[1] )/self.figure.get_dpi() ) def setData(self,inValList): tempVals=inValList #if tempVals[0]==0: # tempVals[0]=1 tempVals[0]=0.00001 val1=tempVals[1]/float(tempVals[0]) if(val1<0.1):val1=0.1 val2=tempVals[2]/float(tempVals[0]) if(val2<0.1):val2=0.2 del self.speedup1[0] self.speedup1.append(val1) del self.speedup2[0] self.speedup2.append(val2) def draw(self): self.axes.clear() self.figure.subplots_adjust(top=0.83) self.axes.bar([1,2,3],[0.01,mean(self.speedup1),mean(self.speedup2)],color=['r','b','g']) self.axes.set_xticks([]) self.axes.set_yticks([]) self.axes.set_xticklabels([]) self.axes.set_yticklabels([]) self.axes.set_title('Speed Relative to GPU-ANN') self.axes.text(0.001,0.5,r'$10^-5x$') speedStr1 = '%4.3ex'%mean(self.speedup1) speedStr2 = '%4.3ex'%mean(self.speedup2) self.axes.text(2.05,mean(self.speedup1)*1.1,speedStr1) self.axes.text(3.05,mean(self.speedup2)/2.0,speedStr2) self.canvas.draw()
class PlotPanel(wx.Panel): ''' Principal Component Analysis (PCA) plot (PCA1 against PCA2) GUI ''' def __init__(self, parent, id=-1, dpi=None, **kwargs): wx.Panel.__init__(self, parent, id=id, **kwargs) self.figure = Figure(dpi=dpi, figsize=(2, 2)) self.canvas = Canvas(self, -1, self.figure) self.figure.set_facecolor((1, 1, 1)) self.figure.set_edgecolor((1, 1, 1)) self.canvas.SetBackgroundColour('white') self.subplot = self.figure.add_subplot(111) self.plot_scores = None self.class_masks = None self.class_names = None self.Loadings = None self.object_opacity = None self.object_accuracies = None self.leg = None self.maskedPCA1 = None self.maskedPCA2 = None self.axes = None # If the script is loaded from ClassifierGUI, load the classification weaklearners try: self.classifier = classifier self.classifier_rules = classifier.algorithm.weak_learners except: self.classifier_rules = [('None', 0, np.array([0, 0]))] self.chMap = p.image_channel_colors self.toolbar = NavigationToolbar2Wx(self.canvas) self.toolbar.Realize() POSITION_OF_CONFIGURE_SUBPLOTS_BTN = 6 self.toolbar.DeleteToolByPos(POSITION_OF_CONFIGURE_SUBPLOTS_BTN) self.statusBar = wx.StatusBar(self, -1) self.statusBar.SetFieldsCount(1) self.motion_event_active = False self.canvas.mpl_connect('motion_notify_event', self.update_status_bar) self.canvas.mpl_connect('button_press_event', self.on_open_image) self.hide_legend_btn = wx.Button(self, -1, " Hide legend ") wx.EVT_BUTTON(self.hide_legend_btn, -1, self.hide_show_legend) self.hide_legend = True tools_sizer = wx.BoxSizer(wx.HORIZONTAL) tools_sizer.Add(self.toolbar, 0, wx.RIGHT | wx.EXPAND) tools_sizer.AddSpacer(5) tools_sizer.Add(self.hide_legend_btn, 0, wx.LEFT | wx.EXPAND) tools_sizer.AddSpacer(5) tools_sizer.Add(self.statusBar, 0, wx.LEFT | wx.EXPAND) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, 1, wx.EXPAND) sizer.Add(tools_sizer, 0, wx.EXPAND) self.SetSizer(sizer) def set_plot_type(self, plot_scores): ''' Set the plot type (Scores. Loadings) for each notebook page ''' self.plot_scores = plot_scores def set_colormap(self, class_array): ''' Set the colormap based on the number of different classes to plot ''' self.colormap = cm.get_cmap('hsv') num_colors = len(class_array) class_value = np.array(list(range(1, (num_colors + 2))), dtype='float') / num_colors color_set = np.array(self.colormap(class_value)) return color_set def on_open_image(self, event): if event.button == 2 and self.plot_scores == "Scores" and event.inaxes: self.open_image() def open_image(self): ''' Open the image of the selected cell in the Scores plot ''' imViewer = ShowImage(self.actual_key[:-1], self.chMap[:], parent=self.classifier, brightness=1.0, contrast=None) imViewer.imagePanel.SelectPoint(db.GetObjectCoords(self.actual_key)) def hide_show_legend(self, event): ''' Hide or show the legend on the canvas by pressing the button ''' if self.leg is not None: if self.hide_legend: self.leg.set_visible(False) self.figure.canvas.draw() self.hide_legend = False self.hide_legend_btn.SetLabel(label='Show legend') else: self.leg.set_visible(True) self.figure.canvas.draw() self.hide_legend = True self.hide_legend_btn.SetLabel(label=' Hide legend ') def update_status_bar(self, event): ''' Show the key for the nearest object (measured as the Euclidean distance) to the mouse pointer in the plot (scores pdimensredux.PlotPanel.__init__dimensredux.PlotPanel.__init__lot) or the nearest feature (loadings plot) ''' if event.inaxes and self.motion_event_active: x, y = event.xdata, event.ydata if self.plot_scores == "Scores": dist = np.hypot((x - self.Scores[:, 0]), (y - self.Scores[:, 1])) object_dict_key = np.where(dist == np.amin(dist)) xy_key = int(object_dict_key[0][0]) if self.object_accuracies: errorData = ', CA = %0.1f%%' % ( (1 - self.object_opacity[xy_key]) * 100.0) else: errorData = '' self.statusBar.SetStatusText( ("Object key = " + str(self.data_dic[xy_key]) + errorData), 0) self.actual_key = self.data_dic[xy_key] elif self.plot_scores == "Loadings": dist = np.hypot((x - self.Loadings[0]), (y - self.Loadings[1])) feature_dict_key = np.where(dist == np.amin(dist)) xy_key = int(feature_dict_key[0]) feat_text = self.features_dic[xy_key].split('_') self.statusBar.SetStatusText(('_'.join(feat_text[1:])), 0) def plot_pca(self): ''' Plot the Principal Component Analysis scores (cells) and loadings (features) along with the percentage of data variance the scores represent ''' self.subplot.clear() # Only obtain class data from the database if no data is available yet if self.class_masks is None or self.class_names is None: self.class_masks, self.class_names = self.create_class_masks() self.data = np.nan_to_num(self.data) # Eliminate NaNs # Calculate PCA-SVD and mask data with class information centered = self.mean_center(self.data) U, S, self.Loadings, explained_variance = self.pca_svd( centered, 100, True) self.Scores = np.array(U[:, 0:2]) self.maskedPCA1, self.maskedPCA2 = self.mask_data( len(self.class_names), self.class_masks, self.Scores) self.axes = explained_variance[0:2] self.color_set = self.set_colormap(self.class_names) # Plot the first two PCAs' Scores in the Scores canvas if self.plot_scores == "Scores": handles = [] labels = [] # Determine the different opacities for the objects. This is set to 1 if no opacities have been specified. if self.object_opacity is None: self.object_opacity = np.ones([self.maskedPCA1.shape[0], 1]) self.object_accuracies = False elif self.object_accuracies is None: self.object_accuracies = True opacities = np.unique(self.object_opacity) nOpacity = len(opacities) # For each class and opacity combination plot the corresponding objects for i in range(len(self.class_names)): cell_count = np.shape(np.nonzero(self.maskedPCA1[:, i])) for j in range(nOpacity): showObjects = np.where(self.object_opacity == opacities[j]) subHandle = self.subplot.scatter( self.maskedPCA1[showObjects[0], i], self.maskedPCA2[showObjects[0], i], 8, c=self.color_set[i, :], linewidth="0.25", alpha=0.25 + 0.75 * opacities[j]) # The highest opacity objects are added to the legend if opacities[j] == np.max(opacities): handles.append(subHandle) labels.append(self.class_names[i] + ': ' + str(cell_count[1])) # Construct the legend and make up the rest of the plot self.leg = self.subplot.legend(handles, labels, loc=4, fancybox=True, handlelength=1) self.leg.get_frame().set_alpha(0.25) x_var = round(((1 - self.axes[0]) * 100), 2) y_var = round(((self.axes[0] - self.axes[1]) * 100), 2) x_axe_var = 'Explained variance: ' + str(x_var) + '%' y_axe_var = 'Explained variance: ' + str(y_var) + '%' self.subplot.set_xlabel(x_axe_var, fontsize=12) self.subplot.set_ylabel(y_axe_var, fontsize=12) self.subplot.axhline(0, -100000, 100000, c='k', lw=0.1) self.subplot.axvline(0, -100000, 100000, c='k', lw=0.1) self.figure.canvas.draw() elif self.plot_scores == "Loadings": # Plot the first two PCAs' Loadings in the Loading canvas weaklearners_mask = np.zeros((np.shape(self.Loadings[0]))) for key in list(self.features_dic.keys()): for value in self.classifier_rules: if value[0] == self.features_dic[key]: weaklearners_mask[key] += 1 scatter_mask = weaklearners_mask + 1 colors_mask = [] size_mask = [] for i in range(len(scatter_mask)): colors_mask.append(COLORS[int(scatter_mask[i])]) size_mask.append((int(scatter_mask[i])**2) * 5) self.subplot.scatter(self.Loadings[0], self.Loadings[1], c=colors_mask, s=size_mask, linewidth="0.5", marker='o') self.subplot.axhline(0, -100000, 100000, c='k', lw=0.1) self.subplot.axvline(0, -100000, 100000, c='k', lw=0.1) self.figure.canvas.draw() self.motion_event_active = True def plot_tsne(self): ''' Plot the t-Distributed Stochastic Neighbor Embedding (t-SNE) distribution of the data ''' self.subplot.clear() self.data = np.nan_to_num(self.data) # Eliminate NaNs centered = self.mean_center(self.data) standardized = self.standardization(centered) # Calculate t-SNE of the data and mask it (python t-SNE version if Intel IPP is not installed) try: from calc_tsne import calc_tsne U = calc_tsne(standardized, 2, 50, 20.0) except: logging.warning( '''Could not use fast t-SNE. You may need to install the Intel Integrated Performance Libraries. Will use normal t-SNE instead.''' ) try: from .tsne import tsne U = tsne(standardized, 2, 50, 20.0) except: logging.error( '''Both t-SNE versions failed. Your dataset may be too large for t-SNE to handle. Will not plot t-SNE results.''' ) return self.Scores = U[:, 0:2] if self.class_masks is None or self.class_names is None: self.class_masks, self.class_names = self.create_class_masks() self.masked_X, self.masked_Y = self.mask_data(len(self.class_names), self.class_masks, self.Scores) # Plot the masked t-SNE results in the Scores canvas self.color_set = self.set_colormap(self.class_names) handles = [] labels = [] # Determine the different opacities for the objects. This is set to 1 if no opacities have been specified. if self.object_opacity is None: self.object_opacity = np.ones([self.masked_X.shape[0], 1]) self.object_accuracies = False elif self.object_accuracies is None: self.object_accuracies = True opacities = np.unique(self.object_opacity) nOpacity = len(opacities) # For each class and opacity combination plot the corresponding objects for i in range(len(self.class_names)): cell_count = np.shape(np.nonzero(self.masked_X[:, i])) for j in range(nOpacity): showObjects = np.where(self.object_opacity == opacities[j]) subHandle = self.subplot.scatter(self.masked_X[showObjects, i], self.masked_Y[showObjects, i], 8, c=self.color_set[i, :], linewidth="0.25", alpha=0.25 + 0.75 * opacities[j]) # The highest opacity objects are added to the legend if opacities[j] == np.max(opacities): handles.append(subHandle) labels.append(self.class_names[i] + ': ' + str(cell_count[1])) self.leg = self.subplot.legend(handles, labels, loc=4, fancybox=True, handlelength=1) self.leg.get_frame().set_alpha(0.25) self.subplot.axhline(0, -100000, 100000, c='k', lw=0.1) self.subplot.axvline(0, -100000, 100000, c='k', lw=0.1) self.figure.canvas.draw() self.motion_event_active = True def clean_canvas(self): self.subplot.clear() def standardization(self, centered_data): ''' Standardize data prior to calculation in order to improve the performance over measurements with large differences in their value ranges ''' standards = np.std(centered_data, 0) for value in standards: if value == 0: logging.error( 'Division by zero, cannot proceed (an object measurements in your dataset has 0 standard deviation, please check your database)' ) standardized_data = centered_data / standards return standardized_data def mean_center(self, raw_data): ''' Centering the measurements data around the mean is necessary prior to calculation ''' row, col = np.shape(raw_data) centered_data = raw_data mean_data = raw_data.mean(axis=0) for i in range(row): centered_data[i] -= mean_data centered_data = centered_data[:, np.var(centered_data, axis=0) != 0] return centered_data def pca_svd(self, data, PCs=100, standardize=True): ''' Calculate the eigenvectors of the data array using SVD (Singular Value Decomposition) method ''' row, col = np.shape(data) if PCs > col: PCs = col if standardize: data = self.standardization(data) import time U, S, V = np.linalg.svd(data, full_matrices=False) # Calculate the percentage of data measurements variance each PCA explains E = data.copy() row, col = np.shape(E) explained_variance = np.zeros((PCs)) total_explained_variance = 0 init_total_error = np.sum(np.square(E)) for k in range(PCs): T = (U[:, k].reshape(row, 1)) * S[k] V_t = np.transpose(V) P = V_t[:, k].reshape(col, 1) E = E - T * (np.transpose(P)) total_error = np.sum(np.square(E)) total_object_residual_variance = (total_error / init_total_error) explained_variance[ k] = 1 - total_object_residual_variance - total_explained_variance total_explained_variance += explained_variance[k] return U, S, V, explained_variance def create_class_masks(self): ''' Create class masks for the data based on the classification data from CPAnalyst. This is done in order to print Scoring plots with points in different colors for each class ''' class_data = db.execute('SELECT class, class_number FROM %s ' \ 'ORDER BY %s ASC, %s ASC' % (p.class_table, \ p.image_id, p.object_id)) class_name_number = set([result for result in class_data]) class_name_number = sorted(class_name_number, key=itemgetter(1)) class_names = [item[0] for item in class_name_number] class_number = np.array([result[1] for result in class_data]) num_classes = len(class_names) # In case class numbers are missing in the range (for instance some classes # that were available to train objects in have no objects classified in them) # the class numbers should be remapped class_ids = [item[1] for item in class_name_number] max_id = np.max(class_ids) if len(class_ids) != max_id: logging.info( 'Found non-consecutive class IDs. Remapping class IDs.') missing_ids = np.flipud( np.setdiff1d(np.arange(max_id) + 1, class_ids)) while missing_ids.shape != (0, ): indices = class_number >= missing_ids[0] class_number[indices] -= 1 missing_ids = np.delete(missing_ids, 0) class_masks = np.zeros((len(class_number), num_classes)) for i in range(len(class_number)): class_col = class_number[i] - 1 class_masks[i, class_col] = 1 return class_masks, class_names def mask_data(self, num_classes, class_masks, Scores): ''' Mask the Score matrixes using the masks from create_class_mask ''' row = np.size(Scores[:, 0]) col = num_classes masked_data_X = np.zeros((row, col)) masked_data_Y = np.zeros((row, col)) for i in range(num_classes): masked_data_X[:, i] = Scores[:, 0] * class_masks[:, i] masked_data_Y[:, i] = Scores[:, 1] * class_masks[:, i] return masked_data_X, masked_data_Y
class plotProb(wx.Panel): def _init_coll_boxSizer1_Items(self, parent): # generated method, don't edit parent.AddWindow(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) parent.AddWindow(self.toolbar, 0, wx.EXPAND) def _init_sizers(self): # generated method, don't edit self.boxSizer1 = wx.BoxSizer(orient=wx.VERTICAL) self._init_coll_boxSizer1_Items(self.boxSizer1) self.SetSizer(self.boxSizer1) def clear(self): self.figure.clear() def close(self): #self.plot.clf() self.plots.close('all') def _init_ctrls(self, prnt): #matplotlib.figure.Figure.__init__(self) wx.Panel.__init__(self, prnt, -1) self.figure = Figure() ax = self.figure.add_subplot(111) ax.axis([0, 1, 0, 1]) # ax.plot([], []) ax.set_title("No Data To Plot") self.islegendvisible = False self.canvas = FigCanvas(self, -1, self.figure) # Create the navigation toolbar, tied to the canvas self.toolbar = NavigationToolbar(self.canvas) self.toolbar.Realize() self.axislist = {} left = 0.125 # the left side of the subplots of the figure right = 0.9 # the right side of the subplots of the figure bottom = 0.51 # the bottom of the subplots of the figure top = 1.2 # the top of the subplots of the figure wspace = .8 # the amount of width reserved for blank space between subplots hspace = .8 # the amount of height reserved for white space between subplots self.figure.subplots_adjust( left=left, bottom=bottom, right=right, top=top, wspace=wspace, hspace=hspace ) self.figure.tight_layout() #self.canvas.SetCursor(wx.StockCursor(wx.CURSOR_CROSS)) #self.canvas.SetScrollbar(wx.HORIZONTAL, 0,5, 1000) self.setColor("WHITE") self.canvas.SetFont(wx.Font(20, wx.SWISS, wx.NORMAL, wx.NORMAL, False, u'Tahoma')) self.fontP = FontProperties() self.fontP.set_size('small') self.canvas.draw() self._init_sizers() def onPlotType(self, ptype): # self.timeSeries.clear() if ptype == "line": ls = '-' m = 'None' elif ptype == "point": ls = 'None' m = 's' else: ls = '-' m = 's' # print plt.setp(self.lines) # print(len(self.lines)) self.format = ls + m for _, line in self.axislist.iteritems(): plt.setp(line, linestyle=ls, marker=m) if self.islegendvisible: self.onShowLegend(self.islegendvisible) self.canvas.draw() def onShowLegend(self, isVisible): self.islegendvisible = isVisible if isVisible: leg = self.plots.legend(loc='upper right', ncol=2, fancybox=True, prop=self.fontP) leg.get_frame().set_alpha(.5) leg.draggable(state=True) else: self.plots.legend_ = None self.canvas.draw() def Plot(self, seriesPlotInfo): self.seriesPlotInfo = seriesPlotInfo self.updatePlot() def updatePlot(self): self.clear() count = self.seriesPlotInfo.count() # keep track of all of the axes self.axislist = {} self.plots = self.figure.add_subplot(111) for oneSeries in self.seriesPlotInfo.getAllSeries(): self.plots.set_xlabel("Cumulative Frequency < Stated Value %") if count > 1: self.plots.set_ylabel("\n".join(textwrap.wrap(oneSeries.axisTitle, 50))) self.plots.set_title("") else: self.plots.set_ylabel("\n".join(textwrap.wrap(oneSeries.axisTitle, 50))) self.plots.set_title("\n".join(textwrap.wrap(oneSeries.siteName, 55))) if len(oneSeries.dataTable) > 0: #self.prob.append( #prop = oneSeries.Probability.plot(column="DataValue", ax=self.plots) #todo FutureWarning: order is deprecated, use sort_values(...) #xValues = oneSeries.Probability.xAxis.order().values xValues = oneSeries.Probability.xAxis.order().values # yValues = oneSeries.Probability.yAxis.order().values yValues = oneSeries.Probability.yAxis.order().values ax = self.plots.plot(xValues, yValues, 'bs', color=oneSeries.color, label=oneSeries.plotTitle) self.axislist[oneSeries.axisTitle] = ax[0] self.setXaxis() '''if count > 1: plt.subplots_adjust(bottom=.1 + .1) self.plot.legend(loc='upper center', ncol=2, prop=self.fontP) else: plt.subplots_adjust(bottom=.1) self.plot.legend_ = None''' #if len(self.plots)>0: self.figure.tight_layout() self.canvas.draw() def addPlot(self, cursor, series, Filter): # self.cursor = Values[0] self.cursor = cursor self.cursor.execute("SELECT DataValue FROM DataValues" + Filter) self.dataValues = [x[0] for x in self.cursor.fetchall()] self.Series = series self.plots.clear() length = len(self.dataValues) self.Yaxis = sorted(self.dataValues) self.Xaxis = [] for it in range(0, length): curFreq = self.calcualteProbabilityFreq(it + 1, length) curX = self.calculateProbabilityXPosition(curFreq) self.Xaxis.append(curX) self.plots.clear() x = range(len(self.Xaxis)) self.plots.set_xlabel("Cumulative Frequency < Stated Value %") self.plots.set_ylabel( "\n".join(textwrap.wrap(self.Series.variable_name + " (" + self.Series.variable_units_name + ")", 50))) self.plots.set_title("\n".join(textwrap.wrap(self.Series.site_name + " " + self.Series.variable_name, 55))) self.plots = self.figure.add_subplot(111) self.prob = self.plots.plot(self.Xaxis, self.Yaxis, 'bs') #self.figure.autofmt_xdate() self.setXaxis() self.canvas.draw() def setXaxis(self): self.plots.set_xbound(1, 100) def setColor(self, color): """Set figure and canvas colours to be the same.""" self.figure.set_facecolor(color) self.figure.set_edgecolor(color) self.canvas.SetBackgroundColour(color) def __init__(self, parent, id, pos, size, style, name): self._init_ctrls(parent)
class plotHist(wx.Panel): def _init_coll_boxSizer1_Items(self, parent): # generated method, don't edit parent.AddWindow(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) parent.AddWindow(self.toolbar, 0, wx.EXPAND) def _init_sizers(self): # generated method, don't edit self.boxSizer1 = wx.BoxSizer(orient=wx.VERTICAL) self._init_coll_boxSizer1_Items(self.boxSizer1) self.SetSizer(self.boxSizer1) def _init_ctrls(self, prnt): # matplotlib.figure.Figure.__init__(self) wx.Panel.__init__(self, prnt, -1) self.figure = Figure() plot = self.figure.add_subplot(111) plot.set_title("No Data To Plot") self.canvas = FigCanvas(self, -1, self.figure) # Create the navigation toolbar, tied to the canvas self.toolbar = NavigationToolbar(self.canvas, True) self.toolbar.Realize() self.figure.tight_layout() self.setColor("WHITE") self.canvas.SetFont(wx.Font(20, wx.SWISS, wx.NORMAL, wx.NORMAL, False, u'Tahoma')) self.canvas.draw() self._init_sizers() self.bins = 50 def changeNumOfBins(self, bins): self.bins = bins self.updatePlot() def clear(self): self.figure.clear() # plt.clear() def gridSize(self, cells): rows = 1 cols = 1 while rows * cols < cells: if rows == cols: cols = cols + 1 else: rows = rows + 1 return rows, cols def textSize(self, cells): wrap = 50 wrap = wrap - (cells * 3) text = 20 - cells return wrap, text def Plot(self, seriesPlotInfo): self.seriesPlotInfo = seriesPlotInfo self.updatePlot() def updatePlot(self): self.clear() rows, cols = self.gridSize(self.seriesPlotInfo.count()) logger.debug("Rows: %s, cols: %s" % (rows,cols)) i = 1 for oneSeries in self.seriesPlotInfo.getAllSeries(): if len(oneSeries.dataTable) > 0: self._createPlot(oneSeries, rows, cols, i) i += 1 #self.figure.tight_layout() self.canvas.draw() def _createPlot(self, oneSeries, rows, cols, index): ax = self.figure.add_subplot(repr(rows) + repr(cols) + repr(index)) logger.debug("HISTOGRAM: %s"% ax) # oneSeries.filteredData.hist(ax= ax, color='k', alpha=0.5, bins=50) his = oneSeries.dataTable.hist(column="DataValue", ax=ax, bins=self.bins, facecolor=oneSeries.color, label=oneSeries.siteName + " " + oneSeries.variableName, grid=False) wrap, text = self.textSize(self.seriesPlotInfo.count()) ax.set_xlabel("\n".join(textwrap.wrap(oneSeries.variableName, wrap))) ax.set_ylabel("Number of Observations") self.canvas.SetFont(wx.Font(text, wx.SWISS, wx.NORMAL, wx.NORMAL, False, u'Tahoma')) ax.set_title("\n".join(textwrap.wrap(oneSeries.siteName, wrap))) def setColor(self, color): """Set figure and canvas colours to be the same.""" self.figure.set_facecolor(color) self.figure.set_edgecolor(color) self.canvas.SetBackgroundColour(color) def __init__(self, parent, id, pos, size, style, name): self._init_ctrls(parent)
class GraphFrame(wx.Frame): def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): global enabled global mplImported self.legendFix = False if not enabled: return try: import matplotlib as mpl if not mplImported: mpl.use('wxagg') from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas from matplotlib.figure import Figure enabled = True if mpl.__version__[0] != "1": print "pyfa: Found matplotlib version ",mpl.__version__, " - activating OVER9000 workarounds" print "pyfa: Recommended minimum matplotlib version is 1.0.0" self.legendFix = True except: print "Problems importing matplotlib; continuing without graphs" enabled = False return mplImported = True wx.Frame.__init__(self, parent, title=u"pyfa: Graph Generator", style=style, size=(520, 390)) i = wx.IconFromBitmap(bitmapLoader.getBitmap("graphs_small", "icons")) self.SetIcon(i) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.CreateStatusBar() self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.mainSizer) sFit = service.Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) self.fits = [fit] if fit is not None else [] self.fitList = FitList(self) self.fitList.SetMinSize((270, -1)) self.fitList.fitList.update(self.fits) self.graphSelection = wx.Choice(self, wx.ID_ANY, style=0) self.mainSizer.Add(self.graphSelection, 0, wx.EXPAND) self.figure = Figure(figsize=(4, 3)) rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor( clr ) self.figure.set_edgecolor( clr ) self.canvas = Canvas(self, -1, self.figure) self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) self.subplot = self.figure.add_subplot(111) self.subplot.grid(True) self.mainSizer.Add(self.canvas, 1, wx.EXPAND) self.mainSizer.Add(wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0 , wx.EXPAND) self.gridPanel = wx.Panel(self) self.mainSizer.Add(self.gridPanel, 0, wx.EXPAND) dummyBox = wx.BoxSizer(wx.VERTICAL) self.gridPanel.SetSizer(dummyBox) self.gridSizer = wx.FlexGridSizer(0, 4) self.gridSizer.AddGrowableCol(1) dummyBox.Add(self.gridSizer, 0, wx.EXPAND) for view in Graph.views: view = view() self.graphSelection.Append(view.name, view) self.graphSelection.SetSelection(0) self.fields = {} self.select(0) self.sl1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) self.mainSizer.Add(self.sl1,0, wx.EXPAND) self.mainSizer.Add(self.fitList, 0, wx.EXPAND) self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.mainFrame.Bind(GE.FIT_CHANGED, self.draw) self.Bind(wx.EVT_CLOSE, self.close) self.Fit() self.SetMinSize(self.GetSize()) def handleDrag(self, type, fitID): if type == "fit": self.AppendFitToList(fitID) def close(self, event): self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem) self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw) event.Skip() def getView(self): return self.graphSelection.GetClientData(self.graphSelection.GetSelection()) def getValues(self): values = {} for fieldName, field in self.fields.iteritems(): values[fieldName] = field.GetValue() return values def select(self, index): view = self.getView() icons = view.getIcons() labels = view.getLabels() sizer = self.gridSizer self.gridPanel.DestroyChildren() self.fields.clear() #Setup textboxes for field, defaultVal in view.getFields().iteritems(): textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) self.fields[field] = textBox textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) if defaultVal is not None: if not isinstance(defaultVal, basestring): defaultVal = ("%f" % defaultVal).rstrip("0") if defaultVal[-1:] == ".": defaultVal = defaultVal + "0" textBox.ChangeValue(defaultVal) imgLabelSizer = wx.BoxSizer(wx.HORIZONTAL) if icons: icon = icons.get(field) if icon is not None: static = wx.StaticBitmap(self.gridPanel) static.SetBitmap(icon) imgLabelSizer.Add(static, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 1) if labels: label = labels.get(field) label = label if label is not None else field else: label = field imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) sizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) self.draw() def draw(self, event=None): values = self.getValues() view = self.getView() self.subplot.clear() self.subplot.grid(True) legend = [] for fit in self.fits: try: success, status = view.getPoints(fit, values) if not success: #TODO: Add a pwetty statys bar to report errors with self.SetStatusText(status) return x, y = success, status self.subplot.plot(x, y) legend.append(fit.name) except: self.SetStatusText("Invalid values in '%s'" % fit.name) self.canvas.draw() return if self.legendFix and len(legend) > 0: leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) elif not self.legendFix and len(legend) >0: leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False, frameon = False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) self.canvas.draw() self.SetStatusText("") if event is not None: event.Skip() def onFieldChanged(self, event): self.draw() def AppendFitToList(self, fitID): sFit = service.Fit.getInstance() fit = sFit.getFit(fitID) if fit not in self.fits: self.fits.append(fit) self.fitList.fitList.update(self.fits) self.draw() def removeItem(self, event): row, _ = self.fitList.fitList.HitTest(event.Position) if row != -1: del self.fits[row] self.fitList.fitList.update(self.fits) self.draw()
class DensityPanel(FigureCanvasWxAgg): def __init__(self, parent, **kwargs): self.figure = Figure() FigureCanvasWxAgg.__init__(self, parent, -1, self.figure, **kwargs) self.canvas = self.figure.canvas self.SetMinSize((100,100)) self.figure.set_facecolor((1,1,1)) self.figure.set_edgecolor((1,1,1)) self.canvas.SetBackgroundColour('white') self.subplot = self.figure.add_subplot(111) self.gate_helper = GatingHelper(self.subplot, self) self.navtoolbar = None self.point_list = [] self.gridsize = 50 self.cb = None self.x_scale = LINEAR_SCALE self.y_scale = LINEAR_SCALE self.color_scale = None self.x_label = '' self.y_label = '' self.cmap ='jet' self.canvas.mpl_connect('button_release_event', self.on_release) def setpointslists(self, points): self.subplot.clear() self.point_list = points plot_pts = np.array(points).astype(float) if self.x_scale == LOG_SCALE: plot_pts = plot_pts[(plot_pts[:,0]>0)] if self.y_scale == LOG_SCALE: plot_pts = plot_pts[(plot_pts[:,1]>0)] hb = self.subplot.hexbin(plot_pts[:, 0], plot_pts[:, 1], gridsize=self.gridsize, xscale=self.x_scale, yscale=self.y_scale, bins=self.color_scale, cmap=matplotlib.cm.get_cmap(self.cmap)) if self.cb: # Remove the existing colorbar and reclaim the space so when we add # a colorbar to the new hexbin subplot, it doesn't get indented. self.figure.delaxes(self.figure.axes[1]) self.figure.subplots_adjust(right=0.90) self.cb = self.figure.colorbar(hb) if self.color_scale==LOG_SCALE: self.cb.set_label('log10(N)') self.subplot.set_xlabel(self.x_label) self.subplot.set_ylabel(self.y_label) xmin = np.nanmin(plot_pts[:,0]) xmax = np.nanmax(plot_pts[:,0]) ymin = np.nanmin(plot_pts[:,1]) ymax = np.nanmax(plot_pts[:,1]) # Pad all sides if self.x_scale==LOG_SCALE: xmin = xmin/1.5 xmax = xmax*1.5 else: xmin = xmin-(xmax-xmin)/20. xmax = xmax+(xmax-xmin)/20. if self.y_scale==LOG_SCALE: ymin = ymin/1.5 ymax = ymax*1.5 else: ymin = ymin-(ymax-ymin)/20. ymax = ymax+(ymax-ymin)/20. self.subplot.axis([xmin, xmax, ymin, ymax]) self.reset_toolbar() def getpointslists(self): return self.point_list def setgridsize(self, gridsize): self.gridsize = gridsize def set_x_scale(self, scale): self.x_scale = scale def set_y_scale(self, scale): self.y_scale = scale def set_color_scale(self, scale): if scale==LINEAR_SCALE: scale = None self.color_scale = scale def set_x_label(self, label): self.x_label = label def set_y_label(self, label): self.y_label = label def set_colormap(self, cmap): self.cmap = cmap self.draw() def get_toolbar(self): if not self.navtoolbar: self.navtoolbar = NavigationToolbar(self.canvas) return self.navtoolbar def reset_toolbar(self): # Cheat since there is no way reset if self.navtoolbar: self.navtoolbar._views.clear() self.navtoolbar._positions.clear() self.navtoolbar.push_current() def set_configpanel(self,configpanel): '''Allow access of the control panel from the plotting panel''' self.configpanel = configpanel def on_release(self, evt): if evt.button == 3: # right click self.show_popup_menu((evt.x, self.canvas.GetSize()[1]-evt.y), None) def show_popup_menu(self, (x,y), data): self.popup_menu_filters = {} popup = wx.Menu() loadimages_table_item = popup.Append(-1, 'Create gated table for CellProfiler LoadImages') selected_gate = self.configpanel.gate_choice.get_gatename_or_none() selected_gates = [] if selected_gate: selected_gates = [selected_gate] self.Bind(wx.EVT_MENU, lambda(e):ui.prompt_user_to_create_loadimages_table(self, selected_gates), loadimages_table_item) show_images_in_gate_item = popup.Append(-1, 'Show images in gate') show_images_in_gate_item.Enable(selected_gate is not None) self.Bind(wx.EVT_MENU, self.show_images_from_gate, show_images_in_gate_item) if p.object_table: show_objects_in_gate_item = popup.Append(-1, 'Show %s in gate'%(p.object_name[1])) show_objects_in_gate_item.Enable(selected_gate is not None) self.Bind(wx.EVT_MENU, self.show_objects_from_gate, show_objects_in_gate_item) self.PopupMenu(popup, (x,y))
class PlotPanel (wxmpl.PlotPanel): def __init__( self, parent, headers, color=None, dpi=None, **kwargs ): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure self.overlay = False self.trend = False if 'id' not in kwargs.keys(): kwargs['id'] = wx.ID_ANY if 'style' not in kwargs.keys(): kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE wx.Panel.__init__( self, parent, **kwargs ) self.figure = Figure( None, dpi ) self.canvas = FigureCanvasWxAgg( self, -1, self.figure ) self.canvas.mpl_connect('button_release_event', self.on_release) self.SetColor( color ) self.hs0 = wx.GridSizer(rows=20, cols=2, vgap=6, hgap=6) self.hs1 = wx.BoxSizer(wx.VERTICAL) self.sizer = wx.BoxSizer(wx.HORIZONTAL) self.sizer.Add(self.hs0, flag=wx.EXPAND) self.sizer.Add(self.canvas, wx.EXPAND) self.sizer.Add(self.hs1, flag=wx.EXPAND) self.add_toolbar() self.SetSizer(self.sizer) self._SetSize() self.draw() self._resizeflag = False self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) def add_toolbar(self): self.toolbar = NavigationToolbar2Wx(self.canvas) self.toolbar.Realize() if wx.Platform == '__WXMAC__': # Mac platform (OSX 10.3, MacPython) does not seem to cope with # having a toolbar in a sizer. This work-around gets the buttons # back, but at the expense of having the toolbar at the top self.SetToolBar(self.toolbar) else: # On Windows platform, default window size is incorrect, so set # toolbar width to figure width. tw, th = self.toolbar.GetSizeTuple() fw, fh = self.canvas.GetSizeTuple() # By adding toolbar in sizer, we are able to put it at the bottom # of the frame - so appearance is closer to GTK version. # As noted above, doesn't work for Mac. self.toolbar.SetSize(wx.Size(fw, th)) self.sizer.Add(self.toolbar, 0, wx.TOP | wx.RIGHT) # update the axes menu on the toolbar self.toolbar.update() def SetColor( self, rgbtuple=None ): if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor( clr ) self.figure.set_edgecolor( clr ) self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) def _onSize( self, event ): self._resizeflag = True def _onIdle( self, evt ): if self._resizeflag: self._resizeflag = False self._SetSize() def _SetSize( self ): pixels = tuple( self.parent.GetClientSize() ) self.SetSize( pixels ) self.canvas.SetSize( pixels ) self.figure.set_size_inches( float( pixels[0] )/self.figure.get_dpi(), float( pixels[1] )/self.figure.get_dpi() ) def on_release(self, event): pass def draw(self): pass def draw_trends(self, event): pass def draw_overlays(self, event): pass def color_quadrants(self, quadrants, color): pass def onAbout(self, event): pass def YouAreHere(self, event): pass def updateYouAreHere(self, you_are_here): pass def contrastSet(self, event): pass
class PlotPanel (wx.Panel): """The PlotPanel has a Figure and a Canvas. OnSize events simply set a flag, and the actual resizing of the figure is triggered by an Idle event.""" def __init__( self, parent, color=None, dpi=None, **kwargs ): from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from matplotlib.figure import Figure self.parent=parent wx.Panel.__init__( self, parent, **kwargs ) # initialize matplotlib stuf self.figure = Figure( None, dpi ) self.axes = self.figure.add_subplot(111) self.canvas = FigureCanvas(self, -1, self.figure) self.SetColor( color ) self._SetSize() self.x=[] self.y=[] #self.draw() self._resizeflag = False self.Bind(wx.EVT_IDLE, self._onIdle) self.Bind(wx.EVT_SIZE, self._onSize) def SetColor( self, rgbtuple=None ): """Set figure and canvas colours to be the same.""" if rgbtuple is None: rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() clr = [c/255. for c in rgbtuple] self.figure.set_facecolor( clr ) self.figure.set_edgecolor( clr ) self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) def _onSize( self, event ): self._resizeflag = True #print 'told to resize' self._SetSize() def _onIdle( self, evt ): if self._resizeflag: self._resizeflag = False #print 'told to resize' self._SetSize() def _SetSize( self ): pixels = self.GetClientSize() #tuple( self.parent.GetClientSize() ) self.SetSize( pixels ) self.canvas.SetSize( pixels ) self.figure.set_size_inches( float( pixels[0] )/self.figure.get_dpi(), float( pixels[1] )/self.figure.get_dpi() ) def draw(self): if self.x != None and self.y !=None: self.figure.subplots_adjust(left=0.145) self.axes.plot(self.x[0][1:],self.y[0][1:], 'rv',linestyle='--',linewidth=4.0) self.axes.plot(self.x[1][1:],self.y[1][1:], 'bo',linestyle=':',linewidth=2.0) self.axes.plot(self.x[2][1:],self.y[2][1:], 'g.',linestyle='-',linewidth=1.0) self.axes.legend(('PWScf','ANN','ANN-GPU'),loc='lower right',fancybox=True) self.axes.set_ylim((0,-2.0)) self.axes.set_xlabel('Timestep') self.axes.set_ylabel=('Mean Potential Energy') #self.axes.set_xlim((0,3000)) #ticks=[0.98*self.y[2].max(),self.y[2].max(),1.02*self.y[2].max()] #self.axes.set_ylim((ticks[0],ticks[2])) #strLabels=['%.3f'%(0.98*ticks[0]), # '%.3f'%ticks[1], # '%.3f'%(1.02*ticks[2])] #self.axes.set_yticklabels(strLabels) #self.axes.set_yticks(ticks) self.canvas.draw()
class BoxPlotPanel(FigureCanvasWxAgg): def __init__(self, parent, points, **kwargs): ''' points -- a dictionary mapping x axis values to lists of values to plot ''' self.figure = Figure() FigureCanvasWxAgg.__init__(self, parent, -1, self.figure, **kwargs) self.canvas = self.figure.canvas self.SetMinSize((100,100)) self.figure.set_facecolor((1,1,1)) self.figure.set_edgecolor((1,1,1)) self.canvas.SetBackgroundColour('white') self.navtoolbar = None self.setpoints(points) def setpoints(self, points): ''' Updates the data to be plotted and redraws the plot. points - list of array samples, where each sample will be plotted as a separate box plot against the same y axis ''' self.xlabels = [] self.points = [] ignored = 0 for label, values in sorted(points.items()): if type(label) in [tuple, list]: self.xlabels += [','.join([str(l) for l in label])] else: self.xlabels += [label] self.points += [np.array(values).astype('f')[~ np.isnan(values)]] ignored += len(np.array(values)[np.isnan(values)]) if not hasattr(self, 'subplot'): self.subplot = self.figure.add_subplot(111) self.subplot.clear() # nothing to plot? if len(self.points)==0: logging.warn('No data to plot.') return self.subplot.boxplot(self.points) if len(self.points) > 1: self.figure.autofmt_xdate() self.subplot.set_xticklabels(self.xlabels) self.reset_toolbar() if ignored == 0: logging.info('Boxplot: Plotted %s points.'%(sum(map(len, self.points)))) else: logging.warn('Boxplot: Plotted %s points. Ignored %s NaNs.' %(sum(map(len, self.points)), ignored)) def set_x_axis_label(self, label): self.subplot.set_xlabel(label) def set_y_axis_label(self, label): self.subplot.set_ylabel(label) def get_point_lists(self): return self.points def get_xlabels(self): return self.xlabels def get_toolbar(self): if not self.navtoolbar: self.navtoolbar = NavigationToolbar(self.canvas) self.navtoolbar.DeleteToolByPos(6) return self.navtoolbar def reset_toolbar(self): # Cheat since there is no way reset if self.navtoolbar: self.navtoolbar._views.clear() self.navtoolbar._positions.clear() self.navtoolbar.push_current()
class MyPlotPanel(wx.Panel): def __init__(self, parent, figsize=None, dpi=None, bgcolor=None, type=None, toolbar=None, aspect=1, **kwargs): """ construction method of MyPlotPanel class :param parent: parent object :param figsize: plot figure size, (w, h) :param dpi: figure dpi, :parma bgcolor: background color of figure and canvas :param type: type of initial figure, 'image' or 'line' :param toolbar: show toolbar if not set None """ wx.Panel.__init__(self, parent, **kwargs) self.parent = parent self.figsize = figsize self.dpi = dpi self.bgcolor = bgcolor self.type = type self.toolbar = toolbar self.aspect = aspect self.figure = Figure(self.figsize, self.dpi) self.canvas = FigureCanvas(self, -1, self.figure) # figure background color self.set_color(self.bgcolor) # initialize plot self._init_plot() # set layout self.set_layout() # binding events self.canvas.mpl_connect('button_press_event', self.on_press) self.canvas.mpl_connect('button_release_event', self.on_release) self.canvas.mpl_connect('motion_notify_event', self.on_motion) self.Bind(wx.EVT_SIZE, self.on_size) def set_layout(self): """ set panel layout """ sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, 1, wx.EXPAND) hbox = wx.BoxSizer(wx.HORIZONTAL) if self.toolbar is not None: self.toobar = MyToolbar(self.canvas) self.toobar.Realize() hbox.Add(self.toobar, 0, wx.EXPAND | wx.RIGHT, 10) self.pos_st = wx.StaticText(self, label='') hbox.Add(self.pos_st, 0, wx.ALIGN_CENTER_VERTICAL) sizer.Add(hbox, 0, wx.EXPAND | wx.BOTTOM, 0) self.SetSizerAndFit(sizer) def _init_plot(self): if not hasattr(self, 'axes'): self.axes = self.figure.add_subplot(111, aspect=self.aspect) if self.type == 'image': # draw image x = y = np.linspace(-np.pi, np.pi, 100) self.x, self.y = np.meshgrid(x, y) self.z = self._func_peaks(self.x, self.y) self.image = self.axes.imshow(self.z) else: # draw line self.x = np.linspace(-10, 10, 200) self.y = np.sin(self.x) self.line, = self.axes.plot(self.x, self.y) def set_color(self, rgb_tuple): """ set figure and canvas with the same color. :param rgb_tuple: rgb color tuple, e.g. (255, 255, 255) for white color """ if rgb_tuple is None: rgb_tuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c/255.0 for c in rgb_tuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgb_tuple)) def on_size(self, event): self.fit_canvas() self.canvas.draw_idle() event.Skip() def on_press(self, event): pass def on_release(self, event): pass def on_motion(self, event): if event.inaxes is not None: self.pos_st.SetLabel("({x:<.4f}, {y:<.4f})".format(x=event.xdata, y=event.ydata)) def fit_canvas(self): """ tight fit canvas layout """ #self.canvas.SetSize(self.GetSize()) self.figure.set_tight_layout(True) def _func_peaks(self, x, y): return 3.0 * (1.0 - x)**2.0 * np.exp(-(x**2) - (y+1)**2) \ - 10*(x/5 - x**3 - y**5) * np.exp(-x**2-y**2) \ - 1.0/3.0*np.exp(-(x+1)**2 - y**2)
class MyPlotPanel(wx.Panel): def __init__(self, parent, figsize=None, dpi=None, bgcolor=None, type=None, toolbar=None, aspect='auto', **kwargs): """ construction method of MyPlotPanel class :param parent: parent object :param figsize: plot figure size, (w, h) :param dpi: figure dpi, :parma bgcolor: background color of figure and canvas :param type: type of initial figure, 'image' or 'line' :param toolbar: show toolbar if not set None :param aspect: axes aspect, float number or 'auto' by default """ wx.Panel.__init__(self, parent, **kwargs) self.parent = parent self.figsize = figsize self.dpi = dpi self.bgcolor = bgcolor self.type = type self.toolbar = toolbar self.aspect = aspect self.figure = Figure(self.figsize, self.dpi) self.canvas = FigureCanvas(self, -1, self.figure) # figure background color self.set_color(self.bgcolor) # initialize plot self._init_plot() # set layout self.set_layout() # post-initialization self._post_init() # binding events self.canvas.mpl_connect('button_press_event', self.on_press) self.canvas.mpl_connect('button_release_event', self.on_release) self.canvas.mpl_connect('motion_notify_event', self.on_motion) self.canvas.mpl_connect('pick_event', self.on_pick) self.Bind(wx.EVT_SIZE, self.on_size) self.xylim_choice.Bind(wx.EVT_CHOICE, self.xylim_choiceOnChoice) self.minlim_tc.Bind(wx.EVT_TEXT_ENTER, self.minlim_tcOnTextEnter) self.maxlim_tc.Bind(wx.EVT_TEXT_ENTER, self.maxlim_tcOnTextEnter) def set_layout(self): """ set panel layout """ sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, 1, wx.EXPAND) hbox = wx.BoxSizer(wx.HORIZONTAL) # set toolbar if defined if self.toolbar is not None: self.toobar = MyToolbar(self.canvas) self.toobar.Realize() hbox.Add(self.toobar, 0, wx.EXPAND | wx.RIGHT, 5) # add x[y]lim control xylim_hbox = wx.BoxSizer(wx.HORIZONTAL) xy_vbox = wx.BoxSizer(wx.VERTICAL) xylim_choiceChoices = [u"X-Limit", u"Y-Limit", u"Auto"] self.xylim_choice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, xylim_choiceChoices, 0) self.xylim_choice.SetSelection(0) xy_vbox.Add(self.xylim_choice, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) xylim_hbox.Add(xy_vbox, 0, wx.ALIGN_CENTER_VERTICAL, 1) lim_vbox = wx.BoxSizer(wx.VERTICAL) min_hbox = wx.BoxSizer(wx.HORIZONTAL) self.minlim_st = wx.StaticText(self, wx.ID_ANY, u"Min", wx.DefaultPosition, wx.DefaultSize, 0) self.minlim_st.Wrap(-1) self.minlim_st.SetFont(wx.Font(6, 70, 90, 90, False, "Monospace")) min_hbox.Add(self.minlim_st, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.TOP, 1) self.minlim_tc = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.minlim_tc.SetFont(wx.Font(6, 70, 90, 90, False, "Monospace")) self.minlim_tc.SetToolTip(u"Min of Limit") min_hbox.Add(self.minlim_tc, 0, wx.ALIGN_CENTER_VERTICAL | wx.TOP, 1) lim_vbox.Add(min_hbox, 1, wx.EXPAND, 1) max_hbox = wx.BoxSizer(wx.HORIZONTAL) self.maxlim_st = wx.StaticText(self, wx.ID_ANY, u"Max", wx.DefaultPosition, wx.DefaultSize, 0) self.maxlim_st.Wrap(-1) self.maxlim_st.SetFont(wx.Font(6, 70, 90, 90, False, "Monospace")) max_hbox.Add(self.maxlim_st, 0, wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.RIGHT | wx.TOP, 1) self.maxlim_tc = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.maxlim_tc.SetFont(wx.Font(6, 70, 90, 90, False, "Monospace")) self.maxlim_tc.SetToolTip(u"Max of Limit") max_hbox.Add(self.maxlim_tc, 0, wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.TOP, 1) lim_vbox.Add(max_hbox, 1, wx.EXPAND, 1) xylim_hbox.Add(lim_vbox, 0, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1) hbox.Add(xylim_hbox, 0, wx.EXPAND | wx.RIGHT, 5) # (x, y) pos label self.pos_st = wx.StaticText(self, label='') hbox.Add(self.pos_st, 0, wx.ALIGN_CENTER_VERTICAL) sizer.Add(hbox, 0, wx.EXPAND | wx.BOTTOM, 0) self.SetSizerAndFit(sizer) def _init_plot(self): if not hasattr(self, 'axes'): self.axes = self.figure.add_subplot(111, aspect=self.aspect) if self.type == 'image': # draw image x = y = np.linspace(-np.pi, np.pi, 100) self.x, self.y = np.meshgrid(x, y) self.z = self._func_peaks(self.x, self.y) self.image = self.axes.imshow(self.z) else: # draw line self.x = np.linspace(-10, 10, 200) self.y = np.sin(self.x) self.line, = self.axes.plot(self.x, self.y) def _post_init(self): self._set_xylim_flag(self.xylim_choice.GetStringSelection()) def set_color(self, rgb_tuple): """ set figure and canvas with the same color. :param rgb_tuple: rgb color tuple, e.g. (255, 255, 255) for white color """ if rgb_tuple is None: #rgb_tuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() rgb_tuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_DESKTOP).Get() clr = [c / 255.0 for c in rgb_tuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgb_tuple)) def on_size(self, event): self.fit_canvas() self.canvas.draw_idle() event.Skip() def on_press(self, event): pass def on_release(self, event): pass def on_pick(self, event): pass def on_motion(self, event): if event.inaxes is not None: self.pos_st.SetLabel("({x:<.4f}, {y:<.4f})".format(x=event.xdata, y=event.ydata)) def fit_canvas(self): """ tight fit canvas layout """ #self.canvas.SetSize(self.GetSize()) self.figure.set_tight_layout(True) def _func_peaks(self, x, y): return 3.0 * (1.0 - x)**2.0 * np.exp(-(x**2) - (y+1)**2) \ - 10*(x/5 - x**3 - y**5) * np.exp(-x**2-y**2) \ - 1.0/3.0*np.exp(-(x+1)**2 - y**2) def refresh(self): self.canvas.draw_idle() def xylim_choiceOnChoice(self, event): sel_str = self.xylim_choice.GetStringSelection() self._set_xylim_flag(sel_str) if sel_str == 'Auto': self.minlim_tc.Disable() self.maxlim_tc.Disable() self.minlim_st.Disable() self.maxlim_st.Disable() # auto set xy limit min_list = [ np.vstack(line.get_data()).min(axis=1).tolist() for line in self.axes.get_lines() ] max_list = [ np.vstack(line.get_data()).max(axis=1).tolist() for line in self.axes.get_lines() ] xmin, ymin = np.array(min_list).min(axis=0) xmax, ymax = np.array(max_list).max(axis=0) x0, xhw = (xmin + xmax) * 0.5, (xmax - xmin) * 0.5 y0, yhw = (ymin + ymax) * 0.5, (ymax - ymin) * 0.5 _xmin, _xmax = x0 - xhw * 1.1, x0 + xhw * 1.1 _ymin, _ymax = y0 - yhw * 1.1, y0 + yhw * 1.1 self.axes.set_xlim([_xmin, _xmax]) self.axes.set_ylim([_ymin, _ymax]) self.refresh() else: self.minlim_tc.Enable() self.maxlim_tc.Enable() self.minlim_st.Enable() self.maxlim_st.Enable() try: _xlim = self.axes.get_xlim() _ylim = self.axes.get_ylim() except: _xlim = [0, 100] _ylim = [0, 100] self._set_default_minlim(_xlim, _ylim) self._set_default_maxlim(_xlim, _ylim) def _set_default_minlim(self, xlim_array, ylim_array): if self._xylim == 'X-Limit': self.minlim_tc.SetValue("{xmin:.3g}".format(xmin=xlim_array[0])) elif self._xylim == 'Y-Limit': self.minlim_tc.SetValue("{ymin:.3g}".format(ymin=ylim_array[0])) def _set_default_maxlim(self, xlim_array, ylim_array): if self._xylim == 'X-Limit': self.maxlim_tc.SetValue("{xmax:.3g}".format(xmax=xlim_array[1])) elif self._xylim == 'Y-Limit': self.maxlim_tc.SetValue("{ymax:.3g}".format(ymax=ylim_array[1])) def minlim_tcOnTextEnter(self, event): xymin = float(self.minlim_tc.GetValue()) xymax = float(self.maxlim_tc.GetValue()) self._set_xylim_range(xymin, xymax) def maxlim_tcOnTextEnter(self, event): xymin = float(self.minlim_tc.GetValue()) xymax = float(self.maxlim_tc.GetValue()) self._set_xylim_range(xymin, xymax) def _set_xylim_flag(self, flag='X-Limit'): """ control x/y limit to be set :param flag: 'X-Limit' or 'Y-Limit' """ self._xylim = flag def _set_xylim_range(self, vmin, vmax): """ set x/y limit according to _xylim value :param vmin: min of limit :param vmax: max of limit """ if self._xylim == 'X-Limit': self.axes.set_xlim([vmin, vmax]) elif self._xylim == 'Y-Limit': self.axes.set_ylim([vmin, vmax]) self.refresh()
class PlotWidget(custom_result.ResultWidget): __gsignals__ = { 'button-press-event': 'override', 'button-release-event': 'override', 'expose-event': 'override', 'size-allocate': 'override', 'unrealize': 'override' } def __init__(self, result): custom_result.ResultWidget.__init__(self) figsize=(DEFAULT_FIGURE_WIDTH, DEFAULT_FIGURE_HEIGHT) self.figure = Figure(facecolor='white', figsize=figsize) self.canvas = _PlotResultCanvas(self.figure) self.axes = self.figure.add_subplot(111) self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE) self.cached_contents = None self.sidebar_width = -1 def do_expose_event(self, event): cr = self.window.cairo_create() if not self.cached_contents: self.cached_contents = cr.get_target().create_similar(cairo.CONTENT_COLOR, self.allocation.width, self.allocation.height) renderer = RendererCairo(self.figure.dpi) renderer.set_width_height(self.allocation.width, self.allocation.height) renderer.set_ctx_from_surface(self.cached_contents) self.figure.draw(renderer) # event.region is not bound: http://bugzilla.gnome.org/show_bug.cgi?id=487158 # gdk_context = gtk.gdk.CairoContext(renderer.ctx) # gdk_context.region(event.region) # gdk_context.clip() cr.set_source_surface(self.cached_contents, 0, 0) cr.paint() def do_size_allocate(self, allocation): if allocation.width != self.allocation.width or allocation.height != self.allocation.height: self.cached_contents = None gtk.DrawingArea.do_size_allocate(self, allocation) def do_unrealize(self): gtk.DrawingArea.do_unrealize(self) self.cached_contents = None def do_button_press_event(self, event): if event.button == 3: custom_result.show_menu(self, event, save_callback=self.__save) return True else: return True def do_button_release_event(self, event): return True def do_realize(self): gtk.DrawingArea.do_realize(self) cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR) self.window.set_cursor(cursor) def do_size_request(self, requisition): try: # matplotlib < 0.98 requisition.width = self.figure.bbox.width() requisition.height = self.figure.bbox.height() except TypeError: # matplotlib >= 0.98 requisition.width = self.figure.bbox.width requisition.height = self.figure.bbox.height def recompute_figure_size(self): width = (self.sidebar_width / self.figure.dpi) height = width / DEFAULT_ASPECT_RATIO self.figure.set_figwidth(width) self.figure.set_figheight(height) self.queue_resize() def sync_dpi(self, dpi): self.figure.set_dpi(dpi) if self.sidebar_width >= 0: self.recompute_figure_size() def set_sidebar_width(self, width): if self.sidebar_width == width: return self.sidebar_width = width if self.sidebar_width >= 0: self.recompute_figure_size() def sync_style(self, style): self.cached_contents = None matplotlib.rcParams['font.size'] = self.parent.style.font_desc.get_size() / pango.SCALE def __save(self, filename): # The save/restore here was added to matplotlib's after 0.90. We duplicate # it for compatibility with older versions. (The code would need modification # for 0.98 and newer, which is the reason for the particular version in the # check) version = [int(x) for x in matplotlib.__version__.split('.')] need_save = version[:2] < [0, 98] if need_save: orig_dpi = self.figure.dpi.get() orig_facecolor = self.figure.get_facecolor() orig_edgecolor = self.figure.get_edgecolor() try: self.canvas.print_figure(filename) finally: if need_save: self.figure.dpi.set(orig_dpi) self.figure.set_facecolor(orig_facecolor) self.figure.set_edgecolor(orig_edgecolor) self.figure.set_canvas(self.canvas)
class GraphCanvasPanel(wx.Panel): def __init__(self, graphFrame, parent): super().__init__(parent) self.graphFrame = graphFrame # Remove matplotlib font cache, see #234 try: cache_dir = mpl._get_cachedir() except: cache_dir = os.path.expanduser(os.path.join('~', '.matplotlib')) cache_file = os.path.join(cache_dir, 'fontList.cache') if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): os.remove(cache_file) mainSizer = wx.BoxSizer(wx.VERTICAL) self.figure = Figure(figsize=(5, 3), tight_layout={'pad': 1.08}) rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c / 255. for c in rgbtuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas = Canvas(self, -1, self.figure) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) self.canvas.mpl_connect('button_press_event', self.OnMplCanvasClick) self.subplot = self.figure.add_subplot(111) self.subplot.grid(True) mainSizer.Add(self.canvas, 1, wx.EXPAND | wx.ALL, 0) self.SetSizer(mainSizer) self.xMark = None self.mplOnDragHandler = None self.mplOnReleaseHandler = None def draw(self, accurateMarks=True): self.subplot.clear() self.subplot.grid(True) allXs = set() allYs = set() plotData = {} legendData = [] chosenX = self.graphFrame.ctrlPanel.xType chosenY = self.graphFrame.ctrlPanel.yType self.subplot.set(xlabel=self.graphFrame.ctrlPanel.formatLabel(chosenX), ylabel=self.graphFrame.ctrlPanel.formatLabel(chosenY)) mainInput, miscInputs = self.graphFrame.ctrlPanel.getValues() view = self.graphFrame.getView() sources = self.graphFrame.ctrlPanel.sources if view.hasTargets: iterList = tuple( itertools.product(sources, self.graphFrame.ctrlPanel.targets)) else: iterList = tuple((f, None) for f in sources) # Draw plot lines and get data for legend for source, target in iterList: # Get line style data try: colorData = BASE_COLORS[source.colorID] except KeyError: pyfalog.warning('Invalid color "{}" for "{}"'.format( source.colorID, source.name)) continue color = colorData.hsl lineStyle = 'solid' if target is not None: try: lightnessData = LIGHTNESSES[target.lightnessID] except KeyError: pyfalog.warning('Invalid lightness "{}" for "{}"'.format( target.lightnessID, target.name)) continue color = lightnessData.func(color) try: lineStyleData = STYLES[target.lineStyleID] except KeyError: pyfalog.warning('Invalid line style "{}" for "{}"'.format( target.lightnessID, target.name)) continue lineStyle = lineStyleData.mplSpec color = hsv_to_rgb(hsl_to_hsv(color)) # Get point data try: xs, ys = view.getPlotPoints(mainInput=mainInput, miscInputs=miscInputs, xSpec=chosenX, ySpec=chosenY, src=source, tgt=target) if not self.__checkNumbers(xs, ys): pyfalog.warning( 'Failed to plot "{}" vs "{}" due to inf or NaN in values' .format(source.name, '' if target is None else target.name)) continue plotData[(source, target)] = (xs, ys) allXs.update(xs) allYs.update(ys) # If we have single data point, show marker - otherwise line won't be shown if len(xs) == 1 and len(ys) == 1: self.subplot.plot(xs, ys, color=color, linestyle=lineStyle, marker='.') else: self.subplot.plot(xs, ys, color=color, linestyle=lineStyle) # Fill data for legend if target is None: legendData.append((color, lineStyle, source.shortName)) else: legendData.append( (color, lineStyle, '{} vs {}'.format(source.shortName, target.shortName))) except Exception: pyfalog.warning('Failed to plot "{}" vs "{}"'.format( source.name, '' if target is None else target.name)) self.canvas.draw() self.Refresh() return # Setting Y limits for canvas if self.graphFrame.ctrlPanel.showY0: allYs.add(0) canvasMinY, canvasMaxY = self._getLimits(allYs, minExtra=0.05, maxExtra=0.1) canvasMinX, canvasMaxX = self._getLimits(allXs, minExtra=0.02, maxExtra=0.02) self.subplot.set_ylim(bottom=canvasMinY, top=canvasMaxY) self.subplot.set_xlim(left=canvasMinX, right=canvasMaxX) # Process X marks line if self.xMark is not None: minX = min(allXs, default=None) maxX = max(allXs, default=None) if minX is not None and maxX is not None: minY = min(allYs, default=None) maxY = max(allYs, default=None) xMark = max(min(self.xMark, maxX), minX) # If in top 10% of X coordinates, align labels differently if xMark > canvasMinX + 0.9 * (canvasMaxX - canvasMinX): labelAlignment = 'right' labelPrefix = '' labelSuffix = ' ' else: labelAlignment = 'left' labelPrefix = ' ' labelSuffix = '' # Draw line self.subplot.axvline(x=xMark, linestyle='dotted', linewidth=1, color=(0, 0, 0)) # Draw its X position if chosenX.unit is None: xLabel = '{}{}{}'.format(labelPrefix, roundToPrec(xMark, 4), labelSuffix) else: xLabel = '{}{} {}{}'.format(labelPrefix, roundToPrec(xMark, 4), chosenX.unit, labelSuffix) self.subplot.annotate(xLabel, xy=(xMark, canvasMaxY - 0.01 * (canvasMaxY - canvasMinY)), xytext=(0, 0), annotation_clip=False, textcoords='offset pixels', ha=labelAlignment, va='top', fontsize='small') # Get Y values yMarks = set() def addYMark(val): if val is None: return # If due to some bug or insufficient plot density we're # out of bounds, do not add anything if minY <= val <= maxY: if abs(val) < 0.0001: val = 0 else: val = roundToPrec(val, 4) yMarks.add(val) for source, target in iterList: xs, ys = plotData[(source, target)] if not xs or xMark < min(xs) or xMark > max(xs): continue # Fetch values from graphs when we're asked to provide accurate data if accurateMarks: try: y = view.getPoint(x=xMark, miscInputs=miscInputs, xSpec=chosenX, ySpec=chosenY, src=source, tgt=target) addYMark(y) except Exception: pyfalog.warning( 'Failed to get X mark for "{}" vs "{}"'.format( source.name, '' if target is None else target.name)) # Silently skip this mark, otherwise other marks and legend display will fail continue # Otherwise just do linear interpolation between two points else: if xMark in xs: # We might have multiples of the same value in our sequence, pick value for the last one idx = len(xs) - xs[::-1].index(xMark) - 1 addYMark(ys[idx]) continue idx = bisect(xs, xMark) yMark = self._interpolateX(x=xMark, x1=xs[idx - 1], y1=ys[idx - 1], x2=xs[idx], y2=ys[idx]) addYMark(yMark) # Draw Y values for yMark in yMarks: self.subplot.annotate('{}{}{}'.format( labelPrefix, yMark, labelSuffix), xy=(xMark, yMark), xytext=(0, 0), textcoords='offset pixels', ha=labelAlignment, va='center', fontsize='small') legendLines = [] for i, iData in enumerate(legendData): color, lineStyle, label = iData legendLines.append( Line2D([0], [0], color=color, linestyle=lineStyle, label=label.replace('$', '\$'))) if len(legendLines) > 0 and self.graphFrame.ctrlPanel.showLegend: legend = self.subplot.legend(handles=legendLines) for t in legend.get_texts(): t.set_fontsize('small') for l in legend.get_lines(): l.set_linewidth(1) self.canvas.draw() self.Refresh() def markXApproximate(self, x): if x is not None: self.xMark = x self.draw(accurateMarks=False) def unmarkX(self): self.xMark = None self.draw() @staticmethod def _getLimits(vals, minExtra=0, maxExtra=0): minVal = min(vals, default=0) maxVal = max(vals, default=0) # Extend range a little for some visual space valRange = maxVal - minVal minVal -= valRange * minExtra maxVal += valRange * maxExtra # Extend by % of value if we show function of a constant if minVal == maxVal: minVal -= minVal * 0.05 maxVal += minVal * 0.05 # If still equal, function is 0, spread out visual space as special case if minVal == maxVal: minVal -= 5 maxVal += 5 return minVal, maxVal @staticmethod def _interpolateX(x, x1, y1, x2, y2): pos = (x - x1) / (x2 - x1) y = y1 + pos * (y2 - y1) return y @staticmethod def __checkNumbers(xs, ys): for number in itertools.chain(xs, ys): if math.isnan(number) or math.isinf(number): return False return True # Matplotlib event handlers def OnMplCanvasClick(self, event): if event.button == 1: if not self.mplOnDragHandler: self.mplOnDragHandler = self.canvas.mpl_connect( 'motion_notify_event', self.OnMplCanvasDrag) if not self.mplOnReleaseHandler: self.mplOnReleaseHandler = self.canvas.mpl_connect( 'button_release_event', self.OnMplCanvasRelease) self.markXApproximate(event.xdata) elif event.button == 3: self.unmarkX() def OnMplCanvasDrag(self, event): self.markXApproximate(event.xdata) def OnMplCanvasRelease(self, event): if event.button == 1: if self.mplOnDragHandler: self.canvas.mpl_disconnect(self.mplOnDragHandler) self.mplOnDragHandler = None if self.mplOnReleaseHandler: self.canvas.mpl_disconnect(self.mplOnReleaseHandler) self.mplOnReleaseHandler = None # Do not write markX here because of strange mouse behavior: when dragging, # sometimes when you release button, x coordinate changes. To avoid that, # we just re-use coordinates set on click/drag and just request to redraw # using accurate data self.draw(accurateMarks=True)
class CanvasPanel(wx.Panel): def __init__(self,parent,figsize=(6,6)): wx.Panel.__init__(self,parent) self.SharedVariables() self.figure = Figure(figsize=figsize) self.axes = self.figure.add_subplot(111) self.canvas = FigureCanvas(self, -1, self.figure) self.SetColor(None) buttongrid = self.ButtonGrid() hbox = wx.BoxSizer(wx.HORIZONTAL) sizer = wx.BoxSizer(wx.VERTICAL) self.add_toolbar(sizer) sizer.Add(self.canvas, 0) sizer.Add(buttongrid,0,wx.ALIGN_CENTER_HORIZONTAL) hbox.Add(sizer,0) self.SetSizer(sizer) self.Show(True) ################### # Shared Variables ################### def SharedVariables(self): '''Get variables defined by another panel, needed by this panel.''' self._definecurpanel ############## # Boxes/Grids ############## def ButtonGrid(self): grid = wx.FlexGridSizer(1,5,0,0) x = wx.ALIGN_CENTER_HORIZONTAL rawdatabutton = wx.Button(self,-1,'Raw Data') self.Bind(wx.EVT_BUTTON,self.OnPlotRawDataClick,rawdatabutton) timeplotbutton = wx.Button(self,-1,'Time Plot') self.Bind(wx.EVT_BUTTON,self.OnTimePlotClick,timeplotbutton) self.freqplotbutton = wx.Button(self,-1,'Bode Plot') self.Bind(wx.EVT_BUTTON,self.OnFreqPlotClick,self.freqplotbutton) self.optimizebutton = wx.Button(self,-1,'Optimize') self.Bind(wx.EVT_BUTTON,self.OnOptimizeClick,self.optimizebutton) clearfigbutton = wx.Button(self,-1,'Clear') self.Bind(wx.EVT_BUTTON, self.OnCLF, clearfigbutton) grid.Add(rawdatabutton,0,wx.ALIGN_CENTER_HORIZONTAL) grid.Add(timeplotbutton,0,wx.ALIGN_CENTER_HORIZONTAL) grid.Add(self.freqplotbutton,0,wx.ALIGN_CENTER_HORIZONTAL) grid.Add(self.optimizebutton,0,x) grid.Add(clearfigbutton,0) return grid def add_toolbar(self,sizer): self.toolbar = NavigationToolbar2Wx(self.canvas) self.toolbar.Realize() tw, th = self.toolbar.GetSizeTuple() fw, fh = self.canvas.GetSizeTuple() self.toolbar.SetSize(wx.Size(fw, th)) sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) self.toolbar.update() ######## # Misc. ######## def _definecurpanel(self): self.curpanel = self.Parent.Parent#self.Parent.curpanel def RenewCurpanel(self): self._definecurpanel() def Plot(self,x,y,**kwargs): if type(x)==list: for cx, cy in zip(x,y): self.axes.plot(cx,cy) else: self.axes.plot(x,y) self.canvas.draw() def show(self): self.ReDraw() def ReDraw(self): self.canvas.draw() def clf(self): self.figure.clf() self.axes = self.figure.add_subplot(111) self.canvas.draw() def cla(self): self.axes.cla() self.canvas.draw() def SetColor(self, rgbtuple): """Set figure and canvas colours to be the same""" if not rgbtuple: rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() col = [c/255.0 for c in rgbtuple] self.figure.set_facecolor(col) self.figure.set_edgecolor(col) self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) ######################### # Widget Bound Functions ######################### def OnPlot(self,e): self.Plot() def OnCLA(self,e): self.cla() def OnCLF(self,e): self.clf() def OnPaint(self, event): self.canvas.draw() def OnFreqPlotClick(self,e): self.RenewCurpanel() self.curpanel.FreqPlot() def OnTimePlotClick(self,e): self.RenewCurpanel() self.curpanel.TimePlot() def OnPlotRawDataClick(self,e): self.RenewCurpanel() self.curpanel.RawDataPlot() def OnOptimizeClick(self,e): self.RenewCurpanel() self.curpanel.Optimize()
class MatPlot_Simple(wx.MiniFrame): #Frame): def __init__(self, parent, title='', size=(500, 300), pos=(150, 150), ini=None): FormStyle = wx.DEFAULT_FRAME_STYLE | \ wx.TINY_CAPTION_HORIZ #wx.STAY_ON_TOP self.parent = parent self.Ini = ini if self.parent: FormStyle = FormStyle | wx.FRAME_FLOAT_ON_PARENT # needs a parent if not (self.parent) and self.Ini: ini.Section = 'MatPlot_Simple' pos = ini.Read('Pos', pos) size = ini.Read('Size', size) line = ini.Read('xxx', '') line = eval('[' + line + ']') self.MatPlot_Simple_Load(line) else: rcParams['grid.color'] = '0.4' self.legends = ('aap', 'beer') self.Pseudo_Color = False wx.MiniFrame.__init__(self, parent, -1, title, size=size, pos=pos, style=FormStyle) # EASIER to set these parameters from rcParams, than from rc, # because you can use deep nested properties #from pylab import rc #rc ('figure.subplot', 'left' = 0.5) ## not accepted rcParams['figure.subplot.top'] = 0.95 rcParams['figure.subplot.bottom'] = 0.05 rcParams['figure.subplot.left'] = 0.07 rcParams['figure.subplot.right'] = 0.97 # ************************************************************* # Create the panel with the plot controls # ************************************************************* self.Panel = wx.Panel(self, size=(100, 50)) self.CB_Grid = wx.CheckBox(self.Panel, -1, "-Grid-", pos=(0, 5)) self.CB_Grid.SetValue(self.Initial_Grid) self.CP_Grid = wx.ColourPickerCtrl(self.Panel, -1, self.MatPlot_2_Color( rcParams['grid.color']), pos=(50, 1)) wx.StaticText(self.Panel, -1, "BackGround-", pos=(90, 5)) self.CP_BG = wx.ColourPickerCtrl(self.Panel, -1, self.MatPlot_2_Color(self.BG_color), pos=(155, 1)) # no support for Axis self.CB_Axis = wx.CheckBox(self.Panel, -1, "-Axis", pos=(195, 5)) self.CB_Axis.SetValue(self.Initial_Axis) self.CB_Legend = wx.CheckBox(self.Panel, -1, "-Legend", pos=(250, 5)) self.CB_Legend.SetValue(self.Initial_Legend) self.CB_Polar = wx.CheckBox(self.Panel, -1, "-Low Res", pos=(315, 5)) self.CB_Polar.SetValue(self.Initial_Polar) bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_BUTTON, (16, 16)) self.Button_Image = wx.BitmapButton(self.Panel, -1, bmp, pos=(385, 0)) self.Button_Image.SetToolTipString('Save as PNG-image') if not (self.parent): self.Spin = wx.SpinCtrl(self.Panel, wx.ID_ANY, min=1, max=5, initial=self.Initial_Spin, pos=(415, 2), size=(40, 20)) self.Spin.SetToolTipString('Select Demo') # background color of the not used part of the button bar self.SetBackgroundColour(self.Panel.GetBackgroundColour()) # ************************************************************* # ************************************************************* # ************************************************************* self.figure = Figure() self.Canvas = FigureCanvas(self, -1, self.figure) self.axes = self.figure.add_subplot(111) self.lx = None if not (self.parent): self.MatPlot_Example(self.Initial_Spin) else: self.Set_Figure_Pars() # ************************************************************* # ************************************************************* # ************************************************************* self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.Canvas, 1, wx.EXPAND) self.sizer.Add(self.Panel, 0) self.SetSizer(self.sizer) #self.Fit() # ************************************************************* # ************************************************************* # ************************************************************* self.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnGridColor, self.CP_Grid) self.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnBGColor, self.CP_BG) self.Bind(wx.EVT_CHECKBOX, self.OnSetGrid, self.CB_Grid) self.Bind(wx.EVT_CHECKBOX, self.OnSetAxis, self.CB_Axis) self.Bind(wx.EVT_CHECKBOX, self.OnSetLegend, self.CB_Legend) self.Bind(wx.EVT_CHECKBOX, self.OnPolar, self.CB_Polar) self.Button_Image.Bind(wx.EVT_BUTTON, self.OnSaveImage, self.Button_Image) if not (self.parent): self.Spin.Bind(wx.EVT_SPINCTRL, self.OnSpinEvent, self.Spin) self.Bind(wx.EVT_CLOSE, self.OnClose) #if not ( self.connect ) : self.connect = self.Canvas.mpl_connect('motion_notify_event', self.on_move) # ************************************************************* # ************************************************************* # ************************************************************* def on_move(self, event): if self.CB_Polar.GetValue() and not (self.Pseudo_Color): return x, y = event.x, event.y if event.inaxes: ax = event.inaxes minx, maxx = ax.get_xlim() miny, maxy = ax.get_ylim() x, y = event.xdata, event.ydata if not (self.lx): self.lx, = self.axes.plot((minx, maxx), (y, y), 'k-') # the horiz line self.ly, = self.axes.plot((x, x), (miny, maxy), 'k-') # the vert line self.meas_txt = self.axes.text(0.02, 0.02, '', transform=self.axes.transAxes) # update the crosshair positions self.lx.set_data((minx, maxx), (y, y)) self.ly.set_data((x, x), (miny, maxy)) self.meas_txt.set_text('x=%1.2f, y=%1.2f' % (x, y)) else: # Hide the cross hair if self.lx: self.lx.set_data((x, x), (y, y)) self.ly.set_data((x, x), (y, y)) self.meas_txt.set_text('') self.Canvas.draw() # ************************************************************* # ************************************************************* def OnSpinEvent(self, event): Example = event.GetInt() self.MatPlot_Example(Example) # ************************************************************* # create an example image # ************************************************************* def MatPlot_Example(self, Example): self.Signals = [] if Example == 1: # Sine x = arange(0.0, 3.0, 0.01) y = sin(2 * pi * x) elif Example == 2: # SPIRAL t = arange(0, 10 * pi, 0.1) x = t * sin(t) y = t * cos(t) elif Example == 3: # CARDIOID t = arange(0, 2 * pi, 0.1) x = (1 + cos(t)) * cos(t) y = (1 + cos(t)) * sin(t) elif Example == 4: # SPIROGRAPH phi = linspace(0, 4, 100) #r=sin(phi*pi) # r = sin(cos(tan(phi))) x = phi y = 20 * r elif Example == 5: # Pseudo Color def func3(x, y): return (1 - x / 2 + x**5 + y**3) * exp(-x**2 - y**2) dx, dy = 0.05, 0.05 x = arange(-3.0, 3.0, dx) y = arange(-3.0, 3.0, dy) X, Y = meshgrid(x, y) self.Signals.append(func3(X, Y)) if self.Signals == []: self.Signals.append(x) self.Signals.append(y) # ************************************************************* # ************************************************************* self.Pseudo_Color = False if len(self.Signals) == 1: if isinstance(self.Signals[0], ndarray): if ( len ( self.Signals[0].shape ) == 2 ) and \ ( self.Signals[0].shape[1] > 10 ) : self.Pseudo_Color = True self.ReCreate_Plot() # ************************************************************* # MatPlot accepts RGB colors in relative range 0..1, # alpha blend is also not accepted # ************************************************************* def Color_2_MatPlot(self, color): # if already in MatPlot format, just return the same value if isinstance(color, float) or isinstance(color[0], float): return color # else limit to 3 elements in the range 0.0 ... 1.0 kleur = [] for c in color[:3]: kleur.append(c / 255.0) return kleur # ************************************************************* # MatPlot accepts RGB colors in relative range 0..1, # alpha blend is also not accepted # ************************************************************* def MatPlot_2_Color(self, color): # if already in normal format, just return the same value if isinstance(color, wx.Color): return color if isinstance(color, basestring): try: color = float(color) except: return color # named color probably if isinstance(color, float): i = int(color * 255) kleur = [i, i, i] else: kleur = [] if isinstance(color[0], float): for c in color[:3]: kleur.append(int(255 * c)) else: kleur = color[:3] kleur = wx.Color(*kleur) return kleur # ************************************************************* # ************************************************************* def Set_Figure_Pars(self): color = self.BG_color if color: #if not ( isinstance ( color, wx.Color ) ) and \ # not ( isinstance ( color , list ) ) : color = wx.NamedColor ( color ) color = self.Color_2_MatPlot(color) self.BG_color = color self.axes.set_axis_bgcolor(color) self.figure.set_facecolor(color) self.figure.set_edgecolor(color) self.axes.grid(self.CB_Grid.GetValue()) if self.CB_Axis.GetValue(): self.axes.set_axis_on() else: self.axes.set_axis_off() # Polar doesn't support legend (use figlegend) if self.Pseudo_Color or not (self.CB_Polar.GetValue()): if self.CB_Legend.GetValue(): self.axes.legend(self.legends) else: self.axes.legend_ = None self.Canvas.draw() # ************************************************************* # Set Grid color of all backgrounds # ************************************************************* def OnGridColor(self, event): rcParams['grid.color'] = self.Color_2_MatPlot(self.CP_Grid.GetColour()) # Because we need to reload the resources, # we force it by recreating to the total plot self.sizer.Remove(self.Canvas) self.figure = Figure() self.Canvas = FigureCanvas(self, -1, self.figure) self.axes = self.figure.add_subplot(111) self.lx = None self.sizer.Prepend(self.Canvas, 1, wx.EXPAND) self.sizer.Layout() self.ReCreate_Plot self.MatPlot_Example(self.Spin.GetValue()) # ************************************************************* # Set Background color of all backgrounds # ************************************************************* def OnBGColor(self, event): self.BG_color = self.CP_BG.GetColour() self.Set_Figure_Pars() # ************************************************************* # ************************************************************* def OnSetAxis(self, event): self.Set_Figure_Pars() # ************************************************************* # ************************************************************* def ReCreate_Plot(self): # BUG, "hold" doesn't work in polar, therefore create a new figure self.figure.clear() self.lx = None if not (self.Pseudo_Color): self.CB_Polar.SetLabel('-Polar') self.axes = self.figure.add_subplot(111, polar=self.CB_Polar.GetValue()) self.axes.hold(True) # needed for measurement cursor self.axes.plot( self.Signals[0], self.Signals[1], ) else: # Pseudo color self.CB_Polar.SetLabel('-Low Res') self.axes = self.figure.add_subplot(111) if self.CB_Polar.GetValue(): cmap = cm.get_cmap('jet', 10) # 10 discrete colors else: cmap = cm.jet #cmap = cm.gray im = self.axes.imshow(self.Signals[0], cmap=cmap) #im.set_interpolation('nearest') #im.set_interpolation('bicubic') im.set_interpolation('bilinear') self.figure.colorbar(im) self.Set_Figure_Pars() # ************************************************************* # ************************************************************* def OnPolar(self, event): self.ReCreate_Plot() # ************************************************************* # ************************************************************* def OnSaveImage(self, event): file = Ask_File_For_Save(os.getcwd(), FileTypes='*.png', Title='Save Plot as PNG-image') if file: self.figure.savefig(file) # ************************************************************* # ************************************************************* def OnSetLegend(self, value): self.Set_Figure_Pars() # ************************************************************* # ************************************************************* def OnSetGrid(self, event): self.Set_Figure_Pars() # ************************************************************* # ************************************************************* def OnClose(self, event): if self.Ini: self.Ini.Section = 'MatPlot_Simple' self.Ini.Write('Pos', self.GetPosition()) self.Ini.Write('Size', self.GetSize()) self.Ini.Write('xxx', self.MatPlot_Simple_Save()) event.Skip() # ************************************************************* # ************************************************************* def MatPlot_Simple_Save(self): line = [] line.append(self.BG_color) print 'GC', rcParams['grid.color'] line.append(rcParams['grid.color']) line.append(self.CB_Grid.GetValue()) line.append(self.CB_Axis.GetValue()) line.append(self.CB_Legend.GetValue()) line.append(self.CB_Polar.GetValue()) if not (self.parent): line.append(self.Spin.GetValue()) return line # ************************************************************* # ************************************************************* def MatPlot_Simple_Load(self, line): self.BG_color = line[0] xx = self.MatPlot_2_Color(self.BG_color) rcParams['grid.color'] = self.Color_2_MatPlot(line[1]) self.Initial_Grid = line[2] self.Initial_Axis = line[3] self.Initial_Legend = line[4] self.Initial_Polar = line[5] if not (self.parent): self.Initial_Spin = line[6] else: self.Initial_Spin = 1