class Qt4MplCanvas(FigureCanvas): """ A customized Qt widget for matplotlib figure. It can be used to replace GraphicsView of QtGui """ def __init__(self, parent): """ Initialization """ # from mpl_toolkits.axes_grid1 import host_subplot # import mpl_toolkits.axisartist as AA # import matplotlib.pyplot as plt # Instantiating matplotlib Figure self.fig = Figure() self.fig.patch.set_facecolor('white') if True: self.axes = self.fig.add_subplot(111) # return: matplotlib.axes.AxesSubplot self.axes2 = None else: self.axes = self.fig.add_host_subplot(111) # Initialize parent class and set parent FigureCanvas.__init__(self, self.fig) self.setParent(parent) # Set size policy to be able to expanding and resizable with frame FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) # Variables to manage all lines/subplot self._lineDict = {} self._lineIndex = 0 # legend and color bar self._colorBar = None return def add_plot_1d(self, vec_x, vec_y, y_err=None, color=None, label="", x_label=None, y_label=None, marker=None, line_style=None, line_width=1): """ :param vec_x: numpy array X :param vec_y: numpy array Y :param y_err: :param color: :param label: :param x_label: :param y_label: :param marker: :param line_style: :param line_width: :return: new key """ # Check input if isinstance(vec_x, np.ndarray) is False or isinstance(vec_y, np.ndarray) is False: raise NotImplementedError('Input vec_x or vec_y for addPlot() must be numpy.array.') plot_error = y_err is not None if plot_error is True: if isinstance(y_err, np.ndarray) is False: raise NotImplementedError('Input y_err must be either None or numpy.array.') if len(vec_x) != len(vec_y): raise NotImplementedError('Input vec_x and vec_y must have same size.') if plot_error is True and len(y_err) != len(vec_x): raise NotImplementedError('Input vec_x, vec_y and y_error must have same size.') # Hold previous data self.axes.hold(True) # process inputs and defaults if color is None: color = (0,1,0,1) if marker is None: marker = 'o' if line_style is None: line_style = '-' # color must be RGBA (4-tuple) if plot_error is False: print "[DB] line_style = ", line_style, "line_width = ", line_width, "marker = ", marker, "color = ", color r = self.axes.plot(vec_x, vec_y, color=color, marker=marker, linestyle=line_style, label=label, linewidth=line_width) # return: list of matplotlib.lines.Line2D object else: r = self.axes.errorbar(vec_x, vec_y, yerr=y_err, color=color, marker=marker, linestyle=line_style, label=label, linewidth=line_width) self.axes.set_aspect('auto') # set x-axis and y-axis label if x_label is not None: self.axes.set_xlabel(x_label, fontsize=20) if y_label is not None: self.axes.set_ylabel(y_label, fontsize=20) # set/update legend self._setupLegend() # Register line_key = self._lineIndex if len(r) == 1: self._lineDict[line_key] = r[0] self._lineIndex += 1 else: print "Impoooooooooooooooosible! Return from plot is a %d-tuple. " % (len(r)) # Flush/commit self.draw() return line_key def add_1d_plot_right(self, x, y, color=None, label="", x_label=None, ylabel=None, marker=None, linestyle=None, linewidth=1): """ Add a line (1-d plot) at right axis """ if self.axes2 is None: self.axes2 = self.axes.twinx() # print self.par1, type(self.par1) # Hold previous data self.axes2.hold(True) # Default if color is None: color = (0, 1, 0, 1) if marker is None: marker = 'o' if linestyle is None: linestyle = '-' # Special default if len(label) == 0: label = 'right' color = 'red' # color must be RGBA (4-tuple) r = self.axes2.plot(x, y, color=color, marker=marker, linestyle=linestyle, label=label, linewidth=linewidth) # return: list of matplotlib.lines.Line2D object self.axes2.set_aspect('auto') # set x-axis and y-axis label if x_label is not None: self.axes2.set_xlabel(x_label, fontsize=20) if ylabel is not None: self.axes2.set_ylabel(ylabel, fontsize=20) # set/update legend self._setupLegend() # Register line_key = -1 if len(r) == 1: line_key = self._lineIndex self._lineDict[line_key] = r[0] self._lineIndex += 1 else: print "Impoooooooooooooooosible!" # Flush/commit self.draw() return line_key def addPlot2D(self, array2d, xmin, xmax, ymin, ymax, holdprev, yticklabels=None): """ Add a 2D plot Arguments: - yticklabels :: list of string for y ticks """ # Release the current image self.axes.hold(holdprev) # Do plot # y ticks will be shown on line 1, 4, 23, 24 and 30 # yticks = [1, 4, 23, 24, 30] # self.axes.set_yticks(yticks) # show image imgplot = self.axes.imshow(array2d, extent=[xmin,xmax,ymin,ymax], interpolation='none') # set y ticks as an option: if yticklabels is not None: # it will always label the first N ticks even image is zoomed in print "--------> [FixMe]: Set up the Y-axis ticks is erroreous" #self.axes.set_yticklabels(yticklabels) # explicitly set aspect ratio of the image self.axes.set_aspect('auto') # Set color bar. plt.colorbar() does not work! if self._colorBar is None: # set color map type imgplot.set_cmap('spectral') self._colorBar = self.fig.colorbar(imgplot) else: self._colorBar.update_bruteforce(imgplot) # Flush... self._flush() return def addImage(self, imagefilename): """ Add an image by file """ #import matplotlib.image as mpimg # set aspect to auto mode self.axes.set_aspect('auto') img = matplotlib.image.imread(str(imagefilename)) # lum_img = img[:,:,0] # FUTURE : refactor for image size, interpolation and origin imgplot = self.axes.imshow(img, extent=[0, 1000, 800, 0], interpolation='none', origin='lower') # Set color bar. plt.colorbar() does not work! if self._colorBar is None: # set color map type imgplot.set_cmap('spectral') self._colorBar = self.fig.colorbar(imgplot) else: self._colorBar.update_bruteforce(imgplot) self._flush() return def clear_all_1d_plots(self): """ Remove all lines from the canvas """ for ikey in self._lineDict.keys(): plot = self._lineDict[ikey] if plot is None: continue if isinstance(plot, tuple) is False: try: self.axes.lines.remove(plot) except ValueError as e: print "[Error] Plot %s is not in axes.lines which has %d lines. Error mesage: %s" % ( str(plot), len(self.axes.lines), str(e)) self._lineDict[ikey] = None else: # error bar plot[0].remove() for line in plot[1]: line.remove() for line in plot[2]: line.remove() self._lineDict[ikey] = None # ENDIF(plot) # ENDFOR self._setupLegend() self.draw() return def clear_canvas(self): """ Clear data including lines and image from canvas """ # clear the image for next operation self.axes.hold(False) # Clear all lines self.clear_all_1d_plots() # clear image self.axes.cla() # Try to clear the color bar if len(self.fig.axes) > 1: self.fig.delaxes(self.fig.axes[1]) self._colorBar = None # This clears the space claimed by color bar but destroys sub_plot too. self.fig.clear() # Re-create subplot self.axes = self.fig.add_subplot(111) # flush/commit self._flush() return def getLastPlotIndexKey(self): """ Get the index/key of the last added line """ return self._lineIndex-1 def getPlot(self): """ reture figure's axes to expose the matplotlib figure to PyQt client """ return self.axes def getXLimit(self): """ Get limit of Y-axis """ return self.axes.get_xlim() def getYLimit(self): """ Get limit of Y-axis """ return self.axes.get_ylim() def setXYLimit(self, xmin, xmax, ymin, ymax): """ """ # for X xlims = self.axes.get_xlim() xlims = list(xlims) if xmin is not None: xlims[0] = xmin if xmax is not None: xlims[1] = xmax self.axes.set_xlim(xlims) # for Y ylims = self.axes.get_ylim() ylims = list(ylims) if ymin is not None: ylims[0] = ymin if ymax is not None: ylims[1] = ymax self.axes.set_ylim(ylims) # try draw self.draw() return def remove_plot_1d(self, plot_key): """ Remove the line with its index as key :param plot_key: :return: """ # self._lineDict[ikey].remove() print 'Remove line... ', # Get all lines in list lines = self.axes.lines assert isinstance(lines, list) print 'Number of lines = %d, List: %s' % (len(lines), str(lines)) print 'Line to remove: key = %s, Line Dict has key = %s' % (str(plot_key), str(self._lineDict.has_key(plot_key))) if plot_key in self._lineDict: self.axes.lines.remove(self._lineDict[plot_key]) self._lineDict[plot_key] = None else: raise RuntimeError('Line with ID %s is not recorded.' % plot_key) # Draw self.draw() return def updateLine(self, ikey, vecx, vecy, linestyle=None, linecolor=None, marker=None, markercolor=None): """ """ line = self._lineDict[ikey] if vecx is not None and vecy is not None: line.set_xdata(vecx) line.set_ydata(vecy) if linecolor is not None: line.set_color(linecolor) if linestyle is not None: line.set_linestyle(linestyle) if marker is not None: line.set_marker(marker) if markercolor is not None: line.set_markerfacecolor(markercolor) oldlabel = line.get_label() line.set_label(oldlabel) self.axes.legend() # commit self.draw() return def getLineStyleList(self): """ """ return MplLineStyles def getLineMarkerList(self): """ """ return MplLineMarkers def getLineBasicColorList(self): """ """ return MplBasicColors def getDefaultColorMarkerComboList(self): """ Get a list of line/marker color and marker style combination as default to add more and more line to plot """ combolist = [] nummarkers = len(MplLineMarkers) numcolors = len(MplBasicColors) for i in xrange(nummarkers): marker = MplLineMarkers[i] for j in xrange(numcolors): color = MplBasicColors[j] combolist.append( (marker, color) ) # ENDFOR (j) # ENDFOR(i) return combolist def _flush(self): """ A dirty hack to flush the image """ w, h = self.get_width_height() self.resize(w+1,h) self.resize(w,h) return def _setupLegend(self, location='best'): """ Set up legend self.axes.legend() Handler is a Line2D object. Lable maps to the line object """ loclist = [ "best", "upper right", "upper left", "lower left", "lower right", "right", "center left", "center right", "lower center", "upper center", "center"] # Check legend location valid or not if location not in loclist: location = 'best' handles, labels = self.axes.get_legend_handles_labels() self.axes.legend(handles, labels, loc=location) # print handles # print labels #self.axes.legend(self._myLegendHandlers, self._myLegentLabels) return
class Qt4MplCanvas(FigureCanvas): """ A customized Qt widget for matplotlib figure. It can be used to replace GraphicsView of QtGui """ def __init__(self, parent): """ Initialization """ from mpl_toolkits.axes_grid1 import host_subplot # import mpl_toolkits.axisartist as AA import matplotlib.pyplot as plt # Instantialize matplotlib Figure self.fig = Figure() self.fig.patch.set_facecolor('white') if True: self.axes = self.fig.add_subplot(111) # return: matplotlib.axes.AxesSubplot self.axes2 = None else: self.axes = self.fig.add_host_subplot(111) # Initialize parent class and set parent FigureCanvas.__init__(self, self.fig) self.setParent(parent) # Set size policy to be able to expanding and resizable with frame FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) # Variables to manage all lines/subplot self._lineDict = {} self._lineIndex = 0 # legend and color bar self._colorBar = None return def add_plot_1d(self, vec_x, vec_y, y_err=None, color=None, label="", x_label=None, y_label=None, marker=None, line_style=None, line_width=1): """ :param vec_x: numpy array X :param vec_y: numpy array Y :param y_err: :param color: :param label: :param x_label: :param y_label: :param marker: :param line_style: :param line_width: :return: new key """ # Check input if isinstance(vec_x, np.ndarray) is False or isinstance(vec_y, np.ndarray) is False: raise NotImplementedError('Input vec_x or vec_y for addPlot() must be numpy.array.') plot_error = y_err is not None if plot_error is True: if isinstance(y_err, np.ndarray) is False: raise NotImplementedError('Input y_err must be either None or numpy.array.') if len(vec_x) != len(vec_y): raise NotImplementedError('Input vec_x and vec_y must have same size.') if plot_error is True and len(y_err) != len(vec_x): raise NotImplementedError('Input vec_x, vec_y and y_error must have same size.') # Hold previous data self.axes.hold(True) # process inputs and defaults if color is None: color = (0,1,0,1) if marker is None: marker = 'o' if line_style is None: line_style = '-' # color must be RGBA (4-tuple) if plot_error is False: print "[DB] line_style = ", line_style, "line_width = ", line_width, "marker = ", marker, "color = ", color r = self.axes.plot(vec_x, vec_y, color=color, marker=marker, linestyle=line_style, label=label, linewidth=line_width) # return: list of matplotlib.lines.Line2D object else: r = self.axes.errorbar(vec_x, vec_y, yerr=y_err, color=color, marker=marker, linestyle=line_style, label=label, linewidth=line_width) self.axes.set_aspect('auto') # set x-axis and y-axis label if x_label is not None: self.axes.set_xlabel(x_label, fontsize=20) if y_label is not None: self.axes.set_ylabel(y_label, fontsize=20) # set/update legend self._setupLegend() # Register line_key = self._lineIndex if len(r) == 1: self._lineDict[line_key] = r[0] self._lineIndex += 1 else: print "Impoooooooooooooooosible! Return from plot is a %d-tuple. " % (len(r)) # Flush/commit self.draw() return line_key def addPlotY2(self, x, y, color=None, label="", xlabel=None, ylabel=None, marker=None, linestyle=None, linewidth=1): """ Add second plot """ if self.axes2 is None: self.axes2 = self.axes.twinx() # print self.par1, type(self.par1) # Hold previous data self.axes2.hold(True) # process inputs and defaults self._x2 = x self._y2 = y if color is None: color = (0,1,0,1) if marker is None: marker = 'o' if linestyle is None: linestyle = '-' # color must be RGBA (4-tuple) r = self.axes2.plot(x, y, color=color, marker=marker, linestyle=linestyle, label=label, linewidth=linewidth) # return: list of matplotlib.lines.Line2D object self.axes2.set_aspect('auto') # set x-axis and y-axis label if xlabel is not None: self.axes2.set_xlabel(xlabel, fontsize=20) if ylabel is not None: self.axes2.set_ylabel(ylabel, fontsize=20) # set/update legend self._setupLegend() # Register if len(r) == 1: self._lineDict[self._lineIndex] = r[0] else: print "Impoooooooooooooooosible!" self._lineIndex += 1 # Flush/commit self.draw() return def addPlot2D(self, array2d, xmin, xmax, ymin, ymax, holdprev, yticklabels=None): """ Add a 2D plot Arguments: - yticklabels :: list of string for y ticks """ # Release the current image self.axes.hold(holdprev) # Do plot # y ticks will be shown on line 1, 4, 23, 24 and 30 # yticks = [1, 4, 23, 24, 30] # self.axes.set_yticks(yticks) # show image imgplot = self.axes.imshow(array2d, extent=[xmin,xmax,ymin,ymax], interpolation='none') # set y ticks as an option: if yticklabels is not None: # it will always label the first N ticks even image is zoomed in print "--------> [FixMe]: Set up the Y-axis ticks is erroreous" #self.axes.set_yticklabels(yticklabels) # explicitly set aspect ratio of the image self.axes.set_aspect('auto') # Set color bar. plt.colorbar() does not work! if self._colorBar is None: # set color map type imgplot.set_cmap('spectral') self._colorBar = self.fig.colorbar(imgplot) else: self._colorBar.update_bruteforce(imgplot) # Flush... self._flush() return def addImage(self, imagefilename): """ Add an image by file """ #import matplotlib.image as mpimg # set aspect to auto mode self.axes.set_aspect('auto') img = matplotlib.image.imread(str(imagefilename)) # lum_img = img[:,:,0] # FUTURE : refactor for image size, interpolation and origin imgplot = self.axes.imshow(img, extent=[0, 1000, 800, 0], interpolation='none', origin='lower') # Set color bar. plt.colorbar() does not work! if self._colorBar is None: # set color map type imgplot.set_cmap('spectral') self._colorBar = self.fig.colorbar(imgplot) else: self._colorBar.update_bruteforce(imgplot) self._flush() return def clear_all_1d_plots(self): """ Remove all lines from the canvas """ for ikey in self._lineDict.keys(): plot = self._lineDict[ikey] if plot is None: continue if isinstance(plot, tuple) is False: try: self.axes.lines.remove(plot) except ValueError as e: print "[Error] Plot %s is not in axes.lines which has %d lines. Error mesage: %s" % ( str(plot), len(self.axes.lines), str(e)) self._lineDict[ikey] = None else: # error bar plot[0].remove() for line in plot[1]: line.remove() for line in plot[2]: line.remove() self._lineDict[ikey] = None # ENDIF(plot) # ENDFOR self._setupLegend() self.draw() return def clear_canvas(self): """ Clear data including lines and image from canvas """ # clear the image for next operation self.axes.hold(False) # Clear all lines self.clear_all_1d_plots() # clear image self.axes.cla() # Try to clear the color bar if len(self.fig.axes) > 1: self.fig.delaxes(self.fig.axes[1]) self.colorBar = None # This clears the space claimed by color bar but destroys sub_plot too. self.fig.clear() # Re-create subplot self.axes = self.fig.add_subplot(111) # flush/commit self._flush() return def getLastPlotIndexKey(self): """ Get the index/key of the last added line """ return self._lineIndex-1 def getPlot(self): """ reture figure's axes to expose the matplotlib figure to PyQt client """ return self.axes def getXLimit(self): """ Get limit of Y-axis """ return self.axes.get_xlim() def getYLimit(self): """ Get limit of Y-axis """ return self.axes.get_ylim() def setXYLimit(self, xmin, xmax, ymin, ymax): """ """ # for X xlims = self.axes.get_xlim() xlims = list(xlims) if xmin is not None: xlims[0] = xmin if xmax is not None: xlims[1] = xmax self.axes.set_xlim(xlims) # for Y ylims = self.axes.get_ylim() ylims = list(ylims) if ymin is not None: ylims[0] = ymin if ymax is not None: ylims[1] = ymax self.axes.set_ylim(ylims) # try draw self.draw() return def removePlot(self, ikey): """ Remove the line with its index as key """ # self._lineDict[ikey].remove() lines = self.axes.lines print str(type(lines)), lines print "ikey = ", ikey, self._lineDict[ikey] self.axes.lines.remove(self._lineDict[ikey]) #self.axes.remove(self._lineDict[ikey]) print self._lineDict[ikey] self._lineDict[ikey] = None return def updateLine(self, ikey, vecx, vecy, linestyle=None, linecolor=None, marker=None, markercolor=None): """ """ line = self._lineDict[ikey] if vecx is not None and vecy is not None: line.set_xdata(vecx) line.set_ydata(vecy) if linecolor is not None: line.set_color(linecolor) if linestyle is not None: line.set_linestyle(linestyle) if marker is not None: line.set_marker(marker) if markercolor is not None: line.set_markerfacecolor(markercolor) oldlabel = line.get_label() line.set_label(oldlabel) self.axes.legend() # commit self.draw() return def getLineStyleList(self): """ """ return MplLineStyles def getLineMarkerList(self): """ """ return MplLineMarkers def getLineBasicColorList(self): """ """ return MplBasicColors def getDefaultColorMarkerComboList(self): """ Get a list of line/marker color and marker style combination as default to add more and more line to plot """ combolist = [] nummarkers = len(MplLineMarkers) numcolors = len(MplBasicColors) for i in xrange(nummarkers): marker = MplLineMarkers[i] for j in xrange(numcolors): color = MplBasicColors[j] combolist.append( (marker, color) ) # ENDFOR (j) # ENDFOR(i) return combolist def _flush(self): """ A dirty hack to flush the image """ w, h = self.get_width_height() self.resize(w+1,h) self.resize(w,h) return def _setupLegend(self, location='best'): """ Set up legend self.axes.legend() Handler is a Line2D object. Lable maps to the line object """ loclist = [ "best", "upper right", "upper left", "lower left", "lower right", "right", "center left", "center right", "lower center", "upper center", "center"] # Check legend location valid or not if location not in loclist: location = 'best' handles, labels = self.axes.get_legend_handles_labels() self.axes.legend(handles, labels, loc=location) # print handles # print labels #self.axes.legend(self._myLegendHandlers, self._myLegentLabels) return