Ejemplo n.º 1
0
    def generateChart(self):
        u_genes = self.getUniqueGenes()
        data = dict()
        for gene, tags in u_genes.iteritems():
            if not data.has_key(len(tags)):
                data[len(tags)] = 0
            data[len(tags)] += 1
        data[0] = self._genes_c - len(u_genes.keys())

        xs = list()
        ys = list()
        # Convert the values to %
        for k, v in data.iteritems():
            xs.append(k)
            ys.append((float(v) / self._genes_c))

        fig = Figure()
        ax = fig.add_subplot(111)
        ax.bar(xs, ys, width=0.5, align='center')
        fig.get_axes()[0].set_ylabel('% of genes')
        fig.get_axes()[0].set_xlabel('# of unique tags')
        #fig.get_axes()[0].set_yscale('log')
        canvas = FigureCanvasAgg(fig)
        canvas.print_figure('enzyme-%s-length-%i.png' % \
                            (self.enzyme, self._original_tag_length),
                            dpi=96)
        return data
Ejemplo n.º 2
0
def create_maps(lat1, lon1, lat2, lon2, dpi, winds, n_maps):
    """Return map figure."""
    fig = Figure(figsize=(1600 / dpi, 800 * n_maps / dpi), dpi=dpi)
    fig.set_constrained_layout_pads(w_pad=4. / dpi, h_pad=4. / dpi)
    """Add gcrs between provided points to the map figure."""
    path = get_gcr_points(lat1, lon1, lat2, lon2, n_points=10)

    for i in range(n_maps):
        ax = fig.add_subplot(n_maps + 1,
                             1,
                             i + 1,
                             projection=ccrs.PlateCarree())
        ax.set_extent([lon1, lon2, lat1, lat2], crs=ccrs.PlateCarree())
        ax.add_feature(cf.LAND)
        ax.add_feature(cf.OCEAN)
        ax.add_feature(cf.COASTLINE)
        ax.gridlines(draw_labels=True)

        hour = i // 3 * 3
        u, v, lats, lons = winds[int(hour)]
        ax.barbs(lons,
                 lats,
                 u,
                 v,
                 length=5,
                 sizes=dict(emptybarb=0.25, spacing=0.2, height=0.5),
                 linewidth=0.95)

        lats = [x[0] for x in path]
        lons = [x[1] for x in path]

        ax = fig.get_axes()[0]
        ax.plot(lons, lats, 'r-', transform=ccrs.PlateCarree())
    return fig
Ejemplo n.º 3
0
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        # Sensor figure
        fig = Figure(figsize=(width, height), dpi=dpi)

        # Plot 3D sensor array for now
        width_x, width_y = 250., 50.
        n_pixel_x, n_pixel_y = 3, 3
        radius, nD = 6., 2
        resolution = 10
        mesh = geometry.mesh_3D_sensor(width_x=width_x,
                                       width_y=width_y,
                                       n_pixel_x=n_pixel_x,
                                       n_pixel_y=n_pixel_y,
                                       radius=radius,
                                       nD=nD,
                                       resolution=resolution)

        plot.get_3D_sensor_plot(fig=fig,
                                width_x=width_x,
                                width_y=width_y,
                                radius=radius,
                                nD=nD,
                                n_pixel_x=n_pixel_x,
                                n_pixel_y=n_pixel_y,
                                V_bias=0,
                                V_readout=1,
                                potential_function=None,
                                field_function=None,
                                mesh=mesh,
                                title=None)

        # Init figure
        fig.get_axes()[0].set_aspect('equal')

        FigureCanvas.__init__(self, fig)
        self.setParent(parent)

        FigureCanvas.setSizePolicy(self,
                                   QtWidgets.QSizePolicy.Expanding,
                                   QtWidgets.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)
Ejemplo n.º 4
0
Archivo: widgets.py Proyecto: e-sr/KG
 def case_alg_comparation(cls, obs, algorithm):
     wavPath = obs.export_to_wav()
     # Canvas
     micSn = kg.MicSignal.from_Obs(obs)
     fig = Figure((15, 10))
     fig.set_dpi(110)
     results = algorithm.plot_alg(fig, micSn)
     mpl = {str(algorithm): {'canvas': FigureCanvas(fig),
                             'axHandle': [Bar(ax) for ax in fig.get_axes()],
                             'setTime':True,
                             'animate':True
                             },
             'aa': {'canvas': FigureCanvas(fig),
                             'axHandle': [Bar(ax) for ax in fig.get_axes()],
                             'setTime':True,
                             'animate':True
                             }
            }
     obj =  cls(setup=True, wavPath=wavPath, t0=micSn.get_t0(), mpl=mpl)
     #obj.timer.start()
     return obj
Ejemplo n.º 5
0
    def __init__(self, annotations: AnnotationsType, fig: Figure,
                 annmarkerfacecolor: str, annfacecolor: str, annedgecolor: str):
        self.annotations = annotations
        self.fig = fig
        self.annmarkerfacecolor = annmarkerfacecolor
        self.annfacecolor = annfacecolor
        self.annedgecolor = annedgecolor

        self.drawn_annotation: Dict[Axis, Union[None, tuple]] = {}
        self.shown: Dict[Axis, Union[None, tuple]] = {}
        for ax in fig.get_axes():
            self.shown[ax] = None
            self.drawn_annotation[ax] = None
Ejemplo n.º 6
0
 def _save_fig(self, fig: Figure, title: str = None) -> None:
     # Saves the figure to data/images with title and closes the figure.
     # Default title to axes title if there's only one axes
     if title is None:
         ax_list = fig.get_axes()
         if len(ax_list) == 1:
             title = ax_list[0].get_title()
         else:
             raise ValueError("title cannot be None")
     out_file = os.path.join(
         str(self.test_data_dir),
         title.replace(' ', '_').replace('.', '-') + ".pdf")
     fig.savefig(out_file, bbox_inches='tight', dpi=300)
     plt.close(fig)
Ejemplo n.º 7
0
class streamPick(PyQt4.QtGui.QMainWindow):
    def __init__(self, stream=None, parent=None, ap=None):
        # Initialising PyQt4.QtGui
        if ap is None:
            self.qApp = PyQt4.QtGui.QApplication(sys.argv)
        else:
            self.qApp = ap
        self.KeepGoing = False

        # Init vars
        if stream is None:
            msg = 'Define stream = obspy.core.Stream()'
            raise ValueError(msg)
        self.st = stream.copy()
        self.st.merge()
        self._picks = []
        self.savefile = None
        self.onset_types = ['emergent', 'impulsive', 'questionable']

        # Load filters from pickle if they exist
        try:
            self.bpfilter = pickle.load(open('.pick_filters', 'r'))
        except:
            self.bpfilter = []
        # Internal variables
        # Gui vars
        self._shortcuts = {'st_next': 'c',
                           'st_previous': 'x',
                           'filter_apply': 'f',
                           'pick_p': 'q',
                           'pick_p_end': 'a',
                           'pick_s': 'w',
                           'pick_s_end': 's',
                           'pick_custom': 't',
                           'pick_remove': 'r',
                           'gain_up': '1',
                           'gain_down': '2',
                           'stream_next': 'v'
                           }
        self._plt_drag = None
        self._current_filter = None
        # Init stations
        self._initStations()  # defines list self._stations
        self._stationCycle = cycle(self._stations)
        self._streamStation(self._stationCycle.next())
        # Init PyQt4.QtGui
        PyQt4.QtGui.QMainWindow.__init__(self)
        self.setupUI()
        # exec QtApp
        self.qApp.exec_()
        # self.qApp.deleteLater()

    def setupUI(self):
        '''
        Setup the UI
        '''
        self.main_widget = PyQt4.QtGui.QWidget(self)
        # Init parts of the UI
        self._initMenu()
        self._createStatusBar()
        self._initPlots()
        self._wadatiPlt = None

        # Define layout
        l = PyQt4.QtGui.QVBoxLayout(self.main_widget)
        # self.setLayout(l)

        l.addLayout(self.btnbar)
        l.addWidget(self.canvas)

        self.setCentralWidget(self.main_widget)
        self.setGeometry(300, 300, 1200, 800)
        self.setWindowTitle('obspy.core.Stream-Picker')
        self.show()

    def _killLayout():
        pass

    def _initPlots(self):
        self.fig = Figure(facecolor='.86', dpi=72, frameon=True)
        # Change facecolor
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setFocusPolicy(PyQt4.QtCore.Qt.StrongFocus)
        # Draw the matplotlib figure
        self._drawFig()
        # Connect the events
        self.fig.canvas.mpl_connect('scroll_event',
                                    self._pltOnScroll)
        self.fig.canvas.mpl_connect('motion_notify_event',
                                    self._pltOnDrag)
        self.fig.canvas.mpl_connect('button_release_event',
                                    self._pltOnButtonRelease)
        self.fig.canvas.mpl_connect('button_press_event',
                                    self._pltOnButtonPress)

    def _initMenu(self):
        # Next and Prev Button
        nxt = PyQt4.QtGui.QPushButton('NextSta >>',
                                      shortcut=self._shortcuts['st_next'], parent=self.main_widget)
        nxt.clicked.connect(self._pltNextStation)
        nxt.setToolTip('shortcut <b>c</d>')
        nxt.setMaximumWidth(150)
        prv = PyQt4.QtGui.QPushButton('<< Prev',
                                      shortcut=self._shortcuts['st_previous'], parent=self.main_widget)
        prv.clicked.connect(self._pltPrevStation)
        prv.setToolTip('shortcut <b>x</d>')
        prv.setMaximumWidth(150)

        # Stations drop-down
        self.stcb = PyQt4.QtGui.QComboBox(self)
        for st in self._stations:
            self.stcb.addItem(st)
        self.stcb.activated.connect(self._pltStation)
        self.stcb.setMaximumWidth(100)
        self.stcb.setMinimumWidth(80)

        # Filter buttons
        self.fltrbtn = PyQt4.QtGui.QPushButton('Filter Trace',
                                               shortcut=self._shortcuts['filter_apply'])
        self.fltrbtn.setToolTip('shortcut <b>f</b>')
        self.fltrbtn.setCheckable(True)
        # self.fltrbtn.setAutoFillBackground(True)
        # self.fltrbtn.setStyleSheet(PyQt4.QtCore.QString(
        # 'QPushButton:checked {background-color: lightgreen;}'))
        self.fltrbtn.clicked.connect(self._appFilter)

        self.fltrcb = PyQt4.QtGui.QComboBox(self)
        self.fltrcb.activated.connect(self._changeFilter)
        self.fltrcb.setMaximumWidth(170)
        self.fltrcb.setMinimumWidth(150)
        self._updateFilterCB()  # fill QComboBox

        # edit/delete filer buttons
        fltredit = PyQt4.QtGui.QPushButton('Edit')
        fltredit.resize(fltredit.sizeHint())
        fltredit.clicked.connect(self._editFilter)

        fltrdel = PyQt4.QtGui.QPushButton('Delete')
        fltrdel.resize(fltrdel.sizeHint())
        fltrdel.clicked.connect(self._deleteFilter)

        nxtstr = PyQt4.QtGui.QPushButton('NextStr >>', shortcut=self._shortcuts['stream_next'])
        nxtstr.clicked.connect(self._pltNextStream)
        nxtstr.setToolTip('shortcut <b>v</d>')
        nxtstr.setMaximumWidth(150)

        btnstyle = PyQt4.QtGui.QFrame(fltredit)
        btnstyle.setFrameStyle(PyQt4.QtGui.QFrame.Box | PyQt4.QtGui.QFrame.Plain)
        btnstyle = PyQt4.QtGui.QFrame(fltrdel)
        btnstyle.setFrameStyle(PyQt4.QtGui.QFrame.Box | PyQt4.QtGui.QFrame.Plain)

        # onset type
        _radbtn = []
        for _o in self.onset_types:
            _radbtn.append(PyQt4.QtGui.QRadioButton(str(_o[0].upper())))
            _radbtn[-1].setToolTip('Onset ' + _o)
            _radbtn[-1].clicked.connect(self._drawPicks)
            if _o == 'impulsive':
                _radbtn[-1].setChecked(True)
        self.onsetGrp = PyQt4.QtGui.QButtonGroup()
        self.onsetGrp.setExclusive(True)
        onsetbtns = PyQt4.QtGui.QHBoxLayout()

        for _i, _btn in enumerate(_radbtn):
            self.onsetGrp.addButton(_btn, _i)
            onsetbtns.addWidget(_btn)

        # Arrange buttons
        vline = PyQt4.QtGui.QFrame()
        vline.setFrameStyle(PyQt4.QtGui.QFrame.VLine | PyQt4.QtGui.QFrame.Raised)
        self.btnbar = PyQt4.QtGui.QHBoxLayout()
        self.btnbar.addWidget(prv)
        self.btnbar.addWidget(nxt)
        self.btnbar.addWidget(nxtstr)
        self.btnbar.addWidget(PyQt4.QtGui.QLabel('Station'))
        self.btnbar.addWidget(self.stcb)
        ##
        self.btnbar.addWidget(vline)
        self.btnbar.addWidget(self.fltrbtn)
        self.btnbar.addWidget(self.fltrcb)
        self.btnbar.addWidget(fltredit)
        self.btnbar.addWidget(fltrdel)
        ##
        self.btnbar.addWidget(vline)
        self.btnbar.addWidget(PyQt4.QtGui.QLabel('Pick Onset: '))
        self.btnbar.addLayout(onsetbtns)
        self.btnbar.addStretch(3)

        # Menubar
        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(PyQt4.QtGui.QIcon().fromTheme('document-save'),
                           'Save', self._saveCatalog)
        fileMenu.addAction(PyQt4.QtGui.QIcon().fromTheme('document-save'),
                           'Save as QuakeML File', self._saveCatalogDlg)
        fileMenu.addAction(PyQt4.QtGui.QIcon().fromTheme('document-open'),
                           'Load QuakeML File', self._openCatalogDlg)
        fileMenu.addSeparator()
        fileMenu.addAction('Save Plot', self._savePlotDlg)
        fileMenu.addSeparator()
        fileMenu.addAction(PyQt4.QtGui.QIcon().fromTheme('application-exit'),
                           'Exit', self._hardExit)
        # windowMenu = menubar.addMenu('&Windows')
        # windowMenu.addAction('Wadati Diagram', self._opnWadatiPlot)
        aboutMenu = menubar.addMenu('&About')
        aboutMenu.addAction(PyQt4.QtGui.QIcon().fromTheme('info'),
                            'Info', self._infoDlg)

    def _hardExit(self):
        # self.qApp.deleteLater()

        self.deleteLater()
        # self.close()
        # sys.exit()

    def _drawFig(self):
        '''
        Draws all matplotlib figures
        '''
        num_plots = len(self._current_st)
        self.fig.clear()
        self._appFilter(draw=False)
        for _i, tr in enumerate(self._current_st):
            ax = self.fig.add_subplot(num_plots, 1, _i + 1)
            ax.plot(tr.data, 'k')
            ax.axhline(0, color='k', alpha=.05)
            ax.set_xlim([0, tr.data.size])
            ax.text(.925, .9, self._current_st[_i].stats.channel,
                    transform=ax.transAxes, va='top', ma='left')
            ax.channel = tr.stats.channel
            if _i == 0:
                ax.set_xlabel('Seconds')

        # plot picks
        self._drawPicks(draw=False)
        self.fig.suptitle('%s - %s - %s' % (self._current_st[-1].stats.network,
                                            self._current_st[-1].stats.station,
                                            self._current_st[-1].stats.starttime.isoformat()),
                          x=.2)
        self._updateSB()
        self._canvasDraw()

    def _initStations(self):
        '''
        Creates a list holding unique station names
        '''
        self._stations = []
        for _tr in self.st:
            if _tr.stats.station not in self._stations:
                self._stations.append(_tr.stats.station)
        self._stations.sort()

    def _getPhases(self):
        '''
        Creates a list holding unique phase names
        '''
        phases = []
        for _pick in self._picks:
            if _pick.phase_hint not in phases:
                phases.append(_pick.phase_hint)
        return phases

    def _streamStation(self, station):
        '''
        Copies the current stream object from self.st through
        obspy.stream.select(station=)
        '''
        if station not in self._stations:
            return
        self._current_st = self.st.select(station=station).copy()
        self._current_stname = station
        self._current_network = self._current_st[0].stats.network
        # Sort and detrend streams
        self._current_st.sort(['channel'])
        try:
            self._current_st.detrend('linear')
        except:
            pass

    def _setPick(self, xdata, phase, channel, polarity='undecideable'):
        '''
        Write obspy.core.event.Pick into self._picks list
        '''
        picktime = self._current_st[0].stats.starttime + \
                   (xdata * self._current_st[0].stats.delta)

        this_pick = event.Pick()
        overwrite = True
        # Overwrite existing phase's picktime
        for _pick in self._getPicks():
            if _pick.phase_hint == phase and \
                            _pick.waveform_id.channel_code == channel:
                this_pick = _pick
                overwrite = False
                break

        creation_info = event.CreationInfo(
            author='ObsPy.Stream.pick()',
            creation_time=UTCDateTime())
        # Create new event.Pick()
        this_pick.time = picktime
        this_pick.phase_hint = phase
        this_pick.waveform_id = event.WaveformStreamID(
            network_code=self._current_st[0].stats.network,
            station_code=self._current_st[0].stats.station,
            location_code=self._current_st[0].stats.location,
            channel_code=channel)
        this_pick.evaluation_mode = 'manual'
        this_pick.creation_info = creation_info
        this_pick.onset = self.onset_types[self.onsetGrp.checkedId()]
        this_pick.evaluation_status = 'preliminary'
        this_pick.polarity = polarity
        if self._current_filter is not None:
            this_pick.comments.append(event.Comment(
                text=str(self.bpfilter[self.fltrcb.currentIndex()])))
        if overwrite:
            self._picks.append(this_pick)

    def _delPicks(self, network, station, channel):
        '''
        Deletes pick from catalog
        '''
        for _i, _pick in enumerate(self._picks):
            if _pick.waveform_id.network_code == network \
                    and _pick.waveform_id.station_code == station \
                    and _pick.waveform_id.channel_code == channel:
                self._picks.remove(_pick)

    def _getPicks(self):
        '''
        Create a list of picks for the current plot
        '''
        this_st_picks = []
        for _i, pick in enumerate(self._picks):
            if pick.waveform_id.station_code == self._current_stname and \
                                    self._current_st[0].stats.starttime < \
                                    pick.time < self._current_st[0].stats.endtime:
                this_st_picks.append(_i)
        return [self._picks[i] for i in this_st_picks]

    def _getPickXPosition(self, picks):
        '''
        Convert picktimes into relative positions along x-axis
        '''
        xpicks = []
        for _pick in picks:
            xpicks.append((_pick.time - self._current_st[0].stats.starttime)
                          / self._current_st[0].stats.delta)
        return np.array(xpicks)

    def _drawPicks(self, draw=True):
        '''
        Draw picklines onto axes
        '''
        picks = self._getPicks()
        xpicks = self._getPickXPosition(picks)

        for _ax in self.fig.get_axes():
            lines = []
            labels = []
            transOffset = offset_copy(_ax.transData, fig=self.fig,
                                      x=5, y=0, units='points')
            for _i, _xpick in enumerate(xpicks):
                if picks[_i].phase_hint == 'S':
                    color = 'r'
                elif picks[_i].phase_hint == 'P':
                    color = 'g'
                else:
                    color = 'b'
                if _ax.channel != picks[_i].waveform_id.channel_code:
                    alpha = .1
                else:
                    alpha = .8

                lines.append(matplotlib.lines.Line2D([_xpick, _xpick],
                                                     [_ax.get_ylim()[0] * .9, _ax.get_ylim()[1] * .8],
                                                     color=color, alpha=alpha))
                lines[-1].obspy_pick = picks[_i]

                labels.append(matplotlib.text.Text(_xpick,
                                                   _ax.get_ylim()[0] * .8, text=picks[_i].phase_hint,
                                                   color=color, size=10, alpha=alpha,
                                                   transform=transOffset))

            # delete all artists
            del _ax.artists[0:]
            # add updated objects
            for line in lines:
                _ax.add_artist(line)
            for label in labels:
                _ax.add_artist(label)

        if draw:
            self._canvasDraw()

    # Plot Controls
    def _pltOnScroll(self, event):
        '''
        Scrolls/Redraws the plots along x axis
        '''
        if event.inaxes is None:
            return

        if event.key == 'control':
            axes = [event.inaxes]
        else:
            axes = self.fig.get_axes()

        for _ax in axes:
            left = _ax.get_xlim()[0]
            right = _ax.get_xlim()[1]
            extent = right - left
            dzoom = .2 * extent
            aspect_left = (event.xdata - _ax.get_xlim()[0]) / extent
            aspect_right = (_ax.get_xlim()[1] - event.xdata) / extent

            if event.button == 'up':
                left += dzoom * aspect_left
                right -= dzoom * aspect_right
            elif event.button == 'down':
                left -= dzoom * aspect_left
                right += dzoom * aspect_right
            else:
                return
            _ax.set_xlim([left, right])
        self._canvasDraw()

    def _pltOnDrag(self, event):
        '''
        Drags/Redraws the plot upon drag
        '''
        if event.inaxes is None:
            return

        if event.key == 'control':
            axes = [event.inaxes]
        else:
            axes = self.fig.get_axes()

        if event.button == 2:
            if self._plt_drag is None:
                self._plt_drag = event.xdata
                return
            for _ax in axes:
                _ax.set_xlim([_ax.get_xlim()[0] +
                              (self._plt_drag - event.xdata),
                              _ax.get_xlim()[1] + (self._plt_drag - event.xdata)])
        else:
            return
        self._canvasDraw()

    def _pltOnButtonRelease(self, event):
        '''
        On Button Release Reset drag variable
        '''
        self._plt_drag = None

    def _pltOnButtonPress(self, event):
        '''
        This Function is evoked when the user picks
        '''
        if event.key is not None:
            event.key = event.key.lower()
        if event.inaxes is None:
            return
        channel = event.inaxes.channel
        tr_amp = event.inaxes.lines[0].get_ydata()[int(event.xdata) + 3] - \
                 event.inaxes.lines[0].get_ydata()[int(event.xdata)]
        if tr_amp < 0:
            polarity = 'negative'
        elif tr_amp > 0:
            polarity = 'positive'
        else:
            polarity = 'undecideable'

        if event.key == self._shortcuts['pick_p'] and event.button == 1:
            self._setPick(event.xdata, phase='P', channel=channel,
                          polarity=polarity)
        elif event.key == self._shortcuts['pick_p_end'] and event.button == 1:
            self._setPick(event.xdata, phase='Pend', channel=channel,
                          polarity=polarity)

        elif event.key == self._shortcuts['stream_next']:
            self._pltNextStream()
        elif event.key == self._shortcuts['pick_s_end'] and event.button == 1:
            self._setPick(event.xdata, phase='Send', channel=channel,
                          polarity=polarity)

        elif event.key == self._shortcuts['pick_s'] and event.button == 1:
            self._setPick(event.xdata, phase='S', channel=channel,
                          polarity=polarity)
        elif event.key == self._shortcuts['pick_custom'] and event.button == 1:
            text, ok = PyQt4.QtGui.QInputDialog.getItem(self, 'Custom Phase',
                                                        'Enter phase name:', self._getPhases())
            if ok:
                self._setPick(event.xdata, phase=text, channel=channel,
                              polarity=polarity)
        elif event.key == self._shortcuts['pick_remove']:
            self._delPicks(network=self._current_network,
                           station=self._current_stname,
                           channel=channel)

        elif event.key == self._shortcuts['gain_up'] or self._shortcuts['gain_down']:
            self._adjustGain(event)
        else:
            return
        self._updateSB()
        self._drawPicks()

    def _adjustGain(self, event):
        '''
        Allows the gain to be changed on plot
        '''

        if event.key is not None:
            event.key = event.key.lower()
        if event.inaxes is None:
            return

        if event.inaxes is None:
            return

        if event.key == self._shortcuts['gain_up'] or self._shortcuts['gain_down']:
            axes = [event.inaxes]
        else:
            axes = self.fig.get_axes()

        for _ax in axes:
            up = _ax.get_ylim()[1]
            down = _ax.get_ylim()[0]
            extent = up - down
            dzoom = .2 * extent
            aspect_up = (event.ydata - _ax.get_ylim()[0]) / extent
            aspect_down = (_ax.get_ylim()[1] - event.ydata) / extent

            if event.key == self._shortcuts['gain_up']:
                down += dzoom * aspect_up
                up -= dzoom * aspect_down
            elif event.key == self._shortcuts['gain_down']:
                down -= dzoom * aspect_up
                up += dzoom * aspect_down
            else:
                return
            _ax.set_ylim([down, up])
        self._canvasDraw()

    def _pltNextStation(self):
        '''
        Plot next station
        '''
        self._streamStation(self._stationCycle.next())
        self._drawFig()

    def _pltNextStream(self):
        '''
        Plot next Stream, used primarily in Detex loops when
        streamPick is called to know to exit loop or go 
        to next stream
        '''

        self.KeepGoing = True
        pickle.dump(self.bpfilter, open('.pick_filters', 'w'))
        # self.closeEvent()
        self.deleteLater()

    def _pltPrevStation(self):
        '''
        Plot previous station
        '''
        for _i in range(len(self._stations) - 1):
            prevStation = self._stationCycle.next()
        self._streamStation(prevStation)
        self._drawFig()

    def _pltStation(self):
        '''
        Plot station from DropDown Menu
        '''
        _i = self.stcb.currentIndex()
        while self._stationCycle.next() != self._stations[_i]:
            pass
        self._streamStation(self._stations[_i])
        self._drawFig()

    # Filter functions
    def _appFilter(self, button=True, draw=True):
        '''
        Apply bandpass filter
        '''
        _i = self.fltrcb.currentIndex()
        self._streamStation(self._current_stname)
        if self.fltrbtn.isChecked() is False:
            self._current_filter = None
        else:
            self._current_st.filter('bandpass',
                                    freqmin=self.bpfilter[_i]['freqmin'],
                                    freqmax=self.bpfilter[_i]['freqmax'],
                                    corners=self.bpfilter[_i]['corners'],
                                    zerophase=True)
            self._current_filter = _i
        for _i, _ax in enumerate(self.fig.get_axes()):
            if len(_ax.lines) == 0:
                continue
            _ax.lines[0].set_ydata(self._current_st[_i].data)
            _ax.relim()
            _ax.autoscale_view()
        if draw is True:
            self._drawPicks(draw=False)
            self._canvasDraw()
        self._updateSB()

    def _newFilter(self):
        '''
        Create new filter
        '''
        newFilter = self.defFilter(self)
        if newFilter.exec_():
            self.bpfilter.append(newFilter.getValues())
            self._updateFilterCB()
            self.fltrcb.setCurrentIndex(len(self.bpfilter) - 1)
            self._appFilter()

    def _editFilter(self):
        '''
        Edit existing filter
        '''
        _i = self.fltrcb.currentIndex()
        this_filter = self.bpfilter[_i]
        editFilter = self.defFilter(self, this_filter)
        if editFilter.exec_():
            self.bpfilter[_i] = editFilter.getValues()
            self._updateFilterCB()
            self.fltrcb.setCurrentIndex(_i)
            self._appFilter()

    def _deleteFilter(self):
        '''
        Delete filter
        '''
        pass
        _i = self.fltrcb.currentIndex()

    #        self.fltrbtn.setChecked(False)
    #        self.bpfilter.pop(_i)
    #        self._updateFilterCB()
    #        self._appFilter()

    def _changeFilter(self, index):
        '''
        Evoke this is filter in drop-down is changed
        '''
        if index == len(self.bpfilter):
            return self._newFilter()
        else:
            return self._appFilter()

    def _updateFilterCB(self):
        '''
        Update the filter QComboBox
        '''
        self.fltrcb.clear()
        self.fltrcb.setCurrentIndex(-1)
        for _i, _f in enumerate(self.bpfilter):
            self.fltrcb.addItem('%s [%.2f - %.2f Hz]' % (_f['name'],
                                                         _f['freqmin'], _f['freqmax']))
        self.fltrcb.addItem('Create new Filter...')

    # Status bar functions
    def _createStatusBar(self):
        '''
        Creates the status bar
        '''
        sb = PyQt4.QtGui.QStatusBar()
        sb.setFixedHeight(18)
        self.setStatusBar(sb)
        self.statusBar().showMessage('Ready')

    def _updateSB(self, statustext=None):
        '''
        Updates the statusbar text
        '''
        if statustext is None:
            self.stcb.setCurrentIndex(
                self._stations.index(self._current_stname))
            msg = 'Station %i/%i - %i Picks' % (
                self._stations.index(self._current_stname) + 1,
                len(self._stations), len(self._getPicks()))
            if self._current_filter is not None:
                msg += ' - Bandpass %s [%.2f - %.2f Hz]' % (
                    self.bpfilter[self._current_filter]['name'],
                    self.bpfilter[self._current_filter]['freqmin'],
                    self.bpfilter[self._current_filter]['freqmax'])
            else:
                msg += ' - Raw Data'
            self.statusBar().showMessage(msg)

    def _openCatalogDlg(self):
        filename = PyQt4.QtGui.QFileDialog.getOpenFileName(self,
                                                           'Load QuakeML Picks',
                                                           os.getcwd(), 'QuakeML Format (*.xml)', '20')
        if filename:
            self._openCatalog(str(filename))
            self.savefile = str(filename)

    def _openCatalog(self, filename):
        '''
        Open existing QuakeML catalog
        '''
        try:
            print('Opening QuakeML Catalog %s' % filename)
            cat = event.readEvents(filename)
            self._picks = cat[0].picks
            self._drawPicks()
        except:
            msg = 'Could not open QuakeML file %s' % (filename)
            raise IOError(msg)

    def _saveCatalogDlg(self):
        '''
        Save catalog through QtDialog
        '''
        self.savefile = PyQt4.QtGui.QFileDialog.getSaveFileName(self,
                                                                'Save QuakeML Picks',
                                                                os.getcwd(), 'QuakeML Format (*.xml)')
        if not self.savefile:
            self.savefile = None
            return
        self.savefile = str(self.savefile)
        if os.path.splitext(self.savefile)[1].lower() != '.xml':
            self.savefile += '.xml'
        self._saveCatalog()

    def _saveCatalog(self, filename=None):
        '''
        Saves the catalog to filename
        '''
        if self.savefile is None and filename is None:
            return self._saveCatalogDlg()
        if filename is not None:
            savefile = filename
        else:
            savefile = self.savefile
        cat = event.Catalog()
        cat.events.append(event.Event(picks=self._picks))
        # cat.write(savefile, format='QUAKEML')
        # print 'Picks saved as %s' % savefile

    def _savePlotDlg(self):
        '''
        Save Plot Image Qt Dialog and Matplotlib wrapper
        '''
        filename = PyQt4.QtGui.QFileDialog.getSaveFileName(self, 'Save Plot',
                                                           os.getcwd(),
                                                           'Image Format (*.png *.pdf *.ps *.svg *.eps)')
        if not filename:
            return
        filename = str(filename)
        format = os.path.splitext(filename)[1][1:].lower()
        if format not in ['png', 'pdf', 'ps', 'svg', 'eps']:
            format = 'png'
            filename += '.' + format
        self.fig.savefig(filename=filename, format=format, dpi=72)

    def getPicks(self):
        return self._picks

    def _opnWadatiPlot(self):
        self._wadatiPlt = PyQt4.QtGui.NewWindow()
        self._wadatiPlt.show()

    def _infoDlg(self):
        msg = """
                <h3><b>obspy.core.stream-Picker</b></h3>
                <br><br>
                <div>
                StreamPick is a lightweight seismological
                wave time picker for <code>obspy.core.Stream()</code>
                objects. It further utilises the <code>obspy.core.event</code>
                class to store picks in the QuakeML format.
                </div>
                <h4>Controls:</h4>
                <blockquote>
                <table>
                    <tr>
                        <td width=20><b>%s</b></td><td>Next station</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td><td>Previous station</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td><td>Toggle filter</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Set P-Phase pick at mouse position</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Set S-Phase pick at mouse position</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Set custom phase pick at mouse position</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Remove last pick in trace</td>
                    </tr>
                </table>
                </blockquote>
                <h4>Plot Controls:</h4>
                <blockquote>
                Use mouse wheel to zoom in- and out. Middle mouse button moves
                plot along x-axis.<br>
                Hit <b>Ctrl</b> to manipulate a single plot.
                <br>
                </blockquote>
                <div>
                Programm stores filter parameters in <code>.pick_filter</code>
                and a backup of recent picks in
                <code>.picks-obspy.xml.bak</code>.<br><br>
                See <a href=http://www.github.org/miili/StreamPick>
                http://www.github.org/miili/StreamPick</a> and
                <a href=http://www.obspy.org>http://www.obspy.org</a>
                for further documentation.
                </div>
                """ % (
            self._shortcuts['st_next'],
            self._shortcuts['st_previous'],
            self._shortcuts['filter_apply'],
            self._shortcuts['pick_p'],
            self._shortcuts['pick_s'],
            self._shortcuts['pick_custom'],
            self._shortcuts['pick_remove'],
        )
        PyQt4.QtGui.QMessageBox.about(self, 'About', msg)

    def _canvasDraw(self):
        '''
        Redraws the canvas and re-sets mouse focus
        '''
        for _i, _ax in enumerate(self.fig.get_axes()):
            _ax.set_xticklabels(_ax.get_xticks() *
                                self._current_st[_i].stats.delta)
        self.fig.canvas.draw()
        self.canvas.setFocus()

    def closeEvent(self, evnt):
        '''
        This function is called upon closing the PyQt4.QtGui
        '''
        # Save Picks
        # Save Catalog

    #        if len(self._picks) > 0:
    #            self._saveCatalog('.picks-obspy.xml.bak')
    #        if self.savefile is None and len(self._picks) > 0:
    #            ask = PyQt4.QtGui.QMessageBox.question(self, 'Save Picks?',
    #                'Do you want to save your picks?',
    #                PyQt4.QtGui.QMessageBox.Save |
    #                PyQt4.QtGui.QMessageBox.Discard |
    #                PyQt4.QtGui.QMessageBox.Cancel, PyQt4.QtGui.QMessageBox.Save)
    #            if ask == PyQt4.QtGui.QMessageBox.Save:
    #                self._saveCatalog()
    #            elif ask == PyQt4.QtGui.QMessageBox.Cancel:
    #                evnt.ignore()
    #        print self._picks


    # Filter Dialog
    class defFilter(PyQt4.QtGui.QDialog):
        def __init__(self, parent=None, filtervalues=None):
            '''
            Bandpass filter dialog... Qt layout and stuff
            '''
            PyQt4.QtGui.QDialog.__init__(self, parent)
            self.setWindowTitle('Create new Bandpass-Filter')

            # Frequency QDoubleSpinBoxes
            self.frqmin = PyQt4.QtGui.QDoubleSpinBox(decimals=2, maximum=100,
                                                     minimum=0.01, singleStep=0.1, value=0.1)
            self.frqmax = PyQt4.QtGui.QDoubleSpinBox(decimals=2, maximum=100,
                                                     minimum=0.01, singleStep=0.1, value=10.0)

            # Radio buttons for corners
            _corners = [2, 4, 8]
            _radbtn = []
            for _c in _corners:
                _radbtn.append(PyQt4.QtGui.QRadioButton(str(_c)))
                if _c == 4:
                    _radbtn[-1].setChecked(True)

            self.corner = PyQt4.QtGui.QButtonGroup()
            self.corner.setExclusive(True)

            radiogrp = PyQt4.QtGui.QHBoxLayout()
            for _i, _r in enumerate(_radbtn):
                self.corner.addButton(_r, _corners[_i])
                radiogrp.addWidget(_radbtn[_i])

            # Filter name
            self.fltname = PyQt4.QtGui.QLineEdit('Filter Name')
            self.fltname.selectAll()

            # Make Layout
            grid = PyQt4.QtGui.QGridLayout()
            grid.addWidget(PyQt4.QtGui.QLabel('Filter Name'), 0, 0)
            grid.addWidget(self.fltname, 0, 1)
            grid.addWidget(PyQt4.QtGui.QLabel('Min. Frequency'), 1, 0)
            grid.addWidget(self.frqmin, 1, 1)
            grid.addWidget(PyQt4.QtGui.QLabel('Max. Frequency'), 2, 0)
            grid.addWidget(self.frqmax, 2, 1)
            grid.addWidget(PyQt4.QtGui.QLabel('Corners'), 3, 0)
            grid.addLayout(radiogrp, 3, 1)
            grid.setVerticalSpacing(10)

            btnbox = PyQt4.QtGui.QDialogButtonBox(PyQt4.QtGui.QDialogButtonBox.Ok |
                                                  PyQt4.QtGui.QDialogButtonBox.Cancel)
            btnbox.accepted.connect(self.accept)
            btnbox.rejected.connect(self.reject)

            layout = PyQt4.QtGui.QVBoxLayout()
            layout.addWidget(PyQt4.QtGui.QLabel('Define a minimum and maximum' +
                                                ' frequency\nfor the bandpass filter.\nFunction utilises ' +
                                                'obspy.signal.filter (zerophase=True).\n'))
            layout.addLayout(grid)
            layout.addWidget(btnbox)

            if filtervalues is not None:
                self.fltname.setText(filtervalues['name'])
                self.frqmin.setValue(filtervalues['freqmin'])
                self.frqmax.setValue(filtervalues['freqmax'])
                self.corner.button(filtervalues['corners']).setChecked(True)

            self.setLayout(layout)
            self.setSizeGripEnabled(False)

        def getValues(self):
            '''
            Return filter dialogs values as a dictionary
            '''
            return dict(name=str(self.fltname.text()),
                        freqmin=float(self.frqmin.cleanText()),
                        freqmax=float(self.frqmax.cleanText()),
                        corners=int(int(self.corner.checkedId())))
Ejemplo n.º 8
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, **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 = {}

    def plot(self,
             xdata,
             ydata,
             side='left',
             title=None,
             xlabel=None,
             ylabel=None,
             y2label=None,
             use_dates=False,
             **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] = [None, None, None, 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.use_datas = 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,
              **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))
        if self.use_dates:
            xdata = [datetime.fromtimestamp(i) for i in xdata]
            xdata = dates.date2num(xdata)
            # axes.xaxis.set_major_locator(dates.AutoDateLocator())

        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
        # print("# oplot done")
        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)

        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:
            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.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.conf.unzoom(full=False)

    def unzoom_all(self, event=None):
        """ zoom out full data range """
        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.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.Fit()

        canvas_draw = self.canvas.draw

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

        self.canvas.draw = draw
        self.addCanvasEvents()

    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= %s, %s" % (self._xfmt, self._yfmt)) % (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= %s, %s, %s" % (self._xfmt, self._yfmt,
                                              self._y2fmt) % (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)
Ejemplo n.º 9
0
class PlotPanel(wx.Panel):
    """The PlotPanel has a Figure and a Canvas"""
    def __init__(self, parent, color=(255, 255, 255), 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)

        # subplotparams = SubplotParams(0.02, 0.02, 0.98, 0.98, 0.1, 0.1)
        # initialize matplotlib stuff
        self.figure = Figure((2.1, 2.97), dpi)
        self.canvas = FigureCanvasWxAgg(self, -1, self.figure)
        self.SetColor(color)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas, 1, wx.EXPAND | wx.SHAPED, 0)
        self.SetSizer(sizer)

        self.Bind(wx.EVT_SIZE, self.set_size)
        self.draw()
        self.Refresh()

    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 set_size(self, evt=None):
        if self.ClientSize[0] > 0 and self.ClientSize[1] > 0:
            self.canvas.SetSize(self.ClientSize)
            self.canvas.draw()

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

    def image_grid(self, num_rows, num_cols):
        return ImageGrid(self.figure,
                         111,
                         share_all=True,
                         nrows_ncols=(num_rows, num_cols),
                         cbar_size="3%",
                         cbar_pad=0.02,
                         cbar_mode='single')

    def get_norm(self, vmin, vmax):
        return matplotlib.colors.normalize(vmax=vmax, vmin=vmin)

    def save_to_pdf(self, pdfpages):
        old_fig = self.figure
        self.figure = Figure((8.5, 11), dpi=300)
        canvas = matplotlib.backends.backend_pdf.FigureCanvasPdf(self.figure)
        self.draw()
        pdfpages.savefig(self.figure)
        self.figure = old_fig

    def align_subplots(self):
        xmin = matplotlib.numpy.inf
        xmax = -matplotlib.numpy.inf
        for subplot in self.figure.get_axes():
            xmin = min(xmin, subplot.get_xlim()[0])
            xmax = max(xmax, subplot.get_xlim()[1])
        for subplot in self.figure.get_axes():
            subplot.set_xlim(xmin, xmax)
Ejemplo n.º 10
0
class PlotPanel (wx.Panel):
    """The PlotPanel has a Figure and a Canvas"""
    def __init__(self, parent, color=(255, 255, 255), 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)

        # subplotparams = SubplotParams(0.02, 0.02, 0.98, 0.98, 0.1, 0.1)
        # initialize matplotlib stuff
        self.figure = Figure((2.1, 2.97), dpi)
        self.canvas = FigureCanvasWxAgg(self, -1, self.figure)
        self.SetColor(color)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas, 1, wx.EXPAND | wx.SHAPED, 0)
        self.SetSizer(sizer)

        self.Bind(wx.EVT_SIZE, self.set_size)
        self.draw()
        self.Refresh()

    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 set_size(self, evt=None):
        if self.ClientSize[0] > 0 and self.ClientSize[1] > 0:
            self.canvas.SetSize(self.ClientSize)
            self.canvas.draw()

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

    def image_grid(self, num_rows, num_cols):
        return ImageGrid(self.figure, 111,
                         share_all = True,
                         nrows_ncols = (num_rows, num_cols),
                         cbar_size = "3%",
                         cbar_pad = 0.02,
                         cbar_mode = 'single')

    def get_norm(self, vmin, vmax):
        return matplotlib.colors.normalize(vmax=vmax, vmin=vmin)

    def save_to_pdf(self, pdfpages):
        old_fig = self.figure
        self.figure = Figure((8.5, 11), dpi=300)
        canvas = matplotlib.backends.backend_pdf.FigureCanvasPdf(self.figure)
        self.draw()
        pdfpages.savefig(self.figure)
        self.figure = old_fig

    def align_subplots(self):
        xmin = matplotlib.numpy.inf
        xmax = - matplotlib.numpy.inf
        for subplot in self.figure.get_axes():
            xmin = min(xmin, subplot.get_xlim()[0])
            xmax = max(xmax, subplot.get_xlim()[1])
        for subplot in self.figure.get_axes():
            subplot.set_xlim(xmin, xmax)
Ejemplo n.º 11
0
class EegpyBaseWin(gtk.Window):
    programName = "eegpy: Window-widget"
    panePosDef = 0
    x = None
    y = None

    # Konstruktor
    def __init__(self):
        gobject.threads_init()
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
        self.setupGUI()

    def cb_quit(self, b):
        self.window.hide()
        gtk.main_quit()

    # This callback quits the program
    def delete_event(self, widget, event, data=None):
        self.hide()
        gtk.main_quit()
        return False

    def setupGUI(self):
        self.set_default_size(700, 500)
        self.set_title(self.programName)

        # Set a handler for delete_event that immediately
        # exits GTK.
        self.connect("delete_event", self.delete_event)

        # Sets the border width of the window.
        self.set_border_width(0)
        self.hpane = gtk.HPaned()
        self.add(self.hpane)
        #self.setupMenu()
        self.inner_pane = gtk.VPaned()
        self.hpane.add1(self.inner_pane)

        self.upper_hbox = gtk.HBox()
        self.lower_vbox = gtk.VBox()
        self.inner_pane.add1(self.upper_hbox)
        self.inner_pane.add2(self.lower_vbox)

        self.setupCanvas()
        #self.setupLog()

        self.set_icon_list(eegpy_logo("small"), eegpy_logo("large"))
        self.show_all()
        self.panePosDef = self.hpane.get_position()

    def setupLog(self):
        self.frameLog = gtk.Frame("Log")
        self.logbuffer = gtk.TextBuffer(None)
        self.lvSw = gtk.ScrolledWindow()
        self.logview = gtk.TextView(self.logbuffer)
        self.logview.set_editable(False)
        #self.logview.set_buffer(self.logbuffer)
        self.logview.set_border_width(1)
        self.lvSw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        self.lvSw.add(self.logview)
        self.frameLog.add(self.lvSw)
        self.upper_hbox.pack_end(self.frameLog)
        pass

    def log(self, message):
        """logs a message to the log window"""
        message = message + "\n"
        self.logbuffer.insert(self.logbuffer.get_start_iter(),
                              message)  #_at_cursor(message, len(message))
        #self.lvSw.set_vadjustment(self.lvSw.get_vadjustment().lower)

    def setupCanvas(self):
        self.f = Figure(figsize=(5, 4),
                        dpi=100,
                        subplotpars=SubplotParams(left=0.06,
                                                  top=0.95,
                                                  right=0.97,
                                                  bottom=0.1))
        self.setupSubplots()
        self.canvas = FigureCanvas(self.f)
        #self.canvas.show()
        self.lower_vbox.pack_start(self.canvas)
        #self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
        self.toolbar = NavigationToolbar(self.canvas, self.window)
        self.lower_vbox.pack_start(self.toolbar, False, False)

    def setupSubplots(self):
        self.f.clear()
        self.a = self.f.add_subplot(111)
        self.subplAxes = self.f.get_axes()
Ejemplo n.º 12
0
class EegpyBaseWin(gtk.Window):
    programName = "eegpy: Window-widget"
    panePosDef = 0
    x=None
    y=None
    
    # Konstruktor
    def __init__(self):
        gobject.threads_init()
        gtk.Window.__init__(self,gtk.WINDOW_TOPLEVEL)
        self.setupGUI()
        
    def cb_quit(self, b):
        self.window.hide()
        gtk.main_quit()
    
    # This callback quits the program
    def delete_event(self, widget, event, data=None):
        self.hide()
        gtk.main_quit()
        return False
            
    def setupGUI(self):
        self.set_default_size(700,500)
        self.set_title(self.programName)

        # Set a handler for delete_event that immediately
        # exits GTK.
        self.connect("delete_event", self.delete_event)

        # Sets the border width of the window.
        self.set_border_width(0)
        self.hpane = gtk.HPaned()
        self.add(self.hpane)
        #self.setupMenu()
        self.inner_pane = gtk.VPaned()
        self.hpane.add1(self.inner_pane)

        self.upper_hbox = gtk.HBox()
        self.lower_vbox = gtk.VBox()
        self.inner_pane.add1(self.upper_hbox)
        self.inner_pane.add2(self.lower_vbox)

        self.setupCanvas()        
        #self.setupLog()
        
        self.set_icon_list(eegpy_logo("small"), eegpy_logo("large"))
        self.show_all()
        self.panePosDef = self.hpane.get_position()
    
    def setupLog(self):
        self.frameLog = gtk.Frame("Log")
        self.logbuffer = gtk.TextBuffer(None)
        self.lvSw = gtk.ScrolledWindow()
        self.logview = gtk.TextView(self.logbuffer)
        self.logview.set_editable(False)
        #self.logview.set_buffer(self.logbuffer)
        self.logview.set_border_width(1)
        self.lvSw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC);
        self.lvSw.add(self.logview)
        self.frameLog.add(self.lvSw)
        self.upper_hbox.pack_end(self.frameLog)
        pass
    
    def log(self,message):
        """logs a message to the log window"""
        message=message+"\n"
        self.logbuffer.insert(self.logbuffer.get_start_iter(),message)#_at_cursor(message, len(message))
        #self.lvSw.set_vadjustment(self.lvSw.get_vadjustment().lower)
            
    def setupCanvas(self):
        self.f = Figure(figsize=(5,4), dpi=100, subplotpars=SubplotParams(left=0.06, top=0.95, right=0.97, bottom=0.1))
        self.setupSubplots()
        self.canvas = FigureCanvas(self.f)
        #self.canvas.show()
        self.lower_vbox.pack_start(self.canvas)
        #self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
        self.toolbar = NavigationToolbar( self.canvas, self.window )
        self.lower_vbox.pack_start(self.toolbar, False, False)
    
    def setupSubplots(self):
        self.f.clear()
        self.a = self.f.add_subplot(111)
        self.subplAxes = self.f.get_axes()
Ejemplo n.º 13
0
class FigurePanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.SetBackgroundColour('#DCE5EE')

        pub().subscribe(self.update_language, T.LANGUAGE_CHANGED)

        self.control_panel = None
        self.dframes = []
        self.order_names = []
        self.key_figure = 1
        self.mode_run = False

        self.current_dataframes = None
        self.current_datacolors = None

        self.run_explorer = False

        self.figure_config_dialog_ref = None

        # ---- inicialización de figura
        self.fig = Figure()
        self.canvas = FigureCanvas(self, -1, self.fig)

        # ---- configuración de figura
        self.fig_config = FigureConfig()
        self.set_figure_config()

        # ---- configuración de axe
        self.ax_conf = AxesConfig()

        # ---- radar chard config
        self.radar_chard_con = RadarChadConfig()

        # ---- toolbar
        self.sizer_tool = wx.BoxSizer(wx.HORIZONTAL)
        _bitmap = play_fig.GetBitmap()
        self.b_play = wx.BitmapButton(self, -1, _bitmap, style=wx.NO_BORDER)
        self.sizer_tool.Add(self.b_play, flag=wx.ALIGN_CENTER_VERTICAL)
        self.b_play.Bind(wx.EVT_BUTTON, self.on_play)
        self.b_play.SetToolTipString(L('VISUALIZE_DATE_CLUSTER'))
        _bitmap = settings_fig.GetBitmap()
        self.b_setting = wx.BitmapButton(self, -1, _bitmap, style=wx.NO_BORDER)
        self.sizer_tool.Add(self.b_setting, flag=wx.ALIGN_CENTER_VERTICAL)
        self.b_setting.Bind(wx.EVT_BUTTON, self.on_config)
        self.b_setting.SetToolTipString(L('FIGURE_CONF'))

        _bitmap = sort_and_filter.GetBitmap()
        self.b_sorted = wx.BitmapButton(self, -1, _bitmap, style=wx.NO_BORDER)
        self.b_sorted.Bind(wx.EVT_BUTTON, self.on_sort_and_filter)
        self.b_sorted.SetToolTipString(L('BUTTON_ORDER_AND_FILTER'))
        self.b_sorted.Disable()
        self.sizer_tool.Add(self.b_sorted, 0, wx.ALIGN_CENTER_VERTICAL)

        _bp = line_highligh.GetBitmap()
        self.b_highligh = wx.BitmapButton(self, -1, _bp, style=wx.NO_BORDER)
        self.b_highligh.Bind(wx.EVT_BUTTON, self.on_highligh)
        self.b_highligh.SetToolTipString(L('BUTTON_HIGHLIGHT'))
        self.b_highligh.Disable()
        self.sizer_tool.Add(self.b_highligh, 0, wx.ALIGN_CENTER_VERTICAL)

        self.toolbar = Toolbar(self.canvas)
        self.toolbar.Realize()
        self.toolbar.SetBackgroundColour('#DCE5EE')

        self.sizer_tool.Add(self.toolbar, 0, wx.ALIGN_CENTER_VERTICAL)

        choice_grafic = self.get_choice_grafic()
        self.sizer_tool.Add(choice_grafic, wx.ALIGN_LEFT)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.sizer_tool, 0, wx.EXPAND)
        self.sizer.Add(self.canvas, 1, wx.EXPAND)

        self.SetSizer(self.sizer)
        self.Fit()
        self._welcome()

    def _welcome(self):
        Axes3D(self.fig)

    def set_figure_config(self):

        self.fig.set_figwidth(self.fig_config.width)
        self.fig.set_figheight(self.fig_config.height)
        self.fig.set_facecolor(self.fig_config.facecolor)

        left = self.fig_config.subplot_left
        bottom = self.fig_config.subplot_bottom
        right = self.fig_config.subplot_right
        top = self.fig_config.subplot_top
        wspace = self.fig_config.subplot_wspace
        hspace = self.fig_config.subplot_hspace
        self.fig.subplots_adjust(left, bottom, right, top, wspace, hspace)

        self.fig.suptitle('Tava Tool', fontsize=14, fontweight='light',
                          style='italic', family='serif', color='c',
                          horizontalalignment='center',
                          verticalalignment='center')

    def draw_graphic(self, dframes, colors):
        key_figure = self.g_figure()
        if key_figure == K_PARALLEL_COORDENATE:
            return kparallelcoordinates(dframes, 'Name', self.fig,
                                        self.ax_conf, self.fig_config,
                                        colors)

        elif key_figure == K_RADAR_CHART_POLYGON:
            return radarchart(dframes, 'Name', self.fig, self.ax_conf,
                              self.radar_chard_con, colors)
        elif key_figure == K_RADVIZ:
            return kradviz(dframes, 'Name', self.fig, self.ax_conf, colors)

    def kdraw(self, dframes, colors, ldic):

        self.current_dataframes = dframes
        self.current_datacolors = colors
        self.ldic = ldic
        self._kdraw(dframes, colors)

    def pre_kdraw_order(self, names_ordered):
        names_ordered.append('Name')
        _dframes = []
        for df in self.current_dataframes:
            _dframes.append(df[names_ordered])

        self._kdraw(_dframes, self.current_datacolors)

    def _kdraw(self, dframes, colors):
        self.fig.clear()
        self.start_busy()
        task = DrawThread(self, dframes, colors)
        task.start()

    def on_play(self, event):
        self.mode_run = True
        self.run_explorer = True
        self.new_order = []
        # ---- dibujar clusters/datos seleccionados
        self.control_panel.run_fig()

    def on_sort_and_filter(self, event):

        self.run_explorer = True
        cdf = self.current_dataframes
        if cdf is None or cdf == []:
            return

        self.old_order = cdf[0].columns.tolist()[:-1]
        ItemsPickerFilterDialog(self, self.old_order)

    def on_highligh(self, event):
        _label_aux = ''
        if self.run_explorer:
            for axe in self.fig.get_axes():
                lines = []
                for line in axe.get_children():
                    if isinstance(line, Line2D):
                        if self.ldic.get(line.get_color()) is not None:
                            _label_aux = self.ldic.get(line.get_color())
                            line.set_label('shape = ' + self.ldic.get(line.get_color()))
                            lines.append(line)
                        else:
                            line.set_label('')
                h = resaltar(lines, highlight_color=self.ax_conf.highlight_color,
                             formatter='{label}'.format)
                if lines != []:
                    h.show_highlight(lines[0])
            self.run_explorer = False
            self.canvas_draw()

    def on_config(self, event):
        if self.figure_config_dialog_ref is None:
            self.figure_config_dialog_ref = FigureConfigDialog(self)
        else:
            self.figure_config_dialog_ref.nb.SetSelection(0)
            self.figure_config_dialog_ref.ShowModal()

    def g_figure(self):
        return self.ch_graph.GetSelection()

    def get_choice_grafic(self):
        grid = wx.FlexGridSizer(cols=2)
        sampleList = self.get_item_list()

        self.ch_graph = wx.Choice(self, -1, choices=sampleList)
        self.ch_graph.SetSelection(0)
        self.ch_graph.Bind(wx.EVT_CHOICE, self.on_graphic)
        self.ch_graph.SetToolTipString(L('SELECT_A_GRAPHIC'))

        grid.Add(self.ch_graph, 0, wx.ALIGN_LEFT | wx.ALL, 5)

        return grid

    def get_item_list(self):
        return [L('PARALLEL_COORDINATES'), 'Radar Chart']

    def on_graphic(self, event):

        if event.GetString() != L('PARALLEL_COORDINATES') or not self.mode_run:
            self.b_highligh.Disable()
            return
        self.b_highligh.Enable()

    def update_language(self, msg):
        s = self.ch_graph.GetSelection()
        self.ch_graph.SetItems(self.get_item_list())
        self.ch_graph.SetSelection(s)
        self.ch_graph.SetToolTipString(L('SELECT_A_GRAPHIC'))
        self.b_setting.SetToolTipString(L('FIGURE_CONF'))
        self.b_play.SetToolTipString(L('VISUALIZE_DATE_CLUSTER'))

    def start_busy(self):
        pub().sendMessage(T.START_BUSY)
        self.b_play.Disable()
        self.toolbar.Disable()
        self.b_setting.Disable()
        self.ch_graph.Disable()
        self.b_highligh.Disable()
        self.b_sorted.Disable()

    def stop_busy(self):
        pub().sendMessage(T.STOP_BUSY)
        self.b_play.Enable()
        self.toolbar.Enable()
        self.b_setting.Enable()
        self.ch_graph.Enable()
        self.b_highligh.Enable()
        self.b_sorted.Enable()

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

    def set_fig(self, fig):
        self.fig = fig
Ejemplo n.º 14
0
class LPlotWidget(LWidget):

    def __init__(self, parent=None):
        super(LPlotWidget, self).__init__(parent)
        self.setName(WIDGET_NAME)
        self.buildUi()
        self.dataContainer = LMyDataContainer(self)
        self.actions = LMyActions(self.dataContainer,self)
        self.updateUi()

    @LInAndOut(DEBUG & WIDGETS)
    def buildUi(self):
        self.verticalLayout = QVBoxLayout(self)
        #width, height = (14, 4)
        #width, height = figaspect(2.)
        width = 1 
        height = 1 
        dpi = 72

        self.figure = Figure(figsize=(width, height), dpi=dpi)		# <-- figs created with same geometry!
        self.figure2 = Figure(figsize=(width, height), dpi=dpi)

        #self.figure = Figure(dpi=dpi)		# <-- figs created with same geometry!
        #self.figure2 = Figure(dpi=dpi)

        #self.axe111 = self.figure2.add_axes((0,0,1,1),title="My Axe Title",xlabel='X',ylabel='Y')
        #self.axesSubplot_111 = self.figure.add_subplot(2,2,1)
        #print self.axesSubplot_111.get_position()
        #self.axesSubplot_111 = self.figure.add_subplot(2,2,2)
        #print self.axesSubplot_111.get_position()
        #self.axesSubplot_111 = self.figure.add_subplot(2,2,3)
        #self.axesSubplot_111.set_frame_on(False)
        #self.bbox = self.axesSubplot_111.get_position()
        #print self.bbox.__class__.__name__
        #print self.axesSubplot_111.get_position()
        self.axesSubplot_111 = self.figure.add_subplot(2,2,4)
        self.axesSubplot_111.plot([1,0])
        #print self.axesSubplot_111.get_position()
        #self.axesSubplot_111.set_position(self.bbox)
        #self.axesSubplot_111.set_position([0.54772727, 0.53636364, 0.9, 0.9      ])

        self.axesSubplot_111 = self.figure2.add_subplot(2,2,2)            # <-- creation in fig2
        self.axesSubplot_111 = self.figure2.add_subplot(1,1,1)            # <-- position in fig2 doesn't impact position in fig1

        print self.axesSubplot_111.__class__.__name__
        print self.axesSubplot_111.get_position()
        print self.axesSubplot_111.get_axes_locator()
        #self.bbox = self.axesSubplot_111.get_position()
        self.axesSubplot_111.grid(color='g', linestyle='-.', linewidth=1)
        self.axesSubplot_111.plot([0,1])

        #Change to the figure
        #self.axesSubplot_111.set_figure(self.figure)
        #self.figure.add_axes(self.axesSubplot_111)
        x0 = 1.0
        y0 = 1.0
        xspan = 8.0
        yspan = 8.0
        pos = [x0,y0,xspan,yspan]
        self.addAxesSubplot(self.axesSubplot_111, position=pos)

        #self.axesSubplot_111.reset_position()
        #self.axesSubplot_111.set_position(pos=pos,which='both')
        #self.axesSubplot_111.set_position(self.bbox,which='both')			# <-- as if the bbox where scalled differently
        self.figureCanvas = FigureCanvas(self.figure)	# <-- figure required
        self.verticalLayout.addWidget(self.figureCanvas)

        #FigureCanvas.setSizePolicy(self.figureCanvas, QSizePolicy.Expanding, QSizePolicy.Expanding)
        #FigureCanvas.updateGeometry(self.figureCanvas)


    @LInAndOut(DEBUG & WIDGETS)
    def updateUi(self):
        self.figure.suptitle(self.dataContainer.figureTitle)

    #----------------------------------------------------------------------
    # Interface (set)

    @LInAndOut(DEBUG & WIDGETS)
    def setOptions(self, options):
        super(LPlotWidget, self).setOptions(options)
        if options.verboseLevel is not None:
            self.setVerboseLevel(options.verboseLevel)
        if options.figureTitle is not None:
            self.setFigureTitle(options.figureTitle)
        self.updateUi()

    @LInAndOut(DEBUG & WIDGETS)
    def setVerboseLevel(self, level):
        self.dataContainer.verboseLevel = level

    #----------------------------------------------------------------------
    # Interface (get)

    #----------------------------------------------------------------------
    # QT SLOTS    

    @LInAndOut(DEBUG & WIDGETS)
    def setFigureTitle(self, title):
        self.dataContainer.figureTitle = title

    @pyqtSlot()
    def clear(self):
        for a in self.figure.get_axes():
            self.figure.delaxes(a)
        #self.axe111.clear()
        #self.figure.draw()
        self.figureCanvas.draw()

    @pyqtSlot()
    def snapshot(self, fileName = None):
        if fileName is None:
            caption = "%s - Save File" % self.name
            filter = "PNG Image (*.png);;All files (*.*)"
            fileName = QFileDialog.getSaveFileName(self.parent(), caption=caption,filter=filter )
            fileName = "%s" % fileName
        if len(fileName) > 0:
            self.figure.savefig(fileName, facecolor='w', edgecolor='w', orientation='portrait', papertype=None, format=None, transparent=False, bbox_inches=None, pad_inches=0.1)

    @pyqtSlot()
    def addAxesSubplot(self, axesSubplot, position=None, bbox=None):
        axesSubplot.set_figure(self.figure)
        self.figure.add_axes(axesSubplot)
        if position is not None:
            axesSubplot.set_position(position)
        if bbox is not None:
            axesSubplot.set_position(bbox)

    @pyqtSlot()
    def addAxesSubplots(self, axesSubplots):
        for axesSubplot in axesSubplots:
            self.addSubplot(axesSubplot)
Ejemplo n.º 15
0
class asaplotbase:
    """
    ASAP plotting base class based on matplotlib.
    """

    def __init__(self, rows=1, cols=0, title='', size=None, buffering=False):
        """
        Create a new instance of the ASAPlot plotting class.

        If rows < 1 then a separate call to set_panels() is required to define
        the panel layout; refer to the doctext for set_panels().
        """
        self.is_dead = False
        self.figure = Figure(figsize=size, facecolor='#ddddee')
        self.canvas = None

        self.set_title(title)
        self.subplots = []
        if rows > 0:
            self.set_panels(rows, cols)

        # Set matplotlib default colour sequence.
        self.colormap = "green red black cyan magenta orange blue purple yellow pink".split()

        c = asaprcParams['plotter.colours']
        if isinstance(c,str) and len(c) > 0:
            self.colormap = c.split()
        # line styles need to be set as a list of numbers to use set_dashes
        self.lsalias = {"line":  [1,0],
                        "dashdot": [4,2,1,2],
                        "dashed" : [4,2,4,2],
                        "dotted" : [1,2],
                        "dashdotdot": [4,2,1,2,1,2],
                        "dashdashdot": [4,2,4,2,1,2]
                        }

        styles = "line dashed dotted dashdot".split()
        c = asaprcParams['plotter.linestyles']
        if isinstance(c,str) and len(c) > 0:
            styles = c.split()
        s = []
        for ls in styles:
            if self.lsalias.has_key(ls):
                s.append(self.lsalias.get(ls))
            else:
                s.append('-')
        self.linestyles = s

        self.color = 0;
        self.linestyle = 0;
        self.attributes = {}
        self.loc = 0

        self.buffering = buffering

        self.events = {'button_press':None,
                       'button_release':None,
                       'motion_notify':None}

    def _alive(self):
        # Return True if the GUI alives.
        if (not self.is_dead) and \
               self.figmgr and hasattr(self.figmgr, "num"):
            figid = self.figmgr.num
            # Make sure figid=0 is what asapplotter expects.
            # It might be already destroied/overridden by matplotlib
            # commands or other methods using asaplot.
            return _pylab_helpers.Gcf.has_fignum(figid) and \
                   (self.figmgr == _pylab_helpers.Gcf.get_fig_manager(figid))
        return False

    def _subplotsOk(self, rows, cols, npanel=0):
        """
        Check if the axes in subplots are actually the ones plotted on
        the figure. Returns a bool.
        This method is to detect manual layout changes using mpl methods.
        """
        # compare with user defined layout
        if (rows is not None) and (rows != self.rows):
            return False
        if (cols is not None) and (cols != self.cols):
            return False
        # check number of subplots
        figaxes = self.figure.get_axes()
        np = self.rows*self.cols
        if npanel > np:
            return False
        if len(figaxes) != np:
            return False
        if len(self.subplots) != len(figaxes):
            return False
        # compare axes instance in this class and on the plotter
        ok = True
        for ip in range(np):
            if self.subplots[ip]['axes'] != figaxes[ip]:
                ok = False
                break
        return ok

    ### Delete artists ###
    def clear(self):
        """
        Delete all lines from the current subplot.
        Line numbering will restart from 0.
        """

        #for i in range(len(self.lines)):
        #   self.delete(i)
        self.axes.clear()
        self.color = 0
        self.linestyle = 0
        self.lines = []
        self.subplots[self.i]['lines'] = self.lines

    def delete(self, numbers=None):
        """
        Delete the 0-relative line number, default is to delete the last.
        The remaining lines are NOT renumbered.
        """

        if numbers is None: numbers = [len(self.lines)-1]

        if not hasattr(numbers, '__iter__'):
            numbers = [numbers]

        for number in numbers:
            if 0 <= number < len(self.lines):
                if self.lines[number] is not None:
                    for line in self.lines[number]:
                        line.set_linestyle('None')
                        self.lines[number] = None
        self.show()


    ### Set plot parameters ###
    def hold(self, hold=True):
        """
        Buffer graphics until subsequently released.
        """
        self.buffering = hold

    def palette(self, color, colormap=None, linestyle=0, linestyles=None):
        if colormap:
            if isinstance(colormap,list):
                self.colormap = colormap
            elif isinstance(colormap,str):
                self.colormap = colormap.split()
        if 0 <= color < len(self.colormap):
            self.color = color
        if linestyles:
            self.linestyles = []
            if isinstance(linestyles,list):
                styles = linestyles
            elif isinstance(linestyles,str):
                styles = linestyles.split()
            for ls in styles:
                if self.lsalias.has_key(ls):
                    self.linestyles.append(self.lsalias.get(ls))
                else:
                    self.linestyles.append(self.lsalias.get('line'))
        if 0 <= linestyle < len(self.linestyles):
            self.linestyle = linestyle

    def legend(self, loc=None):
        """
        Add a legend to the plot.

        Any other value for loc else disables the legend:
             1: upper right
             2: upper left
             3: lower left
             4: lower right
             5: right
             6: center left
             7: center right
             8: lower center
             9: upper center
            10: center

        """
        if isinstance(loc, int):
            self.loc = None
            if 0 <= loc <= 10: self.loc = loc
        else:
            self.loc = None
        #self.show()

    #def set_panels(self, rows=1, cols=0, n=-1, nplots=-1, ganged=True):
    def set_panels(self, rows=1, cols=0, n=-1, nplots=-1, margin=None,ganged=True):
        """
        Set the panel layout.

        rows and cols, if cols != 0, specify the number of rows and columns in
        a regular layout.   (Indexing of these panels in matplotlib is row-
        major, i.e. column varies fastest.)

        cols == 0 is interpreted as a retangular layout that accomodates
        'rows' panels, e.g. rows == 6, cols == 0 is equivalent to
        rows == 2, cols == 3.

        0 <= n < rows*cols is interpreted as the 0-relative panel number in
        the configuration specified by rows and cols to be added to the
        current figure as its next 0-relative panel number (i).  This allows
        non-regular panel layouts to be constructed via multiple calls.  Any
        other value of n clears the plot and produces a rectangular array of
        empty panels.  The number of these may be limited by nplots.
        """
        if n < 0 and len(self.subplots):
            self.figure.clear()
            self.set_title()

        if margin:
            lef, bot, rig, top, wsp, hsp = margin
            self.figure.subplots_adjust(
                left=lef,bottom=bot,right=rig,top=top,wspace=wsp,hspace=hsp)
            del lef,bot,rig,top,wsp,hsp

        if rows < 1: rows = 1

        if cols <= 0:
            i = int(sqrt(rows))
            if i*i < rows: i += 1
            cols = i

            if i*(i-1) >= rows: i -= 1
            rows = i

        if 0 <= n < rows*cols:
            i = len(self.subplots)

            self.subplots.append({})

            self.subplots[i]['axes']  = self.figure.add_subplot(rows,
                                            cols, n+1)
            self.subplots[i]['lines'] = []

            if i == 0: self.subplot(0)

            self.rows = 0
            self.cols = 0

        else:
            self.subplots = []

            if nplots < 1 or rows*cols < nplots:
                nplots = rows*cols
            if ganged:
                hsp,wsp = None,None
                if rows > 1: hsp = 0.0001
                if cols > 1: wsp = 0.0001
                self.figure.subplots_adjust(wspace=wsp,hspace=hsp)
            for i in range(nplots):
                self.subplots.append({})
                self.subplots[i]['lines'] = []
                if not ganged:
                    self.subplots[i]['axes'] = self.figure.add_subplot(rows,
                                                cols, i+1)
                    if asaprcParams['plotter.axesformatting'] != 'mpl':
                        self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
                else:
                    if i == 0:
                        self.subplots[i]['axes'] = self.figure.add_subplot(rows,
                                                cols, i+1)
                        if asaprcParams['plotter.axesformatting'] != 'mpl':

                            self.subplots[i]['axes'].xaxis.set_major_formatter(OldScalarFormatter())
                    else:
                        self.subplots[i]['axes'] = self.figure.add_subplot(rows,
                                                cols, i+1,
                                                sharex=self.subplots[0]['axes'],
                                                sharey=self.subplots[0]['axes'])

                    # Suppress tick labelling for interior subplots.
                    if i <= (rows-1)*cols - 1:
                        if i+cols < nplots:
                            # Suppress x-labels for frames width
                            # adjacent frames
                            for tick in self.subplots[i]['axes'].xaxis.majorTicks:
                                tick.label1On = False
                            #self.subplots[i]['axes'].xaxis.label.set_visible(False)
                    if i%cols:
                        # Suppress y-labels for frames not in the left column.
                        for tick in self.subplots[i]['axes'].yaxis.majorTicks:
                            tick.label1On = False
                        #self.subplots[i]['axes'].yaxis.label.set_visible(False)
                    # disable the first tick of [1:ncol-1] of the last row
                    #if i+1 < nplots:
                    #    self.subplots[i]['axes'].xaxis.majorTicks[0].label1On = False
                # set axes label state for interior subplots.
                if i%cols:
                    self.subplots[i]['axes'].yaxis.label.set_visible(False)
                if (i <= (rows-1)*cols - 1) and (i+cols < nplots):
                    self.subplots[i]['axes'].xaxis.label.set_visible(False)
            self.rows = rows
            self.cols = cols
            self.subplot(0)
        del rows,cols,n,nplots,margin,ganged,i

    def subplot(self, i=None, inc=None):
        """
        Set the subplot to the 0-relative panel number as defined by one or
        more invokations of set_panels().
        """
        l = len(self.subplots)
        if l:
            if i is not None:
                self.i = i

            if inc is not None:
                self.i += inc

            self.i %= l
            self.axes  = self.subplots[self.i]['axes']
            self.lines = self.subplots[self.i]['lines']

    def set_axes(self, what=None, *args, **kwargs):
        """
        Set attributes for the axes by calling the relevant Axes.set_*()
        method.  Colour translation is done as described in the doctext
        for palette().
        """

        if what is None: return
        if what[-6:] == 'colour': what = what[:-6] + 'color'

        key = "colour"
        if kwargs.has_key(key):
            val = kwargs.pop(key)
            kwargs["color"] = val

        getattr(self.axes, "set_%s"%what)(*args, **kwargs)

        self.show(hardrefresh=False)


    def set_figure(self, what=None, *args, **kwargs):
        """
        Set attributes for the figure by calling the relevant Figure.set_*()
        method.  Colour translation is done as described in the doctext
        for palette().
        """

        if what is None: return
        if what[-6:] == 'colour': what = what[:-6] + 'color'
        #if what[-5:] == 'color' and len(args):
        #    args = (get_colour(args[0]),)

        newargs = {}
        for k, v in kwargs.iteritems():
            k = k.lower()
            if k == 'colour': k = 'color'
            newargs[k] = v

        getattr(self.figure, "set_%s"%what)(*args, **newargs)
        self.show(hardrefresh=False)


    def set_limits(self, xlim=None, ylim=None):
        """
        Set x-, and y-limits for each subplot.

        xlim = [xmin, xmax] as in axes.set_xlim().
        ylim = [ymin, ymax] as in axes.set_ylim().
        """
        for s in self.subplots:
            self.axes  = s['axes']
            self.lines = s['lines']
            oldxlim =  list(self.axes.get_xlim())
            oldylim =  list(self.axes.get_ylim())
            if xlim is not None:
                for i in range(len(xlim)):
                    if xlim[i] is not None:
                        oldxlim[i] = xlim[i]
            if ylim is not None:
                for i in range(len(ylim)):
                    if ylim[i] is not None:
                        oldylim[i] = ylim[i]
            self.axes.set_xlim(oldxlim)
            self.axes.set_ylim(oldylim)
        return


    def set_line(self, number=None, **kwargs):
        """
        Set attributes for the specified line, or else the next line(s)
        to be plotted.

        number is the 0-relative number of a line that has already been
        plotted.  If no such line exists, attributes are recorded and used
        for the next line(s) to be plotted.

        Keyword arguments specify Line2D attributes, e.g. color='r'.  Do

            import matplotlib
            help(matplotlib.lines)

        The set_* methods of class Line2D define the attribute names and
        values.  For non-US usage, 'colour' is recognized as synonymous with
        'color'.

        Set the value to None to delete an attribute.

        Colour translation is done as described in the doctext for palette().
        """

        redraw = False
        for k, v in kwargs.iteritems():
            k = k.lower()
            if k == 'colour': k = 'color'

            if 0 <= number < len(self.lines):
                if self.lines[number] is not None:
                    for line in self.lines[number]:
                        getattr(line, "set_%s"%k)(v)
                    redraw = True
            else:
                if v is None:
                    del self.attributes[k]
                else:
                    self.attributes[k] = v

        if redraw: self.show(hardrefresh=False)


    def get_line(self):
        """
        Get the current default line attributes.
        """
        return self.attributes


    ### Actual plot methods ###
    def hist(self, x=None, y=None, fmt=None, add=None):
        """
        Plot a histogram.  N.B. the x values refer to the start of the
        histogram bin.

        fmt is the line style as in plot().
        """
        from numpy import array
        from numpy.ma import MaskedArray
        if x is None:
            if y is None: return
            x = range(len(y))

        if len(x) != len(y):
            return
        l2 = 2*len(x)
        x2 = range(l2)
        y2 = range(12)
        y2 = range(l2)
        m2 = range(l2)
        ymsk = None
        ydat = None
        if hasattr(y, "raw_mask"):
            # numpy < 1.1
            ymsk = y.raw_mask()
            ydat = y.raw_data()
        else:
            ymsk = y.mask
            ydat = y.data
        for i in range(l2):
            x2[i] = x[i/2]
            m2[i] = ymsk[i/2]

        y2[0] = 0.0
        for i in range(1,l2):
            y2[i] = ydat[(i-1)/2]

        self.plot(x2, MaskedArray(y2,mask=m2,copy=0), fmt, add)


    def plot(self, x=None, y=None, fmt=None, add=None):
        """
        Plot the next line in the current frame using the current line
        attributes.  The ASAPlot graphics window will be mapped and raised.

        The argument list works a bit like the matlab plot() function.
        """
        if x is None:
            if y is None: return
            x = range(len(y))

        elif y is None:
            y = x
            x = range(len(y))
        if fmt is None:
            line = self.axes.plot(x, y)
        else:
            line = self.axes.plot(x, y, fmt)
        # add a picker to lines for spectral value mode.
        # matplotlib.axes.plot returns a list of line object (1 element)
        line[0].set_picker(5.0)

        # Add to an existing line?
        i = None
        if add is None or len(self.lines) < add < 0:
            # Don't add.
            self.lines.append(line)
            i = len(self.lines) - 1
        else:
            if add == 0: add = len(self.lines)
            i = add - 1
            self.lines[i].extend(line)

        # Set/reset attributes for the line.
        gotcolour = False
        for k, v in self.attributes.iteritems():
            if k == 'color': gotcolour = True
            for segment in self.lines[i]:
                getattr(segment, "set_%s"%k)(v)

        if not gotcolour and len(self.colormap):
            for segment in self.lines[i]:
                getattr(segment, "set_color")(self.colormap[self.color])
                if len(self.colormap)  == 1:
                    getattr(segment, "set_dashes")(self.linestyles[self.linestyle])

            self.color += 1
            if self.color >= len(self.colormap):
                self.color = 0

            if len(self.colormap) == 1:
                self.linestyle += 1
            if self.linestyle >= len(self.linestyles):
                self.linestyle = 0

        self.show()


    def tidy(self):
        # this needs to be exceuted after the first "refresh"
        nplots = len(self.subplots)
        if nplots == 1: return
        for i in xrange(nplots):
            ax = self.subplots[i]['axes']
            if i%self.cols:
                ax.xaxis.majorTicks[0].label1On = False
            else:
                if i != 0:
                    ax.yaxis.majorTicks[-1].label1On = False
            ## set axes label state for interior subplots.
            #innerax=False
            #if i%self.cols:
            #    ax.yaxis.label.set_visible(innerax)
            #if (i <= (self.rows-1)*self.cols - 1) and (i+self.cols < nplots):
            #    ax.xaxis.label.set_visible(innerax)
            

    def set_title(self, title=None):
        """
        Set the title of the plot window.  Use the previous title if title is
        omitted.
        """
        if title is not None:
            self.title = title

        self.figure.text(0.5, 0.95, self.title, horizontalalignment='center')


    def text(self, *args, **kwargs):
        """
        Add text to the figure.
        """
        self.figure.text(*args, **kwargs)
        self.show()

    def vline_with_label(self, x, y, label,
                         location='bottom', rotate=0.0, **kwargs):
        """
        Plot a vertical line with label.
        It takes 'world' values fo x and y.
        """
        ax = self.axes
        # need this to suppress autoscaling during this function
        self.axes.set_autoscale_on(False)
        ymin = 0.0
        ymax = 1.0
        valign = 'center'
        if location.lower() == 'top':
            y = max(0.0, y)
        elif location.lower() == 'bottom':
            y = min(0.0, y)
        lbloffset = 0.06
        # a rough estimate for the bb of the text
        if rotate > 0.0: lbloffset = 0.03*len(label)
        peakoffset = 0.01
        xy = None
        xy0 = None
        # matplotlib api change 0.98 is using transform now
        if hasattr(ax.transData, "inverse_xy_tup"):
            # get relative coords
            xy0 = ax.transData.xy_tup((x,y))
            xy = ax.transAxes.inverse_xy_tup(xy0)
        else:
            xy0 = ax.transData.transform((x,y))
            # get relative coords
            xy = ax.transAxes.inverted().transform(xy0)
        if location.lower() == 'top':
            ymax = 1.0-lbloffset
            ymin = xy[1]+peakoffset
            valign = 'bottom'
            ylbl = ymax+0.01
        elif location.lower() == 'bottom':
            ymin = lbloffset
            ymax = xy[1]-peakoffset
            valign = 'top'
            ylbl = ymin-0.01
        trans = blended_transform_factory(ax.transData, ax.transAxes)
        l = ax.axvline(x, ymin, ymax, color='black', **kwargs)
        t = ax.text(x, ylbl ,label, verticalalignment=valign,
                                    horizontalalignment='center',
                    rotation=rotate,transform = trans)
        self.axes.set_autoscale_on(True)

    def release(self):
        """
        Release buffered graphics.
        """
        self.buffering = False
        self.show()


    def show(self, hardrefresh=True):
        """
        Show graphics dependent on the current buffering state.
        """
        if not hardrefresh: return
        if not self.buffering:
            if self.loc is not None:
                for sp in self.subplots:
                    lines  = []
                    labels = []
                    i = 0
                    for line in sp['lines']:
                        i += 1
                        if line is not None:
                            lines.append(line[0])
                            lbl = line[0].get_label()
                            if lbl == '':
                                lbl = str(i)
                            labels.append(lbl)

                    if len(lines):
                        fp = FP(size=rcParams['legend.fontsize'])
                        #fsz = fp.get_size_in_points() - len(lines)
                        fsz = fp.get_size_in_points() - max(len(lines),self.cols)
                        #fp.set_size(max(fsz,6))
                        fp.set_size(max(fsz,8))
                        sp['axes'].legend(tuple(lines), tuple(labels),
                                          self.loc, prop=fp)
                    #else:
                    #    sp['axes'].legend((' '))

            from matplotlib.artist import setp
            fpx = FP(size=rcParams['xtick.labelsize'])
            xts = fpx.get_size_in_points()- (self.cols)/2
            fpy = FP(size=rcParams['ytick.labelsize'])
            yts = fpy.get_size_in_points() - (self.rows)/2
            fpa = FP(size=rcParams['axes.labelsize'])
            fpat = FP(size=rcParams['axes.titlesize'])
            axsize =  fpa.get_size_in_points()
            tsize =  fpat.get_size_in_points()-(self.cols)/2
            for sp in self.subplots:
                ax = sp['axes']
                ax.title.set_size(tsize)
                setp(ax.get_xticklabels(), fontsize=xts)
                setp(ax.get_yticklabels(), fontsize=yts)
                off = 0
                if self.cols > 1: off = self.cols
                ax.xaxis.label.set_size(axsize-off)
                off = 0
                if self.rows > 1: off = self.rows
                ax.yaxis.label.set_size(axsize-off)

    def save(self, fname=None, orientation=None, dpi=None, papertype=None):
        """
        Save the plot to a file.

        fname is the name of the output file.  The image format is determined
        from the file suffix; 'png', 'ps', and 'eps' are recognized.  If no
        file name is specified 'yyyymmdd_hhmmss.png' is created in the current
        directory.
        """
        from asap import rcParams
        if papertype is None:
            papertype = rcParams['plotter.papertype']
        if fname is None:
            from datetime import datetime
            dstr = datetime.now().strftime('%Y%m%d_%H%M%S')
            fname = 'asap'+dstr+'.png'

        d = ['png','.ps','eps', 'svg']

        from os.path import expandvars
        fname = expandvars(fname)

        if fname[-3:].lower() in d:
            try:
                if fname[-3:].lower() == ".ps":
                    from matplotlib import __version__ as mv
                    w = self.figure.get_figwidth()
                    h = self.figure.get_figheight()

                    if orientation is None:
                        # oriented
                        if w > h:
                            orientation = 'landscape'
                        else:
                            orientation = 'portrait'
                    from matplotlib.backends.backend_ps import papersize
                    pw,ph = papersize[papertype.lower()]
                    ds = None
                    if orientation == 'landscape':
                        ds = min(ph/w, pw/h)
                    else:
                        ds = min(pw/w, ph/h)
                    ow = ds * w
                    oh = ds * h
                    self.figure.set_size_inches((ow, oh))
                    self.figure.savefig(fname, orientation=orientation,
                                        papertype=papertype.lower())
                    self.figure.set_size_inches((w, h))
                    print 'Written file %s' % (fname)
                else:
                    if dpi is None:
                        dpi =150
                    self.figure.savefig(fname,dpi=dpi)
                    print 'Written file %s' % (fname)
            except IOError, msg:
                #print 'Failed to save %s: Error msg was\n\n%s' % (fname, err)
                asaplog.post()
                asaplog.push('Failed to save %s: Error msg was\n\n%s' % (fname, str(msg)))
                asaplog.post( 'ERROR' )
                return
        else:
Ejemplo n.º 16
0
class Ui_Form(QtGui.QWidget):

    def __init__(self):
        # Declaración de elementos
        QtGui.QWidget.__init__(self)
        self.vinosDB = VinosDBL()
        self.figure = Figure()
        # ==============
        # Activación Elementos
        self.setupUi(self)
        self.databaseConnect()
        # ==============
        # Inicializando Modelos
        self.model_read = ListModel([])
        self.model_save = ListModel([])
        self.initModels()
        self.setAction()

# ===========================================
#              Ui_Form QtDesigner
# ===========================================

    def setupUi(self, ReadSaveData):
        ReadSaveData.setObjectName(_fromUtf8("ReadSaveData"))
        ReadSaveData.resize(692, 378)
        ReadSaveData.setMinimumSize(QtCore.QSize(550, 350))
        self.horizontalLayout_2 = QtGui.QHBoxLayout(ReadSaveData)
        self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))
        self.Tabs = QtGui.QTabWidget(ReadSaveData)
        self.Tabs.setMinimumSize(QtCore.QSize(450, 300))
        self.Tabs.setObjectName(_fromUtf8("Tabs"))
        self.Adquirir = QtGui.QWidget()
        self.Adquirir.setMinimumSize(QtCore.QSize(400, 300))
        self.Adquirir.setObjectName(_fromUtf8("Adquirir"))
        self.verticalLayout_5 = QtGui.QVBoxLayout(self.Adquirir)
        self.verticalLayout_5.setObjectName(_fromUtf8("verticalLayout_5"))
        self.verticalLayout_2 = QtGui.QVBoxLayout()
        self.verticalLayout_2.setSpacing(8)
        self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
        self.horizontalLayout = QtGui.QHBoxLayout()
        self.horizontalLayout.setSpacing(8)
        self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
        self.verticalLayout_4 = QtGui.QVBoxLayout()
        self.verticalLayout_4.setObjectName(_fromUtf8("verticalLayout_4"))
        self.horizontalLayout_4 = QtGui.QHBoxLayout()
        self.horizontalLayout_4.setSpacing(8)
        self.horizontalLayout_4.setObjectName(_fromUtf8("horizontalLayout_4"))
        self.label_estanque = QtGui.QLabel(self.Adquirir)
        self.label_estanque.setMinimumSize(QtCore.QSize(90, 30))
        self.label_estanque.setMaximumSize(QtCore.QSize(90, 30))
        self.label_estanque.setAlignment(QtCore.Qt.AlignCenter)
        self.label_estanque.setObjectName(_fromUtf8("label_estanque"))
        self.horizontalLayout_4.addWidget(self.label_estanque)
        self.linedit_estanquein = QtGui.QLineEdit(self.Adquirir)
        self.linedit_estanquein.setMinimumSize(QtCore.QSize(150, 30))
        self.linedit_estanquein.setMaximumSize(QtCore.QSize(350, 30))
        self.linedit_estanquein.setObjectName(_fromUtf8("linedit_estanquein"))
        self.horizontalLayout_4.addWidget(self.linedit_estanquein)
        self.verticalLayout_4.addLayout(self.horizontalLayout_4)
        self.horizontalLayout_6 = QtGui.QHBoxLayout()
        self.horizontalLayout_6.setObjectName(_fromUtf8("horizontalLayout_6"))
        self.label = QtGui.QLabel(self.Adquirir)
        self.label.setMinimumSize(QtCore.QSize(90, 30))
        self.label.setMaximumSize(QtCore.QSize(90, 30))
        self.label.setText(_fromUtf8(""))
        self.label.setObjectName(_fromUtf8("label"))
        self.horizontalLayout_6.addWidget(self.label)
        self.boton_estado = QtGui.QPushButton(self.Adquirir)
        self.boton_estado.setMinimumSize(QtCore.QSize(150, 30))
        self.boton_estado.setMaximumSize(QtCore.QSize(350, 30))
        self.boton_estado.setObjectName(_fromUtf8("boton_estado"))
        self.horizontalLayout_6.addWidget(self.boton_estado)
        self.verticalLayout_4.addLayout(self.horizontalLayout_6)
        self.horizontalLayout.addLayout(self.verticalLayout_4)
        self.verticalLayout_3 = QtGui.QVBoxLayout()
        self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3"))
        self.display_estanques = QtGui.QTextBrowser(self.Adquirir)
        self.display_estanques.setMinimumSize(QtCore.QSize(195, 90))
        self.display_estanques.setMaximumSize(QtCore.QSize(250, 150))
        self.display_estanques.setObjectName(_fromUtf8("display_estanques"))
        self.verticalLayout_3.addWidget(self.display_estanques)
        self.horizontalLayout.addLayout(self.verticalLayout_3)
        self.verticalLayout_2.addLayout(self.horizontalLayout)
        self.verticalLayout_5.addLayout(self.verticalLayout_2)
        self.verticalLayout_8 = QtGui.QVBoxLayout()
        self.verticalLayout_8.setObjectName(_fromUtf8("verticalLayout_8"))
        self.horizontalLayout_8 = QtGui.QHBoxLayout()
        self.horizontalLayout_8.setObjectName(_fromUtf8("horizontalLayout_8"))
        self.groupBox = QtGui.QGroupBox(self.Adquirir)
        self.groupBox.setMinimumSize(QtCore.QSize(100, 200))
        self.groupBox.setObjectName(_fromUtf8("groupBox"))
        self.verticalLayout_13 = QtGui.QVBoxLayout(self.groupBox)
        self.verticalLayout_13.setObjectName(_fromUtf8("verticalLayout_13"))
        self.verticalLayout_7 = QtGui.QVBoxLayout()
        self.verticalLayout_7.setSpacing(8)
        self.verticalLayout_7.setObjectName(_fromUtf8("verticalLayout_7"))
        self.verticalLayout_9 = QtGui.QVBoxLayout()
        self.verticalLayout_9.setSpacing(8)
        self.verticalLayout_9.setObjectName(_fromUtf8("verticalLayout_9"))
        self.listview_read = QtGui.QListView(self.groupBox)
        self.listview_read.setMinimumSize(QtCore.QSize(220, 70))
        self.listview_read.setMaximumSize(QtCore.QSize(1500, 150))
        self.listview_read.setObjectName(_fromUtf8("listview_read"))
        self.verticalLayout_9.addWidget(self.listview_read, QtCore.Qt.AlignHCenter)
        self.verticalLayout_7.addLayout(self.verticalLayout_9)
        self.horizontalLayout_7 = QtGui.QHBoxLayout()
        self.horizontalLayout_7.setObjectName(_fromUtf8("horizontalLayout_7"))
        self.verticalLayout_14 = QtGui.QVBoxLayout()
        self.verticalLayout_14.setObjectName(_fromUtf8("verticalLayout_14"))
        self.boton_load = QtGui.QPushButton(self.groupBox)
        self.boton_load.setMinimumSize(QtCore.QSize(95, 60))
        self.boton_load.setMaximumSize(QtCore.QSize(95, 70))
        self.boton_load.setObjectName(_fromUtf8("boton_load"))
        self.verticalLayout_14.addWidget(self.boton_load, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        self.horizontalLayout_7.addLayout(self.verticalLayout_14)
        self.verticalLayout_12 = QtGui.QVBoxLayout()
        self.verticalLayout_12.setObjectName(_fromUtf8("verticalLayout_12"))
        self.boton_clearall = QtGui.QPushButton(self.groupBox)
        self.boton_clearall.setMinimumSize(QtCore.QSize(95, 25))
        self.boton_clearall.setMaximumSize(QtCore.QSize(140, 30))
        self.boton_clearall.setObjectName(_fromUtf8("boton_clearall"))
        self.verticalLayout_12.addWidget(self.boton_clearall, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        self.boton_clearselect = QtGui.QPushButton(self.groupBox)
        self.boton_clearselect.setMinimumSize(QtCore.QSize(95, 25))
        self.boton_clearselect.setMaximumSize(QtCore.QSize(140, 30))
        self.boton_clearselect.setObjectName(_fromUtf8("boton_clearselect"))
        self.verticalLayout_12.addWidget(self.boton_clearselect, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        self.horizontalLayout_7.addLayout(self.verticalLayout_12)
        self.verticalLayout_7.addLayout(self.horizontalLayout_7)
        self.verticalLayout_13.addLayout(self.verticalLayout_7)
        self.horizontalLayout_8.addWidget(self.groupBox, QtCore.Qt.AlignHCenter)
        self.groupBox_2 = QtGui.QGroupBox(self.Adquirir)
        self.groupBox_2.setMinimumSize(QtCore.QSize(100, 200))
        self.groupBox_2.setObjectName(_fromUtf8("groupBox_2"))
        self.verticalLayout_17 = QtGui.QVBoxLayout(self.groupBox_2)
        self.verticalLayout_17.setObjectName(_fromUtf8("verticalLayout_17"))
        self.verticalLayout_10 = QtGui.QVBoxLayout()
        self.verticalLayout_10.setObjectName(_fromUtf8("verticalLayout_10"))
        self.verticalLayout_15 = QtGui.QVBoxLayout()
        self.verticalLayout_15.setObjectName(_fromUtf8("verticalLayout_15"))
        self.listview_save = QtGui.QListView(self.groupBox_2)
        self.listview_save.setMinimumSize(QtCore.QSize(220, 70))
        self.listview_save.setMaximumSize(QtCore.QSize(1500, 150))
        self.listview_save.setObjectName(_fromUtf8("listview_save"))
        self.verticalLayout_15.addWidget(self.listview_save)
        self.verticalLayout_10.addLayout(self.verticalLayout_15)
        self.horizontalLayout_9 = QtGui.QHBoxLayout()
        self.horizontalLayout_9.setObjectName(_fromUtf8("horizontalLayout_9"))
        self.verticalLayout_20 = QtGui.QVBoxLayout()
        self.verticalLayout_20.setObjectName(_fromUtf8("verticalLayout_20"))
        self.boton_selecttosave = QtGui.QPushButton(self.groupBox_2)
        self.boton_selecttosave.setMinimumSize(QtCore.QSize(95, 25))
        self.boton_selecttosave.setMaximumSize(QtCore.QSize(140, 30))
        self.boton_selecttosave.setObjectName(_fromUtf8("boton_selecttosave"))
        self.verticalLayout_20.addWidget(self.boton_selecttosave, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        self.boton_clearsave = QtGui.QPushButton(self.groupBox_2)
        self.boton_clearsave.setMinimumSize(QtCore.QSize(95, 25))
        self.boton_clearsave.setMaximumSize(QtCore.QSize(140, 30))
        self.boton_clearsave.setObjectName(_fromUtf8("boton_clearsave"))
        self.verticalLayout_20.addWidget(self.boton_clearsave, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        self.horizontalLayout_9.addLayout(self.verticalLayout_20)
        self.verticalLayout_16 = QtGui.QVBoxLayout()
        self.verticalLayout_16.setObjectName(_fromUtf8("verticalLayout_16"))
        self.boton_save = QtGui.QPushButton(self.groupBox_2)
        self.boton_save.setMinimumSize(QtCore.QSize(95, 60))
        self.boton_save.setMaximumSize(QtCore.QSize(95, 70))
        self.boton_save.setObjectName(_fromUtf8("boton_save"))
        self.verticalLayout_16.addWidget(self.boton_save, QtCore.Qt.AlignHCenter)
        self.horizontalLayout_9.addLayout(self.verticalLayout_16)
        self.verticalLayout_10.addLayout(self.horizontalLayout_9)
        self.verticalLayout_17.addLayout(self.verticalLayout_10)
        self.horizontalLayout_8.addWidget(self.groupBox_2, QtCore.Qt.AlignHCenter)
        self.verticalLayout_8.addLayout(self.horizontalLayout_8)
        self.verticalLayout_5.addLayout(self.verticalLayout_8)
        self.Tabs.addTab(self.Adquirir, _fromUtf8(""))
        self.Visualizar = QtGui.QWidget()
        self.Visualizar.setMinimumSize(QtCore.QSize(400, 300))
        self.Visualizar.setObjectName(_fromUtf8("Visualizar"))
        self.verticalLayout = QtGui.QVBoxLayout(self.Visualizar)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.verticalLayout_6 = QtGui.QVBoxLayout()
        self.verticalLayout_6.setSpacing(8)
        self.verticalLayout_6.setObjectName(_fromUtf8("verticalLayout_6"))
        self.horizontalLayout_5 = QtGui.QHBoxLayout()
        self.horizontalLayout_5.setObjectName(_fromUtf8("horizontalLayout_5"))
        self.label_comboboxplot = QtGui.QLabel(self.Visualizar)
        self.label_comboboxplot.setMinimumSize(QtCore.QSize(120, 25))
        self.label_comboboxplot.setMaximumSize(QtCore.QSize(100, 25))
        self.label_comboboxplot.setObjectName(_fromUtf8("label_comboboxplot"))
        self.horizontalLayout_5.addWidget(self.label_comboboxplot)
        self.combobox_plot = QtGui.QComboBox(self.Visualizar)
        self.combobox_plot.setMinimumSize(QtCore.QSize(340, 25))
        self.combobox_plot.setMaximumSize(QtCore.QSize(1000, 25))
        self.combobox_plot.setObjectName(_fromUtf8("combobox_plot"))
        self.horizontalLayout_5.addWidget(self.combobox_plot)
        self.verticalLayout_6.addLayout(self.horizontalLayout_5)
        self.verticalLayout.addLayout(self.verticalLayout_6)
        self.verticalLayout_18 = QtGui.QVBoxLayout()
        self.verticalLayout_18.setObjectName(_fromUtf8("verticalLayout_18"))
        self.verticalLayout_19 = QtGui.QVBoxLayout()
        self.verticalLayout_19.setObjectName(_fromUtf8("verticalLayout_19"))
        self.label_3 = QtGui.QLabel(self.Visualizar)
        self.label_3.setMinimumSize(QtCore.QSize(250, 25))
        self.label_3.setMaximumSize(QtCore.QSize(400, 25))
        self.label_3.setObjectName(_fromUtf8("label_3"))

        # ======================================================
        #  Se modifica el archivo diseñado en QtDesigner para
        #  incluir clase figure para graficar espectros
        # ======================================================

        self.verticalLayout_19.addWidget(self.label_3)
        self.graphic = FigureCanvas(self.figure)
        self.graphics_view = NavigationToolbar(self.graphic, self)
        self.graphics_view.setMinimumSize(QtCore.QSize(450, 24))
        self.graphics_view.setObjectName(_fromUtf8("graphics_view"))
        self.verticalLayout_19.addWidget(self.graphics_view)
        self.verticalLayout_19.addWidget(self.graphic)

        # ======================================================

        self.verticalLayout_18.addLayout(self.verticalLayout_19)
        self.verticalLayout.addLayout(self.verticalLayout_18)
        self.Tabs.addTab(self.Visualizar, _fromUtf8(""))
        self.horizontalLayout_2.addWidget(self.Tabs)

        self.retranslateUi(ReadSaveData)
        self.Tabs.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(ReadSaveData)

    def retranslateUi(self, ReadSaveData):
        ReadSaveData.setWindowTitle(_translate("ReadSaveData", "Adquisición y Visualización Espectros", None))
        self.label_estanque.setText(_translate("ReadSaveData", "  N° Estanque \n"
                                                               " a Analizar", None))
        self.boton_estado.setText(_translate("ReadSaveData", "Verificar", None))
        self.groupBox.setTitle(_translate("ReadSaveData", "Espectrómetro", None))
        self.boton_load.setText(_translate("ReadSaveData", "Adquirir", None))
        self.boton_clearall.setText(_translate("ReadSaveData", "Clear All", None))
        self.boton_clearselect.setText(_translate("ReadSaveData", "Clear Select", None))
        self.groupBox_2.setTitle(_translate("ReadSaveData", "Base de Datos", None))
        self.boton_selecttosave.setText(_translate("ReadSaveData", "Seleccionar", None))
        self.boton_clearsave.setText(_translate("ReadSaveData", "Clear Select", None))
        self.boton_save.setText(_translate("ReadSaveData", "Guardar a\n"
                                                           " Base de Datos", None))
        self.Tabs.setTabText(self.Tabs.indexOf(self.Adquirir), _translate("ReadSaveData", "Adquirir", None))
        self.label_comboboxplot.setText(_translate("ReadSaveData", "Espectros Adquiridos", None))
        self.label_3.setText(_translate("ReadSaveData", "TextLabel", None))
        self.Tabs.setTabText(self.Tabs.indexOf(self.Visualizar), _translate("ReadSaveData", "Visualizar", None))

    # ===========================================
    #          Metodos para clase Ui_Form
    # ===========================================

    def initModels(self):
        """
        Función que inicializa los modelos utilizados para  la lectura,
        escritura y visualización de espectros.
        """
        self.listview_read.setModel(self.model_read)
        self.combobox_plot.setModel(self.model_read)
        self.listview_save.setModel(self.model_save)

    def databaseConnect(self):
        """
        Función que realiza las configuraciones necesarias para la conexión
        a la base de datos de forma local o al servidor habilitado mediante
        túnel SSH.
        """
        # ssh hace el túnel hacia el servidor donde
        # esta instalada la base de datos
        sshConf = {
            u'sshHost': u'200.1.17.223',
            u'sshPort': 22,
            u'sshUser': u'spartan',
            u'sshPass': u'1Q2w3e4r5t6y7u8i9o0p',
            u'localHost': u'127.0.0.1',
            u'localPort': 3037
        }
        # el servidor corre una base de datos local
        # con al siguiente configuración
        dbConf = {
            u'dbHost': u'127.0.0.1',
            u'dbPort': 3306,
            u'dbUser': u'root',
            u'dbPass': u'1Q2w3e4r5t6y7u8i9o0p',
            u'dbName': u'vinosdb'
        }
        self.vinosDB.connect(dbConf=dbConf, sshConf=sshConf)
        print u'Conectado a Base de Datos:', self.vinosDB.conn.is_connected()

    def setAction(self):
        """
        Función que asocia cada evento en la interfaz con una acción
        determinada.
        """
        self.boton_estado.clicked.connect(lambda: self.boton_estadoHandler(tankName=self.linedit_estanquein.text()))
        self.boton_load.clicked.connect(lambda: self.boton_loadHandler())
        self.boton_clearall.clicked.connect(lambda: self.boton_clearallHandler())
        self.boton_clearselect.clicked.connect(lambda: self.boton_clearselectHandler())
        self.listview_read.selectionModel().selectionChanged.connect(lambda: self.listread2comboboxplot())
        self.combobox_plot.activated.connect(lambda: self.combobox_plotHandler())
        self.combobox_plot.activated.connect(lambda: self.plotEspectro())
        self.Tabs.currentChanged.connect(lambda: self.plotEspectro())
        self.boton_selecttosave.clicked.connect(lambda: self.boton_selecttosaveHandler())
        self.boton_clearsave.clicked.connect(lambda: self.boton_clearsaveHandler())
        self.boton_save.clicked.connect(lambda: self.boton_saveHandler())

    def resizeEvent(self, event):
        """
        Funcion que reajusta el tamaño del gráfico al cambiar el
        tamalo de la interfaz
        :param event:
        """
        if self.figure.get_axes():
            self.figure.tight_layout()

    def boton_estadoHandler(self, tankName):
        """
        Función que asocia una acción al oprimir el botón "boton_estado" (en GUI
        botón "Verificar", Pestaña Adquirir). La acción es verificar si el número
        de estanque ingresado existe en la tabla "estanques" de la base de datos.
        :param tankName: Str.
        """
        if not tankName:
            data = u'*** ERROR: No ha ingresado número de estanque ***'
            flag = 1
            self.display_row1(data, tankName, flag)
        else:
            flag = 0
            tankName = int(tankName)
            data = self.vinosDB.read_estanque(tankName)
            self.display_row1(data, tankName, flag)

    def display_row1(self, data, tankName, flag):
        """
        Función que despliega información asociada al número de estanque del
        parámetro "tankName", en objeto "display_estanques" (Pestaña Adquirir).
        Específicamente la información contenida en la tabla "estanques" de la
        base de datos (nombre, tipo vino, año y descripción).
        :param data: Tuple
        :param tankName: Str
        :param flag: Bool
        """
        self.display_estanques.clear()
        if flag == False:
            if not data:
                error = u'*** ERROR: Estanque N° %d No Existe***' % tankName
                self.display_estanques.append(u' %s' % error)
            else:
                num = data[0][0]
                desc = data[0][1].split(u', ')
                self.display_estanques.append(u' Estanque número: %s \t ' % num)
                for i in range(len(desc)):
                    self.display_estanques.append(u' %s' % desc[i])
        else:
            self.display_estanques.append(u' %s' % data)

    def boton_loadHandler(self):
        """
        Función que asocia una acción al oprimir el botón "boton_load" (en GUI
        botón "Adquirir", Pestaña Adquirir). La acción es cargar un espectro del
        directorio especificado y lo almacena en directorio temporal creado, en
        espera de visualización en gráfico o escritura a base de datos. También
        incluye el nombre del archivo cargado al modelo "model_read" para administrar
        los archivos a guardar o visualizar.
        """
        ts = unicode(datetime.datetime.now().strftime("- %Y-%m-%d %H-%M-%S"))
        path_read = "../data/Espectros_vino_21-03-2018/*.txt"
        # path_read = "../data/Espectros Vinos/*.txt"
        path_temp = "../data/Temporal_Load"
        if not os.path.isdir(path_temp):
            os.mkdir(path_temp)
        files = glob.glob(path_read)
        temp = files[randint(1, len(files) - 1)]
        name = unicode(os.path.splitext(os.path.basename(temp))[0])
        shutil.copy2(temp, path_temp)
        dst_name = 'Espectro'+' '+name+' '+ts+'.txt'
        os.rename(path_temp+'/'+name+'.txt', path_temp+'/'+dst_name)
        self.model_read.addNewValue(dst_name)
        self.model_read.reset()
        self.listview_read.setCurrentIndex(self.model_read.index(0))

    def boton_clearselectHandler(self):
        """
        Función que asocia una acción al oprimir el botón "boton_clearselect" (en GUI
        botón "Clear Select", Pestaña Adquirir). Elimina el archivo seleccionado en
        la lista "listview_read" del directorio temporal y del modelo "model_read".
        Luego de eliminar el archivo se actualiza el modelo "model_read" y la lista
        "listview_read".
        """
        path_temp = "../data/Temporal_Load/"
        itemIndex = self.listview_read.currentIndex().row()
        itemTotal = self.model_read.rowCount(None)
        if itemTotal is not 0 and itemIndex is not -1:
            item = self.model_read.consultData(itemIndex)
            if item not in self.model_save.model_list:
                name = self.model_read.consultData(itemIndex)
                os.remove(path_temp + name)
                self.model_read.removeRows(self.listview_read.currentIndex().row(), 1, QtCore.QModelIndex())
                self.model_read.reset()
                if itemIndex > 0:
                    self.listview_read.setCurrentIndex(self.model_read.index(itemIndex-1))
                else:
                    self.listview_read.setCurrentIndex(self.model_read.index(0))
            else:
                print u'El item a eliminar ha sido seleccionado para guardar'
        else:
            print u'No item selected/available!'

    def boton_clearallHandler(self):
        """
        Función que asocia una acción al oprimir el botón "boton_clearall" (en GUI
        botón "Clear All", Pestaña Adquirir). Elimina todos los archivos del directorio
        temporal, de la lista "listview_read" y del modelo "model_read". Luego de eliminar
        los archivos se actualiza el modelo "model_read" y la lista "listview_read".
        """
        path_temp = "../data/Temporal_Load/"
        aux_list = []
        for item in self.model_read.model_list:
            if item not in self.model_save.model_list:
                itemIndex = self.model_read.model_list.index(item)
                name = self.model_read.consultData(itemIndex)
                os.remove(path_temp + name)
            else:
                aux_list.append(item)
        self.model_read.model_list = aux_list
        self.model_read.model_list.sort()
        self.model_save.model_list.sort()
        self.model_read.reset()
        self.model_save.reset()

    def combobox_plotHandler(self):
        """
        Función que actualiza los índices correspondientes al modelo para ser
        visualizados en el gráfico (Pestaña Visualizar), según lo que se encuentra
        seleccionado en el combobox "combobox_plot" (Pestaña Visualizar).
        """
        itemIndex = self.combobox_plot.currentIndex()
        itemTotal = self.model_read.rowCount(None)
        if itemTotal is not 0 and itemIndex is not -1:
            if itemIndex > 0:
                self.listview_read.setCurrentIndex(self.model_read.index(itemIndex))
            else:
                self.listview_read.setCurrentIndex(self.model_read.index(0))
        else:
            print u'No item selected/available!'

    def listread2comboboxplot(self):
        """
        Asigna la seleccion en la lista "listview_read" a la seleccion
        en el combobox "combobox_plot" (Pestaña Visualizar), para visualizar el espectro
        seleccionado en la lista "listview_read" al cambiar de pestaña
        en la aplicación.
        """
        itemIndex = self.listview_read.currentIndex().row()
        self.combobox_plot.setCurrentIndex(itemIndex)

    def plotEspectro(self):
        """
        Funcion que grafica el espectro seleccionado en el combobox
        "combobox_plot" (Pestaña Visualizar). Al cambiar la selección en el combobox, se
        actualiza también el gráfico al espectro seleccionado.
        """
        path_temp = "../data/Temporal_Load/"
        itemTotal = self.model_read.rowCount(self)
        itemIndex = self.combobox_plot.currentIndex()
        if itemTotal is not 0 and itemIndex is not -1:
            name = self.model_read.consultData(itemIndex)
            file = open(path_temp + name, "r")
            datos = list(csv.reader(file, delimiter=','))
            n = len(datos[:])
            y = np.zeros((n-3, 1))
            for i in range(0, n-3):
                y[i] = float(datos[i+3][1])
            ax = self.figure.add_subplot(111)
            ax.clear()
            ax.spines["top"].set_visible(False)
            ax.spines["right"].set_visible(False)
            ax.get_xaxis().tick_bottom()
            ax.get_yaxis().tick_left()
            ax.set_xlabel("Frecuencia [Hz]", fontsize=12)
            ax.set_ylabel("Amplitud", fontsize=12)
            ax.plot(np.flipud(y), color='g')
            ax.axis('tight')
            self.figure.set_facecolor('white')
            self.graphics_view.draw()
            self.figure.tight_layout()

    def boton_selecttosaveHandler(self):
        """
        Función que asocia una acción al oprimir el botón "boton_selecttosave" (en GUI
        botón "Seleccionar", Pestaña Adquirir). Selecciona archivos de la lista "listview_read"
        relacionada al modelo "model_read", para visualizar en lista "listview_save" y guardar
        en modelo "model_save".
        """
        itemTotal = self.model_read.rowCount(self)
        itemIndex = self.listview_read.currentIndex().row()
        if itemTotal is not 0 and itemIndex is not -1:
            name = self.model_read.consultData(itemIndex)
            if not self.model_save.consultItem(name):
                self.model_save.addNewValue(name)
                self.model_save.reset()
                self.listview_save.setCurrentIndex(self.model_save.index(0))
            else:
                print u'Espectro ya fue seleccionado'
        else:
            print u'Item no seleccionado/disponible!'

    def boton_clearsaveHandler(self):
        """
        Función que asocia una acción al oprimir el botón "boton_clearsave" (en GUI
        botón "Clear Select", Pestaña Adquirir). Elimina el archivo seleccionado en
        la lista "listview_save" y del modelo "model_save". Luego de eliminar el archivo
        de la lista se actualiza el modelo "model_save" y la lista "listview_save".
        """
        itemIndex = self.listview_save.currentIndex().row()
        itemTotal = self.model_save.rowCount(None)
        if itemTotal is not 0 and itemIndex is not -1:
            self.model_save.removeRows(self.listview_save.currentIndex().row(), 1, QtCore.QModelIndex())
            self.model_save.reset()
            if itemIndex > 0:
                self.listview_save.setCurrentIndex(self.model_save.index(itemIndex - 1))
            else:
                self.listview_save.setCurrentIndex(self.model_save.index(0))
        else:
            print u'No item selected/available!'

    def boton_saveHandler(self):
        """
        Función que asocia una acción al oprimir el botón "boton_save" (en GUI
        botón "Guardar a Base de Datos", Pestaña Adquirir). La acción es guardar
        el espectro seleccionado en la lista "listview_save" a la base de datos
        y actualizar el modelo "model save" luego de realizada la carga del archivo.
        """
        tankName = self.linedit_estanquein.text()
        if not tankName:
            data = u'*** ERROR: No ha ingresado número de estanque *** \n ' \
                   u'Debe ingresar estanque para almacenar espectro en base de datos!'
            flag = 1
            self.display_row1(data, tankName, flag)
        else:
            tankName = int(tankName)
            path_temp = "../data/Temporal_Load/"
            itemIndex = self.listview_save.currentIndex().row()
            itemTotal = self.model_save.rowCount(None)
            if itemTotal is not 0 and itemIndex is not -1:
                if tankName is not None:
                    data_estanque = self.vinosDB.read_estanque(tankName)
                    id_estanque = data_estanque[0][2]
                    data_vinos = self.vinosDB.read_fechavino(id_estanque)
                    id_vino = data_vinos[0][1]
                    item = self.model_save.model_list[itemIndex]
                    filename = path_temp + item
                    self.vinosDB.new_espectro(filename, id_vino, id_estanque)
                    itemIndexRead = self.model_read.model_list.index(item)
                    self.model_save.removeRows(itemIndex, 1)
                    self.model_read.removeRows(itemIndexRead, 1)
                    os.remove(filename)
            else:
                print u'No ha seleccionado/adquirido espectro para guardar!'

    def closeEvent(self, event):
        """
        Función que asocia una acción al evento de cierre de la aplicación.
        Limpia los archivos almacenados en el directorio temporal, limpia los
        nombres contenidos en los modelos, cierra la conexión a la base de
        datos y cierra la aplicación.
        """
        path_temp = "../data/Temporal_Load/*.txt"
        files = glob.glob(path_temp)
        for file in files:
            os.remove(file)
        self.model_read.removeAllRows()
        self.model_save.removeAllRows()
        self.model_read.reset()
        self.model_save.reset()
        self.vinosDB.close()
        self.close()
Ejemplo n.º 17
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,
                 **kws):

        self.trace_color_callback = trace_color_callback
        matplotlib.rc('axes', axisbelow=True)
        matplotlib.rc('lines', linewidth=2)
        matplotlib.rc('xtick', labelsize=fontsize, color='k')
        matplotlib.rc('ytick', labelsize=fontsize, color='k')
        matplotlib.rc('legend', fontsize=fontsize)
        matplotlib.rc('grid', linewidth=0.5, linestyle='-')

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

        self.conf = PlotConfig(panel=self, 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
        if facecolor is not None:
            self.conf.bgcolor = facecolor
        if axisbg is not None:
            self.conf.bgcolor = axisbg

        # 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 = {}

    def plot(self,
             xdata,
             ydata,
             side='left',
             title=None,
             xlabel=None,
             ylabel=None,
             y2label=None,
             use_dates=False,
             **kws):
        """
        plot (that is, create a new plot: clear, then oplot)
        """
        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.ntrace = 0
        self.conf.yscale = 'linear'
        self.conf.user_limits[axes] = [None, None, None, None]

        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 use_dates is not None:
            self.use_dates = 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,
              legendfontsize=None,
              fullbox=None,
              axes_style=None,
              zorder=None,
              **kws):
        """ basic plot method, overplotting any existing plot """
        self.cursor_mode = 'zoom'
        conf = self.conf
        conf.plot_type = 'lineplot'

        axes = self.axes
        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))
        if self.use_dates:
            xdata = [datetime.fromtimestamp(i) for i in xdata]
            xdata = dates.date2num(xdata)
            # axes.xaxis.set_major_locator(dates.AutoDateLocator())

        if linewidth is None:
            linewidth = 2

        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 show_legend is not None:
            conf.set_legend_location(legend_loc, legend_on)
            conf.show_legend = show_legend

        if grid is not None:
            conf.show_grid = 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]

        if xmin is not None:
            conf.user_limits[axes][0] = xmin
        if xmax is not None:
            conf.user_limits[axes][1] = xmax
        if ymin is not None:
            conf.user_limits[axes][2] = ymin
        if ymax is not None:
            conf.user_limits[axes][3] = ymax

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

        n = conf.ntrace
        if zorder is None:
            zorder = 5 * (n + 1)
        if axes not in conf.axes_traces:
            conf.axes_traces[axes] = []
        conf.axes_traces[axes].append(n)

        if bgcolor is not None:
            conf.bgcolor = bgcolor
            axes.set_axis_bgcolor(bgcolor)
        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 gridcolor is not None:
            conf.gridcolor = gridcolor
        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)

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

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

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

        # now set plot limits:
        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)

        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,
                    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

        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)))

        fcols = [to_rgba(self.conf.scatter_normalcolor) for x in xdata]
        ecols = [self.conf.scatter_normaledge] * len(xdata)

        self.conf.scatter_data = [(x, y) for x, y in zip(xdata, ydata)]
        self.conf.scatter_size = size
        self.conf.scatter_coll = CircleCollection(
            sizes=(size, ),
            facecolors=fcols,
            edgecolors=ecols,
            offsets=self.conf.scatter_data,
            transOffset=self.axes.transData)
        self.axes.add_collection(self.conf.scatter_coll)

        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)
        self.set_viewlimits()
        self.draw()

    def lassoHandler(self, vertices):
        conf = self.conf

        if self.conf.plot_type == 'scatter':
            fcols = conf.scatter_coll.get_facecolors()
            ecols = conf.scatter_coll.get_edgecolors()
            sdat = conf.scatter_data
            mask = inside_poly(vertices, sdat)
            pts = nonzero(mask)[0]
            self.conf.scatter_mask = mask
            for i in range(len(sdat)):
                if i in pts:
                    ecols[i] = to_rgba(conf.scatter_selectedge)
                    fcols[i] = to_rgba(conf.scatter_selectcolor)
                    fcols[i][3] = 0.3
                    ecols[i][3] = 0.8
                else:
                    fcols[i] = to_rgba(conf.scatter_normalcolor)
                    ecols[i] = to_rgba(conf.scatter_normaledge)
        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)
            # print mask
            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] = 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.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.conf.unzoom(full=False)

    def unzoom_all(self, event=None):
        """ zoom out full data range """
        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'):
        "set log or linear scale for x, y axis"
        self.conf.set_logscale(xscale=xscale, yscale=yscale)

    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)
        kwargs = {'facecolor': self.conf.bgcolor}
        if matplotlib.__version__ < "2.0":
            kwargs = {'axisbg': self.conf.bgcolor}

        self.axes = self.fig.add_subplot(self.gridspec[0], **kwargs)

        self.canvas = FigureCanvas(self, -1, self.fig)

        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.Fit()

        canvas_draw = self.canvas.draw

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

        self.canvas.draw = draw
        self.addCanvasEvents()

    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 = max(dl, (x0 - ox0))
                dt = max(dt, (oy1 - y1))
                dr = max(dr, (ox1 - x1))
                db = max(db, (y0 - oy0))
            except:
                pass

        # print(" > %.3f %.3f %.3f %.3f " % (dl, dt, dr, db))
        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)

    def onExport(self, event=None, **kws):
        ofile = ''
        title = 'unknown plot'

        if self.conf.title is not None:
            title = ofile = self.conf.title.strip()
        if len(ofile) > 64:
            ofile = ofile[:63].strip()
        if len(ofile) < 1:
            ofile = 'plot'

        for c in ' .:";|/\\(){}[]\'&^%*$+=-?!@#':
            ofile = ofile.replace(c, '_')

        while '__' in ofile:
            ofile = ofile.replace('__', '_')

        ofile = ofile + '.dat'

        dlg = wx.FileDialog(self,
                            message='Export Map Data to ASCII...',
                            defaultDir=os.getcwd(),
                            defaultFile=ofile,
                            style=wx.FD_SAVE | wx.FD_CHANGE_DIR)

        if dlg.ShowModal() == wx.ID_OK:
            self.writeASCIIFile(dlg.GetPath(), title=title)

    def writeASCIIFile(self, fname, title='unknown plot'):
        buff = ["# X,Y Data for %s" % title, "#------------", "#     X     Y"]
        lines = self.axes.get_lines()
        x0 = lines[0].get_xaxis()
        y0 = lines[0].get_yaxis()
        lab0 = lines[0].get_label().strip()
        if len(lab0) < 1: lab0 = 'Y'
        buff.append("#   X    %s" % lab0)
        outa = [x0, y0]
        npts = len(x0)
        if len(lines) > 1:
            for ix, line in enumerate(lines[1:]):
                lab = ['#   ', '       ', line.get_label().strip()]
                x = line.get_xdata()
                y = line.get_ydata()
                npts = max(npts, len(y))
                if not all(x == x0):
                    lab0[1] = ' X%i ' % (ix + 2)
                    out.append(x)
                out.append(line.get_ydata())

        fout = open(fname, 'w')
        fout.write("%s\n" % "\n".join(buff))
        fout.close()

        orig_dir = os.path.abspath(os.curdir)
        thisdir = os.getcwd()
        file_choices = "DAT (*.dat)|*.dat|ALL FILES (*.*)|*.*"
        dlg = wx.FileDialog(self,
                            message='Export Plot Data to ASCII...',
                            defaultDir=thisdir,
                            defaultFile=ofile,
                            wildcard=file_choices,
                            style=wx.FD_SAVE | wx.FD_CHANGE_DIR)

        if dlg.ShowModal() == wx.ID_OK:
            self.writeASCIIFile(dlg.GetPath(), title=title)
        os.chdir(orig_dir)

    def writeASCIIFile(self, fname, title='unknown plot'):
        "save plot data to external file"

        buff = ["# Plot Data for %s" % title, "#------"]

        out = []
        labs = []
        itrace = 0
        for ax in self.fig.get_axes():
            for line in ax.lines:
                itrace += 1
                x = line.get_xdata()
                y = line.get_ydata()
                ylab = line.get_label()

                if len(ylab) < 1: ylab = ' Y%i' % itrace
                if len(ylab) < 4: ylab = ' %s%s' % (' ' * 4, lab)
                for c in ' .:";|/\\(){}[]\'&^%*$+=-?!@#':
                    ylab = ylab.replace(c, '_')

                pad = max(1, 13 - len(ylab))
                lab = '     X%i   %s%s  ' % (itrace, ' ' * pad, ylab)
                out.extend([x, y])
                labs.append(lab)
        buff.append('# %s ' % (' '.join(labs)))

        npts = [len(a) for a in out]
        for i in range(max(npts)):
            oline = []
            for a in out:
                d = nan
                if i < len(a): d = a[i]
                oline.append(gformat(d))
            buff.append(' '.join(oline))

        if itrace > 0:
            fout = open(fname, 'w')
            fout.write("%s\n" % "\n".join(buff))
            fout.close()
            self.write_message("Exported data to '%s'" % fname, panel=0)

    ####
    ## 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= %s, %s" % (self._xfmt, self._yfmt)) % (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= %s, %s, %s" % (self._xfmt, self._yfmt,
                                              self._y2fmt) % (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)
Ejemplo n.º 18
0
class Window(QMainWindow):
    save_crop_signal = pyqtSignal()

    samling_ratio = 60

    class State(Enum):
        Initial = 1
        OneCursorPlaced = 2
        TwoCursorPlaced = 3

    def __init__(self):
        super().__init__()
        self.player = QMediaPlayer()
        self.ui = Ui_MainWindow()
        # setup main Window's UI
        self.ui.setupUi(self)
        self._setup_connections()
        self._setup_matplotlib()
        self._setup_directory_browser()
        self.play_timer = QTimer(self)
        self.play_timer.timeout.connect(self.plot_animator)
        self.ymax = 30000
        self.state = None
        self.crop_line_1_pos = None
        self.crop_line_2_pos = None
        self.play_limit = (0, 0)
        self.current_zoom = 100
        self.seconds_to_prefetch = 60
        self.minimum_file_file_length = 2.5
        self.save_crop_signal.connect(self.save_crop_to_file)

    def _setup_matplotlib(self):

        self.figure = Figure()
        self.canvas = FigureCanvas(self.figure)
        layout = self.ui.graph_widget.layout()
        layout.addWidget(self.canvas)
        self.canvas_click_connection = None
        self.canvas_scroll_connection = None
        self.subplot = self.figure.add_subplot(111)

        self.audio_plot, = self.subplot.plot([0], [0],
                                             '-',
                                             color=(0.70, 0.70, 0.70))
        self.playIndicator, = self.subplot.plot([0], [0], '^')
        self.crop_line_1, = self.subplot.plot([], [], '-r')
        self.crop_line_2, = self.subplot.plot([], [], '-r')
        self.ui.vertical_splitter.setSizes([2, 12])

    def _setup_directory_browser(self):
        curdir = os.path.abspath(os.path.curdir)
        self.file_system_model = QFileSystemModel(self)
        self.file_system_model.setReadOnly(True)
        self.file_system_model.setFilter(QDir.AllDirs | QDir.AllEntries
                                         | QDir.NoDotAndDotDot)
        self.file_system_model.setNameFilters(['*.wav', '*.txt'])
        self.file_system_model.setNameFilterDisables(False)

        self.ui.directory_view.setModel(self.file_system_model)
        self.file_system_model.setRootPath(curdir)
        index = self.file_system_model.index(curdir)
        self.ui.directory_view.setRootIndex(index)
        self.ui.directory_view.hideColumn(1)
        self.ui.directory_view.hideColumn(2)
        self.ui.directory_view.hideColumn(3)
        # index: QModelIndex = model.index(os.path.abspath(os.path.curdir))
        # self.ui.directory_view.expand(index);
        # self.ui.directory_view.scrollTo(index)
        # self.ui.directory_view.setCurrentIndex(index)
        self.ui.directory_view.doubleClicked.connect(self.file_selected)

    def plot(self, ydata_byte, start_pos=0):
        ''' plot some random stuff '''
        ploty = numpy.fromstring(ydata_byte, numpy.int16)
        plotdatay = ploty[0:len(ploty):Window.samling_ratio]
        plotdatax = [
            x for x in range(start_pos,
                             len(ploty) + start_pos, Window.samling_ratio)
        ]

        self.set_view_range(start_pos, plotdatax[-1])
        self.view_limit_range = (start_pos, plotdatax[-1])
        _max = max(plotdatay)
        self.figure.get_axes()[0].set_ylim(-_max, _max)
        print("The real plot limit", (start_pos, plotdatax[-1]))
        self.audio_plot.set_data(plotdatax, plotdatay)
        # refresh canvas
        self.canvas.draw()

    def set_view_range(self, start, end):
        self.figure.get_axes()[0].set_xlim(start, end)
        self.current_view = [start, end]

    def plot_animator(self):

        # current_music_time = self.play_started_audio_time + diff
        current_time_ms = self.player.position()
        current_music_frame = int(
            (current_time_ms * self.wave.getframerate()) / 1000)

        self.playIndicator.set_data([current_music_frame], [0])
        self.canvas.draw()
        if current_time_ms >= self.play_limit[1]:
            self.player.pause()
            self.play_timer.stop()
            self.playIndicator.set_data(self.crop_line_2_pos, 0)
        self.canvas.draw()

    def _setup_connections(self):
        self.player.stateChanged.connect(self.player_status_changed)
        self.player.positionChanged.connect(self.playback_progress_change)

    def toggle_pause(self):
        if self.playing:
            self.player.pause()
            self.play_timer.stop()
        else:
            self.player.play()

            self.play_timer.start()
        self.playing = not self.playing

    def seek(self, pos):
        self.player.setPosition(pos)
        self.player.play()
        if self.player.state() != QMediaPlayer.PlayingState:
            self.player.play()
        if not self.play_timer.isActive():
            self.play_timer.start()

    def seek_frame(self, frame_pos):
        self.seek(int((frame_pos * 1000) / (self.wave.getframerate())))
        # self.seek(int(frame_pos / (self.wave.getframerate() * 1000)))

    def canvas_click_listener(self, event):
        if not event.xdata:
            return
        currentframe = event.xdata
        current_time_in_milli = int(currentframe / self.wave.getframerate() *
                                    1000)
        if (current_time_in_milli < 0):
            current_time_in_milli = 0
        current_x = int(event.xdata)
        if (current_x < 0):
            current_x = 0

        if self.state == Window.State.Initial:
            self.crop_line_1.set_data([current_x, current_x],
                                      [-self.ymax, self.ymax])
            self.crop_line_1_pos = current_x
        elif self.state == Window.State.OneCursorPlaced:
            if current_x > self.crop_line_1_pos:
                self.crop_line_2_pos = current_x
                self.crop_line_2.set_data([current_x, current_x],
                                          [-self.ymax, self.ymax])
        self.canvas.draw()
        self.seek(current_time_in_milli)

    def canvas_scroll_listener(self, event):
        current_range = self.current_view[1] - self.current_view[0]
        if current_range < 0:
            return
        if event.button == 'down':
            new_range = current_range / 1.3
        else:
            new_range = current_range * 1.3

        new_view = [event.xdata - new_range / 2, event.xdata + new_range / 2]
        count = 0
        if new_view[0] < self.view_limit_range[0]:
            new_view[1] += self.view_limit_range[0] - new_view[0]
            new_view[0] = self.view_limit_range[0]
            if new_view[1] > self.view_limit_range[1]:
                new_view[1] = self.view_limit_range[1]

        elif new_view[1] > self.view_limit_range[1]:
            new_view[0] -= new_view[1] - self.view_limit_range[1]
            new_view[1] = self.view_limit_range[1]
            if new_view[0] < self.view_limit_range[0]:
                new_view[0] = self.view_limit_range[0]
        if new_view[0] > new_view[1]:
            return
        self.figure.get_axes()[0].set_xlim(new_view[0], new_view[1])
        self.current_view = new_view
        self.canvas.draw()

    def eventFilter(self, event):
        print("New Event", event.type())
        return False

    def keyPressEvent(self, event):

        key = event.key()
        if key == Qt.Key_Space:
            self.toggle_pause()
        if key == Qt.Key_Return:

            if self.state == Window.State.Initial:
                if self.crop_line_1_pos:
                    self.state = Window.State.OneCursorPlaced
                    self.crop_line_1.set_data(
                        [self.crop_line_1_pos, self.crop_line_1_pos],
                        [-self.ymax, self.ymax])
                    self.crop_line_1.set_color("green")
                    self.canvas.draw()

            elif self.state == Window.State.OneCursorPlaced:
                if self.crop_line_2_pos:
                    self.crop_line_2.set_color("green")
                    self.zoom_to_crop()
                    self.state = Window.State.TwoCursorPlaced
                    self.ui.statusbar.showMessage(
                        "Press Enter to save the clip to file", 3000)

            elif self.state == Window.State.TwoCursorPlaced:
                self.crop_to_save = self.data[self.crop_line_1_pos *
                                              2:self.crop_line_2_pos * 2]
                self.crop_nchannel = self.wave.getnchannels()
                self.crop_stampwidth = self.wave.getsampwidth()
                self.crop_framerate = self.wave.getframerate()
                self.save_crop_signal.emit()
                self.update_cropped_plot()

        if key == Qt.Key_Backspace:
            if self.state == Window.State.OneCursorPlaced:
                self.state = Window.State.Initial
                self.crop_line_1_pos = None
                self.crop_line_1.set_color("red")
                self.crop_line_2.set_data([], [])
                self.canvas.draw()
            elif self.state == Window.State.TwoCursorPlaced:

                self.play_limit = self.play_limit_bak
                self.state = Window.State.OneCursorPlaced
                self.crop_line_2_pos = None
                self.crop_line_2.set_color("red")
                self.canvas.draw()

    @pyqtSlot(QMediaPlayer.State)
    def player_status_changed(self, x):
        if x == QMediaPlayer.StoppedState:
            self.play_timer.stop()
            pass
        elif x == QMediaPlayer.PlayingState:
            pass
        elif x == QMediaPlayer.PausedState:
            pass

    @pyqtSlot('qint64')
    def playback_progress_change(self, position):
        pass
        # self.ui.progressBar.setValue(int(position / self.player.duration() * 100))

    def read_file(self, filename):
        # reset the cursors to default values
        self.wave = wave.open(filename, 'rb')
        self.channels = self.wave.getnchannels()
        self.rate = self.wave.getframerate()
        nframes = self.seconds_to_prefetch * self.wave.getframerate()

        self.playing = False
        self.data = self.wave.readframes(nframes)
        self.total_frames_read = len(self.data) // 2
        if self.total_frames_read / self.wave.getframerate(
        ) < self.minimum_file_file_length:
            self.ui.statusbar.showMessage("The file is too short.")
            return

        self.player.setMedia(QMediaContent(QUrl.fromLocalFile(filename)))
        self.plot(self.data)
        nframes = min(self.wave.getnframes(), nframes)
        self.data_shift = 0
        self.play_limit = (0, (nframes * 1000) / self.wave.getframerate())
        self.crop_count = 0
        self.current_open_file_name = filename[:-4]

        self.toggle_pause()
        if not self.canvas_scroll_connection:
            self.canvas_click_connection = self.canvas.mpl_connect(
                "button_press_event", self.canvas_click_listener)
            self.canvas_scroll_connection = self.canvas.mpl_connect(
                "scroll_event", self.canvas_scroll_listener)
        self.crop_line_1_pos = None
        self.crop_line_2_pos = None
        self.state = Window.State.Initial
        self.crop_line_2.set_data([], [])
        self.crop_line_2.set_color("red")
        self.crop_line_1.set_data([], [])
        self.crop_line_1.set_color("red")

    def zoom_to_crop(self):
        ##TODO : make crop from line1 and line2 position
        self.set_view_range(self.crop_line_1_pos - 1000,
                            self.crop_line_2_pos + 1000)
        # cropped_data = self.data[self.crop_line_1_pos:self.crop_line_2_pos]
        self.play_limit_bak = self.play_limit
        self.play_limit = ((self.crop_line_1_pos * 1000) /
                           self.wave.getframerate(),
                           (1000 * self.crop_line_2_pos) /
                           self.wave.getframerate())
        self.seek_frame(self.crop_line_1_pos)
        self.canvas.draw()

    def update_cropped_plot(self):
        # frames remain in the total sound clip
        remaining_frames = (self.wave.getnframes()) - int(self.crop_line_2_pos)
        # time remain for compleliton of sound clip
        remaining_ms = (remaining_frames * 1000) / self.wave.getframerate()

        if remaining_ms < 3000:
            return self.crop_completed(remaining_ms)

        # the no of frames that have been loaded into memory
        frames_in_memory = int(self.total_frames_read - self.crop_line_2_pos)
        data_pos = int(self.crop_line_2_pos * 2 - self.data_shift)
        self.data_shift = self.crop_line_2_pos * 2
        # all the data from sound have been read into the memory
        if frames_in_memory == remaining_frames:
            self.data = self.data[data_pos:len(self.data)]

        else:
            # the no of maximum frames that will be showed in preview
            total_frames_required = self.seconds_to_prefetch * self.wave.getframerate(
            )
            # the no of frames that needs to be read from disk
            frames_to_read = total_frames_required - frames_in_memory
            # the file may not have that many frames, so it's the minimun of frames to read and frames in disk remain
            #  to read
            frames_that_will_be_read = min(
                self.wave.getnframes() - self.total_frames_read,
                frames_to_read)

            self.total_frames_read += frames_that_will_be_read
            self.data = self.data[data_pos:len(
                self.data)] + self.wave.readframes(frames_that_will_be_read)

        self.plot(self.data, self.crop_line_2_pos)
        # frames_remain_to_read = self.wave.getnframes() - self.total_frames_read
        self.state = Window.State.Initial
        self.play_limit = ((self.crop_line_2_pos * 1000) /
                           self.wave.getframerate(),
                           (self.total_frames_read * 1000) /
                           self.wave.getframerate())
        self.view_limit_range = (self.crop_line_2_pos, self.total_frames_read)
        self.seek_frame(self.crop_line_2_pos)
        self.crop_line_1.set_data([], [])
        self.crop_line_2.set_data([], [])
        self.crop_line_1.set_color("red")
        self.crop_line_2.set_color("red")
        self.crop_line_1_pos = None
        self.crop_line_2_pos = None

    @pyqtSlot(QModelIndex)
    def file_selected(self, index):
        filename = self.file_system_model.filePath(index)
        if filename.endswith('.txt'):
            self.text_file = QFile(filename)
            if not self.text_file.open(QFile.ReadOnly | QFile.Text):
                QMessageBox.warning(self, "Application", "Cannot read file",
                                    self.text_file.errorString())
                return
            in_stream = QTextStream(self.text_file)
            # self.ui.text_edit.setPlainText(in_stream.readAll())

            # font: QFont = self.ui.text_browser.font()
            # font.setPixelSize(40)
            # self.ui.text_browser.setFont(font)
            data = in_stream.readAll()
            self.ui.text_browser.setPlainText(data)
            self.ui.text_edit.setPlainText(data)
        else:
            try:
                self.read_file(filename)
            except:
                traceback.print_exc(2)
                self.ui.statusbar.showMessage("Reading the file failed", 300)

    @pyqtSlot()
    def save_crop_to_file(self):
        self.crop_count += 1
        wave_file = wave.open(
            self.current_open_file_name + "_crop_" + str(self.crop_count) +
            '.wav', 'wb')
        wave_file.setnchannels(self.crop_nchannel)
        wave_file.setsampwidth(self.crop_stampwidth)
        wave_file.setframerate(self.crop_framerate)
        wave_file.writeframes(self.crop_to_save)
        wave_file.close()

    def crop_completed(self, remaining_ms):
        self.state = Window.State.Initial
        self.crop_line_1.set_data([], [])
        self.crop_line_2.set_data([], [])
        self.crop_line_1.set_color("red")
        self.crop_line_2.set_color("red")
        self.crop_line_1_pos = None
        self.crop_line_2_pos = None
        self.audio_plot.set_data([], [])
        self.ui.statusbar.showMessage("Cropping this file has been completed")
        self.canvas.mpl_disconnect(self.canvas_click_connection)
        self.canvas.mpl_disconnect(self.canvas_scroll_connection)
        self.canvas_scroll_connection = None
        self.canvas_click_connection = None
        self.canvas.draw()
        self.player.stop()
        self.play_timer.stop()
        self.wave.close()
        self.ui.statusbar.showMessage(
            "Only %f seconds left thus this file is considered completed" %
            (remaining_ms / 1000))
Ejemplo n.º 19
0
class PlotCanvas:
    """
    Class handling the plotting area in the application.
    """
    def __init__(self, container):
        """
        The constructor configures the Matplotlib figure that
        will contain all plots, creates the base axes and connects
        events to the plotting area.

        :param container: The parent container in which to draw plots.
        :rtype: PlotCanvas
        """
        # Options
        self.x_margin = 15  # pixels
        self.y_margin = 25  # Pixels

        # Parent container
        self.container = container

        # Plots go onto a single matplotlib.figure
        self.figure = Figure(dpi=50)  # TODO: dpi needed?
        self.figure.patch.set_visible(False)

        # These axes show the ticks and grid. No plotting done here.
        # New axes must have a label, otherwise mpl returns an existing one.
        self.axes = self.figure.add_axes([0.05, 0.05, 0.9, 0.9],
                                         label="base",
                                         alpha=0.0)
        self.axes.set_aspect(1)
        self.axes.grid(True)

        # The canvas is the top level container (Gtk.DrawingArea)
        self.canvas = FigureCanvas(self.figure)
        self.canvas.set_hexpand(1)
        self.canvas.set_vexpand(1)
        self.canvas.set_can_focus(True)  # For key press

        # Attach to parent
        self.container.attach(self.canvas, 0, 0, 600,
                              400)  # TODO: Height and width are num. columns??

        # Events
        self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
        self.canvas.connect('configure-event', self.auto_adjust_axes)
        self.canvas.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK)
        self.canvas.connect("scroll-event", self.on_scroll)
        self.canvas.mpl_connect('key_press_event', self.on_key_down)
        self.canvas.mpl_connect('key_release_event', self.on_key_up)

        self.mouse = [0, 0]
        self.key = None

    def on_key_down(self, event):
        """

        :param event:
        :return:
        """
        self.key = event.key

    def on_key_up(self, event):
        """

        :param event:
        :return:
        """
        self.key = None

    def mpl_connect(self, event_name, callback):
        """
        Attach an event handler to the canvas through the Matplotlib interface.

        :param event_name: Name of the event
        :type event_name: str
        :param callback: Function to call
        :type callback: func
        :return: Connection id
        :rtype: int
        """
        return self.canvas.mpl_connect(event_name, callback)

    def mpl_disconnect(self, cid):
        """
        Disconnect callback with the give id.
        :param cid: Callback id.
        :return: None
        """
        self.canvas.mpl_disconnect(cid)

    def connect(self, event_name, callback):
        """
        Attach an event handler to the canvas through the native GTK interface.

        :param event_name: Name of the event
        :type event_name: str
        :param callback: Function to call
        :type callback: function
        :return: Nothing
        """
        self.canvas.connect(event_name, callback)

    def clear(self):
        """
        Clears axes and figure.

        :return: None
        """

        # Clear
        self.axes.cla()
        try:
            self.figure.clf()
        except KeyError:
            FlatCAMApp.App.log.warning("KeyError in MPL figure.clf()")

        # Re-build
        self.figure.add_axes(self.axes)
        self.axes.set_aspect(1)
        self.axes.grid(True)

        # Re-draw
        self.canvas.queue_draw()

    def adjust_axes(self, xmin, ymin, xmax, ymax):
        """
        Adjusts all axes while maintaining the use of the whole canvas
        and an aspect ratio to 1:1 between x and y axes. The parameters are an original
        request that will be modified to fit these restrictions.

        :param xmin: Requested minimum value for the X axis.
        :type xmin: float
        :param ymin: Requested minimum value for the Y axis.
        :type ymin: float
        :param xmax: Requested maximum value for the X axis.
        :type xmax: float
        :param ymax: Requested maximum value for the Y axis.
        :type ymax: float
        :return: None
        """

        FlatCAMApp.App.log.debug("PC.adjust_axes()")

        width = xmax - xmin
        height = ymax - ymin
        try:
            r = width / height
        except ZeroDivisionError:
            FlatCAMApp.App.log.error("Height is %f" % height)
            return
        canvas_w, canvas_h = self.canvas.get_width_height()
        canvas_r = float(canvas_w) / canvas_h
        x_ratio = float(self.x_margin) / canvas_w
        y_ratio = float(self.y_margin) / canvas_h

        if r > canvas_r:
            ycenter = (ymin + ymax) / 2.0
            newheight = height * r / canvas_r
            ymin = ycenter - newheight / 2.0
            ymax = ycenter + newheight / 2.0
        else:
            xcenter = (xmax + xmin) / 2.0
            newwidth = width * canvas_r / r
            xmin = xcenter - newwidth / 2.0
            xmax = xcenter + newwidth / 2.0

        # Adjust axes
        for ax in self.figure.get_axes():
            if ax._label != 'base':
                ax.set_frame_on(False)  # No frame
                ax.set_xticks([])  # No tick
                ax.set_yticks([])  # No ticks
                ax.patch.set_visible(False)  # No background
                ax.set_aspect(1)
            ax.set_xlim((xmin, xmax))
            ax.set_ylim((ymin, ymax))
            ax.set_position(
                [x_ratio, y_ratio, 1 - 2 * x_ratio, 1 - 2 * y_ratio])

        # Re-draw
        self.canvas.queue_draw()

    def auto_adjust_axes(self, *args):
        """
        Calls ``adjust_axes()`` using the extents of the base axes.

        :rtype : None
        :return: None
        """

        xmin, xmax = self.axes.get_xlim()
        ymin, ymax = self.axes.get_ylim()
        self.adjust_axes(xmin, ymin, xmax, ymax)

    def zoom(self, factor, center=None):
        """
        Zooms the plot by factor around a given
        center point. Takes care of re-drawing.

        :param factor: Number by which to scale the plot.
        :type factor: float
        :param center: Coordinates [x, y] of the point around which to scale the plot.
        :type center: list
        :return: None
        """

        xmin, xmax = self.axes.get_xlim()
        ymin, ymax = self.axes.get_ylim()
        width = xmax - xmin
        height = ymax - ymin

        if center is None or center == [None, None]:
            center = [(xmin + xmax) / 2.0, (ymin + ymax) / 2.0]

        # For keeping the point at the pointer location
        relx = (xmax - center[0]) / width
        rely = (ymax - center[1]) / height

        new_width = width / factor
        new_height = height / factor

        xmin = center[0] - new_width * (1 - relx)
        xmax = center[0] + new_width * relx
        ymin = center[1] - new_height * (1 - rely)
        ymax = center[1] + new_height * rely

        # Adjust axes
        for ax in self.figure.get_axes():
            ax.set_xlim((xmin, xmax))
            ax.set_ylim((ymin, ymax))

        # Re-draw
        self.canvas.queue_draw()

    def pan(self, x, y):
        xmin, xmax = self.axes.get_xlim()
        ymin, ymax = self.axes.get_ylim()
        width = xmax - xmin
        height = ymax - ymin

        # Adjust axes
        for ax in self.figure.get_axes():
            ax.set_xlim((xmin + x * width, xmax + x * width))
            ax.set_ylim((ymin + y * height, ymax + y * height))

        # Re-draw
        self.canvas.queue_draw()

    def new_axes(self, name):
        """
        Creates and returns an Axes object attached to this object's Figure.

        :param name: Unique label for the axes.
        :return: Axes attached to the figure.
        :rtype: Axes
        """

        return self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label=name)

    def on_scroll(self, canvas, event):
        """
        Scroll event handler.

        :param canvas: The widget generating the event. Ignored.
        :param event: Event object containing the event information.
        :return: None
        """

        # So it can receive key presses
        self.canvas.grab_focus()

        # Event info
        z, direction = event.get_scroll_direction()

        if self.key is None:

            if direction is Gdk.ScrollDirection.UP:
                self.zoom(1.5, self.mouse)
            else:
                self.zoom(1 / 1.5, self.mouse)
            return

        if self.key == 'shift':

            if direction is Gdk.ScrollDirection.UP:
                self.pan(0.3, 0)
            else:
                self.pan(-0.3, 0)
            return

        if self.key == 'ctrl+control':

            if direction is Gdk.ScrollDirection.UP:
                self.pan(0, 0.3)
            else:
                self.pan(0, -0.3)
            return

    def on_mouse_move(self, event):
        """
        Mouse movement event hadler. Stores the coordinates.

        :param event: Contains information about the event.
        :return: None
        """
        self.mouse = [event.xdata, event.ydata]
Ejemplo n.º 20
0
class mpl_widget(QWidget):
    def __init__(self, parent=None, mainWidget=None):
        self._SELECTEDCELLS = list()       # container for instances of selected cells, so we can delete them when we want
        self._SELECTEDCELLS_IJ = list()  # container for coords of selected cells, so we can delete them when we want
        self._SELECTEDCELLLINES = list()   # container for instances of selected cells, so we can delete them when we want
        self._GRIDLINES = None
        QWidget.__init__(self, parent)
        self.mainWidget = mainWidget
        self.create_main_frame()
        self.mpl_menu = mpl_menu(self)
        self.shift_is_held = False
        #self.connect(self.mpl_menu, QtCore.SIGNAL('mySignal'), self.mySlot)
        #print 'my parent is:', parent
        self.clear_selection()
        self.init_tooltips()

    def init_tooltips(self):
        self.canvas.setToolTip('If 2D plot => RMB click toggles menu <br> - RMB click selects cell <br> - selected cells are drawn with black border')
        self.grid_cb.setToolTip('If 2D plot => show computational grid <br> If 1D plot => show normal gridlines')
        self.cbar_button.setToolTip('If 2D plot => controls the color range. <br> Note: <br> - pressing UP and DOWN arrows cycles through colormaps <br> - dragging colorbar with RMB scales the color-range <br> - dragging colorbar with LMB shifts the color-range')
        self.mpl_toolbar.setToolTip('Shows coordinates (i,j, lat,lon) and data-value(z) under the cursor. <br> if you see <i>>>></i> coordinates are not visible. Enlarge the window')

    def create_main_frame(self):

        self.fig = Figure(dpi=100)
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self)
        self.canvas.setFocusPolicy( Qt.ClickFocus )
        self.canvas.setFocus()

        self.mpl_toolbar = myNavigationToolbar(self.canvas, self)
        self.canvas.mpl_connect('button_press_event', self.on_click)
        self.canvas.mpl_connect('key_press_event', self.on_key_press)
        self.canvas.mpl_connect('key_release_event', self.on_key_release)
        #self.canvas.mpl_connect('button_press_event', self.disable_clicks)


        self.cbar_button = QPushButton("Color Range")
        self.cbar_button.setFocusPolicy( Qt.NoFocus )
        self.grid_cb = QCheckBox("Show Grid")
        self.grid_cb.setFocusPolicy( Qt.NoFocus )
        self.grid_cb.stateChanged.connect(self.showGrid)

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()

        vbox.addWidget(self.canvas)  # the matplotlib canvas
        hbox.addWidget(self.mpl_toolbar)
        hbox.addWidget(self.cbar_button)
        hbox.addWidget(self.grid_cb)
        vbox.addLayout(hbox)
        self.setLayout(vbox)



    def on_click(self, event):
        if event.inaxes != self.get_axes()[0]: return
        #if self.get_axes()[0].format_coord(event.x, event.y) == 'outside data area': return
        if self.allow_menu():
            self.allow_popup_menu = True
            if self.shift_is_held:
                self.allow_popup_menu = False

            point = [int(event.xdata + .5), int(event.ydata + .5)]
            #print '>>>', point, '\t currently {0} selected'.format(len(self._SELECTEDCELLS))
            if event.button == 3 :  #if RMB is clicked

                # working with dialog for transect!
                if self.mainWidget.transect_dlg:
                    if self.mainWidget.transect_dlg.toogle_show_after_selected_cell:
                        realx, realy = self.get_real_xy(event.xdata, event.ydata, self.mainWidget.detect_variable_dimensions())
                        realpoint = [realy, realx]
                        #print 'real xy:', realpoint
                        if self.mainWidget.transect_dlg.toogle_show_after_selected_cell == 1:  # select point 1
                            self.allow_popup_menu = False
                            self.mainWidget.transect_dlg.data.set_p1(realpoint)
                        elif self.mainWidget.transect_dlg.toogle_show_after_selected_cell == 2:  # select point 2
                            self.allow_popup_menu = False
                            self.mainWidget.transect_dlg.data.set_p2(realpoint)

                        self.mainWidget.transect_dlg.update()
                        self.mainWidget.transect_dlg.show()
                
                # working with dialog for flux!
                if self.mainWidget.flux_dlg:
                    if self.mainWidget.flux_dlg.toogle_show_after_selected_cell:
                        if self.mainWidget.flux_dlg.toogle_show_after_selected_cell == 1:  # select point 1
                            self.allow_popup_menu = False
                            self.mainWidget.flux_dlg.data.set_p1(point)
                        elif self.mainWidget.flux_dlg.toogle_show_after_selected_cell == 2:  # select point 2
                            self.allow_popup_menu = False
                            self.mainWidget.flux_dlg.data.set_p2(point)

                        self.mainWidget.flux_dlg.update()
                        self.mainWidget.flux_dlg.show()



                if len(self._SELECTEDCELLS) == 0:  # if no cell is selected
                    self.add_selected_cell(point)


                else:  # if some cells are already selected
                    if self.mpl_menu.allow_rmb_select_cells() or self.shift_is_held:
                        # check if this point is already selected:
                        already_selected = False
                        for p in self._SELECTEDCELLS_IJ:
                            if (point[0] == p[0]) and (point[1] == p[1]):
                                already_selected = True
                                print 'cell already selected... is not added'

                        if not already_selected:
                            self.add_selected_cell(point)
                    else:
                        pass
                        #self.clear_selection()
                        #self.add_selected_cell(point)

    def cells_selected(self):
        if self._SELECTEDCELLS: return len(self._SELECTEDCELLS)
        else: return False

    def add_selected_cell(self, point):
        ''' point = [i, j]'''
        print 'selected cell:', point[0], point[1]
        c = self.draw_picked_cell(point)
        self._SELECTEDCELLS.append(c)
        self._SELECTEDCELLS_IJ.append(point)




    def get_selected_cells_ij(self):
        return self._SELECTEDCELLS_IJ

    def clear_selection(self):
        '''
        delete all graphical objects of selected cells
        redraw canvas
        '''
        print 'clearing stuff'
        if len(self._SELECTEDCELLLINES) > 0:
            for line in self._SELECTEDCELLLINES:
                l = line.pop(0)
                l.remove()
                del l
            del self._SELECTEDCELLLINES[:]
        #print 'cells ---- before:', len(self._SELECTEDCELLS)
        if len(self._SELECTEDCELLS) > 0:
            for cell in self._SELECTEDCELLS:
                for line in cell:
                    l = line.pop(0)
                    l.remove()
                    del l
            del self._SELECTEDCELLS[:]
        #print 'cells ---- left:', len(self._SELECTEDCELLS)


        #print 'cells-coords ----'
        #print len(self._SELECTEDCELLS_IJ)
        if self._SELECTEDCELLS_IJ:
            for cellcoords in self._SELECTEDCELLS_IJ:
                #cc = cellcoords.pop(0)
                #cellcoords.remove()
                del cellcoords
            del self._SELECTEDCELLS_IJ[:]
        #print 'cells ---- left,', len(self._SELECTEDCELLS_IJ)



        if len(self._SELECTEDCELLS) != 0:
            raise ValueError('List "self._SELECTEDCELLS" was not flushed')

        if len(self._SELECTEDCELLLINES) != 0:
            raise ValueError('List "self._SELECTEDCELLLINES" was not flushed')

        if len(self._SELECTEDCELLS_IJ) != 0:
            raise ValueError('List "self._SELECTEDCELLLINES" was not flushed')
        # update plot
        self.canvas.draw()
        #print 'finishing clear: cells left', len(self._SELECTEDCELLS)


    def showGrid(self, state):
        if self.fig.axes:
            current_plot = self.mainWidget.get_plotType()
            current_plot2D = self.mainWidget.get_plotType_for_timeseries()
            if state == Qt.Checked:
                if current_plot == '1D' or (current_plot2D =="2DZT"):
                    self.fig.axes[0].grid(True)
                elif current_plot == '2D' and (not current_plot2D =="2DZT"):
                    self.draw_pixel_grid(True)
            else:
                if current_plot == '1D' or (current_plot2D =="2DZT"):
                    self.fig.axes[0].grid(False)
                elif current_plot == '2D' and (not current_plot2D =="2DZT"):
                    self.draw_pixel_grid(False)
            self.canvas.draw()

    def draw_picked_cell(self, point):
        x = point[0]
        y = point[1]
        '''
        approach drawing a patch... not working
        cell_bnd = patches.Rectangle((x-.5, y-.5), 1, 1, fill=False, edgecolor="black", hatch=None, linewidth=1.)
        cell_instance = self.fig.axes[0].add_patch(cell_bnd)
        '''

        b_line = [(x-.5, x+.5), (y-.5, y-.5)]
        r_line = [(x+.5, x+.5), (y-.5, y+.5)]
        t_line = [(x-.5, x+.5), (y+.5, y+.5)]
        l_line = [(x-.5, x-.5), (y-.5, y+.5)]
        cell = [b_line, r_line, t_line, l_line]
        for i, l in enumerate(cell):
            ll = self.fig.axes[0].plot(l[0], l[1], 'k-', lw=.8)
            cell[i] = ll  # overwriting current Line2D object with object binded to an axes
        #self._SELECTEDCELLS.append(cell)  # collecting reference to this cell to be able to delete it
        #self._SELECTEDCELLS_IJ.append(point)  # collecting reference to this cell to be able to delete it

        self.canvas.draw()
        return cell


    def draw_line(self, point1, point2):
        line = [(point1[0], point2[0]), (point1[1], point2[1])]
        l = self.fig.axes[0].plot(line[0], line[1], 'k-', lw=2)
        return l

    def draw_pixel_grid(self, enable=True):
        if enable:
            dx = 1
            dy = 1
            x0 = -.5
            y0 = -.5

            if self.mainWidget.get_plotType_for_timeseries() == '2DXY':
                nx = self.mainWidget.get_nX()
                ny = self.mainWidget.get_nY()
            elif self.mainWidget.get_plotType_for_timeseries() == '2DZY':
                nx = self.mainWidget.get_nY()
                ny = self.mainWidget.get_nZ()
            elif self.mainWidget.get_plotType_for_timeseries() == '2DZX':
                nx = self.mainWidget.get_nX()
                ny = self.mainWidget.get_nZ()

            self._GRIDLINES = list()
            for n_hline in np.arange(ny+1):
                hline = [(x0, x0+nx), (y0+n_hline, y0+n_hline)]
                l = self.fig.axes[0].plot(hline[0], hline[1], 'k-', lw=.2)
                self._GRIDLINES.append(l)  # collecting reference to this line to be able to delete it

            for n_vline in np.arange(nx+1):
                vline = [(x0+n_vline, x0+n_vline), (y0, y0+ny)]
                l = self.fig.axes[0].plot(vline[0], vline[1], 'k-', lw=.2)
                self._GRIDLINES.append(l)  # collecting reference to this line to be able to delete it


        if not enable:
            #print 'deleting lines...'
            if self._GRIDLINES:  # if lines were created
                #print 'lines are here...'
                for line in self._GRIDLINES:
                    #print line
                    l = line.pop(0)
                    l.remove()
                    del l
            self.fig.canvas.draw()

    def on_key_press(self, event):
        #print 'key pressed:', event.key
        if event.key == 'shift':
            self.shift_is_held = True


    

    def on_key_release(self, event):
        #print 'key released:', event.key
        if event.key == 'shift':
            self.shift_is_held = False

        elif event.key == 'escape':
            self.clear_selection()


    def change_coordinate_formatter(self, ax, data2d,  bruteforce_flag=None, bruteforce_dims=None):
        ''' see http://stackoverflow.com/questions/14754931/matplotlib-values-under-cursor
        '''
        numrows, numcols = data2d.shape
        bruteforce_mode_on = False
        bruteforce_mode_on = (bruteforce_flag == '2DXY' and bruteforce_dims[-1] and bruteforce_dims[-2])


        def format_coord(x, y):
            col = int(x+0.5)
            row = int(y+0.5)

            if not bruteforce_mode_on:
                if col >= 0 and col < numcols and row >= 0 and row < numrows:
                    #z = data2d[row, col]
                    # sice we have artificially reversed y-coordinate axes, now reverse data!
                    # numrows-1, because if nrows=10 => row=[0:9]
                    z = data2d[numrows-1-row, col]
                    #return 'x=%1.1f y=%1.1f z=%1.6f' % (x, y, z)
                    return 'i=%d j=%d z=%.6f' % (col, row, z)
                else:
                    #return 'x=%1.4f, y=%1.4f' % (x, y)
                    return 'outside data area'

            elif bruteforce_flag == '2DXY' and bruteforce_dims[-1] and bruteforce_dims[-2]:
                '''
                our extend in X=[-0.5:numcols-0.5], Y=[-0.5:numrows-0.5], because col,row is cell center!
                '''
                if col >= 0 and col < numcols and row >= 0 and row < numrows:
                    #z = data2d[row, col]
                    # sice we have artificially reversed y-coordinate axes, now reverse data!
                    # numrows-1, because if nrows=10 => row=[0:9]
                    z = data2d[numrows-1-row, col]
                    real_x, real_y = self.get_real_xy(x, y, bruteforce_dims)

                    #return 'x=%1.1f y=%1.1f z=%1.6f' % (x, y, z)
                    #return 'i=%d j=%d z=%.3f x=%.4f y=%.4f' % (col, row, z, real_x, real_y)
                    return 'i=%d j=%d z=%.3f, %s=%.2f %s=%.2f' % (
                        col, row, z, bruteforce_dims[-1], real_x, bruteforce_dims[-2], real_y)
                else:
                    #return 'x=%1.4f, y=%1.4f' % (x, y)
                    return 'outside data area'
            else:
                raise ValueError('bruteforce_flag can be $None$ or $"2DXY"$. Passed %s' % bruteforce_flag)
        ax.format_coord = format_coord


    def allow_menu(self):
        allow = False
        #print "self.mainWidget.get_plotType():", self.mainWidget.get_plotType()
        #print "self.mainWidget.get_plotType_for_timeseries():", self.mainWidget.get_plotType_for_timeseries()
        if self.mainWidget.get_plotType() == "2D" and not self.mainWidget.get_plotType_for_timeseries() == "2DZT":
            allow = True
        return allow
    

    def get_real_xy(self, i, j, dimension_list):
        '''
        functions returns values of x,y based on passed indexes i, j

        '''
        if any(dimension_list[-2:-1]) is None:
            print 'Dimensions X,Y of current variable are not specified (variables that have same name as dimensions not found)'
            raise ValueError('Dimensions X,Y of current variable are not specified (variables that have same name as dimensions not found)')
        nc = self.mainWidget.get_selected_ncfile_instance()
        try:
            x_var = nc.variables[dimension_list[-1]]
            y_var = nc.variables[dimension_list[-2]]
            
        except:
            
            print ('Failed to load variables: {0}, {1}'.format(dimension_list[-1], dimension_list[-2]))
            raise ValueError('Failed to load variables: {0}, {1}'.format(dimension_list[-1], dimension_list[-2]))


        x_ratio = (x_var[-1]-x_var[0])/(len(x_var)-1)
        y_ratio = (y_var[-1]-y_var[0])/(len(y_var)-1)

        #x[i] = x_var[0]+x_ratio*i
        #y[j] = y_var[0]+y_ratio*j
        x = x_var[0] + x_ratio*i
        y = y_var[0] + y_ratio*j
        nc.close()
        return (x, y)

    def get_axes(self):
        return self.fig.get_axes()
    

    def fast_redraw(self, artist):
        background = [self.canvas.copy_from_bbox(self.get_axes()[0].bbox)]
        self.get_axes()[0].draw_artist(artist)
        self.canvas.restore_region(background)
        self.canvas.blit(self.get_axes()[0].bbox)
        self.canvas.update()
        self.canvas.flush_events()
Ejemplo n.º 21
0
class MainWin(QtWidgets.QMainWindow):
    here = os.path.abspath(os.path.join(os.path.dirname(__file__)))  # to be overloaded
    media = os.path.join(here, "media")
    font_size = 6
    rc_context = {
        'font.family': 'sans-serif',
        'font.sans-serif': ['Tahoma', 'Bitstream Vera Sans', 'Lucida Grande', 'Verdana'],
        'font.size': font_size,
        'figure.titlesize': font_size + 1,
        'axes.labelsize': font_size,
        'legend.fontsize': font_size,
        'xtick.labelsize': font_size - 1,
        'ytick.labelsize': font_size - 1,
        'axes.linewidth': 0.5,
        'axes.xmargin': 0.01,
        'axes.ymargin': 0.01,
        'lines.linewidth': 1.0,
        'grid.alpha': 0.2,
    }

    def __init__(self, main_win=None):
        super().__init__(main_win)

        self.main_win = main_win

        # set the application name and the version
        self.name = rori_name
        self.version = rori_version
        if self.main_win is None:
            self.setWindowTitle('%s v.%s' % (self.name, self.version))
        else:
            self.setWindowTitle('%s' % (self.name,))
        self.setMinimumSize(200, 200)
        self.resize(600, 900)

        # only called when stand-alone (without Sound Speed Manager)
        # noinspection PyArgumentList
        _app = QtCore.QCoreApplication.instance()
        if _app.applicationName() == 'python':
            _app.setApplicationName('%s v.%s' % (self.name, self.version))
            _app.setOrganizationName("HydrOffice")
            _app.setOrganizationDomain("hydroffice.org")
            logger.debug("set application name: %s" % _app.applicationName())

            # set icons
            icon_info = QtCore.QFileInfo(os.path.join(self.media, 'rori.png'))
            self.setWindowIcon(QtGui.QIcon(icon_info.absoluteFilePath()))
            if (sys.platform == 'win32') or (os.name == "nt"):  # is_windows()

                try:
                    # This is needed to display the app icon on the taskbar on Windows 7
                    import ctypes
                    app_id = '%s v.%s' % (self.name, self.version)
                    ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(app_id)
                except AttributeError as e:
                    logger.debug("Unable to change app icon: %s" % e)

        # set palette
        style_info = QtCore.QFileInfo(os.path.join(self.here, 'styles', 'main.stylesheet'))
        style_content = open(style_info.filePath()).read()
        self.setStyleSheet(style_content)

        # mpl figure settings
        self.is_drawn = False
        self.f_dpi = 120  # dots-per-inch
        self.f_sz = (3.0, 6.0)  # inches
        self.tvu_plot = None
        self.thu_plot = None

        self.iho_color = '#E69F24'
        self.noaa_color = '#1C75C3'

        # outline ui
        self.top_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.top_widget)
        self.vbox = QtWidgets.QVBoxLayout()
        self.vbox.setContentsMargins(4, 4, 4, 4)
        self.top_widget.setLayout(self.vbox)

        # ### Settings ###

        self.settings = QtWidgets.QGroupBox("Settings")
        self.vbox.addWidget(self.settings)
        settings_vbox = QtWidgets.QVBoxLayout()
        self.settings.setLayout(settings_vbox)

        label_hbox = QtWidgets.QHBoxLayout()
        settings_vbox.addLayout(label_hbox)
        # stretch
        label_hbox.addStretch()
        # hssd
        text_1ab = QtWidgets.QLabel("")
        text_1ab.setAlignment(QtCore.Qt.AlignCenter)
        text_1ab.setFixedWidth(100)
        label_hbox.addWidget(text_1ab)
        # spacing
        label_hbox.addSpacing(10)
        # specs
        text_1ab = QtWidgets.QLabel("Great Lakes")
        text_1ab.setAlignment(QtCore.Qt.AlignCenter)
        text_1ab.setFixedWidth(100)
        label_hbox.addWidget(text_1ab)
        # spacing
        label_hbox.addSpacing(10)
        # specs
        text_1ab = QtWidgets.QLabel("")
        text_1ab.setAlignment(QtCore.Qt.AlignCenter)
        text_1ab.setFixedWidth(100)
        label_hbox.addWidget(text_1ab)
        # stretch
        label_hbox.addStretch()

        toggle_hbox = QtWidgets.QHBoxLayout()
        settings_vbox.addLayout(toggle_hbox)
        # stretch
        toggle_hbox.addStretch()
        # HSSD
        self.toggle_hssd = QtWidgets.QDial()
        self.toggle_hssd.setNotchesVisible(True)
        self.toggle_hssd.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.toggle_hssd.setRange(0, 1)
        self.toggle_hssd.setValue(0)
        self.toggle_hssd.setFixedSize(QtCore.QSize(50, 50))
        self.toggle_hssd.setInvertedAppearance(False)
        # noinspection PyUnresolvedReferences
        self.toggle_hssd.valueChanged.connect(self.on_settings_changed)
        toggle_hbox.addWidget(self.toggle_hssd)
        # spacing
        toggle_hbox.addSpacing(105)
        # area
        self.toggle_area = QtWidgets.QDial()
        self.toggle_area.setNotchesVisible(True)
        self.toggle_area.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.toggle_area.setRange(0, 2)
        self.toggle_area.setValue(0)
        self.toggle_area.setFixedSize(QtCore.QSize(50, 50))
        self.toggle_area.setInvertedAppearance(False)
        # noinspection PyUnresolvedReferences
        self.toggle_area.valueChanged.connect(self.on_settings_changed)
        toggle_hbox.addWidget(self.toggle_area)
        # spacing0
        toggle_hbox.addSpacing(105)
        # specs
        self.toggle_z = QtWidgets.QDial()
        self.toggle_z.setNotchesVisible(True)
        self.toggle_z.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.toggle_z.setRange(0, 1)
        self.toggle_z.setValue(0)
        self.toggle_z.setFixedSize(QtCore.QSize(50, 50))
        self.toggle_z.setInvertedAppearance(False)
        # noinspection PyUnresolvedReferences
        self.toggle_z.valueChanged.connect(self.on_settings_changed)
        toggle_hbox.addWidget(self.toggle_z)
        # stretch
        toggle_hbox.addStretch()

        label2_hbox = QtWidgets.QHBoxLayout()
        settings_vbox.addLayout(label2_hbox)
        # stretch
        label2_hbox.addStretch()
        # specs
        text_special = QtWidgets.QLabel("HSSD 2018 ")
        text_special.setAlignment(QtCore.Qt.AlignRight)
        text_special.setFixedWidth(70)
        label2_hbox.addWidget(text_special)
        text_2 = QtWidgets.QLabel(" HSSD 2019+")
        text_2.setAlignment(QtCore.Qt.AlignLeft)
        text_2.setFixedWidth(70)
        label2_hbox.addWidget(text_2)
        # stretch
        label2_hbox.addSpacing(10)
        # area
        text_special = QtWidgets.QLabel("Pacific Coast ")
        text_special.setAlignment(QtCore.Qt.AlignRight)
        text_special.setFixedWidth(70)
        label2_hbox.addWidget(text_special)
        text_2 = QtWidgets.QLabel(" Atlantic Coast")
        text_2.setAlignment(QtCore.Qt.AlignLeft)
        text_2.setFixedWidth(70)
        label2_hbox.addWidget(text_2)
        # stretch
        label2_hbox.addSpacing(10)
        # specs
        text_special = QtWidgets.QLabel("Depth ")
        text_special.setAlignment(QtCore.Qt.AlignRight)
        text_special.setFixedWidth(70)
        label2_hbox.addWidget(text_special)
        text_2 = QtWidgets.QLabel(" Elevation")
        text_2.setAlignment(QtCore.Qt.AlignLeft)
        text_2.setFixedWidth(70)
        label2_hbox.addWidget(text_2)
        # stretch
        label2_hbox.addStretch()

        settings_vbox.addSpacing(8)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch()
        watlev_text = QtWidgets.QLabel("Always Dry: ")
        watlev_text.setFixedWidth(100)
        hbox.addWidget(watlev_text)
        self.always_dry_min_value = QtWidgets.QLineEdit()
        self.always_dry_min_value.setFixedWidth(120)
        self.always_dry_min_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.always_dry_min_value.setDisabled(True)
        self.always_dry_min_value.setText("")
        hbox.addWidget(self.always_dry_min_value)
        self.always_dry_max_value = QtWidgets.QLineEdit()
        self.always_dry_max_value.setFixedWidth(120)
        self.always_dry_max_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.always_dry_max_value.setDisabled(True)
        self.always_dry_max_value.setText("")
        hbox.addWidget(self.always_dry_max_value)
        hbox.addStretch()
        settings_vbox.addLayout(hbox)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch()
        watlev_text = QtWidgets.QLabel("Covers & Uncovers: ")
        watlev_text.setFixedWidth(100)
        hbox.addWidget(watlev_text)
        self.covers_and_uncovers_min_value = QtWidgets.QLineEdit()
        self.covers_and_uncovers_min_value.setFixedWidth(120)
        self.covers_and_uncovers_min_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.covers_and_uncovers_min_value.setDisabled(True)
        self.covers_and_uncovers_min_value.setText("")
        hbox.addWidget(self.covers_and_uncovers_min_value)
        self.covers_and_uncovers_max_value = QtWidgets.QLineEdit()
        self.covers_and_uncovers_max_value.setFixedWidth(120)
        self.covers_and_uncovers_max_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.covers_and_uncovers_max_value.setDisabled(True)
        self.covers_and_uncovers_max_value.setText("")
        hbox.addWidget(self.covers_and_uncovers_max_value)
        hbox.addStretch()
        settings_vbox.addLayout(hbox)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch()
        watlev_text = QtWidgets.QLabel("Awash: ")
        watlev_text.setFixedWidth(100)
        hbox.addWidget(watlev_text)
        self.awash_min_value = QtWidgets.QLineEdit()
        self.awash_min_value.setFixedWidth(120)
        self.awash_min_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.awash_min_value.setDisabled(True)
        self.awash_min_value.setText("")
        hbox.addWidget(self.awash_min_value)
        self.awash_max_value = QtWidgets.QLineEdit()
        self.awash_max_value.setFixedWidth(120)
        self.awash_max_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.awash_max_value.setDisabled(True)
        self.awash_max_value.setText("")
        hbox.addWidget(self.awash_max_value)
        hbox.addStretch()
        settings_vbox.addLayout(hbox)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch()
        watlev_text = QtWidgets.QLabel("Always underwater: ")
        watlev_text.setFixedWidth(100)
        hbox.addWidget(watlev_text)
        self.always_underwater_min_value = QtWidgets.QLineEdit()
        self.always_underwater_min_value.setFixedWidth(120)
        self.always_underwater_min_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.always_underwater_min_value.setDisabled(True)
        self.always_underwater_min_value.setText("")
        hbox.addWidget(self.always_underwater_min_value)
        self.always_underwater_max_value = QtWidgets.QLineEdit()
        self.always_underwater_max_value.setFixedWidth(120)
        self.always_underwater_max_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.always_underwater_max_value.setDisabled(True)
        self.always_underwater_max_value.setText("")
        hbox.addWidget(self.always_underwater_max_value)
        hbox.addStretch()
        settings_vbox.addLayout(hbox)

        # ### Inputs ###

        self.inputs = QtWidgets.QGroupBox("Input")
        self.vbox.addWidget(self.inputs)
        inputs_vbox = QtWidgets.QVBoxLayout()
        self.inputs.setLayout(inputs_vbox)

        # MHW
        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch()
        self.mhw_text = QtWidgets.QLabel("MHW [m]: ")
        hbox.addWidget(self.mhw_text)
        self.mhw_value = QtWidgets.QLineEdit()
        self.mhw_value.setFixedWidth(60)
        self.mhw_value.setValidator(QtGui.QDoubleValidator(-9999, 9999, 5, self.mhw_value))
        self.mhw_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.mhw_value.setText("5.0")
        hbox.addWidget(self.mhw_value)
        depth_text = QtWidgets.QLabel("Depth [m]: ")
        hbox.addWidget(depth_text)
        self.depth_value = QtWidgets.QLineEdit()
        self.depth_value.setFixedWidth(60)
        self.depth_value.setValidator(QtGui.QDoubleValidator(-9999, 9999, 5, self.depth_value))
        self.depth_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.depth_value.setText("1.0")
        hbox.addWidget(self.depth_value)
        hbox.addSpacing(16)
        self.calculate = QtWidgets.QPushButton("Run")
        self.calculate.setFixedHeight(28)
        self.calculate.setFixedWidth(42)
        # noinspection PyUnresolvedReferences
        self.calculate.clicked.connect(self.on_calculate)
        hbox.addWidget(self.calculate)
        button = QtWidgets.QPushButton()
        hbox.addWidget(button)
        button.setFixedHeight(GuiSettings.single_line_height())
        button.setFixedWidth(GuiSettings.single_line_height())
        icon_info = QtCore.QFileInfo(os.path.join(self.media, 'small_info.png'))
        button.setIcon(QtGui.QIcon(icon_info.absoluteFilePath()))
        button.setToolTip('Open the manual page')
        button.setStyleSheet(GuiSettings.stylesheet_info_button())
        # noinspection PyUnresolvedReferences
        button.clicked.connect(self.click_open_manual)
        hbox.addStretch()
        inputs_vbox.addLayout(hbox)

        # ### Outputs ###

        self.outputs = QtWidgets.QGroupBox("Outputs")
        self.vbox.addWidget(self.outputs)
        outputs_vbox = QtWidgets.QVBoxLayout()
        self.outputs.setLayout(outputs_vbox)

        outputs_hbox = QtWidgets.QHBoxLayout()
        outputs_vbox.addLayout(outputs_hbox)

        outputs_hbox.addStretch()
        # pic
        self.out_pic_label = QtWidgets.QLabel()
        outputs_hbox.addWidget(self.out_pic_label)

        # info
        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch()
        out_text_vbox = QtWidgets.QVBoxLayout()
        hbox.addLayout(out_text_vbox)
        self.out_main_label = QtWidgets.QLabel()
        self.out_main_label.setFixedWidth(320)
        self.out_main_label.setStyleSheet("font-weight: bold")
        out_text_vbox.addWidget(self.out_main_label)
        self.out_more_label = QtWidgets.QLabel()
        self.out_more_label.setFixedWidth(320)
        out_text_vbox.addWidget(self.out_more_label)
        hbox.addStretch()
        outputs_hbox.addLayout(hbox)

        outputs_hbox.addStretch()

        # ### PLOTS ###

        # figure and canvas
        with rc_context(self.rc_context):

            self.f = Figure(figsize=self.f_sz, dpi=self.f_dpi)
            self.f.patch.set_alpha(0.0)
            self.c = FigureCanvas(self.f)
            self.c.setParent(self)
            self.c.setFocusPolicy(QtCore.Qt.ClickFocus)  # key for press events!!!
            self.c.setFocus()
            outputs_vbox.addWidget(self.c)

            # axes
            self.levels_ax = self.f.add_subplot(111)
            self.levels_ax.invert_yaxis()

        # toolbar
        self.hbox = QtWidgets.QHBoxLayout()
        outputs_vbox.addLayout(self.hbox)
        # navigation
        self.nav = NavToolbar(canvas=self.c, parent=self.top_widget)
        self.hbox.addWidget(self.nav)

        self.on_settings_changed()
        self.on_first_draw()

    @classmethod
    def is_url(cls, value):
        if len(value) > 7:

            https = "https"
            if value[:len(https)] == https:
                return True

        return False

    @classmethod
    def is_darwin(cls):
        """ Check if the current OS is Mac OS """
        return sys.platform == 'darwin'

    @classmethod
    def is_linux(cls):
        """ Check if the current OS is Linux """
        return sys.platform in ['linux', 'linux2']

    @classmethod
    def is_windows(cls):
        """ Check if the current OS is Windows """
        return (sys.platform == 'win32') or (os.name == "nt")

    @classmethod
    def explore_folder(cls, path):
        """Open the passed path using OS-native commands"""
        if cls.is_url(path):
            import webbrowser
            webbrowser.open(path)
            return True

        if not os.path.exists(path):
            logger.warning('invalid path to folder: %s' % path)
            return False

        path = os.path.normpath(path)

        if cls.is_darwin():
            subprocess.call(['open', '--', path])
            return True

        elif cls.is_linux():
            subprocess.call(['xdg-open', path])
            return True

        elif cls.is_windows():
            subprocess.call(['explorer', path])
            return True

        logger.warning("Unknown/unsupported OS")
        return False

    @classmethod
    def click_open_manual(cls):
        logger.debug("open manual")
        cls.explore_folder(
            "https://www.hydroffice.org/manuals/qctools/stable/user_manual_info.html#rori-your-rock-or-islet-oracle")

    def _draw_grid(self):
        for a in self.f.get_axes():
            a.grid(True)

    def on_first_draw(self):
        """Redraws the figure, it is only called at the first import!!!"""
        with rc_context(self.rc_context):
            # self._set_title()
            self._draw_levels()
            self._draw_grid()
            self.is_drawn = True

    def _draw_levels(self):
        logger.debug("draw levels")

        mhw = float(self.mhw_value.text())
        depth = float(self.depth_value.text())

        if self.toggle_area.value() != 1:
            max_z = 1.5 * max(abs(mhw), abs(depth))
        else:
            max_z = 1.5 * abs(depth)
        alpha = 0.3
        text_color = '#666666'
        text_shift = 0.6

        with rc_context(self.rc_context):

            self.levels_ax.clear()
            # self.levels_ax.set_xlabel('Depth [m]')
            if self.toggle_hssd.value() == 0:  # 2018 HSSD
                if self.toggle_z.value() == 0:
                    self.levels_ax.set_ylabel('Depth [m]')
                    if self.toggle_area.value() == 1:
                        self.levels_ax.axhline(y=0, color='b', linestyle=':')
                        self.levels_ax.text(0.01, -0.01, 'LWD', rotation=0)

                        self.levels_ax.axhline(y=depth, color='r', linestyle='-')
                        self.levels_ax.text(0.01, depth + 0.01, 'depth', rotation=0)

                        self.levels_ax.axhspan(- 1.2192, -max_z, facecolor='orange', alpha=alpha)
                        self.levels_ax.text(text_shift, (- 1.2192 - max_z) / 2.0, 'ALWAYS DRY', color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(-0.6096, - 1.2192, facecolor='yellow', alpha=alpha)
                        self.levels_ax.text(text_shift, (-0.6096 - 1.2192) / 2.0, 'COVERS & UNCOVERS',
                                            color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(0.6096, -0.6096, facecolor='cyan', alpha=alpha)
                        self.levels_ax.text(text_shift, (0.6096 - 0.6096) / 2.0, 'AWASH', color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(max_z, 0.6096, facecolor='#0099ff', alpha=alpha)
                        self.levels_ax.text(text_shift, (max_z + 0.6096) / 2.0, 'ALWAYS UNDERWATER', color=text_color,
                                            rotation=0)
                    else:
                        self.levels_ax.axhline(y=0.0, color='b', linestyle=':')
                        self.levels_ax.text(0.01, 0.01, 'MLLW', rotation=0)

                        self.levels_ax.axhline(y=-mhw, color='orange', linestyle=':')
                        self.levels_ax.text(0.01, -mhw - 0.01, 'MHW', rotation=0)

                        self.levels_ax.axhline(y=depth, color='r', linestyle='-')
                        self.levels_ax.text(0.01, depth + 0.01, 'depth', rotation=0)

                        if self.toggle_area.value() == 0:
                            self.levels_ax.axhspan(-mhw - 0.6096, -max_z, facecolor='orange', alpha=alpha)
                            self.levels_ax.text(text_shift, (-mhw - 0.6096 - max_z) / 2.0, 'ALWAYS DRY',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-0.6096, -mhw - 0.6096, facecolor='yellow', alpha=alpha)
                            self.levels_ax.text(text_shift, (-0.6096 - mhw - 0.6096) / 2.0, 'COVERS & UNCOVERS',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(0.6096, -0.6096, facecolor='cyan', alpha=alpha)
                            self.levels_ax.text(text_shift, (0.6096 - 0.6096) / 2.0, 'AWASH', color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(max_z, 0.6096, facecolor='#0099ff', alpha=alpha)
                            self.levels_ax.text(text_shift, (max_z + 0.6096) / 2.0, 'ALWAYS UNDERWATER',
                                                color=text_color,
                                                rotation=0)
                        else:
                            self.levels_ax.axhspan(-mhw - 0.3048, -max_z, facecolor='orange', alpha=alpha)
                            self.levels_ax.text(text_shift, (-mhw - 0.3048 - max_z) / 2.0, 'ALWAYS DRY',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-0.3048, -mhw - 0.3048, facecolor='yellow', alpha=alpha)
                            self.levels_ax.text(text_shift, (-0.3048 - mhw - 0.3048) / 2.0, 'COVERS & UNCOVERS',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(0.3048, -0.3048, facecolor='cyan', alpha=alpha)
                            self.levels_ax.text(text_shift, (0.3048 - 0.3048) / 2.0, 'AWASH', color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(max_z, 0.3048, facecolor='#0099ff', alpha=alpha)
                            self.levels_ax.text(text_shift, (max_z + 0.3048) / 2.0, 'ALWAYS UNDERWATER',
                                                color=text_color,
                                                rotation=0)
                    self.levels_ax.set_ylim([max_z, -max_z])

                else:
                    self.levels_ax.set_ylabel('Elevation [m]')
                    if self.toggle_area.value() == 1:
                        self.levels_ax.axhline(y=0, color='b', linestyle=':')
                        self.levels_ax.text(0.01, 0.01, 'LWD', rotation=0)

                        self.levels_ax.axhline(y=-depth, color='r', linestyle='-')
                        self.levels_ax.text(0.01, -depth - 0.01, 'depth', rotation=0)

                        self.levels_ax.axhspan(1.2192, max_z, facecolor='orange', alpha=alpha)
                        self.levels_ax.text(text_shift, (1.2192 + max_z) / 2.0, 'ALWAYS DRY', color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(+0.6096, + 1.2192, facecolor='yellow', alpha=alpha)
                        self.levels_ax.text(text_shift, (+0.6096 + 1.2192) / 2.0, 'COVERS & UNCOVERS',
                                            color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(-0.6096, 0.6096, facecolor='cyan', alpha=alpha)
                        self.levels_ax.text(text_shift, (-0.6096 + 0.6096) / 2.0, 'AWASH', color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(-max_z, -0.6096, facecolor='#0099ff', alpha=alpha)
                        self.levels_ax.text(text_shift, (-max_z - 0.6096) / 2.0, 'ALWAYS UNDERWATER', color=text_color,
                                            rotation=0)
                    else:
                        self.levels_ax.axhline(y=0.0, color='orange', linestyle=':')
                        self.levels_ax.text(0.01, 0.01, 'MHW', rotation=0)

                        self.levels_ax.axhline(y=-mhw, color='b', linestyle=':')
                        self.levels_ax.text(0.01, -mhw - 0.01, 'MLLW', rotation=0)

                        self.levels_ax.axhline(y=-mhw - depth, color='r', linestyle='-')
                        self.levels_ax.text(0.01, -mhw - depth - 0.01, 'depth', rotation=0)

                        if self.toggle_area.value() == 0:
                            self.levels_ax.axhspan(0.6096, max_z, facecolor='orange', alpha=alpha)
                            self.levels_ax.text(text_shift, (0.6096 + max_z) / 2.0, 'ALWAYS DRY', color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-mhw + 0.6096, 0.6096, facecolor='yellow', alpha=alpha)
                            self.levels_ax.text(text_shift, (-mhw + 0.6096 + 0.6096) / 2.0, 'COVERS & UNCOVERS',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-mhw - 0.6096, -mhw + 0.6096, facecolor='cyan', alpha=alpha)
                            self.levels_ax.text(text_shift, (-mhw - 0.6096 - mhw + 0.6096) / 2.0, 'AWASH',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-max_z, -mhw - 0.6096, facecolor='#0099ff', alpha=alpha)
                            self.levels_ax.text(text_shift, (-max_z - mhw - 0.6096) / 2.0, 'ALWAYS UNDERWATER',
                                                color=text_color,
                                                rotation=0)
                        else:
                            self.levels_ax.axhspan(0.3048, max_z, facecolor='orange', alpha=alpha)
                            self.levels_ax.text(text_shift, (0.3048 + max_z) / 2.0, 'ALWAYS DRY', color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-mhw + 0.3048, 0.3048, facecolor='yellow', alpha=alpha)
                            self.levels_ax.text(text_shift, (-mhw + 0.3048 + 0.3048) / 2.0, 'COVERS & UNCOVERS',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-mhw - 0.3048, -mhw + 0.3048, facecolor='cyan', alpha=alpha)
                            self.levels_ax.text(text_shift, (-mhw - 0.3048 - mhw + 0.3048) / 2.0, 'AWASH',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-max_z, -mhw - 0.3048, facecolor='#0099ff', alpha=alpha)
                            self.levels_ax.text(text_shift, (-max_z - mhw - 0.3048) / 2.0, 'ALWAYS UNDERWATER',
                                                color=text_color,
                                                rotation=0)

                    self.levels_ax.set_ylim([-max_z, max_z])

            else:  # 2019 HSSD
                if self.toggle_z.value() == 0:
                    self.levels_ax.set_ylabel('Depth [m]')
                    if self.toggle_area.value() == 1:
                        self.levels_ax.axhline(y=0, color='b', linestyle=':')
                        self.levels_ax.text(0.01, -0.01, 'LWD', rotation=0)

                        self.levels_ax.axhline(y=depth, color='r', linestyle='-')
                        self.levels_ax.text(0.01, depth + 0.01, 'depth', rotation=0)

                        self.levels_ax.axhspan(- 0.1, -max_z, facecolor='orange', alpha=alpha)
                        self.levels_ax.text(text_shift, (- 0.1 - max_z) / 2.0, 'ALWAYS DRY', color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(-0.1, - 0.1, facecolor='yellow', alpha=alpha)
                        self.levels_ax.text(text_shift, (-0.1 - 0.1) / 2.0, 'COVERS & UNCOVERS',
                                            color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(0.1, -0.1, facecolor='cyan', alpha=alpha)
                        self.levels_ax.text(text_shift, (0.1 - 0.1) / 2.0, 'AWASH', color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(max_z, 0.1, facecolor='#0099ff', alpha=alpha)
                        self.levels_ax.text(text_shift, (max_z + 0.1) / 2.0, 'ALWAYS UNDERWATER', color=text_color,
                                            rotation=0)
                    else:
                        self.levels_ax.axhline(y=0.0, color='b', linestyle=':')
                        self.levels_ax.text(0.01, 0.01, 'MLLW', rotation=0)

                        self.levels_ax.axhline(y=-mhw, color='orange', linestyle=':')
                        self.levels_ax.text(0.01, -mhw - 0.01, 'MHW', rotation=0)

                        self.levels_ax.axhline(y=depth, color='r', linestyle='-')
                        self.levels_ax.text(0.01, depth + 0.01, 'depth', rotation=0)

                        if self.toggle_area.value() in [0, 2]:
                            self.levels_ax.axhspan(-mhw - 0.1, -max_z, facecolor='orange', alpha=alpha)
                            self.levels_ax.text(text_shift, (-mhw - 0.1 - max_z) / 2.0, 'ALWAYS DRY',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-0.1, -mhw - 0.1, facecolor='yellow', alpha=alpha)
                            self.levels_ax.text(text_shift, (-0.1 - mhw - 0.1) / 2.0, 'COVERS & UNCOVERS',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(0.1, -0.1, facecolor='cyan', alpha=alpha)
                            self.levels_ax.text(text_shift, (0.1 - 0.1) / 2.0, 'AWASH', color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(max_z, 0.1, facecolor='#0099ff', alpha=alpha)
                            self.levels_ax.text(text_shift, (max_z + 0.1) / 2.0, 'ALWAYS UNDERWATER',
                                                color=text_color,
                                                rotation=0)
                    self.levels_ax.set_ylim([max_z, -max_z])

                else:
                    self.levels_ax.set_ylabel('Elevation [m]')
                    if self.toggle_area.value() == 1:
                        self.levels_ax.axhline(y=0, color='b', linestyle=':')
                        self.levels_ax.text(0.01, 0.01, 'LWD', rotation=0)

                        self.levels_ax.axhline(y=-depth, color='r', linestyle='-')
                        self.levels_ax.text(0.01, -depth - 0.01, 'depth', rotation=0)

                        self.levels_ax.axhspan(0.1, max_z, facecolor='orange', alpha=alpha)
                        self.levels_ax.text(text_shift, (0.1 + max_z) / 2.0, 'ALWAYS DRY', color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(+0., + 0.1, facecolor='yellow', alpha=alpha)
                        self.levels_ax.text(text_shift, (+0.1 + 0.1) / 2.0, 'COVERS & UNCOVERS',
                                            color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(-0.1, 0.1, facecolor='cyan', alpha=alpha)
                        self.levels_ax.text(text_shift, (-0.1 + 0.1) / 2.0, 'AWASH', color=text_color,
                                            rotation=0)
                        self.levels_ax.axhspan(-max_z, -0.1, facecolor='#0099ff', alpha=alpha)
                        self.levels_ax.text(text_shift, (-max_z - 0.1) / 2.0, 'ALWAYS UNDERWATER', color=text_color,
                                            rotation=0)
                    else:
                        self.levels_ax.axhline(y=0.0, color='orange', linestyle=':')
                        self.levels_ax.text(0.01, 0.01, 'MHW', rotation=0)

                        self.levels_ax.axhline(y=-mhw, color='b', linestyle=':')
                        self.levels_ax.text(0.01, -mhw - 0.01, 'MLLW', rotation=0)

                        self.levels_ax.axhline(y=-mhw - depth, color='r', linestyle='-')
                        self.levels_ax.text(0.01, -mhw - depth - 0.01, 'depth', rotation=0)

                        if self.toggle_area.value() in [0, 2]:
                            self.levels_ax.axhspan(0.1, max_z, facecolor='orange', alpha=alpha)
                            self.levels_ax.text(text_shift, (0.1 + max_z) / 2.0, 'ALWAYS DRY', color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-mhw + 0.1, 0.1, facecolor='yellow', alpha=alpha)
                            self.levels_ax.text(text_shift, (-mhw + 0.1 + 0.1) / 2.0, 'COVERS & UNCOVERS',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-mhw - 0.1, -mhw + 0.1, facecolor='cyan', alpha=alpha)
                            self.levels_ax.text(text_shift, (-mhw - 0.1 - mhw + 0.1) / 2.0, 'AWASH',
                                                color=text_color,
                                                rotation=0)
                            self.levels_ax.axhspan(-max_z, -mhw - 0.1, facecolor='#0099ff', alpha=alpha)
                            self.levels_ax.text(text_shift, (-max_z - mhw - 0.1) / 2.0, 'ALWAYS UNDERWATER',
                                                color=text_color,
                                                rotation=0)
                    self.levels_ax.set_ylim([-max_z, max_z])

    def on_settings_changed(self):
        if self.toggle_hssd.value() == 0:
            logger.debug("draw HSSD 2018")

            if self.toggle_z.value() == 0:
                logger.debug("draw depth")

                if self.toggle_area.value() == 0:
                    logger.debug("West Coast")
                    self.always_dry_min_value.setText("")
                    self.always_dry_max_value.setText("< -0.6096 MHW")
                    self.covers_and_uncovers_min_value.setText(">= -0.6096 MHW")
                    self.covers_and_uncovers_max_value.setText("< -0.6096 MLLW")
                    self.awash_min_value.setText(">= -0.6096 MLLW")
                    self.awash_max_value.setText("< +0.6096 MLLW")
                    self.always_underwater_min_value.setText(">= +0.6096 MLLW")
                    self.always_underwater_max_value.setText("")

                    self.mhw_text.setText("MHW [m]: ")
                    self.mhw_value.setEnabled(True)

                elif self.toggle_area.value() == 1:
                    logger.debug("Great Lakes")
                    self.always_dry_min_value.setText("")
                    self.always_dry_max_value.setText("< -1.2192 LWD")
                    self.covers_and_uncovers_min_value.setText(">= -1.2192 LWD")
                    self.covers_and_uncovers_max_value.setText("< -0.6096 LWD")
                    self.awash_min_value.setText(">= -0.6096 LWD")
                    self.awash_max_value.setText("< +0.6096 LWD")
                    self.always_underwater_min_value.setText(">= +0.6096 LWD")
                    self.always_underwater_max_value.setText("")

                    self.mhw_text.setText("LWD [m]: ")
                    self.mhw_value.setDisabled(True)
                    self.mhw_value.setText("0.0")

                elif self.toggle_area.value() == 2:
                    logger.debug("East Coast")
                    self.always_dry_min_value.setText("")
                    self.always_dry_max_value.setText("< -0.3048 MHW")
                    self.covers_and_uncovers_min_value.setText(">= -0.3048 MHW")
                    self.covers_and_uncovers_max_value.setText("< -0.3048 MLLW")
                    self.awash_min_value.setText(">= -0.3048 MLLW")
                    self.awash_max_value.setText("< +0.3048 MLLW")
                    self.always_underwater_min_value.setText(">= +0.3048 MLLW")
                    self.always_underwater_max_value.setText("")

                    self.mhw_text.setText("MHW [m]: ")
                    self.mhw_value.setEnabled(True)

                else:
                    logger.warning("unknown area")
                    return

            else:
                logger.debug("draw elevation")

                if self.toggle_area.value() == 0:
                    logger.debug("West Coast")
                    self.always_dry_min_value.setText("")
                    self.always_dry_max_value.setText("> +0.6096 MHW")
                    self.covers_and_uncovers_min_value.setText("<= +0.6096 MHW")
                    self.covers_and_uncovers_max_value.setText("> +0.6096 MLLW")
                    self.awash_min_value.setText("<= +0.6096 MLLW")
                    self.awash_max_value.setText("> -0.6096 MLLW")
                    self.always_underwater_min_value.setText("<= -0.6096 MLLW")
                    self.always_underwater_max_value.setText("")

                    self.mhw_text.setText("MHW [m]: ")
                    self.mhw_value.setEnabled(True)

                elif self.toggle_area.value() == 1:
                    logger.debug("Great Lakes")
                    self.always_dry_min_value.setText("")
                    self.always_dry_max_value.setText("> +1.2192 LWD")
                    self.covers_and_uncovers_min_value.setText("<= +1.2192 LWD")
                    self.covers_and_uncovers_max_value.setText("> +0.6096 LWD")
                    self.awash_min_value.setText("<= +0.6096 LWD")
                    self.awash_max_value.setText("> -0.6096 LWD")
                    self.always_underwater_min_value.setText("<= -0.6096 LWD")
                    self.always_underwater_max_value.setText("")

                    self.mhw_text.setText("LWD [m]: ")
                    self.mhw_value.setDisabled(True)

                elif self.toggle_area.value() == 2:
                    logger.debug("East Coast")
                    self.always_dry_min_value.setText("")
                    self.always_dry_max_value.setText("> +0.3048 MHW")
                    self.covers_and_uncovers_min_value.setText("<= +0.3048 MHW")
                    self.covers_and_uncovers_max_value.setText("> +0.3048 MLLW")
                    self.awash_min_value.setText("<= +0.3048 MLLW")
                    self.awash_max_value.setText("> -0.3048 MLLW")
                    self.always_underwater_min_value.setText("<= -0.3048 MLLW")
                    self.always_underwater_max_value.setText("")

                    self.mhw_text.setText("MHW [m]: ")
                    self.mhw_value.setEnabled(True)

                else:
                    logger.warning("unknown area")
                    return
        else:
            logger.debug("draw HSSD 2019")

            if self.toggle_z.value() == 0:
                logger.debug("draw depth")

                if self.toggle_area.value() in [0, 2]:
                    logger.debug("East/West Coast")
                    self.always_dry_min_value.setText("")
                    self.always_dry_max_value.setText("< -0.1 MHW")
                    self.covers_and_uncovers_min_value.setText(">= -0.1 MHW")
                    self.covers_and_uncovers_max_value.setText("<= -0.1 MLLW")
                    self.awash_min_value.setText("> -0.1 MLLW")
                    self.awash_max_value.setText("<= +0.1 MLLW")
                    self.always_underwater_min_value.setText("> +0.1 MLLW")
                    self.always_underwater_max_value.setText("")

                    self.mhw_text.setText("MHW [m]: ")
                    self.mhw_value.setEnabled(True)

                elif self.toggle_area.value() == 1:
                    logger.debug("Great Lakes")
                    self.always_dry_min_value.setText("")
                    self.always_dry_max_value.setText("< -0.1 LWD")
                    self.covers_and_uncovers_min_value.setText(">= -0.1 LWD")
                    self.covers_and_uncovers_max_value.setText("<= -0.1 LWD")
                    self.awash_min_value.setText("> -0.1 LWD")
                    self.awash_max_value.setText("<= +0.1 LWD")
                    self.always_underwater_min_value.setText("> +0.1 LWD")
                    self.always_underwater_max_value.setText("")

                    self.mhw_text.setText("LWD [m]: ")
                    self.mhw_value.setDisabled(True)

                else:
                    logger.warning("unknown area")
                    return

            else:
                logger.debug("draw elevation")

                if self.toggle_area.value() in [0, 2]:
                    logger.debug("East/West Coast")
                    self.always_dry_min_value.setText("")
                    self.always_dry_max_value.setText("> +0.1 MHW")
                    self.covers_and_uncovers_min_value.setText("<= +0.1 MHW")
                    self.covers_and_uncovers_max_value.setText(">= +0.1 MLLW")
                    self.awash_min_value.setText("< +0.1 MLLW")
                    self.awash_max_value.setText(">= -0.1 MLLW")
                    self.always_underwater_min_value.setText("< -0.1 MLLW")
                    self.always_underwater_max_value.setText("")

                    self.mhw_text.setText("MHW [m]: ")
                    self.mhw_value.setEnabled(True)

                elif self.toggle_area.value() == 1:
                    logger.debug("Great Lakes")
                    self.always_dry_min_value.setText("")
                    self.always_dry_max_value.setText("> +0.1 LWD")
                    self.covers_and_uncovers_min_value.setText("<= +0.1 LWD")
                    self.covers_and_uncovers_max_value.setText(">= +0.1 LWD")
                    self.awash_min_value.setText("< +0.1 LWD")
                    self.awash_max_value.setText(">= -0.1 LWD")
                    self.always_underwater_min_value.setText("< -0.1 LWD")
                    self.always_underwater_max_value.setText("")

                    self.mhw_text.setText("LWD [m]: ")
                    self.mhw_value.setDisabled(True)
                    self.mhw_value.setText("0.0")

                else:
                    logger.warning("unknown area")
                    return

        self.on_calculate()

    def on_calculate(self):
        logger.debug("calculate")

        mhw = float(self.mhw_value.text())
        depth = float(self.depth_value.text())
        elevation = None
        watlev = None

        wl_dict = {
            "Always Dry": None,
            "Awash": 5,
            "Covers & Uncovers": 4,
            "Always Underwater": 3
        }

        if self.toggle_hssd.value() == 0:
            logger.debug("draw HSSD 2018")

            if self.toggle_area.value() == 1:
                if depth < - 1.2192:
                    logger.debug("Islet")
                    elevation = -depth
                else:
                    logger.debug("Rock")
                    if depth < -0.6096:
                        watlev = "Covers & Uncovers"
                    elif depth < 0.6096:
                        watlev = "Awash"
                    else:
                        watlev = "Always Underwater"

                    logger.debug("%s [%s]" % (watlev, wl_dict[watlev]))

            else:

                if self.toggle_area.value() == 0:
                    if depth < (-mhw - 0.6096):
                        logger.debug("Islet")
                        elevation = -mhw - depth
                    else:
                        logger.debug("Rock")
                        if depth < -0.6096:
                            watlev = "Covers & Uncovers"
                        elif depth < 0.6096:
                            watlev = "Awash"
                        else:
                            watlev = "Always Underwater"

                        logger.debug("%s [%s]" % (watlev, wl_dict[watlev]))

                else:
                    if depth < (-mhw - 0.3048):
                        logger.debug("Islet")
                        elevation = -mhw - depth
                    else:
                        logger.debug("Rock")
                        if depth < -0.3048:
                            watlev = "Covers & Uncovers"
                        elif depth < 0.3048:
                            watlev = "Awash"
                        else:
                            watlev = "Always Underwater"

                        logger.debug("%s [%s]" % (watlev, wl_dict[watlev]))

        else:
            logger.debug("draw HSSD 2019")

            if self.toggle_area.value() == 1:
                if depth < - 0.1:
                    logger.debug("Islet")
                    elevation = -depth
                else:
                    logger.debug("Rock")
                    if depth <= -0.1:
                        watlev = "Covers & Uncovers"
                    elif depth <= 0.1:
                        watlev = "Awash"
                    else:
                        watlev = "Always Underwater"

                    logger.debug("%s [%s]" % (watlev, wl_dict[watlev]))

            else:
                if self.toggle_area.value() in [0, 2]:
                    if depth < (-mhw - 0.1):
                        logger.debug("Islet")
                        elevation = -mhw - depth
                    else:
                        logger.debug("Rock")
                        if depth <= -0.1:
                            watlev = "Covers & Uncovers"
                        elif depth <= 0.1:
                            watlev = "Awash"
                        else:
                            watlev = "Always Underwater"

                        logger.debug("%s [%s]" % (watlev, wl_dict[watlev]))

        if elevation is not None:
            logger.debug("elevation: %.3f" % elevation)
            pix = QtGui.QPixmap(os.path.join(self.media, 'islet.png'))
            self.out_pic_label.setPixmap(pix.scaled(60, 60, QtCore.Qt.KeepAspectRatio))
            self.out_main_label.setText("I S L E T")
            self.out_more_label.setText("ELEVAT=%.3f" % elevation)
        else:
            pix = QtGui.QPixmap(os.path.join(self.media, 'rock.png'))
            self.out_pic_label.setPixmap(pix.scaled(60, 60, QtCore.Qt.KeepAspectRatio))
            self.out_main_label.setText("R O C K")
            self.out_more_label.setText("VALSOU=%.3f, WATLEV=%s[%s]" % (depth, watlev, wl_dict[watlev]))

        self._draw_levels()
        self._draw_grid()
        self.c.draw()
Ejemplo n.º 22
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=None, dpi=150,
                 axissize=None, axisbg=None, fontsize=9,
                 trace_color_callback=None,
                 output_title='plot', **kws):

        if size is None: size=(700, 450)

        self.trace_color_callback = trace_color_callback
        matplotlib.rc('axes', axisbelow=True)
        matplotlib.rc('lines', linewidth=2)
        matplotlib.rc('xtick', labelsize=fontsize, color='k')
        matplotlib.rc('ytick', labelsize=fontsize, color='k')
        matplotlib.rc('legend', fontsize=fontsize)
        matplotlib.rc('grid',  linewidth=0.5, linestyle='-')


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

        self.conf = PlotConfig()
        self.data_range = {}
        self.win_config = None
        self.cursor_callback = None
        self.lasso_callback = None
        self.parent    = parent
        self.figsize = (size[0]*1.0/dpi, size[1]*1.0/dpi)
        self.dpi     = dpi

        if axissize is None:  axissize = [0.16, 0.16, 0.72, 0.75]
        if axisbg is None:     axisbg='#FEFFFE'
        self.axisbg = axisbg
        self.axissize = axissize
        self.BuildPanel()
        self.user_limits = {} # [None, None, None, None]
        self.data_range = {}
        self.zoom_lims = []
        self.axes_traces = {}

    def plot(self, xdata, ydata, side='left', title=None,
             xlabel=None, ylabel=None, y2label=None,
             use_dates=False, **kws):
        """
        plot (that is, create a new plot: clear, then oplot)
        """
        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.zoom_lims = []
        self.axes_traces = {}
        self.clear()
        axes = self.axes
        if side == 'right':
            axes = self.get_right_axes()
        self.conf.ntrace  = 0
        self.cursor_mode = 'zoom'
        self.conf.plot_type = 'lineplot'
        self.user_limits[axes] = [None, None, None, None]

        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 use_dates is not None:
            self.use_dates  = 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=False, grid=None,
              xmin=None, xmax=None, ymin=None, ymax=None,
              color=None, style=None, drawstyle=None,
              linewidth=2, marker=None, markersize=None,
              autoscale=True, refresh=True, show_legend=None,
              legend_loc='ur', legend_on=True, delay_draw=False,
              bgcolor=None, framecolor=None, gridcolor=None,
              labelfontsize=None, legendfontsize=None,
              fullbox=True, zorder=None, **kws):
        """ basic plot method, overplotting any existing plot """
        axes = self.axes
        if side == 'right':
            axes = self.get_right_axes()
        # set y scale to log/linear
        yscale = 'linear'
        if ylog_scale:
            yscale = 'log'
            # ydata = ma.masked_where(ydata<=0, 1.0*ydata)
            # ymin = min(ydata[where(ydata>0)])
            ydata[where(ydata<=0)] = None

        axes.set_yscale(yscale, basey=10)
        if linewidth is None:
            linewidth = 2

        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 show_legend is not None:
            self.conf.set_legend_location(legend_loc, legend_on)
            self.conf.show_legend = show_legend

        if grid is not None:
            self.conf.show_grid = grid

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

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

        if xmin is not None: self.user_limits[axes][0] = xmin
        if xmax is not None: self.user_limits[axes][1] = xmax
        if ymin is not None: self.user_limits[axes][2] = ymin
        if ymax is not None: self.user_limits[axes][3] = ymax

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

        axes.xaxis.set_major_formatter(FuncFormatter(self.xformatter))

        conf  = self.conf
        n    = conf.ntrace
        if zorder is None:
            zorder = 10*(n+1)
        if axes not in self.axes_traces:
            self.axes_traces[axes] = []
        self.axes_traces[axes].append(n)

        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.grid_color)
                i.set_zorder(0)
            axes.grid(True)
        else:
            axes.grid(False)
        if dy is None:
            _lines = axes.plot(xdata, ydata, drawstyle=drawstyle, zorder=zorder)
        else:
            _lines = axes.errorbar(xdata, ydata, yerr=dy, zorder=zorder)

        if label is None:
            label = 'trace %i' % (conf.ntrace+1)
        conf.set_trace_label(label)
        conf.set_trace_datarange(datarange)

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

        if color:
            conf.set_trace_color(color)
        if style:
            conf.set_trace_style(style)
        if marker:
            conf.set_trace_marker(marker)
        if linewidth is not None:
            conf.set_trace_linewidth(linewidth)
        if markersize is not None:
            conf.set_trace_markersize(markersize)
        if drawstyle is not None:
            conf.set_trace_drawstyle(drawstyle)

        if gridcolor is not None:
            conf.grid_color = gridcolor
        if labelfontsize is not None:
            conf.labelfont.set_size(labelfontsize)
        if legendfontsize is not None:
            conf.legendfont.set_size(legendfontsize)

        if n < len(conf.lines):
            conf.lines[n] = _lines
        else:
            conf._init_trace(n, 'black', solid)
            conf.lines[n] = _lines

        # now set plot limits:
        self.set_viewlimits()
        if refresh:
            conf.refresh_trace(conf.ntrace)
            conf.relabel()

        if self.conf.show_legend:
            conf.draw_legend()

        # axes style ('box' or 'open')
        conf.axes_style = 'box'
        if not fullbox:  # show only left and bottom lines
            conf.axes_style = 'open'
        conf.set_axes_style()

        if not delay_draw:
            self.canvas.draw()
            self.canvas.Refresh()

        conf.ntrace = conf.ntrace + 1
        return _lines

    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.canvas.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.canvas.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,
                    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

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

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

        fcols = [to_rgba(self.conf.scatter_normalcolor) for x in xdata]
        ecols = [self.conf.scatter_normaledge]*len(xdata)

        self.conf.scatter_data = [(x, y) for x, y in zip(xdata, ydata)]
        self.conf.scatter_size = size
        self.conf.scatter_coll = CircleCollection(
            sizes=(size, ), facecolors=fcols, edgecolors=ecols,
            offsets=self.conf.scatter_data,
            transOffset= self.axes.transData)
        self.axes.add_collection(self.conf.scatter_coll)
        # self.set_viewlimits(axes=axes)

        if self.conf.show_grid:
            for i in axes.get_xgridlines()+axes.get_ygridlines():
                i.set_color(self.conf.grid_color)
                i.set_zorder(-100)
            axes.grid(True)
        else:
            axes.grid(False)
        self.set_viewlimits()
        self.canvas.draw()

    def lassoHandler(self, vertices):
        conf = self.conf
       
        if self.conf.plot_type == 'scatter':
            fcols = conf.scatter_coll.get_facecolors()
            ecols = conf.scatter_coll.get_edgecolors()
            sdat = conf.scatter_data
            mask = inside_poly(vertices,sdat)
            pts = nonzero(mask)[0]
            self.conf.scatter_mask = mask
            for i in range(len(sdat)):
                if i in pts:
                    ecols[i] = to_rgba(conf.scatter_selectedge)
                    fcols[i] = to_rgba(conf.scatter_selectcolor)
                    fcols[i][3] = 0.3
                    ecols[i][3] = 0.8
                else:
                    fcols[i] = to_rgba(conf.scatter_normalcolor)
                    ecols[i] = to_rgba(conf.scatter_normaledge)
        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)
            #print  len(xdata), sdat[:20]
            # print mask
            pts = nonzero(mask)[0]
            #print 'Points selected = ', pts
            
        self.lasso = None
        self.canvas.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):
        "set user-defined limits and apply them"
        if axes not in self.user_limits:
            axes = self.axes
        self.user_limits[axes] = limits
        self.unzoom_all()


    def set_viewlimits(self, autoscale=False):
        """ update xy limits of a plot, as used with .update_line() """

        trace0 = None
        while trace0 is None:
            for axes in self.fig.get_axes():
                if (axes in self.axes_traces and
                   len(self.axes_traces[axes]) > 0):
                    trace0 = self.axes_traces[axes][0]
                    break

        for axes in self.fig.get_axes():
            datlim = self.conf.get_trace_datarange(trace=trace0)
            if axes in self.axes_traces:
                for i in self.axes_traces[axes]:
                    l =  self.conf.get_trace_datarange(trace=i)
                    datlim = [min(datlim[0], l[0]), max(datlim[1], l[1]),
                              min(datlim[2], l[2]), max(datlim[3], l[3])]

            xmin, xmax = axes.get_xlim()
            ymin, ymax = axes.get_ylim()
            limits = [min(datlim[0], xmin),
                      max(datlim[1], xmax),
                      min(datlim[2], ymin),
                      max(datlim[3], ymax)]

            if (axes in self.user_limits and
                (self.user_limits[axes] != 4*[None] or
                len(self.zoom_lims) > 0)):

                for i, val in enumerate(self.user_limits[axes]):
                    if val is not None:
                        limits[i] = val
                xmin, xmax, ymin, ymax = limits
                if len(self.zoom_lims) > 0:
                    limits_set = True
                    xmin, xmax, ymin, ymax = self.zoom_lims[-1][axes]
                axes.set_xlim((xmin, xmax), emit=True)
                axes.set_ylim((ymin, ymax), emit=True)

    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  = ''

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

    def unzoom(self, event=None, set_bounds=True):
        """ zoom out 1 level, or to full data range """
        if len(self.zoom_lims) > 1:
            self.zoom_lims.pop()
        self.set_viewlimits()
        self.canvas.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"""
        try:
            self.win_config.Raise()
        except:
            self.win_config = PlotConfigFrame(parent=self,
                                              config=self.conf,
                                              trace_color_callback=self.trace_color_callback)

    ####
    ## create GUI
    ####
    def BuildPanel(self):
        """ builds basic GUI panel and popup menu"""
        self.fig   = Figure(self.figsize, dpi=self.dpi)
        self.axes  = self.fig.add_axes(self.axissize, axisbg=self.axisbg)

        self.canvas = FigureCanvas(self, -1, self.fig)

        self.printer.canvas = self.canvas
        self.set_bg()
        self.conf.canvas = self.canvas
        self.canvas.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))

        # 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.SetSizer(sizer)
        self.Fit()

        self.addCanvasEvents()

    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()
        # print 'update line ', trace, datarange
        # dr = self.data_range[axes]
        #
        #         self.data_range[axes] = [min(dr[0], xdata.min()),
        #                                  max(dr[1], xdata.max()),
        #                                  min(dr[2], ydata.min()),
        #                                  max(dr[3], ydata.max())]
        # print 'Update ', trace, side, axes == self.get_right_axes(), dr
        # this defeats zooming, which gets ugly in this fast-mode anyway.
        if update_limits:
            self.set_viewlimits()
        if draw:
            self.canvas.draw()

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

    ####
    ## 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= %s, %s" % (self._xfmt, self._yfmt)) % (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= %s, %s, %s" % (self._xfmt, self._yfmt,
                                              self._y2fmt) % (x, y, y2)
            except:
                pass
        self.write_message(msg,  panel=0)
        if (self.cursor_callback is not None and
            hasattr(self.cursor_callback , '__call__')):
            self.cursor_callback(x=event.xdata, y=event.ydata)
Ejemplo n.º 23
0
from matplotlib.backends.backend_gtkcairo import FigureCanvasGTKCairo as FigureCanvas


win = gtk.Window()
win.connect("destroy", lambda x: gtk.main_quit())
win.set_default_size(800, 600)
win.set_title("Embedding in GTK")

box = gtk.VBox();

win.add(box)

f = Figure(figsize=(5, 4), dpi=100)
a = f.add_subplot(111)

print f.get_axes()[0]

t = arange(0.0, 3.0, 0.01)
s = sin(2*pi*t)
a.plot(t, s)

s = cos(2*pi*t)
a.plot(t, s)

s = sin(2*pi*t)*cos(2*pi*t)
a.plot(t, s)

canvas = FigureCanvas(f)  # a gtk.DrawingArea

box.pack_start(canvas,expand=True,padding=10)
Ejemplo n.º 24
0
class MainWin(QtWidgets.QMainWindow):

    here = os.path.abspath(os.path.join(os.path.dirname(__file__)))  # to be overloaded
    media = os.path.join(here, "media")
    font_size = 6
    rc_context = {
        'font.family': 'sans-serif',
        'font.sans-serif': ['Tahoma', 'Bitstream Vera Sans', 'Lucida Grande', 'Verdana'],
        'font.size': font_size,
        'figure.titlesize': font_size + 1,
        'axes.labelsize': font_size,
        'legend.fontsize': font_size,
        'xtick.labelsize': font_size - 1,
        'ytick.labelsize': font_size - 1,
        'axes.linewidth': 0.5,
        'axes.xmargin': 0.01,
        'axes.ymargin': 0.01,
        'lines.linewidth': 1.0,
        'grid.alpha': 0.2,
    }

    def __init__(self, main_win=None):
        super().__init__(main_win)

        self.main_win = main_win

        # set the application name and the version
        self.name = unc_name
        self.version = unc_version
        if self.main_win is None:
            self.setWindowTitle('%s v.%s' % (self.name, self.version))
        else:
            self.setWindowTitle('%s' % (self.name, ))
        self.setMinimumSize(200, 200)
        self.resize(600, 900)

        # only called when stand-alone (without Sound Speed Manager)
        # noinspection PyArgumentList
        _app = QtCore.QCoreApplication.instance()
        if _app.applicationName() == 'python':
            _app.setApplicationName('%s v.%s' % (self.name, self.version))
            _app.setOrganizationName("HydrOffice")
            _app.setOrganizationDomain("hydroffice.org")
            logger.debug("set application name: %s" % _app.applicationName())

            # set icons
            icon_info = QtCore.QFileInfo(os.path.join(self.media, 'unccalc.png'))
            self.setWindowIcon(QtGui.QIcon(icon_info.absoluteFilePath()))
            if (sys.platform == 'win32') or (os.name is "nt"):  # is_windows()

                try:
                    # This is needed to display the app icon on the taskbar on Windows 7
                    import ctypes
                    app_id = '%s v.%s' % (self.name, self.version)
                    ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(app_id)
                except AttributeError as e:
                    logger.debug("Unable to change app icon: %s" % e)

        # set palette
        style_info = QtCore.QFileInfo(os.path.join(self.here, 'styles', 'main.stylesheet'))
        style_content = open(style_info.filePath()).read()
        self.setStyleSheet(style_content)

        # settings = QtCore.QSettings()

        # mpl figure settings
        self.is_drawn = False
        self.f_dpi = 120  # dots-per-inch
        self.f_sz = (3.0, 6.0)  # inches
        self.tvu_plot = None
        self.thu_plot = None

        self.iho_color = '#E69F24'
        self.noaa_color = '#1C75C3'

        # outline ui
        self.top_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.top_widget)
        self.vbox = QtWidgets.QVBoxLayout()
        self.vbox.setContentsMargins(4, 4, 4, 4)
        self.top_widget.setLayout(self.vbox)

        # ### Settings ###

        self.settings = QtWidgets.QGroupBox("Settings")
        self.vbox.addWidget(self.settings)
        settings_vbox = QtWidgets.QVBoxLayout()
        self.settings.setLayout(settings_vbox)

        label_hbox = QtWidgets.QHBoxLayout()
        settings_vbox.addLayout(label_hbox)
        # stretch
        label_hbox.addStretch()
        # specs
        text_1ab = QtWidgets.QLabel("Order 1")
        text_1ab.setAlignment(QtCore.Qt.AlignCenter)
        text_1ab.setFixedWidth(120)
        label_hbox.addWidget(text_1ab)
        # stretch
        label_hbox.addStretch()

        toggle_hbox = QtWidgets.QHBoxLayout()
        settings_vbox.addLayout(toggle_hbox)
        # stretch
        toggle_hbox.addStretch()
        # specs
        self.toggle_order = QtWidgets.QDial()
        self.toggle_order.setNotchesVisible(True)
        self.toggle_order.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.toggle_order.setRange(0, 2)
        self.toggle_order.setValue(0)
        self.toggle_order.setFixedSize(QtCore.QSize(50, 50))
        self.toggle_order.setInvertedAppearance(False)
        # noinspection PyUnresolvedReferences
        self.toggle_order.valueChanged.connect(self.on_tvu_changed)
        toggle_hbox.addWidget(self.toggle_order)
        # self.toggle_specs_v5.valueChanged.connect(self.click_set_profile)
        # stretch
        toggle_hbox.addStretch()

        label2_hbox = QtWidgets.QHBoxLayout()
        settings_vbox.addLayout(label2_hbox)
        # stretch
        label2_hbox.addStretch()
        # specs
        text_special = QtWidgets.QLabel("Special Order")
        text_special.setAlignment(QtCore.Qt.AlignCenter)
        text_special.setFixedWidth(80)
        label2_hbox.addWidget(text_special)
        text_2 = QtWidgets.QLabel("Order 2")
        text_2.setAlignment(QtCore.Qt.AlignCenter)
        text_2.setFixedWidth(80)
        label2_hbox.addWidget(text_2)
        # stretch
        label2_hbox.addStretch()

        settings_vbox.addSpacing(8)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch()
        formula_text = QtWidgets.QLabel("TVU: ")
        formula_text.setFixedWidth(60)
        hbox.addWidget(formula_text)
        formula_label = QtWidgets.QLabel()
        formula_label.setPixmap(QtGui.QPixmap(os.path.join(self.media, 'tvu_formula.png')))
        formula_label.setFixedWidth(120)
        hbox.addWidget(formula_label)
        hbox.addSpacing(6)
        self.thu_a_text = QtWidgets.QLabel("a")
        hbox.addWidget(self.thu_a_text)
        self.thu_a_value = QtWidgets.QLineEdit()
        self.thu_a_value.setFixedWidth(60)
        self.thu_a_value.setValidator(QtGui.QDoubleValidator(0.00001, 9999, 5, self.thu_a_value))
        self.thu_a_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.thu_a_value.setDisabled(True)
        self.thu_a_value.setText("0.25")
        hbox.addWidget(self.thu_a_value)
        self.thu_b_text = QtWidgets.QLabel(", b")
        hbox.addWidget(self.thu_b_text)
        self.thu_b_value = QtWidgets.QLineEdit()
        self.thu_b_value.setFixedWidth(60)
        self.thu_b_value.setValidator(QtGui.QDoubleValidator(0.00001, 9999, 5, self.thu_b_value))
        self.thu_b_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.thu_b_value.setDisabled(True)
        self.thu_b_value.setText("0.0075")
        hbox.addWidget(self.thu_b_value)
        hbox.addStretch()
        settings_vbox.addLayout(hbox)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch()
        formula_text = QtWidgets.QLabel("IHO THU: ")
        formula_text.setFixedWidth(60)
        hbox.addWidget(formula_text)
        formula_label = QtWidgets.QLabel()
        formula_label.setPixmap(QtGui.QPixmap(os.path.join(self.media, 'thu_formula.png')))
        formula_label.setFixedWidth(120)
        hbox.addWidget(formula_label)
        hbox.addSpacing(6)
        self.thu_k_text = QtWidgets.QLabel("k")
        hbox.addWidget(self.thu_k_text)
        self.thu_k_value = QtWidgets.QLineEdit()
        self.thu_k_value.setFixedWidth(60)
        self.thu_k_value.setValidator(QtGui.QDoubleValidator(0.00001, 9999, 5, self.thu_k_value))
        self.thu_k_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.thu_k_value.setDisabled(True)
        self.thu_k_value.setText("2.0")
        hbox.addWidget(self.thu_k_value)
        self.tvu_p_text = QtWidgets.QLabel(", p")
        hbox.addWidget(self.tvu_p_text)
        self.thu_p_value = QtWidgets.QLineEdit()
        self.thu_p_value.setFixedWidth(60)
        self.thu_p_value.setValidator(QtGui.QDoubleValidator(0.00001, 1.0, 5, self.thu_p_value))
        self.thu_p_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.thu_p_value.setDisabled(True)
        self.thu_p_value.setText("0.0")
        hbox.addWidget(self.thu_p_value)
        hbox.addStretch()
        settings_vbox.addLayout(hbox)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch()
        formula_text = QtWidgets.QLabel("NOAA THU: ")
        formula_text.setFixedWidth(60)
        hbox.addWidget(formula_text)
        formula_label = QtWidgets.QLabel()
        formula_label.setPixmap(QtGui.QPixmap(os.path.join(self.media, 'thu_formula.png')))
        formula_label.setFixedWidth(120)
        hbox.addWidget(formula_label)
        hbox.addSpacing(6)
        self.noaa_thu_k_text = QtWidgets.QLabel("k")
        hbox.addWidget(self.noaa_thu_k_text)
        self.noaa_thu_k_value = QtWidgets.QLineEdit()
        self.noaa_thu_k_value.setFixedWidth(60)
        self.noaa_thu_k_value.setValidator(QtGui.QDoubleValidator(0.00001, 9999, 5, self.noaa_thu_k_value))
        self.noaa_thu_k_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.noaa_thu_k_value.setDisabled(True)
        self.noaa_thu_k_value.setText("5.0")
        hbox.addWidget(self.noaa_thu_k_value)
        self.noaa_thu_p_text = QtWidgets.QLabel(", p")
        hbox.addWidget(self.noaa_thu_p_text)
        self.noaa_thu_p_value = QtWidgets.QLineEdit()
        self.noaa_thu_p_value.setFixedWidth(60)
        self.noaa_thu_p_value.setValidator(QtGui.QDoubleValidator(0.00001, 1.0, 5, self.noaa_thu_p_value))
        self.noaa_thu_p_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.noaa_thu_p_value.setDisabled(True)
        self.noaa_thu_p_value.setText("0.05")
        hbox.addWidget(self.noaa_thu_p_value)
        hbox.addStretch()
        settings_vbox.addLayout(hbox)

        # ### Inputs ###

        self.inputs = QtWidgets.QGroupBox("Input")
        self.vbox.addWidget(self.inputs)
        inputs_vbox = QtWidgets.QVBoxLayout()
        self.inputs.setLayout(inputs_vbox)

        # Depth
        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch()
        depth_text = QtWidgets.QLabel("Depth [m]: ")
        hbox.addWidget(depth_text)
        self.depth_value = QtWidgets.QLineEdit()
        self.depth_value.setFixedWidth(60)
        self.depth_value.setValidator(QtGui.QDoubleValidator(-9999, 9999, 5, self.depth_value))
        self.depth_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.depth_value.setText("100.0")
        hbox.addWidget(self.depth_value)
        hbox.addSpacing(16)
        self.calculate = QtWidgets.QPushButton("Run")
        self.calculate.setFixedHeight(28)
        self.calculate.setFixedWidth(42)
        # noinspection PyUnresolvedReferences
        self.calculate.clicked.connect(self.on_calculate)
        hbox.addWidget(self.calculate)
        button = QtWidgets.QPushButton()
        hbox.addWidget(button)
        button.setFixedHeight(GuiSettings.single_line_height())
        button.setFixedWidth(GuiSettings.single_line_height())
        icon_info = QtCore.QFileInfo(os.path.join(self.media, 'small_info.png'))
        button.setIcon(QtGui.QIcon(icon_info.absoluteFilePath()))
        button.setToolTip('Open the manual page')
        button.setStyleSheet(GuiSettings.stylesheet_info_button())
        # noinspection PyUnresolvedReferences
        button.clicked.connect(self.click_open_manual)
        hbox.addStretch()
        inputs_vbox.addLayout(hbox)

        # ### Outputs ###

        self.outputs = QtWidgets.QGroupBox("Outputs")
        self.vbox.addWidget(self.outputs)
        outputs_vbox = QtWidgets.QVBoxLayout()
        self.outputs.setLayout(outputs_vbox)

        # IHO
        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch()

        self.iho_tpu_box = QtWidgets.QGroupBox("IHO")
        hbox.addWidget(self.iho_tpu_box)
        tvu_vbox = QtWidgets.QVBoxLayout()
        self.iho_tpu_box.setLayout(tvu_vbox)

        iho_tvu_hbox = QtWidgets.QHBoxLayout()
        tvu_vbox.addLayout(iho_tvu_hbox)
        iho_tvu_text = QtWidgets.QLabel("TVU: ")
        iho_tvu_text.setFixedWidth(50)
        iho_tvu_hbox.addWidget(iho_tvu_text)
        self.out_tvu_value = QtWidgets.QLineEdit()
        self.out_tvu_value.setFixedWidth(80)
        self.out_tvu_value.setValidator(QtGui.QDoubleValidator(0.00001, 9999, 5, self.out_tvu_value))
        self.out_tvu_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.out_tvu_value.setDisabled(True)
        iho_tvu_hbox.addWidget(self.out_tvu_value)

        iho_tvu_hbox = QtWidgets.QHBoxLayout()
        tvu_vbox.addLayout(iho_tvu_hbox)
        iho_thu_text = QtWidgets.QLabel("THU: ")
        iho_thu_text.setFixedWidth(50)
        iho_tvu_hbox.addWidget(iho_thu_text)
        self.out_thu_value = QtWidgets.QLineEdit()
        self.out_thu_value.setFixedWidth(80)
        self.out_thu_value.setValidator(QtGui.QDoubleValidator(0.00001, 9999, 5, self.out_thu_value))
        self.out_thu_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.out_thu_value.setDisabled(True)
        iho_tvu_hbox.addWidget(self.out_thu_value)

        self.noaa_tpu_box = QtWidgets.QGroupBox("NOAA")
        hbox.addWidget(self.noaa_tpu_box)
        noaa_vbox = QtWidgets.QVBoxLayout()
        self.noaa_tpu_box.setLayout(noaa_vbox)

        noaa_hbox = QtWidgets.QHBoxLayout()
        noaa_vbox.addLayout(noaa_hbox)
        noaa_tvu_text = QtWidgets.QLabel("TVU: ")
        noaa_tvu_text.setFixedWidth(50)
        noaa_hbox.addWidget(noaa_tvu_text)
        self.out_noaa_tvu_value = QtWidgets.QLineEdit()
        self.out_noaa_tvu_value.setFixedWidth(80)
        self.out_noaa_tvu_value.setValidator(QtGui.QDoubleValidator(0.00001, 9999, 5, self.out_noaa_tvu_value))
        self.out_noaa_tvu_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.out_noaa_tvu_value.setDisabled(True)
        noaa_hbox.addWidget(self.out_noaa_tvu_value)

        noaa_hbox = QtWidgets.QHBoxLayout()
        noaa_vbox.addLayout(noaa_hbox)
        noaa_thu_text = QtWidgets.QLabel("THU: ")
        noaa_thu_text.setFixedWidth(50)
        noaa_hbox.addWidget(noaa_thu_text)
        self.out_noaa_thu_value = QtWidgets.QLineEdit()
        self.out_noaa_thu_value.setFixedWidth(80)
        self.out_noaa_thu_value.setValidator(QtGui.QDoubleValidator(0.00001, 9999, 5, self.out_noaa_thu_value))
        self.out_noaa_thu_value.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.out_noaa_thu_value.setDisabled(True)
        noaa_hbox.addWidget(self.out_noaa_thu_value)

        hbox.addStretch()
        outputs_vbox.addLayout(hbox)

        # ### PLOTS ###

        # figure and canvas
        with rc_context(self.rc_context):

            self.f = Figure(figsize=self.f_sz, dpi=self.f_dpi)
            self.f.patch.set_alpha(0.0)
            self.c = FigureCanvas(self.f)
            self.c.setParent(self)
            self.c.setFocusPolicy(QtCore.Qt.ClickFocus)  # key for press events!!!
            self.c.setFocus()
            outputs_vbox.addWidget(self.c)

            # axes
            self.tvu_ax = self.f.add_subplot(211)
            self.tvu_ax.invert_yaxis()
            self.thu_ax = self.f.add_subplot(212, sharex=self.tvu_ax)
            self.thu_ax.invert_yaxis()

        # toolbar
        self.hbox = QtWidgets.QHBoxLayout()
        outputs_vbox.addLayout(self.hbox)
        # navigation
        self.nav = NavToolbar(canvas=self.c, parent=self.top_widget)
        self.hbox.addWidget(self.nav)

        self.on_first_draw()

    @classmethod
    def is_url(cls, value):
        if len(value) > 7:

            https = "https"
            if value[:len(https)] == https:
                return True

        return False

    @classmethod
    def is_darwin(cls):
        """ Check if the current OS is Mac OS """
        return sys.platform == 'darwin'

    @classmethod
    def is_linux(cls):
        """ Check if the current OS is Linux """
        return sys.platform in ['linux', 'linux2']

    @classmethod
    def is_windows(cls):
        """ Check if the current OS is Windows """
        return (sys.platform == 'win32') or (os.name is "nt")

    @classmethod
    def explore_folder(cls, path):
        """Open the passed path using OS-native commands"""
        if cls.is_url(path):
            import webbrowser
            webbrowser.open(path)
            return True

        if not os.path.exists(path):
            logger.warning('invalid path to folder: %s' % path)
            return False

        path = os.path.normpath(path)

        if cls.is_darwin():
            subprocess.call(['open', '--', path])
            return True

        elif cls.is_linux():
            subprocess.call(['xdg-open', path])
            return True

        elif cls.is_windows():
            subprocess.call(['explorer', path])
            return True

        logger.warning("Unknown/unsupported OS")
        return False

    @classmethod
    def click_open_manual(cls):
        logger.debug("open manual")
        cls.explore_folder("https://www.hydroffice.org/manuals/qctools/user_manual_info.html#uncertainty-calculator")

    @classmethod
    def calc_tvu(cls, a2, b, d):
        return math.sqrt(a2 + (b * d) * (b * d))

    @classmethod
    def calc_thu(cls, k, p, d):
        return k + p * d

    def _set_title(self):
        msg = "Total Propagated Uncertainty"
        self.f.suptitle(msg)

    def _draw_grid(self):
        for a in self.f.get_axes():
            a.grid(True)

    def on_first_draw(self):
        """Redraws the figure, it is only called at the first import!!!"""
        with rc_context(self.rc_context):
            # self._set_title()
            self._draw_tvu()
            self._draw_thu()
            self._draw_grid()
            self.is_drawn = True

    def _draw_tvu(self):
        logger.debug("draw tvu")

        with rc_context(self.rc_context):

            self.tvu_ax.clear()
            self.tvu_ax.set_xlabel('Depth [m]')
            self.tvu_ax.set_ylabel('TVU [m]')

            depth = float(self.depth_value.text())
            depth_min = 0.0
            depth_max = 2 * depth
            ds = list(np.arange(0.001, depth_max, depth / 1000))

            tvu_min = 0.0
            tvu_max = 0.0

            a = float(self.thu_a_value.text())
            b = float(self.thu_b_value.text())
            a2 = a*a
            tvus = [self.calc_tvu(a2, b, d) for d in ds]
            tvu_max = max(tvu_max, max(tvus) * 1.2)

            self.tvu_plot, = self.tvu_ax.plot(ds, tvus,
                                              color=self.iho_color,
                                              linestyle='--',
                                              label='TVU'
                                              )

            self.tvu_ax.set_xlim([depth_min, depth_max])
            self.tvu_ax.set_ylim([tvu_min, tvu_max])
            self.tvu_ax.legend(loc='upper right')

            try:
                tvu = float(self.out_tvu_value.text())
                self.tvu_ax.plot([depth], [tvu], marker='o', markersize=3, color="#C03714")

            except Exception:
                logger.info("skip TVU dot")

    def _draw_thu(self):
        logger.debug("draw thu")

        thu_min = 0.0
        thu_max = 0.0

        with rc_context(self.rc_context):

            self.thu_ax.clear()
            self.thu_ax.set_xlabel('Depth [m]')
            self.thu_ax.set_ylabel('THU [m]')

            depth = float(self.depth_value.text())
            depth_min = 0.0
            depth_max = 2*depth

            ds = list(np.arange(0.001, depth_max, depth/1000))

            k = float(self.thu_k_value.text())
            p = float(self.thu_p_value.text())
            thus = [self.calc_thu(k, p, d) for d in ds]
            thu_max = max(thu_max, max(thus) * 1.2)

            self.thu_plot, = self.thu_ax.plot(ds, thus,
                                              color=self.iho_color,
                                              linestyle='--',
                                              label='IHO THU'
                                              )

            k = float(self.noaa_thu_k_value.text())
            p = float(self.noaa_thu_p_value.text())
            thus = [self.calc_thu(k, p, d) for d in ds]
            thu_max = max(thu_max, max(thus) * 1.2)

            self.thu_plot, = self.thu_ax.plot(ds, thus,
                                              color=self.noaa_color,
                                              linestyle=':',
                                              label='NOAA THU'
                                              )

            self.thu_ax.set_xlim([depth_min, depth_max])
            self.thu_ax.set_ylim([thu_min, thu_max])

            self.thu_ax.legend(loc='upper right')

            try:
                thu = float(self.out_noaa_thu_value.text())
                self.thu_ax.plot([depth], [thu], marker='o', markersize=3, color="blue")
                thu = float(self.out_thu_value.text())
                self.thu_ax.plot([depth], [thu], marker='o', markersize=3, color="#C03714")

            except Exception:
                logger.info("skip THU dot")

    def on_tvu_changed(self):
        if self.toggle_order.value() == 0:
            logger.debug("TVU special order")
            self.thu_a_value.setText("0.25")
            self.thu_b_value.setText("0.0075")
            self.thu_k_value.setText("2.0")
            self.thu_p_value.setText("0.0")

        elif self.toggle_order.value() == 1:
            logger.debug("TVU order 1a/b")
            self.thu_a_value.setText("0.5")
            self.thu_b_value.setText("0.013")
            self.thu_k_value.setText("5.0")
            self.thu_p_value.setText("0.05")

        elif self.toggle_order.value() == 2:
            logger.debug("TVU order 2")
            self.thu_a_value.setText("1.0")
            self.thu_b_value.setText("0.023")
            self.thu_k_value.setText("20.0")
            self.thu_p_value.setText("0.1")

        else:
            logger.warning("unknown TVU order")
            return

        self.on_calculate()

    def on_calculate(self):
        logger.debug("calculate")

        a = float(self.thu_a_value.text())
        b = float(self.thu_b_value.text())
        a2 = a * a
        noaa_a = float(self.thu_a_value.text())
        noaa_b = float(self.thu_b_value.text())
        noaa_a2 = noaa_a * noaa_a
        k = float(self.thu_k_value.text())
        p = float(self.thu_p_value.text())
        noaa_k = float(self.noaa_thu_k_value.text())
        noaa_p = float(self.noaa_thu_p_value.text())
        depth = float(self.depth_value.text())
        self.out_tvu_value.setText("%.3f" % self.calc_tvu(a2, b, depth))
        self.out_noaa_tvu_value.setText("%.3f" % self.calc_tvu(noaa_a2, noaa_b, depth))
        self.out_thu_value.setText("%.3f" % self.calc_thu(k, p, depth))
        self.out_noaa_thu_value.setText("%.3f" % self.calc_thu(noaa_k, noaa_p, depth))

        self._draw_tvu()
        self._draw_thu()
        self._draw_grid()
        self.c.draw()
Ejemplo n.º 25
0
def plot_GAaRatio(gases, num, savepath, ident="", save=False, pyplot=False):
    """
    plot alveolo-arterial gradiant
    input :
        gases = list of bg.Gas,
        num = location in the list
        path = path to save
        ident = string to identify in the save name
        save : boolean
        pyplot : boolean (True: retrun a pyplot,    else a Figure obj)
    output : plot (pyplot or FigureObj)
    """
    if num > 1:
        gas = gases[num]
    else:
        gas = gases[0]
    casc = gas.casc()
    gAa = casc[-2] - casc[-1]
    ratio = gas.po2 / gas.fio2

    if pyplot:
        fig = plt.figure(figsize=(14, 6))
    else:
        fig = Figure(figsize=(14, 6))
    #    fig.suptitle("quantification du passage alvéolo-capillaire")
    ax = fig.add_subplot(211)
    ax.axhline(0, color="tab:grey")
    ax.plot(
        [5, 15],
        [0, 0],
        label="line 1",
        linewidth=4,
        color="tab:blue",
        marker="d",
        markersize=10,
    )
    ax.plot(
        [gAa], [0], "v-", color="tab:red", markersize=32, markeredgecolor="k"
    )  # GAa
    st = "gradient alvéolo-artériel ($PA_{O_2} - Pa_{O_2}$)"
    ax.set_title(st, y=0.8, backgroundcolor="w", color="tab:gray")
    print("gAa=", gAa)
    if gAa > 25:
        ax.set_xlim([0, gAa + 10])
    else:
        ax.set_xlim([0, 30])

    ax = fig.add_subplot(212)
    ax.axhline(0, color="tab:grey")
    ax.plot(
        [100, 200],
        [0, 0],
        "tab:red",
        label="line 1",
        linewidth=2,
        marker="d",
        markersize=10,
    )
    ax.plot(
        [200, 300],
        [0, 0],
        "tab:orange",
        label="line 1",
        linewidth=4,
        marker="d",
        markersize=10,
    )
    ax.plot(
        [300, 500],
        [0, 0],
        "tab:blue",
        label="line 1",
        linewidth=6,
        marker="d",
        markersize=10,
    )
    ax.plot([ratio], [0], "rv-", markersize=32, markeredgecolor="k")  # ratio
    st = "ratio $ Pa_{O_2} / Fi_{O_2}$"
    ax.set_title(st, y=0.8, backgroundcolor="w", color="tab:gray")
    ax.plot(300, 0, "tab:blue", label="line 1", linewidth=1)
    ax.text(
        150, -0.025, r"ALI", fontsize=18, color="tab:red", horizontalalignment="center"
    )
    ax.text(
        250,
        -0.025,
        r"ARDS",
        fontsize=18,
        color="tab:orange",
        horizontalalignment="center",
    )
    ax.text(
        400,
        -0.025,
        r"norme",
        fontsize=18,
        color="tab:blue",
        horizontalalignment="center",
    )
    for ax in fig.get_axes():
        ax.axes.tick_params(colors="tab:gray")
        ax.get_yaxis().set_visible(False)
        # ax.grid(True)
        for spine in ["left", "top", "right", "bottom"]:
            ax.spines[spine].set_visible(False)
    # fig.set.tight_layout(True)
    if pyplot:
        plt.show()
        if save:
            name = os.path.join(savepath, (str(ident) + "GAaRatio"))
            name = os.path.expanduser(name)
            saveGraph(name, ext="png", close=True, verbose=True)
    return fig
Ejemplo n.º 26
0
class MyWidget(QWidget):

    # #{ init

    def __init__(self):
        super(MyWidget, self).__init__()
        self.brd = CvBridge()
        self.view = RGB
        ui_file = os.path.join(
            rospkg.RosPack().get_path('balloon_color_picker'), 'resource',
            'ColorPlugin.ui')
        rospy.loginfo('uifile {}'.format(ui_file))
        # self.log_info('uifile {}'.format(ui_file))
        loadUi(ui_file, self)
        # Give QObjects reasonable names
        self.setObjectName('ColorPluginUi')
        self.uav_name = os.environ['UAV_NAME']

        self.orig_h = 920
        self.orig_w = 1080
        self.hist_hsv_orig_h = 180
        self.hist_hsv_orig_w = 256
        self.hist_lab_orig_h = 256
        self.hist_lab_orig_w = 256
        self.hist_status = HSV
        self.select_status = HIST_SELECTION
        self.crop_stat = IMG
        self.hist_mask = np.zeros([self.hist_hsv_orig_h, self.hist_hsv_orig_w])
        self.hist_mask_lab = np.zeros(
            [self.hist_lab_orig_h, self.hist_lab_orig_w])
        self.cur_hist_hs = None
        self.cur_hist_ab = None
        self.selected_count = 0
        # ROS services

        # #{ ros services

        self.sigma_caller = rospy.ServiceProxy('change_sigma', ChangeSigma)
        self.sigma_lab_caller = rospy.ServiceProxy('change_sigma_lab',
                                                   ChangeSigmaLab)
        self.caller = rospy.ServiceProxy('capture', Capture)
        self.capture_cropped_srv = rospy.ServiceProxy('capture_cropped',
                                                      CaptureCropped)
        self.get_count = rospy.ServiceProxy('get_count', GetCount)
        self.clear_count = rospy.ServiceProxy('clear_count', ClearCount)
        self.get_config = rospy.ServiceProxy('get_config', GetConfig)
        self.get_params = rospy.ServiceProxy('get_params', Params)
        self.freeze_service = rospy.ServiceProxy('freeze', Freeze)
        self.update_service = rospy.ServiceProxy('change_obd', UpdateObd)
        self.change_callback = rospy.ServiceProxy('change_callback',
                                                  ChangeCallback)
        self.capture_hist = rospy.ServiceProxy('capture_hist', CaptureHist)

        # #} end of ros services        rospy.wait_for_service('capture')

        rospy.loginfo(
            'waiting for service "get_params" from computation module \n if this is more than 5 secs check for topic remapping'
        )
        # self.log_info('waiting for service')
        rospy.loginfo('uav_name {}'.format(os.environ['UAV_NAME']))
        # self.log_info('uav_name {}'.format(os.environ['UAV_NAME']))
        rospy.wait_for_service('get_params')

        self.config_path, self.save_path, self.circled_param, self.circle_filter_param, self.circle_luv_param, self.object_detect_param, self.save_to_drone = self.set_params(
        )
        # SUBS
        # #{ ros subs

        self.balloon_sub = rospy.Subscriber(self.circled_param,
                                            RosImg,
                                            self.img_callback,
                                            queue_size=1)
        self.filter_sub = rospy.Subscriber(self.circle_filter_param,
                                           RosImg,
                                           self.filter_callback,
                                           queue_size=1)
        self.filter_luv = rospy.Subscriber(self.circle_luv_param,
                                           RosImg,
                                           self.luv_callback,
                                           queue_size=1)
        self.obj_det_sb = rospy.Subscriber(self.object_detect_param,
                                           RosImg,
                                           self.obj_det_callback,
                                           queue_size=1)
        # self.hsv = message_filters.Subscriber(self.circle_filter_param, RosImg)
        # self.luv = message_filters.Subscriber(self.circle_luv_param, RosImg)

        # self.ts = message_filters.ApproximateTimeSynchronizer([self.luv,  self.hsv], 1, 0.5)
        # self.ts.registerCallback(self.both_callback)

        # #} end of ros subs

        self.colors = self.load_config(self.config_path)
        self.add_buttons(self.colors)

        # # DEFAULT IMAGE
        # img = cv2.imread('/home/mrs/balloon_workspace/src/ros_packages/balloon_color_picker/data/blue.png')

        # cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

        # h,w,c = img.shape
        # q_img = QImage(img.data, w,h,3*w, QImage.Format_RGB888)

        # q = QPixmap.fromImage(q_img)

        #DIRECTORY
        #default
        self.directory.setText(self.save_path + "Red.yaml")
        self.color_name = "red"
        self.save_button.clicked.connect(self.save_config)

        #PLOT

        self.figure = Figure()
        self.figure_luv = Figure()
        self.canvas = FigureCanvas(self.figure)
        self.canvas_luv = FigureCanvas(self.figure_luv)
        self.canvas.setParent(self.inner)
        # self.toolbar = NavigationToolbar(self.canvas,self)
        # self.toolbar_luv = NavigationToolbar(self.canvas_luv,self)
        # self.toolbar.setParent(self.inner)
        # self.toolbar_luv.setParent(self.inner_luv)
        self.canvas_luv.setParent(self.inner_luv)
        self.inner_luv.hide()
        self.inner_luv_hist.hide()

        #SLIDER CONFIG

        # #{ slider config

        self.sigma_slider.setRange(0, 100)
        self.sigma_slider.setSingleStep(1)
        self.sigma_slider.setValue(6)

        self.sigma_slider.valueChanged.connect(self.slider_event)

        self.sigma_slider_s.setRange(0, 100)
        self.sigma_slider_s.setSingleStep(1)
        self.sigma_slider_s.setValue(6)

        self.sigma_slider_s.valueChanged.connect(self.slider_event)

        self.sigma_slider_v.setRange(0, 100)
        self.sigma_slider_v.setSingleStep(1)
        self.sigma_slider_v.setValue(80)

        self.sigma_slider_v.valueChanged.connect(self.slider_event)

        self.sigma_slider_lab.setRange(0, 100)
        self.sigma_slider_lab.setSingleStep(1)
        self.sigma_slider_lab.setValue(80)

        self.sigma_slider_lab.valueChanged.connect(self.slider_event_lab)

        self.sigma_slider_a.setRange(0, 100)
        self.sigma_slider_a.setSingleStep(1)
        self.sigma_slider_a.setValue(6)

        self.sigma_slider_a.valueChanged.connect(self.slider_event_lab)

        self.sigma_slider_b.setRange(0, 100)
        self.sigma_slider_b.setSingleStep(1)
        self.sigma_slider_b.setValue(6)

        self.sigma_slider_b.valueChanged.connect(self.slider_event_lab)

        # #} end of slider config

        # #{ font configs

        #SIGMA TEXT
        font = self.font()
        font.setPointSize(16)
        self.sigma_value.setFont(font)
        self.sigma_value_s.setFont(font)
        self.sigma_value_v.setFont(font)
        self.sigma_value_lab.setFont(font)
        self.sigma_value_a.setFont(font)
        self.sigma_value_b.setFont(font)
        #IMAGE COUNT TEXT
        self.image_count.setFont(font)
        #BOX FOR BUTTONS font
        # self.color_buttons.setFont(font)
        font.setPointSize(14)
        self.sigma_value.setFont(font)
        self.log_text.setFont(font)

        #LAB HSV TEXT
        font.setPointSize(23)
        self.label_lab.setFont(font)
        self.label_lab.hide()
        self.label_hsv.setFont(font)
        self.label_hsv.hide()

        # #} end of font configs

        # BUTTONS
        self.change.clicked.connect(self.switch_view_hsv)
        self.change_both.clicked.connect(self.switch_view_both)
        self.change_luv.clicked.connect(self.switch_view_luv)
        self.change_object.clicked.connect(self.switch_view_object_detect)
        self.capture_button.clicked.connect(self.capture)
        self.clear_button.clicked.connect(self.clear)
        self.freeze_button.clicked.connect(self.freeze)
        self.update_button.clicked.connect(self.update_obd)
        # self.wdg_img.setPixmap(q)
        # self.box_layout.addWidget(self.toolbar)
        # self.inner.box_layout.addWidget(self.canvas)
        #shortcuts

        self.short_capture = QShortcut(QKeySequence("C"), self)
        self.short_capture.activated.connect(self.capture)
        self.short_hsv = QShortcut(QKeySequence("1"), self)
        self.short_hsv.activated.connect(self.switch_view_hsv)
        self.short_lab = QShortcut(QKeySequence("2"), self)
        self.short_lab.activated.connect(self.switch_view_luv)
        self.short_object_detect = QShortcut(QKeySequence("3"), self)
        self.short_object_detect.activated.connect(
            self.switch_view_object_detect)
        self.short_object_detect_update = QShortcut(QKeySequence("U"), self)
        self.short_object_detect_update.activated.connect(self.update_obd)
        self.short_both = QShortcut(QKeySequence("4"), self)
        self.short_both.activated.connect(self.switch_view_both)
        self.short_save = QShortcut(QKeySequence("S"), self)
        self.short_save.activated.connect(self.save_config)
        self.short_clear = QShortcut(QKeySequence("N"), self)
        self.short_clear.activated.connect(self.clear)
        self.short_freeze = QShortcut(QKeySequence("F"), self)
        self.short_freeze.activated.connect(self.freeze)

        vbx = QVBoxLayout()
        but_hsv = QRadioButton()
        but_hsv.setText('HSV')
        but_hsv.setChecked(True)
        self.color_space = 'HSV'
        but_hsv.clicked.connect(self.set_colorspace_hsv)
        vbx.addWidget(but_hsv)
        but_lab = QRadioButton()
        but_lab.setText('LAB')
        but_lab.clicked.connect(self.set_colorspace_lab)
        vbx.addWidget(but_lab)
        vbx.addStretch(1)

        self.radio_buttons.setLayout(vbx)
        vbx_method = QVBoxLayout()
        but_lut = QRadioButton()
        but_lut.setText('LUT')
        but_lut.setChecked(False)
        but_lut.clicked.connect(self.set_method_lut)
        vbx_method.addWidget(but_lut)

        but_thr = QRadioButton()
        but_thr.setText('Threshold')
        but_thr.setChecked(True)
        but_thr.clicked.connect(self.set_method_thr)
        vbx_method.addWidget(but_thr)
        vbx.addStretch(1)

        self.radio_buttons_method.setLayout(vbx_method)

        self.load_method = 'THR'

        # self.mousePressEvent.connect(self.mousePressEvent)
        self.plotted = False

        self._rubber = None

        self.frozen = False

# #} end of init

# #{ mouse events

    def mousePressEvent(self, QMouseEvent):
        cursor = QCursor()
        x = QMouseEvent.x()
        y = QMouseEvent.y()
        if (x < 1280 and y < 720) and (x > 15 and y > 15):
            if self._rubber == None:
                if not self.frozen:
                    self.freeze()
                    self.frozen_before = False
                else:
                    self.frozen_before = True
                self.rub_origin = QMouseEvent.pos()
                self._rubber = QRubberBand(QRubberBand.Rectangle, self)
                self._rubber.show()
                self.crop_stat = IMG
        elif (x > 1300 and y > 520) and (x < 1907 and y < 1010):
            self.rub_origin = QMouseEvent.pos()
            self._rubber = QRubberBand(QRubberBand.Rectangle, self)
            self._rubber.show()
            if QMouseEvent.button(
            ) == Qt.RightButton and self.selected_count != 0:
                self.select_status = HIST_DESELECTION
            else:
                self.select_status = HIST_SELECTION
            self.crop_stat = HIST
            self.log_info("hist status {}".format("HSV" if self.hist_status ==
                                                  HSV else "LAB"))

    def mouseMoveEvent(self, QMouseEvent):
        cursor = QCursor()
        x = QMouseEvent.x()
        y = QMouseEvent.y()
        # if in coords of image and crop status is image then draw the rect
        if self._rubber is None:
            return
        if (x < 1280 and y < 720) and (x > 15
                                       and y > 15) and self.crop_stat == IMG:
            self._rubber.setGeometry(
                QRect(self.rub_origin, QMouseEvent.pos()).normalized())
        # if in coords of hist and crop status is hist then draw the rect
        elif (x > 1300 and y > 520) and (x < 1907 and
                                         y < 1010) and self.crop_stat == HIST:
            self._rubber.setGeometry(
                QRect(self.rub_origin, QMouseEvent.pos()).normalized())

    def mouseReleaseEvent(self, QMouseEvent):
        cursor = QCursor()
        x = QMouseEvent.x()
        y = QMouseEvent.y()
        if self._rubber is None:
            return
        if (x < 1280 and y < 720) and (x > 15
                                       and y > 15) and self.crop_stat == IMG:

            if not self.frozen_before:
                self.freeze()

            a = self.mapToGlobal(self.rub_origin)
            b = QMouseEvent.globalPos()
            a = self.wdg_img.mapFromGlobal(a)
            b = self.wdg_img.mapFromGlobal(b)

            self._rubber.hide()
            self._rubber = None

            pix = QPixmap(self.wdg_img.pixmap())
            sx = float(self.wdg_img.rect().width())
            sy = float(self.wdg_img.rect().height())

            # h 1080 w 1920
            sx = self.orig_w / sx
            sy = self.orig_h / sy

            a.setX(int(a.x() * sx))
            a.setY(int(a.y() * sy))

            b.setX(int(b.x() * sx))
            b.setY(int(b.y() * sy))
            rect_ = QRect(a, b)

            h_ = rect_.height()
            w_ = rect_.width()

            y1, x1, y2, x2 = rect_.getCoords()
            rospy.loginfo('Img cropped x1 {} y1 {} x2 {} y2{}'.format(
                x1, y1, x2, y2))
            self.log_info('Img cropped x1 {} y1 {} x2 {} y2{}'.format(
                x1, y1, x2, y2))
            self.capture_cropped(x1, y1, x2, y2)
        elif (x > 1300 and y > 520) and (x < 1907 and
                                         y < 1010) and self.crop_stat == HIST:

            # h 1080 w 1920

            if self.hist_status == HSV:
                cur_hist = self.inner_hist
            elif self.hist_status == LUV:
                cur_hist = self.inner_luv_hist

        # if not self.frozen_before:
        #     self.freeze()
            a = self.mapToGlobal(self.rub_origin)
            b = QMouseEvent.globalPos()
            a = cur_hist.mapFromGlobal(a)
            b = cur_hist.mapFromGlobal(b)

            self._rubber.hide()
            self._rubber = None

            pix = QPixmap(cur_hist.pixmap())
            sx = float(cur_hist.rect().width())
            sy = float(cur_hist.rect().height())

            # h 1080 w 1920
            if self.hist_status == HSV:
                sx = self.hist_hsv_orig_w / sx
                sy = self.hist_hsv_orig_h / sy
            elif self.hist_status == LUV:
                sx = self.hist_lab_orig_w / sx
                sy = self.hist_lab_orig_h / sy

            a.setX(int(a.x() * sx))
            a.setY(int(a.y() * sy))

            b.setX(int(b.x() * sx))
            b.setY(int(b.y() * sy))
            rect_ = QRect(a, b)

            h_ = rect_.height()
            w_ = rect_.width()

            # y1,x1,y2,x2 = rect_.getCoords()
            x1, y1, x2, y2 = rect_.getCoords()
            rospy.loginfo('Hist cropped x1 {} y1 {} x2 {} y2 {}'.format(
                x1, y1, x2, y2))
            self.log_info('Hist cropped x1 {} y1 {} x2 {} y2 {}'.format(
                x1, y1, x2, y2))
            if self.select_status == HIST_SELECTION:
                self.select_hist(x1, y1, x2, y2, h_, w_, self.hist_status)
            elif self.select_status == HIST_DESELECTION:
                self.deselect_hist(x1, y1, x2, y2, self.hist_status)

        else:
            if self._rubber is not None:
                self._rubber.hide()
                self._rubber = None
                self.crop_stat = 0

# #} end of mouse events

# #{ set_colorspaces

    def set_colorspace_hsv(self):
        self.color_space = 'HSV'
        self.inner.show()
        self.inner_hist.show()
        self.hist_status = HSV
        self.log_info("hist status {}".format("HSV"))
        self.inner_luv.hide()
        self.inner_luv_hist.hide()
        if self.view == HSV:
            self.view = RGB
            self.set_view(self.view)
            return
        self.view = HSV
        self.log_info('HSV from radio button')
        self.set_view(self.view)

    def set_colorspace_lab(self):
        self.color_space = 'LAB'
        # but_lab.setChecked(True)
        self.inner_luv.show()
        self.inner_luv_hist.show()
        self.inner.hide()
        self.inner_hist.hide()
        self.hist_status = LUV
        self.log_info("hist status {}".format("LAB"))
        if self.view == LUV:
            self.view = RGB
            self.set_view(self.view)
            return
        rospy.loginfo('LAB from radio button')
        self.log_info('LAB from radio button')
        self.view = LUV
        self.set_view(self.view)

    def set_method_lut(self):
        self.load_method = 'LUT'
        self.log_info('Set method LUT')

    def set_method_thr(self):
        self.load_method = 'THR'
        self.log_info('Set method THR')

# #} end of set_colorspaces

# #{ plot

    def plot(self, h, s, v, l, u, lv, means, sigmas):
        self.mean_h, self.mean_s, self.mean_v, self.mean_l, self.mean_u, self.mean_lv = means
        self.std_h, self.std_s, self.std_v, self.std_l, self.std_u, self.std_lv = sigmas

        self.figure.suptitle('HSV', fontsize=20)
        ax = self.figure.add_subplot(221)
        ax.clear()
        ax.hist(h, normed=True)
        ax.set_title('H', fontsize=16)
        ax.set_xlim([0, 180])
        xmin, xmax = (0, 180)
        x = np.linspace(xmin, xmax, 180)
        y = norm.pdf(x, self.mean_h, self.std_h)
        ax.plot(x, y)
        #thresholds
        val = float(self.sigma_slider.value()) / 2

        ax.axvline(self.mean_h + self.std_h * val, color='r')
        ax.axvline(self.mean_h - self.std_h * val, color='g')

        sx = self.figure.add_subplot(222)
        sx.clear()
        sx.hist(s, normed=True)
        sx.set_title('S', fontsize=16)
        amin, xmax = (0, 255)
        sx.set_xlim([0, 255])
        x = np.linspace(xmin, xmax, 255)
        y = norm.pdf(x, self.mean_s, self.std_s)
        sx.plot(x, y)

        #thresholds
        val = float(self.sigma_slider_s.value()) / 2

        sx.axvline(self.mean_s + self.std_s * val, color='r')
        sx.axvline(self.mean_s - self.std_s * val, color='g')

        vx = self.figure.add_subplot(223)
        vx.clear()
        vx.hist(v, normed=True)
        vx.set_title('V', fontsize=16)
        xmin, xmax = (0, 255)
        vx.set_xlim([0, 255])
        # vx.set_ylim([0,max(v)])
        x = np.linspace(xmin, xmax, 255)
        y = norm.pdf(x, self.mean_v, self.std_v)
        vx.plot(x, y)

        #thresholds
        val = float(self.sigma_slider_v.value()) / 2

        vx.axvline(self.mean_v + self.std_v * val, color='r', ymax=1)
        vx.axvline(self.mean_v - self.std_v * val, color='g', ymax=1)

        # refresh canvas
        self.canvas.draw()

        self.figure_luv.suptitle('LAB', fontsize=20)
        ax = self.figure_luv.add_subplot(221)
        ax.clear()
        ax.set_title('L', fontsize=16)
        ax.hist(l, normed=True)
        xmin, xmax = (0, 255)
        ax.set_xlim([0, 255])
        x = np.linspace(xmin, xmax, 225)
        y = norm.pdf(x, self.mean_l, self.std_l)
        ax.plot(x, y)
        #thresholds
        val = float(self.sigma_slider_lab.value()) / 2

        ax.axvline(self.mean_l + self.std_l * val, color='r')
        ax.axvline(self.mean_l - self.std_l * val, color='g')

        sx = self.figure_luv.add_subplot(222)
        sx.clear()
        sx.set_title('A', fontsize=16)
        sx.hist(u, normed=True)
        xmin, xmax = (0, 256)
        x = np.linspace(xmin, xmax, 256)
        sx.set_xlim([0, 256])
        y = norm.pdf(x, self.mean_u, self.std_u)
        sx.plot(x, y)
        #thresholds
        val = float(self.sigma_slider_a.value()) / 2

        sx.axvline(self.mean_u + self.std_u * val, color='r')
        sx.axvline(self.mean_u - self.std_u * val, color='g')

        vx = self.figure_luv.add_subplot(223)
        vx.clear()
        vx.set_title('B', fontsize=16)
        vx.hist(lv, normed=True)
        xmin, xmax = (0, 223)
        vx.set_xlim([0, 223])
        x = np.linspace(xmin, xmax, 223)
        y = norm.pdf(x, self.mean_lv, self.std_lv)
        vx.plot(x, y)

        #thresholds
        val = float(self.sigma_slider_b.value()) / 2

        vx.axvline(self.mean_lv + self.std_lv * val, color='r')
        vx.axvline(self.mean_lv - self.std_lv * val, color='g')

        # refresh canvas
        self.canvas_luv.draw()
        self.plotted = True

# #} end of plot

# #{ update_plots

    def update_plots(self):
        ax = self.figure.get_axes()[0]
        sx = self.figure.get_axes()[1]
        vx = self.figure.get_axes()[2]
        # print(ax.lines)

        #thresholds
        del ax.lines[len(ax.lines) - 1]
        del ax.lines[len(ax.lines) - 1]

        up = self.mean_h + self.std_h * self.sigma_h
        if up > 180:
            up -= 180
        down = self.mean_h - self.std_h * self.sigma_h
        if down < 0:
            down += 180
        ax.axvline(up, color='r')
        ax.axvline(down, color='g')

        #thresholds

        del sx.lines[len(sx.lines) - 1]
        del sx.lines[len(sx.lines) - 1]
        sx.axvline(self.mean_s + self.std_s * self.sigma_s, color='r')
        sx.axvline(self.mean_s - self.std_s * self.sigma_s, color='g')

        #thresholds

        del vx.lines[len(vx.lines) - 1]
        del vx.lines[len(vx.lines) - 1]
        vx.axvline(self.mean_v + self.std_v * self.sigma_v, color='r', ymax=1)
        vx.axvline(self.mean_v - self.std_v * self.sigma_v, color='g', ymax=1)

        self.canvas.draw()

# #} end of update_plots

# #{ update_plots_lab

    def update_plots_lab(self):
        # refresh canvas
        ax = self.figure_luv.get_axes()[0]
        sx = self.figure_luv.get_axes()[1]
        vx = self.figure_luv.get_axes()[2]
        # print(ax.lines)
        #thresholds
        del ax.lines[len(ax.lines) - 1]
        del ax.lines[len(ax.lines) - 1]

        ax.axvline(self.mean_l + self.std_l * self.sigma_l, color='r')
        ax.axvline(self.mean_l - self.std_l * self.sigma_l, color='g')

        #thresholds
        del sx.lines[len(sx.lines) - 1]
        del sx.lines[len(sx.lines) - 1]

        sx.axvline(self.mean_u + self.std_u * self.sigma_a, color='r')
        sx.axvline(self.mean_u - self.std_u * self.sigma_a, color='g')

        #thresholds
        del vx.lines[len(vx.lines) - 1]
        del vx.lines[len(vx.lines) - 1]

        vx.axvline(self.mean_lv + self.std_lv * self.sigma_b, color='r')
        vx.axvline(self.mean_lv - self.std_lv * self.sigma_b, color='g')

        # refresh canvas
        self.canvas_luv.draw()

# #} end of update_plots_lab

# #{ img_callback

    def img_callback(self, data):
        if self.view != RGB:
            return
        img = self.brd.imgmsg_to_cv2(data, 'rgb8')
        h, w, c = img.shape
        self.orig_h = h
        self.orig_w = w
        # rospy.loginfo('h {} w {}'.format(h,w))
        # cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

        img = cv2.resize(img, dsize=(1280, 720), interpolation=cv2.INTER_CUBIC)
        h, w, c = img.shape
        q_img = QImage(img.data, w, h, 3 * w, QImage.Format_RGB888)

        q = QPixmap.fromImage(q_img)
        self.wdg_img.setFixedWidth(1280)
        self.wdg_img.setFixedHeight(720)
        self.wdg_img.setPixmap(q)

# #} end of img_callback

# #{ clear

    def clear(self):
        self.figure.clf()
        self.clear_count()
        self.image_count.setText('Samples: 0 ')
        print("cleared")
        self.log_info("cleared")

# #} end of clear

# #{ filter callback

    def filter_callback(self, data):
        if self.view != HSV:
            return
        img = self.brd.imgmsg_to_cv2(data, 'rgb8')
        # cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

        img = cv2.resize(img, dsize=(1280, 720), interpolation=cv2.INTER_CUBIC)
        h, w, c = img.shape

        q_img = QImage(img.data, w, h, 3 * w, QImage.Format_RGB888)

        q = QPixmap.fromImage(q_img)

        self.wdg_img.setFixedWidth(1280)
        self.wdg_img.setFixedHeight(720)
        self.wdg_img.setPixmap(q)

# #} end of filter callback

# #{ luv_callback

    def luv_callback(self, data):
        if self.view != LUV:
            return
        img = self.brd.imgmsg_to_cv2(data, 'rgb8')
        # cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

        img = cv2.resize(img, dsize=(1280, 720), interpolation=cv2.INTER_CUBIC)
        h, w, c = img.shape
        # rospy.loginfo('shape {}'.format(img.shape))

        q_img = QImage(img.data, w, h, 3 * w, QImage.Format_RGB888)

        q = QPixmap.fromImage(q_img)
        # self.wdg_img.setFixedWidth(w)
        # self.wdg_img.setFixedHeight(h)

        self.wdg_img.setPixmap(q)

# #} end of luv_callback

# #{ object_detect_callback

    def obj_det_callback(self, data):
        if self.view != OBD:
            return
        if self.frozen:
            return
        img = self.brd.imgmsg_to_cv2(data, 'rgb8')
        # cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

        img = cv2.resize(img, dsize=(1280, 720), interpolation=cv2.INTER_CUBIC)
        h, w, c = img.shape
        q_img = QImage(img.data, w, h, 3 * w, QImage.Format_RGB888)

        q = QPixmap.fromImage(q_img)
        self.wdg_img.setFixedWidth(w)
        self.wdg_img.setFixedHeight(h)
        self.wdg_img.setPixmap(q)

# #} end of luv_callback

# #{ both_callback

    def both_callback(self, luv, hsv):
        if self.view != BOTH:
            self.label_hsv.hide()
            self.label_lab.hide()
            return
        img_luv = self.brd.imgmsg_to_cv2(luv)
        img_hsv = self.brd.imgmsg_to_cv2(hsv)
        # cv2.cvtColor(img_luv, cv2.COLOR_BGR2RGB)
        # cv2.cvtColor(img_hsv, cv2.COLOR_BGR2RGB)

        h, w, c = img_luv.shape
        img = np.zeros([h, w, c])
        luv_2 = cv2.resize(img_luv, (0, 0), fx=0.5, fy=0.5)
        hsv_2 = cv2.resize(img_hsv, (0, 0), fx=0.5, fy=0.5)
        both = np.hstack((hsv_2, luv_2))
        dif = (img.shape[0] - both.shape[0]) // 2
        img[dif:img.shape[0] - dif, 0:img.shape[1]] = both
        q_img = QImage(both.data, both.shape[1], both.shape[0],
                       3 * both.shape[1], QImage.Format_RGB888)

        q = QPixmap.fromImage(q_img)
        self.wdg_img.setFixedWidth(both.shape[1])
        self.wdg_img.setFixedHeight(both.shape[0])
        self.wdg_img.setPixmap(q)

# #} end of both_callback

# #{ slider_event_hsv

    def slider_event(self):

        self.sigma_h = float(self.sigma_slider.value()) / 2
        self.sigma_s = float(self.sigma_slider_s.value()) / 2
        self.sigma_v = float(self.sigma_slider_v.value()) / 2
        res = self.sigma_caller(self.sigma_h, self.sigma_s, self.sigma_v)

        if len(self.figure.get_axes()) > 0:
            rospy.loginfo('axes {}'.format(self.figure.get_axes()))
            self.log_info('axes {}'.format(self.figure.get_axes()))
            self.update_plots()

        self.sigma_value.setText('Sigma H value: {}'.format(self.sigma_h))
        self.sigma_value_s.setText('Sigma S value: {}'.format(self.sigma_s))
        self.sigma_value_v.setText('Sigma V value: {}'.format(self.sigma_v))

# #} end of slider_event_hsv

# #{ slider_event_lab

    def slider_event_lab(self):

        self.sigma_l = float(self.sigma_slider_lab.value()) / 2
        self.sigma_a = float(self.sigma_slider_a.value()) / 2
        self.sigma_b = float(self.sigma_slider_b.value()) / 2
        if len(self.figure_luv.get_axes()) > 0:
            self.update_plots_lab()
        # rospy.loginfo('value {}'.format(self.sigma_l))
        # self.log_info('value {}'.format(self.sigma_l))
        self.sigma_lab_caller(self.sigma_l, self.sigma_a, self.sigma_b)
        self.sigma_value_lab.setText('Sigma L value: {}'.format(self.sigma_l))
        self.sigma_value_a.setText('Sigma A value: {}'.format(self.sigma_a))
        self.sigma_value_b.setText('Sigma B value: {}'.format(self.sigma_b))

# #} end of slider_event_lab

# #{ capture

    def capture(self):

        rospy.wait_for_service('capture')
        req = Capture()
        res = self.caller()
        # rospy.loginfo('response {}'.format(res))
        # self.log_info('response {}'.format(res))
        self.plot(res.h, res.s, res.v, res.l, res.u, res.lv, res.means,
                  res.sigmas)
        self.image_count.setText('Samples taken: {} '.format(res.count))
        return

# #} end of capture

# #{ capture_cropped

    def capture_cropped(self, x1, y1, x2, y2):
        t = time.time()

        res = self.capture_cropped_srv(x1, y1, x2, y2)
        rospy.loginfo('time for capture cropped {}'.format(time.time() - t))

        t = time.time()
        hist = self.capture_hist(x1, y1, x2, y2)

        self.set_hist(hist)
        rospy.loginfo('time for hist cropped {}'.format(time.time() - t))
        if res.success == False:
            rospy.loginfo('capture cropped returned false, NaNs are possible')
            return

        # rospy.loginfo('response {}'.format(res))

        t = time.time()
        self.plot(res.h, res.s, res.v, res.l, res.u, res.lv, res.means,
                  res.sigmas)

        rospy.loginfo('time for plot {}'.format(time.time() - t))
        self.image_count.setText('Samples: {} '.format(res.count))

# #} end of capture_cropped

# #{ switch_view_hsv

    def switch_view_hsv(self):
        if self.view == HSV:
            self.view = RGB
            self.set_view(self.view)
            return
        print("HSV")
        self.log_info("HSV")
        self.hist_status = HSV
        self.view = HSV
        # rospy.loginfo('HSV from radio button {}'.format(self.radio_buttons.buttonClicked()))
        self.set_view(self.view)
        self.inner.show()
        self.inner_hist.show()
        self.inner_luv.hide()
        self.inner_luv_hist.hide()

# #} end of switch_view_hsv

# #{ switch_view_luv

    def switch_view_luv(self):
        if self.view == LUV:
            self.view = RGB
            self.set_view(self.view)
            return
        print("LUV")
        self.hist_status = LUV
        self.log_info("LUV")
        self.view = LUV
        self.set_view(self.view)
        self.inner_luv.show()
        self.inner_luv_hist.show()
        self.inner.hide()
        self.inner_hist.hide()

# #} end of switch_view_luv

# #{ update object detect colors

    def update_obd(self):
        rospy.loginfo('Sending data to Object Detect:{}'.format(
            self.load_method))
        self.log_info('Sending data to Object Detect:{}'.format(
            self.load_method))
        ball_rad = String()
        ball_rad.data = self.ball_radius.text()
        if self.hist_status == HSV:
            hist = np.transpose(self.hist_mask).flatten().astype('uint8')
            shape = self.hist_mask.shape
        elif self.hist_status == LUV:
            hist = np.transpose(self.hist_mask_lab).flatten().astype('uint8')
            shape = self.hist_mask_lab.shape

        if self.ball_radius.text() == "":
            return
        method = String()
        method.data = self.load_method

        color = String()
        if self.load_method == 'LUT':
            rospy.loginfo('load method YES {}'.format(self.load_method))
            rospy.loginfo('color space {}'.format(self.color_space))
            if self.color_space == 'HSV':
                color.data = 'hs_lut'
            elif self.color_space == 'LAB':
                rospy.loginfo('Color space {}'.format(self.color_space))
                color.data = 'ab_lut'
        else:
            rospy.loginfo('load method is {}'.format(self.load_method))
            color.data = self.color_space
        rospy.loginfo('color {}'.format(color))

        try:
            rospy.loginfo('updating object detect {}'.format(
                self.update_service.call(color, ball_rad, method, hist,
                                         shape)))
            self.log_info('updating object detect {}'.format("OBD updated"))
        except:
            rospy.loginfo("Couldn't update the object detect")
            self.log_info("Couldn't update the object detect")

        if self.frozen:
            self.freeze()

# #} end of update object detect colors

# #{ switch_view_both

    def switch_view_both(self):
        if self.view == BOTH:
            self.view = RGB
            self.set_view(self.view)
            return
        print("BOTH")
        self.log_info("BOTH")
        self.view = BOTH
        self.set_view(self.view)

        self.label_hsv.show()
        self.label_lab.show()

# #} end of switch_view_both

# #{ switch_view_object_detect

    def switch_view_object_detect(self):
        if self.view == OBD:
            self.view = RGB
            return
        print("OBD")
        self.log_info("OBD")
        self.view = OBD

# #} end of switch_view_both

# #{ load_config

    def load_config(self, path):
        # rospy.loginfo('cur dir {}'.format(os.path.curdir))
        # path = os.path.join(os.path.curdir,'../../config/balloon_config.yaml')
        # f = file(path,'r')
        # print(path)
        # res = yaml.safe_load(f)
        # TODO: change to reading from a file
        colors = ['Red', 'Green', 'Blue', 'Yellow']
        return colors

# #} end of load_config

# #{ clicked

    def clicked(self, color):
        def clicker():
            self.color_name = color
            self.directory.setText(self.save_path + '/{}.yaml'.format(color))

        return clicker

# #} end of clicked

# #{ add_buttons

    def add_buttons(self, colors):
        vbx = QVBoxLayout()
        i = 1
        for color in colors:

            but = QPushButton('Color {} ({})'.format(color, i))
            but.clicked.connect(self.clicked(color))

            but_short = QShortcut(QKeySequence("Ctrl+" + str(i)), self)
            but_short.activated.connect(self.clicked(color))
            i += 1
            vbx.addWidget(but)

        vbx.addStretch(1)
        self.color_buttons.setLayout(vbx)

# #} end of add_buttons

# #{ freeze

    def freeze(self):
        self.freeze_service.call()
        if self.frozen:
            self.frozen = False
        else:
            self.frozen = True
        rospy.loginfo('Frozen {}'.format(self.frozen))
        self.log_info('Frozen {}'.format(self.frozen))
        return

# #} end of freeze

# #{ save_config

    def save_config(self):
        color = String()
        color.data = self.color_space
        save_dir = String()
        save_dir.data = self.directory.text()
        ball_rad = String()
        if self.ball_radius.text() == "":
            return
        if os.path.isdir(self.directory.text()):
            return
        ball_rad.data = self.ball_radius.text()
        color_name = String()
        rospy.loginfo('Saving with load method {}'.format(self.load_method))
        hist = None
        hist_shape = None
        if self.load_method == 'LUT':
            self.log_info('Saving lut type with color space : {}'.format(
                self.color_space))
            if self.color_space == 'HSV':
                rospy.loginfo('hs_lut')
                color.data = 'hs_lut'
                hist_shape = self.hist_mask.shape
                hist = np.transpose(self.hist_mask).flatten().astype('uint8')
            elif self.color_space == 'LAB':
                rospy.loginfo('ab_lut')
                color.data = 'ab_lut'
                hist = np.transpose(
                    self.hist_mask_lab).flatten().astype('uint8')
                hist_shape = self.hist_mask_lab.shape
        else:
            color_name.data = self.color_name
        rospy.loginfo('color space to SAVE {}'.format(color_name))

        resp = self.get_config(color, save_dir, ball_rad, color_name, hist,
                               hist_shape)

        #conf_obj = {}
        ##HSV
        #hsv = {}
        ## hsv['hist_bins_h'] = resp.hsv[0].bins
        ## hsv['hist_hist_h'] = resp.hsv[0].values
        ## hsv['hist_bins_s'] = resp.hsv[1].bins
        ## hsv['hist_hist_s'] = resp.hsv[1].values
        ## hsv['hist_bins_v'] = resp.hsv[2].bins
        ## hsv['hist_hist_v'] = resp.hsv[2].values
        ## hsv['hsv_roi'] = resp.hsv_roi
        #hsv['hue_center'] = resp.h[0]
        #hsv['hue_range'] = resp.h[1]
        #hsv['sat_center'] = resp.s[0]
        #hsv['sat_range'] = resp.s[1]
        #hsv['val_center'] = resp.v[0]
        #hsv['val_range'] = resp.v[1]
        #conf_obj['hsv'] = hsv
        ##LAB
        #lab = {}
        #lab['l_center'] = resp.l[0]
        #lab['l_range'] = resp.l[1]
        #lab['a_center'] = resp.a[0]
        #lab['a_range'] = resp.a[1]
        #lab['b_center'] = resp.b[0]
        #lab['b_range'] = resp.b[1]
        ## lab['hist_bins_l'] = resp.lab[0].bins
        ## lab['hist_hist_l'] = resp.lab[0].values
        ## lab['hist_bins_a'] = resp.lab[1].bins
        ## lab['hist_hist_a'] = resp.lab[1].values
        ## lab['hist_bins_b'] = resp.lab[2].bins
        ## lab['hist_hist_b'] = resp.lab[2].values
        ## lab['lab_roi'] = resp.lab_roi
        #conf_obj['lab'] = lab

        #conf_obj['binarization_method'] = self.color_space
        #if os.path.isdir(save_dir):
        #    return
        #f = file(save_dir,'w')
        #print('saved to dir {}'.format(save_dir))
        #yaml.safe_dump(conf_obj,f)

        #if self.save_to_drone:
        #    path_to_script = os.path.join(rospkg.RosPack().get_path('balloon_color_picker'), 'scripts', 'copy_to_uav.sh')
        #    print(path_to_script)
        #    print('exectuted command ')
        #    print(save_dir)
        #    print(subprocess.check_call([path_to_script,os.environ['UAV_NAME'], save_dir, name+'.yaml']))

# #} end of save_config

# #{ set_params

    def set_params(self):

        resp = self.get_params()
        print(resp)
        print('params loaded')
        return resp.config_path, resp.save_path, resp.circled, resp.circle_filter, resp.circle_luv, resp.object_detect, resp.save_to_drone

# #} end of set_params

# #{ set_view

    def set_view(self, view):
        self.change_callback(view)

# #} end of set_view

# #{ set_hist

    def set_hist(self, hist_resp):
        hist = np.array(hist_resp.hist_hsv)
        hist = np.reshape(hist, hist_resp.shape_hsv)
        self.selected_count = 0

        self.hist_hsv_orig_h = hist_resp.shape_hsv[0]
        self.hist_hsv_orig_w = hist_resp.shape_hsv[1]
        self.cur_hist_hs = hist
        # self.hist_mask = np.zeros(self.cur_hist_hs.shape)

        hist = np.array(hist_resp.hist_lab)
        hist = np.reshape(hist, hist_resp.shape_lab)

        self.hist_lab_orig_h = hist_resp.shape_lab[0]
        self.hist_lab_orig_w = hist_resp.shape_lab[1]
        self.cur_hist_ab = hist

        self.redraw(HSV)
        self.redraw(LUV)

# #} end of set_hist

# #{ draw_hist

    def draw_hist(self, histRGB):

        # histRGB = np.log2(histRGB)
        new_h = cv2.resize(histRGB.astype('uint8'),
                           dsize=(512, 360),
                           interpolation=cv2.INTER_CUBIC)
        # new_h = histRGB.copy().astype('uint8')

        # cv2.imshow("to draw", new_h)
        # cv2.waitKey(1)
        rospy.loginfo('new_h shape {}'.format(new_h.shape))

        h, w, c = new_h.shape
        total = new_h.nbytes
        perLine = int(total / h)
        if c == 3:
            q_img = QImage(new_h.data, w, h, perLine, QImage.Format_RGB888)
        elif c == 4:
            q_img = QImage(new_h.data, w, h, perLine, QImage.Format_RGBA8888)

        q = QPixmap.fromImage(q_img)
        self.inner_hist.setFixedWidth(512)
        self.inner_hist.setFixedHeight(360)
        self.inner_hist.setPixmap(q)

# #} end of draw_hist

# #{ draw_hist_lab

    def draw_hist_lab(self, histRGB):

        # histRGB = np.log2(histRGB)
        new_h = cv2.resize(histRGB.astype('uint8'),
                           dsize=(400, 400),
                           interpolation=cv2.INTER_CUBIC)
        # new_h = histRGB.copy().astype('uint8')

        # cv2.imshow("to draw", new_h)
        # cv2.waitKey(1)
        rospy.loginfo('new_h shape {}'.format(new_h.shape))

        h, w, c = new_h.shape
        total = new_h.nbytes
        perLine = int(total / h)
        if c == 3:
            q_img = QImage(new_h.data, w, h, perLine, QImage.Format_RGB888)
        elif c == 4:
            q_img = QImage(new_h.data, w, h, perLine, QImage.Format_RGBA8888)

        q = QPixmap.fromImage(q_img)
        self.inner_luv_hist.setFixedWidth(400)
        self.inner_luv_hist.setFixedHeight(400)
        self.inner_luv_hist.setPixmap(q)

# #} end of draw_hist

# #{ select_hist

    def select_hist(self, x1, y1, x2, y2, h, w, color_space):
        rospy.loginfo('select status {}'.format(self.select_status))
        self.selected_count += 1
        if color_space == HSV:
            cv2.rectangle(self.hist_mask, (x1, y1), (x2, y2), (1), -1)
        elif color_space == LUV:
            cv2.rectangle(self.hist_mask_lab, (x1, y1), (x2, y2), (1), -1)
        self.redraw(color_space)

    def redraw(self, color_space):
        if color_space == HSV:
            if self.cur_hist_hs is None:
                return
            hist = self.cur_hist_hs.copy()
        elif color_space == LUV:
            if self.cur_hist_ab is None:
                return
            hist = self.cur_hist_ab.copy()

        #normalizing the histogram
        minVal, maxVal, l, m = cv2.minMaxLoc(hist)
        hist = (hist - minVal) / (maxVal - minVal) * 255.0

        rospy.loginfo('hist shape {}'.format(hist.shape))

        hist = cv2.equalizeHist(hist.astype('uint8'))
        histRGB = cv2.cvtColor(hist.astype('uint8'), cv2.COLOR_GRAY2RGB)

        if color_space == HSV:
            maskRGB = cv2.cvtColor(self.hist_mask.astype('uint8'),
                                   cv2.COLOR_GRAY2RGB)

            maskRGB[self.hist_mask > 0, :] = np.array([255, 0, 0])
        elif color_space == LUV:
            maskRGB = cv2.cvtColor(self.hist_mask_lab.astype('uint8'),
                                   cv2.COLOR_GRAY2RGB)
            maskRGB[self.hist_mask_lab > 0, :] = np.array([255, 0, 0])

        alpha = 0.3
        selected_hist = alpha * maskRGB + (1.0 - alpha) * histRGB
        rospy.loginfo('to draw shape {}'.format(selected_hist.shape))

        if color_space == HSV:
            self.draw_hist(selected_hist)
        elif color_space == LUV:
            self.draw_hist_lab(selected_hist)

# #} end of select_hist

# #{ deselect_hist

    def deselect_hist(self, x1, y1, x2, y2, color_space):
        rospy.loginfo('deselect')
        self.log_info('deselect')
        if self.selected_count == 0:
            rospy.loginfo('nothing is selected, can"t deselect')
            self.log_info('nothing is selected, can"t deselect')
            return
        if color_space == HSV:
            cv2.rectangle(self.hist_mask, (x1, y1), (x2, y2), (0),
                          -1)  # A filled rectangle
        elif color_space == LUV:
            cv2.rectangle(self.hist_mask_lab, (x1, y1), (x2, y2), (0),
                          -1)  # A filled rectangle
        self.redraw(color_space)

# #} end of deselect_hist

# #{ get_mask

    def get_mask(self, arr, x1, y1, x2, y2):
        mask = np.zeros([arr.shape[0], arr.shape[1]])
        if x1 > x2:
            if y1 > y2:
                mask[x2:x1, y2:y1] = 1
            elif y1 < y2:
                mask[x2:x1, y1:y2] = 1
        elif x1 < x2:
            if y1 > y2:
                mask[x1:x2, y2:y1] = 1
            elif y1 < y2:
                mask[x1:x2, y1:y2] = 1

        return mask

# #} end of get_mask

# #{ keyPressEvent

    def keyPressEvent(self, event):
        pass

# #} end of keyPressEvent

# #{ default todo's

    def shutdown_plugin(self):
        # TODO unregister all publishers here
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass

    #def trigger_configuration(self):
    # Comment in to signal that the plugin has a way to configure
    # This will enable a setting button (gear icon) in each dock widget title bar
    # Usually used to open a modal configuration dialog

# #} end of default todo's

    def log_info(self, text):
        self.log_text.setText("Last log message: {}".format(text))
Ejemplo n.º 27
0
class ImagePanel(wx.Panel):
    """This class displays 2D data as a color plot"""
    def __init__(self,parent,posFunction=None,setMin=None,setMax=None):
        """This creates an ImagePanel
        
        Keyword arguments:
        parent -- The container which will hold the panel
        posFunction -- function to be updated with mouse position
        setMin -- function to be called on left click
        setMax -- function to be called on right click
    
        """
        wx.Panel.__init__(self,parent)
        self.Bind(wx.EVT_PAINT,self.__OnPaint)
        self.posFunction = posFunction#Function to call on mouse movement
        self.setMin = setMin#Function to call on right click
        self.setMax = setMax#Function to call on left click
    #The figure which holds the graph
        self.figure = Figure(figsize=(1,1),dpi=512)
        #The object where the graph is drawn
        self.canvas = FigureCanvas(self,-1,self.figure)
        self.canvas.Bind(wx.EVT_MOTION,self.__OnMouseMove)
        self.canvas.Bind(wx.EVT_LEFT_UP,self.__OnMouseLeft)
        self.canvas.Bind(wx.EVT_RIGHT_UP,self.__OnMouseRight)
        
        self.cmap = cm.spectral#The color map for the graph
    #Known file formats for saving images
        self.handlers={u"BMP":wx.BITMAP_TYPE_BMP,
                        u"JPG":wx.BITMAP_TYPE_JPEG,
                        u"PNG":wx.BITMAP_TYPE_PNG,
                        u"PCX":wx.BITMAP_TYPE_PCX,
                        u"PNM":wx.BITMAP_TYPE_PNM,
                        u"TIF":wx.BITMAP_TYPE_TIF}

    def __OnPaint(self,event):
        """Event handler to redraw graph when panel is redrawn."""
        self.canvas.draw()
        event.Skip()
    
    def __OnMouseMove(self,event):
        """Event handler when the mouse moves over the graph"""
        (x,y) = event.GetPosition()
        if self.posFunction is None: return
        self.posFunction(x/32,y/4)
    
    def __OnMouseLeft(self,event):
        """Event handler for left clicking on the graph."""
        (x,y) = event.GetPosition()
        if self.setMin is None: 
            None
        else:
            self.setMin(x/32,y/4)
        event.Skip()
    
    def __OnMouseRight(self,event):
        """Event handler for right clicking on the graph."""
        (x,y) = event.GetPosition()
        if self.setMax is None: 
            None
        else:
            self.setMax(x/32,y/4)
        event.Skip()

    def update(self,data,vmin=10,vmax=20):
        """Change the dataset for the graph
    
        Keyword arguments:
        data -- 2D numpy array
        vmin -- floor value for graphing
        vmax -- ceiling value for graphing
    
        """
        self.data=data
        self.figure.clear()
        self.figure.add_axes((0,0,1,1),autoscale_on=True,frameon=False,yticks=[0,1],xticks=[0,1])
        self.figure.get_axes()[-1].get_xaxis().set_visible(False)
        self.figure.get_axes()[-1].get_yaxis().set_visible(False)
        self.figure.get_axes()[-1].imshow(data,cmap=self.cmap,vmin=vmin,vmax=vmax,aspect="auto",interpolation='none')
        self.figure.canvas.draw()
        self.Refresh()


    
    def saveImage(self,path):
        """Saves the graph to an image file"""
        bitmap = wx.EmptyBitmap(512,512,24)
        memdc = wx.MemoryDC(bitmap)
        self.figure.canvas.draw(memdc)
        image = wx.Bitmap.ConvertToImage(bitmap)
        image.SaveFile(path,self.handlers[path[-3:].encode('ascii')])

    
    def copyToClipboard(self):
        """Copies the image of the graph to the system Clipboard."""
        if wx.TheClipboard.Open():
            bitmap = wx.EmptyBitmap(512,512,24)
            memdc = wx.MemoryDC(bitmap)
            self.figure.canvas.draw(memdc)
            wx.TheClipboard.Clear()
            wx.TheClipboard.SetData(wx.BitmapDataObject(bitmap))
            wx.TheClipboard.Close()
Ejemplo n.º 28
0
class TSBatchThread(QThread):

    plotSignal = pyqtSignal(str)
    plotProgressSignal = pyqtSignal(int, str)
    detrendSignal = pyqtSignal(str)
    detrendProgressSignal = pyqtSignal()

    def __init__(self):
        super(TSBatchThread, self).__init__()
        self.ts_reader = TSReader()

    def _outliers(self, filename, **kwargs):
        df = self.read_file(filename, interval=self.kwargs['interval'])
        self.ts_reader.df = df
        self.ts_reader.kwargs = kwargs
        results = self.ts_reader.outliers(**kwargs)
        sigma = results.get('sigma', None)
        iqr = results.get('iqr', None)
        if sigma is not None:
            df.ix[sigma] = np.nan
        if iqr is not None:
            df.ix[iqr] = np.nan
        if not os.path.isdir(kwargs['dir']):
            os.mkdir(kwargs['dir'])
        saved_name = os.path.join(kwargs['dir'],
                                  os.path.split(filename)[1][:4] + '_%s.neu' % kwargs['name'])
        self.ts_reader.write(df, saved_name)
        return saved_name

    def _detrend(self, filename, **kwargs):
        df = self.read_file(filename, interval=self.kwargs['interval'])
        self.ts_reader.df = df
        self.ts_reader.kwargs = kwargs
        results = self.ts_reader.fit(**kwargs)
        return results

    def _stacking(self, filenames, saved_name='stacking.neu'):
        dfs = []
        for f in filenames:
            dfs.append(self.read_file(f, interval=self.kwargs['interval']))
        cols = self.ts_reader.columns
        cme = []
        for col in cols:
            component = pd.concat([df[col] for df in dfs], axis=1)
            sigma = pd.concat([df['%s_sigma' % col] for df in dfs], axis=1)
            index = component.index
            ind = component.count(axis=1) > 3
            component = component[ind].fillna(0).as_matrix()
            sigma = sigma[ind].fillna(0).as_matrix()
            sigma_2 = sigma**2
            epsilon = np.nansum(component / sigma_2, axis=1) / np.nansum(1.0 / sigma_2, axis=1)
            cme.append(pd.Series(epsilon, index=index[ind]))
        cme = pd.concat(cme, axis=1)
        cme.columns = cols
        cme.insert(0, self.ts_reader.time_unit, 0.0)
        # cme.insert(0, dfs[-1].columns[0], 0.0)
        for col in cols:
            cme['%s_sigma' % col] = 0.0
        self.ts_reader.write(cme, saved_name)
        return cme

    def read_file(self, filename, interval=None):
        self.ts_reader.read_file(filename)
        df = self.ts_reader.df
        if interval is None:
            return df
        start, end = interval['all']
        ind = np.logical_and(df.index >= start, df.index <= end)
        df = df[ind]
        if interval.get(os.path.split(filename)[1][:4].lower(), None):
            start, end = interval[os.path.split(filename)[1][:4].lower()]
            ind = np.logical_and(df.index >= start, df.index <= end)
            df = df[~ind]
        return df

    def detrend(self, **kwargs):
        self.kwargs = kwargs
        filenames = kwargs['files']
        clean_files = []
        if kwargs['outliers']:
            self.detrendSignal.emit('Start removing outliers.')
            for f in filenames:
                outliers_dict = kwargs['outliers'].copy()
                if kwargs['detrend']:
                    outliers_dict['polys'] = kwargs['detrend']['polys']
                    outliers_dict['periods'] = kwargs['detrend']['periods']
                    outliers_dict['offsets'] = kwargs['detrend']['offsets'].get(
                        os.path.split(f)[1][:4].lower(), None)
                else:
                    outliers_dict.update({'polys': 1})
                clean_files.append(self._outliers(f, **outliers_dict))
                self.detrendSignal.emit('cleaned %s, saved in %s' % (f, clean_files[-1]))
                self.detrendProgressSignal.emit()
        else:
            clean_files = filenames
        residual_files = []
        if kwargs['detrend']:
            self.detrendSignal.emit("Start Detrending time series.")
            for filename in clean_files:
                detrend_dict = kwargs['detrend'].copy()
                detrend_dict['offsets'] = kwargs['detrend']['offsets'].get(
                    os.path.split(filename)[1][:4].lower(), None)
                results = self._detrend(filename, **detrend_dict)
                if not os.path.isdir(kwargs['detrend']['log']):
                    os.mkdir(kwargs['detrend']['log'])
                log = os.path.join(kwargs['detrend']['log'],
                                   os.path.split(filename)[1][:4] + '.log')
                with open(log, 'w') as f:
                    f.write(results['log'])
                if not os.path.isdir(kwargs['detrend']['dir']):
                    os.mkdir(kwargs['detrend']['dir'])
                residual_file = os.path.join(kwargs['detrend']['dir'], os.path.split(filename)[1][
                                             :4] + '_%s.neu' % kwargs['detrend']['name'])
                self.ts_reader.write(results['residual'], residual_file)
                self.detrendSignal.emit('detrend %s, saved in %s' % (filename, residual_file))
                self.detrendProgressSignal.emit()
                residual_files.append(residual_file)
        else:
            residual_files = clean_files
        if kwargs['cme']:
            if kwargs['cme']['method'] == 'Stacking':
                saved_name = os.path.join(kwargs['cme']['dir'], 'cme.neu')
                cme = self._stacking(residual_files, saved_name=saved_name)
                if kwargs['detrend']:
                    for f in residual_files:
                        self.ts_reader.read_file(f)
                        df = self.ts_reader.df
                        cols = df.columns.tolist()
                        df = df.subtract(cme, axis=1)
                        df = df[cols]
                        self.ts_reader.write(df, f)
                    kwargs['cme'] = False
                    kwargs['outliers'] = False
                    kwargs['files'] = clean_files
                    self.detrend(**kwargs)

    def plot(self, **kwargs):
        self.figure = Figure(figsize=(8, 6), dpi=kwargs['dpi'])
        FigureCanvas(self.figure)
        filenames = kwargs['files']
        for f in filenames:
            self.ts_reader.read_file(f)
            cols = self.ts_reader.columns
            df = self.ts_reader.df
            self.figure.clear()
            for i, c in enumerate(cols):
                ax = self.figure.add_subplot(len(cols), 1, 1 + i)
                ax.hold(True)
                ax.set_ylabel(c.upper() + ' (%s)' % self.ts_reader.unit, fontname='Microsoft Yahei')
                if kwargs.get('errorbar', False):
                    ax.errorbar(df.index, df[c], yerr=df['%s_sigma' % c],
                                fmt='ko', markersize=2)
                else:
                    ax.plot(df.index, df[c], 'ko', markersize=2)
            ax.set_xlabel('Date', fontname='Microsoft Yahei', fontsize='large')
            _, f = os.path.split(f)
            # offsets = get_offsets(f[:4].lower(), file=offsets)
            if kwargs.get('offsets', None):
                offsets = kwargs['offsets'][f[:4].lower()]
                if offsets is not None:
                    for date_str, components in offsets.iteritems():
                        date = datetime.strptime(date_str, "%Y%m%d")
                        for col, value in components.iteritems():
                            ind = cols.index(col)
                            ax = self.figure.get_axes()[ind]
                            patch = None
                            if value == 'eq':
                                patch = ax.axvline(date, color='r')
                            if value == 'ep':
                                patch = ax.axvline(date, color='b')
                            if value.startswith('eq exp'):
                                trans = blended_transform_factory(ax.transData,
                                                                  ax.transAxes)
                                end = date + timedelta(int(value.split()[-1]))
                                start = date2num(date)
                                end = date2num(end)
                                width = end - start
                                patch = Rectangle((start, 0), width, 1,
                                                  transform=trans,
                                                  visible=True,
                                                  facecolor='b',
                                                  edgecolor='b',
                                                  alpha=0.8)
                                ax.add_patch(patch)
                            if value.startswith('eq log'):
                                trans = blended_transform_factory(ax.transData,
                                                                  ax.transAxes)
                                end = date + timedelta(int(value.split()[-1]))
                                start = date2num(date)
                                end = date2num(end)
                                width = end - start
                                patch = Rectangle((start, 0), width, 1,
                                                  transform=trans,
                                                  visible=True,
                                                  facecolor='r',
                                                  edgecolor='r',
                                                  alpha=0.8)
                                ax.add_patch(patch)
            self.figure.suptitle(f, fontsize='x-large', fontname='Microsoft Yahei')
            saved_filename = os.path.join(kwargs.get('dir', './'), '%s.%s' % (f, kwargs['format']))
            if saved_filename:
                self.figure.savefig(saved_filename, dpi=kwargs['dpi'])
            self.plotProgressSignal.emit(1, saved_filename)
            self.plotSignal.emit('Plot %s successfully!' % f)

    def render(self, **kwargs):
        self.kwargs = kwargs
        self.start()

    def run(self):
        task = self.kwargs['task']
        if task == 'plot':
            self.plot(**self.kwargs)
        if task == 'detrend':
            self.detrend(**self.kwargs)
Ejemplo n.º 29
0
class TrackerApp(Frame):
    '''
    Class that manages the tracking process. Receives video frames, performs image filtering and Kalman or Particle filtering
    '''
    def __init__(self, mainFrame, filter):
        super().__init__(mainFrame)

        self.pack(side=LEFT)

        #Initialize output graph
        self.outFigure = Figure(figsize=(8, 6), dpi=100)
        self.outPlot = self.outFigure.add_axes([0.05, 0.05, 0.9, 0.9])
        self.outCanvas = FigureCanvasTkAgg(self.outFigure, self)
        self.outCanvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=False)

        self.outFigure.get_axes()[0].set_xlim(150, 0)
        self.outFigure.get_axes()[0].set_ylim(150, 0)

        self.outputPlotGraphics = OutputPlotGraphics(self.outPlot)

        self.infoLabel = self.outPlot.text(10, 10, "Label")

        self.infoLabelText = "Initializing..."

        #initialize matplotlib figure for webcam video display
        self.imFigure = Figure(figsize=(8, 2), dpi=100)
        self.imPlot = self.imFigure.add_subplot(121)
        self.imPlot2 = self.imFigure.add_subplot(122)
        self.imCanvas = FigureCanvasTkAgg(self.imFigure, self)
        self.imCanvas.get_tk_widget().pack(side=BOTTOM,
                                           fill=BOTH,
                                           expand=False)

        #detection settings
        self.frames = 0
        self.dtreshold = 0
        self.voidPrior = None
        self.objectlostcounter = 0

        #set filter (particle, kalman)
        self.filter = filter

        #Initialize matchfilter
        self.matchfilter = MatchFilter()

        #Initialize targetFinder
        self.targetFinder = TargetFinder()

        #initialize camera
        self.running = True
        self.camstream = Camstream(self)

        #initialize draw thread
        self.trackerAppThread = TrackerAppThread(self, self.imPlot,
                                                 self.imPlot2, self.imCanvas,
                                                 self.outCanvas, self.outPlot,
                                                 self.infoLabel)

    def updateImage(self, img, delta):

        #converting image to grayscale (image is uint8)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        img_backup = img

        #histogram equalization
        img = cv2.equalizeHist(img)

        #remove noise
        img = cv2.GaussianBlur(img, (3, 3), 1)

        #Laplacian edge detection and thresholding tozero
        img = cv2.Laplacian(img, cv2.CV_8U)
        res, img = cv2.threshold(img,
                                 np.max(img) / 3, np.max(img),
                                 cv2.THRESH_TOZERO)
        img = img.astype(np.float32)

        #plotting matching map
        correlated = self.matchfilter.correlate(img)
        in_val, max_val, min_loc, max_loc = cv2.minMaxLoc(correlated)
        #rect2 = Rectangle((x,y),5,5,linewidth=1,edgecolor='r',facecolor='none')

        if self.frames < 10:
            #First frames serves as initialization of the detection threshold
            self.frames += 1
            self.dtreshold += max_val / 10

            if self.voidPrior is None:
                self.voidPrior = correlated
            else:
                self.voidPrior += correlated

        else:

            #Thresholding the detection map
            res, thr = cv2.threshold(correlated, self.dtreshold * 0.9, max_val,
                                     cv2.THRESH_TOZERO)

            #Finding the target
            x, y = self.targetFinder.findTarget(thr)

            #rect2 = Rectangle((x,y),5,5,linewidth=1,edgecolor='r',facecolor='none')
            #self.imPlot2.add_patch(rect2)

            #Apply filtering (Kalman or Particle)
            s, p = self.filter.compute(x, y, delta)

            #Plots the states output for each model on the video display
            for i, state in enumerate(s):
                r = Rectangle((state[0], state[1]),
                              5,
                              5,
                              linewidth=1,
                              edgecolor=colors[i],
                              facecolor='none')
                self.imPlot2.add_patch(r)
                self.imPlot2.text(state[0], state[1], str(i))

            #Output plot: most probable model and model probabilities
            self.trackerAppThread.setInfoText(str(np.argmax(p)) + str(p))

            #The filter is reset if model 0 is detected for longer than 50 frames
            if np.argmax(p) == 0:
                self.objectlostcounter += 1
                if self.objectlostcounter >= 50:
                    self.objectlostcounter = 0
                    self.filter.resetState(x, y)
                    print("filter reset")
            else:
                self.objectlostcounter = 0

            #Update plots
            #Video images
            self.trackerAppThread.setImages(img_backup, thr)

            #measurement
            self.outputPlotGraphics.setMeasurement(x, y)

            #state
            n = np.argmax(p)
            self.outputPlotGraphics.setModel(np.argmax(p))

            if n != 0:
                self.outputPlotGraphics.setSpeed(s[n][0], s[n][1], s[n][2],
                                                 s[n][3])
                self.outputPlotGraphics.setState(s[n][0], s[n][1])

                if n == 1:
                    self.outputPlotGraphics.setLine(s[n][0], s[n][1])
                if n == 2:
                    R = sqrt(s[n][2]**2 + s[n][3]**2) / s[n][4]
                    theta = atan2(s[n][3], s[n][2])
                    x0 = s[n][0] + R * cos(theta)
                    y0 = s[n][1] + R * sin(theta)
                    #print(s[n][4])
                    self.outputPlotGraphics.setCircle(x0, y0, R)

    def closeCam(self):
        self.camstream.closeStream()
Ejemplo n.º 30
0
class ImagePanel(BasePanel):
    """
    MatPlotlib Image as a wx.Panel, suitable for embedding
    in any wx.Frame.   This provides a right-click popup
    menu for configuration, zoom by dragging, saving an image
    figure, and Ctrl-C for copy-image-to-clipboard, customizations
    of colormap, interpolation, and intesity scaling

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

    def __init__(self, parent, messenger=None, data_callback=None,
                 cursor_callback=None, lasso_callback=None,
                 redraw_callback=None, zoom_callback=None,
                 contour_callback=None, size=(525, 450), dpi=100,
                 output_title='Image', **kws):

        matplotlib.rc('lines', linewidth=2)
        BasePanel.__init__(self, parent,
                           output_title=output_title,
                           messenger=messenger,
                           zoom_callback=zoom_callback, **kws)

        self.conf = ImageConfig()
        self.conf.title = output_title
        self.cursor_mode = 'zoom'
        self.data_callback = data_callback
        self.cursor_callback = cursor_callback
        self.lasso_callback = lasso_callback
        self.contour_callback = contour_callback
        self.redraw_callback = redraw_callback
        self.slice_plotframe = None
        self.win_config = None
        self.size = size
        self.dpi  = dpi
        self.user_limits = {}
        self.scalebar_rect = self.scalerbar_text = None
        self.BuildPanel()

    @property
    def xdata(self):
        return self.conf.xdata

    @xdata.setter
    def xdata(self, value):
        self.conf.xdata = value

    @property
    def ydata(self):
        return self.conf.ydata

    @ydata.setter
    def ydata(self, value):
        self.conf.ydata = value


    def display(self, data, x=None, y=None, xlabel=None, ylabel=None,
                style=None, nlevels=None, levels=None, contour_labels=None,
                store_data=True, col=0, unzoom=True, show_axis=False,
                auto_contrast=False, contrast_level=0, colormap=None, **kws):
        """
        generic display, using imshow (default) or contour
        """
        if style is not None:
            self.conf.style = style
        self.axes.cla()
        conf = self.conf
        conf.log_scale = False
        conf.show_axis = show_axis
        conf.highlight_areas = []
        if 1 in data.shape:
            data = data.squeeze()
        self.data_range = [0, data.shape[1], 0, data.shape[0]]
        if contrast_level not in (0, None):
            conf.contrast_level = contrast_level
        if auto_contrast:
            conf.contrast_level = 1
        if x is not None:
            conf.xdata = np.array(x)
            if conf.xdata.shape[0] != data.shape[1]:
                conf.xdata = None
        if y is not None:
            conf.ydata = np.array(y)
            if conf.ydata.shape[0] != data.shape[0]:
                conf.ydata = None

        if xlabel is not None:
            conf.xlab = xlabel
        if ylabel is not None:
            conf.ylab = ylabel
        if store_data:
            conf.data = data

        if self.conf.style == 'contour':
            if levels is None:
                levels = self.conf.ncontour_levels
            else:
                self.conf.ncontour_levels = levels
            if nlevels is None:
                nlevels = self.conf.ncontour_levels = 9
            nlevels = max(2, nlevels)

            if conf.contrast_level is not None:
                contrast = [conf.contrast_level, 100.0-conf.contrast_level]
                imin, imax = np.percentile(conf.data, contrast)
                data = np.clip(conf.data, imin, imax)

            clevels  = np.linspace(data.min(), data.max(), nlevels+1)
            self.conf.contour_levels = clevels
            self.conf.image = self.axes.contourf(data, cmap=self.conf.cmap[col],
                                                 levels=clevels)

            self.conf.contour = self.axes.contour(data, cmap=self.conf.cmap[col],
                                                  levels=clevels)
            cmap_name = self.conf.cmap[col].name
            xname = 'gray'
            try:
                if cmap_name == 'gray_r':
                    xname = 'Reds_r'
                elif cmap_name == 'gray':
                    xname = 'Reds'
                elif cmap_name.endswith('_r'):
                    xname = 'gray_r'
            except:
                pass
            self.conf.contour.set_cmap(getattr(cmap, xname))

            if contour_labels is None:
                contour_labels = self.conf.contour_labels
            if contour_labels:
                nlog = np.log10(abs(clevels[1]-clevels[0]))
                fmt = "%.4f"
                if nlog < -2:
                    fmt = "%%.%df" % (1-nlog)
                elif nlog > 2:
                    fmt = "%.1f"
                self.axes.clabel(self.conf.contour, fontsize=10, inline=1, fmt=fmt)
            if hasattr(self.contour_callback , '__call__'):
                self.contour_callback(levels=clevels)
        else:
            if data.max() == data.min():
                img = data
            else:
                img = (data - data.min()) /(1.0*data.max() - data.min())
            if colormap is not None:
                self.conf.set_colormap(colormap, icol=col)
            self.conf.image = self.axes.imshow(img, cmap=self.conf.cmap[col],
                                               interpolation=self.conf.interp)

        self.autoset_margins()

        if unzoom:
            self.unzoom_all()

        if hasattr(self.data_callback, '__call__'):
            self.data_callback(data, x=x, y=y, **kws)

        self.conf.indices = None
        self.indices_thread = Thread(target=self.calc_indices, args=(data.shape, ))
        self.indices_thread.start()


    def update_image(self, data):
        """
        update image on panel, as quickly as possible
        """
        if 1 in data.shape:
            data = data.squeeze()
        if self.conf.contrast_level is not None:
            clevels = [self.conf.contrast_level, 100.0-self.conf.contrast_level]
            imin, imax = np.percentile(data, clevels)
            data = np.clip((data - imin)/(imax - imin + 1.e-8), 0, 1)
        self.axes.images[0].set_data(data)
        self.canvas.draw()


    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 self.conf.show_axis:
            self.axes.set_axis_on()
            if self.conf.show_grid:
                self.axes.grid(True,
                               alpha=self.conf.grid_alpha,
                               color=self.conf.grid_color)
            else:
                self.axes.grid(False)
            self.conf.set_formatters()

            l, t, r, b = 0.08, 0.96, 0.96, 0.08
            if self.conf.xlab is not None:
                self.axes.set_xlabel(self.conf.xlab)
                b, t = 0.11, 0.96
            if self.conf.ylab is not None:
                self.axes.set_ylabel(self.conf.ylab)
                l, r = 0.11, 0.96
        else:
            self.axes.set_axis_off()
            l, t, r, b = 0.01, 0.99, 0.99, 0.01
        self.gridspec.update(left=l, top=t, right=r, bottom=b)

        for ax in self.fig.get_axes():
            ax.update_params()
            ax.set_position(ax.figbox)

    def add_highlight_area(self, mask, label=None, col=0):
        """add a highlighted area -- outline an arbitrarily shape --
        as if drawn from a Lasso event.

        This takes a mask, which should be a boolean array of the
        same shape as the image.
        """
        patch = mask * np.ones(mask.shape) * 0.9
        cmap = self.conf.cmap[col]
        area = self.axes.contour(patch, cmap=cmap, levels=[0, 1])
        self.conf.highlight_areas.append(area)
        col = None
        if hasattr(cmap, '_lut'):
            rgb  = [int(i*240)^255 for i in cmap._lut[0][:3]]
            col  = '#%02x%02x%02x' % (rgb[0], rgb[1], rgb[2])

        if label is not None:
            def fmt(*args, **kws): return label
            self.axes.clabel(area, fontsize=9, fmt=fmt,
                             colors=col, rightside_up=True)

        if col is not None:
            for l in area.collections:
                l.set_color(col)

        self.canvas.draw()

    def set_viewlimits(self, axes=None):
        """ update xy limits of a plot"""
        if axes is None:
            axes = self.axes

        xmin, xmax, ymin, ymax = self.data_range
        if len(self.conf.zoom_lims) >1:
            zlims = self.conf.zoom_lims[-1]
            if axes in zlims:
                xmin, xmax, ymin, ymax = zlims[axes]

        xmin = max(self.data_range[0], xmin)
        xmax = min(self.data_range[1], xmax)
        ymin = max(self.data_range[2], ymin)
        ymax = min(self.data_range[3], ymax)
        if (xmax < self.data_range[0] or
            xmin > self.data_range[1] or
            ymax < self.data_range[2] or
            ymin > self.data_range[3] ):
            self.conf.zoom_lims.pop()
            return

        if abs(xmax-xmin) < 2:
            xmin = int(0.5*(xmax+xmin) - 1)
            xmax = xmin + 2

        if abs(ymax-ymin) < 2:
            ymin = int(0.5*(ymax+xmin) - 1)
            ymax = ymin + 2

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

        self.conf.datalimits = [xmin, xmax, ymin, ymax]
        self.conf.reset_formats()
        self.redraw()

    def clear(self):
        """ clear plot """
        self.axes.cla()
        self.conf.title  = ''


    ####
    ## create GUI
    ####
    def BuildPanel(self):
        """ builds basic GUI panel and popup menu"""
        figsize = (1.0*self.size[0]/self.dpi, 1.0*self.size[1]/self.dpi)
        self.fig   = Figure(figsize, dpi=self.dpi)
        self.gridspec = GridSpec(1,1)
        self.axes  = self.fig.add_subplot(self.gridspec[0],
                                          facecolor='#FFFFFD')
        self.canvas = FigureCanvasWxAgg(self, -1, self.fig)
        self.conf.axes  = self.axes
        self.conf.fig   = self.fig
        self.conf.canvas= self.canvas

        # self.canvas.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
        # This way of adding to sizer allows resizing
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.canvas, 1, wx.ALL|wx.GROW)
        self.SetSizer(sizer)
        self.SetSize(self.GetBestVirtualSize())
        self.addCanvasEvents()

    def BuildPopup(self):
        # build pop-up menu for right-click display
        self.popup_menu = popup = wx.Menu()
        MenuItem(self, popup, 'Zoom out', '',   self.unzoom)
        MenuItem(self, popup, 'Zoom all the way out', '',   self.unzoom_all)

        self.popup_menu.AppendSeparator()

        MenuItem(self, popup, 'Rotate 90deg  (CW)', '', self.rotate90)
        MenuItem(self, popup, 'Save Image', '', self.save_figure)

    def rotate90(self, event=None, display=True):
        "rotate 90 degrees, CW"
        self.conf.rotate90()
        if display:
            conf = self.conf
            self.display(conf.data, x=conf.xdata, y=conf.ydata,
                         xlabel=conf.xlab, ylabel=conf.ylab,
                         show_axis=conf.show_axis,
                         levels=conf.ncontour_levels)

    def flip_horiz(self):
        self.conf.flip_horiz()

    def flip_vert(self):
        self.conf.flip_vert()

    def restore_flips_rotations(self):
        "restore flips and rotations"
        conf = self.conf
        if conf.flip_lr:
            self.flip_horiz()
        if conf.flip_ud:
            self.flip_vert()
        if conf.rot_level != 0:
            for i in range(4-conf.rot_level):
                self.rotate90(display=False)
            self.display(conf.data, x=conf.xdata, y=conf.ydata,
                         xlabel=conf.xlab, ylabel=conf.ylab,
                         show_axis=conf.show_axis)




    def toggle_curmode(self, event=None):
        "toggle cursor mode"
        if self.cursor_mode == 'zoom':
            self.cursor_mode = 'lasso'
        else:
            self.cursor_mode = 'zoom'

    ####
    ## GUI events, overriding BasePanel components
    ####
    def calc_indices(self, shape):
        """calculates and stores the set of indices
        ix=[0, nx-1], iy=[0, ny-1] for data of shape (nx, ny)"""
        if len(shape) == 2:
            ny, nx = shape
        elif len(shape) == 3:
            ny, nx, nchan = shape

        inds = []
        for iy in range(ny):
            inds.extend([(ix, iy) for ix in range(nx)])
        self.conf.indices = np.array(inds)

    def lassoHandler(self, vertices):
        if self.conf.indices is None or self.indices_thread.is_alive():
            self.indices_thread.join()
        ind = self.conf.indices
        mask = inside_poly(vertices,ind)
        mask.shape = (self.conf.data.shape[0], self.conf.data.shape[1])
        self.lasso = None
        self.canvas.draw()
        if hasattr(self.lasso_callback , '__call__'):
            self.lasso_callback(mask=mask)

    def unzoom(self, event=None, set_bounds=True):
        """ zoom out 1 level, or to full data range """
        lims = None
        if len(self.conf.zoom_lims) > 1:
            lims = self.conf.zoom_lims.pop()
        ax = self.axes
        if lims is None: # auto scale
            self.conf.zoom_lims = [None]
            xmin, xmax, ymin, ymax = self.data_range
            lims = {self.axes: [xmin, xmax, ymin, ymax]}
        self.set_viewlimits()
        self.canvas.draw()

    def zoom_leftup(self, event=None):
        """leftup event handler for zoom mode  in images"""
        if self.zoom_ini is None:
            return

        ini_x, ini_y, ini_xd, ini_yd = self.zoom_ini
        try:
            dx = abs(ini_x - event.x)
            dy = abs(ini_y - event.y)
        except:
            dx, dy = 0, 0
        t0 = time.time()
        self.rbbox = None
        self.zoom_ini = None
        if (dx > 3) and (dy > 3) and (t0-self.mouse_uptime)>0.1:
            self.mouse_uptime = t0
            zlims, tlims = {}, {}
            ax =  self.axes
            xmin, xmax = ax.get_xlim()
            ymin, ymax = ax.get_ylim()

            zlims[ax] = [xmin, xmax, ymin, ymax]

            if len(self.conf.zoom_lims) == 0:
                self.conf.zoom_lims.append(zlims)


            ax_inv = ax.transData.inverted
            try:
                x1, y1 = ax_inv().transform((event.x, event.y))
            except:
                x1, y1 = self.x_lastmove, self.y_lastmove
            try:
                x0, y0 = ax_inv().transform((ini_x, ini_y))
            except:
                x0, y0 = ini_xd, ini_yd

            tlims[ax] = [int(round(min(x0, x1))), int(round(max(x0, x1))),
                         int(round(min(y0, y1))), int(round(max(y0, y1)))]
            self.conf.zoom_lims.append(tlims)
            # now apply limits:
            self.set_viewlimits()
            if callable(self.zoom_callback):
                self.zoom_callback(wid=self.GetId(), limits=tlims[ax])


    def unzoom_all(self, event=None):
        """ zoom out full data range """
        self.conf.zoom_lims = [None]
        self.unzoom(event)

    def redraw(self, col=0):
        """redraw image, applying
        - log scaling,
        - max/min values from sliders or explicit intensity ranges
        - color map
        - interpolation
        """
        conf = self.conf
        img = conf.data
        if img is None: return
        if len(img.shape) == 2:
            col = 0
        if self.conf.style == 'image':
            if conf.log_scale:
                img = np.log10(1 + 9.0*img)

        # apply intensity scale for current limited (zoomed) image
        if len(img.shape) == 2:
            # apply clipped color scale, as from sliders

            imin = float(conf.int_lo[col])
            imax = float(conf.int_hi[col])
            if conf.log_scale:
                imin = np.log10(1 + 9.0*imin)
                imax = np.log10(1 + 9.0*imax)

            (xmin, xmax, ymin, ymax) = self.conf.datalimits
            if xmin is None:  xmin = 0
            if xmax is None:  xmax = img.shape[1]
            if ymin is None:  ymin = 0
            if ymax is None:  ymax = img.shape[0]


            img = (img - imin)/(imax - imin + 1.e-8)
            mlo = conf.cmap_lo[0]/(1.0*conf.cmap_range)
            mhi = conf.cmap_hi[0]/(1.0*conf.cmap_range)
            if self.conf.style == 'image':
                conf.image.set_data(np.clip((img - mlo)/(mhi - mlo + 1.e-8), 0, 1))
                conf.image.set_interpolation(conf.interp)
        else:
            r, g, b = img[:,:,0], img[:,:,1], img[:,:,2]

            rmin = float(conf.int_lo[0])
            rmax = float(conf.int_hi[0])
            gmin = float(conf.int_lo[1])
            gmax = float(conf.int_hi[1])
            bmin = float(conf.int_lo[2])
            bmax = float(conf.int_hi[2])
            if conf.log_scale:
                rmin = np.log10(1 + 9.0*rmin)
                rmax = np.log10(1 + 9.0*rmax)
                gmin = np.log10(1 + 9.0*gmin)
                gmax = np.log10(1 + 9.0*gmax)
                bmin = np.log10(1 + 9.0*bmin)
                bmax = np.log10(1 + 9.0*bmax)

            rlo = conf.cmap_lo[0]/(1.0*conf.cmap_range)
            rhi = conf.cmap_hi[0]/(1.0*conf.cmap_range)
            glo = conf.cmap_lo[1]/(1.0*conf.cmap_range)
            ghi = conf.cmap_hi[1]/(1.0*conf.cmap_range)
            blo = conf.cmap_lo[2]/(1.0*conf.cmap_range)
            bhi = conf.cmap_hi[2]/(1.0*conf.cmap_range)
            r = (r - rmin)/(rmax - rmin + 1.e-8)
            g = (g - gmin)/(gmax - gmin + 1.e-8)
            b = (b - bmin)/(bmax - bmin + 1.e-8)

            inew = img*1.0
            inew[:,:,0] = np.clip((r - rlo)/(rhi - rlo + 1.e-8), 0, 1)
            inew[:,:,1] = np.clip((g - glo)/(ghi - glo + 1.e-8), 0, 1)
            inew[:,:,2] = np.clip((b - blo)/(bhi - blo + 1.e-8), 0, 1)

            whitebg = conf.tricolor_bg.startswith('wh')

            if whitebg:
                inew = conf.tricolor_white_bg(inew)

            if self.conf.style == 'image':
                conf.image.set_data(inew)
                conf.image.set_interpolation(conf.interp)


        try:
            self.scalebar_rect.remove()
        except:
            pass
        try:
            self.scalebar_text.remove()
        except:
            pass

        if conf.scalebar_show:
            ystep, xstep = conf.scalebar_pixelsize
            if xstep is None or ystep is None:
                ystep, xstep = 1, 1
                if conf.xdata is not None:
                    xstep = abs(np.diff(conf.xdata).mean())
                if conf.ydata is not None:
                    ystep = abs(np.diff(conf.ydata).mean())
                self.scalebar_pixelsize = ystep, xstep
            y, x = conf.scalebar_pos
            y, x = int(y), int(x)
            h, w = conf.scalebar_size
            h, w = int(h), int(w/xstep)
            col =  conf.scalebar_color

            self.scalebar_rect = Rectangle((x, y), w, h,linewidth=1, edgecolor=col,
                                 facecolor=col)
            self.axes.add_patch(self.scalebar_rect)
            if conf.scalebar_showlabel:
                yoff, xoff = conf.scalebar_textoffset
                self.scalebar_text = self.axes.text(x+xoff, y+yoff, conf.scalebar_label,
                                                    color=col)
        self.canvas.draw()
        if callable(self.redraw_callback):
            self.redraw_callback(wid=self.GetId())


    def report_motion(self, event=None):
        if event.inaxes is None:
            return
        fmt = "X,Y= %g, %g"
        x, y  = event.xdata, event.ydata
        if len(self.fig.get_axes()) > 1:
            try:
                x, y = self.axes.transData.inverted().transform((x, y))
            except:
                pass
        if self.motion_sbar is None:
            try:
                self.motion_sbar = self.nstatusbar-1
            except AttributeError:
                self.motion_sbar = 1
        self.write_message(fmt % (x, y), panel=self.motion_sbar)
        conf = self.conf
        if conf.slice_onmotion:
            ix, iy = int(round(x)), int(round(y))
            if (ix >= 0 and ix < conf.data.shape[1] and
                iy >= 0 and iy < conf.data.shape[0]):
                conf.slice_xy = ix, iy
                self.update_slices()

    def report_leftdown(self,event=None):
        if event == None:
            return
        if event.xdata is None or event.ydata is None:
            return

        ix, iy = int(round(event.xdata)), int(round(event.ydata))

        conf = self.conf
        if (ix >= 0 and ix < conf.data.shape[1] and
            iy >= 0 and iy < conf.data.shape[0]):
            pos = ''
            if conf.xdata is not None:
                pos = ' %s=%.4g,' % (conf.xlab, conf.xdata[ix])
            if conf.ydata is not None:
                pos = '%s %s=%.4g,' % (pos, conf.ylab, conf.ydata[iy])
            dval = conf.data[iy, ix]
            if len(conf.data.shape) == 3:
                dval = "%.4g, %.4g, %.4g" % tuple(dval)
            else:
                dval = "%.4g" % dval
            msg = "Pixel [%i, %i], %s Intensity=%s " % (ix, iy, pos, dval)

            self.write_message(msg, panel=0)
            conf.slice_xy = ix, iy
            self.update_slices()
            if hasattr(self.cursor_callback , '__call__'):
                self.cursor_callback(x=event.xdata, y=event.ydata)

    def get_slice_plotframe(self):
        shown = False
        new_plotter = False
        if self.slice_plotframe is not None:
            try:
                self.slice_plotframe.Raise()
                shown = True
            except:
                pass
        if not shown:
            self.slice_plotframe = pf = PlotFrame(self)
            new_plotter = True
            try:
                xpos, ypos = self.parent.GetPosition()
                xsiz, ysiz = self.parent.GetSize()
                pf.SetPosition((xpos+xsiz+10, ypos))
            except:
                pass

        return new_plotter, self.slice_plotframe

    def update_slices(self):
        if self.conf.slices in ('None', None, 0):
            return
        x, y = -1, -1
        try:
            x, y = [int(a) for a in self.conf.slice_xy]
        except:
            return
        if len(self.conf.data.shape) == 3:
            ymax, xmax, nc = self.conf.data.shape
        elif len(self.conf.data.shape) == 2:
            ymax, xmax = self.conf.data.shape
            nc = 0
        else:
            return
        if x < 0 or y < 0 or x > xmax or y > ymax:
            return

        wid = int(self.conf.slice_width)
        new_plotter, pf = self.get_slice_plotframe()

        popts = {'ylabel': 'Intensity', 'linewidth': 3}

        if self.conf.slices.lower() == 'x':
            y1 = int(y - wid/2. + 1)
            y2 = int(y + wid/2.) + 1
            if y1 < 0: y1 = 0
            if y2 > ymax: y2 = ymax
            _x = self.conf.xdata
            if _x is None:
                _x = np.arange(self.conf.data.shape[1])
            _y = self.conf.data[y1:y2].sum(axis=0)
            popts['xlabel'] = 'X'
            popts['title'] = 'X Slice: Y=%d:%d' % (y1, y2)
            if y2 == y1+1:
                popts['title'] = 'X Slice: Y=%d' % y1

        else:
            x1 = int(x - wid/2.0 + 1)
            x2 = int(x + wid/2.0) + 1
            if x1 < 0: x1 = 0
            if x2 > xmax: x2 = xmax
            _x = self.conf.ydata
            if _x is None:
                _x = np.arange(self.conf.data.shape[0])
            _y = self.conf.data[:,x1:x2].sum(axis=1)
            popts['xlabel'] = 'Y'
            popts['title'] = 'Y Slice: X=%d:%d' % (x1, x2)
            if x2 == x1+1:
                popts['title'] = 'Y Slice: X=%d' % x1

        if new_plotter:
            if len(_y.shape) == 2 and _y.shape[1] == 3:
                pf.plot(_x,  _y[:, 0], color=RGB_COLORS[0], delay_draw=True, **popts)
                pf.oplot(_x, _y[:, 1], color=RGB_COLORS[1], delay_draw=True, **popts)
                pf.oplot(_x, _y[:, 2], color=RGB_COLORS[2], **popts)
            else:
                pf.plot(_x, _y, **popts)
        else:
            pf.panel.set_title(popts['title'], delay_draw=True)
            pf.panel.set_xlabel(popts['xlabel'], delay_draw=True)
            if len(_y.shape) == 2 and _y.shape[1] == 3:
                pf.update_line(0, _x,  _y[:, 0], update_limits=True, draw=False)
                pf.update_line(1, _x,  _y[:, 1], update_limits=True, draw=False)
                pf.update_line(2, _x,  _y[:, 2], update_limits=True, draw=True)
            else:
                pf.update_line(0, _x, _y, update_limits=True, draw=True)


        pf.Show()
        self.SetFocus()
        try:
            self.parent.Raise()
        except:
            pass
Ejemplo n.º 31
0
class PlotCanvas:
    """
    Class handling the plotting area in the application.
    """

    def __init__(self, container):
        """
        The constructor configures the Matplotlib figure that
        will contain all plots, creates the base axes and connects
        events to the plotting area.

        :param container: The parent container in which to draw plots.
        :rtype: PlotCanvas
        """
        # Options
        self.x_margin = 15  # pixels
        self.y_margin = 25  # Pixels

        # Parent container
        self.container = container

        # Plots go onto a single matplotlib.figure
        self.figure = Figure(dpi=50)  # TODO: dpi needed?
        self.figure.patch.set_visible(False)

        # These axes show the ticks and grid. No plotting done here.
        # New axes must have a label, otherwise mpl returns an existing one.
        self.axes = self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label="base", alpha=0.0)
        self.axes.set_aspect(1)
        self.axes.grid(True)

        # The canvas is the top level container (Gtk.DrawingArea)
        self.canvas = FigureCanvas(self.figure)
        # self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
        # self.canvas.setFocus()

        #self.canvas.set_hexpand(1)
        #self.canvas.set_vexpand(1)
        #self.canvas.set_can_focus(True)  # For key press

        # Attach to parent
        #self.container.attach(self.canvas, 0, 0, 600, 400)  # TODO: Height and width are num. columns??
        self.container.addWidget(self.canvas)  # Qt

        # Copy a bitmap of the canvas for quick animation.
        # Update every time the canvas is re-drawn.
        self.background = self.canvas.copy_from_bbox(self.axes.bbox)

        # Events
        self.canvas.mpl_connect('button_press_event', self.on_mouse_press)
        self.canvas.mpl_connect('button_release_event', self.on_mouse_release)
        self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
        #self.canvas.connect('configure-event', self.auto_adjust_axes)
        self.canvas.mpl_connect('resize_event', self.auto_adjust_axes)
        #self.canvas.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK)
        #self.canvas.connect("scroll-event", self.on_scroll)
        self.canvas.mpl_connect('scroll_event', self.on_scroll)
        self.canvas.mpl_connect('key_press_event', self.on_key_down)
        self.canvas.mpl_connect('key_release_event', self.on_key_up)
        self.canvas.mpl_connect('draw_event', self.on_draw)

        self.mouse = [0, 0]
        self.key = None

        self.pan_axes = []
        self.panning = False

    def on_key_down(self, event):
        """

        :param event:
        :return:
        """
        FlatCAMApp.App.log.debug('on_key_down(): ' + str(event.key))
        self.key = event.key

    def on_key_up(self, event):
        """

        :param event:
        :return:
        """
        self.key = None

    def mpl_connect(self, event_name, callback):
        """
        Attach an event handler to the canvas through the Matplotlib interface.

        :param event_name: Name of the event
        :type event_name: str
        :param callback: Function to call
        :type callback: func
        :return: Connection id
        :rtype: int
        """
        return self.canvas.mpl_connect(event_name, callback)

    def mpl_disconnect(self, cid):
        """
        Disconnect callback with the give id.
        :param cid: Callback id.
        :return: None
        """
        self.canvas.mpl_disconnect(cid)

    def connect(self, event_name, callback):
        """
        Attach an event handler to the canvas through the native GTK interface.

        :param event_name: Name of the event
        :type event_name: str
        :param callback: Function to call
        :type callback: function
        :return: Nothing
        """
        self.canvas.connect(event_name, callback)

    def clear(self):
        """
        Clears axes and figure.

        :return: None
        """

        # Clear
        self.axes.cla()
        try:
            self.figure.clf()
        except KeyError:
            FlatCAMApp.App.log.warning("KeyError in MPL figure.clf()")

        # Re-build
        self.figure.add_axes(self.axes)
        self.axes.set_aspect(1)
        self.axes.grid(True)

        # Re-draw
        self.canvas.draw_idle()

    def adjust_axes(self, xmin, ymin, xmax, ymax):
        """
        Adjusts all axes while maintaining the use of the whole canvas
        and an aspect ratio to 1:1 between x and y axes. The parameters are an original
        request that will be modified to fit these restrictions.

        :param xmin: Requested minimum value for the X axis.
        :type xmin: float
        :param ymin: Requested minimum value for the Y axis.
        :type ymin: float
        :param xmax: Requested maximum value for the X axis.
        :type xmax: float
        :param ymax: Requested maximum value for the Y axis.
        :type ymax: float
        :return: None
        """

        # FlatCAMApp.App.log.debug("PC.adjust_axes()")

        width = xmax - xmin
        height = ymax - ymin
        try:
            r = width / height
        except ZeroDivisionError:
            FlatCAMApp.App.log.error("Height is %f" % height)
            return
        canvas_w, canvas_h = self.canvas.get_width_height()
        canvas_r = float(canvas_w) / canvas_h
        x_ratio = float(self.x_margin) / canvas_w
        y_ratio = float(self.y_margin) / canvas_h

        if r > canvas_r:
            ycenter = (ymin + ymax) / 2.0
            newheight = height * r / canvas_r
            ymin = ycenter - newheight / 2.0
            ymax = ycenter + newheight / 2.0
        else:
            xcenter = (xmax + xmin) / 2.0
            newwidth = width * canvas_r / r
            xmin = xcenter - newwidth / 2.0
            xmax = xcenter + newwidth / 2.0

        # Adjust axes
        for ax in self.figure.get_axes():
            if ax._label != 'base':
                ax.set_frame_on(False)  # No frame
                ax.set_xticks([])  # No tick
                ax.set_yticks([])  # No ticks
                ax.patch.set_visible(False)  # No background
                ax.set_aspect(1)
            ax.set_xlim((xmin, xmax))
            ax.set_ylim((ymin, ymax))
            ax.set_position([x_ratio, y_ratio, 1 - 2 * x_ratio, 1 - 2 * y_ratio])

        # Sync re-draw to proper paint on form resize
        self.canvas.draw()

    def auto_adjust_axes(self, *args):
        """
        Calls ``adjust_axes()`` using the extents of the base axes.

        :rtype : None
        :return: None
        """

        xmin, xmax = self.axes.get_xlim()
        ymin, ymax = self.axes.get_ylim()
        self.adjust_axes(xmin, ymin, xmax, ymax)

    def zoom(self, factor, center=None):
        """
        Zooms the plot by factor around a given
        center point. Takes care of re-drawing.

        :param factor: Number by which to scale the plot.
        :type factor: float
        :param center: Coordinates [x, y] of the point around which to scale the plot.
        :type center: list
        :return: None
        """

        xmin, xmax = self.axes.get_xlim()
        ymin, ymax = self.axes.get_ylim()
        width = xmax - xmin
        height = ymax - ymin

        if center is None or center == [None, None]:
            center = [(xmin + xmax) / 2.0, (ymin + ymax) / 2.0]

        # For keeping the point at the pointer location
        relx = (xmax - center[0]) / width
        rely = (ymax - center[1]) / height

        new_width = width / factor
        new_height = height / factor

        xmin = center[0] - new_width * (1 - relx)
        xmax = center[0] + new_width * relx
        ymin = center[1] - new_height * (1 - rely)
        ymax = center[1] + new_height * rely

        # Adjust axes
        for ax in self.figure.get_axes():
            ax.set_xlim((xmin, xmax))
            ax.set_ylim((ymin, ymax))

        # Async re-draw
        self.canvas.draw_idle()

    def pan(self, x, y):
        xmin, xmax = self.axes.get_xlim()
        ymin, ymax = self.axes.get_ylim()
        width = xmax - xmin
        height = ymax - ymin

        # Adjust axes
        for ax in self.figure.get_axes():
            ax.set_xlim((xmin + x * width, xmax + x * width))
            ax.set_ylim((ymin + y * height, ymax + y * height))

        # Re-draw
        self.canvas.draw_idle()

    def new_axes(self, name):
        """
        Creates and returns an Axes object attached to this object's Figure.

        :param name: Unique label for the axes.
        :return: Axes attached to the figure.
        :rtype: Axes
        """

        return self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label=name)

    def on_scroll(self, event):
        """
        Scroll event handler.

        :param event: Event object containing the event information.
        :return: None
        """

        # So it can receive key presses
        # self.canvas.grab_focus()
        self.canvas.setFocus()

        # Event info
        # z, direction = event.get_scroll_direction()

        if self.key is None:

            if event.button == 'up':
                self.zoom(1.5, self.mouse)
            else:
                self.zoom(1 / 1.5, self.mouse)
            return

        if self.key == 'shift':

            if event.button == 'up':
                self.pan(0.3, 0)
            else:
                self.pan(-0.3, 0)
            return

        if self.key == 'control':

            if event.button == 'up':
                self.pan(0, 0.3)
            else:
                self.pan(0, -0.3)
            return

    def on_mouse_press(self, event):

        # Check for middle mouse button press
        if event.button == 2:

            # Prepare axes for pan (using 'matplotlib' pan function)
            self.pan_axes = []
            for a in self.figure.get_axes():
                if (event.x is not None and event.y is not None and a.in_axes(event) and
                        a.get_navigate() and a.can_pan()):
                    a.start_pan(event.x, event.y, 1)
                    self.pan_axes.append(a)

            # Set pan view flag
            if len(self.pan_axes) > 0:
                self.panning = True;

    def on_mouse_release(self, event):

        # Check for middle mouse button release to complete pan procedure
        if event.button == 2:
            for a in self.pan_axes:
                a.end_pan()

            # Clear pan flag
            self.panning = False

    def on_mouse_move(self, event):
        """
        Mouse movement event hadler. Stores the coordinates. Updates view on pan.

        :param event: Contains information about the event.
        :return: None
        """
        self.mouse = [event.xdata, event.ydata]

        # Update pan view on mouse move
        if self.panning is True:
            for a in self.pan_axes:
                a.drag_pan(1, event.key, event.x, event.y)

            # Async re-draw (redraws only on thread idle state, uses timer on backend)
            self.canvas.draw_idle()

    def on_draw(self, renderer):

        # Store background on canvas redraw
        self.background = self.canvas.copy_from_bbox(self.axes.bbox)
Ejemplo n.º 32
0
class PlotCanvas(FigureCanvas):
    """
    Plotcanvas class, inherits from matplotlib's FigureCanvas class
    and extends it with specific methods and attributes.
    """
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        """
        init
        :param parent: parent object of plotcanvas (which causes some error for save fig)
        :param width: width of canvas in inches
        :param height: height of canvas in inches
        :param dpi: dots per inch. resolution density
        """

        self.parent = parent
        self.plot_width = width
        self.plot_height = height
        self.plot_dpi = dpi

        # Algorithm buffer array (self.Algorithm.array)
        self.algorithm = None

        # init of matplotlib figure, axes and canvas
        self._init_matplotlib_objects()

        # init drag'n'drop and zoom
        self._init_dragndrop()

        # init spinbox_currentposition
        self.spinbox_currentposition = None

        # init colorbar
        self.colorbar = None

        # save plot and plot objects
        self._plot_inits()

        # more inits
        self._additional_inits()

    def _additional_inits(self):
        """
        misc inits
        """
        # interpolated points
        self.points_for_interpolation = []
        # sleep time for play animation
        self.sleep_time = 500
        # tracing
        self.tracing_switch = False
        self.current_points = None
        self.zoom = 5
        # xy coords
        self.current_x = 0.0
        self.current_y = 0.0
        # start point
        self.clicked_start_points = []
        self.start_point_click = None

    def set_algorithm(self, Algorithm):
        """
        :param Algorithm: Algorithm object from optimization.algorithms
        """
        self.algorithm = Algorithm

###########################################
#  init plotcanvas objects and functions  #
###########################################

    def _plot_inits(self):
        """
        plot (not figure or plot canvas) related inits
        """
        self.curve = self._init_curve()
        self.points = self._init_points()
        self.scatter_points = None  # init needed in Algorithm because of colormap scale (self._init_scatter_points())
        self.vectors = []
        self.lines = []
        self.lowest_point = self._init_lowestpoint()
        self.nextpoint = self._init_nextpoint()

    def _init_dragndrop(self):
        """
        initialisation parameters for drag'n'drop functions
        """
        self.press = None
        self.cur_xlim = None
        self.cur_ylim = None
        self.x0 = None
        self.y0 = None
        self.x1 = None
        self.y1 = None
        self.fig.canvas.mpl_connect('button_press_event',
                                    lambda event: drag(self, event))
        self.fig.canvas.mpl_connect('button_release_event',
                                    lambda event: drop(self))
        self.fig.canvas.mpl_connect('motion_notify_event',
                                    lambda event: move(self, event))

        # init mouse wheel zoom
        self.fig.canvas.mpl_connect('scroll_event',
                                    lambda event: zoom(self, event))

    def _init_matplotlib_objects(self):
        """
        primary initialisation of matplotlib canvas objects
        """
        self.fig = Figure(figsize=(self.plot_width, self.plot_height),
                          dpi=self.plot_dpi)
        self.canvas = FigureCanvas(self.fig)
        self.axes = self.fig.add_subplot(
            111, facecolor=(0.94, 0.94, 0.94))  # bg color of plot frame
        self.fig.set_facecolor((0.94, 0.94, 0.94))  # frame color

        # Superclass FigureCanvas from matplotlib is called.
        FigureCanvas.__init__(self, self.fig)
        self.setParent(self.parent)  # set parenthood

        # size policy
        FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

        # disable display coordinates
        self.axes.format_coord = lambda x, y: ""

        # set axes and grid
        self.axes.cla()
        self.axes.set_xlim(-5, 5)
        self.axes.set_ylim(0, 10)
        self.axes.grid(color='gray',
                       alpha=0.5,
                       linestyle='dashed',
                       linewidth=0.5)

        # set origin axes
        self.axes.spines['left'].set_position('zero')  # x
        self.axes.spines['right'].set_color('none')
        self.axes.yaxis.tick_left()
        self.axes.spines['bottom'].set_position('zero')  # y
        self.axes.spines['top'].set_color('none')

        # no gap between axes and figure
        self.fig.tight_layout()

    def _init_curve(self):
        """
        :return: empty function curve
        """
        curve = self.axes.plot([], [])[0]
        return curve

    def _init_points(self):
        """
        :return: empty set of points
        """
        return self.axes.plot([], [],
                              color='#339966',
                              marker='o',
                              linestyle='dashed',
                              markersize=5)[0]

    def _init_scatter_points(self):
        if self.algorithm.scatter:
            colormap_name = self.algorithm.scatter_colormapname
            minimum = self.algorithm.scatter_min
            maximum = self.algorithm.scatter_max
            self.scatter_points = self.axes.scatter(
                [0, 0], [0, 0],
                marker='o',
                c=[minimum, maximum],
                cmap=plt.get_cmap(colormap_name))

            if self.colorbar is None:
                self.colorbar = self.fig.colorbar(self.scatter_points)
            else:
                self.fig.get_axes()[1].set_visible(False)

    def _init_nextpoint(self):
        """
        :return: empty set of points
        """
        return self.axes.plot([], [],
                              color='black',
                              marker='x',
                              linestyle='dashed',
                              markersize=10)[0]

    def _init_lowestpoint(self):
        """
        :return: empty set of points
        """
        return self.axes.plot([], [],
                              color='c',
                              marker='*',
                              linestyle='dashed',
                              markersize=6)[0]

#############################
# update plotcanvas objects #
#############################

    def show_plot(self):
        """
        sets all necessary updates and flushes them

        """

        if self.algorithm.array().lowest_point:
            self.current_points = self.algorithm.array().lowest_point
            self._update_lowest_point(self.current_points)
        else:
            self._update_lowest_point([])

        if self.algorithm.array().nextpoint:
            self.current_points = self.algorithm.array().nextpoint
            self._update_nextpoint(self.current_points)
        else:
            self._update_nextpoint([(), 'black'])

        # change plot with new x, y values (points)
        if self.algorithm.array().points:
            self.current_points = self.algorithm.array().points
            self._update_figure_points(self.current_points)
        else:
            self._update_figure_points([])

        # change plot with new vectors
        if self.algorithm.array().vectors:
            vectors = self.algorithm.array().vectors
            self._update_figure_vectors(vectors)
        else:
            self._update_figure_vectors([])

        # change plot with new lines
        if self.algorithm.array().lines:
            lines = self.algorithm.array().lines
            self._update_figure_lines(lines)
        else:
            self._update_figure_lines([])

        if self.algorithm.array().scatterpoints_position:
            scatterpos = self.algorithm.array().scatterpoints_position
            self.update_figure_scatter_points(scatterpos)
        else:
            self.update_figure_scatter_points(0, clear=True)

    def update_one_step(self, direction, play=False):
        """
        updates plot canvas and all other plot objects for one step in
        given diretion. Used for next/previous and play button.
        :param direction: direction of update related to buffer array (next or previous)
        :param play: determines if called from animation or one/last/first step call
        :return: current pseudocode position
        """

        if play:
            # sleep to make animation slower or faster
            self._sleep()

        if self.tracing_switch and self.current_points is not None:
            # traces current main point
            self._trace_current_focus()
            self.update_figure_plot()

        if self._check_boundaries(direction):
            if direction == "next":  # forwards
                self.algorithm.array.current_step += 1
            else:  # backwards
                self.algorithm.array.current_step -= 1

            # spinbox
            self.spinbox_currentposition.setValue(
                self.algorithm.array.current_step + 1)

            # update plot
            self.show_plot()

            # pseudocode update
            pseudocode_pos = self.algorithm.array().pseudocodeline

            return pseudocode_pos
        else:
            return None

    def update_figure_plot(self, reset=False, ObjectiveFunction=None):
        """
        updates figure curve with newly set limits.
        Called for example by navigation.
        :param reset: determines if function call has been made from reset button
        :param ObjectiveFunction: = none means, that no funcion has been chosen yet
        """
        if not (ObjectiveFunction or self.algorithm) or reset:
            x, y = (0, 0)
        else:
            # get x values for newly set limits
            x = np.linspace(self.axes.get_xlim()[0],
                            self.axes.get_xlim()[1], 1000)
            # get y values from current algorithm
            if ObjectiveFunction:
                y = ObjectiveFunction(x)
            else:
                y = self.algorithm.ObjectiveFunction(x)

        # draw calculated x/y values
        self.curve.set_data(x, y)
        self.curve.figure.canvas.draw_idle()
        self.fig.canvas.flush_events()

    def _update_figure_points(self, points):
        """
        updates points in plot
        :param points: contains x, y coordinates of to be added points
        """
        x_array, y_array = [], []
        for x, y in points:
            x_array.append(x)
            y_array.append(y)
        self.points.set_data(x_array, y_array)
        self.points.figure.canvas.draw_idle()
        self.fig.canvas.flush_events()

    def update_figure_scatter_points(self, position, clear=False):
        """
        updates scatter points in plot
        :param position: current position in buffer array
        :param clear: boolean to remove scatter points
        """
        if clear or position == 0:
            if self.scatter_points:
                self.scatter_points.remove()
            self._init_scatter_points()
        else:
            points = np.array(
                self.algorithm.array.scatterpoint_array)[:position, :2]
            c = np.array(self.algorithm.array.scatterpoint_array)[:position, 2]
            self.scatter_points.set_offsets(points)
            self.scatter_points.set_array(c)
            self.colorbar.update_bruteforce(self.scatter_points)
            self.fig.get_axes()[1].set_visible(True)

        self.points.figure.canvas.draw_idle()
        self.fig.canvas.flush_events()

    def _update_figure_vectors(self, vectors):
        """
        updates vectors in plot
        :param vectors: arrow coords from (x, y) to (x+dx, y+dy).
        """
        # delete old vectors
        for old_vector in self.vectors:
            old_vector.remove()
        # create new vectors
        self.vectors = []

        for x, y, dx, dy in vectors:
            arrow_length = np.sqrt(dx**2 + dy**2)
            axes_length = self.axes.get_ylim()[1] - self.axes.get_ylim()[0]
            if arrow_length > axes_length * 0.1:
                arrow_width = 0.008 * axes_length
                arrow_head_length = 0.2
            else:
                arrow_width = 0.2 * arrow_length
                arrow_head_length = arrow_length * 0.3
            arrow_head_width = arrow_width * 3
            self.vectors.append(
                self.axes.arrow(x,
                                y,
                                dx,
                                dy,
                                length_includes_head=True,
                                width=arrow_width,
                                head_length=arrow_head_length,
                                head_width=arrow_head_width,
                                fc='lightblue',
                                ec='black'))
        self.curve.figure.canvas.draw_idle()
        self.fig.canvas.flush_events()

    def _update_figure_lines(self, lines):
        """
        updates lines
        :param lines: line objects with start and end x, y coordinates
        """
        # delete old lines
        for line in self.lines:
            line[0].remove()
        # create new lines
        self.lines = []
        for x, y, end_x, end_y in lines:
            self.lines.append(self.axes.plot([x, end_x], [y, end_y], 'g--'))
        self.points.figure.canvas.draw_idle()
        self.fig.canvas.flush_events()

    def _update_nextpoint(self, nextpoint):
        """
        updates nextpoint in plot
        :param nextpoint: x, y coordinates of next point
        """
        points, color = nextpoint
        x_array, y_array = [], []
        for x, y in points:
            x_array.append(x)
            y_array.append(y)
        self.nextpoint.set_color(color)
        self.nextpoint.set_data(x_array, y_array)
        self.nextpoint.figure.canvas.draw_idle()
        self.fig.canvas.flush_events()

    def _update_lowest_point(self, lowest_points):
        """
        updates lowest_point in plot
        :param lowest_points: x, y coordinates of lowest point
        """
        x_array, y_array = [], []
        for x, y in lowest_points:
            x_array.append(x)
            y_array.append(y)
        self.lowest_point.set_data(x_array, y_array)
        self.lowest_point.figure.canvas.draw_idle()
        self.fig.canvas.flush_events()

#########
# Reset #
#########

    def reset_plot_objects(self):
        """
        reset plot objects (doesnt renew plotcanvas)
        """
        self._update_figure_points([])
        if self.algorithm is not None and self.algorithm.scatter is not None:
            self.update_figure_scatter_points(0, clear=True)
        self._update_figure_vectors([])
        self._update_figure_lines([])
        self.axes.set_ylim(0, 10)
        self.axes.set_xlim(-5, 5)

    def reset_figure(self):
        """
        resets curve
        """
        self.update_figure_plot(reset=True)

    def reset_plot(self):
        """
        called when reset button is clicked
        """
        # (re)set axes and grid
        self.axes.clear()
        self.axes.cla()
        self.axes.set_xlim(-5, 5)
        self.axes.set_ylim(0, 10)
        self.axes.grid(color='gray',
                       alpha=0.5,
                       linestyle='dashed',
                       linewidth=0.5)

        # set origin axes
        self.axes.spines['left'].set_position('zero')  # x
        self.axes.spines['right'].set_color('none')
        self.axes.yaxis.tick_left()
        self.axes.spines['bottom'].set_position('zero')  # y
        self.axes.spines['top'].set_color('none')

        # init plot
        self._plot_inits()

################
# click points #
################

    def get_start_point(self, func):
        """
        connects click events to function that collects start point.
        :param func: function needs to be passed on to compute y point later
        :return: connect_id to unconnect after clicking start point points is finished
        """
        self.func = func
        # needs to be disconneted first as interpolated function that collects multiple and not just one
        # set of coordinates could be connected to the same event first
        self.fig.canvas.mpl_disconnect('button_press_event')
        connect_id = self.fig.canvas.mpl_connect('button_press_event',
                                                 self._on_one_click)
        return connect_id

    def _on_one_click(self, click):
        """
        called from get_start_points. saves the last x value as start point and make the former
        clicked ones invisible.
        :param click: click event object that contains x, y coordinates
        """
        if click.xdata is not None and click.ydata is not None:
            x = click.xdata
            # calc y from func(x)
            y = self.func(x)
            # enqueue newest clicked point
            self.clicked_start_points.append(
                self.axes.plot(x,
                               y,
                               color="blue",
                               marker='o',
                               linestyle="dashed",
                               markersize=4))
            # save start point
            self.start_point_click = x
            # make all but the last one invisible
            n = len(self.clicked_start_points)
            for p in self.clicked_start_points[:(n - 1)]:
                p[0].set_visible(False)

    def get_clicked_coords(self):
        """
        connects click events to function that collects interpolated points
        :return: connect_id to unconnect after clicking points is finished
        """
        self.clicked_points = []
        # needs to be disconnected first (see get_start_point)
        self.fig.canvas.mpl_disconnect('button_press_event')
        connect_id = self.fig.canvas.mpl_connect('button_press_event',
                                                 self._onclick)
        return connect_id

    def _onclick(self, click):
        """
        called by get_clicked_coords to get set of points on canvas
        """
        if click.xdata is not None and click.ydata is not None:
            self.clicked_points.append((click.xdata, click.ydata))
            new_point = self.clicked_points[len(self.clicked_points) - 1]
            self.points_for_interpolation.append(
                self.axes.plot(new_point[0],
                               new_point[1],
                               color='green',
                               marker='o',
                               linestyle='dashed',
                               markersize=2))

    def remove_interpolated_points(self):
        """
        set (the green) points that were used to display interpolated points as invisible
        """
        for p in self.points_for_interpolation:
            p[0].set_visible(False)

########################
# Helper defs and misc #
########################

    def set_axes(self, startpoint):
        """
        sets inital depending on funtion object and start point
        :param: startpoint: start point of calculation
        """
        y_min, y_max, x_min, x_max = self.algorithm.ObjectiveFunction.get_axes_parameters(
        )

        if str(self.algorithm.ObjectiveFunction) == "Sinus" \
                or str(self.algorithm.ObjectiveFunction) == "Torsion Potential":
            tmp = x_min
            x_min = startpoint - (x_max - x_min) / 2
            x_max = startpoint + (x_max - tmp) / 2
        if str(self.algorithm.ObjectiveFunction) == "Interpolated" \
                or str(self.algorithm.ObjectiveFunction) == "Polynomial":
            if x_max < startpoint:
                x_max = startpoint + 1
            elif x_min > startpoint:
                x_min = startpoint - 1
            if y_max < self.algorithm.ObjectiveFunction(startpoint):
                y_max = self.algorithm.ObjectiveFunction(startpoint) + 1
            elif y_min > self.algorithm.ObjectiveFunction(startpoint):
                y_min = self.algorithm.ObjectiveFunction(startpoint) - 1

        self.axes.set_ylim(y_min, y_max)
        self.axes.set_xlim(x_min, x_max)

    def _sleep(self):
        """
        sleep between mouse x/y coords request
        (for better performance, rather then tracing mouse movements
        all the time)
        """
        loop = QEventLoop()
        QTimer.singleShot(self.sleep_time, loop.quit)
        loop.exec_()

    def _trace_current_focus(self):
        """
        tracing mode depening on zoom factor
        """
        x, y = self.current_points[0]
        self.axes.set_ylim(y - self.zoom, y + self.zoom)
        self.axes.set_xlim(x - self.zoom, x + self.zoom)

    def _check_boundaries(self, direction):
        """
        'if-overhead' for boundaries
        """
        if (direction == "next" and self.algorithm.array.current_step + 1 < len(self.algorithm.array)) or \
           (direction == "prev" and self.algorithm.array.current_step > 0):
            return True
        else:
            return False
Ejemplo n.º 33
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, **kws):

        self.trace_color_callback = trace_color_callback
        matplotlib.rc('axes', axisbelow=True)
        matplotlib.rc('lines', linewidth=2)
        matplotlib.rc('xtick', labelsize=fontsize, color='k')
        matplotlib.rc('ytick', labelsize=fontsize, color='k')
        matplotlib.rc('legend', fontsize=fontsize)
        matplotlib.rc('grid',  linewidth=0.5, linestyle='-')

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

        self.conf = PlotConfig(panel=self, 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
        if facecolor is not None:
            self.conf.bgcolor = facecolor
        if axisbg is not None:
            self.conf.bgcolor = axisbg

        # 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 = {}

    def plot(self, xdata, ydata, side='left', title=None,
             xlabel=None, ylabel=None, y2label=None,
             use_dates=False, **kws):
        """
        plot (that is, create a new plot: clear, then oplot)
        """
        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.ntrace  = 0
        self.conf.yscale = 'linear'
        self.conf.user_limits[axes] = [None, None, None, 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)
        if use_dates is not None:
            self.use_dates  = 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, legendfontsize=None,
              fullbox=None, axes_style=None, zorder=None, viewpad=None, **kws):
        """ basic plot method, overplotting any existing plot """
        self.cursor_mode = 'zoom'
        conf = self.conf
        conf.plot_type = 'lineplot'
        axes = self.axes
        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))
        if self.use_dates:
            xdata = [datetime.fromtimestamp(i) for i in xdata]
            xdata = dates.date2num(xdata)
            # axes.xaxis.set_major_locator(dates.AutoDateLocator())

        if linewidth is None:
            linewidth = 2

        if viewpad is not None:
            conf.viewpad = 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

        if grid is not None:
            conf.show_grid = 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]

        if xmin is not None:
            conf.user_limits[axes][0] = xmin
        if xmax is not None:
            conf.user_limits[axes][1] = xmax
        if ymin is not None:
            conf.user_limits[axes][2] = ymin
        if ymax is not None:
            conf.user_limits[axes][3] = ymax

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

        n = conf.ntrace
        if zorder is None:
            zorder = 5*(n+1)
        if axes not in conf.axes_traces:
            conf.axes_traces[axes] = []
        conf.axes_traces[axes].append(n)

        if bgcolor is not None:
            conf.bgcolor = bgcolor
            axes.set_axis_bgcolor(bgcolor)
        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 gridcolor is not None:
            conf.gridcolor = gridcolor
        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)
        conf.set_trace_datarange(datarange)
        needs_relabel = False
        if labelfontsize is not None:
            conf.labelfont.set_size(labelfontsize)
            needs_relabel = True

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

        if n < len(conf.lines):
            conf.lines[n] = _lines
        else:
            conf._init_trace(n, '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)

        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:
            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] = 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.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.conf.unzoom(full=False)

    def unzoom_all(self, event=None):
        """ zoom out full data range """
        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)
        kwargs = {'facecolor': self.conf.bgcolor}
        if matplotlib.__version__ < "2.0":
            kwargs = {'axisbg': self.conf.bgcolor}

        self.axes  = self.fig.add_subplot(self.gridspec[0], **kwargs)

        self.canvas = FigureCanvas(self, -1, self.fig)

        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.Fit()

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

    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= %s, %s" % (self._xfmt, self._yfmt)) % (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= %s, %s, %s" % (self._xfmt, self._yfmt,
                                              self._y2fmt) % (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)
Ejemplo n.º 34
0
def plot_varCaO2(gases, num, path, ident="", save=False, pyplot=False):
    """
    plot CaO2 and CaO2 variation (slope)
    input :
        gases = list of bg.Gas,
        num = location in the list
        path = path to save
        ident = string to identify in the save name
        save : boolean
        pyplot : boolean (True: retrun a pyplot,    else a Figure obj)
    output : plot (pyplot or FigureObj)
    """
    if num > 1:
        gas = gases[num]
    else:
        gas = gases[0]
    species = gas.spec
    paO2 = gas.po2
    hb = gas.hb
    if paO2 > 200:
        O2max = paO2 + 50
    else:
        O2max = 200
    O2Range = np.arange(1, O2max)
    sat = satHbO2(species, paO2)

    if pyplot:
        fig = plt.figure(figsize=(10, 8))
    else:
        fig = Figure(figsize=(10, 8))
    st = species + " ( $SatHb_{O_2} \ et \ \Delta SatHb_{O_2}$)"
    fig.suptitle(st, fontsize=24, backgroundcolor="w", color="tab:gray")

    ax1 = fig.add_subplot(111)  # ax1
    ax1.get_yaxis().tick_left()
    ax1.plot([100, 100], [0, 110], "tab:red", linewidth=10, alpha=0.5)  # line ref
    ax1.plot([40, 40], [0, 110], color="tab:blue", linewidth=26, alpha=0.5)
    ax1.plot(
        O2Range, satHbO2(species, O2Range), linewidth=2, color="tab:blue"
    )  # mesure
    ax1.plot([paO2, paO2], [0, sat], "tab:gray", linewidth=1)
    ax1.plot([0, paO2], [sat, sat], "tab:gray", linewidth=1)
    ax1.plot(paO2, sat, "o-", color="tab:red", markersize=22, alpha=0.7)
    ax1.set_ylabel("satHb (%)", color="tab:blue")
    ax1.set_xlabel(r"$P_{O_2}$", color="tab:gray")
    ax1.set_ylim(0, 100)

    ax2 = ax1.twinx()
    ax2.get_yaxis().tick_right()
    ax2.plot(
        O2Range,
        ((caO2(species, hb, O2Range + 1) - caO2(species, hb, O2Range))),
        color="tab:green",
    )
    ax2.plot(
        paO2,
        caO2(species, hb, paO2 + 1) - caO2(species, hb, paO2),
        "o-",
        color="tab:red",
        markersize=22,
        alpha=0.7,
    )
    for tl in ax2.get_yticklabels():
        tl.set_color("tab:green")
    ax2.set_ylabel("variation de CaO2 par variation de PO2", color=("tab:green"))
    #    ax2.set_xlabel(r'$P_{O_2}$')
    for ax in fig.get_axes():
        ax.axes.tick_params(colors="tab:gray")
        # ax.grid()
        for spine in ["left", "top", "right"]:
            ax.spines[spine].set_visible(False)
    # fig.set.tight_layout(True)
    if pyplot:
        plt.show()
        if save:
            name = os.path.join(path, (str(ident) + "varCaO2"))
            name = os.path.expanduser(name)
            saveGraph(name, ext="png", close=True, verbose=True)
    return fig
Ejemplo n.º 35
0
    def initialize(self):
        self.grid()

        # create Frames
        file_frame = tk.Frame(self, width=self.width, height=self.height*0.05, pady=3, relief=tk.GROOVE, borderwidth = 1.5)
        file_frame.grid(row=0, columnspan=3, sticky="nsew")
        display_frame = tk.Frame(self, width=self.width, height=self.height*0.85, padx=3, pady=3)
        display_frame.grid(row=1, columnspan=3, sticky="nsew")
        action_frame = tk.Frame(self, width=self.width, height=self.height*0.1, pady=3)
        action_frame.grid(row=3, columnspan=3, sticky="nsew")

        draw1_frame = tk.Frame(center, width=self.width*0.35, height=self.height*0.85, relief=tk.GROOVE, borderwidth = 0.5)
        draw2_frame = tk.Frame(center, width=self.width*0.35, height=self.height*0.85, relief=tk.GROOVE, padx=3, pady=3, borderwidth = 0.5)
        display1_frame = tk.Frame(center, width=self.width*0.15, height=self.height*0.85, relief=tk.GROOVE, padx=3, pady=3, borderwidth = 0.5)
        display2_frame = tk.Frame(center, width=self.width*0.15, height=self.height*0.85, relief=tk.GROOVE, padx=3, pady=3, borderwidth = 0.5)
        draw1_frame.grid(row=0, column=1, sticky="nsew")
        draw1_frame.grid_propagate(0)
        draw2_frame.grid(row=0, column=2, sticky="nsew")
        draw2_frame.grid_propagate(0)
        display1_frame.grid(row=0, column=0, sticky="nsew")
        display1_frame.grid_propagate(0)
        display2_frame.grid(row=0, column=3, sticky="nsew")
        display2_frame.grid_propagate(0)

        action_frame = tk.Frame(btm_frame, width=self.width*0.6, height=self.height*0.1, relief=tk.GROOVE, borderwidth = 0.5)
        action_frame.grid_propagate(0)
        decision_frame = tk.Frame(btm_frame, width=self.width*0.4, height=self.height*0.1, relief=tk.GROOVE, pady=3, borderwidth = 0.5)
        decision_frame.grid_propagate(0)
        action_frame.grid(row=0, column=0, sticky="nsew")
        decision_frame.grid(row=0, column=2, sticky="nsew")

        option_frame = tk.Frame(display1_frame, width=self.width*0.15, height=self.height*0.85*0.35, relief=tk.GROOVE, borderwidth = 0.5)
        option_frame.grid_propagate(0)
        display_frame = tk.Frame(display1_frame, width=self.width*0.15, height=self.height*0.85*0.45, relief=tk.GROOVE, borderwidth = 0.5)
        display_frame.grid_propagate(0)
        display2_frame_frame = tk.Frame(display1_frame, width=self.width*0.15, height=self.height*0.85*0.2, relief=tk.GROOVE, borderwidth = 0.5)
        display2_frame_frame.grid_propagate(0)
        option_frame.grid(row=0, column=0, sticky="nsew")
        display_frame.grid(row=1, column=0, sticky="nsew")
        display2_frame_frame.grid(row=2, column=0, sticky="nsew")


        # create areas for drawing face
        fig_face = Figure()
        yaw = 90
        pitch = 90
        fig_face.get_axes()[0].view_init(yaw,pitch)
        self.axe_face = fig_face.add_axes((0,0,1,1), projection="3d") 
        self.axe_face.set_axis_off()
        self.canvas_face = FigureCanvasTkAgg(fig_face, master=draw1_frame)
        self.canvas_face.get_tk_widget().pack(fill=tk.BOTH, expand=1)

        # for slider frames
        self.w2 = tk.Scale(draw1_frame, from_=0, to=200, length=width*0.35,tickinterval=10, orient=tk.HORIZONTAL)
        w2.pack(anchor = tk.N)

        # label for display open file names
        self.facelift_filename = tk.StringVar().set("   FaceLift file: ")
        label_facelift = tk.Label(file_frame, textvariable=facelift_filename)
        label_facelift.grid(row=0, columnspan=3, sticky=tk.W)
        
        performer_filename = tk.StringVar()
        performer_filename.set("Performer file: ")
        lable_performer = tk.Label(file_frame, textvariable=performer_filename)
        lable_performer.grid(row=1, columnspan=3, sticky=tk.W)

        # create display global indice
        label_global_index_text = tk.Label(display2_frame_frame, text='Global Index: ', font=("Helvetica", 18))
        label_global_index_text.grid(row=0, column=0, columnspan=2)
        global_indice= tk.StringVar().set("")
        label_global_index_num = tk.Label(display2_frame_frame, textvariable=global_indice, padx=10, pady=10, font=("Helvetica", 24), fg="red")
        label_global_index_num.grid(row=3, column=1)
            
        # create display frame indice
        tk.Label(display_frame, text='Frame Info:', font=("Helvetica", 18)).grid(row=0, column=0, columnspan=2)

        label_avg_text = tk.Label(display_frame, text='Avg abs: ', anchor=tk.W, font=("Helvetica", 14), fg="blue")
        label_avg_text.grid(row=1, column=0, sticky=tk.E)
        self.avg_frame = tk.StringVar()
        label_avg_num = tk.Label(display_frame, textvariable=avg_frame, font=("Helvetica", 14), fg="blue")
        label_avg_num.grid(row=1, column=1, sticky=tk.W)

        tk.Label(display_frame, text='Avg rel(%): ', anchor=tk.W,  font=("Helvetica", 14), fg="blue").grid(row=2, column=0, sticky=tk.E)
        self.rel_frame = tk.StringVar()
        label_rel_tnum = tk.Label(display_frame, textvariable=rel_frame, font=("Helvetica", 14), fg="blue")
        label_rel_tnum.grid(row=2, column=1, sticky=tk.W)

        tk.Label(display_frame, text='Max abs: ', font=("Helvetica", 14), fg="blue").grid(row=3, column=0, sticky=tk.E)
        self.max_frame = tk.StringVar()
        label_max_num = tk.Label(display_frame, textvariable=max_frame, font=("Helvetica", 14), fg="blue")
        label_max_num.grid(row=3, column=1, sticky=tk.W)

        tk.Label(display_frame, text='Max rel(%): ', font=("Helvetica", 14), fg="blue").grid(row=4, column=0, sticky=tk.E)
        self.maxrel_frame = tk.StringVar()
        label_maxrel_text1 = tk.Label(display_frame, textvariable=maxrel_frame, font=("Helvetica", 14), fg="blue")
        label_maxrel_text1.grid(row=4, column=1, sticky=tk.W)

        tk.Label(display_frame, text='Min abs: ', font=("Helvetica", 14), fg="blue").grid(row=5, column=0, sticky=tk.E)
        self.min_frame = tk.StringVar()
        label_min_text1 = tk.Label(display_frame, textvariable=min_frame, font=("Helvetica", 14), fg="blue")
        label_min_text1.grid(row=5, column=1, sticky=tk.W)

        tk.Label(display_frame, text='Min rel(%): ', font=("Helvetica", 14), fg="blue").grid(row=6, column=0, sticky=tk.E)
        self.minrel_frame = tk.StringVar()
        label_minrel_text1 = tk.Label(display_frame, textvariable=minrel_frame, font=("Helvetica", 14), fg="blue")
        label_minrel_text1.grid(row=6, column=1, sticky=tk.W)

        tk.Label(display_frame, text='Avg index: ', anchor=tk.W, font=("Helvetica", 14), fg="green").grid(row=7, column=0, sticky=tk.E)
        self.logavg_frame = tk.StringVar()
        label_logavg_text1 = tk.Label(display_frame, textvariable=logavg_frame, font=("Helvetica", 14), fg="green")
        label_logavg_text1.grid(row=7, column=1, sticky=tk.W)

        tk.Label(display_frame, text='Max index: ',  font=("Helvetica", 14), fg="green").grid(row=8, column=0, sticky=tk.E)
        self.logmin_frame = tk.StringVar()
        label_logmin_text1 = tk.Label(display_frame, textvariable=label_logmin_frame, font=("Helvetica", 14), fg="green")
        label_logmin_text1.grid(row=8, column=1, sticky=tk.W)

        tk.Label(display_frame, text='Min index: ',  font=("Helvetica", 14), fg="green").grid(row=9, column=0, sticky=tk.E)
        self.logmin_frame = tk.StringVar()
        label_logmin_text1 = tk.Label(display_frame, textvariable=logmin_frame, font=("Helvetica", 14), fg="green")
        label_logmin_text1.grid(row=9, column=1, sticky=tk.W)

        
        # buttons
        btn_open = tk.Button(action_frame, width=20, height=3, text="Open", command=getFile)
        btn_open.grid(row=0, column=1, padx=10, pady=10)

        btn_FaceBuilder = tk.Button(action_frame, width=20, height=3, text="FaceBuilder")
        btn_FaceBuilder.grid(row=0, column=3, padx=10, pady=10)

        btn_Definition = tk.Button(action_frame, width=20, height=3, text="Definitions")
        btn_Definition.grid(row=0, column=2, padx=10, pady=10)

        btn_ture_plot = tk.Button(action_frame, width=20, height=3, text="Turn Scene", command=tureFig)
        btn_ture_plot.grid(row=0, column=2, padx=10, pady=10)

        
        label_decision = tk.Label(decision_frame, text='Decision: ', font=("Arial", 14))
        label_decision.grid(row=0, column=0, padx=20, pady=20)
        
        btn_success = tk.Button(decision_frame,  width=20, height=3, text="Sucess", bg='green')
        btn_success.grid(row=0, column=1, padx=10, pady=10), success.grid_propagate(0)
        
        btn_fail = tk.Button(decision_frame, width=20, height=3, text="Fail", bg='red', command=create_window)
        btn_fail.grid(row=0, column=2, padx=10, pady=10)
        
        btn_mesh_plot = tk.Button(action_frame, width=20, height=3, text="MeshFace", command=mesh_face)
        btn_mesh_plot.grid(row=0, column=3, padx=10, pady=10)

        btn_c3d_plot = tk.Button(action_frame, width=20, height=3, text="c3d Points", command=refresh_face2)
        btn_c3d_plot.grid(row=0, column=4, padx=10, pady=10)

        btn_facebuilder = tk.Button(action_frame, width=20, height=3, text="FaceBuilder")
        btn_facebuilder.grid(row=0, column=5, padx=10, pady=10)



        # create display options TODO
        display_label = tk.Label(option_frame, text='Display options:', font=("Helvetica", 18))
        Check1 = tk.IntVar()
        Check2 = tk.IntVar()
        Check3 = tk.IntVar()
        Check4 = tk.IntVar()
        C1 = tk.Checkbutton(option_frame, text = "mean per frame", variable = Check4, onvalue = 0, offvalue = 1, height=1, width = 20)
        C2 = tk.Checkbutton(option_frame, text = "max per frame", variable = Check1, onvalue = 1, offvalue = 0, height=1, width = 20)
        C3 = tk.Checkbutton(option_frame, text = "min per frame", variable = Check2, onvalue = 1, offvalue = 0, height=1, width = 20)
        C4 = tk.Checkbutton(option_frame, text = "overall mean", variable = Check3, onvalue = 1, offvalue = 0, height=1, width = 20)

        display_label.grid(row=0, column=0)
        C1.grid(row=1, column=0, padx=2, pady=2, sticky=tk.W)
        C2.grid(row=2, column=0, padx=2, pady=2, sticky=tk.W)
        C3.grid(row=3, column=0, padx=2, pady=2, sticky=tk.W)
        C4.grid(row=4, column=0, padx=2, pady=2, sticky=tk.W)
        
        
        # to do analyse, TODO
        self.threshold = tk.StringVar()
        self.bad_frame_num = tk.StringVar()
        
            
        tk.Label(display2_frame, text='Threshold: ').grid(row=0, column=0, padx=2, pady=2)
        tk.Entry(display2_frame, textvariable=threshold).grid(row=0, column=1, padx=2, pady=2)
        tk.Button(display2_frame, height=1, width=10, text="Process", command=retrieve_input).grid(row=2, column=0, padx=2, pady=2, columnspan=2)
        tk.Label(display2_frame, text='Number of bad frames: ').grid(row=3, column=0, padx=5, pady=5, columnspan=2)
        tk.Label(display2_frame, textvariable=bad_frame_num ).grid(row=4, column=0, padx=2, pady=2, columnspan=2)
Ejemplo n.º 36
0
class streamPick(QtGui.QMainWindow):
    def __init__(self, stream=None, parent=None):
        # Initialising QtGui
        qApp = QtGui.QApplication(sys.argv)

        # Init vars
        if stream is None:
            msg = 'Define stream = obspy.core.Stream()'
            raise ValueError(msg)
        self.st = stream.copy()
        self._picks = []
        self.savefile = None
        self.onset_types = ['emergent', 'impulsive', 'questionable']

        # Load filters from pickle
        try:
            self.bpfilter = pickle.load(open('.pick_filters', 'r'))
        except:
            self.bpfilter = []
        # Internal variables
        # Gui vars
        self._shortcuts = {'st_next': 'c',
                           'st_previous': 'x',
                           'filter_apply': 'f',
                           'pick_p': 'q',
                           'pick_s': 'w',
                           'pick_custom': 't',
                           'pick_remove': 'r',
                           }
        self._plt_drag = None
        self._current_filter = None
        # Init stations
        self._initStations()  # defines list self._stations
        self._stationCycle = cycle(self._stations)
        self._streamStation(self._stationCycle.next())
        # Init QtGui
        QtGui.QMainWindow.__init__(self)
        self.setupUI()
        # exec QtApp
        qApp.exec_()

    def setupUI(self):
        '''
        Setup the UI
        '''
        self.main_widget = QtGui.QWidget(self)
        # Init parts of the UI
        self._initMenu()
        self._createStatusBar()
        self._initPlots()
        self._wadatiPlt = None

        # Define layout
        l = QtGui.QVBoxLayout(self.main_widget)
        l.addLayout(self.btnbar)
        l.addWidget(self.canvas)

        self.setCentralWidget(self.main_widget)
        self.setGeometry(300, 300, 1200, 800)
        self.setWindowTitle('obspy.core.Stream-Picker')
        self.show()

    def _initPlots(self):
        self.fig = Figure(facecolor='.86', dpi=72, frameon=True)
        # Change facecolor
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
        # Draw the matplotlib figure
        self._drawFig()
        # Connect the events
        self.fig.canvas.mpl_connect('scroll_event',
                                    self._pltOnScroll)
        self.fig.canvas.mpl_connect('motion_notify_event',
                                    self._pltOnDrag)
        self.fig.canvas.mpl_connect('button_release_event',
                                    self._pltOnButtonRelease)
        self.fig.canvas.mpl_connect('button_press_event',
                                    self._pltOnButtonPress)

    def _initMenu(self):
        # Next and Prev Button
        nxt = QtGui.QPushButton('Next >>',
                                shortcut=self._shortcuts['st_next'])
        nxt.clicked.connect(self._pltNextStation)
        nxt.setToolTip('shortcut <b>c</d>')
        nxt.setMaximumWidth(150)
        prv = QtGui.QPushButton('<< Prev',
                                shortcut=self._shortcuts['st_previous'])
        prv.clicked.connect(self._pltPrevStation)
        prv.setToolTip('shortcut <b>x</d>')
        prv.setMaximumWidth(150)

        # Stations drop-down
        self.stcb = QtGui.QComboBox(self)
        for st in self._stations:
            self.stcb.addItem(st)
        self.stcb.activated.connect(self._pltStation)
        self.stcb.setMaximumWidth(100)
        self.stcb.setMinimumWidth(80)

        # Filter buttons
        self.fltrbtn = QtGui.QPushButton('Filter Trace',
                                    shortcut=self._shortcuts['filter_apply'])
        self.fltrbtn.setToolTip('shortcut <b>f</b>')
        self.fltrbtn.setCheckable(True)
        #self.fltrbtn.setAutoFillBackground(True)
        self.fltrbtn.setStyleSheet(QtCore.QString(
                    'QPushButton:checked {background-color: lightgreen;}'))
        self.fltrbtn.clicked.connect(self._appFilter)

        self.fltrcb = QtGui.QComboBox(self)
        self.fltrcb.activated.connect(self._changeFilter)
        self.fltrcb.setMaximumWidth(170)
        self.fltrcb.setMinimumWidth(150)
        self._updateFilterCB()  # fill QComboBox

        # edit/delete filer buttons
        fltredit = QtGui.QPushButton('Edit')
        fltredit.resize(fltredit.sizeHint())
        fltredit.clicked.connect(self._editFilter)

        fltrdel = QtGui.QPushButton('Delete')
        fltrdel.resize(fltrdel.sizeHint())
        fltrdel.clicked.connect(self._deleteFilter)

        btnstyle = QtGui.QFrame(fltredit)
        btnstyle.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain)
        btnstyle = QtGui.QFrame(fltrdel)
        btnstyle.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain)

        # onset type
        _radbtn = []
        for _o in self.onset_types:
                _radbtn.append(QtGui.QRadioButton(str(_o[0].upper())))
                _radbtn[-1].setToolTip('Onset ' + _o)
                _radbtn[-1].clicked.connect(self._drawPicks)
                if _o == 'impulsive':
                    _radbtn[-1].setChecked(True)
        self.onsetGrp = QtGui.QButtonGroup()
        self.onsetGrp.setExclusive(True)
        onsetbtns = QtGui.QHBoxLayout()
        for _i, _btn in enumerate(_radbtn):
            self.onsetGrp.addButton(_btn, _i)
            onsetbtns.addWidget(_btn)

        # Arrange buttons
        vline = QtGui.QFrame()
        vline.setFrameStyle(QtGui.QFrame.VLine | QtGui.QFrame.Raised)
        self.btnbar = QtGui.QHBoxLayout()
        self.btnbar.addWidget(prv)
        self.btnbar.addWidget(nxt)
        self.btnbar.addWidget(QtGui.QLabel('Station'))
        self.btnbar.addWidget(self.stcb)
        ##
        self.btnbar.addWidget(vline)
        self.btnbar.addWidget(self.fltrbtn)
        self.btnbar.addWidget(self.fltrcb)
        self.btnbar.addWidget(fltredit)
        self.btnbar.addWidget(fltrdel)
        ##
        self.btnbar.addWidget(vline)
        self.btnbar.addWidget(QtGui.QLabel('Pick Onset: '))
        self.btnbar.addLayout(onsetbtns)
        self.btnbar.addStretch(3)

        # Menubar
        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(QtGui.QIcon().fromTheme('document-save'),
                            'Save', self._saveCatalog)
        fileMenu.addAction(QtGui.QIcon().fromTheme('document-save'),
                            'Save as QuakeML File', self._saveCatalogDlg)
        fileMenu.addAction(QtGui.QIcon().fromTheme('document-open'),
                            'Load QuakeML File', self._openCatalogDlg)
        fileMenu.addSeparator()
        fileMenu.addAction('Save Plot', self._savePlotDlg)
        fileMenu.addSeparator()
        fileMenu.addAction(QtGui.QIcon().fromTheme('application-exit'),
                            'Exit', self.close)
        #windowMenu = menubar.addMenu('&Windows')
        #windowMenu.addAction('Wadati Diagram', self._opnWadatiPlot)
        aboutMenu = menubar.addMenu('&About')
        aboutMenu.addAction(QtGui.QIcon().fromTheme('info'),
                            'Info', self._infoDlg)

    def _drawFig(self):
        '''
        Draws all matplotlib figures
        '''
        num_plots = len(self._current_st)
        self.fig.clear()
        self._appFilter(draw=False)
        for _i, tr in enumerate(self._current_st):
            ax = self.fig.add_subplot(num_plots, 1, _i)
            ax.plot(tr.data, 'k')
            ax.axhline(0, color='k', alpha=.05)
            ax.set_xlim([0, tr.data.size])
            ax.text(.925, .9, self._current_st[_i].stats.channel,
                        transform=ax.transAxes, va='top', ma='left')
            ax.channel = tr.stats.channel
            if _i == 0:
                ax.set_xlabel('Seconds')

        # plot picks
        self._drawPicks(draw=False)
        self.fig.suptitle('%s - %s - %s' % (self._current_st[-1].stats.network,
                            self._current_st[-1].stats.station,
                            self._current_st[-1].stats.starttime.isoformat()),
                            x=.2)
        self._updateSB()
        self._canvasDraw()

    def _initStations(self):
        '''
        Creates a list holding unique station names
        '''
        self._stations = []
        for _tr in self.st:
            if _tr.stats.station not in self._stations:
                self._stations.append(_tr.stats.station)
        self._stations.sort()

    def _getPhases(self):
        '''
        Creates a list holding unique phase names
        '''
        phases = []
        for _pick in self._picks:
            if _pick.phase_hint not in phases:
                phases.append(_pick.phase_hint)
        return phases

    def _streamStation(self, station):
        '''
        Copies the current stream object from self.st through
        obspy.stream.select(station=)
        '''
        if station not in self._stations:
            return
        self._current_st = self.st.select(station=station).copy()
        self._current_stname = station
        self._current_network = self._current_st[0].stats.network
        # Sort and detrend streams
        self._current_st.sort(['channel'])
        self._current_st.detrend('linear')

    def _setPick(self, xdata, phase, channel, polarity='undecideable'):
        '''
        Write obspy.core.event.Pick into self._picks list
        '''
        picktime = self._current_st[0].stats.starttime +\
                (xdata * self._current_st[0].stats.delta)

        this_pick = event.Pick()
        overwrite = True
        # Overwrite existing phase's picktime
        for _pick in self._getPicks():
            if _pick.phase_hint == phase and\
                    _pick.waveform_id.channel_code == channel:
                this_pick = _pick
                overwrite = False
                break

        creation_info = event.CreationInfo(
            author='ObsPy.Stream.pick()',
            creation_time=UTCDateTime())
        # Create new event.Pick()
        this_pick.time = picktime
        this_pick.phase_hint = phase
        this_pick.waveform_id = event.WaveformStreamID(
            network_code=self._current_st[0].stats.network,
            station_code=self._current_st[0].stats.station,
            location_code=self._current_st[0].stats.location,
            channel_code=channel)
        this_pick.evaluation_mode = 'manual'
        this_pick.creation_info = creation_info
        this_pick.onset = self.onset_types[self.onsetGrp.checkedId()]
        this_pick.evaluation_status = 'preliminary'
        this_pick.polarity = polarity
        #if self._current_filter is not None:
        #    this_pick.comments.append(event.Comment(
        #                text=str(self.bpfilter[self.fltrcb.currentIndex()])))
        if overwrite:
            self._picks.append(this_pick)

    def _delPicks(self, network, station, channel):
        '''
        Deletes pick from catalog
        '''
        for _i, _pick in enumerate(self._picks):
            if _pick.waveform_id.network_code == network\
                    and _pick.waveform_id.station_code == station\
                    and _pick.waveform_id.channel_code == channel:
                self._picks.remove(_pick)

    def _getPicks(self):
        '''
        Create a list of picks for the current plot
        '''
        this_st_picks = []
        for _i, pick in enumerate(self._picks):
            if pick.waveform_id.station_code == self._current_stname and\
                    self._current_st[0].stats.starttime <\
                    pick.time < self._current_st[0].stats.endtime:
                this_st_picks.append(_i)
        return [self._picks[i] for i in this_st_picks]

    def _getPickXPosition(self, picks):
        '''
        Convert picktimes into relative positions along x-axis
        '''
        xpicks = []
        for _pick in picks:
            xpicks.append((_pick.time-self._current_st[0].stats.starttime)
                            / self._current_st[0].stats.delta)
        return np.array(xpicks)

    def _drawPicks(self, draw=True):
        '''
        Draw picklines onto axes
        '''
        picks = self._getPicks()
        xpicks = self._getPickXPosition(picks)

        for _ax in self.fig.get_axes():
            lines = []
            labels = []
            transOffset = offset_copy(_ax.transData, fig=self.fig,
                            x=5, y=0, units='points')
            for _i, _xpick in enumerate(xpicks):
                if picks[_i].phase_hint == 'S':
                    color = 'r'
                elif picks[_i].phase_hint == 'P':
                    color = 'g'
                else:
                    color = 'b'
                if _ax.channel != picks[_i].waveform_id.channel_code:
                    alpha = .1
                else:
                    alpha = .8

                lines.append(matplotlib.lines.Line2D([_xpick, _xpick],
                            [_ax.get_ylim()[0]*.9, _ax.get_ylim()[1]*.8],
                            color=color, alpha=alpha))
                lines[-1].obspy_pick = picks[_i]

                labels.append(matplotlib.text.Text(_xpick,
                            _ax.get_ylim()[0]*.8, text=picks[_i].phase_hint,
                            color=color, size=10, alpha=alpha,
                            transform=transOffset))

            # delete all artists
            del _ax.artists[0:]
            # add updated objects
            for line in lines:
                _ax.add_artist(line)
            for label in labels:
                _ax.add_artist(label)

        if draw:
            self._canvasDraw()

    # Plot Controls
    def _pltOnScroll(self, event):
        '''
        Scrolls/Redraws the plots along x axis
        '''
        if event.inaxes is None:
            return

        if event.key == 'control':
            axes = [event.inaxes]
        else:
            axes = self.fig.get_axes()

        for _ax in axes:
            left = _ax.get_xlim()[0]
            right = _ax.get_xlim()[1]
            extent = right - left
            dzoom = .2 * extent
            aspect_left = (event.xdata - _ax.get_xlim()[0]) / extent
            aspect_right = (_ax.get_xlim()[1] - event.xdata) / extent

            if event.button == 'up':
                left += dzoom * aspect_left
                right -= dzoom * aspect_right
            elif event.button == 'down':
                left -= dzoom * aspect_left
                right += dzoom * aspect_right
            else:
                return
            _ax.set_xlim([left, right])
        self._canvasDraw()

    def _pltOnDrag(self, event):
        '''
        Drags/Redraws the plot upon drag
        '''
        if event.inaxes is None:
            return

        if event.key == 'control':
            axes = [event.inaxes]
        else:
            axes = self.fig.get_axes()

        if event.button == 2:
            if self._plt_drag is None:
                self._plt_drag = event.xdata
                return
            for _ax in axes:
                _ax.set_xlim([_ax.get_xlim()[0] +
                        (self._plt_drag - event.xdata),
                        _ax.get_xlim()[1] + (self._plt_drag - event.xdata)])
        else:
            return
        self._canvasDraw()

    def _pltOnButtonRelease(self, event):
        '''
        On Button Release Reset drag variable
        '''
        self._plt_drag = None

    def _pltOnButtonPress(self, event):
        '''
        This Function is evoked when the user picks
        '''
        if event.key is not None:
            event.key = event.key.lower()
        if event.inaxes is None:
            return
        channel = event.inaxes.channel
        tr_amp = event.inaxes.lines[0].get_ydata()[int(event.xdata)+3] -\
                    event.inaxes.lines[0].get_ydata()[int(event.xdata)]
        if tr_amp < 0:
            polarity = 'negative'
        elif tr_amp > 0:
            polarity = 'positive'
        else:
            polarity = 'undecideable'

        if event.key == self._shortcuts['pick_p'] and event.button == 1:
            self._setPick(event.xdata, phase='P', channel=channel,
                            polarity=polarity)
        elif event.key == self._shortcuts['pick_s'] and event.button == 1:
            self._setPick(event.xdata, phase='S', channel=channel,
                            polarity=polarity)
        elif event.key == self._shortcuts['pick_custom'] and event.button == 1:
            text, ok = QtGui.QInputDialog.getItem(self, 'Custom Phase',
                'Enter phase name:', self._getPhases())
            if ok:
                self._setPick(event.xdata, phase=text, channel=channel,
                                polarity=polarity)
        elif event.key == self._shortcuts['pick_remove']:
            self._delPicks(network=self._current_network,
                            station=self._current_stname,
                            channel=channel)
        else:
            return
        self._updateSB()
        self._drawPicks()

    def _pltNextStation(self):
        '''
        Plot next station
        '''
        self._streamStation(self._stationCycle.next())
        self._drawFig()

    def _pltPrevStation(self):
        '''
        Plot previous station
        '''
        for _i in range(len(self._stations)-1):
            prevStation = self._stationCycle.next()
        self._streamStation(prevStation)
        self._drawFig()

    def _pltStation(self):
        '''
        Plot station from DropDown Menu
        '''
        _i = self.stcb.currentIndex()
        while self._stationCycle.next() != self._stations[_i]:
            pass
        self._streamStation(self._stations[_i])
        self._drawFig()

    # Filter functions
    def _appFilter(self, button=True, draw=True):
        '''
        Apply bandpass filter
        '''
        _i = self.fltrcb.currentIndex()
        self._streamStation(self._current_stname)
        if self.fltrbtn.isChecked() is False:
            self._current_filter = None
        else:
            self._current_st.filter('bandpass',
                                    freqmin=self.bpfilter[_i]['freqmin'],
                                    freqmax=self.bpfilter[_i]['freqmax'],
                                    corners=self.bpfilter[_i]['corners'],
                                    zerophase=True)
            self._current_filter = _i
        for _i, _ax in enumerate(self.fig.get_axes()):
            if len(_ax.lines) == 0:
                continue
            _ax.lines[0].set_ydata(self._current_st[_i].data)
            _ax.relim()
            _ax.autoscale_view()
        if draw is True:
            self._drawPicks(draw=False)
            self._canvasDraw()
        self._updateSB()

    def _newFilter(self):
        '''
        Create new filter
        '''
        newFilter = self.defFilter(self)
        if newFilter.exec_():
                self.bpfilter.append(newFilter.getValues())
                self._updateFilterCB()
                self.fltrcb.setCurrentIndex(len(self.bpfilter)-1)
                self._appFilter()

    def _editFilter(self):
        '''
        Edit existing filter
        '''
        _i = self.fltrcb.currentIndex()
        this_filter = self.bpfilter[_i]
        editFilter = self.defFilter(self, this_filter)
        if editFilter.exec_():
                self.bpfilter[_i] = editFilter.getValues()
                self._updateFilterCB()
                self.fltrcb.setCurrentIndex(_i)
                self._appFilter()

    def _deleteFilter(self):
        '''
        Delete filter
        '''
        _i = self.fltrcb.currentIndex()
        self.fltrbtn.setChecked(False)
        self.bpfilter.pop(_i)
        self._updateFilterCB()
        self._appFilter()

    def _changeFilter(self, index):
        '''
        Evoke this is filter in drop-down is changed
        '''
        if index == len(self.bpfilter):
            return self._newFilter()
        else:
            return self._appFilter()

    def _updateFilterCB(self):
        '''
        Update the filter QComboBox
        '''
        self.fltrcb.clear()
        self.fltrcb.setCurrentIndex(-1)
        for _i, _f in enumerate(self.bpfilter):
            self.fltrcb.addItem('%s [%.2f - %.2f Hz]' % (_f['name'],
                _f['freqmin'], _f['freqmax']))
        self.fltrcb.addItem('Create new Filter...')

    # Status bar functions
    def _createStatusBar(self):
        '''
        Creates the status bar
        '''
        sb = QtGui.QStatusBar()
        sb.setFixedHeight(18)
        self.setStatusBar(sb)
        self.statusBar().showMessage('Ready')

    def _updateSB(self, statustext=None):
        '''
        Updates the statusbar text
        '''
        if statustext is None:
            self.stcb.setCurrentIndex(
                self._stations.index(self._current_stname))
            msg = 'Station %i/%i - %i Picks' % (
                self._stations.index(self._current_stname)+1,
                len(self._stations), len(self._getPicks()))
            if self._current_filter is not None:
                msg += ' - Bandpass %s [%.2f - %.2f Hz]' % (
                    self.bpfilter[self._current_filter]['name'],
                    self.bpfilter[self._current_filter]['freqmin'],
                    self.bpfilter[self._current_filter]['freqmax'])
            else:
                msg += ' - Raw Data'
            self.statusBar().showMessage(msg)

    def _openCatalogDlg(self):
        filename = QtGui.QFileDialog.getOpenFileName(self,
                        'Load QuakeML Picks',
                        os.getcwd(), 'QuakeML Format (*.xml)', '20')
        if filename:
            self._openCatalog(str(filename))
            self.savefile = str(filename)

    def _openCatalog(self, filename):
        '''
        Open existing QuakeML catalog
        '''
        try:
            print 'Opening QuakeML Catalog %s' % filename
            cat = event.readEvents(filename)
            self._picks = cat[0].picks
            self._drawPicks()
        except:
            msg = 'Could not open QuakeML file %s' % (filename)
            raise IOError(msg)

    def _saveCatalogDlg(self):
        '''
        Save catalog through QtDialog
        '''
        self.savefile = QtGui.QFileDialog.getSaveFileName(self,
                        'Save QuakeML Picks',
                        os.getcwd(), 'QuakeML Format (*.xml)')
        if not self.savefile:
            self.savefile = None
            return
        self.savefile = str(self.savefile)
        if os.path.splitext(self.savefile)[1].lower() != '.xml':
            self.savefile += '.xml'
        self._saveCatalog()

    def _saveCatalog(self, filename=None):
        '''
        Saves the catalog to filename
        '''
        if self.savefile is None and filename is None:
            return self._saveCatalogDlg()
        if filename is not None:
            savefile = filename
        else:
            savefile = self.savefile
        cat = event.Catalog()
        cat.events.append(event.Event(picks=self._picks))
        cat.write(savefile, format='QUAKEML')
        print 'Picks saved as %s' % savefile

    def _savePlotDlg(self):
        '''
        Save Plot Image Qt Dialog and Matplotlib wrapper
        '''
        filename = QtGui.QFileDialog.getSaveFileName(self, 'Save Plot',
                        os.getcwd(),
                        'Image Format (*.png *.pdf *.ps *.svg *.eps)')
        if not filename:
            return
        filename = str(filename)
        format = os.path.splitext(filename)[1][1:].lower()
        if format not in ['png', 'pdf', 'ps', 'svg', 'eps']:
            format = 'png'
            filename += '.' + format
        self.fig.savefig(filename=filename, format=format, dpi=72)

    def getPicks(self):
        return self._picks

    def _opnWadatiPlot(self):
        self._wadatiPlt = QtGui.NewWindow()
        self._wadatiPlt.show()

    def _infoDlg(self):
        msg = """
                <h3><b>obspy.core.stream-Picker</b></h3>
                <br><br>
                <div>
                StreamPick is a lightweight seismological
                wave time picker for <code>obspy.core.Stream()</code>
                objects. It further utilises the <code>obspy.core.event</code>
                class to store picks in the QuakeML format.
                </div>
                <h4>Controls:</h4>
                <blockquote>
                <table>
                    <tr>
                        <td width=20><b>%s</b></td><td>Next station</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td><td>Previous station</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td><td>Toggle filter</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Set P-Phase pick at mouse position</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Set S-Phase pick at mouse position</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Set custom phase pick at mouse position</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Remove last pick in trace</td>
                    </tr>
                </table>
                </blockquote>
                <h4>Plot Controls:</h4>
                <blockquote>
                Use mouse wheel to zoom in- and out. Middle mouse button moves
                plot along x-axis.<br>
                Hit <b>Ctrl</b> to manipulate a single plot.
                <br>
                </blockquote>
                <div>
                Programm stores filter parameters in <code>.pick_filter</code>
                and a backup of recent picks in
                <code>.picks-obspy.xml.bak</code>.<br><br>
                See <a href=http://www.github.org/miili/StreamPick>
                http://www.github.org/miili/StreamPick</a> and
                <a href=http://www.obspy.org>http://www.obspy.org</a>
                for further documentation.
                </div>
                """ % (
                    self._shortcuts['st_next'],
                    self._shortcuts['st_previous'],
                    self._shortcuts['filter_apply'],
                    self._shortcuts['pick_p'],
                    self._shortcuts['pick_s'],
                    self._shortcuts['pick_custom'],
                    self._shortcuts['pick_remove'],
                    )
        QtGui.QMessageBox.about(self, 'About', msg)

    def _canvasDraw(self):
        '''
        Redraws the canvas and re-sets mouse focus
        '''
        for _i, _ax in enumerate(self.fig.get_axes()):
            _ax.set_xticklabels(_ax.get_xticks() *
                                self._current_st[_i].stats.delta)
        self.fig.canvas.draw()
        self.canvas.setFocus()

    def closeEvent(self, evnt):
        '''
        This function is called upon closing the QtGui
        '''
        # Save Picks
        pickle.dump(self.bpfilter, open('.pick_filters', 'w'))
        # Save Catalog
        if len(self._picks) > 0:
            self._saveCatalog('.picks-obspy.xml.bak')
        if self.savefile is None and len(self._picks) > 0:
            ask = QtGui.QMessageBox.question(self, 'Save Picks?',
                'Do you want to save your picks?',
                QtGui.QMessageBox.Save |
                QtGui.QMessageBox.Discard |
                QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Save)
            if ask == QtGui.QMessageBox.Save:
                self._saveCatalog()
            elif ask == QtGui.QMessageBox.Cancel:
                evnt.ignore()
        print self._picks


    # Filter Dialog
    class defFilter(QtGui.QDialog):
        def __init__(self, parent=None, filtervalues=None):
            '''
            Bandpass filter dialog... Qt layout and stuff
            '''
            QtGui.QDialog.__init__(self, parent)
            self.setWindowTitle('Create new Bandpass-Filter')

            # Frequency QDoubleSpinBoxes
            self.frqmin = QtGui.QDoubleSpinBox(decimals=2, maximum=100,
                            minimum=0.01, singleStep=0.1, value=0.1)
            self.frqmax = QtGui.QDoubleSpinBox(decimals=2, maximum=100,
                            minimum=0.01, singleStep=0.1, value=10.0)

            # Radio buttons for corners
            _corners = [2, 4, 8]
            _radbtn = []
            for _c in _corners:
                _radbtn.append(QtGui.QRadioButton(str(_c)))
                if _c == 4:
                    _radbtn[-1].setChecked(True)

            self.corner = QtGui.QButtonGroup()
            self.corner.setExclusive(True)

            radiogrp = QtGui.QHBoxLayout()
            for _i, _r in enumerate(_radbtn):
                self.corner.addButton(_r, _corners[_i])
                radiogrp.addWidget(_radbtn[_i])

            # Filter name
            self.fltname = QtGui.QLineEdit('Filter Name')
            self.fltname.selectAll()

            # Make Layout
            grid = QtGui.QGridLayout()
            grid.addWidget(QtGui.QLabel('Filter Name'), 0, 0)
            grid.addWidget(self.fltname, 0, 1)
            grid.addWidget(QtGui.QLabel('Min. Frequency'), 1, 0)
            grid.addWidget(self.frqmin, 1, 1)
            grid.addWidget(QtGui.QLabel('Max. Frequency'), 2, 0)
            grid.addWidget(self.frqmax, 2, 1)
            grid.addWidget(QtGui.QLabel('Corners'), 3, 0)
            grid.addLayout(radiogrp, 3, 1)
            grid.setVerticalSpacing(10)

            btnbox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok |
                                            QtGui.QDialogButtonBox.Cancel)
            btnbox.accepted.connect(self.accept)
            btnbox.rejected.connect(self.reject)

            layout = QtGui.QVBoxLayout()
            layout.addWidget(QtGui.QLabel('Define a minimum and maximum' +
                ' frequency\nfor the bandpass filter.\nFunction utilises ' +
                'obspy.signal.filter (zerophase=True).\n'))
            layout.addLayout(grid)
            layout.addWidget(btnbox)

            if filtervalues is not None:
                self.fltname.setText(filtervalues['name'])
                self.frqmin.setValue(filtervalues['freqmin'])
                self.frqmax.setValue(filtervalues['freqmax'])
                self.corner.button(filtervalues['corners']).setChecked(True)

            self.setLayout(layout)
            self.setSizeGripEnabled(False)

        def getValues(self):
            '''
            Return filter dialogs values as a dictionary
            '''
            return dict(name=str(self.fltname.text()),
                        freqmin=float(self.frqmin.cleanText()),
                        freqmax=float(self.frqmax.cleanText()),
                        corners=int(int(self.corner.checkedId())))
Ejemplo n.º 37
0
class DataPlots(AbstractWidget):
    font_size = 6
    rc_context = {
        'font.family':
        'sans-serif',
        'font.sans-serif':
        ['Tahoma', 'Bitstream Vera Sans', 'Lucida Grande', 'Verdana'],
        'font.size':
        font_size,
        'figure.titlesize':
        font_size + 1,
        'axes.labelsize':
        font_size,
        'legend.fontsize':
        font_size,
        'xtick.labelsize':
        font_size - 1,
        'ytick.labelsize':
        font_size - 1,
        'axes.linewidth':
        0.5,
        'axes.xmargin':
        0.01,
        'axes.ymargin':
        0.01,
        'lines.linewidth':
        1.0,
        'grid.alpha':
        0.2,
    }

    def __init__(self, main_win, lib, server_mode=False):
        AbstractWidget.__init__(self, main_win=main_win, lib=lib)

        self.server_mode = server_mode
        self.is_drawn = False

        # mpl figure settings
        self.f_dpi = 120  # dots-per-inch
        self.f_sz = (6.0, 3.0)  # inches
        self.svi = None  # sis valid indices
        self.vi = None  # proc valid indices
        self.ii = None  # proc invalid indices
        self.draft_color = '#00cc66'
        self.seafloor_color = '#cc6600'
        self.sensor_color = '#00cc66'
        self.valid_color = '#3385ff'
        self.invalid_color = '#999966'
        self.woa09_color = '#ffaaaa'
        self.woa13_color = '#ffcc66'
        self.rtofs_color = '#99cc00'
        self.gomofs_color = '#6cbdbb'
        self.ref_color = '#ff6600'
        self.sis_color = '#0000e6'
        self.dot_style = "x"
        self.dot_ms = 1
        self.dot_alpha = 0.6

        # outline ui
        self.top_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.top_widget)
        self.vbox = QtWidgets.QVBoxLayout()
        self.vbox.setContentsMargins(0, 0, 0, 0)
        self.top_widget.setLayout(self.vbox)

        # figure and canvas
        with rc_context(self.rc_context):
            self.f = Figure(figsize=self.f_sz, dpi=self.f_dpi)
            self.f.patch.set_alpha(0.0)
            self.c = FigureCanvas(self.f)
            self.c.setParent(self.top_widget)
            self.c.setFocusPolicy(
                QtCore.Qt.ClickFocus)  # key for press events!!!
            self.c.setFocus()
            self.vbox.addWidget(self.c)

            # axes
            self.speed_ax = self.f.add_subplot(131)
            self.speed_ax.invert_yaxis()
            self.temp_ax = self.f.add_subplot(132, sharey=self.speed_ax)
            self.temp_ax.invert_yaxis()
            self.sal_ax = self.f.add_subplot(133, sharey=self.speed_ax)
            self.sal_ax.invert_yaxis()

        # lines
        self.speed_draft = None
        self.speed_sensor = None
        self.speed_seafloor = None
        self.speed_sis = None
        self.speed_woa09 = None
        self.temp_woa09 = None
        self.sal_woa09 = None
        self.speed_woa09_min = None
        self.temp_woa09_min = None
        self.sal_woa09_min = None
        self.speed_woa09_max = None
        self.temp_woa09_max = None
        self.sal_woa09_max = None
        self.speed_woa13 = None
        self.temp_woa13 = None
        self.sal_woa13 = None
        self.speed_woa13_min = None
        self.temp_woa13_min = None
        self.sal_woa13_min = None
        self.speed_woa13_max = None
        self.temp_woa13_max = None
        self.sal_woa13_max = None
        self.speed_rtofs = None
        self.temp_rtofs = None
        self.sal_rtofs = None
        self.speed_gomofs = None
        self.temp_gomofs = None
        self.sal_gomofs = None
        self.speed_ref = None
        self.temp_ref = None
        self.sal_ref = None
        self.speed_valid = None
        self.temp_valid = None
        self.sal_valid = None
        self.speed_invalid = None
        self.temp_invalid = None
        self.sal_invalid = None
        # events

        if not self.server_mode:
            # toolbar
            self.hbox = QtWidgets.QHBoxLayout()
            self.vbox.addLayout(self.hbox)
            # navigation
            self.nav = NavToolbar(canvas=self.c,
                                  parent=self.top_widget,
                                  plot_win=self,
                                  prj=self.lib)
            self.hbox.addWidget(self.nav)

        self.on_first_draw()

    def _draw_grid(self):
        for a in self.f.get_axes():
            a.grid(True)

    def _draw_speed(self):
        self.speed_ax.clear()
        self.speed_ax.set_ylabel('Depth [m]')
        self.speed_ax.set_xlabel('Sound Speed [m/s]')
        if self.lib.cur.woa09:
            self.speed_woa09, = self.speed_ax.plot(
                self.lib.cur.woa09.l[0].proc.speed,
                self.lib.cur.woa09.l[0].proc.depth,
                color=self.woa09_color,
                linestyle='--',
                label='WOA09 avg')
            if len(self.lib.cur.woa09.l) == 3:
                self.speed_woa09_min, = self.speed_ax.plot(
                    self.lib.cur.woa09.l[1].proc.speed,
                    self.lib.cur.woa09.l[1].proc.depth,
                    color=self.woa09_color,
                    linestyle=':',
                    label='WOA09 min')
                self.speed_woa09_max, = self.speed_ax.plot(
                    self.lib.cur.woa09.l[2].proc.speed,
                    self.lib.cur.woa09.l[2].proc.depth,
                    color=self.woa09_color,
                    linestyle=':',
                    label='WOA09 max')
        if self.lib.cur.woa13:
            self.speed_woa13, = self.speed_ax.plot(
                self.lib.cur.woa13.l[0].proc.speed,
                self.lib.cur.woa13.l[0].proc.depth,
                color=self.woa13_color,
                linestyle='--',
                label='WOA13 avg')
            if len(self.lib.cur.woa13.l) == 3:
                self.speed_woa13_min, = self.speed_ax.plot(
                    self.lib.cur.woa13.l[1].proc.speed,
                    self.lib.cur.woa13.l[1].proc.depth,
                    color=self.woa13_color,
                    linestyle=':',
                    label='WOA13 min')
                self.speed_woa13_max, = self.speed_ax.plot(
                    self.lib.cur.woa13.l[2].proc.speed,
                    self.lib.cur.woa13.l[2].proc.depth,
                    color=self.woa13_color,
                    linestyle=':',
                    label='WOA13 max')
        if self.lib.cur.rtofs:
            self.speed_rtofs, = self.speed_ax.plot(
                self.lib.cur.rtofs.l[0].proc.speed,
                self.lib.cur.rtofs.l[0].proc.depth,
                color=self.rtofs_color,
                linestyle='--',
                label='RTOFS')
        if self.lib.cur.gomofs:
            self.speed_gomofs, = self.speed_ax.plot(
                self.lib.cur.gomofs.l[0].proc.speed,
                self.lib.cur.gomofs.l[0].proc.depth,
                color=self.gomofs_color,
                linestyle='--',
                label='GoMOFS')
        if self.lib.has_ref():
            self.speed_ref, = self.speed_ax.plot(self.lib.ref.l[0].proc.speed,
                                                 self.lib.ref.l[0].proc.depth,
                                                 color=self.ref_color,
                                                 linestyle='--',
                                                 label='ref')
        self.speed_invalid, = self.speed_ax.plot(
            self.lib.cur.proc.speed[self.ii],
            self.lib.cur.proc.depth[self.ii],
            markerfacecolor=self.invalid_color,
            markeredgecolor=self.invalid_color,
            linestyle='None',
            marker=self.dot_style,
            alpha=self.dot_alpha,
            ms=self.dot_ms,
            picker=3,
            label='flagged')
        self.speed_valid, = self.speed_ax.plot(
            self.lib.cur.proc.speed[self.vi],
            self.lib.cur.proc.depth[self.vi],
            color=self.valid_color,
            picker=3,
            label='valid')
        self.speed_sis, = self.speed_ax.plot(self.lib.cur.sis.speed[self.svi],
                                             self.lib.cur.sis.depth[self.svi],
                                             markerfacecolor=self.sis_color,
                                             markeredgecolor=self.sis_color,
                                             marker=self.dot_style,
                                             linestyle='None',
                                             alpha=self.dot_alpha,
                                             ms=self.dot_ms,
                                             picker=3,
                                             label='SIS')
        self.speed_draft = self.speed_ax.axhline(y=0,
                                                 linewidth=1.5,
                                                 color=self.draft_color,
                                                 linestyle=':',
                                                 label='draft')
        self.speed_sensor = self.speed_ax.axvline(x=1500,
                                                  linewidth=1.5,
                                                  color=self.sensor_color,
                                                  linestyle=':',
                                                  label='tss')
        self.speed_seafloor = self.speed_ax.axhline(y=0,
                                                    linewidth=1.5,
                                                    color=self.seafloor_color,
                                                    linestyle=':',
                                                    label='depth')
        self.speed_draft.set_ydata(None)
        self.speed_sensor.set_xdata(None)
        self.speed_seafloor.set_ydata(None)

        self.speed_ax.set_label("speed")

    def _draw_temp(self):
        self.temp_ax.clear()
        self.temp_ax.set_xlabel('Temperature [deg C]')
        if self.lib.cur.woa09:

            self.temp_woa09, = self.temp_ax.plot(
                self.lib.cur.woa09.l[0].proc.temp,
                self.lib.cur.woa09.l[0].proc.depth,
                color=self.woa09_color,
                linestyle='--',
                label='WOA09 avg')
            if len(self.lib.cur.woa09.l) == 3:
                self.temp_woa09_min, = self.temp_ax.plot(
                    self.lib.cur.woa09.l[1].proc.temp,
                    self.lib.cur.woa09.l[1].proc.depth,
                    color=self.woa09_color,
                    linestyle=':',
                    label='WOA09 min')
                self.temp_woa09_max, = self.temp_ax.plot(
                    self.lib.cur.woa09.l[2].proc.temp,
                    self.lib.cur.woa09.l[2].proc.depth,
                    color=self.woa09_color,
                    linestyle=':',
                    label='WOA09 max')
        if self.lib.cur.woa13:

            self.temp_woa13, = self.temp_ax.plot(
                self.lib.cur.woa13.l[0].proc.temp,
                self.lib.cur.woa13.l[0].proc.depth,
                color=self.woa13_color,
                linestyle='--',
                label='WOA13 avg')
            if len(self.lib.cur.woa13.l) == 3:
                self.temp_woa13_min, = self.temp_ax.plot(
                    self.lib.cur.woa13.l[1].proc.temp,
                    self.lib.cur.woa13.l[1].proc.depth,
                    color=self.woa13_color,
                    linestyle=':',
                    label='WOA13 min')
                self.temp_woa13_max, = self.temp_ax.plot(
                    self.lib.cur.woa13.l[2].proc.temp,
                    self.lib.cur.woa13.l[2].proc.depth,
                    color=self.woa13_color,
                    linestyle=':',
                    label='WOA13 max')
        if self.lib.cur.rtofs:
            self.temp_rtofs, = self.temp_ax.plot(
                self.lib.cur.rtofs.l[0].proc.temp,
                self.lib.cur.rtofs.l[0].proc.depth,
                color=self.rtofs_color,
                linestyle='--',
                label='RTOFS')
        if self.lib.cur.gomofs:
            self.temp_gomofs, = self.temp_ax.plot(
                self.lib.cur.gomofs.l[0].proc.temp,
                self.lib.cur.gomofs.l[0].proc.depth,
                color=self.gomofs_color,
                linestyle='--',
                label='GoMOFS')
        if self.lib.has_ref():
            self.temp_ref, = self.temp_ax.plot(self.lib.ref.l[0].proc.temp,
                                               self.lib.ref.l[0].proc.depth,
                                               color=self.ref_color,
                                               linestyle='--',
                                               label='ref')
        self.temp_invalid, = self.temp_ax.plot(
            self.lib.cur.proc.temp[self.ii],
            self.lib.cur.proc.depth[self.ii],
            markerfacecolor=self.invalid_color,
            markeredgecolor=self.invalid_color,
            linestyle='None',
            marker=self.dot_style,
            alpha=self.dot_alpha,
            ms=self.dot_ms,
            picker=3,
            label='flagged')
        self.temp_valid, = self.temp_ax.plot(self.lib.cur.proc.temp[self.vi],
                                             self.lib.cur.proc.depth[self.vi],
                                             color=self.valid_color,
                                             picker=3,
                                             label='valid')
        self.temp_ax.set_label("temp")
        # hide y-labels
        [label.set_visible(False) for label in self.temp_ax.get_yticklabels()]

    def _draw_sal(self):
        self.sal_ax.clear()
        self.sal_ax.set_xlabel('Salinity [PSU]')
        if self.lib.cur.woa09:
            self.sal_woa09, = self.sal_ax.plot(
                self.lib.cur.woa09.l[0].proc.sal,
                self.lib.cur.woa09.l[0].proc.depth,
                color=self.woa09_color,
                linestyle='--',
                label='WOA09 avg')
            if len(self.lib.cur.woa09.l) == 3:
                self.sal_woa09_min, = self.sal_ax.plot(
                    self.lib.cur.woa09.l[1].proc.sal,
                    self.lib.cur.woa09.l[1].proc.depth,
                    color=self.woa09_color,
                    linestyle=':',
                    label='WOA09 min')
                self.sal_woa09_max, = self.sal_ax.plot(
                    self.lib.cur.woa09.l[2].proc.sal,
                    self.lib.cur.woa09.l[2].proc.depth,
                    color=self.woa09_color,
                    linestyle=':',
                    label='WOA09 max')
        if self.lib.cur.woa13:
            self.sal_woa13, = self.sal_ax.plot(
                self.lib.cur.woa13.l[0].proc.sal,
                self.lib.cur.woa13.l[0].proc.depth,
                color=self.woa13_color,
                linestyle='--',
                label='WOA13 avg')
            if len(self.lib.cur.woa13.l) == 3:
                self.sal_woa13_min, = self.sal_ax.plot(
                    self.lib.cur.woa13.l[1].proc.sal,
                    self.lib.cur.woa13.l[1].proc.depth,
                    color=self.woa13_color,
                    linestyle=':',
                    label='WOA13 min')
                self.sal_woa13_max, = self.sal_ax.plot(
                    self.lib.cur.woa13.l[2].proc.sal,
                    self.lib.cur.woa13.l[2].proc.depth,
                    color=self.woa13_color,
                    linestyle=':',
                    label='WOA13 max')
        if self.lib.cur.rtofs:
            self.sal_rtofs, = self.sal_ax.plot(
                self.lib.cur.rtofs.l[0].proc.sal,
                self.lib.cur.rtofs.l[0].proc.depth,
                color=self.rtofs_color,
                linestyle='--',
                label='RTOFS')
        if self.lib.cur.gomofs:
            self.sal_gomofs, = self.sal_ax.plot(
                self.lib.cur.gomofs.l[0].proc.sal,
                self.lib.cur.gomofs.l[0].proc.depth,
                color=self.gomofs_color,
                linestyle='--',
                label='GoMOFS')
        if self.lib.has_ref():
            self.sal_ref, = self.sal_ax.plot(self.lib.ref.l[0].proc.sal,
                                             self.lib.ref.l[0].proc.depth,
                                             color=self.ref_color,
                                             linestyle='--',
                                             label='ref')
        self.sal_invalid, = self.sal_ax.plot(
            self.lib.cur.proc.sal[self.ii],
            self.lib.cur.proc.depth[self.ii],
            markerfacecolor=self.invalid_color,
            markeredgecolor=self.invalid_color,
            linestyle='None',
            marker=self.dot_style,
            alpha=self.dot_alpha,
            ms=self.dot_ms,
            picker=3,
            label='flagged')
        self.sal_valid, = self.sal_ax.plot(self.lib.cur.proc.sal[self.vi],
                                           self.lib.cur.proc.depth[self.vi],
                                           color=self.valid_color,
                                           picker=3,
                                           label='valid')
        self.sal_ax.set_label("sal")
        # hide y-labels
        [label.set_visible(False) for label in self.sal_ax.get_yticklabels()]

    def _set_title(self):
        # plot title
        msg = str()
        if self.lib.cur_file:
            msg += self.lib.cur_file
        if self.lib.setup.client_list.last_tx_time and self.lib.use_sis():
            if len(msg) > 0:
                msg += " "
            delta = datetime.utcnow() - self.lib.setup.client_list.last_tx_time
            msg += "[%dh %dm since cast time of last tx]" % (
                delta.days * 24 + delta.seconds // 3600,
                (delta.seconds // 60) % 60)
        self.f.suptitle(msg)

    def on_first_draw(self):
        """Redraws the figure, it is only called at the first import!!!"""
        with rc_context(self.rc_context):
            self._set_title()
            # print("cur: %s" % self.lib.cur)
            # if self.lib.cur:
            if self.lib.has_ssp():
                self.update_validity_indices()
                self._draw_speed()
                self._draw_temp()
                self._draw_sal()
                self.is_drawn = True

            self._draw_grid()
            self.update_all_limits()

    def update_data(self):
        """Update plot"""
        self.update_validity_indices()

        with rc_context(self.rc_context):
            # speed
            if self.speed_sis:
                self.speed_sis.set_xdata(self.lib.cur.sis.speed[self.svi])
                self.speed_sis.set_ydata(self.lib.cur.sis.depth[self.svi])
            if self.speed_valid:
                self.speed_valid.set_xdata(self.lib.cur.proc.speed[self.vi])
                self.speed_valid.set_ydata(self.lib.cur.proc.depth[self.vi])
            if self.speed_invalid:
                self.speed_invalid.set_xdata(self.lib.cur.proc.speed[self.ii])
                self.speed_invalid.set_ydata(self.lib.cur.proc.depth[self.ii])
            # temp
            if self.temp_valid:
                self.temp_valid.set_xdata(self.lib.cur.proc.temp[self.vi])
                self.temp_valid.set_ydata(self.lib.cur.proc.depth[self.vi])
            if self.temp_invalid:
                self.temp_invalid.set_xdata(self.lib.cur.proc.temp[self.ii])
                self.temp_invalid.set_ydata(self.lib.cur.proc.depth[self.ii])
            # sal
            if self.sal_valid:
                self.sal_valid.set_xdata(self.lib.cur.proc.sal[self.vi])
                self.sal_valid.set_ydata(self.lib.cur.proc.depth[self.vi])
            if self.sal_invalid:
                self.sal_invalid.set_xdata(self.lib.cur.proc.sal[self.ii])
                self.sal_invalid.set_ydata(self.lib.cur.proc.depth[self.ii])

            if not self.lib.use_sis():  # in case that SIS was disabled
                if self.speed_draft:
                    self.speed_draft.set_ydata(None)
                if self.speed_sensor:
                    self.speed_sensor.set_xdata(None)
                return

            # plot title
            self._set_title()

            # it means that data have not been plotted
            if (not self.speed_draft) or (not self.speed_sensor) or (
                    not self.speed_seafloor):
                return

            if self.lib.listeners.sis.xyz is None:
                self.speed_draft.set_ydata(None)
                self.speed_sensor.set_xdata(None)
            else:
                # sensor speed
                if self.lib.listeners.sis.xyz_transducer_sound_speed is None:
                    self.speed_sensor.set_xdata(None)
                else:
                    self.speed_sensor.set_xdata([
                        self.lib.listeners.sis.xyz_transducer_sound_speed,
                    ])
                # draft
                if self.lib.listeners.sis.xyz_transducer_depth is None:
                    self.speed_draft.set_ydata(None)
                else:
                    self.speed_draft.set_ydata([
                        self.lib.listeners.sis.xyz_transducer_depth,
                    ])
                # seafloor
                mean_depth = self.lib.listeners.sis.xyz_mean_depth
                if mean_depth:
                    self.speed_seafloor.set_ydata([
                        mean_depth,
                    ])
                else:
                    self.speed_seafloor.set_ydata(None)

    def update_all_limits(self):
        self.update_depth_limits()
        self.update_temp_limits()
        self.update_sal_limits()
        self.update_speed_limits()

    def update_depth_limits(self):

        with rc_context(self.rc_context):
            if self.lib.has_ssp():

                if len(self.lib.cur.proc.depth[self.vi]) > 0:

                    max_proc_depth = self.lib.cur.proc.depth[self.vi].max()
                    mean_sis_depth = 0
                    if self.lib.use_sis():
                        if self.lib.listeners.sis.xyz:
                            if self.lib.listeners.sis.xyz_mean_depth:
                                mean_sis_depth = self.lib.listeners.sis.xyz_mean_depth
                    max_proc_sis_depth = max(max_proc_depth, mean_sis_depth)

                    max_depth = max(30. + max_proc_sis_depth,
                                    1.1 * max_proc_sis_depth)
                    min_depth = -0.05 * max_proc_sis_depth
                    if min_depth > 0:
                        min_depth = -5

                    self.speed_ax.set_ylim(bottom=max_depth, top=min_depth)

            self.c.draw()

    def update_speed_limits(self):

        with rc_context(self.rc_context):
            if self.lib.has_ssp():

                if len(self.lib.cur.proc.speed[self.vi]) > 0:
                    max_proc_speed = self.lib.cur.proc.speed[self.vi].max()
                    min_proc_speed = self.lib.cur.proc.speed[self.vi].min()

                    self.speed_ax.set_xlim(left=(min_proc_speed - 3.0),
                                           right=(max_proc_speed + 3.0))

            self.c.draw()

    def update_temp_limits(self):

        with rc_context(self.rc_context):
            if self.lib.has_ssp():

                if len(self.lib.cur.proc.temp[self.vi]) > 0:
                    max_proc_temp = self.lib.cur.proc.temp[self.vi].max()
                    min_proc_temp = self.lib.cur.proc.temp[self.vi].min()

                    self.temp_ax.set_xlim(left=(min_proc_temp - 3.0),
                                          right=(max_proc_temp + 3.0))

            self.c.draw()

    def update_sal_limits(self):

        with rc_context(self.rc_context):
            if self.lib.has_ssp():

                if len(self.lib.cur.proc.sal[self.vi]) > 0:
                    max_proc_sal = self.lib.cur.proc.sal[self.vi].max()
                    min_proc_sal = self.lib.cur.proc.sal[self.vi].min()

                    self.sal_ax.set_xlim(left=(min_proc_sal - 3.0),
                                         right=(max_proc_sal + 3.0))

            self.c.draw()

    def redraw(self):
        """Redraw the canvases, update the locators"""

        with rc_context(self.rc_context):
            for a in self.c.figure.get_axes():

                xaxis = getattr(a, 'xaxis', None)
                yaxis = getattr(a, 'yaxis', None)
                locators = []

                if xaxis is not None:
                    locators.append(xaxis.get_major_locator())
                    locators.append(xaxis.get_minor_locator())

                if yaxis is not None:
                    locators.append(yaxis.get_major_locator())
                    locators.append(yaxis.get_minor_locator())

                for loc in locators:
                    loc.refresh()

            self.c.draw_idle()

    def update_validity_indices(self):
        self.svi = self.lib.cur.sis_thinned  # sis valid indices (thinned!)
        self.vi = self.lib.cur.proc_valid  # proc valid indices
        self.ii = np.logical_and(
            ~self.vi,
            ~self.lib.cur.proc_invalid_direction)  # selected invalid indices

    def set_invalid_visibility(self, value):
        self.speed_invalid.set_visible(value)
        self.temp_invalid.set_visible(value)
        self.sal_invalid.set_visible(value)

    def reset(self):
        if not self.server_mode:
            self.nav.reset()
Ejemplo n.º 38
0
class PGPlottingFrame(Frame):
    def __init__(self,
                 o_master_frame=None,
                 b_do_animate=True,
                 tuple_args_for_animation_call=None,
                 v_init_data=None,
                 f_figure_width=5.0,
                 f_figure_height=3.4,
                 i_figure_dpi=100,
                 i_ticklabelsize=8,
                 i_labelfontsize=8,
                 i_animate_interval=1000,
                 s_xlabel="",
                 s_ylabel="",
                 s_zlabel=""):

        Frame.__init__(self, o_master_frame)

        self.__figure_width = f_figure_width
        self.__figure_height = f_figure_height
        self.__figure_dpi = i_figure_dpi
        self.__ticklabelsize = i_ticklabelsize
        self.label_fontsize = i_labelfontsize
        self.current_data = v_init_data
        self.__animate_interval = 1000
        self.xlabel = s_xlabel
        self.ylabel = s_ylabel
        self.zlabel = s_zlabel
        self.__tuple_of_args_for_animation_call = tuple_args_for_animation_call
        self.__do_animate = b_do_animate
        #Assigned in make_figure def below.
        self.subplot = None
        self.__host_canvas = None
        self.__subframe = None

        self.__make_figure()
        self.__add_plotting_canvas()

        return

    #end __init__

    def __make_figure(self):
        self.__figure = Figure(figsize=(self.__figure_width,
                                        self.__figure_height),
                               dpi=self.__figure_dpi)
        self.subplot = self.__figure.add_subplot(111)
        self.subplot.tick_params(axis='both',
                                 which='major',
                                 labelsize=self.__ticklabelsize)
        return

    #end __make_figure

    def __add_plotting_canvas(self):

        o_scrolling_canvas = Canvas(self)

        self.__plotting_canvas = FigureCanvasTkAgg(self.__figure,
                                                   o_scrolling_canvas)
        self.__plotting_canvas.get_tk_widget().pack(side=BOTTOM,
                                                    fill=BOTH,
                                                    expand=True)

        if self.__do_animate == True:

            self.__plot_animation = animation.FuncAnimation(
                self.__figure,
                self.animate,
                fargs=self.__tuple_of_args_for_animation_call,
                interval=self.__animate_interval)
        #end if do animate

        o_scrolling_canvas.create_window(self.__figure_width,
                                         self.__figure_height,
                                         window=self)
        o_scrolling_canvas.config(scrollregion=o_scrolling_canvas.bbox(ALL))
        self.__host_canvas = o_scrolling_canvas

        self.__host_canvas.grid(row=0, column=0)

        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

        self.__host_canvas.grid_rowconfigure(0, weight=1)
        self.__host_canvas.grid_columnconfigure(0, weight=1)

        #self.bind( '<Configure>', self.__on_configure_frame )

        return

    #end __add_plotting_canvas

    def __on_configure_frame(self, event):

        #we need the scroll region and the canvas dims to
        #always match the subframe dims.
        q_size = (self.winfo_reqwidth(), self.winfo_reqheight())
        self.__host_canvas.config(scrollregion="0 0 %s %s" % q_size)
        self.__host_canvas.config(width=q_size[0])
        self.__host_canvas.config(height=q_size[1])
        return

    #end __configure_subframe

    def animate(self, i_interval=None):
        return

    #end do_animate

    def __save_plot_to_file(self, s_filename, s_type="png"):
        if not (s_filename.endswith(".png") or s_filename.endswith(".pdf")):
            s_filename = s_filename + ".png"
        #end if unknown image file type

        self.__figure.savefig(s_filename)
        return

    #end __save_plot_to_file

    def data(self):
        return self.current_data

    #end current_data

    def updateData(self, v_data=None):
        return

    #end updateData

    def savePlotToFile(self, s_filename):
        self.__save_plot_to_file(s_filename)
        return

    #end savePlotToFile

    def setXLabel(self, s_label):
        self.xlabel = s_label

    #end x_label setter

    def setYLabel(self, s_label):
        self.ylabel = s_label

    #end y_label setter

    def resetFigureWidthAndHeight(self, f_width, f_height):
        self.__figure_width = f_width
        self.__figure_width = f_height
        self.__make_figure()
        self.__add_plotting_canvas()

    #end setFigureWidthAndHeight

    def set_x_axis_margin_and_xtick_rotation(self,
                                             ls_labels,
                                             lv_xtick_vals=None):

        MAX_LABEL_LEN = 10

        NO_ROTATION = 0
        SLIGHT_ROTATION = 10
        HALF_ROTATION = 30

        NORMAL_X_MARGIN_ADJUST = 0.2
        HIGH_X_MARGIN_ADJUST = 0.3

        SHORT_LABEL = 4

        HIGH_LABEL_COUNT = 25
        HIGH_LABEL_COUNT_ANGLE = 60
        HIGH_LABEL_COUNT_MARGIN_ADJ = 0.2

        li_rotation_by_length = [
            NO_ROTATION for i_length in range(SHORT_LABEL)
        ]
        li_rotation_by_length += [
            HALF_ROTATION
            for i_length in range(SHORT_LABEL + 1, MAX_LABEL_LEN + 1)
        ]
        '''
		2018_04_03.  Need to adjust alignment of xtick labels.  Currently
		always 'right' justification, but when labels are not rotated,
		we need to center them:
		'''
        s_xtick_label_alignment = 'right'
        ls_alignment_by_length = ['center' for i_length in range(SHORT_LABEL)]
        ls_alignment_by_length += [ 'right' for i_length \
              in range( SHORT_LABEL + 1, MAX_LABEL_LEN + 1 ) ]
        '''
		Convenience local def:
		'''
        def mylengetter(s_label):
            return 0 if s_label == '' else len(s_label)

        #end mylengetter

        if len(ls_labels) == 0:
            i_len_longest_label = 0
        else:
            i_len_longest_label=max( [ mylengetter(s_label) \
                   for s_label in ls_labels ] )
        #end if we have no data sets, else at least 1

        if i_len_longest_label > MAX_LABEL_LEN:
            self.__bottom_margin_adjustment = HIGH_X_MARGIN_ADJUST
            self.__xtick_rotation_angle = SLIGHT_ROTATION
            s_xtick_label_alignment = 'right'
        else:
            self.__bottom_margin_adjustment = NORMAL_X_MARGIN_ADJUST
            self.__xtick_rotation_angle = li_rotation_by_length[
                i_len_longest_label - 1]
            s_xtick_label_alignment = ls_alignment_by_length[
                i_len_longest_label - 1]
        #end if length longer than max, else not

        if len(ls_labels) >= HIGH_LABEL_COUNT:
            self.__xtick_rotation_angle = HIGH_LABEL_COUNT_ANGLE
            self.__bottom_margin_adjustment += HIGH_LABEL_COUNT_MARGIN_ADJ
        #end if lots of xtick labels, adjust the rotation angle

        self.__figure.subplots_adjust(bottom=self.__bottom_margin_adjustment)

        if lv_xtick_vals is not None:
            self.__figure.get_axes()[0].set_xticks(lv_xtick_vals)
        #end if we have a set of xtick values

        o_xticklabels = self.__figure.get_axes()[0].set_xticklabels(
            ls_labels,
            rotation=self.__xtick_rotation_angle,
            ha=s_xtick_label_alignment)
        return
Ejemplo n.º 39
0
class AppForm(QMainWindow):
    def __init__(self, fileIn, parent=None):
        QMainWindow.__init__(self, parent)

        self.setWindowTitle('PyProf')
        self.currentFile = fileIn

        self.whichShowing = 0
        self.posMouse = [0, 0]

        self.createMainFrame()

    def readParameters(self, rootFile):

        # Read inverted parameters
        ff = nf(rootFile + '.parameters', 'r')
        pars = ff.variables['map'][:]
        ff.close()

        # Read errors
        ff = nf(rootFile + '.errors', 'r')
        errors = ff.variables['map'][:]
        ff.close()

        return pars, errors

    def readProfiles(self, rootFile):

        # Read inverted profiles
        ff = nf(rootFile + '.inversion', 'r')
        synthProf = ff.variables['map'][:]
        ff.close()

        return synthProf

    def readObservations(self, rootFile):

        # Read inverted profiles
        ff = nf(rootFile + '.nc', 'r')
        sizeMask = ff.variables['mask'].shape
        sizeMap = ff.variables['map'].shape
        obsProf = ff.variables['map'][:].reshape(
            (sizeMask[0], sizeMask[1], sizeMap[-2], sizeMap[-1]))
        ff.close()

        return obsProf

    def readData(self):

        # Parameters
        self.obs = self.readObservations(self.currentFile)
        obsShape = self.obs.shape

        self.pars, self.errors = self.readParameters(self.currentFile)
        parsShape = self.pars.shape
        self.pars = self.pars.reshape(
            (obsShape[0], obsShape[1], parsShape[-1]))
        self.errors = self.errors.reshape(
            (obsShape[0], obsShape[1], parsShape[-1]))

        self.syn = self.readProfiles(self.currentFile)
        synShape = self.syn.shape
        self.syn = self.syn.reshape(
            (obsShape[0], obsShape[1], synShape[-2], synShape[-1]))

        self.nx, self.ny, self.nLambda, _ = self.syn.shape

        self.maps = [None] * 4
        self.maps = [None] * 4
        for i in range(4):
            self.maps[i] = np.sum(self.obs[:, :, :, 0], axis=(2))

    def updateProfiles(self):
        # Blit animation. We only redraw the lines
        loop = 0
        for j in range(6):
            for i in range(4):
                self.rightCanvas.restore_region(self.background[loop])
                self.obs[loop].set_ydata(
                    self.profiles[self.rangeFrom[j]:self.rangeTo[j], i])
                self.axes[loop].draw_artist(self.obs[loop])
                self.rightCanvas.blit(self.axes[loop].bbox)
                loop += 1

    def onMouseMove(self, event):
        print(event.xdata, event.ydata)
        if (event.xdata != None and event.ydata != None):
            newPos = np.asarray([event.xdata, event.ydata])
            newPos = newPos.astype(int)

            if (newPos[0] != self.posMouse[0]
                    or newPos[1] != self.posMouse[1]):
                self.posMouse = newPos
                self.profiles = self.getProfiles(newPos[0], newPos[1])
                self.updateProfiles()

    def onScrollMove(self, event):
        pass
        #if (event.button == 'up'):
        #self.newTime += 1
        #if (event.button == 'down'):
        #self.newTime -= 1

        #self.newTime = self.newTime % 35

        #self.profiles = self.getProfiles(self.posMouse[0], self.posMouse[1])
        #self.updateProfiles()

    def getProfiles(self, x, y):
        return self.obs[x, y, :, 0:5], self.syn[x, y, :, :]

    def getMaps(self, x):
        return self.obs[:, :, 0, 0]


#--------------------------------------

    def create_main_frame(self):

        self.mainFrame = QWidget()

        gridLayout = QGridLayout()

        self.dpi = 80
        self.xpos = 0
        self.ypos = 0

        self.titles = [r'I', r'Q', r'U', r'V']

        self.readData()

        self.resize(1500, 900)

        # Left window
        self.leftPlot = QWidget()
        self.leftFig = Figure((2 * self.ny / self.dpi, 4 * self.nx / self.dpi),
                              dpi=self.dpi)
        self.leftCanvas = FigureCanvas(self.leftFig)
        self.leftCanvas.setParent(self.leftPlot)

        self.leftAxes = [None] * 4
        self.drawnMap = [None] * 4
        for i in range(4):
            self.leftAxes[i] = self.leftFig.add_subplot(2, 2, i + 1)
            self.drawnMap[i] = self.leftAxes[i].imshow(self.maps[i],
                                                       aspect='equal')
            # self.leftAxes[i].set_axis_off()
        self.leftCanvas.mpl_connect('motion_notify_event', self.onMouseMove)
        self.leftCanvas.mpl_connect('scroll_event', self.onScrollMove)
        gridLayout.addWidget(self.leftPlot, 0, 0)
        gridLayout.setSpacing(10)

        # Central window
        # self.centralPlot = QWidget()
        # self.centralFig = Figure((10,8), dpi=self.dpi)
        # self.centralCanvas = FigureCanvas(self.centralFig)
        # self.centralCanvas.setParent(self.centralPlot)

        # self.slitMaps = self.getMaps(0)

        # self.centralAxes = [None]*4
        # self.drawnSlitMap = [None]*4
        # # for i in range(4):
        # # 	self.centralAxes[i] = self.centralFig.add_subplot(4,1,i+1)
        # # 	self.drawnSlitMap[i] = self.centralAxes[i].imshow(self.slitMaps[i,:,:])
        # #self.centralCanvas.draw()

        # gridLayout.addWidget(self.centralPlot, 0, 1)

        # Right window
        self.rightPlot = QWidget()
        self.rightFig = Figure((8, 18), dpi=self.dpi)
        self.rightCanvas = FigureCanvas(self.rightFig)
        self.rightCanvas.setParent(self.rightPlot)

        self.stokesObs, self.stokesSyn = self.getProfiles(0, 0)

        # Draw the axes and the first profiles
        nCols = 2
        nRows = 2
        self.axes = [None] * 4
        self.obsLine = [None] * 4
        self.synLine = [None] * 4
        loop = 0
        for j in range(2):
            for i in range(2):
                self.axes[loop] = self.rightFig.add_subplot(
                    nCols, nRows, loop + 1)
                self.obsLine[loop], = self.axes[loop].plot(
                    self.stokesObs[:, loop])
                self.synLine[loop], = self.axes[loop].plot(
                    self.stokesSyn[:, loop])
                loop += 1
        gridLayout.addWidget(self.rightPlot, 0, 2)

        ## Tight layout and redraw
        # self.rightFig.tight_layout()

        self.rightCanvas.draw()

        ## We are using blit animation to do it fast, so we need to recover the axes modified by tight_layout
        self.newAxes = self.rightFig.get_axes()

        ## Save the backgrounds
        # loop = 0
        # for j in range(6):
        # 	for i in range(4):
        # 		self.axes[loop] = self.newAxes[loop]
        # 		self.background[loop] = self.canvasRight.copy_from_bbox(self.axes[loop].bbox)
        # 		loop += 1

        gridLayout.addWidget(self.rightPlot)

        # fullLayout.addLayout(gridLayout)

        self.mainFrame.setLayout(gridLayout)
        self.setCentralWidget(self.mainFrame)
Ejemplo n.º 40
0
class FigurePanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.SetBackgroundColour('#DCE5EE')

        pub().subscribe(self.update_language, T.LANGUAGE_CHANGED)

        self.control_panel = None
        self.dframes = []
        self.order_names = []
        self.key_figure = 1
        self.mode_run = False

        self.current_dataframes = None
        self.current_datacolors = None

        self.run_explorer = False

        self.figure_config_dialog_ref = None

        # ---- inicialización de figura
        self.fig = Figure()
        self.canvas = FigureCanvas(self, -1, self.fig)

        # ---- configuración de figura
        self.fig_config = FigureConfig()
        self.set_figure_config()

        # ---- configuración de axe
        self.ax_conf = AxesConfig()

        # ---- radar chard config
        self.radar_chard_con = RadarChadConfig()

        # ---- toolbar
        self.sizer_tool = wx.BoxSizer(wx.HORIZONTAL)
        _bitmap = play_fig.GetBitmap()
        self.b_play = wx.BitmapButton(self, -1, _bitmap, style=wx.NO_BORDER)
        self.sizer_tool.Add(self.b_play, flag=wx.ALIGN_CENTER_VERTICAL)
        self.b_play.Bind(wx.EVT_BUTTON, self.on_play)
        self.b_play.SetToolTipString(L('VISUALIZE_DATE_CLUSTER'))
        _bitmap = settings_fig.GetBitmap()
        self.b_setting = wx.BitmapButton(self, -1, _bitmap, style=wx.NO_BORDER)
        self.sizer_tool.Add(self.b_setting, flag=wx.ALIGN_CENTER_VERTICAL)
        self.b_setting.Bind(wx.EVT_BUTTON, self.on_config)
        self.b_setting.SetToolTipString(L('FIGURE_CONF'))

        _bitmap = sort_and_filter.GetBitmap()
        self.b_sorted = wx.BitmapButton(self, -1, _bitmap, style=wx.NO_BORDER)
        self.b_sorted.Bind(wx.EVT_BUTTON, self.on_sort_and_filter)
        self.b_sorted.SetToolTipString(L('BUTTON_ORDER_AND_FILTER'))
        self.b_sorted.Disable()
        self.sizer_tool.Add(self.b_sorted, 0, wx.ALIGN_CENTER_VERTICAL)

        _bp = line_highligh.GetBitmap()
        self.b_highligh = wx.BitmapButton(self, -1, _bp, style=wx.NO_BORDER)
        self.b_highligh.Bind(wx.EVT_BUTTON, self.on_highligh)
        self.b_highligh.SetToolTipString(L('BUTTON_HIGHLIGHT'))
        self.b_highligh.Disable()
        self.sizer_tool.Add(self.b_highligh, 0, wx.ALIGN_CENTER_VERTICAL)

        self.toolbar = Toolbar(self.canvas)
        self.toolbar.Realize()
        self.toolbar.SetBackgroundColour('#DCE5EE')

        self.sizer_tool.Add(self.toolbar, 0, wx.ALIGN_CENTER_VERTICAL)

        choice_grafic = self.get_choice_grafic()
        self.sizer_tool.Add(choice_grafic, wx.ALIGN_LEFT)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.sizer_tool, 0, wx.EXPAND)
        self.sizer.Add(self.canvas, 1, wx.EXPAND)

        self.SetSizer(self.sizer)
        self.Fit()
        self._welcome()

    def _welcome(self):
        Axes3D(self.fig)

    def set_figure_config(self):

        self.fig.set_figwidth(self.fig_config.width)
        self.fig.set_figheight(self.fig_config.height)
        self.fig.set_facecolor(self.fig_config.facecolor)

        left = self.fig_config.subplot_left
        bottom = self.fig_config.subplot_bottom
        right = self.fig_config.subplot_right
        top = self.fig_config.subplot_top
        wspace = self.fig_config.subplot_wspace
        hspace = self.fig_config.subplot_hspace
        self.fig.subplots_adjust(left, bottom, right, top, wspace, hspace)

        self.fig.suptitle('Tava Tool',
                          fontsize=14,
                          fontweight='light',
                          style='italic',
                          family='serif',
                          color='c',
                          horizontalalignment='center',
                          verticalalignment='center')

    def draw_graphic(self, dframes, colors):
        key_figure = self.g_figure()
        if key_figure == K_PARALLEL_COORDENATE:
            return kparallelcoordinates(dframes, 'Name', self.fig,
                                        self.ax_conf, self.fig_config, colors)

        elif key_figure == K_RADAR_CHART_POLYGON:
            return radarchart(dframes, 'Name', self.fig, self.ax_conf,
                              self.radar_chard_con, colors)
        elif key_figure == K_RADVIZ:
            return kradviz(dframes, 'Name', self.fig, self.ax_conf, colors)

    def kdraw(self, dframes, colors, ldic):

        self.current_dataframes = dframes
        self.current_datacolors = colors
        self.ldic = ldic
        self._kdraw(dframes, colors)

    def pre_kdraw_order(self, names_ordered):
        names_ordered.append('Name')
        _dframes = []
        for df in self.current_dataframes:
            _dframes.append(df[names_ordered])

        self._kdraw(_dframes, self.current_datacolors)

    def _kdraw(self, dframes, colors):
        self.fig.clear()
        self.start_busy()
        task = DrawThread(self, dframes, colors)
        task.start()

    def on_play(self, event):
        self.mode_run = True
        self.run_explorer = True
        self.new_order = []
        # ---- dibujar clusters/datos seleccionados
        self.control_panel.run_fig()

    def on_sort_and_filter(self, event):

        self.run_explorer = True
        cdf = self.current_dataframes
        if cdf is None or cdf == []:
            return

        self.old_order = cdf[0].columns.tolist()[:-1]
        ItemsPickerFilterDialog(self, self.old_order)

    def on_highligh(self, event):
        _label_aux = ''
        if self.run_explorer:
            for axe in self.fig.get_axes():
                lines = []
                for line in axe.get_children():
                    if isinstance(line, Line2D):
                        if self.ldic.get(line.get_color()) is not None:
                            _label_aux = self.ldic.get(line.get_color())
                            line.set_label('shape = ' +
                                           self.ldic.get(line.get_color()))
                            lines.append(line)
                        else:
                            line.set_label('')
                h = resaltar(lines,
                             highlight_color=self.ax_conf.highlight_color,
                             formatter='{label}'.format)
                if lines != []:
                    h.show_highlight(lines[0])
            self.run_explorer = False
            self.canvas_draw()

    def on_config(self, event):
        if self.figure_config_dialog_ref is None:
            self.figure_config_dialog_ref = FigureConfigDialog(self)
        else:
            self.figure_config_dialog_ref.nb.SetSelection(0)
            self.figure_config_dialog_ref.ShowModal()

    def g_figure(self):
        return self.ch_graph.GetSelection()

    def get_choice_grafic(self):
        grid = wx.FlexGridSizer(cols=2)
        sampleList = self.get_item_list()

        self.ch_graph = wx.Choice(self, -1, choices=sampleList)
        self.ch_graph.SetSelection(0)
        self.ch_graph.Bind(wx.EVT_CHOICE, self.on_graphic)
        self.ch_graph.SetToolTipString(L('SELECT_A_GRAPHIC'))

        grid.Add(self.ch_graph, 0, wx.ALIGN_LEFT | wx.ALL, 5)

        return grid

    def get_item_list(self):
        return [L('PARALLEL_COORDINATES'), 'Radar Chart']

    def on_graphic(self, event):

        if event.GetString() != L('PARALLEL_COORDINATES') or not self.mode_run:
            self.b_highligh.Disable()
            return
        self.b_highligh.Enable()

    def update_language(self, msg):
        s = self.ch_graph.GetSelection()
        self.ch_graph.SetItems(self.get_item_list())
        self.ch_graph.SetSelection(s)
        self.ch_graph.SetToolTipString(L('SELECT_A_GRAPHIC'))
        self.b_setting.SetToolTipString(L('FIGURE_CONF'))
        self.b_play.SetToolTipString(L('VISUALIZE_DATE_CLUSTER'))

    def start_busy(self):
        pub().sendMessage(T.START_BUSY)
        self.b_play.Disable()
        self.toolbar.Disable()
        self.b_setting.Disable()
        self.ch_graph.Disable()
        self.b_highligh.Disable()
        self.b_sorted.Disable()

    def stop_busy(self):
        pub().sendMessage(T.STOP_BUSY)
        self.b_play.Enable()
        self.toolbar.Enable()
        self.b_setting.Enable()
        self.ch_graph.Enable()
        self.b_highligh.Enable()
        self.b_sorted.Enable()

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

    def set_fig(self, fig):
        self.fig = fig
Ejemplo n.º 41
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, fontsize=9,
                 trace_color_callback=None, output_title='plot', **kws):

        self.trace_color_callback = trace_color_callback
        matplotlib.rc('axes', axisbelow=True)
        matplotlib.rc('lines', linewidth=2)
        matplotlib.rc('xtick', labelsize=fontsize, color='k')
        matplotlib.rc('ytick', labelsize=fontsize, color='k')
        matplotlib.rc('legend', fontsize=fontsize)
        matplotlib.rc('grid',  linewidth=0.5, linestyle='-')


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

        self.conf = PlotConfig()
        self.data_range = {}
        self.win_config = None
        self.cursor_callback = None
        self.lasso_callback = None
        self.parent    = parent
        self.figsize = (size[0]*1.0/dpi, size[1]*1.0/dpi)
        self.dpi     = dpi

        if axisbg is None:     axisbg='#FEFFFE'
        self.axisbg = axisbg
        # axesmargins : margins in px left/top/right/bottom
        self.axesmargins = (30, 30, 30, 30)

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

    def plot(self, xdata, ydata, side='left', title=None,
             xlabel=None, ylabel=None, y2label=None,
             use_dates=False, **kws):
        """
        plot (that is, create a new plot: clear, then oplot)
        """
        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.axes_traces = {}
        self.clear()
        axes = self.axes
        if side == 'right':
            axes = self.get_right_axes()
        self.conf.ntrace  = 0
        self.conf.yscale = 'linear'
        self.cursor_mode = 'zoom'
        self.conf.plot_type = 'lineplot'
        self.user_limits[axes] = [None, None, None, None]

        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 use_dates is not None:
            self.use_dates  = 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, grid=None,
              xmin=None, xmax=None, ymin=None, ymax=None,
              color=None, style=None, drawstyle=None,
              linewidth=2, marker=None, markersize=None,
              autoscale=True, refresh=True, show_legend=None,
              legend_loc='best', legend_on=True, delay_draw=False,
              bgcolor=None, framecolor=None, gridcolor=None,
              labelfontsize=None, legendfontsize=None,
              fullbox=None, axes_style=None, zorder=None, **kws):
        """ basic plot method, overplotting any existing plot """
        axes = self.axes
        if side == 'right':
            axes = self.get_right_axes()
        # set y scale to log/linear
        if ylog_scale is not None:
            self.conf.yscale = {False:'linear', True:'log'}[ylog_scale]

            # ydata = ma.masked_where(ydata<=0, 1.0*ydata)
            # ymin = min(ydata[where(ydata>0)])
            # ydata[where(ydata<=0)] = None

        try:
            axes.set_yscale(self.conf.yscale, basey=10)
        except:
            axes.set_yscale('linear')

        axes.xaxis.set_major_formatter(FuncFormatter(self.xformatter))
        if self.use_dates:
            x_dates = [datetime.fromtimestamp(i) for i in xdata]
            xdata = dates.date2num(x_dates)
            axes.xaxis.set_major_locator(dates.AutoDateLocator())

        if linewidth is None:
            linewidth = 2

        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 show_legend is not None:
            self.conf.set_legend_location(legend_loc, legend_on)
            self.conf.show_legend = show_legend

        if grid is not None:
            self.conf.show_grid = grid
        # set data range for this trace
        datarange = [min(xdata), max(xdata), min(ydata), max(ydata)]

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

        if xmin is not None: self.user_limits[axes][0] = xmin
        if xmax is not None: self.user_limits[axes][1] = xmax
        if ymin is not None: self.user_limits[axes][2] = ymin
        if ymax is not None: self.user_limits[axes][3] = ymax

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

        conf  = self.conf
        n    = conf.ntrace
        if zorder is None:
            zorder = 5*(n+1)
        if axes not in self.axes_traces:
            self.axes_traces[axes] = []
        self.axes_traces[axes].append(n)
        if dy is None:
            _lines = axes.plot(xdata, ydata, drawstyle=drawstyle, zorder=zorder)
        else:
            _lines = axes.errorbar(xdata, ydata, yerr=dy, zorder=zorder)

        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.grid_color)
                i.set_zorder(-100)
            axes.grid(True)
        else:
            axes.grid(False)

        if label is None:
            label = 'trace %i' % (conf.ntrace+1)
        conf.set_trace_label(label)
        conf.set_trace_datarange(datarange)

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

        conf.set_trace_zorder(zorder)
        if color:
            conf.set_trace_color(color)
        if style:
            conf.set_trace_style(style)
        if marker:
            conf.set_trace_marker(marker)
        if linewidth is not None:
            conf.set_trace_linewidth(linewidth)
        if markersize is not None:
            conf.set_trace_markersize(markersize)
        if drawstyle is not None:
            conf.set_trace_drawstyle(drawstyle)

        if gridcolor is not None:
            conf.grid_color = gridcolor
        if labelfontsize is not None:
            conf.labelfont.set_size(labelfontsize)
        if legendfontsize is not None:
            conf.legendfont.set_size(legendfontsize)

        if n < len(conf.lines):
            conf.lines[n] = _lines
        else:
            conf._init_trace(n, 'black', solid)
            conf.lines[n] = _lines

        # now set plot limits:
        self.set_viewlimits()
        if refresh:
            conf.refresh_trace(conf.ntrace)
            conf.relabel()

        if self.conf.show_legend:
            conf.draw_legend()

        # 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()

        if not delay_draw:
            self.draw()
            self.canvas.Refresh()

        conf.ntrace = conf.ntrace + 1
        return _lines

    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,
                    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

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

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

        fcols = [to_rgba(self.conf.scatter_normalcolor) for x in xdata]
        ecols = [self.conf.scatter_normaledge]*len(xdata)

        self.conf.scatter_data = [(x, y) for x, y in zip(xdata, ydata)]
        self.conf.scatter_size = size
        self.conf.scatter_coll = CircleCollection(
            sizes=(size, ), facecolors=fcols, edgecolors=ecols,
            offsets=self.conf.scatter_data,
            transOffset= self.axes.transData)
        self.axes.add_collection(self.conf.scatter_coll)
        self.set_viewlimits()

        if self.conf.show_grid:
            for i in axes.get_xgridlines()+axes.get_ygridlines():
                i.set_color(self.conf.grid_color)
                i.set_zorder(-30)
            axes.grid(True)
        else:
            axes.grid(False)
        self.set_viewlimits()
        self.draw()

    def lassoHandler(self, vertices):
        conf = self.conf

        if self.conf.plot_type == 'scatter':
            fcols = conf.scatter_coll.get_facecolors()
            ecols = conf.scatter_coll.get_edgecolors()
            sdat = conf.scatter_data
            mask = inside_poly(vertices,sdat)
            pts = nonzero(mask)[0]
            self.conf.scatter_mask = mask
            for i in range(len(sdat)):
                if i in pts:
                    ecols[i] = to_rgba(conf.scatter_selectedge)
                    fcols[i] = to_rgba(conf.scatter_selectcolor)
                    fcols[i][3] = 0.3
                    ecols[i][3] = 0.8
                else:
                    fcols[i] = to_rgba(conf.scatter_normalcolor)
                    ecols[i] = to_rgba(conf.scatter_normaledge)
        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)
            # print mask
            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.user_limits[axes] = limits
        self.unzoom_all()

    def set_viewlimits(self, autoscale=False):
        """ update xy limits of a plot, as used with .update_line() """

        trace0 = None
        while trace0 is None:
            for axes in self.fig.get_axes():
                if (axes in self.axes_traces and
                   len(self.axes_traces[axes]) > 0):
                    trace0 = self.axes_traces[axes][0]
                    break

        for axes in self.fig.get_axes():
            datlim = self.conf.get_trace_datarange(trace=trace0)
            if axes in self.axes_traces:
                for i in self.axes_traces[axes]:
                    l =  self.conf.get_trace_datarange(trace=i)
                    datlim = [min(datlim[0], l[0]), max(datlim[1], l[1]),
                              min(datlim[2], l[2]), max(datlim[3], l[3])]

            xmin, xmax = axes.get_xlim()
            ymin, ymax = axes.get_ylim()
            limits = [min(datlim[0], xmin),
                      max(datlim[1], xmax),
                      min(datlim[2], ymin),
                      max(datlim[3], ymax)]

            if (axes in self.user_limits and
                (self.user_limits[axes] != 4*[None] or
                len(self.conf.zoom_lims) > 0)):

                for i, val in enumerate(self.user_limits[axes]):
                    if val is not None:
                        limits[i] = val
                xmin, xmax, ymin, ymax = limits
                if len(self.conf.zoom_lims) > 0:
                    limits_set = True
                    xmin, xmax, ymin, ymax = self.conf.zoom_lims[-1][axes]
                axes.set_xlim((xmin, xmax), emit=True)
                axes.set_ylim((ymin, ymax), emit=True)

    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  = ''

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

    def unzoom(self, event=None, set_bounds=True):
        """ zoom out 1 level, or to full data range """
        if len(self.conf.zoom_lims) > 1:
            self.conf.zoom_lims.pop()
        self.set_viewlimits()
        self.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], axisbg=self.axisbg)

        self.canvas = FigureCanvas(self, -1, self.fig)

        self.printer.canvas = self.canvas
        self.set_bg()
        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.Fit()

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

    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)))

        # print "AutoSet Margins 0b: (%.3f, %.3f, %.3f, %.3f)" %  (l, t, r, b)
        # 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()
            (ox0, oy0), (ox1, oy1) = ax.get_tightbbox(self.canvas.get_renderer()).get_points()
            (ox0, oy0), (ox1, oy1) = trans(((ox0 ,oy0),(ox1 ,oy1)))

            dl = max(dl, (x0 - ox0))
            dt = max(dt, (oy1 - y1))
            dr = max(dr, (ox1 - x1))
            db = max(db, (y0 - oy0))

        # print(" > %.3f %.3f %.3f %.3f " % (dl, dt, dr, db))
        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
        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)


    def onExport(self, event=None, **kws):
        ofile  = ''
        title = 'unknown plot'

        if self.conf.title is not None:
            title = ofile = self.conf.title.strip()
        if len(ofile) > 64:
            ofile = ofile[:63].strip()
        if len(ofile) < 1:
            ofile = 'plot'

        for c in ' .:";|/\\(){}[]\'&^%*$+=-?!@#':
            ofile = ofile.replace(c, '_')

        while '__' in ofile:
            ofile = ofile.replace('__', '_')

        ofile = ofile + '.dat'

        dlg = wx.FileDialog(self, message='Export Map Data to ASCII...',
                            defaultDir = os.getcwd(),
                            defaultFile=ofile,
                            style=wx.FD_SAVE|wx.FD_CHANGE_DIR)

        if dlg.ShowModal() == wx.ID_OK:
            self.writeASCIIFile(dlg.GetPath(), title=title)

    def writeASCIIFile(self, fname, title='unknown plot'):
        buff = ["# X,Y Data for %s" % title,
                "#------------", "#     X     Y"]
        lines= self.axes.get_lines()
        x0 = lines[0].get_xaxis()
        y0 = lines[0].get_yaxis()
        lab0 = lines[0].get_label().strip()
        if len(lab0) < 1: lab0 =  'Y'
        buff.append("#   X    %s" % lab0)
        outa = [x0, y0]
        npts = len(x0)
        if len(lines)  > 1:
            for ix, line in enumerate(lines[1:]):
                lab = ['#   ', '       ',
                       line.get_label().strip()]
                x = line.get_xdata()
                y = line.get_ydata()
                npts = max(npts, len(y))
                if not all(x==x0):
                    lab0[1]  = ' X%i ' % (ix+2)
                    out.append(x)
                out.append(line.get_ydata())

        fout = open(fname, 'w')
        fout.write("%s\n" % "\n".join(buff))
        fout.close()

        orig_dir = os.path.abspath(os.curdir)
        thisdir = os.getcwd()
        file_choices = "DAT (*.dat)|*.dat|ALL FILES (*.*)|*.*"
        dlg = wx.FileDialog(self,
                            message='Export Plot Data to ASCII...',
                            defaultDir=thisdir,
                            defaultFile=ofile,
                            wildcard=file_choices,
                            style=wx.FD_SAVE|wx.FD_CHANGE_DIR)

        if dlg.ShowModal() == wx.ID_OK:
            self.writeASCIIFile(dlg.GetPath(), title=title)
        os.chdir(orig_dir)

    def writeASCIIFile(self, fname, title='unknown plot'):
        "save plot data to external file"

        buff = ["# Plot Data for %s" % title, "#------"]

        out = []
        labs = []
        itrace = 0
        for ax in self.fig.get_axes():
            for line in ax.lines:
                itrace += 1
                x = line.get_xdata()
                y = line.get_ydata()
                ylab = line.get_label()

                if len(ylab) < 1: ylab = ' Y%i' % itrace
                if len(ylab) < 4: ylab = ' %s%s' % (' '*4, lab)
                for c in ' .:";|/\\(){}[]\'&^%*$+=-?!@#':
                    ylab = ylab.replace(c, '_')

                pad = max(1, 13-len(ylab))
                lab = '     X%i   %s%s  ' % (itrace, ' '*pad, ylab)
                out.extend([x, y])
                labs.append(lab)
        buff.append('# %s ' % (' '.join(labs)))

        npts = [len(a) for a in out]
        for i in range(max(npts)):
            oline = []
            for a in out:
                d = nan
                if i < len(a):  d = a[i]
                oline.append(gformat(d))
            buff.append(' '.join(oline))

        if itrace > 0:
            fout = open(fname, 'w')
            fout.write("%s\n" % "\n".join(buff))
            fout.close()
            self.write_message("Exported data to '%s'" % fname,
                               panel=0)

    ####
    ## 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= %s, %s" % (self._xfmt, self._yfmt)) % (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= %s, %s, %s" % (self._xfmt, self._yfmt,
                                              self._y2fmt) % (x, y, y2)
            except:
                pass
        self.write_message(msg,  panel=0)
        if (self.cursor_callback is not None and
            hasattr(self.cursor_callback , '__call__')):
            self.cursor_callback(x=event.xdata, y=event.ydata)
Ejemplo n.º 42
0
class VNA(QMainWindow, Ui_VNA):

  max_size = 16384

  def __init__(self):
    super(VNA, self).__init__()
    self.setupUi(self)
    # IP address validator
    rx = QRegExp('^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$')
    self.addrValue.setValidator(QRegExpValidator(rx, self.addrValue))
    # state variables
    self.idle = True
    self.reading = False
    # sweep parameters
    self.sweep_start = 100
    self.sweep_stop = 60000
    self.sweep_size = 600
    self.xaxis, self.sweep_step = np.linspace(self.sweep_start, self.sweep_stop, self.sweep_size, retstep = True)
    self.xaxis *= 1000
    # buffer and offset for the incoming samples
    self.buffer = bytearray(24 * VNA.max_size)
    self.offset = 0
    self.data = np.frombuffer(self.buffer, np.complex64)
    self.adc1 = np.zeros(VNA.max_size, np.complex64)
    self.adc2 = np.zeros(VNA.max_size, np.complex64)
    self.dac1 = np.zeros(VNA.max_size, np.complex64)
    self.open = np.zeros(VNA.max_size, np.complex64)
    self.short = np.zeros(VNA.max_size, np.complex64)
    self.load = np.zeros(VNA.max_size, np.complex64)
    self.dut = np.zeros(VNA.max_size, np.complex64)
    self.mode = 'dut'
    # create figure
    self.figure = Figure()
    self.figure.set_facecolor('none')
    self.canvas = FigureCanvas(self.figure)
    self.plotLayout.addWidget(self.canvas)
    # create navigation toolbar
    self.toolbar = NavigationToolbar(self.canvas, self.plotWidget, False)
    # initialize cursor
    self.cursor = None
    # remove subplots action
    actions = self.toolbar.actions()
    self.toolbar.removeAction(actions[7])
    self.plotLayout.addWidget(self.toolbar)
    # create TCP socket
    self.socket = QTcpSocket(self)
    self.socket.connected.connect(self.connected)
    self.socket.readyRead.connect(self.read_data)
    self.socket.error.connect(self.display_error)
    # connect signals from buttons and boxes
    self.sweepFrame.setEnabled(False)
    self.dutSweep.setEnabled(False)
    self.connectButton.clicked.connect(self.start)
    self.writeButton.clicked.connect(self.write_cfg)
    self.readButton.clicked.connect(self.read_cfg)
    self.openSweep.clicked.connect(self.sweep_open)
    self.shortSweep.clicked.connect(self.sweep_short)
    self.loadSweep.clicked.connect(self.sweep_load)
    self.dutSweep.clicked.connect(self.sweep_dut)
    self.csvButton.clicked.connect(self.write_csv)
    self.s1pButton.clicked.connect(self.write_s1p)
    self.s2pButton.clicked.connect(self.write_s2p)
    self.startValue.valueChanged.connect(self.set_start)
    self.stopValue.valueChanged.connect(self.set_stop)
    self.sizeValue.valueChanged.connect(self.set_size)
    self.rateValue.addItems(['500', '100', '50', '10', '5', '1'])
    self.rateValue.lineEdit().setReadOnly(True)
    self.rateValue.lineEdit().setAlignment(Qt.AlignRight)
    for i in range(0, self.rateValue.count()):
      self.rateValue.setItemData(i, Qt.AlignRight, Qt.TextAlignmentRole)
    self.rateValue.currentIndexChanged.connect(self.set_rate)
    self.corrValue.valueChanged.connect(self.set_corr)
    self.levelValue.valueChanged.connect(self.set_level)
    self.openPlot.clicked.connect(self.plot_open)
    self.shortPlot.clicked.connect(self.plot_short)
    self.loadPlot.clicked.connect(self.plot_load)
    self.dutPlot.clicked.connect(self.plot_dut)
    self.smithPlot.clicked.connect(self.plot_smith)
    self.impPlot.clicked.connect(self.plot_imp)
    self.rcPlot.clicked.connect(self.plot_rc)
    self.swrPlot.clicked.connect(self.plot_swr)
    self.rlPlot.clicked.connect(self.plot_rl)
    self.gainPlot.clicked.connect(self.plot_gain)
    # create timer
    self.startTimer = QTimer(self)
    self.startTimer.timeout.connect(self.timeout)

  def start(self):
    if self.idle:
      self.connectButton.setEnabled(False)
      self.socket.connectToHost(self.addrValue.text(), 1001)
      self.startTimer.start(5000)
    else:
      self.stop()

  def stop(self):
    self.idle = True
    self.socket.abort()
    self.connectButton.setText('Connect')
    self.connectButton.setEnabled(True)
    self.sweepFrame.setEnabled(False)
    self.selectFrame.setEnabled(True)
    self.dutSweep.setEnabled(False)

  def timeout(self):
    self.display_error('timeout')

  def connected(self):
    self.startTimer.stop()
    self.idle = False
    self.set_start(self.startValue.value())
    self.set_stop(self.stopValue.value())
    self.set_size(self.sizeValue.value())
    self.set_rate(self.rateValue.currentIndex())
    self.set_corr(self.corrValue.value())
    self.set_level(self.levelValue.value())
    self.connectButton.setText('Disconnect')
    self.connectButton.setEnabled(True)
    self.sweepFrame.setEnabled(True)
    self.dutSweep.setEnabled(True)

  def read_data(self):
    if not self.reading:
      self.socket.readAll()
      return
    size = self.socket.bytesAvailable()
    self.progress.setValue((self.offset + size) / 24)
    limit = 24 * (self.sweep_size + 1)
    if self.offset + size < limit:
      self.buffer[self.offset:self.offset + size] = self.socket.read(size)
      self.offset += size
    else:
      self.buffer[self.offset:limit] = self.socket.read(limit - self.offset)
      self.adc1 = self.data[0::3]
      self.adc2 = self.data[1::3]
      self.dac1 = self.data[2::3]
      getattr(self, self.mode)[0:self.sweep_size] = self.adc1[1:self.sweep_size + 1] / self.dac1[1:self.sweep_size + 1]
      getattr(self, 'plot_%s' % self.mode)()
      self.reading = False
      self.sweepFrame.setEnabled(True)
      self.selectFrame.setEnabled(True)

  def display_error(self, socketError):
    self.startTimer.stop()
    if socketError == 'timeout':
      QMessageBox.information(self, 'VNA', 'Error: connection timeout.')
    else:
      QMessageBox.information(self, 'VNA', 'Error: %s.' % self.socket.errorString())
    self.stop()

  def set_start(self, value):
    self.sweep_start = value
    self.xaxis, self.sweep_step = np.linspace(self.sweep_start, self.sweep_stop, self.sweep_size, retstep = True)
    self.xaxis *= 1000
    if self.idle: return
    self.socket.write(struct.pack('<I', 0<<28 | int(value * 1000)))

  def set_stop(self, value):
    self.sweep_stop = value
    self.xaxis, self.sweep_step = np.linspace(self.sweep_start, self.sweep_stop, self.sweep_size, retstep = True)
    self.xaxis *= 1000
    if self.idle: return
    self.socket.write(struct.pack('<I', 1<<28 | int(value * 1000)))

  def set_size(self, value):
    self.sweep_size = value
    self.xaxis, self.sweep_step = np.linspace(self.sweep_start, self.sweep_stop, self.sweep_size, retstep = True)
    self.xaxis *= 1000
    if self.idle: return
    self.socket.write(struct.pack('<I', 2<<28 | int(value)))

  def set_rate(self, value):
    if self.idle: return
    rate = [1, 5, 10, 50, 100, 500][value]
    self.socket.write(struct.pack('<I', 3<<28 | int(rate)))

  def set_corr(self, value):
    if self.idle: return
    self.socket.write(struct.pack('<I', 4<<28 | int(value)))

  def set_level(self, value):
    if self.idle: return
    self.socket.write(struct.pack('<I', 5<<28 | int(32767 * np.power(10.0, value / 20.0))))

  def sweep(self):
    if self.idle: return
    self.sweepFrame.setEnabled(False)
    self.selectFrame.setEnabled(False)
    self.socket.write(struct.pack('<I', 6<<28))
    self.offset = 0
    self.reading = True
    self.progress = QProgressDialog('Sweep status', 'Cancel', 0, self.sweep_size + 1)
    self.progress.setModal(True)
    self.progress.setMinimumDuration(1000)
    self.progress.canceled.connect(self.cancel)

  def cancel(self):
    self.offset = 0
    self.reading = False
    self.socket.write(struct.pack('<I', 7<<28))
    self.sweepFrame.setEnabled(True)
    self.selectFrame.setEnabled(True)

  def sweep_open(self):
    self.mode = 'open'
    self.sweep()

  def sweep_short(self):
    self.mode = 'short'
    self.sweep()

  def sweep_load(self):
    self.mode = 'load'
    self.sweep()

  def sweep_dut(self):
    self.mode = 'dut'
    self.sweep()

  def gain(self):
    size = self.sweep_size
    return self.dut[0:size]/self.short[0:size]

  def impedance(self):
    size = self.sweep_size
    return 50.0 * (self.open[0:size] - self.load[0:size]) * (self.dut[0:size] - self.short[0:size]) / ((self.load[0:size] - self.short[0:size]) * (self.open[0:size] - self.dut[0:size]))

  def gamma(self):
    z = self.impedance()
    return (z - 50.0)/(z + 50.0)

  def plot_gain(self):
    if self.cursor is not None: self.cursor.hide().disable()
    matplotlib.rcdefaults()
    self.figure.clf()
    self.figure.subplots_adjust(left = 0.12, bottom = 0.12, right = 0.88, top = 0.98)
    axes1 = self.figure.add_subplot(111)
    axes1.cla()
    axes1.xaxis.set_major_formatter(FuncFormatter(metric_prefix))
    axes1.yaxis.set_major_formatter(FuncFormatter(metric_prefix))
    axes1.tick_params('y', color = 'blue', labelcolor = 'blue')
    axes1.yaxis.label.set_color('blue')
    gain = self.gain()
    axes1.plot(self.xaxis, 20.0 * np.log10(np.absolute(gain)), color = 'blue', label = 'Gain')
    axes2 = axes1.twinx()
    axes2.spines['left'].set_color('blue')
    axes2.spines['right'].set_color('red')
    axes1.set_xlabel('Hz')
    axes1.set_ylabel('Gain, dB')
    axes2.set_ylabel('Phase angle')
    axes2.tick_params('y', color = 'red', labelcolor = 'red')
    axes2.yaxis.label.set_color('red')
    axes2.plot(self.xaxis, np.angle(gain, deg = True), color = 'red', label = 'Phase angle')
    self.cursor = datacursor(axes = self.figure.get_axes(), formatter = LabelFormatter(), display = 'multiple')
    self.canvas.draw()

  def plot_magphase(self, data):
    if self.cursor is not None: self.cursor.hide().disable()
    matplotlib.rcdefaults()
    self.figure.clf()
    self.figure.subplots_adjust(left = 0.12, bottom = 0.12, right = 0.88, top = 0.98)
    axes1 = self.figure.add_subplot(111)
    axes1.cla()
    axes1.xaxis.set_major_formatter(FuncFormatter(metric_prefix))
    axes1.yaxis.set_major_formatter(FuncFormatter(metric_prefix))
    axes1.tick_params('y', color = 'blue', labelcolor = 'blue')
    axes1.yaxis.label.set_color('blue')
    axes1.plot(self.xaxis, np.absolute(data), color = 'blue', label = 'Magnitude')
    axes2 = axes1.twinx()
    axes2.spines['left'].set_color('blue')
    axes2.spines['right'].set_color('red')
    axes1.set_xlabel('Hz')
    axes1.set_ylabel('Magnitude')
    axes2.set_ylabel('Phase angle')
    axes2.tick_params('y', color = 'red', labelcolor = 'red')
    axes2.yaxis.label.set_color('red')
    axes2.plot(self.xaxis, np.angle(data, deg = True), color = 'red', label = 'Phase angle')
    self.cursor = datacursor(axes = self.figure.get_axes(), formatter = LabelFormatter(), display = 'multiple')
    self.canvas.draw()

  def plot_open(self):
    self.plot_magphase(self.open[0:self.sweep_size])

  def plot_short(self):
    self.plot_magphase(self.short[0:self.sweep_size])

  def plot_load(self):
    self.plot_magphase(self.load[0:self.sweep_size])

  def plot_dut(self):
    self.plot_magphase(self.dut[0:self.sweep_size])

  def plot_smith_grid(self, axes, color):
    load = 50.0
    ticks = np.array([0.0, 0.2, 0.5, 1.0, 2.0, 5.0])
    for tick in ticks * load:
      axis = np.logspace(-4, np.log10(1.0e3), 200) * load
      z = tick + 1.0j * axis
      gamma = (z - load)/(z + load)
      axes.plot(gamma.real, gamma.imag, color = color, linewidth = 0.4, alpha = 0.3)
      axes.plot(gamma.real, -gamma.imag, color = color, linewidth = 0.4, alpha = 0.3)
      z = axis + 1.0j * tick
      gamma = (z - load)/(z + load)
      axes.plot(gamma.real, gamma.imag, color = color, linewidth = 0.4, alpha = 0.3)
      axes.plot(gamma.real, -gamma.imag, color = color, linewidth = 0.4, alpha = 0.3)
      if tick == 0.0:
        axes.text(1.0, 0.0, u'\u221E', color = color, ha = 'left', va = 'center', clip_on = True, fontsize = 18.0)
        axes.text(-1.0, 0.0, u'0\u03A9', color = color, ha = 'left', va = 'bottom', clip_on = True, fontsize = 12.0)
        continue
      lab = u'%d\u03A9' % tick
      x = (tick - load) / (tick + load)
      axes.text(x, 0.0, lab, color = color, ha = 'left', va = 'bottom', clip_on = True, fontsize = 12.0)
      lab = u'j%d\u03A9' % tick
      z =  1.0j * tick
      gamma = (z - load)/(z + load) * 1.05
      x = gamma.real
      y = gamma.imag
      angle = np.angle(gamma) * 180.0 / np.pi - 90.0
      axes.text(x, y, lab, color = color, ha = 'center', va = 'center', clip_on = True, rotation = angle, fontsize = 12.0)
      lab = u'-j%d\u03A9' % tick
      axes.text(x, -y, lab, color = color, ha = 'center', va = 'center', clip_on = True, rotation = -angle, fontsize = 12.0)

  def plot_smith(self):
    if self.cursor is not None: self.cursor.hide().disable()
    matplotlib.rcdefaults()
    self.figure.clf()
    self.figure.subplots_adjust(left = 0.0, bottom = 0.0, right = 1.0, top = 1.0)
    axes1 = self.figure.add_subplot(111)
    self.plot_smith_grid(axes1, 'blue')
    gamma = self.gamma()
    plot, = axes1.plot(gamma.real, gamma.imag, color = 'red')
    axes1.axis('equal')
    axes1.set_xlim(-1.12, 1.12)
    axes1.set_ylim(-1.12, 1.12)
    axes1.xaxis.set_visible(False)
    axes1.yaxis.set_visible(False)
    for loc, spine in axes1.spines.items():
      spine.set_visible(False)
    self.cursor = datacursor(plot, formatter = SmithFormatter(self.xaxis), display = 'multiple')
    self.canvas.draw()

  def plot_imp(self):
    self.plot_magphase(self.impedance())

  def plot_rc(self):
    self.plot_magphase(self.gamma())

  def plot_swr(self):
    if self.cursor is not None: self.cursor.hide().disable()
    matplotlib.rcdefaults()
    self.figure.clf()
    self.figure.subplots_adjust(left = 0.12, bottom = 0.12, right = 0.88, top = 0.98)
    axes1 = self.figure.add_subplot(111)
    axes1.cla()
    axes1.xaxis.set_major_formatter(FuncFormatter(metric_prefix))
    axes1.yaxis.set_major_formatter(FuncFormatter(metric_prefix))
    axes1.set_xlabel('Hz')
    axes1.set_ylabel('SWR')
    magnitude = np.absolute(self.gamma())
    swr = np.maximum(1.0, np.minimum(100.0, (1.0 + magnitude) / np.maximum(1.0e-20, 1.0 - magnitude)))
    axes1.plot(self.xaxis, swr, color = 'blue', label = 'SWR')
    self.cursor = datacursor(axes = self.figure.get_axes(), formatter = LabelFormatter(), display = 'multiple')
    self.canvas.draw()

  def plot_rl(self):
    if self.cursor is not None: self.cursor.hide().disable()
    matplotlib.rcdefaults()
    self.figure.clf()
    self.figure.subplots_adjust(left = 0.12, bottom = 0.12, right = 0.88, top = 0.98)
    axes1 = self.figure.add_subplot(111)
    axes1.cla()
    axes1.xaxis.set_major_formatter(FuncFormatter(metric_prefix))
    axes1.set_xlabel('Hz')
    axes1.set_ylabel('Return loss, dB')
    magnitude = np.absolute(self.gamma())
    axes1.plot(self.xaxis, 20.0 * np.log10(magnitude), color = 'blue', label = 'Return loss')
    self.cursor = datacursor(axes = self.figure.get_axes(), formatter = LabelFormatter(), display = 'multiple')
    self.canvas.draw()

  def write_cfg(self):
    dialog = QFileDialog(self, 'Write configuration settings', '.', '*.ini')
    dialog.setDefaultSuffix('ini')
    dialog.setAcceptMode(QFileDialog.AcceptSave)
    dialog.setOptions(QFileDialog.DontConfirmOverwrite)
    if dialog.exec() == QDialog.Accepted:
      name = dialog.selectedFiles()
      settings = QSettings(name[0], QSettings.IniFormat)
      self.write_cfg_settings(settings)

  def read_cfg(self):
    dialog = QFileDialog(self, 'Read configuration settings', '.', '*.ini')
    dialog.setDefaultSuffix('ini')
    dialog.setAcceptMode(QFileDialog.AcceptOpen)
    if dialog.exec() == QDialog.Accepted:
      name = dialog.selectedFiles()
      settings = QSettings(name[0], QSettings.IniFormat)
      self.read_cfg_settings(settings)

  def write_cfg_settings(self, settings):
    settings.setValue('addr', self.addrValue.text())
    settings.setValue('start', self.startValue.value())
    settings.setValue('stop', self.stopValue.value())
    settings.setValue('rate', self.rateValue.currentIndex())
    settings.setValue('corr', self.corrValue.value())
    size = self.sizeValue.value()
    settings.setValue('size', size)
    for i in range(0, size):
      settings.setValue('open_real_%d' % i, float(self.open.real[i]))
      settings.setValue('open_imag_%d' % i, float(self.open.imag[i]))
    for i in range(0, size):
      settings.setValue('short_real_%d' % i, float(self.short.real[i]))
      settings.setValue('short_imag_%d' % i, float(self.short.imag[i]))
    for i in range(0, size):
      settings.setValue('load_real_%d' % i, float(self.load.real[i]))
      settings.setValue('load_imag_%d' % i, float(self.load.imag[i]))
    for i in range(0, size):
      settings.setValue('dut_real_%d' % i, float(self.dut.real[i]))
      settings.setValue('dut_imag_%d' % i, float(self.dut.imag[i]))

  def read_cfg_settings(self, settings):
    self.addrValue.setText(settings.value('addr', '192.168.1.100'))
    self.startValue.setValue(settings.value('start', 100, type = int))
    self.stopValue.setValue(settings.value('stop', 60000, type = int))
    self.rateValue.setCurrentIndex(settings.value('rate', 0, type = int))
    self.corrValue.setValue(settings.value('corr', 0, type = int))
    size = settings.value('size', 600, type = int)
    self.sizeValue.setValue(size)
    for i in range(0, size):
      real = settings.value('open_real_%d' % i, 0.0, type = float)
      imag = settings.value('open_imag_%d' % i, 0.0, type = float)
      self.open[i] = real + 1.0j * imag
    for i in range(0, size):
      real = settings.value('short_real_%d' % i, 0.0, type = float)
      imag = settings.value('short_imag_%d' % i, 0.0, type = float)
      self.short[i] = real + 1.0j * imag
    for i in range(0, size):
      real = settings.value('load_real_%d' % i, 0.0, type = float)
      imag = settings.value('load_imag_%d' % i, 0.0, type = float)
      self.load[i] = real + 1.0j * imag
    for i in range(0, size):
      real = settings.value('dut_real_%d' % i, 0.0, type = float)
      imag = settings.value('dut_imag_%d' % i, 0.0, type = float)
      self.dut[i] = real + 1.0j * imag

  def write_csv(self):
    dialog = QFileDialog(self, 'Write csv file', '.', '*.csv')
    dialog.setDefaultSuffix('csv')
    dialog.setAcceptMode(QFileDialog.AcceptSave)
    dialog.setOptions(QFileDialog.DontConfirmOverwrite)
    if dialog.exec() == QDialog.Accepted:
      name = dialog.selectedFiles()
      fh = open(name[0], 'w')
      gamma = self.gamma()
      size = self.sizeValue.value()
      fh.write('frequency;open.real;open.imag;short.real;short.imag;load.real;load.imag;dut.real;dut.imag\n')
      for i in range(0, size):
        fh.write('0.0%.8d;%12.9f;%12.9f;%12.9f;%12.9f;%12.9f;%12.9f;%12.9f;%12.9f\n' % (self.xaxis[i], self.open.real[i], self.open.imag[i], self.short.real[i], self.short.imag[i], self.load.real[i], self.load.imag[i], self.dut.real[i], self.dut.imag[i]))
      fh.close()

  def write_s1p(self):
    dialog = QFileDialog(self, 'Write s1p file', '.', '*.s1p')
    dialog.setDefaultSuffix('s1p')
    dialog.setAcceptMode(QFileDialog.AcceptSave)
    dialog.setOptions(QFileDialog.DontConfirmOverwrite)
    if dialog.exec() == QDialog.Accepted:
      name = dialog.selectedFiles()
      fh = open(name[0], 'w')
      gamma = self.gamma()
      size = self.sizeValue.value()
      fh.write('# GHz S MA R 50\n')
      for i in range(0, size):
        fh.write('0.0%.8d   %8.6f %7.2f\n' % (self.xaxis[i], np.absolute(gamma[i]), np.angle(gamma[i], deg = True)))
      fh.close()

  def write_s2p(self):
    dialog = QFileDialog(self, 'Write s2p file', '.', '*.s2p')
    dialog.setDefaultSuffix('s2p')
    dialog.setAcceptMode(QFileDialog.AcceptSave)
    dialog.setOptions(QFileDialog.DontConfirmOverwrite)
    if dialog.exec() == QDialog.Accepted:
      name = dialog.selectedFiles()
      fh = open(name[0], 'w')
      gain = self.gain()
      gamma = self.gamma()
      size = self.sizeValue.value()
      fh.write('# GHz S MA R 50\n')
      for i in range(0, size):
        fh.write('0.0%.8d   %8.6f %7.2f   %8.6f %7.2f   0.000000    0.00   0.000000    0.00\n' % (self.xaxis[i], np.absolute(gamma[i]), np.angle(gamma[i], deg = True), np.absolute(gain[i]), np.angle(gain[i], deg = True)))
      fh.close()
Ejemplo n.º 43
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.figure = Figure()
        self.figureCanvas = FigureCanvas(self.figure)
        self.plotToolbar = NavigationToolbar(self.figureCanvas, self)
        self.data = StiffnessData()

    def setup_ui(self):
        self.ui.plotLayout.addWidget(self.figureCanvas)
        self.ui.plotLayout.addWidget(self.plotToolbar)
        self.setup_handlers()

    def setup_handlers(self):
        self.ui.setupBraketParametersBtn.clicked.connect(
            self.bracket_parameters_setup)
        self.ui.meshGenerateBtn.clicked.connect(self.mesh_parameters_setup)
        self.ui.setupLFparametersBtn.clicked.connect(
            self.load_fix_parameters_setup)
        self.ui.loadTypeBox.currentIndexChanged.connect(
            self.load_fix_box_type_changed)
        self.ui.fixTypeBox.currentIndexChanged.connect(
            self.load_fix_box_type_changed)

    def draw_plot(self):
        axis = self.figure.add_subplot(121) if len(
            self.figure.get_axes()) == 0 else self.figure.get_axes()[0]
        bracket = default_bracket.copy()
        bracket.append(bracket[0])
        bracket = np.array(bracket)
        axis.plot(bracket[:, 0], bracket[:, 1], c="blue")

    def draw_mesh(self):
        x = self.data.get_tri_x()
        y = self.data.get_tri_y()
        if x is not None and y is not None:
            axis = self.figure.get_axes()[0] if len(
                self.figure.get_axes()) > 0 else self.figure.add_subplot(121)
            axis.triplot(x, y, self.data.tri.simplices, c="lime")
            axis.plot(x, y, 'o')
            self.figure.canvas.draw()

    def calculate(self, p):
        if self.data.is_prepared():
            self.data.set_fixing(Node(default_bracket[0]))
            self.data.set_fixing(Node(default_bracket[1]))
            self.data.set_load(Node(default_bracket[2]), p)
            fg = Finite2DGenerator(self.data)
            moving = fg.calculate_moving()
            self.ui.resultText.setText(f"0 - {min(moving)}")
            fg.draw_displaced(
                self.figure.add_subplot(122) if len(self.figure.get_axes()) < 2
                else self.figure.get_axes()[1])
            self.figure.canvas.draw()
        else:
            self.show_popup("Ошибка", "Не введены необходимые данные")

    def bracket_parameters_setup(self):
        try:
            y = float(self.ui.youngaParameter.text())
            m = float(self.ui.poissonParameter.text())
            h = float(self.ui.thicknessParameter.text())
            self.ui.bracketParametersText.setText(f"""Параметры кронштейна:
Коэффициент Юнга: {y}
Коэффициент Пуассона: {m}
Толщина кронштейна: {h} мм""")
            self.data.poisson, self.data.young, self.data.thickness = m, y, float(
                h) / 1000.0
            self.draw_plot()
            self.ui.pageMesh.setEnabled(True)
        except ValueError:
            self.show_popup("Ошибка ввода", "Неккоректное число")

    def mesh_parameters_setup(self):
        try:
            size = float(self.ui.meshSizeInput.text())
            self.data.define_nodes_mesh(default_bracket.copy(), size)
            self.draw_mesh()
            self.setup_node_fix_load_info()
            self.set_checkable_boxes()
            val = float(self.ui.powerValue.text())
            self.calculate(val)
        except ValueError as e:
            self.show_popup("Ошибка ввода", "Неккоректное число",
                            QMessageBox.Critical, None, f"{e}\n{e}")

    def load_fix_parameters_setup(self):
        fix = self.ui.fixParametersBox.findChildren(QtWidgets.QCheckBox)
        load = self.ui.loadParametersBox.findChildren(QtWidgets.QCheckBox)
        fix_indexes = []
        load_indexes = []
        i = 0
        for f, l in zip(fix, load):
            if f.isEnabled() and f.isChecked():
                fix_indexes.append(self.define_node_edge(f.text(), i))
            if l.isEnabled() and l.isChecked():
                load_indexes.append(self.define_node_edge(l.text(), i))
            i += 1
        if self.check_fix_load_validate(fix_indexes, load_indexes):
            print(fix_indexes, load_indexes)

    @staticmethod
    def define_node_edge(text, default_value):
        for i in default_bracket:
            if text == f"{i}":
                return i
        return default_value

    def check_fix_load_validate(self, fix, load):
        valid = True
        if len(fix) == 0 or len(load) == 0:
            self.show_popup("Неверные данные",
                            "Не заданы закрепления или фиксация",
                            QMessageBox.Warning)
            valid = False
        elif len(fix) + len(load) > 3:
            self.show_popup("Неверные данные",
                            "Закрепления и фиксация заданны не корректно",
                            QMessageBox.Warning)
            valid = False
        elif len([e for e in fix if e in load]) > 0:
            self.show_popup("Неверные данные",
                            "Закрепления и фиксация совпадают",
                            QMessageBox.Warning)
            valid = False
        return valid

    def load_fix_box_type_changed(self):
        self.set_checkable_boxes()

    def set_checkable_boxes(self):
        if self.ui.fixTypeBox.currentIndex() == 0:
            self.enable_fix_node_checkboxes()
        else:
            self.enable_fix_node_checkboxes(False)
        if self.ui.loadTypeBox.currentIndex() == 0:
            self.enable_load_node_checkboxes()
        else:
            self.enable_load_node_checkboxes(False)

    def setup_node_fix_load_info(self):
        fix = self.ui.fixParametersBox.findChildren(QtWidgets.QCheckBox)[0:3]
        load = self.ui.loadParametersBox.findChildren(QtWidgets.QCheckBox)[0:3]
        index = 0
        for f, l in zip(fix, load):
            f.setText(f"{default_bracket[index]}")
            l.setText(f"{default_bracket[index]}")
            index += 1
        self.ui.bottomFixNodeCheckBox.setChecked(True)
        self.ui.leftFixNodeCheckBox.setChecked(True)
        self.ui.rightLoadNodeCheckBox.setChecked(True)

    def enable_fix_node_checkboxes(self, enable=True):
        self.enable_checkboxes(self.ui.fixParametersBox, 0, 3, enable)
        self.enable_checkboxes(self.ui.fixParametersBox, 3, 6, not enable)

    def enable_load_node_checkboxes(self, enable=True):
        self.enable_checkboxes(self.ui.loadParametersBox, 0, 3, enable)
        self.enable_checkboxes(self.ui.loadParametersBox, 3, 6, not enable)

    @staticmethod
    def enable_checkboxes(widget, i, j, enable):
        for w in widget.findChildren(QtWidgets.QCheckBox)[i:j]:
            w.setCheckable(enable)
            w.setEnabled(enable)

    @staticmethod
    def show_popup(title,
                   text,
                   icon=QMessageBox.Critical,
                   info=None,
                   details=None):
        msg = QMessageBox()
        msg.setWindowTitle(title)
        msg.setText(text)
        msg.setIcon(icon)
        if info is not None:
            msg.setInformativeText(info)
        if details is not None:
            msg.setDetailedText(details)
        return msg.exec_()
Ejemplo n.º 44
0
class PlotCanvas(QtCore.QObject):
    """
    Class handling the plotting area in the application.
    """

    # Signals:
    # Request for new bitmap to display. The parameter
    # is a list with [xmin, xmax, ymin, ymax, zoom(optional)]
    update_screen_request = QtCore.pyqtSignal(list)

    def __init__(self, container, app):
        """
        The constructor configures the Matplotlib figure that
        will contain all plots, creates the base axes and connects
        events to the plotting area.

        :param container: The parent container in which to draw plots.
        :rtype: PlotCanvas
        """

        super(PlotCanvas, self).__init__()

        self.app = app

        # Options
        self.x_margin = 15  # pixels
        self.y_margin = 25  # Pixels

        # Parent container
        self.container = container

        # Plots go onto a single matplotlib.figure
        self.figure = Figure(dpi=50)  # TODO: dpi needed?
        self.figure.patch.set_visible(False)

        # These axes show the ticks and grid. No plotting done here.
        # New axes must have a label, otherwise mpl returns an existing one.
        self.axes = self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label="base", alpha=0.0)
        self.axes.set_aspect(1)
        self.axes.grid(True)
        self.axes.axhline(color='Black')
        self.axes.axvline(color='Black')

        # The canvas is the top level container (FigureCanvasQTAgg)
        self.canvas = FigureCanvas(self.figure)
        # self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
        # self.canvas.setFocus()

        #self.canvas.set_hexpand(1)
        #self.canvas.set_vexpand(1)
        #self.canvas.set_can_focus(True)  # For key press

        # Attach to parent
        #self.container.attach(self.canvas, 0, 0, 600, 400)  # TODO: Height and width are num. columns??
        self.container.addWidget(self.canvas)  # Qt

        # Copy a bitmap of the canvas for quick animation.
        # Update every time the canvas is re-drawn.
        self.background = self.canvas.copy_from_bbox(self.axes.bbox)

        ### Bitmap Cache
        self.cache = CanvasCache(self, self.app)
        self.cache_thread = QtCore.QThread()
        self.cache.moveToThread(self.cache_thread)
        #super(PlotCanvas, self).connect(self.cache_thread, QtCore.SIGNAL("started()"), self.cache.run)
        # self.connect()
        self.cache_thread.start()
        self.cache.new_screen.connect(self.on_new_screen)

        # Events
        self.canvas.mpl_connect('button_press_event', self.on_mouse_press)
        self.canvas.mpl_connect('button_release_event', self.on_mouse_release)
        self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
        #self.canvas.connect('configure-event', self.auto_adjust_axes)
        self.canvas.mpl_connect('resize_event', self.auto_adjust_axes)
        #self.canvas.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK)
        #self.canvas.connect("scroll-event", self.on_scroll)
        self.canvas.mpl_connect('scroll_event', self.on_scroll)
        self.canvas.mpl_connect('key_press_event', self.on_key_down)
        self.canvas.mpl_connect('key_release_event', self.on_key_up)
        self.canvas.mpl_connect('draw_event', self.on_draw)

        self.mouse = [0, 0]
        self.key = None

        self.pan_axes = []
        self.panning = False

    def on_new_screen(self):

        log.debug("Cache updated the screen!")

    def on_key_down(self, event):
        """

        :param event:
        :return:
        """
        FlatCAMApp.App.log.debug('on_key_down(): ' + str(event.key))
        self.key = event.key

    def on_key_up(self, event):
        """

        :param event:
        :return:
        """
        self.key = None

    def mpl_connect(self, event_name, callback):
        """
        Attach an event handler to the canvas through the Matplotlib interface.

        :param event_name: Name of the event
        :type event_name: str
        :param callback: Function to call
        :type callback: func
        :return: Connection id
        :rtype: int
        """
        return self.canvas.mpl_connect(event_name, callback)

    def mpl_disconnect(self, cid):
        """
        Disconnect callback with the give id.
        :param cid: Callback id.
        :return: None
        """
        self.canvas.mpl_disconnect(cid)

    def connect(self, event_name, callback):
        """
        Attach an event handler to the canvas through the native Qt interface.

        :param event_name: Name of the event
        :type event_name: str
        :param callback: Function to call
        :type callback: function
        :return: Nothing
        """
        self.canvas.connect(event_name, callback)

    def clear(self):
        """
        Clears axes and figure.

        :return: None
        """

        # Clear
        self.axes.cla()
        try:
            self.figure.clf()
        except KeyError:
            FlatCAMApp.App.log.warning("KeyError in MPL figure.clf()")

        # Re-build
        self.figure.add_axes(self.axes)
        self.axes.set_aspect(1)
        self.axes.grid(True)

        # Re-draw
        self.canvas.draw_idle()

    def adjust_axes(self, xmin, ymin, xmax, ymax):
        """
        Adjusts all axes while maintaining the use of the whole canvas
        and an aspect ratio to 1:1 between x and y axes. The parameters are an original
        request that will be modified to fit these restrictions.

        :param xmin: Requested minimum value for the X axis.
        :type xmin: float
        :param ymin: Requested minimum value for the Y axis.
        :type ymin: float
        :param xmax: Requested maximum value for the X axis.
        :type xmax: float
        :param ymax: Requested maximum value for the Y axis.
        :type ymax: float
        :return: None
        """

        # FlatCAMApp.App.log.debug("PC.adjust_axes()")

        width = xmax - xmin
        height = ymax - ymin
        try:
            r = width / height
        except ZeroDivisionError:
            FlatCAMApp.App.log.error("Height is %f" % height)
            return
        canvas_w, canvas_h = self.canvas.get_width_height()
        canvas_r = float(canvas_w) / canvas_h
        x_ratio = float(self.x_margin) / canvas_w
        y_ratio = float(self.y_margin) / canvas_h

        if r > canvas_r:
            ycenter = (ymin + ymax) / 2.0
            newheight = height * r / canvas_r
            ymin = ycenter - newheight / 2.0
            ymax = ycenter + newheight / 2.0
        else:
            xcenter = (xmax + xmin) / 2.0
            newwidth = width * canvas_r / r
            xmin = xcenter - newwidth / 2.0
            xmax = xcenter + newwidth / 2.0

        # Adjust axes
        for ax in self.figure.get_axes():
            if ax._label != 'base':
                ax.set_frame_on(False)  # No frame
                ax.set_xticks([])  # No tick
                ax.set_yticks([])  # No ticks
                ax.patch.set_visible(False)  # No background
                ax.set_aspect(1)
            ax.set_xlim((xmin, xmax))
            ax.set_ylim((ymin, ymax))
            ax.set_position([x_ratio, y_ratio, 1 - 2 * x_ratio, 1 - 2 * y_ratio])

        # Sync re-draw to proper paint on form resize
        self.canvas.draw()

        ##### Temporary place-holder for cached update #####
        self.update_screen_request.emit([0, 0, 0, 0, 0])

    def auto_adjust_axes(self, *args):
        """
        Calls ``adjust_axes()`` using the extents of the base axes.

        :rtype : None
        :return: None
        """

        xmin, xmax = self.axes.get_xlim()
        ymin, ymax = self.axes.get_ylim()
        self.adjust_axes(xmin, ymin, xmax, ymax)

    def zoom(self, factor, center=None):
        """
        Zooms the plot by factor around a given
        center point. Takes care of re-drawing.

        :param factor: Number by which to scale the plot.
        :type factor: float
        :param center: Coordinates [x, y] of the point around which to scale the plot.
        :type center: list
        :return: None
        """

        xmin, xmax = self.axes.get_xlim()
        ymin, ymax = self.axes.get_ylim()
        width = xmax - xmin
        height = ymax - ymin

        if center is None or center == [None, None]:
            center = [(xmin + xmax) / 2.0, (ymin + ymax) / 2.0]

        # For keeping the point at the pointer location
        relx = (xmax - center[0]) / width
        rely = (ymax - center[1]) / height

        new_width = width / factor
        new_height = height / factor

        xmin = center[0] - new_width * (1 - relx)
        xmax = center[0] + new_width * relx
        ymin = center[1] - new_height * (1 - rely)
        ymax = center[1] + new_height * rely

        # Adjust axes
        for ax in self.figure.get_axes():
            ax.set_xlim((xmin, xmax))
            ax.set_ylim((ymin, ymax))

        # Async re-draw
        self.canvas.draw_idle()

        ##### Temporary place-holder for cached update #####
        self.update_screen_request.emit([0, 0, 0, 0, 0])

    def pan(self, x, y):
        xmin, xmax = self.axes.get_xlim()
        ymin, ymax = self.axes.get_ylim()
        width = xmax - xmin
        height = ymax - ymin

        # Adjust axes
        for ax in self.figure.get_axes():
            ax.set_xlim((xmin + x * width, xmax + x * width))
            ax.set_ylim((ymin + y * height, ymax + y * height))

        # Re-draw
        self.canvas.draw_idle()

        ##### Temporary place-holder for cached update #####
        self.update_screen_request.emit([0, 0, 0, 0, 0])

    def new_axes(self, name):
        """
        Creates and returns an Axes object attached to this object's Figure.

        :param name: Unique label for the axes.
        :return: Axes attached to the figure.
        :rtype: Axes
        """

        return self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label=name)

    def on_scroll(self, event):
        """
        Scroll event handler.

        :param event: Event object containing the event information.
        :return: None
        """

        # So it can receive key presses
        # self.canvas.grab_focus()
        self.canvas.setFocus()

        # Event info
        # z, direction = event.get_scroll_direction()

        if self.key is None:

            if event.button == 'up':
                self.zoom(1.5, self.mouse)
            else:
                self.zoom(1 / 1.5, self.mouse)
            return

        if self.key == 'shift':

            if event.button == 'up':
                self.pan(0.3, 0)
            else:
                self.pan(-0.3, 0)
            return

        if self.key == 'control':

            if event.button == 'up':
                self.pan(0, 0.3)
            else:
                self.pan(0, -0.3)
            return

    def on_mouse_press(self, event):

        # Check for middle mouse button press
        if event.button == self.app.mouse_pan_button:

            # Prepare axes for pan (using 'matplotlib' pan function)
            self.pan_axes = []
            for a in self.figure.get_axes():
                if (event.x is not None and event.y is not None and a.in_axes(event) and
                        a.get_navigate() and a.can_pan()):
                    a.start_pan(event.x, event.y, 1)
                    self.pan_axes.append(a)

            # Set pan view flag
            if len(self.pan_axes) > 0: self.panning = True;

    def on_mouse_release(self, event):

        # Check for middle mouse button release to complete pan procedure
        if event.button == self.app.mouse_pan_button:
            for a in self.pan_axes:
                a.end_pan()

            # Clear pan flag
            self.panning = False

    def on_mouse_move(self, event):
        """
        Mouse movement event hadler. Stores the coordinates. Updates view on pan.

        :param event: Contains information about the event.
        :return: None
        """
        self.mouse = [event.xdata, event.ydata]

        # Update pan view on mouse move
        if self.panning is True:
            for a in self.pan_axes:
                a.drag_pan(1, event.key, event.x, event.y)

            # Async re-draw (redraws only on thread idle state, uses timer on backend)
            self.canvas.draw_idle()

            ##### Temporary place-holder for cached update #####
            self.update_screen_request.emit([0, 0, 0, 0, 0])

    def on_draw(self, renderer):

        # Store background on canvas redraw
        self.background = self.canvas.copy_from_bbox(self.axes.bbox)

    def get_axes_pixelsize(self):
        """
        Axes size in pixels.

        :return: Pixel width and height
        :rtype: tuple
        """
        bbox = self.axes.get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
        width, height = bbox.width, bbox.height
        width *= self.figure.dpi
        height *= self.figure.dpi
        return width, height

    def get_density(self):
        """
        Returns unit length per pixel on horizontal
        and vertical axes.

        :return: X and Y density
        :rtype: tuple
        """
        xpx, ypx = self.get_axes_pixelsize()

        xmin, xmax = self.axes.get_xlim()
        ymin, ymax = self.axes.get_ylim()
        width = xmax - xmin
        height = ymax - ymin

        return width / xpx, height / ypx
Ejemplo n.º 45
0
class QTwostageModelWindow(QTclPopupWindow):
    def __init__(self, parent, title, twostage_inp, is_modal=False):
        self.dresult = "NONE"
        self.durvar = self.getvar(twostage_inp.duration)
        self.cNTvar = self.getvar(twostage_inp.cNT)
        self.caggvar = self.getvar(twostage_inp.cagg)
        self.rNTvar = self.getvar(twostage_inp.rNT)
        self.raggvar = self.getvar(twostage_inp.ragg)
        super().__init__(parent, title, is_modal)

    def loaded(self):
        pass

    def make_gui(self, title):
        self.setwintitle(title)

        row = 0
        setfr = self.make_label_frame(lrow=row,
                                      caption='Enter data for core and rim',
                                      padx=(5, 2),
                                      pady=(5, 5))

        irow = 0
        self.makelabel(setfr, lrow=irow, lcol=1, caption='core', sticky=tk.EW)
        self.makelabel(setfr, lrow=irow, lcol=2, caption='rim', sticky=tk.EW)

        irow += 1
        self.cNT, self.rNT = self.make_double_entrypair(setfr,
                                                        lrow=irow,
                                                        e1row=irow,
                                                        e2row=irow,
                                                        caption='[NT]: ',
                                                        var1=self.cNTvar,
                                                        var2=self.rNTvar,
                                                        padx=(5, 5),
                                                        pady=(5, 2))
        self.makelabel(setfr, lrow=irow, lcol=3, caption='(ppm)', sticky=tk.W)

        irow += 1
        self.cNT, self.rNT = self.make_double_entrypair(setfr,
                                                        lrow=irow,
                                                        e1row=irow,
                                                        e2row=irow,
                                                        caption='[NB]/[NT]: ',
                                                        var1=self.caggvar,
                                                        var2=self.raggvar,
                                                        padx=(5, 5),
                                                        pady=(2, 2))
        self.makelabel(setfr, lrow=irow, lcol=3, caption='(-)', sticky=tk.W)

        irow += 1
        self.make_double_entry(setfr,
                               lrow=irow,
                               erow=irow,
                               caption='Total duration: ',
                               textvariable=self.durvar,
                               padx=(2, 2),
                               pady=(2, 5))
        self.makelabel(setfr, lrow=irow, lcol=2, caption='(Ma)', sticky=tk.W)

        irow += 1
        self.makebutton(setfr,
                        erow=irow,
                        ecol=1,
                        cmd=self.calc_model,
                        caption='Calculate')
        self.savebutton = self.makebutton(setfr,
                                          erow=irow,
                                          ecol=2,
                                          cmd=self.save_to_file,
                                          caption='Save results to file')
        self.savebutton.config(state=tk.DISABLED)

        plotfr = self.make_label_frame(lrow=row,
                                       lcol=2,
                                       caption='Model',
                                       pady=(5, 5))
        self.fig = Figure(dpi=100)
        jrow = 0
        self.canv = self.make_mplcanvas(plotfr, fig=self.fig, erow=jrow)

        jrow += 1
        toolbar_fr = self.make_frame(plotfr, erow=jrow)
        self.toolbar = NavigationToolbar2Tk(self.canv, toolbar_fr)

        row += 1
        self.add_std_buttons(row=row, dismisscol=0)

    def calc_model(self):
        for ax in self.fig.get_axes():
            self.fig.delaxes(ax)

        self.durations, self.temps1, self.temps2 = ts.model(
            self.durvar.get(), self.cNTvar.get(), self.caggvar.get(),
            self.rNTvar.get(), self.raggvar.get())
        sp = self.fig.add_subplot(111)
        sp.plot(self.durations, self.temps1, 'r.', label='core')
        sp.plot(self.durations, self.temps2, 'b.', label='rim')
        sp.set(ylim=(1000, 1500),
               xlim=(0, float(self.durvar.get())),
               xlabel='Duration of first anneal (Ma)',
               ylabel='Temperature ($\mathregular{^{\circ}}$C)')
        sp.legend(loc='best')

        self.canv.draw()
        self.savebutton.config(state=tk.NORMAL)

    def save_to_file(self):
        fname = fd.asksaveasfilename(title='Save results to...',
                                     initialdir=QSettings.userhome,
                                     defaultextension='.csv')
        if fname != '':
            data = np.column_stack((self.durations, self.temps1, self.temps2))

            header = 'two-stage model with {} Ma; core: NT {} agg {}; rim: NT {} ragg {}\nduration of first anneal, T_core, T_rim'.format(
                self.durvar.get(), self.cNTvar.get(), self.caggvar.get(),
                self.rNTvar.get(), self.raggvar.get())
            np.savetxt(fname, data, delimiter=',', header=header)
        else:
            raise NameError('No file created, results not saved.')
Ejemplo n.º 46
0
class AppForm(QMainWindow):

	def __init__(self, fileIn, parent=None):
		QMainWindow.__init__(self, parent)
		
		self.setWindowTitle('PyProf')
		self.currentFile = fileIn
				
		self.whichShowing = 0
		self.posMouse = [0,0]

		self.createMainFrame()

	def readParameters(self, rootFile):
	
# Read inverted parameters
		ff = nf(rootFile+'.parameters', 'r')
		pars = ff.variables['map'][:]
		ff.close()
	
# Read errors
		ff = nf(rootFile+'.errors', 'r')
		errors = ff.variables['map'][:]
		ff.close()
		
		return pars, errors

	def readProfiles(self, rootFile):

# Read inverted profiles
		ff = nf(rootFile+'.inversion', 'r')
		synthProf = ff.variables['map'][:]
		ff.close()
	
		return synthProf

	def readObservations(self, rootFile):

# Read inverted profiles
		ff = nf(rootFile+'.nc', 'r')
		sizeMask = ff.variables['mask'].shape
		sizeMap = ff.variables['map'].shape
		obsProf = ff.variables['map'][:].reshape((sizeMask[0],sizeMask[1],sizeMap[-2],sizeMap[-1]))
		ff.close()
	
		return obsProf
			
	def readData(self):
	
	# Parameters
		self.obs = self.readObservations(self.currentFile)
		obsShape = self.obs.shape

		self.pars, self.errors = self.readParameters(self.currentFile)
		parsShape = self.pars.shape
		self.pars = self.pars.reshape((obsShape[0],obsShape[1],parsShape[-1]))
		self.errors = self.errors.reshape((obsShape[0],obsShape[1],parsShape[-1]))
		
		self.syn = self.readProfiles(self.currentFile)
		synShape = self.syn.shape
		self.syn = self.syn.reshape((obsShape[0],obsShape[1],synShape[-2],synShape[-1]))
				
		self.nx, self.ny, self.nLambda, _ = self.syn.shape		
						
		self.maps = [None] * 4
		self.maps = [None] * 4		
		for i in range(4):
			self.maps[i] = np.sum(self.obs[:,:,:,0], axis=(2))
				
	def updateProfiles(self):		
# Blit animation. We only redraw the lines
		loop = 0
		for j in range(6):
			for i in range(4):
				self.rightCanvas.restore_region(self.background[loop])
				self.obs[loop].set_ydata(self.profiles[self.rangeFrom[j]:self.rangeTo[j],i])
				self.axes[loop].draw_artist(self.obs[loop])
				self.rightCanvas.blit(self.axes[loop].bbox)
				loop += 1		
						
	def onMouseMove(self, event):	
		print(event.xdata, event.ydata)
		if (event.xdata != None and event.ydata != None):			
			newPos = np.asarray([event.xdata, event.ydata])
			newPos = newPos.astype(int)

			if (newPos[0] != self.posMouse[0] or newPos[1] != self.posMouse[1]):
				self.posMouse = newPos
				self.profiles = self.getProfiles(newPos[0], newPos[1])
				self.updateProfiles()
			
	def onScrollMove(self,event):		
		pass
		#if (event.button == 'up'):
			#self.newTime += 1			
		#if (event.button == 'down'):
			#self.newTime -= 1
			
		#self.newTime = self.newTime % 35
		
		#self.profiles = self.getProfiles(self.posMouse[0], self.posMouse[1])
		#self.updateProfiles()

			
	def getProfiles(self, x, y):
		return self.obs[x,y,:,0:5], self.syn[x,y,:,:]
	
	def getMaps(self, x):
		return self.obs[:,:,0,0]
		
		
#--------------------------------------    
	def create_main_frame(self):
        
		self.mainFrame = QWidget()
		
		gridLayout = QGridLayout()
                       
		self.dpi = 80
		self.xpos = 0
		self.ypos = 0
				
		self.titles = [r'I',r'Q',r'U',r'V']
		
		self.readData()
		
		self.resize(1500,900)
			
# Left window
		self.leftPlot = QWidget()		
		self.leftFig = Figure((2*self.ny / self.dpi, 4*self.nx / self.dpi), dpi=self.dpi)		
		self.leftCanvas = FigureCanvas(self.leftFig)
		self.leftCanvas.setParent(self.leftPlot)
					
		self.leftAxes = [None]*4
		self.drawnMap = [None]*4		
		for i in range(4):
			self.leftAxes[i] = self.leftFig.add_subplot(2,2,i+1)
			self.drawnMap[i] = self.leftAxes[i].imshow(self.maps[i], aspect='equal')
			# self.leftAxes[i].set_axis_off()
		self.leftCanvas.mpl_connect('motion_notify_event', self.onMouseMove)
		self.leftCanvas.mpl_connect('scroll_event', self.onScrollMove)		
		gridLayout.addWidget(self.leftPlot, 0, 0)
		gridLayout.setSpacing(10)
		
# Central window				
		# self.centralPlot = QWidget()		
		# self.centralFig = Figure((10,8), dpi=self.dpi)
		# self.centralCanvas = FigureCanvas(self.centralFig)
		# self.centralCanvas.setParent(self.centralPlot)
				
		# self.slitMaps = self.getMaps(0)
		
		# self.centralAxes = [None]*4
		# self.drawnSlitMap = [None]*4
		# # for i in range(4):
		# # 	self.centralAxes[i] = self.centralFig.add_subplot(4,1,i+1)
		# # 	self.drawnSlitMap[i] = self.centralAxes[i].imshow(self.slitMaps[i,:,:])
		# #self.centralCanvas.draw()
			
		# gridLayout.addWidget(self.centralPlot, 0, 1)

# Right window		
		self.rightPlot = QWidget()
		self.rightFig = Figure((8,18), dpi=self.dpi)
		self.rightCanvas = FigureCanvas(self.rightFig)
		self.rightCanvas.setParent(self.rightPlot)
							
		self.stokesObs, self.stokesSyn = self.getProfiles(0,0)
		
# Draw the axes and the first profiles
		nCols = 2
		nRows = 2
		self.axes = [None] * 4
		self.obsLine = [None] * 4
		self.synLine = [None] * 4
		loop = 0
		for j in range(2):
			for i in range(2):
				self.axes[loop] = self.rightFig.add_subplot(nCols, nRows, loop+1)
				self.obsLine[loop], = self.axes[loop].plot(self.stokesObs[:,loop])
				self.synLine[loop], = self.axes[loop].plot(self.stokesSyn[:,loop])
				loop += 1
		gridLayout.addWidget(self.rightPlot, 0, 2)		
		
		
## Tight layout and redraw
		# self.rightFig.tight_layout()
		
		self.rightCanvas.draw()
				
## We are using blit animation to do it fast, so we need to recover the axes modified by tight_layout
		self.newAxes = self.rightFig.get_axes()
		
## Save the backgrounds
		# loop = 0
		# for j in range(6):
		# 	for i in range(4):
		# 		self.axes[loop] = self.newAxes[loop]
		# 		self.background[loop] = self.canvasRight.copy_from_bbox(self.axes[loop].bbox)
		# 		loop += 1
															
					
		gridLayout.addWidget(self.rightPlot)
		
		# fullLayout.addLayout(gridLayout)
		
		self.mainFrame.setLayout(gridLayout)
		self.setCentralWidget(self.mainFrame)
Ejemplo n.º 47
0
class streamPick(QtGui.QMainWindow):
    def __init__(self, stream=None, parent=None):
        # Initialising QtGui
        QtCore.QLocale.setDefault(QtCore.QLocale.c())
        qApp = QtGui.QApplication(sys.argv)

        # Init vars
        if stream is None:
            msg = "Define stream = obspy.core.Stream()"
            raise ValueError(msg)
        self.st = stream.copy()
        self._picks = []
        self.savefile = None
        self.onset_types = ["emergent", "impulsive", "questionable"]

        # Load filters from pickle
        try:
            self.bpfilter = pickle.load(open(".pick_filters", "r"))
        except:
            self.bpfilter = []
        # Internal variables
        # Gui vars
        self._shortcuts = {
            "st_next": "c",
            "st_previous": "x",
            "filter_apply": "f",
            "pick_p": "p",
            "pick_s": "s",
            "pick_custom": "t",
            "pick_remove": "r",
        }
        self._plt_drag = None
        self._current_filter = None
        # Init stations
        self._initStations()  # defines list self._stations
        self._stationCycle = cycle(self._stations)
        self._streamStation(self._stationCycle.next())
        # Init QtGui
        QtGui.QMainWindow.__init__(self)
        self.setupUI()
        # exec QtApp
        qApp.exec_()

    def setupUI(self):
        """
        Setup the UI
        """
        self.main_widget = QtGui.QWidget(self)
        # Init parts of the UI
        self._initMenu()
        self._createStatusBar()
        self._initPlots()
        self._wadatiPlt = None

        # Define layout
        l = QtGui.QVBoxLayout(self.main_widget)
        l.addLayout(self.btnbar)
        l.addWidget(self.canvas)

        self.setCentralWidget(self.main_widget)
        self.setGeometry(300, 300, 1200, 800)
        self.setWindowTitle("obspy.core.Stream-Picker")
        self.show()

    def _initPlots(self):
        self.fig = Figure(facecolor=".86", dpi=72, frameon=True)
        # Change facecolor
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
        # Draw the matplotlib figure
        self._drawFig()
        # Connect the events
        self.fig.canvas.mpl_connect("scroll_event", self._pltOnScroll)
        self.fig.canvas.mpl_connect("motion_notify_event", self._pltOnDrag)
        self.fig.canvas.mpl_connect("button_release_event", self._pltOnButtonRelease)
        self.fig.canvas.mpl_connect("button_press_event", self._pltOnButtonPress)

    def _initMenu(self):
        # Next and Prev Button
        nxt = QtGui.QPushButton("Next >>", shortcut=self._shortcuts["st_next"])
        nxt.clicked.connect(self._pltNextStation)
        nxt.setToolTip("shortcut <b>c</d>")
        nxt.setMaximumWidth(150)
        prv = QtGui.QPushButton("<< Prev", shortcut=self._shortcuts["st_previous"])
        prv.clicked.connect(self._pltPrevStation)
        prv.setToolTip("shortcut <b>x</d>")
        prv.setMaximumWidth(150)

        # Stations drop-down
        self.stcb = QtGui.QComboBox(self)
        for st in self._stations:
            self.stcb.addItem(st)
        self.stcb.activated.connect(self._pltStation)
        self.stcb.setMaximumWidth(100)
        self.stcb.setMinimumWidth(80)

        # Filter buttons
        self.fltrbtn = QtGui.QPushButton("Filter Trace", shortcut=self._shortcuts["filter_apply"])
        self.fltrbtn.setToolTip("shortcut <b>f</b>")
        self.fltrbtn.setCheckable(True)
        # self.fltrbtn.setAutoFillBackground(True)
        self.fltrbtn.setStyleSheet(QtCore.QString("QPushButton:checked {background-color: lightgreen;}"))
        self.fltrbtn.clicked.connect(self._appFilter)

        self.fltrcb = QtGui.QComboBox(self)
        self.fltrcb.activated.connect(self._changeFilter)
        self.fltrcb.setMaximumWidth(170)
        self.fltrcb.setMinimumWidth(150)
        self._updateFilterCB()  # fill QComboBox

        # edit/delete filer buttons
        fltredit = QtGui.QPushButton("Edit")
        fltredit.resize(fltredit.sizeHint())
        fltredit.clicked.connect(self._editFilter)

        fltrdel = QtGui.QPushButton("Delete")
        fltrdel.resize(fltrdel.sizeHint())
        fltrdel.clicked.connect(self._deleteFilter)

        btnstyle = QtGui.QFrame(fltredit)
        btnstyle.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain)
        btnstyle = QtGui.QFrame(fltrdel)
        btnstyle.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain)

        # onset type
        _radbtn = []
        for _o in self.onset_types:
            _radbtn.append(QtGui.QRadioButton(str(_o[0].upper())))
            _radbtn[-1].setToolTip("Onset " + _o)
            _radbtn[-1].clicked.connect(self._drawPicks)
            if _o == "impulsive":
                _radbtn[-1].setChecked(True)
        self.onsetGrp = QtGui.QButtonGroup()
        self.onsetGrp.setExclusive(True)
        onsetbtns = QtGui.QHBoxLayout()
        for _i, _btn in enumerate(_radbtn):
            self.onsetGrp.addButton(_btn, _i)
            onsetbtns.addWidget(_btn)

        # Arrange buttons
        vline = QtGui.QFrame()
        vline.setFrameStyle(QtGui.QFrame.VLine | QtGui.QFrame.Raised)
        self.btnbar = QtGui.QHBoxLayout()
        self.btnbar.addWidget(prv)
        self.btnbar.addWidget(nxt)
        self.btnbar.addWidget(QtGui.QLabel("Station"))
        self.btnbar.addWidget(self.stcb)
        ##
        self.btnbar.addWidget(vline)
        self.btnbar.addWidget(self.fltrbtn)
        self.btnbar.addWidget(self.fltrcb)
        self.btnbar.addWidget(fltredit)
        self.btnbar.addWidget(fltrdel)
        ##
        self.btnbar.addWidget(vline)
        self.btnbar.addWidget(QtGui.QLabel("Pick Onset: "))
        self.btnbar.addLayout(onsetbtns)
        self.btnbar.addStretch(3)

        # Menubar
        menubar = self.menuBar()
        fileMenu = menubar.addMenu("&File")
        fileMenu.addAction(QtGui.QIcon().fromTheme("document-save"), "Save", self._saveCatalog)
        fileMenu.addAction(QtGui.QIcon().fromTheme("document-save"), "Save as QuakeML File", self._saveCatalogDlg)
        fileMenu.addAction(QtGui.QIcon().fromTheme("document-open"), "Load QuakeML File", self._openCatalogDlg)
        fileMenu.addSeparator()
        fileMenu.addAction("Save Plot", self._savePlotDlg)
        fileMenu.addSeparator()
        fileMenu.addAction(QtGui.QIcon().fromTheme("application-exit"), "Exit", self.close)
        # windowMenu = menubar.addMenu('&Windows')
        # windowMenu.addAction('Wadati Diagram', self._opnWadatiPlot)
        aboutMenu = menubar.addMenu("&About")
        aboutMenu.addAction(QtGui.QIcon().fromTheme("info"), "Info", self._infoDlg)

    def _drawFig(self):
        """
        Draws all matplotlib figures
        """
        num_plots = len(self._current_st)
        self.fig.clear()
        self._appFilter(draw=False)
        for _i, tr in enumerate(self._current_st):
            ax = self.fig.add_subplot(num_plots, 1, _i)
            ax.plot(tr.data, "k", antialiased=True, rasterized=True, lod=False)
            ax.axhline(0, color="k", alpha=0.05)
            ax.set_xlim([0, tr.data.size])
            ax.text(0.02, 0.925, self._current_st[_i].id, transform=ax.transAxes, va="top", ha="left", alpha=0.75)
            ax.channel = tr.stats.channel
            if _i == 0:
                ax.set_xlabel("Seconds")

        # plot picks
        self._drawPicks(draw=False)
        self.fig.suptitle(
            "%s - %s - %s / %.1f Hz / %d samples per chanel"
            % (
                self._current_st[-1].stats.network,
                self._current_st[-1].stats.station,
                self._current_st[-1].stats.starttime.isoformat(),
                1.0 / self._current_st[-1].stats.delta,
                self._current_st[-1].stats.npts,
            ),
            x=0.2,
        )
        self._updateSB()
        self._canvasDraw()

    def _initStations(self):
        """
        Creates a list holding unique station names
        """
        self._stations = []
        for _tr in self.st:
            if _tr.stats.station not in self._stations:
                self._stations.append(_tr.stats.station)
        self._stations.sort()

    def _getPhases(self):
        """
        Creates a list holding unique phase names
        """
        phases = []
        for _pick in self._picks:
            if _pick.phase_hint not in phases:
                phases.append(_pick.phase_hint)
        return phases

    def _streamStation(self, station):
        """
        Copies the current stream object from self.st through
        obspy.stream.select(station=)
        """
        if station not in self._stations:
            return
        self._current_st = self.st.select(station=station).copy()
        self._current_stname = station
        self._current_network = self._current_st[0].stats.network
        # Sort and detrend streams
        self._current_st.sort(["channel"])
        self._current_st.detrend("linear")

    def _setPick(self, xdata, phase, channel, polarity="undecideable", overwrite_existing=False):
        """
        Write obspy.core.event.Pick into self._picks list
        """
        picktime = self._current_st[0].stats.starttime + (xdata * self._current_st[0].stats.delta)

        this_pick = event.Pick()
        overwrite = True
        # Overwrite existing phase's picktime
        if overwrite_existing:
            for _pick in self._getPicks():
                if _pick.phase_hint == phase and _pick.waveform_id.channel_code == channel:
                    this_pick = _pick
                    overwrite = False
                    break

        creation_info = event.CreationInfo(author="ObsPy.StreamPick", creation_time=UTCDateTime())
        # Create new event.Pick()
        this_pick.time = picktime
        this_pick.phase_hint = phase
        this_pick.waveform_id = event.WaveformStreamID(
            network_code=self._current_st[0].stats.network,
            station_code=self._current_st[0].stats.station,
            location_code=self._current_st[0].stats.location,
            channel_code=channel,
        )
        this_pick.evaluation_mode = "manual"
        this_pick.creation_info = creation_info
        this_pick.onset = self.onset_types[self.onsetGrp.checkedId()]
        this_pick.evaluation_status = "preliminary"
        this_pick.polarity = polarity
        # if self._current_filter is not None:
        #    this_pick.comments.append(event.Comment(
        #                text=str(self.bpfilter[self.fltrcb.currentIndex()])))
        if overwrite:
            self._picks.append(this_pick)

    def _delPicks(self, network, station, channel):
        """
        Deletes pick from catalog
        """
        for _i, _pick in enumerate(self._picks):
            if (
                _pick.waveform_id.network_code == network
                and _pick.waveform_id.station_code == station
                and _pick.waveform_id.channel_code == channel
            ):
                self._picks.remove(_pick)

    def _getPicks(self):
        """
        Create a list of picks for the current plot
        """
        this_st_picks = []
        for _i, pick in enumerate(self._picks):
            if (
                pick.waveform_id.station_code == self._current_stname
                and self._current_st[0].stats.starttime < pick.time < self._current_st[0].stats.endtime
            ):
                this_st_picks.append(_i)
        return [self._picks[i] for i in this_st_picks]

    def _getPickXPosition(self, picks):
        """
        Convert picktimes into relative positions along x-axis
        """
        xpicks = []
        for _pick in picks:
            xpicks.append((_pick.time - self._current_st[0].stats.starttime) / self._current_st[0].stats.delta)
        return np.array(xpicks)

    def _drawPicks(self, draw=True):
        """
        Draw picklines onto axes
        """
        picks = self._getPicks()
        xpicks = self._getPickXPosition(picks)

        for _ax in self.fig.get_axes():
            lines = []
            labels = []
            points = []
            transOffset = offset_copy(_ax.transData, fig=self.fig, x=5, y=0, units="points")
            for _i, _xpick in enumerate(xpicks):
                if picks[_i].phase_hint == "S":
                    color = "r"
                elif picks[_i].phase_hint == "P":
                    color = "g"
                else:
                    color = "b"
                if _ax.channel != picks[_i].waveform_id.channel_code:
                    alpha = 0.2
                else:
                    alpha = 0.8

                lines.append(
                    matplotlib.lines.Line2D(
                        [_xpick, _xpick],
                        [_ax.get_ylim()[0] * 0.9, _ax.get_ylim()[1] * 0.8],
                        color=color,
                        alpha=alpha,
                        rasterized=True,
                    )
                )
                lines[-1].obspy_pick = picks[_i]

                points.append(
                    matplotlib.lines.Line2D(
                        [_xpick],
                        [_ax.lines[0].get_ydata()[int(_xpick)]],
                        marker="o",
                        mfc=color,
                        mec=color,
                        alpha=alpha,
                        ms=5,
                    )
                )

                labels.append(
                    matplotlib.text.Text(
                        _xpick,
                        _ax.get_ylim()[0] * 0.8,
                        text=picks[_i].phase_hint,
                        color=color,
                        size=10,
                        alpha=alpha,
                        transform=transOffset,
                    )
                )

            # delete all artists
            del _ax.artists[0:]
            # add updated objects
            for li, la, po in zip(lines, labels, points):
                _ax.add_artist(li)
                _ax.add_artist(la)
                _ax.add_artist(po)

        if draw:
            self._canvasDraw()

    # Plot Controls
    def _pltOnScroll(self, event):
        """
        Scrolls/Redraws the plots along x axis
        """
        if event.inaxes is None:
            return

        if event.key == "control":
            axes = [event.inaxes]
        else:
            axes = self.fig.get_axes()

        for _ax in axes:
            left = _ax.get_xlim()[0]
            right = _ax.get_xlim()[1]
            extent = right - left
            dzoom = 0.2 * extent
            aspect_left = (event.xdata - _ax.get_xlim()[0]) / extent
            aspect_right = (_ax.get_xlim()[1] - event.xdata) / extent

            if event.button == "up":
                left += dzoom * aspect_left
                right -= dzoom * aspect_right
            elif event.button == "down":
                left -= dzoom * aspect_left
                right += dzoom * aspect_right
            else:
                return
            _ax.set_xlim([left, right])
        self._canvasDraw()

    def _pltOnDrag(self, event):
        """
        Drags/Redraws the plot upon drag
        """
        if event.inaxes is None:
            return

        if event.key == "control":
            axes = [event.inaxes]
        else:
            axes = self.fig.get_axes()

        if event.button == 2:
            if self._plt_drag is None:
                self._plt_drag = event.xdata
                return
            for _ax in axes:
                _ax.set_xlim(
                    [
                        _ax.get_xlim()[0] + (self._plt_drag - event.xdata),
                        _ax.get_xlim()[1] + (self._plt_drag - event.xdata),
                    ]
                )
        else:
            return
        self._canvasDraw()

    def _pltOnButtonRelease(self, event):
        """
        On Button Release Reset drag variable
        """
        self._plt_drag = None

    def _pltOnButtonPress(self, event):
        """
        This Function is evoked when the user picks
        """
        print event.key
        if event.key is not None:
            event.key = event.key.lower()
        if event.inaxes is None:
            return
        channel = event.inaxes.channel
        tr_amp = (
            event.inaxes.lines[0].get_ydata()[int(event.xdata) + 3]
            - event.inaxes.lines[0].get_ydata()[int(event.xdata)]
        )
        if tr_amp < 0:
            polarity = "negative"
        elif tr_amp > 0:
            polarity = "positive"
        else:
            polarity = "undecideable"

        if event.key == self._shortcuts["pick_p"] and event.button == 1:
            self._setPick(event.xdata, phase="P", channel=channel, polarity=polarity)
        elif event.key == self._shortcuts["pick_s"] and event.button == 1:
            self._setPick(event.xdata, phase="S", channel=channel, polarity=polarity)
        elif event.key == self._shortcuts["pick_custom"] and event.button == 1:
            text, ok = QtGui.QInputDialog.getItem(self, "Custom Phase", "Enter phase name:", self._getPhases())
            if ok:
                self._setPick(event.xdata, phase=text, channel=channel, polarity=polarity)
        elif event.key == self._shortcuts["pick_remove"]:
            self._delPicks(network=self._current_network, station=self._current_stname, channel=channel)
        else:
            return
        self._updateSB()
        self._drawPicks()

    def _pltNextStation(self):
        """
        Plot next station
        """
        self._streamStation(self._stationCycle.next())
        self._drawFig()

    def _pltPrevStation(self):
        """
        Plot previous station
        """
        for _i in range(len(self._stations) - 1):
            prevStation = self._stationCycle.next()
        self._streamStation(prevStation)
        self._drawFig()

    def _pltStation(self):
        """
        Plot station from DropDown Menu
        """
        _i = self.stcb.currentIndex()
        while self._stationCycle.next() != self._stations[_i]:
            pass
        self._streamStation(self._stations[_i])
        self._drawFig()

    # Filter functions
    def _appFilter(self, button=True, draw=True):
        """
        Apply bandpass filter
        """
        _i = self.fltrcb.currentIndex()
        self._streamStation(self._current_stname)
        if self.fltrbtn.isChecked() is False:
            self._current_filter = None
        else:
            self._current_st.filter(
                "bandpass",
                freqmin=self.bpfilter[_i]["freqmin"],
                freqmax=self.bpfilter[_i]["freqmax"],
                corners=self.bpfilter[_i]["corners"],
                zerophase=True,
            )
            self._current_filter = _i
        for _i, _ax in enumerate(self.fig.get_axes()):
            if len(_ax.lines) == 0:
                continue
            _ax.lines[0].set_ydata(self._current_st[_i].data)
            _ax.relim()
            _ax.autoscale_view()
        if draw is True:
            self._drawPicks(draw=False)
            self._canvasDraw()
        self._updateSB()

    def _newFilter(self):
        """
        Create new filter
        """
        newFilter = self.defFilter(self)
        if newFilter.exec_():
            self.bpfilter.append(newFilter.getValues())
            self._updateFilterCB()
            self.fltrcb.setCurrentIndex(len(self.bpfilter) - 1)
            self._appFilter()

    def _editFilter(self):
        """
        Edit existing filter
        """
        _i = self.fltrcb.currentIndex()
        this_filter = self.bpfilter[_i]
        editFilter = self.defFilter(self, this_filter)
        if editFilter.exec_():
            self.bpfilter[_i] = editFilter.getValues()
            self._updateFilterCB()
            self.fltrcb.setCurrentIndex(_i)
            self._appFilter()

    def _deleteFilter(self):
        """
        Delete filter
        """
        _i = self.fltrcb.currentIndex()
        self.fltrbtn.setChecked(False)
        self.bpfilter.pop(_i)
        self._updateFilterCB()
        self._appFilter()

    def _changeFilter(self, index):
        """
        Evoke this is filter in drop-down is changed
        """
        if index == len(self.bpfilter):
            return self._newFilter()
        else:
            return self._appFilter()

    def _updateFilterCB(self):
        """
        Update the filter QComboBox
        """
        self.fltrcb.clear()
        self.fltrcb.setCurrentIndex(-1)
        for _i, _f in enumerate(self.bpfilter):
            self.fltrcb.addItem("%s [%.2f - %.2f Hz]" % (_f["name"], _f["freqmin"], _f["freqmax"]))
        self.fltrcb.addItem("Create new Filter...")

    # Status bar functions
    def _createStatusBar(self):
        """
        Creates the status bar
        """
        sb = QtGui.QStatusBar()
        sb.setFixedHeight(18)
        self.setStatusBar(sb)
        self.statusBar().showMessage("Ready")

    def _updateSB(self, statustext=None):
        """
        Updates the statusbar text
        """
        if statustext is None:
            self.stcb.setCurrentIndex(self._stations.index(self._current_stname))
            msg = "Station %i/%i - %i Picks" % (
                self._stations.index(self._current_stname) + 1,
                len(self._stations),
                len(self._getPicks()),
            )
            if self._current_filter is not None:
                msg += " - Bandpass %s [%.2f - %.2f Hz]" % (
                    self.bpfilter[self._current_filter]["name"],
                    self.bpfilter[self._current_filter]["freqmin"],
                    self.bpfilter[self._current_filter]["freqmax"],
                )
            else:
                msg += " - Raw Data"
            self.statusBar().showMessage(msg)

    def _openCatalogDlg(self):
        filename = QtGui.QFileDialog.getOpenFileName(
            self, "Load QuakeML Picks", os.getcwd(), "QuakeML Format (*.xml)", "20"
        )
        if filename:
            self._openCatalog(str(filename))
            self.savefile = str(filename)

    def _openCatalog(self, filename):
        """
        Open existing QuakeML catalog
        """
        try:
            print "Opening QuakeML Catalog %s" % filename
            cat = event.readEvents(filename)
            self._picks = cat[0].picks
            self._drawPicks()
        except:
            msg = "Could not open QuakeML file %s" % (filename)
            raise IOError(msg)

    def _saveCatalogDlg(self):
        """
        Save catalog through QtDialog
        """
        self.savefile = QtGui.QFileDialog.getSaveFileName(
            self, "Save QuakeML Picks", os.getcwd(), "QuakeML Format (*.xml)"
        )
        if not self.savefile:
            self.savefile = None
            return
        self.savefile = str(self.savefile)
        if os.path.splitext(self.savefile)[1].lower() != ".xml":
            self.savefile += ".xml"
        self._saveCatalog()

    def _saveCatalog(self, filename=None):
        """
        Saves the catalog to filename
        """
        if self.savefile is None and filename is None:
            return self._saveCatalogDlg()
        if filename is not None:
            savefile = filename
        else:
            savefile = self.savefile
        cat = event.Catalog()
        cat.events.append(event.Event(picks=self._picks))
        cat.write(savefile, format="QUAKEML")
        print "Picks saved as %s" % savefile

    def _savePlotDlg(self):
        """
        Save Plot Image Qt Dialog and Matplotlib wrapper
        """
        filename = QtGui.QFileDialog.getSaveFileName(
            self, "Save Plot", os.getcwd(), "Image Format (*.png *.pdf *.ps *.svg *.eps)"
        )
        if not filename:
            return
        filename = str(filename)
        format = os.path.splitext(filename)[1][1:].lower()
        if format not in ["png", "pdf", "ps", "svg", "eps"]:
            format = "png"
            filename += "." + format
        self.fig.savefig(filename=filename, format=format, dpi=72)

    def getPicks(self):
        return self._picks

    def _opnWadatiPlot(self):
        self._wadatiPlt = QtGui.NewWindow()
        self._wadatiPlt.show()

    def _infoDlg(self):
        msg = """
                <h3><b>obspy.core.stream-Picker</b></h3>
                <br><br>
                <div>
                StreamPick is a lightweight seismological
                wave time picker for <code>obspy.core.Stream()</code>
                objects. It further utilises the <code>obspy.core.event</code>
                class to store picks in the QuakeML format.
                </div>
                <h4>Controls:</h4>
                <blockquote>
                <table>
                    <tr>
                        <td width=20><b>%s</b></td><td>Next station</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td><td>Previous station</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td><td>Toggle filter</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Set P-Phase pick at mouse position</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Set S-Phase pick at mouse position</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Set custom phase pick at mouse position</td>
                    </tr>
                    <tr>
                        <td width=20><b>%s</b></td>
                        <td>Remove last pick in trace</td>
                    </tr>
                </table>
                </blockquote>
                <h4>Plot Controls:</h4>
                <blockquote>
                Use mouse wheel to zoom in- and out. Middle mouse button moves
                plot along x-axis.<br>
                Hit <b>Ctrl</b> to manipulate a single plot.
                <br>
                </blockquote>
                <div>
                Programm stores filter parameters in <code>.pick_filter</code>
                and a backup of recent picks in
                <code>.picks-obspy.xml.bak</code>.<br><br>
                See <a href=http://www.github.org/miili/StreamPick>
                http://www.github.org/miili/StreamPick</a> and
                <a href=http://www.obspy.org>http://www.obspy.org</a>
                for further documentation.
                </div>
                """ % (
            self._shortcuts["st_next"],
            self._shortcuts["st_previous"],
            self._shortcuts["filter_apply"],
            self._shortcuts["pick_p"],
            self._shortcuts["pick_s"],
            self._shortcuts["pick_custom"],
            self._shortcuts["pick_remove"],
        )
        QtGui.QMessageBox.about(self, "About", msg)

    def _canvasDraw(self):
        """
        Redraws the canvas and re-sets mouse focus
        """
        for _i, _ax in enumerate(self.fig.get_axes()):
            _ax.set_xticklabels(_ax.get_xticks() * self._current_st[_i].stats.delta)
        self.canvas.draw_idle()
        self.canvas.flush_events()
        self.canvas.setFocus()
        return

    def closeEvent(self, evnt):
        """
        This function is called upon closing the QtGui
        """
        # Save Picks
        pickle.dump(self.bpfilter, open(".pick_filters", "w"))
        # Save Catalog
        if len(self._picks) > 0:
            self._saveCatalog(".picks-obspy.xml.bak")
        if self.savefile is None and len(self._picks) > 0:
            ask = QtGui.QMessageBox.question(
                self,
                "Save Picks?",
                "Do you want to save your picks?",
                QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | QtGui.QMessageBox.Cancel,
                QtGui.QMessageBox.Save,
            )
            if ask == QtGui.QMessageBox.Save:
                self._saveCatalog()
            elif ask == QtGui.QMessageBox.Cancel:
                evnt.ignore()
        print self._picks

    # Filter Dialog
    class defFilter(QtGui.QDialog):
        def __init__(self, parent=None, filtervalues=None):
            """
            Bandpass filter dialog... Qt layout and stuff
            """
            QtGui.QDialog.__init__(self, parent)
            self.setWindowTitle("Create new Bandpass-Filter")

            # Frequency QDoubleSpinBoxes
            self.frqmin = QtGui.QDoubleSpinBox(decimals=2, maximum=100, minimum=0.01, singleStep=0.1, value=0.1)
            self.frqmax = QtGui.QDoubleSpinBox(decimals=2, maximum=100, minimum=0.01, singleStep=0.1, value=10.0)

            # Radio buttons for corners
            _corners = [2, 4, 8]
            _radbtn = []
            for _c in _corners:
                _radbtn.append(QtGui.QRadioButton(str(_c)))
                if _c == 4:
                    _radbtn[-1].setChecked(True)

            self.corner = QtGui.QButtonGroup()
            self.corner.setExclusive(True)

            radiogrp = QtGui.QHBoxLayout()
            for _i, _r in enumerate(_radbtn):
                self.corner.addButton(_r, _corners[_i])
                radiogrp.addWidget(_radbtn[_i])

            # Filter name
            self.fltname = QtGui.QLineEdit("Filter Name")
            self.fltname.selectAll()

            # Make Layout
            grid = QtGui.QGridLayout()
            grid.addWidget(QtGui.QLabel("Filter Name"), 0, 0)
            grid.addWidget(self.fltname, 0, 1)
            grid.addWidget(QtGui.QLabel("Min. Frequency"), 1, 0)
            grid.addWidget(self.frqmin, 1, 1)
            grid.addWidget(QtGui.QLabel("Max. Frequency"), 2, 0)
            grid.addWidget(self.frqmax, 2, 1)
            grid.addWidget(QtGui.QLabel("Corners"), 3, 0)
            grid.addLayout(radiogrp, 3, 1)
            grid.setVerticalSpacing(10)

            btnbox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
            btnbox.accepted.connect(self.accept)
            btnbox.rejected.connect(self.reject)

            layout = QtGui.QVBoxLayout()
            layout.addWidget(
                QtGui.QLabel(
                    "Define a minimum and maximum"
                    + " frequency\nfor the bandpass filter.\nFunction utilises "
                    + "obspy.signal.filter (zerophase=True).\n"
                )
            )
            layout.addLayout(grid)
            layout.addWidget(btnbox)

            if filtervalues is not None:
                self.fltname.setText(filtervalues["name"])
                self.frqmin.setValue(filtervalues["freqmin"])
                self.frqmax.setValue(filtervalues["freqmax"])
                self.corner.button(filtervalues["corners"]).setChecked(True)

            self.setLayout(layout)
            self.setSizeGripEnabled(False)

        def getValues(self):
            """
            Return filter dialogs values as a dictionary
            """
            return dict(
                name=str(self.fltname.text()),
                freqmin=float(self.frqmin.cleanText()),
                freqmax=float(self.frqmax.cleanText()),
                corners=int(int(self.corner.checkedId())),
            )