Exemple #1
0
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)
Exemple #2
0
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])
Exemple #3
0
    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)
Exemple #4
0
    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()
Exemple #5
0
    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)
Exemple #6
0
    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)
Exemple #7
0
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()
Exemple #8
0
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()
Exemple #9
0
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)
Exemple #10
0
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()
Exemple #11
0
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
Exemple #12
0
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()
Exemple #15
0
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
Exemple #16
0
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()
Exemple #17
0
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()
Exemple #19
0
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
Exemple #20
0
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()
Exemple #21
0
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()
Exemple #22
0
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()
Exemple #23
0
    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)
Exemple #24
0
 def _figure_default(self):
     figure = Figure(facecolor='white')
     figure.set_tight_layout(self.tight_layout)
     return figure
Exemple #25
0
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()
Exemple #26
0
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()
Exemple #27
0
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()
Exemple #28
0
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
Exemple #29
0
    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_())
Exemple #31
0
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)
Exemple #32
0
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)
Exemple #35
0
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)
Exemple #36
0
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
Exemple #37
0
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
Exemple #38
0
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
Exemple #39
0
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
Exemple #40
0
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()
Exemple #41
0
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)
Exemple #43
0
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()
Exemple #44
0
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()
Exemple #45
0
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()
Exemple #47
0
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()
Exemple #49
0
 def _reference_figure_default(self):
     figure = Figure(facecolor='white')
     figure.set_tight_layout(True)
     return figure
Exemple #50
0
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()
Exemple #51
0
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)
Exemple #53
0
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()
Exemple #54
0
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])
Exemple #55
0
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()
Exemple #56
0
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
        ])
Exemple #57
0
    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()
Exemple #58
0
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.")
Exemple #59
0
    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)
Exemple #60
0
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()