示例#1
0
class wxMatplotPanelSimple(wx.Panel):
    def __init__(self, renderPanel, color=None, dpi=None, **kwargs):
        from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
        from matplotlib.figure import Figure
        # initialize Panel
        if 'id' not in list(kwargs.keys()):
            kwargs['id'] = wx.ID_ANY
        if 'style' not in list(kwargs.keys()):
            kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE
        wx.Panel.__init__(self, renderPanel, **kwargs)

        self.figure = Figure(None, dpi)
        #self.canvas = NoRepaintCanvas( self, -1, self.figure )

        self.canvas = FigureCanvasWxAgg(self, -1, self.figure)
        sizer = wx.BoxSizer()
        sizer.Add(self.canvas, 1, wx.EXPAND)
        self.SetSizer(sizer)
        self.axes = self.figure.add_subplot(111)
        self.axes.set_aspect('auto')
        self.Bind(wx.EVT_SIZE, self._onSize)

    def _onSize(self, event=None):
        pixels = tuple([self.GetSize()[0], self.GetSize()[1]])
        print(pixels)

        # if self.canvas.GetMinSize(  )[0] != pixels[0] or \
        # self.canvas.GetMinSize(  )[1] != pixels[1] :
        self.canvas.SetMinSize(pixels)

        self.figure.set_size_inches(
            float(pixels[0]) / self.figure.get_dpi(),
            float(pixels[1]) / self.figure.get_dpi())
        self.canvas.draw()
示例#2
0
    def drawPlot(self, figureArray):
        for indx in reversed(range(len(self.plotBox.GetChildren()))):
            self.plotBox.GetItem(indx).DeleteWindows()
            self.plotBox.Remove(indx)

        for fig in figureArray:
            plot = FigureCanvas(self, -1, fig)
            plot.SetMinSize((200, 200))
            self.plotBox.Add(plot, 1, wx.SHAPED, 0)

        self.Layout()
示例#3
0
class ColourPickerPanel(StylePickerPanel):
    """
	Based on StylePickerPanel, a Panel for selecting a list of colours, and their order
	"""
    def __init__(
        self,
        parent: wx.Window,
        id: wx.WindowID = wx.ID_ANY,
        pos: wx.Point = wx.DefaultPosition,
        size: wx.Size = wx.DefaultSize,
        style: int = wx.TAB_TRAVERSAL,
        name: str = wx.PanelNameStr,
        label: str = "Choose Colours: ",
        picker_choices: List[str] = None,
        selection_choices: List[str] = None,
    ):
        """
		:param parent: The parent window.
		:type parent: wx.Window
		:param id: An identifier for the panel. wx.ID_ANY is taken to mean a default.
		:type id: wx.WindowID, optional
		:param pos: The panel position. The value ::wxDefaultPosition indicates a default position, chosen by either the windowing system or wxWidgets, depending on platform.
		:type pos: wx.Point, optional
		:param size: The panel size. The value ::wxDefaultSize indicates a default size, chosen by either the windowing system or wxWidgets, depending on platform.
		:type size: wx.Size, optional
		:param style: The window style. See wxPanel.
		:type style: int, optional
		:param name: Window name.
		:type name: str, optional
		:param label: Label for the panel
		:type label: str, optional
		:param picker_choices: A list of hex value choices to populate the 'picker' size of the panel with
		:type picker_choices: list of str
		:param selection_choices: A list of hex value choices to populate the 'selection' size of the panel with
		:type selection_choices: list of str
		"""

        args = (parent, id, pos, size)
        kwds = {"style": style, "name": name}

        if picker_choices is None:
            picker_choices = default_picker_choices

        if selection_choices is None:
            selection_choices = default_colours[:]

        self.label = label
        self.picker_choices = picker_choices
        self.selection_choices = selection_choices

        # begin wxGlade: ColourPickerPanel.__init__
        kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
        wx.Panel.__init__(self, *args, **kwds)
        self.main_panel = wx.Panel(self, wx.ID_ANY)
        self.move_panel = wx.Panel(self.main_panel, wx.ID_ANY)
        self.picker_list_box = wx.ListBox(self.main_panel,
                                          wx.ID_ANY,
                                          choices=[])

        self.picker_figure = Figure()

        self.picker_canvas = FigureCanvas(self.main_panel, wx.ID_ANY,
                                          self.picker_figure)
        self.add_btn = wx.Button(self.main_panel, wx.ID_ANY, u"Add 🡲")
        self.remove_btn = wx.Button(self.main_panel, wx.ID_ANY, u"🡰 Remove")
        self.selection_list_box = wx.ListBox(self.main_panel,
                                             wx.ID_ANY,
                                             choices=[])
        self.up_btn = wx.Button(self.main_panel, wx.ID_ANY, u"🡱 Up")
        self.down_btn = wx.Button(self.main_panel, wx.ID_ANY, u"🡳 Down")

        self.selection_figure = Figure()

        self.selection_canvas = FigureCanvas(self.main_panel, wx.ID_ANY,
                                             self.selection_figure)

        self.__set_properties()
        self.__do_layout()
        # end wxGlade
        self.Bind(wx.EVT_LISTBOX, self.update_picker_preview,
                  self.picker_list_box)
        self.Bind(wx.EVT_LISTBOX_DCLICK, self.add, self.picker_list_box)
        self.Bind(wx.EVT_BUTTON, self.add, self.add_btn)
        self.Bind(wx.EVT_BUTTON, self.remove, self.remove_btn)
        self.Bind(wx.EVT_LISTBOX, self.update_selection_preview,
                  self.selection_list_box)
        self.Bind(wx.EVT_BUTTON, self.move_up, self.up_btn)
        self.Bind(wx.EVT_BUTTON, self.move_down, self.down_btn)

        self.Bind(wx.EVT_LISTBOX_DCLICK, self.remove, self.selection_list_box)

        self.remove_btn.SetLabel("Remove")

        self.selection_list_box.Clear()
        self.selection_list_box.AppendItems(self.selection_choices)
        if not self.selection_list_box.IsEmpty():
            self.selection_list_box.SetSelection(0)

        self.picker_list_box.Clear()
        self.picker_list_box.AppendItems(self.picker_choices)
        if not self.picker_list_box.IsEmpty():
            self.picker_list_box.SetSelection(0)

        self.picker_axes = self.picker_figure.add_subplot(111)
        self.selection_axes = self.selection_figure.add_subplot(111)
        self.update_picker_preview()
        self.update_selection_preview()

    def __set_properties(self):
        # begin wxGlade: ColourPickerPanel.__set_properties
        self.move_panel.SetMinSize((170, -1))
        self.picker_list_box.SetMinSize((170, 256))
        self.picker_canvas.SetMinSize((64, 64))
        self.selection_list_box.SetMinSize((170, 256))
        self.up_btn.SetMinSize((80, -1))
        self.down_btn.SetMinSize((80, -1))
        self.selection_canvas.SetMinSize((64, 64))
        self.main_panel.SetMinSize((450, -1))
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: ColourPickerPanel.__do_layout
        parent_sizer = wx.BoxSizer(wx.HORIZONTAL)
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        list_grid_sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer_4 = wx.BoxSizer(wx.VERTICAL)
        selection_preview_sizer = wx.BoxSizer(wx.HORIZONTAL)
        move_grid = wx.GridSizer(1, 2, 0, 5)
        sizer_3 = wx.BoxSizer(wx.VERTICAL)
        sizer_2 = wx.BoxSizer(wx.VERTICAL)
        picker_preview_sizer = wx.BoxSizer(wx.HORIZONTAL)
        grid_sizer = wx.GridSizer(1, 3, 10, 10)
        borders_label = wx.StaticText(self.main_panel, wx.ID_ANY,
                                      "Choose Styles: ")
        borders_label.SetMinSize((128, 20))
        borders_label.SetFont(
            wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer.Add(borders_label, 0, wx.BOTTOM, 7)
        grid_sizer.Add(self.move_panel, 1, wx.ALIGN_CENTER | wx.EXPAND, 0)
        main_sizer.Add(grid_sizer, 0, 0, 0)
        sizer_2.Add(self.picker_list_box, 5, wx.EXPAND, 0)
        sizer_2.Add((0, 0), 0, 0, 0)
        preview_label = wx.StaticText(self.main_panel, wx.ID_ANY, "Preview: ")
        picker_preview_sizer.Add(preview_label, 0, 0, 0)
        picker_preview_sizer.Add(self.picker_canvas, 1, wx.EXPAND, 0)
        sizer_2.Add(picker_preview_sizer, 0, wx.EXPAND | wx.TOP, 5)
        list_grid_sizer.Add(sizer_2, 1, wx.EXPAND, 0)
        sizer_3.Add(self.add_btn, 0, wx.ALIGN_CENTER | wx.BOTTOM, 10)
        sizer_3.Add(self.remove_btn, 0, wx.ALIGN_CENTER, 0)
        list_grid_sizer.Add(sizer_3, 5, wx.ALIGN_CENTER | wx.BOTTOM, 64)
        sizer_4.Add(self.selection_list_box, 5, wx.EXPAND, 0)
        move_grid.Add(self.up_btn, 0, wx.ALIGN_CENTER, 0)
        move_grid.Add(self.down_btn, 0, wx.ALIGN_CENTER, 0)
        sizer_4.Add(move_grid, 0, wx.EXPAND, 0)
        preview_label_1 = wx.StaticText(self.main_panel, wx.ID_ANY,
                                        "Preview: ")
        selection_preview_sizer.Add(preview_label_1, 0, 0, 0)
        selection_preview_sizer.Add(self.selection_canvas, 1, wx.EXPAND, 0)
        sizer_4.Add(selection_preview_sizer, 0, wx.EXPAND | wx.TOP, 5)
        list_grid_sizer.Add(sizer_4, 1, wx.EXPAND, 0)
        main_sizer.Add(list_grid_sizer, 0, 0, 0)
        static_line_11 = wx.StaticLine(self.main_panel, wx.ID_ANY)
        main_sizer.Add(static_line_11, 0, wx.BOTTOM | wx.EXPAND | wx.TOP, 5)
        self.main_panel.SetSizer(main_sizer)
        parent_sizer.Add(self.main_panel, 0, wx.ALL, 10)
        self.SetSizer(parent_sizer)
        parent_sizer.Fit(self)
        self.Layout()
        # end wxGlade
        borders_label.SetLabel(self.label)

    def add(self, event):  # wxGlade: ColourPickerPanel.<event_handler>
        """
		Event handler for adding the colour currently selected in the 'picker' to the 'selection'
		"""

        selection = self.picker_list_box.GetSelection()
        if selection == -1:
            return

        selection_string = self.picker_list_box.GetString(selection)
        if selection_string == '':
            return

        self.selection_list_box.Append(selection_string)

        self.update_picker_preview()

        self.selection_list_box.SetSelection(
            self.selection_list_box.GetCount() - 1)
        self.update_selection_preview()
        event.Skip()

    def remove(self, event):  # wxGlade: ColourPickerPanel.<event_handler>
        """
		Event handler for removing the colour currently selected in the 'selection'
		"""

        selection = self.selection_list_box.GetSelection()
        if selection == -1:
            return

        selection_string = self.selection_list_box.GetString(selection)
        if selection_string == '':
            return

        self.selection_list_box.Delete(self.selection_list_box.GetSelection())

        self.update_selection_preview()
        event.Skip()

    def update_preview(self, list_obj: wx.ListBox, axes: matplotlib.axes.Axes):
        """
		Update the preview from the given list

		:param list_obj: The list to update the preview for
		:type list_obj: wx.ListBox
		:param axes: The preview axes to update
		:type axes: matplotlib.axes.Axes
		"""

        axes.clear()
        axes.axis('off')
        selection_string = list_obj.GetStringSelection()
        if selection_string == '':
            return

        axes.scatter(1, 1, s=400, color=selection_string, marker="s")

    def pick(self, *args):
        """
		Open a wx.ColourDialog to edit the colour currently selected in the picker
		"""

        selection = self.selection_list_box.GetSelection()
        if selection == -1:
            return

        selection_string = self.selection_list_box.GetString(selection)
        if selection_string == '':
            return

        colour = wx.ColourData()
        colour.SetColour(selection_string)

        dlg = wx.ColourDialog(self, data=colour)

        res = dlg.ShowModal()
        if res == wx.ID_OK:
            self.selection_list_box.Delete(selection)
            self.selection_list_box.InsertItems(
             [dlg.GetColourData().GetColour().GetAsString(wx.C2S_HTML_SYNTAX)],
             selection)  # yapf: disable
            self.selection_list_box.SetSelection(selection)
            self.update_selection_preview()
            dlg.Destroy()

    def GetSelection(self) -> List[str]:
        """
		Returns a list of the currently selected colours

		:rtype: list of str
		"""
        return [
            self.selection_list_box.GetString(item)
            for item in range(self.selection_list_box.GetCount())
        ]

    get_selection = GetSelection
示例#4
0
class PlotPanel(wx.Panel):
    """
    The PlotPanel
    """
    def __init__(self, parent, color=None, dpi=None, **kwargs):
        # initialize Panel
        if 'id' not in kwargs.keys():
            kwargs['id'] = wx.ID_ANY
        if 'style' not in kwargs.keys():
            kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE
        wx.Panel.__init__(self, parent, **kwargs)
        self.SetMinSize((100, 40))

        # initialize matplotlib stuff
        self.figure = Figure(None, dpi=dpi, facecolor='white')
        self.canvas = FigureCanvas(self, -1, self.figure)
        self.canvas.SetMinSize((30, 10))
        self.SetBackgroundColour('white')

        # Add the canvas to the sizer.
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.canvas, 1, wx.EXPAND)
        self.SetSizer(self.sizer)

        #self.canvas.mpl_connect('button_press_event', self.onClick)
        self.canvas.Bind(wx.EVT_SET_FOCUS, self.onSetFocus)
        self.Bind(wx.EVT_SET_FOCUS, self.onSetFocus2)
        self.canvas.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
        self.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
        self.canvas.Bind(wx.EVT_KEY_UP, self.onKeyUp)
        self.Bind(wx.EVT_KEY_UP, self.onKeyUp)
        self.Bind(wx.EVT_LEFT_DOWN, self.onLeftDown)
        self.canvas.Bind(wx.EVT_LEFT_DOWN, self.onLeftDown)

    def onClick(self, event):
        print "Clicked in View. event: %s" % event.guiEvent
        event.guiEvent.ResumePropagation(1)
        event.guiEvent.Skip()

    def onWxClick(self, event):
        print "Got the WX event."

    def onSetFocus(self, event):
        print "Canvas got Focus"
        event.Skip()

    def onSetFocus2(self, event):
        print "PlotPanel got Focus"

    def onKeyDown(self, event):
        print "Propagating keyDown in plotPanel"
        event.ResumePropagation(1)
        event.Skip()

    def onKeyUp(self, event):
        print "Propagating keyUp in plotPanel"
        event.ResumePropagation(1)
        event.Skip()

    def onLeftDown(self, event):
        print "PlotPanel LEFT DOWN"
        event.ResumePropagation(30)
        event.Skip()

    def SetColor(self, rgbtuple=None):
        """Set figure and canvas colours to be the same."""
        if rgbtuple is None:
            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
        clr = [c / 255. for c in rgbtuple]
        self.figure.set_facecolor(clr)
        self.figure.set_edgecolor(clr)
        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))
        self.canvas.Refresh()
示例#5
0
class plot1(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, size=(700, 500))
        self.figure = plt.figure()

        self.canvas = FigureCanvas(self, -1, self.figure)
        self.canvas.SetMinSize(wx.Size(1, 1))
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Hide()
        self._init_plots()

    def _init_plots(self):
        self.ax_time = self.figure.add_subplot(111)
        self.ax_freq = self.figure.add_subplot(111)
        self.ax_hist = self.figure.add_subplot(111)

        self.ax_time.plot([], [])
        self.ax_time.set_title("Loaded Data")
        self.ax_time.set_xlabel("Time [s]")
        self.ax_time.set_ylabel("Amplitude [V]")

    def plot(self, data, time, type, rate=None):
        if (type == 1):
            self.ax_time.clear()
            self.ax_freq.clear()
            self.ax_hist.clear()

            y = data
            t = time
            self.ax_time.plot(t, y)

            #set the plot params
            self.ax_time.set_title("Time Series Measurement")
            self.ax_time.set_xlabel("Time [s]")
            self.ax_time.set_ylabel("Voltage [V]")

            #draw the plot
            self.canvas.draw()

        elif (type == 2):
            self.ax_time.clear()
            self.ax_freq.clear()
            self.ax_hist.clear()

            #freq domain
            N = len(data)
            T = 1 / rate
            xf = np.linspace(0.0, 1.0 / (2.0 * T), N // 2)

            # taking the fft
            fftData = np.abs(np.fft.rfft(data))
            # determine the offset
            offset = len(fftData) - len(xf)
            if (offset < 0):
                # pad the data array with zeros
                for i in range(offset):
                    fftData.append[0]
            elif (offset > 0):
                fftData = fftData[:-offset]
            # fftTime = np.fft.rfftfreq(self.chunksize, 1./self.samplerate)
            self.ax_freq.plot(xf, fftData)
            self.ax_freq.set_title("Signal FFT")
            self.ax_freq.set_xlabel("Frequency [Hz]")
            self.ax_freq.set_ylabel("Amplitude |P(f)|")
            self.canvas.draw()
        elif (type == 3):
            self.ax_time.clear()
            self.ax_freq.clear()
            self.ax_hist.clear()

            counts, bins, patches = self.ax_hist.hist(data, 30)
            self.ax_hist.set_title("Signal Histogram")
            self.ax_hist.set_xlabel("Voltage [V]")
            self.ax_hist.set_ylabel("Counts")
            self.canvas.draw()
            #hist
            ''''''

    def OnDelete(self):
        print(">>> closing plots")
        plt.close(self.figure)
示例#6
0
class FIRBandpassConfigPanel(FilterConfigPanel):
    def __init__(self, *args, **kwargs):
        FilterConfigPanel.__init__(self, *args, **kwargs)

        # options go in top-level sizer
        self.initOptions()

        # other stuff split horizontally by bottomSizer
        self.bottomSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.initSliders()
        self.initResponse()
        self.sizer.Add(self.bottomSizer, proportion=1, flag=wx.EXPAND)

        self.initLayout()

    def initOptions(self):
        optionsSizer = wx.BoxSizer(wx.HORIZONTAL)

        self.filtTypeComboBox = wx.ComboBox(self, choices=list(self.flt.filtMap.keys()),
            value=self.flt.filtType, style=wx.CB_DROPDOWN)
        self.Bind(wx.EVT_COMBOBOX, self.setFiltType, self.filtTypeComboBox)
        optionsSizer.Add(self.filtTypeComboBox, proportion=1,
                flag=wx.LEFT | wx.TOP | wx.RIGHT | wx.ALIGN_CENTER, border=20)

        self.sizer.Add(optionsSizer, proportion=0)#, flag=wx.EXPAND)

    def setFiltType(self, event):
        filtType = self.filtTypeComboBox.GetValue()

        if filtType not in self.flt.filtMap.keys():
            raise RuntimeError('Invalid filter type: %s.' % str(filtType))

        self.flt.filtType = filtType
        self.updateResponse()

    def initSliders(self):
        sliderSizer = wx.BoxSizer(wx.HORIZONTAL)

        lowFreqControlBox = widgets.ControlBox(self, label='lowFreq', orient=wx.VERTICAL)
        self.lowFreqText = wx.StaticText(self, label='%6.2f(Hz)' % self.flt.lowFreq)
        lowFreqTextSizer = wx.BoxSizer(orient=wx.VERTICAL)
        lowFreqTextSizer.Add(self.lowFreqText, proportion=0, flag=wx.ALIGN_CENTER_HORIZONTAL)
        self.lowFreqSlider = wx.Slider(self, style=wx.SL_VERTICAL | wx.SL_INVERSE,
                minValue=0, maxValue=int(self.flt.nyquist*4), value=int(self.flt.lowFreq*4))
        self.Bind(wx.EVT_SLIDER, self.setLowFreq, self.lowFreqSlider)
        lowFreqControlBox.Add(lowFreqTextSizer, proportion=0,
                flag=wx.TOP | wx.BOTTOM | wx.EXPAND, border=8)
        lowFreqControlBox.Add(self.lowFreqSlider, proportion=1,
                flag=wx.LEFT | wx.RIGHT | wx.EXPAND, border=25)

        sliderSizer.Add(lowFreqControlBox, proportion=1,
                flag=wx.ALL | wx.EXPAND, border=10)

        highFreqControlBox = widgets.ControlBox(self, label='highFreq', orient=wx.VERTICAL)
        self.highFreqText = wx.StaticText(self, label='%6.2f(Hz)' % self.flt.highFreq)
        highFreqTextSizer = wx.BoxSizer(orient=wx.VERTICAL)
        highFreqTextSizer.Add(self.highFreqText, proportion=0, flag=wx.ALIGN_CENTER_HORIZONTAL)
        self.highFreqSlider = wx.Slider(self, style=wx.SL_VERTICAL | wx.SL_INVERSE,
                minValue=0, maxValue=int(self.flt.nyquist*4), value=int(self.flt.highFreq*4))
        self.Bind(wx.EVT_SLIDER, self.setHighFreq, self.highFreqSlider)
        highFreqControlBox.Add(highFreqTextSizer, proportion=0,
                flag=wx.TOP | wx.BOTTOM | wx.EXPAND, border=8)
        highFreqControlBox.Add(self.highFreqSlider, proportion=1,
                flag=wx.LEFT | wx.RIGHT | wx.EXPAND, border=25)

        sliderSizer.Add(highFreqControlBox, proportion=1,
                flag=wx.ALL | wx.EXPAND, border=10)

        orderControlBox = widgets.ControlBox(self, label='Order', orient=wx.VERTICAL)
        self.orderText = wx.StaticText(self, label='%2d' % self.flt.order)
        orderTextSizer = wx.BoxSizer(orient=wx.VERTICAL)
        orderTextSizer.Add(self.orderText, proportion=0, flag=wx.ALIGN_CENTER_HORIZONTAL)
        self.orderSlider = wx.Slider(self, style=wx.SL_VERTICAL | wx.SL_INVERSE,
                minValue=2, maxValue=50, value=self.flt.order // 2)
        self.Bind(wx.EVT_SLIDER, self.setOrder, self.orderSlider)
        orderControlBox.Add(orderTextSizer, proportion=0,
                flag=wx.TOP | wx.BOTTOM | wx.EXPAND, border=8)
        orderControlBox.Add(self.orderSlider, proportion=1,
                flag=wx.LEFT | wx.RIGHT | wx.EXPAND, border=25)

        sliderSizer.Add(orderControlBox, proportion=1,
                flag=wx.ALL | wx.EXPAND, border=10)

        self.bottomSizer.Add(sliderSizer, proportion=1, flag=wx.EXPAND)

    def setLowFreq(self, event):
        self.flt.lowFreq = self.lowFreqSlider.GetValue() / 4.0
        self.lowFreqText.SetLabel('%6.2f(Hz)' % self.flt.lowFreq)
        self.updateResponse()

    def setHighFreq(self, event):
        self.flt.highFreq = self.highFreqSlider.GetValue() / 4.0
        self.highFreqText.SetLabel('%6.2f(Hz)' % self.flt.highFreq)
        self.updateResponse()

    def setOrder(self, event):
        self.flt.order = self.orderSlider.GetValue() * 2
        self.orderText.SetLabel('%2d' % self.flt.order)
        self.updateResponse()

    def initResponse(self):
        self.freqResponseFig = plt.Figure()
        self.freqResponseCanvas = FigureCanvas(parent=self,
                id=wx.ID_ANY, figure=self.freqResponseFig)
        self.freqResponseAx = self.freqResponseFig.add_subplot(1,1,1)
        #self.freqResponseFig.tight_layout()

        self.phaseResponseFig = plt.Figure()
        self.phaseResponseCanvas = FigureCanvas(parent=self,
                id=wx.ID_ANY, figure=self.phaseResponseFig)
        self.phaseResponseAx = self.phaseResponseFig.add_subplot(1,1,1)
        #self.freqResponseFig.tight_layout()

        responseSizer = wx.BoxSizer(wx.VERTICAL)

        freqResponseControlBox = widgets.ControlBox(self,
                label='Frequency Response', orient=wx.VERTICAL)
        freqResponseControlBox.Add(self.freqResponseCanvas, proportion=1,
                flag=wx.ALL | wx.EXPAND, border=8)
        responseSizer.Add(freqResponseControlBox, proportion=1,
                flag=wx.TOP | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=10)

        phaseResponseControlBox = widgets.ControlBox(self,
                label='Phase Response', orient=wx.VERTICAL)
        phaseResponseControlBox.Add(self.phaseResponseCanvas, proportion=1,
                flag=wx.ALL | wx.EXPAND, border=8)
        responseSizer.Add(phaseResponseControlBox, proportion=1,
                flag=wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=10)

        self.bottomSizer.Add(responseSizer, proportion=1, flag=wx.EXPAND)

        self.freqResponseCanvas.SetMinSize((0,0))
        self.phaseResponseCanvas.SetMinSize((0,0))

        # could we prevent resize when panel is not visible? XXX - idfah
        self.freqResponseLastSize = (0,0)
        self.freqResponseCanvas.Bind(wx.EVT_SIZE, self.freqResponseResize)
        self.phaseResponseLastSize = (0,0)
        self.phaseResponseCanvas.Bind(wx.EVT_SIZE, self.phaseResponseResize)

        self.updateResponse()

    def freqResponseResize(self, event):
        # prevents handling extra resize events, hack XXX - idfah
        size = self.freqResponseCanvas.GetSize()
        if self.freqResponseLastSize == size:
            return

        self.freqResponseLastSize = size
        event.Skip()

    def phaseResponseResize(self, event):
        # prevents handling extra resize events, hack XXX - idfah
        size = self.phaseResponseCanvas.GetSize()
        if self.phaseResponseLastSize == size:
            return

        self.phaseResponseLastSize = size
        event.Skip()

    def updateResponse(self):
        self.flt.updateFilter()

        self.freqResponseAx.cla()
        self.flt.bp.plotFreqResponse(ax=self.freqResponseAx, linewidth=2)
        self.freqResponseAx.autoscale(tight=True)
        self.freqResponseAx.legend(prop={'size': 12})
        self.freqResponseCanvas.draw()

        self.phaseResponseAx.cla()
        self.flt.bp.plotPhaseResponse(ax=self.phaseResponseAx, linewidth=2)
        self.phaseResponseAx.legend(prop={'size': 12})
        self.phaseResponseAx.autoscale(tight=True)

        self.phaseResponseCanvas.draw()
示例#7
0
class StylePickerPanel(wx.Panel):
    def __init__(
        self,
        parent,
        id=wx.ID_ANY,
        pos=wx.DefaultPosition,
        size=wx.DefaultSize,
        style=wx.TAB_TRAVERSAL,
        name=wx.PanelNameStr,
        label="Choose Styles: ",
        selection_choices=None,
    ):
        if selection_choices is None:
            selection_choices = default_styles[:]
        self.label = label
        self.selection_choices = selection_choices

        args = (parent, id, pos, size)
        kwds = {"style": style, "name": name}

        # begin wxGlade: StylePickerPanel.__init__
        kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
        wx.Panel.__init__(self, *args, **kwds)
        self.main_panel = wx.Panel(self, wx.ID_ANY)
        self.move_panel = wx.Panel(self.main_panel, wx.ID_ANY)
        self.picker_list_box = wx.ListBox(self.main_panel,
                                          wx.ID_ANY,
                                          choices=[])

        self.picker_figure = Figure()

        self.picker_canvas = FigureCanvas(self.main_panel, wx.ID_ANY,
                                          self.picker_figure)
        self.add_btn = wx.Button(self.main_panel, wx.ID_ANY, u"Add 🡲")
        self.remove_btn = wx.Button(self.main_panel, wx.ID_ANY, u"🡰 Remove")
        self.selection_list_box = wx.ListBox(self.main_panel,
                                             wx.ID_ANY,
                                             choices=[])
        self.up_btn = wx.Button(self.main_panel, wx.ID_ANY, u"🡱 Up")
        self.down_btn = wx.Button(self.main_panel, wx.ID_ANY, u"🡳 Down")

        self.selection_figure = Figure()

        self.selection_canvas = FigureCanvas(self.main_panel, wx.ID_ANY,
                                             self.selection_figure)

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_LISTBOX, self.update_picker_preview,
                  self.picker_list_box)
        self.Bind(wx.EVT_LISTBOX_DCLICK, self.add, self.picker_list_box)
        self.Bind(wx.EVT_BUTTON, self.add, self.add_btn)
        self.Bind(wx.EVT_BUTTON, self.remove, self.remove_btn)
        self.Bind(wx.EVT_LISTBOX, self.update_selection_preview,
                  self.selection_list_box)
        self.Bind(wx.EVT_BUTTON, self.move_up, self.up_btn)
        self.Bind(wx.EVT_BUTTON, self.move_down, self.down_btn)
        # end wxGlade

        self.Bind(wx.EVT_LISTBOX_DCLICK, self.remove, self.selection_list_box)

        self.markers = {
            "point": ".",
            "pixel": ",",
            "circle": "o",
            "triangle_down": "v",
            "triangle_up": "^",
            "triangle_left": "<",
            "triangle_right": ">",
            "tri_down": "1",
            "tri_up": "2",
            "tri_left": "3",
            "tri_right": "4",
            "octagon": "8",
            "square": "s",
            "pentagon": "p",
            "plus (filled)": "P",
            "star": "*",
            "hexagon1": "h",
            "hexagon2": "H",
            "plus": "+",
            "x": "x",
            "x (filled)": "X",
            "diamond": "D",
            "thin_diamond": "d",
            "caretleft": 4,  # (CARETLEFT),
            "caretright": 5,  # (CARETRIGHT),
            "caretup": 6,  # (CARETUP),
            "caretdown": 7,  # (CARETDOWN),
        }

        for marker in self.selection_choices:
            for key in self.markers.keys():
                if marker == self.markers[key]:
                    self.selection_list_box.Append(key)
        if not self.selection_list_box.IsEmpty():
            self.selection_list_box.SetSelection(0)

        for marker in list(
                filter(lambda x: x not in self.selection_choices,
                       [self.markers[y] for y in self.markers])):
            for key in self.markers.keys():
                if marker == self.markers[key]:
                    self.picker_list_box.Append(key)
        self.picker_list_box.SetSelection(0)

        self.picker_axes = self.picker_figure.add_subplot(111)
        self.selection_axes = self.selection_figure.add_subplot(111)
        self.update_picker_preview()
        self.update_selection_preview()

    def __set_properties(self):
        # begin wxGlade: StylePickerPanel.__set_properties
        self.move_panel.SetMinSize((170, -1))
        self.picker_list_box.SetMinSize((170, 256))
        self.picker_canvas.SetMinSize((64, 64))
        self.selection_list_box.SetMinSize((170, 256))
        self.up_btn.SetMinSize((80, -1))
        self.down_btn.SetMinSize((80, -1))
        self.selection_canvas.SetMinSize((64, 64))
        self.main_panel.SetMinSize((450, -1))
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: StylePickerPanel.__do_layout
        parent_sizer = wx.BoxSizer(wx.HORIZONTAL)
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        list_grid_sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer_4 = wx.BoxSizer(wx.VERTICAL)
        selection_preview_sizer = wx.BoxSizer(wx.HORIZONTAL)
        move_grid = wx.GridSizer(1, 2, 0, 5)
        sizer_3 = wx.BoxSizer(wx.VERTICAL)
        sizer_2 = wx.BoxSizer(wx.VERTICAL)
        picker_preview_sizer = wx.BoxSizer(wx.HORIZONTAL)
        grid_sizer = wx.GridSizer(1, 3, 10, 10)
        borders_label = wx.StaticText(self.main_panel, wx.ID_ANY,
                                      "Choose Styles: ")
        borders_label.SetMinSize((128, 20))
        borders_label.SetFont(
            wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer.Add(borders_label, 0, wx.BOTTOM, 7)
        grid_sizer.Add(self.move_panel, 1, wx.ALIGN_CENTER | wx.EXPAND, 0)
        main_sizer.Add(grid_sizer, 0, 0, 0)
        sizer_2.Add(self.picker_list_box, 5, wx.EXPAND, 0)
        sizer_2.Add((0, 0), 0, 0, 0)
        preview_label = wx.StaticText(self.main_panel, wx.ID_ANY, "Preview: ")
        picker_preview_sizer.Add(preview_label, 0, 0, 0)
        picker_preview_sizer.Add(self.picker_canvas, 1, wx.EXPAND, 0)
        sizer_2.Add(picker_preview_sizer, 0, wx.EXPAND | wx.TOP, 5)
        list_grid_sizer.Add(sizer_2, 1, wx.EXPAND, 0)
        sizer_3.Add(self.add_btn, 0, wx.ALIGN_CENTER | wx.BOTTOM, 10)
        sizer_3.Add(self.remove_btn, 0, wx.ALIGN_CENTER, 0)
        list_grid_sizer.Add(sizer_3, 5, wx.ALIGN_CENTER | wx.BOTTOM, 64)
        sizer_4.Add(self.selection_list_box, 5, wx.EXPAND, 0)
        move_grid.Add(self.up_btn, 0, wx.ALIGN_CENTER, 0)
        move_grid.Add(self.down_btn, 0, wx.ALIGN_CENTER, 0)
        sizer_4.Add(move_grid, 0, wx.EXPAND, 0)
        preview_label_1 = wx.StaticText(self.main_panel, wx.ID_ANY,
                                        "Preview: ")
        selection_preview_sizer.Add(preview_label_1, 0, 0, 0)
        selection_preview_sizer.Add(self.selection_canvas, 1, wx.EXPAND, 0)
        sizer_4.Add(selection_preview_sizer, 0, wx.EXPAND | wx.TOP, 5)
        list_grid_sizer.Add(sizer_4, 1, wx.EXPAND, 0)
        main_sizer.Add(list_grid_sizer, 0, 0, 0)
        static_line_11 = wx.StaticLine(self.main_panel, wx.ID_ANY)
        main_sizer.Add(static_line_11, 0, wx.BOTTOM | wx.EXPAND | wx.TOP, 5)
        self.main_panel.SetSizer(main_sizer)
        parent_sizer.Add(self.main_panel, 0, wx.ALL, 10)
        self.SetSizer(parent_sizer)
        parent_sizer.Fit(self)
        self.Layout()
        # end wxGlade
        borders_label.SetLabel(self.label)

    def move_up(self, event):  # wxGlade: StylePickerPanel.<event_handler>
        self.move(-1)
        event.Skip()

    def move_down(self, event):  # wxGlade: StylePickerPanel.<event_handler>
        self.move(1)
        event.Skip()

    def move(self, direction=1):
        selection = self.selection_list_box.GetSelection()
        selection_string = self.selection_list_box.GetString(selection)
        if self.selection_list_box.GetCount(
        ) == selection + direction or selection + direction < 0:
            return

        self.selection_list_box.Delete(selection)
        self.selection_list_box.InsertItems([selection_string],
                                            selection + direction)
        self.selection_list_box.SetSelection(selection + direction)

    def add(self, event):  # wxGlade: StylePickerPanel.<event_handler>
        selection = self.picker_list_box.GetSelection()
        if selection == -1:
            return

        selection_string = self.picker_list_box.GetString(selection)
        if selection_string == '':
            return

        self.selection_list_box.Append(selection_string)
        self.picker_list_box.Delete(selection)

        self.update_picker_preview()
        event.Skip()

    def remove(self, event):  # wxGlade: StylePickerPanel.<event_handler>
        selection = self.selection_list_box.GetSelection()
        if selection == -1:
            return

        selection_string = self.selection_list_box.GetString(selection)
        if selection_string == '':
            return

        self.picker_list_box.Append(selection_string)
        self.selection_list_box.Delete(self.selection_list_box.GetSelection())

        self.update_selection_preview()
        event.Skip()

    def update_picker_preview(self,
                              *_):  # wxGlade: StylePickerPanel.<event_handler>
        self.update_preview(self.picker_list_box, self.picker_axes)
        self.picker_canvas.draw_idle()

    def update_selection_preview(
            self, *_):  # wxGlade: StylePickerPanel.<event_handler>
        self.update_preview(self.selection_list_box, self.selection_axes)
        self.selection_canvas.draw_idle()

    def update_preview(self, list_obj, axes):
        axes.clear()
        axes.axis('off')
        selection_string = list_obj.GetStringSelection()
        if selection_string == '':
            return

        axes.scatter(1,
                     1,
                     s=200,
                     color="red",
                     marker=self.markers[selection_string])

    do_layout = __do_layout
    set_properties = __set_properties

    def get_selection(self):
        return [
            self.markers[self.selection_list_box.GetString(item)]
            for item in range(self.selection_list_box.GetCount())
        ]

    def _do_layout(self):
        return self.__do_layout()

    def _set_properties(self):
        return self.__set_properties()
示例#8
0
class HistoryTab(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, wx.ID_ANY)

        self.parent = parent
        self.plot_panel = wx.Panel(self, wx.ID_ANY, style=wx.SIMPLE_BORDER)

        # PLOT Panel ---------------------------------------------------------------------------------------------------
        self.figure = plt.figure(figsize=(1,
                                          1))  # look into Figure((5, 4), 75)
        self.canvas = FigureCanvas(self.plot_panel, -1, self.figure)
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Realize()

        self.ax1 = self.figure.add_subplot(211)
        self.ax2 = self.figure.add_subplot(212)

        self.temporal, = self.ax1.plot([], [], linestyle='-')
        self.spectral, = self.ax2.plot([], [], color='#C02942')

        # Open history dialog ------------------------------------------------------------------------------------------
        self.btn_openHistory = wx.Button(self, wx.ID_ANY, "Open History")
        self.Bind(wx.EVT_BUTTON, self.OnOpen, self.btn_openHistory)

        self.text_ctrl_fs = wx.TextCtrl(self, wx.ID_ANY, "")
        self.text_ctrl_samples = wx.TextCtrl(self, wx.ID_ANY, "")
        self.text_ctrl_rms = wx.TextCtrl(self, wx.ID_ANY, "")
        self.text_ctrl_thdn = wx.TextCtrl(self, wx.ID_ANY, "")
        self.text_ctrl_thd = wx.TextCtrl(self, wx.ID_ANY, "")

        self.__set_properties()
        self.__do_layout()
        self.__do_plot_layout()

    def __set_properties(self):
        self.SetBackgroundColour(wx.Colour(240, 240, 240))
        self.canvas.SetMinSize((700, 490))

    def __do_layout(self):
        sizer_2 = wx.GridSizer(1, 1, 0, 0)
        grid_sizer_1 = wx.FlexGridSizer(1, 2, 0, 0)
        grid_sizer_plot = wx.GridBagSizer(0, 0)
        grid_sizer_report = wx.GridBagSizer(0, 0)

        # LEFT PANEL ---------------------------------------------------------------------------------------------------
        lbl_fs = wx.StaticText(self, wx.ID_ANY, "Fs:")
        lbl_samples = wx.StaticText(self, wx.ID_ANY, "Samples:")
        lbl_rms = wx.StaticText(self, wx.ID_ANY, "RMS:")
        lbl_thdn = wx.StaticText(self, wx.ID_ANY, "THD+N:")
        lbl_THD = wx.StaticText(self, wx.ID_ANY, "THD:")

        static_line_1 = wx.StaticLine(self, wx.ID_ANY)
        static_line_1.SetMinSize((180, 2))
        static_line_2 = wx.StaticLine(self, wx.ID_ANY)
        static_line_2.SetMinSize((180, 2))

        grid_sizer_report.Add(self.btn_openHistory, (0, 0), (1, 2),
                              wx.LEFT | wx.TOP, 5)
        grid_sizer_report.Add(static_line_2, (1, 0), (1, 2), wx.ALL, 5)
        grid_sizer_report.Add(lbl_fs, (2, 0), (1, 1), wx.LEFT | wx.RIGHT, 5)
        grid_sizer_report.Add(self.text_ctrl_fs, (2, 1), (1, 1), wx.BOTTOM, 5)
        grid_sizer_report.Add(lbl_samples, (3, 0), (1, 1), wx.LEFT | wx.RIGHT,
                              5)
        grid_sizer_report.Add(self.text_ctrl_samples, (3, 1), (1, 1),
                              wx.BOTTOM, 5)
        grid_sizer_report.Add(static_line_1, (4, 0), (1, 2), wx.ALL, 5)
        grid_sizer_report.Add(lbl_rms, (5, 0), (1, 1), wx.LEFT | wx.RIGHT, 5)
        grid_sizer_report.Add(self.text_ctrl_rms, (5, 1), (1, 1), wx.BOTTOM, 5)
        grid_sizer_report.Add(lbl_thdn, (6, 0), (1, 1), wx.LEFT | wx.RIGHT, 5)
        grid_sizer_report.Add(self.text_ctrl_thdn, (6, 1), (1, 1), wx.BOTTOM,
                              5)
        grid_sizer_report.Add(lbl_THD, (7, 0), (1, 1), wx.LEFT | wx.RIGHT, 5)
        grid_sizer_report.Add(self.text_ctrl_thd, (7, 1), (1, 1), 0, 0)
        grid_sizer_1.Add(grid_sizer_report, 1, wx.EXPAND, 0)

        # PLOT PANEL ---------------------------------------------------------------------------------------------------
        grid_sizer_plot.Add(self.canvas, (0, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.Add(self.toolbar, (1, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.AddGrowableRow(0)
        grid_sizer_plot.AddGrowableCol(0)
        self.plot_panel.SetSizer(grid_sizer_plot)
        grid_sizer_1.Add(self.plot_panel, 1, wx.EXPAND, 5)

        grid_sizer_1.AddGrowableRow(0)
        grid_sizer_1.AddGrowableCol(1)

        sizer_2.Add(grid_sizer_1, 0, wx.EXPAND, 0)

        self.SetSizer(sizer_2)
        self.Layout()

    def error_dialog(self, error_message):
        print(error_message)
        dial = wx.MessageDialog(None, str(error_message), 'Error',
                                wx.OK | wx.ICON_ERROR)
        dial.ShowModal()

    def OnOpen(self, event):
        Path("results/history").mkdir(parents=True, exist_ok=True)
        with wx.FileDialog(self,
                           "Open previous measurement:",
                           wildcard="CSV files (*.csv)|*.csv",
                           defaultDir="results/history",
                           style=wx.FD_OPEN
                           | wx.FD_FILE_MUST_EXIST) as fileDialog:

            if fileDialog.ShowModal() == wx.ID_CANCEL:
                return  # the user changed their mind

            # Proceed loading the file chosen by the user
            pathname = fileDialog.GetPath()
            try:
                with open(pathname, 'r') as file:
                    self.open_history(file)
            except (IOError, ValueError) as e:
                wx.LogError("Cannot open file '%s'." % pathname)
                self.error_dialog(e)

    def open_history(self, file):
        df = pd.read_csv(file)

        try:
            xt = df['xt'].to_numpy()
            yt = df['yt'].to_numpy()
            xf = df['xf'].to_numpy()

            # https://stackoverflow.com/a/18919965/3382269
            # https://stackoverflow.com/a/51725795/3382269
            df['yf'] = df['yf'].str.replace('i',
                                            'j').apply(lambda x: np.complex(x))
            yf = df['yf'].to_numpy()
        except KeyError:
            raise ValueError(
                'Incorrect file attempted to be opened. '
                '\nCheck data headers. xt, yt, xf, yf should be present')

        self.process_raw_input(xt, yt, xf, yf)

    def process_raw_input(self, xt, yt, xf, yf):
        yrms = np.sqrt(np.mean(np.abs(yt)**2))
        N = len(xt)
        Fs = round(1 / (xt[1] - xt[0]), 2)

        # SPECTRAL -----------------------------------------------------------------------------------------------------
        if (N % 2) == 0:
            # for even values of N: length is (N / 2) + 1
            fft_length = int(N / 2) + 1
        else:
            # for odd values of N: length is (N + 1) / 2
            fft_length = int((N + 2) / 2)

        xf_rfft, yf_rfft = xf[:fft_length], yf[:fft_length]

        thdn, *_ = THDN_F(yf_rfft, Fs, N)
        thd = THD(yf_rfft, Fs)

        self.plot(xt, yt, fft_length, xf_rfft, yf_rfft)
        self.results_update(Fs, N, yrms, thdn, thd)

    # ------------------------------------------------------------------------------------------------------------------
    def __do_plot_layout(self):
        self.ax1.set_title('SAMPLED TIMED SERIES DATA')
        self.ax1.set_xlabel('TIME (ms)')
        self.ax1.set_ylabel('AMPLITUDE')
        self.ax2.set_title('DIGITIZED WAVEFORM SPECTRAL RESPONSE')
        self.ax2.set_xlabel('FREQUENCY (kHz)')
        self.ax2.set_ylabel('MAGNITUDE (dB)')
        self.ax2.grid()
        self.figure.align_ylabels([self.ax1, self.ax2])
        self.figure.tight_layout()

    def plot(self, xt, yt, fft_length, xf, yf):
        # TEMPORAL -----------------------------------------------------------------------------------------------------
        self.temporal.set_data(xt, yt)

        try:
            self.ax1.relim()  # recompute the ax.dataLim
        except ValueError:
            print(
                f'Are the lengths of xt: {len(xt)} and yt: {len(yt)} mismatched?'
            )
            raise
        self.ax1.margins(x=0)
        self.ax1.autoscale()

        # SPECTRAL -----------------------------------------------------------------------------------------------------
        yf_peak = max(abs(yf))
        self.spectral.set_data(xf / 1000, 20 * np.log10(np.abs(yf / yf_peak)))
        try:
            self.ax2.relim()  # recompute the ax.dataLim
        except ValueError:
            print(
                f'Are the lengths of xt: {len(xf)} and yt: {len(yf)} mismatched?'
            )
            raise
        self.ax2.autoscale()

        xf_left = 0
        xf_right = xf[fft_length - 1]
        self.ax2.set_xlim(left=xf_left / 1000, right=xf_right / 1000)

        # UPDATE PLOT FEATURES -----------------------------------------------------------------------------------------
        self.figure.tight_layout()

        self.toolbar.update()  # Not sure why this is needed - ADS
        self.canvas.draw()
        self.canvas.flush_events()

    def results_update(self, Fs, N, yrms, thdn, thd):
        self.text_ctrl_fs.SetLabelText(str(Fs))
        self.text_ctrl_samples.SetLabelText(str(N))
        self.text_ctrl_rms.SetValue(f"{'{:0.3e}'.format(yrms)}")
        self.text_ctrl_thdn.SetValue(
            f"{round(thdn * 100, 3)}% or {round(np.log10(thdn), 1)}dB")
        self.text_ctrl_thd.SetValue(
            f"{round(thd * 100, 3)}% or {round(np.log10(thd), 1)}dB")
示例#9
0
class CTRL_Graphique(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self,
                          parent,
                          -1,
                          style=wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
        self.dictParametres = None
        self.afficher_valeurs = False

        # Init canvas
        self.figure = matplotlib.pyplot.figure()
        self.canvas = Canvas(self, -1, self.figure)
        self.canvas.SetMinSize((20, 20))
        self.SetColor((255, 255, 255))

        # Layout
        sizer_canvas = wx.BoxSizer(wx.VERTICAL)
        sizer_canvas.Add(self.canvas, 1, wx.EXPAND, 0)
        self.SetSizer(sizer_canvas)
        self.Layout()

    def OnBoutonZoom(self, event):
        import DLG_Zoom_graphe
        dlg = DLG_Zoom_graphe.Dialog(self, figure=self.figure)
        dlg.ShowModal()
        dlg.Destroy()

    def OnBoutonOptions(self, event):
        # Création du menu contextuel
        menuPop = UTILS_Adaptations.Menu()

        item = wx.MenuItem(menuPop, 10, _(u"Afficher les valeurs"),
                           _(u"Afficher les valeurs"), wx.ITEM_CHECK)
        menuPop.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.On_afficher_valeurs, id=10)
        if self.afficher_valeurs == True: item.Check(True)

        self.PopupMenu(menuPop)
        menuPop.Destroy()

    def On_afficher_valeurs(self, event):
        self.afficher_valeurs = not self.afficher_valeurs
        self.MAJ()

    def SetColor(self, rgbtuple=None):
        """Set figure and canvas colours to be the same."""
        if rgbtuple is None:
            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
        clr = [c / 255. for c in rgbtuple]
        self.figure.set_facecolor(clr)
        self.figure.set_edgecolor(clr)
        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))

    def ConvertCouleur(self, couleur=None):
        return [c / 255. for c in couleur]

    def SetParametres(self, dictParametres=None):
        self.dictParametres = dictParametres

    def MAJ(self):
        self.figure.clear()
        if self.dictParametres == None:
            wx.CallAfter(self.SendSizeEvent)
            return

        if self.dictParametres[
                "IDmodele"] == "repartition_categories_debit_tresorerie":
            self.Graphe_repartition_categories(typeCategorie="debit",
                                               typeDonnees="tresorerie")
        if self.dictParametres[
                "IDmodele"] == "repartition_categories_credit_tresorerie":
            self.Graphe_repartition_categories(typeCategorie="credit",
                                               typeDonnees="tresorerie")
        if self.dictParametres[
                "IDmodele"] == "repartition_categories_debit_budgetaires":
            self.Graphe_repartition_categories(typeCategorie="debit",
                                               typeDonnees="budgetaires")
        if self.dictParametres[
                "IDmodele"] == "repartition_categories_credit_budgetaires":
            self.Graphe_repartition_categories(typeCategorie="credit",
                                               typeDonnees="budgetaires")
        if self.dictParametres[
                "IDmodele"] == "repartition_categories_debit_tresorerie_budgetaires":
            self.Graphe_repartition_categories(
                typeCategorie="debit", typeDonnees="tresorerie+budgetaires")
        if self.dictParametres[
                "IDmodele"] == "repartition_categories_credit_tresorerie_budgetaires":
            self.Graphe_repartition_categories(
                typeCategorie="credit", typeDonnees="tresorerie+budgetaires")
        if self.dictParametres["IDmodele"] == "tiers_debit":
            self.Graphe_tiers(typeCategorie="debit")
        if self.dictParametres["IDmodele"] == "tiers_credit":
            self.Graphe_tiers(typeCategorie="credit")
        self.Layout()

    def Graphe_repartition_categories(self,
                                      typeCategorie="",
                                      typeDonnees="tresorerie+budgetaires"):
        # Récupération des données
        conditions = []
        if self.dictParametres["date_debut"] != None:
            conditions.append("date_budget>='%s'" %
                              self.dictParametres["date_debut"])
            conditions.append("date_budget<='%s'" %
                              self.dictParametres["date_fin"])
        if self.dictParametres["IDanalytique"] != None:
            conditions.append("IDanalytique=%d" %
                              self.dictParametres["IDanalytique"])
        if len(conditions) > 0:
            ConditionsStr = "AND " + " AND ".join(conditions)
        else:
            ConditionsStr = ""

        DB = GestionDB.DB()
        dictDonnees = {}

        if "budgetaires" in typeDonnees:
            req = """SELECT compta_operations_budgetaires.IDcategorie, compta_categories.nom, SUM(compta_operations_budgetaires.montant)
            FROM compta_operations_budgetaires
            LEFT JOIN compta_categories ON compta_categories.IDcategorie = compta_operations_budgetaires.IDcategorie
            WHERE compta_operations_budgetaires.type='%s' %s
            GROUP BY compta_operations_budgetaires.IDcategorie
            ;""" % (typeCategorie, ConditionsStr)
            DB.ExecuterReq(req)
            listeDonnees = DB.ResultatReq()
            for IDcategorie, nom, montant in listeDonnees:
                if dictDonnees.has_key(IDcategorie) == False:
                    dictDonnees[IDcategorie] = {"nom": nom, "montant": 0.0}
                dictDonnees[IDcategorie]["montant"] += montant

        if "tresorerie" in typeDonnees:
            req = """SELECT compta_ventilation.IDcategorie, compta_categories.nom, SUM(compta_ventilation.montant)
            FROM compta_ventilation
            LEFT JOIN compta_categories ON compta_categories.IDcategorie = compta_ventilation.IDcategorie
            WHERE type='%s' %s
            GROUP BY compta_ventilation.IDcategorie
            ;""" % (typeCategorie, ConditionsStr)
            DB.ExecuterReq(req)
            listeDonnees = DB.ResultatReq()
            for IDcategorie, nom, montant in listeDonnees:
                if dictDonnees.has_key(IDcategorie) == False:
                    dictDonnees[IDcategorie] = {"nom": nom, "montant": 0.0}
                dictDonnees[IDcategorie]["montant"] += montant

        DB.Close()
        if len(dictDonnees) == 0:
            return

        listeValeurs = []
        listeLabels = []
        listeCouleurs = []

        montantTotal = 0.0
        for IDcategorie, dictTemp in dictDonnees.iteritems():
            montantTotal += dictTemp["montant"]

        index = 1
        for IDcategorie, dictTemp in dictDonnees.iteritems():
            listeValeurs.append(dictTemp["montant"])
            label = dictTemp["nom"]
            if self.afficher_valeurs == True:
                label += u"\n%.2f %s" % (float(montant), SYMBOLE)
            listeLabels.append(label)

            couleur = 1.0 * montant / montantTotal
            couleur = matplotlib.cm.hsv(index * 0.1)
            listeCouleurs.append(couleur)

            index += 1

        # Création du graphique
        ax = self.figure.add_subplot(111)
        cam = ax.pie(listeValeurs,
                     labels=listeLabels,
                     colors=listeCouleurs,
                     autopct='%1.1f%%',
                     shadow=False)
        title = ax.set_title(
            self.dictParametres["nom"],
            weight="bold",
            horizontalalignment='center')  #, position=(0.5, 0.97))
        matplotlib.pyplot.setp(title, rotation=0, fontsize=11)
        ax.set_aspect(1)
        labels, labelsPourcent = cam[1], cam[2]
        matplotlib.pyplot.setp(labels, rotation=0, fontsize=11)
        matplotlib.pyplot.setp(labelsPourcent, rotation=0, fontsize=9)

        # Finalisation
        ax.autoscale_view('tight')
        ax.figure.canvas.draw()
        wx.CallAfter(self.SendSizeEvent)

    def Graphe_tiers(self, typeCategorie=""):
        # Récupération des données
        conditions = []
        if self.dictParametres["date_debut"] != None:
            conditions.append("date_budget>='%s'" %
                              self.dictParametres["date_debut"])
            conditions.append("date_budget<='%s'" %
                              self.dictParametres["date_fin"])
        if self.dictParametres["IDanalytique"] != None:
            conditions.append("IDanalytique=%d" %
                              self.dictParametres["IDanalytique"])
        if len(conditions) > 0:
            ConditionsStr = "AND " + " AND ".join(conditions)
        else:
            ConditionsStr = ""

        DB = GestionDB.DB()
        req = """SELECT compta_tiers.IDtiers, compta_tiers.nom, SUM(compta_ventilation.montant)
        FROM compta_tiers
        LEFT JOIN compta_operations ON compta_operations.IDtiers = compta_tiers.IDtiers
        LEFT JOIN compta_ventilation ON compta_ventilation.IDoperation = compta_operations.IDoperation
        WHERE type='%s' %s
        GROUP BY compta_tiers.IDtiers
        ;""" % (typeCategorie, ConditionsStr)
        DB.ExecuterReq(req)
        listeDonnees = DB.ResultatReq()
        DB.Close()
        if len(listeDonnees) == 0:
            return

        listeValeurs = []
        listeLabels = []
        listeCouleurs = []

        for IDtiers, nom, montant in listeDonnees:
            listeValeurs.append(montant)
            listeLabels.append(nom)

        listeIndex = np.arange(len(listeLabels))
        bar_height = 0.2
        opacity = 0.4

        ax = self.figure.add_subplot(111)
        barres = ax.barh(listeIndex,
                         listeValeurs,
                         height=bar_height,
                         align='center',
                         alpha=opacity)

        # Formatage des montants sur x
        majorFormatter = FormatStrFormatter(u"%d " + SYMBOLE)
        ax.xaxis.set_major_formatter(majorFormatter)

        # Affichage des labels x
        ax.set_yticks(listeIndex)
        ax.set_yticklabels(listeLabels)

        def autolabel(rects):
            # attach some text labels
            for rect in rects:
                width = rect.get_width()
                ax.text(width + 10,
                        rect.get_y() + rect.get_height() / 2.,
                        u"%.2f %s" % (int(width), SYMBOLE),
                        ha='left',
                        va='center',
                        fontsize=8,
                        color="grey")

        if self.afficher_valeurs == True:
            autolabel(barres)

        # Recherche la largeur de texte max
        largeurMax = 0
        for label in listeLabels:
            if len(label) > largeurMax:
                largeurMax = len(label)

        # Espaces autour du graph
        margeGauche = 0.1 + largeurMax * 0.008
        self.figure.subplots_adjust(left=margeGauche,
                                    right=None,
                                    wspace=None,
                                    hspace=None)

        # Finalisation
        ax.autoscale_view('tight')
        ##        ax.grid(True)
        ax.figure.canvas.draw()
        wx.CallAfter(self.SendSizeEvent)
        return

        # Récupération des données
        from Ol import OL_Suivi_budget
        analyse = OL_Suivi_budget.Analyse(self.dictBudget)
        listeCategories = analyse.GetValeurs()

        listeRealise = []
        listeBudgete = []
        listeLabels = []

        for dictCategorie in listeCategories:
            listeRealise.append(dictCategorie["realise"])
            listeBudgete.append(dictCategorie["plafond"])
            listeLabels.append(dictCategorie["nomCategorie"])

##            if dictCategorie["typeCategorie"] == "debit" :
##                solde = plafond - realise
##            else :
##                solde = realise - plafond

##        # TEST
##        listeIndex = np.arange(len(listeLabels))
##        bar_width = 0.2
##        opacity = 0.4
##
##        ax = self.figure.add_subplot(111)
##        barres = ax.bar(listeIndex, listeRealise, width=bar_width, alpha=opacity, color="g", label=_(u"Réel"))
##        barres = ax.bar(listeIndex + bar_width, listeBudgete, width=bar_width, alpha=opacity, color="b", label=_(u"Budgété"))
##
##        # Formatage des montants sur y
##        majorFormatter = FormatStrFormatter(SYMBOLE + u" %d")
##        ax.yaxis.set_major_formatter(majorFormatter)
##
##        # Affichage des labels x
##        ax.set_xticks(listeIndex + bar_width)
##        ax.set_xticklabels(listeLabels)
##
##        labels = ax.get_xticklabels()
##        setp(labels, rotation=45)
##
##        # Légende
##        props = matplotlib.font_manager.FontProperties(size=10)
##        leg = ax.legend(loc='best', shadow=False, fancybox=True, prop=props)
##        leg.get_frame().set_alpha(0.5)
##
##        # Espaces autour du graph
##        self.figure.subplots_adjust(left=0.12, bottom=0.40, right=None, wspace=None, hspace=None)

# TEST
        listeIndex = np.arange(len(listeLabels))
        bar_height = 0.2
        opacity = 0.4

        ax = self.figure.add_subplot(111)
        barresRealise = ax.barh(listeIndex,
                                listeRealise,
                                height=bar_height,
                                alpha=opacity,
                                color="g",
                                label=_(u"Réel"))
        barresBudgete = ax.barh(listeIndex + bar_height,
                                listeBudgete,
                                height=bar_height,
                                alpha=opacity,
                                color="b",
                                label=_(u"Budgété"))

        # Formatage des montants sur x
        majorFormatter = FormatStrFormatter(u"%d " + SYMBOLE)
        ax.xaxis.set_major_formatter(majorFormatter)

        # Affichage des labels x
        ax.set_yticks(listeIndex + bar_height)
        ax.set_yticklabels(listeLabels)

        def autolabel(rects):
            # attach some text labels
            for rect in rects:
                width = rect.get_width()
                ax.text(width + 20,
                        rect.get_y() + rect.get_height() / 2.,
                        u"%.2f %s" % (int(width), SYMBOLE),
                        ha='left',
                        va='center',
                        fontsize=8,
                        color="grey")

        if self.afficher_valeurs == True:
            autolabel(barresRealise)
            autolabel(barresBudgete)

        # Recherche la largeur de texte max
        largeurMax = 0
        for label in listeLabels:
            if len(label) > largeurMax:
                largeurMax = len(label)

        # Espaces autour du graph
        margeGauche = 0.1 + largeurMax * 0.008
        self.figure.subplots_adjust(left=margeGauche,
                                    right=None,
                                    wspace=None,
                                    hspace=None)

        # Légende
        props = matplotlib.font_manager.FontProperties(size=10)
        leg = ax.legend(loc='best', shadow=False, fancybox=True, prop=props)
        leg.get_frame().set_alpha(0.5)

        # Finalisation
        ax.autoscale_view('tight')
        ax.grid(True)
        ax.figure.canvas.draw()
        wx.CallAfter(self.SendSizeEvent)
        return
示例#10
0
class MatplotPanel(wx.Panel):
    clsFrame = None
    clsID_new_figure = wx.NOT_FOUND
    isInitialized = False
    kwargs = {}
    clsID_delete_datatip = wx.NewId()
    clsID_clear_datatip = wx.NewId()

    def __init__(self, parent, title=None, num=-1, thisFig=None):
        # set the size to positive value, otherwise the toolbar will assert
        # wxpython/ext/wxWidgets/src/gtk/bitmap.cpp(539): assert ""width > 0 &&
        # height > 0"" failed in Create(): invalid bitmap size
        wx.Panel.__init__(self, parent, size=(100, 100))
        # initialize matplotlib stuff
        self.figure = thisFig
        if not self.figure:
            self.figure = Figure(None, None)
        self.canvas = FigureCanvas(self, -1, self.figure)
        # since matplotlib 3.2, it does not allow canvas size to become smaller
        # than MinSize in wx backend. So the canvas size (e.g., (640, 480))may
        # be large than the window size.
        self.canvas.SetMinSize((1, 1))
        #self.canvas.manager = self

        self.num = num
        if title is None:
            title = 'Figure %d' % self.num
        self.title = title
        self.isdestory = False
        szAll = wx.BoxSizer(wx.VERTICAL)

        self.figure.set_label(title)
        self.toolbar = Toolbar(self.canvas, self.figure)
        szAll.Add(self.toolbar, 0, wx.EXPAND)
        szAll.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)

        self.toolbar.update()
        self.SetSizer(szAll)

        self.figmgr = FigureManagerWx(self.canvas, num, self)
        self.Bind(wx.EVT_CLOSE, self._onClose)

        self.canvas.mpl_connect('button_press_event', self._onClick)
        dp.connect(self.simLoad, 'sim.loaded')
        self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)
        self.Bind(wx.EVT_MENU,
                  self.OnProcessCommand,
                  id=self.clsID_delete_datatip)
        self.Bind(wx.EVT_MENU,
                  self.OnProcessCommand,
                  id=self.clsID_clear_datatip)
        self.Bind(wx.EVT_MENU, self.OnProcessCommand, id=wx.ID_NEW)

    def GetToolBar(self):
        """Override wxFrame::GetToolBar as we don't have managed toolbar"""
        return self.toolbar

    def simLoad(self, num):
        for l in self.figure.gca().lines:
            if hasattr(l, 'trace'):
                sz = len(l.get_ydata())
                for s in l.trace:
                    if (not s) or (not s.startswith(str(num) + '.')):
                        continue
                    #dispatcher.send(signal='sim.trace_buf', objects=s, size=sz)

    def _onClick(self, event):
        if event.dblclick:
            self.toolbar.home()

    def OnProcessCommand(self, evt):
        if self.toolbar.datacursor.ProcessCommand(evt.GetId()):
            self.canvas.draw()

    def _show_context_menu(self):
        if self.toolbar.mode != 'datatip':
            return

        menu = wx.Menu()
        delMenu = menu.Append(self.clsID_delete_datatip,
                              "Delete current datatip")
        note = self.toolbar.datacursor.active
        delMenu.Enable((note is not None) and note.get_visible())
        menu.Append(self.clsID_clear_datatip, "Delete all datatips")
        self.PopupMenu(menu)
        menu.Destroy()

    def OnContextMenu(self, event):
        # Show menu after the current and pending event handlers have been
        # completed, otherwise it causes the following error in some system
        # (e.g., xubuntu, matplotlib 3.2.2, wx 4.1.0), and the menu doesn't show.
        # GLib-GObject-CRITICAL **: g_object_set_data: assertion 'G_IS_OBJECT
        # (object)' failed
        wx.CallAfter(self._show_context_menu)

    def _onClose(self, evt):
        self.canvas.close_event()
        self.canvas.stop_event_loop()
        Gcf.destroy(self.num)

    def destroy(self, *args):
        if self.isdestory is False:
            dp.send('frame.delete_panel', panel=self)
            wx.WakeUpIdle()

    def Destroy(self, *args, **kwargs):
        self.isdestory = True
        self.canvas.close_event()
        self.canvas.stop_event_loop()
        Gcf.destroy(self.num)
        return super(MatplotPanel, self).Destroy(*args, **kwargs)

    def GetTitle(self):
        """return the figure title"""
        return self.title

    def SetTitle(self, title):
        """set the figure title"""
        if title == self.title:
            return
        self.title = title
        dp.send('frame.update_panel_title', pane=self, title=self.title)

    def show(self):
        """show figure"""
        if self.IsShown() is False:
            self.canvas.draw()
            dp.send('frame.show_panel', panel=self)

    def update_buffer(self, bufs):
        """update the data used in plot_trace"""
        for l in self.figure.gca().lines:
            if hasattr(l, 'trace'):
                x = l.trace[0]
                y = l.trace[1]
                if x is None:
                    if y in bufs:
                        l.set_data(numpy.arange(len(bufs[y])), bufs[y])
                elif x in bufs or y in bufs:
                    xd = l.get_xdata()
                    yd = l.get_ydata()
                    if y in bufs:
                        yd = bufs[y]
                    if x in bufs:
                        xd = bufs[x]
                    if len(xd) != len(yd):
                        sz = min(len(xd), len(yd))
                        xd = xd[0:sz]
                        yd = yd[0:sz]
                    l.set_data(xd, yd)
                if hasattr(l, 'autorelim') and l.autorelim:
                    #Need both of these in order to rescale
                    self.figure.gca().relim()
                    self.figure.gca().autoscale_view()
        self.canvas.draw()

    def plot_trace(self, x, y, autorelim, *args, **kwargs):
        """plot and trace"""
        if y is None:
            return
        if x is None:
            l, = self.figure.gca().plot(list(y.values())[0], *args, **kwargs)
            l.trace = [None, list(y.keys())[0]]
        else:
            xd = list(x.values())[0]
            yd = list(y.values())[0]
            if len(xd) != len(yd):
                sz = min(len(xd), len(yd))
                if sz > 0:
                    xd = xd[0:sz]
                    yd = yd[0:sz]
                else:
                    xd = 0
                    yd = 0
            l, = self.figure.gca().plot(xd, yd, *args, **kwargs)
            l.trace = [list(x.keys())[0], list(y.keys())[0]]
        l.autorelim = autorelim
        self.canvas.draw()

    def set_window_title(self, label):
        pass

    @classmethod
    def setactive(cls, pane):
        """set the active figure"""
        if pane and isinstance(pane, MatplotPanel):
            Gcf.set_active(pane)

    @classmethod
    def addFigure(cls, title=None, num=None, thisFig=None):
        direction = cls.kwargs.get('direction', 'top')
        fig = cls(cls.clsFrame, title=title, num=num, thisFig=thisFig)
        # set the minsize to be large enough to avoid some following assert; it
        # will not eliminate all as if a page is added to a notebook, the
        # minsize of notebook is not the max of all its children pages (check
        # frameplus.py).
        # wxpython/ext/wxWidgets/src/gtk/bitmap.cpp(539): assert ""width > 0 &&
        # height > 0"" failed in Create(): invalid bitmap size
        dp.send('frame.add_panel',
                panel=fig,
                direction=direction,
                title=fig.GetTitle(),
                target=Gcf.get_active(),
                minsize=(75, 75))
        return fig

    @classmethod
    def Initialize(cls, frame, **kwargs):
        if cls.isInitialized:
            return
        cls.isInitialized = True
        cls.clsFrame = frame
        cls.kwargs = kwargs
        resp = dp.send('frame.add_menu',
                       path='File:New:Figure',
                       rxsignal='bsm.figure')
        if resp:
            cls.clsID_new_figure = resp[0][1]

        if cls.clsID_new_figure is not wx.NOT_FOUND:
            dp.connect(cls.ProcessCommand, 'bsm.figure')
        dp.connect(cls.Uninitialize, 'frame.exit')
        dp.connect(cls.Initialized, 'frame.initialized')
        dp.connect(cls.setactive, 'frame.activate_panel')
        dp.connect(cls.OnBufferChanged, 'sim.buffer_changed')

    @classmethod
    def Initialized(cls):
        dp.send('shell.run',
                command='from matplotlib.pyplot import *',
                prompt=False,
                verbose=False,
                history=False)

    @classmethod
    def OnBufferChanged(cls, bufs):
        """the buffer has be changes, update the plot_trace"""
        for p in Gcf.get_all_fig_managers():
            p.update_buffer(bufs)

    @classmethod
    def Uninitialize(cls):
        """destroy the module"""
        Gcf.destroy_all()

    @classmethod
    def ProcessCommand(cls, command):
        """process the menu command"""
        if command == cls.clsID_new_figure:
            plt.figure()
示例#11
0
class Graph:
    def __init__(self, wxPanel, wxMinSize):
        self.figure = Figure()
        self.axes = self.figure.add_subplot(111)
        self.canvas = FigureCanvas(wxPanel, -1, self.figure)
        self.canvas.SetMinSize(wxMinSize)
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.set_message = wxPanel.GetTopLevelParent(
        ).updateStatus  # Overrides function as the default does not work
        self.nameTracker = NameTracker()

        # Real labels
        self.dataSetLabel = 'Data Set'
        self.bestFitLabel = 'Best Fit'
        self.residualsLabel = 'Residuals'

        # Arrays of all known plot data
        self.backupXData = None
        self.backupYData = None
        self.xData = None
        self.yData = None
        self.baseline = None
        self.initialFit = None
        self.bestFit = None
        self.residuals = None

        self.axes.grid(True)

    def __getLineIndexFromDisplayLabel(self, displayLabel):
        """
        Searches the lines array in the axes for a line with the given
        label and returns its index.
        :param displayLabel:
        :return:
        """
        lines = self.axes.get_lines()
        for i in range(len(lines)):
            if lines[i].get_label() == displayLabel:
                return i
        return -1

    def __getLineIndexFromRealLabel(self, realLabel):
        return self.__getLineIndexFromDisplayLabel(
            self.nameTracker.getDisplayNameFromRealName(realLabel))

    def getLegendList(self):
        """
        Returns a string list of all of the lines in the legend,
        even those that are invisible and not displayed.
        :return: a string list of all legend elements
        """
        return [line.get_label() for line in self.axes.get_lines()]

    def getLineAlpha(self, lineIndex):
        return self.axes.get_lines()[lineIndex].get_alpha()

    def getLineColor(self, lineIndex):
        return matplotlib.colors.to_hex(
            self.axes.get_lines()[lineIndex].get_color())

    def getLineInfo(self, lineIndex):
        """
        Returns the alpha, color, line style, visibility state, z order, and label
        of the line we are looking for, given the label of the line.
        :param lineIndex:
        :return: alpha, color, line style, visibility state, z order, and label of line
        """
        line = self.axes.get_lines()[lineIndex]
        return line.get_alpha(), matplotlib.colors.to_hex(
            line.get_color()), line.get_linestyle(), line.get_visible(
            ), line.get_zorder(), line.get_label()

    def getLineStyle(self, lineIndex):
        """
        Returns the linestyle of the line at the given index in the axes array.
        :param lineIndex:
        :return:
        """
        return self.axes.get_lines()[lineIndex].get_linestyle()

    def getLineVisibilityState(self, lineIndex):
        """
        Returns the visibility state of the line at the given index in the axes array.
        :param lineIndex:
        :return:
        """
        return self.axes.get_lines()[lineIndex].get_visible()

    def getLineZOrder(self, lineIndex):
        """
        Returns the zOrder of the line at the given index in the axes array.
        :param lineIndex:
        :return:
        """
        return self.axes.get_lines()[lineIndex].get_zorder()

    def setColorMultipleLines(self, axesIndexes, color):
        lines = self.axes.get_lines()
        for index in axesIndexes:
            lines[index].set_color(color)

    def setPlotTitle(self, title):
        """
        Renames the title of the graph.
        :param title: The new title of the graph.
        """
        self.axes.set_title(title)

    def setXLabel(self, xLabel):
        """
        Renames the x axis label on the graph.
        :param xLabel: The new label of the x axis
        """
        self.axes.set_xlabel(xLabel)

    def setYLabel(self, yLabel):
        """
        Renames the y axis label on the graph.
        :param yLabel: The new label of the y axis
        """
        self.axes.set_ylabel(yLabel)

    def changeLineAttributesGivenIndex(self, index, alpha, color, lineStyle,
                                       visibility, zOrder, newLabel):
        """
        Changes the attributes of the line in the axes array with the given index.
        :param index:
        :param alpha:
        :param color:
        :param lineStyle:
        :param visibility:
        :param zOrder:
        :param newLabel:
        :return:
        """
        line = self.axes.get_lines()[index]
        if alpha is not None:
            line.set_alpha(alpha)
        if color is not None and color != '':
            line.set_color(color)
        if lineStyle is not None and lineStyle != '':
            line.set_linestyle(lineStyle)
        if visibility is not None:
            line.set_visible(visibility)
        if zOrder is not None:
            line.set_zorder(zOrder)
        if newLabel is not None and newLabel != '':
            if True:
                self.nameTracker.renameDisplayNameGivenOldDisplayName(
                    line.get_label(), newLabel)
                line.set_label(newLabel)
            else:
                errorStr += 'Cannot rename ' + oldLabel + ' to ' + newLabel + ' because the name already exists.'

    def __plot(self, xData, yData, label, style=None, visible=True):
        """
        Plots the given x and y data to the given axes.
        :param xData:
        :param yData:
        :param label:
        :param style:
        :param visible:
        """
        if style is None:
            self.axes.plot(xData, yData, label=label, alpha=1, visible=visible)
        else:
            self.axes.plot(xData,
                           yData,
                           style,
                           label=label,
                           alpha=1,
                           visible=visible)
        self.nameTracker.addEntry(label)

    def plotNewDataset(self, xData, yData):
        """
        Plots a new signal. Will overwrite signal data as well as backup.
        Do not mistake for updateSignalData() as that function is intended
        for existing signals to modify.
        data.
        :param xData:
        :param yData:
        :return:
        """
        self.xData = xData.copy()
        self.yData = yData.copy()
        self.backupXData = xData.copy()
        self.backupYData = yData.copy()
        self.__plot(xData, yData, self.dataSetLabel, 'b')

    def redrawCanvas(self):
        """
        This function redraws the canvas on screen. Is necessary to call anytime
        that the plot is updated in anyway.
        :return:
        """
        self.canvas.draw()

    def rescale(self):
        """
        This function will fix the scaling with when there are different sized
        plots on screen. Is necessary to call when the use adds or removes a new
        segment to the graph. Will only scale visible plots.
        :return:
        """
        self.axes.relim(True)
        self.axes.autoscale_view()
        self.toolbar.Update()

    def __removeLineGivenDisplayLabel(self, displayLabel):
        lines = self.axes.get_lines()
        for i in range(len(lines)):
            if displayLabel == lines[i].get_label():
                lines.pop(i).remove()
                self.nameTracker.removeEntryGivenRealName(displayLabel)
                break

    # May not work. get displayLabel from realLabel if this does not work
    def __removeLineGivenRealLabel(self, realLabel):
        lines = self.axes.get_lines()
        for i in range(len(lines)):
            if lines[i].get_label() == realLabel:
                lines.pop(i).remove()
                self.nameTracker.removeEntryGivenRealName(realLabel)
                break

    def __removeLinesGivenRealLabelPrefix(self, realLabelPrefix):
        lines = self.axes.get_lines()
        segLen = len(realLabelPrefix)
        for i in reversed(range(len(lines))):
            line = lines[i]
            if self.nameTracker.getRealNameFromDisplayName(
                    line.get_label())[:segLen] == realLabelPrefix:
                lines.pop(i).remove()
        self.nameTracker.removeAllEntriesWithRealPrefix(realLabelPrefix)

    def removeAllLinesInAxes(self):
        lines = self.axes.get_lines()
        for i in reversed(range(len(lines))):
            lines.pop(i).remove()
        self.nameTracker.clear()
        self.backupXData = None
        self.backupYData = None
        self.xData = None
        self.yData = None
        self.baseline = None
        self.initialFit = None
        self.bestFit = None
        self.residuals = None

    def removeBaseLine(self):
        self.__removeLineGivenDisplayLabel(self.dataSetLabel)
        self.baseline = None

    def removeInputSpecta(self):
        self.__removeLinesGivenRealLabelPrefix(
            self.realInputSpectraLabelPrefix)
        self.inputPredictedPeaksXData = None
        self.inputPredictedPeaksYData = None

    # TODO Identify which lines are being deleted and update fields
    def removeLinesWithAxeIndexes(self, indexes):
        """
        Removes the lines at the given indexes.
        :param indexes: a sorted list of line indexes in axes lines array
        :return:
        """
        lines = self.axes.get_lines()
        for i in reversed(indexes):
            realLabel = self.nameTracker.getRealNameFromDisplayName(
                lines[i].get_label())
            if realLabel == self.dataSetLabel:
                self.baseline = None
            elif realLabel == self.bestFitLabel:
                self.bestFit = None
            elif realLabel == self.realInitialFitLabel:
                self.initialFit = None
            elif realLabel == self.realResidualLabel:
                self.residuals = None
            elif realLabel == self.realSignalLabel:
                self.xData = None
                self.yData = None
                self.backupXData = None
                self.backupYData = None
                # Remove mapping and decon data.
            elif realLabel[:len(self.realDeconPeakLabelPrefix
                                )] == self.realDeconPeakLabelPrefix:
                print('Need to remove decon data')
            elif realLabel[:len(self.realGuessPeaksLabelPrefix
                                )] == self.realGuessPeaksLabelPrefix:
                print('Need to remove guess peak')
            elif realLabel[:len(self.realInputSpectraLabelPrefix
                                )] == self.realInputSpectraLabelPrefix:
                print('Need to remove input spectra')
            elif realLabel[:len(self.mappingPrefix)] == self.mappingPrefix:
                print('Need to remove mapping')
            lines.pop(i).remove()
            self.nameTracker.removeEntryGivenRealName(realLabel)

    def removeDataSet(self):
        self.__removeLineGivenRealLabel(self.dataSetLabel)
        self.xData = None
        self.yData = None
        self.backupXData = None
        self.backupYData = None

    def restoreOriginalPlot(self):
        """
        Will replace the altered signal with its backup data and re-plots it.
        :return:
        """
        self.xData = self.backupXData.copy()
        self.yData = self.backupYData.copy()
        self.updateSignalData(self.xData, self.yData)

    def updateLegend(self):
        """
        Updates the legend to the graph. Will only display visible plots.
        If there are no visible plots, the legend will disappear.
        :return:
        """
        lines = self.axes.get_lines()
        legendLines = [line for line in lines if line.get_visible()]
        if self.axes.get_legend() is not None and len(legendLines) == 0:
            self.axes.get_legend().remove()
        else:
            self.axes.legend(handles=legendLines)

    def updateGraph(self):
        """
        Updates the legend, scale and graph canvas. These functions can
        be found separately in this class, but for convenience, this function
        compresses them all into one. Only use this function if you are updating
        all three!
        """
        self.rescale()
        self.updateLegend()
        self.redrawCanvas()
示例#12
0
class MultimeterTab(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, wx.ID_ANY)

        # instance variables -------------------------------------------------------------------------------------------
        self.DUT_choice = 'f5560A'
        self.DMM_choice = 'f8588A'
        self.dmm = dmm(self)

        self.t = threading.Thread()
        self.flag_complete = True  # Flag indicates any active threads (False) or thread completed (True)
        self.user_input = {
            'autorange': True,
            'always_voltage': True,
            'mode': 0,
            'amplitude': '',
            'rms': 0,
            'frequency': '',
        }

        self.frame = parent
        self.left_panel = wx.Panel(self, wx.ID_ANY)
        self.plot_panel = wx.Panel(self, wx.ID_ANY, style=wx.SIMPLE_BORDER)

        # LEFT Panel ---------------------------------------------------------------------------------------------------
        self.combo_DUT_choice = wx.ComboBox(
            self.left_panel,
            wx.ID_ANY,
            choices=["Fluke 5560A", "Fluke 5730A"],
            style=wx.CB_DROPDOWN | wx.CB_READONLY)
        self.combo_DMM_choice = wx.ComboBox(
            self.left_panel,
            wx.ID_ANY,
            choices=["Fluke 884xA", "Fluke 8588A"],
            style=wx.CB_DROPDOWN | wx.CB_READONLY)
        self.text_DUT_report = wx.TextCtrl(self.left_panel,
                                           wx.ID_ANY,
                                           "",
                                           style=wx.TE_READONLY)
        self.text_DMM_report = wx.TextCtrl(self.left_panel,
                                           wx.ID_ANY,
                                           "",
                                           style=wx.TE_READONLY)
        self.label_source = wx.StaticText(self.left_panel, wx.ID_ANY,
                                          "Fluke 5560A")

        self.btn_connect = wx.Button(self.left_panel, wx.ID_ANY, "Connect")
        self.btn_config = wx.Button(self.left_panel, wx.ID_ANY, "Config")
        self.checkbox_autorange = wx.CheckBox(self.left_panel, wx.ID_ANY,
                                              "Auto Range")

        self.text_amplitude = wx.TextCtrl(self.left_panel, wx.ID_ANY, "10uA")
        self.combo_rms_or_peak = wx.ComboBox(self.left_panel,
                                             wx.ID_ANY,
                                             choices=["RMS", "Peak"],
                                             style=wx.CB_DROPDOWN
                                             | wx.CB_READONLY)
        self.text_frequency = wx.TextCtrl(self.left_panel, wx.ID_ANY, "1000")

        self.checkbox_always_voltage = wx.CheckBox(self.left_panel, wx.ID_ANY,
                                                   "Always Voltage")
        self.spreadsheet = MyGrid(self.left_panel)
        self.btn_cleardata = wx.Button(self.left_panel, wx.ID_ANY,
                                       "Clear Data")
        self.checkbox_errorbar = wx.CheckBox(self.left_panel, wx.ID_ANY,
                                             "Error Bars")

        self.btn_start = wx.Button(self.left_panel, wx.ID_ANY, "RUN")
        self.combo_mode = wx.ComboBox(self.left_panel,
                                      wx.ID_ANY,
                                      choices=["Single", "Sweep"],
                                      style=wx.CB_DROPDOWN)
        self.btn_breakpoints = wx.Button(self.left_panel, wx.ID_ANY,
                                         "Breakpoints")

        # PLOT Panel ---------------------------------------------------------------------------------------------------
        self.figure = plt.figure(figsize=(1,
                                          1))  # look into Figure((5, 4), 75)
        self.canvas = FigureCanvas(self.plot_panel, -1, self.figure)
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Realize()

        self.x, self.y, self.std = np.NaN, np.NaN, np.NaN
        self.errorbars = False
        self.ax1 = self.figure.add_subplot(111)
        self.line, (self.err_top,
                    self.err_btm), (self.bars, ) = self.ax1.errorbar(
                        np.zeros(1),
                        np.zeros(1),
                        yerr=np.zeros(1),
                        fmt='o',
                        ecolor='red',
                        capsize=4)
        # BINDINGS =====================================================================================================
        # Configure Instruments ----------------------------------------------------------------------------------------
        on_DUT_selection = lambda event: self._get_DUT_choice(event)
        self.Bind(wx.EVT_COMBOBOX_CLOSEUP, on_DUT_selection,
                  self.combo_DUT_choice)

        on_DMM_selection = lambda event: self._get_DMM_choice(event)
        self.Bind(wx.EVT_COMBOBOX_CLOSEUP, on_DMM_selection,
                  self.combo_DMM_choice)

        on_connect = lambda event: self.on_connect_instr(event)
        self.Bind(wx.EVT_BUTTON, on_connect, self.btn_connect)

        on_config = lambda event: self.config(event)
        self.Bind(wx.EVT_BUTTON, on_config, self.btn_config)

        on_toggle_autorange = lambda event: self.toggle_autorange(event)
        self.Bind(wx.EVT_CHECKBOX, on_toggle_autorange,
                  self.checkbox_autorange)

        on_toggle_always_voltage = lambda event: self.toggle_always_voltage(
            event)
        self.Bind(wx.EVT_CHECKBOX, on_toggle_always_voltage,
                  self.checkbox_always_voltage)

        on_cleardata = lambda event: self.cleardata(event)
        self.Bind(wx.EVT_BUTTON, on_cleardata, self.btn_cleardata)

        on_toggle_errorbar = lambda event: self.toggle_errorbar(event)
        self.Bind(wx.EVT_CHECKBOX, on_toggle_errorbar, self.checkbox_errorbar)

        # Run Measurement (start subprocess) ---------------------------------------------------------------------------
        on_run_event = lambda event: self.on_run(event)
        self.Bind(wx.EVT_BUTTON, on_run_event, self.btn_start)

        on_open_breakpoints = lambda event: open_breakpoints()
        self.Bind(wx.EVT_BUTTON, on_open_breakpoints, self.btn_breakpoints)

        on_combo_select = lambda event: self.lock_controls(event)
        self.Bind(wx.EVT_COMBOBOX_CLOSEUP, on_combo_select, self.combo_mode)

        self.__set_properties()
        self.__do_layout()
        self.__do_plot_layout()
        self.__do_table_header()
        self.cleardata(wx.Event)

    def __set_properties(self):
        self.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.canvas.SetMinSize((700, 490))

        self.left_panel.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.plot_panel.SetBackgroundColour(wx.Colour(255, 255, 255))

        self.text_DUT_report.SetMinSize((200, 23))
        self.text_DMM_report.SetMinSize((200, 23))
        self.checkbox_autorange.SetValue(1)

        self.combo_DUT_choice.SetSelection(0)
        self.combo_DUT_choice.SetMinSize((87, 23))
        self.combo_DMM_choice.SetSelection(1)
        self.combo_DMM_choice.SetMinSize((87, 23))
        self.btn_connect.SetMinSize((87, 23))
        self.btn_start.SetMinSize((87, 23))
        self.btn_breakpoints.SetMinSize((87, 23))

        self.combo_rms_or_peak.SetSelection(0)
        self.combo_mode.SetSelection(0)
        self.checkbox_always_voltage.SetValue(0)
        self.checkbox_errorbar.SetValue(0)

        self.spreadsheet.CreateGrid(60, 3)
        self.spreadsheet.SetRowLabelSize(40)
        self.spreadsheet.SetColLabelValue(0, 'Frequency')
        self.spreadsheet.SetColLabelValue(1, 'Value')
        self.spreadsheet.SetColLabelValue(2, 'STD')
        self.spreadsheet.SetMinSize((300, 215))

        self.combo_mode.SetMinSize((110, 23))

    def __do_layout(self):
        sizer_2 = wx.GridSizer(1, 1, 0, 0)
        grid_sizer_1 = wx.FlexGridSizer(1, 2, 0, 0)
        grid_sizer_left_panel = wx.GridBagSizer(0, 0)
        grid_sizer_left_sub_btn_row = wx.GridBagSizer(0, 0)
        grid_sizer_plot = wx.GridBagSizer(0, 0)

        # LEFT PANEL ===================================================================================================
        # TITLE --------------------------------------------------------------------------------------------------------
        row = 0
        label_1 = wx.StaticText(self.left_panel, wx.ID_ANY, "MULTIMETER")
        label_1.SetFont(
            wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_1, (row, 0), (1, 2), 0, 0)

        row += 1
        static_line_1 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_1.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_1, (row, 0), (1, 3),
                                  wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        # INSTRUMENT INFO  ---------------------------------------------------------------------------------------------
        row += 1
        grid_sizer_left_panel.Add(self.combo_DUT_choice, (row, 0), (1, 1),
                                  wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_DUT_report, (row, 1), (1, 2),
                                  wx.BOTTOM | wx.LEFT, 5)

        row += 1
        grid_sizer_left_panel.Add(self.combo_DMM_choice, (row, 0), (1, 1),
                                  wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_DMM_report, (row, 1), (1, 2),
                                  wx.BOTTOM | wx.LEFT, 5)

        row += 1
        grid_sizer_left_panel.Add(self.btn_connect, (row, 0), (1, 1),
                                  wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.btn_config, (row, 1), (1, 1),
                                  wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.checkbox_autorange, (row, 2), (1, 1),
                                  wx.ALIGN_CENTRE_VERTICAL | wx.BOTTOM, 5)

        # f5560A SETUP -------------------------------------------------------------------------------------------------
        row += 1
        self.label_source.SetFont(
            wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(self.label_source, (row, 0), (1, 2), wx.TOP,
                                  10)

        row += 1
        static_line_2 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_2.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_2, (row, 0), (1, 3),
                                  wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        row += 1
        label_amplitude = wx.StaticText(self.left_panel, wx.ID_ANY,
                                        "Amplitude:")
        grid_sizer_left_panel.Add(label_amplitude, (row, 0), (1, 1),
                                  wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_amplitude, (row, 1), (1, 1),
                                  wx.BOTTOM | wx.LEFT, 5)
        grid_sizer_left_panel.Add(self.combo_rms_or_peak, (row, 2), (1, 1),
                                  wx.BOTTOM | wx.LEFT, 5)

        row += 1
        label_frequency = wx.StaticText(self.left_panel, wx.ID_ANY,
                                        "Frequency (Ft):")
        grid_sizer_left_panel.Add(label_frequency, (row, 0), (1, 1),
                                  wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_frequency, (row, 1), (1, 1),
                                  wx.LEFT, 5)
        label_Hz = wx.StaticText(self.left_panel, wx.ID_ANY, "(Hz)")
        grid_sizer_left_panel.Add(label_Hz, (row, 2), (1, 1),
                                  wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)

        row += 1
        label_measure = wx.StaticText(self.left_panel, wx.ID_ANY, "Measure")
        label_measure.SetFont(
            wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_measure, (row, 0), (1, 1), wx.TOP, 10)
        grid_sizer_left_panel.Add(self.checkbox_always_voltage, (row, 1),
                                  (1, 3), wx.ALIGN_BOTTOM | wx.LEFT, 10)

        # RESULTS ------------------------------------------------------------------------------------------------------
        row += 1
        static_line_3 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_3.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_3, (row, 0), (1, 3),
                                  wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        row += 1
        grid_sizer_left_panel.Add(self.spreadsheet, (row, 0), (1, 3),
                                  wx.ALIGN_LEFT | wx.RIGHT | wx.EXPAND, 5)

        row += 1
        grid_sizer_left_panel.Add(self.btn_cleardata, (row, 0), (1, 1),
                                  wx.LEFT | wx.TOP, 5)
        grid_sizer_left_panel.Add(self.checkbox_errorbar, (row, 1), (1, 1),
                                  wx.LEFT | wx.TOP, 5)
        grid_sizer_left_panel.AddGrowableRow(11)

        # BUTTONS ------------------------------------------------------------------------------------------------------
        row += 1
        static_line_4 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_4.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_4, (row, 0), (1, 3),
                                  wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        row += 1
        grid_sizer_left_sub_btn_row.Add(self.btn_start, (0, 0), (1, 1),
                                        wx.ALIGN_CENTER_VERTICAL)
        grid_sizer_left_sub_btn_row.Add(self.combo_mode, (0, 1), (1, 1),
                                        wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
        grid_sizer_left_sub_btn_row.Add(self.btn_breakpoints, (0, 2), (1, 1),
                                        wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
        grid_sizer_left_panel.Add(grid_sizer_left_sub_btn_row, (row, 0),
                                  (1, 3), wx.ALIGN_TOP | wx.BOTTOM, 13)

        self.left_panel.SetSizer(grid_sizer_left_panel)

        # PLOT PANEL ===================================================================================================
        grid_sizer_plot.Add(self.canvas, (0, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.Add(self.toolbar, (1, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.AddGrowableRow(0)
        grid_sizer_plot.AddGrowableCol(0)
        self.plot_panel.SetSizer(grid_sizer_plot)

        # add to main panel --------------------------------------------------------------------------------------------
        grid_sizer_1.Add(self.left_panel, 0, wx.EXPAND | wx.RIGHT, 5)
        grid_sizer_1.Add(self.plot_panel, 1, wx.EXPAND, 5)
        grid_sizer_1.AddGrowableRow(0)
        grid_sizer_1.AddGrowableCol(1)

        sizer_2.Add(grid_sizer_1, 0, wx.EXPAND, 0)

        self.SetSizer(sizer_2)
        self.Layout()

    # GET SELECTED INSTRUMENTS =========================================================================================
    def _get_instr_choice(self, type, selection, text_ctrl):
        # Get [NEW] instr choice ---------------------------------------------------------------------------------------
        print(f"The {selection} has been selected.")
        new_choice = 'f' + selection.strip('Fluke ')

        # Get [PREVIOUS] instr choice ----------------------------------------------------------------------------------
        if type.lower() == 'dut':
            current_choice = self.dmm.DUT_choice
        elif type.lower() == 'dmm':
            current_choice = self.dmm.DMM_choice
        else:
            raise ValueError(
                "invalid instrument type. Please specify whether 'dut' or 'dmm'."
            )

        # Conditionally refresh GUI ------------------------------------------------------------------------------------
        if not self.dmm.M.connected:
            # if instruments are not connected, then user is free to change the DMM choice
            pass

        elif self.dmm.DUMMY_DATA:
            # if using dummy data, then we only need to set DMM choice to True before running
            self.dmm.DMM_choice = self.DMM_choice

        elif new_choice != current_choice and self.dmm.M.connected:
            # if the selected instrument does not currently match the remote instrument connected, there's a problem.
            self.dmm.M.connected = False
            print(
                f"[WARNING] the {selection} is NOT the current remote instrument connected!"
            )
            # set text box color to red (chantilly: #EAB9C1)
            text_ctrl.SetBackgroundColour(wx.Colour(234, 185, 193))
            self.left_panel.Refresh()

        elif new_choice == self.dmm.DMM_choice:
            # if the selected instrument does match the remote instrument connected, reset the color if necessary
            self.dmm.M.connected = True
            # Reset color (white smoke: #F0F0F0)
            text_ctrl.SetBackgroundColour(wx.Colour(240, 240, 240))
            self.left_panel.Refresh()

        return new_choice

    def _get_DUT_choice(self, evt):
        selection = self.combo_DUT_choice.GetValue()
        self.DUT_choice = self._get_instr_choice(
            type='dut', selection=selection, text_ctrl=self.text_DUT_report)
        self.label_source.SetLabelText(str(selection))

        return self.DUT_choice

    def _get_DMM_choice(self, evt):
        selection = self.combo_DMM_choice.GetValue()
        self.DMM_choice = self._get_instr_choice(
            type='dmm', selection=selection, text_ctrl=self.text_DMM_report)

        return self.DMM_choice

    # CONFIGURE INSTR FOR MEASUREMENT ==================================================================================
    def config(self, evt):
        dlg = InstrumentDialog(
            self,
            [self.DUT_choice, self.DMM_choice],
            None,
            wx.ID_ANY,
        )
        dlg.ShowModal()
        dlg.Destroy()

    def get_instruments(self):
        config_dict = ReadConfig()

        dut = config_dict[self.DUT_choice]
        dmm = config_dict[self.DMM_choice]

        instruments = {
            self.DUT_choice: {
                'address': dut['address'],
                'port': dut['port'],
                'gpib': dut['gpib'],
                'mode': dut['mode']
            },
            self.DMM_choice: {
                'address': dmm['address'],
                'port': dmm['port'],
                'gpib': dmm['gpib'],
                'mode': dmm['mode']
            }
        }

        return instruments

    def set_ident(self, idn_dict):
        self.text_DUT_report.SetValue(idn_dict['DUT'])  # DUT
        self.text_DMM_report.SetValue(idn_dict['DMM'])  # current DMM

    def on_connect_instr(self, evt):
        wait = wx.BusyCursor()
        msg = "Establishing remote connections to instruments."
        busyDlg = wx.BusyInfo(msg, parent=self)

        print(
            '\nResetting connection. Closing communication with any connected instruments'
        )
        self.text_DUT_report.Clear()
        self.text_DMM_report.Clear()
        self.dmm.DUT_choice = self.DUT_choice
        self.dmm.DMM_choice = self.DMM_choice
        # self.thread_this(self.dmm.connect, (self.get_instruments(),))
        self.dmm.connect(self.get_instruments(), )

        busyDlg = None
        del wait

    # ------------------------------------------------------------------------------------------------------------------
    def lock_controls(self, evt):
        choice = self.combo_mode.GetSelection()
        if choice == 1:
            self.text_amplitude.Disable()
            self.combo_rms_or_peak.Disable()
            self.text_frequency.Disable()
        else:
            self.text_amplitude.Enable()
            self.combo_rms_or_peak.Enable()
            self.text_frequency.Enable()

    def toggle_controls(self):
        if self.text_amplitude.IsEnabled():
            self.text_amplitude.Disable()
            self.combo_rms_or_peak.Disable()
            self.text_frequency.Disable()
        else:
            self.text_amplitude.Enable()
            self.combo_rms_or_peak.Enable()
            self.text_frequency.Enable()

    def toggle_autorange(self, evt):
        if self.checkbox_autorange.IsChecked():
            print("[Update] Auto Ranging turned ON for Fluke 884xA.")
        else:
            print("[Update] Auto Ranging turned OFF for Fluke 884xA.")

    def toggle_always_voltage(self, evt):
        DMM_choice = {self.DMM_choice}

        if self.checkbox_always_voltage.IsChecked():
            print(
                f"[Update] {DMM_choice} will always measure voltage. Use external shunt if sourcing current."
            )
        else:
            print(
                f"[Update] {DMM_choice} will perform direct measurement of the DUT."
            )

    # ------------------------------------------------------------------------------------------------------------------
    def get_values(self):
        autorange = bool(self.checkbox_autorange.GetValue())
        always_voltage = bool(self.checkbox_always_voltage.GetValue())
        mode = self.combo_mode.GetSelection()
        rms = self.combo_rms_or_peak.GetSelection()

        amp_string = self.text_amplitude.GetValue()
        freq_string = self.text_frequency.GetValue()

        self.user_input = {
            'autorange': autorange,
            'always_voltage': always_voltage,
            'mode': mode,
            'amplitude': amp_string,
            'rms': rms,
            'frequency': freq_string,
        }

    # ------------------------------------------------------------------------------------------------------------------
    def thread_this(self, func, arg=()):
        self.t = threading.Thread(target=func, args=arg, daemon=True)
        self.t.start()

    # ------------------------------------------------------------------------------------------------------------------
    def on_run(self, evt):
        self.get_values()

        if not self.t.is_alive() and self.flag_complete:
            # start new thread
            self.thread_this(self.dmm.start, (self.user_input, ))
            self.checkbox_autorange.Disable()
            self.checkbox_always_voltage.Disable()
            self.btn_start.SetLabel('STOP')

        elif self.t.is_alive() and self.user_input['mode'] == 1:
            # stop sweep
            # https://stackoverflow.com/a/36499538
            self.t.do_run = False
            self.checkbox_autorange.Enable()
            self.checkbox_always_voltage.Enable()
            self.btn_start.SetLabel('RUN')
        else:
            print('thread already running.')

    # ------------------------------------------------------------------------------------------------------------------
    def __do_plot_layout(self):
        self.ax1.set_title('MULTIMETER')
        self.ax1.set_xlabel('FREQUENCY (kHz)')
        self.ax1.set_ylabel('AMPLITUDE')
        self.ax1.grid(True)
        self.figure.tight_layout()

    def toggle_errorbar(self, evt):
        if self.errorbars:
            self.errorbars = False
            print("[Update] Error bars have been turned OFF")
        else:
            self.errorbars = True
            print("[Update] Error bars have been turned ON")

        if hasattr(self.x, 'size'):
            y_err = self.std / np.sqrt(self.x.size)
            self.plot(yerr=y_err)

    def update_plot(self, x, y, std):
        self.results_update([x, y, std])
        # TODO: np.NaN is always index 0. Should this be fixed?
        self.x = np.append(self.x, x)
        self.y = np.append(self.y, y)
        self.std = np.append(self.std, std)

        yerr = self.std / np.sqrt(self.x.size)

        self.plot(yerr)

    def plot(self, yerr=None):
        if self.errorbars and yerr is not None:
            yerr_top = self.y + yerr
            yerr_btm = self.y - yerr

            self.line.set_data(self.x, self.y)
            self.err_top.set_data(self.x, yerr_top)
            self.err_btm.set_data(self.x, yerr_btm)

            new_segments = [
                np.array([[x, yt], [x, yb]])
                for x, yt, yb in zip(self.x, yerr_top, yerr_btm)
            ]
            self.bars.set_segments(new_segments)
        else:
            self.line.set_data(self.x, self.y)
            self.err_top.set_ydata(None)
            self.err_btm.set_ydata(None)

            new_segments = []
            self.bars.set_segments(new_segments)

        self.plot_redraw()

    def plot_redraw(self):
        try:
            self.ax1.relim()  # recompute the ax.dataLim
        except ValueError:
            yerr = self.err_top.get_ydata()
            print(
                f'Are the lengths of x: {len(self.x)}, y: {len(self.y)}, and yerr: {len(yerr)} mismatched?'
            )
            raise
        self.ax1.autoscale()

        # UPDATE PLOT FEATURES -----------------------------------------------------------------------------------------
        self.figure.tight_layout()

        self.toolbar.update()  # Not sure why this is needed - ADS
        self.canvas.draw()
        self.canvas.flush_events()

    def cleardata(self, evt):
        self.x, self.y, self.std = np.NaN, np.NaN, np.NaN
        self.spreadsheet.cleardata()

        self.plot()

    def __do_table_header(self):
        header = ['frequency', 'value', 'std']
        self.spreadsheet.append_rows(header)

    def results_update(self, row):
        """
        :param row: of type list
        :return: True iff rows successfully appended to spreadsheet (grid)
        """
        # self.text_rms_report.SetLabelText(str(y))
        # self.text_frequency_report.SetLabelText(str(x))
        if isinstance(row, list):
            self.spreadsheet.append_rows(row)
            return True
        else:
            raise ValueError('Row to be appended not of type list.')

    def error_dialog(self, error_message):
        print(error_message)
        dial = wx.MessageDialog(None, str(error_message), 'Error',
                                wx.OK | wx.ICON_ERROR)
        dial.ShowModal()
示例#13
0
class StylePickerPanel(wx.Panel):
    """
	Based on StylePickerPanel, a Panel for selecting a list of colours, and their order.

	:param parent: The parent window.
	:param id: An identifier for the panel. wx.ID_ANY is taken to mean a default.
	:param pos: The panel position. The value ``wx.DefaultPosition`` indicates a default position, chosen by either the windowing system or wxWidgets, depending on platform.
	:param size: The panel size. The value ``wx.DefaultSize`` indicates a default size, chosen by either the windowing system or wxWidgets, depending on platform.
	:param style: The window style. See wxPanel.
	:param name: The window name.
	:param label: Label for the panel
	:param selection_choices: A list of hex value choices to populate the 'selection' side of the panel with
	"""
    def __init__(
        self,
        parent: wx.Window,
        id=wx.ID_ANY,
        pos=wx.DefaultPosition,
        size=wx.DefaultSize,
        style=wx.TAB_TRAVERSAL,
        name=wx.PanelNameStr,
        label="Choose Styles: ",
        selection_choices: List[str] = None,
    ):
        if selection_choices is None:
            selection_choices = default_styles[:]
        self.label = label
        self.selection_choices = selection_choices

        args = (parent, id, pos, size)
        kwds = {"style": style, "name": name}

        # begin wxGlade: StylePickerPanel.__init__
        kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
        wx.Panel.__init__(self, *args, **kwds)
        self.main_panel = wx.Panel(self, wx.ID_ANY)
        self.move_panel = wx.Panel(self.main_panel, wx.ID_ANY)
        self.picker_list_box = wx.ListBox(self.main_panel,
                                          wx.ID_ANY,
                                          choices=[])

        self.picker_figure = Figure()

        self.picker_canvas = FigureCanvas(self.main_panel, wx.ID_ANY,
                                          self.picker_figure)
        self.add_btn = wx.Button(self.main_panel, wx.ID_ANY, "Add 🡲")
        self.remove_btn = wx.Button(self.main_panel, wx.ID_ANY, "🡰 Remove")
        self.selection_list_box = wx.ListBox(self.main_panel,
                                             wx.ID_ANY,
                                             choices=[])
        self.up_btn = wx.Button(self.main_panel, wx.ID_ANY, "🡱 Up")
        self.down_btn = wx.Button(self.main_panel, wx.ID_ANY, "🡳 Down")

        self.selection_figure = Figure()

        self.selection_canvas = FigureCanvas(self.main_panel, wx.ID_ANY,
                                             self.selection_figure)

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_LISTBOX, self.update_picker_preview,
                  self.picker_list_box)
        self.Bind(wx.EVT_LISTBOX_DCLICK, self.add, self.picker_list_box)
        self.Bind(wx.EVT_BUTTON, self.add, self.add_btn)
        self.Bind(wx.EVT_BUTTON, self.remove, self.remove_btn)
        self.Bind(wx.EVT_LISTBOX, self.update_selection_preview,
                  self.selection_list_box)
        self.Bind(wx.EVT_BUTTON, self.move_up, self.up_btn)
        self.Bind(wx.EVT_BUTTON, self.move_down, self.down_btn)
        # end wxGlade

        self.Bind(wx.EVT_LISTBOX_DCLICK, self.remove, self.selection_list_box)

        self.markers = {
            "point": '.',
            "pixel": ',',
            "circle": 'o',
            "triangle_down": 'v',
            "triangle_up": '^',
            "triangle_left": '<',
            "triangle_right": '>',
            "tri_down": '1',
            "tri_up": '2',
            "tri_left": '3',
            "tri_right": '4',
            "octagon": '8',
            "square": 's',
            "pentagon": 'p',
            "plus (filled)": 'P',
            "star": '*',
            "hexagon1": 'h',
            "hexagon2": 'H',
            "plus": '+',
            'x': 'x',
            "x (filled)": 'X',
            "diamond": 'D',
            "thin_diamond": 'd',
            "caretleft": 4,
            "caretright": 5,
            "caretup": 6,
            "caretdown": 7,
        }

        for marker in self.selection_choices:
            for key in self.markers.keys():
                if marker == self.markers[key]:
                    self.selection_list_box.Append(key)
        if not self.selection_list_box.IsEmpty():
            self.selection_list_box.SetSelection(0)

        for mark in filter(lambda x: x not in self.selection_choices,
                           [self.markers[y] for y in self.markers]):
            for key in self.markers.keys():
                if mark == self.markers[key]:
                    self.picker_list_box.Append(key)
        self.picker_list_box.SetSelection(0)

        self.picker_axes = self.picker_figure.add_subplot(111)
        self.selection_axes = self.selection_figure.add_subplot(111)
        self.update_picker_preview()
        self.update_selection_preview()

    def __set_properties(self):
        # begin wxGlade: StylePickerPanel.__set_properties
        self.move_panel.SetMinSize((170, -1))
        self.picker_list_box.SetMinSize((170, 256))
        self.picker_canvas.SetMinSize((64, 64))
        self.selection_list_box.SetMinSize((170, 256))
        self.up_btn.SetMinSize((80, -1))
        self.down_btn.SetMinSize((80, -1))
        self.selection_canvas.SetMinSize((64, 64))
        self.main_panel.SetMinSize((450, -1))
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: StylePickerPanel.__do_layout
        parent_sizer = wx.BoxSizer(wx.HORIZONTAL)
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        list_grid_sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer_4 = wx.BoxSizer(wx.VERTICAL)
        selection_preview_sizer = wx.BoxSizer(wx.HORIZONTAL)
        move_grid = wx.GridSizer(1, 2, 0, 5)
        sizer_3 = wx.BoxSizer(wx.VERTICAL)
        sizer_2 = wx.BoxSizer(wx.VERTICAL)
        picker_preview_sizer = wx.BoxSizer(wx.HORIZONTAL)
        grid_sizer = wx.GridSizer(1, 3, 10, 10)
        borders_label = wx.StaticText(self.main_panel, wx.ID_ANY,
                                      "Choose Styles: ")
        borders_label.SetMinSize((128, 20))
        borders_label.SetFont(
            wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ''))
        grid_sizer.Add(borders_label, 0, wx.BOTTOM, 7)
        grid_sizer.Add(self.move_panel, 1, wx.ALIGN_CENTER | wx.EXPAND, 0)
        main_sizer.Add(grid_sizer, 0, 0, 0)
        sizer_2.Add(self.picker_list_box, 5, wx.EXPAND, 0)
        sizer_2.Add((0, 0), 0, 0, 0)
        preview_label = wx.StaticText(self.main_panel, wx.ID_ANY, "Preview: ")
        picker_preview_sizer.Add(preview_label, 0, 0, 0)
        picker_preview_sizer.Add(self.picker_canvas, 1, wx.EXPAND, 0)
        sizer_2.Add(picker_preview_sizer, 0, wx.EXPAND | wx.TOP, 5)
        list_grid_sizer.Add(sizer_2, 1, wx.EXPAND, 0)
        sizer_3.Add(self.add_btn, 0, wx.ALIGN_CENTER | wx.BOTTOM, 10)
        sizer_3.Add(self.remove_btn, 0, wx.ALIGN_CENTER, 0)
        list_grid_sizer.Add(sizer_3, 5, wx.ALIGN_CENTER | wx.BOTTOM, 64)
        sizer_4.Add(self.selection_list_box, 5, wx.EXPAND, 0)
        move_grid.Add(self.up_btn, 0, wx.ALIGN_CENTER, 0)
        move_grid.Add(self.down_btn, 0, wx.ALIGN_CENTER, 0)
        sizer_4.Add(move_grid, 0, wx.EXPAND, 0)
        preview_label_1 = wx.StaticText(self.main_panel, wx.ID_ANY,
                                        "Preview: ")
        selection_preview_sizer.Add(preview_label_1, 0, 0, 0)
        selection_preview_sizer.Add(self.selection_canvas, 1, wx.EXPAND, 0)
        sizer_4.Add(selection_preview_sizer, 0, wx.EXPAND | wx.TOP, 5)
        list_grid_sizer.Add(sizer_4, 1, wx.EXPAND, 0)
        main_sizer.Add(list_grid_sizer, 0, 0, 0)
        static_line_11 = wx.StaticLine(self.main_panel, wx.ID_ANY)
        main_sizer.Add(static_line_11, 0, wx.BOTTOM | wx.EXPAND | wx.TOP, 5)
        self.main_panel.SetSizer(main_sizer)
        parent_sizer.Add(self.main_panel, 0, wx.ALL, 10)
        self.SetSizer(parent_sizer)
        parent_sizer.Fit(self)
        self.Layout()
        # end wxGlade
        borders_label.SetLabel(self.label)

    def move_up(self, event):  # wxGlade: StylePickerPanel.<event_handler>
        self.move(-1)
        event.Skip()

    def move_down(self, event):  # wxGlade: StylePickerPanel.<event_handler>
        self.move(1)
        event.Skip()

    def move(self, direction=1):
        selection = self.selection_list_box.GetSelection()
        selection_string = self.selection_list_box.GetString(selection)
        if self.selection_list_box.GetCount(
        ) == selection + direction or selection + direction < 0:
            return

        self.selection_list_box.Delete(selection)
        self.selection_list_box.InsertItems([selection_string],
                                            selection + direction)
        self.selection_list_box.SetSelection(selection + direction)

    def add(self, event):  # wxGlade: StylePickerPanel.<event_handler>
        selection = self.picker_list_box.GetSelection()
        if selection == -1:
            return

        selection_string = self.picker_list_box.GetString(selection)
        if selection_string == '':
            return

        self.selection_list_box.Append(selection_string)
        self.picker_list_box.Delete(selection)

        self.update_picker_preview()
        event.Skip()

    def remove(self, event):  # wxGlade: StylePickerPanel.<event_handler>
        selection = self.selection_list_box.GetSelection()
        if selection == -1:
            return

        selection_string = self.selection_list_box.GetString(selection)
        if selection_string == '':
            return

        self.picker_list_box.Append(selection_string)
        self.selection_list_box.Delete(self.selection_list_box.GetSelection())

        self.update_selection_preview()
        event.Skip()

    def update_picker_preview(self,
                              *_):  # wxGlade: StylePickerPanel.<event_handler>
        self.update_preview(self.picker_list_box, self.picker_axes)
        self.picker_canvas.draw_idle()

    def update_selection_preview(
            self, *_):  # wxGlade: StylePickerPanel.<event_handler>
        self.update_preview(self.selection_list_box, self.selection_axes)
        self.selection_canvas.draw_idle()

    def update_preview(self, list_obj, axes):
        axes.clear()
        axes.axis("off")
        selection_string = list_obj.GetStringSelection()
        if selection_string == '':
            return

        axes.scatter(1,
                     1,
                     s=200,
                     color="red",
                     marker=self.markers[selection_string])

    do_layout = __do_layout
    set_properties = __set_properties

    def get_selection(self):
        return [
            self.markers[self.selection_list_box.GetString(item)]
            for item in range(self.selection_list_box.GetCount())
        ]

    def _do_layout(self):
        return self.__do_layout()

    def _set_properties(self):
        return self.__set_properties()
class MyDemoPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, wx.ID_ANY)

        self.frame = parent
        self.left_panel = wx.Panel(self, wx.ID_ANY)
        self.plot_panel = wx.Panel(self, wx.ID_ANY, style=wx.SIMPLE_BORDER)

        # PLOT Panel ---------------------------------------------------------------------------------------------------
        self.figure = plt.figure(figsize=(1,
                                          1))  # look into Figure((5, 4), 75)
        self.canvas = FigureCanvas(self.plot_panel, -1, self.figure)
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Realize()

        # Plot objects -------------------------------------------------------------------------------------------------
        self.ax1 = self.figure.add_subplot(311)
        self.ax2 = self.figure.add_subplot(312)
        self.ax3 = self.figure.add_subplot(313)

        self.temporal, = self.ax1.plot([], [], linestyle='-')
        self.temporal_sum, = self.ax2.plot([], [], linestyle='-', marker='')
        self.temporal_hilbert, = self.ax2.plot([], [],
                                               linestyle='-',
                                               marker='')
        self.spectral, = self.ax3.plot([], [], color='#C02942')
        self.spectral_envelope, = self.ax3.plot([], [], color='tab:blue')

        # Plot Annotations ---------------------------------------------------------------------------------------------
        # https://stackoverflow.com/a/38677732
        self.arrow_dim_obj = self.ax3.annotate(
            "",
            xy=(0, 0),
            xytext=(0, 0),
            textcoords=self.ax3.transData,
            arrowprops=dict(arrowstyle='<->'))
        self.bar_dim_obj = self.ax3.annotate("",
                                             xy=(0, 0),
                                             xytext=(0, 0),
                                             textcoords=self.ax3.transData,
                                             arrowprops=dict(arrowstyle='|-|'))
        bbox = dict(fc="white", ec="none")
        self.dim_text = self.ax3.text(0,
                                      0,
                                      "",
                                      ha="center",
                                      va="center",
                                      bbox=bbox)

        # BINDINGS =====================================================================================================
        self.combo_window = wx.ComboBox(self.left_panel,
                                        wx.ID_ANY,
                                        choices=[
                                            "Rectangular", "Bartlett",
                                            "Hanning", "Hamming", "Blackman"
                                        ],
                                        style=wx.CB_DROPDOWN | wx.CB_READONLY)
        self.combo_bandwidth_shape = wx.ComboBox(
            self.left_panel,
            wx.ID_ANY,
            choices=["Flat-Top", "Gaussian"],
            style=wx.CB_DROPDOWN | wx.CB_READONLY)

        self.text_ctrl_fc = wx.TextCtrl(self.left_panel,
                                        wx.ID_ANY,
                                        style=wx.TE_PROCESS_ENTER)
        self.text_ctrl_laser_bw = wx.TextCtrl(self.left_panel,
                                              wx.ID_ANY,
                                              style=wx.TE_PROCESS_ENTER)
        self.text_ctrl_mainlobe = wx.TextCtrl(self.left_panel,
                                              wx.ID_ANY,
                                              style=wx.TE_PROCESS_ENTER)
        self.text_ctrl_mainlobe.SetToolTip("Mainlobe width")
        self.text_ctrl_emitted_modes = wx.TextCtrl(self.left_panel,
                                                   wx.ID_ANY,
                                                   style=wx.TE_PROCESS_ENTER)
        self.text_ctrl_index = wx.TextCtrl(self.left_panel,
                                           wx.ID_ANY,
                                           style=wx.TE_PROCESS_ENTER)
        self.checkbox_random_phase = wx.CheckBox(self.left_panel, wx.ID_ANY,
                                                 "Random Phase")

        self.report_runtime = wx.TextCtrl(self.left_panel,
                                          wx.ID_ANY,
                                          "",
                                          style=wx.TE_READONLY)
        self.report_laser_bw = wx.TextCtrl(self.left_panel,
                                           wx.ID_ANY,
                                           "",
                                           style=wx.TE_READONLY)
        self.report_wavelength = wx.TextCtrl(self.left_panel,
                                             wx.ID_ANY,
                                             "",
                                             style=wx.TE_READONLY)
        self.report_cavity_modes = wx.TextCtrl(self.left_panel,
                                               wx.ID_ANY,
                                               "",
                                               style=wx.TE_READONLY)
        self.report_cavity_length = wx.TextCtrl(self.left_panel,
                                                wx.ID_ANY,
                                                "",
                                                style=wx.TE_READONLY)
        self.report_df = wx.TextCtrl(self.left_panel,
                                     wx.ID_ANY,
                                     "",
                                     style=wx.TE_READONLY)
        self.report_longitudinal_modes = wx.TextCtrl(self.left_panel,
                                                     wx.ID_ANY,
                                                     "",
                                                     style=wx.TE_READONLY)
        self.report_fwhm = wx.TextCtrl(self.left_panel,
                                       wx.ID_ANY,
                                       "",
                                       style=wx.TE_READONLY)
        self.report_fwhm_width = wx.TextCtrl(self.left_panel,
                                             wx.ID_ANY,
                                             "",
                                             style=wx.TE_READONLY)

        on_update = lambda event: self.update(event)
        self.Bind(wx.EVT_TEXT_ENTER, on_update, self.text_ctrl_fc)
        self.Bind(wx.EVT_TEXT_ENTER, on_update, self.text_ctrl_laser_bw)
        self.Bind(wx.EVT_TEXT_ENTER, on_update, self.text_ctrl_mainlobe)
        self.Bind(wx.EVT_TEXT_ENTER, on_update, self.text_ctrl_emitted_modes)
        self.Bind(wx.EVT_TEXT_ENTER, on_update, self.text_ctrl_index)
        self.Bind(wx.EVT_COMBOBOX_CLOSEUP, on_update,
                  self.combo_bandwidth_shape)
        self.Bind(wx.EVT_CHECKBOX, on_update, self.checkbox_random_phase)

        self.__set_properties()
        self.__do_layout()
        self.__do_plot_layout()
        self.update(wx.Event)

    def __set_properties(self):
        self.SetBackgroundColour(wx.Colour(240, 240, 240))
        self.canvas.SetMinSize((700, 490))

        self.combo_window.SetSelection(0)
        self.combo_bandwidth_shape.SetSelection(0)
        self.checkbox_random_phase.SetValue(0)

        self.text_ctrl_fc.SetValue("473.613")  # (THz)
        self.text_ctrl_laser_bw.SetValue("0.1")
        self.text_ctrl_index.SetValue("1.0")
        self.text_ctrl_emitted_modes.SetValue("15")
        self.text_ctrl_mainlobe.SetValue("0.01")

        self.report_runtime.SetValue("--")
        self.report_laser_bw.SetValue("--")
        self.report_wavelength.SetValue("--")
        self.report_cavity_modes.SetValue("--")
        self.report_cavity_length.SetValue("--")
        self.report_df.SetValue("--")
        self.report_longitudinal_modes.SetValue("--")
        self.report_fwhm.SetValue("--")
        self.report_fwhm_width.SetValue("--")

    def __do_layout(self):
        sizer_2 = wx.GridSizer(1, 1, 0, 0)
        grid_sizer_1 = wx.FlexGridSizer(1, 2, 0, 0)
        grid_sizer_plot = wx.GridBagSizer(0, 0)
        grid_sizer_left_panel = wx.GridBagSizer(0, 0)

        # LEFT PANEL ---------------------------------------------------------------------------------------------------
        # TITLE --------------------------------------------------------------------------------------------------------
        row = 0
        label_1 = wx.StaticText(self.left_panel, wx.ID_ANY,
                                "LASER MODE-LOCKING")
        label_1.SetFont(
            wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_1, (row, 0), (1, 3),
                                  wx.LEFT | wx.RIGHT | wx.TOP, 5)

        row += 1
        static_line_1 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_1.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_1, (row, 0), (1, 3),
                                  wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        # PARAMETERS ---------------------------------------------------------------------------------------------------
        row += 2
        lbl_settings = wx.StaticText(self.left_panel, wx.ID_ANY, "Parameters")
        lbl_settings.SetFont(
            wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(lbl_settings, (row, 0), (1, 3),
                                  wx.LEFT | wx.RIGHT, 5)

        row += 1
        static_line_2 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_2.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_2, (row, 0), (1, 3),
                                  wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        row += 1
        lbl_fc = wx.StaticText(self.left_panel, wx.ID_ANY, "Fc:")
        grid_sizer_left_panel.Add(
            lbl_fc, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_fc, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        lbl_units_THz = wx.StaticText(self.left_panel, wx.ID_ANY, "(THz):")
        grid_sizer_left_panel.Add(
            lbl_units_THz, (row, 2), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)

        row += 1
        lbl_laser_bw = wx.StaticText(self.left_panel, wx.ID_ANY, "Laser BW:")
        grid_sizer_left_panel.Add(
            lbl_laser_bw, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_laser_bw, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        lbl_units_laser_bw = wx.StaticText(self.left_panel, wx.ID_ANY,
                                           "(x Fc)")
        grid_sizer_left_panel.Add(
            lbl_units_laser_bw, (row, 2), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)

        row += 1
        lbl_emitted_modes = wx.StaticText(self.left_panel, wx.ID_ANY,
                                          "Emitted Modes:")
        lbl_emitted_modes.SetToolTip(
            "The number of emitted modes inside the cavity")
        grid_sizer_left_panel.Add(
            lbl_emitted_modes, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_emitted_modes, (row, 1),
                                  (1, 1), wx.BOTTOM, 5)

        row += 1
        lbl_reflective_index = wx.StaticText(self.left_panel, wx.ID_ANY,
                                             "Reflective Index:")
        grid_sizer_left_panel.Add(
            lbl_reflective_index, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_index, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)

        row += 1
        label_bandwidth_shape = wx.StaticText(self.left_panel, wx.ID_ANY,
                                              "Gain Bandwidth Shape:")
        grid_sizer_left_panel.Add(
            label_bandwidth_shape, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.combo_bandwidth_shape, (row, 1), (1, 2),
                                  wx.BOTTOM, 5)

        row += 1
        grid_sizer_left_panel.Add(self.checkbox_random_phase, (row, 1), (1, 1),
                                  wx.LEFT | wx.TOP, 5)

        # SAMPLING PARAMETERS ------------------------------------------------------------------------------------------
        row += 1
        lbl_results = wx.StaticText(self.left_panel, wx.ID_ANY, "SAMPLING")
        lbl_results.SetFont(
            wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(lbl_results, (row, 0), (1, 3),
                                  wx.LEFT | wx.RIGHT, 5)

        row += 1
        static_line_3 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_3.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_3, (row, 0), (1, 3),
                                  wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        row += 1
        label_window = wx.StaticText(self.left_panel, wx.ID_ANY,
                                     "Windowing Function:")
        grid_sizer_left_panel.Add(
            label_window, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.combo_window, (row, 1), (1, 2),
                                  wx.BOTTOM, 5)

        row += 1
        label_Hz = wx.StaticText(self.left_panel, wx.ID_ANY, "Mainlobe Width:")
        grid_sizer_left_panel.Add(
            label_Hz, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_mainlobe, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        label_Hz = wx.StaticText(self.left_panel, wx.ID_ANY, "MLW (Hz)")
        grid_sizer_left_panel.Add(label_Hz, (row, 2), (1, 1), wx.BOTTOM, 5)

        # REPORT -------------------------------------------------------------------------------------------------------
        row += 1

        row += 1
        lbl_results = wx.StaticText(self.left_panel, wx.ID_ANY, "REPORT")
        lbl_results.SetFont(
            wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(lbl_results, (row, 0), (1, 3),
                                  wx.LEFT | wx.RIGHT, 5)

        row += 1
        static_line_3 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_3.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_3, (row, 0), (1, 3),
                                  wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        row += 1
        lbl_runtime = wx.StaticText(self.left_panel, wx.ID_ANY,
                                    "Total Runtime:")
        grid_sizer_left_panel.Add(
            lbl_runtime, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.report_runtime, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        label_ps = wx.StaticText(self.left_panel, wx.ID_ANY, "(ps)")
        grid_sizer_left_panel.Add(label_ps, (row, 2), (1, 1), wx.BOTTOM, 5)

        row += 1
        label_bandwidth_shape = wx.StaticText(self.left_panel, wx.ID_ANY,
                                              "Laser BW:")
        grid_sizer_left_panel.Add(
            label_bandwidth_shape, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.report_laser_bw, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        label_THz = wx.StaticText(self.left_panel, wx.ID_ANY, "(THz)")
        grid_sizer_left_panel.Add(label_THz, (row, 2), (1, 1), wx.BOTTOM, 5)

        row += 1
        label_wavelength = wx.StaticText(self.left_panel, wx.ID_ANY,
                                         "Wavelength, λ:")
        grid_sizer_left_panel.Add(
            label_wavelength, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.report_wavelength, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        label_nm = wx.StaticText(self.left_panel, wx.ID_ANY, "(nm)")
        grid_sizer_left_panel.Add(label_nm, (row, 2), (1, 1), wx.BOTTOM, 5)

        row += 1
        label_cavity_modes = wx.StaticText(self.left_panel, wx.ID_ANY,
                                           "Cavity Modes, m:")
        grid_sizer_left_panel.Add(
            label_cavity_modes, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.report_cavity_modes, (row, 1), (1, 2),
                                  wx.BOTTOM, 5)

        row += 1
        label_cavity_length = wx.StaticText(self.left_panel, wx.ID_ANY,
                                            "Cavity Length, L:")
        grid_sizer_left_panel.Add(
            label_cavity_length, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.report_cavity_length, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        label_nm = wx.StaticText(self.left_panel, wx.ID_ANY, "(mm)")
        grid_sizer_left_panel.Add(label_nm, (row, 2), (1, 1), wx.BOTTOM, 5)

        row += 1
        label_df = wx.StaticText(self.left_panel, wx.ID_ANY,
                                 "Frequency Separation, df:")
        grid_sizer_left_panel.Add(
            label_df, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.report_df, (row, 1), (1, 1), wx.BOTTOM,
                                  5)
        label_GHz = wx.StaticText(self.left_panel, wx.ID_ANY, "(GHz)")
        grid_sizer_left_panel.Add(label_GHz, (row, 2), (1, 1), wx.BOTTOM, 5)

        row += 1
        label_longitudinal_modes = wx.StaticText(self.left_panel, wx.ID_ANY,
                                                 "Longitudinal Modes:")
        grid_sizer_left_panel.Add(
            label_longitudinal_modes, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.report_longitudinal_modes, (row, 1),
                                  (1, 2), wx.BOTTOM, 5)

        row += 1
        lbl_fwhm = wx.StaticText(self.left_panel, wx.ID_ANY, "FWHM:")
        grid_sizer_left_panel.Add(
            lbl_fwhm, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.report_fwhm, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        label_blank = wx.StaticText(self.left_panel, wx.ID_ANY, "")
        grid_sizer_left_panel.Add(label_blank, (row, 2), (1, 1), wx.BOTTOM, 5)

        row += 1
        lbl_fwhm_width = wx.StaticText(self.left_panel, wx.ID_ANY,
                                       "FWHM Width:")
        grid_sizer_left_panel.Add(
            lbl_fwhm_width, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.report_fwhm_width, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        label_GHz = wx.StaticText(self.left_panel, wx.ID_ANY, "(GHz)")
        grid_sizer_left_panel.Add(label_GHz, (row, 2), (1, 1), wx.BOTTOM, 5)

        self.left_panel.SetSizer(grid_sizer_left_panel)

        # PLOT PANEL ===================================================================================================
        grid_sizer_plot.Add(self.canvas, (0, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.Add(self.toolbar, (1, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.AddGrowableRow(0)
        grid_sizer_plot.AddGrowableCol(0)
        self.plot_panel.SetSizer(grid_sizer_plot)

        # add to main panel --------------------------------------------------------------------------------------------
        grid_sizer_1.Add(self.left_panel, 0, wx.EXPAND | wx.RIGHT, 5)
        grid_sizer_1.Add(self.plot_panel, 1, wx.EXPAND, 5)
        grid_sizer_1.AddGrowableRow(0)
        grid_sizer_1.AddGrowableCol(1)

        sizer_2.Add(grid_sizer_1, 0, wx.EXPAND, 0)

        self.SetSizer(sizer_2)
        self.Layout()

    def popup_dialog(self, message):
        print(message)
        dial = wx.MessageDialog(None, str(message), 'Error',
                                wx.OK | wx.ICON_ERROR)
        dial.ShowModal()

    def get_values(self):
        fc = to_float(self.text_ctrl_fc.GetValue())
        laser_bw = to_float(self.text_ctrl_laser_bw.GetValue())
        emitted_modes = to_integer(self.text_ctrl_emitted_modes.GetValue())
        refraction_index = to_float(self.text_ctrl_index.GetValue())
        bandwidth_shape = str(self.combo_bandwidth_shape.GetValue()).lower()
        window = str(self.combo_window.GetValue()).lower()
        MLW = to_float(self.text_ctrl_mainlobe.GetValue())
        random_phase = bool(self.checkbox_random_phase.GetValue())

        return fc, laser_bw, emitted_modes, refraction_index, bandwidth_shape, window, MLW, random_phase

    def update(self, evt):
        try:
            params = self.get_values()

            data, plot_data, plot_limits = worker.worker(params)

            self.results_update(data)
            self.plot(plot_data, plot_limits)

        except ValueError as e:
            self.popup_dialog(e)

    # ------------------------------------------------------------------------------------------------------------------
    def __do_plot_layout(self):
        self.ax1.set_title('SAMPLED TIMED SERIES DATA')
        self.ax1.set_xlabel('TIME (ps)')
        self.ax1.set_ylabel('AMPLITUDE')

        self.ax2.set_title('SUMMATION OF ALL MODES/TONES')
        self.ax2.set_xlabel('TIME (ps)')
        self.ax2.set_ylabel('AMPLITUDE')

        self.ax3.set_title('SPECTRAL DATA')
        self.ax3.set_xlabel('FREQUENCY (THz)')
        self.ax3.set_ylabel('MAGNITUDE (V)')

        self.ax3.grid()
        self.figure.align_ylabels([self.ax1, self.ax2, self.ax3])
        self.figure.tight_layout()

    def plot(self, plot_data, plot_limits):

        xt, yt_list, yt, yt_envelope, xf_rfft, yf_rfft, yf_smooth = plot_data
        xt1_left, xt1_right, xt2_left, xt2_right, xf_left, xf_right, dim_left, dim_right, dim_height, dim_label, dim_label_pos = plot_limits

        xt_scale = 1e12
        xf_scale = 1e12

        # TEMPORAL -----------------------------------------------------------------------------------------------------
        xt_delta = xt[1] - xt[0]
        yt_limit = int(xt1_right / xt_delta)
        yt_limit2 = int(xt2_right / xt_delta)

        self.ax1.clear()
        self.ax1.plot(xt[:yt_limit] * xt_scale,
                      (yt_list[:, :yt_limit]).T)  # All signals
        self.ax1.set_title('SAMPLED TIMED SERIES DATA')
        self.ax1.set_xlabel('TIME (ps)')
        self.ax1.set_ylabel('AMPLITUDE')

        self.temporal_sum.set_data(
            xt[:yt_limit2] * xt_scale,
            yt[:yt_limit2])  # The summation of all signals
        self.temporal_hilbert.set_data(
            xt[:yt_limit2] * xt_scale,
            yt_envelope[:yt_limit2])  # The envelope of the summation

        self.ax1.set_xlim(left=xt1_left * xt_scale, right=xt1_right * xt_scale)

        self.ax2.set_xlim(left=xt2_left * xt_scale, right=xt2_right * xt_scale)

        # SPECTRAL -----------------------------------------------------------------------------------------------------
        self.spectral.set_data(xf_rfft / xf_scale,
                               np.abs(yf_rfft))  # The spectral plot of sum
        self.spectral_envelope.set_data(xf_rfft / xf_scale,
                                        yf_smooth)  # The spectral plot of sum

        self.ax3.set_xlim(left=xf_left / xf_scale, right=xf_right / xf_scale)

        # Arrow dimension line update ----------------------------------------------------------------------------------
        # https://stackoverflow.com/a/48684902 -------------------------------------------------------------------------
        self.arrow_dim_obj.xy = (dim_left, dim_height)
        self.arrow_dim_obj.set_position((dim_right, dim_height))
        self.arrow_dim_obj.textcoords = self.ax3.transData

        # dimension text update ----------------------------------------------------------------------------------------
        self.dim_text.set_position((dim_left + dim_label_pos, dim_height))
        self.dim_text.set_text(dim_label)

        # REDRAW PLOT --------------------------------------------------------------------------------------------------
        self.plot_redraw()

    def plot_redraw(self):
        try:
            self.ax1.relim()  # recompute the ax.dataLim
            self.ax2.relim()  # recompute the ax.dataLim
            self.ax3.relim()  # recompute the ax.dataLim
        except MemoryError as e:
            raise ValueError(str(e))

        self.ax1.margins(x=0)
        self.ax1.autoscale(axis='y')
        self.ax2.autoscale(axis='y')
        self.ax3.autoscale(axis='y')

        # UPDATE PLOT FEATURES -----------------------------------------------------------------------------------------
        self.figure.tight_layout()
        self.toolbar.update()  # Not sure why this is needed - ADS
        self.canvas.draw()
        self.canvas.flush_events()

    def results_update(self, data):
        wavelength, laser_bw, df_max, cavity_modes, cavity_length, cavity_df, longitudinal_modes, fwhm_val, fwhm_width, runtime = data

        self.report_runtime.SetValue(str(round(runtime * 1e12, 3)))
        self.report_laser_bw.SetValue(str(laser_bw / 1e12))
        self.report_cavity_length.SetValue(str(laser_bw / 1e12))
        self.report_wavelength.SetValue(str(round(wavelength * 1e9, 3)))

        self.report_df.SetValue(str(round(df_max / 1e9, 3)))
        self.report_cavity_modes.SetValue(str(cavity_modes))
        self.report_cavity_length.SetValue(str(round(cavity_length * 1e3, 3)))
        self.report_longitudinal_modes.SetValue(str(longitudinal_modes))

        self.report_fwhm.SetValue(str(round(fwhm_val, 2)))
        self.report_fwhm_width.SetValue(str(round(fwhm_width * 1e12, 3)))

        print('total runtime:', round(runtime * 1e12, 3), 'ps')
        print('laser bandwidth:', laser_bw / 1e12, 'THz')
        print('full wave, half maximum:', laser_bw / 1e12, 'THz')
        print('wavelength, lambda:', round(wavelength * 1e9, 3), 'nm')
        print()
        print('max frequency separation for number of emitted modes, df:',
              round(df_max / 1e9, 3), 'GHz')
        print('cavity modes, m:', cavity_modes)
        print('cavity length, L:', round(cavity_length * 1e2, 3), 'cm',
              round(cavity_length * 1e3, 3), '(mm)')
        print('frequency separation of cavity, df:', round(cavity_df / 1e9, 3),
              'GHz')
        print('longitudinal modes supported:', longitudinal_modes)
        print()
        print('FWHM value:', round(fwhm_val, 2))
        print('FWHM width:', round(fwhm_width * 1e12, 3), 'ps')
        print()
示例#15
0
class wxMatplotPanel(scrolled.ScrolledPanel):
    """
    The PlotPanel has a Figure and a Canvas.

    OnSize events simply set a flag, and the actually redrawing of the
    figure is triggered by an Idle event.
    """
    def __init__(self, renderPanel, color=None, dpi=None, **kwargs):
        from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
        from matplotlib.figure import Figure

        # initialize Panel
        if 'id' not in list(kwargs.keys()):
            kwargs['id'] = wx.ID_ANY
        if 'style' not in list(kwargs.keys()):
            kwargs['style'] = wx.NO_FULL_REPAINT_ON_RESIZE

        scrolled.ScrolledPanel.__init__(self, renderPanel, **kwargs)
        self.renderPanel = renderPanel

        # initialize matplotlib stuff
        self.figure = Figure(None, dpi)
        #self.canvas = NoRepaintCanvas( self, -1, self.figure )

        self.canvas = FigureCanvasWxAgg(self, -1, self.figure)

        self.canvas.mpl_connect('button_press_event', self.onMousePress)
        self.canvas.mpl_connect('pick_event', self.onPick)

        sizer = wx.BoxSizer()
        sizer.Add(self.canvas, 1, wx.EXPAND)
        self.SetSizer(sizer)
        # self.SetAutoLayout(1)
        # self.SetupScrolling()

        self.SetColor(color)
        self._refresh = False
        self._updateDraw = False

        self.toolBar_ = None

        self.canvasZoomWidth = 1.0

        self.Bind(wx.EVT_IDLE, self._onIdle)
        self.Bind(wx.EVT_SIZE, self._onSize)

        self.resfreshCounter = 0
        self.needUpdateHack_ = False
        self.needDrawing = False
        self.refresh()

    def onPick(self, event):
        pass

    def onMousePress(self, event):
        pass

    def onZoomChanged(self):
        pass

    def onPanChanged(self):
        pass

    def getToolBar(self, parent=None):
        if not self.toolBar_:
            self.toolBar_ = wxAUIMatplotPanelToolbar(self, self.canvas, parent)

        return self.toolBar_

    def SetColor(self, rgbtuple=None):
        """Set figure and canvas colours to be the same."""
        if rgbtuple is None:
            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()

        clr = [c / 255. for c in rgbtuple]
        self.figure.set_facecolor(clr)
        self.figure.set_edgecolor(clr)
        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))

    def _onSize(self, event):
        self._refresh = True

    def _onIdle(self, evt):
        if self.IsShownOnScreen():

            if self.needDrawing:
                self.redraw()

            if self._refresh:
                self.refresh()
                self._refresh = False

            if self._updateDraw:
                swatch = Stopwatch(True)

                self.canvas.draw()
                if self.needUpdateHack_:
                    self.needUpdateHack()
                    self.needUpdateHack_ = False
                self._updateDraw = False

                if self.canvasZoomWidth == 1.0:
                    self.SetupScrolling(False, False)
                print("draw: ", swatch.duration())

    def updateDrawOnIdle(self):
        self._updateDraw = True

    def resizeOnIdle(self):
        self._refresh = True

    def refresh(self):
        swatch = Stopwatch(True)
        #pixels = tuple( self.GetParent().GetClientSize() )
        self.resfreshCounter += 1

        pixels = tuple([
            int(self.GetSize()[0] * self.canvasZoomWidth),
            int(self.GetSize()[1] * 1.0)
        ])
        #    print self, self.resfreshCounter, pixels

        if self.canvas.GetMinSize(  )[0] != pixels[0] \
                or self.canvas.GetMinSize()[1] != pixels[1]:
            # print "resize canvas"
            # print "parent-size", self.renderPanel.GetSize()
            # print "self-size", self.GetSize()
            # print "tupel-size", pixels
            # to avoid _onSize loop under linux
            # if self.GetSize() != self.parent.GetClientSize():
            # if self.GetSize() != pixels:
            #self.SetSize( pixels )

            #self.canvas.SetSize( pixels )
            self.canvas.SetMinSize(pixels)

            self.figure.set_size_inches(
                float(pixels[0]) / self.figure.get_dpi(),
                float(pixels[1]) / self.figure.get_dpi())

            adjust = True
            if hasattr(self, 'cbar'):
                if self.cbar.active:
                    adjust = False

            if pixels[0] > 50 and adjust:
                self.figure.subplotpars.update(left=50.0 / pixels[0],
                                               right=(pixels[0] - 20.0) /
                                               pixels[0])

                for a in self.figure.axes:
                    if hasattr(a, "update_params"):
                        a.update_params()
                        #a.set_position( a.figbox, which = 'original' )
                        a.set_position(a.figbox, which='both')

                #self.figure.subplots_adjust( left = 50.0 / pixels[ 0 ] )
                #self.figure.subplots_adjust( right = ( pixels[ 0 ] - 20.0 ) / pixels[ 0 ] )

        # self.canvas.draw()
        self.updateDrawOnIdle()
        self.needUpdateHack_ = True
        # print "refresh: ",  swatch.duration()

    def draw(self):
        pass  # abstract, to be overridden by child classes

    def needUpdateHack(self):
        pass
示例#16
0
class DistortionAnalyzerTab(wx.Panel):
    def __init__(self, parent, frame):
        wx.Panel.__init__(self, parent, wx.ID_ANY)

        # instance variables -------------------------------------------------------------------------------------------
        self.flag_complete = True  # Flag indicates any active threads (False) or thread completed (True)
        self.t = threading.Thread()
        self.da = da(self)
        self.user_input = {}

        self.DUT_choice = 'f5560A'
        self.DMM_choice = 'f8588A'

        self.parent = parent
        self.frame = frame

        self.left_panel = wx.Panel(self, wx.ID_ANY)
        self.left_sub_panel = wx.Panel(self.left_panel, wx.ID_ANY)  # amplitude/frequency panel
        self.plot_panel = wx.Panel(self, wx.ID_ANY, style=wx.SIMPLE_BORDER)

        # PANELS =======================================================================================================
        # LEFT Panel ---------------------------------------------------------------------------------------------------
        self.combo_DUT_choice = wx.ComboBox(self.left_panel, wx.ID_ANY,
                                            choices=["Fluke 5560A", "Fluke 5730A"],
                                            style=wx.CB_DROPDOWN | wx.CB_READONLY)
        self.text_DUT_report = wx.TextCtrl(self.left_panel, wx.ID_ANY, "", style=wx.TE_READONLY)
        self.text_DMM_report = wx.TextCtrl(self.left_panel, wx.ID_ANY, "", style=wx.TE_READONLY)
        self.label_source = wx.StaticText(self.left_panel, wx.ID_ANY, "Fluke 5560A")

        self.btn_connect = wx.Button(self.left_panel, wx.ID_ANY, "Connect")
        self.btn_config = wx.Button(self.left_panel, wx.ID_ANY, "Config")

        self.checkbox_1 = wx.CheckBox(self.left_panel, wx.ID_ANY, "Local")
        self.text_amplitude = wx.TextCtrl(self.left_sub_panel, wx.ID_ANY, "10uA")
        self.combo_rms_or_peak = wx.ComboBox(self.left_sub_panel, wx.ID_ANY,
                                             choices=["RMS", "Peak"],
                                             style=wx.CB_DROPDOWN | wx.CB_READONLY)
        self.text_frequency = wx.TextCtrl(self.left_sub_panel, wx.ID_ANY, "1000")

        self.combo_mainlobe = wx.ComboBox(self.left_panel, wx.ID_ANY,
                                          choices=["Relative", "Absolute"],
                                          style=wx.CB_DROPDOWN | wx.CB_READONLY)
        self.combo_mainlobe.SetToolTip("Mainlobe width can be set relative to the signal frequency\n"
                                       "or as an absolute width independent of signal frequency")
        self.text_mainlobe = wx.TextCtrl(self.left_panel, wx.ID_ANY, "100")
        self.label_mainlobe = wx.StaticText(self.left_panel, wx.ID_ANY, "MLW (Hz)")
        self.label_mainlobe.SetToolTip("Main Lobe Width")

        self.combo_filter = wx.ComboBox(self.left_panel, wx.ID_ANY,
                                        choices=["None", "100kHz", "2MHz", "2.4MHz", "3MHz"],
                                        style=wx.CB_DROPDOWN | wx.CB_READONLY)
        self.combo_coupling = wx.ComboBox(self.left_panel, wx.ID_ANY,
                                          choices=["AC1M", "AC10M", "DC1M", "DC10M", "DCAuto"],
                                          style=wx.CB_DROPDOWN | wx.CB_READONLY)

        self.label_fs_report = wx.StaticText(self.left_panel, wx.ID_ANY, "--")
        self.label_samples_report = wx.StaticText(self.left_panel, wx.ID_ANY, "--")
        self.label_aperture_report = wx.StaticText(self.left_panel, wx.ID_ANY, "--")
        self.text_rms_report = wx.TextCtrl(self.left_panel, wx.ID_ANY, "", style=wx.TE_READONLY)
        self.text_thdn_report = wx.TextCtrl(self.left_panel, wx.ID_ANY, "", style=wx.TE_READONLY)
        self.text_thd_report = wx.TextCtrl(self.left_panel, wx.ID_ANY, "", style=wx.TE_READONLY)

        self.btn_start = wx.Button(self.left_panel, wx.ID_ANY, "RUN")
        self.combo_selected_test = wx.ComboBox(self.left_panel, wx.ID_ANY,
                                               choices=["Single", "Sweep",
                                                        "Single w/ shunt", "Sweep w/ shunt",
                                                        "Continuous"],
                                               style=wx.CB_DROPDOWN)
        self.btn_breakpoints = wx.Button(self.left_panel, wx.ID_ANY, "Breakpoints")

        # PLOT Panel ---------------------------------------------------------------------------------------------------
        self.figure = plt.figure(figsize=(1, 1))  # look into Figure((5, 4), 75)
        self.canvas = FigureCanvas(self.plot_panel, -1, self.figure)
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Realize()

        self.ax1 = self.figure.add_subplot(211)
        self.ax2 = self.figure.add_subplot(212)

        self.temporal, = self.ax1.plot([], [], linestyle='-')
        self.spectral, = self.ax2.plot([], [], color='#C02942')

        # BINDINGS =====================================================================================================
        # Configure Instruments ----------------------------------------------------------------------------------------
        on_DUT_selection = lambda event: self._get_DUT_choice(event)
        self.Bind(wx.EVT_COMBOBOX_CLOSEUP, on_DUT_selection, self.combo_DUT_choice)

        on_connect = lambda event: self.on_connect_instr(event)
        self.Bind(wx.EVT_BUTTON, on_connect, self.btn_connect)

        on_config = lambda event: self.config(event)
        self.Bind(wx.EVT_BUTTON, on_config, self.btn_config)

        on_mainlobe = lambda event: self.on_mainlobe_change(event)
        self.Bind(wx.EVT_COMBOBOX_CLOSEUP, on_mainlobe, self.combo_mainlobe)

        # Run Measurement (start subprocess) ---------------------------------------------------------------------------
        on_run_event = lambda event: self.on_run(event)
        self.Bind(wx.EVT_BUTTON, on_run_event, self.btn_start)

        on_open_breakpoints = lambda event: open_breakpoints()
        self.Bind(wx.EVT_BUTTON, on_open_breakpoints, self.btn_breakpoints)

        on_toggle = lambda event: self.toggle_panel(event)
        self.Bind(wx.EVT_CHECKBOX, on_toggle, self.checkbox_1)

        on_combo_select = lambda event: self.lock_controls(event)
        self.Bind(wx.EVT_COMBOBOX_CLOSEUP, on_combo_select, self.combo_selected_test)

        self.__set_properties()
        self.__do_layout()
        self.__do_plot_layout()

    def __set_properties(self):
        self.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.canvas.SetMinSize((700, 490))

        self.left_panel.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.plot_panel.SetBackgroundColour(wx.Colour(255, 255, 255))

        self.left_panel.SetMinSize((310, 502))
        # self.left_sub_panel.SetBackgroundColour(wx.Colour(255, 0, 255))
        self.plot_panel.SetMinSize((700, 502))

        self.combo_DUT_choice.SetSelection(0)
        self.combo_DUT_choice.SetMinSize((87, 23))
        self.btn_connect.SetMinSize((87, 23))
        self.btn_start.SetMinSize((87, 23))
        self.btn_breakpoints.SetMinSize((87, 23))

        self.text_DUT_report.SetMinSize((200, 23))
        self.text_DMM_report.SetMinSize((200, 23))
        self.canvas.SetMinSize((700, 490))

        self.checkbox_1.SetValue(0)
        self.combo_rms_or_peak.SetSelection(0)

        self.combo_mainlobe.SetSelection(1)
        self.combo_mainlobe.SetMinSize((87, 23))

        self.combo_filter.SetSelection(1)
        self.combo_filter.SetMinSize((110, 23))
        self.combo_coupling.SetSelection(0)
        self.combo_coupling.SetMinSize((110, 23))

        self.combo_selected_test.SetSelection(0)
        self.combo_selected_test.SetMinSize((110, 23))

    def __do_layout(self):
        sizer_2 = wx.GridSizer(1, 1, 0, 0)
        grid_sizer_1 = wx.FlexGridSizer(1, 2, 0, 0)

        grid_sizer_left_panel = wx.GridBagSizer(0, 0)
        grid_sizer_left_sub_panel = wx.GridBagSizer(0, 0)
        grid_sizer_plot = wx.GridBagSizer(0, 0)

        # LEFT PANEL ===================================================================================================
        # TITLE --------------------------------------------------------------------------------------------------------
        row = 0
        label_1 = wx.StaticText(self.left_panel, wx.ID_ANY, "DISTORTION ANALYZER")
        label_1.SetFont(wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_1, (row, 0), (1, 3), 0, 0)

        row += 1
        static_line_1 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_1.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_1, (row, 0), (1, 3), wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        # INSTRUMENT INFO  ---------------------------------------------------------------------------------------------
        row += 1
        grid_sizer_left_panel.Add(self.combo_DUT_choice, (row, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_DUT_report, (row, 1), (1, 2), wx.BOTTOM | wx.LEFT, 5)

        row += 1
        label_DMM = wx.StaticText(self.left_panel, wx.ID_ANY, "Fluke 8588A")
        label_DMM.SetFont(wx.Font(9, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_DMM, (row, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_DMM_report, (row, 1), (1, 2), wx.BOTTOM | wx.LEFT, 5)

        row += 1
        grid_sizer_left_panel.Add(self.btn_connect, (row, 0), (1, 1), wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.btn_config, (row, 1), (1, 1), wx.BOTTOM | wx.LEFT, 5)
        grid_sizer_left_panel.Add(self.checkbox_1, (row, 2), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)

        # f5560A SETUP -------------------------------------------------------------------------------------------------
        row += 1
        self.label_source.SetFont(wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(self.label_source, (row, 0), (1, 2), wx.TOP, 10)

        row += 1
        static_line_2 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_2.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_2, (row, 0), (1, 3), wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        # SUB PANEL  ---------------------------------------------------------------------------------------------------
        row += 1
        label_amplitude = wx.StaticText(self.left_sub_panel, wx.ID_ANY, "Amplitude:")
        label_amplitude.SetMinSize((87, 16))
        grid_sizer_left_sub_panel.Add(label_amplitude, (0, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_sub_panel.Add(self.text_amplitude, (0, 1), (1, 1),
                                      wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)
        grid_sizer_left_sub_panel.Add(self.combo_rms_or_peak, (0, 2), (1, 1),
                                      wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)

        label_frequency = wx.StaticText(self.left_sub_panel, wx.ID_ANY, "Frequency (Ft):")
        label_frequency.SetMinSize((87, 16))
        grid_sizer_left_sub_panel.Add(label_frequency, (1, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_sub_panel.Add(self.text_frequency, (1, 1), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
        label_Hz = wx.StaticText(self.left_sub_panel, wx.ID_ANY, "(Hz)")
        grid_sizer_left_sub_panel.Add(label_Hz, (1, 2), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)

        self.left_sub_panel.SetSizer(grid_sizer_left_sub_panel)
        grid_sizer_left_panel.Add(self.left_sub_panel, (row, 0), (1, 3), wx.LEFT, 0)

        # Measurement --------------------------------------------------------------------------------------------------
        row += 1
        label_measure = wx.StaticText(self.left_panel, wx.ID_ANY, "Measurement")
        label_measure.SetFont(wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_measure, (row, 0), (1, 3), wx.TOP, 10)

        row += 1
        static_line_3 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_3.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_3, (row, 0), (1, 3), wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        row += 1
        grid_sizer_left_panel.Add(self.combo_mainlobe, (row, 0), (1, 1), 0, 0)
        grid_sizer_left_panel.Add(self.text_mainlobe, (row, 1), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)
        grid_sizer_left_panel.Add(self.label_mainlobe, (row, 2), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)

        row += 1
        label_filter = wx.StaticText(self.left_panel, wx.ID_ANY, "Filter:")
        grid_sizer_left_panel.Add(label_filter, (row, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)
        grid_sizer_left_panel.Add(self.combo_filter, (row, 1), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)

        row += 1
        label_coupling = wx.StaticText(self.left_panel, wx.ID_ANY, "Coupling:")
        grid_sizer_left_panel.Add(label_coupling, (row, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)
        grid_sizer_left_panel.Add(self.combo_coupling, (row, 1), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)

        row += 1
        label_fs = wx.StaticText(self.left_panel, wx.ID_ANY, "Fs:")
        grid_sizer_left_panel.Add(label_fs, (row, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)
        grid_sizer_left_panel.Add(self.label_fs_report, (row, 1), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)

        row += 1
        label_samples = wx.StaticText(self.left_panel, wx.ID_ANY, "Samples:")
        grid_sizer_left_panel.Add(label_samples, (row, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)
        grid_sizer_left_panel.Add(self.label_samples_report, (row, 1), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)

        row += 1
        label_aperture = wx.StaticText(self.left_panel, wx.ID_ANY, "Aperture:")
        grid_sizer_left_panel.Add(label_aperture, (row, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)
        grid_sizer_left_panel.Add(self.label_aperture_report, (row, 1), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)

        # REPORT -------------------------------------------------------------------------------------------------------
        row += 1
        static_line_4 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_4.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_4, (row, 0), (1, 3), wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        row += 1
        label_rms = wx.StaticText(self.left_panel, wx.ID_ANY, "RMS:")
        grid_sizer_left_panel.Add(label_rms, (row, 0), (1, 1), 0, 0)
        grid_sizer_left_panel.Add(self.text_rms_report, (row, 1), (1, 1), wx.BOTTOM | wx.LEFT, 5)

        row += 1
        label_thdn = wx.StaticText(self.left_panel, wx.ID_ANY, "THD+N:")
        grid_sizer_left_panel.Add(label_thdn, (row, 0), (1, 1), 0, 0)
        grid_sizer_left_panel.Add(self.text_thdn_report, (row, 1), (1, 1), wx.BOTTOM | wx.LEFT, 5)

        row += 1
        label_thd = wx.StaticText(self.left_panel, wx.ID_ANY, "THD:")
        grid_sizer_left_panel.Add(label_thd, (row, 0), (1, 1), 0, 0)
        grid_sizer_left_panel.Add(self.text_thd_report, (row, 1), (1, 1), wx.BOTTOM | wx.LEFT, 5)

        # BUTTONS ------------------------------------------------------------------------------------------------------
        row += 1
        static_line_9 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_9.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_9, (row, 0), (1, 3), wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        row += 1
        grid_sizer_left_panel.Add(self.btn_start, (row, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL)
        grid_sizer_left_panel.Add(self.combo_selected_test, (row, 1), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
        grid_sizer_left_panel.Add(self.btn_breakpoints, (row, 2), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)

        self.left_panel.SetSizer(grid_sizer_left_panel)

        # PLOT PANEL ===================================================================================================
        grid_sizer_plot.Add(self.canvas, (0, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.Add(self.toolbar, (1, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.AddGrowableRow(0)
        grid_sizer_plot.AddGrowableCol(0)
        self.plot_panel.SetSizer(grid_sizer_plot)

        # add to main panel --------------------------------------------------------------------------------------------
        grid_sizer_1.Add(self.left_panel, 0, wx.EXPAND | wx.RIGHT, 0)
        grid_sizer_1.Add(self.plot_panel, 1, wx.EXPAND, 5)
        grid_sizer_1.AddGrowableRow(0)
        grid_sizer_1.AddGrowableCol(1)

        sizer_2.Add(grid_sizer_1, 0, wx.EXPAND, 0)

        self.SetSizer(sizer_2)
        self.Layout()

    # GET SELECTED INSTRUMENTS =========================================================================================
    def _get_instr_choice(self, type, selection, text_ctrl):
        # Get [NEW] instr choice ---------------------------------------------------------------------------------------
        print(f"The {selection} has been selected.")
        new_choice = 'f' + selection.strip('Fluke ')

        # Get [PREVIOUS] instr choice ----------------------------------------------------------------------------------
        if type.lower() == 'dut':
            current_choice = self.da.DUT_choice
        elif type.lower() == 'dmm':
            current_choice = self.da.DMM_choice
        else:
            raise ValueError("invalid instrument type. Please specify whether 'dut' or 'dmm'.")

        # Conditionally refresh GUI ------------------------------------------------------------------------------------
        if not self.da.M.connected:
            # if instruments are not connected, then user is free to change the DMM choice
            pass

        elif self.da.DUMMY_DATA:
            # if using dummy data, then we only need to set DMM choice to True before running
            self.da.DMM_choice = self.DMM_choice

        elif new_choice != current_choice and self.da.M.connected:
            # if the selected instrument does not currently match the remote instrument connected, there's a problem.
            self.da.M.connected = False
            print(f"[WARNING] the {selection} is NOT the current remote instrument connected!")
            # set text box color to red (chantilly: #EAB9C1)
            text_ctrl.SetBackgroundColour(wx.Colour(234, 185, 193))
            self.left_panel.Refresh()

        elif new_choice == self.da.DMM_choice:
            # if the selected instrument does match the remote instrument connected, reset the color if necessary
            self.da.M.connected = True
            # Reset color (white smoke: #F0F0F0)
            text_ctrl.SetBackgroundColour(wx.Colour(240, 240, 240))
            self.left_panel.Refresh()

        return new_choice

    def _get_DUT_choice(self, evt):
        selection = self.combo_DUT_choice.GetValue()
        self.DUT_choice = self._get_instr_choice(type='dut', selection=selection, text_ctrl=self.text_DUT_report)
        self.label_source.SetLabelText(str(selection))

        return self.DUT_choice

    # ------------------------------------------------------------------------------------------------------------------
    def config(self, evt):
        dlg = InstrumentDialog(self, [self.DUT_choice, self.DMM_choice], None, wx.ID_ANY, )
        dlg.ShowModal()
        dlg.Destroy()

    def get_instruments(self):
        config_dict = ReadConfig()

        dut = config_dict[self.DUT_choice]
        dmm = config_dict[self.DMM_choice]

        instruments = {self.DUT_choice: {'address': dut['address'], 'port': dut['port'],
                                         'gpib': dut['gpib'], 'mode': dut['mode']},
                       'f8588A': {'address': dmm['address'], 'port': dmm['port'],
                                  'gpib': dmm['gpib'], 'mode': dmm['mode']}}

        return instruments

    def set_ident(self, idn_dict):
        self.text_DUT_report.SetValue(idn_dict['DUT'])  # DUT
        self.text_DMM_report.SetValue(idn_dict['DMM'])  # current DMM

    def on_connect_instr(self, evt):
        wait = wx.BusyCursor()
        msg = "Establishing remote connections to instruments."
        busyDlg = wx.BusyInfo(msg, parent=self)

        print('\nResetting connection. Closing communication with any connected instruments')
        self.text_DUT_report.Clear()
        self.text_DMM_report.Clear()
        self.da.DUT_choice = self.DUT_choice
        self.da.DMM_choice = self.DMM_choice
        # self.thread_this(self.da.connect, (self.get_instruments(),))
        self.da.connect(self.get_instruments(),)

        busyDlg = None
        del wait

    # ------------------------------------------------------------------------------------------------------------------
    def toggle_panel(self, evt):
        local = self.checkbox_1.GetValue()
        if not local:
            if not self.left_sub_panel.IsShown():
                self.left_sub_panel.Show()
                print(f"{self.DUT_choice} is in REMOTE and will be controlled by software")
        else:
            self.left_sub_panel.Hide()
            print(f"{self.DUT_choice} is in LOCAL and will not be controlled by software")

    def lock_controls(self, evt):
        local = self.checkbox_1.GetValue()
        choice = self.combo_selected_test.GetSelection()
        if choice in (1, 3):
            if local:
                self.checkbox_1.SetValue(0)
                self.toggle_panel(evt)
            self.checkbox_1.Disable()
            self.text_amplitude.Disable()
            self.combo_rms_or_peak.Disable()
            self.text_frequency.Disable()
        else:
            self.checkbox_1.Enable()
            self.text_amplitude.Enable()
            self.combo_rms_or_peak.Enable()
            self.text_frequency.Enable()

    def toggle_controls(self):
        if self.text_amplitude.IsEnabled():
            self.checkbox_1.Disable()
            self.text_amplitude.Disable()
            self.combo_rms_or_peak.Disable()
            self.text_frequency.Disable()
        else:
            self.checkbox_1.Enable()
            self.text_amplitude.Enable()
            self.combo_rms_or_peak.Enable()
            self.text_frequency.Enable()

    def on_mainlobe_change(self, evt):
        value = self.combo_mainlobe.GetValue()
        if value == 'Relative':
            self.text_mainlobe.SetValue('0.1')
            self.label_mainlobe.SetLabelText('(MLW/f0)')
            self.label_mainlobe.SetToolTip("Relative Main Lobe Width (MLW)\nwith respect to the fundamental")
        else:
            self.text_mainlobe.SetValue('100')
            self.label_mainlobe.SetLabelText('MLW (Hz)')
            self.label_mainlobe.SetToolTip("Main Lobe Width")

    # ------------------------------------------------------------------------------------------------------------------
    def get_values(self):
        selected_test = self.combo_selected_test.GetSelection()
        local = self.checkbox_1.GetValue()  # local if True (1)
        mainlobe_type = self.combo_mainlobe.GetValue().lower()
        mainlobe_value = float(self.text_mainlobe.GetValue())
        rms = self.combo_rms_or_peak.GetSelection()

        coupling = self.combo_coupling.GetValue()
        filter = self.combo_filter.GetValue()

        amp_string = self.text_amplitude.GetValue()
        freq_string = self.text_frequency.GetValue()

        self.user_input = {'selected_test': selected_test,
                           'local': local,
                           'amplitude': amp_string,
                           'frequency': freq_string,
                           'rms': rms,
                           'coupling': coupling,
                           'mainlobe_type': mainlobe_type,
                           'mainlobe_value': mainlobe_value,
                           'filter': filter
                           }

    # ------------------------------------------------------------------------------------------------------------------
    def thread_this(self, func, arg=()):
        self.t = threading.Thread(target=func, args=arg, daemon=True)
        self.t.start()

    # ------------------------------------------------------------------------------------------------------------------
    def on_run(self, evt):
        self.get_values()
        if not self.t.is_alive() and self.flag_complete:
            # start new thread
            self.thread_this(self.da.start, (self.user_input,))
            self.btn_start.SetLabel('STOP')

        elif self.t.is_alive() and self.user_input['selected_test'] in (1, 4):
            # stop continuous
            # https://stackoverflow.com/a/36499538
            self.t.do_run = False
            self.btn_start.SetLabel('RUN')
        else:
            print('thread already running.')

    # ------------------------------------------------------------------------------------------------------------------
    def __do_plot_layout(self):
        self.ax1.set_title('SAMPLED TIMED SERIES DATA')
        self.ax1.set_xlabel('TIME (ms)')
        self.ax1.set_ylabel('AMPLITUDE')
        self.ax2.set_title('DIGITIZED WAVEFORM SPECTRAL RESPONSE')
        self.ax2.set_xlabel('FREQUENCY (kHz)')
        self.ax2.set_ylabel('MAGNITUDE (dB)')
        self.ax2.grid()
        self.figure.align_ylabels([self.ax1, self.ax2])
        self.figure.tight_layout()

    def plot(self, params):
        # TEMPORAL -----------------------------------------------------------------------------------------------------
        xt = params['xt']
        yt = params['yt']

        self.temporal.set_data(xt, yt)

        xt_left = params['xt_left']
        xt_right = params['xt_right']
        yt_btm = params['yt_btm']
        yt_top = params['yt_top']
        yt_tick = params['yt_tick']

        self.ax1.set_xlim(left=xt_left, right=xt_right)
        self.ax1.set_yticks(np.arange(yt_btm, yt_top, yt_tick))

        # SPECTRAL -----------------------------------------------------------------------------------------------------
        xf = params['xf']
        yf = params['yf']

        self.spectral.set_data(xf, yf)

        xf_left = params['xf_left']
        xf_right = params['xf_right']
        yf_btm = params['yf_btm']
        yf_top = params['yf_top']

        self.ax2.set_xlim(left=xf_left, right=xf_right)
        self.ax2.set_ylim(bottom=yf_btm, top=yf_top)

        # REDRAW PLOT --------------------------------------------------------------------------------------------------
        self.plot_redraw()

    def plot_redraw(self):
        try:
            self.ax1.relim()  # recompute the ax.dataLim
        except ValueError:
            xt_length = len(self.ax1.get_xdata())
            yt_length = len(self.ax1.get_ydata())
            print(f'Are the lengths of xt: {xt_length} and yt: {yt_length} mismatched?')
            raise
        self.ax1.margins(x=0)
        self.ax1.autoscale(axis='y')

        # UPDATE PLOT FEATURES -----------------------------------------------------------------------------------------
        self.figure.tight_layout()

        self.toolbar.update()  # Not sure why this is needed - ADS
        self.canvas.draw()
        self.canvas.flush_events()

    def results_update(self, results):
        amplitude = results['Amplitude']
        freq_ideal = results['freq_ideal']
        freq_sampled = results['freq_sampled']

        fs = results['Fs']
        N = results['N']
        aperture = results['Aperture']
        yrms = results['yrms']
        units = results['units']
        thdn = results['THDN']
        thd = results['THD']
        rms_noise = results['RMS NOISE']

        self.label_fs_report.SetLabelText(str(fs))
        self.label_samples_report.SetLabelText(str(N))
        self.label_aperture_report.SetLabelText(str(aperture))
        self.text_rms_report.SetValue(f"{'{:0.3e}'.format(yrms)} {units}")
        self.text_thdn_report.SetValue(f"{round(thdn * 100, 3)}% or {round(np.log10(thdn), 1)}dB")
        self.text_thd_report.SetValue(f"{round(thd * 100, 3)}% or {round(np.log10(thd), 1)}dB")

        row = [amplitude, freq_ideal, freq_sampled, yrms, thdn, thd, rms_noise, fs, N, aperture]
        self.frame.append_row(row)

    def error_dialog(self, error_message):
        print(error_message)
        dial = wx.MessageDialog(None, str(error_message), 'Error', wx.OK | wx.ICON_ERROR)
        dial.ShowModal()
示例#17
0
class TestFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        # begin wxGlade: TestFrame.__init__
        kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        self.SetSize((1234, 669))
        self.panel_frame = wx.Panel(self, wx.ID_ANY)

        self.thread = threading.Thread(target=self.run, args=())
        self.thread.daemon = True
        self.row = 0
        self.prevLine = ''
        self.line = ''
        self.table = {}
        self.overlay = {}
        self.ax = None
        self.x, self.y = [0.], [[0.]]
        self.flag_complete = False

        self.panel_main = wx.Panel(self.panel_frame, wx.ID_ANY)
        self.notebook = wx.Notebook(self.panel_main, wx.ID_ANY)
        self.notebook_program = wx.Panel(self.notebook, wx.ID_ANY)
        self.panel_3 = wx.Panel(self.notebook_program, wx.ID_ANY)
        self.text_ctrl_log = wx.TextCtrl(self.panel_3,
                                         wx.ID_ANY,
                                         "",
                                         style=wx.TE_MULTILINE
                                         | wx.TE_READONLY)
        self.text_ctrl_log_entry = wx.TextCtrl(self.panel_3,
                                               wx.ID_ANY,
                                               "",
                                               style=wx.TE_PROCESS_ENTER)
        sys.stdout = self.text_ctrl_log
        self.button_1 = wx.Button(self.panel_3, wx.ID_ANY, "Enter")
        self.notebook_1 = wx.Notebook(self.notebook_program, wx.ID_ANY)
        self.notebook_1_pane_2 = wx.Panel(self.notebook_1, wx.ID_ANY)

        self.figure = plt.figure(figsize=(2,
                                          2))  # look into Figure((5, 4), 75)
        self.canvas = FigureCanvas(self.notebook_1_pane_2, -1, self.figure)
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Realize()
        self.notebook_1_Settings = wx.Panel(self.notebook_1, wx.ID_ANY)
        # Use PropertyGridManger instead, if pages are desired
        self.property_grid_1 = wx.propgrid.PropertyGrid(
            self.notebook_1_Settings, wx.ID_ANY)

        self.notebook_Spreadsheet = wx.Panel(self.notebook, wx.ID_ANY)
        self.grid_1 = MyGrid(self.notebook_Spreadsheet)
        self.btn_run = wx.Button(self.panel_main, wx.ID_ANY, "Run Test")
        # TODO - Pause: https://stackoverflow.com/a/34313474/3382269
        self.btn_pause = wx.Button(self.panel_main, wx.ID_ANY, "Pause")
        self.btn_stop = wx.Button(self.panel_main, wx.ID_ANY, "Stop")
        self.btn_save = wx.Button(self.panel_main, wx.ID_ANY, "Save")

        # Run Measurement (start subprocess)
        on_run_event = lambda event: self.on_run(event)
        self.Bind(wx.EVT_BUTTON, on_run_event, self.btn_run)

        self.Bind(wxpg.EVT_PG_CHANGED, self.OnGridChangeEvent)

        self.__set_properties()
        self.__do_layout()

    def __set_properties(self):
        # begin wxGlade: TestFrame.__set_properties
        self.SetTitle("Test Application")
        self.text_ctrl_log.SetMinSize((580, 410))
        self.text_ctrl_log_entry.SetMinSize((483, 23))
        self.canvas.SetMinSize((585, 406))
        self.grid_1.CreateGrid(31, 16)
        self.grid_1.SetDefaultColSize(150)
        self.notebook.SetMinSize((1200, 500))
        self._create_plot_properties()

    def __do_layout(self):
        # begin wxGlade: TestFrame.__do_layout
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_2 = wx.BoxSizer(wx.VERTICAL)
        grid_sizer_1 = wx.GridBagSizer(0, 0)
        sizer_4 = wx.BoxSizer(wx.VERTICAL)
        grid_sizer_2 = wx.GridSizer(1, 2, 0, 0)
        sizer_5 = wx.BoxSizer(wx.VERTICAL)
        sizer_3 = wx.BoxSizer(wx.VERTICAL)
        grid_sizer_4 = wx.GridBagSizer(0, 0)
        grid_sizer_3 = wx.GridBagSizer(0, 0)
        label_1 = wx.StaticText(self.panel_main, wx.ID_ANY, "Test Application")
        label_1.SetFont(
            wx.Font(20, wx.FONTFAMILY_DECORATIVE, wx.FONTSTYLE_ITALIC,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_1.Add(label_1, (0, 0), (1, 3), wx.ALIGN_CENTER_VERTICAL, 0)
        # bitmap_1 = wx.StaticBitmap(self.panel_main, wx.ID_ANY, wx.Bitmap("Fluke Logo.png", wx.BITMAP_TYPE_ANY))
        # grid_sizer_1.Add(bitmap_1, (0, 3), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.ALL, 0)
        static_line_1 = wx.StaticLine(self.panel_main, wx.ID_ANY)
        static_line_1.SetMinSize((1200, 2))
        grid_sizer_1.Add(static_line_1, (1, 0), (1, 4),
                         wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.TOP, 5)
        grid_sizer_3.Add(self.text_ctrl_log, (0, 0), (1, 2),
                         wx.LEFT | wx.RIGHT | wx.TOP, 10)
        grid_sizer_3.Add(self.text_ctrl_log_entry, (1, 0), (1, 1),
                         wx.ALIGN_CENTER_VERTICAL | wx.ALL, 10)
        grid_sizer_3.Add(self.button_1, (1, 1), (1, 1),
                         wx.ALIGN_CENTER_VERTICAL, 0)
        self.panel_3.SetSizer(grid_sizer_3)
        grid_sizer_2.Add(self.panel_3, 1, wx.EXPAND, 0)

        grid_sizer_4.Add(self.canvas, (0, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_4.Add(self.toolbar, (1, 0), (1, 1), wx.ALL | wx.EXPAND)

        sizer_3.Add(grid_sizer_4, 1, wx.EXPAND, 0)
        self.notebook_1_pane_2.SetSizer(sizer_3)
        sizer_5.Add(self.property_grid_1, 1, wx.EXPAND, 0)
        self.notebook_1_Settings.SetSizer(sizer_5)
        self.notebook_1.AddPage(self.notebook_1_pane_2, "Plot")
        self.notebook_1.AddPage(self.notebook_1_Settings, "Settings")
        grid_sizer_2.Add(self.notebook_1, 1, wx.EXPAND, 0)

        self.notebook_program.SetSizer(grid_sizer_2)
        sizer_4.Add(self.grid_1, 1, wx.EXPAND, 0)
        self.notebook_Spreadsheet.SetSizer(sizer_4)
        self.notebook.AddPage(self.notebook_program, "Program")
        self.notebook.AddPage(self.notebook_Spreadsheet, "Spreadsheet")
        grid_sizer_1.Add(self.notebook, (2, 0), (1, 4), wx.EXPAND, 0)
        static_line_2 = wx.StaticLine(self.panel_main, wx.ID_ANY)
        static_line_2.SetMinSize((1200, 2))
        grid_sizer_1.Add(static_line_2, (3, 0), (1, 4),
                         wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.TOP, 5)
        grid_sizer_1.Add(self.btn_run, (4, 0), (1, 1),
                         wx.ALIGN_RIGHT | wx.RIGHT, 20)
        grid_sizer_1.Add(self.btn_pause, (4, 1), (1, 1),
                         wx.ALIGN_RIGHT | wx.RIGHT, 20)
        grid_sizer_1.Add(self.btn_stop, (4, 2), (1, 1),
                         wx.ALIGN_RIGHT | wx.RIGHT, 10)
        grid_sizer_1.Add(self.btn_save, (4, 3), (1, 1), wx.ALIGN_RIGHT, 0)
        self.panel_main.SetSizer(grid_sizer_1)
        sizer_2.Add(self.panel_main, 1, wx.ALL | wx.EXPAND, 5)
        self.panel_frame.SetSizer(sizer_2)
        sizer_1.Add(self.panel_frame, 1, wx.EXPAND, 0)
        self.SetSizer(sizer_1)
        self.Layout()

    def _create_plot_properties(self):
        pg = self.property_grid_1
        # pg.AddPage("Page 1 - Plot Settings")  # if pages are needed, use PropertyGridManger instead
        dir_path = os.path.dirname(os.path.realpath(__file__))
        pg.Append(wxpg.PropertyCategory("1 - Basic Properties"))
        pg.Append(wxpg.StringProperty(label="Title", value="Title"))
        pg.Append(wxpg.StringProperty(label="X Label", value="X Label"))
        pg.Append(wxpg.StringProperty(label="Y Label", value="Y Label"))
        pg.Append(wxpg.BoolProperty(label="Grid", value=True))
        pg.SetPropertyAttribute(id="Grid", attrName="UseCheckbox", value=True)

        pg.Append(wxpg.PropertyCategory("2 - Data"))
        # https://discuss.wxpython.org/t/wxpython-pheonix-4-0-2-question-regarding-multichoiceproperty-and-setting-the-selection-of-the-choices/30044
        # https://discuss.wxpython.org/t/how-to-set-propertygrid-values/30152/4
        pg.Append(
            wxpg.EnumProperty(
                label="Scale",
                labels=['Linear', 'SemilogX', 'SemilogY', 'LogLog']))
        pg.Append(
            wxpg.EnumProperty(label="X Data",
                              name="X Data",
                              labels=['NaN'],
                              values=[0]))
        pg.Append(
            wxpg.MultiChoiceProperty(label="Y Data",
                                     name='Y Data',
                                     choices=['NaN'],
                                     value=['NaN']))
        pg.Append(
            wxpg.EnumProperty(label="Data Labels",
                              name="Data Labels",
                              labels=['NaN'],
                              values=[0]))

        pg.Append(wxpg.PropertyCategory("3 - Optional Static Plot Overlay"))
        pg.Append(wxpg.FileProperty(label="Overlay Plot",
                                    value=rf"{dir_path}"))
        pg.Append(wxpg.EnumProperty(label="X Axis Variable", labels=['']))
        pg.Append(wxpg.EnumProperty(label="Y Axis Variable", labels=['']))

        pg.Append(wxpg.PropertyCategory("4 - Advanced Properties"))
        pg.Append(wxpg.ArrayStringProperty(label="xLim", value=['0', '100']))
        pg.Append(wxpg.ArrayStringProperty(label="yLim", value=['0', '100']))
        pg.Append(wxpg.DateProperty(label="Date", value=wx.DateTime.Now()))
        pg.Append(wxpg.ColourProperty(label="Line Colour", value='#1f77b4'))
        pg.Append(wxpg.FontProperty(label="Font", value=self.GetFont()))
        pg.Grid.FitColumns()

    def on_run(self, event):
        self.thread.start()

    def run(self):
        print('run!')
        self.flag_complete = False
        T = Test(self)
        T.run()
        self.flag_complete = True

    def OnGridChangeEvent(self, evt):
        pg = self.property_grid_1
        prop = evt.GetProperty()
        if prop.GetName() == 'Overlay Plot':
            self.get_static_overlay_data()
            choices = list(self.overlay.keys())
            pg.GetProperty('X Axis Variable').SetChoices(
                wxpg.PGChoices(['Select option'] + choices))
            pg.GetProperty('Y Axis Variable').SetChoices(
                wxpg.PGChoices(['Select option'] + choices))

        elif prop.GetName(
        ) == 'X Axis Variable' or 'Y Axis Variable' and self.flag_complete:
            self.update_yAxisData()

    def get_static_overlay_data(self):
        pg = self.property_grid_1
        file = pg.GetPropertyValue('Overlay Plot')
        # Read CSV file
        kwargs = {
            'newline': '',
            'encoding': "utf-8-sig"
        }  # https://stackoverflow.com/a/49543191/3382269
        mode = 'r'
        if sys.version_info < (3, 0):
            kwargs.pop('newline', None)
            mode = 'rb'
        with open(f'{file}', mode, **kwargs) as csvfile:
            spamreader = csv.reader(csvfile, delimiter=',', quotechar='"')
            for row in spamreader:
                if not self.overlay:
                    self.overlay = {key: [] for key in row}
                else:
                    for idx, key in enumerate(self.overlay.keys()):
                        self.overlay[key].append(row[idx])

    def write_header(self, header):
        if not self.table:
            self.table = {key: [] for key in header}
        else:
            self.table = {
                header[col]: self.table[key]
                for col, key in enumerate(self.table.keys())
            }

        self.grid_1.write_list_to_row(self.row, self.table.keys())
        self.row += 1

    def write_to_log(self, row_data):
        self.grid_1.write_list_to_row(self.row, row_data)
        self.row += 1

        if not self.table:
            self.table = {
                f'col {idx}': item
                for idx, item in enumerate(row_data)
            }
        else:
            for idx, key in enumerate(self.table.keys()):
                self.table[key].append(row_data[idx])

    def plot_data(self):
        if not self.ax:
            self.draw_2dplot()
        else:
            pg = self.property_grid_1
            # self.x = self.table[pg.GetProperty('X Data').GetValueAsString()]
            self.x = self.table[pg.GetPropertyValueAsString('X Data')]
            self.y = [self.table[col] for col in pg.GetPropertyValue('Y Data')]
            self.update_yAxisData()

    def _plot_helper(self):
        pg = self.property_grid_1
        self.plot = [self.ax.plot] * len(self.y)
        for idx, y in enumerate(self.y):
            # plot returns a list of artists of which you want the first element when changing x or y data
            scale = pg.GetPropertyValueAsString('Scale')
            if scale == 'Linear':
                self.plot[idx], = self.ax.plot(self.x, y)
                self.draw_overlay(self.ax.plot)
            if scale == 'SemilogX':
                self.plot[idx], = self.ax.semilogx(self.x, y)
                self.draw_overlay(self.ax.semilogx)
            if scale == 'SemilogY':
                self.plot[idx], = self.ax.semilogy(self.x, y)
                self.draw_overlay(self.ax.semilogy)
            if scale == 'LogLog':
                self.plot[idx], = self.ax.loglog(self.x, y)
                self.draw_overlay(self.ax.loglog)

    def draw_overlay(self, plot_type):
        pg = self.property_grid_1
        x_var = pg.GetPropertyValueAsString('X Axis Variable')
        y_var = pg.GetPropertyValueAsString('Y Axis Variable')
        if x_var and y_var != ('' or 'Select option'):
            plot_type(self.overlay[x_var], self.overlay[y_var])

    def draw_2dplot(self):
        pg = self.property_grid_1
        if self.table:
            choices = list(self.table.keys())
            pg.GetProperty('X Data').SetChoices(wxpg.PGChoices(choices))
            pg.GetProperty('Y Data').SetChoices(wxpg.PGChoices(choices))
            pg.GetProperty('Data Labels').SetChoices(
                wxpg.PGChoices([""] + choices))
            pg.SetPropertyValue('X Data', choices[0])
            pg.SetPropertyValue('Y Data', [choices[1]])

            self.x, self.y = self.table[choices[0]], [self.table[choices[1]]]
        else:
            self.x, self.y = [0.], [[0.]]

        self.ax = self.figure.add_subplot(111)
        self._plot_helper()

        self.update_axis_labels()
        self.figure.tight_layout()
        self.toolbar.update()  # Not sure why this is needed - ADS

    def update_yAxisData(self):
        self.ax.clear()
        self._plot_helper()

        self.update_data_labels()
        self.update_axis_labels()
        self.ax.relim()
        self.ax.autoscale_view()

        self.canvas.draw()
        self.canvas.flush_events()

    def update_data_labels(self):
        pg = self.property_grid_1
        label_var = pg.GetPropertyValueAsString('Data Labels')
        if label_var:
            for idx, text in enumerate(self.table[label_var]):
                plt.annotate(f'{label_var}={round(text, 3)}',
                             (self.x[idx], self.y[0][idx]),
                             xytext=(0, 10),
                             textcoords='offset pixels',
                             horizontalalignment='center')

    def update_axis_labels(self):
        pg = self.property_grid_1
        self.ax.set_title(pg.GetPropertyValue('Title'),
                          fontsize=15,
                          fontweight="bold")
        self.ax.set_xlabel(pg.GetPropertyValue('X Label'), fontsize=8)
        self.ax.set_ylabel(pg.GetPropertyValue('Y Label'), fontsize=8)
        self.ax.grid(pg.GetPropertyValue('Grid'))
        self.plot[0].set_color(
            tuple(x / 255 for x in pg.GetPropertyValue('Line Colour')))
示例#18
0
class Panel_Graphe(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1, style=wx.TAB_TRAVERSAL)
        self.afficher_valeurs = False

        self.panel = wx.Panel(self,
                              -1,
                              style=wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)

        self.figure = matplotlib.pyplot.figure()
        self.canvas = Canvas(self.panel, -1, self.figure)
        self.canvas.SetMinSize((20, 20))
        self.SetColor((255, 255, 255))

        # Boutons
        self.bouton_apercu = wx.BitmapButton(
            self, -1,
            wx.Bitmap(Chemins.GetStaticPath(u"Images/16x16/Apercu.png"),
                      wx.BITMAP_TYPE_ANY))
        self.bouton_options = wx.BitmapButton(
            self, -1,
            wx.Bitmap(Chemins.GetStaticPath(u"Images/16x16/Mecanisme.png"),
                      wx.BITMAP_TYPE_ANY))

        # Binds
        self.Bind(wx.EVT_BUTTON, self.Apercu, self.bouton_apercu)
        self.Bind(wx.EVT_BUTTON, self.Options, self.bouton_options)

        # Properties
        self.bouton_apercu.SetToolTipString(
            _(u"Cliquez ici pour ouvrir le visualiseur de graphe pour accéder aux fonctions d'export et d'impression"
              ))
        self.bouton_options.SetToolTipString(
            _(u"Cliquez ici pour accéder aux options du graphe"))

        # Layout
        grid_sizer_base = wx.FlexGridSizer(1, 2, 5, 5)

        sizer_canvas = wx.BoxSizer(wx.VERTICAL)
        sizer_canvas.Add(self.canvas, 1, wx.EXPAND, 0)
        self.panel.SetSizer(sizer_canvas)

        grid_sizer_base.Add(self.panel, 1,
                            wx.EXPAND | wx.TOP | wx.LEFT | wx.BOTTOM, 10)

        grid_sizer_boutons = wx.FlexGridSizer(5, 1, 5, 5)
        grid_sizer_boutons.Add(self.bouton_apercu, 0, 0, 0)
        grid_sizer_boutons.Add(self.bouton_options, 0, 0, 0)
        grid_sizer_base.Add(grid_sizer_boutons, 1,
                            wx.EXPAND | wx.TOP | wx.BOTTOM | wx.RIGHT, 10)

        grid_sizer_base.AddGrowableCol(0)
        grid_sizer_base.AddGrowableRow(0)

        self.SetSizer(grid_sizer_base)
        self.Layout()

    def SetColor(self, rgbtuple=None):
        """Set figure and canvas colours to be the same."""
        if rgbtuple is None:
            rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get()
        clr = [c / 255. for c in rgbtuple]
        self.figure.set_facecolor(clr)
        self.figure.set_edgecolor(clr)
        self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple))

    def ConvertCouleur(self, couleur=None):
        return [c / 255. for c in couleur]

    def Apercu(self, event):
        import DLG_Zoom_graphe
        dlg = DLG_Zoom_graphe.Dialog(self, figure=self.figure)
        dlg.ShowModal()
        dlg.Destroy()

    def Options(self, event):
        # Création du menu contextuel
        menuPop = wx.Menu()

        item = wx.MenuItem(menuPop, 10, _(u"Afficher les valeurs"),
                           _(u"Afficher les valeurs"), wx.ITEM_CHECK)
        menuPop.AppendItem(item)
        self.Bind(wx.EVT_MENU, self.On_afficher_valeurs, id=10)
        if self.afficher_valeurs == True: item.Check(True)

        self.PopupMenu(menuPop)
        menuPop.Destroy()

    def On_afficher_valeurs(self, event):
        self.afficher_valeurs = not self.afficher_valeurs
        self.MAJ()

    def SetDictBudget(self, dictBudget=None):
        self.dictBudget = dictBudget
        self.MAJ()

    def MAJ(self):
        self.figure.clear()
        if self.dictBudget == None:
            wx.CallAfter(self.SendSizeEvent)
            return

        # Récupération des données
        from Ol import OL_Suivi_budget
        analyse = OL_Suivi_budget.Analyse(self.dictBudget)
        listeCategories = analyse.GetValeurs()

        listeRealise = []
        listeBudgete = []
        listeLabels = []

        for dictCategorie in listeCategories:
            listeRealise.append(dictCategorie["realise"])
            listeBudgete.append(dictCategorie["plafond"])
            listeLabels.append(dictCategorie["nomCategorie"])

##            if dictCategorie["typeCategorie"] == "debit" :
##                solde = plafond - realise
##            else :
##                solde = realise - plafond

##        # TEST
##        listeIndex = np.arange(len(listeLabels))
##        bar_width = 0.2
##        opacity = 0.4
##
##        ax = self.figure.add_subplot(111)
##        barres = ax.bar(listeIndex, listeRealise, width=bar_width, alpha=opacity, color="g", label=_(u"Réel"))
##        barres = ax.bar(listeIndex + bar_width, listeBudgete, width=bar_width, alpha=opacity, color="b", label=_(u"Budgété"))
##
##        # Formatage des montants sur y
##        majorFormatter = FormatStrFormatter(SYMBOLE + u" %d")
##        ax.yaxis.set_major_formatter(majorFormatter)
##
##        # Affichage des labels x
##        ax.set_xticks(listeIndex + bar_width)
##        ax.set_xticklabels(listeLabels)
##
##        labels = ax.get_xticklabels()
##        setp(labels, rotation=45)
##
##        # Légende
##        props = matplotlib.font_manager.FontProperties(size=10)
##        leg = ax.legend(loc='best', shadow=False, fancybox=True, prop=props)
##        leg.get_frame().set_alpha(0.5)
##
##        # Espaces autour du graph
##        self.figure.subplots_adjust(left=0.12, bottom=0.40, right=None, wspace=None, hspace=None)

# TEST
        listeIndex = np.arange(len(listeLabels))
        bar_height = 0.2
        opacity = 0.4

        ax = self.figure.add_subplot(111)
        barresRealise = ax.barh(listeIndex,
                                listeRealise,
                                height=bar_height,
                                alpha=opacity,
                                color="g",
                                label=_(u"Réel"))
        barresBudgete = ax.barh(listeIndex + bar_height,
                                listeBudgete,
                                height=bar_height,
                                alpha=opacity,
                                color="b",
                                label=_(u"Budgété"))

        # Formatage des montants sur x
        majorFormatter = FormatStrFormatter(u"%d " + SYMBOLE)
        ax.xaxis.set_major_formatter(majorFormatter)

        # Affichage des labels x
        ax.set_yticks(listeIndex + bar_height)
        ax.set_yticklabels(listeLabels)

        def autolabel(rects):
            # attach some text labels
            for rect in rects:
                width = rect.get_width()
                ax.text(width + 20,
                        rect.get_y() + rect.get_height() / 2.,
                        u"%.2f %s" % (int(width), SYMBOLE),
                        ha='left',
                        va='center',
                        fontsize=8,
                        color="grey")

        if self.afficher_valeurs == True:
            autolabel(barresRealise)
            autolabel(barresBudgete)

        # Recherche la largeur de texte max
        largeurMax = 0
        for label in listeLabels:
            if len(label) > largeurMax:
                largeurMax = len(label)

        # Espaces autour du graph
        margeGauche = 0.1 + largeurMax * 0.008
        self.figure.subplots_adjust(left=margeGauche,
                                    right=None,
                                    wspace=None,
                                    hspace=None)

        # Légende
        props = matplotlib.font_manager.FontProperties(size=10)
        leg = ax.legend(loc='best', shadow=False, fancybox=True, prop=props)
        leg.get_frame().set_alpha(0.5)

        # Finalisation
        ax.autoscale_view('tight')
        ##        ax.grid(True)
        ax.figure.canvas.draw()
        wx.CallAfter(self.SendSizeEvent)
        return
示例#19
0
class PlotPanel(BasePanel):
    """
    MatPlotlib 2D plot as a wx.Panel, suitable for embedding
    in any wx.Frame.   This does provide a right-click popup
    menu for configuration, zooming, saving an image of the
    figure, and Ctrl-C for copy-image-to-clipboard.

    For more features, see PlotFrame, which embeds a PlotPanel
    and also provides, a Menu, StatusBar, and Printing support.
    """

    def __init__(self, parent, size=(700, 450), dpi=150, axisbg=None,
                 facecolor=None, fontsize=9, trace_color_callback=None,
                 output_title='plot', with_data_process=True, theme=None,
                 **kws):

        self.trace_color_callback = trace_color_callback
        BasePanel.__init__(self, parent,
                           output_title=output_title, size=size, **kws)

        self.conf = PlotConfig(panel=self, theme=theme,
                               with_data_process=with_data_process)
        self.data_range = {}
        self.win_config = None
        self.cursor_callback = None
        self.lasso_callback = None
        self.cursor_mode = 'zoom'
        self.parent  = parent
        self.figsize = (size[0]*1.0/dpi, size[1]*1.0/dpi)
        self.dpi  = dpi
        self.conf.facecolor = ifnotNone(axisbg, self.conf.facecolor)
        self.conf.facecolor = ifnotNone(facecolor, self.conf.facecolor)

        # axesmargins : margins in px left/top/right/bottom
        self.axesmargins = (30, 30, 30, 30)

        self.BuildPanel()
        self.conf.user_limits = {} # [None, None, None, None]
        self.data_range = {}
        self.conf.zoom_lims = []
        self.conf.axes_traces = {}
        self.use_dates = False
        self.dates_style = None

    def plot(self, xdata, ydata, side='left', title=None,
             xlabel=None, ylabel=None, y2label=None,
             use_dates=False, dates_style=None, **kws):
        """
        create a new plot of x/y data, clearing any existing plot on the panel

        """
        allaxes = self.fig.get_axes()
        if len(allaxes) > 1:
            for ax in allaxes[1:]:
                if ax in self.data_range:
                    self.data_range.pop(ax)
                self.fig.delaxes(ax)

        self.data_range = {}
        self.conf.zoom_lims = []
        self.conf.axes_traces = {}
        self.clear()
        axes = self.axes
        if side == 'right':
            axes = self.get_right_axes()
        self.conf.reset_lines()
        self.conf.yscale = 'linear'
        self.conf.user_limits[axes] = 4*[None]

        if xlabel is not None:
            self.set_xlabel(xlabel, delay_draw=True)
        if ylabel is not None:
            self.set_ylabel(ylabel, delay_draw=True)
        if y2label is not None:
            self.set_y2label(y2label, delay_draw=True)
        if title is not None:
            self.set_title(title, delay_draw=True)
        self.dates_style = ifnotNone(dates_style, self.dates_style)
        self.use_dates = ifnotNone(use_dates, self.use_dates)
        return self.oplot(xdata, ydata, side=side, **kws)


    def oplot(self, xdata, ydata, side='left', label=None, xlabel=None,
              ylabel=None, y2label=None, title=None, dy=None,
              ylog_scale=None, xlog_scale=None, grid=None, xmin=None,
              xmax=None, ymin=None, ymax=None, color=None, style=None,
              drawstyle=None, linewidth=2, marker=None, markersize=None,
              refresh=True, show_legend=None, legend_loc='best',
              legend_on=True, delay_draw=False, bgcolor=None,
              framecolor=None, gridcolor=None, labelfontsize=None,
              titlefontsize=None, legendfontsize=None, fullbox=None,
              axes_style=None, zorder=None, viewpad=None, theme=None,
              use_dates=None, dates_style=None, **kws):
        """
        basic plot method, adding to an existing display

        """
        self.cursor_mode = 'zoom'
        conf = self.conf
        conf.plot_type = 'lineplot'
        axes = self.axes
        if theme is not None:
            conf.set_theme(theme=theme)
        if side == 'right':
            axes = self.get_right_axes()
        # set y scale to log/linear
        if ylog_scale is not None:
            conf.yscale = {False:'linear', True:'log'}[ylog_scale]

        if xlog_scale is not None:
            conf.xscale = {False:'linear', True:'log'}[xlog_scale]

        axes.xaxis.set_major_formatter(FuncFormatter(self.xformatter))
        self.dates_style = ifnotNone(dates_style, self.dates_style)
        self.use_dates = ifnotNone(use_dates, self.use_dates)
        if isinstance(xdata[0], datetime):
            self.use_dates = True

        if self.use_dates:
            # date handling options to get xdate to mpl dates
            #   1. xdate are in datetime: convert to mpl dates
            #   2. xdata are strings: parse with datestr2num
            #   3. xdata are floats:
            #        a) dates_styles=='dates': use directly
            #        b) else: convert as unix timestamp to mpl dates
            x0 = xdata[0]
            dstyle = self.dates_style
            if dstyle is None: dstyle = ''
            if isinstance(x0, datetime):
                xdata = dates.date2num(xdata)
            elif isinstance(x0, str) or dstyle.lower().startswith('str'):
                xdata = dates.datestr2num(xdata)
            elif not dstyle.lower().startswith('dates'):
                xdata = dates.epoch2num(xdata)
        linewidth = ifNone(linewidth, 2)
        conf.viewpad = ifnotNone(viewpad, conf.viewpad)

        if xlabel is not None:
            self.set_xlabel(xlabel, delay_draw=delay_draw)
        if ylabel is not None:
            self.set_ylabel(ylabel, delay_draw=delay_draw)
        if y2label is not None:
            self.set_y2label(y2label, delay_draw=delay_draw)
        if title  is not None:
            self.set_title(title, delay_draw=delay_draw)
        if show_legend is not None:
            conf.set_legend_location(legend_loc, legend_on)
            conf.show_legend = show_legend

        conf.show_grid = ifnotNone(grid, conf.show_grid)

        # set data range for this trace
        # datarange = [min(xdata), max(xdata), min(ydata), max(ydata)]

        if axes not in conf.user_limits:
            conf.user_limits[axes] = [None, None, None, None]

        conf.user_limits[axes][0] = ifnotNone(xmin, conf.user_limits[axes][0])
        conf.user_limits[axes][1] = ifnotNone(xmax, conf.user_limits[axes][1])
        conf.user_limits[axes][2] = ifnotNone(ymin, conf.user_limits[axes][2])
        conf.user_limits[axes][3] = ifnotNone(ymax, conf.user_limits[axes][3])

        if axes == self.axes:
            axes.yaxis.set_major_formatter(FuncFormatter(self.yformatter))
        else:
            axes.yaxis.set_major_formatter(FuncFormatter(self.y2formatter))

        zorder = ifNone(zorder, 5*(conf.ntrace+1))

        if axes not in conf.axes_traces:
            conf.axes_traces[axes] = []
        conf.axes_traces[axes].append(conf.ntrace)

        conf.gridcolor = ifnotNone(gridcolor, conf.gridcolor)
        conf.facecolor = ifnotNone(bgcolor, conf.facecolor)

        if framecolor is not None:
            self.canvas.figure.set_facecolor(framecolor)

        conf.set_trace_zorder(zorder, delay_draw=True)
        if color:
            conf.set_trace_color(color, delay_draw=True)
        if style:
            conf.set_trace_style(style, delay_draw=True)
        if marker:
            conf.set_trace_marker(marker, delay_draw=True)
        if linewidth is not None:
            conf.set_trace_linewidth(linewidth, delay_draw=True)
        if markersize is not None:
            conf.set_trace_markersize(markersize, delay_draw=True)
        if drawstyle is not None:
            conf.set_trace_drawstyle(drawstyle, delay_draw=True)
        if dy is None:
            _lines = axes.plot(xdata, ydata, drawstyle=drawstyle, zorder=zorder)
        else:
            _lines = axes.errorbar(xdata, ydata, yerr=dy, zorder=zorder)

        if axes not in conf.data_save:
            conf.data_save[axes] = []
        conf.data_save[axes].append((xdata, ydata))

        if conf.show_grid and axes == self.axes:
            # I'm sure there's a better way...
            for i in axes.get_xgridlines() + axes.get_ygridlines():
                i.set_color(conf.gridcolor)
                i.set_zorder(-100)
            axes.grid(True)
        else:
            axes.grid(False)

        if (self.conf.xscale == 'log' or self.conf.yscale == 'log'):
            self.set_logscale(xscale=self.conf.xscale,
                              yscale=self.conf.yscale,
                              delay_draw=delay_draw)

        if label is None:
            label = 'trace %i' % (conf.ntrace+1)
        conf.set_trace_label(label, delay_draw=True)
        needs_relabel = False
        if labelfontsize is not None:
            conf.labelfont.set_size(labelfontsize)
            needs_relabel = True
        if titlefontsize is not None:
            conf.titlefont.set_size(titlefontsize)
            needs_relabel = True

        if legendfontsize is not None:
            conf.legendfont.set_size(legendfontsize)
            needs_relabel = True

        if conf.ntrace < len(conf.lines):
            conf.lines[conf.ntrace] = _lines
        else:
            conf.init_trace(conf.ntrace, 'black', 'solid')
            conf.lines.append(_lines)

        # now set plot limits:
        if not delay_draw:
            self.set_viewlimits()

        if refresh:
            conf.refresh_trace(conf.ntrace)
            needs_relabel = True

        if conf.show_legend and not delay_draw:
            conf.draw_legend()

        if needs_relabel and not delay_draw:
            conf.relabel()


        # axes style ('box' or 'open')
        conf.axes_style = 'box'
        if fullbox is not None and not fullbox:
            conf.axes_style = 'open'
        if axes_style in ('open', 'box', 'bottom'):
            conf.axes_style = axes_style
        conf.set_axes_style(delay_draw=delay_draw)
        if not delay_draw:
            self.draw()
            self.canvas.Refresh()
        conf.ntrace = conf.ntrace + 1
        return _lines

    def plot_many(self, datalist, side='left', title=None,
                  xlabel=None, ylabel=None, **kws):
        """
        plot many traces at once, taking a list of (x, y) pairs
        """
        def unpack_tracedata(tdat, **kws):
            if (isinstance(tdat, dict) and
                'xdata' in tdat and 'ydata' in tdat):
                xdata = tdat.pop('xdata')
                ydata = tdat.pop('ydata')
                out = kws
                out.update(tdat)
            elif isinstance(tdat, (list, tuple)):
                out = kws
                xdata = tdat[0]
                ydata = tdat[1]
            return (xdata, ydata, out)

        opts = dict(side=side, title=title, xlabel=xlabel, ylabel=ylabel,
                    delay_draw=True)
        opts.update(kws)
        x0, y0, opts = unpack_tracedata(datalist[0], **opts)

        self.plot(x0, y0, **opts)

        for dat in datalist[1:]:
            x, y, opts = unpack_tracedata(dat, delay_draw=True)
            self.oplot(x, y, **opts)

        self.reset_formats()
        conf = self.conf
        if conf.show_legend:
            conf.draw_legend()
        conf.relabel()
        self.draw()
        self.canvas.Refresh()

    def add_text(self, text, x, y, side='left', size=None,
                 rotation=None, ha='left', va='center',
                 family=None, **kws):
        """add text at supplied x, y position
        """
        axes = self.axes
        if side == 'right':
            axes = self.get_right_axes()
        dynamic_size = False
        if size is None:
            size = self.conf.legendfont.get_size()
            dynamic_size = True
        t = axes.text(x, y, text, ha=ha, va=va, size=size,
                      rotation=rotation, family=family, **kws)
        self.conf.added_texts.append((dynamic_size, t))
        self.draw()

    def add_arrow(self, x1, y1, x2, y2,  side='left',
                  shape='full', color='black',
                  width=0.01, head_width=0.03, overhang=0, **kws):
        """add arrow supplied x, y position"""
        dx, dy = x2-x1, y2-y1

        axes = self.axes
        if side == 'right':
            axes = self.get_right_axes()
        axes.arrow(x1, y1, dx, dy, shape=shape,
                   length_includes_head=True,
                   fc=color, edgecolor=color,
                   width=width, head_width=head_width,
                   overhang=overhang, **kws)
        self.draw()

    def scatterplot(self, xdata, ydata, label=None, size=10,
                    color=None, edgecolor=None,
                    selectcolor=None, selectedge=None,
                    xlabel=None, ylabel=None, y2label=None,
                    xmin=None, xmax=None, ymin=None, ymax=None,
                    viewpad=None, title=None, grid=None, callback=None, **kw):

        if xlabel is not None:
            self.set_xlabel(xlabel)
        if ylabel is not None:
            self.set_ylabel(ylabel)
        if y2label is not None:
            self.set_y2label(y2label)
        if title  is not None:
            self.set_title(title)
        if grid is not None:
            self.conf.show_grid = grid
        if callback is not None:
            self.lasso_callback = callback

        self.conf.plot_type = 'scatter'
        self.cursor_mode = 'lasso'
        if color is not None:
            self.conf.scatter_normalcolor = color
        if edgecolor is not None:
            self.conf.scatter_normaledge  = edgecolor
        if selectcolor is not None:
            self.conf.scatter_selectcolor = selectcolor
        if selectedge is not None:
            self.conf.scatter_selectedge = selectedge
        if viewpad is not None:
            self.conf.viewpad = viewpad

        axes = self.axes
        self.conf.user_limits[axes] = [xmin, xmax, ymin, ymax]

        self.conf.axes_traces = {axes: [0]}
        self.conf.set_trace_label('scatterplot')
        # self.conf.set_trace_datarange((min(xdata), max(xdata),
        #                                min(ydata), max(ydata)))

        self.conf.scatter_xdata = xdata
        self.conf.scatter_ydata = ydata
        self.axes.scatter(xdata, ydata, c=self.conf.scatter_normalcolor,
                          edgecolors=self.conf.scatter_normaledge)

        if self.conf.show_grid:
            for i in axes.get_xgridlines()+axes.get_ygridlines():
                i.set_color(self.conf.gridcolor)
                i.set_zorder(-30)
            axes.grid(True)
        else:
            axes.grid(False)
        xrange = max(xdata) - min(xdata)
        yrange = max(ydata) - min(ydata)

        xmin = min(xdata) - xrange/25.0
        xmax = max(xdata) + xrange/25.0
        ymin = min(ydata) - yrange/25.0
        ymax = max(ydata) + yrange/25.0

        axes.set_xlim((xmin, xmax), emit=True)
        axes.set_ylim((ymin, ymax), emit=True)
        self.set_viewlimits()
        self.draw()

    def lassoHandler(self, vertices):
        conf = self.conf
        if self.conf.plot_type == 'scatter':
            xd, yd = conf.scatter_xdata, conf.scatter_ydata
            sdat = list(zip(xd, yd))
            oldmask = conf.scatter_mask
            try:
                self.axes.scatter(xd[where(oldmask)], yd[where(oldmask)],
                                  s=conf.scatter_size,
                                  c=conf.scatter_normalcolor,
                                  edgecolors=conf.scatter_normaledge)
            except IndexError:
                self.axes.scatter(xd, yd, s=conf.scatter_size,
                                  c=conf.scatter_normalcolor,
                                  edgecolors=conf.scatter_normaledge)

            mask = conf.scatter_mask = inside_poly(vertices, sdat)
            pts = nonzero(mask)[0]
            self.axes.scatter(xd[where(mask)], yd[where(mask)],
                              s=conf.scatter_size,
                              c=conf.scatter_selectcolor,
                              edgecolors=conf.scatter_selectedge)

        else:
            xdata = self.axes.lines[0].get_xdata()
            ydata = self.axes.lines[0].get_ydata()
            sdat = [(x, y) for x, y in zip(xdata, ydata)]
            mask = inside_poly(vertices,sdat)
            pts = nonzero(mask)[0]

        self.lasso = None
        self.draw()
        # self.canvas.draw_idle()
        if (self.lasso_callback is not None and
            hasattr(self.lasso_callback , '__call__')):
            self.lasso_callback(data = sdat,
                                selected=pts, mask=mask)

    def set_xylims(self, limits, axes=None, side='left'):
        "set user-defined limits and apply them"
        if axes is None:
            axes = self.axes
            if side == 'right':
                axes = self.get_right_axes()
        self.conf.user_limits[axes] = list(limits)
        self.unzoom_all()

    def set_viewlimits(self):
        """updates xy limits of a plot based on current data,
        user defined limits, and any zoom level

        """
        self.reset_formats()
        self.conf.set_viewlimits()

    def get_viewlimits(self, axes=None):
        if axes is None: axes = self.axes
        xmin, xmax = axes.get_xlim()
        ymin, ymax = axes.get_ylim()
        return (xmin, xmax, ymin, ymax)

    def clear(self):
        """ clear plot """
        for ax in self.fig.get_axes():
            ax.cla()
        self.conf.ntrace = 0
        self.conf.xlabel = ''
        self.conf.ylabel = ''
        self.conf.y2label = ''
        self.conf.title  = ''
        self.conf.data_save = {}

    def reset_config(self):
        """reset configuration to defaults."""
        self.conf.set_defaults()

    def unzoom(self, event=None, **kws):
        """ zoom out 1 level, or to full data range """
        self.reset_formats()
        self.conf.unzoom(full=False)

    def unzoom_all(self, event=None):
        """ zoom out full data range """
        self.reset_formats()
        self.conf.unzoom(full=True)

    def process_data(self, event=None, expr=None):
        if expr in self.conf.data_expressions:
            self.conf.data_expr = expr
            self.conf.process_data()
            self.draw()
        if expr is None:
            expr = ''
        if self.conf.data_deriv:
            if expr is None:
                expr = 'y'
            expr = "deriv(%s)" % expr
        self.write_message("plotting %s" % expr, panel=0)

    def toggle_deriv(self, evt=None, value=None):
        "toggle derivative of data"
        if value is None:
            self.conf.data_deriv = not self.conf.data_deriv

            expr = self.conf.data_expr or ''
            if self.conf.data_deriv:
                expr = "deriv(%s)" % expr
            self.write_message("plotting %s" % expr, panel=0)

            self.conf.process_data()

    def set_logscale(self, event=None, xscale='linear', yscale='linear',
                     delay_draw=False):
        "set log or linear scale for x, y axis"
        self.conf.set_logscale(xscale=xscale, yscale=yscale,
                               delay_draw=delay_draw)

    def toggle_legend(self, evt=None, show=None):
        "toggle legend display"
        if show is None:
            show = not self.conf.show_legend
            self.conf.show_legend = show
        self.conf.draw_legend()

    def toggle_grid(self, evt=None, show=None):
        "toggle grid display"
        if show is None:
            show = not self.conf.show_grid
        self.conf.enable_grid(show)

    def configure(self, event=None):
        """show configuration frame"""
        if self.win_config is not None:
            try:
                self.win_config.Raise()
            except:
                self.win_config = None

        if self.win_config is None:
            self.win_config = PlotConfigFrame(parent=self,
                                              config=self.conf,
                                              trace_color_callback=self.trace_color_callback)
            self.win_config.Raise()

    ####
    ## create GUI
    ####
    def BuildPanel(self):
        """ builds basic GUI panel and popup menu"""
        self.fig   = Figure(self.figsize, dpi=self.dpi)
        # 1 axes for now
        self.gridspec = GridSpec(1,1)
        self.axes  = self.fig.add_subplot(self.gridspec[0],
                                          facecolor=self.conf.facecolor)
        self.canvas = FigureCanvas(self, -1, self.fig)
        self.canvas.SetClientSize((self.figsize[0]*self.dpi, self.figsize[1]*self.dpi))
        self.canvas.SetMinSize((100, 100))

        self.printer.canvas = self.canvas
        self.set_bg(self.conf.framecolor)
        self.conf.canvas = self.canvas
        self.canvas.SetCursor(wxCursor(wx.CURSOR_CROSS))
        self.canvas.mpl_connect("pick_event", self.__onPickEvent)

        # overwrite ScalarFormatter from ticker.py here:
        self.axes.xaxis.set_major_formatter(FuncFormatter(self.xformatter))
        self.axes.yaxis.set_major_formatter(FuncFormatter(self.yformatter))

        # This way of adding to sizer allows resizing
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas, 2, wx.LEFT|wx.TOP|wx.BOTTOM|wx.EXPAND, 0)
        # self.SetAutoLayout(True)
        self.autoset_margins()
        self.SetSizer(sizer)
        self.SetSize(self.GetBestVirtualSize())

        canvas_draw = self.canvas.draw
        def draw(*args, **kws):
            self.autoset_margins()
            canvas_draw(*args, **kws)
        self.canvas.draw = draw
        self.addCanvasEvents()

    def BuildPopup(self):
        # build pop-up menu for right-click display
        self.popup_menu = popup = wx.Menu()
        MenuItem(self, popup, 'Configure', '',   self.configure)

        MenuItem(self, popup, 'Save Image', '',   self.save_figure)
        popup.AppendSeparator()

        MenuItem(self, popup, 'Undo Zoom/Pan', '',   self.unzoom)
        MenuItem(self, popup, 'Zoom all the way out', '', self.unzoom_all)

        popup.AppendSeparator()
        MenuItem(self, popup, 'Zoom X and Y', '',
                 partial(self.onZoomStyle, style='both x and y'),
                 kind=wx.ITEM_RADIO, checked=True)
        MenuItem(self, popup, 'Zoom X Only', '',
                 partial(self.onZoomStyle, style='x only'),
                 kind=wx.ITEM_RADIO)
        MenuItem(self, popup, 'Zoom Y Only', '',
                 partial(self.onZoomStyle, style='y only'),
                 kind=wx.ITEM_RADIO)

    def onZoomStyle(self, event=None, style='both x and y'):
        self.conf.zoom_style = style

    def _updateCanvasDraw(self):
        """ Overload of the draw function that update
        axes position before each draw"""
        fn = self.canvas.draw
        def draw2(*a,**k):
            self._updateGridSpec()
            return fn(*a,**k)
        self.canvas.draw = draw2

    def get_default_margins(self):
        """get default margins"""
        trans = self.fig.transFigure.inverted().transform

        # Static margins
        l, t, r, b = self.axesmargins
        (l, b), (r, t) = trans(((l, b), (r, t)))

        # Extent
        dl, dt, dr, db = 0, 0, 0, 0
        for i, ax in enumerate(self.fig.get_axes()):
            (x0, y0),(x1, y1) = ax.get_position().get_points()
            try:
                (ox0, oy0), (ox1, oy1) = ax.get_tightbbox(self.canvas.get_renderer()).get_points()
                (ox0, oy0), (ox1, oy1) = trans(((ox0 ,oy0),(ox1 ,oy1)))
                dl = min(0.2, max(dl, (x0 - ox0)))
                dt = min(0.2, max(dt, (oy1 - y1)))
                dr = min(0.2, max(dr, (ox1 - x1)))
                db = min(0.2, max(db, (y0 - oy0)))
            except:
                pass

        return (l + dl, t + dt, r + dr, b + db)

    def autoset_margins(self):
        """auto-set margins  left, bottom, right, top
        according to the specified margins (in pixels)
        and axes extent (taking into account labels,
        title, axis)
        """
        if not self.conf.auto_margins:
            return
        # coordinates in px -> [0,1] in figure coordinates
        trans = self.fig.transFigure.inverted().transform

        # Static margins
        if not self.use_dates:
            self.conf.margins = l, t, r, b = self.get_default_margins()
            self.gridspec.update(left=l, top=1-t, right=1-r, bottom=b)
        # Axes positions update
        for ax in self.fig.get_axes():
            try:
                ax.update_params()
            except ValueError:
                pass
            ax.set_position(ax.figbox)

    def draw(self):
        self.canvas.draw()

    def update_line(self, trace, xdata, ydata, side='left', draw=False,
                    update_limits=True):
        """ update a single trace, for faster redraw """

        x = self.conf.get_mpl_line(trace)
        x.set_data(xdata, ydata)
        # datarange = [xdata.min(), xdata.max(), ydata.min(), ydata.max()]
        # self.conf.set_trace_datarange(datarange, trace=trace)
        axes = self.axes
        if side == 'right':
            axes = self.get_right_axes()

        if update_limits:
            self.set_viewlimits()
        if draw:
            self.draw()

    def get_figure(self):
        return self.fig

    def __onPickEvent(self, event=None):
        """pick events"""
        legline = event.artist
        trace = self.conf.legend_map.get(legline, None)
        visible = True
        if trace is not None and self.conf.hidewith_legend:
            line, legline, legtext = trace
            visible = not line.get_visible()
            line.set_visible(visible)
            if visible:
                legline.set_zorder(10.00)
                legline.set_alpha(1.00)
                legtext.set_zorder(10.00)
                legtext.set_alpha(1.00)
            else:
                legline.set_alpha(0.50)
                legtext.set_alpha(0.50)


    ####
    ## GUI events
    ####
    def report_leftdown(self, event=None):
        if event is None:
            return
        ex, ey = event.x, event.y
        msg = ''
        try:
            x, y = self.axes.transData.inverted().transform((ex, ey))
        except:
            x, y = event.xdata, event.ydata

        if x is not None and y is not None:
            msg = "X,Y= %g, %g" % (x, y)
        if len(self.fig.get_axes()) > 1:
            ax2 = self.fig.get_axes()[1]
            try:
                x2, y2 = ax2.transData.inverted().transform((ex, ey))
                msg = "X,Y,Y2= %g, %g, %g" % (x, y, y2)
            except:
                pass

        nsbar = getattr(self, 'nstatusbar', 1)
        self.write_message(msg,  panel=max(0, nsbar - 2))
        if (self.cursor_callback is not None and
            hasattr(self.cursor_callback , '__call__')):
            self.cursor_callback(x=event.xdata, y=event.ydata)
示例#20
0
class PhaseModulatorPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, wx.ID_ANY)

        self.parent = parent

        self.left_panel = wx.Panel(self, wx.ID_ANY)
        self.plot_panel = wx.Panel(self, wx.ID_ANY, style=wx.SIMPLE_BORDER)

        # PANELS =======================================================================================================
        # LEFT Panel ---------------------------------------------------------------------------------------------------
        self.text_sample_rate = wx.TextCtrl(self.left_panel, wx.ID_ANY, "80000")
        self.text_mainlobe_error = wx.TextCtrl(self.left_panel, wx.ID_ANY, "0.06")

        self.text_carrier_amplitude = wx.TextCtrl(self.left_panel, wx.ID_ANY, "1")
        self.text_carrier_frequency = wx.TextCtrl(self.left_panel, wx.ID_ANY, "1e3")

        self.combo_waveform = wx.ComboBox(self.left_panel, wx.ID_ANY,
                                          choices=["Sine", "Triangle", 'Sawtooth', "Square", "Shift Keying"],
                                          style=wx.CB_DROPDOWN | wx.CB_READONLY)
        self.combo_modulation = wx.ComboBox(self.left_panel, wx.ID_ANY,
                                            choices=["Amplitude", "Frequency", "Phase"],
                                            style=wx.CB_DROPDOWN | wx.CB_READONLY)

        self.text_modulation_index = wx.TextCtrl(self.left_panel, wx.ID_ANY, "1")
        self.text_message_frequency = wx.TextCtrl(self.left_panel, wx.ID_ANY, "100")
        self.text_message_phase = wx.TextCtrl(self.left_panel, wx.ID_ANY, "0")
        self.text_report_rms = wx.TextCtrl(self.left_panel, wx.ID_ANY, "", style=wx.TE_READONLY)
        self.text_report_bw = wx.TextCtrl(self.left_panel, wx.ID_ANY, "", style=wx.TE_READONLY)

        self.btn_start = wx.Button(self.left_panel, wx.ID_ANY, "RUN")
        self.combo_mode = wx.ComboBox(self.left_panel, wx.ID_ANY,
                                      choices=["Single", "Continuous"],
                                      style=wx.CB_DROPDOWN)

        # PLOT Panel ---------------------------------------------------------------------------------------------------
        self.figure = plt.figure(figsize=(1, 1))  # look into Figure((5, 4), 75)
        self.canvas = FigureCanvas(self.plot_panel, -1, self.figure)
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Realize()

        # instance variables -------------------------------------------------------------------------------------------
        self.flag_complete = True  # Flag indicates any active threads (False) or thread completed (True)
        self.t = threading.Thread()
        self.pm = pm(self)
        self.user_input = {}

        # Plot objects -------------------------------------------------------------------------------------------------
        self.ax1 = self.figure.add_subplot(211)
        self.ax2 = self.figure.add_subplot(212)

        self.temporal, = self.ax1.plot([], [], linestyle='-')
        self.temporal_2, = self.ax1.plot([], [], linestyle='--')
        self.spectral, = self.ax2.plot([], [], color='#C02942')

        # Plot Annotations ---------------------------------------------------------------------------------------------
        # https://stackoverflow.com/a/38677732
        self.arrow_dim_obj = self.ax2.annotate("", xy=(0, 0), xytext=(0, 0),
                                               textcoords=self.ax2.transData, arrowprops=dict(arrowstyle='<->'))
        self.bar_dim_obj = self.ax2.annotate("", xy=(0, 0), xytext=(0, 0),
                                             textcoords=self.ax2.transData, arrowprops=dict(arrowstyle='|-|'))
        bbox = dict(fc="white", ec="none")
        self.dim_text = self.ax2.text(0, 0, "", ha="center", va="center", bbox=bbox)

        # BINDINGS =====================================================================================================
        # Run Measurement (start subprocess) ---------------------------------------------------------------------------
        on_run_event = lambda event: self.on_run(event)
        self.Bind(wx.EVT_BUTTON, on_run_event, self.btn_start)

        on_combo_modulation_select = lambda event: self.combo_modulation_select(event)
        self.Bind(wx.EVT_COMBOBOX_CLOSEUP, on_combo_modulation_select, self.combo_modulation)
        self.Bind(wx.EVT_COMBOBOX_CLOSEUP, on_combo_modulation_select, self.combo_waveform)

        self.__set_properties()
        self.__do_layout()
        self.__do_plot_layout()

    def __set_properties(self):
        self.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.canvas.SetMinSize((700, 490))

        self.left_panel.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.plot_panel.SetBackgroundColour(wx.Colour(255, 255, 255))

        self.left_panel.SetMinSize((310, 502))
        # self.left_sub_panel.SetBackgroundColour(wx.Colour(255, 0, 255))
        self.plot_panel.SetMinSize((700, 502))
        self.combo_modulation.SetSelection(0)
        self.combo_waveform.SetSelection(0)
        self.canvas.SetMinSize((700, 490))

        self.combo_mode.SetSelection(0)
        self.combo_mode.SetMinSize((110, 23))

    def __do_layout(self):
        sizer_2 = wx.GridSizer(1, 1, 0, 0)
        grid_sizer_1 = wx.FlexGridSizer(1, 2, 0, 0)
        grid_sizer_left_panel = wx.GridBagSizer(0, 0)
        grid_sizer_left_sub_btn_row = wx.GridBagSizer(0, 0)
        grid_sizer_plot = wx.GridBagSizer(0, 0)

        # LEFT PANEL ===================================================================================================
        # TITLE --------------------------------------------------------------------------------------------------------
        label_1 = wx.StaticText(self.left_panel, wx.ID_ANY, "MODULATION SCHEMES")
        label_1.SetFont(wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_1, (0, 0), (1, 2), 0, 0)

        # static_line_1 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        # static_line_1.SetMinSize((300, 2))
        # grid_sizer_left_panel.Add(static_line_1, (1, 0), (1, 3), wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        # SIMULATION SETUP ---------------------------------------------------------------------------------------------
        label_source = wx.StaticText(self.left_panel, wx.ID_ANY, "Simulation Settings")
        label_source.SetFont(wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_source, (2, 0), (1, 2), wx.TOP, 10)

        static_line_2 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_2.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_2, (3, 0), (1, 3), wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        label_sample_rate = wx.StaticText(self.left_panel, wx.ID_ANY, "Sample Rate")
        grid_sizer_left_panel.Add(label_sample_rate, (4, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_sample_rate, (4, 1), (1, 1), wx.BOTTOM | wx.LEFT, 5)
        label_Hz = wx.StaticText(self.left_panel, wx.ID_ANY, "(Hz)")
        grid_sizer_left_panel.Add(label_Hz, (4, 2), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)

        label_error = wx.StaticText(self.left_panel, wx.ID_ANY, "Error")
        grid_sizer_left_panel.Add(label_error, (5, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_mainlobe_error, (5, 1), (1, 1), wx.BOTTOM | wx.LEFT, 5)
        label_percent = wx.StaticText(self.left_panel, wx.ID_ANY, "(%)")
        grid_sizer_left_panel.Add(label_percent, (5, 2), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)

        # CARRIER SETUP ------------------------------------------------------------------------------------------------
        label_source = wx.StaticText(self.left_panel, wx.ID_ANY, "Carrier Signal")
        label_source.SetFont(wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_source, (6, 0), (1, 1), wx.TOP, 10)

        static_line_2 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_2.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_2, (7, 0), (1, 3), wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        label_carrier_amplitude = wx.StaticText(self.left_panel, wx.ID_ANY, "Amplitude:")
        grid_sizer_left_panel.Add(label_carrier_amplitude, (8, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_carrier_amplitude, (8, 1), (1, 1), wx.BOTTOM | wx.LEFT, 5)

        label_carrier_frequency = wx.StaticText(self.left_panel, wx.ID_ANY, "Frequency:")
        grid_sizer_left_panel.Add(label_carrier_frequency, (9, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_carrier_frequency, (9, 1), (1, 1), wx.LEFT, 5)
        label_Hz = wx.StaticText(self.left_panel, wx.ID_ANY, "(Hz)")
        grid_sizer_left_panel.Add(label_Hz, (9, 2), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)

        # MODULATION SETUP ---------------------------------------------------------------------------------------------
        label_source = wx.StaticText(self.left_panel, wx.ID_ANY, "Message Signal")
        label_source.SetFont(wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_source, (10, 0), (1, 2), wx.TOP, 10)

        static_line_2 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_2.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_2, (11, 0), (1, 3), wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        label_modulation = wx.StaticText(self.left_panel, wx.ID_ANY, "Modulation:")
        grid_sizer_left_panel.Add(label_modulation, (12, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.combo_modulation, (12, 1), (1, 1), wx.EXPAND | wx.BOTTOM | wx.LEFT, 5)

        label_waveform = wx.StaticText(self.left_panel, wx.ID_ANY, "Waveform:")
        grid_sizer_left_panel.Add(label_waveform, (13, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.combo_waveform, (13, 1), (1, 1), wx.EXPAND | wx.BOTTOM | wx.LEFT, 5)

        label_modulation_index = wx.StaticText(self.left_panel, wx.ID_ANY, "Modulation Index:")
        grid_sizer_left_panel.Add(label_modulation_index, (14, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_modulation_index, (14, 1), (1, 1), wx.BOTTOM | wx.LEFT, 5)

        label_message_frequency = wx.StaticText(self.left_panel, wx.ID_ANY, "Frequency:")
        grid_sizer_left_panel.Add(label_message_frequency, (15, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_message_frequency, (15, 1), (1, 1), wx.BOTTOM | wx.LEFT, 5)
        label_Hz = wx.StaticText(self.left_panel, wx.ID_ANY, "(Hz)")
        grid_sizer_left_panel.Add(label_Hz, (15, 2), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT, 5)

        label_message_phase = wx.StaticText(self.left_panel, wx.ID_ANY, "Phase:")
        grid_sizer_left_panel.Add(label_message_phase, (16, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_message_phase, (16, 1), (1, 1), wx.LEFT, 5)
        label_deg = wx.StaticText(self.left_panel, wx.ID_ANY, "(deg)")
        grid_sizer_left_panel.Add(label_deg, (16, 2), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)

        # METRICS (RESULTS) --------------------------------------------------------------------------------------------
        label_source = wx.StaticText(self.left_panel, wx.ID_ANY, "Metrics")
        label_source.SetFont(wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_source, (17, 0), (1, 1), wx.TOP, 10)

        static_line_2 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_2.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_2, (18, 0), (1, 3), wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        label_report_rms = wx.StaticText(self.left_panel, wx.ID_ANY, "RMS:")
        grid_sizer_left_panel.Add(label_report_rms, (19, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_report_rms, (19, 1), (1, 1), wx.BOTTOM | wx.LEFT, 5)

        label_report_bw = wx.StaticText(self.left_panel, wx.ID_ANY, "BW:")
        grid_sizer_left_panel.Add(label_report_bw, (20, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, 5)
        grid_sizer_left_panel.Add(self.text_report_bw, (20, 1), (1, 1), wx.BOTTOM | wx.LEFT, 5)

        # BUTTONS ------------------------------------------------------------------------------------------------------
        static_line_4 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_4.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_4, (21, 0), (1, 3), wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        grid_sizer_left_sub_btn_row.Add(self.btn_start, (0, 0), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
        grid_sizer_left_sub_btn_row.Add(self.combo_mode, (0, 1), (1, 1), wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
        grid_sizer_left_panel.Add(grid_sizer_left_sub_btn_row, (22, 0), (1, 3), wx.ALIGN_TOP | wx.BOTTOM, 13)

        self.left_panel.SetSizer(grid_sizer_left_panel)

        # PLOT PANEL ===================================================================================================
        grid_sizer_plot.Add(self.canvas, (0, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.Add(self.toolbar, (1, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.AddGrowableRow(0)
        grid_sizer_plot.AddGrowableCol(0)
        self.plot_panel.SetSizer(grid_sizer_plot)

        # add to main panel --------------------------------------------------------------------------------------------
        grid_sizer_1.Add(self.left_panel, 0, wx.EXPAND | wx.RIGHT, 5)
        grid_sizer_1.Add(self.plot_panel, 1, wx.EXPAND, 5)
        grid_sizer_1.AddGrowableRow(0)
        grid_sizer_1.AddGrowableCol(1)

        sizer_2.Add(grid_sizer_1, 0, wx.EXPAND, 0)

        self.SetSizer(sizer_2)
        self.Layout()

    # ------------------------------------------------------------------------------------------------------------------
    def toggle_controls(self):
        if self.text_carrier_amplitude.Enabled:
            self.combo_modulation.Disable()
            self.text_sample_rate.Disable()
            self.text_mainlobe_error.Disable()
            self.text_carrier_amplitude.Disable()
            self.text_carrier_frequency.Disable()
            self.text_modulation_index.Disable()
            self.text_message_frequency.Disable()
            self.text_message_phase.Disable()

        else:
            self.combo_modulation.Enable()
            self.text_sample_rate.Enable()
            self.text_mainlobe_error.Enable()
            self.text_carrier_amplitude.Enable()
            self.text_carrier_frequency.Enable()
            self.text_modulation_index.Enable()
            self.text_message_frequency.Enable()
            self.text_message_phase.Enable()

    def combo_modulation_select(self, evt):
        if self.combo_modulation.GetSelection() == 0:
            # amplitude modulation
            self.text_modulation_index.SetValue('1')

        elif self.combo_modulation.GetSelection() == 1:
            # frequency modulation
            self.text_modulation_index.SetValue('5')

        elif self.combo_modulation.GetSelection() == 2:
            # phase modulation
            self.text_modulation_index.SetValue('5')

        else:
            raise ValueError("Invalid modulation selected!")

        # PSK
        if self.combo_waveform.GetSelection() in (3, 4):
            if self.combo_modulation.GetSelection() == 2:
                self.text_message_phase.SetValue('180')
            else:
                self.text_message_phase.SetValue('0')
        else:
            self.text_message_phase.SetValue('0')

    # ------------------------------------------------------------------------------------------------------------------
    def get_values(self):
        mode = self.combo_mode.GetSelection()

        sample_rate = to_float(self.text_sample_rate.GetValue(), property="sample rate")
        main_lobe_error = to_float(self.text_mainlobe_error.GetValue(), property="main lobe error")
        modulation_type = self.combo_modulation.GetSelection()
        waveform_lookup = {0: 'sine', 1: 'triangle', 2: 'sawtooth', 3: 'square', 4: 'keying'}
        waveform_type = waveform_lookup[self.combo_waveform.GetSelection()]
        carrier_amplitude = to_float(self.text_carrier_amplitude.GetValue(), property="carrier amplitude")
        carrier_frequency = to_float(self.text_carrier_frequency.GetValue(), property="carrier frequency")
        modulation_index = to_float(self.text_modulation_index.GetValue(), property="modulation index")
        message_frequency = to_float(self.text_message_frequency.GetValue(), property="message frequency")
        message_phase = to_float(self.text_message_phase.GetValue(), property="message phase")

        self.user_input = {'mode': mode,
                           'sample_rate': sample_rate,
                           'main_lobe_error': main_lobe_error,
                           'waveform_type': waveform_type,
                           'modulation_type': modulation_type,
                           'carrier_amplitude': carrier_amplitude,
                           'carrier_frequency': carrier_frequency,
                           'modulation_index': modulation_index,
                           'message_frequency': message_frequency,
                           'message_phase': message_phase,
                           }

        self.pm.user_values_good = True

    # ------------------------------------------------------------------------------------------------------------------
    def thread_this(self, func, arg=()):
        self.t = threading.Thread(target=func, args=arg, daemon=True)
        self.t.start()

    # ------------------------------------------------------------------------------------------------------------------
    def on_run(self, evt):
        self.get_values()
        if not self.t.is_alive() and self.flag_complete:
            # start new thread
            self.thread_this(self.pm.start, (self.user_input,))
            self.btn_start.SetLabel('STOP')

        elif self.t.is_alive() and self.user_input['mode'] == 1:
            # stop continuous
            # https://stackoverflow.com/a/36499538
            self.t.do_run = False
            self.btn_start.SetLabel('RUN')
        else:
            print('thread already running.')

    # ------------------------------------------------------------------------------------------------------------------
    def __do_plot_layout(self):
        self.ax1.set_title('SAMPLED TIMED SERIES DATA')
        self.ax1.set_xlabel('TIME (ms)')
        self.ax1.set_ylabel('AMPLITUDE')

        self.ax2.set_title('SPECTRAL DATA')
        self.ax2.set_xlabel('FREQUENCY (kHz)')
        self.ax2.set_ylabel('AMPLITUDE (V)')
        self.ax2.grid()
        self.figure.align_ylabels([self.ax1, self.ax2])
        self.figure.tight_layout()

    def plot(self, params):
        # TEMPORAL -----------------------------------------------------------------------------------------------------
        xt = params['xt']
        yt = params['yt']
        mt = params['mt']

        self.temporal.set_data(xt, yt)
        self.temporal_2.set_data(xt, mt)

        xt_left = params['xt_left']
        xt_right = params['xt_right']
        yt_btm = params['yt_btm']
        yt_top = params['yt_top']
        yt_tick = params['yt_tick']

        self.ax1.set_xlim(left=xt_left, right=xt_right)
        # self.ax1.set_yticks(np.arange(yt_btm, yt_top, yt_tick))

        # SPECTRAL -----------------------------------------------------------------------------------------------------
        xf = params['xf']
        yf = params['yf']

        self.spectral.set_data(xf, yf)

        xf_left = params['xf_left']
        xf_right = params['xf_right']
        yf_btm = params['yf_btm']
        yf_top = params['yf_top']
        yf_ticks = params['yf_ticks']

        self.ax2.set_xlim(left=xf_left, right=xf_right)
        self.ax2.set_ylim(bottom=yf_btm, top=yf_top)
        self.ax2.set_yticks(yf_ticks)

        # Annotations --------------------------------------------------------------------------------------------------
        dim_height = params['dim_height']
        dim_left = params['dim_left']
        dim_right = params['dim_right']
        bw = params['bw']

        # Arrow dimension line update ----------------------------------------------------------------------------------
        # https://stackoverflow.com/a/48684902 -------------------------------------------------------------------------
        self.arrow_dim_obj.xy = (dim_left, dim_height)
        self.arrow_dim_obj.set_position((dim_right, dim_height))
        self.arrow_dim_obj.textcoords = self.ax2.transData

        # Bar dimension line update ------------------------------------------------------------------------------------
        self.bar_dim_obj.xy = (dim_left, dim_height)
        self.bar_dim_obj.set_position((dim_right, dim_height))
        self.bar_dim_obj.textcoords = self.ax2.transData

        # dimension text update ----------------------------------------------------------------------------------------
        self.dim_text.set_position((dim_left + (bw / 2), dim_height))
        self.dim_text.set_text(params['bw_text'])

        # REDRAW PLOT --------------------------------------------------------------------------------------------------
        self.plot_redraw()

    def plot_redraw(self):
        try:
            self.ax1.relim()  # recompute the ax.dataLim
        except ValueError:
            xt_length = len(self.ax1.get_xdata())
            yt_length = len(self.ax1.get_ydata())
            print(f'Are the lengths of xt: {xt_length} and yt: {yt_length} mismatched?')
            raise
        self.ax1.margins(x=0)
        self.ax1.autoscale(axis='y')

        # UPDATE PLOT FEATURES -----------------------------------------------------------------------------------------
        self.figure.tight_layout()

        self.toolbar.update()  # Not sure why this is needed - ADS
        self.canvas.draw()
        self.canvas.flush_events()

    def results_update(self, results):
        yrms = results['yrms']
        bw = results['bw']

        self.text_report_rms.SetValue(f"{'{:0.3e}'.format(yrms)}")
        self.text_report_bw.SetValue(f"{'{:0.3e}'.format(bw)}")

    def error_dialog(self, error_message):
        print(error_message)
        dial = wx.MessageDialog(None, str(error_message), 'Error', wx.OK | wx.ICON_ERROR)
        dial.ShowModal()
示例#21
0
class MyDemoPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, wx.ID_ANY)

        self.frame = parent
        self.left_panel = wx.Panel(self, wx.ID_ANY)
        self.plot_panel = wx.Panel(self, wx.ID_ANY, style=wx.SIMPLE_BORDER)

        # PLOT Panel ---------------------------------------------------------------------------------------------------
        self.figure = plt.figure(figsize=(1,
                                          1))  # look into Figure((5, 4), 75)
        self.canvas = FigureCanvas(self.plot_panel, -1, self.figure)
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Realize()

        self.ax1 = self.figure.add_subplot(211)
        self.ax2 = self.figure.add_subplot(212)

        self.temporal, = self.ax1.plot([], [],
                                       linestyle='-',
                                       linewidth=5,
                                       alpha=0.3)
        self.temporal_sampled, = self.ax1.plot([], [],
                                               linestyle='-',
                                               marker='x')
        self.spectral, = self.ax2.plot([], [], color='#C02942')
        self.x, self.y = [], []

        self.text_ctrl_fs = wx.TextCtrl(self.left_panel,
                                        wx.ID_ANY,
                                        style=wx.TE_PROCESS_ENTER)
        self.text_ctrl_ldf = wx.TextCtrl(self.left_panel,
                                         wx.ID_ANY,
                                         style=wx.TE_PROCESS_ENTER)
        self.text_ctrl_ldf.SetToolTip("Lowest Recoverable Frequency")

        self.text_ctrl_samples = wx.TextCtrl(self.left_panel,
                                             wx.ID_ANY,
                                             "",
                                             style=wx.TE_READONLY)
        self.text_ctrl_rms_true = wx.TextCtrl(self.left_panel,
                                              wx.ID_ANY,
                                              "",
                                              style=wx.TE_READONLY)
        self.text_ctrl_rms_sampled = wx.TextCtrl(self.left_panel,
                                                 wx.ID_ANY,
                                                 "",
                                                 style=wx.TE_READONLY)
        self.text_ctrl_rms_delta = wx.TextCtrl(self.left_panel,
                                               wx.ID_ANY,
                                               "",
                                               style=wx.TE_READONLY)

        self.text_ctrl_cycles = wx.TextCtrl(self.left_panel,
                                            wx.ID_ANY,
                                            "",
                                            style=wx.TE_READONLY)
        self.text_ctrl_samples_per_cycle = wx.TextCtrl(self.left_panel,
                                                       wx.ID_ANY,
                                                       "",
                                                       style=wx.TE_READONLY)
        self.text_ctrl_aliased_freq01 = wx.TextCtrl(self.left_panel,
                                                    wx.ID_ANY,
                                                    "",
                                                    style=wx.TE_READONLY)
        self.text_ctrl_aliased_freq02 = wx.TextCtrl(self.left_panel,
                                                    wx.ID_ANY,
                                                    "",
                                                    style=wx.TE_READONLY)
        self.text_ctrl_aliased_freq03 = wx.TextCtrl(self.left_panel,
                                                    wx.ID_ANY,
                                                    "",
                                                    style=wx.TE_READONLY)

        on_update = lambda event: self.update(event)
        self.Bind(wx.EVT_TEXT_ENTER, on_update, self.text_ctrl_fs)
        self.Bind(wx.EVT_TEXT_ENTER, on_update, self.text_ctrl_ldf)

        self.__set_properties()
        self.__do_layout()
        self.__do_plot_layout()
        self.update(wx.Event)

    def __set_properties(self):
        self.SetBackgroundColour(wx.Colour(240, 240, 240))
        self.canvas.SetMinSize((700, 490))

        self.text_ctrl_fs.SetValue('12')
        self.text_ctrl_ldf.SetValue('100')

        # width = 200
        # self.text_ctrl_fs.SetMaxSize((width, 23))
        # self.text_ctrl_ldf.SetMaxSize((width, 23))
        #
        # self.text_ctrl_samples.SetMaxSize((width, 23))
        # self.text_ctrl_rms_true.SetMaxSize((width, 23))
        # self.text_ctrl_rms_sampled.SetMaxSize((width, 23))
        # self.text_ctrl_rms_delta.SetMaxSize((width, 23))
        #
        # self.text_ctrl_cycles.SetMaxSize((width, 23))
        # self.text_ctrl_samples_per_cycle.SetMaxSize((width, 23))
        # self.text_ctrl_aliased_freq01.SetMaxSize((width, 23))
        # self.text_ctrl_aliased_freq02.SetMaxSize((width, 23))
        # self.text_ctrl_aliased_freq03.SetMaxSize((width, 23))

    def __do_layout(self):
        sizer_2 = wx.GridSizer(1, 1, 0, 0)
        grid_sizer_1 = wx.FlexGridSizer(1, 2, 0, 0)
        grid_sizer_plot = wx.GridBagSizer(0, 0)
        grid_sizer_left_panel = wx.GridBagSizer(0, 0)

        # LEFT PANEL ---------------------------------------------------------------------------------------------------
        # TITLE --------------------------------------------------------------------------------------------------------
        row = 0
        label_1 = wx.StaticText(self.left_panel, wx.ID_ANY, "RMS && ALIASING")
        label_1.SetFont(
            wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(label_1, (row, 0), (1, 3),
                                  wx.LEFT | wx.RIGHT | wx.TOP, 5)

        row += 1
        static_line_1 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_1.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_1, (row, 0), (1, 3),
                                  wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        # SIGNAL -------------------------------------------------------------------------------------------------------
        row += 1
        lbl_signal = wx.StaticText(self.left_panel, wx.ID_ANY,
                                   "Signal Characteristics")
        lbl_signal.SetFont(
            wx.Font(9, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(lbl_signal, (row, 0), (1, 3),
                                  wx.LEFT | wx.RIGHT, 5)

        row += 1
        lbl_signal_rms = wx.StaticText(self.left_panel, wx.ID_ANY, "RMS:")
        grid_sizer_left_panel.Add(lbl_signal_rms, (row, 0), (1, 1),
                                  wx.LEFT | wx.RIGHT, 5)
        lbl_signal_rms_val = wx.StaticText(self.left_panel, wx.ID_ANY, "1")
        grid_sizer_left_panel.Add(lbl_signal_rms_val, (row, 1), (1, 2),
                                  wx.BOTTOM, 5)

        row += 1
        lbl_signal_freq = wx.StaticText(self.left_panel, wx.ID_ANY,
                                        "Frequency (f0):")
        grid_sizer_left_panel.Add(lbl_signal_freq, (row, 0), (1, 1),
                                  wx.LEFT | wx.RIGHT, 5)
        lbl_signal_freq_val = wx.StaticText(self.left_panel, wx.ID_ANY,
                                            "1000 Hz")
        grid_sizer_left_panel.Add(lbl_signal_freq_val, (row, 1), (1, 2),
                                  wx.BOTTOM, 5)

        # SETTINGS -----------------------------------------------------------------------------------------------------
        row += 2
        lbl_settings = wx.StaticText(self.left_panel, wx.ID_ANY, "Settings")
        lbl_settings.SetFont(
            wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(lbl_settings, (row, 0), (1, 3),
                                  wx.LEFT | wx.RIGHT, 5)

        row += 1
        static_line_2 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_2.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_2, (row, 0), (1, 3),
                                  wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        row += 1
        lbl_fs = wx.StaticText(self.left_panel, wx.ID_ANY, "Fs:")
        grid_sizer_left_panel.Add(
            lbl_fs, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_fs, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        lbl_units_kHz = wx.StaticText(self.left_panel, wx.ID_ANY, "(kHz):")
        grid_sizer_left_panel.Add(
            lbl_units_kHz, (row, 2), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)

        row += 1
        lbl_ldf = wx.StaticText(self.left_panel, wx.ID_ANY, "LDF:")
        lbl_ldf.SetToolTip("Lowest Recoverable Frequency")
        grid_sizer_left_panel.Add(
            lbl_ldf, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_ldf, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        lbl_units_hz = wx.StaticText(self.left_panel, wx.ID_ANY, "(Hz):")
        grid_sizer_left_panel.Add(
            lbl_units_hz, (row, 2), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)

        # RESULTS ------------------------------------------------------------------------------------------------------
        row += 1
        lbl_results = wx.StaticText(self.left_panel, wx.ID_ANY, "Results")
        lbl_results.SetFont(
            wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(lbl_results, (row, 0), (1, 3),
                                  wx.LEFT | wx.RIGHT, 5)

        row += 1
        static_line_3 = wx.StaticLine(self.left_panel, wx.ID_ANY)
        static_line_3.SetMinSize((300, 2))
        grid_sizer_left_panel.Add(static_line_3, (row, 0), (1, 3),
                                  wx.BOTTOM | wx.RIGHT | wx.TOP, 5)

        row += 1
        lbl_samples = wx.StaticText(self.left_panel, wx.ID_ANY, "Samples (N):")
        grid_sizer_left_panel.Add(
            lbl_samples, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_samples, (row, 1), (1, 2),
                                  wx.BOTTOM, 5)

        row += 1
        lbl_cycles = wx.StaticText(self.left_panel, wx.ID_ANY, "cycles:")
        grid_sizer_left_panel.Add(
            lbl_cycles, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_cycles, (row, 1), (1, 2),
                                  wx.BOTTOM, 5)

        row += 1
        lbl_samples_per_cycle = wx.StaticText(self.left_panel, wx.ID_ANY,
                                              "N/cycles:")
        grid_sizer_left_panel.Add(
            lbl_samples_per_cycle, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_samples_per_cycle, (row, 1),
                                  (1, 2), wx.BOTTOM, 5)

        row += 2
        lbl_rms_true = wx.StaticText(self.left_panel, wx.ID_ANY, "RMS (True):")
        grid_sizer_left_panel.Add(
            lbl_rms_true, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_rms_true, (row, 1), (1, 2),
                                  wx.BOTTOM, 5)

        row += 1
        lbl_rms_sampled = wx.StaticText(self.left_panel, wx.ID_ANY,
                                        "RMS (Sampled):")
        grid_sizer_left_panel.Add(
            lbl_rms_sampled, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_rms_sampled, (row, 1), (1, 2),
                                  wx.BOTTOM, 5)

        row += 1
        lbl_delta = wx.StaticText(self.left_panel, wx.ID_ANY, "Delta RMS:")
        grid_sizer_left_panel.Add(
            lbl_delta, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_rms_delta, (row, 1), (1, 1),
                                  wx.BOTTOM, 5)
        lbl_units_ppm = wx.StaticText(self.left_panel, wx.ID_ANY, "(ppm):")
        grid_sizer_left_panel.Add(
            lbl_units_ppm, (row, 2), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)

        row += 2
        lbl_aliased = wx.StaticText(self.left_panel, wx.ID_ANY,
                                    "Aliased Frequency:")
        lbl_aliased.SetFont(
            wx.Font(9, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_BOLD, 0, ""))
        grid_sizer_left_panel.Add(
            lbl_aliased, (row, 0), (1, 3),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)

        row += 1
        lbl_h1 = wx.StaticText(self.left_panel, wx.ID_ANY, "1st Harmonic:")
        grid_sizer_left_panel.Add(
            lbl_h1, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_aliased_freq01, (row, 1),
                                  (1, 1), wx.BOTTOM, 5)
        lbl_units_hz = wx.StaticText(self.left_panel, wx.ID_ANY, "(kHz):")
        grid_sizer_left_panel.Add(
            lbl_units_hz, (row, 2), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)

        row += 1
        lbl_h2 = wx.StaticText(self.left_panel, wx.ID_ANY, "3rd Harmonic:")
        grid_sizer_left_panel.Add(
            lbl_h2, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_aliased_freq02, (row, 1),
                                  (1, 1), wx.BOTTOM, 5)
        lbl_units_hz = wx.StaticText(self.left_panel, wx.ID_ANY, "(kHz):")
        grid_sizer_left_panel.Add(
            lbl_units_hz, (row, 2), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)

        row += 1
        lbl_h3 = wx.StaticText(self.left_panel, wx.ID_ANY, "5th Harmonic:")
        grid_sizer_left_panel.Add(
            lbl_h3, (row, 0), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)
        grid_sizer_left_panel.Add(self.text_ctrl_aliased_freq03, (row, 1),
                                  (1, 1), wx.BOTTOM, 5)
        lbl_units_hz = wx.StaticText(self.left_panel, wx.ID_ANY, "(kHz):")
        grid_sizer_left_panel.Add(
            lbl_units_hz, (row, 2), (1, 1),
            wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5)

        self.left_panel.SetSizer(grid_sizer_left_panel)

        # PLOT PANEL ===================================================================================================
        grid_sizer_plot.Add(self.canvas, (0, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.Add(self.toolbar, (1, 0), (1, 1), wx.ALL | wx.EXPAND)
        grid_sizer_plot.AddGrowableRow(0)
        grid_sizer_plot.AddGrowableCol(0)
        self.plot_panel.SetSizer(grid_sizer_plot)

        # add to main panel --------------------------------------------------------------------------------------------
        grid_sizer_1.Add(self.left_panel, 0, wx.EXPAND | wx.RIGHT, 5)
        grid_sizer_1.Add(self.plot_panel, 1, wx.EXPAND, 5)
        grid_sizer_1.AddGrowableRow(0)
        grid_sizer_1.AddGrowableCol(1)

        sizer_2.Add(grid_sizer_1, 0, wx.EXPAND, 0)

        self.SetSizer(sizer_2)
        self.Layout()

    def popup_dialog(self, message):
        print(message)
        dial = wx.MessageDialog(None, str(message), 'Error',
                                wx.OK | wx.ICON_ERROR)
        dial.ShowModal()

    def update(self, evt):
        try:
            f0 = 1000
            signal_rms = 1

            Fs = to_float(self.text_ctrl_fs.GetValue()) * 1e3
            LDF = to_float(self.text_ctrl_ldf.GetValue())

            # TEMPORAL -------------------------------------------------------------------------------------------------
            N = getWindowLength(f0,
                                200e3,
                                windfunc='blackman',
                                error=LDF,
                                mainlobe_type='absolute')
            self.x, self.y = ADC(signal_rms, f0, 200e3, N, CONTAINS_HARMONICS,
                                 CONTAINS_NOISE)

            N = getWindowLength(f0,
                                Fs,
                                windfunc='blackman',
                                error=LDF,
                                mainlobe_type='absolute')
            xdt, ydt = ADC(signal_rms, f0, Fs, N, CONTAINS_HARMONICS,
                           CONTAINS_NOISE)

            rms_true = round(
                true_signal(signal_rms, N, CONTAINS_HARMONICS, CONTAINS_NOISE),
                8)
            rms_sampled = round(rms_flat(ydt), 8)
            rms_delta = round(1e6 * (rms_true - rms_sampled), 2)

            cycles = N * f0 / Fs
            samples_per_cycles = N / cycles

            # ALIASING -------------------------------------------------------------------------------------------------
            aliased_freq = [1.0, 3.0, 5.0]

            for idx, harmonic in enumerate(aliased_freq):
                # https://ez.analog.com/partnerzone/fidus-partnerzone/m/file-uploads/212
                Fn = Fs / 2
                nyquist_zone = np.floor(f0 * harmonic / Fn) + 1

                if nyquist_zone % 2 == 0:
                    aliased_freq[idx] = (Fn - (f0 * harmonic % Fn)) / 1000
                else:
                    aliased_freq[idx] = (f0 * harmonic % Fn) / 1000

            # WINDOW ---------------------------------------------------------------------------------------------------
            w = np.blackman(N)

            # Calculate amplitude correction factor after windowing ----------------------------------------------------
            # https://stackoverflow.com/q/47904399/3382269
            amplitude_correction_factor = 1 / np.mean(w)

            # Calculate the length of the FFT --------------------------------------------------------------------------
            if (N % 2) == 0:
                # for even values of N: FFT length is (N / 2) + 1
                fft_length = int(N / 2) + 1
            else:
                # for odd values of N: FFT length is (N + 1) / 2
                fft_length = int((N + 2) / 2)

            # SPECTRAL -------------------------------------------------------------------------------------------------
            xf_fft = np.round(np.fft.fftfreq(N, d=1. / Fs), 6)
            yf_fft = (np.fft.fft(ydt * w) /
                      fft_length) * amplitude_correction_factor

            yf_rfft = yf_fft[:fft_length]
            xf_rfft = np.round(np.fft.rfftfreq(N, d=1. / Fs), 6)  # one-sided

            if aliased_freq[0] == 0:
                fh1 = f0
            else:
                fh1 = 1000 * aliased_freq[0]

            self.plot(fh1, xdt, ydt, fft_length, xf_rfft, yf_rfft)
            self.results_update(N, rms_true, rms_sampled, rms_delta,
                                np.floor(cycles), samples_per_cycles,
                                aliased_freq)
        except ValueError as e:
            self.popup_dialog(e)

    # ------------------------------------------------------------------------------------------------------------------
    def __do_plot_layout(self):
        self.ax1.set_title('SAMPLED TIMED SERIES DATA')
        self.ax1.set_xlabel('TIME (ms)')
        self.ax1.set_ylabel('AMPLITUDE')
        self.ax2.set_title('DIGITIZED WAVEFORM SPECTRAL RESPONSE')
        self.ax2.set_xlabel('FREQUENCY (kHz)')
        self.ax2.set_ylabel('MAGNITUDE (dB)')
        self.ax2.grid()
        self.figure.align_ylabels([self.ax1, self.ax2])
        self.figure.tight_layout()

    def plot(self, f0, xt, yt, fft_length, xf, yf):
        # TEMPORAL -----------------------------------------------------------------------------------------------------
        self.temporal.set_data(self.x * 1000, self.y)
        self.temporal_sampled.set_data(xt * 1000, yt)

        xt_left = 0
        xt_right = 4 / f0  # 4 periods are displayed by default
        self.ax1.set_xlim(left=xt_left * 1000, right=xt_right * 1000)

        # SPECTRAL -----------------------------------------------------------------------------------------------------
        if NORMALIZE_FFT:
            yf_peak = max(abs(yf))
            self.spectral.set_data(xf / 1000,
                                   20 * np.log10(np.abs(yf / yf_peak)))
            self.ax2.set_ylabel('MAGNITUDE (dB)')
        else:
            self.spectral.set_data(xf / 1000, np.abs(yf) / np.sqrt(2))
            self.ax2.set_ylabel('Amplitude (Vrms)')
        try:
            self.ax2.relim()  # recompute the ax.dataLim
        except ValueError:
            print(
                f'Are the lengths of xt: {len(xf)} and yt: {len(yf)} mismatched?'
            )
            raise
        self.ax2.autoscale()

        xf_left = 0
        xf_right = xf[fft_length - 1]
        self.ax2.set_xlim(left=xf_left / 1000, right=xf_right / 1000)

        # REDRAW PLOT --------------------------------------------------------------------------------------------------
        self.plot_redraw()

    def plot_redraw(self):
        try:
            self.ax1.relim()  # recompute the ax.dataLim
        except ValueError:
            xt_length = len(self.ax1.get_xdata())
            yt_length = len(self.ax1.get_ydata())
            print(
                f'Are the lengths of xt: {xt_length} and yt: {yt_length} mismatched?'
            )
            raise
        self.ax1.margins(x=0)
        self.ax1.autoscale(axis='y')

        # UPDATE PLOT FEATURES -----------------------------------------------------------------------------------------
        self.figure.tight_layout()

        self.toolbar.update()  # Not sure why this is needed - ADS
        self.canvas.draw()
        self.canvas.flush_events()

    def results_update(self, N, rms_true, rms_sampled, rms_delta, cycles,
                       samples_per_cycle, aliased_freq):
        self.text_ctrl_samples.SetLabelText(str(N))
        self.text_ctrl_rms_true.SetLabelText(str(rms_true))
        self.text_ctrl_rms_sampled.SetLabelText(str(rms_sampled))
        self.text_ctrl_rms_delta.SetLabelText(str(rms_delta))

        self.text_ctrl_cycles.SetLabelText(str(cycles))
        self.text_ctrl_samples_per_cycle.SetLabelText(str(samples_per_cycle))
        self.text_ctrl_aliased_freq01.SetLabelText(str(aliased_freq[0]))
        self.text_ctrl_aliased_freq02.SetLabelText(str(aliased_freq[1]))
        self.text_ctrl_aliased_freq03.SetLabelText(str(aliased_freq[2]))