def matplotlib_figure(width, height): """Create a Matplotlib figure with specified width and height for rendering. w Width of desired plot. h Height of desired plot. return A Matplotlib figure. """ try: from matplotlib.backends.backend_agg import FigureCanvasAgg except: paraview.print_error( "Error: Cannot import matplotlib.backends.backend_agg.FigureCanvasAgg" ) try: from matplotlib.figure import Figure except: paraview.print_error("Error: Cannot import matplotlib.figure.Figure") figure = Figure() figureCanvas = FigureCanvasAgg(figure) figure.set_dpi(72) figure.set_size_inches(float(width) / 72.0, float(height) / 72.0) return figure
def canvas(self): type = self.get("imageType", "png") fig = Figure() if type == "png": canvas = FigureCanvasAgg(fig) (self.file, self.filename) = mkstemp(".%s" % type) elif type == "svg": canvas = FigureCanvasSVG(fig) (self.file, self.filename) = mkstemp(".%s" % type) elif type == "pdf": canvas = FigureCanvasPdf(fig) (self.file, self.filename) = mkstemp(".%s" % type) elif type == "ps" or type == "eps": canvas = FigureCanvasPS(fig) (self.file, self.filename) = mkstemp(".%s" % type) else: raise "Invalid render target requested" # Set basic figure parameters dpi = float(self.get('dpi')) (w, h) = (float(self.get('width')), float(self.get('height'))) (win, hin) = (w/dpi, h/dpi) fig.set_size_inches(win, hin) fig.set_dpi(dpi) fig.set_facecolor('white') return (fig, canvas, w, h)
def matplotlib_figure(width, height): """Create a Matplotlib figure with specified width and height for rendering. w Width of desired plot. h Height of desired plot. return A Matplotlib figure. """ try: from matplotlib.backends.backend_agg import FigureCanvasAgg except: paraview.print_error("Error: Cannot import matplotlib.backends.backend_agg.FigureCanvasAgg") try: from matplotlib.figure import Figure except: paraview.print_error("Error: Cannot import matplotlib.figure.Figure") figure = Figure() figureCanvas = FigureCanvasAgg(figure) figure.set_dpi(72) figure.set_size_inches(float(width)/72.0, float(height)/72.0) return figure
def __get_column_width(self): max_length = 0 max_column_text = '' flag = self.prefs.get('legend_numbers',True) unit = self.prefs.get('legend_unit',False) for label,num in self.labels: if not flag: num = None if num is not None: column_length = len(str(label)+str(num)) + 1 else: column_length = len(str(label)) + 1 if column_length > max_length: max_length = column_length if flag: if type(num) == types.IntType or type(num) == types.LongType: numString = str(num) else: numString = "%.1f" % float(num) max_column_text = '%s %s' % (str(label),numString) if unit: max_column_text += "%" else: max_column_text = '%s ' % str(label) figure = Figure() canvas = FigureCanvasAgg(figure) dpi = self.prefs['dpi'] figure.set_dpi( dpi ) l_size,l_padding = self.__get_legend_text_size() self.text_size = pixelToPoint(l_size,dpi) text = Text(0.,0.,text=max_column_text,size=self.text_size) text.set_figure(figure) bbox = text.get_window_extent(canvas.get_renderer()) self.column_width = bbox.width+6*l_size
class PlotCanvas(tk.Frame): """ the canvas class for drawing the Nelder plot design """ borderpoint_color = "black" datapoint_color = ("red", "blue", "green") wheel_color = "black" spoke_color = "black" defaul_dpi = 100 def __init__(self, parent): tk.Frame.__init__(self, parent) self.figure = Figure(figsize=(5,5), dpi=self.defaul_dpi, tight_layout = True) self.plot = self.figure.add_subplot(111) self.plot.axis('scaled') self.plot.set_axis_off() self.canvas = FigureCanvasTkAgg(self.figure, self) self.canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True) self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True) self.nelder_plot = None def updatePlotSize(self): """ redraw the plot when the size changes """ if self.nelder_plot.width > 0 and self.nelder_plot.height > 0: self.plot.axis([0,self.nelder_plot.width,0,self.nelder_plot.height]) self.plot.set_axis_on() else: self.plot.set_axis_off() def setNelderPlot(self, nelder_plot: NelderPlot): """ define the Nelder plot object to be drawn """ self.plot.clear() self.nelder_plot = nelder_plot self.updatePlotSize() if not nelder_plot.isValid(): self.canvas.draw() return for w in nelder_plot.wheels.values(): wheel = Wedge((w.center_point.x, w.center_point.y), w.radius, 0, 360, linewidth=0.5, width = 0, edgecolor = self.wheel_color, alpha = 0.5, linestyle = (0, (4, 8))) self.plot.add_patch(wheel) for l in nelder_plot.spokes: line = Line2D([l.intersect_point.x, l.end_point.x], [l.intersect_point.y, l.end_point.y], linewidth=0.5, color = self.spoke_color, alpha = 0.5, linestyle = ":") self.plot.add_line(line) p_size = min(self.nelder_plot.width, self.nelder_plot.height)/120 for p in nelder_plot.datapoints: point = Circle((p.x, p.y), p_size, facecolor = self.datapoint_color[p.group_id]) if p.is_border: point.set_edgecolor(self.borderpoint_color) point.set_radius(p_size * 0.9) self.plot.add_patch(point) self.canvas.draw() def saveImage(self, fileName, dpi = defaul_dpi): """ save the image to the file and size (DPI) defined """ self.figure.set_dpi(dpi) self.figure.savefig(fileName) self.figure.set_dpi(self.defaul_dpi)
def __init__(self, parent=None, width=8, height=8, dpi=100): fig = Figure(figsize=(width, height), dpi=200) # 创建一个Figure,注意:该Figure为matplotlib下的figure,不是matplotlib.pyplot下面的figure pdf = camelot.read_pdf(r"C:\Users\localhost\Desktop\石家庄市2018年市本级和全市财政总决算报表.pdf", flavor='stream', pages='5') if pdf: fig = camelot.plot(pdf[0], kind='textedge') fig.set_dpi(150) axis('tight') FigureCanvas.__init__(self, fig) # 初始化父类 self.setParent(parent)
def save_plot(portrait, points, name, out_dir="./"): fig = Figure() fig.set_dpi(50) fig.set_size_inches(12, 16) canvas = FigureCanvas(fig) ax = fig.add_subplot(111) ax.axis('off') ax.imshow(portrait) ax.scatter(points[:, 0], points[:, 1], s=50) out_path = out_dir + name + "_points.png" fig.savefig(out_path, bbox_inches='tight', pad_inches=0)
def on_paint_request(self, printer): from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas rect = printer.pageRect( QtGui.QPrinter.Inch ) dpi = printer.resolution() fig = Figure( facecolor='#ffffff') fig.set_figsize_inches( (rect.width(),rect.height()) ) fig.set_dpi( dpi ) self._value.plot_on_figure( fig ) canvas = FigureCanvas(fig) canvas.render( printer )
def print_(self, printer): from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas rect = printer.pageRect(QtPrintSupport.QPrinter.Inch) dpi = printer.resolution() fig = Figure(facecolor='#ffffff') fig.set_size_inches((rect.width(), rect.height())) fig.set_dpi(dpi) self.chart.plot_on_figure(fig) canvas = FigureCanvas(fig) canvas.render(printer)
def alg_results(cls, obs, algorithm): wavPath = obs.export_to_wav() # Canvas micSn = kg.MicSignal.from_Obs(obs) fig = Figure((15, 10)) fig.set_dpi(110) results = algorithm.plot_alg(fig, micSn) mpl = {str(algorithm): {'canvas': FigureCanvas(fig), 'axHandle': [Bar(ax) for ax in fig.get_axes()], 'setTime':True, 'animate':True }} obj = cls(setup=True, wavPath=wavPath, t0=micSn.get_t0(), mpl=mpl) #obj.timer.start() return obj
def spectro(file_name, offset): clip = get_clip(file_name, offset) y, sr = librosa.load(clip) S = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=128, fmax=8000) S_dB = librosa.power_to_db(S, ref=np.max) fig = Figure() fig.set_dpi(60) canvas = FigureCanvas(fig) ax = fig.add_subplot(1, 1, 1) lrd.specshow(S_dB, ax=ax, x_axis='time', y_axis='mel', sr=sr, fmax=8000) fig.colorbar(ax.collections[0], format='%+2.0f dB') out_f = io.BytesIO() canvas.print_png(out_f) response = Response(out_f.getvalue(), mimetype='image/png') return response
def main(): fig = Figure() canvas = FigureCanvas(fig) ax = fig.add_subplot(111) data = eval(open('data.py').read()) colors = { 'terrible': '#ff5555', 'bad': '#aa0000', 'moderate': '#ff00ff', 'good': '#5555ff', } for f, T, key in data: ax.plot(float(f), float(T), marker='.', color=colors[key]) ax.plot(*approx()) fig.set_dpi(270) canvas.print_png('plot.png')
def main(fn: str): infile = Path(f'{fn}') outfile = infile.parent.joinpath(infile.stem + '.pdf') outfilebw = infile.parent.joinpath(infile.stem + '.pdf.bw.txt') topo = json.loads(infile.read_text()) nxg = nx_graph_from_topo(topo) pos = spring_layout(nxg, iterations=4000) fig = Figure() fig.set_dpi(300) ax = fig.add_subplot(111) ax.axis('off') labels = dict() for edge in nxg.edges: bw = nxg.get_edge_data(edge[0], edge[1], dict()).get('bw', None) bw = '' if bw is None else '%.1f mbps' % (bw, ) labels[edge] = bw if len(set(labels.values())) > 1: draw_networkx_edge_labels(nxg, pos, ax=ax, edge_labels=labels, bbox=dict(facecolor='#FFFFFF88', edgecolor='none'), font_size='x-small') else: sbw = next(iter(set(labels.values()))) outfilebw.write_text(sbw) draw_networkx_edges(nxg, pos, ax=ax, edgelist=nxg.edges, edge_color='black') draw_networkx_nodes(nxg, pos, ax=ax, nodelist=topo[0], node_color='lightgreen', edgecolors="green") draw_networkx_nodes(nxg, pos, ax=ax, nodelist=topo[1], node_color='cyan', edgecolors="darkcyan") draw_networkx_labels(nxg, pos, ax=ax, font_size='small') fig.savefig(str(outfile))
def __get_column_width(self): max_length = 0 max_column_text = "" flag = self.prefs.get("legend_numbers", True) unit = self.prefs.get("legend_unit", False) for label, num in self.labels: if not flag: num = None if num is not None: column_length = len(str(label) + str(num)) + 1 else: column_length = len(str(label)) + 1 if column_length > max_length: max_length = column_length if flag: if isinstance(num, six.integer_types): numString = str(num) else: numString = "%.1f" % float(num) max_column_text = "%s %s" % (str(label), numString) if unit: max_column_text += "%" else: max_column_text = "%s " % str(label) figure = Figure() canvas = FigureCanvasAgg(figure) dpi = self.prefs["dpi"] figure.set_dpi(dpi) l_size, _ = self.__get_legend_text_size() self.text_size = pixelToPoint(l_size, dpi) text = Text(0.0, 0.0, text=max_column_text, size=self.text_size) text.set_figure(figure) bbox = text.get_window_extent(canvas.get_renderer()) columnwidth = bbox.width + 6 * l_size # make sure the legend fit in the box self.column_width = (columnwidth if columnwidth <= self.prefs["legend_width"] else self.prefs["legend_width"] - 6 * l_size)
def __get_column_width(self): max_length = 0 max_column_text = '' flag = self.prefs.get('legend_numbers', True) unit = self.prefs.get('legend_unit', False) for label, num in self.labels: if not flag: num = None if num is not None: column_length = len(str(label) + str(num)) + 1 else: column_length = len(str(label)) + 1 if column_length > max_length: max_length = column_length if flag: if type(num) == types.IntType or type( num) == types.LongType: numString = str(num) else: numString = "%.1f" % float(num) max_column_text = '%s %s' % (str(label), numString) if unit: max_column_text += "%" else: max_column_text = '%s ' % str(label) figure = Figure() canvas = FigureCanvasAgg(figure) dpi = self.prefs['dpi'] figure.set_dpi(dpi) l_size, _ = self.__get_legend_text_size() self.text_size = pixelToPoint(l_size, dpi) text = Text(0., 0., text=max_column_text, size=self.text_size) text.set_figure(figure) bbox = text.get_window_extent(canvas.get_renderer()) columnwidth = bbox.width + 6 * l_size self.column_width = columnwidth if columnwidth <= self.prefs[ 'legend_width'] else self.prefs[ 'legend_width'] - 6 * l_size #make sure the legend fit in the box
def print_result(self, print_context, render=True): figure = Figure(facecolor='white', figsize=(6, 4.5)) figure.set_dpi(72) # Don't draw the frame, please. figure.set_frameon(False) canvas = _PlotResultCanvas(figure) axes = figure.add_subplot(111) self._replay(axes) width, height = figure.bbox.width, figure.bbox.height if render: cr = print_context.get_cairo_context() renderer = RendererCairo(figure.dpi) renderer.set_width_height(width, height) if hasattr(renderer, 'gc'): # matplotlib-0.99 and newer renderer.gc.ctx = cr else: # matplotlib-0.98 # RendererCairo.new_gc() does a restore to get the context back # to its original state after changes cr.save() renderer.ctx = cr figure.draw(renderer) if not hasattr(renderer, 'gc'): # matplotlib-0.98 # Reverse the save() we did before drawing cr.restore() return height
def print_result(self, print_context, render=True): figure = Figure(facecolor='white', figsize=(6,4.5)) figure.set_dpi(72) # Don't draw the frame, please. figure.set_frameon(False) canvas = _PlotResultCanvas(figure) axes = figure.add_subplot(111) self._replay(axes) width, height = figure.bbox.width, figure.bbox.height if render: cr = print_context.get_cairo_context() renderer = RendererCairo(figure.dpi) renderer.set_width_height(width, height) if hasattr(renderer, 'gc'): # matplotlib-0.99 and newer renderer.gc.ctx = cr else: # matplotlib-0.98 # RendererCairo.new_gc() does a restore to get the context back # to its original state after changes cr.save() renderer.ctx = cr figure.draw(renderer) if not hasattr(renderer, 'gc'): # matplotlib-0.98 # Reverse the save() we did before drawing cr.restore() return height
def listsize(): """ [DEPRECATED] Returns info about the inverted index """ import StringIO import random from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas from matplotlib.figure import Figure fig = Figure() fig.set_dpi = 100 fig.set_size_inches(9, 12) fig.set_facecolor('w') ax = fig.add_subplot(211) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.get_xaxis().tick_bottom() ax.get_yaxis().tick_left() ax.set_title('Inverted list sizes') y = index.listsizes(filter_small=False) x = range(len(y)) ax.plot(x, y, '-') ax = fig.add_subplot(212) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.get_xaxis().tick_bottom() ax.get_yaxis().tick_left() ax.set_xscale('log') ax.set_yscale('log') ax.set_title('Inverted list sizes (log scale)') y = index.listsizes(filter_small=False) x = range(len(y)) ax.plot(x, y, '-') canvas = FigureCanvas(fig) png_output = StringIO.StringIO() canvas.print_png(png_output) response = make_response(png_output.getvalue()) response.headers['Content-Type'] = 'image/png' return response
class Canvas_sourceImage: def __init__(self, ui, W_matrix, N): # tab ui.list_of_tabs.append(QtWidgets.QWidget()) ui.list_of_tabs[-1].setObjectName("tab_plotSourceImage") # gridlayout TAB SourceImag ui.list_grid_tabs.append( QtWidgets.QGridLayout(self.tab_plotSourceImage)) ui.list_grid_tabs[-1].setObjectName("gridLayout_TabSourceImage") # Scroll Area Source Image ui.scrollArea_sourceImage = QtWidgets.QScrollArea( ui.tab_plotSourceImage) ui.scrollArea_sourceImage.setPalette(palette_scrollPlotProp) ui.scrollArea_sourceImage.setWidgetResizable(True) ui.scrollArea_sourceImage.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) ui.scrollArea_sourceImage.setObjectName("scrollArea_sourceImage") # Scroll Area Options Source ui.scrollArea_sourceImageOpt = QtWidgets.QScrollArea( ui.tab_plotSourceImage) ui.scrollArea_sourceImageOpt.setWidgetResizable(True) ui.scrollArea_sourceImageOpt.setObjectName("scrollArea_sourceImageOpt") ui.scrollArea_sourceImageOpt.setPalette(palette_scrollPlotProp) ui.scrollAreaWidgetContents_sourceImageOpt = QtWidgets.QWidget() ui.scrollAreaWidgetContents_sourceImageOpt.setObjectName( "scrollAreaWidgetContents_sourceImageOpt") ui.scrollArea_sourceImageOpt.setWidget( ui.scrollAreaWidgetContents_sourceImageOpt) ui.gridLayout_sourceImageOpt = QtWidgets.QGridLayout( ui.scrollAreaWidgetContents_sourceImageOpt) ui.gridLayout_sourceImageOpt.setObjectName("gridLayout_sourceImageOpt") ui.gridLayout_TabSourceImage.addWidget(ui.scrollArea_sourceImage, 1, 0, 1, 1) ui.gridLayout_TabSourceImage.addWidget(ui.scrollArea_sourceImageOpt, 3, 0, 1, 1) #======================================================================= # Parameters #======================================================================= # User interface self.ui = ui # parent self.parent = ui.scrollArea_sourceImage # parent optioncs self.parentOptions = ui.scrollArea_sourceImageOpt # grid parent options self.gridOptions = ui.gridLayout_sourceImageOpt #_______________________________________________________________________ self.build_fig(W_matrix, N) def build_fig(self, W_matrix, N): #======================================================================= # Create the mpl Figure and FigCanvas objects. #======================================================================= self.dpi = 100 self.fig = Figure((5.0, 4.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) # Since we have only one plot, we can use add_axes # instead of add_subplot, but then the subplot # configuration tool in the navigation toolbar wouldn't # work. # self.axes = self.fig.add_subplot(111) #_______________________________________________________________________ #======================================================================= # Plotting 2D figure and configuring #======================================================================= # # creating image source image_source = zeros((N, N)) for i in range(0, N): for j in range(0, N): image_source[i, j] = W_matrix[i, j, i, j].real # PLOT self.im = self.axes.pcolormesh(image_source) self.cbar = self.fig.colorbar(self.im) # font size self.fsize = 12 # x,y Labels self.axes.set_xlabel("x (m)", fontsize=self.fsize) self.axes.set_ylabel("y (m)", fontsize=self.fsize) #_______________________________________________________________________ #======================================================================= # Tool bar #======================================================================= # Bind the 'pick' event for clicking on one of the bars self.canvas.mpl_connect( 'pick_event', self.ui.on_pick ) #self.canvas.mpl_connect("scroll_event", ui.scrolling) # Create the navigation toolbar, tied to the canvas self.mpl_toolbar = NavigationToolbar(self.canvas, self.parent) #ui.gridLayout_TabSourceImage.addWidget(self.mpl_toolbar, 2, 0, 1, 3) self.ui.gridLayout_TabSourceImage.addWidget(self.mpl_toolbar, 2, 0, 1, 3) #_______________________________________________________________________ #======================================================================= # Canvas in Scroll Area #======================================================================= self.label_opt = QtWidgets.QLabel() self.label_opt.setObjectName("label_opt") self.label_opt.setText("Plot Options:") #self.gridOptions.addWidget(self.label_opt, 3, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) #self.canvas.draw() #self.canvas.setParent(parent) self.canvas.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) # Container for VBOX self.containerGraph = QWidget(self.parent) self.containerGraph.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.containerGraph.setMinimumWidth(self.canvas.width()) self.containerGraph.setMinimumHeight(self.canvas.height()) self.containerGraph.setMaximumWidth(self.canvas.width() + 5) self.containerGraph.setMaximumHeight(self.canvas.height() + 5) # VBOX for canvas self.vbox = QVBoxLayout(self.containerGraph) #self.vbox.setGeometry(QRect(0, 0, self.canvas.width(), self.canvas.height())) self.vbox.addWidget(self.canvas) self.parent.setWidget(self.containerGraph) #_______________________________________________________________________ #======================================================================= # Figure Options #======================================================================= # Options """ self.label_opt = QtWidgets.QLabel(self.parentOptions) self.label_opt.setObjectName("label_opt") self.label_opt.setText("Plot Options:") self.gridOptions.addWidget(self.label_opt, 3, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) """ # label title self.label_title = QtWidgets.QLabel(self.parentOptions) self.label_title.setObjectName("label_title") self.label_title.setText("Title") self.gridOptions.addWidget(self.label_title, 4, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit title label self.lineEdit_title = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_title.setObjectName("lineEdit_title") self.lineEdit_title.textChanged.connect(self.change_title) self.gridOptions.addWidget(self.lineEdit_title, 4, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.change_title() self.lineEdit_title.setText("Source Image") # label x self.label_x = QtWidgets.QLabel(self.parentOptions) self.label_x.setObjectName("label_x") self.label_x.setText("Label x-axis") self.gridOptions.addWidget(self.label_x, 6, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit x label self.lineEdit_xLabel = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_xLabel.setObjectName("lineEdit_xlabel") self.lineEdit_xLabel.textChanged.connect(self.change_labelx) self.gridOptions.addWidget(self.lineEdit_xLabel, 6, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.change_labelx() self.lineEdit_xLabel.setText("x") # label y self.label_y = QtWidgets.QLabel(self.parentOptions) self.label_y.setObjectName("label_y") self.label_y.setText("Label y-axis") self.gridOptions.addWidget(self.label_y, 8, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit y label self.lineEdit_yLabel = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_yLabel.setObjectName("lineEdit_ylabel") self.lineEdit_yLabel.textChanged.connect(self.change_labely) self.gridOptions.addWidget(self.lineEdit_yLabel, 8, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.change_labely() self.lineEdit_yLabel.setText("y") # label xlim self.label_xlim = QtWidgets.QLabel(self.parentOptions) self.label_xlim.setObjectName("label_xlim") self.label_xlim.setText("xlim") self.gridOptions.addWidget(self.label_xlim, 10, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit xlim self.lineEdit_xlim = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_xlim.setObjectName("lineEdit_ylabel") self.lineEdit_xlim.textChanged.connect(self.change_xlim) self.gridOptions.addWidget(self.lineEdit_xlim, 10, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_xlim.setText("(_,_)") self.change_xlim() # label ylim self.label_ylim = QtWidgets.QLabel(self.parentOptions) self.label_ylim.setObjectName("label_ylim") self.label_ylim.setText("ylim") self.gridOptions.addWidget(self.label_ylim, 12, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit ylim self.lineEdit_ylim = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_ylim.setObjectName("lineEdit_ylabel") self.lineEdit_ylim.textChanged.connect(self.change_ylim) self.gridOptions.addWidget(self.lineEdit_ylim, 12, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_ylim.setText("(_,_)") self.change_ylim() # label cmap self.label_cmap = QtWidgets.QLabel(self.parentOptions) self.label_cmap.setObjectName("label_cmap") self.label_cmap.setText("Color Map") self.gridOptions.addWidget(self.label_cmap, 18, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit cmap self.lineEdit_cmap = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_cmap.setObjectName("lineEdit_cmap") self.lineEdit_cmap.textChanged.connect(self.change_cmap) self.gridOptions.addWidget(self.lineEdit_cmap, 18, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_cmap.setText("hot") self.change_cmap() # label Font Size self.label_fsize = QtWidgets.QLabel(self.parentOptions) self.label_fsize.setObjectName("label_fsize") self.label_fsize.setText("Font Size") self.gridOptions.addWidget(self.label_fsize, 20, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit font size self.lineEdit_fsize = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_fsize.setObjectName("label_fsize") self.lineEdit_fsize.textChanged.connect(self.change_fsize) self.gridOptions.addWidget(self.lineEdit_fsize, 20, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_fsize.setText(str(self.fsize)) self.change_fsize() # label DPI self.label_dpi = QtWidgets.QLabel(self.parentOptions) self.label_dpi.setObjectName("label_dpi") self.label_dpi.setText("dpi (100-500)") self.gridOptions.addWidget(self.label_dpi, 22, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit DPI self.lineEdit_dpi = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_dpi.setObjectName("label_dpi") self.lineEdit_dpi.textChanged.connect(self.change_dpi) self.gridOptions.addWidget(self.lineEdit_dpi, 22, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_dpi.setText(str(self.dpi)) self.change_fsize() def update_draw(self): self.canvas.draw() self.canvas.updateGeometry() def change_dpi(self): try: new = int(self.lineEdit_dpi.text()) if new >= 100 and new <= 500: self.dpi = new self.fig.set_dpi(new) self.update_draw() except: pass def change_fsize(self): try: self.fsize = int(self.lineEdit_fsize.text()) self.change_title() self.change_labelx() self.change_labely() self.update_draw() print(self.fsize) except Exception as error: pass #self.ui.update_outputText(error) def change_cmap(self): try: self.im.set_cmap(self.lineEdit_cmap.text()) self.canvas.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_xlim(self): try: temp_txt = self.lineEdit_xlim.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0] == "(" and temp_txt[-1] == ")": actual = "" for i in range(1, Ntxt - 1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i == Ntxt - 2: actual += temp_txt[i] numList.append(float(actual)) else: actual += temp_txt[i] self.axes.set_xlim(numList[0], numList[1]) except Exception as error: pass #self.ui.update_outputText(error) def change_ylim(self): try: temp_txt = self.lineEdit_ylim.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0] == "(" and temp_txt[-1] == ")": actual = "" for i in range(1, Ntxt - 1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i == Ntxt - 2: actual += temp_txt[i] numList.append(float(actual)) else: actual += temp_txt[i] self.axes.set_ylim(numList[0], numList[1]) self.canvas.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_title(self): try: self.axes.set_title(self.lineEdit_title.text(), fontsize=self.fsize) self.canvas.draw() except: pass def change_labelx(self): if self.lineEdit_xLabel.text() == "": self.axes.set_xlabel("") else: try: self.axes.set_xlabel(self.lineEdit_xLabel.text(), fontsize=self.fsize) self.canvas.draw() except: pass #self.ui.update_outputText("Unable to update x-label.") def change_labely(self): if self.lineEdit_yLabel.text() == "": self.axes.set_ylabel("") else: try: self.axes.set_ylabel(self.lineEdit_yLabel.text(), fontsize=self.fsize) self.canvas.draw() except: pass
class Canvas_propSDC2D: def __init__(self, ui, W_matrix, N): # Scroll Area Propagation SDC ui.scrollArea_propSDC = QtWidgets.QScrollArea(ui.tab_plotPropSDC2D) ui.scrollArea_propSDC.setPalette(palette_scrollPlotProp) ui.scrollArea_propSDC.setWidgetResizable(True) ui.scrollArea_propSDC.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) ui.scrollArea_propSDC.setObjectName("scrollArea_propSDC") # Scroll Area Options prop ui.scrollArea_propSDCOpt = QtWidgets.QScrollArea(ui.tab_plotPropSDC2D) ui.scrollArea_propSDCOpt.setWidgetResizable(True) ui.scrollArea_propSDCOpt.setObjectName("scrollArea_propImageOpt") ui.scrollArea_propSDCOpt.setPalette(palette_scrollPlotProp) ui.scrollAreaWidgetContents_propSDCOpt = QtWidgets.QWidget() ui.scrollAreaWidgetContents_propSDCOpt.setObjectName( "scrollAreaWidgetContents_propSDCOpt") ui.scrollArea_propSDCOpt.setWidget( ui.scrollAreaWidgetContents_propSDCOpt) ui.gridLayout_propSDCOpt = QtWidgets.QGridLayout( ui.scrollAreaWidgetContents_propSDCOpt) ui.gridLayout_propSDCOpt.setObjectName("gridLayout_propSDCOpt") ui.gridLayout_TabPropSDC2D.addWidget(ui.scrollArea_propSDC, 3, 0, 1, 1) ui.gridLayout_TabPropSDC2D.addWidget(ui.scrollArea_propSDCOpt, 10, 0, 1, 1) # Scroll Area Propagation SDC Phase ui.scrollArea_propSDC_phase = QtWidgets.QScrollArea( ui.tab_plotPropSDC2D) ui.scrollArea_propSDC_phase.setPalette(palette_scrollPlotProp) ui.scrollArea_propSDC_phase.setWidgetResizable(True) ui.scrollArea_propSDC_phase.setSizePolicy( QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) ui.scrollArea_propSDC_phase.setObjectName("scrollArea_propSDC_phase") # Scroll Area Options Propagation SDC Phase ui.scrollArea_propSDCOpt_phase = QtWidgets.QScrollArea( ui.tab_plotPropSDC2D) ui.scrollArea_propSDCOpt_phase.setWidgetResizable(True) ui.scrollArea_propSDCOpt_phase.setObjectName( "scrollArea_propImageOpt_phase") ui.scrollArea_propSDCOpt_phase.setPalette(palette_scrollPlotProp) ui.scrollAreaWidgetContents_propSDCOpt_phase = QtWidgets.QWidget() ui.scrollAreaWidgetContents_propSDCOpt_phase.setObjectName( "scrollAreaWidgetContents_propSDCOpt_phase") ui.scrollArea_propSDCOpt_phase.setWidget( ui.scrollAreaWidgetContents_propSDCOpt_phase) ui.gridLayout_propSDCOpt_phase = QtWidgets.QGridLayout( ui.scrollAreaWidgetContents_propSDCOpt_phase) ui.gridLayout_propSDCOpt_phase.setObjectName( "gridLayout_propSDCOpt_phase") ui.gridLayout_TabPropSDC2D.addWidget(ui.scrollArea_propSDC_phase, 3, 1, 1, 1) ui.gridLayout_TabPropSDC2D.addWidget(ui.scrollArea_propSDCOpt_phase, 10, 1, 1, 1) #_______________________________________________________________________ #======================================================================= # Parameters #======================================================================= # N self.N = N # W matrix self.W_matrix = W_matrix # User interface self.ui = ui # parent self.parent = ui.scrollArea_propSDC self.parent_phase = ui.scrollArea_propSDC_phase # parent optioncs self.parentOptions = ui.scrollArea_propSDCOpt self.parentOptions_phase = ui.scrollArea_propSDCOpt_phase # grid parent options self.gridOptions = ui.gridLayout_propSDCOpt self.gridOptions_phase = ui.gridLayout_propSDCOpt_phase # first plot self.first_plot = False #_______________________________________________________________________ # Initial points (middle) self.P1x = int(N / 2) self.P1y = int(N / 2) self.build_fig() def build_fig(self): #======================================================================= # Create the mpl Figure and FigCanvas objects. #======================================================================= # magnitude self.dpi = 100 self.fig = Figure((5.0, 4.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) # phase self.dpi_phase = 100 self.fig_phase = Figure((5.0, 4.0), dpi=self.dpi_phase) self.canvas_phase = FigureCanvas(self.fig_phase) # axes self.axes = self.fig.add_subplot(111) self.axes_phase = self.fig_phase.add_subplot(111) #_______________________________________________________________________ #======================================================================= # Plotting 2D figure and configuring #======================================================================= # creating image prop self.propSDC_mag = sqrt(self.W_matrix[self.P1x, self.P1y].real**2 + self.W_matrix[self.P1x, self.P1y].imag**2) self.propSDC_phase = angle(self.W_matrix[self.P1y, self.P1y]) # PLOT magnitude self.im = self.axes.imshow(self.propSDC_mag) self.cbar = self.fig.colorbar(self.im) # PLOT phase self.im_phase = self.axes_phase.imshow(self.propSDC_phase) self.cbar_phase = self.fig_phase.colorbar(self.im_phase) # font size self.fsize = 12 self.fsize_phase = 12 # x,y Labels self.axes.set_xlabel("x (m)", fontsize=self.fsize) self.axes.set_ylabel("y (m)", fontsize=self.fsize) #_______________________________________________________________________ #======================================================================= # Tool bar #======================================================================= # Bind the 'pick' event for clicking on one of the bars self.canvas.mpl_connect( 'pick_event', self.ui.on_pick ) #self.canvas.mpl_connect("scroll_event", ui.scrolling) # Bind the 'pick' event for clicking on one of the bars self.canvas_phase.mpl_connect( 'pick_event', self.ui.on_pick ) #self.canvas.mpl_connect("scroll_event", ui.scrolling) # Create the navigation toolbar, tied to the canvas self.mpl_toolbar = NavigationToolbar(self.canvas, self.parent) self.mpl_toolbar_phase = NavigationToolbar(self.canvas_phase, self.parent_phase) #ui.gridLayout_TabPropImage.addWidget(self.mpl_toolbar, 2, 0, 1, 3) self.ui.gridLayout_TabPropSDC2D.addWidget( self.mpl_toolbar, 8, 0, 1, 3, alignment=QtCore.Qt.AlignLeft) self.ui.gridLayout_TabPropSDC2D.addWidget( self.mpl_toolbar_phase, 8, 1, 1, 3, alignment=QtCore.Qt.AlignLeft) #_______________________________________________________________________ #======================================================================= # Canvas in Scroll Area - magnitude #======================================================================= #self.canvas.draw() #self.canvas.setParent(parent) self.canvas.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) # Container for VBOX self.containerGraph = QWidget(self.parent) self.containerGraph.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.containerGraph.setMinimumWidth(self.canvas.width()) self.containerGraph.setMinimumHeight(self.canvas.height()) self.containerGraph.setMaximumWidth(self.canvas.width() + 5) self.containerGraph.setMaximumHeight(self.canvas.height() + 5) # VBOX for canvas self.vbox = QVBoxLayout(self.containerGraph) #self.vbox.setGeometry(QRect(0, 0, self.canvas.width(), self.canvas.height())) self.vbox.addWidget(self.canvas) self.parent.setWidget(self.containerGraph) #_______________________________________________________________________ #======================================================================= # Canvas in Scroll Area - phase #======================================================================= self.canvas_phase.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) # Container for VBOX self.containerGraph_phase = QWidget(self.parent_phase) self.containerGraph_phase.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.containerGraph_phase.setMinimumWidth(self.canvas_phase.width()) self.containerGraph_phase.setMinimumHeight(self.canvas_phase.height()) self.containerGraph_phase.setMaximumWidth(self.canvas_phase.width() + 5) self.containerGraph_phase.setMaximumHeight(self.canvas_phase.height() + 5) # VBOX for canvas self.vbox_phase = QVBoxLayout(self.containerGraph_phase) #self.vbox.setGeometry(QRect(0, 0, self.canvas.width(), self.canvas.height())) self.vbox_phase.addWidget(self.canvas_phase) self.parent_phase.setWidget(self.containerGraph_phase) #_______________________________________________________________________ if not self.first_plot: self.figure_options() self.figure_options_phase() self.first_plot = True def figure_options(self): # label Point P1x self.label_P1x = QtWidgets.QLabel(self.parentOptions) self.label_P1x.setObjectName("label_P1x") self.label_P1x.setText("Point ") self.gridOptions.addWidget(self.label_P1x, 2, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit Point P1x self.lineEdit_P1x = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_P1x.setObjectName("lineEdit_P1x") self.gridOptions.addWidget(self.lineEdit_P1x, 2, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_P1x.setText(str(int(self.N / 2))) self.lineEdit_P1x.textChanged.connect(self.change_P1x) # label Point P1y self.label_P1y = QtWidgets.QLabel(self.parentOptions) self.label_P1y.setObjectName("label_P1y") self.label_P1y.setText('$Point$') self.gridOptions.addWidget(self.label_P1y, 4, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit Point P1x self.lineEdit_P1y = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_P1y.setObjectName("lineEdit_P1y") self.gridOptions.addWidget(self.lineEdit_P1y, 4, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_P1y.setText(str(int(self.N / 2))) self.lineEdit_P1y.textChanged.connect(self.change_P1y) # label title self.label_title = QtWidgets.QLabel(self.parentOptions) self.label_title.setObjectName("label_title") self.label_title.setText("Title") self.gridOptions.addWidget(self.label_title, 6, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit title label self.lineEdit_title = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_title.setObjectName("lineEdit_title") self.lineEdit_title.textChanged.connect(self.change_title) self.gridOptions.addWidget(self.lineEdit_title, 6, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.change_title() self.lineEdit_title.setText("Propagation SDC") # label x self.label_x = QtWidgets.QLabel(self.parentOptions) self.label_x.setObjectName("label_x") self.label_x.setText("Label x-axis") self.gridOptions.addWidget(self.label_x, 8, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit x label self.lineEdit_xLabel = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_xLabel.setObjectName("lineEdit_xlabel") self.lineEdit_xLabel.textChanged.connect(self.change_labelx) self.gridOptions.addWidget(self.lineEdit_xLabel, 8, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.change_labelx() self.lineEdit_xLabel.setText("x") # label y self.label_y = QtWidgets.QLabel(self.parentOptions) self.label_y.setObjectName("label_y") self.label_y.setText("Label y-axis") self.gridOptions.addWidget(self.label_y, 10, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit y label self.lineEdit_yLabel = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_yLabel.setObjectName("lineEdit_ylabel") self.lineEdit_yLabel.textChanged.connect(self.change_labely) self.gridOptions.addWidget(self.lineEdit_yLabel, 10, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.change_labely() self.lineEdit_yLabel.setText("y") # label xlim self.label_xlim = QtWidgets.QLabel(self.parentOptions) self.label_xlim.setObjectName("label_xlim") self.label_xlim.setText("xlim") self.gridOptions.addWidget(self.label_xlim, 12, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit xlim self.lineEdit_xlim = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_xlim.setObjectName("lineEdit_ylabel") self.lineEdit_xlim.textChanged.connect(self.change_xlim) self.gridOptions.addWidget(self.lineEdit_xlim, 12, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_xlim.setText("(_,_)") self.change_xlim() # label ylim self.label_ylim = QtWidgets.QLabel(self.parentOptions) self.label_ylim.setObjectName("label_ylim") self.label_ylim.setText("ylim") self.gridOptions.addWidget(self.label_ylim, 14, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit ylim self.lineEdit_ylim = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_ylim.setObjectName("lineEdit_ylabel") self.lineEdit_ylim.textChanged.connect(self.change_ylim) self.gridOptions.addWidget(self.lineEdit_ylim, 14, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_ylim.setText("(_,_)") self.change_ylim() # label cmap self.label_cmap = QtWidgets.QLabel(self.parentOptions) self.label_cmap.setObjectName("label_cmap") self.label_cmap.setText("Color Map") self.gridOptions.addWidget(self.label_cmap, 20, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit cmap self.lineEdit_cmap = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_cmap.setObjectName("lineEdit_cmap") self.lineEdit_cmap.textChanged.connect(self.change_cmap) self.gridOptions.addWidget(self.lineEdit_cmap, 20, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_cmap.setText("hot") self.change_cmap() # label Font Size self.label_fsize = QtWidgets.QLabel(self.parentOptions) self.label_fsize.setObjectName("label_fsize") self.label_fsize.setText("Font Size") self.gridOptions.addWidget(self.label_fsize, 22, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit font size self.lineEdit_fsize = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_fsize.setObjectName("label_fsize") self.lineEdit_fsize.textChanged.connect(self.change_fsize) self.gridOptions.addWidget(self.lineEdit_fsize, 22, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_fsize.setText(str(self.fsize)) self.change_fsize() # label DPI self.label_dpi = QtWidgets.QLabel(self.parentOptions) self.label_dpi.setObjectName("label_dpi") self.label_dpi.setText("dpi (100-500)") self.gridOptions.addWidget(self.label_dpi, 24, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit DPI self.lineEdit_dpi = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_dpi.setObjectName("label_dpi") self.lineEdit_dpi.textChanged.connect(self.change_dpi) self.gridOptions.addWidget(self.lineEdit_dpi, 24, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_dpi.setText(str(self.dpi)) self.change_fsize() #_______________________________________________________________________ def figure_options_phase(self): try: # label title_phase self.label_title_phase = QtWidgets.QLabel(self.parentOptions) self.label_title_phase.setObjectName("label_title") self.label_title_phase.setText("Title") self.gridOptions_phase.addWidget(self.label_title_phase, 6, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit title label_phase self.lineEdit_title_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_title_phase.setObjectName("lineEdit_title_phase") self.lineEdit_title_phase.textChanged.connect( self.change_title_phase) self.gridOptions_phase.addWidget(self.lineEdit_title_phase, 6, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_title_phase.setText("Prop SDC Phase") self.change_title_phase() # label x_phase self.label_x_phase = QtWidgets.QLabel(self.parentOptions) self.label_x_phase.setObjectName("label_xv") self.label_x_phase.setText("Label x-axis") self.gridOptions_phase.addWidget(self.label_x_phase, 8, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit x label_phase_phase self.lineEdit_xLabel_phase = QtWidgets.QLineEdit( self.parentOptions) self.lineEdit_xLabel_phase.setObjectName("lineEdit_xlabel_phase") self.lineEdit_xLabel_phase.textChanged.connect( self.change_labelx_phase) self.gridOptions_phase.addWidget(self.lineEdit_xLabel_phase, 8, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.change_labelx_phase() self.lineEdit_xLabel_phase.setText("x") # label y_phase self.label_y_phase = QtWidgets.QLabel(self.parentOptions) self.label_y_phase.setObjectName("label_y_phase") self.label_y_phase.setText("Label y-axis") self.gridOptions_phase.addWidget(self.label_y_phase, 10, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit y label_phase self.lineEdit_yLabel_phase = QtWidgets.QLineEdit( self.parentOptions) self.lineEdit_yLabel_phase.setObjectName("lineEdit_ylabel_phase") self.lineEdit_yLabel_phase.textChanged.connect( self.change_labely_phase) self.gridOptions_phase.addWidget(self.lineEdit_yLabel_phase, 10, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.change_labely_phase() self.lineEdit_yLabel_phase.setText("y") # label xlim_phase self.label_xlim_phase = QtWidgets.QLabel(self.parentOptions) self.label_xlim_phase.setObjectName("label_xlim_phase") self.label_xlim_phase.setText("xlim") self.gridOptions_phase.addWidget(self.label_xlim_phase, 12, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit xlim_phase self.lineEdit_xlim_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_xlim_phase.setObjectName("lineEdit_ylabel_phase") self.lineEdit_xlim_phase.textChanged.connect( self.change_xlim_phase) self.gridOptions_phase.addWidget(self.lineEdit_xlim_phase, 12, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_xlim_phase.setText("(_,_)") self.change_xlim_phase() # label ylim_phase self.label_ylim_phase = QtWidgets.QLabel(self.parentOptions) self.label_ylim_phase.setObjectName("label_ylim_phase") self.label_ylim_phase.setText("ylim") self.gridOptions_phase.addWidget(self.label_ylim_phase, 14, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit ylim_phase self.lineEdit_ylim_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_ylim_phase.setObjectName("lineEdit_ylabel_phase") self.lineEdit_ylim_phase.textChanged.connect( self.change_ylim_phase) self.gridOptions_phase.addWidget(self.lineEdit_ylim_phase, 14, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_ylim_phase.setText("(_,_)") self.change_ylim_phase() # label cmap_phase self.label_cmap_phase = QtWidgets.QLabel(self.parentOptions) self.label_cmap_phase.setObjectName("label_cmap_phase") self.label_cmap_phase.setText("Color Map") self.gridOptions_phase.addWidget(self.label_cmap_phase, 20, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit cmap_phase self.lineEdit_cmap_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_cmap_phase.setObjectName("lineEdit_cmap_phase") self.lineEdit_cmap_phase.textChanged.connect( self.change_cmap_phase) self.gridOptions_phase.addWidget(self.lineEdit_cmap_phase, 20, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_cmap_phase.setText("hot") self.change_cmap_phase() # label Font Size_phase self.label_fsize_phase = QtWidgets.QLabel(self.parentOptions) self.label_fsize_phase.setObjectName("label_fsize") self.label_fsize_phase.setText("Font Size") self.gridOptions_phase.addWidget(self.label_fsize_phase, 22, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit font size_phase self.lineEdit_fsize_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_fsize_phase.setObjectName("label_fsize_phase") self.lineEdit_fsize_phase.textChanged.connect(self.change_fsize) self.gridOptions_phase.addWidget(self.lineEdit_fsize_phase, 22, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_fsize_phase.setText(str(self.fsize)) self.change_fsize_phase() # label DPI_phase self.label_dpi_phase = QtWidgets.QLabel(self.parentOptions) self.label_dpi_phase.setObjectName("label_dpi_phase") self.label_dpi_phase.setText("dpi (100-500)") self.gridOptions_phase.addWidget(self.label_dpi_phase, 24, 0, 1, 1, alignment=QtCore.Qt.AlignLeft) # line edit DPI_phase self.lineEdit_dpi_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_dpi_phase.setObjectName("label_dpi_phase") self.lineEdit_dpi_phase.textChanged.connect(self.change_dpi_phase) self.gridOptions_phase.addWidget(self.lineEdit_dpi_phase, 24, 1, 1, 1, alignment=QtCore.Qt.AlignLeft) self.lineEdit_dpi_phase.setText(str(self.dpi_phase)) self.change_fsize_phase() #_______________________________________________________________________ except Exception as error: self.ui.update_outputText(str(error)) def change_P1x(self): try: temp = self.lineEdit_P1x.text() if temp != "": new = int(temp) if new >= 0 and new < self.N: self.P1x = new self.update_pcolor() self.update_draw() except Except as error: self.ui.update_outputText(str(error)) def change_P1y(self): try: temp = self.lineEdit_P1y.text() if temp != "": new = int(temp) if new >= 0 and new < self.N: self.P1y = new self.update_pcolor() self.update_draw() except Except as error: self.ui.update_outputText(str(error)) def update_pcolor(self): try: # creating image prop self.propSDC_mag = sqrt(self.W_matrix[self.P1x, self.P1y].real**2 + self.W_matrix[self.P1x, self.P1y].imag**2) self.propSDC_phase = angle(self.W_matrix[self.P1y, self.P1y]) self.im.set_array(self.propSDC_mag) self.im_phase.set_array(self.propSDC_phase) except Exception as error: self.ui.update_outputText(str(error)) def update_draw(self): self.canvas.draw() self.canvas.updateGeometry() self.canvas_phase.draw() self.canvas_phase.updateGeometry() #=========================================================================== # Magnitude #=========================================================================== def change_dpi(self): try: new = int(self.lineEdit_dpi.text()) if new >= 100 and new <= 500: self.dpi = new self.fig.set_dpi(new) self.update_draw() except: pass def change_fsize(self): try: self.fsize = int(self.lineEdit_fsize.text()) self.change_title() self.change_labelx() self.change_labely() self.update_draw() print(self.fsize) except Exception as error: pass #self.ui.update_outputText(error) def change_cmap(self): try: self.im.set_cmap(self.lineEdit_cmap.text()) self.canvas.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_xlim(self): try: temp_txt = self.lineEdit_xlim.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0] == "(" and temp_txt[-1] == ")": actual = "" for i in range(1, Ntxt - 1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i == Ntxt - 2: actual += temp_txt[i] numList.append(float(actual)) else: actual += temp_txt[i] self.axes.set_xlim(numList[0], numList[1]) except Exception as error: pass #self.ui.update_outputText(error) def change_ylim(self): try: temp_txt = self.lineEdit_ylim.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0] == "(" and temp_txt[-1] == ")": actual = "" for i in range(1, Ntxt - 1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i == Ntxt - 2: actual += temp_txt[i] numList.append(float(actual)) else: actual += temp_txt[i] self.axes.set_ylim(numList[0], numList[1]) self.canvas.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_title(self): try: self.axes.set_title(self.lineEdit_title.text(), fontsize=self.fsize) self.canvas.draw() except: pass def change_labelx(self): if self.lineEdit_xLabel.text() == "": self.axes.set_xlabel("") else: try: self.axes.set_xlabel(self.lineEdit_xLabel.text(), fontsize=self.fsize) self.canvas.draw() except Exception as error: self.ui.update_outputText(str(error)) def change_labely(self): if self.lineEdit_yLabel.text() == "": self.axes.set_ylabel("") else: try: self.axes.set_ylabel(self.lineEdit_yLabel.text(), fontsize=self.fsize) self.canvas.draw() except: pass #self.ui.update_outputText("Unable to update y-label.") #___________________________________________________________________________ #=========================================================================== # Phase #=========================================================================== try: def change_dpi_phase(self): try: new = int(self.lineEdit_dpi_phase.text()) if new >= 100 and new <= 500: self.dpi_phase = new self.fig_phase.set_dpi(new) self.update_draw() except: pass def change_fsize_phase(self): try: self.fsize_phase = int(self.lineEdit_fsize.text()) self.change_title_phase() self.change_labelx_phase() self.change_labely_phase() self.update_draw() except Exception as error: pass #self.ui.update_outputText(error) def change_cmap_phase(self): try: self.im_phase.set_cmap(self.lineEdit_cmap_phase.text()) self.canvas_phase.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_xlim_phase(self): try: temp_txt = self.lineEdit_xlim_phase.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0] == "(" and temp_txt[-1] == ")": actual = "" for i in range(1, Ntxt - 1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i == Ntxt - 2: actual += temp_txt[i] numList.append(float(actual)) else: actual += temp_txt[i] self.axes_phase.set_xlim(numList[0], numList[1]) except Exception as error: pass #self.ui.update_outputText(error) def change_ylim_phase(self): try: temp_txt = self.lineEdit_ylim_phase.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0] == "(" and temp_txt[-1] == ")": actual = "" for i in range(1, Ntxt - 1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i == Ntxt - 2: actual += temp_txt[i] numList.append(float(actual)) else: actual += temp_txt[i] self.axes_phase.set_ylim(numList[0], numList[1]) self.canvas.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_title_phase(self): try: self.axes_phase.set_title(self.lineEdit_title_phase.text(), fontsize=self.fsize_phase) self.canvas_phase.draw() except: pass def change_labelx_phase(self): if self.lineEdit_xLabel_phase.text() == "": self.axes_phase.set_xlabel("") else: try: self.axes_phase.set_xlabel( self.lineEdit_xLabel_phase.text(), fontsize=self.fsize_phase) self.canvas_phase.draw() except Exception as error: self.ui.update_outputText(str(error)) def change_labely_phase(self): if self.lineEdit_yLabel_phase.text() == "": self.axes_phase.set_ylabel("") else: try: self.axes_phase.set_ylabel( self.lineEdit_yLabel_phase.text(), fontsize=self.fsize_phase) self.canvas_phase.draw() except: pass #self.ui.update_outputText("Unable to update y-label.") except Exception as error: self.ui.update_outputText(error)
class CalibPH(tk.Frame): def __init__(self, parent, controller, ChannelID, channelName): tk.Frame.__init__(self, parent) defs_common.logtoconsole("Initializing CalibPH...", fg="YELLOW", bg="MAGENTA", style="BRIGHT") self.controller = controller self.parent = parent self.ChannelID = ChannelID self.ChannelName = channelName # registering validation command #self.vldt_ifnum_cmd = (self.register(self.ValidateIfNum),'%s', '%S') self.isCalRunning = False self.calType = "none" self.calPoints = [] self.currentDVlistCounter = 0 self.currentDVlist = [] strHeadLabel = "Channel " + str(self.ChannelID) + " [" + str( self.ChannelName) + "]" headlabel = tk.Label(self, text=strHeadLabel, font=LARGE_FONT) headlabel.grid(row=0, column=0, pady=10, sticky=W, columnspan=3) lbl_calPoint = headlabel = tk.Label(self, text="Cal Point") lbl_calPoint.grid(row=1, column=0, padx=10) lbl_refVal = headlabel = tk.Label(self, text="PH Reference") lbl_refVal.grid(row=1, column=1, padx=10) lbl_digVal = headlabel = tk.Label(self, text="Digital Value") lbl_digVal.grid(row=1, column=2, padx=10) # we want to underline the header, so: # clone the font, set the underline attribute, # and assign it to our widget f = font.Font(lbl_calPoint, lbl_calPoint.cget("font")) f.configure(underline=True) lbl_calPoint.configure(font=f) lbl_refVal.configure(font=f) lbl_digVal.configure(font=f) # read values from config file # Low Val lbl_pointLow = tk.Label(self, text="Low") lbl_pointLow.grid(row=2, column=0) lbl_phrefLow = tk.Label(self, text="4.0") lbl_phrefLow.grid(row=2, column=1) strval = "ch" + str(ChannelID) + "_ph_low" val = self.controller.controller.controller.controller.downloadsettings( "mcp3008", strval, "900") self.lbl_low_val = tk.Label(self, text=val) self.lbl_low_val.grid(row=2, column=2) btn_LowCalStart = Button(self, text="Start Low Calibration", command=lambda: self.startCalLoop('low')) btn_LowCalStart.grid(row=2, column=3, sticky=EW) # Med Val lbl_pointMed = tk.Label(self, text="Mid") lbl_pointMed.grid(row=3, column=0) lbl_phrefMed = tk.Label(self, text="7.0") lbl_phrefMed.grid(row=3, column=1) strval = "ch" + str(ChannelID) + "_ph_med" val = self.controller.controller.controller.controller.downloadsettings( "mcp3008", strval, "800") self.lbl_med_val = tk.Label(self, text=val) self.lbl_med_val.grid(row=3, column=2) btn_MedCalStart = Button(self, text="Start Mid Calibration", command=lambda: self.startCalLoop('med')) btn_MedCalStart.grid(row=3, column=3, sticky=EW) # High Val lbl_pointHigh = tk.Label(self, text="High") lbl_pointHigh.grid(row=4, column=0) lbl_phrefHigh = tk.Label(self, text="10.0") lbl_phrefHigh.grid(row=4, column=1) strval = "ch" + str(ChannelID) + "_ph_high" val = self.controller.controller.controller.controller.downloadsettings( "mcp3008", strval, "700") self.lbl_high_val = tk.Label(self, text=val) self.lbl_high_val.grid(row=4, column=2) btn_HighCalStart = Button(self, text="Start High Calibration", command=lambda: self.startCalLoop('high')) btn_HighCalStart.grid(row=4, column=3, sticky=EW) # Show current PH and DV val frame_LiveData = LabelFrame(self, text="Live Data") frame_LiveData.grid(row=5, column=0, columnspan=4, sticky=EW) lbl_curPH = tk.Label(frame_LiveData, text="Current PH:") lbl_curPH.grid(row=5, column=0, sticky=E, columnspan=2) self.lbl_curPHval = tk.Label(frame_LiveData, text="waiting...") self.lbl_curPHval.grid(row=5, column=2, sticky=W, padx=20) lbl_curDV = tk.Label(frame_LiveData, text="Current Digital Value:") lbl_curDV.grid(row=6, column=0, sticky=E, columnspan=2) self.lbl_curDVval = tk.Label(frame_LiveData, text="waiting...") self.lbl_curDVval.grid(row=6, column=2, sticky=W, padx=20, pady=10) # calibration data frame_CalData = LabelFrame(self, text="Calibration Data") frame_CalData.grid(row=7, column=0, columnspan=4, sticky=EW) # cal label self.calLabel = Label(frame_CalData, text=" ", font=LARGE_FONT_BOLD) self.calLabel.grid(row=0, column=0, columnspan=4, padx=5, pady=4, sticky=W) # progress bar self.calprogress = ttk.Progressbar(frame_CalData, orient='horizontal', mode='determinate', maximum=NUMCALPOINTS, length=450) self.calprogress.grid(row=1, column=0, sticky=EW, pady=10, padx=5, columnspan=6) # data points plot style.use("ggplot") self.figprobe = Figure(figsize=(6, 2.5), dpi=100) self.figprobe.set_facecolor("gainsboro") self.aniprobe = self.figprobe.add_subplot(111) self.aniprobe.set_title("Data Points") self.canvasprobe = FigureCanvasTkAgg(self.figprobe, frame_CalData) self.canvasprobe.show() self.canvasprobe.get_tk_widget().grid(sticky=EW, row=2, column=2, columnspan=4) # histogram plot ## self.fighist, self.axhist = plt.subplots() ## plt.title("Histogram") self.fighist = Figure(figsize=(6, 2.5), dpi=100) self.axhist = self.fighist.add_subplot(111) self.axhist.set_title("Histogram") self.fighist.set_facecolor("gainsboro") self.fighist.set_size_inches(6, 2.5) self.fighist.set_dpi(100) self.axhist.axes.tick_params(axis='x', labelsize=8) self.axhist.axes.tick_params(axis='y', labelsize=8) self.axhist.axes.set_xlim([0, 1023]) self.canvashist = FigureCanvasTkAgg(self.fighist, frame_CalData) self.canvashist.show() self.canvashist.get_tk_widget().grid(sticky=EW, row=3, column=2, columnspan=4) ani = animation.FuncAnimation(self.figprobe, self.animate_probe, interval=1000) # scrolled textbox for calibration values self.txt_calPoints = tkst.ScrolledText(frame_CalData, width=15, height=10, wrap='word') self.txt_calPoints.grid(row=2, column=0, sticky=NSEW, padx=5, columnspan=2) # results area self.frame_Results = LabelFrame(frame_CalData, text="Results") self.frame_Results.grid(row=3, column=0, sticky=NSEW, columnspan=2) self.lbl_num_Samples = Label(self.frame_Results, text="Num. Samples:") self.lbl_num_Samples.grid(row=0, column=0, sticky=E) self.lbl_num_SamplesVal = Label(self.frame_Results, text="", width=10) self.lbl_num_SamplesVal.grid(row=0, column=1, sticky=EW) self.lbl_resultMin = Label(self.frame_Results, text="Min:") self.lbl_resultMin.grid(row=1, column=0, sticky=E) self.lbl_resultMinVal = Label(self.frame_Results, text="", width=10) self.lbl_resultMinVal.grid(row=1, column=1, sticky=EW) self.lbl_resultMax = Label(self.frame_Results, text="Max:") self.lbl_resultMax.grid(row=2, column=0, sticky=E) self.lbl_resultMaxVal = Label(self.frame_Results, text="", width=10) self.lbl_resultMaxVal.grid(row=2, column=1, sticky=EW) self.lbl_resultMean = Label(self.frame_Results, text="Mean:") self.lbl_resultMean.grid(row=3, column=0, sticky=E) self.lbl_resultMeanVal = Label(self.frame_Results, text="", width=10) self.lbl_resultMeanVal.grid(row=3, column=1, sticky=EW) self.lbl_resultStdDev = Label(self.frame_Results, text="Std. Deviation:") self.lbl_resultStdDev.grid(row=4, column=0, sticky=E) self.lbl_resultStdDevVal = Label(self.frame_Results, text="", width=10) self.lbl_resultStdDevVal.grid(row=4, column=1, sticky=EW) self.lbl_resultCalval = Label(self.frame_Results, text="Calibration Value:") self.lbl_resultCalval.grid(row=5, column=0, sticky=E) self.lbl_resultCalvalVal = Label(self.frame_Results, text="", width=10, font=LARGE_FONT_BOLD) self.lbl_resultCalvalVal.grid(row=5, column=1, sticky=EW, pady=20) self.btn_ApplyCal = Button(self.frame_Results, text="Apply Calibration Value", command=self.ApplyCal) self.btn_ApplyCal.grid(row=6, column=0, columnspan=2, sticky=EW) self.currentADCLoop(self.ChannelID) def startCalLoop(self, calPoint): self.isCalRunning = True #reset stats self.calprogress['value'] = 0 self.txt_calPoints.delete(1.0, END) self.lbl_resultMeanVal.config(text="") self.lbl_num_SamplesVal.config(text="") self.lbl_resultStdDevVal.config(text="") self.lbl_resultMinVal.config(text="") self.lbl_resultMaxVal.config(text="") self.lbl_resultCalvalVal.config(text="") #clear old data plot self.aniprobe.cla() #remove old histogram if exists try: t = [b.remove() for b in self.patches] print(t) self.canvashist.show() except: print("no bars") self.calPoints.clear() if calPoint == 'low': print("calLoop low") self.caltype = "low" self.calLabel['text'] = "Low Calibration: In Progress..." if calPoint == 'med': print("calLoop med") self.caltype = "med" self.calLabel['text'] = "Mid Calibration: In Progress..." if calPoint == 'high': print("calLoop high") self.caltype = "high" self.calLabel['text'] = "High Calibration: In Progress..." def currentADCLoop(self, chnum): try: # get setting value from server request = { "rpc_req": "get_ADCfromMCP3008", "ch_num": str(chnum), } request = json.dumps(request) val = self.controller.controller.controller.controller.rpc_call( request, "rpc_queue") val = val.decode() val = json.loads(val) val = val.get("dv") print(val) #self.lbl_curDVval.configure(text=val) # buffer for current PH vals if self.currentDVlistCounter < 9: self.currentDVlistCounter = self.currentDVlistCounter + 1 else: self.currentDVlistCounter = 0 if len(self.currentDVlist) < 10: self.currentDVlist.append(val) #print("in") else: self.currentDVlist[self.currentDVlistCounter] = val #print("out") print("counter = " + str(self.currentDVlistCounter)) print("Current DV list: = " + str(self.currentDVlist)) phVal = ph_sensor.dv2ph(int(mean(self.currentDVlist)), self.ChannelID, self.lbl_low_val.cget("text"), self.lbl_med_val.cget("text"), self.lbl_high_val.cget("text")) print('{0:.2f}'.format(float(phVal))) self.lbl_curPHval.configure(text='{0:.2f}'.format(float(phVal)), font=LARGE_FONT_BOLD) self.lbl_curDVval.configure(text=str(self.currentDVlist)) if self.isCalRunning == True: print("cal in progress") self.calPoints.append(val) print(self.calPoints) self.calprogress['value'] = len(self.calPoints) strVal = str(len(self.calPoints)) + ":" + str(val) self.txt_calPoints.insert(END, str(strVal) + '\n') self.txt_calPoints.see('end') #plot new point #self.animate_probe(self.figprobe) calPtArray = numpy.array(self.calPoints) stdDev = numpy.std(calPtArray, axis=0) meanDV = mean(self.calPoints) minDV = min(self.calPoints) maxDV = max(self.calPoints) print("std dev = " + str(stdDev)) print(meanDV + stdDev) self.lbl_resultMeanVal.config( text=str('{:.2f}'.format(meanDV))) self.lbl_num_SamplesVal.config(text=str(len(self.calPoints))) self.lbl_resultStdDevVal.config( text=str('{:.2f}'.format(stdDev))) self.lbl_resultMinVal.config(text=str(minDV)) self.lbl_resultMaxVal.config(text=str(maxDV)) if len(self.calPoints) >= NUMCALPOINTS: print("mean = " + str(mean(self.calPoints))) if self.caltype == 'low': #self.lbl_low_val.config(text=int(mean(self.calPoints))) self.calLabel[ 'text'] = "Low Calibration: Aquisition Complete" elif self.caltype == 'med': #self.lbl_med_val.config(text=int(mean(self.calPoints))) self.calLabel[ 'text'] = "Mid Calibration: Aquisition Complete" elif self.caltype == 'high': #self.lbl_high_val.config(text=int(mean(self.calPoints))) self.calLabel[ 'text'] = "High Calibration: Aquisition Complete" FilteredCountList = [ x for x in self.calPoints if (x >= meanDV - ph_Sigma * stdDev) ] FilteredCountList = [ x for x in FilteredCountList if (x <= meanDV + ph_Sigma * stdDev) ] print("Cal point = " + str(int(mean(FilteredCountList)))) self.lbl_resultCalvalVal.config( text=str(int(math.ceil(mean(FilteredCountList))))) self.plot_hist() self.isCalRunning = False self.after(1000, self.currentADCLoop, chnum) #return val except Exception as e: print(e) pass def getADCval(self, chnum): # get setting value from server request = { "rpc_req": "get_ADCfromMCP3008", "ch_num": str(chnum), } request = json.dumps(request) val = self.controller.controller.controller.controller.rpc_call( request, "rpc_queue") val = val.decode() val = json.loads(val) val = val.get("dv") print(val) return val # plot calibration data def animate_probe(self, i): if self.isCalRunning == True: self.aniprobe.clear() chartData = self.calPoints dim = numpy.arange(1, len(chartData) + 1) #print(dim) #print(chartData) self.aniprobe.set_title("Data Points") try: self.aniprobe.plot(dim, chartData, "-", color='GREEN') self.aniprobe.axes.tick_params(axis='x', labelsize=8) self.aniprobe.axes.tick_params(axis='y', labelsize=8) self.aniprobe.axes.set_xlim([1, None]) except: print("Error plotting data") pass if len(chartData) > 1: meanDV = mean(self.calPoints) calPtArray = numpy.array(chartData) stdDev = numpy.std(calPtArray, axis=0) FilteredCountList = [ x for x in self.calPoints if (x >= meanDV - ph_Sigma * stdDev) ] FilteredCountList = [ x for x in FilteredCountList if (x <= meanDV + ph_Sigma * stdDev) ] targetMeanDV = mean(FilteredCountList) targetDV = int(math.ceil(targetMeanDV)) self.aniprobe.axhline(y=(targetDV), color='red') #self.aniprobe.axhline(y=(meanDV), color='red') self.aniprobe.axhline(y=math.ceil(meanDV + stdDev), color='black', linestyle='-.') self.aniprobe.axhline(y=math.ceil(meanDV - stdDev), color='black', linestyle='-.') t = self.aniprobe.text(1, targetDV, str(targetDV), fontsize=24) #t = self.aniprobe.text(1, meanDV, str("{:.2f}".format(meanDV)), fontsize=24) t.set_bbox( dict(facecolor='white', alpha=0.5, edgecolor='black')) return # plot calibration histogram def plot_hist(self): try: # lets plot the histogram bins = range(0, 1023, binsize) # set up bins for histogram n, dvbins, self.patches = self.axhist.hist(self.calPoints, bins, color="royalblue", ec="royalblue") self.canvashist.show() except: print("Error plotting histogram") pass return def ApplyCal(self): self.running = 0 try: calVal = self.lbl_resultCalvalVal.cget("text") if self.caltype == 'low': self.lbl_low_val.config(text=int(calVal)) elif self.caltype == 'med': self.lbl_med_val.config(text=int(calVal)) elif self.caltype == 'high': self.lbl_high_val.config(text=int(calVal)) except Exception as e: print(e)
class Chart(object): """ Simple and clean facade to Matplotlib's plotting API. A chart instance abstracts a plotting device, on which one or multiple related plots can be drawn. Charts can be exported as images, or visualized interactively. Each chart instance will always open in its own GUI window, and this window will never block the execution of the rest of the program, or interfere with other L{Chart}s. The GUI can be safely opened in the background and closed infinite number of times, as long as the client program is still running. By default, a chart contains a single plot: >>> chart.plot matplotlib.axes.AxesSubplot >>> chart.plot.hist(...) If C{rows} and C{columns} are defined, the chart will contain C{rows} x C{columns} number of plots (equivalent to MPL's sub-plots). Each plot can be assessed by its index: >>> chart.plots[0] first plot or by its position in the grid: >>> chart.plots[0, 1] plot at row=0, column=1 @param number: chart number; by default this a L{Chart.AUTONUMBER} @type number: int or None @param title: chart master title @type title: str @param rows: number of rows in the chart window @type rows: int @param columns: number of columns in the chart window @type columns: int @note: additional arguments are passed directly to Matplotlib's Figure constructor. """ AUTONUMBER = None _serial = 0 def __init__(self, number=None, title='', rows=1, columns=1, backend=Backends.WX_WIDGETS, *fa, **fk): if number == Chart.AUTONUMBER: Chart._serial += 1 number = Chart._serial if rows < 1: rows = 1 if columns < 1: columns = 1 self._rows = int(rows) self._columns = int(columns) self._number = int(number) self._title = str(title) self._figure = Figure(*fa, **fk) self._figure._figure_number = self._number self._figure.suptitle(self._title) self._beclass = backend self._hasgui = False self._plots = PlotsCollection(self._figure, self._rows, self._columns) self._canvas = FigureCanvasAgg(self._figure) formats = [(f.upper(), f) for f in self._canvas.get_supported_filetypes()] self._formats = csb.core.Enum.create('OutputFormats', **dict(formats)) def __getitem__(self, i): if i in self._plots: return self._plots[i] else: raise KeyError('No such plot number: {0}'.format(i)) def __enter__(self): return self def __exit__(self, *a, **k): self.dispose() @property def _backend(self): return Backend.get(self._beclass, started=True) @property def _backend_started(self): return Backend.query(self._beclass) @property def title(self): """ Chart title @rtype: str """ return self._title @property def number(self): """ Chart number @rtype: int """ return self._number @property def plots(self): """ Index-based access to the plots in this chart @rtype: L{PlotsCollection} """ return self._plots @property def plot(self): """ First plot @rtype: matplotlib.AxesSubplot """ return self._plots[0] @property def rows(self): """ Number of rows in this chart @rtype: int """ return self._rows @property def columns(self): """ Number of columns in this chart @rtype: int """ return self._columns @property def width(self): """ Chart's width in inches @rtype: int """ return self._figure.get_figwidth() @width.setter def width(self, inches): self._figure.set_figwidth(inches) if self._backend_started: self._backend.resize(self._figure) @property def height(self): """ Chart's height in inches @rtype: int """ return self._figure.get_figheight() @height.setter def height(self, inches): self._figure.set_figheight(inches) if self._backend_started: self._backend.resize(self._figure) @property def dpi(self): """ Chart's DPI @rtype: int """ return self._figure.get_dpi() @dpi.setter def dpi(self, dpi): self._figure.set_dpi(dpi) self._backend.resize(self._figure) @property def formats(self): """ Supported output file formats @rtype: L{csb.core.enum} """ return self._formats def show(self): """ Show the GUI window (non-blocking). """ if not self._hasgui: self._backend.add(self._figure) self._hasgui = True self._backend.show(self._figure) def hide(self): """ Hide (but do not dispose) the GUI window. """ self._backend.hide(self._figure) def dispose(self): """ Dispose the GUI interface. Must be called at the end if any chart.show() calls have been made. Automatically called if using the chart in context manager ("with" statement). @note: Failing to call this method if show() has been called at least once may cause backend-related errors. """ if self._backend_started: service = self._backend if service and service.running: service.destroy(self._figure, wait=True) service.client_disposed(self) def save(self, file, format='png', crop=False, dpi=None, *a, **k): """ Save all plots to an image. @param file: destination file name @type file: str @param format: output image format; see C{chart.formats} for enumeration @type format: str or L{csb.core.EnumItem} @param crop: if True, crop the image (equivalent to MPL's bbox=tight) @type crop: bool @note: additional arguments are passed directly to MPL's savefig method """ if 'bbox_inches' in k: bbox = k['bbox_inches'] del k['bbox_inches'] else: if crop: bbox = 'tight' else: bbox = None self._canvas.print_figure(file, format=str(format), bbox_inches=bbox, dpi=dpi, *a, **k)
def calculateWallMap(self, resolution=0.1, level=0, zlim=(0.15, 1.50), xlim=None, ylim=None, roomIds=None): figSize = (10, 10) fig = Figure(figsize=figSize, dpi=100, frameon=False) canvas = FigureCanvas(fig) ax = fig.add_subplot(111, aspect='equal') fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0) ax.axis('off') ax.set_aspect('equal') floorZ = self._getFloorReferenceZ(level) zlim = np.array(zlim) + floorZ if roomIds is not None: layoutModels = [] for roomId in roomIds: layoutModels.extend([ model for model in self.scene.scene.findAllMatches( '**/level-%d/room-%s/layouts/object-*/+ModelNode' % (level, roomId)) ]) else: layoutModels = [ model for model in self.scene.scene.findAllMatches( '**/level-%d/**/layouts/object-*/+ModelNode' % level) ] # Loop for all walls in the scene: for model in layoutModels: modelId = model.getNetTag('model-id') if not modelId.endswith('w'): continue addModelTriangles(ax, model, zlim=zlim) if xlim is not None and ylim is not None: ax.set_xlim(xlim) ax.set_ylim(ylim) else: ax.autoscale(True) set_axes_equal(ax) xlim, ylim = ax.get_xlim(), ax.get_ylim() xrange = xlim[1] - xlim[0] yrange = ylim[1] - ylim[0] assert np.allclose(xrange, yrange, atol=1e-6) dpi = (xrange / resolution) / figSize[0] fig.set_dpi(dpi) wallMap = canvas2image(canvas) plt.close(fig) # RGB to binary wallMap = np.round(np.mean(wallMap[:, :], axis=-1)) # NOTE: inverse image so that obstacle areas are shown in white wallMap = 1.0 - wallMap return wallMap, xlim, ylim
def do_it(cfg): DPI = 96 fig = Figure() FigureCanvasAgg(fig) ax: Axes = fig.subplots( 1, 1, subplot_kw=dict(xticks=[], yticks=[]), gridspec_kw=dict(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0), ) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) ax.set_axis_off() ax.set_xlim(-1, 1) ax.set_ylim(-1, 1) fig.set_dpi(DPI) fig.set_size_inches(cfg.dim / DPI, cfg.dim / DPI) # params tau = 2 * np.pi NPOINTS = 1000 XMAX = 1 FREQ = 1.3 def lerp(x, y, a: float): return x * (1 - a) + y * a def sintau(x): return np.sin(tau * x) def costau(x): return np.cos(tau * x) gauss = lambda xs, W: np.exp(-(xs / W)**2) cos_win = lambda xs: costau(xs / 4) def win(xs): assert xs[0] == -1 assert xs[-1] == 1 W = 0.6 e = 1 return gauss(xs, W) * cos_win(xs)**e # plot xs = np.linspace(-XMAX, XMAX, NPOINTS) def sinusoid(dx, freq=1, yscale=1): # sintau # x: compress=freq, shift=dx # y: mul=yscale return lambda xs: sintau((xs - dx) * freq) * yscale def plot_sinusoid(dx, freq, yscale, alpha, color=None): func = sinusoid(dx, freq, yscale) ax.plot(xs, func(xs) * win(xs), alpha=alpha, color=color, linewidth=cfg.line_width) top = "narrow" blue = "narrow" top_blue = top == blue if top_blue: i = cfg.nline - 1 di = -1 else: i = 0 di = 1 freqs = np.geomspace(0.2, 1, cfg.nline) if top == "wide": freqs = freqs[::-1] e = 0 for freq in freqs: plot_sinusoid(0, freq=freq, yscale=freq**e, alpha=1, color=cmap(i)) i += di fig.savefig(f"{cfg.dim}.png", transparent=True)
class PlotSettings(object): """ The PlotSettings-class initializes the default values for the plot. The class can return those values to the main window and the plot-settings dialog. The plot-settings dialog will call the instance of this class to change the different settings. This class also stores the figure and can return different subplot-layouts. This class also stores the normal and inverse transformations of the stereonet, and will return the correct one for either the Schmidt- or Wulff-Net. """ def __init__(self, testing): """ Initalizes the default values, colors and the matplotlib-figure. Initializes and stores the default settings. Initializes the matplotlib-figure, a folder-icon for the group-layers of the layer-view. """ self.folder_icon = Gtk.IconTheme.get_default().load_icon( "folder", 16, 0) self.props = OrderedDict(sorted({"draw_grid": True, "equal_area_projection": True, "minor_grid_spacing": 2, "major_grid_spacing": 10, "grid_cutoff_lat": 80, "show_north": True, "show_cross": True, "pixel_density": 75, "grid_linestyle": "--", "grid_color": "#787878", "grid_width": 0.4, "draw_legend": True, "canvas_color": "#bfbfbf", "highlight": False }.items())) self.night_mode = False self.fig = Figure(dpi=self.props["pixel_density"]) if testing == False: try: self.g_settings = Gio.Settings.new("org.gtk.innstereo") self.get_defaults() except: pass def get_defaults(self): """ Gets the defaults from the Gio.Settings. """ self.props["draw_legend"] = self.g_settings.get_boolean("show-legend") self.props["draw_grid"] = self.g_settings.get_boolean("draw-grid") self.props["equal_area_projection"] = self.g_settings.get_boolean("stereonet-projection") self.props["show_cross"] = self.g_settings.get_boolean("center-cross") self.night_mode = self.g_settings.get_boolean("night-mode") self.props["pixel_density"] = self.g_settings.get_value("pixel-density").get_int32() self.props["highlight"] = self.g_settings.get_boolean("highlight-mode") def get_fig(self): """ Returns the Matplotlib-Figure. Returns the figure that is stored by this class. The MainWindow class calls this function once during initialization to add the figure to the FigureCanvas. """ return self.fig def get_inverse_transform(self): """ Returns the inverse transform for the current stereonet projection. If the projection is equal are (True) the function returns the InvertedLambertTransform- or else the InvertedSterreographicTransform-class. """ if self.props["equal_area_projection"] is True: return mplstereonet.stereonet_transforms.\ InvertedLambertTransform(0, 0, self.props["pixel_density"]) else: return mplstereonet.stereonet_transforms.\ InvertedStereographicTransform(0, 0, self.props["pixel_density"]) def get_transform(self): """ Returns the normal transform for the current stereonet projection. If the projection is equal are (True) the function returns the LambertTransform- or else the SterreographicTransform-class. """ if self.props["equal_area_projection"] is True: return mplstereonet.stereonet_transforms.\ LambertTransform(0, 0, self.props["pixel_density"]) else: return mplstereonet.stereonet_transforms.\ StereographicTransform(0, 0, self.props["pixel_density"]) def get_draw_grid_state(self): """ Returns if the grid should be drawn for the stereonet. Returns a boolean. True mean that a grid should be drawn. False means that no grid should be drawn. This method is called by the MainWindow- redraw_plot-method. """ return self.props["draw_grid"] def set_draw_grid_state(self, new_state): """ Sets if the grid should be drawn for the stereonet. Expects a boolean. True mean that a grid should be drawn. False means that no grid should be drawn. This method is called by the LayerProperties-dialog when the setting is changed. """ self.props["draw_grid"] = new_state def get_folder_icon(self): """ Returns the folder icon used for the group-layer pixbuf. Always returns the "folder" icon from the Gtk.IconTheme. The folder will therefore match the desktop-theme set by the user. This method is called by the MainWindow "on_toolbutton_create_group_layer_clicked"- method. """ return self.folder_icon def get_pixel_density(self): """ Returns the pixel density the plot is using. The pixel density is an int and the default value is 75. This method is called by the LayerProperties-dialog so it can display the current value. """ return self.props["pixel_density"] def set_pixel_density(self, new_pixel_density): """ Sets a new pixel density for the plot. Expects an int. This method is called by the LayerProperties-dialog. The new value will be used when the plot redraws when the settings in the dialog are applied. """ self.props["pixel_density"] = new_pixel_density def get_projection(self): """ Returns the projection currently used by the stereonet. Returns one of two strings that MPLStereonet uses to distinguish between the equal-area and equal-angle projection. This method is only called from this class when the view is switched. """ if self.props["equal_area_projection"] is True: return "equal_area_stereonet" else: return "equal_angle_stereonet" def get_projection_state(self): """ Returns the projection state for the stereonet. Returns a boolean. True means that the stereonet should be drawn with equal-area. False mean equal-angle. This method is called by the StereonetProperties-dialog to load the current setting. """ return self.props["equal_area_projection"] def set_projection_state(self, new_state): """ Sets a new projection state. Expects a boolean. True means that the projection will be equal-area, False means equal-angle. This method is called by the StereonetProperties-dialog when a new setting for the projection is applied. """ self.props["equal_area_projection"] = new_state def get_grid_linestyle(self): """ Returns the linestyle of the grid. The linestyle is returned as a string. Default is "--" (dashed). This method is called by the MainWindow "redraw_plot"-method. """ return self.props["grid_linestyle"] def get_grid_color(self): """ Returns the color of the grid. Returns the color as a hex-triplet. The default is "#787878". This method is called by the MainWindow "redraw_plot"-method. """ return self.props["grid_color"] def get_grid_width(self): """ Returns the width of the grid lines. The width of the grid lines is returned as a float or int. The default is "0.4". This method is called by the MainWindow "redraw_plot"-method. """ return self.props["grid_width"] def get_draw_legend(self): """ Returns if the legend should be drawn as a boolean. The returned value is either True if a legend should be drawn, or False if no legend should be drawn. This method is called by the MainWindow "redraw_plot"-method and the StereonetProperties-dialog. """ return self.props["draw_legend"] def set_draw_legend(self, new_state): """ Sets a new state for whether the legend should be drawn. Expects a boolean. True means that the legend should be drawn. False means that no legend should be drawn. This method is called by the StereonetProperties-dialog when a new setting is applied. """ self.props["draw_legend"] = new_state def get_canvas_rgba(self): """ Returns the canvas color as a Gdk.RGBA. This method is called by the StereonetProperties-dialog to apply the current canvas color to the ColorButton. """ rgba = Gdk.RGBA() rgba.parse(self.props["canvas_color"]) return rgba.to_color() def set_canvas_color(self, new_color): """ Sets a new canvas color. Expects a hex-triplet string (e.g. "#bfbfbf"). This method is called by the StereonetProperties-dialog when a new color is applied to the canvas. """ self.props["canvas_color"] = new_color def get_stereonet(self): """ Resets the figure and returns the stereonet axis. When the view in the main window is changed to only stereoent. The figure is reset. Then the current settings are applied and one subplot for the stereonet is created. This method is called when the MainWindow "__init__"-method and the "redraw_plot"-method. """ self.fig.clf() self.fig.patch.set_facecolor(self.props["canvas_color"]) self.fig.set_dpi(self.props["pixel_density"]) gridspec = GridSpec(2, 3) sp_stereo = gridspec.new_subplotspec((0, 0), rowspan=2, colspan=2) sp_cbar = gridspec.new_subplotspec((1, 2), rowspan=1, colspan=1) ax_stereo = self.fig.add_subplot(sp_stereo, projection=self.get_projection()) ax_cbar = self.fig.add_subplot(sp_cbar) ax_cbar.axis("off") ax_cbar.set_aspect(8) return ax_stereo, ax_cbar def get_stereo_rose(self): """ Resets the figure and returns a stereonet and rose diagram axis. When the view in the main window is changed to stereonet and rose diagram, the figure is reset. The current settings are applied and two subplots for the stereonet and rose diagram are created. The axis of the stereonet and rose diagram are returned. This method is called by the MainWindow "redraw_plot"-method. """ self.fig.clf() self.fig.patch.set_facecolor(self.props["canvas_color"]) self.fig.set_dpi(self.props["pixel_density"]) gridspec = GridSpec(2, 5) sp_stereo = gridspec.new_subplotspec((0, 0), rowspan=2, colspan=2) sp_cbar = gridspec.new_subplotspec((1, 2), rowspan=1, colspan=1) sp_rose = gridspec.new_subplotspec((0, 3), rowspan=2, colspan=2) ax_stereo = self.fig.add_subplot(sp_stereo, projection=self.get_projection()) ax_rose = self.fig.add_subplot(sp_rose, projection="northpolar") ax_cbar = self.fig.add_subplot(sp_cbar) ax_cbar.axis("off") ax_cbar.set_aspect(8) return ax_stereo, ax_rose, ax_cbar def get_stereo_two_rose(self): """ Resets the figure and returns a stereonet two rose diagrams axis. When the view in the main window is changed to this setting, this function is called and sets up a plot with a stereonet and two rose diagram axis. One axis is for azimuth, the other one for dip. """ self.fig.clf() self.fig.patch.set_facecolor(self.props["canvas_color"]) self.fig.set_dpi(self.props["pixel_density"]) gridspec = GridSpec(2, 4) sp_stereo = gridspec.new_subplotspec((0, 0), rowspan=2, colspan=2) sp_cbar = gridspec.new_subplotspec((1, 2), rowspan=1, colspan=1) sp_rose = gridspec.new_subplotspec((0, 3), rowspan=1, colspan=1) sp_drose = gridspec.new_subplotspec((1, 3), rowspan=1, colspan=1) ax_stereo = self.fig.add_subplot(sp_stereo, projection=self.get_projection()) ax_rose = self.fig.add_subplot(sp_rose, projection="northpolar") ax_drose = self.fig.add_subplot(sp_drose, projection="dippolar") ax_cbar = self.fig.add_subplot(sp_cbar) ax_cbar.axis("off") ax_cbar.set_aspect(8) return ax_stereo, ax_rose, ax_drose, ax_cbar def get_rose_diagram(self): """ Resets the figure and returns the rose diagram axis. When the view in the main window is changed to rose-diagram-only the figure is reset. The current settings are applied and one subplot for the rose diagram is created. The axis of the rose-diagram is returned. This method is called by the MainWindow "redraw_plot"-method. """ self.fig.clf() self.fig.patch.set_facecolor(self.props["canvas_color"]) self.fig.set_dpi(self.props["pixel_density"]) gridspec = GridSpec(1, 1) sp_rose = gridspec.new_subplotspec((0, 0)) ax_rose = self.fig.add_subplot(sp_rose, projection="northpolar") return ax_rose def get_pt_view(self): """ Resets the canvas and returns the 3 axis of the paleostress view. When the view in the main window is changed to paleostress the figure is reset. The current settings are applied and 3 subplots are created. The 3 axis of the subplots are returned. This method is called by the MainWindow "redraw_plot"-method when the view has been changed. """ self.fig.clf() self.fig.patch.set_facecolor(self.props["canvas_color"]) self.fig.set_dpi(self.props["pixel_density"]) gridspec = GridSpec(2, 5) sp_stereo = gridspec.new_subplotspec((0, 0), colspan=3, rowspan=2) sp_fluc = gridspec.new_subplotspec((0, 3), colspan=2) sp_mohr = gridspec.new_subplotspec((1, 3), colspan=2) ax_stereo = self.fig.add_subplot(sp_stereo, projection=self.get_projection()) ax_fluc = self.fig.add_subplot(sp_fluc, aspect="equal") ax_mohr = self.fig.add_subplot(sp_mohr, aspect="equal") return ax_stereo, ax_fluc, ax_mohr def get_show_north(self): """ Returns if the stereonet should show the North symbol or degrees Returns True if the North symbol should be drawn (the default value), or False in which case numbers will be drawn for different degrees. """ return self.props["show_north"] def set_show_north(self, new_state): """ Sets a new state for whether the North symbol should be drawn. Expects a boolean. True means the North symbol will be drawn. False means that the stereonet will show different degrees along the outside. """ self.props["show_north"] = new_state def get_show_cross(self): """ Returns if the stereonet should draw a cross at the center. Returns True if the cross should be drawn (the default value) or False if the cross should not be drawn. """ return self.props["show_cross"] def set_show_cross(self, new_state): """ Sets a new state for whether the center cross should be drawn. Expects a boolean. True means the center cross will be drawn. False means it will not be drawn. """ self.props["show_cross"] = new_state def get_properties(self): """ Returns the current plot properties in a dictionary. The plot properties are stored in a dictionary. For loading and saving the dict is requested by the main window. """ return self.props def set_properties(self, new_props): """ Sets the properties to those passed in a dictionary. Loading a file will also set the plot properties to the saved state. The properties are appllied to this plot. """ for key in new_props: self.props[key] = new_props[key] def get_highlight(self): """ Gets the state of selection highlighting. Default is False. """ return self.props["highlight"] def set_highlight(self, new_state): """ Sets a new state for highlighting. Expects a boolean. """ self.props["highlight"] = new_state def get_night_mode(self): """ Gets the state of the night mode. Default is False, which is usually a lighter interface color. """ return self.night_mode def set_night_mode(self, new_state): """ Sets a new state for the night mode. Expects a boolean. """ self.night_mode = new_state
class matplotsink(wx.Panel): def __init__(self, parent, title, queue,gsz,zoom): wx.Panel.__init__(self, parent, wx.SIMPLE_BORDER) self.gsz = gsz self.parent = parent self.title = title self.q = queue self.zoom=zoom self.paused = False # self.create_menu() # self.create_status_bar() self.create_main_panel() def create_menu(self): self.menubar = wx.MenuBar() menu_file = wx.Menu() m_expt = menu_file.Append(-1, "&Save plot\tCtrl-S", "Save plot to file") self.Bind(wx.EVT_MENU, self.on_save_plot, m_expt) menu_file.AppendSeparator() m_exit = menu_file.Append(-1, "E&xit\tCtrl-X", "Exit") self.Bind(wx.EVT_MENU, self.on_exit, m_exit) self.menubar.Append(menu_file, "&File") self.SetMenuBar(self.menubar) def create_main_panel(self): self.panel = self self.init_plot() self.canvas = FigCanvas(self.panel, -1, self.fig) self.scroll_range = 400 self.canvas.SetScrollbar(wx.HORIZONTAL,0,5,self.scroll_range) self.canvas.Bind(wx.EVT_SCROLLWIN,self.OnScrollEvt) self.pause_button = wx.Button(self.panel, -1, "Pause") self.Bind(wx.EVT_BUTTON, self.on_pause_button, self.pause_button) self.Bind(wx.EVT_UPDATE_UI, self.on_update_pause_button, self.pause_button) self.cb_grid = wx.CheckBox(self.panel, -1, "Show Grid", style=wx.ALIGN_RIGHT) self.Bind(wx.EVT_CHECKBOX, self.on_cb_grid, self.cb_grid) self.cb_grid.SetValue(True) self.cb_xlab = wx.CheckBox(self.panel, -1, "Show X labels", style=wx.ALIGN_RIGHT) self.Bind(wx.EVT_CHECKBOX, self.on_cb_xlab, self.cb_xlab) self.cb_xlab.SetValue(True) self.hbox1 = wx.BoxSizer(wx.HORIZONTAL) self.hbox1.Add(self.pause_button, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.hbox1.AddSpacer(20) self.hbox1.Add(self.cb_grid, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.hbox1.AddSpacer(10) self.hbox1.Add(self.cb_xlab, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.vbox = wx.BoxSizer(wx.VERTICAL) self.vbox.Add(self.canvas, 1, flag=wx.LEFT | wx.TOP | wx.GROW) self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_LEFT | wx.TOP) self.panel.SetSizer(self.vbox) self.vbox.Fit(self) self.ani=animation.FuncAnimation(self.fig,self.draw_plot,interval=100) def OnScrollEvt(self,event): self.i_start = event.GetPosition() self.i_end = self.i_window + event.GetPosition() self.draw_plot(0) def create_status_bar(self): self.statusbar = self.CreateStatusBar() def draw_test(self,event): self.xar=np.arange(len(self.q.queue)) self.yar=np.array(self.q.queue) self.axes.plot(self.xar,self.yar) def init_plot(self): self.dpi = 100 self.fig = Figure((3.0, 3.0), dpi=self.dpi) self.fig.set_size_inches(7.0,4.0) self.fig.set_dpi(self.dpi) self.axes = self.fig.add_subplot(111) self.axes.set_axis_bgcolor('black') self.axes.set_title(self.title, size=12) pylab.setp(self.axes.get_xticklabels(), fontsize=8) pylab.setp(self.axes.get_yticklabels(), fontsize=8) self.i_window = self.gsz self.i_start = 0 self.i_end = self.i_start + self.i_window # plot the data as a line series, and save the reference # to the plotted line series # self.plot_data = self.axes.plot( [], linewidth=1, color=(1, 1, 0), )[0] def draw_plot(self,event): """ Redraws the plot """ if len(list(self.q.queue))>1 and not self.paused: if self.zoom: xmax = len(list(self.q.queue)) if len(list(self.q.queue)) > 50 else 50 xmin = xmax - 50 # for ymin and ymax, find the minimal and maximal values # in the data set and add a mininal margin. # # note that it's easy to change this scheme to the # minimal/maximal value in the current display, and not # the whole data set. # ymin = round(min(list(self.q.queue)), 0) - 1 ymax = round(max(list(self.q.queue)), 0) + 1 self.axes.set_xbound(lower=xmin, upper=xmax) self.axes.set_ybound(lower=ymin, upper=ymax) # anecdote: axes.grid assumes b=True if any other flag is # given even if b is set to False. # so just passing the flag into the first statement won't # work. # if self.cb_grid.IsChecked(): self.axes.grid(True, color='gray') else: self.axes.grid(False) # Using setp here is convenient, because get_xticklabels # returns a list over which one needs to explicitly # iterate, and setp already handles this. # pylab.setp(self.axes.get_xticklabels(), visible=self.cb_xlab.IsChecked()) self.plot_data.set_xdata(np.arange(len(list(self.q.queue)))) self.plot_data.set_ydata(np.array(list(self.q.queue))) self.canvas.draw() else: if self.cb_grid.IsChecked(): self.axes.grid(True, color='gray') else: self.axes.grid(False) # Using setp here is convenient, because get_xticklabels # returns a list over which one needs to explicitly # iterate, and setp already handles this. pylab.setp(self.axes.get_xticklabels(), visible=self.cb_xlab.IsChecked()) self.plot_data.set_xdata(np.arange(len(list(self.q.queue)))[self.i_start:self.i_end]) self.plot_data.set_ydata(np.array(list(self.q.queue))[self.i_start:self.i_end]) self.axes.set_xlim(min(np.arange(len(list(self.q.queue)))[self.i_start:self.i_end]),max(np.arange(len(list(self.q.queue)))[self.i_start:self.i_end])) # if self.zoom: self.axes.set_ylim(min(np.array(list(self.q.queue))),max(np.array(list(self.q.queue)))) self.canvas.draw() def on_pause_button(self, event): self.paused = not self.paused def on_update_pause_button(self, event): label = "Resume" if self.paused else "Pause" self.pause_button.SetLabel(label) def on_cb_grid(self, event): self.draw_plot(0) def on_cb_xlab(self, event): self.draw_plot(0) def on_save_plot(self, event): file_choices = "PNG (*.png)|*.png" dlg = wx.FileDialog( self, message="Save plot as...", defaultDir=os.getcwd(), defaultFile="plot.png", wildcard=file_choices, style=wx.SAVE) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() self.canvas.print_figure(path, dpi=self.dpi) self.flash_status_message("Saved to %s" % path) def on_redraw_timer(self, event): # if paused do not add data, but still redraw the plot # (to respond to scale modifications, grid change, etc.) # if not self.paused: self.data += self.datagen.next() self.draw_plot(0) def on_exit(self, event): self.Destroy() def flash_status_message(self, msg, flash_len_ms=1500): self.statusbar.SetStatusText(msg) self.timeroff = wx.Timer(self) self.Bind( wx.EVT_TIMER, self.on_flash_status_off, self.timeroff) self.timeroff.Start(flash_len_ms, oneShot=True) def on_flash_status_off(self, event): self.statusbar.SetStatusText('')
class Canvas_2DSDC: def __init__(self,ui,W_matrix,N,dx,title="Source 2D SDC"): #======================================================================= # Adding Tab to UI #======================================================================= ui.list_of_tabs.append(QtWidgets.QWidget()) ui.list_of_tabs[-1].setObjectName("tab_plotSDC2D") # gridlayout TAB SourceImag ui.list_grid_tabs.append(QtWidgets.QGridLayout(ui.list_of_tabs[-1])) ui.list_grid_tabs[-1].setObjectName("gridLayout_TabSDC2D") #_______________________________________________________________________ #======================================================================= # Scroll Area SDC Points #======================================================================= self.scrollArea_points = QtWidgets.QScrollArea(ui.list_of_tabs[-1]) self.scrollArea_points.setWidgetResizable(True) self.scrollArea_points.setObjectName("scrollArea_points") self.scrollArea_points.setPalette(palette_scrollPlotProp) self.scrollAreaWidgetContents_points = QtWidgets.QWidget() self.scrollAreaWidgetContents_points.setObjectName("scrollAreaWidgetContents_points") self.scrollArea_points.setWidget(self.scrollAreaWidgetContents_points) self.gridLayout_points = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_points) self.gridLayout_points.setObjectName("gridLayout_points") ui.list_grid_tabs[-1].addWidget(self.scrollArea_points, 1, 0, 1, 2) #_______________________________________________________________________ #======================================================================= # Button update point #======================================================================= self.pushButton_updatePoint = QtWidgets.QPushButton(ui.list_of_tabs[-1]) self.pushButton_updatePoint.setPalette(palette_buttonStart) self.pushButton_updatePoint.setFont(font_button) self.pushButton_updatePoint.setObjectName("pushButton_updatePoint") self.gridLayout_points.addWidget(self.pushButton_updatePoint, 2, 8, 1, 1,alignment=QtCore.Qt.AlignLeft) self.pushButton_updatePoint.setMinimumWidth(ui.rect.width()/8) self.pushButton_updatePoint.setText("Update Point") self.pushButton_updatePoint.clicked.connect(self.change_point) #_______________________________________________________________________ # Scroll Area Source Image self.scrollArea_sourceSDC = QtWidgets.QScrollArea(ui.list_of_tabs[-1]) self.scrollArea_sourceSDC.setPalette(palette_scrollPlotProp) self.scrollArea_sourceSDC.setWidgetResizable(True) self.scrollArea_sourceSDC.setSizePolicy(QtWidgets.QSizePolicy.Minimum,QtWidgets.QSizePolicy.Minimum) self.scrollArea_sourceSDC.setObjectName("scrollArea_sourceSDC2D") # Scroll Area Options prop self.scrollArea_sourceSDCOpt = QtWidgets.QScrollArea(ui.list_of_tabs[-1]) self.scrollArea_sourceSDCOpt.setWidgetResizable(True) self.scrollArea_sourceSDCOpt.setObjectName("scrollArea_2DSDC") self.scrollArea_sourceSDCOpt.setPalette(palette_scrollPlotProp) self.scrollAreaWidgetContents_sourceSDCOpt = QtWidgets.QWidget() self.scrollAreaWidgetContents_sourceSDCOpt.setObjectName("scrollAreaWidgetContents_sourceSDCOpt") self.scrollArea_sourceSDCOpt.setWidget(self.scrollAreaWidgetContents_sourceSDCOpt) self.gridLayout_sourceSDCOpt = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_sourceSDCOpt) self.gridLayout_sourceSDCOpt.setObjectName("gridLayout_sourceSDCOpt") ui.list_grid_tabs[-1].addWidget(self.scrollArea_sourceSDC, 3, 0, 1, 1) ui.list_grid_tabs[-1].addWidget(self.scrollArea_sourceSDCOpt, 10, 0, 1, 1) # Scroll Area Source SDC Phase self.scrollArea_sourceSDC_phase = QtWidgets.QScrollArea(ui.list_of_tabs[-1]) self.scrollArea_sourceSDC_phase.setPalette(palette_scrollPlotProp) self.scrollArea_sourceSDC_phase.setWidgetResizable(True) self.scrollArea_sourceSDC_phase.setSizePolicy(QtWidgets.QSizePolicy.Minimum,QtWidgets.QSizePolicy.Minimum) self.scrollArea_sourceSDC_phase.setObjectName("scrollArea_sourceSDC_phase") # Scroll Area Options Source SDC Phase self.scrollArea_sourceSDCOpt_phase = QtWidgets.QScrollArea(ui.list_of_tabs[-1]) self.scrollArea_sourceSDCOpt_phase.setWidgetResizable(True) self.scrollArea_sourceSDCOpt_phase.setObjectName("scrollArea_propImageOpt_phase") self.scrollArea_sourceSDCOpt_phase.setPalette(palette_scrollPlotProp) self.scrollAreaWidgetContents_sourceSDCOpt_phase = QtWidgets.QWidget() self.scrollAreaWidgetContents_sourceSDCOpt_phase.setObjectName("scrollAreaWidgetContents_sourceSDCOpt_phase") self.scrollArea_sourceSDCOpt_phase.setWidget(self.scrollAreaWidgetContents_sourceSDCOpt_phase) self.gridLayout_sourceSDCOpt_phase = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_sourceSDCOpt_phase) self.gridLayout_sourceSDCOpt_phase.setObjectName("gridLayout_sourceSDCOpt_phase") ui.list_grid_tabs[-1].addWidget(self.scrollArea_sourceSDC_phase, 3, 1, 1, 1) ui.list_grid_tabs[-1].addWidget(self.scrollArea_sourceSDCOpt_phase, 10, 1, 1, 1) #_______________________________________________________________________ #======================================================================= # Parameters #======================================================================= # N self.N = N # spatial resolution self.dx = dx # W matrix self.W_matrix = W_matrix # User interface self.ui = ui # title self.title = title # parent self.parent = self.scrollArea_sourceSDC self.parent_phase = self.scrollArea_sourceSDC_phase # parent optioncs self.parentOptions = self.scrollArea_sourceSDCOpt self.parentOptions_phase = self.scrollArea_sourceSDCOpt_phase # grid parent options self.gridOptions = self.gridLayout_sourceSDCOpt self.gridOptions_phase = self.gridLayout_sourceSDCOpt_phase # first plot self.first_plot = False #_______________________________________________________________________ # Initial points (middle) self.P1x = int(N/2) self.P1y = int(N/2) self.build_fig() def build_fig(self): #======================================================================= # Create the mpl Figure and FigCanvas objects. #======================================================================= # magnitude self.dpi = 100 self.fig = Figure((5.0, 4.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) # phase self.dpi_phase = 100 self.fig_phase = Figure((5.0, 4.0), dpi=self.dpi_phase) self.canvas_phase = FigureCanvas(self.fig_phase) # axes self.axes = self.fig.add_subplot(111) self.axes_phase = self.fig_phase.add_subplot(111) #_______________________________________________________________________ #======================================================================= # Plotting 2D figure and configuring #======================================================================= self.SDC2D_real = self.W_matrix[self.P1x,self.P1y].real self.SDC2D_imag = self.W_matrix[self.P1x,self.P1y].imag self.SDC2D_mag = zeros((self.N,self.N),dtype=float) S1 = abs(self.W_matrix[self.P1x,self.P1y,self.P1x,self.P1y].real) for i in range(0,self.N): for j in range(0,self.N): if self.W_matrix[self.P1x,self.P1y,self.P1x,self.P1y].real!=0.0 and self.W_matrix[i,j,i,j].real!=0.0: S2 = abs(self.W_matrix[i,j,i,j].real) self.SDC2D_mag[i,j] = sqrt(self.W_matrix[self.P1x,self.P1y,i,j].real**2+self.W_matrix[self.P1x,self.P1y,i,j].imag**2)/(sqrt(S1)*sqrt(S2)) else: self.SDC2D_mag[i,j]=0.0 #self.SDC2D_mag = abs(sqrt(temp.real**2+temp.imag**2)) #self.SDC2D_mag = sqrt(self.W_matrix[self.P1x,self.P1y].real**2+self.W_matrix[self.P1x,self.P1y].imag**2) self.SDC2D_phase = abs(angle(self.W_matrix[self.P1y,self.P1y])) # xy array self.x_array = arange(0,self.N,1,dtype=float32) self.x_array -= int(self.N/2) self.x_array *= self.dx # PLOT magnitude self.im = self.axes.pcolormesh(self.x_array,self.x_array,self.SDC2D_mag,shading="auto") self.cbar = self.fig.colorbar(self.im) # PLOT phase self.im_phase = self.axes_phase.pcolormesh(self.x_array,self.x_array,self.SDC2D_phase,shading="auto") self.cbar_phase = self.fig_phase.colorbar(self.im_phase) # font size self.fsize = 12 self.fsize_phase = 12 # x,y Labels self.axes.set_xlabel("x (m)",fontsize = self.fsize) self.axes.set_ylabel("y (m)",fontsize = self.fsize) #_______________________________________________________________________ #======================================================================= # Tool bar #======================================================================= # Bind the 'pick' event for clicking on one of the bars self.canvas.mpl_connect('pick_event', self.ui.on_pick) #self.canvas.mpl_connect("scroll_event", ui.scrolling) # Bind the 'pick' event for clicking on one of the bars self.canvas_phase.mpl_connect('pick_event', self.ui.on_pick) #self.canvas.mpl_connect("scroll_event", ui.scrolling) # Create the navigation toolbar, tied to the canvas self.mpl_toolbar = NavigationToolbar(self.canvas, self.parent) self.mpl_toolbar_phase = NavigationToolbar(self.canvas_phase, self.parent_phase) #ui.gridLayout_TabPropImage.addWidget(self.mpl_toolbar, 2, 0, 1, 3) self.ui.list_grid_tabs[-1].addWidget(self.mpl_toolbar, 8, 0, 1, 3,alignment=QtCore.Qt.AlignLeft) self.ui.list_grid_tabs[-1].addWidget(self.mpl_toolbar_phase, 8, 1, 1, 3,alignment=QtCore.Qt.AlignLeft) #_______________________________________________________________________ #======================================================================= # Canvas in Scroll Area - magnitude #======================================================================= #self.canvas.draw() #self.canvas.setParent(parent) self.canvas.setSizePolicy(QtWidgets.QSizePolicy.Minimum,QtWidgets.QSizePolicy.Minimum) # Container for VBOX self.containerGraph = QWidget(self.parent) self.containerGraph.setSizePolicy(QtWidgets.QSizePolicy.Minimum,QtWidgets.QSizePolicy.Minimum) self.containerGraph.setMinimumWidth(self.canvas.width()) self.containerGraph.setMinimumHeight(self.canvas.height()) self.containerGraph.setMaximumWidth(self.canvas.width()+5) self.containerGraph.setMaximumHeight(self.canvas.height()+5) # VBOX for canvas self.vbox = QVBoxLayout(self.containerGraph) #self.vbox.setGeometry(QRect(0, 0, self.canvas.width(), self.canvas.height())) self.vbox.addWidget(self.canvas) self.parent.setWidget(self.containerGraph) #_______________________________________________________________________ #======================================================================= # Canvas in Scroll Area - phase #======================================================================= self.canvas_phase.setSizePolicy(QtWidgets.QSizePolicy.Minimum,QtWidgets.QSizePolicy.Minimum) # Container for VBOX self.containerGraph_phase = QWidget(self.parent_phase) self.containerGraph_phase.setSizePolicy(QtWidgets.QSizePolicy.Minimum,QtWidgets.QSizePolicy.Minimum) self.containerGraph_phase.setMinimumWidth(self.canvas_phase.width()) self.containerGraph_phase.setMinimumHeight(self.canvas_phase.height()) self.containerGraph_phase.setMaximumWidth(self.canvas_phase.width()+5) self.containerGraph_phase.setMaximumHeight(self.canvas_phase.height()+5) # VBOX for canvas self.vbox_phase = QVBoxLayout(self.containerGraph_phase) #self.vbox.setGeometry(QRect(0, 0, self.canvas.width(), self.canvas.height())) self.vbox_phase.addWidget(self.canvas_phase) self.parent_phase.setWidget(self.containerGraph_phase) #_______________________________________________________________________ if not self.first_plot: self.figure_options() self.figure_options_phase() self.first_plot = True def figure_options(self): # label Point P1x self.label_P1x = QtWidgets.QLabel(self.parentOptions) self.label_P1x.setObjectName("label_P1x") self.label_P1x.setText("x_1, y_1:") self.gridLayout_points.addWidget(self.label_P1x, 2, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit Point P1x self.lineEdit_P1x = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_P1x.setObjectName("lineEdit_P1x") self.gridLayout_points.addWidget(self.lineEdit_P1x,2, 2, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_P1x.setText(str(int(self.N/2))) #self.lineEdit_P1x.textChanged.connect(self.change_P1x) # line edit Point P1x self.lineEdit_P1y = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_P1y.setObjectName("lineEdit_P1y") self.gridLayout_points.addWidget(self.lineEdit_P1y,2, 4, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_P1y.setText(str(int(self.N/2))) #self.lineEdit_P1y.textChanged.connect(self.change_P1y) # label title self.label_title = QtWidgets.QLabel(self.parentOptions) self.label_title.setObjectName("label_title") self.label_title.setText("Title") self.gridOptions.addWidget(self.label_title, 6, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit title label self.lineEdit_title = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_title.setObjectName("lineEdit_title") self.lineEdit_title.textChanged.connect(self.change_title) self.gridOptions.addWidget(self.lineEdit_title,6, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_title.setText(self.title+" Magnitude") self.change_title() # label x self.label_x = QtWidgets.QLabel(self.parentOptions) self.label_x.setObjectName("label_x") self.label_x.setText("Label x-axis") self.gridOptions.addWidget(self.label_x, 8, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit x label self.lineEdit_xLabel = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_xLabel.setObjectName("lineEdit_xlabel") self.lineEdit_xLabel.textChanged.connect(self.change_labelx) self.gridOptions.addWidget(self.lineEdit_xLabel,8, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.change_labelx() self.lineEdit_xLabel.setText("x") # label y self.label_y = QtWidgets.QLabel(self.parentOptions) self.label_y.setObjectName("label_y") self.label_y.setText("Label y-axis") self.gridOptions.addWidget(self.label_y, 10, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit y label self.lineEdit_yLabel = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_yLabel.setObjectName("lineEdit_ylabel") self.lineEdit_yLabel.textChanged.connect(self.change_labely) self.gridOptions.addWidget(self.lineEdit_yLabel,10, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.change_labely() self.lineEdit_yLabel.setText("y") # label xlim self.label_xlim = QtWidgets.QLabel(self.parentOptions) self.label_xlim.setObjectName("label_xlim") self.label_xlim.setText("xlim") self.gridOptions.addWidget(self.label_xlim, 12, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit xlim self.lineEdit_xlim = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_xlim.setObjectName("lineEdit_ylabel") self.lineEdit_xlim.textChanged.connect(self.change_xlim) self.gridOptions.addWidget(self.lineEdit_xlim,12, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_xlim.setText("(_,_)") self.change_xlim() # label ylim self.label_ylim = QtWidgets.QLabel(self.parentOptions) self.label_ylim.setObjectName("label_ylim") self.label_ylim.setText("ylim") self.gridOptions.addWidget(self.label_ylim, 14, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit ylim self.lineEdit_ylim = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_ylim.setObjectName("lineEdit_ylabel") self.lineEdit_ylim.textChanged.connect(self.change_ylim) self.gridOptions.addWidget(self.lineEdit_ylim,14, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_ylim.setText("(_,_)") self.change_ylim() # label cmap self.label_cmap = QtWidgets.QLabel(self.parentOptions) self.label_cmap.setObjectName("label_cmap") self.label_cmap.setText("Color Map") self.gridOptions.addWidget(self.label_cmap, 20, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit cmap self.lineEdit_cmap = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_cmap.setObjectName("lineEdit_cmap") self.lineEdit_cmap.textChanged.connect(self.change_cmap) self.gridOptions.addWidget(self.lineEdit_cmap,20, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_cmap.setText("hot") self.change_cmap() # label Font Size self.label_fsize = QtWidgets.QLabel(self.parentOptions) self.label_fsize.setObjectName("label_fsize") self.label_fsize.setText("Font Size") self.gridOptions.addWidget(self.label_fsize, 22, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit font size self.lineEdit_fsize = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_fsize.setObjectName("label_fsize") self.lineEdit_fsize.textChanged.connect(self.change_fsize) self.gridOptions.addWidget(self.lineEdit_fsize,22, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_fsize.setText(str(self.fsize)) self.change_fsize() # label DPI self.label_dpi = QtWidgets.QLabel(self.parentOptions) self.label_dpi.setObjectName("label_dpi") self.label_dpi.setText("dpi (100-500)") self.gridOptions.addWidget(self.label_dpi, 24, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit DPI self.lineEdit_dpi = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_dpi.setObjectName("label_dpi") self.lineEdit_dpi.textChanged.connect(self.change_dpi) self.gridOptions.addWidget(self.lineEdit_dpi,24, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_dpi.setText(str(self.dpi)) self.change_fsize() #_______________________________________________________________________ def figure_options_phase(self): try: # label title_phase self.label_title_phase = QtWidgets.QLabel(self.parentOptions) self.label_title_phase.setObjectName("label_title") self.label_title_phase.setText("Title") self.gridOptions_phase.addWidget(self.label_title_phase, 6, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit title label_phase self.lineEdit_title_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_title_phase.setObjectName("lineEdit_title_phase") self.lineEdit_title_phase.textChanged.connect(self.change_title_phase) self.gridOptions_phase.addWidget(self.lineEdit_title_phase,6, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_title_phase.setText(self.title+" Phase") self.change_title_phase() # label x_phase self.label_x_phase = QtWidgets.QLabel(self.parentOptions) self.label_x_phase.setObjectName("label_xv") self.label_x_phase.setText("Label x-axis") self.gridOptions_phase.addWidget(self.label_x_phase, 8, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit x label_phase_phase self.lineEdit_xLabel_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_xLabel_phase.setObjectName("lineEdit_xlabel_phase") self.lineEdit_xLabel_phase.textChanged.connect(self.change_labelx_phase) self.gridOptions_phase.addWidget(self.lineEdit_xLabel_phase,8, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.change_labelx_phase() self.lineEdit_xLabel_phase.setText("x") # label y_phase self.label_y_phase = QtWidgets.QLabel(self.parentOptions) self.label_y_phase.setObjectName("label_y_phase") self.label_y_phase.setText("Label y-axis") self.gridOptions_phase.addWidget(self.label_y_phase, 10, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit y label_phase self.lineEdit_yLabel_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_yLabel_phase.setObjectName("lineEdit_ylabel_phase") self.lineEdit_yLabel_phase.textChanged.connect(self.change_labely_phase) self.gridOptions_phase.addWidget(self.lineEdit_yLabel_phase,10, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.change_labely_phase() self.lineEdit_yLabel_phase.setText("y") # label xlim_phase self.label_xlim_phase = QtWidgets.QLabel(self.parentOptions) self.label_xlim_phase.setObjectName("label_xlim_phase") self.label_xlim_phase.setText("xlim") self.gridOptions_phase.addWidget(self.label_xlim_phase, 12, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit xlim_phase self.lineEdit_xlim_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_xlim_phase.setObjectName("lineEdit_ylabel_phase") self.lineEdit_xlim_phase.textChanged.connect(self.change_xlim_phase) self.gridOptions_phase.addWidget(self.lineEdit_xlim_phase,12, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_xlim_phase.setText("(_,_)") self.change_xlim_phase() # label ylim_phase self.label_ylim_phase = QtWidgets.QLabel(self.parentOptions) self.label_ylim_phase.setObjectName("label_ylim_phase") self.label_ylim_phase.setText("ylim") self.gridOptions_phase.addWidget(self.label_ylim_phase, 14, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit ylim_phase self.lineEdit_ylim_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_ylim_phase.setObjectName("lineEdit_ylabel_phase") self.lineEdit_ylim_phase.textChanged.connect(self.change_ylim_phase) self.gridOptions_phase.addWidget(self.lineEdit_ylim_phase,14, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_ylim_phase.setText("(_,_)") self.change_ylim_phase() # label cmap_phase self.label_cmap_phase = QtWidgets.QLabel(self.parentOptions) self.label_cmap_phase.setObjectName("label_cmap_phase") self.label_cmap_phase.setText("Color Map") self.gridOptions_phase.addWidget(self.label_cmap_phase, 20, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit cmap_phase self.lineEdit_cmap_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_cmap_phase.setObjectName("lineEdit_cmap_phase") self.lineEdit_cmap_phase.textChanged.connect(self.change_cmap_phase) self.gridOptions_phase.addWidget(self.lineEdit_cmap_phase,20, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_cmap_phase.setText("hot") self.change_cmap_phase() # label Font Size_phase self.label_fsize_phase = QtWidgets.QLabel(self.parentOptions) self.label_fsize_phase.setObjectName("label_fsize") self.label_fsize_phase.setText("Font Size") self.gridOptions_phase.addWidget(self.label_fsize_phase, 22, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit font size_phase self.lineEdit_fsize_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_fsize_phase.setObjectName("label_fsize_phase") self.lineEdit_fsize_phase.textChanged.connect(self.change_fsize) self.gridOptions_phase.addWidget(self.lineEdit_fsize_phase,22, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_fsize_phase.setText(str(self.fsize)) self.change_fsize_phase() # label DPI_phase self.label_dpi_phase = QtWidgets.QLabel(self.parentOptions) self.label_dpi_phase.setObjectName("label_dpi_phase") self.label_dpi_phase.setText("dpi (100-500)") self.gridOptions_phase.addWidget(self.label_dpi_phase, 24, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit DPI_phase self.lineEdit_dpi_phase = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_dpi_phase.setObjectName("label_dpi_phase") self.lineEdit_dpi_phase.textChanged.connect(self.change_dpi_phase) self.gridOptions_phase.addWidget(self.lineEdit_dpi_phase,24, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_dpi_phase.setText(str(self.dpi_phase)) self.change_fsize_phase() #_______________________________________________________________________ except Exception as error: self.ui.update_outputText(str(error)) def change_point(self): try: P1x = int(self.lineEdit_P1x.text()) P1y = int(self.lineEdit_P1y.text()) if P1x>=0 and P1x<self.N: if P1y>=0 and P1y<self.N: # defining points self.P1x = P1x self.P1y = P1y self.update_pcolor() self.update_draw() #self.fig.canvas.flush_events() except Except as error: self.ui.update_outputText("Insert valid points.") #if debug: #self.ui.update_outputText(str(error)+" at <windowPlot_propSDC> in <change_point> function.") def update_pcolor(self): try: # creating image prop #self.SDC2D_mag = sqrt(self.W_matrix[self.P1x,self.P1y].real**2+self.W_matrix[self.P1x,self.P1y].imag**2) self.SDC2D_mag = zeros((self.N,self.N),dtype=float) S1 = abs(self.W_matrix[self.P1x,self.P1y,self.P1x,self.P1y].real) for i in range(0,self.N): for j in range(0,self.N): if self.W_matrix[self.P1x,self.P1y,self.P1x,self.P1y].real!=0.0 and self.W_matrix[i,j,i,j].real!=0.0: S2 = abs(self.W_matrix[i,j,i,j].real) self.SDC2D_mag[i,j] = sqrt(self.W_matrix[self.P1x,self.P1y,i,j].real**2+self.W_matrix[self.P1x,self.P1y,i,j].imag**2)/sqrt(S1*S2) else: self.SDC2D_mag[i,j]=0.0 self.SDC2D_phase = abs(angle(self.W_matrix[self.P1y,self.P1y])) self.im.set_array(self.SDC2D_mag[:-1,:-1].ravel()) self.im_phase.set_array(self.SDC2D_phase[:-1,:-1].ravel()) self.update_draw() except Exception as error: self.ui.update_outputText(str(error)) def update_draw(self): self.canvas.draw() self.canvas.updateGeometry() self.canvas_phase.draw() self.canvas_phase.updateGeometry() #=========================================================================== # Magnitude #=========================================================================== def change_dpi(self): try: new = int(self.lineEdit_dpi.text()) if new>=100 and new<=500: self.dpi = new self.fig.set_dpi(new) self.update_draw() except: pass def change_fsize(self): try: self.fsize = int(self.lineEdit_fsize.text()) self.change_title() self.change_labelx() self.change_labely() self.update_draw() except Exception as error: pass #self.ui.update_outputText(error) def change_cmap(self): try: self.im.set_cmap(self.lineEdit_cmap.text()) self.canvas.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_xlim(self): try: temp_txt = self.lineEdit_xlim.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0]=="(" and temp_txt[-1]==")": actual="" for i in range(1,Ntxt-1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i==Ntxt-2: actual+=temp_txt[i] numList.append(float(actual)) else: actual+=temp_txt[i] self.axes.set_xlim(numList[0],numList[1]) except Exception as error: pass #self.ui.update_outputText(error) def change_ylim(self): try: temp_txt = self.lineEdit_ylim.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0]=="(" and temp_txt[-1]==")": actual="" for i in range(1,Ntxt-1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i==Ntxt-2: actual+=temp_txt[i] numList.append(float(actual)) else: actual+=temp_txt[i] self.axes.set_ylim(numList[0],numList[1]) self.canvas.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_title(self): try: self.axes.set_title(self.lineEdit_title.text(),fontsize = self.fsize) self.canvas.draw() ##self.title = str(self.lineEdit_title.text()) except: pass def change_labelx(self): if self.lineEdit_xLabel.text()=="": self.axes.set_xlabel("") else: try: self.axes.set_xlabel(self.lineEdit_xLabel.text(),fontsize = self.fsize) self.canvas.draw() except Exception as error: self.ui.update_outputText(str(error)) def change_labely(self): if self.lineEdit_yLabel.text()=="": self.axes.set_ylabel("") else: try: self.axes.set_ylabel(self.lineEdit_yLabel.text(),fontsize = self.fsize) self.canvas.draw() except: pass #self.ui.update_outputText("Unable to update y-label.") #___________________________________________________________________________ #=========================================================================== # Phase #=========================================================================== try: def change_dpi_phase(self): try: new = int(self.lineEdit_dpi_phase.text()) if new>=100 and new<=500: self.dpi_phase = new self.fig_phase.set_dpi(new) self.update_draw() except: pass def change_fsize_phase(self): try: self.fsize_phase = int(self.lineEdit_fsize.text()) self.change_title_phase() self.change_labelx_phase() self.change_labely_phase() self.update_draw() except Exception as error: pass #self.ui.update_outputText(error) def change_cmap_phase(self): try: self.im_phase.set_cmap(self.lineEdit_cmap_phase.text()) self.canvas_phase.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_xlim_phase(self): try: temp_txt = self.lineEdit_xlim_phase.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0]=="(" and temp_txt[-1]==")": actual="" for i in range(1,Ntxt-1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i==Ntxt-2: actual+=temp_txt[i] numList.append(float(actual)) else: actual+=temp_txt[i] self.axes_phase.set_xlim(numList[0],numList[1]) except Exception as error: pass #self.ui.update_outputText(error) def change_ylim_phase(self): try: temp_txt = self.lineEdit_ylim_phase.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0]=="(" and temp_txt[-1]==")": actual="" for i in range(1,Ntxt-1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i==Ntxt-2: actual+=temp_txt[i] numList.append(float(actual)) else: actual+=temp_txt[i] self.axes_phase.set_ylim(numList[0],numList[1]) self.canvas.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_title_phase(self): try: self.axes_phase.set_title(self.lineEdit_title_phase.text(),fontsize = self.fsize_phase) self.canvas_phase.draw() except: pass def change_labelx_phase(self): if self.lineEdit_xLabel_phase.text()=="": self.axes_phase.set_xlabel("") else: try: self.axes_phase.set_xlabel(self.lineEdit_xLabel_phase.text(),fontsize = self.fsize_phase) self.canvas_phase.draw() except Exception as error: self.ui.update_outputText(str(error)) def change_labely_phase(self): if self.lineEdit_yLabel_phase.text()=="": self.axes_phase.set_ylabel("") else: try: self.axes_phase.set_ylabel(self.lineEdit_yLabel_phase.text(),fontsize = self.fsize_phase) self.canvas_phase.draw() except: pass #self.ui.update_outputText("Unable to update y-label.") except Exception as error: self.ui.update_outputText(error) #___________________________________________________________________________ #=============================================================================== #/////////////////////////////////////////////////////////////////////////////// #===============================================================================
class BaseVisualizer(object): visualizer_name = _('Base Visualizer') def __init__(self, canvas_panel, data, parent_frame): trigger(events.EVENT_VISUALIZER_DRAW, visualizer=self) self.conf = app_config # variables section self.canvas_panel = canvas_panel self.data = data self.processed_data = None self.frame = parent_frame self.events = app_events self.plots = [] self.vline = [] # calculate data self.process() # prepare canvas self.canvas_width, self.canvas_height = self.canvas_panel.Size figsize = (float(self.canvas_width) / self.conf.draw_dpi, self.conf.draw_figure_height * len(self.data)) self.fig = Figure( dpi=self.conf.draw_dpi, facecolor=self.conf.draw_facecolor, figsize=figsize ) self.canvas = FigCanvas(self.canvas_panel, -1, self.fig) # display controls self.draw() # bind events self.canvas_panel.Bind(wx.EVT_SIZE, self.evt_on_resize_panel) on(events.EVENT_VISUALIZER_DRAW, self.on_any_visualizer_draw) self.conf.on(events.EVENT_CHANGED_PARAMETER_key('draw_*'), self.on_config_changed) self.on('button_press_event', self.on_button_press_event) self.on('motion_notify_event', self.on_motion_notify_event) def create_cursor(self): if self.plots: self.cursor = MultiCursor(self.fig.canvas, self.plots, color=self.conf.draw_dynamic_cursor_color, lw=1) self.cursor.val_texts = [] for i, plot in enumerate(self.plots): val_text = plot.text(0.15, 1.04, '', transform=plot.transAxes, fontsize=9, color=self.conf.draw_dynamic_cursor_color) val_text.plot = plot val_text.data = self.processed_data[i] self.cursor.val_texts.append(val_text) def create_vline(self): for i, plot in enumerate(self.plots): line = plot.axvline(color=self.conf.draw_static_cursor_color) line.plot = plot line.data = self.processed_data[i] line.val_text = plot.text(0.05, 1.04, '', transform=plot.transAxes, fontsize=9, color=self.conf.draw_static_cursor_color) self.vline.append(line) def on(self, event, func): """ Canvas events """ self.fig.canvas.mpl_connect(event, func) def on_any_visualizer_draw(self, visualizer): if visualizer is not self: self.clear() self.conf.off(events.EVENT_CHANGED_PARAMETER_key('draw_*'), [self.on_config_changed]) def on_config_changed(self, key, value): if key in ('draw_position', 'draw_page_size'): self.update_plots() elif key in ('draw_dpi', 'draw_figure_height', 'draw_facecolor'): self.update_figure() elif key == 'draw_static_cursor_color': for line in self.vline: line.set_color(value) self.canvas.draw() elif key == 'draw_dynamic_cursor_color': for line in self.cursor.lines: line.set_color(value) self.canvas.draw() def on_button_press_event(self, event): self.update_vline(event) if event.inaxes is not None and self.canvas.widgetlock.available(self): self.events.trigger(events.EVENT_VISUALIZER_STATIC_CURSOR_CHANGED, plot_event=event, data=self.processed_data) def on_motion_notify_event(self, event): self.update_cursor_label(event) if event.inaxes is not None and self.canvas.widgetlock.available(self): self.events.trigger(events.EVENT_VISUALIZER_DYNAMIC_CURSOR_CHANGED, plot_event=event, data=self.processed_data) def evt_on_resize_panel(self, event): cur_width = self.canvas_panel.Size[0] if self.canvas_width != cur_width: self.canvas_width = cur_width self.fig.set_figwidth(float(self.canvas_width) / self.conf.draw_dpi) self.canvas_panel.Refresh() self.canvas.draw() def _prepare_static_cursor_value(self, data, event): return '(%.3f, %.3f)' % ( data.to_time(event.xdata), data.to_value(event.xdata)) def update_vline(self, event): if event.inaxes: for line in self.vline: line.set_xdata(event.xdata) line.val_text.set_text(self._prepare_static_cursor_value(line.data, event)) self.canvas.draw() def _prepare_dynamic_cursor_label(self, data, event): return '(%.3f, %.3f)' % ( data.to_time(event.xdata), data.to_value(event.xdata)) def update_cursor_label(self, event): if event.inaxes and False: #disabled for text in self.cursor.val_texts: text.set_text(self._prepare_dynamic_cursor_label(text.data, event)) self.canvas.draw() def update_plots(self): position = self.conf.draw_position page_size = self.conf.draw_page_size frame = (position - page_size if position > page_size else 0, position if position >= page_size else page_size) for plt in self.plots: plt.set_xlim(frame) self.canvas.draw() def update_figure(self): self.fig.set_dpi(self.conf.draw_dpi) self.fig.set_figheight(self.conf.draw_figure_height * len(self.data)) self.fig.set_facecolor(self.conf.draw_facecolor) self.canvas_panel.SetBackgroundColour(self.conf.draw_facecolor) self.canvas_panel.Update() self.canvas.draw() def create_aux(self): self.create_cursor() self.create_vline() self.canvas.figure.tight_layout() self.canvas_panel.update_scroll(self.canvas.Size) def draw(self): """Visualizes calculated data""" def clear(self): """Clear canvas""" self.canvas.figure.clear() self.canvas.draw() def process(self): """Calculates needed information""" self.processed_data = self.data def print_figure(self, path): """Export canvas into the file ``path``""" self.canvas.print_figure(path, dpi=self.conf.draw_dpi)
(width_dpi, height_dpi)) # Pixel width and height of a character in the fixed-width font courier. # Note that two chars gives you exactly twice what one char is. my_font = tkf.Font(font='monospace', size=10) pixel_width_per_char = my_font.measure("w") pixel_height_per_char = my_font.metrics("linespace") print('Pixel width per character: %f px, Pixel height per character: %f px' % (pixel_width_per_char, pixel_height_per_char)) # Figure size in inches figure_size = (13, 6) f = Figure() f.set_dpi(96) f.set_size_inches(figure_size[0], figure_size[1]) # a.text could add text to the figure. a = f.add_subplot(111) frame1 = f.gca() frame1.axes.get_xaxis().set_visible(False) frame1.axes.get_yaxis().set_visible(False) # This we would input from the user, generally. row_number = 16 def x_chars_to_xu(x_chars: float) -> float: """ This function takes in a number of characters, and returns the
class Chart(object): """ Simple and clean facade to Matplotlib's plotting API. A chart instance abstracts a plotting device, on which one or multiple related plots can be drawn. Charts can be exported as images, or visualized interactively. Each chart instance will always open in its own GUI window, and this window will never block the execution of the rest of the program, or interfere with other L{Chart}s. The GUI can be safely opened in the background and closed infinite number of times, as long as the client program is still running. By default, a chart contains a single plot: >>> chart.plot matplotlib.axes.AxesSubplot >>> chart.plot.hist(...) If C{rows} and C{columns} are defined, the chart will contain C{rows} x C{columns} number of plots (equivalent to MPL's sub-plots). Each plot can be assessed by its index: >>> chart.plots[0] first plot or by its position in the grid: >>> chart.plots[0, 1] plot at row=0, column=1 @param number: chart number; by default this a L{Chart.AUTONUMBER} @type number: int or None @param title: chart master title @type title: str @param rows: number of rows in the chart window @type rows: int @param columns: number of columns in the chart window @type columns: int @note: additional arguments are passed directly to Matplotlib's Figure constructor. """ AUTONUMBER = None _serial = 0 def __init__(self, number=None, title='', rows=1, columns=1, backend=Backends.WX_WIDGETS, *fa, **fk): if number == Chart.AUTONUMBER: Chart._serial += 1 number = Chart._serial if rows < 1: rows = 1 if columns < 1: columns = 1 self._rows = int(rows) self._columns = int(columns) self._number = int(number) self._title = str(title) self._figure = Figure(*fa, **fk) self._figure._figure_number = self._number self._figure.suptitle(self._title) self._beclass = backend self._hasgui = False self._plots = PlotsCollection(self._figure, self._rows, self._columns) self._canvas = FigureCanvasAgg(self._figure) formats = [ (f.upper(), f) for f in self._canvas.get_supported_filetypes() ] self._formats = csb.core.Enum.create('OutputFormats', **dict(formats)) def __getitem__(self, i): if i in self._plots: return self._plots[i] else: raise KeyError('No such plot number: {0}'.format(i)) def __enter__(self): return self def __exit__(self, *a, **k): self.dispose() @property def _backend(self): return Backend.get(self._beclass, started=True) @property def _backend_started(self): return Backend.query(self._beclass) @property def title(self): """ Chart title @rtype: str """ return self._title @property def number(self): """ Chart number @rtype: int """ return self._number @property def plots(self): """ Index-based access to the plots in this chart @rtype: L{PlotsCollection} """ return self._plots @property def plot(self): """ First plot @rtype: matplotlib.AxesSubplot """ return self._plots[0] @property def rows(self): """ Number of rows in this chart @rtype: int """ return self._rows @property def columns(self): """ Number of columns in this chart @rtype: int """ return self._columns @property def width(self): """ Chart's width in inches @rtype: int """ return self._figure.get_figwidth() @width.setter def width(self, inches): self._figure.set_figwidth(inches) if self._backend_started: self._backend.resize(self._figure) @property def height(self): """ Chart's height in inches @rtype: int """ return self._figure.get_figheight() @height.setter def height(self, inches): self._figure.set_figheight(inches) if self._backend_started: self._backend.resize(self._figure) @property def dpi(self): """ Chart's DPI @rtype: int """ return self._figure.get_dpi() @dpi.setter def dpi(self, dpi): self._figure.set_dpi(dpi) self._backend.resize(self._figure) @property def formats(self): """ Supported output file formats @rtype: L{csb.core.enum} """ return self._formats def show(self): """ Show the GUI window (non-blocking). """ if not self._hasgui: self._backend.add(self._figure) self._hasgui = True self._backend.show(self._figure) def hide(self): """ Hide (but do not dispose) the GUI window. """ self._backend.hide(self._figure) def dispose(self): """ Dispose the GUI interface. Must be called at the end if any chart.show() calls have been made. Automatically called if using the chart in context manager ("with" statement). @note: Failing to call this method if show() has been called at least once may cause backend-related errors. """ if self._backend_started: service = self._backend if service and service.running: service.destroy(self._figure, wait=True) service.client_disposed(self) def save(self, file, format='png', crop=False, dpi=None, *a, **k): """ Save all plots to an image. @param file: destination file name @type file: str @param format: output image format; see C{chart.formats} for enumeration @type format: str or L{csb.core.EnumItem} @param crop: if True, crop the image (equivalent to MPL's bbox=tight) @type crop: bool @note: additional arguments are passed directly to MPL's savefig method """ if 'bbox_inches' in k: bbox = k['bbox_inches'] del k['bbox_inches'] else: if crop: bbox = 'tight' else: bbox = None self._canvas.print_figure(file, format=str(format), bbox_inches=bbox, dpi=dpi, *a, **k)
class PlotSettings(object): """ The PlotSettings-class initializes the default values for the plot. The class can return those values to the main window and the plot-settings dialog. The plot-settings dialog will call the instance of this class to change the different settings. This class also stores the figure and can return different subplot-layouts. This class also stores the normal and inverse transformations of the stereonet, and will return the correct one for either the Schmidt- or Wulff-Net. """ def __init__(self): """ Initalizes the default values, colors and the matplotlib-figure. Initializes and stores the default settings. Initializes the matplotlib-figure, a folder-icon for the group-layers of the layer-view. """ self.folder_icon = Gtk.IconTheme.get_default().load_icon( "folder", 16, 0) self.draw_grid = True self.equal_area_projection = True self.minor_grid_spacing = 2 self.major_grid_spacing = 10 self.grid_cutoff_lat = 80 self.show_north = True self.show_cross = True self.pixel_density = 75 self.grid_linestyle = "--" self.grid_color = "#787878" self.grid_width = 0.4 self.fig = Figure(dpi=self.pixel_density) self.draw_legend = True self.canvas_color = "#bfbfbf" def get_fig(self): """ Returns the Matplotlib-Figure. Returns the figure that is stored by this class. The MainWindow class calls this function once during initialization to add the figure to the FigureCanvas. """ return self.fig def get_inverse_transform(self): """ Returns the inverse transform for the current stereonet projection. If the projection is equal are (True) the function returns the InvertedLambertTransform- or else the InvertedSterreographicTransform-class. """ if self.equal_area_projection is True: return mplstereonet.stereonet_transforms.\ InvertedLambertTransform(0, 0, self.pixel_density) else: return mplstereonet.stereonet_transforms.\ InvertedStereographicTransform(0, 0, self.pixel_density) def get_transform(self): """ Returns the normal transform for the current stereonet projection. If the projection is equal are (True) the function returns the LambertTransform- or else the SterreographicTransform-class. """ if self.equal_area_projection is True: return mplstereonet.stereonet_transforms.\ LambertTransform(0, 0, self.pixel_density) else: return mplstereonet.stereonet_transforms.\ StereographicTransform(0, 0, self.pixel_density) def get_draw_grid_state(self): """ Returns if the grid should be drawn for the stereonet. Returns a boolean. True mean that a grid should be drawn. False means that no grid should be drawn. This method is called by the MainWindow- redraw_plot-method. """ return self.draw_grid def set_draw_grid_state(self, new_state): """ Sets if the grid should be drawn for the stereonet. Expects a boolean. True mean that a grid should be drawn. False means that no grid should be drawn. This method is called by the LayerProperties-dialog when the setting is changed. """ self.draw_grid = new_state def get_folder_icon(self): """ Returns the folder icon used for the group-layer pixbuf. Always returns the "folder" icon from the Gtk.IconTheme. The folder will therefore match the desktop-theme set by the user. This method is called by the MainWindow "on_toolbutton_create_group_layer_clicked"- method. """ return self.folder_icon def get_pixel_density(self): """ Returns the pixel density the plot is using. The pixel density is an int and the default value is 75. This method is called by the LayerProperties-dialog so it can display the current value. """ return self.pixel_density def set_pixel_density(self, new_pixel_density): """ Sets a new pixel density for the plot. Expects an int. This method is called by the LayerProperties-dialog. The new value will be used when the plot redraws when the settings in the dialog are applied. """ self.pixel_density = new_pixel_density def get_projection(self): """ Returns the projection currently used by the stereonet. Returns one of two strings that MPLStereonet uses to distinguish between the equal-area and equal-angle projection. This method is only called from this class when the view is switched. """ if self.equal_area_projection is True: return "equal_area_stereonet" else: return "equal_angle_stereonet" def get_projection_state(self): """ Returns the projection state for the stereonet. Returns a boolean. True means that the stereonet should be drawn with equal-area. False mean equal-angle. This method is called by the StereonetProperties-dialog to load the current setting. """ return self.equal_area_projection def set_projection_state(self, new_state): """ Sets a new projection state. Expects a boolean. True means that the projection will be equal-area, False means equal-angle. This method is called by the StereonetProperties-dialog when a new setting for the projection is applied. """ self.equal_area_projection = new_state def get_grid_linestyle(self): """ Returns the linestyle of the grid. The linestyle is returned as a string. Default is "--" (dashed). This method is called by the MainWindow "redraw_plot"-method. """ return self.grid_linestyle def get_grid_color(self): """ Returns the color of the grid. Returns the color as a hex-triplet. The default is "#787878". This method is called by the MainWindow "redraw_plot"-method. """ return self.grid_color def get_grid_width(self): """ Returns the width of the grid lines. The width of the grid lines is returned as a float or int. The default is "0.4". This method is called by the MainWindow "redraw_plot"-method. """ return self.grid_width def get_draw_legend(self): """ Returns if the legend should be drawn as a boolean. The returned value is either True if a legend should be drawn, or False if no legend should be drawn. This method is called by the MainWindow "redraw_plot"-method and the StereonetProperties-dialog. """ return self.draw_legend def set_draw_legend(self, new_state): """ Sets a new state for whether the legend should be drawn. Expects a boolean. True means that the legend should be drawn. False means that no legend should be drawn. This method is called by the StereonetProperties-dialog when a new setting is applied. """ self.draw_legend = new_state def get_canvas_rgba(self): """ Returns the canvas color as a Gdk.RGBA. This method is called by the StereonetProperties-dialog to apply the current canvas color to the ColorButton. """ rgba = Gdk.RGBA() rgba.parse(self.canvas_color) return rgba.to_color() def set_canvas_color(self, new_color): """ Sets a new canvas color. Expects a hex-triplet string (e.g. "#bfbfbf"). This method is called by the StereonetProperties-dialog when a new color is applied to the canvas. """ self.canvas_color = new_color def get_stereonet(self): """ Resets the figure and returns the stereonet axis. When the view in the main window is changed to only stereoent. The figure is reset. Then the current settings are applied and one subplot for the stereonet is created. This method is called when the MainWindow "__init__"-method and the "redraw_plot"-method. """ self.fig.clf() self.fig.patch.set_facecolor(self.canvas_color) self.fig.set_dpi(self.pixel_density) gridspec = GridSpec(1, 1) sp_stereo = gridspec.new_subplotspec((0, 0)) ax_stereo = self.fig.add_subplot(sp_stereo, projection=self.get_projection()) return ax_stereo def get_stereo_rose(self): """ Resets the figure and returns a stereonet and rose diagram axis. When the view in the main window is changed to stereonet and rose diagram, the figure is reset. The current settings are applied and two subplots for the stereonet and rose diagram are created. The axis of the stereonet and rose diagram are returned. This method is called by the MainWindow "redraw_plot"-method. """ self.fig.clf() self.fig.patch.set_facecolor(self.canvas_color) self.fig.set_dpi(self.pixel_density) gridspec = GridSpec(1, 2) sp_stereo = gridspec.new_subplotspec((0, 0), rowspan=1, colspan=1) sp_rose = gridspec.new_subplotspec((0, 1), rowspan=1, colspan=1) ax_stereo = self.fig.add_subplot(sp_stereo, projection=self.get_projection()) ax_rose = self.fig.add_subplot(sp_rose, projection="northpolar") return ax_stereo, ax_rose def get_rose_diagram(self): """ Resets the figure and returns the rose diagram axis. When the view in the main window is changed to rose-diagram-only the figure is reset. The current settings are applied and one subplot for the rose diagram is created. The axis of the rose-diagram is returned. This method is called by the MainWindow "redraw_plot"-method. """ self.fig.clf() self.fig.patch.set_facecolor(self.canvas_color) self.fig.set_dpi(self.pixel_density) gridspec = GridSpec(1, 1) sp_rose = gridspec.new_subplotspec((0, 0)) ax_rose = self.fig.add_subplot(sp_rose, projection="northpolar") return ax_rose def get_pt_view(self): """ Resets the canvas and returns the 3 axis of the paleostress view. When the view in the main window is changed to paleostress the figure is reset. The current settings are applied and 3 subplots are created. The 3 axis of the subplots are returned. This method is called by the MainWindow "redraw_plot"-method when the view has been changed. """ self.fig.clf() self.fig.patch.set_facecolor(self.canvas_color) self.fig.set_dpi(self.pixel_density) gridspec = GridSpec(2, 5) sp_stereo = gridspec.new_subplotspec((0, 0), colspan=3, rowspan=2) sp_fluc = gridspec.new_subplotspec((0, 3), colspan=2) sp_mohr = gridspec.new_subplotspec((1, 3), colspan=2) ax_stereo = self.fig.add_subplot(sp_stereo, projection=self.get_projection()) ax_fluc = self.fig.add_subplot(sp_fluc, aspect="equal") ax_mohr = self.fig.add_subplot(sp_mohr, aspect="equal") return ax_stereo, ax_fluc, ax_mohr def get_show_north(self): """ Returns if the stereonet should show the North symbol or degrees Returns True if the North symbol should be drawn (the default value), or False in which case numbers will be drawn for different degrees. """ return self.show_north def set_show_north(self, new_state): """ Sets a new state for whether the North symbol should be drawn. Expects a boolean. True means the North symbol will be drawn. False means that the stereonet will show different degrees along the outside. """ self.show_north = new_state def get_show_cross(self): """ Returns if the stereonet should draw a cross at the center. Returns True if the cross should be drawn (the default value) or False if the cross should not be drawn. """ return self.show_cross def set_show_cross(self, new_state): """ Sets a new state for whether the center cross should be drawn. Expects a boolean. True means the center cross will be drawn. False means it will not be drawn. """ self.show_cross = new_state
class PlottingController(object): """ Plotting Controller This class controlls all the plotting related functionality. """ def __init__(self, parameters, defaults): """ Constructor """ v = ParameterValidator(parameters, defaults) self.parameters = v.validate() self.DPI = 100.0 self.bbox = BBox(parameters["bbox"]) # 1. Retrieve the data self.dset = self.__evaluate_datasource_type() ( self.parameters["source_url"], \ self.bbox, \ self.parameters["layers"][0], \ self.parameters["time"], \ self.parameters["time_index"], \ True ) self.lat = self.dset.get_lats() self.lon = self.dset.get_lons() self.var = self.dset.get_data() # 1.1 Normalise data self.bbox,self.lon,self.var = \ self.__transform_lons(self.bbox, \ self.lon, \ self.var) # 2. Set up figure self.bmapuclon = self.bbox.lon_max self.bmaplclon = self.bbox.lon_min self.bmapuclat = min(90, self.bbox.lat_max) self.bmaplclat = max(-90, self.bbox.lat_min) self.fig = Figure() self.canvas = FigureCanvas(self.fig) # 3. Set up basemap ax = self.fig.add_axes( (0,0,1,1), \ frame_on = False, \ axis_bgcolor = 'k', \ alpha = 0 , \ visible = False ) self.m = Basemap( projection = 'cyl', \ resolution = 'c' , \ llcrnrlon = self.bmaplclon, \ llcrnrlat = self.bmaplclat, \ urcrnrlon = self.bmapuclon, \ urcrnrlat = self.bmapuclat, \ suppress_ticks = True, \ fix_aspect = False, \ ax = ax) def get_legend(self): """ Responsible for wrapping the GetLegend request Returns an image. """ #FIXME: Ugly ... replace without having to print map first ax = self.fig.add_axes( (0,0,1,1), \ frame_on = False, \ axis_bgcolor = 'k') self.m.ax = ax self.__create_contours(pt.ContourPlot) self.fig = Figure(figsize=(64/self.DPI,256/self.DPI)) self.canvas = FigureCanvas(self.fig) self.__create_legend((0,0.1,0.2,0.8)) return self.__create_image() def get_contour(self): """ Responsible for wrapping the GetMap request. Returns an image. """ # Calculate offsets for stuff bmaplatmin, bmaplonmin = self.m(self.bbox.lat_min, self.bbox.lon_min) bmaplatmax, bmaplonmax = self.m(self.bbox.lat_max, self.bbox.lon_max) lon_offset1 = abs(self.bmaplclon - bmaplonmin) lat_offset1 = abs(self.bmaplclat - bmaplatmin) #lon_offset2 = abs(self.bmapuclon - bmaplonmax) #lat_offset2 = abs(self.bmapuclat - bmaplatmax) lon_normstart = lon_offset1 / abs(bmaplonmax - bmaplonmin) lat_normstart = lat_offset1 / abs(bmaplatmax - bmaplatmin) ax_xfrac = abs( self.bmapuclon - self.bmaplclon ) / \ abs( bmaplonmax - bmaplonmin ) ax_yfrac = abs( self.bmapuclat - self.bmaplclat ) / \ abs( bmaplatmax - bmaplatmin) coords = (lon_normstart, lat_normstart, ax_xfrac, ax_yfrac) ##### ax = self.fig.add_axes( coords, \ frame_on = False, \ axis_bgcolor = 'k') self.m.ax = ax self.__create_contours(self.__evaluate_plot_type()) return self.__create_image() def get_full_figure(self, n_merid = 5, n_para = 5): """ Responsibe for wrapping the GetFullFigure request. Returns an image. n_merid: Number of meridians we want to print as an overlay n_para: number of parallels we want printed as an overlay """ tick_font_size = 8 title_font_size = 9 plot_dims = self.__calc_plot_dims() ax = self.fig.add_axes( plot_dims, \ frame_on = False, \ axis_bgcolor = 'k') self.m.ax = ax self.__create_contours(self.__evaluate_plot_type()) self.__create_legend((0.8,0.1,0.02,plot_dims[3])) # Creating the overlay base = (self.bbox.lon_max - self.bbox.lon_min)/float(n_merid) meridians = [ self.bbox.lon_min + i*base for i in range(n_merid)] base = (self.bbox.lat_max - self.bbox.lat_min)/float(n_para) parallels = [ self.bbox.lat_min + i*base for i in range(1,n_para+1)] self.m.drawcoastlines() self.m.drawmeridians(meridians, \ labels = [0,1,0,1], \ fmt = "%3.1f", \ fontsize = tick_font_size) self.m.drawparallels(parallels, \ labels= [1,0,0,0], \ fmt = "%3.1f", \ fontsize = tick_font_size) self.m.drawparallels([0], \ linewidth = 1, \ dashes = [1,0], \ labels = [0,1,1,1], \ fontsize = tick_font_size) self.fig.text(0.05,0.98,self.__get_plot_title(), \ va='top', fontsize=title_font_size) return self.__create_image(transparent=False) def __create_image(self,transparent=True): """ Create image with the given format an returns it. If iamge type is not supported, an exception is raised: transparent: True/False for transparent background """ supported_formats = [ 'png', 'svg' ] img_format = self.parameters["format"] if not img_format in supported_formats: raise ex.InvalidFormatError(img_format) img_data = StringIO.StringIO() self.fig.savefig(img_data, \ format = img_format, \ transparent=transparent) value = img_data.getvalue() img_data.close() return value def __create_contours(self,style_type): """ This class does the actual work, but does not create images. Only manipulates the Basemap object. style_type: The class we have to create """ # Set size of the image self.fig.set_dpi(self.DPI) self.fig.set_size_inches(self.parameters["width"]/self.DPI, \ self.parameters["height"]/self.DPI) try: plot = style_type(self.parameters, \ self.m, \ self.lon, \ self.lat, \ self.var, \ self.fig ) except Exception, e: raise ex.InvalidParameterValueError(repr(e)) self.main_render = plot.plot()
def __init__(self, author, observations, local=True, db=None): # init super super(CaseCreatorWidget, self).__init__(setup=False) self.setWindowTitle('Create Case') # set wavepath casepath and local self.author = author self.local = local if self.local: self.db = None self.wavpath = pathlib.Path().joinpath('wav') self.casepath = pathlib.Path().joinpath('test_case').joinpath(self.author) else: self.db = db self.wavpath = pathlib.Path(WAV_PATH) self.casepath = pathlib.Path(CASE_FOLDER).joinpath(self.author) try: self.wavpath.mkdir() self.casepath.mkdir() except: pass # init logger fn = self.casepath.joinpath('saved_cases.log') logging.basicConfig(format='%(asctime)s %(message)s', filename=str(fn), level=logging.DEBUG, # filemode = 'w+', datefmt='%Y-%m-%d %H:%M:%S') logging.captureWarnings(True) logging.info('Start case creator session, local: {}.'.format(self.local)) # set observations and cases to analyzee, dict observatioins contain observation casess ans its metadata self.obsKeys = [] self.observations = {} for obs in observations: self.init_obs(obs) # set current noise self.currentNoise = 'Z' self.bothVisibles = True # Add New Widgets # structure: # # main Phonon Widget # ------------------------------------------------------ # Figure canvas widget # ------------------------------------------------------ # selectCase GB | selectSOI GB| # set quality GB | caseinfo GB | save button # ------------------------------------------------------ # Figure Canvas plt.ioff() fig = Figure((15, 10)) fig.set_dpi(110) # default 110 fig.set_facecolor('#272822') # canvas Widget: first fidget self.canvas = FigureCanvas(fig) # add axes self.ax = fig.add_subplot(111) # case Selector self.minspan = 0.05 self.CS = CaseSelector(self.ax, self.onselect, self.onclick, nrect=[50, 50], update_on_ext_event=True, minspan=self.minspan) self.ca_bar_handle = [self.CS] self.ca_widget_handle = [self.CS] # add canvas to main vBox self.vBox.addWidget(self.canvas) # begin second hBox # ................. secondhBox = QtGui.QHBoxLayout() # left vBox of second hBox leftvBox = QtGui.QVBoxLayout() # add first row to left vBox: hBox1 hBox1 = QtGui.QHBoxLayout() # select case combo groupBox = QtGui.QGroupBox('Select observation to analyze ') groupBox.setFixedWidth(280) self.obsCombo = QtGui.QComboBox() self.obsCombo.addItems(self.obsKeys) self.saved_obs_to_green() hbox1_1 = QtGui.QHBoxLayout() hbox1_1.addWidget(self.obsCombo) groupBox.setLayout(hbox1_1) hBox1.addWidget(groupBox) # select noise Type Group groupBox = QtGui.QGroupBox('Noise Type to select') groupBox.setFixedWidth(220) hbox1_2 = QtGui.QHBoxLayout() # noise Type combo self.SOIcombo = QtGui.QComboBox() self.SOIcombo.addItem('Zischen', 'Z') self.SOIcombo.setItemData(0, QtGui.QColor('#984ea3'), QtCore.Qt.BackgroundRole) self.SOIcombo.setItemData(0, QtGui.QColor('#f5f5f5'), QtCore.Qt.ForegroundRole) # change text color self.SOIcombo.addItem('Kreischen', 'KG') # add backgroundcolor of combo K self.SOIcombo.setItemData(1, QtGui.QColor('#ffff33'), QtCore.Qt.BackgroundRole) # change text color self.SOIcombo.setItemData(1, QtGui.QColor('#272822'), QtCore.Qt.ForegroundRole) hbox1_2.addWidget(self.SOIcombo) # visualize both cb self.cb = QtGui.QCheckBox('both visible', self) self.cb.setChecked(self.bothVisibles) hbox1_2.addWidget(self.cb) groupBox.setLayout(hbox1_2) hBox1.addWidget(groupBox) hBox1.addStretch(1) leftvBox.addLayout(hBox1) # add second row to leftvBox: # quality radios groupBox = QtGui.QGroupBox("Select quality: are noise events detectable?") groupBox.setFixedWidth(507) self.Qradios = [QtGui.QRadioButton(q) for q in ['good', 'medium', 'bad']] hbox2_1 = QtGui.QHBoxLayout() self.rbG = QtGui.QButtonGroup() for rb in self.Qradios: self.rbG.addButton(rb) hbox2_1.addWidget(rb) groupBox.setLayout(hbox2_1) # add to leftvBox leftvBox.addWidget(groupBox) # add leftvBox to second hBox secondhBox.addLayout(leftvBox) # centre GB of secondhBox # add info Groupbox groupBox = QtGui.QGroupBox("case informations") # add centre GB to second hBox secondhBox.addWidget(groupBox) # rigth of second hBox # save button self.buttonSave = QtGui.QPushButton("save", self) # rigth second hBox secondhBox.addWidget(self.buttonSave) secondhBox.addStretch() # end second hBox # add second hBox to main vBox self.vBox.addLayout(secondhBox) # ------------- # set and centralWidget centralWidget = QtGui.QWidget() centralWidget.setLayout(self.vBox) self.setCentralWidget(centralWidget) # add connections self.connections() self.set_current_obs(0)
class MainPlotController(object): """ A controller for the main plot canvas. Sets up the widgets and has image exporting functionality. """ file_filters = ("Portable Network Graphics (PNG)", "*.png"), \ ("Scalable Vector Graphics (SVG)", "*.svg"), \ ("Portable Document Format (PDF)", "*.pdf") _canvas = None @property def canvas(self): if not self._canvas: self.setup_figure() self.setup_canvas() self.setup_content() return self._canvas # ------------------------------------------------------------ # View integration getters # ------------------------------------------------------------ def get_toolbar_widget(self, window): return NavigationToolbar(self.canvas, window) def get_canvas_widget(self): return self.canvas # ------------------------------------------------------------ # Initialization and other internals # ------------------------------------------------------------ def __init__(self, status_callback, marker_callback, *args, **kwargs): self.setup_layout_cache() self.setup_figure() self.setup_canvas() self.setup_content(status_callback, marker_callback) def setup_layout_cache(self): self.position_setup = PositionSetup() self.labels = list() self.marker_lbls = list() self._proxies = dict() self.scale = 1.0 self.stats = False self._last_pos = None def setup_figure(self): self.figure = Figure(dpi=72, facecolor="#FFFFFF", linewidth=0) self.figure.subplots_adjust(hspace=0.0, wspace=0.0) def setup_canvas(self): self._canvas = FigureCanvasGTK(self.figure) def setup_content(self, status_callback, marker_callback): # Create subplot and add it to the figure: self.plot = Subplot(self.figure, 211, facecolor=(1.0, 1.0, 1.0, 0.0)) self.plot.set_autoscale_on(False) self.figure.add_axes(self.plot) # Connect events: self.canvas.mpl_connect('draw_event', self.fix_after_drawing) self.canvas.mpl_connect('resize_event', self.fix_after_drawing) self.mtc = MotionTracker(self, status_callback) self.cc = ClickCatcher(self, marker_callback) #self.update() # ------------------------------------------------------------ # Update methods # ------------------------------------------------------------ def draw(self): self._last_pos = self.fix_before_drawing() self.figure.canvas.draw() def fix_after_drawing(self, *args): _new_pos = self.fix_before_drawing() if _new_pos != self._last_pos: self.figure.canvas.draw() self._last_pos = _new_pos return False def fix_before_drawing(self, *args): """ Fixes alignment issues due to longer labels or smaller windows Is executed after an initial draw event, since we can then retrieve the actual label dimensions and shift/resize the plot accordingly. """ renderer = get_renderer(self.figure) if not renderer or not self._canvas.get_realized(): return False # Fix left side for wide specimen labels: if len(self.labels) > 0: bbox = self._get_joint_bbox(self.labels, renderer) if bbox is not None: self.position_setup.left = self.position_setup.default_left + bbox.width # Fix top for high marker labels: if len(self.marker_lbls) > 0: bbox = self._get_joint_bbox([ label for label, flag, _ in self.marker_lbls if flag ], renderer) if bbox is not None: self.position_setup.top = self.position_setup.default_top - bbox.height # Fix bottom for x-axis label: bottom_label = self.plot.axis["bottom"].label if bottom_label is not None: bbox = self._get_joint_bbox([bottom_label], renderer) if bbox is not None: self.position_setup.bottom = self.position_setup.default_bottom + (bbox.ymax - bbox.ymin) * 2.0 # somehow we need this? # Calculate new plot position & set it: plot_pos = self.position_setup.position self.plot.set_position(plot_pos) # Adjust specimen label position for label in self.labels: label.set_x(plot_pos[0] - 0.025) # Adjust marker label position for label, flag, y_offset in self.marker_lbls: if flag: newy = plot_pos[1] + plot_pos[3] + y_offset - 0.025 label.set_y(newy) _new_pos = self.position_setup.to_string() return _new_pos def update(self, clear=False, project=None, specimens=None): """ Updates the entire plot with the given information. """ if clear: self.plot.cla() if project and specimens: self.labels, self.marker_lbls = plot_specimens( self.plot, self.position_setup, self.cc, project, specimens ) # get mixtures for the selected specimens: plot_mixtures(self.plot, project, [ mixture for mixture in project.mixtures if any(specimen in mixture.specimens for specimen in specimens) ]) update_axes( self.plot, self.position_setup, project, specimens ) self.draw() # ------------------------------------------------------------ # Plot position and size calculations # ------------------------------------------------------------ def _get_joint_bbox(self, container, renderer): bboxes = [] try: for text in container: bbox = text.get_window_extent(renderer=renderer) # the figure transform goes from relative coords->pixels and we # want the inverse of that bboxi = bbox.inverse_transformed(self.figure.transFigure) bboxes.append(bboxi) except (RuntimeError, ValueError): logger.exception("Caught unhandled exception when joining boundig boxes") return None # don't continue # this is the bbox that bounds all the bboxes, again in relative # figure coords if len(bboxes) > 0: bbox = transforms.Bbox.union(bboxes) return bbox else: return None # ------------------------------------------------------------ # Graph exporting # ------------------------------------------------------------ def save(self, parent=None, current_name="graph", size="auto", num_specimens=1, offset=0.75): """ Displays a save dialog to export an image from the current plot. """ # Parse arguments: width, height = 0, 0 if size == "auto": descr, width, height, dpi = settings.OUTPUT_PRESETS[0] else: width, height, dpi = list(map(float, size.replace("@", "x").split("x"))) # Load gui: builder = Gtk.Builder() builder.add_from_file(resource_filename("pyxrd.specimen", "glade/save_graph_size.glade")) # FIXME move this to this namespace!! size_expander = builder.get_object("size_expander") cmb_presets = builder.get_object("cmb_presets") # Setup combo with presets: cmb_store = Gtk.ListStore(str, int, int, float) for row in settings.OUTPUT_PRESETS: cmb_store.append(row) cmb_presets.clear() cmb_presets.set_model(cmb_store) cell = Gtk.CellRendererText() cmb_presets.pack_start(cell, True) cmb_presets.add_attribute(cell, 'text', 0) def on_cmb_changed(cmb, *args): itr = cmb.get_active_iter() w, h, d = cmb_store.get(itr, 1, 2, 3) entry_w.set_text(str(w)) entry_h.set_text(str(h)) entry_dpi.set_text(str(d)) cmb_presets.connect('changed', on_cmb_changed) # Setup input boxes: entry_w = builder.get_object("entry_width") entry_h = builder.get_object("entry_height") entry_dpi = builder.get_object("entry_dpi") entry_w.set_text(str(width)) entry_h.set_text(str(height)) entry_dpi.set_text(str(dpi)) # What to do when the user wants to save this: def on_accept(dialog): # Get the width, height & dpi width = float(entry_w.get_text()) height = float(entry_h.get_text()) dpi = float(entry_dpi.get_text()) i_width, i_height = width / dpi, height / dpi # Save it all right! self.save_figure(dialog.filename, dpi, i_width, i_height) # Ask the user where, how and if he wants to save: DialogFactory.get_save_dialog( "Save Graph", parent=parent, filters=self.file_filters, current_name=current_name, extra_widget=size_expander ).run(on_accept) def save_figure(self, filename, dpi, i_width, i_height): """ Save the current plot Arguments: filename: the filename to save to (either .png, .pdf or .svg) dpi: Dots-Per-Inch resolution i_width: the width in inch i_height: the height in inch """ # Get original settings: original_dpi = self.figure.get_dpi() original_width, original_height = self.figure.get_size_inches() # Set everything according to the user selection: self.figure.set_dpi(dpi) self.figure.set_size_inches((i_width, i_height)) self.figure.canvas.draw() # replot bbox_inches = matplotlib.transforms.Bbox.from_bounds(0, 0, i_width, i_height) # Save the figure: self.figure.savefig(filename, dpi=dpi, bbox_inches=bbox_inches) # Put everything back the way it was: self.figure.set_dpi(original_dpi) self.figure.set_size_inches((original_width, original_height)) self.figure.canvas.draw() # replot pass # end of class
class MatplotlibRenderer(Renderer): """ Renderer backend which takes data and produces images. Does not touch Wave or Channel. If __init__ reads cfg, cfg cannot be hotswapped. Reasons to hotswap cfg: RendererCfg: - GUI preview size - Changing layout - Changing #smp drawn (samples_visible) (see RendererCfg) Original OVGen does not support hotswapping. It disables changing options during rendering. Reasons to hotswap trigger algorithms: - changing scan_nsamp (cannot be hotswapped, since correlation buffer is incompatible) So don't. """ def __init__(self, *args, **kwargs): Renderer.__init__(self, *args, **kwargs) dict.__setitem__(matplotlib.rcParams, "lines.antialiased", self.cfg.antialiasing) self._fig: "Figure" # _axes2d[wave][chan] = Axes self._axes2d: List[List["Axes"]] # set by set_layout() # _lines2d[wave][chan] = Line2D self._lines2d: List[List[Line2D]] = [] self._lines_flat: List["Line2D"] = [] transparent = "#00000000" layout: RendererLayout def _set_layout(self, wave_nchans: List[int]) -> None: """ Creates a flat array of Matplotlib Axes, with the new layout. Opens a window showing the Figure (and Axes). Inputs: self.cfg, self.fig Outputs: self.nrows, self.ncols, self.axes """ self.layout = RendererLayout(self.lcfg, wave_nchans) # Create Axes # https://matplotlib.org/api/_as_gen/matplotlib.pyplot.subplots.html if hasattr(self, "_fig"): raise Exception( "I don't currently expect to call _set_layout() twice") # plt.close(self.fig) grid_color = self.cfg.grid_color self._fig = Figure() FigureCanvasAgg(self._fig) # RegionFactory def axes_factory(r: RegionSpec) -> "Axes": width = 1 / r.ncol left = r.col / r.ncol assert 0 <= left < 1 height = 1 / r.nrow bottom = (r.nrow - r.row - 1) / r.nrow assert 0 <= bottom < 1 # Disabling xticks/yticks is unnecessary, since we hide Axises. ax = self._fig.add_axes([left, bottom, width, height], xticks=[], yticks=[]) if grid_color: # Initialize borders # Hide Axises # (drawing them is very slow, and we disable ticks+labels anyway) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) # Background color # ax.patch.set_fill(False) sets _fill=False, # then calls _set_facecolor(...) "alpha = self._alpha if self._fill else 0". # It is no faster than below. ax.set_facecolor(self.transparent) # Set border colors for spine in ax.spines.values(): spine.set_color(grid_color) def hide(key: str): ax.spines[key].set_visible(False) # Hide all axes except bottom-right. hide("top") hide("left") # If bottom of screen, hide bottom. If right of screen, hide right. if r.screen_edges & Edges.Bottom: hide("bottom") if r.screen_edges & Edges.Right: hide("right") # Dim stereo gridlines if self.cfg.stereo_grid_opacity > 0: dim_color = matplotlib.colors.to_rgba_array(grid_color)[0] dim_color[-1] = self.cfg.stereo_grid_opacity def dim(key: str): ax.spines[key].set_color(dim_color) else: dim = hide # If not bottom of wave, dim bottom. If not right of wave, dim right. if not r.wave_edges & Edges.Bottom: dim("bottom") if not r.wave_edges & Edges.Right: dim("right") else: ax.set_axis_off() return ax # Generate arrangement (using self.lcfg, wave_nchans) # _axes2d[wave][chan] = Axes self._axes2d = self.layout.arrange(axes_factory) # Setup figure geometry self._fig.set_dpi(DPI) self._fig.set_size_inches(self.cfg.width / DPI, self.cfg.height / DPI) def render_frame(self, datas: List[np.ndarray]) -> None: ndata = len(datas) if self.nplots != ndata: raise ValueError( f"incorrect data to plot: {self.nplots} plots but {ndata} datas" ) # Initialize axes and draw waveform data if not self._lines2d: assert len(datas[0].shape) == 2, datas[0].shape wave_nchans = [data.shape[1] for data in datas] self._set_layout(wave_nchans) cfg = self.cfg # Setup background/axes self._fig.set_facecolor(cfg.bg_color) for idx, wave_data in enumerate(datas): wave_axes = self._axes2d[idx] for ax in unique_by_id(wave_axes): max_x = len(wave_data) - 1 ax.set_xlim(0, max_x) ax.set_ylim(-1, 1) # Setup midlines (depends on max_x and wave_data) midline_color = cfg.midline_color midline_width = pixels(1) # zorder=-100 still draws on top of gridlines :( kw = dict(color=midline_color, linewidth=midline_width) if cfg.v_midline: ax.axvline(x=max_x / 2, **kw) if cfg.h_midline: ax.axhline(y=0, **kw) self._save_background() # Plot lines over background line_width = pixels(cfg.line_width) # Foreach wave for wave_idx, wave_data in enumerate(datas): wave_axes = self._axes2d[wave_idx] wave_lines = [] # Foreach chan for chan_idx, chan_data in enumerate(wave_data.T): ax = wave_axes[chan_idx] line_color = self._line_params[wave_idx].color chan_line: Line2D = ax.plot(chan_data, color=line_color, linewidth=line_width)[0] wave_lines.append(chan_line) self._lines2d.append(wave_lines) self._lines_flat.extend(wave_lines) # Draw waveform data else: # Foreach wave for wave_idx, wave_data in enumerate(datas): wave_lines = self._lines2d[wave_idx] # Foreach chan for chan_idx, chan_data in enumerate(wave_data.T): chan_line = wave_lines[chan_idx] chan_line.set_ydata(chan_data) self._redraw_over_background() bg_cache: Any # "matplotlib.backends._backend_agg.BufferRegion" def _save_background(self) -> None: """ Draw static background. """ # https://stackoverflow.com/a/8956211 # https://matplotlib.org/api/animation_api.html#funcanimation fig = self._fig fig.canvas.draw() self.bg_cache = fig.canvas.copy_from_bbox(fig.bbox) def _redraw_over_background(self) -> None: """ Redraw animated elements of the image. """ canvas: FigureCanvasAgg = self._fig.canvas canvas.restore_region(self.bg_cache) for line in self._lines_flat: line.axes.draw_artist(line) # https://bastibe.de/2013-05-30-speeding-up-matplotlib.html # thinks fig.canvas.blit(ax.bbox) leaks memory # and fig.canvas.update() works. # Except I found no memory leak... # and update() doesn't exist in FigureCanvasBase when no GUI is present. canvas.blit(self._fig.bbox) def get_frame(self) -> ByteBuffer: """ Returns ndarray of shape w,h,3. """ canvas = self._fig.canvas # Agg is the default noninteractive backend except on OSX. # https://matplotlib.org/faq/usage_faq.html if not isinstance(canvas, FigureCanvasAgg): raise RuntimeError( f"oh shit, cannot read data from {type(canvas)} != FigureCanvasAgg" ) w = self.cfg.width h = self.cfg.height assert (w, h) == canvas.get_width_height() buffer_rgb = canvas.tostring_rgb() assert len(buffer_rgb) == w * h * RGB_DEPTH return buffer_rgb
def calculateFloorMap(self, resolution=0.01, level=0, xlim=None, ylim=None, ignoreGround=False, roomIds=None): figSize = (10, 10) fig = Figure(figsize=figSize, dpi=100, frameon=False) canvas = FigureCanvas(fig) ax = fig.add_subplot(111, aspect='equal') fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0) ax.axis('off') ax.set_aspect('equal') if roomIds is not None: models = [] for roomId in roomIds: models.extend([ model for model in self.scene.scene.findAllMatches( '**/level-%d/room-%s/layouts/object-*/+ModelNode' % (level, roomId)) ]) else: models = [ model for model in self.scene.scene.findAllMatches( '**/level-%d/**/layouts/object-*/+ModelNode' % level) ] # Loop for all floors in the scene: for model in models: modelId = model.getNetTag('model-id') if not modelId.endswith('f') or (ignoreGround and 'gd' in modelId): continue addModelTriangles(ax, model) if xlim is not None and ylim is not None: ax.set_xlim(xlim) ax.set_ylim(ylim) else: ax.autoscale(True) set_axes_equal(ax) xlim, ylim = ax.get_xlim(), ax.get_ylim() xrange = xlim[1] - xlim[0] yrange = ylim[1] - ylim[0] assert np.allclose(xrange, yrange, atol=1e-6) dpi = (xrange / resolution) / figSize[0] fig.set_dpi(dpi) floorMap = canvas2image(canvas) plt.close(fig) # RGB to binary floorMap = np.round(np.mean(floorMap[:, :], axis=-1)) # NOTE: Filter out small gaps that can exist between rooms floorMap = ndimage.gaussian_filter(floorMap, sigma=(1, 1), order=0) floorMap = np.round(floorMap) # NOTE: inverse image so that floor areas are shown in white floorMap = 1.0 - floorMap return floorMap, xlim, ylim
class Canvas_sourceSpec: def __init__(self,ui,CSDA,N): # tab ui.list_of_tabs.append(QtWidgets.QWidget()) # ui.gridLayout_TabSourceImage ui.list_of_tabs[-1].setObjectName("tab_plotSourceSpec") #ui.tab_plotSourceSpec = QtWidgets.QWidget() #ui.tab_plotSourceSpec.setObjectName("tab_plotSourceSpec") # gridlayout TAB Source Spectrum ui.list_grid_tabs.append(QtWidgets.QGridLayout(ui.list_of_tabs[-1])) ui.list_grid_tabs[-1].setObjectName("gridLayout_TabSourceSpec") #ui.gridLayout_TabSourceSpec = QtWidgets.QGridLayout(ui.list_of_tabs[-1]) #ui.gridLayout_TabSourceSpec.setObjectName("gridLayout_TabSourceSpec") # Scroll Area Source Spectrum ui.scrollArea_sourceSpec = QtWidgets.QScrollArea(ui.list_of_tabs[-1]) ui.scrollArea_sourceSpec.setPalette(palette_scrollPlotProp) ui.scrollArea_sourceSpec.setWidgetResizable(True) ui.scrollArea_sourceSpec.setSizePolicy(QtWidgets.QSizePolicy.Minimum,QtWidgets.QSizePolicy.Minimum) ui.scrollArea_sourceSpec.setObjectName("scrollArea_sourceSpec") # Scroll Area Options Source Spectrum ui.scrollArea_sourceSpecOpt = QtWidgets.QScrollArea(ui.list_of_tabs[-1]) ui.scrollArea_sourceSpecOpt.setWidgetResizable(True) ui.scrollArea_sourceSpecOpt.setObjectName("scrollArea_sourceSpecOpt") ui.scrollArea_sourceSpecOpt.setPalette(palette_scrollPlotProp) ui.scrollAreaWidgetContents_sourceSpecOpt = QtWidgets.QWidget() ui.scrollAreaWidgetContents_sourceSpecOpt.setObjectName("scrollAreaWidgetContents_sourceSpecOpt") ui.scrollArea_sourceSpecOpt.setWidget(ui.scrollAreaWidgetContents_sourceSpecOpt) ui.gridLayout_sourceSpecOpt = QtWidgets.QGridLayout(ui.scrollAreaWidgetContents_sourceSpecOpt) ui.gridLayout_sourceSpecOpt.setObjectName("gridLayout_sourceSpecOpt") ui.list_grid_tabs[-1].addWidget(ui.scrollArea_sourceSpec, 3, 0, 1, 1) ui.list_grid_tabs[-1].addWidget(ui.scrollArea_sourceSpecOpt, 10, 0, 1, 1) #_______________________________________________________________________ #======================================================================= # Parameters #======================================================================= # N self.N = N # W matrix self.CSDA = CSDA # User interface self.ui = ui # parent self.parent = ui.scrollArea_sourceSpec # parent optioncs self.parentOptions = ui.scrollArea_sourceSpecOpt # grid parent options self.gridOptions = ui.gridLayout_sourceSpecOpt # first plot self.first_plot = False #_______________________________________________________________________ # Initial points (middle) #self.P1x = int(N/2) #self.P1y = int(N/2) #self.P2x = int(N/2) self.build_fig() def build_fig(self): #======================================================================= # Create the mpl Figure and FigCanvas objects. #======================================================================= # magnitude self.dpi = 100 self.fig = Figure((5.0, 4.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) # axes self.axes = self.fig.add_subplot(111) #_______________________________________________________________________ #======================================================================= # Plotting figure and configuring #======================================================================= # creating image prop spectrum = self.CSDA.spectrum # b array b_array=self.CSDA.omega_array # PLOT magnitude self.im = self.axes.plot(b_array,spectrum,marker="o",linewidth=1.0,label="Source Spectrum") #self.cbar = self.fig.colorbar(self.im) self.leg = self.axes.legend() # font size self.fsize = 12 # x,y Labels self.axes.set_xlabel("x (m)",fontsize = self.fsize) self.axes.set_ylabel("y (m)",fontsize = self.fsize) #_______________________________________________________________________ #======================================================================= # Tool bar #======================================================================= # Bind the 'pick' event for clicking on one of the bars self.canvas.mpl_connect('pick_event', self.ui.on_pick) #self.canvas.mpl_connect("scroll_event", ui.scrolling) # Create the navigation toolbar, tied to the canvas self.mpl_toolbar = NavigationToolbar(self.canvas, self.parent) #ui.gridLayout_TabPropImage.addWidget(self.mpl_toolbar, 2, 0, 1, 3) self.ui.list_grid_tabs[-1].addWidget(self.mpl_toolbar, 8, 0, 1, 3,alignment=QtCore.Qt.AlignLeft) #_______________________________________________________________________ #======================================================================= # Canvas in Scroll Area - magnitude #======================================================================= #self.canvas.draw() #self.canvas.setParent(parent) self.canvas.setSizePolicy(QtWidgets.QSizePolicy.Minimum,QtWidgets.QSizePolicy.Minimum) # Container for VBOX self.containerGraph = QWidget(self.parent) self.containerGraph.setSizePolicy(QtWidgets.QSizePolicy.Minimum,QtWidgets.QSizePolicy.Minimum) self.containerGraph.setMinimumWidth(self.canvas.width()) self.containerGraph.setMinimumHeight(self.canvas.height()) self.containerGraph.setMaximumWidth(self.canvas.width()+5) self.containerGraph.setMaximumHeight(self.canvas.height()+5) # VBOX for canvas self.vbox = QVBoxLayout(self.containerGraph) #self.vbox.setGeometry(QRect(0, 0, self.canvas.width(), self.canvas.height())) self.vbox.addWidget(self.canvas) self.parent.setWidget(self.containerGraph) #_______________________________________________________________________ if not self.first_plot: self.figure_options() self.first_plot = True def figure_options(self): """ # label Point P1x self.label_P1x = QtWidgets.QLabel(self.parentOptions) self.label_P1x.setObjectName("label_P1x") self.label_P1x.setText("Point ") self.gridOptions.addWidget(self.label_P1x, 2, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit Point P1x self.lineEdit_P1x = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_P1x.setObjectName("lineEdit_P1x") self.gridOptions.addWidget(self.lineEdit_P1x,2, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_P1x.setText(str(int(self.N/2))) self.lineEdit_P1x.textChanged.connect(self.change_P1x) # label Point P1y self.label_P1y = QtWidgets.QLabel(self.parentOptions) self.label_P1y.setObjectName("label_P1y") self.label_P1y.setText('$Point$') self.gridOptions.addWidget(self.label_P1y, 4, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit Point P1x self.lineEdit_P1y = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_P1y.setObjectName("lineEdit_P1y") self.gridOptions.addWidget(self.lineEdit_P1y,4, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_P1y.setText(str(int(self.N/2))) self.lineEdit_P1y.textChanged.connect(self.change_P1y) """ # label title self.label_title = QtWidgets.QLabel(self.parentOptions) self.label_title.setObjectName("label_title") self.label_title.setText("Title") self.gridOptions.addWidget(self.label_title, 6, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit title label self.lineEdit_title = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_title.setObjectName("lineEdit_title") self.lineEdit_title.textChanged.connect(self.change_title) self.gridOptions.addWidget(self.lineEdit_title,6, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.change_title() self.lineEdit_title.setText("Source Spectrum") # label x self.label_x = QtWidgets.QLabel(self.parentOptions) self.label_x.setObjectName("label_x") self.label_x.setText("Label x-axis") self.gridOptions.addWidget(self.label_x, 8, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit x label self.lineEdit_xLabel = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_xLabel.setObjectName("lineEdit_xlabel") self.lineEdit_xLabel.textChanged.connect(self.change_labelx) self.gridOptions.addWidget(self.lineEdit_xLabel,8, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.change_labelx() self.lineEdit_xLabel.setText(r'$\omega\,(\mathrm{rad\,s^{-1})}$') # label y self.label_y = QtWidgets.QLabel(self.parentOptions) self.label_y.setObjectName("label_y") self.label_y.setText("Label y-axis") self.gridOptions.addWidget(self.label_y, 10, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit y label self.lineEdit_yLabel = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_yLabel.setObjectName("lineEdit_ylabel") self.lineEdit_yLabel.textChanged.connect(self.change_labely) self.gridOptions.addWidget(self.lineEdit_yLabel,10, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.change_labely() self.lineEdit_yLabel.setText("Normalized Spectrum (a.u.)") # label xlim self.label_xlim = QtWidgets.QLabel(self.parentOptions) self.label_xlim.setObjectName("label_xlim") self.label_xlim.setText("xlim") self.gridOptions.addWidget(self.label_xlim, 12, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit xlim self.lineEdit_xlim = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_xlim.setObjectName("lineEdit_ylabel") self.lineEdit_xlim.textChanged.connect(self.change_xlim) self.gridOptions.addWidget(self.lineEdit_xlim,12, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_xlim.setText("(_,_)") self.change_xlim() # label ylim self.label_ylim = QtWidgets.QLabel(self.parentOptions) self.label_ylim.setObjectName("label_ylim") self.label_ylim.setText("ylim") self.gridOptions.addWidget(self.label_ylim, 14, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit ylim self.lineEdit_ylim = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_ylim.setObjectName("lineEdit_ylabel") self.lineEdit_ylim.textChanged.connect(self.change_ylim) self.gridOptions.addWidget(self.lineEdit_ylim,14, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_ylim.setText("(_,_)") self.change_ylim() # label Font Size self.label_fsize = QtWidgets.QLabel(self.parentOptions) self.label_fsize.setObjectName("label_fsize") self.label_fsize.setText("Font Size") self.gridOptions.addWidget(self.label_fsize, 22, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit font size self.lineEdit_fsize = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_fsize.setObjectName("label_fsize") self.lineEdit_fsize.textChanged.connect(self.change_fsize) self.gridOptions.addWidget(self.lineEdit_fsize,22, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_fsize.setText(str(self.fsize)) self.change_fsize() # label DPI self.label_dpi = QtWidgets.QLabel(self.parentOptions) self.label_dpi.setObjectName("label_dpi") self.label_dpi.setText("dpi (100-500)") self.gridOptions.addWidget(self.label_dpi, 24, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit DPI self.lineEdit_dpi = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_dpi.setObjectName("label_dpi") self.lineEdit_dpi.textChanged.connect(self.change_dpi) self.gridOptions.addWidget(self.lineEdit_dpi,24, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_dpi.setText(str(self.dpi)) self.change_fsize() # checkbox grid self.checkBox_grid = QtWidgets.QCheckBox(self.parentOptions) self.checkBox_grid.setObjectName("Grid") self.gridOptions.addWidget(self.checkBox_grid, 30, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) self.checkBox_grid.setText("Grid") self.checkBox_grid.stateChanged.connect(self.change_grid) self.checkBox_grid.setChecked(True) # checkbox legend self.checkBox_legend = QtWidgets.QCheckBox(self.parentOptions) self.checkBox_legend.setObjectName("legend") self.gridOptions.addWidget(self.checkBox_legend, 32, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) self.checkBox_legend.setText("Legend") self.checkBox_legend.stateChanged.connect(self.change_legend) self.checkBox_legend.setChecked(True) # label legend font size self.label_legendFS = QtWidgets.QLabel(self.parentOptions) self.label_legendFS.setObjectName("label_legendFS") self.label_legendFS.setText("Legend Font Size") self.gridOptions.addWidget(self.label_legendFS, 34, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit legend font size self.lineEdit_legendFS = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_legendFS.setObjectName("label_legendFS") self.lineEdit_legendFS.textChanged.connect(self.change_legendFS) self.gridOptions.addWidget(self.lineEdit_legendFS,34, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_legendFS.setText(str(self.fsize)) # label legend loc self.label_legendLOC = QtWidgets.QLabel(self.parentOptions) self.label_legendLOC.setObjectName("label_legendLOC") self.label_legendLOC.setText("Legend Location (1-10)") self.gridOptions.addWidget(self.label_legendLOC, 36, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit legend loc self.lineEdit_legendLOC = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_legendLOC.setObjectName("label_legendLOC") self.lineEdit_legendLOC.textChanged.connect(self.change_legendLOC) self.gridOptions.addWidget(self.lineEdit_legendLOC,36, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_legendLOC.setText(str(self.fsize)) # label legend Text self.label_legendText = QtWidgets.QLabel(self.parentOptions) self.label_legendText.setObjectName("label_legendText") self.label_legendText.setText("Legend Text") self.gridOptions.addWidget(self.label_legendText, 37, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit legend Text self.lineEdit_legendText = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_legendText.setObjectName("label_legendText") self.lineEdit_legendText.textChanged.connect(self.change_legendText) self.gridOptions.addWidget(self.lineEdit_legendText,37, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_legendText.setText(str("Spectrum")) self.change_legendText() # label combobox markers self.label_markerStyle = QtWidgets.QLabel(self.parentOptions) self.label_markerStyle.setObjectName("label_markerStyle") self.label_markerStyle.setText("Marker Style") self.gridOptions.addWidget(self.label_markerStyle, 38, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # combobox markers self.markers_dict = {"None":0, "o":1, ".":2, ",":3, "v":4, "^":5, "<":6, ">":7, "1":8, "2":9, "3":10, "4":11, "8":12, "s":13, "p":14, "P":15, "*":16, "h":17, "H":18, "+":19, "x": 20, "X":21, "D":22, "d":23, "|":24, "_":25} self.comboBox_markers = QtWidgets.QComboBox(self.parentOptions) self.comboBox_markers.setObjectName("comboBox_markers") self.comboBox_markers.addItems(self.markers_dict) self.gridOptions.addWidget(self.comboBox_markers, 38, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.comboBox_markers.currentIndexChanged.connect(self.change_markers) #self.change_markers() # label Marker Size self.label_markerSize = QtWidgets.QLabel(self.parentOptions) self.label_markerSize.setObjectName("label_markerSize") self.label_markerSize.setText("Marker Size (10-30)") self.gridOptions.addWidget(self.label_markerSize, 40, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit Marker Size self.lineEdit_markerSize= QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_markerSize.setObjectName("label_markersize") self.lineEdit_markerSize.textChanged.connect(self.change_markerSize) self.gridOptions.addWidget(self.lineEdit_markerSize,40, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_markerSize.setText(str(6)) self.change_markerSize() # label linewidth self.label_linewidth = QtWidgets.QLabel(self.parentOptions) self.label_linewidth.setObjectName("label_linewidth") self.label_linewidth.setText("Linewidth (1.0-12.0)") self.gridOptions.addWidget(self.label_linewidth, 42, 0, 1, 1,alignment=QtCore.Qt.AlignLeft) # line edit linewidth self.lineEdit_linewidth = QtWidgets.QLineEdit(self.parentOptions) self.lineEdit_linewidth.setObjectName("label_linewidth") self.lineEdit_linewidth.textChanged.connect(self.change_linewidth) self.gridOptions.addWidget(self.lineEdit_linewidth,42, 1, 1, 1,alignment=QtCore.Qt.AlignLeft) self.lineEdit_linewidth.setText(str(1.0)) #_______________________________________________________________________ #=========================================================================== # Functions #=========================================================================== def change_P1x(self): try: temp = self.lineEdit_P1x.text() if temp != "": new = int(temp) if new>=0 and new<self.N: self.P1x = new #self.update_pcolor() self.update_draw() except Except as error: self.ui.update_outputText(str(error)) def change_P1y(self): try: temp = self.lineEdit_P1y.text() if temp != "": new = int(temp) if new>=0 and new<self.N: self.P1y = new #self.update_pcolor() self.update_draw() except Except as error: self.ui.update_outputText(str(error)) def update_draw(self): self.canvas.draw() self.canvas.updateGeometry() #--------------------------------------------------------------------------- # Magnitude #--------------------------------------------------------------------------- def change_linewidth(self): try: self.axes.lines[0].set_linewidth(float(self.lineEdit_linewidth.text())) self.canvas.draw() except Exception as error: pass def change_legendText(self): try: self.axes.legend(labels=[str(self.lineEdit_legendText.text())]) self.canvas.draw() except Exception as error: self.ui.update_outputText(error) def change_markerSize(self): try: self.axes.lines[0].set_markersize(int(self.lineEdit_markerSize.text())) self.canvas.draw() except Exception as error: pass def change_markers(self): try: if self.comboBox_markers.currentIndex()==0: self.axes.lines[0].set_marker(None) self.canvas.draw() else: self.axes.lines[0].set_marker(str(self.comboBox_markers.currentText())) self.canvas.draw() except Exception as error: self.ui.update_outputText(error) def change_legendLOC(self): try: new = self.lineEdit_legendLOC.text() if new != "": new = int(self.lineEdit_legendLOC.text()) if new>=1 and new<=10: self.axes.legend(loc=new) self.canvas.draw() except Exception as error: self.ui.update_outputText(error) def change_legendFS(self): try: new = self.lineEdit_legendFS.text() if new != "": new = int(self.lineEdit_legendFS.text()) self.axes.legend(fontsize=new) self.canvas.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_dpi(self): try: new = int(self.lineEdit_dpi.text()) if new>=100 and new<=500: self.dpi = new self.fig.set_dpi(new) self.update_draw() except: pass def change_fsize(self): try: self.fsize = int(self.lineEdit_fsize.text()) self.change_title() self.change_labelx() self.change_labely() self.update_draw() ##print(self.fsize) except Exception as error: pass #self.ui.update_outputText(error) def change_grid(self): try: if self.checkBox_grid.checkState(): self.axes.grid(True) self.canvas.draw() else: self.axes.grid(False) self.canvas.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_legend(self): try: if self.checkBox_legend.checkState(): self.axes.legend().set_visible(True) self.canvas.draw() else: self.axes.legend().set_visible(False) self.canvas.draw() except Exception as error: self.ui.update_outputText(error) def change_xlim(self): try: temp_txt = self.lineEdit_xlim.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0]=="(" and temp_txt[-1]==")": actual="" for i in range(1,Ntxt-1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i==Ntxt-2: actual+=temp_txt[i] numList.append(float(actual)) else: actual+=temp_txt[i] self.axes.set_xlim(numList[0],numList[1]) except Exception as error: pass #self.ui.update_outputText(error) def change_ylim(self): try: temp_txt = self.lineEdit_ylim.text() Ntxt = len(temp_txt) numList = [] if temp_txt[0]=="(" and temp_txt[-1]==")": actual="" for i in range(1,Ntxt-1): if temp_txt[i] == ",": numList.append(float(actual)) actual = "" elif i==Ntxt-2: actual+=temp_txt[i] numList.append(float(actual)) else: actual+=temp_txt[i] self.axes.set_ylim(numList[0],numList[1]) self.canvas.draw() except Exception as error: pass #self.ui.update_outputText(error) def change_title(self): try: self.axes.set_title(self.lineEdit_title.text(),fontsize = self.fsize) self.canvas.draw() except: pass def change_labelx(self): if self.lineEdit_xLabel.text()=="": self.axes.set_xlabel("") else: try: self.axes.set_xlabel(self.lineEdit_xLabel.text(),fontsize = self.fsize) self.canvas.draw() except Exception as error: self.ui.update_outputText(str(error)) def change_labely(self): if self.lineEdit_yLabel.text()=="": self.axes.set_ylabel("") else: try: self.axes.set_ylabel(self.lineEdit_yLabel.text(),fontsize = self.fsize) self.canvas.draw() except: pass
class matplotsink(wx.Panel): def __init__(self, parent, title, queue, gsz, zoom): wx.Panel.__init__(self, parent, wx.SIMPLE_BORDER) self.gsz = gsz self.parent = parent self.title = title self.q = queue self.zoom = zoom self.paused = False # self.create_menu() # self.create_status_bar() self.create_main_panel() def create_menu(self): self.menubar = wx.MenuBar() menu_file = wx.Menu() m_expt = menu_file.Append(-1, "&Save plot\tCtrl-S", "Save plot to file") self.Bind(wx.EVT_MENU, self.on_save_plot, m_expt) menu_file.AppendSeparator() m_exit = menu_file.Append(-1, "E&xit\tCtrl-X", "Exit") self.Bind(wx.EVT_MENU, self.on_exit, m_exit) self.menubar.Append(menu_file, "&File") self.SetMenuBar(self.menubar) def create_main_panel(self): self.panel = self self.init_plot() self.canvas = FigCanvas(self.panel, -1, self.fig) self.scroll_range = 400 self.canvas.SetScrollbar(wx.HORIZONTAL, 0, 5, self.scroll_range) self.canvas.Bind(wx.EVT_SCROLLWIN, self.OnScrollEvt) self.pause_button = wx.Button(self.panel, -1, "Pause") self.Bind(wx.EVT_BUTTON, self.on_pause_button, self.pause_button) self.Bind(wx.EVT_UPDATE_UI, self.on_update_pause_button, self.pause_button) self.cb_grid = wx.CheckBox(self.panel, -1, "Show Grid", style=wx.ALIGN_RIGHT) self.Bind(wx.EVT_CHECKBOX, self.on_cb_grid, self.cb_grid) self.cb_grid.SetValue(True) self.cb_xlab = wx.CheckBox(self.panel, -1, "Show X labels", style=wx.ALIGN_RIGHT) self.Bind(wx.EVT_CHECKBOX, self.on_cb_xlab, self.cb_xlab) self.cb_xlab.SetValue(True) self.hbox1 = wx.BoxSizer(wx.HORIZONTAL) self.hbox1.Add(self.pause_button, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.hbox1.AddSpacer(20) self.hbox1.Add(self.cb_grid, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.hbox1.AddSpacer(10) self.hbox1.Add(self.cb_xlab, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.vbox = wx.BoxSizer(wx.VERTICAL) self.vbox.Add(self.canvas, 1, flag=wx.LEFT | wx.TOP | wx.GROW) self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_LEFT | wx.TOP) self.panel.SetSizer(self.vbox) self.vbox.Fit(self) self.ani = animation.FuncAnimation(self.fig, self.draw_plot, interval=100) def OnScrollEvt(self, event): self.i_start = event.GetPosition() self.i_end = self.i_window + event.GetPosition() self.draw_plot(0) def create_status_bar(self): self.statusbar = self.CreateStatusBar() def draw_test(self, event): self.xar = np.arange(len(self.q.queue)) self.yar = np.array(self.q.queue) self.axes.plot(self.xar, self.yar) def init_plot(self): self.dpi = 100 self.fig = Figure((3.0, 3.0), dpi=self.dpi) self.fig.set_size_inches(7.0, 4.0) self.fig.set_dpi(self.dpi) self.axes = self.fig.add_subplot(111) self.axes.set_axis_bgcolor('black') self.axes.set_title(self.title, size=12) pylab.setp(self.axes.get_xticklabels(), fontsize=8) pylab.setp(self.axes.get_yticklabels(), fontsize=8) self.i_window = self.gsz self.i_start = 0 self.i_end = self.i_start + self.i_window # plot the data as a line series, and save the reference # to the plotted line series # self.plot_data = self.axes.plot( [], linewidth=1, color=(1, 1, 0), )[0] def draw_plot(self, event): """ Redraws the plot """ if len(list(self.q.queue)) > 1 and not self.paused: if self.zoom: xmax = len(list( self.q.queue)) if len(list(self.q.queue)) > 50 else 50 xmin = xmax - 50 # for ymin and ymax, find the minimal and maximal values # in the data set and add a mininal margin. # # note that it's easy to change this scheme to the # minimal/maximal value in the current display, and not # the whole data set. # ymin = round(min(list(self.q.queue)), 0) - 1 ymax = round(max(list(self.q.queue)), 0) + 1 self.axes.set_xbound(lower=xmin, upper=xmax) self.axes.set_ybound(lower=ymin, upper=ymax) # anecdote: axes.grid assumes b=True if any other flag is # given even if b is set to False. # so just passing the flag into the first statement won't # work. # if self.cb_grid.IsChecked(): self.axes.grid(True, color='gray') else: self.axes.grid(False) # Using setp here is convenient, because get_xticklabels # returns a list over which one needs to explicitly # iterate, and setp already handles this. # pylab.setp(self.axes.get_xticklabels(), visible=self.cb_xlab.IsChecked()) self.plot_data.set_xdata(np.arange(len(list(self.q.queue)))) self.plot_data.set_ydata(np.array(list(self.q.queue))) self.canvas.draw() else: if self.cb_grid.IsChecked(): self.axes.grid(True, color='gray') else: self.axes.grid(False) # Using setp here is convenient, because get_xticklabels # returns a list over which one needs to explicitly # iterate, and setp already handles this. pylab.setp(self.axes.get_xticklabels(), visible=self.cb_xlab.IsChecked()) self.plot_data.set_xdata( np.arange(len(list( self.q.queue)))[self.i_start:self.i_end]) self.plot_data.set_ydata( np.array(list(self.q.queue))[self.i_start:self.i_end]) self.axes.set_xlim( min( np.arange(len(list( self.q.queue)))[self.i_start:self.i_end]), max( np.arange(len(list( self.q.queue)))[self.i_start:self.i_end])) # if self.zoom: self.axes.set_ylim(min(np.array(list(self.q.queue))), max(np.array(list(self.q.queue)))) self.canvas.draw() def on_pause_button(self, event): self.paused = not self.paused def on_update_pause_button(self, event): label = "Resume" if self.paused else "Pause" self.pause_button.SetLabel(label) def on_cb_grid(self, event): self.draw_plot(0) def on_cb_xlab(self, event): self.draw_plot(0) def on_save_plot(self, event): file_choices = "PNG (*.png)|*.png" dlg = wx.FileDialog(self, message="Save plot as...", defaultDir=os.getcwd(), defaultFile="plot.png", wildcard=file_choices, style=wx.SAVE) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() self.canvas.print_figure(path, dpi=self.dpi) self.flash_status_message("Saved to %s" % path) def on_redraw_timer(self, event): # if paused do not add data, but still redraw the plot # (to respond to scale modifications, grid change, etc.) # if not self.paused: self.data += self.datagen.next() self.draw_plot(0) def on_exit(self, event): self.Destroy() def flash_status_message(self, msg, flash_len_ms=1500): self.statusbar.SetStatusText(msg) self.timeroff = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.on_flash_status_off, self.timeroff) self.timeroff.Start(flash_len_ms, oneShot=True) def on_flash_status_off(self, event): self.statusbar.SetStatusText('')
class PlotSettings(object): """ The PlotSettings-class initializes the default values for the plot. The class can return those values to the main window and the plot-settings dialog. The plot-settings dialog will call the instance of this class to change the different settings. This class also stores the figure and can return different subplot-layouts. This class also stores the normal and inverse transformations of the stereonet, and will return the correct one for either the Schmidt- or Wulff-Net. """ def __init__(self, testing): """ Initalizes the default values, colors and the matplotlib-figure. Initializes and stores the default settings. Initializes the matplotlib-figure, a folder-icon for the group-layers of the layer-view. """ self.folder_icon = Gtk.IconTheme.get_default().load_icon( "folder", 16, 0) self.props = OrderedDict( sorted({ "draw_grid": True, "equal_area_projection": True, "minor_grid_spacing": 2, "major_grid_spacing": 10, "grid_cutoff_lat": 80, "show_north": True, "show_cross": True, "pixel_density": 75, "grid_linestyle": "--", "grid_color": "#787878", "grid_width": 0.4, "draw_legend": True, "canvas_color": "#bfbfbf", "highlight": False }.items())) self.night_mode = False self.fig = Figure(dpi=self.props["pixel_density"]) if testing == False: try: self.g_settings = Gio.Settings.new("org.gtk.innstereo") self.get_defaults() except: pass def get_defaults(self): """ Gets the defaults from the Gio.Settings. """ self.props["draw_legend"] = self.g_settings.get_boolean("show-legend") self.props["draw_grid"] = self.g_settings.get_boolean("draw-grid") self.props["equal_area_projection"] = self.g_settings.get_boolean( "stereonet-projection") self.props["show_cross"] = self.g_settings.get_boolean("center-cross") self.night_mode = self.g_settings.get_boolean("night-mode") self.props["pixel_density"] = self.g_settings.get_value( "pixel-density").get_int32() self.props["highlight"] = self.g_settings.get_boolean("highlight-mode") def get_fig(self): """ Returns the Matplotlib-Figure. Returns the figure that is stored by this class. The MainWindow class calls this function once during initialization to add the figure to the FigureCanvas. """ return self.fig def get_inverse_transform(self): """ Returns the inverse transform for the current stereonet projection. If the projection is equal are (True) the function returns the InvertedLambertTransform- or else the InvertedSterreographicTransform-class. """ if self.props["equal_area_projection"] is True: return mplstereonet.stereonet_transforms.\ InvertedLambertTransform(0, 0, self.props["pixel_density"]) else: return mplstereonet.stereonet_transforms.\ InvertedStereographicTransform(0, 0, self.props["pixel_density"]) def get_transform(self): """ Returns the normal transform for the current stereonet projection. If the projection is equal are (True) the function returns the LambertTransform- or else the SterreographicTransform-class. """ if self.props["equal_area_projection"] is True: return mplstereonet.stereonet_transforms.\ LambertTransform(0, 0, self.props["pixel_density"]) else: return mplstereonet.stereonet_transforms.\ StereographicTransform(0, 0, self.props["pixel_density"]) def get_draw_grid_state(self): """ Returns if the grid should be drawn for the stereonet. Returns a boolean. True mean that a grid should be drawn. False means that no grid should be drawn. This method is called by the MainWindow- redraw_plot-method. """ return self.props["draw_grid"] def set_draw_grid_state(self, new_state): """ Sets if the grid should be drawn for the stereonet. Expects a boolean. True mean that a grid should be drawn. False means that no grid should be drawn. This method is called by the LayerProperties-dialog when the setting is changed. """ self.props["draw_grid"] = new_state def get_folder_icon(self): """ Returns the folder icon used for the group-layer pixbuf. Always returns the "folder" icon from the Gtk.IconTheme. The folder will therefore match the desktop-theme set by the user. This method is called by the MainWindow "on_toolbutton_create_group_layer_clicked"- method. """ return self.folder_icon def get_pixel_density(self): """ Returns the pixel density the plot is using. The pixel density is an int and the default value is 75. This method is called by the LayerProperties-dialog so it can display the current value. """ return self.props["pixel_density"] def set_pixel_density(self, new_pixel_density): """ Sets a new pixel density for the plot. Expects an int. This method is called by the LayerProperties-dialog. The new value will be used when the plot redraws when the settings in the dialog are applied. """ self.props["pixel_density"] = new_pixel_density def get_projection(self): """ Returns the projection currently used by the stereonet. Returns one of two strings that MPLStereonet uses to distinguish between the equal-area and equal-angle projection. This method is only called from this class when the view is switched. """ if self.props["equal_area_projection"] is True: return "equal_area_stereonet" else: return "equal_angle_stereonet" def get_projection_state(self): """ Returns the projection state for the stereonet. Returns a boolean. True means that the stereonet should be drawn with equal-area. False mean equal-angle. This method is called by the StereonetProperties-dialog to load the current setting. """ return self.props["equal_area_projection"] def set_projection_state(self, new_state): """ Sets a new projection state. Expects a boolean. True means that the projection will be equal-area, False means equal-angle. This method is called by the StereonetProperties-dialog when a new setting for the projection is applied. """ self.props["equal_area_projection"] = new_state def get_grid_linestyle(self): """ Returns the linestyle of the grid. The linestyle is returned as a string. Default is "--" (dashed). This method is called by the MainWindow "redraw_plot"-method. """ return self.props["grid_linestyle"] def get_grid_color(self): """ Returns the color of the grid. Returns the color as a hex-triplet. The default is "#787878". This method is called by the MainWindow "redraw_plot"-method. """ return self.props["grid_color"] def get_grid_width(self): """ Returns the width of the grid lines. The width of the grid lines is returned as a float or int. The default is "0.4". This method is called by the MainWindow "redraw_plot"-method. """ return self.props["grid_width"] def get_draw_legend(self): """ Returns if the legend should be drawn as a boolean. The returned value is either True if a legend should be drawn, or False if no legend should be drawn. This method is called by the MainWindow "redraw_plot"-method and the StereonetProperties-dialog. """ return self.props["draw_legend"] def set_draw_legend(self, new_state): """ Sets a new state for whether the legend should be drawn. Expects a boolean. True means that the legend should be drawn. False means that no legend should be drawn. This method is called by the StereonetProperties-dialog when a new setting is applied. """ self.props["draw_legend"] = new_state def get_canvas_rgba(self): """ Returns the canvas color as a Gdk.RGBA. This method is called by the StereonetProperties-dialog to apply the current canvas color to the ColorButton. """ rgba = Gdk.RGBA() rgba.parse(self.props["canvas_color"]) return rgba.to_color() def set_canvas_color(self, new_color): """ Sets a new canvas color. Expects a hex-triplet string (e.g. "#bfbfbf"). This method is called by the StereonetProperties-dialog when a new color is applied to the canvas. """ self.props["canvas_color"] = new_color def get_stereonet(self): """ Resets the figure and returns the stereonet axis. When the view in the main window is changed to only stereoent. The figure is reset. Then the current settings are applied and one subplot for the stereonet is created. This method is called when the MainWindow "__init__"-method and the "redraw_plot"-method. """ self.fig.clf() self.fig.patch.set_facecolor(self.props["canvas_color"]) self.fig.set_dpi(self.props["pixel_density"]) gridspec = GridSpec(2, 3) sp_stereo = gridspec.new_subplotspec((0, 0), rowspan=2, colspan=2) sp_cbar = gridspec.new_subplotspec((1, 2), rowspan=1, colspan=1) ax_stereo = self.fig.add_subplot(sp_stereo, projection=self.get_projection()) ax_cbar = self.fig.add_subplot(sp_cbar) ax_cbar.axis("off") ax_cbar.set_aspect(8) return ax_stereo, ax_cbar def get_stereo_rose(self): """ Resets the figure and returns a stereonet and rose diagram axis. When the view in the main window is changed to stereonet and rose diagram, the figure is reset. The current settings are applied and two subplots for the stereonet and rose diagram are created. The axis of the stereonet and rose diagram are returned. This method is called by the MainWindow "redraw_plot"-method. """ self.fig.clf() self.fig.patch.set_facecolor(self.props["canvas_color"]) self.fig.set_dpi(self.props["pixel_density"]) gridspec = GridSpec(2, 5) sp_stereo = gridspec.new_subplotspec((0, 0), rowspan=2, colspan=2) sp_cbar = gridspec.new_subplotspec((1, 2), rowspan=1, colspan=1) sp_rose = gridspec.new_subplotspec((0, 3), rowspan=2, colspan=2) ax_stereo = self.fig.add_subplot(sp_stereo, projection=self.get_projection()) ax_rose = self.fig.add_subplot(sp_rose, projection="northpolar") ax_cbar = self.fig.add_subplot(sp_cbar) ax_cbar.axis("off") ax_cbar.set_aspect(8) return ax_stereo, ax_rose, ax_cbar def get_stereo_two_rose(self): """ Resets the figure and returns a stereonet two rose diagrams axis. When the view in the main window is changed to this setting, this function is called and sets up a plot with a stereonet and two rose diagram axis. One axis is for azimuth, the other one for dip. """ self.fig.clf() self.fig.patch.set_facecolor(self.props["canvas_color"]) self.fig.set_dpi(self.props["pixel_density"]) gridspec = GridSpec(2, 4) sp_stereo = gridspec.new_subplotspec((0, 0), rowspan=2, colspan=2) sp_cbar = gridspec.new_subplotspec((1, 2), rowspan=1, colspan=1) sp_rose = gridspec.new_subplotspec((0, 3), rowspan=1, colspan=1) sp_drose = gridspec.new_subplotspec((1, 3), rowspan=1, colspan=1) ax_stereo = self.fig.add_subplot(sp_stereo, projection=self.get_projection()) ax_rose = self.fig.add_subplot(sp_rose, projection="northpolar") ax_drose = self.fig.add_subplot(sp_drose, projection="dippolar") ax_cbar = self.fig.add_subplot(sp_cbar) ax_cbar.axis("off") ax_cbar.set_aspect(8) return ax_stereo, ax_rose, ax_drose, ax_cbar def get_rose_diagram(self): """ Resets the figure and returns the rose diagram axis. When the view in the main window is changed to rose-diagram-only the figure is reset. The current settings are applied and one subplot for the rose diagram is created. The axis of the rose-diagram is returned. This method is called by the MainWindow "redraw_plot"-method. """ self.fig.clf() self.fig.patch.set_facecolor(self.props["canvas_color"]) self.fig.set_dpi(self.props["pixel_density"]) gridspec = GridSpec(1, 1) sp_rose = gridspec.new_subplotspec((0, 0)) ax_rose = self.fig.add_subplot(sp_rose, projection="northpolar") return ax_rose def get_pt_view(self): """ Resets the canvas and returns the 3 axis of the paleostress view. When the view in the main window is changed to paleostress the figure is reset. The current settings are applied and 3 subplots are created. The 3 axis of the subplots are returned. This method is called by the MainWindow "redraw_plot"-method when the view has been changed. """ self.fig.clf() self.fig.patch.set_facecolor(self.props["canvas_color"]) self.fig.set_dpi(self.props["pixel_density"]) gridspec = GridSpec(2, 5) sp_stereo = gridspec.new_subplotspec((0, 0), colspan=3, rowspan=2) sp_fluc = gridspec.new_subplotspec((0, 3), colspan=2) sp_mohr = gridspec.new_subplotspec((1, 3), colspan=2) ax_stereo = self.fig.add_subplot(sp_stereo, projection=self.get_projection()) ax_fluc = self.fig.add_subplot(sp_fluc, aspect="equal") ax_mohr = self.fig.add_subplot(sp_mohr, aspect="equal") return ax_stereo, ax_fluc, ax_mohr def get_show_north(self): """ Returns if the stereonet should show the North symbol or degrees Returns True if the North symbol should be drawn (the default value), or False in which case numbers will be drawn for different degrees. """ return self.props["show_north"] def set_show_north(self, new_state): """ Sets a new state for whether the North symbol should be drawn. Expects a boolean. True means the North symbol will be drawn. False means that the stereonet will show different degrees along the outside. """ self.props["show_north"] = new_state def get_show_cross(self): """ Returns if the stereonet should draw a cross at the center. Returns True if the cross should be drawn (the default value) or False if the cross should not be drawn. """ return self.props["show_cross"] def set_show_cross(self, new_state): """ Sets a new state for whether the center cross should be drawn. Expects a boolean. True means the center cross will be drawn. False means it will not be drawn. """ self.props["show_cross"] = new_state def get_properties(self): """ Returns the current plot properties in a dictionary. The plot properties are stored in a dictionary. For loading and saving the dict is requested by the main window. """ return self.props def set_properties(self, new_props): """ Sets the properties to those passed in a dictionary. Loading a file will also set the plot properties to the saved state. The properties are appllied to this plot. """ for key in new_props: self.props[key] = new_props[key] def get_highlight(self): """ Gets the state of selection highlighting. Default is False. """ return self.props["highlight"] def set_highlight(self, new_state): """ Sets a new state for highlighting. Expects a boolean. """ self.props["highlight"] = new_state def get_night_mode(self): """ Gets the state of the night mode. Default is False, which is usually a lighter interface color. """ return self.night_mode def set_night_mode(self, new_state): """ Sets a new state for the night mode. Expects a boolean. """ self.night_mode = new_state
class Graph(object): """ Class for matplotlib graphs, i.e. for popups, krw graphs - Calculates correct size - Horizontal axis = dates - Vertical axis = user defined - Outputs httpresponse for png """ def __init__(self, start_date, end_date, width=None, height=None, today=datetime.datetime.now(), restrict_to_month=None, tz=None): self.start_date = start_date self.end_date = end_date self.today = today self.restrict_to_month = restrict_to_month self.tz = tz self.figure = Figure() if width is None or not width: width = 380.0 if height is None or not height: height = 240.0 self.width = float(width) self.height = float(height) self.figure.set_size_inches((_inches_from_pixels(self.width), _inches_from_pixels(self.height))) self.figure.set_dpi(SCREEN_DPI) # Figure color self.figure.set_facecolor('white') # Axes and legend location: full width is "1". self.legend_width = 0.08 # ^^^ No legend by default, but we *do* allow a little space to the # right of the graph to prevent the rightmost label from being cut off # (at least, in a reasonable percentage of the cases). self.left_label_width = LEFT_LABEL_WIDTH / self.width self.bottom_axis_location = BOTTOM_LINE_HEIGHT / self.height self.x_label_height = 0.08 self.legend_on_bottom_height = 0.0 self.axes = self.figure.add_subplot(111) self.axes.grid(True) # We track whether y_lim has been set manually self._y_min_set_manually = False self._y_max_set_manually = False # Fixup_axes in init, so axes can be customised (for example set_ylim). self.fixup_axes() #deze kan je zelf zetten self.ax2 = None def set_ylim(self, y_min, y_max, min_manual=False, max_manual=False): logger.debug('set_ylim y_min = %f y_max = %f' % (y_min, y_max)) self.axes.set_ylim(y_min, y_max) self._y_min_set_manually = min_manual self._y_max_set_manually = max_manual def add_today(self): # Show line for today. self.axes.axvline(self.today, color='orange', lw=1, ls='--') def set_ylim_margin(self, top=0.1, bottom=0.0): """Adjust y-margin of axes. The standard margin is sometimes zero. This method sets the margin based on already present data in the visible part of the plot, so call it after plotting and before http_png(). Note that it is assumed here that the y-axis is not reversed. From matplotlib 1.0 on there is a set_ymargin method like this already. """ lines = self.axes.lines arrays = [numpy.array(l.get_data()) for l in lines] # axhline and axvline give trouble - remove short lines from list big_arrays = [a for a in arrays if a.size > 4] if len(big_arrays) > 0: data = numpy.concatenate(big_arrays, axis=1) if len(data[0]) > 0: # Datatimes from database may have timezone information. # In that case, start_date and end_date cannot be naive. # Assume all datetimes do have the same timezone, so we # can do the comparison. start_date_tz =\ self.start_date.replace(tzinfo=data[0][0].tzinfo) end_date_tz =\ self.end_date.replace(tzinfo=data[0][0].tzinfo) index_in_daterange = ((data[0] < end_date_tz) & (data[0] > start_date_tz)) # Calculate correct y_min and y_max, but use old if they have # already been set manually if index_in_daterange.any(): data_low = numpy.min(data[1, index_in_daterange]) data_high = numpy.max(data[1, index_in_daterange]) data_span = data_high - data_low view_low = data_low - data_span * bottom view_high = data_high + data_span * top # Don't zoom in too much if values are essentially the same # and differ only in noise. Values shown at the Y-axis should # only have 2 decimals. view_low = math.floor(view_low * 40) / 40 view_high = math.ceil(view_high * 40) / 40 while (view_high - view_low) < 0.03: # Difference is only 0.025 (or 0!), differences of # smaller than 0.01 show up at the y-axis. view_low -= 1.0 / 80 view_high += 1.0 / 80 if self._y_min_set_manually: view_low, _ = self.axes.get_ylim() if self._y_max_set_manually: _, view_high = self.axes.get_ylim() self.axes.set_ylim(view_low, view_high) return None def suptitle(self, title): self.figure.suptitle(title, x=self.left_label_width, horizontalalignment='left') def set_xlabel(self, xlabel): self.axes.set_xlabel(xlabel) self.x_label_height = BOTTOM_LINE_HEIGHT / self.height def fixup_axes(self, second=False): """Fix up the axes by limiting the amount of items.""" axes_to_change = self.axes if second: if self.ax2 is None: return else: axes_to_change = self.ax2 if not self.restrict_to_month: major_locator = LessTicksAutoDateLocator() axes_to_change.xaxis.set_major_locator(major_locator) major_formatter = MultilineAutoDateFormatter( major_locator, axes_to_change, tz=self.tz) axes_to_change.xaxis.set_major_formatter(major_formatter) available_height = (self.height - BOTTOM_LINE_HEIGHT - self.x_label_height - self.legend_on_bottom_height) approximate_lines = int(available_height / (FONT_SIZE * 2.5)) logger.info("#lines: %s", approximate_lines) max_number_of_ticks = approximate_lines if max_number_of_ticks < 2: max_number_of_ticks = 2 locator = MaxNLocator(nbins=max_number_of_ticks - 1) if not second: axes_to_change.yaxis.set_major_locator(locator) # ^^^ [Arjan:] Turns out default amount of ticks wasn't that bad. # [Reinout:] I keep hearing complaints so I've re-enabled it. axes_to_change.yaxis.set_major_formatter( ScalarFormatter(useOffset=False)) self.axes.set_ylabel(self.axes.get_ylabel(), size='x-large') def legend_space(self): """Reserve space for legend (on the right side). even when there is no legend displayed """ self.legend_width = LEGEND_WIDTH / self.width def legend(self, handles=None, labels=None, ncol=1, force_legend_below=False): """ Displays legend. Default is right side, but if the width is too small, it will display under the graph. Handles is list of matplotlib objects (e.g. matplotlib.lines.Line2D) Labels is list of strings """ # experimental update: do not reserve space for legend by # default, just place over graph. use legend_space to manually # add space if handles is None and labels is None: handles, labels = self.axes.get_legend_handles_labels() if handles and labels: # Determine 'small' or 'large' # if self.width < 500 or force_legend_below: if force_legend_below: # TODO: Maybe remove this feature? Needs tweaking. The # legend is still on top of the graph, while the graph # reserves room for the legend below. legend_loc = 4 # lower right # approximation of legend height self.legend_on_bottom_height = min( (len(labels) / ncol + 2) * BOTTOM_LINE_HEIGHT / self.height, 0.5) else: legend_loc = 1 # Upper right' # For width 464 (empty space 150px assumed), we have: # <= 40 = medium # > 40 = small # > 50 = x-small # > 65 = xx-small # Fixes #3095 font_len = max([len(label) for label in labels]) font_size = 'medium' # 'medium' if font_len > 40 * ((self.width - 150) / 314.0): font_size = 'small' if font_len > 50 * ((self.width - 150) / 314.0): font_size = 'x-small' if font_len > 65 * ((self.width - 150) / 314.0): font_size = 'xx-small' prop = {'size': font_size} return self.axes.legend( handles, labels, bbox_to_anchor=(1 - self.legend_width, 0, # self.bottom_axis_location self.legend_width, # 1 = Upper right of above bbox. Use 0 for # 'best' 1), prop=prop, loc=legend_loc, ncol=ncol, fancybox=True, shadow=True,) #legend.set_size('medium') # TODO: get rid of the border around the legend. # to get rid of the border: graph.axes.legend_.draw_frame(False) def init_second_axes(self): """Init second axes """ self.ax2 = self.axes.twinx() self.fixup_axes(second=True) def http_png(self): """Output plot to png. Also calculates size of plot and put 'now' line.""" axes_left = self.left_label_width axes_bottom = (self.bottom_axis_location + self.x_label_height + self.legend_on_bottom_height) axes_width = 1 - self.legend_width - self.left_label_width axes_height = (1 - 2 * self.bottom_axis_location - self.x_label_height - self.legend_on_bottom_height) self.axes.set_position((axes_left, axes_bottom, axes_width, axes_height)) if self.ax2 is not None: self.ax2.set_position((axes_left, axes_bottom, axes_width, axes_height)) # Set date range # Somehow, the range cannot be set in __init__ if not self.restrict_to_month: self.axes.set_xlim(date2num((self.start_date, self.end_date))) try: self.set_ylim_margin(top=0.1, bottom=0.0) except: pass # Because of the use of setlocale to nl_NL, dutch monthnames can no # Longer be top-aligned. if locale.getlocale(locale.LC_TIME) == ('nl_NL', 'UTF8'): for l in self.axes.get_xticklabels(): l.set_verticalalignment('baseline') l.set_position((0, -0.05)) canvas = FigureCanvas(self.figure) response = HttpResponse(content_type='image/png') canvas.print_png(response) return response def render(self): ''' more general alias for http_png(), to support FlotGraph should return a valid HttpResponse ''' return self.http_png()
class OldGraph(object): """ Class for matplotlib graphs, i.e. for popups, krw graphs - calculates correct size - horizontal axis = dates - vertical axis = user defined - outputs httpresponse for png """ def __init__(self, start_date, end_date, width=None, height=None, today=datetime.datetime.now(), restrict_to_month=None): self.restrict_to_month = restrict_to_month self.start_date = start_date self.end_date = end_date self.today = today self.figure = Figure() if width is None or not width: width = 380.0 if height is None or not height: height = 250.0 self.width = float(width) self.height = float(height) self.figure.set_size_inches((_inches_from_pixels(self.width), _inches_from_pixels(self.height))) self.figure.set_dpi(SCREEN_DPI) # Figure color self.figure.set_facecolor('white') # Axes and legend location: full width is "1". self.legend_width = 0.08 # ^^^ No legend by default, but we *do* allow a little space to the # right of the graph to prevent the rightmost label from being cut off # (at least, in a reasonable percentage of the cases). self.left_label_width = LEFT_LABEL_WIDTH / self.width self.bottom_axis_location = BOTTOM_LINE_HEIGHT / self.height self.x_label_height = 0.08 self.legend_on_bottom_height = 0.0 self.axes = self.figure.add_subplot(111) self.axes.grid(True) # Fixup_axes in init, so axes can be customised (for example set_ylim). self.fixup_axes() #deze kan je zelf zetten self.ax2 = None def add_today(self): # Show line for today. self.axes.axvline(self.today, color='orange', lw=1, ls='--') def set_ylim_margin(self, top=0.1, bottom=0.0): """Adjust y-margin of axes. The standard margin is sometimes zero. This method sets the margin based on already present data in the visible part of the plot, so call it after plotting and before http_png(). Note that it is assumed here that the y-axis is not reversed. From matplotlib 1.0 on there is a set_ymargin method like this already.""" lines = self.axes.lines arrays = [numpy.array(l.get_data()) for l in lines] # axhline and axvline give trouble - remove short lines from list big_arrays = [a for a in arrays if a.size > 4] if len(big_arrays) > 0: data = numpy.concatenate(big_arrays, axis=1) if len(data[0]) > 0: # Datatimes from database may have timezone information. # In that case, start_date and end_date cannot be naive. # Assume all datetimes do have the same timezone, so we # can do the comparison. start_date_tz =\ self.start_date.replace(tzinfo=data[0][0].tzinfo) end_date_tz =\ self.end_date.replace(tzinfo=data[0][0].tzinfo) index_in_daterange = ((data[0] < end_date_tz) & (data[0] > start_date_tz)) if index_in_daterange.any(): data_low = numpy.min(data[1, index_in_daterange]) data_high = numpy.max(data[1, index_in_daterange]) data_span = data_high - data_low view_low = data_low - data_span * bottom view_high = data_high + data_span * top self.axes.set_ylim(view_low, view_high) return None def suptitle(self, title): self.figure.suptitle(title, x=self.left_label_width, horizontalalignment='left') def set_xlabel(self, xlabel): self.axes.set_xlabel(xlabel) self.x_label_height = BOTTOM_LINE_HEIGHT / self.height def fixup_axes(self, second=False): """Fix up the axes by limiting the amount of items.""" axes_to_change = self.axes if second: if self.ax2 is None: return else: axes_to_change = self.ax2 # available_width = self.width - LEFT_LABEL_WIDTH - LEGEND_WIDTH # approximate_characters = int(available_width / (FONT_SIZE / 2)) # max_number_of_ticks = approximate_characters // 20 # if max_number_of_ticks < 2: # max_number_of_ticks = 2 if not self.restrict_to_month: major_locator = LessTicksAutoDateLocator() axes_to_change.xaxis.set_major_locator(major_locator) major_formatter = MultilineAutoDateFormatter( major_locator, axes_to_change) axes_to_change.xaxis.set_major_formatter(major_formatter) available_height = (self.height - BOTTOM_LINE_HEIGHT - self.x_label_height - self.legend_on_bottom_height) approximate_lines = int(available_height / (FONT_SIZE * 1.5)) max_number_of_ticks = approximate_lines if max_number_of_ticks < 2: max_number_of_ticks = 2 locator = MaxNLocator(nbins=max_number_of_ticks - 1) if not second: axes_to_change.yaxis.set_major_locator(locator) axes_to_change.yaxis.set_major_formatter( ScalarFormatter(useOffset=False)) def legend_space(self): """reserve space for legend (on the right side). even when there is no legend displayed""" self.legend_width = LEGEND_WIDTH / self.width def legend(self, handles=None, labels=None, ncol=1): """ Displays legend. Default is right side, but if the width is too small, it will display under the graph. handles is list of matplotlib objects (e.g. matplotlib.lines.Line2D) labels is list of strings """ # experimental update: do not reserve space for legend by # default, just place over graph. use legend_space to manually # add space if handles is None and labels is None: handles, labels = self.axes.get_legend_handles_labels() if handles and labels: # Determine 'small' or 'large' if self.width < 500: legend_loc = 4 # lower right # approximation of legend height self.legend_on_bottom_height = min( (len(labels) / ncol + 2) * BOTTOM_LINE_HEIGHT / self.height, 0.5) else: legend_loc = 1 # Upper right' return self.figure.legend( handles, labels, bbox_to_anchor=(1 - self.legend_width, 0, # self.bottom_axis_location self.legend_width, # 1 = Upper right of above bbox. Use 0 for # 'best' 1), loc=legend_loc, ncol=ncol, fancybox=True, shadow=True,) #legend.set_size('medium') # TODO: get rid of the border around the legend. def init_second_axes(self): """ init second axes """ self.ax2 = self.axes.twinx() self.fixup_axes(second=True) def http_png(self): """Output plot to png. Also calculates size of plot and put 'now' line.""" axes_left = self.left_label_width axes_bottom = (self.bottom_axis_location + self.x_label_height + self.legend_on_bottom_height) axes_width = 1 - self.legend_width - self.left_label_width axes_height = (1 - 2 * self.bottom_axis_location - self.x_label_height - self.legend_on_bottom_height) self.axes.set_position((axes_left, axes_bottom, axes_width, axes_height)) if self.ax2 is not None: self.ax2.set_position((axes_left, axes_bottom, axes_width, axes_height)) # Set date range # Somehow, the range cannot be set in __init__ if not self.restrict_to_month: self.axes.set_xlim(date2num((self.start_date, self.end_date))) try: self.set_ylim_margin(top=0.1, bottom=0.0) except: pass canvas = FigureCanvas(self.figure) response = HttpResponse(content_type='image/png') canvas.print_png(response) return response
def calculateObstacleMap(self, resolution=0.1, level=0, zlim=(0.15, 1.50), xlim=None, ylim=None, roomIds=None, layoutOnly=False): figSize = (10, 10) fig = Figure(figsize=figSize, dpi=100, frameon=False) canvas = FigureCanvas(fig) ax = fig.add_subplot(111, aspect='equal') fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0) ax.axis('off') ax.set_aspect('equal') floorZ = self._getFloorReferenceZ(level) zlim = np.array(zlim) + floorZ if roomIds is not None: layoutModels = [] objModels = [] for roomId in roomIds: layoutModels.extend([ model for model in self.scene.scene.findAllMatches( '**/level-%d/room-%s/layouts/object-*/+ModelNode' % (level, roomId)) ]) objModels.extend([ model for model in self.scene.scene.findAllMatches( '**/level-%d/room-%s/objects/object-*/+ModelNode' % (level, roomId)) ]) else: layoutModels = [ model for model in self.scene.scene.findAllMatches( '**/level-%d/**/layouts/object-*/+ModelNode' % level) ] objModels = [ model for model in self.scene.scene.findAllMatches( '**/level-%d/**/objects/object-*/+ModelNode' % level) ] # Loop for all walls in the scene: for model in layoutModels: modelId = model.getNetTag('model-id') if not modelId.endswith('w'): continue addModelTriangles(ax, model, zlim=zlim) # Loop for all doors in the scene: for model in objModels: modelId = model.getNetTag('model-id') if self._isDoor(modelId): if modelId in self.openedStandardDoorModelIds: # Shift the model a little more to the wall transform = TransformState.makePos( LVector3f(0.0, -0.10, 0.0)) # Reduce width by 25% not to mess with close corner walls transform = transform.compose( TransformState.makeScale(LVector3f(0.75, 1.0, 1.0))) elif modelId in self.openedThinDoorModelIds: # Rescale the model to be able to cover the entire depth of walls # Reduce width by 25% not to mess with close corner walls transform = TransformState.makeScale( LVector3f(0.75, 4.0, 1.0)) elif modelId in self.openedGarageDoorModelIds: # Shift the model a little more to the wall transform = TransformState.makePos( LVector3f(0.0, 0.10, 0.0)) # Reduce width by 10% not to mess with close corner walls transform = transform.compose( TransformState.makeScale(LVector3f(0.90, 1.0, 1.0))) else: raise Exception('Unsupported model id: %s' % (modelId)) # TODO: would be more efficient if it needed not copying the # model parentNp = NodePath('tmp-objnode') parentNp.setTransform(model.getParent().getNetTransform()) midNp = parentNp.attachNewNode('tmp-transform') midNp.setTransform(transform) model = model.copyTo(midNp) approxModel = getApproximationForModel(model, mode='box') addModelTriangles(ax, approxModel, invert=True, zlim=zlim) approxModel.removeNode() midNp.removeNode() parentNp.removeNode() # Loop for all objects in the scene: if not layoutOnly: for model in objModels: modelId = model.getNetTag('model-id') if self._isDoor(modelId): continue approxModel = getApproximationForModel(model, mode='box') addModelTriangles(ax, approxModel, zlim=zlim) if xlim is not None and ylim is not None: ax.set_xlim(xlim) ax.set_ylim(ylim) else: ax.autoscale(True) set_axes_equal(ax) xlim, ylim = ax.get_xlim(), ax.get_ylim() xrange = xlim[1] - xlim[0] yrange = ylim[1] - ylim[0] assert np.allclose(xrange, yrange, atol=1e-6) dpi = (xrange / resolution) / figSize[0] fig.set_dpi(dpi) obstacleMap = canvas2image(canvas) plt.close(fig) # RGB to binary obstacleMap = np.round(np.mean(obstacleMap[:, :], axis=-1)) # NOTE: inverse image so that obstacle areas are shown in white obstacleMap = 1.0 - obstacleMap return obstacleMap, xlim, ylim
class AbstractMatplotlibRenderer(_RendererBackend, ABC): """Matplotlib renderer which can use any backend (agg, mplcairo). To pick a backend, subclass and set _canvas_type at the class level. """ _canvas_type: Type["FigureCanvasBase"] = abstract_classvar @staticmethod @abstractmethod def _canvas_to_bytes(canvas: "FigureCanvasBase") -> ByteBuffer: pass def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) dict.__setitem__(matplotlib.rcParams, "lines.antialiased", self.cfg.antialiasing) self._setup_axes(self.wave_nchans) self._artists: List["Artist"] = [] _fig: "Figure" # _axes2d[wave][chan] = Axes # Primary, used to draw oscilloscope lines and gridlines. _axes2d: List[List["Axes"]] # set by set_layout() # _axes_mono[wave] = Axes # Secondary, used for titles and debug plots. _axes_mono: List["Axes"] def _setup_axes(self, wave_nchans: List[int]) -> None: """ Creates a flat array of Matplotlib Axes, with the new layout. Sets up each Axes with correct region limits. """ self.layout = RendererLayout(self.lcfg, wave_nchans) self.layout_mono = RendererLayout(self.lcfg, [1] * self.nplots) if hasattr(self, "_fig"): raise Exception( "I don't currently expect to call _setup_axes() twice") # plt.close(self.fig) cfg = self.cfg self._fig = Figure() self._canvas_type(self._fig) px_inch = PX_INCH / cfg.res_divisor self._fig.set_dpi(px_inch) """ Requirements: - px_inch /= res_divisor (to scale visual elements correctly) - int(set_size_inches * px_inch) == self.w,h - matplotlib uses int instead of round. Who knows why. - round(set_size_inches * px_inch) == self.w,h - just in case matplotlib changes its mind Solution: - (set_size_inches * px_inch) == self.w,h + 0.25 - set_size_inches == (self.w,h + 0.25) / px_inch """ offset = 0.25 self._fig.set_size_inches((self.w + offset) / px_inch, (self.h + offset) / px_inch) real_dims = self._fig.canvas.get_width_height() assert (self.w, self.h) == real_dims, [(self.w, self.h), real_dims] # Setup background self._fig.set_facecolor(cfg.bg_color) # Create Axes (using self.lcfg, wave_nchans) # _axes2d[wave][chan] = Axes self._axes2d = self.layout.arrange(self._axes_factory) """ Adding an axes using the same arguments as a previous axes currently reuses the earlier instance. In a future version, a new instance will always be created and returned. Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance. ax=fig.add_axes(label=) is unused, even if you call ax.legend(). """ # _axes_mono[wave] = Axes self._axes_mono = [] # Returns 2D list of [self.nplots][1]Axes. axes_mono_2d = self.layout_mono.arrange(self._axes_factory, label="mono") for axes_list in axes_mono_2d: (axes, ) = axes_list # type: Axes # List of colors at # https://matplotlib.org/gallery/color/colormap_reference.html # Discussion at https://github.com/matplotlib/matplotlib/issues/10840 cmap: ListedColormap = get_cmap("Accent") colors = cmap.colors axes.set_prop_cycle(color=colors) self._axes_mono.append(axes) # Setup axes for idx, N in enumerate(self.wave_nsamps): wave_axes = self._axes2d[idx] viewport_stride = self.render_strides[idx] * cfg.viewport_width ylim = cfg.viewport_height def scale_axes(ax: "Axes"): xlim = calc_limits(N, viewport_stride) ax.set_xlim(*xlim) ax.set_ylim(-ylim, ylim) scale_axes(self._axes_mono[idx]) for ax in unique_by_id(wave_axes): scale_axes(ax) # Setup midlines (depends on max_x and wave_data) midline_color = cfg.midline_color midline_width = cfg.grid_line_width # Not quite sure if midlines or gridlines draw on top kw = dict(color=midline_color, linewidth=midline_width) if cfg.v_midline: ax.axvline(x=calc_center(viewport_stride), **kw) if cfg.h_midline: ax.axhline(y=0, **kw) self._save_background() transparent = "#00000000" # satisfies RegionFactory def _axes_factory(self, r: RegionSpec, label: str = "") -> "Axes": cfg = self.cfg width = 1 / r.ncol left = r.col / r.ncol assert 0 <= left < 1 height = 1 / r.nrow bottom = (r.nrow - r.row - 1) / r.nrow assert 0 <= bottom < 1 # Disabling xticks/yticks is unnecessary, since we hide Axises. ax = self._fig.add_axes([left, bottom, width, height], xticks=[], yticks=[], label=label) grid_color = cfg.grid_color if grid_color: # Initialize borders # Hide Axises # (drawing them is very slow, and we disable ticks+labels anyway) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) # Background color # ax.patch.set_fill(False) sets _fill=False, # then calls _set_facecolor(...) "alpha = self._alpha if self._fill else 0". # It is no faster than below. ax.set_facecolor(self.transparent) # Set border colors for spine in ax.spines.values(): # type: Spine spine.set_linewidth(cfg.grid_line_width) spine.set_color(grid_color) def hide(key: str): ax.spines[key].set_visible(False) # Hide all axes except bottom-right. hide("top") hide("left") # If bottom of screen, hide bottom. If right of screen, hide right. if r.screen_edges & Edges.Bottom: hide("bottom") if r.screen_edges & Edges.Right: hide("right") # Dim stereo gridlines if cfg.stereo_grid_opacity > 0: dim_color = matplotlib.colors.to_rgba_array(grid_color)[0] dim_color[-1] = cfg.stereo_grid_opacity def dim(key: str): ax.spines[key].set_color(dim_color) else: dim = hide # If not bottom of wave, dim bottom. If not right of wave, dim right. if not r.wave_edges & Edges.Bottom: dim("bottom") if not r.wave_edges & Edges.Right: dim("right") else: ax.set_axis_off() return ax # Public API def add_lines_stereo(self, dummy_datas: List[np.ndarray], strides: List[int]) -> UpdateLines: cfg = self.cfg # Plot lines over background line_width = cfg.line_width # Foreach wave, plot dummy data. lines2d = [] for wave_idx, wave_data in enumerate(dummy_datas): wave_zeros = np.zeros_like(wave_data) wave_axes = self._axes2d[wave_idx] wave_lines = [] xs = calc_xs(len(wave_zeros), strides[wave_idx]) # Foreach chan for chan_idx, chan_zeros in enumerate(wave_zeros.T): ax = wave_axes[chan_idx] line_color = self._line_params[wave_idx].color chan_line: Line2D = ax.plot(xs, chan_zeros, color=line_color, linewidth=line_width)[0] wave_lines.append(chan_line) lines2d.append(wave_lines) self._artists.extend(wave_lines) return lambda datas: self._update_lines_stereo(lines2d, datas) @staticmethod def _update_lines_stereo(lines2d: "List[List[Line2D]]", datas: List[np.ndarray]) -> None: """ Preconditions: - lines2d[wave][chan] = Line2D - datas[wave] = ndarray, [samp][chan] = FLOAT """ nplots = len(lines2d) ndata = len(datas) if nplots != ndata: raise ValueError( f"incorrect data to plot: {nplots} plots but {ndata} dummy_datas" ) # Draw waveform data # Foreach wave for wave_idx, wave_data in enumerate(datas): wave_lines = lines2d[wave_idx] # Foreach chan for chan_idx, chan_data in enumerate(wave_data.T): chan_line = wave_lines[chan_idx] chan_line.set_ydata(chan_data) def _add_xy_line_mono(self, wave_idx: int, xs: Sequence[float], ys: Sequence[float], stride: int) -> CustomLine: cfg = self.cfg # Plot lines over background line_width = cfg.line_width ax = self._axes_mono[wave_idx] mono_line: Line2D = ax.plot(xs, ys, linewidth=line_width)[0] self._artists.append(mono_line) # noinspection PyTypeChecker return CustomLine(stride, xs, mono_line.set_xdata, mono_line.set_ydata) # Channel labels def add_labels(self, labels: List[str]) -> List["Text"]: """ Updates background, adds text. Do NOT call after calling self.add_lines(). """ nlabel = len(labels) if nlabel != self.nplots: raise ValueError( f"incorrect labels: {self.nplots} plots but {nlabel} labels") cfg = self.cfg color = cfg.get_label_color size_pt = cfg.label_font.size distance_px = cfg.label_padding_ratio * size_pt @attr.dataclass class AxisPosition: pos_axes: float offset_px: float align: str xpos = cfg.label_position.x.match( left=AxisPosition(0, distance_px, "left"), right=AxisPosition(1, -distance_px, "right"), ) ypos = cfg.label_position.y.match( bottom=AxisPosition(0, distance_px, "bottom"), top=AxisPosition(1, -distance_px, "top"), ) pos_axes = (xpos.pos_axes, ypos.pos_axes) offset_pt = (xpos.offset_px, ypos.offset_px) out: List["Text"] = [] for label_text, ax in zip(labels, self._axes_mono): # https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.annotate.html # Annotation subclasses Text. text: "Annotation" = ax.annotate( label_text, # Positioning xy=pos_axes, xycoords="axes fraction", xytext=offset_pt, textcoords="offset points", horizontalalignment=xpos.align, verticalalignment=ypos.align, # Cosmetics color=color, fontsize=px_from_points(size_pt), fontfamily=cfg.label_font.family, fontweight=("bold" if cfg.label_font.bold else "normal"), fontstyle=("italic" if cfg.label_font.italic else "normal"), ) out.append(text) self._save_background() return out # Output frames def get_frame(self) -> ByteBuffer: """Returns bytes with shape (h, w, self.bytes_per_pixel). The actual return value's shape may be flat. """ self._redraw_over_background() canvas = self._fig.canvas # Agg is the default noninteractive backend except on OSX. # https://matplotlib.org/faq/usage_faq.html if not isinstance(canvas, self._canvas_type): raise RuntimeError( f"oh shit, cannot read data from {obj_name(canvas)} != {self._canvas_type.__name__}" ) buffer_rgb = self._canvas_to_bytes(canvas) assert len(buffer_rgb) == self.w * self.h * self.bytes_per_pixel return buffer_rgb # Pre-rendered background bg_cache: Any # "matplotlib.backends._backend_agg.BufferRegion" def _save_background(self) -> None: """ Draw static background. """ # https://stackoverflow.com/a/8956211 # https://matplotlib.org/api/animation_api.html#funcanimation fig = self._fig fig.canvas.draw() self.bg_cache = fig.canvas.copy_from_bbox(fig.bbox) def _redraw_over_background(self) -> None: """ Redraw animated elements of the image. """ # Both FigureCanvasAgg and FigureCanvasCairo, but not FigureCanvasBase, # support restore_region(). canvas: FigureCanvasAgg = self._fig.canvas canvas.restore_region(self.bg_cache) for artist in self._artists: artist.axes.draw_artist(artist)
class PlotController(DialogMixin): """ A base class for matplotlib-canvas controllers that, sets up the widgets and has image exporting functionality. """ file_filters = ("Portable Network Graphics (PNG)", "*.png"), \ ("Scalable Vector Graphics (SVG)", "*.svg"), \ ("Portable Document Format (PDF)", "*.pdf") _canvas = None @property def canvas(self): if not self._canvas: self.setup_figure() self.setup_canvas() self.setup_content() return self._canvas # ------------------------------------------------------------ # Initialisation and other internals # ------------------------------------------------------------ def __init__(self): self._proxies = dict() self.setup_figure() self.setup_canvas() self.setup_content() def setup_figure(self): style = gtk.Style() self.figure = Figure(dpi=72, edgecolor=str(style.bg[2]), facecolor=str(style.bg[0])) self.figure.subplots_adjust(hspace=0.0, wspace=0.0) def setup_canvas(self): self._canvas = FigureCanvasGTK(self.figure) def setup_content(self): raise NotImplementedError # ------------------------------------------------------------ # Update subroutines # ------------------------------------------------------------ def draw(self): try: self.figure.canvas.draw() self.fix_after_drawing() except ParseFatalException: logger.exception("Caught unhandled exception when drawing") def fix_after_drawing(self): pass # nothing to fix # ------------------------------------------------------------ # Graph exporting # ------------------------------------------------------------ def save(self, parent=None, suggest_name="graph", size="auto", num_specimens=1, offset=0.75): """ Displays a save dialog to export an image from the current plot. """ # Parse arguments: width, height = 0, 0 if size == "auto": descr, width, height, dpi = settings.OUTPUT_PRESETS[0] else: width, height, dpi = map(float, size.replace("@", "x").split("x")) # Load gui: builder = gtk.Builder() builder.add_from_file(resource_filename("pyxrd.specimen", "glade/save_graph_size.glade")) # FIXME move this to this namespace!! size_expander = builder.get_object("size_expander") cmb_presets = builder.get_object("cmb_presets") # Setup combo with presets: cmb_store = gtk.ListStore(str, int, int, float) for row in settings.OUTPUT_PRESETS: cmb_store.append(row) cmb_presets.clear() cmb_presets.set_model(cmb_store) cell = gtk.CellRendererText() cmb_presets.pack_start(cell, True) cmb_presets.add_attribute(cell, 'text', 0) def on_cmb_changed(cmb, *args): itr = cmb_presets.get_active_iter() w, h, d = cmb_store.get(itr, 1, 2, 3) entry_w.set_text(str(w)) entry_h.set_text(str(h)) entry_dpi.set_text(str(d)) cmb_presets.connect('changed', on_cmb_changed) # Setup input boxes: entry_w = builder.get_object("entry_width") entry_h = builder.get_object("entry_height") entry_dpi = builder.get_object("entry_dpi") entry_w.set_text(str(width)) entry_h.set_text(str(height)) entry_dpi.set_text(str(dpi)) # What to do when the user wants to save this: def on_accept(dialog): # Get the selected file type and name: cur_fltr = dialog.get_filter() filename = dialog.get_filename() # Add the correct extension if not present yet: for fltr in self.file_filters: if cur_fltr.get_name() == fltr[0]: if filename[len(filename) - 4:] != fltr[1][1:]: filename = "%s%s" % (filename, fltr[1][1:]) break # Get the width, height & dpi width = float(entry_w.get_text()) height = float(entry_h.get_text()) dpi = float(entry_dpi.get_text()) i_width, i_height = width / dpi, height / dpi # Save it all right! self.save_figure(filename, dpi, i_width, i_height) # Ask the user where, how and if he wants to save: self.run_save_dialog("Save Graph", on_accept, None, parent=parent, suggest_name=suggest_name, extra_widget=size_expander) def save_figure(self, filename, dpi, i_width, i_height): """ Save the current plot Arguments: filename: the filename to save to (either .png, .pdf or .svg) dpi: Dots-Per-Inch resolution i_width: the width in inch i_height: the height in inch """ # Get original settings: original_dpi = self.figure.get_dpi() original_width, original_height = self.figure.get_size_inches() # Set everything according to the user selection: self.figure.set_dpi(dpi) self.figure.set_size_inches((i_width, i_height)) self.figure.canvas.draw() # replot bbox_inches = matplotlib.transforms.Bbox.from_bounds(0, 0, i_width, i_height) # Save the figure: self.figure.savefig(filename, dpi=dpi, bbox_inches=bbox_inches) # Put everything back the way it was: self.figure.set_dpi(original_dpi) self.figure.set_size_inches((original_width, original_height)) self.figure.canvas.draw() # replot
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 Diagram(): zoom_factor = 1.2 def __init__(self): self.figure = Figure() self.canvas = FigureCanvas(self.figure) self.canvas.setMinimumWidth(200) self.canvas.setMinimumHeight(200) self.previous_dpi = self.figure.get_dpi() # Add axes that fill the figure (while maintaining aspect ratio) # https://stackoverflow.com/a/6377406/ self.axes = self.figure.add_axes([0, 0, 1, 1]) # Make it possible to focus on the canvas by clicking on it. # This allows the canvas to capture keypresses. # https://stackoverflow.com/questions/22043549/ # http://doc.qt.io/qt-5/qt.html#FocusPolicy-enum self.canvas.setFocusPolicy(Qt.ClickFocus) self.canvas.setCursor(Qt.OpenHandCursor) self.drag_position = None self.rectangle_select_interaction = None self.save_rectangle = None self.rect_rubberband = QRubberBand(QRubberBand.Rectangle, self.canvas) self.canvas.mpl_connect('button_press_event', self.button_press_event) self.canvas.mpl_connect('button_release_event', self.button_release_event) self.canvas.mpl_connect('motion_notify_event', self.motion_notify_event) self.canvas.mpl_connect('key_press_event', self.key_press_event) self.canvas.mpl_connect('scroll_event', self.scroll_event) self.canvas.mpl_connect('resize_event', self.resize_event) def refresh(self): if not self.status.course_code: return old_axes_xlim = self.axes.get_xlim() old_axes_ylim = self.axes.get_ylim() self.axes.clear() self.draw_checkpoints() self.setup_figure() self.status.update_save_dimensions() # If the course is the same as the last refresh, we'd like to keep the # user's current pan and zoom positions. # draw_checkpoints() calls plot() which seems to unavoidably change # the limits, so we must revert the limits to before that call. if not self.status.course_code_changed: self.axes.set_xlim(old_axes_xlim) self.axes.set_ylim(old_axes_ylim) self.canvas.draw() self.deactivate_rectangle_select() def canvas_width(self): return self.canvas.get_width_height()[0] def canvas_height(self): return self.canvas.get_width_height()[1] def convert_coords_canvas_to_game(self, x, y): xlim = self.axes.get_xlim() ylim = self.axes.get_ylim() canvas_width, canvas_height = self.canvas.get_width_height() return (xlim[0] + (xlim[1] - xlim[0]) * (x / canvas_width), ylim[0] + (ylim[1] - ylim[0]) * (y / canvas_height)) def button_press_event(self, event): # Mouse button press self.rectangle_select_button_press_event(event) if not self.rectangle_select_interaction: # Start pan #print(f'Button press: {event.x}, {event.y}, {event.button}') self.drag_position = (event.x, event.y) self.canvas.setCursor(Qt.ClosedHandCursor) def button_release_event(self, event): # Mouse button release self.rectangle_select_button_release_event(event) # End pan #print(f'Button release: {event.x}, {event.y}, {event.button}') self.drag_position = None self.canvas.setCursor(Qt.OpenHandCursor) def motion_notify_event(self, event): # Mouse motion self.rectangle_select_motion_notify_event(event) # If mouse button pressed, pan the figure #print(f'Motion: {event.x}, {event.y}') if self.drag_position: self.pan(event.x - self.drag_position[0], event.y - self.drag_position[1]) self.drag_position = (event.x, event.y) # Update coordinates display coords = self.convert_coords_canvas_to_game(event.x, event.y) # Instead of "-z = 490.73", display "z = -490.73" if self.status.axis_1.startswith('-'): coord_1_str = f'{self.status.axis_1[1:]} = {-coords[0]:.3f}' else: coord_1_str = f'{self.status.axis_1} = {coords[0]:.3f}' if self.status.axis_2.startswith('-'): coord_2_str = f'{self.status.axis_2[1:]} = {-coords[1]:.3f}' else: coord_2_str = f'{self.status.axis_2} = {coords[1]:.3f}' self.status.update_diagram_coords_text(f'{coord_1_str}, {coord_2_str}') def scroll_event(self, event): # Mousewheel scrolling #print(f'Scroll: {event.x}, {event.y}, {event.step}') if event.step > 0: # Scroll up -> zoom in on the current mouse position self.zoom_in(event.x, event.y) else: # Scroll down -> zoom out from the current mouse position self.zoom_out(event.x, event.y) def key_press_event(self, event): #print(f'Key press: {event.key}') if event.key == 'up': # Up arrow -> zoom in on the diagram center canvas_width, canvas_height = self.canvas.get_width_height() self.zoom_in(canvas_width / 2, canvas_height / 2) elif event.key == 'down': # Down arrow -> zoom out from the diagram center canvas_width, canvas_height = self.canvas.get_width_height() self.zoom_out(canvas_width / 2, canvas_height / 2) def resize_event(self, event): # Canvas is resized # Note: This does not get called if the canvas size changes due to # a diagram DPI update. It only gets called when a window resize # triggers a canvas resize. #print(f'Resize: {event.width}, {event.height}') # Fix aspect ratio of the diagram. axes_hmin, axes_hmax = self.axes.get_xlim() axes_vmin, axes_vmax = self.axes.get_ylim() hrange = axes_hmax - axes_hmin vrange = axes_vmax - axes_vmin if hrange / vrange >= event.width / event.height: # Add extra vertical range to maintain aspect ratio target_vrange = hrange * (event.height / event.width) extra_vspace_one_side = (target_vrange - vrange) / 2 axes_vmin = axes_vmin - extra_vspace_one_side axes_vmax = axes_vmax + extra_vspace_one_side else: # Add extra horizontal range to maintain aspect ratio target_hrange = vrange * (event.width / event.height) extra_hspace_one_side = (target_hrange - hrange) / 2 axes_hmin = axes_hmin - extra_hspace_one_side axes_hmax = axes_hmax + extra_hspace_one_side # Apply the new axes limits to fix the aspect ratio self.axes.set_xlim(axes_hmin, axes_hmax) self.axes.set_ylim(axes_vmin, axes_vmax) self.status.update_save_dimensions() # Rectangle can get wonky after a canvas resize, so just erase it self.deactivate_rectangle_select() def pan(self, change_x, change_y): canvas_width, canvas_height = self.canvas.get_width_height() xlim = self.axes.get_xlim() x_coord_ratio_game_to_canvas = (xlim[1] - xlim[0]) / canvas_width self.axes.set_xlim(xlim[0] - change_x * x_coord_ratio_game_to_canvas, xlim[1] - change_x * x_coord_ratio_game_to_canvas) ylim = self.axes.get_ylim() y_coord_ratio_game_to_canvas = (ylim[1] - ylim[0]) / canvas_height self.axes.set_ylim(ylim[0] - change_y * y_coord_ratio_game_to_canvas, ylim[1] - change_y * y_coord_ratio_game_to_canvas) self.canvas.draw() # print(f"Pan: {change_x}, {change_y}") # print(f"xlim: {self.axes.get_xlim()}") # print(f"ylim: {self.axes.get_ylim()}") def zoom(self, x, y, direction_is_inward): """Zoom in/out, centered on the current mouse position""" game_coords = self.convert_coords_canvas_to_game(x, y) if direction_is_inward: # Zoom in space_stretch_factor = 1 / self.zoom_factor else: # Zoom out space_stretch_factor = self.zoom_factor xlim = self.axes.get_xlim() self.axes.set_xlim( game_coords[0] - (game_coords[0] - xlim[0]) * space_stretch_factor, game_coords[0] - (game_coords[0] - xlim[1]) * space_stretch_factor) ylim = self.axes.get_ylim() self.axes.set_ylim( game_coords[1] - (game_coords[1] - ylim[0]) * space_stretch_factor, game_coords[1] - (game_coords[1] - ylim[1]) * space_stretch_factor) self.canvas.draw() def zoom_in(self, x, y): self.zoom(x, y, True) def zoom_out(self, x, y): self.zoom(x, y, False) def activate_rectangle_select(self): self.rect_rubberband.setGeometry(0, 0, 0, 0) self.rect_rubberband.show() self.canvas.setCursor(Qt.CrossCursor) self.rectangle_select_interaction = 'ready' self.save_rectangle = None self.status.update_save_dimensions() def deactivate_rectangle_select(self): self.rect_rubberband.hide() self.canvas.setCursor(Qt.OpenHandCursor) self.rectangle_select_interaction = None self.save_rectangle = None self.status.update_save_dimensions() def rectangle_select_button_press_event(self, event): # Mouse button press if self.rectangle_select_interaction == 'ready': # Start drawing rectangle. # Rectangle dimensions have opposite direction y from # event/canvas dimensions self.rectangle_select_origin_x = event.x self.rectangle_select_origin_y = event.y self.rectangle_select_interaction = 'drawing' else: self.deactivate_rectangle_select() def rectangle_select_motion_notify_event(self, event): # Mouse motion if self.rectangle_select_interaction == 'drawing': # Change the rectangle shape according to the mouse position. canvas_width, canvas_height = self.canvas.get_width_height() # If the mouse is outside of the canvas boundary, snap the # rectangle to the boundary event_x_bounded = event.x event_x_bounded = max(event_x_bounded, 0) event_x_bounded = min(event_x_bounded, canvas_width) event_y_bounded = event.y event_y_bounded = max(event_y_bounded, 0) event_y_bounded = min(event_y_bounded, canvas_height) # 1. Rect rubberband dimensions have opposite direction y from # event/canvas dimensions, so need to do canvas height minus # event y # 2. setGeometry() only works when the sizes are positive, so need # to start with the lower coordinates and ensure positive sizes self.rect_rubberband.setGeometry( min(self.rectangle_select_origin_x, event_x_bounded), min(canvas_height - self.rectangle_select_origin_y, canvas_height - event_y_bounded), abs(self.rectangle_select_origin_x - event_x_bounded), abs(self.rectangle_select_origin_y - event_y_bounded)) def rectangle_select_button_release_event(self, event): # Mouse button release if self.rectangle_select_interaction == 'drawing': # Finish the rectangle. self.rectangle_select_interaction = None # If the mouse is outside of the canvas boundary, snap the # rectangle to the boundary canvas_width, canvas_height = self.canvas.get_width_height() event_x_bounded = event.x event_x_bounded = max(event_x_bounded, 0) event_x_bounded = min(event_x_bounded, canvas_width) event_y_bounded = event.y event_y_bounded = max(event_y_bounded, 0) event_y_bounded = min(event_y_bounded, canvas_height) self.save_rectangle = ((self.rectangle_select_origin_x, self.rectangle_select_origin_y), (event_x_bounded, event_y_bounded)) self.status.update_save_dimensions() #print(self.save_rectangle) def draw_checkpoints(self): # Prepare to plot checkpoints/paths on the chosen axes. The first will # appear as the horizontal axis, and the second will appear as the # vertical axis on the figure. haxis = get_checkpoint_position_function(self.status.axis_1) vaxis = get_checkpoint_position_function(self.status.axis_2) haxis_point = get_point_position_function(self.status.axis_1) vaxis_point = get_point_position_function(self.status.axis_2) # Track the farthest values on either axis so we can compute the # display boundaries. self.data_hmin = math.inf self.data_hmax = -math.inf self.data_vmin = math.inf self.data_vmax = -math.inf # Draw the checkpoints. for c in self.status.checkpoints: if c['checkpoint'] in self.status.hidden_checkpoints: continue track_width = c['track_width'] half_track_width = track_width / 2 # Draw markers on the checkpoint's center, and on both edges # of the track directly lateral from the checkpoint. # http://stackoverflow.com/a/8409110 haxis_coords = [ haxis(-half_track_width, c), haxis(0, c), haxis(half_track_width, c) ] vaxis_coords = [ vaxis(-half_track_width, c), vaxis(0, c), vaxis(half_track_width, c) ] self.axes.plot(haxis_coords, vaxis_coords, color=c['color'], marker='o') base_3d_length = half_track_width base_plane_length = math.sqrt( (haxis_coords[1] - haxis_coords[0])**2 + (vaxis_coords[1] - vaxis_coords[0])**2) if c['checkpoint'] in self.status.extended_checkpoints: # Draw an extended line for the checkpoint, # with equal line length on both sides. # The line length is defined in the diagram's coord plane. extended_plane_length = self.status.extend_length if base_plane_length > 0: extended_3d_length = (extended_plane_length * (base_3d_length / base_plane_length)) else: # In this case the line is perpendicular to the # diagram's plane. extended_3d_length = 0 extend_haxis_coords = [ haxis(-extended_3d_length, c), haxis(extended_3d_length, c) ] extend_vaxis_coords = [ vaxis(-extended_3d_length, c), vaxis(extended_3d_length, c) ] self.axes.plot(extend_haxis_coords, extend_vaxis_coords, c['color']) # Label the checkpoint with its checkpoint number. # Position the label a certain distance away from one end # of the (non-extended) checkpoint line. # Negative distances put the number on the other side. # Again, make sure to define distance in the diagram's coord plane. if c['checkpoint'] not in self.status.hidden_numbers: if self.status.number_distance > 0: label_distance = (self.status.number_distance + half_track_width) side_track_is_on = 'left' else: label_distance = (self.status.number_distance - half_track_width) side_track_is_on = 'right' if base_plane_length > 0 or base_plane_length < 0: label_3d_distance = (label_distance * (base_3d_length / base_plane_length)) else: label_3d_distance = 0 label_coords = (haxis(label_3d_distance, c), vaxis(label_3d_distance, c)) self.axes.text( label_coords[0], label_coords[1], # Ensure the checkpoint number on the plot # shows no decimal places int(c['checkpoint']), fontdict=dict( # Number color should match the line color color=c['color'], # Font size size=self.status.number_size, # Base the position on the side of the text, not the # center. This way, 1 digit and 3 digit numbers # are the same distance from the side of the track. # And it generally reduces instances where the text is # struck-through by extended checkpoint lines. horizontalalignment=side_track_is_on, # Put the bottom of the text at this position. # This generally reduces instances where the text is # struck-through by extended checkpoint lines. verticalalignment='bottom', ), ) # Update coordinate boundaries to ensure they contain the # checkpoint numbers. self.data_hmin = min([self.data_hmin, label_coords[0]]) self.data_hmax = max([self.data_hmax, label_coords[0]]) self.data_vmin = min([self.data_vmin, label_coords[1]]) self.data_vmax = max([self.data_vmax, label_coords[1]]) # Update coordinate boundaries to ensure they contain the # (non-extended) checkpoint lines. self.data_hmin = min([self.data_hmin] + haxis_coords) self.data_hmax = max([self.data_hmax] + haxis_coords) self.data_vmin = min([self.data_vmin] + vaxis_coords) self.data_vmax = max([self.data_vmax] + vaxis_coords) # Plot the path, if any. if self.status.data_path_points: haxis_coords = [ haxis_point(p) for p in self.status.data_path_points ] vaxis_coords = [ vaxis_point(p) for p in self.status.data_path_points ] # Plot in black. color = rgb2hex(hsv_to_rgb([0, 0, 0.0])) self.axes.plot(haxis_coords, vaxis_coords, color) # Plot crossing data, if any. if self.status.crossing_data: for c in self.status.crossing_data: if c['success'] == "Y": # Success = black color = rgb2hex(hsv_to_rgb([0, 0, 0.0])) else: # Failure = gray color = rgb2hex(hsv_to_rgb([0, 0, 0.6])) # Add line segments. p1 = dict(x=c['x1'], y=c['y1'], z=c['z1']) p2 = dict(x=c['x2'], y=c['y2'], z=c['z2']) haxis_coords = [haxis_point(p1), haxis_point(p2)] vaxis_coords = [vaxis_point(p1), vaxis_point(p2)] self.axes.plot(haxis_coords, vaxis_coords, color) # Add dot markers. self.axes.plot(haxis_coords, vaxis_coords, color, marker='o') def setup_figure(self): # Set the desired DPI. self.figure.set_dpi(self.status.dpi) # Fix the figure size in case of a new DPI. # If the DPI decreased then the figure inches need to increase, # and vice versa. # If this is not done, then updating the DPI will make the figure not # fit in the canvas. (It only fixes itself upon a window resize.) if self.previous_dpi != self.status.dpi: x_inches, y_inches = self.figure.get_size_inches() self.figure.set_size_inches( x_inches * (self.previous_dpi / self.status.dpi), y_inches * (self.previous_dpi / self.status.dpi)) self.previous_dpi = self.status.dpi # Figure out what the axes limits should be to contain all the # checkpoints. margin_factor = 0.1 axes_hmin = (self.data_hmin - (self.data_hmax - self.data_hmin) * margin_factor) axes_hmax = (self.data_hmax + (self.data_hmax - self.data_hmin) * margin_factor) axes_vmin = (self.data_vmin - (self.data_vmax - self.data_vmin) * margin_factor) axes_vmax = (self.data_vmax + (self.data_vmax - self.data_vmin) * margin_factor) hrange = axes_hmax - axes_hmin vrange = axes_vmax - axes_vmin # Expand one dimension as needed to fill the canvas while maintaining # aspect ratio. canvas_width, canvas_height = self.canvas.get_width_height() if hrange / vrange >= canvas_width / canvas_height: # Add extra vertical range to maintain aspect ratio target_vrange = hrange * (canvas_height / canvas_width) extra_vspace_one_side = (target_vrange - vrange) / 2 axes_vmin = axes_vmin - extra_vspace_one_side axes_vmax = axes_vmax + extra_vspace_one_side else: # Add extra horizontal range to maintain aspect ratio target_hrange = vrange * (canvas_width / canvas_height) extra_hspace_one_side = (target_hrange - hrange) / 2 axes_hmin = axes_hmin - extra_hspace_one_side axes_hmax = axes_hmax + extra_hspace_one_side # Apply the axes limits. self.axes.set_xlim(axes_hmin, axes_hmax) self.axes.set_ylim(axes_vmin, axes_vmax) # Debug info # print(f"canvas size: {canvas_width}, {canvas_height}") # print(f"figure size: {self.figure.get_size_inches()}") # print( # f"data ranges: {self.data_hmin:.3f}~{self.data_hmax:.3f}" # f" {self.data_vmin:.3f}~{self.data_vmax:.3f}") # print(f"axes range sizes: {hrange}, {vrange}") # print(f"xlim: {self.axes.get_xlim()}") # print(f"ylim: {self.axes.get_ylim()}") def compute_save_dimensions(self): x_inches, y_inches = self.figure.get_size_inches() if self.save_rectangle: canvas_width, canvas_height = self.canvas.get_width_height() save_rectangle_width = (abs(self.save_rectangle[0][0] - self.save_rectangle[1][0])) save_rectangle_height = (abs(self.save_rectangle[0][1] - self.save_rectangle[1][1])) x_inches = x_inches * (save_rectangle_width / canvas_width) y_inches = y_inches * (save_rectangle_height / canvas_height) save_dpi = self.status.save_dpi # Matplotlib seems to round down the resolution to a whole pixel # when saving, so use int() instead of int(round()). return (int(x_inches * save_dpi), int(y_inches * save_dpi)) def save(self, filepath): if self.save_rectangle: # We've specified a specific region of the diagram to save. dpi = self.status.dpi # save_rectangle is in canvas pixels; need to convert to inches bbox_inches = Bbox([ (min(self.save_rectangle[0][0], self.save_rectangle[1][0]) / dpi, min(self.save_rectangle[0][1], self.save_rectangle[1][1]) / dpi), (max(self.save_rectangle[0][0], self.save_rectangle[1][0]) / dpi, max(self.save_rectangle[0][1], self.save_rectangle[1][1]) / dpi), ]) else: # Save the whole diagram. bbox_inches = None self.figure.savefig( filepath, # Force PNG format; JPEG is not supported by our MPL backend format='png', # DPI to use for saving. # This solely affects level of detail/resolution of the resulting # image file. Font size, line thickness, etc. should be the same # as seen in the figure canvas. dpi=self.status.save_dpi, bbox_inches=bbox_inches, )
class MainPlotController(object): """ A controller for the main plot canvas. Sets up the widgets and has image exporting functionality. """ file_filters = ("Portable Network Graphics (PNG)", "*.png"), \ ("Scalable Vector Graphics (SVG)", "*.svg"), \ ("Portable Document Format (PDF)", "*.pdf") _canvas = None @property def canvas(self): if not self._canvas: self.setup_figure() self.setup_canvas() self.setup_content() return self._canvas # ------------------------------------------------------------ # View integration getters # ------------------------------------------------------------ def get_toolbar_widget(self, window): return NavigationToolbar(self.canvas, window) def get_canvas_widget(self): return self.canvas # ------------------------------------------------------------ # Initialization and other internals # ------------------------------------------------------------ def __init__(self, status_callback, marker_callback, *args, **kwargs): self.setup_layout_cache() self.setup_figure() self.setup_canvas() self.setup_content(status_callback, marker_callback) def setup_layout_cache(self): self.position_setup = PositionSetup() self.labels = list() self.marker_lbls = list() self._proxies = dict() self.scale = 1.0 self.stats = False self._last_pos = None def setup_figure(self): self.figure = Figure(dpi=72, facecolor="#FFFFFF", linewidth=0) self.figure.subplots_adjust(hspace=0.0, wspace=0.0) def setup_canvas(self): self._canvas = FigureCanvasGTK(self.figure) def setup_content(self, status_callback, marker_callback): # Create subplot and add it to the figure: self.plot = Subplot(self.figure, 211, facecolor=(1.0, 1.0, 1.0, 0.0)) self.plot.set_autoscale_on(False) self.figure.add_axes(self.plot) # Connect events: self.canvas.mpl_connect('draw_event', self.fix_after_drawing) self.canvas.mpl_connect('resize_event', self.fix_after_drawing) self.mtc = MotionTracker(self, status_callback) self.cc = ClickCatcher(self, marker_callback) #self.update() # ------------------------------------------------------------ # Update methods # ------------------------------------------------------------ def draw(self): self._last_pos = self.fix_before_drawing() self.figure.canvas.draw() def fix_after_drawing(self, *args): _new_pos = self.fix_before_drawing() if _new_pos != self._last_pos: self._last_pos = _new_pos self._redraw_later() return False def _redraw_later(self): self.timer = self.figure.canvas.new_timer(interval=10) self.timer.single_shot = True self.timer.add_callback(lambda: self.figure.canvas.draw_idle()) self.timer.start() def fix_before_drawing(self, *args): """ Fixes alignment issues due to longer labels or smaller windows Is executed after an initial draw event, since we can then retrieve the actual label dimensions and shift/resize the plot accordingly. """ renderer = get_renderer(self.figure) if not renderer or not self._canvas.get_realized(): return False # Fix left side for wide specimen labels: if len(self.labels) > 0: bbox = self._get_joint_bbox(self.labels, renderer) if bbox is not None: self.position_setup.left = self.position_setup.default_left + bbox.width # Fix top for high marker labels: if len(self.marker_lbls) > 0: bbox = self._get_joint_bbox( [label for label, flag, _ in self.marker_lbls if flag], renderer) if bbox is not None: self.position_setup.top = self.position_setup.default_top - bbox.height # Fix bottom for x-axis label: bottom_label = self.plot.axis["bottom"].label if bottom_label is not None: bbox = self._get_joint_bbox([bottom_label], renderer) if bbox is not None: self.position_setup.bottom = self.position_setup.default_bottom + ( bbox.ymax - bbox.ymin) * 2.0 # somehow we need this? # Calculate new plot position & set it: plot_pos = self.position_setup.position self.plot.set_position(plot_pos) # Adjust specimen label position for label in self.labels: label.set_x(plot_pos[0] - 0.025) # Adjust marker label position for label, flag, y_offset in self.marker_lbls: if flag: newy = plot_pos[1] + plot_pos[3] + y_offset - 0.025 label.set_y(newy) _new_pos = self.position_setup.to_string() return _new_pos def update(self, clear=False, project=None, specimens=None): """ Updates the entire plot with the given information. """ if clear: self.plot.cla() if project and specimens: self.labels, self.marker_lbls = plot_specimens( self.plot, self.position_setup, self.cc, project, specimens) # get mixtures for the selected specimens: plot_mixtures(self.plot, project, [ mixture for mixture in project.mixtures if any(specimen in mixture.specimens for specimen in specimens) ]) update_axes(self.plot, self.position_setup, project, specimens) self.draw() # ------------------------------------------------------------ # Plot position and size calculations # ------------------------------------------------------------ def _get_joint_bbox(self, container, renderer): bboxes = [] try: for text in container: bbox = text.get_window_extent(renderer=renderer) # the figure transform goes from relative coords->pixels and we # want the inverse of that bboxi = bbox.inverse_transformed(self.figure.transFigure) bboxes.append(bboxi) except (RuntimeError, ValueError): logger.exception( "Caught unhandled exception when joining boundig boxes") return None # don't continue # this is the bbox that bounds all the bboxes, again in relative # figure coords if len(bboxes) > 0: bbox = transforms.Bbox.union(bboxes) return bbox else: return None # ------------------------------------------------------------ # Graph exporting # ------------------------------------------------------------ def save(self, parent=None, current_name="graph", size="auto", num_specimens=1, offset=0.75): """ Displays a save dialog to export an image from the current plot. """ # Parse arguments: width, height = 0, 0 if size == "auto": descr, width, height, dpi = settings.OUTPUT_PRESETS[0] else: width, height, dpi = list( map(float, size.replace("@", "x").split("x"))) # Load gui: builder = Gtk.Builder() builder.add_from_file( resource_filename("pyxrd.specimen", "glade/save_graph_size.glade") ) # FIXME move this to this namespace!! size_expander = builder.get_object("size_expander") cmb_presets = builder.get_object("cmb_presets") # Setup combo with presets: cmb_store = Gtk.ListStore(str, int, int, float) for row in settings.OUTPUT_PRESETS: cmb_store.append(row) cmb_presets.clear() cmb_presets.set_model(cmb_store) cell = Gtk.CellRendererText() cmb_presets.pack_start(cell, True) cmb_presets.add_attribute(cell, 'text', 0) def on_cmb_changed(cmb, *args): itr = cmb.get_active_iter() w, h, d = cmb_store.get(itr, 1, 2, 3) entry_w.set_text(str(w)) entry_h.set_text(str(h)) entry_dpi.set_text(str(d)) cmb_presets.connect('changed', on_cmb_changed) # Setup input boxes: entry_w = builder.get_object("entry_width") entry_h = builder.get_object("entry_height") entry_dpi = builder.get_object("entry_dpi") entry_w.set_text(str(width)) entry_h.set_text(str(height)) entry_dpi.set_text(str(dpi)) # What to do when the user wants to save this: def on_accept(dialog): # Get the width, height & dpi width = float(entry_w.get_text()) height = float(entry_h.get_text()) dpi = float(entry_dpi.get_text()) i_width, i_height = width / dpi, height / dpi # Save it all right! self.save_figure(dialog.filename, dpi, i_width, i_height) # Ask the user where, how and if he wants to save: DialogFactory.get_save_dialog( "Save Graph", parent=parent, filters=self.file_filters, current_name=current_name, extra_widget=size_expander).run(on_accept) def save_figure(self, filename, dpi, i_width, i_height): """ Save the current plot Arguments: filename: the filename to save to (either .png, .pdf or .svg) dpi: Dots-Per-Inch resolution i_width: the width in inch i_height: the height in inch """ # Get original settings: original_dpi = self.figure.get_dpi() original_width, original_height = self.figure.get_size_inches() # Set everything according to the user selection: self.figure.set_dpi(dpi) self.figure.set_size_inches((i_width, i_height)) self.figure.canvas.draw() # replot bbox_inches = matplotlib.transforms.Bbox.from_bounds( 0, 0, i_width, i_height) # Save the figure: self.figure.savefig(filename, dpi=dpi, bbox_inches=bbox_inches) # Put everything back the way it was: self.figure.set_dpi(original_dpi) self.figure.set_size_inches((original_width, original_height)) self.figure.canvas.draw() # replot pass # end of class
def activity(ui, repo, **opts): # The doc string below will show up in 'hg activity --help' """ Create a file called activity.png displaying the activity of the current repository By default, the activity is computed using the number of commits. There is an option to consider instead the number of lines modified in the changesets (--uselines). Most options are self explanatory. The map file format used to specify aliases is fairly simple: <alias name>; <actual name> This file is only used when --split=authors is used. The name listed after the option --exclude are those found in the mercurial repository. That is, before the map file is applied. """ # do it try: options = check_options(repo, opts) ui.write('There are %d changesets\n' % options.length) # do something with this data: if options.mode=='display': try: import matplotlib.pyplot as plt except: raise UiException('you need matplotlib in your python path in order to use the hg activity extension.') from draw import draw_graph # harvest data dates_tab = collect_data(repo.changelog, options) if len(dates_tab)<1 or not options.length: raise UiException('no data available with those options.') fig = plt.figure() draw_graph(fig, options, dates_tab) plt.show() elif options.mode=='file': try: from matplotlib.figure import Figure from matplotlib.backends.backend_agg import FigureCanvasAgg except: raise UiException('you need matplotlib in your python path in order to use the hg activity extension.') from draw import draw_graph # harvest data dates_tab = collect_data(repo.changelog, options) if len(dates_tab)<1 or not options.length: raise UiException('no data available with those options.') fig = Figure() canvas = FigureCanvasAgg(fig) draw_graph(fig, options, dates_tab) fig.set_dpi(100) fig.set_size_inches(options.width/100.0,options.height/100.0) canvas.print_figure(options.filename) ui.status('Created the file \'%s\'\n' % options.filename) elif options.mode=='gui': from mode_qt import displayQtGui displayQtGui(repo, options) else: raise UiException('unknown mode %s', options.mode) except UiException, error: from sys import exit ui.warn("Hg activity, checking options: %s\n" % error.message) exit(1)