class MplCanvas(FigureCanvas): def __init__(self): # self.x = x self.fig = Figure() self.fig.set_tight_layout(True) self.ax1 = self.fig.add_subplot(211) self.ax1.set_title("Whole Chr") self.ax2 = self.fig.add_subplot(212) self.ax2.set_title("Zoom in Region") self.line2 = object() FigureCanvas.__init__(self, self.fig) FigureCanvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self)
class PlotWidget(QWidget): def __init__(self, name, plotFunction, plot_condition_function_list, plotContextFunction, parent=None): QWidget.__init__(self, parent) self.__name = name self.__plotFunction = plotFunction self.__plotContextFunction = plotContextFunction self.__plot_conditions = plot_condition_function_list """:type: list of functions """ self.__figure = Figure() self.__figure.set_tight_layout(True) self.__canvas = FigureCanvas(self.__figure) self.__canvas.setParent(self) self.__canvas.setFocusPolicy(Qt.StrongFocus) self.__canvas.setFocus() vbox = QVBoxLayout() vbox.addWidget(self.__canvas) self.__toolbar = NavigationToolbar(self.__canvas, self) vbox.addWidget(self.__toolbar) self.setLayout(vbox) self.__dirty = True self.__active = False self.resetPlot() def getFigure(self): """ :rtype: matplotlib.figure.Figure""" return self.__figure def resetPlot(self): self.__figure.clear() def updatePlot(self): if self.isDirty() and self.isActive(): print("Drawing: %s" % self.__name) self.resetPlot() plot_context = self.__plotContextFunction(self.getFigure()) self.__plotFunction(plot_context) self.__canvas.draw() self.setDirty(False) def setDirty(self, dirty=True): self.__dirty = dirty def isDirty(self): return self.__dirty def setActive(self, active=True): self.__active = active def isActive(self): return self.__active def canPlotKey(self, key): return any([plotConditionFunction(key) for plotConditionFunction in self.__plot_conditions])
def __init__(self, parent=None, width=5, height=5, dpi=100): fig = Figure(figsize=(width, height), dpi=dpi) fig.set_tight_layout({"pad": 1}) self.axes = fig.add_subplot(111) self.plot_data = None FigureCanvas.__init__(self, fig) self.setParent(parent) FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding) FigureCanvas.updateGeometry(self)
def __init__(self, parent=None, width=5, height=5, dpi=100): fig = Figure(figsize=(width, height), dpi=dpi) fig.set_facecolor("#282828") fig.set_tight_layout({"pad": 1}) self.axes = fig.add_subplot(111) self.plot_data = [[[0], [0]], [datetime.datetime.now()]] FigureCanvas.__init__(self, fig) self.setParent(parent) FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) self.compute_initial_figure()
def _plot(self, channel, title, data, classes): fig = Figure(facecolor="white") ax = fig.add_subplot(111) class_names = [v.name for v in classes.values()] colors = [v.color.name() for v in classes.values()] names = sorted(data.keys()) w1 = 1.0/(len(data) + 2) ind = np.arange(len(classes)) legendbars = list() ticks = list() tick_labels = list() for i, tn in enumerate(names): # ensure to have it ordered values = [data[tn][cn] for cn in class_names] wprops = dict(color="black") bprops = dict(color="black") bp = ax.boxplot(values, positions=(ind + i*w1), widths=0.75*w1, whiskerprops=wprops, boxprops=bprops) bars = self.autoColor(ax, bp, tn, colors) ticks.extend(ind + i*w1) tick_labels.extend([tn]*len(class_names)) ax.set_ylabel("%s (arb.)" %title) ax.set_title("%s" %channel) ax.set_xticks(ticks) if len(data) >= 2: ax.set_xticklabels(tick_labels, rotation=90) else: ax.set_xticklabels([""]*len(class_names)) ax.set_xlim((min(ticks) - w1, max(ticks) + w1)) yr = np.array(ax.get_ylim()) yr = yr+np.array((-1, 1))*0.1*yr.ptp() ax.set_ylim(yr) ax.legend(bars, class_names, frameon=False, fontsize=10, loc=2, ncol=10) fig.set_tight_layout(True) self.addFigure(channel, fig)
def _plot(self, class_counts, treatments, classes): fig = Figure(facecolor="white") ax = fig.add_subplot(111) class_names = [v.name for v in classes.values()] colors = [v.color.name() for v in classes.values()] names = sorted(treatments.keys()) w1 = 1.0/(len(treatments) + 2) ind = np.arange(len(classes)) ticks = list() tick_labels = list() for i, tn in enumerate(names): # ensure to have it oredered values = [treatments[tn][cn] for cn in class_names] bars = ax.bar(ind + i*w1, values, w1, color=colors) ticks.extend(ind + (i + 0.5)*w1) tick_labels.extend([tn]*len(class_names)) autolabel(ax, bars) ax.set_ylabel('counts') ax.set_xticks(ticks) shift = 0.5*(max(len(treatments), 2))*w1 if len(treatments) >= 2: ax.set_xlim((ind.min()- 0.5*shift, ind.max()+ 2.5*shift)) ax.set_xticklabels(tick_labels, rotation=90) else: ax.set_xticklabels([""]*len(class_names)) ax.set_xlim((ind.min()- 0.5*shift, ind.max()+ 1.5*shift)) yr = np.array(ax.get_ylim()) yr = yr+np.array((0, 1))*0.1*yr.ptp() ax.set_ylim(yr) ax.legend(bars, class_names, frameon=False, fontsize=10, loc=2, ncol=10) fig.set_tight_layout(True) qfw = QFigureWidget(fig, self) self.vbox.addWidget(qfw)
class MplCanvas(FigureCanvas): """Class to represent the FigureCanvas widget""" #signals rightDrag = Signal(float, float) leftDrag = Signal(float, float) def __init__(self): interactive = matplotlib.is_interactive() matplotlib.interactive(False) # setup Matplotlib Figure and Axis self.fig = Figure(facecolor='#ffffff') try: self.fig.set_tight_layout(True) except AttributeError: # matplotlib < 1.1 pass # initialization of the canvas FigureCanvas.__init__(self, self.fig) # we define the widget as expandable FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) # notify the system of updated policy FigureCanvas.updateGeometry(self) self.manager = FigureManager(self, 0) matplotlib.interactive(interactive) def paintEvent(self, event): #draw the zoom rectangle more prominently drawRect = self.drawRect self.drawRect = False super(MplCanvas, self).paintEvent(event) if drawRect: p = QtGui.QPainter(self) p.setPen(QtGui.QPen(Qt.red, 2, Qt.DotLine)) p.drawRect(self.rect[0], self.rect[1], self.rect[2], self.rect[3]) p.end()
class Gui: def __init__( self ): self.root = tk.Tk() self.root.wm_title( "Log grapher" ) matplotlib.rc('font', size=8 ) self.fig = Figure( figsize=(11,5), dpi=100 ) self.fig.set_tight_layout( True ) self.axis = self.fig.add_subplot( 111 ) self.axis.set_title( 'Graph' ) self.axis.set_xlabel( 'X axis label' ) self.axis.set_ylabel( 'Y label' ) self.canvas = FigureCanvasTkAgg( self.fig, master=self.root ) self.canvas.show() self.canvas.get_tk_widget().pack( side=TOP, fill=BOTH, expand=1 ) def setLabels( self, labelList ): """setLabels before doing anything else - configures axes etc""" self.labels = labelList for i in range( 0, len( labelList ) ): self.axis.plot( [] ) self.fig.legend( self.axis.lines, self.labels, 'lower center', ncol=len(self.labels), borderpad=0.3, handletextpad=0.2, columnspacing=0.3 ) def append( self, xVal, yValList ): """ yValList must be the same length as labelList, None values will be ignored. Call update() afterwards. """ #print( "gui append " + str( xVal ) + ", " ) #pprint( yValList ) for idx, yVal in enumerate( yValList ): if yVal is not None: hl = self.axis.lines[idx] hl.set_xdata( numpy.append( hl.get_xdata(), xVal ) ) hl.set_ydata( numpy.append( hl.get_ydata(), yVal ) ) def update( self ): self.axis.relim() self.axis.autoscale_view() self.canvas.draw()
class MplCanvas(FigureCanvas): """Class to represent the FigureCanvas widget""" #signals rightDrag = pyqtSignal(float, float) leftDrag = pyqtSignal(float, float) def __init__(self): # setup Matplotlib Figure and Axis self.fig = Figure(facecolor='#ffffff') try: self.fig.set_tight_layout(True) except AttributeError: # matplotlib < 1.1 pass # initialization of the canvas FigureCanvas.__init__(self, self.fig) # we define the widget as expandable FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) # notify the system of updated policy FigureCanvas.updateGeometry(self)
class Plot: # TODO scaling of size when resizing # TODO tighter fit of plot # TODO BUG: weird redrawing issue on changing panes, probably should not redraw graph on changing panes # Plot object used GUI def __init__(self, builder, GPUs, maxpoints, precision, linux_kernelmain, linux_kernelsub): # Can used for kernel specific workarounds self.linux_kernelmain = linux_kernelmain self.linux_kernelsub = linux_kernelsub self.precision = precision self.builder = builder self.GPUs = GPUs self.GPU = GPUs[0] self.maxpoints = maxpoints self.fig = Figure(figsize=(1000, 150), dpi=100, facecolor="#00000000") self.fig.set_tight_layout(True) self.ax = self.fig.add_subplot(111) # enable, name, unit, mean, max, current self.signalstore = Gtk.ListStore(bool, bool, bool, str, str, str, str, str, str, str) self.Plotsignals = self.init_signals(self.GPU) # Set top panel height in accordance to number of signals (with saturation) height_top_panel = len(self.Plotsignals) * 32.5 if height_top_panel < 150: self.builder.get_object("MainPane").set_position(150) elif height_top_panel > 235: self.builder.get_object("MainPane").set_position(235) else: self.builder.get_object("MainPane").set_position(height_top_panel) self.init_treeview() self.update_signals() self.canvas = FigureCanvas(self.fig) self.canvas.set_size_request(1000, 150) self.object = self.builder.get_object("matplot") self.object.add(self.canvas) self.object.show_all() def change_GPU(self, cardnr): print(f"Changing plot to GPU {self.GPUs[cardnr].fancyname}") self.GPU = self.GPUs[cardnr] self.Plotsignals = self.init_signals(self.GPU) self.init_treeview() self.update_signals() def init_signals(self, GPU): Plotsignals = [] # Define signals with: names units max min path plotenable plotnormalise plotcolor parser and outputargument used from parser if GPU.gpu_clock != 'N/A': Plotsignals.append( Plotsignal("GPU Clock", "[MHz]", GPU.pstate_clock[-1], GPU.pstate_clock[0], "/pp_dpm_sclk", True, True, "#1f77b4", GPU.get_current_clock, 0)) Plotsignals.append( Plotsignal("GPU State", "[-]", len(GPU.pstate_clock) - 1, 0, "/pp_dpm_sclk", True, True, "#ff7f0e", GPU.get_current_clock, 1)) if GPU.mem_clock != 'N/A': Plotsignals.append( Plotsignal("MEM Clock", "[MHz]", GPU.pmem_clock[-1], GPU.pmem_clock[0], "/pp_dpm_mclk", True, True, "#d62728", GPU.get_current_clock, 0)) Plotsignals.append( Plotsignal("MEM State", "[-]", len(GPU.pmem_clock) - 1, 0, "/pp_dpm_mclk", True, True, "#9467bd", GPU.get_current_clock, 1)) self.add_available_signal(GPU.sensors, Plotsignals, hwmonpath=GPU.hwmonpath) # GPU busy percent only properly available in linux version 4.19+ if (self.linux_kernelmain == 4 and self.linux_kernelsub > 18) or (self.linux_kernelmain >= 5): Plotsignals.append( Plotsignal("GPU Usage", "[-]", 100, 0, "/gpu_busy_percent", True, True, "#2ca02c", GPU.read_sensor)) # as final check remove signals that return None: checked_plotlist = [] for i, signal in enumerate(Plotsignals): signal.retrieve_data(self.maxpoints) if signal.get_last_value() is not None: checked_plotlist.append(signal) else: print( f"Removing {signal.name} from plotsignals, returning Nonetype" ) if len(checked_plotlist) == 0: print("Nothing to plot! Hiding the plot pane.") self.builder.get_object("Plot").hide() return checked_plotlist def add_available_signal(self, signals, Plotsignals, hwmonpath="", subsystem="", stop_recursion=False): for key, value in signals.items(): if key in subsystem_unit_color: subsystem = key stop_recursion = False if "path" in value: if subsystem == "": continue if any(path_sensor_to_plot in value["path"] for path_sensor_to_plot in sensors_to_plot): signallabel = value["path"][1:].split("_")[0] signalmax = 0 signalmin = 0 signalpath = hwmonpath + value["path"] if "min" in signals: signalmin = signals['min']['value'] stop_recursion = True if "max" in signals: signalmax = signals['max']['value'] stop_recursion = True if "crit" in signals: signalmax = signals['crit']['value'] stop_recursion = True if "label" in signals: signallabel = signals["label"]["value"] if signallabel == "vddgfx" and len( self.GPU.volt_range) > 0: signalmax = self.GPU.volt_range[1] signalmin = 0 stop_recursion = True if "cap" in signals: signalmax = signals["cap"]["value"] stop_recursion = True if "pwm" in value["path"]: signalmax = 255 signallabel = "(fan)" + signallabel Plotsignals.append( Plotsignal(signallabel, subsystem_unit_color[subsystem]["unit"], signalmax, signalmin, signalpath, True, True, subsystem_unit_color[subsystem]["color"], read)) else: if not stop_recursion: self.add_available_signal(value, Plotsignals, hwmonpath=hwmonpath, subsystem=subsystem, stop_recursion=stop_recursion) else: continue def init_treeview(self): textrenderer = Gtk.CellRendererText() self.plotrenderer = Gtk.CellRendererToggle() self.plotrenderer.connect("toggled", self.on_plot_toggled) self.normaliserenderer = Gtk.CellRendererToggle() self.normaliserenderer.connect("toggled", self.on_normalise_toggled) self.tree = self.builder.get_object("Signal Selection") self.tree.append_column( Gtk.TreeViewColumn("Plot", self.plotrenderer, active=0)) self.tree.append_column( Gtk.TreeViewColumn("Scale", self.normaliserenderer, active=1, activatable=2)) columnnames = ["Name", "Unit", "min", "mean", "max", "current"] for i, column in enumerate(columnnames): tcolumn = Gtk.TreeViewColumn(column, textrenderer, text=i + 3, foreground=9) self.tree.append_column(tcolumn) for plotsignal in self.Plotsignals: self.signalstore.append([ plotsignal.plotenable, plotsignal.plotnormalise, True, plotsignal.name, convert_to_si(plotsignal.unit)[0], '0', '0', '0', '0', plotsignal.plotcolor ]) self.tree.set_model(self.signalstore) def update_signals(self): # Retrieve signal and set appropriate values in signalstore to update left pane in GUI for i, Plotsignal in enumerate(self.Plotsignals): Plotsignal.retrieve_data(self.maxpoints) disable_scaling = len( Plotsignal.get_values()) > 3 and Plotsignal.all_equal( ) and Plotsignal.plotnormalise and (Plotsignal.max == Plotsignal.min) self.signalstore[i][2] = not disable_scaling if disable_scaling: print( f"cannot scale values of {self.signalstore[i][3]} disabling scaling" ) self.on_normalise_toggled(self.normaliserenderer, i, disable_refresh=True) if disable_plots_if_scaling_error: print( f"disabling {self.signalstore[i][3]} plot since disable_plots_if_scaling_error is set" ) self.on_plot_toggled(self.plotrenderer, i) self.signalstore[i][5] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_min())[1], self.precision)) self.signalstore[i][6] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_mean())[1], self.precision)) self.signalstore[i][7] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_max())[1], self.precision)) self.signalstore[i][8] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_last_value())[1], self.precision)) def on_plot_toggled(self, widget, path, disable_refresh=False): self.signalstore[path][0] = not self.signalstore[path][0] self.Plotsignals[int( path)].plotenable = not self.Plotsignals[int(path)].plotenable if not disable_refresh: self.update_plot() def on_normalise_toggled(self, widget, path, disable_refresh=False): self.signalstore[path][1] = not self.signalstore[path][1] self.Plotsignals[int(path)].plotnormalise = not self.Plotsignals[int( path)].plotnormalise if not disable_refresh: self.update_plot() def update_plot(self): if len(self.Plotsignals) == 0: return self.ax.clear() for Plotsignal in self.Plotsignals: if Plotsignal.plotenable: if Plotsignal.plotnormalise: data = Plotsignal.get_normalised_values() * 100 self.ax.plot(data, color=Plotsignal.plotcolor) else: data = Plotsignal.get_values() self.ax.plot(convert_to_si(Plotsignal.unit, data)[1], color=Plotsignal.plotcolor) self.ax.grid(True) self.ax.get_yaxis().tick_right() self.ax.get_yaxis().set_label_position("right") self.ax.get_yaxis().set_visible(True) self.ax.get_xaxis().set_visible(False) all_normalised = True all_same_unit = True unit = "" iter = self.signalstore.get_iter(0) while iter is not None: if self.signalstore[iter][0] == True: if unit == "": unit = self.signalstore[iter][4] if self.signalstore[iter][1] == False: all_normalised = False if self.signalstore[iter][4] != unit: all_same_unit = False if not all_normalised and not all_same_unit: break iter = self.signalstore.iter_next(iter) if all_normalised: self.ax.set_yticks(np.arange(0, 101, step=25)) self.ax.set_ylabel('Percent [%]') else: self.ax.yaxis.set_major_locator(AutoLocator()) if all_same_unit: self.ax.set_ylabel(unit) else: self.ax.set_ylabel("") self.canvas.draw() self.canvas.flush_events() def refresh(self): # This is run in thread self.update_signals() self.update_plot()
class TardieuPlot: """Create matplotlib graphs for Tardieu analysis""" def __init__(self): """Initialize but do not plot anything yet""" # adjustable params # TODO: some could go into config self.marker_button = 1 # mouse button for placing markers self.marker_del_button = 3 # remove marker self.marker_key = 'shift' # modifier key for markers self.markers = None # FIXME: check colors self.marker_colors = [ 'orange', 'green', 'red', 'brown', 'gray', 'purple' ] self.marker_width = 1.5 self.width_ratio = [1, 5] self.text_fontsize = 9 self.margin = 0.025 # margin at edge of plots self.margin = 0 self.narrow = False self.hspace = 0.4 self.wspace = 0.5 self.emg_automark_chs = ['Gas', 'Sol'] # FIXME: into config? self.data_axes = list() # axes that actually contain data self.emg_axes = list() # these are callbacks that should be registered by the creator self._update_marker_status = None self._update_status = None self.fig = Figure() def load_data(self, source, emg_chs, emg_passband, ang0_nexus): """Load the Tardieu data. emg_chs: list of EMG channel names to use Returns True on successful data load, False otherwise """ self.trial = Trial(source) self.trial.emg.passband = emg_passband # the 'true' physiological starting angle (given as a param) self.ang0_nexus = ang0_nexus self.emg_chs = emg_chs self.time = self.trial.t / self.trial.framerate # time axis in sec self.tmax = self.time[-1] self.nframes = len(self.time) # read EMG data self.emgdata = dict() self.emg_rms = dict() for ch in self.emg_chs: t_, self.emgdata[ch] = self.trial.get_emg_data(ch) self.emg_rms[ch] = rms(self.emgdata[ch], cfg.emg.rms_win) if cfg.tardieu.acc_chs: accdata_ = self.trial.accelerometer_data['data'] # compute vector sum over given accelerometer chs try: accsigs = [accdata_[ch] for ch in cfg.tardieu.acc_chs] except KeyError: raise GaitDataError(f'No such accelerometer channel {ch}') acctot = np.stack(accsigs) self.acctot = np.sqrt(np.sum(acctot**2, 0)) else: self.acctot = None # FIXME: self.time? self.time_analog = t_ / self.trial.analograte # read marker data and compute segment angle mnames = cfg.tardieu.marker_names data = read_data.get_marker_data(source, mnames) P0 = data[mnames[0]] P1 = data[mnames[1]] P2 = data[mnames[2]] # stack so that marker changes along 2nd dim for segment_angles Pall = np.stack([P0, P1, P2], axis=1) # compute segment angles (deg) self.angd = _segment_angles(Pall) / np.pi * 180 # this is our calculated starting angle ang0_our = np.median(self.angd[~np.isnan(self.angd)][:10]) # normalize: plantarflexion negative, our starting angle equals # the starting angle given in Nexus (i.e. if ang0_nexus is 95, # we offset the data to start at -5 deg) # if starting angle is not specified in Nexus, it defaults to 90 deg self.angd = 90 - self.ang0_nexus - self.angd + ang0_our return True def plot_data(self, interactive=True, emg_yscale=None): """Plot the data. Can plot either on the main (interactive) display or a new mpl Figure() (which will be returned)""" fig = self.fig if interactive else Figure() fig.clear() data_axes = list() if interactive: # save trace objects for later modification by GUI self.emg_traces, self.rms_traces = dict(), dict() nrows = len(self.emg_chs) + 3 # emgs + acc + marker data if self.acctot is not None: nrows += 1 # add one row for legend if not in interactive mode if not interactive: hr = [1] * nrows + [0.5] nrows += 1 gs = gridspec.GridSpec(nrows, 1, height_ratios=hr) legend_ax = fig.add_subplot(gs[-1, 0]) legend_ax.set_axis_off() else: gs = gridspec.GridSpec(nrows, 1) legend_ax = None # EMG plots ind = 0 for ch in self.emg_chs: sharex = None if ind == 0 or not interactive else data_axes[0] ax = fig.add_subplot(gs[ind, 0], sharex=sharex) (emgtr_, ) = ax.plot( self.time_analog, self.emgdata[ch] * 1e3, linewidth=cfg.plot.emg_linewidth, ) (rmstr_, ) = ax.plot( self.time_analog, self.emg_rms[ch] * 1e3, linewidth=cfg.plot.emg_rms_linewidth, color='black', ) data_axes.append(ax) if interactive: self.emg_traces[ind] = emgtr_ self.rms_traces[ind] = rmstr_ self.emg_axes.append(ax) ysc = cfg.plot.emg_yscale if emg_yscale is None else emg_yscale / 1.0e3 ax.set_ylim([-ysc * 1e3, ysc * 1e3]) ax.set(ylabel='mV') ax.set_title(ch) ind += 1 # total acceleration if self.acctot is not None: sharex = None if ind == 0 or not interactive else data_axes[0] ax = fig.add_subplot(gs[ind, 0], sharex=sharex) ax.plot(self.time_analog, self.acctot, linewidth=cfg.plot.emg_linewidth) # FIXME: no calibration yet so data is assumed to be in mV # ax.set(ylabel='m/s²') ax.set(ylabel='mV') ax.set_title('Accelerometer vector sum') data_axes.append(ax) ind += 1 # angle plot sharex = None if ind == 0 or not interactive else data_axes[0] ax = fig.add_subplot(gs[ind, 0], sharex=sharex) ax.plot(self.time, self.angd, linewidth=cfg.plot.model_linewidth) ax.set(ylabel='deg') ax.set_title('Angle') data_axes.append(ax) ind += 1 # angular velocity plot sharex = None if ind == 0 or not interactive else data_axes[0] ax = fig.add_subplot(gs[ind, 0], sharex=sharex) self.angveld = self.trial.framerate * np.diff(self.angd, axis=0) ax.plot(self.time[:-1], self.angveld, linewidth=cfg.plot.model_linewidth) ax.set(ylabel='deg/s') ax.set_title('Angular velocity') data_axes.append(ax) ind += 1 # angular acceleration plot sharex = None if ind == 0 or not interactive else data_axes[0] ax = fig.add_subplot(gs[ind, 0], sharex=sharex) self.angaccd = np.diff(self.angveld, axis=0) ax.plot(self.time[:-2], self.angaccd, linewidth=cfg.plot.model_linewidth) ax.set(xlabel='Time (s)', ylabel='deg/s²') ax.set_title('Angular acceleration') data_axes.append(ax) for ax in data_axes: self._adj_fonts(ax) if interactive: self.data_axes = data_axes self.tmin, self.tmax = self.data_axes[0].get_xlim() # create markers markers = Markers(self.marker_colors, self.marker_width, self.data_axes) # place the auto markers tmin_ = max(self.time[0], self.tmin) tmax_ = min(self.time[-1], self.tmax) fmin, fmax = self._time_to_frame([tmin_, tmax_], self.trial.framerate) smin, smax = self._time_to_frame([tmin_, tmax_], self.trial.analograte) # max. velocity velr = self.angveld[fmin:fmax] velmaxind = np.nanargmax(velr) / self.trial.framerate + tmin_ markers.add(velmaxind, annotation='Max. velocity') # min. acceleration accr = self.angaccd[fmin:fmax] accmaxind = np.nanargmin(accr) / self.trial.framerate + tmin_ markers.add(accmaxind, annotation='Min. acceleration') for ch in self.emg_chs: # check if channel is tagged for automark if any([s in ch for s in self.emg_automark_chs]): rmsdata = self.emg_rms[ch][smin:smax] rmsmaxind = np.argmax( rmsdata) / self.trial.analograte + tmin_ markers.add(rmsmaxind, annotation=f'{ch} max. RMS') # connect callbacks for ax in self.data_axes: ax.callbacks.connect('xlim_changed', self._xlim_changed) fig.canvas.mpl_connect('button_press_event', self._onclick) # catch key press # fig.canvas.mpl_connect('key_press_event', self._onpress) # pick handler fig.canvas.mpl_connect('pick_event', self._onpick) # self._last_click_event = None self.markers = markers fig.set_tight_layout(True) return fig, data_axes, legend_ax def _rescale_emg(self, yscale): """Takes new EMG yscale in mV""" for ax in self.emg_axes: ax.set_ylim(-yscale, yscale) def _get_fast_movement(self): """Get x range around fast movement""" velmaxt = self.time[np.nanargmax(self.angveld)] return velmaxt - 0.5, velmaxt + 1.5 def _xzoom(self, x1, x2): for ax in self.data_axes: ax.set_xlim(x1, x2) def _xzoom_to_fast(self): """Zoom x to fast movement""" rng = self._get_fast_movement() self._xzoom(rng[0], rng[1]) def _xzoom_reset(self): self._xzoom(self.time[0], self.time[-1]) def _reset_emg_filter(self, f1, f2): """Takes new EMG lowpass and highpass values""" logger.debug(f'reset EMG filter to {f1:.2f}-{f2:.2f}') self.trial.emg.passband = [f1, f2] for ind, ch in enumerate(self.emg_chs): t_, self.emgdata[ch] = self.trial.get_emg_data(ch) self.emg_rms[ch] = rms(self.emgdata[ch], cfg.emg.rms_win) self.emg_traces[ind].set_ydata(self.emgdata[ch] * 1e3) self.rms_traces[ind].set_ydata(self.emg_rms[ch] * 1e3) @staticmethod def _adj_fonts(ax): """Adjust font sizes on an axis""" ax.xaxis.label.set_fontsize(cfg.plot_matplotlib.label_fontsize) ax.yaxis.label.set_fontsize(cfg.plot_matplotlib.label_fontsize) ax.title.set_fontsize(cfg.plot_matplotlib.subtitle_fontsize) ax.tick_params(axis='both', which='major', labelsize=cfg.plot_matplotlib.ticks_fontsize) @staticmethod def _time_to_frame(times, rate): """Convert time to samples (according to rate)""" return np.round(rate * np.array(times)).astype(int) def tight_layout(self): """Auto set spacing between/around axes""" self.fig.set_tight_layout(True) # not sure if works/needed # self.gs.update(hspace=self.hspace, wspace=self.wspace, # left=self.margin, right=1-self.margin) # probably needed # self.fig.canvas.draw() def _xlim_changed(self, ax): """Callback for x limits change, e.g. on zoom""" # we need to get the limits from the axis that was zoomed # (the limits are not instantly propagated by sharex) self.tmin, self.tmax = ax.get_xlim() self.fig.canvas.draw() self._update_status() def _toggle_narrow_callback(self, event): """Toggle narrow/wide display""" self.narrow = not self.narrow wratios = [1, 1] if self.narrow else self.width_ratio btext = 'Wide view' if self.narrow else 'Narrow view' self.gs.set_width_ratios(wratios) self._narrowbutton.label.set_text(btext) self.gs.update() # FIXME: canvas ref self.fig.canvas.draw() def _onpick(self, event): """Gets triggered on pick event, i.e. selection of existing marker""" if self._toolbar.mode: # do not respond if toolbar buttons enabled return mevent = event.mouseevent # prevent handling the same onpick event multiple times (e.g. # if multiple markers get picked on a single click) if self._last_click_event == mevent: return if mevent.button != self.marker_del_button or mevent.key != self.marker_key: return self.markers.delete_artist(event.artist, mevent.inaxes) self._last_click_event = mevent self.fig.canvas.draw() self._update_marker_status() def _onpress(self, event): """Keyboard event handler""" if event.key == 'tab': self._toggle_narrow_callback(event) def _onclick(self, event): """Gets triggered by a mouse click on canvas""" if self._toolbar.mode: # do not respond if toolbar buttons are enabled return if event.inaxes not in self.data_axes: return # prevent handling the same click event multiple times # onpick and onclick may get triggered simultaneously if event == self._last_click_event: return if event.button != self.marker_button or event.key != self.marker_key: return x = event.xdata self.markers.add_on_click(x) self._last_click_event = event self.fig.canvas.draw() self._update_marker_status() @property def marker_status_text(self): """Return marker status text in HTML""" s = '' # each marker gets text of its own color for marker, anno, col in self.markers.marker_info: frame = self._time_to_frame(marker, self.trial.framerate) s += f"<font color='{col}'>" # len of acc signal is nframes - 2 if frame < 0 or frame >= self.nframes - 2: s += 'Marker outside data range' else: s += f'Marker @{marker:.3f} s' s += (' (%s):<br>') % anno if anno else ':<br>' s += 'dflex: %.2f° vel: %.2f°/s' % ( self.angd[frame], self.angveld[frame], ) s += f' acc: {self.angaccd[frame]:.2f}°/s²<br><br>' s += '</font>' return s @property def status_text(self): """Create the status text""" # find the limits of the data that is shown tmin_ = max(self.time[0], self.tmin) tmax_ = min(self.time[-1], self.tmax) s = f'Trial name: {self.trial.trialname}\n' s += f"Description: {self.trial.eclipse_data['DESCRIPTION']}\n" s += f"Notes: {self.trial.eclipse_data['NOTES']}\n" s += 'Angle offset: ' s += f' {self.ang0_nexus:.1f}°\n' if self.ang0_nexus else 'none\n' s += f'Data range shown: {tmin_:.2f} - {tmax_:.2f} s\n' # frame indices corresponding to time limits fmin, fmax = self._time_to_frame([tmin_, tmax_], self.trial.framerate) if fmin == fmax: s += 'Zoomed in to a single frame\nPlease zoom out for info' return s else: smin, smax = self._time_to_frame([tmin_, tmax_], self.trial.analograte) s += 'In frames: %d - %d\n\n' % (fmin, fmax) # foot angle in chosen range and the maximum angr = self.angd[fmin:fmax] # check if we zoomed to all-nan region of angle data if np.all(np.isnan(angr)): s += 'No valid data in region' return s angmax = np.nanmax(angr) angmaxind = np.nanargmax(angr) / self.trial.framerate + tmin_ angmin = np.nanmin(angr) angminind = np.nanargmin(angr) / self.trial.framerate + tmin_ # same for velocity velr = self.angveld[fmin:fmax] velmax = np.nanmax(velr) velmaxind = np.nanargmax(velr) / self.trial.framerate + tmin_ s += 'Values for range shown:\n' s += f'Max. dorsiflexion: {angmax:.2f}° @ {angmaxind:.2f} s\n' s += f'Max. plantarflexion: {angmin:.2f}° @ {angminind:.2f} s\n' s += f'Max velocity: {velmax:.2f}°/s @ {velmaxind:.2f} s\n' for ch in self.emg_chs: rmsdata = self.emg_rms[ch][smin:smax] rmsmax = rmsdata.max() rmsmaxind = np.argmax(rmsdata) / self.trial.analograte + tmin_ s += f'{ch} max RMS: {rmsmax * 1000.0:.2f} mV @ {rmsmaxind:.2f} s\n' return s
class FlipangleDialog(Flipangle_Dialog_Base, Flipangle_Dialog_Form): def __init__(self, parent=None): # split tools for finding center frequency and flipangle super(FlipangleDialog, self).__init__(parent) self.setupUi(self) # setup closeEvent self.ui = loadUi('ui/flipangleDialog.ui') self.ui.closeEvent = self.closeEvent # Setup Buttons # self.uploadSeq.clicked.connect(self.upload_pulse) # self.findCenterBtn.clicked.connect(self.start_find_Center) # self.findAtBtn.clicked.connect(self.start_find_At) self.startCenterBtn.clicked.connect(self.start_find_Center) self.startAtBtn.clicked.connect(self.start_find_At) self.confirmAtBtn.clicked.connect(self.at_confirmed) # Setup line edit for estimated frequency # self.freqEstimation.valueChanged(self.setEstFreqValue()) self.freqEstimation.setKeyboardTracking(False) # Setup line edit as read only self.pulsePath.setReadOnly(True) self.centerFreqValue.setReadOnly(True) self.at90Value.setReadOnly(True) self.at180Value.setReadOnly(True) self.at180Value.setEnabled(False) # Disable/Enable UI elements self.uploadSeq.setEnabled(False) self.freqEstimation.setEnabled(True) self.freqSpan.setEnabled(True) self.freqSteps.setEnabled(True) self.atStart.setEnabled(True) self.atStop.setEnabled(True) self.atSteps.setEnabled(True) self.timeoutValue.setEnabled(True) self.startCenterBtn.setEnabled(True) self.startAtBtn.setEnabled(True) self.confirmAtBtn.setEnabled(False) self.init_var() # self.at_values = [15.0, 17.5, 20.0, 22.5, 25.0] # self.at_results = [] self.figure = Figure() self.figure.set_facecolor('none') self.axes = self.figure.add_subplot() self.axes.set_xlabel('Attenuation') self.axes.set_ylabel('Peak') self.axes.grid() self.figure.set_tight_layout(True) self.plotWidget = FigureCanvas(self.figure) self.plotLayout.addWidget(self.plotWidget) self.fid = parent ''' def upload_pulse(self): dialog = QFileDialog() fname = dialog.getOpenFileName(None, "Import Pulse Sequence", "", "Text files (*.txt)") print("\tUploading 90 degree flip sequence to server.") try: self.send_pulse(fname[0]) self.uploadSeq.setText(fname[0]) except IOError as e: print("\tError: required txt file doesn't exist.") return print("\tUploaded successfully to server.") ''' def init_var(self): self.centeringFlag = False self.attenuationFlag = False self.acqTimeout = self.timeoutValue.value() / 1000 self.acqCount = 0 self.centerFreq = 0 self.centerPeak = 0 self.at_results = [] self.freqEstimation.setValue(parameters.get_freq()) self.freqSpan.setValue(0.60) self.freqSteps.setValue(6) self.atStart.setValue(16) self.atStop.setValue(24) self.atSteps.setValue(8) def freq_search_init(self): center = self.freqEstimation.value() span = self.freqSpan.value() steps = self.freqSteps.value() self.acqTimeout = self.timeoutValue.value() / 1000 self.search_space = np.arange(center - span / 2, center + span / 2, span / steps) def flip_calib_init(self): start = self.atStart.value() stop = self.atStop.value() steps = self.atSteps.value() self.acqTimeout = self.timeoutValue.value() / 1000 self.at_values = np.arange(start, stop, (stop - start) / steps) self.at_results = [] def start_find_Center(self): self.freqEstimation.setEnabled(False) self.freqSpan.setEnabled(False) self.freqSteps.setEnabled(False) self.startCenterBtn.setEnabled(False) self.timeoutValue.setEnabled(False) self.startAtBtn.setEnabled(False) self.resetFigure() self.freq_search_init() self.at_results = [] self.acqCount = 0 self.centeringFlag = True self.find_Ceter() def find_Ceter(self): if self.fid.peak_value > self.centerPeak: # Change peak and center frequency value self.centerPeak = self.fid.peak_value self.centerFreq = round(self.fid.center_freq, 5) # Set up text edit self.centerFreqValue.setText(str(round(self.centerFreq, 4))) if self.acqCount <= len(self.search_space) - 1: # Continue until all frequencies are aquired print("\nAcquisition counter: ", self.acqCount + 1, "/", len(self.search_space), ":") self.centerFreqValue.setText( str(round(self.search_space[self.acqCount], 5))) self.fid.set_freq(round(self.search_space[self.acqCount], 5)) self.acqCount += 1 else: # Acquisition finished self.acqCount = 0 self.centerFreqValue.setText(str(round(self.centerFreq, 4))) print("Acquisition for confirmation:") self.fid.set_freq(self.centerFreq) # Disable/Enable GUI elements self.freqEstimation.setEnabled(True) self.freqSpan.setEnabled(True) self.freqSteps.setEnabled(True) self.timeoutValue.setEnabled(True) self.startCenterBtn.setEnabled(True) self.startAtBtn.setEnabled(True) self.centeringFlag = False def start_find_At(self): print("Center frequency confirmed.") self.resetFigure() # Disable/Enable GUI elements self.atStart.setEnabled(False) self.atStop.setEnabled(False) self.atSteps.setEnabled(False) self.timeoutValue.setEnabled(False) self.startCenterBtn.setEnabled(False) self.startAtBtn.setEnabled(False) self.flip_calib_init() self.acqCount = 0 self.attenuationFlag = True self.fid.set_at(self.at_values[self.acqCount]) self.find_At() def find_At(self): if self.acqCount > 0: self.at_results.append(round(self.fid.peak_value, 2)) self.axes.plot(self.at_values[self.acqCount - 1], self.at_results[self.acqCount - 1], 'x', color='red') self.plotWidget.draw() if self.acqCount < len(self.at_values): # Continue until all AT values are aquired print("Acquisition counter: ", self.acqCount + 1, "/", len(self.at_values), ":") self.fid.set_at(self.at_values[self.acqCount]) self.acqCount += 1 else: # Disable/Enable GUI elements self.atStart.setEnabled(True) self.atStop.setEnabled(True) self.atSteps.setEnabled(True) self.confirmAtBtn.setEnabled(True) self.timeoutValue.setEnabled(True) self.startCenterBtn.setEnabled(True) self.startAtBtn.setEnabled(True) self.attenuationFlag = False self.acqCount = 0 # init optional # init = [np.max(self.at_results), 1/(self.at_values[-1]-self.at_values[0]), np.min(self.at_results)] init = [np.max(self.at_results), 1 / 15, np.min(self.at_results)] try: self.fit_x, self.fit_at = self.fit_At(init) self.axes.plot(self.fit_x, self.fit_at, linewidth=1, color='red') self.plotWidget.draw() except: print('ERROR: No fit found.') self.at90Value.setText(str(np.max(self.at_results))) def fit_At(self, init): # parameters = sol(func, x, y, init, method) params, params_covariance = curve_fit(self.at_func, self.at_values, self.at_results, init, method='lm') x = np.arange(self.at_values[0], self.at_values[-1] + 1, 0.1) fit = self.at_func(x, params[0], params[1], params[2]) return x, fit # Function model for sinus fitting def at_func(self, x, a, b, c): return abs(a * np.sin(b * x) + c) def at_confirmed(self): return def resetFigure(self): # Reset Plot self.axes.clear() self.axes.set_xlabel('Attenuation') self.axes.set_ylabel('Peak') self.axes.grid() self.figure.set_tight_layout(True)
def __init__(self, parent, ip): tk.Frame.__init__(self, parent) # Initialize Network Tables NetworkTables.initialize(server=ip) nt_instance = NetworkTables.getTable('Live Dashboard') # Font kanit_italic = fm.FontProperties(fname='kanitfont/kanit-italic.otf') # Figure fig = Figure(figsize=(13, 9), dpi=90) field_plot = fig.add_subplot(111) fig.set_tight_layout('True') # Robot Values robot_x_values = [1.5] robot_y_values = [23.5] robot_heading_values = [0.0] # Path Values path_x_values = [1.5] path_y_values = [23.5] path_heading_values = [0.0] # Lookahead Values lookahead_x_values = [3.5] lookahead_y_values = [23.5] # Robot Location Display robot_x_display = field_plot.text(0, -2, '', fontproperties=kanit_italic, size=11, color='#690a0f') robot_y_display = field_plot.text(0, -3, '', fontproperties=kanit_italic, size=11, color='#690a0f') robot_heading_display = field_plot.text(10, -2, '', fontproperties=kanit_italic, size=11, color='#690a0f') # Auto Display starting_pos_display = field_plot.text(53.9, -2, '', fontproperties=kanit_italic, size=9, horizontalalignment='right', color='#303030') same_side_auto_display = field_plot.text(53.9, -3, '', fontproperties=kanit_italic, size=9, horizontalalignment='right', color='#303030') cross_auto_display = field_plot.text(53.9, -4, '', fontproperties=kanit_italic, size=9, horizontalalignment='right', color='#303030') # Subsystems Display subsystems_display = field_plot.text(0, -6, '', fontproperties=kanit_italic, size=8, color='#a4969b') climb_display = field_plot.text(0, -7, '', fontproperties=kanit_italic, size=8, color='#a4969b') # Robot Status Display connection_display = field_plot.text(53.9, -6, '', fontproperties=kanit_italic, size=8, color='#a4969b', horizontalalignment='right') is_enabled_display = field_plot.text(53.9, -7, '', fontproperties=kanit_italic, size=8, color='#a4969b', horizontalalignment='right') # Game Data Display game_data_display = field_plot.text(53.9, 27.7, '', fontproperties=kanit_italic, size=12, color='#303030', horizontalalignment='right', alpha=0.9) # Title Display field_plot.text(0, 27.7, 'FRC 321 Live Dashboard', fontproperties=kanit_italic, size=20, color='#690a0f') # IP Display field_plot.text(17, 27.7, ip, fontproperties=kanit_italic, size=10, color="#303030", alpha=0.5) def draw_field(subplot): subplot.set_axis_off() red_alliance = imread('images/red_alliance.png') blue_alliance = imread('images/blue_alliance.png') subplot.imshow(red_alliance, extent=[0, 32, 0, 27]) subplot.imshow(blue_alliance, extent=[22, 54, 0, 27]) def rotate_point(p, center, angle): sin = math.sin(angle) cos = math.cos(angle) px = p[0] - center[0] py = p[1] - center[1] pxn = px * cos - py * sin pyn = px * sin + py * cos px = pxn + center[0] py = pyn + center[1] return px, py def gen_robot_square(p, heading): robot_width = 33.0 / 12.0 robot_length = 2.458 top_left = (p[0] - robot_width / 2.0, p[1] + robot_length / 2.0) top_right = (p[0] + robot_width / 2.0, p[1] + robot_length / 2.0) bottom_left = (p[0] - robot_width / 2.0, p[1] - robot_length / 2.0) bottom_right = (p[0] + robot_width / 2.0, p[1] - robot_length / 2.0) mid = (top_right[0] + 0.5, (top_right[1] + bottom_right[1]) / 2) top_left = rotate_point(top_left, p, heading) top_right = rotate_point(top_right, p, heading) bottom_left = rotate_point(bottom_left, p, heading) bottom_right = rotate_point(bottom_right, p, heading) mid = rotate_point(mid, p, heading) box = [ top_left, top_right, mid, bottom_right, bottom_left, top_left ] return box def update_text(rx, ry, rh, sp, ssa, ca, dle, dlp, dla, dre, drp, dra, ee, ep, ea, ae, ap, aa, ce, cp, cam, ic, c, e, gd): robot_x_display.set_text('Robot X: ' + "%.3f" % (rx)) robot_y_display.set_text('Robot Y: ' + "%.3f" % (ry)) robot_heading_display.set_text('Robot Angle: ' + "{0:.3f}".format(np.degrees(rh)) + '°') starting_pos_display.set_text('Starting Position: ' + sp) if sp == 'Center': same_side_auto_display.set_text('Switch Auto: 1.5 Switch') cross_auto_display.set_text('') else: same_side_auto_display.set_text('Same Side Auto: ' + ssa) cross_auto_display.set_text('Cross Auto: ' + ca) subsystem_str = 'LEFT DRIVE enc ' + \ str(dle) + ': ' + str(dlp) + '% at ' + str(dla) + 'A' subsystem_str += ' ' subsystem_str += 'RIGHT DRIVE enc ' + \ str(dre) + ': ' + str(drp) + '% at ' + str(dra) + 'A' subsystem_str += ' ' subsystem_str += 'ELEVATOR enc ' + \ str(ee) + ': ' + str(ep) + '% at ' + str(ea) + 'A' subsystem_str += ' ' subsystem_str += 'ARM enc ' + \ str(ae) + ': ' + str(ap) + '% at ' + str(aa) + 'A' subsystems_display.set_text(subsystem_str) if (ic): climb_display.set_text('CLIMB enc ' + str(ce) + ': ' + str(cp) + '% at ' + str(cam) + 'A') else: climb_display.set_text('') connection_display.set_text('Robot ' + c) is_enabled_display.set_text(e) game_data_display.set_text('Game Data: ' + gd) return [ robot_x_display, robot_y_display, robot_heading_display, starting_pos_display, same_side_auto_display, cross_auto_display, subsystems_display, climb_display, connection_display, is_enabled_display, game_data_display ] def reset_arrays(): del robot_x_values[:] del robot_y_values[:] del robot_heading_values[:] del path_x_values[:] del path_y_values[:] del path_heading_values[:] def update_plot(_, robot_point, path_point, lookahead_point, robot, robot_path, path): if nt_instance.getBoolean('Reset', False): reset_arrays() nt_instance.putBoolean('Reset', False) sp = nt_instance.getString('Starting Position', 'Left') ssa = nt_instance.getString('Near Scale Auto Mode', 'ThreeCube') ca = nt_instance.getString('Far Scale Auto Mode', 'ThreeCube') default_y = 23.5 default_heading = np.pi if sp == 'Right': default_heading = np.pi default_y = 27 - default_y elif sp == 'Center': default_y = 13.25 default_heading = 0 rx = nt_instance.getNumber('Robot X', 1.5) ry = nt_instance.getNumber('Robot Y', default_y) rh = nt_instance.getNumber('Robot Heading', default_heading) px = nt_instance.getNumber('Path X', 1.5) py = nt_instance.getNumber('Path Y', default_y) ph = nt_instance.getNumber('Path Heading', default_heading) lx = nt_instance.getNumber('Lookahead X', 3.5) ly = nt_instance.getNumber('Lookahead Y', default_y) dle = nt_instance.getNumber('Drive Left Encoder', 0) dlp = nt_instance.getNumber('Drive Left Pct', 0.0) dla = nt_instance.getNumber('Drive Left Amps', 0.0) dre = nt_instance.getNumber('Drive Right Encoder', 0) drp = nt_instance.getNumber('Drive Right Pct', 0.0) dra = nt_instance.getNumber('Drive Right Amps', 0.0) ee = nt_instance.getNumber('Elevator Encoder', 0) ep = nt_instance.getNumber('Elevator Pct', 0.0) ea = nt_instance.getNumber('Elevator Amps', 0.0) ae = nt_instance.getNumber('Arm Encoder', 0) ap = nt_instance.getNumber('Arm Pct', 0.0) aa = nt_instance.getNumber('Arm Amps', 0.0) ce = nt_instance.getNumber('Climb Encoder', 0) cp = nt_instance.getNumber('Climb Pct', 0.0) cam = nt_instance.getNumber('Climb Amps', 0.0) ic = nt_instance.getBoolean('Is Climbing', False) c = '' if NetworkTables.isConnected(): c = 'Connected' else: c = 'Disconnected' e = nt_instance.getString('Is Enabled', 'Disabled') gd = nt_instance.getString('Game Data', 'None') if rx > 0.01 or ry > .01: robot_x_values.append(rx) robot_y_values.append(ry) robot_heading_values.append(rh) if px > 0.01 or py > .01: path_x_values.append(px) path_y_values.append(py) path_heading_values.append(ph) robot_point.set_data(np.array([rx, ry])) path_point.set_data(np.array([px, py])) lookahead_point.set_data(np.array([lx, ly])) robot_data = gen_robot_square((rx, ry), rh) robot.set_data([p[0] for p in robot_data], [p[1] for p in robot_data]) robot_path.set_data(robot_x_values, robot_y_values) path.set_data(path_x_values, path_y_values) return [ robot_point, path_point, lookahead_point, robot, robot_path, path, *update_text( rx, ry, rh, sp, ssa, ca, dle, dlp, dla, dre, drp, dra, ee, ep, ea, ae, ap, aa, ce, cp, cam, ic, c, e, gd) ] def on_click(event): if event.ydata < -1.5: # Change Starting Position if event.xdata > 45 and event.xdata < 54 and event.ydata > -2.5: current = nt_instance.getString('Starting Position', 'Left') to_set = '' if current == 'Left': to_set = 'Center' reset_arrays() elif current == 'Center': to_set = 'Right' reset_arrays() else: to_set = 'Left' reset_arrays() nt_instance.putString('Starting Position', to_set) # Change Same Side Auto elif 45 < event.xdata < 54 and event.ydata > -3.5 and nt_instance.getString( 'Starting Position', 'Left') != 'Center': current = nt_instance.getString('Near Scale Auto Mode', 'ThreeCube') if current == 'ThreeCube': to_set = 'Baseline' else: to_set = 'ThreeCube' nt_instance.putString('Near Scale Auto Mode', to_set) # Change Cross Auto elif 45 < event.xdata < 54 and event.ydata > -4.5 and nt_instance.getString( 'Starting Position', 'Left') != 'Center': current = nt_instance.getString('Far Scale Auto Mode', 'ThreeCube') to_set = '' if current == 'ThreeCube': to_set = 'Baseline' else: to_set = 'ThreeCube' nt_instance.putString('Near Scale Auto Mode', to_set) draw_field(field_plot) canvas = FigureCanvasTkAgg(fig, master=parent) canvas.get_tk_widget().pack() fig.canvas.mpl_connect('button_press_event', on_click) target_path, = field_plot.plot(path_x_values, path_y_values, color='red', alpha=0.5) actual_path, = field_plot.plot(robot_x_values, robot_y_values, color='black', alpha=0.25) path_point, = field_plot.plot(path_x_values[0], path_y_values[0], marker='o', markersize=2, color='green') robot_point, = field_plot.plot(robot_x_values[0], robot_y_values[0], marker='o', markersize=2, color='blue') lookahead_point, = field_plot.plot(lookahead_x_values[0], lookahead_y_values[0], marker='*', markersize=5, color='black') starting_robot = gen_robot_square( (robot_x_values[0], robot_y_values[0]), 0.0) robot, = field_plot.plot([p[0] for p in starting_robot], [p[1] for p in starting_robot], color='#690a0f') field_plot.set_ylim(bottom=-7.25, top=28.5) ani = animation.FuncAnimation(fig, update_plot, frames=20, interval=20, fargs=(robot_point, path_point, lookahead_point, robot, actual_path, target_path), blit=True) canvas.draw()
class MyMplCanvas(FigureCanvas): '''FigureCanvas的最終的父類別其實是QWidget。''' def __init__(self, parent=None, width=5, height=4, dpi=100): # 設定中文顯示 plt.rcParams['font.family'] = ['SimHei'] # 正常顯示中文標籤 # plt.rcParams['font.family'] = ['SimSun'] # 正常顯示中文標籤 plt.rcParams['axes.unicode_minus'] = False # 正常顯示負號 self.fig = Figure(figsize=(width, height), dpi=dpi) # 新建一個figure self.fig.set_tight_layout(True) self.axes = self.fig.add_subplot(111) # 建立一個子圖,如果要建立複合圖,可在這裡修改 self.axes.hold(False) # 每次繪圖時不保留上一次繪圖的結果 FigureCanvas.__init__(self, self.fig) self.setParent(parent) '''定義FigureCanvas的尺寸策略,意思是設定FigureCanvas,使其盡可能地向外填充空間。''' FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) '''繪製靜態圖,可在這裡定義自己的繪圖邏輯''' def start_static_plot(self, qx=None): if qx != None: # 與zwquant結合,qx是zwquant的一個實例物件 df = qx.qxLib.copy() df.set_index('date', inplace=True) df.rename_axis(lambda x: pd.to_datetime(x), inplace=True) ax1 = self.axes ax1.plot(df['dret'], color='green', label='dret', linewidth=0.5) ax1.legend(loc='upper left') ax2 = ax1.twinx() ax2.plot(df['val'], color='red', label='val', linewidth=2) ax2.legend(loc='upper right') else: # 用於測試,不需要zwquant也能執行,方面快速開發自己的GUI介面 t = arange(0.0, 3.0, 0.01) s = sin(2 * pi * t) self.axes.plot(t, s) self.axes.set_ylabel('靜態圖:Y軸') self.axes.set_xlabel('靜態圖:X軸') self.axes.grid(True) '''繪製動態圖''' def start_dynamic_plot(self, *args, **kwargs): timer = QtCore.QTimer(self) timer.timeout.connect( self.update_figure) # 每隔一段時間就會觸發一次update_figure函數 timer.start(1000) # 觸發的時間間隔為1秒 '''動態圖的繪圖邏輯可以在這裡修改''' def update_figure(self): self.fig.suptitle('測試動態圖') l = [random.randint(0, 10) for i in range(4)] self.axes.plot([0, 1, 2, 3], l, 'r') self.axes.set_ylabel('動態圖:Y軸') self.axes.set_xlabel('動態圖:X軸') self.axes.grid(True) self.draw()
class CanvasFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Point Selector") self.dirname = "" self.filename = "" self.exdirname = None self.SetBackgroundColour(wx.NamedColour("WHITE")) self.SetFont(wx.Font(18 if wx.Platform == "__WXMAC__" else 11, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) # Setting up the menu. filemenu = wx.Menu() menuOpen = filemenu.Append(wx.ID_OPEN, "&Open", " Open a data file") menuExport = filemenu.Append(wx.ID_SAVE, "&Export selection", " Export selected data to a file.") menuAbout = filemenu.Append(wx.ID_ABOUT, "&About", " Information about this program") menuExit = filemenu.Append(wx.ID_EXIT, "E&xit", " Terminate the program") # Creating the menubar. menuBar = wx.MenuBar() menuBar.Append(filemenu, "&File") # Adding the "filemenu" to the MenuBar self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content. # Events. self.Bind(wx.EVT_MENU, self.onOpen, menuOpen) self.Bind(wx.EVT_MENU, self.onExport, menuExport) self.Bind(wx.EVT_MENU, self.onExit, menuExit) self.Bind(wx.EVT_MENU, self.onAbout, menuAbout) self.numSelected = 0 self.conc = 0 self.targetSelected = 0 self.numPoints = 0 self.figure = Figure(figsize=(10, 10)) self.figure.set_tight_layout(True) self.axes = self.figure.add_subplot(111) self.datfn = "" self.dat = [["", ""], array([[], []])] self.dirname, self.filename = os.path.split(self.datfn) self.plot, = self.axes.plot([], [], ",") self.axes.grid(color="k", alpha=0.75, lw=1, ls="-") self.statbar = StatusBar(self) self.SetStatusBar(self.statbar) self.canvas = FigureCanvas(self, -1, self.figure) self.canvas.parentFrame = self self.canvas.SetInitialSize(wx.Size(self.figure.bbox.width, self.figure.bbox.height)) self.canvas.SetFocus() self.sizer = wx.BoxSizer(wx.VERTICAL) # Build the side bar self.sideBar = wx.BoxSizer(wx.VERTICAL) heading = wx.StaticText(self, label="Measurements") heading.SetFont(self.GetFont().MakeBold()) self.sideBar.Add(heading, 0, wx.TOP | wx.ALIGN_CENTER) self.sideBar.Add(wx.StaticLine(self, size=(100, -1)), 0, wx.BOTTOM | wx.CENTER) self.sideBar.AddSpacer(3) self.positionDSP = wx.StaticText(self, style=wx.ALIGN_LEFT) self.sideBar.Add(self.positionDSP, 0, wx.BOTTOM | wx.LEFT) self.sideBar.AddSpacer(3) self.whDSP = wx.StaticText(self, style=wx.ALIGN_LEFT) self.sideBar.Add(self.whDSP, 0, wx.BOTTOM | wx.LEFT) self.sideBar.AddSpacer(3) self.areaDSP = wx.StaticText(self, style=wx.ALIGN_LEFT) self.sideBar.Add(self.areaDSP, 0, wx.BOTTOM | wx.LEFT) self.sideBar.AddSpacer(3) self.numberDSP = wx.StaticText(self, style=wx.ALIGN_LEFT) self.sideBar.Add(self.numberDSP, 0, wx.BOTTOM | wx.LEFT) self.sideBar.AddSpacer(3) self.concDSP = wx.StaticText(self, style=wx.ALIGN_LEFT) self.sideBar.Add(self.concDSP, 0, wx.BOTTOM | wx.LEFT) self.sideBar.AddSpacer(3) self.sideBar.Add(wx.StaticLine(self, size=(100, -1)), 0, wx.BOTTOM | wx.CENTER) self.sideBar.AddSpacer(9) self.titleCtrl = wx.TextCtrl(self, value="", size=(120, -1)) self.sideBar.Add(wx.StaticText(self, label="Title:", style=wx.ALIGN_LEFT), 0, wx.LEFT) self.sideBar.Add(self.titleCtrl, 0, wx.EXPAND) self.sideBar.AddSpacer(5) self.widthCtrl = wx.SpinCtrlDouble(self, min=0, initial=0, inc=1) self.heightCtrl = wx.SpinCtrlDouble(self, min=0, initial=0, inc=1) self.widthCtrl.SetDigits(2) self.heightCtrl.SetDigits(2) self.fixedSizeCB = wx.CheckBox(self, label="Fixed size", style=wx.ALIGN_LEFT) box = wx.StaticBoxSizer(wx.StaticBox(self, label="Size (um):"), wx.VERTICAL) box.Add(self.fixedSizeCB, 0, wx.LEFT) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add(wx.StaticText(self, label="W:", style=wx.ALIGN_RIGHT), 0, wx.CENTER) hbox.Add(self.widthCtrl, 0, wx.EXPAND | wx.LEFT) box.Add(hbox, 1, wx.EXPAND) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add(wx.StaticText(self, label="H:", style=wx.ALIGN_RIGHT), 0, wx.CENTER) hbox.Add(self.heightCtrl, 0, wx.EXPAND | wx.LEFT) box.Add(hbox, 1, wx.EXPAND) self.sideBar.Add(box, 1, wx.LEFT | wx.EXPAND) self.sideBar.AddSpacer(5) self.fixedNumberCB = wx.CheckBox(self, label="Fixed nr.", style=wx.ALIGN_LEFT) self.numPtsCtrl = wx.SpinCtrl(self, min=0, max=1000, initial=0) self.numPtsCtrl.Disable() box = wx.StaticBoxSizer(wx.StaticBox(self, label="# Points:"), wx.VERTICAL) box.Add(self.fixedNumberCB, 0, wx.LEFT) box.Add(self.numPtsCtrl, 0, wx.EXPAND) hbox = wx.BoxSizer(wx.HORIZONTAL) box.Add(hbox, 1, wx.EXPAND) self.sideBar.Add(box, 0, wx.LEFT | wx.EXPAND) self.sideBar.AddSpacer(5) # Anchor switch self.anchorRB = wx.RadioBox( self, label="Anchor:", choices=["LT", "L", "LB", "T", "C", "B", "RT", "R", "RB"], majorDimension=3, style=wx.RA_SPECIFY_ROWS, ) # Center is default self.anchorRB.SetSelection(4) for i in [1, 3, 5, 7]: self.anchorRB.ShowItem(i, False) self.sideBar.Add(self.anchorRB, 0, wx.BOTTOM | wx.LEFT | wx.EXPAND) self.sideBar.AddSpacer(9) # Flip buttons box = wx.StaticBoxSizer(wx.StaticBox(self, label="Flip data:"), wx.HORIZONTAL) self.flipXBTN = wx.BitmapButton(self, bitmap=wx.Bitmap("flip_x.png")) self.flipYBTN = wx.BitmapButton(self, bitmap=wx.Bitmap("flip_y.png")) box.Add(wx.StaticText(self, label="X:", style=wx.ALIGN_LEFT), 0, wx.CENTER) box.Add(self.flipXBTN, 0, wx.LEFT) box.Add(wx.StaticText(self, label=" Y:", style=wx.ALIGN_LEFT), 0, wx.CENTER) box.Add(self.flipYBTN, 0, wx.LEFT) self.sideBar.Add(box, 0, wx.LEFT | wx.EXPAND) self.sideBar.AddSpacer(9) # Aspect ratio switch self.aspectRB = wx.RadioBox(self, label="Aspect ratio:", choices=["auto", "equal"]) # Auto is default self.aspectRB.SetSelection(0) self.sideBar.Add(self.aspectRB, 0, wx.BOTTOM | wx.LEFT | wx.EXPAND) # Build the window # Add plot canvas and sidebar cont = wx.BoxSizer(wx.HORIZONTAL) self.sizer.Add(cont, 1, wx.TOP | wx.LEFT | wx.EXPAND) cont.Add(self.canvas, 1, wx.TOP | wx.LEFT | wx.EXPAND) cont.AddSpacer(3) cont.Add(self.sideBar, 0, wx.TOP | wx.RIGHT) cont.AddSpacer(3) # Add toolbar self.toolbar = self._get_toolbar(self.statbar) # Bind the methods to the GUI elements self.fixedSizeCB.Bind(wx.EVT_CHECKBOX, self.toolbar.onFixedSize) self.widthCtrl.Bind(wx.EVT_SPINCTRLDOUBLE, self.onWidthChange) self.heightCtrl.Bind(wx.EVT_SPINCTRLDOUBLE, self.onHeightChange) self.fixedNumberCB.Bind(wx.EVT_CHECKBOX, self.onFixedNumber) self.numPtsCtrl.Bind(wx.EVT_SPINCTRL, self.onNumberChange) self.titleCtrl.Bind(wx.EVT_TEXT, self.onTitleChange) self.anchorRB.Bind(wx.EVT_RADIOBOX, self.onAnchorChange) self.flipXBTN.Bind(wx.EVT_BUTTON, self.onFlipX) self.flipYBTN.Bind(wx.EVT_BUTTON, self.onFlipY) self.aspectRB.Bind(wx.EVT_RADIOBOX, self.onAspectChange) if self.toolbar is not None: self.toolbar.Realize() # Default window size is incorrect, so set # toolbar width to figure width. tw, th = self.toolbar.GetSizeTuple() fw, fh = self.canvas.GetSizeTuple() # By adding toolbar in sizer, we are able to put it at the bottom # of the frame - so appearance is closer to GTK version. self.toolbar.SetSize(wx.Size(fw, th)) self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) # update the axes menu on the toolbar self.toolbar.update() self.SetSizer(self.sizer) self.Fit() # Read example data. To be removed in the future. try: self.datfn = sys.argv[1] except IndexError: # Example data # self.datfn='data/A339-SiC-560-1-SiC-WDS.txt' pass try: self.dirname, self.filename = os.path.split(self.datfn) self.dat = self.readData(self.datfn) self.displayData(self.dat[1], self.dat[0]) self.axes.set_title(self.filename) except IOError: if self.datfn != "": print("Warning: Cannot open file ", self.datfn) self.titleCtrl.SetValue(self.filename) self.redrawPlot() # Init sidebar self.showLTRB() self.showWH() self.showArea() self.showNumber() self.showConc() def _get_toolbar(self, statbar): toolbar = CustomToolbar(self.canvas) # toolbar = NavigationToolbar2Wx(self.canvas) toolbar.set_status_bar(statbar) return toolbar def readData(self, fn, skip=1): """ Read and translate the data from the file named fn. The data is returned as an array of rows x cols in the second member of the returned list. The first skip (default 1) line are skipped. If the skip is >0 the contents of this line is returned in the first member of the returned list as a list of labels (split on ;). """ df = open(fn).readlines() # print(df[0].strip()) # The translation replaces ; and , by space and dot. if skip > 0: lbl = df[0].replace("#", "").strip().split(";") else: lbl = None r = [ lbl, array([map(float, ln.replace(";", " ").replace(",", ".").split()) for ln in df[skip:] if ln[0] != "#"]).T, ] d = r[1] # print(d.shape) self._shift_to_origin(d) return r def getSelected(self, lrbt=None): """ Return an array of points inside the lrbt bounding box. """ if lrbt is None: try: l, b, r, t = array(self.toolbar.roi.get_bbox()).reshape(4) except AttributeError: return None else: # The bbox is expected as l,r,b,t tuple! l, r, b, t = array(lrbt).reshape(4) # print('LTRB:', l,t,r,b) d = self.dat[1] sel = d[..., (l < d[0]) & (d[0] < r) & (b < d[1]) & (d[1] < t)] return sel def exportData(self, fn): hdr = " ;".join([" %s" % s.strip() for s in self.dat[0]]) sel = self.getSelected() try: x, y = self.toolbar.roi.get_xy() w = self.toolbar.roi.get_width() h = self.toolbar.roi.get_height() hdr += "\n" hdr += " ROI (um): X=%.2f Y=%.2f W=%.2f H=%.2f Points=%d Concentration=%g" % ( x, y, w, h, sel.shape[1], sum(sel[2]) / (w * h), ) except AttributeError: # No roi pass if sel is None: wx.MessageBox( "Nothing to save yet. Make some selection before trying to export data.", "Nothing to export!" ) else: d = array(sel) # Shift exported data to the origin d[0] -= min(d[0]) d[1] -= min(d[1]) np.savetxt(fn, d.T, fmt="%.3f", delimiter=" ", newline="\n", header=hdr, footer="", comments="#") def setLimits(self): self.widthCtrl.SetMax(self.maxX - self.minX) self.heightCtrl.SetMax(self.maxY - self.minY) self.numPtsCtrl.SetRange(0, self.numPoints) def showArea(self, a=0): self.areaDSP.SetLabel("Area (um^2): \n %-8g" % (a)) def showLTRB(self, l=0, t=0, r=0, b=0): self.positionDSP.SetLabel(u"Position (um): \n L: %-8g\n T: %-8g\n R: %-8g\n B: %-8g" % (l, t, r, b)) def showWH(self, w=0, h=0): self.whDSP.SetLabel("Size (um): \n W: %-8g\n H: %-8g" % (w, h)) def showNumber(self, n=0): self.numberDSP.SetLabel("Selected pnts: \n %-d" % (n)) def showConc(self, g=0): self.concDSP.SetLabel("Concentration: \n %.3f" % (g)) def showROI(self, x, y, w, h): self.showLTRB(l=x, t=y + h, r=x + w, b=y) self.showArea(w * h) self.showWH(w, h) def setWH(self, w, h): self.widthCtrl.SetValue(w) self.heightCtrl.SetValue(h) self.showArea(w * h) self.showWH(w, h) try: sel = self.getSelected() self.numSelected = sel.shape[1] self.conc = sum(sel[2]) / (w * h) except AttributeError: self.numSelected = 0 self.conc = 0.0 if not self.fixedNumberCB.IsChecked(): self.numPtsCtrl.SetValue(self.numSelected) self.showNumber(self.numSelected) self.showConc(self.conc) def displayData(self, dat, lbl=None, cols=(0, 1)): """ Display the points in the cols of the dat array. The axes are lebeled with labels from the lbl parameter. The lbl must contain a list of labels for columns. """ self.axes.set_autoscale_on(True) # self.plot.set_data([],[]) self.plot.set_data(dat[cols[0]], dat[cols[1]]) self.titleCtrl.SetValue(self.filename) if lbl: self.axes.set_xlabel(lbl[cols[0]]) self.axes.set_ylabel(lbl[cols[1]]) # self.axes.legend((self.filename,)) def set_markers(self): sel = self.getSelected(self.axes.get_xlim() + self.axes.get_ylim()) if sel.shape[1] < 5000: self.plot.set_marker("o") else: self.plot.set_marker(",") def redrawPlot(self): self.axes.relim() self.axes.autoscale_view(True, True, True) self.set_markers() self.figure.canvas.draw() def onAbout(self, e): # Create a message dialog box dlg = wx.MessageDialog(self, "A data point selector", "About PointSel", wx.OK) dlg.ShowModal() # Shows it dlg.Destroy() # finally destroy it when finished. def onExit(self, e): self.Close(True) # Close the frame. def onOpen(self, e): """ Open a file""" dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", wx.OPEN) if dlg.ShowModal() == wx.ID_OK: self.filename = dlg.GetFilename() self.dirname = dlg.GetDirectory() self.datfn = os.path.join(self.dirname, self.filename) try: self.dat = self.readData(self.datfn) self.displayData(self.dat[1], self.dat[0]) self.updateROI(0, 0, self.maxX, self.maxY) self.axes.set_xlim(0, self.maxX) self.axes.set_ylim(0, self.maxY) self.toolbar._views.clear() self.toolbar._positions.clear() self.toolbar.push_current() self.redrawPlot() except (IOError, IndexError, ValueError) as ex: wx.MessageBox( "The data from:\n\n" + self.datfn + "\n\ncould not be read properly." + "\nProbably the format is incorrect.", "Error reading data", ) dlg.Destroy() def onPaint(self, event): self.canvas.draw() def onExport(self, e): """Export the selected points""" if self.exdirname is None: self.exdirname = self.dirname dlg = wx.FileDialog( self, "Choose a file", self.exdirname, "*.txt", "Data file (*.txt)|*.txt|" + "Data file (*.dat)|*.dat|" + "All files (*.*)|*.*", wx.SAVE | wx.FD_OVERWRITE_PROMPT, ) if dlg.ShowModal() == wx.ID_OK: # The file name is local here. # We are saving a selection not the data. filename = dlg.GetFilename() self.exdirname = dlg.GetDirectory() self.exportData(os.path.join(self.exdirname, filename)) dlg.Destroy() def onFixedSize(self, ev): if self.toolbar: self.toolbar.onFixedSize(ev) def onFixedNumber(self, ev): if self.fixedNumberCB.IsChecked(): if self.toolbar.roi is None: wx.MessageBox( "You need to make some selection before " + "using fixed number of points selection mode.", "Make a selection!", ) self.fixedNumberCB.SetValue(False) return self.numPtsCtrl.Enable() self.fixedSizeCB.SetValue(True) self.targetSelected = self.numPtsCtrl.GetValue() self.onFixedSize(ev) self.handleROIforN() else: self.numPtsCtrl.Disable() def updateROI(self, x, y, w, h): self.showArea(w * h) self.showLTRB(l=x, t=y + h, r=x + w, b=y) self.toolbar.updateROI(x, y, w, h) self.redrawPlot() def onWidthChange(self, ev): if self.toolbar: self.toolbar.onWidthChange(ev) def onHeightChange(self, ev): if self.toolbar: self.toolbar.onHeightChange(ev) def onNumberChange(self, ev): if not self.fixedNumberCB.IsChecked(): # Just reset the value to the number of selected points. self.numPtsCtrl.SetValue(self.numSelected) return self.targetSelected = self.numPtsCtrl.GetValue() self.handleROIforN() def onTitleChange(self, ev): self.axes.set_title(ev.GetString()) self.redrawPlot() def onAnchorChange(self, ev): pass # s=self.anchorRB.GetSelection() # print(self.anchorRB.GetString(s)) def _shift_to_origin(self, d=None): if d is None: d = self.dat[1] d[0] -= min(d[0]) d[1] -= min(d[1]) self.minX = 0 self.minY = 0 self.maxX = max(d[0]) self.maxY = max(d[1]) self.numPoints = d.shape[1] self.setLimits() def onFlipX(self, ev): self.dat[1][0] = -self.dat[1][0] self._shift_to_origin() self.plot.set_xdata(self.dat[1][0]) self.toolbar.updateCanvas() def onFlipY(self, ev): self.dat[1][1] = -self.dat[1][1] self._shift_to_origin() self.plot.set_ydata(self.dat[1][1]) self.toolbar.updateCanvas() def onAspectChange(self, ev): s = self.aspectRB.GetString(self.aspectRB.GetSelection()) self.axes.set_aspect(s, "datalim") if s == "auto": self.axes.set_xlim(0, self.maxX) self.axes.set_ylim(0, self.maxY) self.redrawPlot() def handleROIforN(self): """ GUI part of fixed number selection mode """ if not self.fixedNumberCB.IsChecked(): # Not our mode. Nothing to do! return n = self.targetSelected x, y = self.toolbar.roi.get_xy() w = self.toolbar.roi.get_width() h = self.toolbar.roi.get_height() ncx, ncy, tw = self.findROIforN(x, y, w, h, n, self.anchorRB.GetString(self.anchorRB.GetSelection())) # print('ROIforN:',cx,cy,tw) self.updateROI(ncx, ncy, tw, tw) self.setWH(tw, tw) def findROIforN(self, x, y, w, h, n, fp="C"): """ Find the squere ROI around target point (cx, cy) containing as close as possible to target number of points (n). The function does not care about the GUI. Just the computation. """ def optfunC(w, x, y, d): hw = w / 2 l = x - hw b = y - hw r = x + hw t = y + hw return n - np.count_nonzero((l < d[0]) & (d[0] < r) & (b < d[1]) & (d[1] < t)) def optfunLB(w, x, y, d): l = x b = y r = x + w t = y + w return n - np.count_nonzero((l < d[0]) & (d[0] < r) & (b < d[1]) & (d[1] < t)) def optfunLT(w, x, y, d): l = x b = y - w r = x + w t = y return n - np.count_nonzero((l < d[0]) & (d[0] < r) & (b < d[1]) & (d[1] < t)) def optfunRT(w, x, y, d): l = x - w b = y - w r = x t = y return n - np.count_nonzero((l < d[0]) & (d[0] < r) & (b < d[1]) & (d[1] < t)) def optfunRB(w, x, y, d): l = x - w b = y r = x t = y + w return n - np.count_nonzero((l < d[0]) & (d[0] < r) & (b < d[1]) & (d[1] < t)) optfun = {"C": optfunC, "LB": optfunLB, "LT": optfunLT, "RB": optfunRB, "RT": optfunRT} # print(fp) d = self.dat[1] minW = 0 maxW = 2 * max(self.maxX - self.minX, self.maxY - self.minY) if fp == "C": cx = x + w / 2 cy = y + h / 2 elif fp == "LB": cx = x cy = y elif fp == "LT": cx = x cy = y + h elif fp == "RT": cx = x + w cy = y + h elif fp == "RB": cx = x + w cy = y else: print("This should not happen! Inform the author") cx = min(cx, self.maxX) cx = max(cx, self.minX) cy = min(cy, self.maxY) cy = max(cy, self.minY) try: nw = bisect(optfun[fp], minW, maxW, args=(cx, cy, d), xtol=10e-12) except ValueError: # wx.MessageBox('Cannot find a good solution for the selection box. ', # 'Solver error!') return x, y, math.sqrt(w * h) if fp == "C": cx -= nw / 2 cy -= nw / 2 elif fp == "LB": pass elif fp == "LT": cy -= nw elif fp == "RT": cx -= nw cy -= nw elif fp == "RB": cx -= nw else: print("This should not happen! Inform the author") return cx, cy, nw
class TimeCanvas(FigureCanvasQTAgg): """Plotting info with UTC timestamp on the x-axis.""" def __init__(self, parent=None, width=5, height=4, dpi=100): logging.info("Initialize TimeCanvas") self.fig = Figure(figsize=(width, height), dpi=dpi) self.main = parent self.trajectories: Dict[str, List[Artist]] = defaultdict(list) self.lock = Lock() FigureCanvasQTAgg.__init__(self, self.fig) self.setParent(parent) FigureCanvasQTAgg.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding) FigureCanvasQTAgg.updateGeometry(self) self.create_plot() def create_plot(self): with plt.style.context("traffic"): self.fig.clear() self.ax = self.fig.add_subplot(111) self.fig.set_tight_layout(True) def plot_callsigns( self, traffic: Traffic, callsigns: List[str], y: List[str], secondary_y: List[str], ) -> None: if len(y) == 0: y = ["altitude"] extra_dict = dict() if len(y) > 1: # just to avoid confusion... callsigns = callsigns[:1] for key, value in self.trajectories.items(): for elt in value: elt.remove() self.trajectories.clear() for callsign in callsigns: flight = traffic[callsign] if len(y) == 1: extra_dict["label"] = callsign if flight is not None: try: flight.plot_time(self.ax, y=y, secondary_y=secondary_y, **extra_dict) except Exception: # no numeric data to plot pass if len(callsigns) > 1: self.ax.legend() for elt in self.ax.get_xticklabels(): elt.set_size(12) for elt in self.ax.get_yticklabels(): elt.set_size(12) self.ax.set_xlabel("") if len(callsigns) > 0: low, up = self.ax.get_ylim() if (up - low) / up < 0.05: self.ax.set_ylim(up - 0.05 * up, up + 0.05 * up) if len(callsigns) > 0 and len(secondary_y) > 0: ax2, _ = next(iter(self.ax.get_shared_x_axes())) low, up = ax2.get_ylim() if (up - low) / up < 0.05: ax2.set_ylim(up - 0.05 * up, up + 0.05 * up) self.draw() def draw(self): with self.lock: if self.fig is None: return super().draw()
class Kanvas(FigCanvas): """ Canvas za prikaz grafova init sa dictom lebela za osi """ def __init__(self, meta=None, parent=None, width=3, height=3, dpi=100): self.fig = Figure(figsize=(width, height), dpi=dpi) self.axes = self.fig.add_subplot(111) FigCanvas.__init__(self, self.fig) self.setParent(parent) FigCanvas.setSizePolicy(self, QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed) FigCanvas.updateGeometry(self) self.meta = meta self.setup_labels() self.fig.set_tight_layout(True) def set_slope_offset(self, s, o): """setter za slope i offset ako je provjera linearnosti ON""" self.slope = s self.offset = o def setup_labels(self): """ Try to set labels to graph """ try: self.axes.set_xlabel(self.meta['xlabel'], fontsize=8) self.axes.set_ylabel(self.meta['ylabel'], fontsize=8) self.axes.set_title(self.meta['title'], fontsize=10) except (KeyError, ValueError, TypeError): pass def clear_graf(self): """ clear graf & redo labels """ self.axes.clear() self.setup_labels() def crtaj(self, x, y): """ Naredba za plot, trazi listu x, y tocaka. """ self.clear_graf() self.axes.scatter(x, y, marker='o', s=10, color='b', alpha=0.5) minimum = min(x) maksimum = max(x) delta = (maksimum - minimum) / 20 minimum = minimum - delta maksimum = maksimum + delta self.axes.set_xlim((minimum, maksimum)) allXLabels = self.axes.get_xticklabels(which='both') #dohvati sve labele for label in allXLabels: label.set_rotation(20) label.set_fontsize(8) try: a = float(self.slope) b = float(self.offset) except Exception as err: logging.warning('slope i/ili offset nije broj. slope={0}, offset={1}'.format(str(self.slope), str(self.offset))) a = None b = None if a is not None and b is not None: xos = [minimum, maksimum] yos = [(a*minimum)+b, (a*maksimum)+b] #korelacija i slope/offset labeli try: korelacija = np.corrcoef(x, y)[0][1] korelacija = round(korelacija, 4) except Exception as err: logging.error('problem kod racunanja korelacije, {0}'.format(str(err))) korelacija = np.NaN if b > 0: tekstl1 = 'pravac: c={0}*cref+{1}'.format(str(round(a, 2)), str(round(b, 2))) elif b < 0: tekstl1 = 'pravac: c={0}*cref{1}'.format(str(round(a, 2)), str(round(b, 2))) else: tekstl1 = 'pravac: c={0}*cref'.format(str(round(a, 2))) tekstl2 = 'korelacija = ' + str(korelacija) tekst = "\n".join([tekstl1, tekstl2]) self.axes.text(0.8, 0.2, tekst, horizontalalignment='center', verticalalignment='center', fontsize=8, transform = self.axes.transAxes, bbox=dict(facecolor='blue', alpha=0.2)) self.axes.plot(xos, yos, 'k-') self.draw()
class RuntimeTab(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) self.prime_sizer = wx.BoxSizer(wx.VERTICAL) self.prime_figure = Figure() self.prime_figure.patch.set_alpha(0) plt.rc('font', family='sans-serif', size=10) plt.rc('mathtext', default='regular') # Create nested GridSpec gsp = gridspec.GridSpec(2, 2, height_ratios=[2, 3]) self.cc_axes = self.prime_figure.add_subplot(gsp[0]) self.comp_axes = self.prime_figure.add_subplot(gsp[1]) self.mult_axes = self.comp_axes.twinx() self.rej_table = self.prime_figure.add_subplot(gsp[2]) gsub = gridspec.GridSpecFromSubplotSpec(3, 1, subplot_spec=gsp[3], hspace=0) self.bcc_axes = self.prime_figure.add_subplot(gsub[0]) self.bcomp_axes = self.prime_figure.add_subplot(gsub[1], sharex=self.bcc_axes) self.bmult_axes = self.prime_figure.add_subplot(gsub[2], sharex=self.bcc_axes) self.draw_axes() # Set layout, create canvas, add to sizer self.prime_figure.set_tight_layout(True) self.canvas = FigureCanvas(self, -1, self.prime_figure) self.prime_sizer.Add(self.canvas, proportion=1, flag =wx.EXPAND) # Initialize main sizer self.SetSizer(self.prime_sizer) def draw_axes(self): self.rej_table.axis('off') self.cc_axes.set_title(r'$CC_{1/2}$', fontsize=12) self.cc_axes.set_xlabel('Cycle') self.cc_axes.set_ylabel(r'$CC_{1/2}$ (%)') self.comp_axes.set_title('Completeness / Multiplicity', fontsize=12) self.comp_axes.set_xlabel('Cycle') self.comp_axes.set_ylabel('Completeness (%)') self.mult_axes.set_ylabel('# of Observations') self.bcc_axes.yaxis.get_major_ticks()[0].label1.set_visible(False) self.bcc_axes.yaxis.get_major_ticks()[-1].label1.set_visible(False) self.bcc_axes.set_ylabel(r'$CC_{1/2}$ (%)') plt.setp(self.bcc_axes.get_xticklabels(), visible=False) self.bcomp_axes.yaxis.get_major_ticks()[0].label1.set_visible(False) self.bcomp_axes.yaxis.get_major_ticks()[-1].label1.set_visible(False) self.bcomp_axes.set_ylabel("Comp (%)") plt.setp(self.bcomp_axes.get_xticklabels(), visible=False) self.bmult_axes.yaxis.get_major_ticks()[0].label1.set_visible(False) self.bmult_axes.yaxis.get_major_ticks()[-1].label1.set_visible(False) self.bmult_axes.set_xlabel("Resolution ($\AA$)") self.bmult_axes.set_ylabel("# of Obs") self.prime_sizer.Layout() def draw_plots(self, info, total_cycles): # Plot mean CC1/2 meanCC = info['total_cc12'] cycles = range(len(meanCC)) self.cc_axes.clear() self.cc_axes.plot(cycles, meanCC, 'o', c='#2b8cbe', ls='-', lw=3) self.cc_axes.set_xlim(0, total_cycles) # Plot mean completeness and multiplicity mean_comp = info['total_completeness'] mean_mult = info['total_n_obs'] cycles = range(len(mean_comp)) self.comp_axes.clear() self.mult_axes.clear() self.comp_axes.set_xlim(0, total_cycles) self.mult_axes.set_xlim(0, total_cycles) self.comp_axes.plot(cycles, mean_comp, c='#f03b20', ls='-', lw=2) comp = self.comp_axes.scatter(cycles, mean_comp, marker='o', s=25, edgecolors='black', color='#f03b20') self.mult_axes.plot(cycles, mean_mult, c='#feb24c', ls='-', lw=2) mult = self.mult_axes.scatter(cycles, mean_mult, marker='o', s=25, edgecolors='black', color='#feb24c') labels = ['Completeness', 'Multiplicity'] self.comp_axes.legend([comp, mult], labels, loc='upper right', fontsize=9, fancybox=True) # Binned bar plots x = info['binned_resolution'][-1] bins = np.arange(len(x)) xticks = bins[0::len(bins) // 6] xlabels = ["{:.2f}".format(i) for i in x] # plot binned stats self.bcc_axes.clear() self.bcc_axes.bar(bins, info['binned_cc12'][-1], color='#2b8cbe', alpha=0.5, width=1, lw=0) self.bcc_axes.step(bins, info['binned_cc12'][-1], color='blue', where='post') self.bcomp_axes.clear() self.bcomp_axes.bar(bins, info['binned_completeness'][-1], alpha=0.5, color='#f03b20', width=1, lw=0) self.bcomp_axes.step(bins, info['binned_completeness'][-1], color='red', where='post') self.bmult_axes.clear() self.bmult_axes.bar(bins, info['binned_n_obs'][-1], alpha=0.5, color='#feb24c', width=1, lw=0) self.bmult_axes.step(bins, info['binned_n_obs'][-1], color='orange', where='post') # Set x-axis tick labels self.bmult_axes.set_xticks(xticks) self.bmult_axes.set_xticklabels(xlabels) self.draw_axes() # Rejection table txt = 'No. good frames: {}\n' \ 'No. bad CC frames: {}\n' \ 'No. bad G frames: {}\n' \ 'No. bad unit cell frames: {}\n' \ 'No. bad gamma_e frames: {}\n' \ 'No. bad SE frames: {}\n' \ 'No. observations: {}\n' \ ''.format(info['n_frames_good'][-1], info['n_frames_bad_cc'][-1], info['n_frames_bad_G'][-1], info['n_frames_bad_uc'][-1], info['n_frames_bad_gamma_e'][-1], info['n_frames_bad_SE'][-1], info['n_observations'][-1]) self.rej_table.clear() self.rej_table.axis('off') font = {'family': 'monospace', 'color': 'darkblue', 'weight': 'normal', 'size': 13, 'linespacing': 2.5 } self.rej_table.text(0, 0.85, txt, fontdict=font, transform=self.rej_table.transAxes, va='top') # Redraw canvas self.canvas.draw()
class MyMplCanvas(FigureCanvas): """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).""" def __init__(self, streamline, parent=None, width=5, height=4, dpi=50, subs=1): self.fig = Figure(figsize=(width - 50, height), dpi=dpi) pos = 0 self.sl = streamline self.subs = subs self.cpunum = self.sl.mDevice.cpu_num self.gpu_pos = 0 self.fps_pos = 0 self.temp_pos = 0 self.axes = [] for i in range(self.cpunum): self.axes.append(self.fig.add_subplot(self.subs, 1, i + 1)) # For CPU CORES # We want the axes cleared every time plot() is called self.axes[i].set_title('CPU' + str(i)) # self.axes[i].set_xticks([]) # not show x self.axes[i].set_xlim(0, 20000) self.axes[i].set_ylim(0, 2500) if self.sl.mDevice.show_gpu == 1: self.gpu_pos = pos self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + pos + 1)) # FOR GPU self.axes[self.cpunum + self.gpu_pos].set_title('GPU') self.axes[self.cpunum + self.gpu_pos].set_xlim(0, 20000) self.axes[self.cpunum + self.gpu_pos].set_ylim(0, 850) pos += 1 if self.sl.mDevice.show_fps == 1: self.fps_pos = pos self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + self.fps_pos + 1)) # FOR FPS self.axes[self.cpunum + self.fps_pos].set_title('FPS') self.axes[self.cpunum + self.fps_pos].set_xlim(0, 20000) self.axes[self.cpunum + self.fps_pos].set_ylim(0, 100) pos += 1 if self.sl.mDevice.show_temp == 1: self.temp_pos = pos self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + self.temp_pos + 1)) # FOR CPU TEMP self.axes[self.cpunum + self.temp_pos].set_title('CPU Temperature') self.axes[self.cpunum + self.temp_pos].set_xlim(0, 20000) self.axes[self.cpunum + self.temp_pos].set_ylim(0, 100) self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + self.temp_pos + 2)) # FOR BOARD TEMP self.axes[self.cpunum + self.temp_pos + 1].set_title('Board Temperature') self.axes[self.cpunum + self.temp_pos + 1].set_xlim(0, 20000) self.axes[self.cpunum + self.temp_pos + 1].set_ylim(0, 100) self.fig.set_tight_layout(True) self.compute_initial_figure() FigureCanvas.__init__(self, self.fig) self.setParent(parent) FigureCanvas.setFixedSize(self, width - 50, subs * 100) FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) def compute_initial_figure(self): pass
class MyPlotPanel(wx.Panel): def __init__(self, parent, figsize=None, dpi=None, bgcolor=None, type=None, toolbar=None, aspect='auto', **kwargs): """ construction method of MyPlotPanel class :param parent: parent object :param figsize: plot figure size, (w, h) :param dpi: figure dpi, :parma bgcolor: background color of figure and canvas :param type: type of initial figure, 'image' or 'line' :param toolbar: show toolbar if not set None :param aspect: axes aspect, float number or 'auto' by default """ wx.Panel.__init__(self, parent, **kwargs) self.parent = parent self.figsize = figsize self.dpi = dpi self.bgcolor = bgcolor self.type = type self.toolbar = toolbar self.aspect = aspect self.figure = Figure(self.figsize, self.dpi) self.canvas = FigureCanvas(self, -1, self.figure) # figure background color self.set_color(self.bgcolor) # initialize plot self._init_plot() # set layout self.set_layout() # post-initialization self._post_init() # binding events self.canvas.mpl_connect('button_press_event', self.on_press) self.canvas.mpl_connect('button_release_event', self.on_release) self.canvas.mpl_connect('motion_notify_event', self.on_motion) self.canvas.mpl_connect('pick_event', self.on_pick) self.Bind(wx.EVT_SIZE, self.on_size) self.xylim_choice.Bind(wx.EVT_CHOICE, self.xylim_choiceOnChoice) self.minlim_tc.Bind(wx.EVT_TEXT_ENTER, self.minlim_tcOnTextEnter) self.maxlim_tc.Bind(wx.EVT_TEXT_ENTER, self.maxlim_tcOnTextEnter) def set_layout(self): """ set panel layout """ sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, 1, wx.EXPAND) hbox = wx.BoxSizer(wx.HORIZONTAL) # set toolbar if defined if self.toolbar is not None: self.toobar = MyToolbar(self.canvas) self.toobar.Realize() hbox.Add(self.toobar, 0, wx.EXPAND | wx.RIGHT, 5) # add x[y]lim control xylim_hbox = wx.BoxSizer(wx.HORIZONTAL) xy_vbox = wx.BoxSizer(wx.VERTICAL) xylim_choiceChoices = [u"X-Limit", u"Y-Limit", u"Auto"] self.xylim_choice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, xylim_choiceChoices, 0) self.xylim_choice.SetSelection(0) xy_vbox.Add(self.xylim_choice, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) xylim_hbox.Add(xy_vbox, 0, wx.ALIGN_CENTER_VERTICAL, 1) lim_vbox = wx.BoxSizer(wx.VERTICAL) min_hbox = wx.BoxSizer(wx.HORIZONTAL) self.minlim_st = wx.StaticText(self, wx.ID_ANY, u"Min", wx.DefaultPosition, wx.DefaultSize, 0) self.minlim_st.Wrap(-1) self.minlim_st.SetFont(wx.Font(6, 70, 90, 90, False, "Monospace")) min_hbox.Add(self.minlim_st, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.TOP, 1) self.minlim_tc = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.minlim_tc.SetFont(wx.Font(6, 70, 90, 90, False, "Monospace")) self.minlim_tc.SetToolTip(u"Min of Limit") min_hbox.Add(self.minlim_tc, 0, wx.ALIGN_CENTER_VERTICAL | wx.TOP, 1) lim_vbox.Add(min_hbox, 1, wx.EXPAND, 1) max_hbox = wx.BoxSizer(wx.HORIZONTAL) self.maxlim_st = wx.StaticText(self, wx.ID_ANY, u"Max", wx.DefaultPosition, wx.DefaultSize, 0) self.maxlim_st.Wrap(-1) self.maxlim_st.SetFont(wx.Font(6, 70, 90, 90, False, "Monospace")) max_hbox.Add(self.maxlim_st, 0, wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.RIGHT | wx.TOP, 1) self.maxlim_tc = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER) self.maxlim_tc.SetFont(wx.Font(6, 70, 90, 90, False, "Monospace")) self.maxlim_tc.SetToolTip(u"Max of Limit") max_hbox.Add(self.maxlim_tc, 0, wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.TOP, 1) lim_vbox.Add(max_hbox, 1, wx.EXPAND, 1) xylim_hbox.Add(lim_vbox, 0, wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 1) hbox.Add(xylim_hbox, 0, wx.EXPAND | wx.RIGHT, 5) # (x, y) pos label self.pos_st = wx.StaticText(self, label='') hbox.Add(self.pos_st, 0, wx.ALIGN_CENTER_VERTICAL) sizer.Add(hbox, 0, wx.EXPAND | wx.BOTTOM, 0) self.SetSizerAndFit(sizer) def _init_plot(self): if not hasattr(self, 'axes'): self.axes = self.figure.add_subplot(111, aspect=self.aspect) if self.type == 'image': # draw image x = y = np.linspace(-np.pi, np.pi, 100) self.x, self.y = np.meshgrid(x, y) self.z = self._func_peaks(self.x, self.y) self.image = self.axes.imshow(self.z) else: # draw line self.x = np.linspace(-10, 10, 200) self.y = np.sin(self.x) self.line, = self.axes.plot(self.x, self.y) def _post_init(self): self._set_xylim_flag(self.xylim_choice.GetStringSelection()) def set_color(self, rgb_tuple): """ set figure and canvas with the same color. :param rgb_tuple: rgb color tuple, e.g. (255, 255, 255) for white color """ if rgb_tuple is None: #rgb_tuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() rgb_tuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_DESKTOP).Get() clr = [c / 255.0 for c in rgb_tuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgb_tuple)) def on_size(self, event): self.fit_canvas() self.canvas.draw_idle() event.Skip() def on_press(self, event): pass def on_release(self, event): pass def on_pick(self, event): pass def on_motion(self, event): if event.inaxes is not None: self.pos_st.SetLabel( "({x:<.4f}, {y:<.4f})".format(x=event.xdata, y=event.ydata)) def fit_canvas(self): """ tight fit canvas layout """ #self.canvas.SetSize(self.GetSize()) self.figure.set_tight_layout(True) def _func_peaks(self, x, y): return 3.0 * (1.0 - x)**2.0 * np.exp(-(x**2) - (y+1)**2) \ - 10*(x/5 - x**3 - y**5) * np.exp(-x**2-y**2) \ - 1.0/3.0*np.exp(-(x+1)**2 - y**2) def refresh(self): self.canvas.draw_idle() def xylim_choiceOnChoice(self, event): sel_str = self.xylim_choice.GetStringSelection() self._set_xylim_flag(sel_str) if sel_str == 'Auto': self.minlim_tc.Disable() self.maxlim_tc.Disable() self.minlim_st.Disable() self.maxlim_st.Disable() # auto set xy limit min_list = [ np.vstack(line.get_data()).min(axis=1).tolist() for line in self.axes.get_lines() ] max_list = [ np.vstack(line.get_data()).max(axis=1).tolist() for line in self.axes.get_lines() ] xmin, ymin = np.array(min_list).min(axis=0) xmax, ymax = np.array(max_list).max(axis=0) x0, xhw = (xmin + xmax) * 0.5, (xmax - xmin) * 0.5 y0, yhw = (ymin + ymax) * 0.5, (ymax - ymin) * 0.5 _xmin, _xmax = x0 - xhw * 1.1, x0 + xhw * 1.1 _ymin, _ymax = y0 - yhw * 1.1, y0 + yhw * 1.1 self.axes.set_xlim([_xmin, _xmax]) self.axes.set_ylim([_ymin, _ymax]) self.refresh() else: self.minlim_tc.Enable() self.maxlim_tc.Enable() self.minlim_st.Enable() self.maxlim_st.Enable() try: _xlim = self.axes.get_xlim() _ylim = self.axes.get_ylim() except: _xlim = [0, 100] _ylim = [0, 100] self._set_default_minlim(_xlim, _ylim) self._set_default_maxlim(_xlim, _ylim) def _set_default_minlim(self, xlim_array, ylim_array): if self._xylim == 'X-Limit': self.minlim_tc.SetValue("{xmin:.3g}".format(xmin=xlim_array[0])) elif self._xylim == 'Y-Limit': self.minlim_tc.SetValue("{ymin:.3g}".format(ymin=ylim_array[0])) def _set_default_maxlim(self, xlim_array, ylim_array): if self._xylim == 'X-Limit': self.maxlim_tc.SetValue("{xmax:.3g}".format(xmax=xlim_array[1])) elif self._xylim == 'Y-Limit': self.maxlim_tc.SetValue("{ymax:.3g}".format(ymax=ylim_array[1])) def minlim_tcOnTextEnter(self, event): xymin = float(self.minlim_tc.GetValue()) xymax = float(self.maxlim_tc.GetValue()) self._set_xylim_range(xymin, xymax) def maxlim_tcOnTextEnter(self, event): xymin = float(self.minlim_tc.GetValue()) xymax = float(self.maxlim_tc.GetValue()) self._set_xylim_range(xymin, xymax) def _set_xylim_flag(self, flag='X-Limit'): """ control x/y limit to be set :param flag: 'X-Limit' or 'Y-Limit' """ self._xylim = flag def _set_xylim_range(self, vmin, vmax): """ set x/y limit according to _xylim value :param vmin: min of limit :param vmax: max of limit """ if self._xylim == 'X-Limit': self.axes.set_xlim([vmin, vmax]) elif self._xylim == 'Y-Limit': self.axes.set_ylim([vmin, vmax]) self.refresh()
class RiseFallKanvas(FigCanvas): """ Canvas za prikaz grafova init sa dictom lebela za osi """ def __init__(self, meta=None, parent=None, width=8, height=5, dpi=100): self.fig = Figure(figsize=(width, height), dpi=dpi) self.axes = self.fig.add_subplot(111) FigCanvas.__init__(self, self.fig) self.setParent(parent) FigCanvas.setSizePolicy(self, QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed) FigCanvas.updateGeometry(self) self.meta = meta self.setup_labels() self.fig.set_tight_layout(True) self.cid = self.mpl_connect('button_press_event', self.on_pick) #callback za pick def on_pick(self, event): """callback metoda za button_press_event na grafu. Emitira se pandas timestamp.""" if event.inaxes == self.axes: try: xpoint = matplotlib.dates.num2date(event.xdata) #datetime.datetime #problem.. rounding offset aware i offset naive datetimes..workaround xpoint = datetime.datetime(xpoint.year, xpoint.month, xpoint.day, xpoint.hour, xpoint.minute, xpoint.second) xpoint = pd.to_datetime(xpoint) self.emit(QtCore.SIGNAL('izabrano_vrijeme(PyQt_PyObject)'), xpoint) except Exception as err: logging.error(str(err), exc_info=True) def setup_labels(self): """ Try to set labels to graph """ try: self.axes.set_xlabel(self.meta['xlabel'], fontsize=8) self.axes.set_ylabel(self.meta['ylabel'], fontsize=8) self.axes.set_title(self.meta['title'], fontsize=10) except (KeyError, ValueError, TypeError): pass def clear_graf(self): """ clear graf & redo labels """ self.axes.clear() self.setup_labels() def crtaj(self, podaci=None, rezultati=None, high=90, low=10): """ naredba za plot -podaci su frejm sirovih podataka -rezultati su frejm izabranih vremena upona i padova """ self.clear_graf() #print podataka x = list(podaci.index) y = list(podaci[:]) self.axes.plot(x, y, color='b', linewidth=0.8) #set vertikalni raspon ymin = 0 ymax = 10 if len(y): ymin = min(y) ymax = max(y) delta = (ymax - ymin) / 20 ymin = ymin - delta ymax = ymax + delta self.axes.set_ylim((ymin, ymax)) #set horizontalni raspon minimum = 0 maksimum = 10 if len(x): minimum = min(x) maksimum = max(x) delta = (maksimum - minimum) / 100 minimum = minimum - delta maksimum = maksimum + delta self.axes.set_xlim((minimum, maksimum)) #label size & orientation allXLabels = self.axes.get_xticklabels(which='both') for label in allXLabels: label.set_rotation(20) label.set_fontsize(8) #print vertikalnih linija iz rezultata indeksi = rezultati.index if len(indeksi) != 0: for ind in indeksi: value = rezultati.loc[ind, 'Naziv'] start = rezultati.loc[ind, 'Pocetak'] kraj= rezultati.loc[ind, 'Kraj'] delta = rezultati.loc[ind, 'Delta'] if isinstance(kraj, pd.tslib.NaTType): pass else: if 'RISE' in value: self.axes.vlines(start, ymin, ymax, linestyles='dotted', color='g') self.axes.vlines(kraj, ymin, ymax, linestyles='dotted', color='g') self.axes.axvspan(start, kraj, color='g', alpha=0.2) elif 'FALL' in value: self.axes.vlines(start, ymin, ymax, linestyles='dotted', color='r') self.axes.vlines(kraj, ymin, ymax, linestyles='dotted', color='r') self.axes.axvspan(start, kraj, color='r', alpha=0.2) else: pass # raspon za rise i fall self.axes.hlines(low, minimum, maksimum, linestyles='dashed', color='k', alpha=0.4) self.axes.hlines(high, minimum, maksimum, linestyles='dashed', color='k', alpha=0.4) #zavrsna naredba za crtanje self.draw()
class GrafPreuzetihPodataka(FigCanvas): """kanvas za graficki prikaz preuzetih podataka""" def __init__(self, meta=None, parent=None, width=9, height=5, dpi=100): self.fig = Figure(figsize=(width, height), dpi=dpi) self.axes = self.fig.add_subplot(111) FigCanvas.__init__(self, self.fig) self.setParent(parent) FigCanvas.setSizePolicy(self, QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed) FigCanvas.updateGeometry(self) self.meta = meta self.setup_labels() self.fig.set_tight_layout(True) #dodatni niz random boja za graf self.randomBoje = [ (0.09, 0.58, 0.58), (0.09, 0.09, 0.58), (0.09, 0.58, 0.09), (0.58, 0.09, 0.09)] rc = [tuple(np.random.random(size=3)) for i in range(10)] for i in rc: self.randomBoje.append(i) self.ymin = 0.0 self.ymax = 100.0 def set_ymax(self, value): value = float(value) if value != self.ymax: self.ymax = value def set_ymin(self, value): value = float(value) if value != self.ymin: self.ymin = value def setup_labels(self): try: self.axes.set_xlabel(self.meta['xlabel'], fontsize=8) self.axes.set_ylabel(self.meta['ylabel'], fontsize=8) self.axes.set_title(self.meta['title'], fontsize=10) except (KeyError, ValueError, TypeError): pass def clear_graf(self): """ clear graf & redo labels """ self.axes.clear() self.setup_labels() def reset_graf(self): self.clear_graf() self.draw() def crtaj(self, frejm=None, raspon=None): """ metoda za crtanje podataka na graf frejm --> pandas datafrejm sa podacima raspon --> integer broj minuta za prikaz na grafu """ self.clear_graf() zadnji = frejm.index[-1] #zadnji indeks prvi = zadnji - datetime.timedelta(minutes=raspon) #prvi indeks if frejm.index[0] > prvi: prozor = frejm shift = False else: prozor = frejm[(frejm.index >= prvi) & (frejm.index <= zadnji)] shift = True vrijeme = list(prozor.index) for column in prozor.columns: y = prozor.loc[:, column] i = list(prozor.columns).index(column) boja = self.randomBoje[i] #plot txt=" ".join(['Plin',str(column)]) self.axes.plot(vrijeme, y, linewidth=1, color=boja, alpha=0.5, label=txt) if shift: minimum = max(vrijeme) - datetime.timedelta(minutes=raspon) maksimum = max(vrijeme) else: minimum = min(vrijeme) maksimum = min(vrijeme) + datetime.timedelta(minutes=raspon) delta = (maksimum - minimum) / 20 minimum = minimum - delta maksimum = maksimum + delta self.axes.set_xlim((minimum, maksimum)) self.axes.set_ylim((self.ymin, self.ymax)) allXLabels = self.axes.get_xticklabels(which='both') #dohvati sve labele for label in allXLabels: label.set_rotation(20) label.set_fontsize(8) self.legenda = self.axes.legend(loc=1, fontsize=8, fancybox=True) self.draw()
def plot(self, data_dict, predictions, sample=0): """ Creates a default interactive visualization, with a slider enabling to move forth and back along the time axis (iteration over the sequence elements in a given episode). The default visualization contains the input, output and target sequences. For a more model/problem - dependent visualization, please overwrite this method in the derived model class. :param data_dict: DataDict containing - input sequences: [BATCH_SIZE x SEQUENCE_LENGTH x INPUT_SIZE], - target sequences: [BATCH_SIZE x SEQUENCE_LENGTH x OUTPUT_SIZE] :param predictions: Predicted sequences [BATCH_SIZE x SEQUENCE_LENGTH x OUTPUT_SIZE] :type predictions: torch.tensor :param sample: Number of sample in batch (default: 0) :type sample: int """ # Check if we are supposed to visualize at all. if not self.app_state.visualize: return # Initialize timePlot window - if required. if self.plotWindow is None: from miprometheus.utils.time_plot import TimePlot self.plotWindow = TimePlot() from matplotlib.figure import Figure import matplotlib.ticker as ticker # Change fonts globally - for all figures/subsplots at once. # from matplotlib import rc # rc('font', **{'family': 'Times New Roman'}) import matplotlib.pylab as pylab params = { # 'legend.fontsize': '28', 'axes.titlesize': 'large', 'axes.labelsize': 'large', 'xtick.labelsize': 'medium', 'ytick.labelsize': 'medium' } pylab.rcParams.update(params) # Create a single "figure layout" for all displayed frames. fig = Figure() axes = fig.subplots( 3, 1, sharex=True, sharey=False, gridspec_kw={'width_ratios': [predictions.shape[0]]}) # Set ticks. axes[0].xaxis.set_major_locator(ticker.MaxNLocator(integer=True)) axes[0].yaxis.set_major_locator(ticker.MaxNLocator(integer=True)) axes[1].yaxis.set_major_locator(ticker.MaxNLocator(integer=True)) axes[2].yaxis.set_major_locator(ticker.MaxNLocator(integer=True)) # Set labels. axes[0].set_title('Inputs') axes[0].set_ylabel('Control/Data bits') axes[1].set_title('Targets') axes[1].set_ylabel('Data bits') axes[2].set_title('Predictions') axes[2].set_ylabel('Data bits') axes[2].set_xlabel('Item number') fig.set_tight_layout(True) # Detach a sample from batch and copy it to CPU. inputs_seq = data_dict['sequences'][sample].cpu().detach().numpy() targets_seq = data_dict['targets'][sample].cpu().detach().numpy() predictions_seq = predictions[sample].cpu().detach().numpy() # Create empty matrices. x = np.transpose(np.zeros(inputs_seq.shape)) y = np.transpose(np.zeros(predictions_seq.shape)) z = np.transpose(np.zeros(targets_seq.shape)) # Log sequence length - so the user can understand what is going on. self.logger.info( "Generating dynamic visualization of {} figures, please wait...". format(inputs_seq.shape[0])) # Create frames - a list of lists, where each row is a list of artists # used to draw a given frame. frames = [] for i, (input_word, prediction_word, target_word) in enumerate( zip(inputs_seq, predictions_seq, targets_seq)): # Display information every 10% of figures. if (inputs_seq.shape[0] > 10) and (i % (inputs_seq.shape[0] // 10) == 0): self.logger.info("Generating figure {}/{}".format( i, inputs_seq.shape[0])) # Add words to adequate positions. x[:, i] = input_word y[:, i] = target_word z[:, i] = prediction_word # Create "Artists" drawing data on "ImageAxes". artists = [None] * len(fig.axes) # Tell artists what to do artists[0] = axes[0].imshow(x, interpolation='nearest', aspect='auto') artists[1] = axes[1].imshow(y, interpolation='nearest', aspect='auto') artists[2] = axes[2].imshow(z, interpolation='nearest', aspect='auto') # Add "frame". frames.append(artists) # Plot figure and list of frames. self.plotWindow.update(fig, frames)
def _figure_default(self): figure = Figure(facecolor='white') figure.set_tight_layout(self.tight_layout) return figure
class GraphWindow: def __init__(self, parent, samples, graph_type, title): self.samples = samples self.title = title self.graph_type = graph_type self.parent = parent self.window = tk.Toplevel() self.window.title(graph_type + ": " + ", ".join(title)) self.window.geometry = "800x600" self.normalize = tk.BooleanVar() self.interval = tk.IntVar() self.custom_fwhm = tk.DoubleVar() self.custom_mu2 = tk.DoubleVar() self.frame = tk.Frame(self.window) self.frame.pack(expand=False, side=tk.TOP, fill=tk.X) self.canvas = None self.f = Figure() self.f.set_tight_layout(True) if graph_type in ("t-Y-Graph", "t-S-Graph", "Average Line", "t-S-Fourier", "t-Y-Fourier", "Slope adjusted t-Y-Graph"): self.slider = tk.Scale(self.frame, from_=1, to=max(len(sample.data[0]) for sample in samples) // 2, orient=tk.HORIZONTAL, variable=self.interval, label="Interval for moving average: ") self.slider.bind("<ButtonRelease-1>", lambda x: self._redraw()) self.slider.pack(fill=tk.BOTH, expand=True) self.slider.set(self.samples[0].delta_pix()) if graph_type in ("t-S-Graph", "Raw Crosssection", "Aligned Crosssection", "Slope adjusted Crosssection", "Binary Star Separation"): self.normalize_check = tk.Checkbutton(self.frame, variable=self.normalize, offvalue=False, onvalue=True, text="Normalize", command=self._redraw) self.normalize_check.pack(side=tk.LEFT) if graph_type == "Binary Star Separation": self.slider = tk.Scale(self.frame, resolution=0.01, from_=1, to=max(len(sample.data) for sample in samples) // 2, orient=tk.HORIZONTAL, variable=self.custom_fwhm, label="FWHM") self.slider.bind("<ButtonRelease-1>", lambda x: self._redraw()) self.slider.pack(fill=tk.BOTH, expand=True) self.slider = tk.Scale(self.frame, resolution=0.01, from_=-1, to=1 , orient=tk.HORIZONTAL, variable=self.custom_mu2, label="mu2") self.slider.bind("<ButtonRelease-1>", lambda x: self._redraw()) self.slider.pack(fill=tk.BOTH, expand=True) self.draw_figure(self.f, samples, graph_type, interval=self.samples[0].delta_pix()) self.window.protocol("WM_DELETE_WINDOW", self.on_closing) def draw_figure(self, f, samples, graph_type, interval=1, normalize=False): if graph_type == "Raw Crosssection": data = [sample.get_crosssection() for sample in samples] f.clear() a = f.add_subplot(111, frameon=False) if not self.normalize.get(): a.set_ylabel("ADUs") else: a.set_ylabel("Relative ADUs") a.set_xlabel("Pixel from Centre") for d, t in zip(data, self.title): if normalize: graph_max = np.max(d) d = d / graph_max offset = list(d).index(max(d)) a.plot(np.array([i for i in range(len(d))]) - offset, d, label=t) if len(data) == 1: fwhm, height, lo, hi = samples[0].get_fwhm() if normalize: height /= graph_max a.hlines(height, lo, hi, label=f"FWHM = {fwhm}", color="C2", linestyles="dotted") a.legend(bbox_to_anchor=(1, 1), loc="upper left") elif graph_type == "t-Y-Graph": data = [sample.get_maximum_shift_moving_average(interval=interval) for sample in samples] axis_x = [i for i in range(interval, interval + max(map(len, data)))] f.clear() a = f.add_subplot(111, frameon=False) a.set_ylabel("Pixel from Mean") a.set_xlabel("Pixel from Start") for d, t in zip(data, self.title): a.plot(axis_x, d, label=t) elif graph_type == "t-S-Graph": data = [sample.get_flattened_moving_average(interval) for sample in samples] axis_x = [i for i in range(interval, interval + max(map(len, data)))] f.clear() a = f.add_subplot(111, frameon=False) if not self.normalize.get(): a.set_ylabel("ADUs") else: a.set_ylabel("Relative ADUs") a.set_xlabel("Pixel from Start") for d, t in zip(data, self.title): if normalize: d = d / np.mean(d) a.plot(axis_x, d, label=t) a.legend(bbox_to_anchor=(1,1), loc="upper left") elif graph_type == "Average Line": data = np.array([sample.get_flattened_moving_average(interval) for sample in samples]) axis_x = [i for i in range(interval, interval + max(map(len, data)))] for d in range(len(data)): data[d] = data[d] / np.mean(data[d]) line = np.median(data, axis=0) f.clear() a = f.add_subplot(111, frameon=False) a.set_ylabel("Relative ADUs") a.set_xlabel("Pixel from Start") a.plot(axis_x, line, label="median") elif graph_type == "Vertical align": data = samples[0].data slope_adjusted_data = samples[0].get_slope_adjusted_data() aligned_data = samples[0].get_realigned_to_maximum() f.clear() ax1 = f.add_subplot(311, ylabel="raw") ax2 = f.add_subplot(312, ylabel="slope adjusted") ax3 = f.add_subplot(313, ylabel="realigned to maximum") ax1.imshow(data) ax2.imshow(slope_adjusted_data) ax3.imshow(aligned_data) elif graph_type == "Aligned Crosssection": data = [sample.get_realigned_crosssection() for sample in samples] f.clear() a = f.add_subplot(111, frameon=False) if not self.normalize.get(): a.set_ylabel("ADUs") else: a.set_ylabel("Relative ADUs") a.set_xlabel("Pixel from Centre") for d, t in zip(data, self.title): if normalize: graph_max = np.max(d) d = d / graph_max offset = list(d).index(max(d)) a.plot(np.array([i for i in range(len(d))]) - offset, d, label=t) if len(data) == 1: fwhm, height, lo, hi = samples[0].get_realigned_fwhm() if normalize: height /= graph_max a.hlines(height, lo, hi, label=f"FWHM = {fwhm}", color="C2", linestyles="dotted") a.legend(bbox_to_anchor=(1, 1), loc="upper left") elif graph_type == "t-S-Fourier": data = [sample.get_t_s_fourier(interval=interval) for sample in samples] data = [data[i][5:len(data[i]) // 2] for i in range(len(data))] axis_x = [i for i in range(5, len(data[0]) + 5)] f.clear() a = f.add_subplot(111, frameon=False) a.set_ylabel("Amplitude") a.set_xlabel("Frequency") a.set_xscale("log") a.set_yscale("log") for d, t in zip(data, self.title): a.plot(axis_x, d, label=t) a.legend(bbox_to_anchor=(1, 1), loc="upper left") elif graph_type == "t-Y-Fourier": data = [sample.get_t_y_fourier(interval=interval) for sample in samples] data = [data[i][5:len(data[i]) // 2] for i in range(len(data))] axis_x = [i for i in range(5, len(data[0]) + 5)] f.clear() a = f.add_subplot(111, frameon=False) a.set_ylabel("Amplitude") a.set_xlabel("Frequency") a.set_xscale("log") a.set_yscale("log") for d, t in zip(data, self.title): a.plot(axis_x, d, label=t) a.legend(bbox_to_anchor=(1, 1), loc="upper left") elif graph_type == "Slope adjusted t-Y-Graph": data = [sample.get_slope_adjusted_t_y(interval=interval) for sample in samples] axis_x = [i for i in range(interval, interval + max(map(len, data)))] f.clear() a = f.add_subplot(111, frameon=False) a.set_ylabel("Pixel from Max") a.set_xlabel("Pixel from Start") for d, t in zip(data, self.title): a.plot(axis_x, d, label=t) a.legend(bbox_to_anchor=(1, 1), loc="upper left") elif graph_type == "Slope adjusted Crosssection": data = [sample.get_slope_adjusted_crosssection() for sample in samples] f.clear() a = f.add_subplot(111, frameon=False) if not self.normalize.get(): a.set_ylabel("ADUs") else: a.set_ylabel("Relative ADUs") a.set_xlabel("Pixel from Centre") for d, t in zip(data, self.title): if normalize: graph_max = np.max(d) d = d / graph_max offset = list(d).index(max(d)) a.plot(np.array([i for i in range(len(d))]) - offset, d, label=t) if len(data) == 1: fwhm, height, lo, hi = samples[0].get_slope_adjusted_fwhm() if normalize: height /= graph_max a.hlines(height, lo, hi, label=f"FWHM = {fwhm}", color="C2", linestyles="dotted") a.legend(bbox_to_anchor=(1, 1), loc="upper left") elif graph_type == "Get PSF from Single Stars": crosssection = self.parent.psf a = f.add_subplot(111, frameon=False) a.set_xlabel("Pixel from Max") a.plot(np.arange(len(crosssection)) - list(crosssection).index(np.max(crosssection)), crosssection) elif graph_type == "Binary Star Separation": crosssection = samples[0].get_realigned_crosssection() fwhm = self.custom_fwhm.get() max_val = np.max(crosssection) x_val = np.arange(len(crosssection)) - list(crosssection).index(max_val) if normalize: crosssection /= max_val max_val = 1 f.clear() a = f.add_subplot(111, frameon=False) # psf approach """ data_psf = self.parent.psf def psf(x): # get continuous psf through interpolation def get_interpolated(x, lo, hi): x = x - int(x) return lo + (hi-lo) * x x_pos = x - int(x) lo = data_psf[int(np.floor(x))] hi = data_psf[int(np.ceil(x))] return get_interpolated(x_pos, lo, hi) def star_from_psf(x_values, max_pos, max_height): if len(x_values) != len(data_psf): raise ValueError(f"PSF not fit for Aperture of width {len(x_val)}") star = np.zeros(len(crosssection)) for i in range(len(crosssection)): if 0 <= i - max_pos <= len(crosssection) - 1: star[i] = psf(i - max_pos) * max_height return star def two_stars(x_values, max_pos2, max_height2): return star_from_psf(x_values, 0, max_val) + star_from_psf(x_values, max_pos2, max_height2) (mp2, mh2), fitness = optimize.curve_fit(two_stars, x_val, crosssection, bounds=[[-np.inf, 0], [np.inf, np.inf]], p0=[10, 1000000]) mp1 = 0 mh1 = max_val a.plot(x_val, crosssection, label="Raw") a.plot(x_val, star_from_psf(x_val, mp1, mh1), label=f"Star 1: {(star1 := np.sum(star_from_psf(x_val, mp1, mh1)))}, X: {mp1}") a.plot(x_val, star_from_psf(x_val, mp2, mh2), label=f"Star 2: {(star2 := np.sum(star_from_psf(x_val, mp2, mh2)))}, X: {mp2}") error = crosssection - star_from_psf(x_val, mp1, mh1) - star_from_psf(x_val, mp2, mh2) a.plot(x_val, error, label=f"Error: {np.std(error)}\nMag difference: {abs(np.log(star1) / np.log(100**.2) - np.log(star2) / np.log(100**.2))}") """ # gaussian approach """ def gaussian(x, max_value, mu, sigma): res = norm.pdf(x, mu, sigma) res = res * (1 / np.max(res)) * max_value return res def two_gaussians(x, max2, mu2, sigma): mu1 = 0 max1 = max_val return gaussian(x, max1, mu1, sigma) + gaussian(x, max2, mu2, sigma) def get_error(params): print(params[0]) e = np.std(two_gaussians(x_val, *params) - crosssection) return e #(max2, mu2, sigma), stats = optimize.curve_fit(two_gaussians, x_val, crosssection , bounds=([150000, x_val[0], 0], [np.inf, x_val[-1], np.inf]), p0=[max_val, 1, 1]) (max2, mu2, sigma) = optimize.brute(get_error, [(0, max_val / 4), (0, 15), (0, 10)], Ns=30, finish=None) print(max2, mu2, sigma) mu1 = 0 max1 = max_val gauss1 = gaussian(x_val, max1, mu1, sigma) gauss2 = gaussian(x_val, max2, mu2, sigma) error = crosssection - two_gaussians(x_val, max2, mu2, sigma) a.plot(x_val, crosssection, label="Raw Data") a.plot(x_val, gauss1, "--", label=f"Star 1: S = {np.sum(gauss1)}, mu = {mu1}") a.plot(x_val, gauss2, "--", label=f"Star 2: S = {np.sum(gauss2)}, mu = {mu2}") a.plot(x_val, error, ":", label=f"Standard Error = {np.std(error)}\nMag difference: {abs(np.log(np.sum(gauss1)) / np.log(100**.2) - np.log(np.sum(gauss2)) / np.log(100**.2))}") """ # Manual approach star1_mu = 0 star1_sigma = fwhm / (2*np.sqrt(2*np.log(2))) aprox = norm.pdf(x_val, star1_mu, star1_sigma) aprox = aprox * (1/np.max(aprox)) * max_val a.plot(x_val, aprox, "--", label=f"Star 1: S = {np.sum(aprox):.2f}, mu = {star1_mu}") remainder = crosssection - aprox mu_remainder = list(remainder).index(np.max(remainder)) - list(crosssection).index(np.max(crosssection)) + self.custom_mu2.get() aprox_remainder = norm.pdf(x_val, mu_remainder, star1_sigma) aprox_remainder = aprox_remainder * (1/np.max(aprox_remainder)) * np.max(remainder) a.plot(x_val, aprox_remainder, "--", label=f"Star 2: S = {np.sum(aprox_remainder):.2f}, mu = {list(aprox_remainder).index(max(aprox_remainder)) - list(crosssection).index(max_val) + self.custom_mu2.get()}") remainder_2 = remainder - aprox_remainder a.plot(x_val, remainder_2, "r:", label=f"Error: {np.std(remainder_2):.4f}") # a.plot(x_val, crosssection, "+-", label=f"Raw Data\nMagnitude Difference: {abs(np.log(np.sum(aprox)) / np.log(100**.2) - np.log(np.sum(aprox_remainder)) / np.log(100**.2))}", alpha=.5) a.legend(bbox_to_anchor=(1, 1), loc="upper left") else: raise ValueError(f"Invalid Mode: {graph_type}") if self.canvas: self.canvas.get_tk_widget().destroy() self.canvas.get_tk_widget().destroy() self.canvas = FigureCanvasTkAgg(self.f, self.window) self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) def _redraw(self): self.draw_figure(self.f, self.samples, self.graph_type, interval=self.interval.get(), normalize=self.normalize.get()) def on_closing(self): self.parent.open_windows.remove(self) self.window.destroy()
class MyMplCanvas(FigureCanvas): """FigureCanvas的最终的父类其实是QWidget。""" def __init__(self, parent=None, width=5, height=4, dpi=100): # 配置中文显示 plt.rcParams['font.family'] = ['SimHei'] # 用来正常显示中文标签 # plt.rcParams['font.family'] = ['SimSun'] # 用来正常显示中文标签 plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 self.fig = Figure(figsize=(width, height), dpi=dpi) # 新建一个figure self.fig.set_tight_layout(True) self.axes = self.fig.add_subplot(111) # 建立一个子图,如果要建立复合图,可以在这里修改 self.axes.hold(False) # 每次绘图的时候不保留上一次绘图的结果 FigureCanvas.__init__(self, self.fig) self.setParent(parent) '''定义FigureCanvas的尺寸策略,这部分的意思是设置FigureCanvas,使之尽可能的向外填充空间。''' FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) '''绘制静态图,可以在这里定义自己的绘图逻辑''' def start_static_plot(self,qx=None): if qx != None: # 与zwquant结合,qx是zwquant的一个类实例 df = qx.qxLib.copy() df.set_index('date',inplace=True) df.rename_axis(lambda x: pd.to_datetime(x),inplace=True) ax1 = self.axes ax1.plot(df['dret'], color='green', label='dret', linewidth=0.5) ax1.legend(loc='upper left') ax2 = ax1.twinx() ax2.plot(df['val'], color='red', label='val', linewidth=2) ax2.legend(loc='upper right') else:# 用于测试,不需要zwquant也能运行,方面快速开发自己的GUI界面。 t = arange(0.0, 3.0, 0.01) s = sin(2 * pi * t) self.axes.plot(t, s) self.axes.set_ylabel('静态图:Y轴') self.axes.set_xlabel('静态图:X轴') self.axes.grid(True) '''启动绘制动态图''' def start_dynamic_plot(self, *args, **kwargs): timer = QtCore.QTimer(self) timer.timeout.connect(self.update_figure) # 每隔一段时间就会触发一次update_figure函数。 timer.start(1000) # 触发的时间间隔为1秒。 '''动态图的绘图逻辑可以在这里修改''' def update_figure(self): self.fig.suptitle('测试动态图') l = [random.randint(0, 10) for i in range(4)] self.axes.plot([0, 1, 2, 3], l, 'r') self.axes.set_ylabel('动态图:Y轴') self.axes.set_xlabel('动态图:X轴') self.axes.grid(True) self.draw()
def main(): global root global canvas global fig global subplot global lblError global lblFileName global currentImage global previous_image global transformedImage global imageSelection global popup_menu_image global reload_local_file global showAxis global resizedImgBtn global flip_H global flip_V global btnFlip_H global btnFlip_V global rotateImgBtn global selectedSize global selectedAngle global rotationAngle global anchor global anchorSelection global rotationApplied global lblCurrentAngle global currentAngle global imgGamma global hue global saturation global sharpen global gaussian_blur global posterize global solarize global show_edges global low_threshold global sliderLowThreshold global show_emboss global emboss global embossKernel global sliderEmboss global sobel_threshold # create our main window root = Tk() root.config(background='white') root.title('CAER Image GUI - Python v' + pythonVersion) root.geometry('1024x768') root.bind('<Destroy>', on_exit) # the following works for a single screen setup # if using a multi-screen setup then see the following link: # Ref: https://stackoverflow.com/questions/3129322/how-do-i-get-monitor-resolution-in-python/56913005#56913005 screenDPI = root.winfo_fpixels('1i') screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() currentImage = None previous_image = 'Island' transformedImage = None reload_local_file = False rotationApplied = False anchor = None showAxis = False flip_H, flip_V = False, False currentAngle = 0.0 embossKernel = caer.data.np.array([[0, 1, 0], [0, 0, 0], [0, -1, 0]]) # bind the 'q' keyboard key to quit root.bind('q', lambda event: root.destroy()) #----------------------------------------------------------------------- # add a frame to hold top controls frame1 = Frame(root, background='black') frame1.pack(side=TOP, fill=X) # create the built-in image selection variable and choices imageSelection = StringVar() imageChoices = [ 'Open File >>', 'Bear', 'Beverages', 'Black Cat', 'Camera', 'Gold Fish', 'Guitar', 'Island', 'Mountain', 'Night', 'Puppies', 'Snow', 'Sunrise', 'Sea Turtle', 'Tent' ] imageSelection.set('Island') imageSelection.trace('w', show_original_image) # create the built-in image selection popup menu popup_menu_image = OptionMenu(frame1, imageSelection, *imageChoices) popup_menu_image['width'] = 10 popup_menu_image['bg'] = 'lightgreen' popup_menu_image.pack(side=LEFT, padx=2) # create a button to re-size the image resizedImgBtn = Button(frame1, text='Resize', width=6, bg='lightgrey', relief=RAISED, command=resize_image) resizedImgBtn.pack(side=LEFT, padx=2, pady=2) # create an entry box for re-size dimensions selectedSize = StringVar() resizedImgSize = Entry(frame1, justify=CENTER, textvariable=selectedSize, font='Helvetica 10', width=10, bg='white', relief=RAISED) resizedImgSize.pack(side=LEFT, padx=2, pady=2) selectedSize.set('') # create a button to rotate the image rotateImgBtn = Button(frame1, text='Rotate', width=6, bg='lightgrey', relief=RAISED, command=show_rotated_image) rotateImgBtn.pack(side=LEFT, padx=2, pady=2) # create a label for the rotation angle lblAngle = Label(frame1, text=u'\u2220', fg='yellow', bg='black', font='Helvetica 10') lblAngle.pack(side=LEFT, padx=2, pady=2) # create the rotation angle selection variable and an entry box selectedAngle = StringVar() rotationAngle = Entry(frame1, justify=CENTER, textvariable=selectedAngle, font='Helvetica 10', width=5, bg='white', relief=RAISED) rotationAngle.pack(side=LEFT, padx=2, pady=2) selectedAngle.set('0.0') # create a read-only label for the current angle lblCurrentAngle = Label(frame1, text='0.0', state='disabled', fg='lightgrey', bg='white', font='Helvetica 8', width=5) lblCurrentAngle.pack(side=LEFT, padx=2, pady=2) # create a label for the rotation anchor lblAnchor = Label(frame1, text=u'\u2693', fg='yellow', bg='black', font='Helvetica 10') lblAnchor.pack(side=LEFT, padx=2, pady=2) # create the rotation anchor selection variable and choices anchorSelection = StringVar() anchorChoices = [ 'BottomLeft', 'BottomMiddle', 'BottomRight', 'Center', 'MiddleLeft', 'MiddleRight', 'TopLeft', 'TopMiddle', 'TopRight' ] anchorSelection.set('Center') # create the anchor selection popup menu popup_menu_anchor = OptionMenu(frame1, anchorSelection, *anchorChoices) popup_menu_anchor['width'] = 12 popup_menu_anchor.pack(side=LEFT, padx=2) # create a label to show the name of the local image file opened by user lblFileName = Label(frame1, text='', fg='yellow', bg='black', font='Helvetica 10') lblFileName.pack(side=RIGHT, padx=10, pady=2) #----------------------------------------------------------------------- # add a frame to hold side controls, screen attributes and the Error labels frame2 = Frame(root, background='black') frame2.pack(side=RIGHT, fill=Y) # create the image gamma slider control imgGamma = DoubleVar() sliderGamma = Scale(frame2, label='Gamma', variable=imgGamma, troughcolor='blue', from_=0.1, to=2.0, resolution=0.05, sliderlength=15, showvalue=False, orient=HORIZONTAL, command=adjust_ghsps) sliderGamma.pack(side=TOP, anchor=E, padx=2) imgGamma.set(1.05) # create the image hue slider control hue = DoubleVar() sliderHue = Scale(frame2, label='Hue', variable=hue, troughcolor='blue', from_=-0.5, to=0.5, resolution=0.05, sliderlength=15, showvalue=False, orient=HORIZONTAL, command=adjust_ghsps) sliderHue.pack(side=TOP, anchor=E, padx=2, pady=3) hue.set(0.0) # create the image saturation slider control saturation = DoubleVar() sliderSaturation = Scale(frame2, label='Saturation', variable=saturation, troughcolor='blue', from_=0.0, to=2.0, resolution=0.1, sliderlength=15, showvalue=False, orient=HORIZONTAL, command=adjust_ghsps) sliderSaturation.pack(side=TOP, anchor=E, padx=2) saturation.set(1.0) # create the image sharpen slider control sharpen = DoubleVar() sliderSharpen = Scale(frame2, label='Sharpen', variable=sharpen, troughcolor='blue', from_=7.9, to=9.9, resolution=0.05, sliderlength=15, showvalue=False, orient=HORIZONTAL, command=set_sharpen_kernel) sliderSharpen.pack(side=TOP, padx=2, pady=3) sharpen.set(8.9) # create the image Gaussian Blur slider control gaussian_blur = IntVar() sliderGaussianBlur = Scale(frame2, label='Gaussian Blur', variable=gaussian_blur, troughcolor='blue', from_=0, to=10, resolution=2, sliderlength=15, showvalue=False, orient=HORIZONTAL, command=adjust_ghsps) sliderGaussianBlur.pack(side=TOP, padx=2) gaussian_blur.set(0) # create the image posterize slider control posterize = IntVar() sliderPosterize = Scale(frame2, label='Posterize', variable=posterize, troughcolor='blue', from_=6, to=1, resolution=1, sliderlength=15, showvalue=False, orient=HORIZONTAL, command=adjust_ghsps) sliderPosterize.pack(side=TOP, padx=2, pady=3) posterize.set(6) # create the image solarize slider control solarize = IntVar() sliderSolarize = Scale(frame2, label='Solarize', variable=solarize, troughcolor='blue', from_=255, to=0, resolution=1, sliderlength=15, showvalue=False, orient=HORIZONTAL, command=adjust_ghsps) sliderSolarize.pack(side=TOP, padx=2) solarize.set(255) # create the image sobel threshold slider control sobel_threshold = IntVar() sliderSobelThreshold = Scale(frame2, label='Sobel Gradient', variable=sobel_threshold, troughcolor='blue', from_=0, to=4, resolution=1, sliderlength=15, showvalue=False, orient=HORIZONTAL, command=adjust_ghsps) sliderSobelThreshold.pack(side=TOP, padx=2, pady=3) sobel_threshold.set(0) # add 'Edges' checkbox show_edges = IntVar() chbShowEdges = Checkbutton(frame2, text='Edges', variable=show_edges, width=7, command=set_edges) chbShowEdges.pack(side=TOP, padx=2) show_edges.set(0) # create the image edges low threshold slider control low_threshold = IntVar() sliderLowThreshold = Scale(frame2, label='Edges Threshold', variable=low_threshold, troughcolor='blue', from_=100, to=0, resolution=1, sliderlength=15, showvalue=False, orient=HORIZONTAL, command=adjust_ghsps) sliderLowThreshold.pack(side=TOP, padx=2, pady=3) low_threshold.set(50) # add 'Emboss' checkbox show_emboss = IntVar() chbShowEmboss = Checkbutton(frame2, text='Emboss', variable=show_emboss, width=7, command=set_emboss) chbShowEmboss.pack(side=TOP, padx=2) show_emboss.set(0) # create the image emboss slider control emboss = IntVar() sliderEmboss = Scale(frame2, label='Emboss Threshold', variable=emboss, troughcolor='blue', from_=128, to=99, resolution=1, sliderlength=15, showvalue=False, orient=HORIZONTAL, command=adjust_ghsps) sliderEmboss.pack(side=TOP, padx=2, pady=3) emboss.set(114) lblScreen = Label(frame2, text='Screen', fg='grey', bg='black', font='Helvetica 9') lblScreen.pack(side=TOP, anchor=CENTER, pady=7) lblResolution = Label(frame2, text='res: ' + str(screen_width) + ' x ' + str(screen_height), fg='grey', bg='black', font='Helvetica 9') lblResolution.pack(side=TOP, anchor=CENTER) lblDPI = Label(frame2, text='dpi: ' + str(int(screenDPI)), fg='grey', bg='black', font='Helvetica 9') lblDPI.pack(side=TOP, anchor=CENTER) # add exit button exitBtn = Button(frame2, text='Exit', width=7, fg='red', bg='lightgrey', relief=RAISED, command=root.destroy) exitBtn.pack(side=BOTTOM, anchor=CENTER, pady=5) lblError = Label(frame2, text='', fg='red', bg='black', font='Helvetica 12') lblError.pack(side=BOTTOM, anchor=CENTER, pady=10) #----------------------------------------------------------------------- # create matplotlib figure, subplot, canvas and toolbar fig = Figure(figsize=(640 // screenDPI, 427 // screenDPI), dpi=int(screenDPI)) subplot = fig.add_subplot(111) subplot.xaxis.set_visible(False), subplot.yaxis.set_visible(False) fig.set_tight_layout(True) canvas = FigureCanvasTkAgg(fig, master=root) canvas.draw() toolbar = NavigationToolbar2Tk(canvas, root) toolbar._Spacer() toolbar._Button('Show Axis', None, toggle=True, command=refresh_axis) toolbar._Spacer() toolbar._Button('Reload Image', None, toggle=False, command=reload_image) toolbar._Spacer() btnFlip_H = toolbar._Button('FlipH', None, toggle=True, command=flip_image_horizontally) toolbar._Spacer() btnFlip_V = toolbar._Button('FlipV', None, toggle=True, command=flip_image_vertically) toolbar._Spacer() toolbar._Button('Histogram', None, toggle=False, command=show_histogram_window) toolbar.update() canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1) #----------------------------------------------------------------------- # set the minimum window size to the current size root.update() root.minsize(root.winfo_width(), root.winfo_height()) show_original_image() root.mainloop()
class ScalarCollection(Atom): """ ScalarCollection is a bundle of ScalarModels. The ScalarCollection has an instance of a DataMuxer which notifies it of new data which then updates its ScalarModels. When instantiated, the data_muxer instance is asked for the names of its columns. All columns which represent scalar values are then shoved into ScalarModels and the ScalarCollection manages the ScalarModels. Attributes ---------- data_muxer : replay.pipeline.pipeline.DataMuxer The data manager backing the ScalarModel. The DataMuxer's new_data signal is connected to the notify_new_data function of the ScalarModel so that the ScalarModel can decide what to do when the DataMuxer receives new data. scalar_models : atom.Dict The collection of scalar_models that the ScalarCollection knows about data_cols : atom.List The names of the data sets that are in the DataMuxer redraw_every : atom.Float The frequency with which to redraw the plot. The meaning of this parameter changes based on `redraw_type` redraw_type : {'max rate', 's'} Gives meaning to the float stored in `redraw_every`. Should be read as 'Update the plot at a rate of `redraw_every` per `redraw_type`'. Since there are only the two options in `ScalarCollection`, it should be understood that the previous statement is only relevant when 's' is selected as the `redraw_type`. If `max_rate` is selected, then the plot will attempt to update itself as fast as data is coming in. Beware that this may cause significant performance issues if your data rate is > 20 Hz update_rate : atom.Str Formatted rate that new data is coming in. x : atom.Str The name of the x-axis that the `scalar_models` should be plotted against """ # dictionary of lines that can be toggled on and off scalar_models = Dict(key=Str(), value=ScalarModel) # dictionary of data for the pandas dataframe that backs the # ScalarCollection column_models = Dict(key=Str(), value=ColumnModel) # the thing that holds all the data dataframe = Typed(pd.DataFrame) # name of the x axis x = Str() # names of the currently plotting things on the y-axis y = List() # attribute needed to keep the x axis combo box selector in sync x_index = Int() # name of the column to align against x_is_index = Bool(True) # name of all columns that the data muxer knows about data_cols = List() # MPL PLOTTING STUFF _fig = Typed(Figure) _ax = Typed(Axes) # configuration properties for the 1-D plot _conf = Typed(ScalarConfig) # some id that replay uses as a key for plotting state dataframe_uid = Str() # the sql database that keeps track of headers from run-to-run history = Typed(History) # tell replay to use the state from the last selected header use_ram_state = Bool(False) # tell replay to use the state from the last time this header was viewed use_disk_state = Bool(True) autolim_axes = Bool(True) xlim = Coerced(tuple) ylim = Coerced(tuple) def __init__(self, history, **kwargs): self.history = history for k, v in kwargs.items(): setattr(self, k, v) with self.suppress_notifications(): super(ScalarCollection, self).__init__() # plotting initialization self._fig = Figure(figsize=(1, 1)) self._fig.set_tight_layout(True) self._ax = self._fig.add_subplot(111) self._conf = ScalarConfig(self._ax) self.dataframe_uid = '' @observe('dataframe_uid') def dataframe_uid_changed(self, changed): dataframe_uid = changed['value'] if dataframe_uid is None or dataframe_uid == 'None' or dataframe_uid == '': dataframe_uid = '' with self.suppress_notifications(): self.dataframe_uid = dataframe_uid logger.debug('dataframe id in scalar model: %s', self.dataframe_uid) try: logger.debug('getting state for dataframe id: %s', self.dataframe_uid) state = self.history.get(six.text_type(self.dataframe_uid)) logger.debug('state retrieved for dataframe id: %s\nstate: %s', self.dataframe_uid, state) except IndexError: # there are no entries in the db for 'state' logger.debug('no state found for dataframe id: %s', self.dataframe_uid) state = {} if self.use_ram_state: # the state has already been correctly configured return elif self.use_disk_state and state: # update the plot with the data sets that were plotting last time self.__setstate__(state) y = state.get('y', None) if y: for name, model in self.scalar_models.items(): model.is_plotting = name in y self.get_new_data_and_plot() x = state.get('x', None) if x: self.x_index = self.scalar_models.keys().index(self.x) @observe('x', 'x_is_index', 'y', 'autolim_axes', 'xlim', 'ylim') def save_plotting_state(self, changed): plotting_state = {'x': self.x, 'y': self.y, 'x_is_index': self.x_is_index, 'xlim': self.xlim, 'ylim': self.ylim, 'autolim_axes': self.autolim_axes} logger.debug('writing plotting state for id: [%s] ... %s', self.dataframe_uid, plotting_state) replay.core.save_state(self.history, self.dataframe_uid, plotting_state) def clear_scalar_models(self): self._ax.cla() self.data_cols = [] self.column_models = {} self.scalar_models = {} def new_dataframe(self, changed): self.dataframe = changed['value'] @observe('dataframe') def dataframe_changed(self, changed): if self.dataframe is not None: self.new_data() def new_data(self): old_plotting_values = [col_name for col_name, col_model in self.scalar_models.items() if col_model.is_plotting] old_x = self.x old_x_is_index = self.x_is_index scalar_cols = [col for col in self.dataframe.columns if self.dataframe[col].dropna().values[0].shape == tuple()] # figure out if the dataframe has one or more levels of labels # for now these need to be handled differently if isinstance(self.dataframe.columns[0], six.string_types): # then the dataframe does not have hierarchical indexing self._do_magic(scalar_cols) elif isinstance(self.dataframe.columns[0], tuple): # then the dataframe has hierarchical indexing # self._do_nested_magic(scalar_cols) # but for now treat them the same... self._do_magic(scalar_cols) for scalar_name, scalar_model in self.scalar_models.items(): scalar_model.is_plotting = scalar_name in old_plotting_values if old_x in self.scalar_models.keys(): self.x = old_x self.x_is_index = old_x_is_index else: self.x = self.scalar_models.keys()[0] self.x_is_index = True self.get_new_data_and_plot() def _do_magic(self, scalar_cols): # create new scalar models scalar_models = {} column_models = {} for col_name, col_model in self.column_models.items(): if col_model.column_address in scalar_cols: column_models[col_name] = self.column_models.pop(col_name) scalar_models[col_name] = self.scalar_models.pop(col_name) self.clear_scalar_models() for col_name in scalar_cols: # if col_name in self.column_models or col_name in self.scalar_models: # col_model = self.column_models[col_name] # col_model.dataframe = self.dataframe # self.scalar_models[col_name].set_data(col_model.index, col_model.data) # continue # create a new line artist and scalar model column_model = ColumnModel(dataframe=self.dataframe, column_address=col_name) column_models[column_model.name] = column_model line_artist, = self._ax.plot([], [], label=column_model.name, marker='D') scalar_model = ScalarModel(line_artist=line_artist, is_plotting=False, name=column_model.name) scalar_models[scalar_model.name] = scalar_model # throw an empty list at data cols before using list comprehension to # set the new values. This is one method to trigger the Atom magic, # though I'm sure there is a better way to do it # determine if the column_models and scalar_models need to be updated #new_column_models = [col_name for col_name in column_models.keys() # if col_name not in self.column_models] #new_scalar_models = [scalar_name for scalar_name in scalar_models.keys() # if scalar_name not in self.scalar_models] #if len(new_column_models) > 0: self.column_models = column_models #if len(new_scalar_models) > 0: self.scalar_models = scalar_models self.data_cols = [] self.data_cols = list({name.split('-')[0] for name in scalar_models.keys()}) def _do_nested_magic(self, scalar_cols): pass @observe('data_cols') def update_col_names(self, changed): pass @observe('autolim_axes') def _autolim_changed(self, changed): self.xlim = self._ax.get_xlim() self.ylim = self._ax.get_ylim() self.reformat_view() @observe('x') def update_x(self, changed): try: self.x_index = self.scalar_models.keys().index(self.x) except ValueError: self.x_index = 0 self._conf.xlabel = self.x if not self.x: return self.get_new_data_and_plot() def get_new_data_and_plot(self): """Helper function to shunt plotting with the index or a column as x. """ if self.dataframe is None: return if self.x_is_index: self.plot_by_index() else: self.plot_by_x() def plot_by_index(self): self._conf.xlabel = 'index (sequential counting numbers)' data_dict = {model_name: (model.index, model.data) for model_name, model in self.column_models.items()} self._plot(data_dict) def plot_by_x(self): if not self.x: return self._conf.xlabel = self.x x_data = self.column_models[six.text_type(self.x)].data data_dict = {model_name: (x_data, model.data) for model_name, model in self.column_models.items()} self._plot(data_dict) def _plot(self, data_dict): for model_name, xy_tuple in data_dict.items(): self.scalar_models[model_name].set_data(*xy_tuple) # self.scalar_models[dname].is_plotting = True self.reformat_view() def reformat_view(self, *args, **kwargs): """ Recompute the limits, rescale the view, reformat the legend and redraw the canvas """ # ignore the args and kwargs. They are here so that any function can be # connected to this one try: legend_pairs = [(v.line_artist, k) for k, v in six.iteritems(self.scalar_models) if v.line_artist.get_visible()] if legend_pairs: arts, labs = zip(*legend_pairs) self._ax.legend(arts, labs).draggable() else: self._ax.legend(legend_pairs) if self.autolim_axes: self._ax.relim(visible_only=True) self._ax.autoscale_view(tight=True) else: self._ax.set_xlim(self.xlim) self._ax.set_ylim(self.ylim) self._ax.grid(self._conf.grid) self._ax.set_ylabel(self._conf.ylabel) self._ax.set_xlabel(self._conf.xlabel) self._ax.set_title(self._conf.title) self._fig.canvas.draw() self.xlim = self._ax.get_xlim() self.ylim = self._ax.get_ylim() except AttributeError: # should only happen once pass
def setup_plot(cls, width=16, height=4, ncols=1, nrows=1, interactive=None, link_dataframes=None, cursor_delta=None, **kwargs): """ Common helper for setting up a matplotlib plot :param width: Width of the plot (inches) :type width: int or float :param height: Height of each subplot (inches) :type height: int or float :param ncols: Number of plots on a single row :type ncols: int :param nrows: Number of plots in a single column :type nrows: int :param link_dataframes: Link the provided dataframes to the axes using :func:`lisa.notebook.axis_link_dataframes` :type link_dataframes: list(pandas.DataFrame) or None :param cursor_delta: Add two vertical lines set with left and right clicks, and show the time delta between them in a widget. :type cursor_delta: bool or None :param interactive: If ``True``, use the pyplot API of matplotlib, which integrates well with notebooks. However, it can lead to memory leaks in scripts generating lots of plots, in which case it is better to use the non-interactive API. Defaults to ``True`` when running under IPython or Jupyter notebook, `False`` otherwise. :type interactive: bool :Keywords arguments: Extra arguments to pass to :obj:`matplotlib.figure.Figure.subplots` :returns: tuple(matplotlib.figure.Figure, matplotlib.axes.Axes (or an array of, if ``nrows`` > 1)) """ running_ipython = is_running_ipython() if interactive is None: interactive = running_ipython if tuple(map(int, matplotlib.__version__.split('.'))) <= (3, 0, 3): warnings.warn('This version of matplotlib does not allow saving figures from axis created using Figure(), forcing interactive=True') interactive = True if interactive: figure, axes = plt.subplots( ncols=ncols, nrows=nrows, figsize=(width, height * nrows), **kwargs ) else: figure = Figure(figsize=(width, height * nrows)) axes = figure.subplots(ncols=ncols, nrows=nrows, **kwargs) if isinstance(axes, Iterable): ax_list = axes else: ax_list = [axes] use_widgets = interactive and running_ipython if link_dataframes: if not use_widgets: cls.get_logger().error('Dataframes can only be linked to axes in interactive widget plots') else: for axis in ax_list: axis_link_dataframes(axis, link_dataframes) if cursor_delta or cursor_delta is None and use_widgets: if not use_widgets and cursor_delta is not None: cls.get_logger().error('Cursor delta can only be used in interactive widget plots') else: for axis in ax_list: axis_cursor_delta(axis) # Needed for multirow plots to not overlap with each other figure.set_tight_layout(dict(h_pad=3.5)) return figure, axes
fLambdaplot.autoscale(tight=True) else: fLambdaplot = fLambda.add_subplot(111) fLambdaplot.set_title('$G13$ - $Lifetime$ x $Charge$ $density$') fLambdaplot.set_xlabel('$n( cm^{-3} )$', fontsize=15) fLambdaplot.tick_params(axis='x', labelsize=12) fLambdaplot.set_ylabel('$Lifetime$ $(s)$', fontsize=15) fLambdaplot.tick_params(axis='y', labelsize=12) fLambdaplot.loglog(ndiffC, tau, 'k-o', lw=1, ms=8, label='from Diff. Cap. ($\lambda$ = %1.2f)' % float(lambdas['lambDC'])) fLambdaplot.loglog(np.exp(lnQ1), np.exp(lnLT1), color=self.colorsfit[2], lw=3, alpha=0.8) fLambdaplot.loglog(ndiffC_rev, tau, 'k-o', mfc='w', lw=1, ms=5, label='from Diff. Cap. [rev] ($\lambda$ = %1.2f)' % float(lambdas['lambDC_rev'])) fLambdaplot.loglog(np.exp(lnQ2), np.exp(lnLT2), color=self.colorsfit[3], lw=3, alpha=0.8) if len(lnLT3) : fLambdaplot.loglog(nce, tau, 'b-o', lw=1, ms=8, label='from CE ($\lambda$ = %1.2f)' % float(lambdas['lambCE'])) fLambdaplot.loglog(np.exp(lnQ3), np.exp(lnLT3), color=self.colorsfit[4], lw=3, alpha=0.8) if len(lnLT4) : fLambdaplot.loglog(nce_rev, tau, 'b-o', mfc='w', lw=1, ms=5, label='from CE [rev] ($\lambda$ = %1.2f)' % float(lambdas['lambCE_rev'])) fLambdaplot.loglog(np.exp(lnQ4), np.exp(lnLT4), color=self.colorsfit[5], lw=3, alpha=0.8) fLambdaplot.legend(fontsize="14",loc="best", framealpha=0.5) if self.useTightLayoutLambda.isChecked() : fLambdaplot.autoscale(tight=True) fLambda.set_tight_layout(True) return fLambda app = QtGui.QApplication(sys.argv) main = Main() main.show() sys.exit(app.exec_())
class MyPlotPanel(wx.Panel): def __init__(self, parent, figsize=None, dpi=None, bgcolor=None, type=None, toolbar=None, aspect=1, **kwargs): """ construction method of MyPlotPanel class :param parent: parent object :param figsize: plot figure size, (w, h) :param dpi: figure dpi, :parma bgcolor: background color of figure and canvas :param type: type of initial figure, 'image' or 'line' :param toolbar: show toolbar if not set None """ wx.Panel.__init__(self, parent, **kwargs) self.parent = parent self.figsize = figsize self.dpi = dpi self.bgcolor = bgcolor self.type = type self.toolbar = toolbar self.aspect = aspect self.figure = Figure(self.figsize, self.dpi) self.canvas = FigureCanvas(self, -1, self.figure) # figure background color self.set_color(self.bgcolor) # initialize plot self._init_plot() # set layout self.set_layout() # binding events self.canvas.mpl_connect('button_press_event', self.on_press) self.canvas.mpl_connect('button_release_event', self.on_release) self.canvas.mpl_connect('motion_notify_event', self.on_motion) self.Bind(wx.EVT_SIZE, self.on_size) def set_layout(self): """ set panel layout """ sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas, 1, wx.EXPAND) hbox = wx.BoxSizer(wx.HORIZONTAL) if self.toolbar is not None: self.toobar = MyToolbar(self.canvas) self.toobar.Realize() hbox.Add(self.toobar, 0, wx.EXPAND | wx.RIGHT, 10) self.pos_st = wx.StaticText(self, label='') hbox.Add(self.pos_st, 0, wx.ALIGN_CENTER_VERTICAL) sizer.Add(hbox, 0, wx.EXPAND | wx.BOTTOM, 0) self.SetSizerAndFit(sizer) def _init_plot(self): if not hasattr(self, 'axes'): self.axes = self.figure.add_subplot(111, aspect=self.aspect) if self.type == 'image': # draw image x = y = np.linspace(-np.pi, np.pi, 100) self.x, self.y = np.meshgrid(x, y) self.z = self._func_peaks(self.x, self.y) self.image = self.axes.imshow(self.z) else: # draw line self.x = np.linspace(-10, 10, 200) self.y = np.sin(self.x) self.line, = self.axes.plot(self.x, self.y) def set_color(self, rgb_tuple): """ set figure and canvas with the same color. :param rgb_tuple: rgb color tuple, e.g. (255, 255, 255) for white color """ if rgb_tuple is None: rgb_tuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() clr = [c/255.0 for c in rgb_tuple] self.figure.set_facecolor(clr) self.figure.set_edgecolor(clr) self.canvas.SetBackgroundColour(wx.Colour(*rgb_tuple)) def on_size(self, event): self.fit_canvas() self.canvas.draw_idle() event.Skip() def on_press(self, event): pass def on_release(self, event): pass def on_motion(self, event): if event.inaxes is not None: self.pos_st.SetLabel("({x:<.4f}, {y:<.4f})".format(x=event.xdata, y=event.ydata)) def fit_canvas(self): """ tight fit canvas layout """ #self.canvas.SetSize(self.GetSize()) self.figure.set_tight_layout(True) def _func_peaks(self, x, y): return 3.0 * (1.0 - x)**2.0 * np.exp(-(x**2) - (y+1)**2) \ - 10*(x/5 - x**3 - y**5) * np.exp(-x**2-y**2) \ - 1.0/3.0*np.exp(-(x+1)**2 - y**2)
import serial.tools.list_ports # from serial import * # from Tkinter import * arduino = serial.Serial('/dev/ttyACM0', 9600) db = mdb.connect(charset='utf8', host="127.0.0.1", user="******", passwd="root", db="lwc_members") cur = db.cursor() LARGE_FONT= ("Times", 20) style.use("ggplot") f = Figure(figsize=(5,5), dpi=100) f.set_tight_layout(False) a = f.add_subplot(221) b = f.add_subplot(222) c = f.add_subplot(223) d = f.add_subplot(224) def animate(i): ''' Animate for sunday Service attendance stats ''' event_type = "Sunday Service" month = 9 cur.execute("SELECT * FROM att_summ_2 WHERE event_type='%s' AND month(event_date)=%d " % (event_type, month) ) data = cur.fetchall()
def _create_graph(self, graph_model): """ Create graph with all its contents. :param graph_model: Model describing layout from model_wrapper. :return: Graph element. """ assert isinstance(graph_model, models.Graph) widget = QtGui.QWidget() widget_layout = QtGui.QVBoxLayout() widget.setLayout(widget_layout) widget.setContentsMargins(0, 0, 0, 0) fig = Figure() if graph_model.properties_as_dict()['projection'].get() == 'polar': axes = fig.add_subplot(111, projection='polar') else: axes = fig.add_subplot(111) axes.hold(True) axes.set_autoscalex_on(False) axes.set_autoscaley_on(False) fig.set_facecolor((0.0, 0.0, 0.0, 0.0)) fig.set_tight_layout(True) canvas = FigureCanvas(fig) def set_title(axes_, canvas_, value): axes_.set_title(value) canvas_.draw_idle() def set_grid(axes_, canvas_, value): axes_.grid(value) canvas_.draw_idle() accessor_map = { 'width': (widget.width, widget.setFixedWidth), 'height': (widget.height, widget.setFixedHeight), 'title': (axes.get_title, functools.partial(set_title, axes, canvas)), 'grid': (lambda: axes._gridOn, functools.partial(set_grid, axes, canvas)) } dimension_list = None for p in graph_model.properties: if p.property in accessor_map: wrap_and_bind(self.binding_context, canvas, p, *accessor_map[p.property]) for child in graph_model.children: if isinstance(child, models.GraphDimensions): dimension_list = self._create_graph_dimensions(child) # or layers. elif isinstance(child, models.GraphLayers): self._create_graph_layers(child, axes, canvas) # We let each layer take responsibility for managing and adding their # own artists, hence removing code below. This is necessary since some # plots change the number of artists depending on some property. # One example is a 1D-histogram where the number of rectangles change # when the number of bins change. It also simplifies being able to # reuse existing MPL-plots which are adding the elements automatically. self._bind_axes(dimension_list, axes, canvas, graph_model) widget_layout.addWidget(canvas) return widget
class MplGraphQt5Widget(QWidget): def __init__(self, parent=None): super(MplGraphQt5Widget, self).__init__(parent) self.width = 3 self.height = 3 self.dpi = 100 self._dataY = np.array([]) self._dataX = np.array([]) self._spCols = 1 self._spRows = 1 self.all_sp_axes = [] self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi) self.all_sp_axes.append(self.fig.add_subplot(self._spCols, self._spRows, 1)) self.fig.set_frameon(False) self.fig.set_tight_layout(True) self.canvas = Canvas(self.fig) self._navBarOn = False self.mpl_toolbar = NavigationToolbar(self.canvas, parent) self.mpl_toolbar.dynamic_update() self.canvas.mpl_connect('key_press_event', self.on_key_press) self.canvas.mpl_connect('button_press_event', self.on_button_press) self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move) self.canvas.setFocusPolicy(Qt.ClickFocus) self.canvas.setFocus() self.canvas.setParent(parent) self.canvas.clearMask() self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.canvas.updateGeometry() vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) if not self._navBarOn: self.mpl_toolbar.hide() self.setLayout(vbox) def get_icon(name): """Return Matplotlib icon *name*""" return QIcon(osp.join(rcParams['datapath'], 'images', name)) key_pressed = pyqtSignal(object, name='keyPressed') def on_key_press(self, event): self.key_pressed.emit(event) key_press_handler(event, self.canvas, self.mpl_toolbar) button_pressed = pyqtSignal(object, name='buttonPressed') def on_button_press(self, event): self.button_pressed.emit(event) key_press_handler(event, self.canvas, self.mpl_toolbar) mouse_move = pyqtSignal(object, name='mouseMoved') def on_mouse_move(self, event): self.mouse_move.emit(event) key_press_handler(event, self.canvas, self.mpl_toolbar) def generateNewAxes(self): for ax in self.all_sp_axes: self.fig.delaxes(ax) self.all_sp_axes = [] numOfAxes = (self._spRows*self._spCols)+1 for i in np.arange(1,numOfAxes): self.all_sp_axes.append(self.fig.add_subplot(self._spRows, self._spCols, i)) self.canvas.setGeometry(100, 100, 300, 300) #Used to update the new number of axes self.canvas.updateGeometry() #This will bring the size of the canvas back to the original (defined by the vbox) spRowsChanged = pyqtSignal(int) def getspRows(self): return self._spRows @pyqtSlot(int) def setspRows(self, spRows): self._spRows = spRows self.generateNewAxes() self.spRowsChanged.emit(spRows) def resetspRows(self): self.setspRows(1) spRows = pyqtProperty(int, getspRows, setspRows, resetspRows) spColsChanged = pyqtSignal(int) def getspCols(self): return self._spCols @pyqtSlot(int) def setspCols(self, spCols): self._spCols = spCols self.generateNewAxes() self.spRowsChanged.emit(spCols) def resetspCols(self): self.setspCols(1) spCols = pyqtProperty(int, getspCols, setspCols, resetspCols) dataChanged = pyqtSignal(bool) def get_Y_data(self): return self._dataY @pyqtSlot(int) def set_Y_data(self, y_data): self._dataY = y_data self.dataChanged.emit(True) def plot(self, on_axes=0): if np.size(self._dataX) == 0: self.all_sp_axes[on_axes].plot(self._dataY) else: self.all_sp_axes[on_axes].plot(self._dataX, self._dataY) def getNavBarOn(self): return self._navBarOn def setNavBarOn(self, navBarOn): self._navBarOn = navBarOn if not navBarOn: self.mpl_toolbar.hide() else: self.mpl_toolbar.show() def resetNavBarOn(self): self._navBarOn = True navBarOn = pyqtProperty(bool, getNavBarOn, setNavBarOn, resetNavBarOn) @pyqtSlot(bool) def set_autoscale(self, autoscale): for axis in self.all_sp_axes: axis.set_autoscale(autoscale)
class MplCanvas(FigureCanvas): """Class to represent the FigureCanvas widget""" rightDrag = Signal(float, float) leftDrag = Signal(float, float) homeButton = Signal() resize_begin = Signal() resize_end = Signal() def __init__(self): interactive = matplotlib.is_interactive() matplotlib.interactive(False) self.roi_callback = None self.fig = Figure(facecolor='#ffffff') try: self.fig.set_tight_layout(True) except AttributeError: # matplotlib < 1.1 pass FigureCanvas.__init__(self, self.fig) FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) self.manager = FigureManager(self, 0) matplotlib.interactive(interactive) self._resize_timer = QTimer() self._resize_timer.setInterval(250) self._resize_timer.setSingleShot(True) self._resize_timer.timeout.connect(self._on_timeout) self.renderer = None def _on_timeout(self): buttons = QtGui.QApplication.instance().mouseButtons() if buttons != Qt.NoButton: self._resize_timer.start() else: self.resize_end.emit() def paintEvent(self, event): # draw the zoom rectangle more prominently drawRect = self.drawRect self.drawRect = False # super needs this if self.renderer is None: self.renderer = self.get_renderer() super(MplCanvas, self).paintEvent(event) if drawRect: p = QtGui.QPainter(self) p.setPen(QtGui.QPen(Qt.red, 2, Qt.DotLine)) p.drawRect(self.rect[0], self.rect[1], self.rect[2], self.rect[3]) p.end() if self.roi_callback is not None: self.roi_callback(self) def resizeEvent(self, event): if not self._resize_timer.isActive(): self.resize_begin.emit() self._resize_timer.start() super(MplCanvas, self).resizeEvent(event)
class PixelTracker(object): def __init__( self, row=0, col=0, buffer_size=100, width=500, height=400, dpi=100, master=None, ): self.width = width self.height = height self.dpi = dpi self.fig = Figure( figsize=(self.width / self.dpi, self.height / self.dpi), dpi=self.dpi ) self.fig.set_tight_layout(True) self.ax = self.fig.add_subplot(111) self.plt_canvas = FigureCanvasTkAgg(self.fig, master=master) self.plt_canvas.draw() # Attributes for the pixel tracker self.buffer_size = buffer_size self.reset(row, col) self.pixels = {} def add(self, pixel, label=None): if label is None: label = pixel_to_str(pixel) # If this is not a preview and the label is already # in the list, we abort if label != PREVIEW_LABEL and label in self.pixels: return # Add the pixel self.pixels[label] = { "loc": pixel, "values": [0 for i in range(self.buffer_size)], } def remove(self, pixel_str): self.pixels.pop(pixel_str) def grid(self, *args, **kwargs): self.plt_canvas.get_tk_widget().grid(*args, **kwargs) def reset(self, row, col): self.row = row self.col = col self.values = [0 for i in range(self.buffer_size)] def push(self, frame): for lbl, pxl in self.pixels.items(): col, row = pxl["loc"] # Make the value grayscale if it isn't already if frame.ndim == 3: value = cv2.cvtColor( frame[row, col, None, None, :], cv2.COLOR_RGB2GRAY )[0, 0] elif frame.ndim == 2: value = frame[row, col] else: raise ValueError("Bad input") # add to the buffer pxl["values"].pop(0) pxl["values"].append(value) def update(self): # update the graph self.ax.clear() for lbl, pxl in self.pixels.items(): self.ax.plot( np.arange(-len(pxl["values"]) + 1, 1), pxl["values"], label=lbl ) if len(self.pixels) > 0: self.ax.legend(loc="lower left") self.ax.set_ylim([0, 255]) self.ax.set_xticks([]) self.ax.set_yticks([]) self.fig.tight_layout(pad=0) self.plt_canvas.draw() def mark(self, frame, conv_func): """ Mark the locations of pixels with circles on a frame """ mark_radius = 4 mark_color_preview = (0, 0, 255) mark_color_selected = (255, 0, 0) for lbl, p in self.pixels.items(): if lbl == PREVIEW_LABEL: c = mark_color_preview else: c = mark_color_selected if conv_func is not None: loc = conv_func(*p["loc"]) else: loc = p["loc"] frame = cv2.circle(frame, loc, mark_radius, c, 2) return frame
class TrackChart(wx.Panel): def __init__(self, parent, main_window): wx.Panel.__init__(self, parent, size=(100, 100)) self.main_window = main_window self.main_box = wx.StaticBox(self, label='Spotfinding Chart') self.main_fig_sizer = wx.StaticBoxSizer(self.main_box, wx.VERTICAL) self.SetSizer(self.main_fig_sizer) self.track_figure = Figure() self.track_axes = self.track_figure.add_subplot(111) self.track_axes.set_ylabel('Found Spots') self.track_axes.set_xlabel('Frame') self.track_figure.set_tight_layout(True) self.track_canvas = FigureCanvas(self, -1, self.track_figure) self.track_axes.patch.set_visible(False) self.plot_sb = wx.Slider(self, minValue=0, maxValue=1) self.plot_sb.Hide() self.main_fig_sizer.Add(self.track_canvas, 1, wx.EXPAND) self.main_fig_sizer.Add(self.plot_sb, flag=wx.EXPAND) # Scroll bar binding self.Bind(wx.EVT_SCROLL, self.onScroll, self.plot_sb) # Plot bindings self.track_figure.canvas.mpl_connect('button_press_event', self.onPress) # self.track_figure.canvas.mpl_connect('scroll_event', self.onScroll) self.reset_chart() def onSelect(self, xmin, xmax): ''' Called when SpanSelector is used (i.e. click-drag-release); passes on the boundaries of the span to tracker window for selection and display of the selected images ''' if self.selector == 'select': self.select_span.set_visible(True) self.patch_x = int(xmin) self.patch_x_last = int(xmax) + 1 self.patch_width = self.patch_x_last - self.patch_x self.bracket_set = True self.main_window.update_image_list() gp = self.main_window.tracker_panel.graph_panel ip = self.main_window.tracker_panel.image_list_panel sp = self.main_window.tracker_panel.chart_sash_position if sp == 0: sp = int(self.main_window.GetSize()[0] * 0.70) self.main_window.tracker_panel.chart_splitter.SplitVertically(gp, ip, sp) self.main_window.tracker_panel.Layout() elif self.selector == 'zoom': if (int(xmax) - int(xmin) >= 5): self.x_min = int(xmin) self.x_max = int(xmax) self.plot_zoom = True self.max_lock = False self.chart_range = int(self.x_max - self.x_min) self.main_window.tracker_panel.chart_window.toggle.SetValue(True) self.main_window.tracker_panel.chart_window.toggle_boxes(flag_on=True) self.main_window.tracker_panel.chart_window.ctr.SetValue(self.chart_range) sb_center = self.x_min + self.chart_range / 2 self.plot_sb.SetValue(sb_center) self.plot_sb.Show() self.draw_plot() if self.bracket_set: self.bracket_set = False self.main_window.tracker_panel.chart_sash_position = \ self.main_window.tracker_panel.chart_splitter.GetSashPosition() self.main_window.tracker_panel.chart_splitter.Unsplit() self.main_window.tracker_panel.Layout() def onScroll(self, e): sb_center = self.plot_sb.GetValue() half_span = (self.x_max - self.x_min) / 2 if sb_center - half_span == 0: self.x_min = 0 self.x_max = half_span * 2 else: self.x_min = sb_center - half_span self.x_max = sb_center + half_span if self.plot_sb.GetValue() == self.plot_sb.GetMax(): self.max_lock = True else: self.max_lock = False self.draw_plot() def onPress(self, e): ''' If left mouse button is pressed, activates the SpanSelector; otherwise, makes the span invisible and sets the toggle that clears the image list; if shift key is held, does this for the Selection Span, otherwise does this for the Zoom Span ''' if e.button != 1: self.zoom_span.set_visible(False) self.select_span.set_visible(False) self.bracket_set = False self.plot_zoom = False self.plot_sb.Hide() self.draw_plot() # Hide list of images self.main_window.tracker_panel.chart_sash_position = \ self.main_window.tracker_panel.chart_splitter.GetSashPosition() self.main_window.tracker_panel.chart_splitter.Unsplit() self.main_window.tracker_panel.chart_window.toggle.SetValue(False) self.main_window.tracker_panel.chart_window.toggle_boxes(flag_on=False) self.main_window.tracker_panel.Layout() else: if self.main_window.tb_btn_view.IsToggled(): self.selector = 'select' self.zoom_span.set_visible(False) self.select_span.set_visible(True) elif self.main_window.tb_btn_zoom.IsToggled(): self.selector = 'zoom' self.zoom_span.set_visible(True) self.select_span.set_visible(False) def reset_chart(self): self.track_axes.clear() self.track_figure.patch.set_visible(False) self.track_axes.patch.set_visible(False) self.xdata = [] self.ydata = [] self.idata = [] self.x_min = 0 self.x_max = 1 self.y_max = 1 self.bracket_set = False self.button_hold = False self.plot_zoom = False self.chart_range = None self.selector = None self.max_lock = True self.patch_x = 0 self.patch_x_last = 1 self.patch_width = 1 self.start_edge = 0 self.end_edge = 1 self.acc_plot = self.track_axes.plot([], [], 'o', color='#4575b4')[0] self.rej_plot = self.track_axes.plot([], [], 'o', color='#d73027')[0] self.idx_plot = self.track_axes.plot([], [], 'wo', ms=2)[0] self.bragg_line = self.track_axes.axhline(0, c='#4575b4', ls=':', alpha=0) self.highlight = self.track_axes.axvspan(0.5, 0.5, ls='--', alpha=0, fc='#deebf7', ec='#2171b5') self.track_axes.set_autoscaley_on(True) self.select_span = SpanSelector(ax=self.track_axes, onselect=self.onSelect, direction='horizontal', rectprops=dict(alpha=0.5, ls = ':', fc='#deebf7', ec='#2171b5')) self.select_span.set_active(False) self.zoom_span = SpanSelector(ax=self.track_axes, onselect=self.onSelect, direction='horizontal', rectprops=dict(alpha=0.5, ls = ':', fc='#ffffd4', ec='#8c2d04')) self.zoom_span.set_active(False) def draw_bragg_line(self): min_bragg = self.main_window.tracker_panel.min_bragg.ctr.GetValue() if min_bragg > 0: self.bragg_line.set_alpha(1) else: self.bragg_line.set_alpha(0) self.bragg_line.set_ydata(min_bragg) try: self.draw_plot() except AttributeError, e: pass
class Cut: def __init__(self, nb, app): self.nb = nb self.app = app self.frame = ttk.Frame(self.nb) self.levelCut = 30 self.nb.add(self.frame, text=' Cut ') self.lastGcodeFileName = "" #self.l_frame= ttk.Frame(self.frame, relief="groove",padding=10) #self.r_frame= ttk.Frame(self.frame, relief="groove",padding=10) self.l_frame = ttk.Frame(self.frame) self.r_frame = ttk.Frame(self.frame) self.l_frame.pack(side=LEFT, fill=Y) self.r_frame.pack(side=RIGHT, fill=Y) r = 0 self.cutBtn = tk.Button(self.l_frame, text='Cut', width=25, command=self.cut, state='disabled') self.cutBtn.grid(column=0, row=r, padx=1, pady=(20, 1)) r += 1 self.cancelBtn = tk.Button(self.l_frame, text='Cancel', command=self.app.tGrbl.resetGrbl, width=25, state='disabled') self.cancelBtn.grid(column=0, columnspan=2, row=r, pady=(10, 1), sticky=W) r += 1 self.button2 = tk.Button(self.l_frame, text='Save Gcode', width=25, command=self.saveGcode) self.button2.grid(column=0, row=r, padx=1, pady=(20, 1)) r += 1 tk.Label(self.l_frame, text="Usual cutting speed (mm/sec)").grid(column=0, row=r, pady=(20, 1), sticky=W) EntryFloat(self.l_frame, self.app.vCut, 0.1, 10, self.levelCut, width='6').grid(column=1, row=r, padx=1, pady=(20, 1), sticky=W) #r += 1 #tk.Label(self.l_frame, text="Entry slope (degree)").grid(column=0, row=r, pady=(2,1), sticky=W) r += 1 tk.Label(self.l_frame, text="Entry slope (degree) At root)").grid(column=0, row=r, pady=(10, 1), sticky=E) EntryFloat(self.l_frame, self.app.angleInRoot, -70, +70, self.levelCut, width='6').grid(column=1, row=r, padx=1, pady=(10, 1), sticky=W) r += 1 tk.Label(self.l_frame, text="At tip)").grid(column=0, row=r, pady=(1, 1), sticky=E) EntryFloat(self.l_frame, self.app.angleInTip, -70, +70, self.levelCut, width='6').grid(column=1, row=r, padx=1, pady=(1, 1), sticky=W) #r += 1 #tk.Label(self.l_frame, text="Exit slope (degree)").grid(column=0, row=r, pady=(5,1), sticky=W) r += 1 tk.Label(self.l_frame, text="Exit slope (degree) At root)").grid(column=0, row=r, pady=(1, 1), sticky=E) EntryFloat(self.l_frame, self.app.angleOutRoot, -70, +70, self.levelCut, width='6').grid(column=1, row=r, padx=1, pady=(1, 1), sticky=W) r += 1 tk.Label(self.l_frame, text="At tip)").grid(column=0, row=r, pady=(1, 1), sticky=E) EntryFloat(self.l_frame, self.app.angleOutTip, -70, +70, self.levelCut, width='6').grid(column=1, row=r, padx=1, pady=(1, 1), sticky=W) r += 1 tk.Label(self.l_frame, text="Errors").grid(column=0, row=r, pady=(20, 1), padx=(2, 1), sticky=W) r += 1 tk.Label(self.l_frame, textvariable=self.app.cutMsg, height=10).grid(column=0, columnspan=2, row=r, pady=(1, 1), padx=(10, 1), sticky=NW) r += 1 tk.Label(self.l_frame, text=" Display starts at ").grid(column=0, row=r, pady=(10, 1), sticky=E) r += 1 tk.Label(self.l_frame, text="X (mm)").grid(column=0, row=r, pady=(1, 1), sticky=E) EntryFloat(self.l_frame, self.app.limMinX, 0, 1500, self.levelCut, width='6').grid(column=1, row=r, padx=1, pady=(1, 1), sticky=W) r += 1 tk.Label(self.l_frame, text="Y (mm)").grid(column=0, row=r, pady=(1, 1), sticky=E) EntryFloat(self.l_frame, self.app.limMinY, 0, 500, self.levelCut, width='6').grid(column=1, row=r, padx=1, pady=(1, 1), sticky=W) r += 1 tk.Label(self.l_frame, text="Zoom factor").grid(column=0, row=r, pady=(1, 1), sticky=E) ttk.Combobox(self.l_frame, textvariable=self.app.zoom, values=["1X", "2X", "5X", "10X", "20X", "50", "100X"], state="readonly", width='4').grid(column=1, row=r, padx=1, pady=(1, 1), sticky=E) self.app.zoom.trace('w', self.calculateRedraw2) self.dotX = 10 # define the size and ratio of the figure self.dotY = 3 self.figRoot = Figure(figsize=(self.dotX, self.dotY), dpi=100) self.axesRoot = self.figRoot.add_subplot(1, 1, 1) self.axesRoot.spines['top'].set_visible(False) #avoid top frame self.axesRoot.spines['right'].set_visible(False) self.axesRoot.autoscale(enable=False) #self.axesRoot.set_xlim(0, 100) #self.axesRoot.set_ylim(0, 30) #self.axesRoot.set_title('Root') self.lineWireRoot1, = self.axesRoot.plot([], [], 'k:', color='black') self.lineProfileRoot2, = self.axesRoot.plot([], [], color='red') self.lineBlocRoot, = self.axesRoot.plot([], [], color='black') self.arrowRoot = None #use to annotate the line with an arrow self.figRoot.legend( (self.lineProfileRoot2, self.lineWireRoot1, self.lineBlocRoot), ('Root profile', 'Wire', 'Bloc'), 'upper right') self.figRoot.set_tight_layout(True) self.canvasRoot = FigureCanvasTkAgg( self.figRoot, master=self.r_frame) # A tk.DrawingArea. self.canvasRoot.draw() self.canvasRoot.get_tk_widget().grid(column=2, row=0, rowspan=22, padx=10, pady=(2, 2)) self.figTip = Figure(figsize=(self.dotX, self.dotY), dpi=100) self.axesTip = self.figTip.add_subplot(1, 1, 1) self.axesTip.spines['top'].set_visible(False) #avoid top frame self.axesTip.spines['right'].set_visible(False) self.axesTip.autoscale(enable=False) self.lineWireTip1, = self.axesTip.plot([], [], 'k:', color='black') self.lineProfileTip2, = self.axesTip.plot([], [], color='blue') self.lineBlocTip, = self.axesTip.plot([], [], color='black') self.arrowTip = None self.figTip.legend( (self.lineProfileTip2, self.lineWireTip1, self.lineBlocTip), ('Tip profile', 'Wire', 'Bloc'), 'upper right') self.figTip.set_tight_layout(True) self.canvasTip = FigureCanvasTkAgg( self.figTip, master=self.r_frame) # A tk.DrawingArea. self.canvasTip.draw() self.canvasTip.get_tk_widget().grid(column=2, row=22, rowspan=22, padx=10, pady=(10, 2)) def calback(self, a, b, c): self.calculateRedraw() def updateOProfil( self): #update plotting the bloc (2 canvas: Top and Back) blocRootX = [ self.app.blocToTableTrailingRoot.get(), self.app.blocToTableLeadingRoot.get(), self.app.blocToTableLeadingRoot.get(), self.app.blocToTableTrailingRoot.get(), self.app.blocToTableTrailingRoot.get() ] blocTipX = [ self.app.blocToTableTrailingTip.get(), self.app.blocToTableLeadingTip.get(), self.app.blocToTableLeadingTip.get(), self.app.blocToTableTrailingTip.get(), self.app.blocToTableTrailingTip.get() ] hOffset = self.app.hOffset.get() blocHZ = self.app.blocHZ.get() blocRootY = [ hOffset, hOffset, hOffset + blocHZ, hOffset + blocHZ, hOffset ] blocTipY = blocRootY maxX = np.amax( np.concatenate((self.oSimRX, self.app.pRootX, self.oSimTX, self.app.pTipX, blocRootX, blocTipX))) maxY = np.amax( np.concatenate((self.oSimRY, self.app.pRootY, self.oSimTY, self.app.pTipY, blocRootY, blocTipY))) + 1 limMinX = self.app.limMinX.get() limMinY = self.app.limMinY.get() zoomString = self.app.zoom.get() zoom = float(zoomString[0:len(zoomString) - 1]) #print("zoom=", zoom) if (maxY / maxX) < (self.dotY / self.dotX): limMaxX = limMinX + (maxX / zoom) limMaxY = limMinY + (maxX / zoom * self.dotY / self.dotX) else: limMaxY = limMinY + (maxY / zoom) limMaxX = limMinX + (maxY / zoom * self.dotX / self.dotY) self.axesRoot.set_xlim(limMinX, limMaxX) self.axesRoot.set_ylim(limMinY, limMaxY) self.lineWireRoot1.set_xdata(self.oSimRX) self.lineWireRoot1.set_ydata(self.oSimRY) self.lineProfileRoot2.set_xdata(self.app.pRootX) self.lineProfileRoot2.set_ydata(self.app.pRootY) self.lineBlocRoot.set_xdata(blocRootX) self.lineBlocRoot.set_ydata(blocRootY) if self.arrowRoot != None: self.axesRoot.texts.remove(self.arrowRoot) deltaX, deltaY = self.calculateDelta(self.oSimRX, self.oSimRY, 1, limMaxX - limMinX) self.arrowRoot = self.axesRoot.annotate( '', xy=(self.oSimRX[1] + deltaX, self.oSimRY[1] + deltaY), xycoords='data', #horizontalalignment='center', #verticalalignment='center', color='black', alpha=0.5, xytext=(self.oSimRX[1], self.oSimRY[1]), textcoords='data', arrowprops=dict(facecolor='black', headlength=10, shrink=0.05, alpha=0.5, width=2, headwidth=5), ) self.canvasRoot.draw_idle() self.axesTip.set_xlim(limMinX, limMaxX) self.axesTip.set_ylim(limMinY, limMaxY) self.lineWireTip1.set_xdata(self.oSimTX) self.lineWireTip1.set_ydata(self.oSimTY) self.lineProfileTip2.set_xdata(self.app.pTipX) self.lineProfileTip2.set_ydata(self.app.pTipY) self.lineBlocTip.set_xdata(blocTipX) self.lineBlocTip.set_ydata(blocTipY) if self.arrowTip != None: self.axesTip.texts.remove(self.arrowTip) deltaX, deltaY = self.calculateDelta(self.oSimTX, self.oSimTY, 1, limMaxX - limMinX) self.arrowTip = self.axesTip.annotate( '', xy=(self.oSimTX[1] + deltaX, self.oSimTY[1] + deltaY), xycoords='data', #horizontalalignment='center', #verticalalignment='center', color='black', alpha=0.5, xytext=(self.oSimTX[1], self.oSimTY[1]), textcoords='data', arrowprops=dict(facecolor='black', headlength=10, shrink=0.05, alpha=0.5, width=2, headwidth=5), ) self.canvasTip.draw_idle() def calculateDelta( self, X, Y, i, distX ): #i=index of the point in the array, distX= size of the X axis (mm) dX = X[i + 1] - X[i] dY = Y[i + 1] - Y[i] lXY = math.sqrt(dX * dX + dY * dY) #print("X, X+1 , Y, Y+1, distX ", X[i], X[i+1], Y[i], Y[i+1], distX) #print("dX, dY, lXY, deltaX, deltaY", dX, dY, lXY, dX / lXY * 0.05 * distX , dY / lXY * 0.05 * distX ) return dX / lXY * 0.05 * distX, dY / lXY * 0.05 * distX def cut(self): self.app.tGrbl.stream(self.gcode) pass def saveGcode(self): gcodeFileName = filedialog.asksaveasfilename(title="Save as...", defaultextension="*.gcode",\ filetypes=[("Gcode files","*.gcode"),("All files", "*")], initialfile=self.lastGcodeFileName) if len(gcodeFileName) > 0: f = open(gcodeFileName, 'w') f.write(self.gcode) f.close() self.lastGcodeFileName = gcodeFileName def calculateRedraw(self): self.calculate() self.updateOProfil() def calculateRedraw2(self, a, b, c): self.calculateRedraw() def calculate(self): #it start from PRoot and pTip (= profile taking care of bloc size and margin) #add enty and exit points in bloc #applies offset for radiance #simplifies the profiles if possible = simRoot and Tip #calculate the projection GX GY DX and DY #Vérifier la présence et l'égalité du nombre de points dans les 2 profils #to do control number of synchro points if (len(self.app.tRootX) > 0) and ( len(self.app.tTipX) > 0): # and (len(self.app.tRootX) == len(self.app.tTipX)): # create eRoot and eTip and add entry and exit point in the bloc (before applying heating offset) #print("pRootX pTipX", self.app.pRootX , self.app.pTipX) #print("pRootY pTipY", self.app.pRootY , self.app.pTipY) #print("pRootS pTipS", self.app.pRootS , self.app.pTipS) eRootX = self.app.pRootX.tolist() eRootY = self.app.pRootY.tolist() eRootS = list(self.app.pRootS) eTipX = self.app.pTipX.tolist() eTipY = self.app.pTipY.tolist() eTipS = list(self.app.pTipS) deltaInRoot = math.tan(self.app.angleInRoot.get() / 180 * math.pi) * self.app.mTrailingRoot.get() deltaOutRoot = math.tan(self.app.angleOutRoot.get() / 180 * math.pi) * self.app.mTrailingRoot.get() deltaInTip = math.tan(self.app.angleInTip.get() / 180 * math.pi) * self.app.mTrailingTip.get() deltaOutTip = math.tan(self.app.angleOutTip.get() / 180 * math.pi) * self.app.mTrailingTip.get() eRootX.insert(0, self.app.blocToTableTrailingRoot.get()) eRootY.insert(0, self.app.pRootY[0] - deltaInRoot) eTipX.insert(0, self.app.blocToTableTrailingTip.get()) eTipY.insert(0, self.app.pTipY[0] - deltaInTip) eRootX.append(self.app.blocToTableTrailingRoot.get()) eRootY.append(self.app.pRootY[-1] - deltaOutRoot) eTipX.append(self.app.blocToTableTrailingTip.get()) eTipY.append(self.app.pTipY[-1] - deltaOutTip) eRootS.insert(0, 4) # add a Synchro (with radiance) point eTipS.insert(0, 4) # add a Synchro (with radiance) point eRootS[ -1] = 4 # mark the last point as Synchro (with radiance) point eTipS[ -1] = 4 # mark the last point as Synchro (with radiance) point eRootS.append(4) #add a last point as Synchro eTipS.append(4) #add a last point as Synchro #print("Root=", list(zip(eRootX, eRootY, eRootS))) #build 2 listes with length of segments rootL = lengthSegment(eRootX, eRootY) tipL = lengthSegment(eTipX, eTipY) #build list of index of pt of synchro and length of sections between synchro eRootI, eRootL = lengthSection(eRootS, rootL) eTipI, eTipL = lengthSection(eTipS, tipL) #print("Root: Synchro code & Index, length segments & sections", eRootS , eRootI, rootL , eRootL) #print("Tip: Synchro code & Index, length segments & sections", eTipS , eTipI , tipL , eTipL) #compare les longueurs pour trouver le coté le plus long compLength = compareLength(eRootL, eTipL) #print("compare length", compLength) #Calcule la radiance de chaque côté ; met 0 si rRoot, rTip = self.calculate2Radiance(compLength, self.app.vCut.get()) #print("radiance root", rRoot ) #print("radiance tip", rTip) #create eRootR and eTipR with the radiance to fit the same nbr of item as exxxS eRootR = self.createRadiance(eRootS, rRoot) #print('full radiance root', eRootR) eTipR = self.createRadiance(eTipS, rTip) #print('full radiance tip', eTipR) # calculate offset on each side; create one new point at each synchronisation to take care of different radiance offsets self.offsetRootX, self.offsetRootY, self.offsetRootS = self.calculateOffset( eRootX, eRootY, eRootR, eRootS) #print("offset root", list(zip( self.offsetRootX , self.offsetRootY ,self.offsetRootS))) self.offsetTipX, self.offsetTipY, self.offsetTipS = self.calculateOffset( eTipX, eTipY, eTipR, eTipS) #print("offset tip", list(zip( self.offsetTipX , self.offsetTipY ,self.offsetTipS))) #print("len R T",len(self.offsetRootX) , len(self.offsetTipX) ) # adjust points in order to have the same number of points on each section self.syncRX, self.syncRY, self.syncTX, self.syncTY = self.synchrAllSections( self.offsetRootX, self.offsetRootY, self.offsetRootS, self.offsetTipX, self.offsetTipY, self.offsetTipS) """ print("eRoot X Y", eRootX, eRootY) print("eTip X Y", eTipX, eTipY) print("offset RX RY", self.offsetRootX , self.offsetRootY) print("offset TX TY", self.offsetTipX , self.offsetTipY) print("distance offset Rx Ry", self.printDistance(self.offsetRootX , self.offsetRootY)) print("distance offset Tx Ty", self.printDistance(self.offsetTipX , self.offsetTipY)) """ #Remove points if they are to close from each other (on both sides) #print("Offset ", self.offsetRootX , self.offsetRootY , self.offsetTipX , self.offsetTipY) self.oSimRX, self.oSimRY, self.oSimTX, self.oSimTY = self.simplifyProfiles( self.syncRX, self.syncRY, self.syncTX, self.syncTY) #print("len before after", len(self.syncRX), len(self.oSimRX)) #Calculate projections on cnc axis, messages, speed and feedRate (inverted) if self.app.leftRightWing.get( ) == 'Right': #for right wing, the root is on the left side self.GX, self.DX, self.GY, self.DY, self.warningMsg, self.speed, self.feedRate = self.projectionAll( self.oSimRX, self.oSimTX, self.oSimRY, self.oSimTY, self.app.blocToTableLeft.get() + self.app.tableYG.get(), self.app.blocLX.get(), self.app.tableYY.get() - self.app.blocToTableLeft.get() - self.app.tableYG.get() - self.app.blocLX.get()) else: #Left wing = root is on rigth side self.GX, self.DX, self.GY, self.DY, self.warningMsg, self.speed, self.feedRate = self.projectionAll( self.oSimTX, self.oSimRX, self.oSimTY, self.oSimRY, self.app.blocToTableLeft.get() + self.app.tableYG.get(), self.app.blocLX.get(), self.app.tableYY.get() - self.app.blocToTableLeft.get() - self.app.tableYG.get() - self.app.blocLX.get()) #print(self.warningMsg) self.app.cutMsg.set(self.warningMsg) #print("Projection ", self.GX , self.DX , self.GY, self.DY) #print("Projection ", self.GX , self.DX ) #genère le Gcode # set G54 à la valeur actuelle, set absolu et mm, set feed rate, met en chauffe, attend 5 sec # monte à la hauteur du ppremier point puis avance au premier point # passe tous les points # revient à la verticale de l'origine puis à l'origine # attend 5 sec puis éteint la chauffe puis éteint les moteurs self.gcode = self.generateGcode(self.GX, self.DX, self.GY, self.DY, self.speed, self.feedRate) def createRadiance(self, s, r): # create a radiance list with the same nbr of items as s and using the radiance in r imax = len(s) i = 0 rIdx = 0 rTemp = 0 result = [] while i < (imax - 1): if s[i] > 0: if s[i] > 4: rTemp = 0 else: rTemp = r[rIdx] rIdx += 1 result.append(rTemp) i += 1 return result def printDistance(self, x, y): imax = len(x) i = 0 result = [] while i < imax - 1: d1 = x[i + 1] - x[i] d2 = y[i + 1] - y[i] result.append(math.sqrt(d1 * d1 + d2 * d2)) i += 1 return result def generateGcode(self, GX, DX, GY, DY, axisSpeed, feedRate): #gCodeStart ="G10 L20 P1 X0 Y0 Z0 A0 \n G90 G21 M3 \n G04 P5.0\n G01 X0 \n" #gCodeEnd = "G04 P5.0\n M5\n M2\n" heat = self.app.tGuillotine.calculateHeating(self.app.vCut.get()) formatAxis = "{:.3f} " #A="X{:.3f} Y{:.3f} Z{:.3f} A{:.3f}\n" L = self.app.gCodeLetters.get() + "XYZA" # contains 4 letters G00 = "G00 " + L[0] + formatAxis + L[1] + formatAxis + L[ 2] + formatAxis + L[3] + formatAxis + "\n" G01 = "G01 " + L[0] + formatAxis + L[1] + formatAxis + L[ 2] + formatAxis + L[3] + formatAxis + "F{:d}\n" xyza = L[0] + formatAxis + L[1] + formatAxis + L[2] + formatAxis + L[ 3] + formatAxis + "\n" st = self.app.gCodeStart1.get() + "\n" + self.app.gCodeStart2.get( ) + "\n" + self.app.gCodeStart3.get( ) + "\n" + self.app.gCodeStart4.get() + "\n" st = st + "G10 L20 P1" + xyza.format( 0, 0, 0, 0) #set wcs to current position: G10 L20 P1 X0 Y0 Z0 A0 st = st + "G54\n" # apply wcs1 st = st + "S{:d}\n".format( int(heat)) # set the heating value based on speed st = st + "G90 G21 G93 M3\n" # apply Absolute and mm and inverted feedrate and heating st = st + "G04 P{:.1f}\n".format( self.app.tPreHeat.get()) # pause for the preheat delay en = "G04 P{:.1f}\n".format( self.app.tPostHeat.get()) # pause for the post delay en = en + "G94\nM5\nM2\n" # go back to normal feedrate , stop heating and stop motor en = en + self.app.gCodeEnd1.get() + "\n" + self.app.gCodeEnd2.get( ) + "\n" + self.app.gCodeEnd3.get() + "\n" + self.app.gCodeEnd4.get( ) + "\n" li = [] imax = len(GX) if imax > 1: i = 1 li.append(st) #append start li.append(G00.format(0.0, GY[0], 0.0, DY[0]), ) #move up li.append(G00.format(GX[0], GY[0], DX[0], DY[0]), ) #move up to entry of bloc while i < imax: li.append( G01.format(GX[i], GY[i], DX[i], DY[i], int( feedRate[i - 1]))) # we use inverted feedRate i += 1 li.append(G00.format(0, GY[-1], 0, DY[-1])) li.append(G00.format(0.0, 0.0, 0.0, 0.0)) li.append(en) #append End #print("".join(li)) #print the Gcode return "".join(li) # return a string containing the /n def projectionAll(self, x1, x2, y1, y2, lg, l, ld): # lg = outside length on the left side (from bloc to axis) # l = legnth between the 2 sides of bloc # ld = outside length on the right side (from bloc to axis) # x1 y1 = profil on the left side # x2 y2 = profil on the rigth side # return projection, warning msg and speed xg = [] xd = [] yg = [] yd = [] speed = [] # in mm/sec feedRate = [ ] # inverted feed rate for G93: e.g. 2 means that the movement has to be executed in 1/2 min xgmin = x1[0] xgmax = x1[0] xdmin = x2[0] xdmax = x2[0] ygmin = y1[0] ygmax = y1[0] ydmin = y2[0] ydmax = y2[0] vxGMax = 0 vyGMax = 0 vxDMax = 0 vyDMax = 0 msg = "" i = 0 imax = len(x1) if imax > 0: while i < imax: xg.append(((x1[i] - x2[i]) / l * lg) + x1[i]) xd.append(((x2[i] - x1[i]) / l * (l + ld)) + x1[i]) yg.append(((y1[i] - y2[i]) / l * lg) + y1[i]) yd.append(((y2[i] - y1[i]) / l * (l + ld)) + y1[i]) if xg[i] < xgmin: xgmin = xg[i] if xg[i] > xgmax: xgmax = xg[i] if xd[i] < xdmin: xdmin = xd[i] if xd[i] > xdmax: xdmax = xd[i] if yg[i] < ygmin: ygmin = yg[i] if yg[i] > ygmax: ygmax = yg[i] if yd[i] < ydmin: ydmin = yd[i] if yd[i] > ydmax: ydmax = yd[i] if i > 0: #calculate speed on Y axis #calculate legnth of segment dx1 = x1[i - 1] - x1[i] dy1 = y1[i - 1] - y1[i] dx2 = x2[i - 1] - x2[i] dy2 = y2[i - 1] - y2[i] dxG = abs(xg[i - 1] - xg[i]) dyG = abs(yg[i - 1] - yg[i]) dxD = abs(xd[i - 1] - xd[i]) dyD = abs(yd[i - 1] - yd[i]) d1 = dx1 * dx1 + dy1 * dy1 d2 = dx2 * dx2 + dy2 * dy2 dG = dxG * dxG + dyG * dyG dD = dxD * dxD + dyD * dyD #select the longest side if d1 >= d2: d1 = math.sqrt(d1) dG = math.sqrt(dG) d2 = math.sqrt(d2) dD = math.sqrt(dD) v1 = self.app.vCut.get() speed.append(v1 * dG / d1) feedRate.append(v1 / d1 * 60) vGD = v1 * dG / d1 vxG = v1 * dxG / d1 vyG = v1 * dyG / d1 vxD = v1 * dxD / d1 vyD = v1 * dyD / d1 else: d1 = math.sqrt(d1) dG = math.sqrt(dG) d2 = math.sqrt(d2) dD = math.sqrt(dD) v2 = self.app.vCut.get() speed.append(v2 * dD / d2) feedRate.append(v2 / d2 * 60) vGD = v2 * dD / d2 vxG = v2 * dxG / d2 vyG = v2 * dyG / d2 vxD = v2 * dxD / d2 vyD = v2 * dyD / d2 #print(" point {} dG={:.3f} d1={:.3f} dD={:.3f} d2={:.3f} vGD={:.3f} vxG={:.3f} vyG={:.3f} , vxD={:.3f} , vyD={:.3f} "\ # .format(i , dG , d1, dD , d2, vGD, vxG , vyG , vxD , vyD)) if vxG > vxGMax: vxGMax = vxG if vyG > vyGMax: vyGMax = vyG if vxD > vxDMax: vxDMax = vxD if vyD > vyDMax: vyDMax = vyD i += 1 if xgmin < 0: msg = msg + "Left hor. axis exceeds origin\n" if xgmax > self.app.cMaxY.get(): msg = msg + "Left hor. axis exceeds limit\n" if xdmin < 0: msg = msg + "Right hor. axis exceeds origin\n" if xdmax > self.app.cMaxY.get(): msg = msg + "Right vertical axis exceeds limit\n" if ygmin < 0: msg = msg + "Left vertical axis exceeds origin\n" if ygmax > self.app.cMaxZ.get(): msg = msg + "Left vertical axis exceeds limit\n" if ydmin < 0: msg = msg + "Right vertical axis exceeds origin\n" if ydmax > self.app.cMaxZ.get(): msg = msg + "Right vertical axis exceeds limit\n" if vxGMax > self.app.vMaxY.get(): msg = msg + "Left hor. axis exceeds speed {:.3f}\n".format( vxGMax) if vyGMax > self.app.vMaxZ.get(): msg = msg + "Left vertical axis exceeds speed\n" if vxDMax > self.app.vMaxY.get(): msg = msg + "Right hor. axis exceeds speed\n" if vyDMax > self.app.vMaxZ.get(): msg = msg + "Right vertical axis exceeds speed\n" return xg, xd, yg, yd, msg, speed, feedRate def simplifyProfiles(self, rX, rY, tX, tY): imax = len(rX) oRX = [] oRY = [] oTX = [] oTY = [] i = 0 if imax > 0: rXp = rX[i] rYp = rY[i] tXp = tX[i] tYp = tY[i] oRX.append(rXp) oRY.append(rYp) oTX.append(tXp) oTY.append(tYp) i = 1 while i < imax: dRX = rX[i] - rXp dRX *= dRX dRY = rY[i] - rYp dRY *= dRY dTX = tX[i] - tXp dTX *= dTX dTY = tY[i] - tYp dTY *= dTY if dRX > 0.01 or dRY > 0.01 or dTX > 0.01 or dTY > 0.01: rXp = rX[i] rYp = rY[i] tXp = tX[i] tYp = tY[i] oRX.append(rXp) oRY.append(rYp) oTX.append(tXp) oTY.append(tYp) i += 1 return oRX, oRY, oTX, oTY def calculateOffset(self, x, y, r, s): #create an offset for curve x y at a distance r (which varies) taking care of synchronisations # for each synchronisation point, create 2 offset points instead of 1 #x ,y, r (radiance) , s (synchro) have the same length # return new x y s # pour 3 points successifs p1-p2-p3 avec r1-r2, calcule les pt d'intersection des 2 offsets #print("offset for x, y, r,s", x ,y , r ,s) ox = [] oy = [] os = [] imax = len(r) i = 0 if imax >= 1: #met le premier point oxi, oxj, oyi, oyj = offset1Segment(x[0], x[1], y[0], y[1], r[0]) ox.append(oxi) oy.append(oyi) os.append(s[0]) while i < (imax - 1): if s[i + 1] == 0: #if it not a syncho, then offset is the same on both segements and we just take the intersection of the 2 offsets oxi, oyi = offset2Segment(x[i], x[i + 1], x[i + 2], y[i], y[i + 1], y[i + 2], r[i]) ox.append(oxi) oy.append(oyi) os.append(s[i + 1]) else: # for a synchro point, whe calculate 2 intersects (one for first offset and one for second offset) # and we add each of them (so we add a point) #print("offset2Segments" , x[i] , x[i+1] ,x[i+2], y[i] ,y[i+1] ,y[i+2] ,r[i]) oxi, oyi = offset2Segment(x[i], x[i + 1], x[i + 2], y[i], y[i + 1], y[i + 2], r[i]) ox.append(oxi) oy.append(oyi) os.append(s[i + 1]) oxi, oyi = offset2Segment(x[i], x[i + 1], x[i + 2], y[i], y[i + 1], y[i + 2], r[i + 1]) ox.append(oxi) oy.append(oyi) os.append(s[i + 1]) i += 1 oxi, oxj, oyi, oyj = offset1Segment(x[i], x[i + 1], y[i], y[i + 1], r[i]) ox.append(oxj) oy.append(oyj) os.append(s[-1]) return ox, oy, os def calculate2Radiance(self, compLength, speedMax): #print("in calculate2Radaiance, speedMax =" , speedMax) oMin = self.radiance(speedMax) #print("oMin =", oMin ) imax = len(compLength) rR = [] #radiance at root rT = [] #radiance at tip i = 0 if imax > 0: while i < imax: cLi = compLength[i] speedLow = speedMax * cLi #print("speedLow=", speedLow) #print("radiance speedLow" , self.radiance(speedLow) ) if cLi >= 0: # root is longer rR.append(oMin) rT.append(self.radiance(speedLow)) else: rT.append(oMin) rR.append(self.radiance(-speedLow)) i += 1 return rR, rT def radiance(self, speed): #print("mRadSpHalf=", self.app.mRadSpHalf.get() ) #print("mRadSpHigh" , self.app.mRadSpHigh.get() ) #print("mSpeedHalf" , self.app.mSpeedHalf.get() ) #print("mSpeedHigh" , self.app.mSpeedHigh.get() ) a = (self.app.mRadSpHalf.get() - self.app.mRadSpHigh.get()) / ( self.app.mSpeedHalf.get() - self.app.mSpeedHigh.get()) return 0.5 * ( (a * (speed - self.app.mSpeedHalf.get())) + self.app.mRadSpHalf.get() ) # use only 1/2 of radiance for the offset """ synchronise 2 profiles extrait le premier tronçon R et T pour chaque tronçon calcule la longueur R et T calcule la longueur cumulée R et T Ramène les longueurs cumulées dans un range 0 <> 1 pour R et T Crée une liste qui mélange les 2 (concatene, trie, élimine les doublons) Fait un interpolate de R et de T suivant cette liste Simplifie en enlevant les points trop rapprochés des 2 cotés """ def synchrAllSections(self, rX, rY, rS, tX, tY, tS): #synchronise 2 profiles in order to get the same number of points #it has to respect synchronisation points sectionsIdxR = self.sectionsIdx(rS) sectionsIdxT = self.sectionsIdx(tS) #print("sectionIdxR", sectionsIdxR) #print("sectionIdxT", sectionsIdxT) imax = len(sectionsIdxR) i = 0 syncRX = [] syncRY = [] syncTX = [] syncTY = [] if imax > 0: while i < imax: firstR = sectionsIdxR[i][0] lastR = sectionsIdxR[i][1] + 1 firstT = sectionsIdxT[i][0] lastT = sectionsIdxT[i][1] + 1 #print( "first fast", firstR, lastR , firstT, lastT) #print("rX" , rX[firstR:lastR] ) sRX, sRY, sTX, sTY = self.synchroOneSection( rX[firstR:lastR], rY[firstR:lastR], tX[firstT:lastT], tY[firstT:lastT]) syncRX = syncRX + sRX.tolist() syncRY = syncRY + sRY.tolist() syncTX = syncTX + sTX.tolist() syncTY = syncTY + sTY.tolist() i += 1 return syncRX, syncRY, syncTX, syncTY def sectionsIdx(self, s): # return a list of turple with begin and end Idx of each section i = 0 imax = len(s) result = [] if imax > 1: while i < (imax - 1): j = i + 1 while s[j] == 0: j += 1 result.append((i, j)) i = j + 1 return result def synchroOneSection(self, rX, rY, tX, tY): """pour chaque tronçon calcule la longueur cumulée R et T Ramène les longueurs cumulées dans un range 0 <> 1 pour R et T Crée une liste qui mélange les 2 (concatene, trie, élimine les doublons) Fait un interpolate de R et de T suivant cette liste Simplifie en enlevant les points trop rapprochés des 2 cotés """ #print("synchro one section", rX , rY ,tX , tY) cumulLengthR = np.array(self.cumulLength(rX, rY)) cumulLengthT = np.array(self.cumulLength(tX, tY)) totLengthR = cumulLengthR[-1] totLengthT = cumulLengthT[-1] if totLengthR == 0: # this happens when the 2 points are identical normLengthR = cumulLengthR else: normLengthR = cumulLengthR / totLengthR if totLengthT == 0: normLengthT = cumulLengthT else: normLengthT = cumulLengthT / totLengthT mergedLength = np.concatenate([normLengthR, normLengthT]) # concatenate mergedLength = np.unique(mergedLength) if mergedLength[ 0] > 0: #Add a point at zero except if it already exists (when a totalLength = 0) mergedLength = np.insert(mergedLength, 0, 0) if totLengthR == 0: rXnew = np.asarray([rX[0]] * len(mergedLength)) rYnew = np.asarray([rY[0]] * len(mergedLength)) else: mytck, myu = interpolate.splprep([rX, rY], k=1, s=0) rXnew, rYnew = interpolate.splev(mergedLength, mytck) if totLengthT == 0: tXnew = np.asarray([tX[0]] * len(mergedLength)) tYnew = np.asarray([tY[0]] * len(mergedLength)) else: mytck, myu = interpolate.splprep([tX, tY], k=1, s=0) tXnew, tYnew = interpolate.splev(mergedLength, mytck) #print("one section result" , rXnew , rYnew , tXnew , tYnew, type(rXnew)) return rXnew, rYnew, tXnew, tYnew def cumulLength(self, x, y): imax = len(x) i = 0 cL = [] cumLength = 0 if imax > 1: while i < (imax - 1): dx = x[i + 1] - x[i] dy = y[i + 1] - y[i] cumLength += math.sqrt(dx * dx + dy * dy) #calculate cumulative length cL.append(cumLength) i += 1 return cL
def main(): if sys.version_info < (3, 2): raise Exception( 'Unsupported Python version, please use at least Python 3.2') args = parse_arguments() ''' This could be done directly from glucometerutils instead of via CSV ''' with open(args.input_file, 'r', newline='') as f: rows = from_csv(f) ''' Skip ketone entries ''' rketones = re.compile('Ketone', flags=re.IGNORECASE) for row in rows: if rketones.search(row.get('measure_method')): rows.remove(row) elif rketones.search(row.get('comment')): rows.remove(row) ''' Skip finger stick test entries ''' rfinger = re.compile('Blood', flags=re.IGNORECASE) if not args.fingerstick: for row in rows: if rfinger.search(row.get('comment')): rows.remove(row) for row in rows: row = parse_entry(row, args.icons) ''' Ensure that the rows are sorted by date ''' rows = sorted(rows, key=lambda row: row.get('date'), reverse=False) ''' Fill in gaps that might exist in the data, in order to smooth the curves and fills ''' ''' We're using 10 minute gaps in order to have more accurate fills ''' rows = fill_gaps(rows, interval=dt.timedelta(minutes=10)) ''' If we're on the default values for units, highs and lows, check that the average value is under 35 (assuming that average mmol/L < 35 and average mg/dL > 35) ''' if args.units == UNIT_MMOLL and (args.high == DEFAULT_HIGH or args.low == DEFAULT_LOW): mean = round(np.mean([l.get('value') for l in rows]), 1) if mean > 35: args.units = UNIT_MGDL args.high = convert_glucose_unit(args.high, UNIT_MMOLL) args.low = convert_glucose_unit(args.low, UNIT_MMOLL) ''' Manually specify max and min for mg/dL ''' args.graph_max = GRAPH_MAX_MGDL args.graph_min = GRAPH_MIN_MGDL ''' Set some defaults ''' rcParams['font.size'] = 8 rcParams['axes.titlesize'] = 12 rcParams['font.family'] = 'sans-serif' rcParams['font.sans-serif'] = [ 'Calibri', 'Verdana', 'Geneva', 'Arial', 'Helvetica', 'DejaVu Sans', 'Bitstream Vera Sans', 'sans-serif' ] rcParams['mathtext.default'] = 'regular' ''' Load custom fonts for the icon sets At present, backend_pdf does not parse unicode correctly, and unicode characters from many fonts that lack proper glyph names are massed together and printed as the same character. The IcoGluco font, generated from Noto Sans and custom icons on IcoMoon, works around this. ''' if args.icons: args.customfont = import_font('fonts/icogluco.ttf') #args.customfont = import_font('fonts/OpenSansEmoji.ttf') # Alternate font ''' Calculate the days and weeks in which we are interested ''' ''' Note that trim_weeks should be adjusted based on the interval passed to fill_gaps() ''' (days, weeks) = list_days_and_weeks(rows, trim_weeks=300) totalweeks = sum([len(weeks[y]) for y in weeks]) totaldays = len(days) nrows = args.graphs_per_page ncols = 1 plotnum = 1 with FigurePDF(args.output_file) as pdf: ''' Overall averages for all data by hour of the day ''' start = rows[0].get('date') end = rows[-1].get('date') period = start.strftime('%A, %-d %B %Y') + ' to ' + end.strftime( '%A, %-d %B %Y') title = 'Overall Average Glucose Summary for ' + period data = {} for row in rows: mpdate = dt.datetime.combine(rows[0]['date'], row.get('date').time()) data[mdates.date2num(mpdate)] = { 'value': row.get('value'), 'comment': row.get('comment'), } ''' Calculate max and min values for each 15 minute interval across the data set ''' intervals = calculate_max_min(rows) intervaldata = {} for i in intervals: mpdate = dt.datetime.combine(rows[0]['date'], i) intervaldata[mdates.date2num(mpdate)] = { 'max': intervals.get(i).get('max'), 'min': intervals.get(i).get('min'), } ''' Calculate the mean and median blood glucose and HbA1c levels ''' (g_mean, g_median, a_mean, a_median) = calculate_averages(data, args) figure = Figure(figsize=args.pagesize) canvas = FigureCanvas(figure) ax = figure.add_subplot(nrows, ncols, plotnum) ax.set_title(title) figure.set_tight_layout({'pad': 3}) ''' Draw the target range ''' ax.axhspan(args.low, args.high, facecolor='#0072b2', edgecolor='#a8a8a8', alpha=0.1, zorder=15) ''' The maxmin fill (maximum and minimum values for each 15 minute period of the data set, by day) ''' generate_plot( intervaldata, ax=ax, transforms={ 'spline': True, 'maxmin': True }, args=args, color='#979797', ) ''' The graph with a bezier curve applied, and a boundary transform to change line colour above and below the target values ''' generate_plot(data, ax=ax, transforms={'bezier':True, 'avga1c':a_median, \ 'color':[RED, BLUE, RED], 'boundaries':[args.graph_min, args.low, args.high, args.graph_max]}, args=args, color=BLUE, ) ''' Save the graph to the output PDF if we're at the end of the page ''' pdf.savefig(figure) ax.clear() ''' Overall averages for a week by hour of the dday ''' cnt = 0 for year in reversed(sorted(weeks.keys())): for week in reversed(sorted(weeks[year].keys())): ''' Turn the year into a date (the first week of the year is the one containing January 4th) ''' time = dt.datetime.combine(dt.date(year, 1, 4), dt.time(0, 0, 0)) monday = time + dt.timedelta(days=-time.weekday(), weeks=week - 1) sunday = monday + dt.timedelta(days=6) period = monday.strftime( '%A, %-d %B %Y') + ' to ' + sunday.strftime( '%A, %-d %B %Y') title = 'Average Glucose for ' + period weekrows = [] for row in rows: for dow in range(7): day = monday + dt.timedelta(days=dow) if row.get('date').date() == day.date(): weekrows.append(row) data = {} for row in weekrows: mpdate = dt.datetime.combine(monday, row.get('date').time()) data[mdates.date2num(mpdate)] = { 'value': row.get('value'), 'comment': row.get('comment'), } ''' Calculate the maximum and minimum value for each 15-minute period of the day, across the week ''' intervals = calculate_max_min(weekrows) intervaldata = {} for i in intervals: mpdate = dt.datetime.combine(monday.date(), i) intervaldata[mdates.date2num(mpdate)] = { 'max': intervals.get(i).get('max'), 'min': intervals.get(i).get('min'), } ''' Calculate the mean and median blood glucose levels for the week ''' (g_mean, g_median, a_mean, a_median) = calculate_averages(data, args) if cnt % nrows == 0: figure = Figure(figsize=args.pagesize) canvas = FigureCanvas(figure) plotnum = (cnt % nrows) + 1 ax = figure.add_subplot(nrows, ncols, plotnum) ax.set_title(title) figure.set_tight_layout({'pad': 3}) ''' Draw the target range ''' ax.axhspan(args.low, args.high, facecolor='#0072b2', edgecolor='#a8a8a8', alpha=0.1, zorder=15) ''' The maxmin fill of maximum and minimum values ''' generate_plot( intervaldata, ax=ax, transforms={ 'spline': True, 'maxmin': True, 'avga1c': a_median }, args=args, color='#979797', ) ''' The graph with a bezier curve applied, and a boundary transform to change line colour above and below the target values ''' generate_plot(data, ax=ax, transforms={'bezier':True, \ 'color':[RED, BLUE, RED], 'boundaries':[args.graph_min, args.low, args.high, args.graph_max]}, args=args, color=BLUE, ) ''' Save the graph to the output PDF if we're at the end of the page or at the end of the data ''' if (cnt + 1) % nrows == 0 or (cnt + 1) == totalweeks: pdf.savefig(figure) ax.clear() cnt += 1 ''' Daily graphs ''' cnt = 0 for day in reversed(sorted(days.keys())): title = 'Daily Glucose Summary for ' + day.strftime( '%A, %-d %B %Y') data = {} for row in rows: if row.get('date').date() == day.date(): mpdate = dt.datetime.combine(day.date(), row.get('date').time()) data[mdates.date2num(mpdate)] = { 'value': row.get('value'), 'comment': row.get('comment'), } ''' Calculate the mean and median blood glucose levels for the day ''' (g_mean, g_median, a_mean, a_median) = calculate_averages(data, args) if cnt % nrows == 0: figure = Figure(figsize=args.pagesize) canvas = FigureCanvas(figure) plotnum = (cnt % nrows) + 1 ax = figure.add_subplot(nrows, ncols, plotnum) ax.set_title(title) figure.set_tight_layout({'pad': 3}) ''' Draw the target range ''' ax.axhspan(args.low, args.high, facecolor='#0072b2', edgecolor='#a8a8a8', alpha=0.2, zorder=15) ''' Draw graph with a spline tranform and labels ''' generate_plot( data, ax=ax, transforms={ 'spline': True, 'label': True, 'avgglucose': g_median }, args=args, color=BLUE, ) ''' Fill the chart with colour when line is higher or lower than target range ''' generate_plot( data, ax=ax, transforms={ 'spline': True, 'fill': True }, args=args, ) ''' Save the graph to the output PDF if we're at the end of the page ''' if (cnt + 1) % nrows == 0 or (cnt + 1) == totaldays: pdf.savefig(figure) ax.clear() cnt += 1 return 1
class KeithleyGuiApp(QtWidgets.QMainWindow): """ Provides a GUI for transfer and output sweeps on the Keithley 2600.""" def __init__(self, keithley): super(self.__class__, self).__init__() # load user interface layout from .ui file uic.loadUi( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'main.ui'), self) self.keithley = keithley # create figure area self._set_up_fig() # create LED indicator self.led = LedIndicator(self) self.led.setDisabled(True) # Make the led non clickable self.statusBar.addPermanentWidget(self.led) self.led.setChecked(False) # change color of status bar self.statusBar.setStyleSheet('QStatusBar{background:transparent;}') # set validators for lineEdit fields self.lineEditVgStart.setValidator(QtGui.QDoubleValidator()) self.lineEditVgStop.setValidator(QtGui.QDoubleValidator()) self.lineEditVgStep.setValidator(QtGui.QDoubleValidator()) self.lineEditVdStart.setValidator(QtGui.QDoubleValidator()) self.lineEditVdStop.setValidator(QtGui.QDoubleValidator()) self.lineEditVdStep.setValidator(QtGui.QDoubleValidator()) self.lineEditInt.setValidator(QtGui.QDoubleValidator()) self.lineEditSettling.setValidator(QtGui.QDoubleValidator()) ## load default settings into GUI self._on_load_default() # connect to call-backs self.pushButtonTransfer.clicked.connect(self._on_transfer_clicked) self.pushButtonOutput.clicked.connect(self._on_output_clicked) self.pushButtonAbort.clicked.connect(self._on_abort_clicked) self.comboBoxGateSMU.currentIndexChanged.connect( self._on_smu_gate_changed) self.comboBoxDrainSMU.currentIndexChanged.connect( self._on_smu_drain_changed) self.actionSettings.triggered.connect(self._on_settings_clicked) self.actionConnect.triggered.connect(self._on_connect_clicked) self.actionDisconnect.triggered.connect(self._on_disconnect_clicked) self.action_Exit.triggered.connect(self._on_exit_clicked) self.actionSaveSweepData.triggered.connect(self._on_save_clicked) self.actionLoad_data_from_file.triggered.connect(self._on_load_clicked) self.actionSaveDefaults.triggered.connect(self._on_save_default) self.actionLoadDefaults.triggered.connect(self._on_load_default) self.actionSaveSweepData.setEnabled(False) # update when keithley is connected self._update_Gui_connection() # create address dialog self.addressDialog = KeithleyAddressDialog(self.keithley) # connection update timer: check periodically if keithley is connected # and busy, act accordingly self.timer = QtCore.QTimer() self.timer.timeout.connect(self.update_connection_status) self.timer.start(10000) # Call every 10 seconds def update_connection_status(self): # disconncet if keithley does not respond, test by querying model if self.keithley.connected and not self.keithley.busy: try: self.keithley.localnode.model except (InvalidSession, OSError): self.keithley.disconnect() self._update_Gui_connection() def setIntialPosition(self): screen = QtWidgets.QDesktopWidget().screenGeometry(self) xPos = screen.left() + screen.width() / 4 yPos = screen.top() + screen.height() / 3 self.setGeometry(xPos, yPos, 900, 500) def _convert_to_Vd(self, string): try: return float(string) except ValueError: if string.find('trailing') > 0: return 'trailing' else: raise ValueError('Invalid drain voltage.') def _check_if_busy(self): if self.keithley.busy: msg = ('Keithley is currently used by antoher program. ' + 'Please try again later.') QtWidgets.QMessageBox.information(None, str('error'), msg) # ============================================================================= # Measurement callbacks # ============================================================================= def _on_transfer_clicked(self): """ Start a transfer measurement with current settings.""" self._check_if_busy() params = {'Measurement': 'transfer'} # get current settings # get current settings smugate = self.comboBoxGateSMU.currentText() params['smu_gate'] = getattr(self.keithley, smugate) smudrain = self.comboBoxDrainSMU.currentText() params['smu_drain'] = getattr(self.keithley, smudrain) params['VgStart'] = float(self.lineEditVgStart.text()) params['VgStop'] = float(self.lineEditVgStop.text()) params['VgStep'] = float(self.lineEditVgStep.text()) VdListString = self.lineEditVdList.text() VdStringList = VdListString.split(',') params['VdList'] = [self._convert_to_Vd(x) for x in VdStringList] params['tInt'] = float(self.lineEditInt.text()) params['delay'] = float(self.lineEditSettling.text()) # get combo box status if self.comboBoxSweepType.currentIndex() == 0: params['pulsed'] = False elif self.comboBoxSweepType.currentIndex() == 1: params['pulsed'] = True # run measurement self.statusBar.showMessage(' Recording transfer curve.') self.measureThread = MeasureThread(self.keithley, params) self.measureThread.finishedSig.connect(self._on_measure_done) # reflect idle and busy states in GUI self._gui_state_busy() self.measureThread.start() def _on_output_clicked(self): """ Start an output measurement with current settings.""" self._check_if_busy() params = {'Measurement': 'output'} # get current settings smugate = self.comboBoxGateSMU.currentText() params['smu_gate'] = getattr(self.keithley, smugate) smudrain = self.comboBoxDrainSMU.currentText() params['smu_drain'] = getattr(self.keithley, smudrain) params['VdStart'] = float(self.lineEditVdStart.text()) params['VdStop'] = float(self.lineEditVdStop.text()) params['VdStep'] = float(self.lineEditVdStep.text()) VgListString = self.lineEditVgList.text() VgStringList = VgListString.split(',') params['VgList'] = [float(x) for x in VgStringList] params['tInt'] = float(self.lineEditInt.text()) params['delay'] = float(self.lineEditSettling.text()) # get combo box status if self.comboBoxSweepType.currentIndex() == 0: params['pulsed'] = False elif self.comboBoxSweepType.currentIndex() == 1: params['pulsed'] = True # run measurement self.statusBar.showMessage(' Recording output curve.') self.measureThread = MeasureThread(self.keithley, params) self.measureThread.finishedSig.connect(self._on_measure_done) self._gui_state_busy() self.measureThread.start() def _on_measure_done(self, sweepData): self.statusBar.showMessage(' Ready.') self._gui_state_idle() self.actionSaveSweepData.setEnabled(True) self.sweepData = sweepData self.plot_new_data() if not self.keithley.abort_event.is_set(): self._on_save_clicked() def _on_abort_clicked(self): """ Aborts current measurement. """ self.keithley.abort_event.set() # ============================================================================= # Interface callbacks # ============================================================================= def _on_smu_gate_changed(self, intSMU): """ Triggered when the user selects a different gate SMU. """ if intSMU == 0 and len(self.keithley.SMU_LIST) < 3: self.comboBoxDrainSMU.setCurrentIndex(1) elif intSMU == 1 and len(self.keithley.SMU_LIST) < 3: self.comboBoxDrainSMU.setCurrentIndex(0) def _on_smu_drain_changed(self, intSMU): """ Triggered when the user selects a different drain SMU. """ if intSMU == 0 and len(self.keithley.SMU_LIST) < 3: self.comboBoxGateSMU.setCurrentIndex(1) elif intSMU == 1 and len(self.keithley.SMU_LIST) < 3: self.comboBoxGateSMU.setCurrentIndex(0) def _on_connect_clicked(self): self.keithley.connect() self._update_Gui_connection() if not self.keithley.connected: msg = ('Keithley cannot be reached at %s. ' % self.keithley.visa_address + 'Please check if address is correct and Keithley is ' + 'turned on.') QtWidgets.QMessageBox.information(None, str('error'), msg) def _on_disconnect_clicked(self): self.keithley.disconnect() self._update_Gui_connection() self.statusBar.showMessage(' No Keithley connected.') def _on_settings_clicked(self): self.addressDialog.show() def _on_save_clicked(self): """Show GUI to save current sweep data as text file.""" prompt = 'Save as file' filename = 'untitled.txt' formats = 'Text file (*.txt)' filepath = QtWidgets.QFileDialog.getSaveFileName( self, prompt, filename, formats) if len(filepath[0]) < 4: return self.sweepData.save(filepath[0]) def _on_load_clicked(self): """Show GUI to load sweep data from file.""" prompt = 'Load file' filepath = QtWidgets.QFileDialog.getOpenFileName(self, prompt) if len(filepath[0]) < 4: return self.sweepData = TransistorSweepData() self.sweepData.load(filepath[0]) self.plot_new_data() self.actionSaveSweepData.setEnabled(True) def _on_exit_clicked(self): self.keithley.disconnect() self.timer.stop() self.deleteLater() def _update_Gui_connection(self): """Check if Keithley is connected and update GUI.""" if self.keithley.connected and not self.keithley.busy: self._gui_state_idle() self.led.setChecked(True) elif self.keithley.connected and self.keithley.busy: self._gui_state_busy() self.led.setChecked(True) elif not self.keithley.connected: self._gui_state_disconnected() self.led.setChecked(False) def _gui_state_busy(self): """Set GUI to state for running measurement.""" self.pushButtonTransfer.setEnabled(False) self.pushButtonOutput.setEnabled(False) self.pushButtonAbort.setEnabled(True) self.actionConnect.setEnabled(False) self.actionDisconnect.setEnabled(False) self.statusBar.showMessage(' Measuring.') def _gui_state_idle(self): """Set GUI to state for IDLE Keithley.""" self.pushButtonTransfer.setEnabled(True) self.pushButtonOutput.setEnabled(True) self.pushButtonAbort.setEnabled(True) self.actionConnect.setEnabled(False) self.actionDisconnect.setEnabled(True) self.statusBar.showMessage(' Ready.') def _gui_state_disconnected(self): """Set GUI to state for disconnected Keithley.""" self.pushButtonTransfer.setEnabled(False) self.pushButtonOutput.setEnabled(False) self.pushButtonAbort.setEnabled(False) self.actionConnect.setEnabled(True) self.actionDisconnect.setEnabled(False) self.statusBar.showMessage(' No Keithley connected.') def _on_save_default(self): """Saves current settings from GUI as defaults.""" # save transfer settings CONF.set('Ktransfer2600', 'VgStart', float(self.lineEditVgStart.text())) CONF.set('Ktransfer2600', 'VgStop', float(self.lineEditVgStop.text())) CONF.set('Ktransfer2600', 'VgStep', float(self.lineEditVgStep.text())) VdListString = self.lineEditVdList.text() VdStringList = VdListString.split(',') CONF.set('Ktransfer2600', 'VdList', [self._convert_to_Vd(x) for x in VdStringList]) # save output settings CONF.set('Ktransfer2600', 'VdStart', float(self.lineEditVdStart.text())) CONF.set('Ktransfer2600', 'VdStop', float(self.lineEditVdStop.text())) CONF.set('Ktransfer2600', 'VdStep', float(self.lineEditVdStep.text())) VgListString = self.lineEditVgList.text() VgStringList = VgListString.split(',') CONF.set('Ktransfer2600', 'VgList', [float(x) for x in VgStringList]) # save general settings CONF.set('Ktransfer2600', 'tInt', float(self.lineEditInt.text())) CONF.set('Ktransfer2600', 'delay', float(self.lineEditSettling.text())) # get combo box status if self.comboBoxSweepType.currentIndex() == 0: CONF.set('Ktransfer2600', 'pulsed', False) elif self.comboBoxSweepType.currentIndex() == 1: CONF.set('Ktransfer2600', 'pulsed', True) CONF.set('Ktransfer2600', 'gate', self.comboBoxGateSMU.currentText()) CONF.set('Ktransfer2600', 'drain', self.comboBoxDrainSMU.currentText()) def _on_load_default(self): """Load default settings to interface.""" ## set text box contents # transfer curve settings self.lineEditVgStart.setText(str(CONF.get('Ktransfer2600', 'VgStart'))) self.lineEditVgStop.setText(str(CONF.get('Ktransfer2600', 'VgStop'))) self.lineEditVgStep.setText(str(CONF.get('Ktransfer2600', 'VgStep'))) self.lineEditVdList.setText( str(CONF.get('Ktransfer2600', 'VdList')).strip('[]')) # output curve settings self.lineEditVdStart.setText(str(CONF.get('Ktransfer2600', 'VdStart'))) self.lineEditVdStop.setText(str(CONF.get('Ktransfer2600', 'VdStop'))) self.lineEditVdStep.setText(str(CONF.get('Ktransfer2600', 'VdStep'))) self.lineEditVgList.setText( str(CONF.get('Ktransfer2600', 'VgList')).strip('[]')) # other self.lineEditInt.setText(str(CONF.get('Ktransfer2600', 'tInt'))) self.lineEditSettling.setText(str(CONF.get('Ktransfer2600', 'delay'))) # set PULSED comboBox status pulsed = CONF.get('Ktransfer2600', 'pulsed') if pulsed is False: self.comboBoxSweepType.setCurrentIndex(0) elif pulsed is True: self.comboBoxSweepType.setCurrentIndex(1) # Set SMU selection comboBox status cmbList = list(self.keithley.SMU_LIST) # get list of all SMUs # We have to comboBoxes. If there are less SMU's, extend list. while len(cmbList) < 2: cmbList.append('--') self.comboBoxGateSMU.clear() self.comboBoxDrainSMU.clear() self.comboBoxGateSMU.addItems(cmbList) self.comboBoxDrainSMU.addItems(cmbList) try: self.comboBoxGateSMU.setCurrentIndex( cmbList.index(CONF.get('Ktransfer2600', 'gate'))) self.comboBoxDrainSMU.setCurrentIndex( cmbList.index(CONF.get('Ktransfer2600', 'drain'))) except ValueError: self.comboBoxGateSMU.setCurrentIndex(0) self.comboBoxDrainSMU.setCurrentIndex(1) msg = 'Could not find last used SMUs in Keithley driver.' QtWidgets.QMessageBox.information(None, str('error'), msg) # ============================================================================= # Plotting commands # ============================================================================= def _set_up_fig(self): # get figure frame to match window color color = QtGui.QPalette().window().color().getRgb() color = [x / 255 for x in color] # set up figure itself self.fig = Figure(facecolor=color) self.fig.set_tight_layout('tight') self.ax = self.fig.add_subplot(111) self.ax.set_title('Sweep data', fontsize=10) self.ax.set_xlabel('Voltage [V]', fontsize=9) self.ax.set_ylabel('Current [A]', fontsize=9) self.ax.tick_params(axis='both', which='major', direction='out', colors='black', color=[0.5, 0.5, 0.5, 1], labelsize=9) self.canvas = FigureCanvas(self.fig) self.canvas.setMinimumWidth(530) self.canvas.draw() self.gridLayout2.addWidget(self.canvas) def plot_new_data(self): """ Plots the transfer or output curves. """ self.ax.clear() # clear current plot if self.sweepData.sweepType == 'transfer': for v in self.sweepData.step_list(): self.ax.semilogy(self.sweepData.vSweep[v], abs(self.sweepData.iDrain[v]), '-', label='Drain current, Vd = %s' % v) self.ax.semilogy(self.sweepData.vSweep[v], abs(self.sweepData.iGate[v]), '--', label='Gate current, Vd = %s' % v) self.ax.legend(loc=3) self.ax.autoscale(axis='x', tight=True) self.ax.set_title('Transfer data') self.ax.set_xlabel('Gate voltage [V]') self.ax.set_ylabel('Current [A]') self.canvas.draw() if self.sweepData.sweepType == 'output': for v in self.sweepData.step_list(): self.ax.plot(self.sweepData.vSweep[v], abs(self.sweepData.iDrain[v]), '-', label='Drain current, Vg = %s' % v) self.ax.plot(self.sweepData.vSweep[v], abs(self.sweepData.iGate[v]), '--', label='Gate current, Vg = %s' % v) self.ax.legend() self.ax.autoscale(axis='x', tight=True) self.ax.set_title('Output data') self.ax.set_xlabel('Drain voltage [V]') self.ax.set_ylabel('Current [A]') self.canvas.draw()
class MplCanvas(FigureCanvas): """Class to represent the FigureCanvas widget""" rightDrag = Signal(float, float) leftDrag = Signal(float, float) homeButton = Signal() resize_begin = Signal() resize_end = Signal() def __init__(self): self._draw_count = 0 interactive = matplotlib.is_interactive() matplotlib.interactive(False) self.roi_callback = None self.fig = Figure(facecolor='#ffffff') try: self.fig.set_tight_layout(True) except AttributeError: # matplotlib < 1.1 pass FigureCanvas.__init__(self, self.fig) FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) self.manager = FigureManager(self, 0) matplotlib.interactive(interactive) self._resize_timer = QTimer() self._resize_timer.setInterval(250) self._resize_timer.setSingleShot(True) self._resize_timer.timeout.connect(self._on_timeout) self.renderer = None def _on_timeout(self): buttons = QtGui.QApplication.instance().mouseButtons() if buttons != Qt.NoButton: self._resize_timer.start() else: self.resize_end.emit() def paintEvent(self, event): # draw the zoom rectangle more prominently try: drawRect = self.drawRect self.drawRect = False except AttributeError: # mpl 1.4 drawRect = self._drawRect self._drawRect = None # super needs this if self.renderer is None: self.renderer = self.get_renderer() super(MplCanvas, self).paintEvent(event) if drawRect: try: x, y, w, h = self.rect[0], self.rect[1], self.rect[ 2], self.rect[3] except TypeError: # mpl 1.4 x, y, w, h = drawRect p = QtGui.QPainter(self) p.setPen(QtGui.QPen(Qt.red, 2, Qt.DotLine)) p.drawRect(x, y, w, h) p.end() if self.roi_callback is not None: self.roi_callback(self) def resizeEvent(self, event): if not self._resize_timer.isActive(): self.resize_begin.emit() self._resize_timer.start() super(MplCanvas, self).resizeEvent(event) def draw(self, *args, **kwargs): self._draw_count += 1 return super(MplCanvas, self).draw(*args, **kwargs)
def initUI(self): #Parent Frame self.parent.title("Test Stand Control Panel") self.style = Style() self.style.theme_use("default") self.pack(fill=BOTH, expand=1) # Frame 1 (top) frame1 = Frame(self) frame1.pack(fill=X, expand=1) #Start motor button startButton = Button(frame1, text="Start Motor", command=self.startMotor) startButton.pack(side=LEFT, padx=5, pady=5) #Throttle slider lbl1 = Label(frame1, text="Throttle (0-100):", width=14) lbl1.pack(side=LEFT, padx=5, pady=5) self.scale = Scale(frame1, from_=0, to=100, command=self.onScaleThrottle) self.scale.pack(side=LEFT, padx=15) self.label = Label(frame1, text="throttle", textvariable=self.throttlevar, width=5) self.label.pack(side=LEFT) #Throttlesweep checkbutton self.autovar = BooleanVar() cb = Checkbutton(frame1, text="Throttle Sweep", variable=self.autovar, command=self.onClickAuto) cb.pack(side=LEFT, padx=15) #Com port selection field droplbl = Label(frame1, text="Serial Port:", width=10) droplbl.pack(side=LEFT, padx=5, pady=5) self.selected_s_Port = StringVar() self.s_Ports = [] self.drop = OptionMenu(frame1,self.selected_s_Port,"None",*self.s_Ports) self.drop.pack(side=LEFT, padx=5) #baudrate selection field (disabled) ## drop2lbl = Label(frame1, text="Baudrate:", width=9) ## drop2lbl.pack(side=LEFT, padx=5, pady=5) ## self.baudrate = StringVar() ## baudrates = [9600, 19200, 38400, 57600, 115200] ## drop2 = OptionMenu(frame1,self.baudrate,*baudrates) ## drop2.pack(side=LEFT, padx=5) #Start serial button comsButton = Button(frame1, text="Start Serial", command=self.startSerial) comsButton.pack(side=LEFT, padx=5, pady=5) #Stop serial button comsStopButton = Button(frame1, text="Stop Serial", command=self.stopSerial) comsStopButton.pack(side=LEFT, padx=5, pady=5) # Frame 2 (second line) frame2 = Frame(self) frame2.pack(fill=X, expand=1) #Amperage entry lbl2 = Label(frame2, text="Max Motor Current (A):", width=21) lbl2.pack(side=LEFT, padx=5, pady=5) self.MaxA_Entry = Entry(frame2) self.MaxA_Entry.pack(side="left", fill=X, padx=5, expand=False) self.MaxA_Entry.insert(0, 10) #Voltage entry lbl3 = Label(frame2, text="Max Motor Voltage (V):", width=20) lbl3.pack(side=LEFT, padx=5, pady=5) self.MaxV_Entry = Entry(frame2) self.MaxV_Entry.pack(side="left", fill=X, padx=5, expand=False) self.MaxV_Entry.insert(0, 14) #Update button updateButton = Button(frame2, text="Update Values", command=self.updateValues) updateButton.pack(side=LEFT, padx=5, pady=5) # Graph Frame framegraph = Frame(self) framegraph.pack(fill=X, expand=1) #Init figures f = Figure(figsize=(4,4), dpi=100) self.a = f.add_subplot(2, 2, 1) self.d = f.add_subplot(2, 2, 4) self.c = f.add_subplot(2, 2, 3) self.b = f.add_subplot(2, 2, 2) f.set_tight_layout(True) self.canvas = matplotlib.backends.backend_tkagg.FigureCanvasTkAgg(f, master=self) self.canvas.show() self.canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True) #Display Toolbar toolbar = NavigationToolbar2TkAgg(self.canvas, framegraph) toolbar.update() self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True) # Frame 0 (Bottom text) frame0 = Frame(self) frame0.pack(side="bottom", fill="x", expand=1) #Display text (allows to give user information) self.textboxvar = StringVar() self.info = Label(frame0, textvariable=self.textboxvar) self.info.pack(side=LEFT, padx=5, pady=5) # Button Frame (large buttons, near bottom) s = Style() #has its own style s.configure('My.TFrame',background='#f7edc3') #fancy colors framered = Frame(self, style='My.TFrame') framered.pack(side="bottom", fill="x", expand=1) #used the tk instead of ttk library for this, allows font and color mods #Save Button self.saveButton = tk.Button(framered, text="Save Data", bg='green', font=('Arial',20,'bold'), command=self.saveData) self.saveButton.pack(side="left", padx=5, pady=5) #Log button self.logButton = tk.Button(framered, text="Start Data Logging", bg="blue", font=('Arial',20,'bold'), command=self.logData) self.logButton.pack(side="left", padx=5, pady=5) #Stop button self.stopButton = tk.Button(framered, text="Stop Motor", bg='red', font=('Arial',20,'bold'), command=self.stopMotor) self.stopButton.pack(side="right", padx=5, pady=5)
class MRI_SE_Widget(MRI_SE_Widget_Base, MRI_SE_Widget_Form): def __init__(self): super(MRI_SE_Widget, self).__init__() self.setupUi(self) self.idle = True # state variable: True-stop, False-start self.seq_filename = 'sequence/basic/se_default.txt' self.startButton.clicked.connect(self.start) self.stopButton.clicked.connect(self.stop) # don't emit valueChanged signal while typing self.freqValue.setKeyboardTracking(False) self.atValue.setKeyboardTracking(False) self.freqValue.valueChanged.connect(self.set_freq) self.atValue.valueChanged.connect(self.set_at) self.zoomCheckBox = QCheckBox('Zoom') self.zoomLayout.addWidget(self.zoomCheckBox) self.peakWindowCheckBox = QCheckBox('Peak Window') self.peakWindowLayout.addWidget(self.peakWindowCheckBox) self.acquireButton.clicked.connect(self.acquire) # self.cycAcqBtn.clicked.connect(self.averaging) # Don't emit valueChanged signal while typing self.gradOffset_x.setKeyboardTracking(False) self.gradOffset_y.setKeyboardTracking(False) self.gradOffset_z.setKeyboardTracking(False) self.gradOffset_z2.setKeyboardTracking(False) self.gradOffset_x.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_x)) self.gradOffset_y.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_y)) self.gradOffset_z.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z)) self.gradOffset_z2.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z2)) self.saveShimButton.clicked.connect(self.save_shim) self.loadShimButton.clicked.connect(self.load_shim) self.zeroShimButton.clicked.connect(self.zero_shim) self.peak.setReadOnly(True) self.fwhm.setReadOnly(True) self.snr.setReadOnly(True) # Disable if not start yet self.startButton.setEnabled(True) self.stopButton.setEnabled(False) self.gradOffset_x.setEnabled(False) self.gradOffset_y.setEnabled(False) self.gradOffset_z.setEnabled(False) self.gradOffset_z2.setEnabled(False) self.acquireButton.setEnabled(False) self.saveShimButton.setEnabled(False) self.loadShimButton.setEnabled(False) self.zeroShimButton.setEnabled(False) self.cycAcqBtn.setEnabled(False) self.cyclesValue.setEnabled(False) self.zoomCheckBox.setEnabled(False) self.peakWindowCheckBox.setEnabled(False) # Setup buffer and offset for incoming data self.size = 50000 # total data received (defined by the server code) self.buffer = bytearray(8 * self.size) self.offset = 0 self.data = np.frombuffer(self.buffer, np.complex64) # Implementation of averaging flag self.averageFlag = False # self.averageData = 0 # self.averageData = self.data[0:2500] # self.averageMag = self.data[0:2500] # self.averageReal = self.data[0:2500] # self.averageImag = self.data[0:2500] # self.averageCycle = 0 # setup display self.figure = Figure() self.figure.set_facecolor('none') # top and bottom axes: 2 rows, 1 column self.axes_top = self.figure.add_subplot(2, 1, 1) self.axes_bottom = self.figure.add_subplot(2, 1, 2) self.axes_top.set_xlabel('frequency [Hz]') self.axes_top.set_ylabel('freq. domain') self.axes_bottom.set_xlabel('time [ms]') self.axes_bottom.set_ylabel('time domain') self.axes_top.grid() self.axes_bottom.grid() self.figure.set_tight_layout(True) self.canvas = FigureCanvas(self.figure) self.plotLayout.addWidget(self.canvas) # create navigation toolbar # self.toolbar = NavigationToolbar(self.canvas, self.plotWidget, False) # remove subplots action (might be useful in the future) # actions = self.toolbar.actions() # self.toolbar.removeAction(actions[7]) # self.plotLayout.addWidget(self.toolbar) def start(self): print("Starting MRI_SE_Widget") # send 2 as signal to start MRI_SE_Widget gsocket.write(struct.pack('<I', 2)) # enable/disable GUI elements self.startButton.setEnabled(False) self.stopButton.setEnabled(True) self.gradOffset_x.setEnabled(True) self.gradOffset_y.setEnabled(True) self.gradOffset_z.setEnabled(True) self.gradOffset_z2.setEnabled(True) self.acquireButton.setEnabled(True) self.saveShimButton.setEnabled(True) self.loadShimButton.setEnabled(True) self.zeroShimButton.setEnabled(True) self.zoomCheckBox.setEnabled(True) self.peakWindowCheckBox.setEnabled(True) self.cycAcqBtn.setEnabled(False) self.cyclesValue.setEnabled(False) # setup global socket for receive data gsocket.readyRead.connect(self.read_data) # send the sequence to the backend ass = Assembler() seq_byte_array = ass.assemble(self.seq_filename) print(len(seq_byte_array)) gsocket.write(struct.pack('<I', len(seq_byte_array))) gsocket.write(seq_byte_array) self.load_shim() self.idle = False def stop(self): self.idle = True gsocket.write(struct.pack('<I', 0)) self.startButton.setEnabled(True) self.stopButton.setEnabled(False) self.gradOffset_x.setEnabled(False) self.gradOffset_y.setEnabled(False) self.gradOffset_z.setEnabled(False) self.gradOffset_z2.setEnabled(False) self.acquireButton.setEnabled(False) self.saveShimButton.setEnabled(False) self.loadShimButton.setEnabled(False) self.zeroShimButton.setEnabled(False) self.zoomCheckBox.setEnabled(False) self.peakWindowCheckBox.setEnabled(False) self.cycAcqBtn.setEnabled(False) self.cyclesValue.setEnabled(False) # Disconnect global socket # if (gsocket.readyRead.isSignalConnected()): gsocket.readyRead.disconnect() def set_freq(self, freq): print("\tSetting frequency.") parameters.set_freq(freq) gsocket.write(struct.pack('<I', 1 << 28 | int(1.0e6 * freq))) # 2^28 = 268,435,456 for frequency setting if not self.idle: print("\tAcquiring data.") def acquire(self): gsocket.write(struct.pack('<I', 2 << 28 | 0 << 24)) print("Acquiring data") def set_at(self, at): print("\tSetting attenuation.") # at = round(at/0.25)*4 parameters.set_at(at) gsocket.write(struct.pack('<I', 3 << 28 | int(at / 0.25))) if not self.idle: print("\tAquiring data.") ''' def averaging(self): self.startButton.setEnabled(False) self.stopButton.setEnabled(False) self.gradOffset_x.setEnabled(False) self.gradOffset_y.setEnabled(False) self.gradOffset_z.setEnabled(False) self.gradOffset_z2.setEnabled(False) self.acquireButton.setEnabled(False) self.saveShimButton.setEnabled(False) self.loadShimButton.setEnabled(False) self.zeroShimButton.setEnabled(False) self.cycAcqBtn.setEnabled(False) self.cyclesValue.setEnabled(False) cycles = self.cyclesValue.value() # set averaging flag to high: # moving average is calculated during display data self.averageFlag = True for i in range(cycles): print("\tNew cycle.") self.averageCycle = i+1 gsocket.write(struct.pack('<I', 2 << 28 | 0 << 24)) print("\tAveraging Cyce: ", self.averageCycle) # Blocks until new data is available for reading, returns true if new data is available for reading # Arg "-1" function does not time out, pass msec as int otherwise gsocket.waitForReadyRead(100) print("\tWait for read finished.") self.startButton.setEnabled(True) self.stopButton.setEnabled(True) self.gradOffset_x.setEnabled(True) self.gradOffset_y.setEnabled(True) self.gradOffset_z.setEnabled(True) self.gradOffset_z2.setEnabled(True) self.acquireButton.setEnabled(True) self.saveShimButton.setEnabled(True) self.loadShimButton.setEnabled(True) self.zeroShimButton.setEnabled(True) self.cycAcqBtn.setEnabled(True) self.cyclesValue.setEnabled(True) # Reset averaging flag to false self.averageFlag = False ''' def set_grad_offset(self, spinBox): if spinBox.objectName() == 'gradOffset_x': print("\tSetting grad offset x.") offsetX = self.gradOffset_x.value() # self.horizontalSlider_x.setValue(offsetX) if offsetX > 0: gsocket.write(struct.pack('<I', 2 << 28 | 1 << 24 | offsetX)) else: gsocket.write( struct.pack('<I', 2 << 28 | 1 << 24 | 1 << 20 | -offsetX)) print("\tAcquiring data.") elif spinBox.objectName() == 'gradOffset_y': print("\tSetting grad offset y.") offsetY = self.gradOffset_y.value() # self.horizontalSlider_y.setValue(offsetY) if offsetY > 0: gsocket.write(struct.pack('<I', 2 << 28 | 2 << 24 | offsetY)) else: gsocket.write( struct.pack('<I', 2 << 28 | 2 << 24 | 1 << 20 | -offsetY)) print("\tAcquiring data.") elif spinBox.objectName() == 'gradOffset_z': print("\tSetting grad offset z.") offsetZ = self.gradOffset_z.value() if offsetZ > 0: gsocket.write(struct.pack('<I', 2 << 28 | 3 << 24 | offsetZ)) else: gsocket.write( struct.pack('<I', 2 << 28 | 3 << 24 | 1 << 20 | -offsetZ)) print("\tAcquiring data.") elif spinBox.objectName() == 'gradOffset_z2': print("\tSetting grad offset z2.") offsetZ2 = self.gradOffset_z2.value() if offsetZ2 > 0: gsocket.write(struct.pack('<I', 2 << 28 | 4 << 24 | offsetZ2)) else: gsocket.write( struct.pack('<I', 2 << 28 | 4 << 24 | 1 << 20 | -offsetZ2)) print("\tAcquiring data.") else: print('\tError: set_grad_offset.') return def save_shim(self): parameters.set_grad_offset_x(self.gradOffset_x.value()) parameters.set_grad_offset_y(self.gradOffset_y.value()) parameters.set_grad_offset_z(self.gradOffset_z.value()) parameters.set_grad_offset_z2(self.gradOffset_z2.value()) def load_shim(self): print("\tLoad grad offsets.") self.gradOffset_x.valueChanged.disconnect() self.gradOffset_y.valueChanged.disconnect() self.gradOffset_z.valueChanged.disconnect() self.gradOffset_z2.valueChanged.disconnect() self.gradOffset_x.setValue(parameters.get_grad_offset_x()) self.gradOffset_y.setValue(parameters.get_grad_offset_y()) self.gradOffset_z.setValue(parameters.get_grad_offset_z()) self.gradOffset_z2.setValue(parameters.get_grad_offset_z2()) self.gradOffset_x.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_x)) self.gradOffset_y.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_y)) self.gradOffset_z.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z)) self.gradOffset_z2.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z2)) offsetX = self.gradOffset_x.value() if offsetX > 0: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | offsetX)) else: gsocket.write( struct.pack('<I', 2 << 28 | 5 << 24 | 1 << 20 | -offsetX)) offsetY = self.gradOffset_y.value() if offsetY > 0: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | offsetY)) else: gsocket.write( struct.pack('<I', 2 << 28 | 5 << 24 | 1 << 20 | -offsetY)) offsetZ = self.gradOffset_z.value() if offsetZ > 0: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | offsetZ)) else: gsocket.write( struct.pack('<I', 2 << 28 | 5 << 24 | 1 << 20 | -offsetZ)) offsetZ2 = self.gradOffset_z2.value() if offsetZ2 > 0: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | offsetZ2)) else: gsocket.write( struct.pack('<I', 2 << 28 | 5 << 24 | 1 << 20 | -offsetZ2)) if self.idle: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | 0 << 20)) else: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | 1 << 20)) print("Acquiring data.") def zero_shim(self): print("\tZero grad offsets.") self.gradOffset_x.valueChanged.disconnect() self.gradOffset_y.valueChanged.disconnect() self.gradOffset_z.valueChanged.disconnect() self.gradOffset_z2.valueChanged.disconnect() self.gradOffset_x.setValue(0) self.gradOffset_y.setValue(0) self.gradOffset_z.setValue(0) self.gradOffset_z2.setValue(0) self.gradOffset_x.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_x)) self.gradOffset_y.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_y)) self.gradOffset_z.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z)) self.gradOffset_z2.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z2)) gsocket.write(struct.pack('<I', 2 << 28 | 6 << 24)) print("\tAcquiring data.") def read_data(self): # wait for enough data and read to self.buffer size = gsocket.bytesAvailable() if size <= 0: return elif self.offset + size < 8 * self.size: self.buffer[self.offset:self.offset + size] = gsocket.read(size) self.offset += size # if the buffer is not complete, return and wait for more return else: self.buffer[self.offset:8 * self.size] = gsocket.read(8 * self.size - self.offset) self.offset = 0 self.display_data() def display_data(self): # Clear the plots: bottom-time domain, top-frequency domain self.axes_bottom.clear() self.axes_top.clear() self.axes_top.set_xlabel('frequency [Hz]') self.axes_top.set_ylabel('freq. domain') self.axes_bottom.set_xlabel('time [ms]') self.axes_bottom.set_ylabel('time domain') self.axes_top.grid() self.axes_bottom.grid() self.figure.set_tight_layout(True) # Get magnitude, real and imaginary part of data data = self.data mag = np.abs(data) real = np.real(data) imag = np.imag(data) # Plot the bottom (time domain): display time signal from 0~21ms [0~5250] time = 10 data_idx = int(time * 250) mag_t = mag[0:data_idx] real_t = real[0:data_idx] imag_t = imag[0:data_idx] time_axis = np.linspace(0, time, data_idx) if self.averageFlag == True: # Update average values if flag was set self.averageMag = (np.array(self.averageMag) + np.array(mag_t)) / self.averageCycle self.averageMag = (np.array(self.averageReal) + np.array(real_t)) / self.averageCycle self.averageMag = (np.array(self.averageImag) + np.array(imag_t)) / self.averageCycle # Plot average values self.curve_bottom = self.axes_bottom.plot(time_axis, self.averageMag, linewidth=1) # blue self.curve_bottom = self.axes_bottom.plot(time_axis, self.averageReal, linewidth=1) # red self.curve_bottom = self.axes_bottom.plot(time_axis, self.averageImag, linewidth=1) # green else: # Plot real time signals, if averaging flag was not set self.curve_bottom = self.axes_bottom.plot(time_axis, mag_t, linewidth=1) # blue self.curve_bottom = self.axes_bottom.plot(time_axis, real_t, linewidth=1) # red self.curve_bottom = self.axes_bottom.plot(time_axis, imag_t, linewidth=1) # green # Plot the top (frequency domain): use signal from 0.5~20.5ms: first 0.5ms junk # update: the junk is already taken care of by the sequence timing dclip = data[0:data_idx] freqaxis = np.linspace(-125000, 125000, data_idx) # 2500 points ~ 20ms fft_mag = abs(np.fft.fftshift(np.fft.fft(np.fft.fftshift(dclip)))) if self.averageFlag == True: print("\tAveraging data.") self.averageData = (np.array(self.averageData) + np.array(fft_mag)) / self.averageCycle print(self.averageData) fft_mag = self.averageData # Data Analysis # Calculate and display properties of the frequency peak_value = round(np.max(fft_mag), 2) self.peak.setText(str(peak_value)) max_value = np.max(fft_mag[int(data_idx / 2 - data_idx / 10):int(data_idx / 2 + data_idx / 10)]) max_index = np.argmax(fft_mag) bound_high = max_index bound_low = max_index while 1: if fft_mag[bound_low] < 0.5 * max_value: break bound_low = bound_low - 1 while 1: if fft_mag[bound_high] < 0.5 * max_value: break bound_high = bound_high + 1 # Calculate and set FWHM fwhm_value = bound_high - bound_low self.fwhm.setText(str(fwhm_value)) # Plot frequency spectrum with and without zoom if not self.zoomCheckBox.isChecked(): # non zoomed self.curve_top = self.axes_top.plot( freqaxis[int(data_idx / 2 - data_idx / 10):int(data_idx / 2 + data_idx / 10)], fft_mag[int(data_idx / 2 - data_idx / 10):int(data_idx / 2 + data_idx / 10)], linewidth=1) if self.averageFlag == True: print("\tPlot average data.") self.curve_top = self.axes_top.plot( freqaxis[int(data_idx / 2 - data_idx / 10):int(data_idx / 2 + data_idx / 10)], self.averageData[int(data_idx / 2 - data_idx / 10):int(data_idx / 2 + data_idx / 10)], linewidth=1) else: # zoomed self.curve_top = self.axes_top.plot( freqaxis[int(data_idx / 2 - data_idx / 100):int(data_idx / 2 + data_idx / 100)], fft_mag[int(data_idx / 2 - data_idx / 100):int(data_idx / 2 + data_idx / 100)], linewidth=1) if self.averageFlag == True: self.curve_top = self.axes_top.plot( freqaxis[int(data_idx / 2 - data_idx / 100):int(data_idx / 2 + data_idx / 100)], self.averageData[int(data_idx / 2 - data_idx / 100):int(data_idx / 2 + data_idx / 100)], linewidth=1) # Calculate the SNR value inside a peak window peak_window = fwhm_value * 5 noise_bound_low = int(max_index - peak_window / 2) noise_bound_high = int(max_index + peak_window / 2) # Hightlight the peak window if self.peakWindowCheckBox.isChecked(): print("\tPeak window checked.") if int(noise_bound_low) >= int( data_idx / 2 - data_idx / 10) and int( noise_bound_high) <= int(data_idx / 2 + data_idx / 10): print("\tPeak inside the view.") self.curve_top = self.axes_top.plot( freqaxis[noise_bound_low:noise_bound_high], fft_mag[noise_bound_low:noise_bound_high], linewidth=1, linestyle="--") elif max_index < int(data_idx / 2 - data_idx / 10): print("\tPeak outside the view.") self.axes_top.text(freqaxis[int(data_idx / 2 - data_idx / 10)], 0.001, "<", fontsize=20) elif max_index > int(data_idx / 2 + data_idx / 10): print("\tPeak outside the view.") self.axes_top.text(freqaxis[int(data_idx / 2 + data_idx / 10)], 0.001, ">", fontsize=20) # Join noise outside peak window, calculate std. dev. and snr = peak/std.dev. noise = np.concatenate( (fft_mag[0:noise_bound_low], fft_mag[noise_bound_high:])) snr_value = round(peak_value / np.std(noise), 2) # print("snr_value: ", snr_value) self.snr.setText(str(snr_value)) # Update the figure self.canvas.draw()
class Chart: # I got the how to do this from the following website and I thank the author for the information. # http://blog.rcnelson.com/building-a-matplotlib-gui-with-qt-designer-part-1/ ''' Developer Note: Add generic container widget called mplwindow, drop any widget within mplwindow and layout vertical then delete the temporary widget. Set the vertical layout name to mplvl ''' def __init__(self, ui): ''' :param ui: The UI to which we need for displaying the chart. :param datatranslator: The data translator for access to the imported data. :param metadata: The metadata we found when doing a csv import :param instrument: The instrument configuration which includes the base metadata as set by the instrument xml :param config: The application config parser. :return: ''' ##initialise logger logger = logging.getLogger('graphics.Chart.init') self.ui = ui self.data_store = None self.data_source = None self.instrument = None self.metadata = None self.application_configuration = None self.run_once = False mpl.rcParams['font.monospace'] = 'Courier New' mpl.rcParams['savefig.bbox'] = 'tight' mpl.rcParams['axes.linewidth'] = 0.5 mpl.rcParams['axes.facecolor'] = "#FDFDF0" mpl.rcParams['figure.max_open_warning'] = 2 mpl.rcParams['figure.facecolor'] = '#FFFFE0' mpl.rcParams['legend.framealpha'] = 0.5 mpl.rcParams['legend.fancybox'] = True mpl.rcParams['lines.markersize'] = 5 mpl.rcParams['figure.autolayout'] = True mpl.rcParams['ytick.labelsize'] = 'small' mpl.rcParams['xtick.labelsize'] = 'small' self.fig = Figure() self.ax1f1 = self.fig.add_subplot(111) self.canvas = FigureCanvas(self.fig) if mpl.get_backend().lower() in ['agg', 'macosx']: self.fig.set_tight_layout(True) else: self.fig.tight_layout() logger.info('Initialised charting.') def chart_instrument_setup(self, datastore, instrument, metadata, application_configuration, datasource): self.data_store = datastore self.data_source = datasource self.instrument = instrument self.metadata = metadata self.application_configuration = application_configuration mpl.rcParams['savefig.directory'] = self.application_configuration.get('Application', 'instrument_data_path') def add_data(self, type): logger = logging.getLogger('graphics.Chart.add_data') # Clear the current chart if present. if self.ax1f1 is not None: self.ax1f1.clear() # print('Data source : %s' % str(self.data_store.DataSource)) # Set labels and ranges from metadata depending on data source. if self.data_store.DataSource == 'Controller': title = self.instrument.instrument_description channel_names = self.instrument.channel_names channel_colours = self.instrument.channel_colours channel_units = self.instrument.channel_units y_axis_label = self.instrument.YaxisLabel x_axis_label = self.instrument.XaxisLabel y_axis_range = self.instrument.YaxisRange elif self.data_store.DataSource == 'CSV': title = self.metadata.ObservationTitle channel_names = self.metadata.channel_names channel_colours = self.metadata.channel_colours channel_units = self.metadata.channel_units y_axis_label = self.metadata.YaxisLabel x_axis_label = self.metadata.XaxisLabel y_axis_range = None else: return 'PREMATURE_TERINATION', 'Unknown data source : %s' % str(self.data_source) # set labels and title. self.ax1f1.set_title(title) self.ax1f1.set_xlabel(x_axis_label) self.ax1f1.set_ylabel(y_axis_label) self.ax1f1.grid(True) # set y axis range if set. if y_axis_range is not None: axis_range = y_axis_range.split(',') yrange_start = int(axis_range[0]) yrange_stop = int(axis_range[1]) self.ax1f1.set_ylim(yrange_start, yrange_stop) try: number_of_channels = int(self.data_store.channel_count) logger.debug('add_data to chart channel count : ' + str(number_of_channels)) except AttributeError as msg: logger.critical('Channel count not found : %s' % str(msg)) return 'PREMATURE_TERMINATION', 'Channel count : %s' % str(msg) # Set what type the data is raw, rawcsv, processed. if type == 'raw': logger.debug('Length of data : %s' % str(len(self.data_store.RawData))) data = self.data_store.RawData elif type == 'rawCsv': logger.debug('Length of data : %s' % str(len(self.data_store.RawDataCsv))) data = self.data_store.RawDataCsv elif type == 'processed': logger.debug('Length of data : %s' % str(len(self.data_store.ProcessedData))) data = self.data_store.ProcessedData epoch = [n[0] for n in data] channels = [] try: for i in range(number_of_channels): channels.append([n[i + 1] for n in data]) for i in range(0, number_of_channels): self.ax1f1.plot(epoch, channels[i], channel_colours[i], label=channel_names[i]) except ValueError: return 'PREMATURE_TERMINATION', 'No data to display.' self.add_mpl() return 'SUCCESS', None def add_mpl(self): # Something to beware of I'm not sure what will happen if and Index X axis is used. hfmt = mpl.dates.DateFormatter('%H:%M:%S\n%Y-%m-%d') self.ax1f1.xaxis.set_major_formatter(hfmt) self.ax1f1.fmt_xdata = mpl.dates.DateFormatter('%Y-%m-%d %H:%M:%S') if self.run_once is False: # set the mplwindow widget background to a gray otherwise splash page disfigures the toolbar look. self.ui.mplwindow.setStyleSheet('QWidget{ background-color: #EDEDED; }') self.ui.mplvl.addWidget(self.canvas) toolbar = NavigationToolbar(self.canvas, self.ui.mplwindow, coordinates=True) self.ui.mplvl.addWidget(toolbar) self.run_once = True else: self.canvas.draw() def chart_legend(self, state): fontP = FontProperties() fontP.set_size(self.application_configuration.get('Legend', 'font')) if state is True: self.ax1f1.legend(prop=fontP, loc=self.application_configuration.get('Legend', 'location'), ncol=int(self.application_configuration.get('Legend', 'columns'))).set_visible(True) # set the line width of each legend object for legend_handle in self.ax1f1.legend(prop=fontP, loc=self.application_configuration.get('Legend', 'location'), ncol=int(self.application_configuration.get('Legend', 'columns')) ).legendHandles: legend_handle.set_linewidth(10.0) elif state is False: self.ax1f1.legend().set_visible(False) self.canvas.draw() def channel_control(self, channel, state): if state is False: self.ax1f1.lines[channel].set_visible(False) else: self.ax1f1.lines[channel].set_visible(True) self.canvas.draw()
class MapCanvas(FigureCanvasQTAgg): """Plotting maps.""" def __init__(self, parent=None, width=5, height=4, dpi=100): logging.info("Initialize MapCanvas") self.trajectories = defaultdict(list) self.fig = Figure(figsize=(width, height), dpi=dpi) self.main = parent self.lock = Lock() FigureCanvasQTAgg.__init__(self, self.fig) self.setParent(parent) FigureCanvasQTAgg.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding) FigureCanvasQTAgg.updateGeometry(self) self.create_map() def wheelEvent(self, event): if sys.platform == "darwin": # rather use pinch return self.zoom(event.angleDelta().y() > 0, 0.8) def zoom(self, zoom_in, factor): min_x, max_x, min_y, max_y = self.ax.axis() if not zoom_in: factor = 1.0 / factor center_x = 0.5 * (max_x + min_x) delta_x = 0.5 * (max_x - min_x) center_y = 0.5 * (max_y + min_y) delta_y = 0.5 * (max_y - min_y) self.ax.axis(( center_x - factor * delta_x, center_x + factor * delta_x, center_y - factor * delta_y, center_y + factor * delta_y, )) self.fig.tight_layout() self.lims = self.ax.axis() fmt = ", ".join("{:.5e}".format(t) for t in self.lims) logging.info("Zooming to {}".format(fmt)) self.draw() def create_map(self, projection: Union[str, Projection] = "EuroPP()") -> None: if isinstance(projection, str): if not projection.endswith(")"): projection = projection + "()" projection = eval(projection) self.projection = projection self.trajectories.clear() with plt.style.context("traffic"): self.fig.clear() self.ax = self.fig.add_subplot(111, projection=self.projection) projection_name = projection.__class__.__name__.split(".")[-1] self.ax.add_feature( countries(scale="10m" if projection_name not in ["Mercator", "Orthographic"] else "110m")) if projection_name in ["Lambert93", "GaussKruger", "Amersfoort"]: # Hardcoded projection list?! :o/ self.ax.add_feature(rivers()) self.fig.set_tight_layout(True) self.ax.background_patch.set_visible(False) self.ax.outline_patch.set_visible(False) self.ax.format_coord = lambda x, y: "" self.ax.set_global() self.draw() def plot_callsigns(self, traffic: Traffic, callsigns: List[str]) -> None: if traffic is None: return for key, value in self.trajectories.items(): for elt in value: elt.remove() self.trajectories.clear() self.ax.set_prop_cycle(None) for c in callsigns: f = traffic[c] if f is not None: try: self.trajectories[c] += f.plot(self.ax) f_at = f.at() if (f_at is not None and hasattr(f_at, "latitude") and f_at.latitude == f_at.latitude): self.trajectories[c] += f_at.plot(self.ax, s=8, text_kw=dict(s=c)) except AttributeError: # 'DataFrame' object has no attribute 'longitude' pass except TypeError: # NoneType object is not iterable pass if len(callsigns) == 0: self.default_plot(traffic) self.draw() def default_plot(self, traffic: Traffic) -> None: if traffic is None: return # clear all trajectory pieces for key, value in self.trajectories.items(): for elt in value: elt.remove() self.trajectories.clear() lon_min, lon_max, lat_min, lat_max = self.ax.get_extent(PlateCarree()) cur_ats = list(f.at() for f in traffic) cur_flights = list( at for at in cur_ats if at is not None if hasattr(at, "latitude") and at.latitude is not None and lat_min <= at.latitude <= lat_max and lon_min <= at.longitude <= lon_max) def params(at): if len(cur_flights) < 10: return dict(s=8, text_kw=dict(s=at.callsign)) else: return dict(s=8, text_kw=dict(s="")) for at in cur_flights: if at is not None: self.trajectories[at.callsign] += at.plot( self.ax, **params(at)) self.draw() def draw(self): with self.lock: if self.fig is None: return super().draw()
class PlotWindow(tk.Toplevel): def __init__(self, plot_type='', master=None, parameters=dict(), data=list(), figure_number=None, application=None, *args, **kwargs): # Class parameters self.master = master self.parameters = parameters self.application = application self.data = data self.pick_data_points_flag = False self.normalization_flag = False self.normalization_single_flag = False self.normalization_value = 1 self.new_normalization_value = 1 self.base_value = 0 self.new_base_value = 0 self.right_profile = None self.bottom_profile = None self.plot_type = plot_type self.widgets = dict() # To be used by class descendants self.picker_tolerance = 5.0 self.selected_artist = None self.profiler = Profiler() self.figure_number = figure_number # Init window tk.Toplevel.__init__(self, master=self.master) # Set window title if plot_type == 'Clipboard': self.title('Clipboard') elif plot_type == 'MogonioCalibration': self.title('Mogonio Calibration Wizard') elif plot_type == 'Calibration': self.title('Calibration Fit Curve') else: if figure_number is not None: self.title('Figure %.0f - %s - %s' % (figure_number, plot_type, (self.application.filename))) self.protocol("WM_DELETE_WINDOW", self.action_close) self.fig = Figure() self.main_axes = self.fig.add_subplot(111) self.config_plot() self.add_default_widgets() # This functions loops every 10 seconds self.after(0, self.timer()) def artist_label_prefix(self): return 'Fig. ' + str(self.figure_number) def find_closest_in_array(self, X, x0): # Find x position in array X x_pos = 0 x_index = -1 last_diff = np.abs(np.amax(X)-np.amin(X)) for x in X: if abs(x-x0) < last_diff: x_index = x_pos last_diff = abs(x-x0) x_pos += 1 return x_index def onpick(self, event): xy = [ event.mouseevent.x, event.mouseevent.y ] xydata = [event.mouseevent.xdata, event.mouseevent.ydata] # Reset previous selected artist if self.selected_artist is not None: if xy == self.selected_artist['xy'] and xydata == self.selected_artist['xydata']: return # Make sure that only one artist is selected by a single mouseclick self.selected_artist['artist'].set_color(self.selected_artist['color']) self.selected_artist['artist'].set_linewidth(self.selected_artist['width']) if self.selected_artist is not None and self.selected_artist['artist'] is event.artist: self.selected_artist = None # Clear selection if clicking twice on the same object else: self.select_artist(event.artist, xy, xydata) self.log('* Selected plot object: ' + event.artist.get_label()) # Redraw changes self.fig.canvas.draw() def select_artist(self, artist, xy, xydata): # Store current selected artist self.selected_artist = { 'artist': artist, 'color': artist.get_color(), 'width': artist.get_linewidth(), 'xy': xy, 'xydata': xydata } # Change some attributes to show the user that the artist is currently selected artist.set_color('purple') artist.set_linewidth(3) def add_profiles_and_colorbar(self): # Right axes sharing Y axis divider = make_axes_locatable(self.main_axes) self.right_axes = divider.append_axes("right", size=1, pad=0.2, sharey=self.main_axes) # Colorbar axes self.colorbar_axes = divider.append_axes("right", size="5%", pad=0.2) # Bottom axes sharing X axis self.bottom_axes = divider.append_axes("bottom", size=1, pad=0.2, sharex=self.main_axes) def add_default_widgets(self): ########################## # Top frame ########################## # New container for buttons self.widgets['frame_widgets'] = ttk.Frame(self) # Normalization button self.widgets['btn_normalization'] = ttk.Button(self.widgets['frame_widgets'], text='Normalize y-axis') self.widgets['btn_normalization']["command"] = self.action_btn_normalization # Zoom all button self.widgets['btn_zoomall'] = ttk.Button(self.widgets['frame_widgets'], text='Zoom all') self.widgets['btn_zoomall']["command"] = self.action_btn_zoomall self.widgets['btn_zoomall'].pack(side=tk.LEFT, padx=10, pady=5) # Export button self.widgets['btn_export'] = ttk.Button(self.widgets['frame_widgets'], text='Export data') self.widgets['btn_export']["command"] = self.action_btn_export self.widgets['btn_export'].pack(side=tk.LEFT, padx=10, pady=5) # Quick normalization button self.widgets['btn_quick_normalization'] = ttk.Button(self.widgets['frame_widgets'], text='Auto normalization') self.widgets['btn_quick_normalization']["command"] = self.action_btn_quick_normalization self.widgets['btn_quick_normalization'].pack(side=tk.LEFT, padx=10, pady=5) # Sum plots checkbox #self.widgets['cb_auto_refresh'] = Checkbox(self.widgets['frame_widgets'], text='Auto refresh') #self.widgets['cb_auto_refresh'].pack(side=tk.LEFT, padx=10, pady=10) # Pack buttons frame self.widgets['frame_widgets'].grid(row=0, column=0) ########################## # Selection artist widgets ########################## # New container for buttons self.widgets['frame_artist_widgets'] = ttk.Frame(self) # Pack buttons frame self.widgets['frame_artist_widgets'].grid(row=1, column=0) # Copy to desktop plot self.widgets['btn_copy'] = ttk.Button(self.widgets['frame_artist_widgets'], text='Copy to clipboard') self.widgets['btn_copy']["command"] = self.action_btn_copy self.widgets['btn_copy'].pack(side=tk.LEFT, padx=10, pady=5) # Zoom line button self.widgets['btn_zoomsingle'] = ttk.Button(self.widgets['frame_artist_widgets'], text='Zoom curve') self.widgets['btn_zoomsingle']["command"] = self.action_btn_zoomsingle self.widgets['btn_zoomsingle'].pack(side=tk.LEFT, padx=10, pady=5) ########################## # Bottom frame ########################## self.widgets['bottom_frame'] = ttk.Frame(self) #self.widgets['btn_test'] = ttk.Button(self.widgets['bottom_frame'], text='Teste') #self.widgets['btn_test'].grid(row=0, column=0, sticky='nsew') self.widgets['log_listbox'] = ScrollableListbox(self.widgets['bottom_frame'], height=4) self.widgets['log_listbox'].grid(row=0, column=0, sticky='nsew') self.widgets['bottom_frame'].grid(row=3, column=0, sticky="nsew", pady=10, padx=10) # Elastic columns tk.Grid.columnconfigure(self.widgets['bottom_frame'], 0, weight=1) tk.Grid.columnconfigure(self, 0, weight=1) tk.Grid.rowconfigure(self, 2, weight=1) tk.Grid.rowconfigure(self, 0, weight=0) def log(self, text): self.widgets['log_listbox'].append(text) self.widgets['log_listbox'].see(tk.END) def default_config(self): self.main_axes.get_xaxis().get_major_formatter().set_useOffset(False) self.main_axes.get_yaxis().get_major_formatter().set_useOffset(False) self.fig.set_tight_layout(True) def action_btn_zoomsingle(self, *args, **kwargs): if self.selected_artist is not None: line = self.selected_artist['artist'] min_x = min(line.get_xdata()) max_x = max(line.get_xdata()) min_y = min(line.get_ydata()) max_y = max(line.get_ydata()) self.main_axes.set_xlim([min_x, max_x]) self.main_axes.set_ylim([min_y, max_y]) # Redraw changes self.fig.canvas.draw() def action_btn_export(self, *args, **kwargs): file_path = fd.asksaveasfilename() if file_path: line_num = 0 for line in self.main_axes.get_lines(): line_num += 1 path = file_path + '_' + self.plot_type + '_' + str(line_num) + '.txt' np.savetxt(path, np.column_stack([line.get_xdata(), line.get_ydata()])) def action_btn_quick_normalization(self, *args, **kwargs): for line in self.main_axes.get_lines(): y = line.get_ydata() y = y-np.amin(y) y = np.divide(y, np.amax(y)) line.set_ydata(y) self.action_btn_zoomall() def disable_picker(self, *args, **kwargs): for line in self.main_axes.get_lines(): line.set_picker(None) def action_btn_copy(self, *args, **kwargs): if self.application is not None and self.selected_artist is not None: if self.application.clipboard_plot is None: self.application.clipboard_plot = ClipboardPlot(master = self.application.master, application = self.application) self.application.clipboard_plot.main_axes.plot(self.selected_artist['artist'].get_xdata(), self.selected_artist['artist'].get_ydata(), picker=self.picker_tolerance, label=self.selected_artist['artist'].get_label()) self.application.clipboard_plot.fig.canvas.draw() def action_cb_transferred_click(self, *args, **kwargs): self.refresh_plot() # This functions loops every 10 seconds def timer(self): #auto_refresh = self.widgets['cb_auto_refresh'].var.get() auto_refresh = False if auto_refresh: self.application.update_current_selected_data() self.data = self.application.current_selected_data self.parameters = self.application.current_parameters self.refresh_plot() self.after(10000, self.timer) def show(self): # Show plot self.widgets['canvas_frame'] = ttk.Frame(self) self.canvas = FigureCanvasTkAgg(self.fig, master=self.widgets['canvas_frame']) self.canvas.show() self.canvas.get_tk_widget().pack() self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.widgets['canvas_frame']) self.toolbar.update() self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True) self.canvas.mpl_connect('pick_event', self.onpick) self.widgets['canvas_frame'].grid(row=2, column=0, sticky="nsew", pady=10, padx=10) def config_plot(self): self.default_config() # Try to execute custom config from children, if exists try: self.config_plot_custom() except AttributeError: # If method does not exist pass def plot_redraw(self): self.config_plot() self.fig.canvas.draw() def columns_names_parse_as_int(self, columns_names): return_list = list() for name in columns_names: return_list.append(int(re.findall('([0-9]+)', str(name))[0])) return return_list def columns_names_parse_as_float(self, columns_names): return_list = list() for name in columns_names: return_list.append(float(re.findall('([0-9]+)', str(name))[0])) return np.array(return_list) def action_close(self): # Custom code when closing plot window try: self.action_close_custom() except AttributeError: # If method does not exist pass plt.close(self.fig) self.destroy() def roi_axis(self): ' Used on plots which have ROI as x axis ' p = self.parameters rois_numbers = self.columns_names_parse_as_float(p['intensity_names']) if p['use_calibration'] and p['calibration_data']: try: self.bottom_axes.set_xlabel('Energy (keV)') self.main_axes.set_xlabel('') except: self.main_axes.set_xlabel('Energy (keV)') return self.rois_to_energies() else: try: self.bottom_axes.set_xlabel('ROI') self.main_axes.set_xlabel('') except: self.main_axes.set_xlabel('ROI') return rois_numbers def mogonio_to_energy(self): p = self.parameters energies_values = np.array(self.data[:, p['energy_column']], dtype=float) if p['use_mogonio_calibration']: A = p['mogonio_calibration_a'] B = p['mogonio_calibration_b'] hc = 1239.8 # nm.eV a = 0.543102 # lattice parameter for Si, in nm miller_indices = [1, 1, 1] m = miller_indices return (hc * np.sqrt(m[0]**2 + m[1]**2 + m[2]**2)/(2*a*np.sin(np.radians(A + B*energies_values))))/1000.0 else: return energies_values def rois_to_energies(self, fresh=False): ' Used on plots which have ROI as x axis ' p = self.parameters rois_numbers = self.columns_names_parse_as_int(p['intensity_names']) # Fitting if not fresh: calib = Tools.list_to_numpy(p['calibration_data']) else: calib = Tools.list_to_numpy(self.application.widgets['calib_tree'].get_data()) equation_parameters = np.polyfit(calib[:, 1], calib[:, 0], min(2, calib.shape[0]-1)) self.log('* Energy calibration coefficients: ' + str(equation_parameters)) # Apply fitting equation to ROIs numbers energies = np.polyval(equation_parameters, rois_numbers) return energies def action_btn_zoomall(self, *args, **kwargs): first = True for line in self.main_axes.lines: if first: min_x = min(line.get_xdata()) max_x = max(line.get_xdata()) min_y = min(line.get_ydata()) max_y = max(line.get_ydata()) first = False else: min_x = min(min_x, min(line.get_xdata())) max_x = max(max_x, max(line.get_xdata())) min_y = min(min_y, min(line.get_ydata())) max_y = max(max_y, max(line.get_ydata())) if not first: # If there is at least 1 line self.main_axes.set_xlim([min_x, max_x]) self.main_axes.set_ylim([min_y, max_y]) # Redraw changes self.fig.canvas.draw() def action_btn_normalization(self, *args, **kwargs): if not self.normalization_flag: self.normalization_flag = True self.widgets['btn_normalization']['text'] = 'Please double-click on y=0...' self.normalization_connection = self.canvas.mpl_connect('button_press_event', self.action_normalization_firstclick) else: self.widgets['btn_normalization']['text'] = 'Normalize y-axis' self.normalization_flag = False self.canvas.mpl_disconnect(self.normalization_connection) def action_normalization_firstclick(self, event, *args, **kwargs): if event.dblclick and event.inaxes == self.main_axes: y = event.ydata self.new_base_value = self.base_value + y * self.normalization_value self.canvas.mpl_disconnect(self.normalization_connection) self.normalization_connection = self.canvas.mpl_connect('button_press_event', self.action_normalization_secondclick) self.widgets['btn_normalization']['text'] = 'Please double-click on y=1...' def action_normalization_secondclick(self, event, *args, **kwargs): if event.dblclick and event.inaxes == self.main_axes: y = event.ydata self.normalization_value = (self.normalization_value * y + self.base_value - self.new_base_value) self.base_value = self.new_base_value self.action_btn_normalization() self.refresh_plot() def action_btn_derivative(self, *args, **kwargs): if self.selected_artist is not None: y = self.selected_artist['artist'].get_ydata() x = self.selected_artist['artist'].get_xdata() # Border effects may exist derivative_data = np.convolve(np.array([-1, 0, 1]), y, mode='same') delta_data = np.convolve(np.array([-1, 0, 1]), x, mode='same') derivative_data = np.divide(derivative_data, delta_data) self.main_axes.plot(x, derivative_data, picker=self.picker_tolerance, label='<Derivative of ' + self.selected_artist['artist'].get_label() + '>') self.fig.canvas.draw()
class PlotterXZ(object): '''Superclass for plotting. The workflow is like: plotter = PlotterXZ(**kwargs) plotter.add_data(loaded_data_1) # load by other means plotter.add_data(loaded_data_2) # parsing more data ... plotter.add_data(loaded_data_n) # parsing more data plotter.save_plot() # create the figure, plot, and save Inspect `../analyze_cm.py` for more details on use. ''' # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Static methods # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # list of options to be evaluated # using classmethod since class static variables are not inherited @classmethod def _get_opt_bool(cls): # these keywords may also be used for float or string # setting a kw to true here should then use the defaults opts = dict() opts['curve'] = 'Show curve plot' opts['scatter'] = 'Show scatter plot' opts['lowess'] = 'Show lowess plot' opts['interpolate'] = 'Show interpolation' opts['lowess_zx'] = 'Show lowess for x(z)' opts['interpolate_zx'] = 'Show interpolation for x(z)' opts['errorbar'] = 'Show errorbars' opts['minmaxbar'] = 'Show min-max bars' opts['minmaxfill'] = 'Show fill between min-max' opts['colors'] = 'Cycle colors (default)' opts['markers'] = 'Cycle markers' opts['logx'] = 'Show x-axis logarithmic' opts['logz'] = 'Show z-axis logarithmic' opts['title'] = 'Show default title' opts['xlabel'] = 'Show x-axis label' opts['zlabel'] = 'Show z-axis label' opts['figure'] = 'Save the figure' opts['movie'] = 'Create a movie' opts['frames'] = 'Save each frame of the movie' opts['tight'] = 'Use tight layout' opts['sparsify'] = 'Reduce the size of scatter plots' opts['save_data'] = 'Save plotted data as json-file' opts['legend'] = 'Show legend (default).' return opts @classmethod def _get_opt_float(cls): opts = dict() opts['xscale'] = 'Set scale of x-axis' opts['zscale'] = 'Set scale of z-axis' opts['zmin'] = 'Set z-axis minimum' opts['zmax'] = 'Set z-axis maximum' opts['xmin'] = 'Set x-axis minimum' opts['xmax'] = 'Set x-axis maximum' opts['lowess'] = 'Set lowess fraction' opts['fps'] = 'Set Frame Per Second (for movie)' opts['tscale'] = 'Set time scale (move time / simulation time)' opts['diffx'] = 'Set offset in x for each data series' opts['diffz'] = 'Set offset in z for each data series' opts['sparsify'] = 'Set number of bins in sparsify.' # todo: consider implementing: # opts['errorbar'] = 'Set errorbar deviations' # opts['minmaxbar'] = 'Set minmaxbar fraction' # opts['minmaxfill'] = 'Set minmaxfill alpha' return opts @classmethod def _get_opt_string(cls): opts = dict() opts['fpath'] = 'Set output file path' opts['fpath_head'] = 'Set output file head' opts['fpath_ext'] = 'Set output file extension' opts['folder'] = 'Set output file folder' opts['title'] = 'Set plot title' opts['legend'] = 'Legend `legend=v_rp_gs_unsorted_loc=best`' opts['annotate'] = 'Annotate plot, `annotate=x_y_text`.' opts['xlabel'] = 'Set x-axis label' opts['zlabel'] = 'Set z-axis label' opts['tlabel'] = 'Set time label' opts['fx'] = 'Add a line, fx=a_b_c --> a+bx+cx^2' return opts @classmethod def opt2kwargs(cls, options=None): return tools.opt2kwargs( options=options, bools=cls._get_opt_bool(), floats=cls._get_opt_float(), strings=cls._get_opt_string(), ) @classmethod def opt2help(cls): return tools.opt2help( bools=cls._get_opt_bool(), floats=cls._get_opt_float(), strings=cls._get_opt_string(), ) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Initialize # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def __init__(self, # control output fpath=None, fpath_head=None, fpath_ext=None, folder=None, # control axis title=None, legend=True, xlabel='X', xscale=1, xmin=None, xmax=None, zlabel='Z', zscale=1, zmin=None, zmax=None, diffx=0, diffz=0, logx=False, logz=False, # plot type curve=False, scatter=False, lowess=False, interpolate=False, lowess_zx=False, interpolate_zx=False, errorbar=False, minmaxbar=False, minmaxfill=False, # control movie movie=False, frames=False, fps=5, tscale=1e6, tlabel='\u00B5s', # control plot figure=True, save_data=False, colors=True, markers=False, tight=False, sparsify=None, # other fx=None, annotate=False, ): logger.debug('Initiating ' + str(self.__class__.__name__)) # get input / set defaults / sub-class to override if needed self.title = title self.fpath_head = fpath_head self.fpath_ext = fpath_ext or mpl.rcParams['savefig.format'] self.folder = folder self.fpath = fpath # if None: set by add_data self.xlabel = xlabel self.zlabel = zlabel self.xscale = xscale self.zscale = zscale self.figure = figure self.save_data = save_data self.logx_on = bool(logx) self.logz_on = bool(logz) # parse legend commands self.legend_on = True self.legend_sorted = True self.legend_loc = 'best' # location self.legend_fmt = None # format of legend values self.legend_lkw = [] # label keywords (from sim parameters) if type(legend) is str: legend = legend.split('_') for key in legend: if key.startswith('loc='): self.legend_loc = key[len('loc='):] elif key.startswith('fmt='): self.legend_fmt = key[len('fmt='):] elif key == 'on': self.legend_on = True elif key == 'off': self.legend_on = False elif key == 'sorted': self.legend_sorted = True elif key == 'unsorted': self.legend_sorted = False else: self.legend_lkw.append(key) if self.legend_lkw == []: self.legend_lkw = ['name'] # use filename as default # parse annotate commands # assume `annotate=x_y_text_format` self.annotate_on = False # self.annotate_format = 'some default' # todo: implement this if type(annotate) is str: annotate = annotate.split('_') if len(annotate) < 3: msg = 'Give at least three arguments to `annotate`. Skipping.' logger.info(msg) else: self.annotate_on = True self.annotate_x = float(annotate[0]) self.annotate_y = float(annotate[1]) self.annotate_text = str(annotate[2]) if len(annotate) > 3: self.annotate_format = str(annotate[3]) # define what to plot self.curve_on = curve self.scatter_on = scatter self.lowess_on = lowess self.lowess_frac = 0.2 if type(lowess) is not float else lowess self.interpolate_on = interpolate self.errorbar_on = errorbar self.minmaxbar_on = minmaxbar self.minmaxfill_on = minmaxfill self.sparsify_on = bool(sparsify) if (sparsify is None or sparsify is True): self.sparsify_no = 100 else: self.sparsify_no = int(sparsify) self.lowess_zx_on = lowess_zx self.lowess_zx_frac = 0.2 if type(lowess_zx) is not float else lowess_zx self.interpolate_zx_on = interpolate_zx # define how to add a line # note, the line need to be added after other data to get limits self.fx = None if type(fx) is str: # fy(x) = sum([ai * x**i for i, ai in enumerate(self.fx)]) self.fx = [float(ai) for ai in fx.split('_')] # plot limits, set by initiation # leave as 'none' to use plotted data instead self.zmin = zmin self.zmax = zmax self.xmin = xmin self.xmax = xmax # plot limits, set by plotted data # idea: why not just get these later instead? self._zmin = +1e10 self._zmax = -1e10 self._xmin = +1e10 self._xmax = -1e10 # possibly add offset to plots self.originx = 0 self.originz = 0 self.diffx = diffx self.diffz = diffz # plot setup # note: set label explicitly to None for all other than 'desc' # or explicitly supress label when plotting self.desc = {'lw': 2, 'alpha': 1} self.curve_desc = {'ls': '-', 'marker': None, 'label': None} self.lowess_desc = {'ls': '--', 'marker': None, 'label': None} self.interp_desc = {'ls': '--', 'marker': None, 'label': None} self.scatter_desc = {'s': 5, 'marker': '.', 'label': None} self.xz_ratio = False # set ratio only for equal plots self.symx = False # symmetric xlim self.symz = False # symmetric zlim self.colors = colors self.markers = markers # create figure self.fig = None self._fig_ghost = None # for ghost axes self.axes = None # the axes to be used self._axes_ghost = None # for ghost axes self.tight = bool(tight) # plot data # append one list for each plot # idea: make a plot dict for all these instead (to_plot_data?) self.pd_x = [] # x-data self.pd_z = [] # z-data self.pd_t = [] # time-data self.pd_l = [] # label self.pd_f = [] # filename self.pd_m = [] # mask (used to mask plotted data) self.pd_d = [] # dict, plot description # plotted data # add one dict for each plotted data self.plotted_data = [] # for movie creation self.fps = fps self.tscale = tscale self.tlabel = tlabel self.frames = frames self.movie = movie def reset_mc_cc(self): ''' Reset the marker and color cycle iterators. ''' self.mc = cerman_rc_params.marker_cycler()() # invoke the iterator self.cc = cerman_rc_params.color_cycler()() # self.desc should have a color, even if `colors=off` self.desc.update(color=next(self.cc)['color']) self.cc = cerman_rc_params.color_cycler()() def _clear_fig(self): '''Delete any axis and clear figure. ''' if self.axes is not None: for ax in self.axes: self.fig.delaxes(ax) if self._axes_ghost is not None: for ax in self._axes_ghost: self._fig_ghost.delaxes(ax) if self.fig is not None: self.fig.clf() if self._fig_ghost is not None: self._fig_ghost.clf() def _set_fig(self): '''Create figure and add axes.''' self.reset_mc_cc() # reset colors self._clear_fig() # clear any present figure # set-up dummy figure (for unused axes) # note: plotters with more axes set _fig_ghost and _axes_ghost here self.fig = Figure() # create new figure gs = GridSpec(1, 1, figure=self.fig) self.ax_xz = self.fig.add_subplot(gs[0]) # add axis self.axes = [self.ax_xz] # store axis list # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Manage data # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def add_data(self, data): ''' Parse data, append data to plot. Set fpath, llabel, and update origin, as needed. Parameters ---------- data : dict loaded simulation data ''' self.set_fpath(fpath=self.fpath, data=data) self.set_llabel(data) # get data x, z, t = self.get_xzt(data) x = x * self.xscale z = z * self.zscale t = t if len(t) == 0 else t * self.tscale x, z = self.update_origin(x, z) # append data - to be plotted in the end self.pd_x.append(x) self.pd_z.append(z) self.pd_t.append(t) self.pd_l.append(self.desc['label']) self.pd_d.append(self.desc.copy()) self.pd_f.append(data['header']['sim_input']['name']) msg = 'Added {} data points for: {}' logger.debug(msg.format(x.size, self.desc['label'])) def set_fpath(self, fpath=None, data=None): '''Set fpath. Use fpath or get from data, literally.''' if fpath is not None: self.fpath = fpath elif data is not None: head = self.fpath_head self.fpath = tools.get_fpath( data, head=head, folder=self.folder, ext=self.fpath_ext) logger.log(5, f'Set fpath: {self.fpath}') def set_llabel(self, data): '''Use simulation input to set legend label. ''' label = '' lkw = self.legend_lkw.copy() # copy so name may be removed if 'name' in lkw: # get filename label += ', ' + data['header']['sim_input']['name'] lkw.remove('name') if 'sname' in lkw: # get filename label += ', ' + data['header']['save_spec_to_dict']['key'] lkw.remove('sname') if lkw: # get value strings label += ', ' + tools.get_key_data( data, keys=lkw, fmt=self.legend_fmt) # set label, remove first comma label = label[2:] logger.log(5, 'Setting label: ' + label) self.desc['label'] = label return label def get_xzt(self, data): '''Get plot data. ''' # this method need to be defined in sub-class raise NotImplementedError('Plotter need to be sub-classed') def update_origin(self, x, z): ''' Offset plot by changing origin. ''' # allow plotting of nothing, then do nothing # to ensure correct legend sequence if len(x) != 0: # update positions x = x + self.originx z = z + self.originz # update origin after self.originx = self.originx + self.diffx self.originz = self.originz + self.diffz return x, z # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Manage plotting methods (plot all data) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def plot_all_pd(self): ''' Plot all data in `self.pd_*` to the axis. Plot in given order, or sort by legend, as specified. Remove non-finite elements. Apply the mask in `pd_m` (used for e.g. plotting movie). Save the actual plotted data, for dumping later, if specified. ''' def _argsort(seq): # return indexes of the sorted sequence. sbv = sorted((v, i) for (i, v) in enumerate(seq)) return [i for (v, i) in sbv] # define methods for sorting # idea: add other ways to sort # idea: allow user to chose sorting ksa = list(range(len(self.pd_f))) # added ksf = _argsort(self.pd_f) # filename ksl = _argsort(self.pd_l) # legend # set sorting method (key-index'es) kis = ksf if self.legend_sorted: kis = ksl # plot in correct order for ki in kis: # key-index # use masked values, if specified if self.pd_m: mask = self.pd_m[ki] else: mask = np.isfinite(self.pd_x[ki]) # get the time-series if sum(len(t) for t in self.pd_t) == 0: t = [] elif len(self.pd_t[ki]) == 0: t = [] else: t = self.pd_t[ki][mask] # get correct data and plot x = self.pd_x[ki][mask] z = self.pd_z[ki][mask] d = self.pd_d[ki] self.desc.update(d) label = self.plot_xz(x, z) # self.append_plotted_data(x, z, t, label) # tod: add this as proper function # add fx if self.fx is not None: # just get extents instead? xmin, xmax, zmin, zmax = self.calc_limits() x = np.linspace(xmin, xmax, num=100) z = np.array([ sum([ai * xj**i for i, ai in enumerate(self.fx)]) for xj in x ]) self.curve(x, z) def append_plotted_data(self, x, y, t, label): ''' Append the plotted data to a dict as strings. ''' pd = OrderedDict() pd['title'] = str(self.title) # str to ensure copy pd['l_lab'] = str(label) pd['x_lab'] = str(self.xlabel) pd['y_lab'] = str(self.zlabel) pd['t_lab'] = str(self.tlabel) pd['x'] = str(np.array(x).tolist()) pd['y'] = str(np.array(y).tolist()) pd['t'] = str(np.array(t).tolist()) self.plotted_data.append(pd) def save_plotted_data(self): ''' Save plotted data to file. ''' fpath = str(Path(self.fpath).with_suffix('.json')) tools.json_dump(self.plotted_data, fpath) logger.info(f'Saved {fpath}') def plot_xz(self, x, z): ''' Actually plot data to the axis, return label. Update plot description. Plot legend. Invoke any/all plotting method(s) specified. ''' # todo: consider making (x, z) into kwargs for generality # update descriptions if self.colors: self.desc.update(color=next(self.cc)['color']) if self.markers: self.scatter_desc.update(marker=next(self.mc)['marker']) # plot the legend label desc = self.add_label_to_axes() label = desc['label'] # plot nothing if nothing is found # ensures correct legend sequence if len(x) == 0: logger.debug('Nothing to plot.') return label # use same description for all these if self.curve_on: self.curve(x, z) if self.scatter_on: self.scatter(x, z) if self.lowess_on: self.lowess(x, z) if self.interpolate_on: self.interpolate(x, z) if self.errorbar_on: self.errorbar(x, z) if self.minmaxbar_on: self.minmaxbar(x, z) if self.minmaxfill_on: self.minmaxfill(x, z) if self.lowess_zx_on: self.lowess_zx(x, z) if self.interpolate_zx_on: self.interpolate_zx(x, z) return label def update_min_max(self, x, z): '''Update plot limits. ''' # idea: check if this can just be stolen from matplotlib self._xmax = max(self._xmax, x.max()) self._zmax = max(self._zmax, z.max()) self._xmin = min(self._xmin, x.min()) self._zmin = min(self._zmin, z.min()) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Plot to given axis # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def add_label_to_axes(self, desc=None): ''' Add legend label to all axes. Control visibility elsewhere. ''' desc = desc or self.desc desc = dict(desc) # to copy desc.pop('edgecolor', None) # key only valid for scatter desc.pop('s', None) # key only valid for scatter if self.markers: desc['marker'] = self.scatter_desc['marker'] for ax in self.axes: ax.plot([], [], **desc) return desc def _ax_curve(self, ax, x, z, desc=None): '''Add curve plot. ''' logger.log(5, self._ax_curve.__doc__) desc = desc or dict(self.desc, **self.curve_desc) ax.plot(x, z, **desc) self.update_min_max(x, z) @staticmethod def _sparsify(x, z, n=100): ''' Grid data into a regular nxn grid. Return the average position in each group. ''' if len(x) < n**2: logger.debug('Small length, skipping sparsify') return x, z def _bin_idx(x): bl = (max(x) - min(x)) / n # bin length left = min(x) - bl # padding right = max(x) + bl xf = (x - left) / (right - left) return np.floor(xf * n) # index values xi = _bin_idx(x) zi = _bin_idx(z) # sort by xi then zi xzi = xi * n**2 + zi idx_as = np.argsort(xzi) xs = x[idx_as] zs = z[idx_as] # get grouping by looking at grouping in zi zg = zi[idx_as] zg = np.insert(zg, 0, 0) # predecessor of first is 0 zg = zg[0:-1] != zg[1:] # equal predecessor? zg = np.cumsum(zg) # give each group a number zg -= zg[0] # ensure start on 0 # calculate average in each group xo = np.array([np.average(xs[zg == i]) for i in range(zg[-1])]) zo = np.array([np.average(zs[zg == i]) for i in range(zg[-1])]) logger.debug(f'Sparsified from {len(x)} to {len(xo)}') return xo, zo def _ax_scatter(self, ax, x, z, desc=None): '''Add scatter plot. ''' logger.log(5, self._ax_scatter.__doc__) desc = desc or dict(self.desc, **self.scatter_desc) if self.sparsify_on: x, z = self._sparsify(x, z, n=self.sparsify_no) ax.scatter(x, z, **desc) self.update_min_max(x, z) def _ax_lowess(self, ax, x, z, desc=None, frac=None): '''Add lowess smoothing to plot. ''' logger.log(5, self._ax_lowess.__doc__) desc = desc or dict(self.desc, **self.lowess_desc) frac = frac or self.lowess_frac nnx = x[~np.isnan(x)] # non-nan x if np.unique(nnx).size < 10: logger.info('Skipping lowess, too few datapoints.') return if self.logz_on: z = np.log(z) # note: lowess can give a warning if there are too few neighbors # https://github.com/statsmodels/statsmodels/issues/2449 with warnings.catch_warnings(record=True) as w: # note, input to lowess is (val, point), so (z, x) is correct out = smoothers_lowess.lowess(z, x, frac=frac, return_sorted=True) if w: logger.info('Data point issues with lowess.') for wi in w: logger.log(5, wi) (x, z) = (out[:, 0], out[:, 1]) if self.logz_on: z = np.exp(z) ax.plot(x, z, **desc) self.update_min_max(x, z) def _ax_lowess_zx(self, ax, x, z, desc=None, frac=None): '''Add lowess smoothing to plot. Use x = x(z).''' logger.log(5, self._ax_lowess.__doc__) desc = desc or dict(self.desc, **self.lowess_desc) frac = frac or self.lowess_frac nnz = z[~np.isnan(z)] # non-nan z if np.unique(nnz).size < 10: logger.info('Skipping lowess, too few datapoints.') return if self.logx_on: x = np.log(x) # note: lowess can give a warning if there are too few neighbors # https://github.com/statsmodels/statsmodels/issues/2449 with warnings.catch_warnings(record=True) as w: # note, input to lowess is (val, point), so (x, z) is correct out = smoothers_lowess.lowess(x, z, frac=frac, return_sorted=True) if w: logger.info('Data point issues with lowess.') for wi in w: logger.log(5, wi) (z, x) = (out[:, 0], out[:, 1]) if self.logx_on: x = np.exp(x) ax.plot(x, z, **desc) self.update_min_max(x, z) @staticmethod def _calc_stat(x, z, sort=True): ''' Calculate statistics for each unique x-value. Parameters ---------- x : float x-values z : float z-values Returns ------- xu: float x-values, unique, sorted za: float z-values, average zs: float z-values, standard deviation zmin: float z-values, minimum zmax: float z-values, maximum ''' xu = np.unique(x) if sort: xu = np.array(sorted(xu)) za = np.array([np.average(z[x == xi]) for xi in xu]) zs = np.array([np.std(z[x == xi]) for xi in xu]) zmin = np.array([np.min(z[x == xi]) for xi in xu]) zmax = np.array([np.max(z[x == xi]) for xi in xu]) return xu, za, zs, zmin, zmax def _ax_interpolate(self, ax, x, z, desc=None): '''Add interpolation to plot. ''' logger.log(5, self._ax_interpolate.__doc__) desc = desc or dict(self.desc, **self.interp_desc) if np.unique(x).size < 2: msg = 'Skipping interpolate, too few datapoints ({}).' logger.info(msg.format(np.unique(x))) return xu, za, zs, zmin, zmax = self._calc_stat(x, z, sort=True) xl = np.linspace(min(xu), max(xu), 500) za = pchip_interpolate(xu, za, xl) ax.plot(xl, za, **desc) self.update_min_max(xl, za) def _ax_interpolate_zx(self, ax, x, z, desc=None): '''Add interpolation to plot. Use x = x(z).''' logger.log(5, self._ax_interpolate.__doc__) desc = desc or dict(self.desc, **self.interp_desc) if np.unique(z).size < 2: msg = 'Skipping interpolate, too few datapoints ({}).' logger.info(msg.format(np.unique(z).size)) return zu, xa, xs, xmin, xmax = self._calc_stat(z, x, sort=True) zl = np.linspace(min(zu), max(zu), 500) xa = pchip_interpolate(zu, xa, zl) ax.plot(xa, zl, **desc) self.update_min_max(xa, zl) def _ax_errorbar(self, ax, x, z, desc=None): '''Add errorbars to plot. ''' logger.log(5, self._ax_errorbar.__doc__) desc = desc or dict(self.desc, **self.scatter_desc) desc.pop('s', None) xu, za, zs, zmin, zmax = self._calc_stat(x, z, sort=True) ax.errorbar(xu, za, yerr=zs * 2, fmt='o', **desc) self.update_min_max(x, z) def _ax_minmaxbar(self, ax, x, z, desc=None): '''Add minmaxbars to plot. ''' logger.log(5, self._ax_minmaxbar.__doc__) desc = desc or dict(self.desc, **self.scatter_desc) desc.pop('s', None) xu, za, zs, zmin, zmax = self._calc_stat(x, z, sort=True) zerr = [za - zmin, zmax - za] ax.errorbar(xu, za, yerr=zerr, fmt='o', **desc) self.update_min_max(x, z) def _ax_minmaxfill(self, ax, x, z, desc=None): '''Add fill min-max to plot. ''' logger.log(5, self._ax_minmaxfill.__doc__) desc = desc or dict(self.desc, **self.interp_desc) desc.pop('ls', None) # invalid key desc.pop('marker', None) # invalid key desc.pop('edgecolor', None) # invalid key desc.pop('s', None) # invalid key xu, za, zs, zmin, zmax = self._calc_stat(x, z, sort=True) xl = np.linspace(min(xu), max(xu), 500) za = pchip_interpolate(xu, za, xl) zmin = pchip_interpolate(xu, zmin, xl) zmax = pchip_interpolate(xu, zmax, xl) diff = (zmax - zmin) / 20 * 0 # center space? ax.fill_between(xl, zmax, za - diff, alpha=.15, **desc) ax.fill_between(xl, za + diff, zmin, alpha=.15, **desc) self.update_min_max(xl, za) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Plot to all axes # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def curve(self, x, z, desc=None): ''' Add curve plot to all axes. ''' self._ax_curve(self.ax_xz, x, z, desc=desc) def scatter(self, x, z, desc=None): ''' Add scatter plot to all axes. ''' self._ax_scatter(self.ax_xz, x, z, desc=desc) def lowess(self, x, z, desc=None): ''' Add lowess plot to all axes. ''' self._ax_lowess(self.ax_xz, x, z, desc=desc) def lowess_zx(self, x, z, desc=None): ''' Add lowess_zx plot to all axes. ''' self._ax_lowess_zx(self.ax_xz, x, z, desc=desc) def interpolate(self, x, z, desc=None): ''' Add interpolate plot to all axes. ''' self._ax_interpolate(self.ax_xz, x, z, desc=desc) def interpolate_zx(self, x, z, desc=None): ''' Add interpolate_zx plot to all axes. ''' self._ax_interpolate_zx(self.ax_xz, x, z, desc=desc) def errorbar(self, x, z): ''' Add errorbar plot to all axes. ''' self._ax_errorbar(self.ax_xz, x, z) def minmaxbar(self, x, z, desc=None): ''' Add minmaxbar plot to all axes. ''' self._ax_minmaxbar(self.ax_xz, x, z, desc=desc) def minmaxfill(self, x, z, desc=None): ''' Add minmaxfill plot to all axes. ''' self._ax_minmaxfill(self.ax_xz, x, z, desc=desc) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Finalize plot # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # def calc_limits(self): ''' Return plot limits. Use the min/max of the added data. Make symmetric, if specified. Use ratio, if specified. ''' # calculate plot limits # set symmetric if self.symx: self._xmax = max(-self._xmin, self._xmax) self._xmin = -self._xmax if self.symz: self._zmax = max(-self._zmin, self._zmax) self._zmin = -self._zmax # pre-calculation dx = self._xmax - self._xmin # x-distance dz = self._zmax - self._zmin ax = (self._xmax + self._xmin) / 2 # average az = (self._zmax + self._zmin) / 2 # average if self.xz_ratio: # set z/x ratio # note, this only works when the axes are equal r = self.xz_ratio # keep the below as a single operation (dx, dz) = (max(dx, dz / r), max(dz, dx * r)) # add padding (after correcting ratio) f = 0.1 # add this fraction to plot limit px = dx * f # x-padding pz = dz * f # keep min = 0, change other xmin = self._xmin zmin = self._zmin if xmin != 0: xmin = ax - (dx + px) / 2 if zmin != 0: zmin = az - (dz + pz) / 2 # set max relative to min xmax = xmin + px + dx zmax = zmin + pz + dz logger.debug(f'Limits calc: x=({xmin},{xmax}), z=({zmin},{zmax})') return xmin, xmax, zmin, zmax def set_limits(self): ''' Set axes lin/log and set limits. Set calculated limits, then override with user defined values. Note: xlim is applied for all the axes, just at first. ''' logger.debug('Setting limits') # todo: consider adding scientific notation as option # ax.ticklabel_format(style=sci, scilimits=(0, 0)) # set equal limits for all axes xmin, xmax, zmin, zmax = self.calc_limits() if self.logx_on: xmin = None if (xmin <= 0) else xmin xmax = None if (xmax <= 0) else xmax for ax in self.axes: ax.set_xscale('log') if self.logz_on: zmin = None if (zmin <= 0) else zmin zmax = None if (zmax <= 0) else zmax for ax in self.axes: ax.set_yscale('log') # if min==max, then do not override Matplotlib's default if (xmin == xmax): (xmin, xmax) = (None, None) if (zmin == zmax): (zmin, zmax) = (None, None) for ax in self.axes: # set calculated limits # sub-classes with more axes should use xmin/xmax as default ax.set_xlim((xmin, xmax)) # xlim may vary between axes ax.set_ylim((zmin, zmax)) # ylim to be equal for all axes # override with user defined values # note: warning is fine if user specifies negative values with log ax.set_xlim(left=self.xmin, right=self.xmax) ax.set_ylim(bottom=self.zmin, top=self.zmax) xt, zt = self.axes[0].get_xlim(), self.axes[0].get_ylim(), logger.debug(f'Limits set: x=({xt}), z=({zt})') def set_annotation(self): ''' Annotate axis, if specified. ''' if not self.annotate_on: return logger.debug('Adding annotation') # idea: add possibility to fix axis # idea: add possibility to fix style bbox_props = dict(boxstyle="round", fc="w", ec="0.5", alpha=0.9) bbox_props = dict(boxstyle="circle", fc="w", ec="0.5", alpha=0.9) self.axes[0].text( self.annotate_x, self.annotate_y, self.annotate_text, ha="center", va="center", bbox=bbox_props ) def set_labels(self): '''Control legend, title, and axis labels. ''' logger.debug('Setting labels') if self.legend_on: # add to only one axis, -1 for furthest right self.axes[-1].legend(numpoints=1, loc=self.legend_loc) if self.xlabel: self.ax_xz.set_xlabel(self.xlabel) if self.zlabel: # add to only one axis, 0 for furthest left self.axes[0].set_ylabel(self.zlabel) if self.title == 'fpath': self.title = str(Path(self.fpath).stem) if self.title: self.fig.suptitle(self.title) if self.annotate_on: self.set_annotation() def save_plot(self, close=True): ''' Save added data as plotted figure, movie, and/or json-file. Create figure, add all plots, set limits, set labels, save figure. ''' if self.fpath is None: logger.error('Error: cannot save plot without name.') return self._set_fig() # create the figure self.plot_all_pd() # plot to figure self.set_limits() self.set_labels() self.fig.set_tight_layout(self.tight) logger.info('Saving: {}'.format(self.fpath)) if self.figure: Canvas(self.fig).print_figure(self.fpath) if self.movie: self.save_movie() if close: self._clear_fig() if self.save_data: self.save_plotted_data() def save_movie(self): '''Save a movie of a plot with a time-series. Note: matplotlib.animation is imported below. FFMpegWriter is used. This method solved problems when running in background. Calculate time position of each frame. tscale=1e6 implies that 1 us takes 1 s. fps=5 implies 5 frames per second. ''' # assume that everything is plotted # that axes and labels are correct # idea: add option to use no or idx instead of time if (len(self.pd_t) == 0) or sum(len(t) for t in self.pd_t) == 0: msg = 'Unable to create movie. No time data.' logger.info(msg) return # temporary change rcParams # (since tight bbox breaks ffmpeg writer) # ffmpeg_file could be used, but is possibly slower # self.fig.set_tight_layout(True) bbox = mpl.rcParams['savefig.bbox'] # mpl.rcParams['savefig.bbox'] = 'standard' # get min/maximum/diff time # assume t is scaled correctly, filter empty series tmax = max(t.max() for t in self.pd_t if len(t) > 0) tmin = min(t.min() for t in self.pd_t if len(t) > 0) tdiff = tmax - tmin # calc number of frames # (10 us * 1e7(tscale) * 5 f/s = 500 frames) frame_no = int(tdiff * self.fps) logger.info('Creating movie of {} frames.'.format(frame_no)) logger.log(5, 'tmax * self.fps, ' + str(tmax * self.fps)) logger.debug('Max time: {}'.format(tmax)) # set up linspace of which times to plot fts = np.linspace(tmin, tmax, frame_no, endpoint=True) # configure writer import matplotlib.animation as mpla # hack: see comment on top FFMpegWriter = mpla.FFMpegWriter # pipe based # FFMpegWriter = mpla.FFMpegFileWriter # writing temp file metadata = dict(title=self.title, artist='CerMan') writer = FFMpegWriter(fps=self.fps, metadata=metadata) dpi = mpl.rcParams['savefig.dpi'] vpath = str(Path(self.fpath).with_suffix('.mp4')) # note: do note clear&set figure after this point! writer.setup(fig=self.fig, outfile=vpath, dpi=dpi) # choose at which iterations to log log_i = [int(frame_no * j / 10) for j in range(1, 10)] # create movie frames for i, ft in enumerate(fts): # logging if i in log_i: msg = 'Creating movie {:4.1f} %' logger.info(msg.format(i / frame_no * 100)) msg = 'Plotting frame {:d} of {:d}' logger.log(5, msg.format(i + 1, frame_no)) # todo: consider skipping of reset/redraw # if there is no new info (sum(t<ft), now vs last) # reset plot for ax in self.axes: ax.clear() self.reset_mc_cc() # add time to legend tlabel = f'Time: {ft:6.3f} {self.tlabel}' self.add_label_to_axes(desc={'label': tlabel, 'alpha': 0}) # set mask and plot a frame self.pd_m = [ti < ft for ti in self.pd_t] # empty when empty, ok self.plot_all_pd() self.set_limits() self.set_labels() Canvas(self.fig).draw() # redraw figure needed writer.grab_frame() # grab frame for video # save individual frames if self.frames: path = Path(self.fpath).with_suffix('') L = len(str(frame_no)) # pad length path = str(path) + '{0:0{1}d}'.format(i, L) logger.info('Saving frame: {}'.format(path)) Canvas(self.fig).print_figure(path) # save video logger.info('Saving: {}'.format(vpath)) writer.finish() # rewind setting mpl.rcParams['savefig.bbox'] = bbox
class RealTimeLIAValues(tk.Frame): def __init__(self, parent, **kwargs): tk.Frame.__init__(self, parent, **kwargs) self.traces = dict() # Define matplotlib figure self.f = Figure(figsize=(4.5, 3), dpi=100) self.a = self.f.add_subplot(111) self.f.set_tight_layout(True) # create arrays to plot the values self.y = [0] * 50 self.x = arange(0, 50, 1) self.i = 0 # Create the GUI for the live readings from LIA self.real_time_LIA_values_LF = ttk.LabelFrame(self, text="LIA Output, R") self.values_Frame = ttk.Frame( self.real_time_LIA_values_LF, borderwidth=5, ) self.trigger_Button = ttk.Button(self.real_time_LIA_values_LF, text='Start', state='disabled', command=self.on_off) # Stick it to the grid self.real_time_LIA_values_LF.grid(column=0, row=0, padx=5) self.values_Frame.grid( column=0, row=0, ) self.trigger_Button.grid(column=0, row=2, pady=7) # Create, stick and draw canvas for the figure self.canvas = FigureCanvasTkAgg(self.f, master=self.values_Frame) self.canvas.get_tk_widget().grid( column=0, row=0, ) self.canvas.draw() # Change the name of the button, need for the animation def on_off(self): if self.trigger_Button['text'] == "Stop": self.trigger_Button['text'] = "Start" else: self.trigger_Button['text'] = "Stop" # Trace the line def trace(self, name, dataset_x, dataset_y): if name in self.traces: self.line1.set_data(dataset_x, dataset_y) data = self.line1.get_ydata() self.a.set_ylim(min(data), max(data)) else: self.traces[name] = True self.line1, = self.a.plot(dataset_x, dataset_y) self.canvas.draw()
def _reference_figure_default(self): figure = Figure(facecolor='white') figure.set_tight_layout(True) return figure
class ChartWidget(FigureCanvas): def __init__(self, symbol, filepath, width, height, parent, app=None, colorup='g', colordown='r'): self.colorup = colorup self.colordown = colordown self.forecast_window = 30 self.dataGen = minuteStockDataFeeder(filepath) self.chartData = PriceChartData('') #for i in range(0, 200): # self.chartData.add(next(self.dataGen)) df = pd.DataFrame({ 'Date': self.chartData.dates, 'Time': self.chartData.times, 'Open': self.chartData.opens, 'High': self.chartData.highs, 'Low': self.chartData.lows, 'Close': self.chartData.closes, 'Volume': self.chartData.volumes }) self.fig = Figure(figsize=(width, height)) self.fig.set_tight_layout({"pad": 1.5}) self.fig.suptitle(symbol) self.canvas = FigureCanvas(self.fig) gs = gridspec.GridSpec(7, 7) gs.update(wspace=0.0, hspace=0.0) # set the spacing between axes. self.tickindex = df.index.values[:-30] self.tick_length = len(self.tickindex) self.axPrice = self.fig.add_subplot(gs[0:4, :-1]) self.axPrice.tick_params(axis="y", direction="in", left=True, right=True) self.axPrice.get_xaxis().set_visible(False) # Specify tick label size self.axPrice.tick_params(axis='x', which='major', labelsize=4) self.axPrice.tick_params(axis='x', which='minor', labelsize=0) self.axPrice.set_xticks(np.arange(0, self.tick_length, 10)) self.axPrice.set_xticks(np.arange(0, self.tick_length, 1), minor=True) self.axPrice.set_xticklabels( np.array([ i - self.tick_length for i in np.arange(0, self.tick_length, 10) ])) major_ticks = np.arange(-10, 110, 10) minor_ticks = np.arange(-10, 110, 1) self.axPrice.set_yticks(major_ticks) self.axPrice.set_yticks(minor_ticks, minor=True) self.axPrice.set_ylabel('Price(% of range)', fontsize=8) self.axPrice.set_xlim(-170, 0) self.axPrice.grid(True) self.axPrice.set_alpha(0.) self.axLearning = self.fig.add_subplot(gs[0:4, 5:6]) self.axLearning.set_facecolor('None') self.axLearning.set_alpha(0.) #self.axLearning.get_yaxis().set_visible(False) self.axLearning.set_xticks(np.arange(0, 30, 5)) self.axLearning.set_xticks(np.arange(0, 30, 1), minor=True) self.axLearning.tick_params(axis="x", direction="in", top=True, bottom=False) self.axLearning.xaxis.set_ticks_position('top') self.axLearning.set_yticks(major_ticks) self.axLearning.tick_params(labelleft='off') self.axLearning.yaxis.grid(True) self.axLearning.set_xticklabels( np.array([i - 30 for i in np.arange(0, 30, 5)])) self.axForecast = self.fig.add_subplot(gs[0:4, 6:]) #self.axForecast.get_yaxis().set_visible(False) self.axForecast.set_xticks(np.arange(0, 30, 5)) self.axForecast.set_xticks(np.arange(0, 30, 1), minor=True) self.axForecast.tick_params(axis="x", direction="in", top=True, bottom=False) self.axForecast.set_yticks(major_ticks) self.axForecast.tick_params(labelleft='off') self.axForecast.xaxis.set_ticks_position('top') self.axForecast.yaxis.grid(True) self.axVolume = self.fig.add_subplot(gs[4, :-1], sharex=self.axPrice) self.axCash = self.fig.add_subplot(gs[5, :-1]) self.axCash.get_yaxis().set_visible(False) self.axCash.get_xaxis().set_visible(False) self.ax5 = self.fig.add_subplot(gs[5, 6:], sharey=self.axPrice) self.ax5.get_yaxis().set_visible(False) self.ax5.get_xaxis().set_visible(False) self.axAction = self.fig.add_subplot(gs[6:, :-1], sharex=self.axPrice) self.axAction.get_yaxis().set_visible(False) self.ax7 = self.fig.add_subplot(gs[6:, 6:]) self.ax7.get_yaxis().set_visible(False) self.ax7.get_xaxis().set_visible(False) self.price_candles = self.candlestick(self.axPrice, df.Open[:-30], df.High[:-30], df.Low[:-30], df.Close[:-30], width=1, colorup=colorup, colordown=colordown) self.axPrice.set_ylabel('Price(% of range)', fontsize=8) self.axPrice.grid(True) self.volume_bars = self.volume_overlay(self.axVolume, df.Open[:-30], df.Close[:-30], df.Volume[:-30], colorup=colorup, colordown=colordown, width=1) self.axVolume.get_xaxis().set_visible(False) self.axVolume.yaxis.tick_right() self.axVolume.set_yticks(np.arange(0, 10, 2)) self.axVolume.set_yticks(np.arange(0, 10, 1), minor=True) self.axVolume.set_ylabel('Volume', fontsize=8) self.axVolume.yaxis.set_label_position('right') self.axVolume.grid(True) self.axVolume.set_ylim(0, 10) self.axVolume.get_xaxis().set_visible(False) FigureCanvas.__init__(self, self.fig) self.app = app FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding) FigureCanvas.updateGeometry(self) def candlestick( self, ax, opens, highs, lows, closes, width=4, colorup='g', colordown='r', alpha=0.75, ): delta = width / 2. barVerts = [((i - delta, open), (i - delta, close), (i + delta, close), (i + delta, open)) for i, open, close in zip(range(len(opens)), opens, closes) ] downSegments = [((i, low), (i, min(open, close))) for i, low, high, open, close in zip( range(len(lows)), lows, highs, opens, closes)] upSegments = [((i, max(open, close)), (i, high)) for i, low, high, open, close in zip( range(len(lows)), lows, highs, opens, closes)] rangeSegments = upSegments + downSegments r, g, b = colorConverter.to_rgb(colorup) colorup = r, g, b, alpha r, g, b = colorConverter.to_rgb(colordown) colordown = r, g, b, alpha colord = { True: colorup, False: colordown, } colors = [colord[open < close] for open, close in zip(opens, closes)] useAA = 0, # use tuple here lw = 0.5, # and here rangeCollection = LineCollection( rangeSegments, colors=((0, 0, 0, 1), ), linewidths=lw, antialiaseds=useAA, ) barCollection = PolyCollection( barVerts, facecolors=colors, edgecolors=((0, 0, 0, 1), ), antialiaseds=useAA, linewidths=lw, ) minx, maxx = 0, len(rangeSegments) / 2 miny = min([low for low in lows]) maxy = max([high for high in highs]) corners = (minx, miny), (maxx, maxy) ax.update_datalim(corners) # add these last rangeCollection.set_alpha(0.4) barCollection.set_alpha(0.4) ax.collections.clear() ax.add_collection(rangeCollection) ax.add_collection(barCollection) return [rangeCollection, barCollection] def volume_overlay(self, ax, opens, closes, volumes, colorup='g', colordown='r', width=4, alpha=1.0): colorup = mcolors.to_rgba(colorup, alpha) colordown = mcolors.to_rgba(colordown, alpha) colord = {True: colorup, False: colordown} colors = [ colord[open < close] for open, close in zip(opens, closes) if open != -1 and close != -1 ] delta = width / 2. bars = [((i - delta, 0), (i - delta, v), (i + delta, v), (i + delta, 0)) for i, v in enumerate(volumes) if v != -1] barCollection = PolyCollection( bars, facecolors=colors, edgecolors=((0, 0, 0, 1), ), antialiaseds=(0, ), linewidths=(0.5, ), ) barCollection.set_alpha(0.4) corners = (0, 0), (len(bars), max(volumes)) ax.collections.clear() ax.add_collection(barCollection) ax.update_datalim(corners) #ax.autoscale(True) #ax.set_aspect('auto') #ax.autoscale(False) # add these last return [barCollection] def my_scatter(self, ax, x, y): from matplotlib.collections import PathCollection from matplotlib.path import Path import matplotlib.transforms as mtransforms phi = np.linspace(0, 2 * np.pi, 100) # Scale, in pixel coordinates rad = 2 x_circle = np.cos(phi) * rad y_circle = np.sin(phi) * rad verts = np.vstack([x_circle, y_circle]).T path = Path(verts, closed=False) collection = PathCollection( [path], facecolor='blue', edgecolor='black', transOffset=ax.transData, ) collection.set_transform(mtransforms.IdentityTransform()) ax.add_collection(collection, autolim=True) ax.autoscale() def millions(self, x): 'The two args are the value and tick position' return '%1.1fM' % (x * 1e-6) def thousands(self, x): 'The two args are the value and tick position' return '%1.1fK' % (x * 1e-3) def update_chart(self, i=0): import time tstart = time.time() self.chartData.add(next(self.dataGen)) yh = self.chartData.highs.max() yl = self.chartData.lows.min() r = 100.0 / (yh - yl) vl = self.chartData.volumes.min() vh = self.chartData.volumes.max() vr = 10.0 / (vh - vl) self.df = pd.DataFrame({ 'Date': self.chartData.dates, 'Time': self.chartData.times, 'Open': (self.chartData.opens - yl) * r, 'High': (self.chartData.highs - yl) * r, 'Low': (self.chartData.lows - yl) * r, 'Close': (self.chartData.closes - yl) * r, 'Volume': (self.chartData.volumes - vl) * vr }) ## Learning zone #### self.target_data1 = ( self.df.High[-2 * self.forecast_window:-self.forecast_window] + self.df.Low[-2 * self.forecast_window:-self.forecast_window]) / 2.0 # ts = self.df.Date[-self.forecast_window:] # self.axLearning.lines.clear() # self.axLearning.collections.clear() # # ##### date change line in target zone ###### # # ts2 = ts.ne(ts.shift().bfill()).astype(int) # txs = ts2.diff()[ts2.diff() != 0].index.values # # if len(txs) > 1: # x = txs[1] - self.tick_length # else: # x = 0 # # self.tline = self.axForecast.axvline(x=x, linewidth=.5, color='k', alpha=0.5, linestyle='--') self.axLearning.lines.clear() self.axLearning.collections.clear() self.targets1 = self.axLearning.scatter(range(30), self.target_data1, s=15, c="b", alpha=0.9, marker="o") data = self.target_data1 + self.target_data1 * ( 0.5 - np.random.random(30)) * .2 self.predhistory = self.axLearning.scatter(range(30), data, s=15, c="k", marker="x") # self.axForecast.set_xticks(np.arange(0, 30, 5)) # self.axForecast.set_xticks(np.arange(0, 30, 1), minor=True) self.axLearning.set_ylim(-10.0, 110.0) self.axLearning.set_xlim(0.0, 30.0) #### forecast Targets ############ self.target_data = (self.df.High[-self.forecast_window:] + self.df.Low[-self.forecast_window:]) / 2.0 #self.target_data = 100.*np.random.random(30) self.target_data = self.target_data + self.target_data * ( 0.5 - np.random.random(30)) * .2 ts = self.df.Date[-self.forecast_window:] self.axForecast.lines.clear() self.axForecast.collections.clear() ##### date change line in target zone ###### ts2 = ts.ne(ts.shift().bfill()).astype(int) txs = ts2.diff()[ts2.diff() != 0].index.values if len(txs) > 1: x = txs[1] - self.tick_length else: x = 0 self.tline = self.axForecast.axvline(x=x, linewidth=.5, color='k', alpha=0.5, linestyle='--') self.targets = self.axForecast.scatter(range(30), self.target_data, s=15, c="k", marker="x") #self.axForecast.set_xticks(np.arange(0, 30, 5)) #self.axForecast.set_xticks(np.arange(0, 30, 1), minor=True) self.axForecast.set_ylim(-10.0, 110.0) self.axForecast.set_xlim(0.0, 30.0) ##### axPrice ################# self.axPrice.texts.clear() self.axPrice.lines.clear() self.price_candles = self.candlestick(self.axPrice, self.df.Open[:-30], self.df.High[:-30], self.df.Low[:-30], self.df.Close[:-30], width=1, colorup=self.colorup, colordown=self.colordown) self.axPrice.set_ylim(-10.0, 110.0) self.axPrice.set_xlim(0.0, 170.0) self.price_range = [yh - i * (yh - yl) / 10 for i in range(1, 11)] self.price_range = [self.price_range[0] + self.price_range[0] - self.price_range[1]] + self.price_range + \ [ self.price_range[9] - self.price_range[0] + self.price_range[1]] self.ranges = [] for i in range(0, 12): self.ranges.append( self.axPrice.text(self.tick_length - 40, (10 - i) * 10, '${:1.2f}'.format(self.price_range[i]))) self.last_price = "Date: {}, Time:{}, Open:{:2.2f}, High:{:2.2f}, Low:{:2.2f}, Close:{:2.2f}, Volume:{}".format( self.chartData.dates[-1], self.chartData.times[-1], self.chartData.opens[-1], self.chartData.highs[-1], self.chartData.lows[-1], self.chartData.closes[-1], self.chartData.volumes[-1]) #print(self.last_price) self.text = self.axPrice.text(0.80, 0.95, self.last_price, horizontalalignment='right', verticalalignment='bottom', transform=self.axPrice.transAxes) ##### axVolume ######### self.axVolume.texts.clear() self.volume_bars = self.volume_overlay(self.axVolume, self.df.Open[:-30], self.df.Close[:-30], self.df.Volume[:-30], colorup=self.colorup, colordown=self.colordown, width=1) self.vol_range = [(vh - i * (vh - vl) / 5) for i in range(1, 5)] self.vol_ranges = [] for i in range(0, 4): self.vol_ranges.append( self.axVolume.text( self.tick_length - 10, (4 - i) * 2, '{}'.format(self.thousands(self.vol_range[i])))) ##### date change line ###### s = self.df.Date[:-self.forecast_window] s2 = s.ne(s.shift().bfill()).astype(int) xs = s2.diff()[s2.diff() != 0].index.values if len(xs) > 1: x = xs[1] else: x = 0 self.line = self.axPrice.axvline(x=x, linewidth=.5, color='k', alpha=0.5, linestyle='--') #################### import gc gc.collect() print('FPS:', 1 / (time.time() - tstart)) print('Artists:', len(self.fig.findobj())) return self.price_candles + self.volume_bars + self.ranges + [ self.line ] + self.vol_ranges + [self.targets] + [self.targets1] + [ self.predhistory ] + [self.tline] + [self.text] def refresh(self): self.update_chart() self.draw() self.app.processEvents()
class Window(QMainWindow): """Class for the whole window. """ def __init__(self, parent=None): """Load and initialise the lattices. """ super(Window, self).__init__(parent) # Lattice loading ring = atip.utils.load_at_lattice('DIAD') sp_len = ring.circumference/6.0 ring.s_range = [0, sp_len] self.lattice = ring[ring.i_range[0]:ring.i_range[-1]]# + [ring[1491]] """ self.lattice = at.load_tracy('../atip/atip/rings/for_Tobyn.lat') zl = [] for idx, elem in enumerate(self.lattice): elem.Index = idx + 1 if elem.Length == 0.0: zl.append(idx) zl.reverse() for idx in zl: self.lattice.__delitem__(idx) print(len(self.lattice)) """ self._atsim = atip.simulator.ATSimulator(self.lattice, emit_calc=False) self.s_selection = None # Super-period support self.total_len = self.lattice.get_s_pos(len(self.lattice))[0] self.symmetry = 6 #self.symmetry = vars(self.lattice).get('periodicity', 1) # Create UI self.initUI() def initUI(self): """Low level UI building of the core sections and their components. """ # Set initial window size in pixels self.setGeometry(0, 0, 1500, 800) # Initialise layouts layout = QHBoxLayout() layout.setSpacing(20) self.left_side = QVBoxLayout() self.left_side.setAlignment(Qt.AlignLeft) # Create graph graph = QHBoxLayout() self.figure = Figure() self.canvas = FigureCanvasQTAgg(self.figure) self.canvas.mpl_connect('button_press_event', self.graph_onclick) self.figure.set_tight_layout({"pad": 0.5, "w_pad": 0, "h_pad": 0}) self.plot() # Make graph fixed size to prevent autoscaling self.canvas.setMinimumWidth(1000) self.canvas.setMaximumWidth(1000) self.canvas.setMinimumHeight(480) self.canvas.setMaximumHeight(480) self.graph_width = 1000 self.graph_height = 480 graph.addWidget(self.canvas) graph.setStretchFactor(self.canvas, 0) # Add graph to left side layout self.left_side.addLayout(graph) # Create lattice representation bar self.full_disp = QVBoxLayout() self.full_disp.setSpacing(0) self.lat_disp = QHBoxLayout() self.lat_disp.setSpacing(0) self.lat_disp.setContentsMargins(QMargins(0, 0, 0, 0)) # Add a stretch at both ends to keep the lattice representation centred self.lat_disp.addStretch() # Add startline self.lat_disp.addWidget(element_repr(-1, Qt.black, 1, drag=False)) # Add elements self.lat_repr = self.create_lat_repr() for el_repr in self.lat_repr: self.lat_disp.addWidget(el_repr) # Add endline self.lat_disp.addWidget(element_repr(-1, Qt.black, 1, drag=False)) # Add offset self.lat_disp.addWidget(element_repr(-1, Qt.white, 3, drag=False)) # Add a stretch at both ends to keep the lattice representation centred self.lat_disp.addStretch() # Add non-zero length representation to lattice representation layout self.full_disp.addLayout(self.lat_disp) # Add horizontal dividing line self.black_bar = QHBoxLayout() self.black_bar.setSpacing(0) self.black_bar.addStretch() # Keep it centred self.mid_line = element_repr(-1, Qt.black, 1000, height=1, drag=False) self.black_bar.addWidget(self.mid_line) # Add offset self.black_bar.addWidget(element_repr(-1, Qt.white, 3, height=1, drag=False)) self.black_bar.addStretch() # Keep it centred self.full_disp.addLayout(self.black_bar) # Create zero length element representation bar self.zl_disp = QHBoxLayout() self.zl_disp.setSpacing(0) # Add a stretch at both ends to keep the lattice representation centred self.zl_disp.addStretch() # Add startline self.zl_disp.addWidget(element_repr(-1, Qt.black, 1, drag=False)) # Add elements self.zl_repr = self.calc_zero_len_repr(1000) for el_repr in self.zl_repr: self.zl_disp.addWidget(el_repr) # Add endline self.zl_disp.addWidget(element_repr(-1, Qt.black, 1, drag=False)) # Add offset self.zl_disp.addWidget(element_repr(-1, Qt.white, 3, drag=False)) # Add a stretch at both ends to keep the lattice representation centred self.zl_disp.addStretch() # Add zero length representation to lattice representation layout self.full_disp.addLayout(self.zl_disp) # Add full lattice representation to left side layout self.left_side.addLayout(self.full_disp) # Create element editing boxes to drop to bottom = QHBoxLayout() self.edit_boxes = [] # Future possibility to auto determine number of boxes by window size for i in range(4): box = edit_box(self, self._atsim) self.edit_boxes.append(box) bottom.addWidget(box) # Add edit boxes to left side layout self.left_side.addLayout(bottom) # All left side components now set, add them to main layout layout.addLayout(self.left_side) # Create lattice and element data sidebar sidebar_border = QWidget() # Dividing line sidebar_border.setStyleSheet(".QWidget {border-left: 1px solid black}") sidebar = QGridLayout(sidebar_border) sidebar.setSpacing(10) # Determine correct global title if self.symmetry == 1: title = QLabel("Global Lattice Parameters:") else: title = QLabel("Global Super Period Parameters:") # Ensure sidebar width remains fixed title.setMaximumWidth(220) title.setMinimumWidth(220) title.setStyleSheet("font-weight:bold; text-decoration:underline;") sidebar.addWidget(title, 0, 0) # Ensure sidebar width remains fixed spacer = QLabel("") spacer.setMaximumWidth(220) spacer.setMinimumWidth(220) sidebar.addWidget(spacer, 0, 1) self.lattice_data_widgets = {} row_count = 1 # start after global title row # Create global fields for field, value in self.get_lattice_data().items(): sidebar.addWidget(QLabel("{0}: ".format(field)), row_count, 0) lab = QLabel(self.stringify(value)) sidebar.addWidget(lab, row_count, 1) self.lattice_data_widgets[field] = lab row_count += 1 # Add element title title = QLabel("Selected Element Parameters:") title.setStyleSheet("font-weight:bold; text-decoration:underline;") sidebar.addWidget(title, row_count, 0) self.element_data_widgets = {} row_count += 1 # continue after element title row # Create local fields for field, value in self.get_element_data(0).items(): sidebar.addWidget(QLabel("{0}: ".format(field)), row_count, 0) lab = QLabel("N/A") # default until s selection is made sidebar.addWidget(lab, row_count, 1) self.element_data_widgets[field] = lab row_count += 1 # Add units tool tips where applicable self.lattice_data_widgets["Total Length"].setToolTip("m") self.lattice_data_widgets["Horizontal Emittance"].setToolTip("pm") self.lattice_data_widgets["Linear Dispersion Action"].setToolTip("m") self.lattice_data_widgets["Energy Loss per Turn"].setToolTip("eV") self.lattice_data_widgets["Damping Times"].setToolTip("msec") self.lattice_data_widgets["Total Bend Angle"].setToolTip("deg") self.lattice_data_widgets["Total Absolute Bend Angle"].setToolTip("deg") self.element_data_widgets["Selected S Position"].setToolTip("m") self.element_data_widgets["Element Start S Position"].setToolTip("m") self.element_data_widgets["Element Length"].setToolTip("m") self.element_data_widgets["Horizontal Linear Dispersion"].setToolTip("m") self.element_data_widgets["Beta Function"].setToolTip("m") # Add sidebar to main window layout layout.addWidget(sidebar_border) # Set and display layout wid = QWidget(self) wid.setLayout(layout) self.setCentralWidget(wid) self.setStyleSheet("background-color:white;") self.show() def create_lat_repr(self): """Create a list of element representations, in the order that they appear in the lattice, colour coded according to their type. See also: calc_zero_len_repr """ lat_repr = [] self.zero_length = [] self.base_widths = [] for elem in self.lattice:#[:self.lattice.i_range[-1]]: width = math.ceil(elem.Length) if width == 0: if not (isinstance(elem, at.elements.Drift) or isinstance(elem, at.elements.Marker) or isinstance(elem, at.elements.Aperture)): # don't care about zero length drifts, markers or apertures self.zero_length.append(elem) else: self.base_widths.append(elem.Length) if isinstance(elem, at.elements.Drift): elem_repr = element_repr(elem.Index, Qt.white, width) elif isinstance(elem, at.elements.Dipole): elem_repr = element_repr(elem.Index, Qt.green, width) elif isinstance(elem, at.elements.Quadrupole): elem_repr = element_repr(elem.Index, Qt.red, width) elif isinstance(elem, at.elements.Sextupole): elem_repr = element_repr(elem.Index, Qt.yellow, width) elif isinstance(elem, at.elements.Corrector): elem_repr = element_repr(elem.Index, Qt.blue, width) else: elem_repr = element_repr(elem.Index, Qt.gray, width) lat_repr.append(elem_repr) return lat_repr def calc_new_width(self, new_width): """Calculate the new widths of the element representations so that they may be dynamically scaled to fit into the new window size, whilst remaining roughly proportional to their lengths. """ scale_factor = new_width / sum(self.base_widths) scaled_widths = [width * scale_factor for width in self.base_widths] rounding = [] for index in range(len(scaled_widths)): if scaled_widths[index] == 0: pass elif scaled_widths[index] < 1: scaled_widths[index] = 1 else: value = scaled_widths[index] scaled_widths[index] = round(value) if round(value) >= 2: rounding.append((value, index)) rounding.sort() # sort smallest to biggest diff = round(sum(scaled_widths) - new_width) if abs(diff) > len(rounding): raise ValueError("too many elements with 0<length<1") if diff > 0: # overshoot for i in range(diff): _, index = rounding.pop() scaled_widths[index] = numpy.maximum(scaled_widths[index]-1, 1) elif diff < 0: # undershoot for i in range(abs(diff)): _, index = rounding.pop(0) scaled_widths[index] = scaled_widths[index]+1 return scaled_widths def calc_zero_len_repr(self, width): """Create element representations for elements in the lattice with 0 length, to be displayed below the non-zero length element representations. See also: create_lat_repr """ scale_factor = width / self.total_len all_s = self._atsim.get_s() positions = [0.0] for elem in self.zero_length: positions.append(all_s[elem.Index-1] * scale_factor) zero_len_repr = [] for i in range(1, len(positions), 1): gap_length = int(round(positions[i] - positions[i-1])) # N.B. zero length gap spacers are not drag-and-drop-able as they # are not drifts, however this could potentially be added in future # to allow zero length elements to be moved. zero_len_repr.append(element_repr(-1, Qt.white, gap_length, drag=False)) elem = self.zero_length[i-1] if isinstance(elem, at.elements.Monitor): elem_repr = element_repr(elem.Index, Qt.magenta, 1) elif isinstance(elem, at.elements.RFCavity): elem_repr = element_repr(elem.Index, Qt.cyan, 1) elif isinstance(elem, at.elements.Corrector): elem_repr = element_repr(elem.Index, Qt.blue, 1) else: elem_repr = element_repr(elem.Index, Qt.gray, 1) zero_len_repr.append(elem_repr) diff = int(sum([el_repr.width for el_repr in zero_len_repr]) - width) if diff < 0: # undershoot # unless the last zero length element is very close to the end of # the displayed section this should always occur. zero_len_repr.append(element_repr(-1, Qt.white, abs(diff), drag=False)) elif diff > 0: # overshoot # this should rarely occur # add zero len elem_repr at the end to maintain consistent length zero_len_repr.append(element_repr(-1, Qt.white, 0, drag=False)) while diff > 1: for i in range(len(zero_len_repr)): el_repr = zero_len_repr[i] if el_repr.width > 1: el_repr.changeSize(el_repr.width - 1) diff -= 1 if diff < 1: break else: # add zero len elem_repr at the end to maintain consistent length zero_len_repr.append(element_repr(-1, Qt.white, 0, drag=False)) return zero_len_repr def get_lattice_data(self): """Calculate the global linear optics data for the lattice, and return it in a dictionary by its field names. """ self._atsim.wait_for_calculations() data_dict = OrderedDict() data_dict["Number of Elements"] = len(self.lattice) data_dict["Total Length"] = self.total_len data_dict["Total Bend Angle"] = self._atsim.get_total_bend_angle() data_dict["Total Absolute Bend Angle"] = self._atsim.get_total_absolute_bend_angle() data_dict["Cell Tune"] = [self._atsim.get_tune('x'), self._atsim.get_tune('y')] data_dict["Linear Chromaticity"] = [self._atsim.get_chromaticity('x'), self._atsim.get_chromaticity('y')] data_dict["Horizontal Emittance"] = self._atsim.get_horizontal_emittance() * 1e12 data_dict["Linear Dispersion Action"] = self._atsim.get_linear_dispersion_action() data_dict["Momentum Spread"] = self._atsim.get_energy_spread() data_dict["Linear Momentum Compaction"] = self._atsim.get_momentum_compaction() data_dict["Energy Loss per Turn"] = self._atsim.get_energy_loss() data_dict["Damping Times"] = self._atsim.get_damping_times() * 1e3 data_dict["Damping Partition Numbers"] = self._atsim.get_damping_partition_numbers() return data_dict def get_element_data(self, selected_s_pos): """Calculate the local (for the element at the selected s position) linear optics data for the lattice, and return it in a dictionary by its field names. """ self._atsim.wait_for_calculations() data_dict = OrderedDict() all_s = self._atsim.get_s() index = int(numpy.where([s <= selected_s_pos for s in all_s])[0][-1]) data_dict["Selected S Position"] = selected_s_pos data_dict["Element Index"] = index + 1 data_dict["Element Start S Position"] = all_s[index] data_dict["Element Length"] = self._atsim.get_at_element(index+1).Length data_dict["Horizontal Linear Dispersion"] = self._atsim.get_dispersion()[index, 0] data_dict["Beta Function"] = self._atsim.get_beta()[index] data_dict["Derivative of Beta Function"] = self._atsim.get_alpha()[index] data_dict["Normalized Phase Advance"] = self._atsim.get_mu()[index]/(2*numpy.pi) return data_dict def stringify(self, value): """Convert numerical data into a string that can be displayed. """ v = [] if numpy.issubdtype(type(value), numpy.number): value = [value] for val in value: if isinstance(val, int): v.append("{0:d}".format(val)) else: if val == 0: v.append("0.0") elif abs(val) < 0.1: v.append("{0:.5e}".format(val)) else: v.append("{0:.5f}".format(val)) if len(v) == 1: return v[0] else: return "[" + ', '.join(v) + "]" def update_lattice_data(self): """Iterate over the global linear optics data and update the values of each field. Usually called after a change has been made to the lattice. """ for field, value in self.get_lattice_data().items(): self.lattice_data_widgets[field].setText(self.stringify(value)) def update_element_data(self, s_pos): """Iterate over the local linear optics data and update the values of each field. Usually called when a new s position selection is made. """ for field, value in self.get_element_data(s_pos).items(): self.element_data_widgets[field].setText(self.stringify(value)) def plot(self): """Plot the graph inside the figure. """ self.figure.clear() self.axl = self.figure.add_subplot(111, xmargin=0, ymargin=0.025) self.axl.set_xlabel('s position [m]') self.axr = self.axl.twinx() self.axr.margins(0, 0.025) self.lattice.radiation_off() # ensure radiation state for linopt call at.plot.plot_beta(self.lattice, axes=(self.axl, self.axr)) self.canvas.draw() def graph_onclick(self, event): """Left click to make an s position selection and display a black dashed line at that position on the graph. Right click to clear a selection. """ if event.xdata is not None: if self.s_selection is not None: self.s_selection.remove() # remove old s selection line if event.button == 1: self.s_selection = self.axl.axvline(event.xdata, color="black", linestyle='--', zorder=3) self.update_element_data(event.xdata) else: # if not right click clear selection data self.s_selection = None for lab in self.element_data_widgets.values(): lab.setText("N/A") self.canvas.draw() def resize_graph(self, width, height, redraw=False): """Resize the graph to a new width and(or) height; can also be used to force a redraw of the graph, without resizing, by way of the redraw argument. """ if not redraw: # doesn't redraw if not necessary and not forced redraw = bool((int(width) != int(self.graph_width)) or (int(height) != int(self.graph_height))) if redraw: self.canvas.flush_events() self.canvas.setMaximumWidth(int(width)) self.canvas.setMaximumHeight(int(height)) self.canvas.resize(int(width), int(height)) self.graph_width = int(width) self.graph_height = int(height) def refresh_all(self): """Refresh the graph, global linear optics data, and local linear optics data. """ self.plot() self.resizeEvent(None) self.update_lattice_data() s_pos = self.element_data_widgets["Selected S Position"].text() if s_pos != "N/A": self.update_element_data(float(s_pos)) self.s_selection.remove() self.s_selection = self.axl.axvline(float(s_pos), color="black", linestyle='--', zorder=3) self.canvas.draw() for box in self.edit_boxes: box.refresh() def resizeEvent(self, event): """Called when the window is resized; resizes the graph and lattice representation accordingly for the new window size. N.B. 1) The hard-coded pixel offsets are almost entirely arbitrary and "just work^TM" for me, but may need to be changed for alignment to work properly on a different machine. 2) All resizing related code is held together by willpower and voodoo magic and will break if it senses fear. """ # Determine graph width from window size width = int(max([self.frameGeometry().width() - 500, 1000])) height = int(max([self.frameGeometry().height() - 350, 480])) # Resize graph self.resize_graph(width, height) # Get non-zero length element representation widths from graph width widths = self.calc_new_width(width - 127) for el_repr, w in zip(self.lat_repr, widths): if w != el_repr.width: el_repr.changeSize(w) # Two px more to account for end bars self.mid_line.changeSize(width - 125) # Get lattice representation width from graph width zlr = self.calc_zero_len_repr(width - 127) zl_widths = [el_repr.width for el_repr in zlr] for el_repr, w in zip(self.zl_repr, zl_widths): el_repr.changeSize(w) # If not a refresh call then resize the window if event is not None: super().resizeEvent(event)
class Main(QMainWindow, Ui_MainWindow): def __init__(self, ): super(Main, self).__init__() self.setupUi(self) self.showMaximized() self.ave = np.array([]) # empty array for average bore self.auto_flag = False # autoscale flag self.spinBoxval = 0 # defualt 4D data_cube dimension self.spinBox.hide() # hide option unless 4D self.colourmap = 'viridis' # default colourmap self.interpMethod = 'nearest' # default interp method self.cmapmin = None # default colourbar range, i.e let matplotlib decide self.cmapmax = None self.hres = 1 # default res, i.e jut voxel numbers on axis self.vres = 1 self.Normx = None # normalisation method. default set to None self.Normy = None self.Normz = None self.XView.setChecked(True) self.fig = Figure() self.ax1 = self.fig.add_subplot(111) self.X = datacube() self.AveBoreView = 'X' # change view of cube self.XView.toggled.connect(lambda: self.btnstate(self.XView)) self.YView.toggled.connect(lambda: self.btnstate(self.YView)) self.ZView.toggled.connect(lambda: self.btnstate(self.ZView)) self.Bore.toggled.connect(lambda: self.btnstate(self.Bore)) self.AverageBore.toggled.connect( lambda: self.btnstate(self.AverageBore)) # update data when slider moved self.Scroll_Horz.valueChanged[int].connect(self.sliderval) self.Scroll_Vert.valueChanged[int].connect(self.sliderval) self.file_open(args) self.Open.triggered.connect(self.file_open) self.Save_Avg_Bore.triggered.connect(self.saveBore) self.Reset.triggered.connect(self.reset_plot) self.AutoScale.triggered.connect(self.Auto_Scale_plot) # self.Bore_View.triggered.connect(self.ViewBore) self.action_Save_Gif.triggered.connect(self.saveGif) self.action_Colour_Map.triggered.connect(self.changeColourMap) self.action_Interpolation_Method.triggered.connect(self.changeInterpolationMethod) self.action_Colour_Bar_Clip.triggered.connect(self.changeclipColourBarRange) self.action_Save_Image.triggered.connect(self.saveImage) self.action_Normalisation_Method.triggered.connect(self.changeNormMethod) self.action_Bore_Location.triggered.connect(self.setBoreLocation) self.spinBox.valueChanged.connect(self.changeSpinbox) def setBoreLocation(self, ): xloc, ok = QtWidgets.QInputDialog.getInt( self, 'Input location', 'Enter X location:') yloc, ok = QtWidgets.QInputDialog.getInt( self, 'Input location', 'Enter Y location:') self.Scroll_Horz.setValue(xloc) self.Scroll_Vert.setValue(yloc) if self.Bore.isChecked(): if self.BoreView == 'X': self.im.set_ydata(self.X.data[:, xloc, yloc][::]) elif self.BoreView == 'Y': self.im.set_ydata(self.X.data[xloc, :, yloc][::]) elif self.BoreView == 'Z': self.im.set_ydata(self.X.data[yloc, xloc, :][::]) # try and redraw try: self.im.axes.figure.canvas.draw() if self.auto_flag: self.im.autoscale() except AttributeError: pass def changeNormMethod(self, ): # func to change Normalisation method of matshow method = self.getNormDialog() if(method == 'Log'): self.Normx = colors.LogNorm(vmin=0.1, vmax=self.X.data[self.ind, :, :].max()) self.Normy = colors.LogNorm(vmin=0.1, vmax=self.X.data[:, self.ind, :].max()) self.Normz = colors.LogNorm(vmin=0.1, vmax=self.X.data[:, :, self.ind].max()) elif(method == 'Symmetric Log'): self.Normx = colors.SymLogNorm(linthresh=1., vmin=self.X.data[self.ind, :, :].min(), vmax=self.X.data[self.ind, :, :].max()) self.Normy = colors.SymLogNorm(linthresh=1., vmin=self.X.data[:, self.ind, :].min(), vmax=self.X.data[:, self.ind, :].max()) self.Normz = colors.SymLogNorm(linthresh=1., vmin=self.X.data[:, :, self.ind].max(), vmax=self.X.data[:, :, self.ind].max()) elif(method == 'Linear'): self.Normx = None self.Normy = None self.Normz = None self.reset_plot(False) self.init_plot() def saveImage(self, ): # saves data and image of current view name = self.showGifDialog() if self.XView.isChecked(): np.savetxt(name + '.dat', self.X.data[self.Scroll_Vert.value(), :, :], delimiter=' ') elif self.YView.isChecked(): np.savetxt(name + '.dat', self.X.data[:, self.Scroll_Vert.value(), :], delimiter=' ') elif self.ZView.isChecked(): np.savetxt(name + '.dat', self.X.data[:, :, self.Scroll_Vert.value()], delimiter=' ') self.hres, self.vres = self.showextentDialog() # scale x, y ticks to actual scale based upon user definition # thanks https://stackoverflow.com/a/17816809/6106938 # change so that it uses extent=[xmin, xmax, ymin, ymax] # set default as None # then change here. extent added to matshow(*args, extent=[...]) ticks = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x * self.hres)) self.ax1.xaxis.set_major_formatter(ticks) ticks = ticker.FuncFormatter(lambda y, pos: '{0:g}'.format(y * self.vres)) self.ax1.yaxis.set_major_formatter(ticks) self.fig.savefig(name + '.png') def changeColourMap(self, ): # change cmap self.colourmap = self.showColourmapsDialog() self.reset_plot(False) self.init_plot() def changeclipColourBarRange(self, ): # change vmin, vmax for cbar self.cmapmin, self.cmapmax = self.showclipColourBarDialog() self.reset_plot(False) self.init_plot() def changeInterpolationMethod(self, ): # change interpolation method for image self.interpMethod = str(self.showInterpolationDialog()) self.reset_plot(False) self.init_plot() def saveGif(self): rang = self.showGifframesDialog() # get range of frames step = self.showGifstepDialog() # get number of images name = self.showGifDialog() # name of file tight = self.showGifExtent() # tight or not # loop over range and make images tmpplace = self.Scroll_Vert.value() if rang * step > tmpplace: rang = tmpplace for i in range(rang): self.Scroll_Horz.setValue(self.ind) self.Scroll_Vert.setValue(tmpplace - (i * step)) self.sliderval() if tight: extent = self.ax1.get_window_extent().transformed(fig.dpi_scale_trans.inverted()) self.fig.savefig(str(i).zfill(3) + 'pic.png', bbox_inches=extent) else: self.fig.savefig(str(i).zfill(3) + 'pic.png') # use ffmpeg to create gif if tight: os.system("mogrify -trim *pic.png") os.system("ffmpeg -framerate 10 -pattern_type glob -i '*pic.png' -c:v libx264 -r 24 -pix_fmt yuv420p -vf 'pad=ceil(iw/2)*2:ceil(ih/2)*2' " + name + ".mp4") os.system('rm *pic.png') print('done') def changeSpinbox(self): # for 4d data cubes self.spinBoxval = int(self.spinBox.value()) fd = open(self.name, 'rb') self.readslice(fd, self.ndim, np.float64, self.cubeorder) self.reset_plot() self.init_plot() def Auto_Scale_plot(self): # autoscale cbar on plot and reset clipping if any self.cmapmin = None self.cmapmax = None if not self.auto_flag: self.auto_flag = True else: self.auto_flag = False def sliderval(self): # move slider and update data if self.XView.isChecked(): # fd = open(self.name, 'rb') self.X.readslice(self.Scroll_Horz.value()) self.im.set_data(self.X.data[self.Scroll_Vert.value(), :, :]) # self.Scroll_Horz.setValue(0) # pin unsed slider elif self.YView.isChecked(): self.X.readslice(self.Scroll_Horz.value()) self.im.set_data(self.X.data[:, self.Scroll_Vert.value(), :]) # self.Scroll_Horz.setValue(0) # pin unsed slider elif self.ZView.isChecked(): self.X.readslice(self.Scroll_Horz.value()) self.im.set_data(self.X.data[:, :, self.Scroll_Vert.value()]) # self.Scroll_Horz.setValue(0) # pin unsed slider elif self.Bore.isChecked(): if self.BoreView == 'X': self.im.set_ydata(self.X.data[:, self.Scroll_Horz.value(), self.Scroll_Vert.value()][::]) if self.auto_flag: self.ax1.relim() self.ax1.autoscale_view(True, True, True) elif self.BoreView == 'Y': self.im.set_ydata(self.X.data[self.Scroll_Horz.value(), :, self.Scroll_Vert.value()][::]) if self.auto_flag: self.ax1.relim() self.ax1.autoscale_view(True, True, True) elif self.BoreView == 'Z': self.im.set_ydata(self.X.data[self.Scroll_Vert.value(), self.Scroll_Horz.value(), :][::]) if self.auto_flag: self.ax1.relim() self.ax1.autoscale_view(True, True, True) elif self.AverageBore.isChecked(): self.Scroll_Horz.setValue(self.ind) self.Scroll_Vert.setValue(self.ind) # try and redraw try: self.im.axes.figure.canvas.draw() if self.auto_flag: self.im.autoscale() except AttributeError: pass def addmpl(self): # add plot to anvas self.rmmpl() self.canvas = FigureCanvas(self.fig) self.mplvl.addWidget(self.canvas) self.canvas.draw() self.toolbar = NavigationToolbar(self.canvas, self.mplwindow) self.mplvl.addWidget(self.toolbar) def rmmpl(self): # delete plot from canvas try: self.canvas.close() self.canvas.deleteLater() self.toolbar.close() self.toolbar.deleteLater() gc.collect() except: pass def saveBore(self,): # save bore as a list of points name = QtWidgets.QFileDialog.getSaveFileName(self, 'Save File') f = open(name, 'w') if len(self.ave) > 1: for i in range(len(self.ave)): f.write(str(self.ave[i]) + '\n') f.close() else: if self.BoreView == "X": tmp = self.X[:, self.Scroll_Horz.value(), self.Scroll_Vert.value()] elif self.BoreView == "Y": tmp = self.X[self.Scroll_Horz.value(), :, self.Scroll_Vert.value()] elif self.BoreView == "Z": tmp = self.X[self.Scroll_Horz.value(), self.Scroll_Vert.value(), :] for i in range(len(tmp)): f.write(str(tmp[i]) + '\n') def file_open(self, args): self.reset_plot() while True: try: # get file name if args.file is None: self.X.name = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File')[0] else: self.X.name = args.file # get precision of data cube self.X.dtype, self.X.cubeorder, item = self.getPrec(args) # get dimensions of data cube. can be guessed bool4d = False if self.X.cubeorder == 4: bool4d = True self.X.ndim = self.getSize(args, item, bool4d) try: fd = open(self.X.name, 'rb') except FileNotFoundError: self.ErrorDialog("File not Found!") self.X.name = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File')[0] self.X.readslice(0) self.init_plot() break except ValueError: size = os.path.getsize(self.X.name) if "Real*8" in item: size /= 8 elif "Real*4" in item: size /= 4 mssg = "Value of Ndim or precision is incorrect for this data cube.\n On disk size is: {:010d}.\n".format(int(size)) val2 = self.X.is_perfect_n(size, 2.) val3 = self.X.is_perfect_n(size, 3.) if (val2 and val3) != 0: mssg += " Try x=y={:04d}, z=1\n or x=y=z={:04d}.".format(int(val2), int(val3)) elif val2 != 0: mssg += "Try x=y={:04d}, z=1.".format(int(val2)) elif val3 != 0: mssg += "Try x=y=z={:04d}.".format(int(val3)) self.ErrorDialog(mssg) args.ndim = None args.fpprec = None except UnboundLocalError: pass break def getSize(self, args, item, bool4d): if args.ndim is None and item: size = os.path.getsize(self.X.name) if "Real*8" in item: size /= 8 elif "Real*4" in item: size /= 4 if self.X.is_perfect_n(size, 3.) != 0: size = self.X.is_perfect_n(size, 3.) ndim = (size, size, size) else: ndim = self.showNdimDialog(bool4d) else: ndim = (args.ndim, args.ndim, args.ndim) return ndim def getPrec(self, args): # get precision of data cube item = None if args.fpprec is None: item = str(self.showDtDialog()) if "Real*8" in item: dt = np.float64 elif "Real*4" in item: dt = np.float32 if "4 dim" in item: dim = 4 elif "3 dim" in item: dim = 3 else: if args.fpprec == 1: dt = np.float32 dim = 4 elif args.fpprec == 2: dt = np.float64 dim = 4 elif args.fpprec == 3: dt = np.float32 dim = 3 elif args.fpprec == 4: dt = np.float64 dim = 3 return dt, dim, item def btnstate(self, b): if b.text() == "X View": if b.isChecked() is True: self.reset_plot(False) self.Scroll_Vert.setMaximum(self.rows - 1) self.im = self.ax1.matshow(self.X.data[self.ind, :, :], vmin=self.cmapmin, vmax=self.cmapmax, cmap=str(self.colourmap), interpolation=self.interpMethod, norm=self.Normx) self.fig.colorbar(self.im) self.fig.set_tight_layout(True) self.ax1.set_aspect('auto') self.addmpl() if b.text() == "Y View": if b.isChecked() is True: self.reset_plot(False) self.Scroll_Vert.setMaximum(self.cols - 1) self.im = self.ax1.matshow(self.X.data[:, self.ind, :], vmin=self.cmapmin, vmax=self.cmapmax, cmap=str(self.colourmap), interpolation=self.interpMethod, norm=self.Normy) self.fig.colorbar(self.im) self.fig.set_tight_layout(True) self.ax1.set_aspect('auto') self.addmpl() if b.text() == "Z View": if b.isChecked() is True: self.reset_plot(False) self.Scroll_Vert.setMaximum(self.slices - 1) self.im = self.ax1.matshow(self.X.data[:, :, self.ind], vmin=self.cmapmin, vmax=self.cmapmax, cmap=str(self.colourmap), interpolation=self.interpMethod, norm=self.Normz) try: self.fig.colorbar(self.im) except ZeroDivisionError: self.ErrorDialog("Divison by zero, try another range") self.Normz = None self.Normy = None self.Normz = None self.cmapmin = None self.cmapmax = None self.btnstate(b) self.fig.set_tight_layout(True) self.ax1.set_aspect('auto') self.addmpl() if b.text() == "Draw Bore": if b.isChecked() is True: self.ViewBore() self.reset_plot(False) if self.BoreView == 'X': self.im, = self.ax1.plot(self.X.data[:, self.ind, self.ind]) elif self.BoreView == 'Y': self.im, = self.ax1.plot(self.X.data[self.ind, :, self.ind]) elif self.BoreView == 'Z': self.im, = self.ax1.plot(self.X.data[self.ind, self.ind, :]) self.fig.set_tight_layout(True) self.addmpl() if b.text() == "Avg. Bore": if b.isChecked() is True: self.AveBoreChecked() def ViewBore(self): self.BoreView = self.showBoreViewDialog() if self.BoreView == 'X': self.view = (1, 2) elif self.BoreView == 'Y': self.view = (0, 2) elif self.BoreView == 'Z': self.view = (0, 1) def AveBoreChecked(self): self.ViewBore() self.reset_plot(False) if len(self.ave) == 0: self.ave = np.array([]) self.ave = np.sum(self.X.data, self.view) self.ave /= (len(self.X.data[self.view[0]]) * len(self.X.data[self.view[1]])) self.im = self.ax1.plot(self.ave[::]) self.fig.set_tight_layout(True) self.addmpl() def reset_plot(self, *args): self.ave = np.array([]) self.fig.clf() self.ax1.clear() gc.collect() # fixes most of memory leak self.fig = Figure() self.ax1 = self.fig.add_subplot(111) self.rmmpl() def init_plot(self, ): self.rmmpl() if self.X.cubeorder == 4: self.rows, self.cols, self.slices, self.depth = self.X.ndim else: self.rows, self.cols, self.slices = self.X.ndim self.depth = 0 self.ind = 0 # int(rows / 2) if self.XView.isChecked(): view = self.XView self.Scroll_Vert.setMaximum(self.rows) self.Scroll_Horz.setMaximum(self.depth) elif self.YView.isChecked(): view = self.YView self.Scroll_Vert.setMaximum(self.cols) self.Scroll_Horz.setMaximum(self.depth) elif self.ZView.isChecked(): view = self.ZView self.Scroll_Vert.setMaximum(self.slices) self.Scroll_Horz.setMaximum(self.cols) elif self.AverageBore.isChecked(): view = self.AverageBore self.Scroll_Vert.setMaximum(self.rows) elif self.Bore_View.isChecked(): view = self.ViewBore self.Scroll_Vert.setMaximum(self.cols) self.Scroll_Horz.setMaximum(self.rows) self.btnstate(view) self.Scroll_Horz.setValue(self.ind) self.Scroll_Vert.setValue(self.ind) def showGifframesDialog(self, ): text, ok = QtWidgets.QInputDialog.getInt( self, '# of frames', 'Enter # of frames:') if ok: return(text) def showGifstepDialog(self, ): text, ok = QtWidgets.QInputDialog.getInt( self, 'Step size', 'Enter value of step:') if ok: return(text) def showGifExtent(self, ): items = ("Colour Bar", "No Colour Bar") text, ok = QtWidgets.QInputDialog.getItem( self, "Colour Bar on GIF?", " ", items, 0, False) if ok and text: if items == "Colour Bar": text = False else: text = True return text def showNdimDialog(self, bool4d): text1, ok1 = QtWidgets.QInputDialog.getInt( self, 'Input Ndim', 'Enter X Ndim:') if ok1: text2, ok2 = QtWidgets.QInputDialog.getInt( self, 'Input Ndim', 'Enter Y Ndim:') if ok2: text3, ok3 = QtWidgets.QInputDialog.getInt( self, 'Input Ndim', 'Enter Z Ndim:') if ok3 and bool4d: text4, ok4 = QtWidgets.QInputDialog.getInt( self, 'Input Ndim', 'Enter T Ndim:') return (text1, text2, text3, text4) else: return (text1, text2, text3) def showDtDialog(self, ): items = ("4 dim Real*4", "4 dim Real*8", "3 dim Real*4", "3 dim Real*8") item, ok = QtWidgets.QInputDialog.getItem(self, "Select Fortran Precision", "Precisions", items, 0, False) if ok and item: return item def showBoreViewDialog(self, ): items = ("X", "Y", "Z") item, ok = QtWidgets.QInputDialog.getItem(self, "Select Average Bore Direction", "Views", items, 0, False) if ok and item: return item def showGifDialog(self, ): text, ok = QtWidgets.QInputDialog.getText( self, 'Filename Dialog', 'Enter filename:') if ok: return str(text) def showextentDialog(self, ): hres, ok = QtWidgets.QInputDialog.getDouble( self, 'Data Extent', 'Enter horizontal resolution:', 0, -100, 100, 9,) if ok: vres, ok = QtWidgets.QInputDialog.getDouble( self, 'Data Extent', 'Enter vertical resolution:', 0, -100, 100, 9,) if ok: return (hres, vres) def getNormDialog(self, ): items = ("Log", "Linear", "Symmetric Log") item, ok = QtWidgets.QInputDialog.getItem(self, "Select cbar normalisation method", "Method:", items, 0, False) if ok and item: return item def showColourmapsDialog(self, ): items = ('viridis', 'inferno', 'plasma', 'magma', 'Blues', 'BuGn', 'BuPu', 'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd', 'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu', 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd', 'afmhot', 'autumn', 'bone', 'cool', 'copper', 'gist_heat', 'gray', 'hot', 'pink', 'spring', 'summer', 'winter', 'BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr', 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral', 'seismic', 'Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3', 'Vega10', 'Vega20', 'Vega20b', 'Vega20c', 'gist_earth', 'terrain', 'ocean', 'gist_stern', 'brg', 'CMRmap', 'cubehelix', 'gnuplot', 'gnuplot2', 'gist_ncar', 'nipy_spectral', 'jet', 'rainbow', 'gist_rainbow', 'hsv', 'flag', 'prism') item, ok = QtWidgets.QInputDialog.getItem(self, "Select Colour Map", "Cmaps", items, 0, False) if ok and item: return item def showInterpolationDialog(self, ): items = ('none', 'nearest', 'bilinear', 'bicubic', 'spline16', 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos') item, ok = QtWidgets.QInputDialog.getItem(self, "Select Interpolation Method", "Methods", items, 0, False) if ok and item: return item def showclipColourBarDialog(self, ): text1, ok1 = QtWidgets.QInputDialog.getDouble( self, 'Input cbar min', 'Enter min:', 0., np.finfo("d").min, np.finfo("d").max, 10) if ok1: text2, ok2 = QtWidgets.QInputDialog.getDouble( self, 'Input cbar max', 'Enter max:', 0., np.finfo("d").min, np.finfo("d").max, 10) if ok2: return (float(text1), float(text2)) def ErrorDialog(self, ErrMsg): QtWidgets.QMessageBox.warning(self, "Error", ErrMsg)
class Principal(Frame): # Class for helping Tooltips class ToolTip(object): def __init__(self, widget): self.widget = widget self.tipwindow = None self.id = None self.x = self.y = 0 def showtip(self, text, lang): self.text = text if self.tipwindow or not self.text: return x, y, cx, cy = self.widget.bbox("insert") x = x + self.widget.winfo_rootx() + 27 y = y + cy + self.widget.winfo_rooty() + 27 self.tipwindow = tw = Toplevel(self.widget) tw.wm_overrideredirect(1) tw.wm_geometry("+%d+%d" % (x, y)) try: # For Mac OS tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w, "help", "noActivates") except TclError: pass label = Label(tw, text=self.text[lang], justify=LEFT, background="#ffffe0", relief=SOLID, borderwidth=1, font=("tahoma", "8", "normal")) label.pack(ipadx=1) def hidetip(self): tw = self.tipwindow self.tipwindow = None if tw: tw.destroy() # Initialization function def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent # Spiral parameters (defined as StringVars for convenience) self.a = StringVar() self.a.set('0') self.b = StringVar() self.b.set('0.5') self.c = StringVar() self.c.set('1') self.lMax = StringVar() self.lMax.set(158) self.frec = StringVar() self.frec.set(500) self.StringLongitud = StringVar() self.StringRadio = StringVar() # Help mode flag self.ayuda = False # Figure object self.f = Figure(figsize=(5, 5)) self.initUI() # Tooltip creator function (allowed by help mode flag) def createToolTip(self, widget, text): toolTip = self.ToolTip(widget) def enter(event): if self.ayuda: toolTip.showtip(text, self.lang) def leave(event): toolTip.hidetip() widget.bind('<Enter>', enter) widget.bind('<Leave>', leave) # Euclidean distance calculator function def distancia(self, r1, phi1, r2, phi2): return sqrt(r1**2 + r2**2 - 2 * r1 * r2 * cos(phi1 - phi2)) # Polar to Cartesian coordinates def pol2cart(self, rho, phi): x = rho * cos(phi) y = rho * sin(phi) return (x, y) # def grafico(self): # Set figure size self.f = Figure(figsize=(5, 5)) # Check whether negative parameters are present and show an error if float(self.c.get()) < 0 or float(self.b.get()) < 0: print self.lang tkMessageBox.showerror("Error", self.error.get()) return # Set figure axis ax = self.f.add_subplot(111, polar=True) # Initialize r and theta lists at the center point self.theta_disc = [0] self.r_disc = [0] self.theta_disc_n = [0] # Initialize length value and list l = 0 l_vec = [] # Loop limited by antenna length while l < int(self.lMax.get()): # Length of each antenna segment in cm, computed as 1/15 of the wave length lseg = 300 / float(self.frec.get()) * 100 / 15 if self.tipoCurva.get() == 1: # Archimedean spiral # New theta values are calculated according to the following: # In an Archimedean spiral # /r - a\ c # theta = |-------| # \ b / # In order to get an approximately equally spaced segments, new theta values are computed according to the next formula. This formula has been worked # out gradually, not basing on any well-known expression. # /0.5 * lseg - a\ c # |----------------| - lseg # \ b / # ------------------------------ + lseg # 10 * theta + 1 # n # theta = theta + --------------------------------------- # n + 1 n r + 1 # n self.theta_disc.append(self.theta_disc[-1] + \ (( ((0.5*lseg - float(self.a.get()))/float(self.b.get()))**(float(self.c.get())) - lseg) / (10*self.theta_disc[-1] + 1) + lseg) \ / (self.r_disc[-1] + 1)) # print str(lseg) # print str(self.r_disc[-1]) else: # print "Eh: " + str(self.theta_disc[-1]) # print "Ra: " + str(self.r_disc[-1]) # print "Ls: " + str(lseg) # print "Ot: " + str(log(0.5*lseg/float(self.a.get()))/float(self.b.get())) self.theta_disc.append(self.theta_disc[-1] + \ (( max(log(0.5*lseg/float(self.a.get()))/float(self.b.get()),float(self.a.get())) - lseg) * exp(-1*self.theta_disc[-1]) + lseg) \ / (self.r_disc[-1] + 1)) #print str(lseg) #print str(self.r_disc[-1]) if self.tipoCurva.get() == 1: self.r_disc.append( float(self.b.get()) * self.theta_disc[-1]**(1 / float(self.c.get())) + float(self.a.get())) elif self.tipoCurva.get() == 2: self.r_disc.append( float(self.a.get()) * exp(float(self.b.get()) * self.theta_disc[-1])) self.theta_disc_n.append(pi + self.theta_disc[-1]) l_vec.append( self.distancia(self.r_disc[-1], self.theta_disc[-1], self.r_disc[-2], self.theta_disc[-2])) l += l_vec[-1] if self.fuente.get() and str( self.checkFuente.cget('state')) == 'normal': self.theta_disc.remove(0) self.r_disc.remove(0) self.theta_disc_n.remove(0) ax.plot([self.theta_disc[0], self.theta_disc_n[0]], [self.r_disc[0], self.r_disc[0]], color='r') ax.plot([0], [0], color='m', marker='o', markersize=5) self.StringLongitud.set("%#.1f cm" % l) self.StringRadio.set("%#.1f cm" % max(self.r_disc)) ax.plot(self.theta_disc, self.r_disc, color='b', marker='.', markersize=4) if self.espejar.get(): ax.plot(self.theta_disc_n, self.r_disc, color='g', marker='.', markersize=4) ax.set_rmax(max(self.r_disc)) ax.grid(True) #with open('distancias.csv', 'wb') as f: # writer = csv.writer(f) # writer.writerows(izip(self.theta_disc, l_vec)) def regraficar(self): self.grafico() self.canvas.get_tk_widget().pack_forget() self.canvas = FigureCanvasTkAgg(self.f, master=self.frame2) #canvas.show() self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1, padx=10, pady=10) def cambiaFormula(self): curvas = [ '''R0lGODlhbAAWAOMPAAwMDLa2thYWFiIiIlBQUJ6enubm5gQEBGJiYszMzEBAQDAwMHR0dIqKigAAAP///yH5BAEKAA8ALAAAAABsABYAAAT+8MlJq7046817ToYnjmRpXsqpriw3JGgrz2pDHHDFFHTvezjL4kcsWoKUBIHCQDQQxmhy4EhZDATGkoKcEHIPwjIBkJoljsZVEFIwuGDJUJJwhM7ngN0i4D0YcxJdDwVqEg0CeC0DHQhlOokSCJGCcVYSAYyHiiaaGwOXEwCGDwqRBQgOC28PBqEPCAgMDDANgH8MCnEzAQSxCFufHQ6xuSF6FACeFgwBG1AHCwYGaSgC19jZAssViHQOrMIbelsIQwoHCuoLDsFCGwUgDn67LXVgDvUX3BeOEw0OHgCAcmgeBgME4QUssoBSgQMe+Am5lOqBQQkKHq0gIHEGMS9yHU1lO6CN34FwDamBOZBQhYCWGERqyyaxjp8HLyNuoOYMDYI6//awcNDzh0oJ1HiEy9CRwsIHDSBanCBg6YkCT4kA8EPAToGiTDkIgGEAQM8XsAKtuGUkgRsoYqxiaDrBbS4wbmNx2iuBLt+/HNQCfhABADs=''', '''R0lGODlhQwASAOMPAAwMDLa2thYWFiIiIlBQUJ6enubm5gQEBGJiYszMzEBAQDAwMHR0dIqKigAAAP///yH5BAEKAA8ALAAAAABDABIAAATn8MlJq70463ZSJQyhjWSpGUe1BM/imXCcNQvVDNLQyHz/KAOGgiXYPQAsn7IEKDwKg4SDgCA4DMtsBiVpCAqALk5LrhRqPwIt5yy7H4GaAWBIKJ7391uBULyoIhMNDDUMQi9uAVQIVRQJCAyMMAgPBwsGBg5GFAoCnp+gAmMXXhJSDBOEE3kkBQmZbYhkUogOLwEHWHCBJgUOehMLAhMFKTlBkG0wBKN6DpQSzBMOqD4C0BmdoaHNE1LK1xKwSg5Jepkv46gOyk+yGr7AE03RVwUsCrwF1SWq8g92Ij0gAGIClUjmSEQAADs=''', '''''' ] formula = PhotoImage(data=curvas[self.tipoCurva.get() - 1]) self.formulaLabel.configure(image=formula) self.formulaLabel.image = formula if self.tipoCurva.get() == 1: self.parC.config(state=NORMAL) self.labelC.config(state=NORMAL) else: self.parC.config(state=DISABLED) self.labelC.config(state=DISABLED) def activarFuente(self): if self.espejar.get(): self.checkFuente.config(state=NORMAL) else: self.checkFuente.config(state=DISABLED) def escribirFichero(self): tipoCurva = ['Arq', 'Log'] c = [self.c.get() + ' ', ''] self.file_opt = options = {} options['defaultextension'] = '.nec' options['filetypes'] = [('NEC2 files', '.nec'), ('all files', '.*')] options['initialdir'] = '~/Documentos/Antenas/Espirales' options['initialfile'] = 'Spiral ' + tipoCurva[int(self.tipoCurva.get())-1] + ' ' + \ self.a.get() + ' ' + self.b.get() + ' ' + c[int(self.tipoCurva.get())-1] + self.lMax.get() + ' ' + self.frec.get() + '.nec' options['parent'] = self.parent options['title'] = 'Save NEC' fich = tkFileDialog.asksaveasfile(mode='w', **self.file_opt) r_final = list(reversed(self.r_disc)) + self.r_disc theta_final = list(reversed(self.theta_disc_n)) + self.theta_disc x_ant, y_ant = self.pol2cart(r_final[0], theta_final[0]) tipoCurvaExt = ['Archimedean', 'Logarithmic'] fich.write('CM Created with PySAD\n') fich.write('CM L = %#.1f\n' % float(self.lMax.get())) fich.write('CM ' + tipoCurvaExt[int(self.tipoCurva.get()) - 1] + ' spiral') fich.write('CM a = ' + self.a.get()) fich.write('CM b = ' + self.b.get()) if int(self.tipoCurva.get()) == 0: fich.write('CM c = ' + self.c.get()) fich.write('CE\n') print len(r_final) for i in range(len(r_final) - 1): x, y = self.pol2cart(r_final[i + 1], theta_final[i + 1]) linea = 'GW\t%#d\t%#d\t%#.5f\t%#.5f\t%#.5f\t%#.5f\t%#.5f\t%#.5f\t%#.5f\n' % ( i + 1, 1, x_ant / 100, y_ant / 100, 0, x / 100, y / 100, 0, 0.001) fich.write(linea) x_ant, y_ant = x, y fich.write('GE\t0\nGN\t-1\nEK\n') fich.write('EX\t%#d\t%#d\t%#d\t%#d\t%#d\t%#d\n' % (0, len(r_final) / 2, 1, 0, 1, 0)) fich.write('FR\t0\t0\t0\t0\t299.8\t0\nEN') fich.close() def escribirPDF(self): tipoCurva = ['Arq', 'Log'] c = [self.c.get() + ' ', ''] self.file_opt = options = {} options['defaultextension'] = '.pdf' options['filetypes'] = [('PDF files', '.pdf'), ('all files', '.*')] options['initialdir'] = '~' options['initialfile'] = 'Spiral ' + tipoCurva[int(self.tipoCurva.get())-1] + ' ' + \ self.a.get() + ' ' + self.b.get() + ' ' + c[int(self.tipoCurva.get())-1] + self.lMax.get() + ' ' + self.frec.get() + '.nec' options['parent'] = self.parent options['title'] = 'Save PDF' fich = tkFileDialog.asksaveasfile(mode='w', **self.file_opt) #self.f.axis('off') matplotlib.rcParams.update({'font.size': 1}) self.f.gca().axes.get_xaxis().set_visible(False) self.f.gca().axes.get_yaxis().set_visible(False) papeles_w = [21, 29.7, 42, 59.4, 84.1] papeles_h = [29.7, 42, 59.4, 84.1, 118.9] for i_pap in range(0, len(papeles_w) - 1): if 2 * max(self.r_disc) < papeles_w[i_pap]: break print i_pap self.f.set_size_inches(papeles_w[i_pap] / 2.54, papeles_h[i_pap] / 2.54) noMargen = dict(pad=72 * (papeles_w[i_pap] - 2 * max(self.r_disc)) / 2 / 2.54, h_pad=0, w_pad=0) self.f.set_tight_layout(noMargen) self.f.suptitle('test title') self.f.savefig(fich, format='pdf', dpi='90') fich.close() def mostrarAyuda(self): self.ayuda = not self.ayuda if self.ayuda: self.helpButton.state(["pressed"]) self.config(cursor="question_arrow") else: self.helpButton.state(["!pressed"]) self.config(cursor="") def initText(self): self.curTip = StringVar() self.ArcSpi = StringVar() self.LogSpi = StringVar() self.aaa = StringVar() self.bbb = StringVar() self.ccc = StringVar() self.Lma = StringVar() self.fre = StringVar() self.Mir = StringVar() self.Sou = StringVar() self.Gen = StringVar() self.lenlen = StringVar() self.radrad = StringVar() self.error = StringVar() def updateText(self, lang): self.lang = lang if lang == 0: self.espButton.state(["pressed"]) self.engButton.state(["!pressed"]) else: self.engButton.state(["pressed"]) self.espButton.state(["!pressed"]) self.stringText = { 'curTip': ["Tipo de curva", "Curve type"], 'ArcSpi': ["Espiral de Arquímedes", "Archimedean spiral "], 'LogSpi': ["Espiral logarítmica", "Logarithmic spiral"], 'aaa': ["a (cm)", "a (cm)"], 'bbb': ["b (cm/rad)", "b (cm/rad)"], 'ccc': ["c", "c"], 'Lma': ["Lmax (cm)", "Lmax (cm)"], 'fre': ["frec (MHz)", "freq (MHz)"], 'LmaToo': [ "Longitud máxima de cada brazo de la antena", "Maximum length of each antenna's branch" ], 'FreToo': [ "Frecuencia de diseño (aumentar para disminuir la longitud de los segmentos)", "Design frequency (increase for decreasing segment length)" ], 'Mir': ["Espejar", "Mirror"], 'MirToo': [ "Crea otra rama de la espiral girada 180º", "Create another spiral branch, twisted 180º" ], 'Sou': ["Colocar fuente", "Put source"], 'SouToo': [ "Une las dos mitades y crea una fuente NEC2 en el centro", "Join both halves and create a NEC2 source at the middle" ], 'Gen': ["Generar", "Generate"], 'GenToo': [ "Genera la espiral con los parámetros indicados", "Generate spiral following given parameters" ], 'NEC': ["NEC", "NEC"], 'NECToo': [ "Guarda la espiral como archivo NEC2", "Save the spiral as a NEC2 file" ], 'PDF': ["PDF", "PDF"], 'PDFToo': [ "Imprime la espiral a tamaño real en un documento PDF (máximo A0)", "Print the real sized spiral into a PDF document (A0 maximum)" ], 'HHH': ["H", "H"], 'lenlen': ["Longitud:", "Length:"], 'radrad': ["Radio:", "Radius:"], 'error': [ "No se permiten valores negativos de b o c", "Negative values of b or c are not allowed" ] } self.curTip.set(self.stringText['curTip'][self.lang]) self.ArcSpi.set(self.stringText['ArcSpi'][self.lang]) self.LogSpi.set(self.stringText['LogSpi'][self.lang]) self.aaa.set(self.stringText['aaa'][self.lang]) self.bbb.set(self.stringText['bbb'][self.lang]) self.ccc.set(self.stringText['ccc'][self.lang]) self.Lma.set(self.stringText['Lma'][self.lang]) self.fre.set(self.stringText['fre'][self.lang]) self.Mir.set(self.stringText['Mir'][self.lang]) self.Sou.set(self.stringText['Sou'][self.lang]) self.Gen.set(self.stringText['Gen'][self.lang]) self.lenlen.set(self.stringText['lenlen'][self.lang]) self.radrad.set(self.stringText['radrad'][self.lang]) self.error.set(self.stringText['error'][self.lang]) def initUI(self): self.initText() self.parent.title("PySAD") self.style = Style() self.style.theme_use("clam") self.pack(fill=BOTH, expand=1) barraLateral = Frame(self, borderwidth=1) barraLateral.pack(side=RIGHT, padx=5, pady=5) idiomaFrame = Frame(barraLateral, relief=FLAT) idiomaFrame.pack(side=TOP) self.espButton = Button(idiomaFrame, text="es", width=0, command=lambda: self.updateText(0)) self.espButton.grid(row=0, column=0) self.engButton = Button(idiomaFrame, text="en", width=0, command=lambda: self.updateText(1)) self.engButton.grid(row=0, column=1) self.updateText(0) editarFrame = Frame(barraLateral, relief=RAISED, borderwidth=1, width=1000) editarFrame.pack(fill=BOTH, expand=1, side=TOP, padx=5, pady=5) self.tipoCurva = IntVar() tituloSelector = Label(editarFrame, textvariable=self.curTip) tituloSelector.grid(row=0, columnspan=2, padx=2, pady=4) Radiobutton(editarFrame, textvariable=self.ArcSpi, variable=self.tipoCurva, value=1, command=self.cambiaFormula, width=17).grid(row=1, columnspan=2, sticky=W, padx=4) Radiobutton(editarFrame, textvariable=self.LogSpi, variable=self.tipoCurva, value=2, command=self.cambiaFormula).grid(row=2, columnspan=2, sticky=W, padx=4) self.formulaLabel = Label(editarFrame) self.formulaLabel.grid(row=4, columnspan=2, pady=4) Label(editarFrame, textvariable=self.aaa).grid(row=5, column=0, pady=2) Label(editarFrame, textvariable=self.bbb).grid(row=6, column=0, pady=2) self.labelC = Label(editarFrame, textvariable=self.ccc) self.labelC.grid(row=7, column=0, pady=2) self.labelC.config(state=DISABLED) Label(editarFrame, textvariable=self.Lma).grid(row=8, column=0, pady=2) Label(editarFrame, textvariable=self.fre).grid(row=9, column=0, pady=2) parA = Entry(editarFrame, width=4, textvariable=self.a) parA.grid(row=5, column=1, sticky=W) parB = Entry(editarFrame, width=4, textvariable=self.b) parB.grid(row=6, column=1, sticky=W) self.parC = Entry(editarFrame, width=4, textvariable=self.c) self.parC.grid(row=7, column=1, sticky=W) self.parC.config(state=DISABLED) lMax = Entry(editarFrame, width=4, textvariable=self.lMax) lMax.grid(row=8, column=1, sticky=W) self.createToolTip(lMax, self.stringText['LmaToo']) frec = Entry(editarFrame, width=4, textvariable=self.frec) frec.grid(row=9, column=1, sticky=W) self.createToolTip(frec, self.stringText['FreToo']) self.espejar = IntVar() checkEspejar = Checkbutton(editarFrame, textvariable=self.Mir, variable=self.espejar, command=self.activarFuente) checkEspejar.grid(row=10, columnspan=2, pady=2, sticky=W, padx=4) self.createToolTip(checkEspejar, self.stringText['MirToo']) self.fuente = IntVar() self.checkFuente = Checkbutton(editarFrame, textvariable=self.Sou, state=DISABLED, variable=self.fuente) self.checkFuente.grid(row=11, columnspan=2, pady=2, sticky=W, padx=4) self.createToolTip(self.checkFuente, self.stringText['SouToo']) okButton = Button(editarFrame, textvariable=self.Gen, command=self.regraficar) okButton.grid(row=12, columnspan=2, pady=5) self.createToolTip(okButton, self.stringText['GenToo']) self.frame2 = Frame(self, borderwidth=1) self.frame2.pack(fill=BOTH, expand=1, side=LEFT, padx=5, pady=5) self.canvas = FigureCanvasTkAgg(self.f, master=self.frame2) self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1, padx=10, pady=10) frameGuardar = Frame(barraLateral, relief=FLAT, borderwidth=1) frameGuardar.pack(fill=BOTH, expand=1, side=BOTTOM, padx=5, pady=5) icGuardar = PhotoImage( data= '''R0lGODlhEAAQAIABADMzM////yH5BAEKAAEALAAAAAAQABAAAAIlDI55wchvQJQOxontUktTbkHcSJZkGCao161N5U5SLNM1vZlOAQA7''' ) saveButtonNEC = Button(frameGuardar, text=self.stringText['NEC'][0], image=icGuardar, compound=LEFT, command=self.escribirFichero, width=3) saveButtonNEC.image = icGuardar saveButtonNEC.grid(row=0, column=0, pady=2, padx=2, sticky=W) self.createToolTip(saveButtonNEC, self.stringText['NECToo']) saveButtonPDF = Button(frameGuardar, text=self.stringText['PDF'][0], image=icGuardar, compound=LEFT, command=self.escribirPDF, width=3) saveButtonPDF.image = icGuardar saveButtonPDF.grid(row=0, column=2, pady=2, padx=2, sticky=E) self.createToolTip(saveButtonPDF, self.stringText['PDFToo']) self.helpButton = Button(frameGuardar, text="?", command=self.mostrarAyuda, width=2) self.helpButton.grid(row=0, column=3, pady=2, padx=2, sticky=E) frame3 = Frame(barraLateral, relief=RAISED, borderwidth=1) frame3.pack(fill=BOTH, expand=1, side=BOTTOM, padx=5, pady=5) Label(frame3, textvariable=self.lenlen).grid(row=1, column=0, pady=4, padx=12) Label(frame3, textvariable=self.radrad).grid(row=2, column=0, pady=4, padx=12) Label(frame3, textvariable=self.StringLongitud).grid(row=1, column=1, pady=4) Label(frame3, textvariable=self.StringRadio).grid(row=2, column=1, pady=4) def onExit(self): self.quit()
class PlotWidget(QWidget): customizationTriggered = pyqtSignal() def __init__(self, name, plotFunction, plot_condition_function_list, plotContextFunction, parent=None): QWidget.__init__(self, parent) self._name = name self._plotFunction = plotFunction self._plotContextFunction = plotContextFunction self._plot_conditions = plot_condition_function_list """:type: list of functions """ self._figure = Figure() self._figure.set_tight_layout(True) self._canvas = FigureCanvas(self._figure) self._canvas.setParent(self) self._canvas.setFocusPolicy(Qt.StrongFocus) self._canvas.setFocus() vbox = QVBoxLayout() vbox.addWidget(self._canvas) self._toolbar = CustomNavigationToolbar(self._canvas, self) self._toolbar.customizationTriggered.connect(self.customizationTriggered) vbox.addWidget(self._toolbar) self.setLayout(vbox) self._dirty = True self._active = False self.resetPlot() def getFigure(self): """ :rtype: matplotlib.figure.Figure""" return self._figure def resetPlot(self): self._figure.clear() @property def name(self): """ @rtype: str """ return self._name def updatePlot(self): if self.isDirty() and self.isActive(): # print("Drawing: %s" % self._name) self.resetPlot() plot_context = self._plotContextFunction(self.getFigure()) try: self._plotFunction(plot_context) self._canvas.draw() except StandardError as e: exc_type, exc_value, exc_tb = sys.exc_info() sys.stderr.write("%s\n" % ("-" * 80)) traceback.print_tb(exc_tb) sys.stderr.write("Exception type: %s\n" % exc_type.__name__) sys.stderr.write("%s\n" % e.message) sys.stderr.write("%s\n" % ("-" * 80)) sys.stderr.write("An error occurred during plotting. This stack trace is helpful for diagnosing the problem.") self.setDirty(False) def setDirty(self, dirty=True): self._dirty = dirty def isDirty(self): return self._dirty def setActive(self, active=True): self._active = active def isActive(self): return self._active def canPlotKey(self, key): return any([plotConditionFunction(key) for plotConditionFunction in self._plot_conditions])
class RuntimeTab(wx.Panel): def __init__(self, parent, params=None): wx.Panel.__init__(self, parent) self.pparams = params self.prime_sizer = wx.BoxSizer(wx.VERTICAL) self.prime_figure = Figure() self.prime_figure.patch.set_alpha(0) plt.rc('font', family='sans-serif', size=10) plt.rc('mathtext', default='regular') # Create nested GridSpec gsp = gridspec.GridSpec(2, 2, height_ratios=[2, 3]) self.cc_axes = self.prime_figure.add_subplot(gsp[0]) self.comp_axes = self.prime_figure.add_subplot(gsp[1]) self.mult_axes = self.comp_axes.twinx() self.rej_table = self.prime_figure.add_subplot(gsp[2]) gsub = gridspec.GridSpecFromSubplotSpec(3, 1, subplot_spec=gsp[3], hspace=0) self.bcc_axes = self.prime_figure.add_subplot(gsub[0]) self.bcomp_axes = self.prime_figure.add_subplot(gsub[1], sharex=self.bcc_axes) self.bmult_axes = self.prime_figure.add_subplot(gsub[2], sharex=self.bcc_axes) self.draw_axes() # Set layout, create canvas, add to sizer self.prime_figure.set_tight_layout(True) self.canvas = FigureCanvas(self, -1, self.prime_figure) self.prime_sizer.Add(self.canvas, proportion=1, flag=wx.EXPAND) # Initialize main sizer self.SetSizer(self.prime_sizer) def draw_axes(self): self.rej_table.axis('off') self.cc_axes.set_title(r'$CC_{1/2}$', fontsize=12) self.cc_axes.set_xlabel('Cycle') self.cc_axes.set_ylabel(r'$CC_{1/2}$ (%)') self.comp_axes.set_title('Completeness / Multiplicity', fontsize=12) self.comp_axes.set_xlabel('Cycle') self.comp_axes.set_ylabel('Completeness (%)') self.mult_axes.set_ylabel('# of Observations') self.bcc_axes.yaxis.get_major_ticks()[0].label1.set_visible(False) self.bcc_axes.yaxis.get_major_ticks()[-1].label1.set_visible(False) if self.pparams.target_anomalous_flag: self.bcc_axes.set_ylabel(r'$CC_{1/2}$ anom (%)') else: self.bcc_axes.set_ylabel(r'$CC_{1/2}$ (%)') plt.setp(self.bcc_axes.get_xticklabels(), visible=False) self.bcomp_axes.yaxis.get_major_ticks()[0].label1.set_visible(False) self.bcomp_axes.yaxis.get_major_ticks()[-1].label1.set_visible(False) self.bcomp_axes.set_ylabel("Comp (%)") plt.setp(self.bcomp_axes.get_xticklabels(), visible=False) self.bmult_axes.yaxis.get_major_ticks()[0].label1.set_visible(False) self.bmult_axes.yaxis.get_major_ticks()[-1].label1.set_visible(False) self.bmult_axes.set_xlabel("Resolution ($\AA$)") self.bmult_axes.set_ylabel("# of Obs") self.prime_sizer.Layout() def draw_plots(self, info, total_cycles): # Plot mean CC1/2 meanCC = info['total_cc12'] cycles = range(len(meanCC)) self.cc_axes.clear() self.cc_axes.set_xlim(0, total_cycles) self.cc_axes.set_ylim(0, 100) self.cc_axes.ticklabel_format(axis='y', style='plain') self.cc_axes.plot(cycles, meanCC, 'o', c='#2b8cbe', ls='-', lw=3) # Plot mean completeness and multiplicity mean_comp = info['total_completeness'] mean_mult = info['total_n_obs'] cycles = range(len(mean_comp)) self.comp_axes.clear() self.mult_axes.clear() self.comp_axes.set_xlim(0, total_cycles) self.comp_axes.set_ylim(0, 100) self.mult_axes.set_xlim(0, total_cycles) self.comp_axes.ticklabel_format(axis='y', style='plain') self.mult_axes.ticklabel_format(axis='y', style='plain') self.comp_axes.plot(cycles, mean_comp, c='#f03b20', ls='-', lw=2) comp = self.comp_axes.scatter(cycles, mean_comp, marker='o', s=25, edgecolors='black', color='#f03b20') self.mult_axes.plot(cycles, mean_mult, c='#feb24c', ls='-', lw=2) mult = self.mult_axes.scatter(cycles, mean_mult, marker='o', s=25, edgecolors='black', color='#feb24c') labels = ['Completeness', 'Multiplicity'] self.comp_axes.legend([comp, mult], labels, loc='upper right', fontsize=9, fancybox=True) # Binned bar plots x = info['binned_resolution'][-1] bins = np.arange(len(x)) xlabels = ["{:.2f}".format(i) for i in x] sel_bins = bins[0::len(bins) // 6] sel_xlabels = [xlabels[t] for t in sel_bins] # plot binned stats self.bcc_axes.clear() if self.pparams.target_anomalous_flag: binned_cc = [c * 100 for c in info['binned_cc12_anom'][-1]] else: binned_cc = [c * 100 for c in info['binned_cc12'][-1]] self.bcc_axes.bar(bins, binned_cc, color='#2b8cbe', alpha=0.5, width=1, lw=0) self.bcc_axes.step(bins, binned_cc, color='blue', where='mid') self.bcomp_axes.clear() self.bcomp_axes.bar(bins, info['binned_completeness'][-1], alpha=0.5, color='#f03b20', width=1, lw=0) self.bcomp_axes.step(bins, info['binned_completeness'][-1], color='red', where='mid') self.bmult_axes.clear() self.bmult_axes.bar(bins, info['binned_n_obs'][-1], alpha=0.5, color='#feb24c', width=1, lw=0) self.bmult_axes.step(bins, info['binned_n_obs'][-1], color='orange', where='mid') # Set x-axis tick labels self.bmult_axes.set_xticks(sel_bins) self.bmult_axes.set_xticklabels(sel_xlabels) self.draw_axes() # Rejection table txt = 'No. good frames: {}\n' \ 'No. bad CC frames: {}\n' \ 'No. bad G frames: {}\n' \ 'No. bad unit cell frames: {}\n' \ 'No. bad gamma_e frames: {}\n' \ 'No. bad SE frames: {}\n' \ 'No. observations: {}\n' \ ''.format(info['n_frames_good'][-1], info['n_frames_bad_cc'][-1], info['n_frames_bad_G'][-1], info['n_frames_bad_uc'][-1], info['n_frames_bad_gamma_e'][-1], info['n_frames_bad_SE'][-1], info['n_observations'][-1]) self.rej_table.clear() self.rej_table.axis('off') font = { 'family': 'monospace', 'color': 'darkblue', 'weight': 'normal', 'size': 11, 'linespacing': 2.5 } self.rej_table.text(0, 0.85, txt, fontdict=font, transform=self.rej_table.transAxes, va='top') # Redraw canvas self.canvas.draw()
class PlotWidget(QWidget): customizationTriggered = pyqtSignal() def __init__(self, name, plotFunction, plot_condition_function_list, plotContextFunction, parent=None): QWidget.__init__(self, parent) self._name = name self._plotFunction = plotFunction self._plotContextFunction = plotContextFunction self._plot_conditions = plot_condition_function_list """:type: list of functions """ self._figure = Figure() self._figure.set_tight_layout(True) self._canvas = FigureCanvas(self._figure) self._canvas.setParent(self) self._canvas.setFocusPolicy(Qt.StrongFocus) self._canvas.setFocus() vbox = QVBoxLayout() vbox.addWidget(self._canvas) self._toolbar = CustomNavigationToolbar(self._canvas, self) self._toolbar.customizationTriggered.connect( self.customizationTriggered) vbox.addWidget(self._toolbar) self.setLayout(vbox) self._dirty = True self._active = False self.resetPlot() def getFigure(self): """ :rtype: matplotlib.figure.Figure""" return self._figure def resetPlot(self): self._figure.clear() @property def name(self): """ @rtype: str """ return self._name def updatePlot(self): if self.isDirty() and self.isActive(): # print("Drawing: %s" % self._name) self.resetPlot() plot_context = self._plotContextFunction(self.getFigure()) try: self._plotFunction(plot_context) self._canvas.draw() except StandardError as e: exc_type, exc_value, exc_tb = sys.exc_info() sys.stderr.write("%s\n" % ("-" * 80)) traceback.print_tb(exc_tb) sys.stderr.write("Exception type: %s\n" % exc_type.__name__) sys.stderr.write("%s\n" % e.message) sys.stderr.write("%s\n" % ("-" * 80)) sys.stderr.write( "An error occurred during plotting. This stack trace is helpful for diagnosing the problem." ) self.setDirty(False) def setDirty(self, dirty=True): self._dirty = dirty def isDirty(self): return self._dirty def setActive(self, active=True): self._active = active def isActive(self): return self._active def canPlotKey(self, key): return any([ plotConditionFunction(key) for plotConditionFunction in self._plot_conditions ])
scaled_pic50 = params[0]**2 + pic50_lower_bound scaled_hill = params[1]**2 # Hill bounded below at 0 return [scaled_pic50, scaled_hill] def compute_best_sigma_analytic(sum_of_squares, num_data_pts): return np.sqrt((1.*sum_of_squares)/num_data_pts) width, height = 4, 3 fig = Figure(figsize=(width, height)) ax = fig.add_subplot(111) ax.set_ylim(0,100) ax.set_xscale("log") ax.set_xlim(10**-3, 10**3) ax.set_ylabel("% Block") ax.set_xlabel("Concentration") fig.set_tight_layout(True) class MyFirstGUI: def __init__(self, master): self.master = master master.title("PyHillGUI") self.text = tk.Text(master, height=16, width=10) self.text.grid(column=0) self.text.insert(tk.END, 'Data here') self.read_box_button = tk.Button(master, text="Fit and plot", command=self.read_box) self.read_box_button.grid(column=0, padx=4) self.close_button = tk.Button(master, text="Close", command=master.quit) self.close_button.grid()
class MRI_FID_Widget(MRI_FID_Widget_Base, MRI_FID_Widget_Form): def __init__(self, parent=None): super(MRI_FID_Widget, self).__init__(parent) self.setupUi(self) self.idle = True # state variable: True-stop, False-start self.seq_filename = 'sequence/basic/fid_default.txt' self.flipangleTool = FlipangleDialog(self) # connect basic GUI signals self.startButton.clicked.connect(self.start) self.stopButton.clicked.connect(self.stop) self.openFlipangletoolBtn.clicked.connect(self.open_flipangleDialog) self.acquireButton.clicked.connect(self.acquire) # setup frequency related GUI # don't emit valueChanged signal while typing self.freqValue.setKeyboardTracking(False) self.atValue.setKeyboardTracking(False) self.freqValue.valueChanged.connect(self.set_freq) self.atValue.valueChanged.connect(self.set_at) self.freqWindowCheckBox = QCheckBox() self.zoomCheckBox = QCheckBox('Zoom') self.zoomLayout.addWidget(self.zoomCheckBox) self.peakWindowCheckBox = QCheckBox('Peak Window') self.peakWindowLayout.addWidget(self.peakWindowCheckBox) self.center_freq = 0 self.applyFreqButton.clicked.connect(self.apply_center_freq) # Don't emit valueChanged signal while typing self.gradOffset_x.setKeyboardTracking(False) self.gradOffset_y.setKeyboardTracking(False) self.gradOffset_z.setKeyboardTracking(False) self.gradOffset_z2.setKeyboardTracking(False) self.gradOffset_x.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_x)) self.gradOffset_y.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_y)) self.gradOffset_z.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z)) self.gradOffset_z2.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z2)) self.saveShimButton.clicked.connect(self.save_shim) self.loadShimButton.clicked.connect(self.load_shim) self.zeroShimButton.clicked.connect(self.zero_shim) self.peak.setReadOnly(True) self.fwhm.setReadOnly(True) # disable GUI elements at first self.startButton.setEnabled(True) self.stopButton.setEnabled(False) self.applyFreqButton.setEnabled(False) self.gradOffset_x.setEnabled(False) self.gradOffset_y.setEnabled(False) self.gradOffset_z.setEnabled(False) self.gradOffset_z2.setEnabled(False) self.acquireButton.setEnabled(False) self.saveShimButton.setEnabled(False) self.loadShimButton.setEnabled(False) self.zeroShimButton.setEnabled(False) self.openFlipangletoolBtn.setEnabled(False) # setup buffer and offset for incoming data self.size = 50000 # total data received (defined by the server code) self.buffer = bytearray(8 * self.size) self.offset = 0 self.data = np.frombuffer(self.buffer, np.complex64) # Declare global Variables self.data_idx = [] self.mag_t = [] self.real_t = [] self.imag_t = [] self.time_axis = [] self.dclip = [] self.freqaxis = [] self.fft_mag = [] self.peak_value = 0 self.max_value = 0 self.fwhm_value = 0 self.noise_bound_low = 0 self.noise_bound_high = 0 self.snr_value = 0 self.center_freq = 0 # setup display self.figure = Figure() self.figure.set_facecolor('none') # top and bottom axes: 2 rows, 1 column self.axes_top = self.figure.add_subplot(2, 1, 1) self.axes_bottom = self.figure.add_subplot(2, 1, 2) self.axes_top.set_xlabel('frequency [Hz]') self.axes_top.set_ylabel('freq. domain') self.axes_bottom.set_xlabel('time [ms]') self.axes_bottom.set_ylabel('time domain') self.axes_top.grid() self.axes_bottom.grid() self.figure.set_tight_layout(True) self.canvas = FigureCanvas(self.figure) self.plotLayout.addWidget(self.canvas) def start(self): print("Starting MRI_FID_Widget") # send 1 as signal to start MRI_FID_Widget gsocket.write(struct.pack('<I', 1)) # enable/disable GUI elements self.startButton.setEnabled(False) self.stopButton.setEnabled(True) self.applyFreqButton.setEnabled(True) self.gradOffset_x.setEnabled(True) self.gradOffset_y.setEnabled(True) self.gradOffset_z.setEnabled(True) self.gradOffset_z2.setEnabled(True) self.acquireButton.setEnabled(True) self.saveShimButton.setEnabled(True) self.loadShimButton.setEnabled(True) self.zeroShimButton.setEnabled(True) self.openFlipangletoolBtn.setEnabled(True) # setup global socket for receive data gsocket.setReadBufferSize(8 * self.size) gsocket.readyRead.connect(self.read_data) # send the sequence to the backend ass = Assembler() seq_byte_array = ass.assemble(self.seq_filename) print(len(seq_byte_array)) gsocket.write(struct.pack('<I', len(seq_byte_array))) gsocket.write(seq_byte_array) self.load_shim() self.idle = False def stop(self): print("Stopping MRI_FID_Widget") # send 0 as signal to stop MRI_FID_Widget gsocket.write(struct.pack('<I', 0)) # enable/disable GUI elements self.startButton.setEnabled(True) self.stopButton.setEnabled(False) self.applyFreqButton.setEnabled(False) self.gradOffset_x.setEnabled(False) self.gradOffset_y.setEnabled(False) self.gradOffset_z.setEnabled(False) self.gradOffset_z2.setEnabled(False) self.acquireButton.setEnabled(False) self.saveShimButton.setEnabled(False) self.loadShimButton.setEnabled(False) self.zeroShimButton.setEnabled(False) self.openFlipangletoolBtn.setEnabled(False) # Disconnect global socket gsocket.readyRead.disconnect() self.idle = True def set_freq(self, freq): # Setting frequency without triggering aquisition: parameters.set_freq print("Setting frequency.") parameters.set_freq(freq) gsocket.write(struct.pack('<I', 1 << 28 | int(1.0e6 * freq))) # 2^28 = 268,435,456 for frequency setting if not self.idle: print("\tAcquiring data.") def apply_center_freq(self): # print(self.center_freq) if self.center_freq != 0: self.freqValue.setValue(self.center_freq) print("\tCenter frequency applied.") def open_flipangleDialog(self): self.flipangleTool.show() def acquire(self): gsocket.write(struct.pack('<I', 2 << 28 | 0 << 24)) print("\tAcquiring data.") def set_at(self, at): # Setting attenuation without triggering aquisition: parameters.set_at print("Setting attenuation.") # at = round(at/0.25)*4 parameters.set_at(at) gsocket.write(struct.pack('<I', 3 << 28 | int(at / 0.25))) if not self.idle: print("\tAquiring data.") def set_grad_offset(self, spinBox): if spinBox.objectName() == 'gradOffset_x': print("Setting grad offset x.") offsetX = self.gradOffset_x.value() # self.horizontalSlider_x.setValue(offsetX) if offsetX > 0: gsocket.write(struct.pack('<I', 2 << 28 | 1 << 24 | offsetX)) else: gsocket.write( struct.pack('<I', 2 << 28 | 1 << 24 | 1 << 20 | -offsetX)) print("\tAcquiring data.") elif spinBox.objectName() == 'gradOffset_y': print("Setting grad offset y.") offsetY = self.gradOffset_y.value() # self.horizontalSlider_y.setValue(offsetY) if offsetY > 0: gsocket.write(struct.pack('<I', 2 << 28 | 2 << 24 | offsetY)) else: gsocket.write( struct.pack('<I', 2 << 28 | 2 << 24 | 1 << 20 | -offsetY)) print("\tAcquiring data.") elif spinBox.objectName() == 'gradOffset_z': print("Setting grad offset z.") offsetZ = self.gradOffset_z.value() # self.horizontalSlider_z.setValue(offsetZ) if offsetZ > 0: gsocket.write(struct.pack('<I', 2 << 28 | 3 << 24 | offsetZ)) else: gsocket.write( struct.pack('<I', 2 << 28 | 3 << 24 | 1 << 20 | -offsetZ)) print("\tAcquiring data.") elif spinBox.objectName() == 'gradOffset_z2': print("Setting grad offset z2.") offsetZ2 = self.gradOffset_z2.value() if offsetZ2 > 0: gsocket.write(struct.pack('<I', 2 << 28 | 4 << 24 | offsetZ2)) else: gsocket.write( struct.pack('<I', 2 << 28 | 4 << 24 | 1 << 20 | -offsetZ2)) print("\tAcquiring data.") else: print('Error: set_grad_offset.') return def save_shim(self): parameters.set_grad_offset_x(self.gradOffset_x.value()) parameters.set_grad_offset_y(self.gradOffset_y.value()) parameters.set_grad_offset_z(self.gradOffset_z.value()) parameters.set_grad_offset_z2(self.gradOffset_z2.value()) def load_shim(self): print("Loading shim.") self.gradOffset_x.valueChanged.disconnect() self.gradOffset_y.valueChanged.disconnect() self.gradOffset_z.valueChanged.disconnect() self.gradOffset_z2.valueChanged.disconnect() self.gradOffset_x.setValue(parameters.get_grad_offset_x()) self.gradOffset_y.setValue(parameters.get_grad_offset_y()) self.gradOffset_z.setValue(parameters.get_grad_offset_z()) self.gradOffset_z2.setValue(parameters.get_grad_offset_z2()) self.gradOffset_x.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_x)) self.gradOffset_y.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_y)) self.gradOffset_z.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z)) self.gradOffset_z2.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z2)) offsetX = self.gradOffset_x.value() if offsetX > 0: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | offsetX)) else: gsocket.write( struct.pack('<I', 2 << 28 | 5 << 24 | 1 << 20 | -offsetX)) offsetY = self.gradOffset_y.value() if offsetY > 0: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | offsetY)) else: gsocket.write( struct.pack('<I', 2 << 28 | 5 << 24 | 1 << 20 | -offsetY)) offsetZ = self.gradOffset_z.value() if offsetZ > 0: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | offsetZ)) else: gsocket.write( struct.pack('<I', 2 << 28 | 5 << 24 | 1 << 20 | -offsetZ)) offsetZ2 = self.gradOffset_z2.value() if offsetZ2 > 0: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | offsetZ2)) else: gsocket.write( struct.pack('<I', 2 << 28 | 5 << 24 | 1 << 20 | -offsetZ2)) if self.idle: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | 0 << 20)) else: gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24 | 1 << 20)) print("\tAcquiring data.") def zero_shim(self): print("Zero shims.") self.gradOffset_x.valueChanged.disconnect() self.gradOffset_y.valueChanged.disconnect() self.gradOffset_z.valueChanged.disconnect() self.gradOffset_z2.valueChanged.disconnect() self.gradOffset_x.setValue(0) self.gradOffset_y.setValue(0) self.gradOffset_z.setValue(0) self.gradOffset_z2.setValue(0) self.gradOffset_x.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_x)) self.gradOffset_y.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_y)) self.gradOffset_z.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z)) self.gradOffset_z2.valueChanged.connect( lambda: self.set_grad_offset(self.gradOffset_z2)) gsocket.write(struct.pack('<I', 2 << 28 | 5 << 24)) print("\tAcquiring data.") def read_data(self): ''' size = gsocket.bytesAvailable() if size == self.size: self.buffer = gsocket.read(8*self.size) print(len(self.buffer)) print("Start processing readout.") self.process_readout() print("Start analyzing data.") self.analytics() print("Display data.") self.display_data() ''' # Test if buffer can be filled by one line (see code above) # print("Reading...") # wait for enough data and read to self.buffer size = gsocket.bytesAvailable() print(size) if size <= 0: return elif self.offset + size < 8 * self.size: self.buffer[self.offset:self.offset + size] = gsocket.read(size) self.offset += size # if the buffer is not complete, return and wait for more return else: print("Finished Readout.") self.buffer[self.offset:8 * self.size] = gsocket.read(8 * self.size - self.offset) self.offset = 0 # print("\tBuffer size: ", len(self.buffer)) print("Start processing readout.") self.process_readout() print("Start analyzing data.") self.analytics() print("Display data.") self.display_data() if self.flipangleTool.acqCount > 0 and self.flipangleTool.centeringFlag == True: print(time.ctime()) time.sleep(self.flipangleTool.acqTimeout) print(time.ctime()) self.flipangleTool.find_Ceter() elif self.flipangleTool.acqCount > 0 and self.flipangleTool.attenuationFlag == True: print(time.ctime()) time.sleep(self.flipangleTool.acqTimeout) print(time.ctime()) self.flipangleTool.find_At() def process_readout(self): # Get magnitude, real and imaginary part of data data = self.data mag = np.abs(data) real = np.real(data) imag = np.imag(data) time = 20 self.data_idx = int(time * 250) self.mag_t = mag[0:self.data_idx] self.real_t = real[0:self.data_idx] self.imag_t = imag[0:self.data_idx] self.time_axis = np.linspace(0, time, self.data_idx) self.dclip = data[0:self.data_idx] self.freqaxis = np.linspace(-125000, 125000, self.data_idx) # 5000 points ~ 20ms self.fft_mag = abs( np.fft.fftshift(np.fft.fft(np.fft.fftshift(self.dclip)))) print("\tReadout processed.") def analytics(self): self.peak_value = round(np.max(self.fft_mag), 2) self.peak.setText(str(self.peak_value)) # Calculate fwhm max_value = np.max(self.fft_mag) self.max_index = np.argmax(self.fft_mag) bound_high = self.max_index bound_low = self.max_index while 1: if self.fft_mag[bound_low] < 0.5 * max_value: break bound_low = bound_low - 1 while 1: if self.fft_mag[bound_high] < 0.5 * max_value: break bound_high = bound_high + 1 self.fwhm_value = bound_high - bound_low freq_span = abs(np.min(self.freqaxis)) + abs(np.max(self.freqaxis)) self.fwhm.setText( str(round(self.fwhm_value * freq_span / self.data_idx)) + " Hz") # Calculate the SNR value inside a peak window peak_window = self.fwhm_value * 5 self.noise_bound_low = int(self.max_index - peak_window / 2) self.noise_bound_high = int(self.max_index + peak_window / 2) # Join noise outside peak window, calculate std. dev. and snr = peak/std.dev. noise = np.concatenate((self.fft_mag[0:self.noise_bound_low], self.fft_mag[self.noise_bound_high:])) self.snr_value = round(self.peak_value / np.std(noise), 2) # print("snr_value: ", snr_value) self.snr.setText(str(self.snr_value)) # Calculate center frequency self.center_freq = parameters.get_freq() + ( (self.max_index - 5000 / 2) * 250000 / 5000) / 1.0e6 # 250000 sampling rate, 5000 number of samples for FFT self.centerFreq.setText(str(round(self.center_freq, 5))) print("\tData analysed.") def display_data(self): # Clear the plots: bottom-time domain, top-frequency domain self.axes_bottom.clear() self.axes_top.clear() self.axes_top.set_xlabel('frequency [Hz]') self.axes_top.set_ylabel('freq. domain') self.axes_bottom.set_xlabel('time [ms]') self.axes_bottom.set_ylabel('time domain') self.axes_top.grid() self.axes_bottom.grid() self.figure.set_tight_layout(True) # Plot the bottom (time domain): display time signal from 0~21ms [0~5250] self.curve_bottom = self.axes_bottom.plot(self.time_axis, self.mag_t, linewidth=1) # blue self.curve_bottom = self.axes_bottom.plot(self.time_axis, self.real_t, linewidth=1) # red self.curve_bottom = self.axes_bottom.plot(self.time_axis, self.imag_t, linewidth=1) # green self.axes_bottom.set_xlabel('time [ms]') # Plot the top (frequency domain): use signal from 0.5~20.5ms: first 0.5ms junk # update: the junk is already taken care of by the sequence timing if not self.zoomCheckBox.isChecked(): # non zoomed self.curve_top = self.axes_top.plot( self.freqaxis[int(self.data_idx / 2 - self.data_idx / 10):int(self.data_idx / 2 + self.data_idx / 10)], self.fft_mag[int(self.data_idx / 2 - self.data_idx / 10):int(self.data_idx / 2 + self.data_idx / 10)], linewidth=1) else: # zoomed self.curve_top = self.axes_top.plot( self.freqaxis[int(self.data_idx / 2 - self.data_idx / 100):int(self.data_idx / 2 + self.data_idx / 100)], self.fft_mag[int(self.data_idx / 2 - self.data_idx / 100):int(self.data_idx / 2 + self.data_idx / 100)], linewidth=1) self.axes_top.set_xlabel('frequency [Hz]') # Hightlight the peak window if self.peakWindowCheckBox.isChecked(): print("\tPeak window checked.") if int(self.noise_bound_low) >= int( self.data_idx / 2 - self.data_idx / 10) and int( self.noise_bound_high) <= int(self.data_idx / 2 + self.data_idx / 10): print("\tPeak inside the view.") self.curve_top = self.axes_top.plot( self.freqaxis[self.noise_bound_low:self.noise_bound_high], self.fft_mag[self.noise_bound_low:self.noise_bound_high], linewidth=1, linestyle="--") elif self.max_index < int(self.data_idx / 2 - self.data_idx / 10): print("\tPeak outside the view.") self.axes_top.text(self.freqaxis[int(self.data_idx / 2 - self.data_idx / 10)], 0.001, "<", fontsize=20) elif self.max_index > int(self.data_idx / 2 + self.data_idx / 10): print("\tPeak outside the view.") self.axes_top.text(self.freqaxis[int(self.data_idx / 2 + self.data_idx / 10)], 0.001, ">", fontsize=20) # Update the figure self.canvas.draw() print("\tData plot updated.")
def __init__(self, filename, image, parent=None): super(QualityWidget, self).__init__(parent) x = np.arange(1, 101) y = loss_curve(image) tail = 5 qm = np.argmin(y[:-tail]) + 1 if qm == 100 - tail: qm = 100 figure = Figure() canvas = FigureCanvas(figure) axes = canvas.figure.subplots() axes.plot(x, y * 100, label="compression loss") axes.fill_between(x, y * 100, alpha=0.2) axes.axvline(qm, linestyle=":", color="k", label=f"min error (q = {qm})") xt = axes.get_xticks() xt = np.append(xt, 1) axes.set_xticks(xt) axes.set_xlim([1, 100]) axes.set_ylim([0, 100]) axes.set_xlabel(self.tr("JPEG quality (%)")) axes.set_ylabel(self.tr("average error (%)")) axes.grid(True, which="both") axes.legend(loc="upper center") axes.figure.canvas.draw() figure.set_tight_layout(True) main_layout = QVBoxLayout() main_layout.addWidget(canvas) MRK = b"\xFF" SOI = b"\xD8" DQT = b"\xDB" # DHT = b'\xC4' MSK = b"\x0F" PAD = b"\x00" MAX_TABLES = 2 LEN_OFFSET = 2 LUMA_IDX = 0 CHROMA_IDX = 1 luma = np.zeros((DCT_SIZE, DCT_SIZE), dtype=int) chroma = np.zeros((DCT_SIZE, DCT_SIZE), dtype=int) temp_file = QTemporaryFile() try: if temp_file.open(): copyfile(filename, temp_file.fileName()) subprocess.run( [ exiftool_exe(), "-all=", "-overwrite_original", temp_file.fileName() ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) found = False with open(temp_file.fileName(), "rb") as file: first = file.read(1) if first not in [MRK, SOI]: raise ValueError(self.tr("File is not a JPEG image!")) while True: if not self.find_next(file, [MRK, DQT, PAD]): break length = file.read(1)[0] - LEN_OFFSET if length <= 0 or length % (TABLE_SIZE + 1) != 0: continue while length > 0: mode = file.read(1) if not mode: break index = mode[0] & MSK[0] if index >= MAX_TABLES: break length -= 1 for k in range(TABLE_SIZE): b = file.read(1)[0] if not b: break length -= 1 i, j = ZIG_ZAG[k] if index == LUMA_IDX: luma[i, j] = b elif index == CHROMA_IDX: chroma[i, j] = b else: found = True if not found: raise ValueError(self.tr("Unable to find JPEG tables!")) levels = [(1 - (np.mean(t.ravel()[1:]) - 1) / 254) * 100 for t in [luma, chroma]] distance = np.zeros(101) for qm in range(101): lu, ch = cv.split(get_tables(qm)) lu_diff = np.mean(cv.absdiff(luma, lu)) ch_diff = np.mean(cv.absdiff(chroma, ch)) distance[qm] = (lu_diff + 2 * ch_diff) / 3 closest = np.argmin(distance) deviation = distance[closest] if deviation == 0: quality = closest message = "(standard tables)" else: quality = int(np.round(closest - deviation)) message = f"(deviation from standard tables = {deviation:.4f})" if quality == 0: quality = 1 quality_label = QLabel( self.tr( f"[JPEG FORMAT] Last saved quality: {quality}% {message}")) modify_font(quality_label, bold=True) luma_label = QLabel( self. tr(f"Luminance Quantization Table (level = {levels[0]:.2f}%)\n" )) luma_label.setAlignment(Qt.AlignCenter) modify_font(luma_label, underline=True) luma_table = self.create_table(luma) luma_table.setFixedSize(420, 190) chroma_label = QLabel( self. tr(f"Chrominance Quantization Table (level = {levels[1]:.2f}%)\n" )) chroma_label.setAlignment(Qt.AlignCenter) modify_font(chroma_label, underline=True) chroma_table = self.create_table(chroma) chroma_table.setFixedSize(420, 190) table_layout = QGridLayout() table_layout.addWidget(luma_label, 0, 0) table_layout.addWidget(luma_table, 1, 0) table_layout.addWidget(chroma_label, 0, 1) table_layout.addWidget(chroma_table, 1, 1) table_layout.addWidget(quality_label, 2, 0, 1, 2) main_layout.addLayout(table_layout) except ValueError: modelfile = "models/jpeg_qf.mdl" try: model = load(modelfile) limit = model.best_ntree_limit if hasattr( model, "best_ntree_limit") else None # f = self.get_features(image) # p = model.predict_proba(f, ntree_limit=limit)[0, 0] qp = model.predict(np.reshape(y, (1, len(y))), ntree_limit=limit)[0] # if p > 0.5: # p = 2 * (p - 0.5) * 100 # output = self.tr('Uncompressed image (p = {:.2f}%)'.format(p)) # else: # p = (1 - 2 * p) * 100 # output = self.tr('Compressed image (p = {:.2f}%) ---> Estimated JPEG quality = {}%'.format(p, qm)) message = self.tr( f"[LOSSLESS FORMAT] Estimated last saved quality = {qp:.1f}%{'' if qp <= 99 else ' (uncompressed)'}" ) if qp == 100: message += " (uncompressed)" prob_label = QLabel(message) modify_font(prob_label, bold=True) main_layout.addWidget(prob_label) except FileNotFoundError: QMessageBox.critical( self, self.tr("Error"), self.tr(f'Model not found ("{modelfile}")!')) main_layout.addStretch() self.setLayout(main_layout)
class Principal(Frame): # Class for helping Tooltips class ToolTip(object): def __init__(self, widget): self.widget = widget self.tipwindow = None self.id = None self.x = self.y = 0 def showtip(self, text, lang): self.text = text if self.tipwindow or not self.text: return x, y, cx, cy = self.widget.bbox("insert") x = x + self.widget.winfo_rootx() + 27 y = y + cy + self.widget.winfo_rooty() +27 self.tipwindow = tw = Toplevel(self.widget) tw.wm_overrideredirect(1) tw.wm_geometry("+%d+%d" % (x, y)) try: # For Mac OS tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w, "help", "noActivates") except TclError: pass label = Label(tw, text=self.text[lang], justify=LEFT, background="#ffffe0", relief=SOLID, borderwidth=1, font=("tahoma", "8", "normal")) label.pack(ipadx=1) def hidetip(self): tw = self.tipwindow self.tipwindow = None if tw: tw.destroy() # Initialization function def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent # Spiral parameters (defined as StringVars for convenience) self.a = StringVar() self.a.set('0') self.b = StringVar() self.b.set('0.5') self.c = StringVar() self.c.set('1') self.lMax = StringVar() self.lMax.set(158) self.frec = StringVar() self.frec.set(500) self.StringLongitud = StringVar() self.StringRadio = StringVar() # Help mode flag self.ayuda = False # Figure object self.f = Figure(figsize=(5,5)) self.initUI() # Tooltip creator function (allowed by help mode flag) def createToolTip(self, widget, text): toolTip = self.ToolTip(widget) def enter(event): if self.ayuda: toolTip.showtip(text, self.lang) def leave(event): toolTip.hidetip() widget.bind('<Enter>', enter) widget.bind('<Leave>', leave) # Euclidean distance calculator function def distancia( self, r1, phi1, r2, phi2 ): return sqrt(r1**2 + r2**2 - 2*r1*r2*cos(phi1-phi2)) # Polar to Cartesian coordinates def pol2cart(self, rho, phi): x = rho*cos(phi) y = rho*sin(phi) return(x, y) # def grafico(self): # Set figure size self.f = Figure(figsize=(5,5)) # Check whether negative parameters are present and show an error if float(self.c.get()) < 0 or float(self.b.get()) < 0: print self.lang tkMessageBox.showerror("Error", self.error.get()) return # Set figure axis ax = self.f.add_subplot(111, polar=True) # Initialize r and theta lists at the center point self.theta_disc = [0] self.r_disc = [0] self.theta_disc_n = [0] # Initialize length value and list l = 0 l_vec = [] # Loop limited by antenna length while l < int(self.lMax.get()): # Length of each antenna segment in cm, computed as 1/15 of the wave length lseg = 300/float(self.frec.get())*100/15 if self.tipoCurva.get() == 1: # Archimedean spiral # New theta values are calculated according to the following: # In an Archimedean spiral # /r - a\ c # theta = |-------| # \ b / # In order to get an approximately equally spaced segments, new theta values are computed according to the next formula. This formula has been worked # out gradually, not basing on any well-known expression. # /0.5 * lseg - a\ c # |----------------| - lseg # \ b / # ------------------------------ + lseg # 10 * theta + 1 # n # theta = theta + --------------------------------------- # n + 1 n r + 1 # n self.theta_disc.append(self.theta_disc[-1] + \ (( ((0.5*lseg - float(self.a.get()))/float(self.b.get()))**(float(self.c.get())) - lseg) / (10*self.theta_disc[-1] + 1) + lseg) \ / (self.r_disc[-1] + 1)) # print str(lseg) # print str(self.r_disc[-1]) else: # print "Eh: " + str(self.theta_disc[-1]) # print "Ra: " + str(self.r_disc[-1]) # print "Ls: " + str(lseg) # print "Ot: " + str(log(0.5*lseg/float(self.a.get()))/float(self.b.get())) self.theta_disc.append(self.theta_disc[-1] + \ (( max(log(0.5*lseg/float(self.a.get()))/float(self.b.get()),float(self.a.get())) - lseg) * exp(-1*self.theta_disc[-1]) + lseg) \ / (self.r_disc[-1] + 1)) #print str(lseg) #print str(self.r_disc[-1]) if self.tipoCurva.get() == 1: self.r_disc.append(float(self.b.get())*self.theta_disc[-1]**(1/float(self.c.get())) + float(self.a.get())) elif self.tipoCurva.get() == 2: self.r_disc.append(float(self.a.get())*exp(float(self.b.get())*self.theta_disc[-1])) self.theta_disc_n.append(pi + self.theta_disc[-1]) l_vec.append(self.distancia(self.r_disc[-1],self.theta_disc[-1],self.r_disc[-2],self.theta_disc[-2])) l += l_vec[-1] if self.fuente.get() and str(self.checkFuente.cget('state')) == 'normal': self.theta_disc.remove(0) self.r_disc.remove(0) self.theta_disc_n.remove(0) ax.plot([self.theta_disc[0],self.theta_disc_n[0]], [self.r_disc[0], self.r_disc[0]], color='r') ax.plot([0],[0], color='m', marker='o', markersize=5) self.StringLongitud.set("%#.1f cm" % l) self.StringRadio.set("%#.1f cm" % max(self.r_disc)) ax.plot(self.theta_disc, self.r_disc, color='b', marker='.', markersize=4) if self.espejar.get(): ax.plot(self.theta_disc_n, self.r_disc, color='g', marker='.', markersize=4) ax.set_rmax(max(self.r_disc)) ax.grid(True) #with open('distancias.csv', 'wb') as f: # writer = csv.writer(f) # writer.writerows(izip(self.theta_disc, l_vec)) def regraficar(self): self.grafico() self.canvas.get_tk_widget().pack_forget() self.canvas = FigureCanvasTkAgg(self.f, master=self.frame2) #canvas.show() self.canvas.get_tk_widget().pack(side=TOP,fill=BOTH, expand=1, padx=10, pady=10) def cambiaFormula(self): curvas = ['''R0lGODlhbAAWAOMPAAwMDLa2thYWFiIiIlBQUJ6enubm5gQEBGJiYszMzEBAQDAwMHR0dIqKigAAAP///yH5BAEKAA8ALAAAAABsABYAAAT+8MlJq7046817ToYnjmRpXsqpriw3JGgrz2pDHHDFFHTvezjL4kcsWoKUBIHCQDQQxmhy4EhZDATGkoKcEHIPwjIBkJoljsZVEFIwuGDJUJJwhM7ngN0i4D0YcxJdDwVqEg0CeC0DHQhlOokSCJGCcVYSAYyHiiaaGwOXEwCGDwqRBQgOC28PBqEPCAgMDDANgH8MCnEzAQSxCFufHQ6xuSF6FACeFgwBG1AHCwYGaSgC19jZAssViHQOrMIbelsIQwoHCuoLDsFCGwUgDn67LXVgDvUX3BeOEw0OHgCAcmgeBgME4QUssoBSgQMe+Am5lOqBQQkKHq0gIHEGMS9yHU1lO6CN34FwDamBOZBQhYCWGERqyyaxjp8HLyNuoOYMDYI6//awcNDzh0oJ1HiEy9CRwsIHDSBanCBg6YkCT4kA8EPAToGiTDkIgGEAQM8XsAKtuGUkgRsoYqxiaDrBbS4wbmNx2iuBLt+/HNQCfhABADs=''', '''R0lGODlhQwASAOMPAAwMDLa2thYWFiIiIlBQUJ6enubm5gQEBGJiYszMzEBAQDAwMHR0dIqKigAAAP///yH5BAEKAA8ALAAAAABDABIAAATn8MlJq70463ZSJQyhjWSpGUe1BM/imXCcNQvVDNLQyHz/KAOGgiXYPQAsn7IEKDwKg4SDgCA4DMtsBiVpCAqALk5LrhRqPwIt5yy7H4GaAWBIKJ7391uBULyoIhMNDDUMQi9uAVQIVRQJCAyMMAgPBwsGBg5GFAoCnp+gAmMXXhJSDBOEE3kkBQmZbYhkUogOLwEHWHCBJgUOehMLAhMFKTlBkG0wBKN6DpQSzBMOqD4C0BmdoaHNE1LK1xKwSg5Jepkv46gOyk+yGr7AE03RVwUsCrwF1SWq8g92Ij0gAGIClUjmSEQAADs=''', ''''''] formula = PhotoImage(data=curvas[self.tipoCurva.get()-1]) self.formulaLabel.configure(image=formula) self.formulaLabel.image = formula if self.tipoCurva.get() == 1: self.parC.config(state=NORMAL) self.labelC.config(state=NORMAL) else: self.parC.config(state=DISABLED) self.labelC.config(state=DISABLED) def activarFuente(self): if self.espejar.get(): self.checkFuente.config(state=NORMAL) else: self.checkFuente.config(state=DISABLED) def escribirFichero(self): tipoCurva = ['Arq', 'Log'] c = [self.c.get() + ' ', ''] self.file_opt = options = {} options['defaultextension'] = '.nec' options['filetypes'] = [('NEC2 files', '.nec'),('all files', '.*')] options['initialdir'] = '~/Documentos/Antenas/Espirales' options['initialfile'] = 'Spiral ' + tipoCurva[int(self.tipoCurva.get())-1] + ' ' + \ self.a.get() + ' ' + self.b.get() + ' ' + c[int(self.tipoCurva.get())-1] + self.lMax.get() + ' ' + self.frec.get() + '.nec' options['parent'] = self.parent options['title'] = 'Save NEC' fich = tkFileDialog.asksaveasfile(mode='w', **self.file_opt) r_final = list(reversed(self.r_disc)) + self.r_disc theta_final = list(reversed(self.theta_disc_n)) + self.theta_disc x_ant, y_ant = self.pol2cart(r_final[0], theta_final[0]) tipoCurvaExt = ['Archimedean', 'Logarithmic'] fich.write('CM Created with PySAD\n') fich.write('CM L = %#.1f\n' % float(self.lMax.get())) fich.write('CM ' + tipoCurvaExt[int(self.tipoCurva.get())-1] + ' spiral') fich.write('CM a = ' + self.a.get()) fich.write('CM b = ' + self.b.get()) if int(self.tipoCurva.get()) == 0 : fich.write('CM c = ' + self.c.get()) fich.write('CE\n') print len(r_final) for i in range(len(r_final)-1): x, y = self.pol2cart(r_final[i+1], theta_final[i+1]) linea = 'GW\t%#d\t%#d\t%#.5f\t%#.5f\t%#.5f\t%#.5f\t%#.5f\t%#.5f\t%#.5f\n' % (i+1,1,x_ant/100,y_ant/100,0,x/100,y/100,0,0.001) fich.write(linea) x_ant, y_ant = x, y fich.write('GE\t0\nGN\t-1\nEK\n') fich.write('EX\t%#d\t%#d\t%#d\t%#d\t%#d\t%#d\n' % (0,len(r_final)/2,1,0,1,0)) fich.write('FR\t0\t0\t0\t0\t299.8\t0\nEN') fich.close() def escribirPDF(self): tipoCurva = ['Arq', 'Log'] c = [self.c.get() + ' ', ''] self.file_opt = options = {} options['defaultextension'] = '.pdf' options['filetypes'] = [('PDF files', '.pdf'),('all files', '.*')] options['initialdir'] = '~' options['initialfile'] = 'Spiral ' + tipoCurva[int(self.tipoCurva.get())-1] + ' ' + \ self.a.get() + ' ' + self.b.get() + ' ' + c[int(self.tipoCurva.get())-1] + self.lMax.get() + ' ' + self.frec.get() + '.nec' options['parent'] = self.parent options['title'] = 'Save PDF' fich = tkFileDialog.asksaveasfile(mode='w', **self.file_opt) #self.f.axis('off') matplotlib.rcParams.update({'font.size': 1}) self.f.gca().axes.get_xaxis().set_visible(False) self.f.gca().axes.get_yaxis().set_visible(False) papeles_w = [21, 29.7, 42, 59.4, 84.1] papeles_h = [29.7, 42, 59.4, 84.1, 118.9] for i_pap in range(0, len(papeles_w)-1): if 2*max(self.r_disc) < papeles_w[i_pap]: break print i_pap self.f.set_size_inches(papeles_w[i_pap]/2.54, papeles_h[i_pap]/2.54) noMargen = dict(pad=72*(papeles_w[i_pap] - 2*max(self.r_disc))/2/2.54, h_pad=0, w_pad=0) self.f.set_tight_layout(noMargen) self.f.suptitle('test title') self.f.savefig(fich, format='pdf', dpi='90') fich.close() def mostrarAyuda(self): self.ayuda = not self.ayuda if self.ayuda: self.helpButton.state(["pressed"]) self.config(cursor="question_arrow") else: self.helpButton.state(["!pressed"]) self.config(cursor="") def initText(self): self.curTip = StringVar() self.ArcSpi = StringVar() self.LogSpi = StringVar() self.aaa = StringVar() self.bbb = StringVar() self.ccc = StringVar() self.Lma = StringVar() self.fre = StringVar() self.Mir = StringVar() self.Sou = StringVar() self.Gen = StringVar() self.lenlen = StringVar() self.radrad = StringVar() self.error = StringVar() def updateText(self, lang): self.lang = lang if lang == 0: self.espButton.state(["pressed"]) self.engButton.state(["!pressed"]) else: self.engButton.state(["pressed"]) self.espButton.state(["!pressed"]) self.stringText = {'curTip': ["Tipo de curva", "Curve type"],'ArcSpi': ["Espiral de Arquímedes", "Archimedean spiral "],'LogSpi': ["Espiral logarítmica", "Logarithmic spiral"],'aaa': ["a (cm)", "a (cm)"],'bbb': ["b (cm/rad)", "b (cm/rad)"],'ccc': ["c", "c"],'Lma': ["Lmax (cm)", "Lmax (cm)"],'fre': ["frec (MHz)", "freq (MHz)"],'LmaToo': ["Longitud máxima de cada brazo de la antena", "Maximum length of each antenna's branch"],'FreToo': ["Frecuencia de diseño (aumentar para disminuir la longitud de los segmentos)", "Design frequency (increase for decreasing segment length)"],'Mir': ["Espejar", "Mirror"],'MirToo': ["Crea otra rama de la espiral girada 180º", "Create another spiral branch, twisted 180º"],'Sou': ["Colocar fuente", "Put source"],'SouToo': ["Une las dos mitades y crea una fuente NEC2 en el centro", "Join both halves and create a NEC2 source at the middle"],'Gen': ["Generar", "Generate"],'GenToo': ["Genera la espiral con los parámetros indicados", "Generate spiral following given parameters"],'NEC': ["NEC", "NEC"],'NECToo': ["Guarda la espiral como archivo NEC2", "Save the spiral as a NEC2 file"],'PDF': ["PDF", "PDF"],'PDFToo': ["Imprime la espiral a tamaño real en un documento PDF (máximo A0)", "Print the real sized spiral into a PDF document (A0 maximum)"],'HHH': ["H", "H"], 'lenlen': ["Longitud:", "Length:"], 'radrad': ["Radio:", "Radius:"], 'error': ["No se permiten valores negativos de b o c", "Negative values of b or c are not allowed"]} self.curTip.set(self.stringText['curTip'][self.lang]) self.ArcSpi.set(self.stringText['ArcSpi'][self.lang]) self.LogSpi.set(self.stringText['LogSpi'][self.lang]) self.aaa.set(self.stringText['aaa'][self.lang]) self.bbb.set(self.stringText['bbb'][self.lang]) self.ccc.set(self.stringText['ccc'][self.lang]) self.Lma.set(self.stringText['Lma'][self.lang]) self.fre.set(self.stringText['fre'][self.lang]) self.Mir.set(self.stringText['Mir'][self.lang]) self.Sou.set(self.stringText['Sou'][self.lang]) self.Gen.set(self.stringText['Gen'][self.lang]) self.lenlen.set(self.stringText['lenlen'][self.lang]) self.radrad.set(self.stringText['radrad'][self.lang]) self.error.set(self.stringText['error'][self.lang]) def initUI(self): self.initText() self.parent.title("PySAD") self.style = Style() self.style.theme_use("clam") self.pack(fill=BOTH, expand=1) barraLateral = Frame(self, borderwidth=1) barraLateral.pack(side=RIGHT, padx=5, pady=5) idiomaFrame = Frame(barraLateral, relief=FLAT) idiomaFrame.pack(side=TOP) self.espButton = Button(idiomaFrame, text="es", width=0, command=lambda: self.updateText(0)) self.espButton.grid(row=0, column=0) self.engButton = Button(idiomaFrame, text="en", width=0, command=lambda: self.updateText(1)) self.engButton.grid(row=0, column=1) self.updateText(0) editarFrame = Frame(barraLateral, relief=RAISED, borderwidth=1, width=1000) editarFrame.pack(fill=BOTH, expand=1, side=TOP, padx=5, pady=5) self.tipoCurva = IntVar() tituloSelector = Label(editarFrame, textvariable=self.curTip) tituloSelector.grid(row=0,columnspan=2, padx=2, pady=4) Radiobutton(editarFrame, textvariable=self.ArcSpi, variable=self.tipoCurva , value=1, command=self.cambiaFormula, width=17).grid(row=1,columnspan=2,sticky=W,padx=4) Radiobutton(editarFrame, textvariable=self.LogSpi, variable=self.tipoCurva , value=2, command=self.cambiaFormula).grid(row=2,columnspan=2,sticky=W,padx=4) self.formulaLabel = Label(editarFrame) self.formulaLabel.grid(row=4, columnspan=2, pady=4) Label(editarFrame,textvariable=self.aaa).grid(row=5, column=0,pady=2) Label(editarFrame,textvariable=self.bbb).grid(row=6, column=0,pady=2) self.labelC = Label(editarFrame,textvariable=self.ccc) self.labelC.grid(row=7, column=0,pady=2) self.labelC.config(state=DISABLED) Label(editarFrame,textvariable=self.Lma).grid(row=8, column=0,pady=2) Label(editarFrame,textvariable=self.fre).grid(row=9, column=0,pady=2) parA = Entry(editarFrame,width=4,textvariable=self.a) parA.grid(row=5, column=1, sticky=W) parB = Entry(editarFrame,width=4,textvariable=self.b) parB.grid(row=6, column=1, sticky=W) self.parC = Entry(editarFrame,width=4,textvariable=self.c) self.parC.grid(row=7, column=1, sticky=W) self.parC.config(state=DISABLED) lMax = Entry(editarFrame,width=4,textvariable=self.lMax) lMax.grid(row=8, column=1, sticky=W) self.createToolTip(lMax, self.stringText['LmaToo']) frec = Entry(editarFrame,width=4,textvariable=self.frec) frec.grid(row=9, column=1, sticky=W) self.createToolTip(frec, self.stringText['FreToo']) self.espejar = IntVar() checkEspejar = Checkbutton(editarFrame, textvariable=self.Mir, variable=self.espejar, command=self.activarFuente) checkEspejar.grid(row=10, columnspan=2, pady=2, sticky=W, padx=4) self.createToolTip(checkEspejar, self.stringText['MirToo']) self.fuente = IntVar() self.checkFuente = Checkbutton(editarFrame, textvariable=self.Sou, state=DISABLED, variable=self.fuente) self.checkFuente.grid(row=11, columnspan=2, pady=2, sticky=W, padx=4) self.createToolTip(self.checkFuente, self.stringText['SouToo']) okButton = Button(editarFrame, textvariable=self.Gen, command=self.regraficar) okButton.grid(row=12, columnspan=2, pady=5) self.createToolTip(okButton, self.stringText['GenToo']) self.frame2 = Frame(self, borderwidth=1) self.frame2.pack(fill=BOTH, expand=1, side=LEFT, padx=5, pady=5) self.canvas = FigureCanvasTkAgg(self.f, master=self.frame2) self.canvas.get_tk_widget().pack(side=TOP,fill=BOTH, expand=1, padx=10, pady=10) frameGuardar = Frame(barraLateral, relief=FLAT, borderwidth=1) frameGuardar.pack(fill=BOTH, expand=1, side=BOTTOM, padx=5, pady=5) icGuardar = PhotoImage(data='''R0lGODlhEAAQAIABADMzM////yH5BAEKAAEALAAAAAAQABAAAAIlDI55wchvQJQOxontUktTbkHcSJZkGCao161N5U5SLNM1vZlOAQA7''') saveButtonNEC = Button(frameGuardar, text=self.stringText['NEC'][0], image=icGuardar, compound=LEFT, command=self.escribirFichero, width=3) saveButtonNEC.image = icGuardar saveButtonNEC.grid(row=0, column=0, pady=2, padx=2, sticky=W) self.createToolTip(saveButtonNEC, self.stringText['NECToo']) saveButtonPDF = Button(frameGuardar, text=self.stringText['PDF'][0], image=icGuardar, compound=LEFT, command=self.escribirPDF, width=3) saveButtonPDF.image = icGuardar saveButtonPDF.grid(row=0, column=2, pady=2, padx=2, sticky=E) self.createToolTip(saveButtonPDF, self.stringText['PDFToo']) self.helpButton = Button(frameGuardar, text="?", command=self.mostrarAyuda, width=2) self.helpButton.grid(row=0, column=3, pady=2, padx=2, sticky=E) frame3 = Frame(barraLateral, relief=RAISED, borderwidth=1) frame3.pack(fill=BOTH, expand=1, side=BOTTOM, padx=5, pady=5) Label(frame3,textvariable=self.lenlen).grid(row=1, column=0,pady=4,padx=12) Label(frame3,textvariable=self.radrad).grid(row=2, column=0,pady=4,padx=12) Label(frame3,textvariable=self.StringLongitud).grid(row=1, column=1,pady=4) Label(frame3,textvariable=self.StringRadio).grid(row=2, column=1,pady=4) def onExit(self): self.quit()