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
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
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)
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
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
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)
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())))
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)
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)
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)
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()
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()
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
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)
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:
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()
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)
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))
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]
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()
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()
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)
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)
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()
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
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))
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()
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)
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()
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
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)
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
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)
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
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)
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())))
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()
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
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)
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)
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()
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_()
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
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.')
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)
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())), )