def plot_LO_horiz_stripes(): ''' This uses data that has been processed through pik1 but w/ the hanning filter disabled s.t. the stripes are more readily apparent. ''' fig = Figure((10, 4)) canvas = FigureCanvas(fig) ax = fig.add_axes([0, 0, 1, 1]) ax.axis('off') plot_radar(ax, 'TOT_stacked_nofilter', 3200, None, [135000, 230000]) xlim = ax.get_xlim() ylim = ax.get_ylim() TOT_bounds, VCD_bounds, THW_bounds = find_quiet_regions() ax.vlines(TOT_bounds[0:2], 0, 3200, colors='red', linewidth=3, linestyles='dashed') plot_bounding_box(ax, TOT_bounds, '', linewidth=4) ax.set_xlim(xlim) ax.set_ylim(ylim) canvas.print_figure('../FinalReport/figures/TOT_LO_stripes_d.jpg') zoom_bounds = [5000, 9000, 3000, 3200] zoom_fig = Figure((2.5, 9)) zoom_canvas = FigureCanvas(zoom_fig) zoom_ax = zoom_fig.add_axes([0, 0, 1, 1]) zoom_ax.axis('off') plot_radar(zoom_ax, 'TOT_stacked_nofilter', 3200, zoom_bounds, [135000, 230000]) zoom_canvas.print_figure('../FinalReport/figures/TOT_LO_stripes_zoom.jpg')
def plot_LO_horiz_stripes(): ''' This uses data that has been processed through pik1 but w/ the hanning filter disabled s.t. the stripes are more readily apparent. ''' fig = Figure((10, 4)) canvas = FigureCanvas(fig) ax = fig.add_axes([0, 0, 1, 1]) ax.axis('off') plot_radar(ax, 'TOT_stacked_nofilter', 3200, None, [135000, 230000]) xlim = ax.get_xlim() ylim = ax.get_ylim() TOT_bounds, VCD_bounds, THW_bounds = find_quiet_regions() ax.vlines(TOT_bounds[0:2], 0, 3200, colors='red', linewidth=3, linestyles='dashed') plot_bounding_box(ax, TOT_bounds, '', linewidth=4) ax.set_xlim(xlim) ax.set_ylim(ylim) canvas.print_figure('../FinalReport/figures/TOT_LO_stripes_d.jpg') zoom_bounds = [5000, 9000, 3000, 3200] zoom_fig = Figure((2.5, 9)) zoom_canvas = FigureCanvas(zoom_fig) zoom_ax = zoom_fig.add_axes([0, 0, 1, 1]) zoom_ax.axis('off') plot_radar(zoom_ax, 'TOT_stacked_nofilter', 3200, zoom_bounds, [135000, 230000]) zoom_canvas.print_figure('../FinalReport/figures/TOT_LO_stripes_zoom.jpg')
class GEMPlotterDialog(QtGui.QDialog): def __init__(self): QtGui.QDialog.__init__(self) # Set up the user interface from Designer. self.ui = Ui_GEMPlotter() self.ui.setupUi(self) self.modelfile = None self.ff = {} # fragility functions dictionary self.fig = Figure() self.axes = self.fig.add_subplot(111) self.axes.grid(True) self.canvas = FigureCanvasQTAgg(self.fig) self.canvas.setParent(self) self.ui.plotLayout.addWidget(self.canvas) @QtCore.pyqtSlot() def on_cancelButton_clicked(self): self.close() @QtCore.pyqtSlot() def on_saveButton_clicked(self): choices = 'PNG (*.png)|*.png' path = unicode(QtGui.QFileDialog.getSaveFileName( self, 'Save plot', '', choices)) self.canvas.print_figure(path) @QtCore.pyqtSlot(int) def plot_ff(self, taxonomy_idx): if taxonomy_idx <= 0: return self.axes.clear() taxonomy = str(self.ui.taxonomyCombo.itemText(taxonomy_idx)) iml, ys = self.ff[taxonomy] for state, y in zip(self.states, ys): self.axes.plot(iml['imls'], y, label=state) self.axes.legend(loc='upper left') self.canvas.draw() self.ui.saveButton.setEnabled(True) @QtCore.pyqtSlot() def on_chooseButton_clicked(self): self.modelfile = unicode(QtGui.QFileDialog.getOpenFileName( self, 'Select Fragility Model file', QtCore.QDir.homePath(), 'Model files (*.xml)')) # TODO: what to do if modelfile is empty? # what to do if the file is incorrect? self._fillCombo() self.ui.taxonomyCombo.currentIndexChanged.connect(self.plot_ff) def _fillCombo(self): p = iter(FragilityModelParser(self.modelfile)) kind, self.states = next(p) self.ff = dict((taxonomy, (iml, y)) for taxonomy, iml, y, no_damage_limit in p) self.ui.taxonomyCombo.clear() self.ui.taxonomyCombo.addItems(['Taxonomy'] + self.ff.keys()) self.ui.taxonomyCombo.setEnabled(True)
def write_eps(fig, outfile, dpi=72): """ Write a eps of the given figure, indendent of the pyplot backend. However, if the figure was created from pyplot, an existing pyplot backend will be permanently changed and may be dysfunctional. """ from matplotlib.backends.backend_ps import FigureCanvasPS as FigureCanvas canvas = FigureCanvas(fig) canvas.print_figure(outfile, dpi=dpi)
def plot_all_utig_data(): ''' Plots all UTIG transects on a simple basemap, with transects color-coded by season. ''' fig = Figure((24, 20)) canvas = FigureCanvas(fig) ax = fig.add_axes([0, 0, 1, 1]) bgs = deva.basemapUtilities.make_background_dict(fig, ax) bg = bgs['modis_simple'] bg.set_background() gls = deva.devaUtilities.make_grounding_line_dict() deva.devaUtilities.set_grounding_line(ax, gls, 'modis') ax.axis('equal') ax.set_xlim([-3000000, 3000000]) ax.set_ylim([-2500000, 2500000]) ax.tick_params(which='both', bottom=False, top=False, left=False, right=False) for side in ['bottom', 'top', 'left', 'right']: ax.spines[side].set_visible(False) transects = deva.devaUtilities.load_transects(antarctic=True) season_lookup = deva.utilities.SeasonLookup() for pst, data in transects.iteritems(): season, _ = season_lookup.get_season(pst) if season is None: print "No season found for %s" % (pst) continue elif season in [ 'ASE1', '2001', 'ICP1', 'ICP2', 'ICP3', 'ICP4', 'ICP5' ]: color = 'k' zorder = 3 elif season in ['ICP6']: color = 'darkgrey' zorder = 2 else: color = 'lightgrey' zorder = 1 ax.plot(data[:, 1], data[:, 2], color=color, linewidth=1.0, zorder=zorder) canvas.print_figure('../FinalReport/figures/all_data.png')
def plot_data_products(): # Define all bounds first s.t. we can draw the context boxes on the # dechirped image. # The raw bounds are for the full image raw_min_sample = 150 raw_max_sample = 2300 raw_bounds = [0, 9075, raw_min_sample, raw_max_sample] # The filtering needs to zoom in on the surface filter_bounds = [1700, 5900, 425, 650] # For the SNR improvements of incoherent stacking, look at the layers that just pop out. layer_bounds = [7400, 8580, 200, 1750] # For filtered, clim = [110000, 195000] and for stacked, clim=[140000, 225000] # Zooming in on the crevasses shows speckle nicely incoherent_bounds = [2880, 4900, 850, 1700] #The clim for this is best is the coherent is [150000, 234000] and incoherent is [160000, 234000] # Appropriate color limits depend on the processing used raw_clim = [25000, 90000] dechirped_clim = [115000, 200000] filtered_clim = [115000, 200000] # First, generate the raw figure that requires raw data + dechirped data # over the full transect. raw_shape = (50, 17) #raw_shape = (10, 17./5) # This is ugly ... # fig_raw = Figure(raw_shape, dpi=150) # canvas_raw = FigureCanvas(fig_raw) # ax_raw = fig_raw.add_axes([0, 0, 1, 1]) # ax_raw.axis('off') # plot_radar(ax_raw, 'TOT_raw', 3200, raw_bounds, raw_clim) # canvas_raw.print_figure('../FinalReport/figures/TOT_raw_full.jpg') multiple_bounds = [3050, 5325, 325, 2675] fig_multiples = Figure(raw_shape, dpi=150) canvas_multiples = FigureCanvas(fig_multiples) ax_multiples = fig_multiples.add_axes([0, 0, 1, 1]) ax_multiples.axis('off') plot_radar(ax_multiples, 'TOT_no_blanking', 3200, multiple_bounds, clim=[140000, 230000]) ax_multiples.text(3950, 900, 'surface multiple', color='red', fontsize=70, horizontalalignment='left', verticalalignment='bottom') ax_multiples.text(3950, 2380, 'basal multiple', color='red', fontsize=70, horizontalalignment='left', verticalalignment='top') canvas_multiples.print_figure('../FinalReport/figures/TOT_multiples.jpg')
def plot_before_after(): shape = (36, 27) fig_old = Figure(shape, dpi=150) canvas_old = FigureCanvas(fig_old) ax_old = fig_old.add_axes([0, 0, 1, 1]) ax_old.axis('off') old_filename = WAIS + '/targ/xtra/ICP3/CMP/pik1.RADnh3/TOT/JKB2d/X16a/MagLoResInco2' plot_radar(ax_old, old_filename, 3200, clim=[103000, 200000]) canvas_old.print_figure('../FinalReport/figures/TOT_X16a_old.jpg') fig_new = Figure(shape, dpi=150) canvas_new = FigureCanvas(fig_new) ax_new = fig_new.add_axes([0, 0, 1, 1]) ax_new.axis('off') plot_radar(ax_new, 'TOT_LO', 3200, clim=[136000, 233000]) canvas_new.print_figure('../FinalReport/figures/TOT_X16a_new.jpg')
def plot_before_after(): shape = (36, 27) fig_old = Figure(shape, dpi=150) canvas_old = FigureCanvas(fig_old) ax_old = fig_old.add_axes([0, 0, 1, 1]) ax_old.axis('off') old_filename = WAIS + '/targ/xtra/ICP3/CMP/pik1.RADnh3/TOT/JKB2d/X16a/MagLoResInco2' plot_radar(ax_old, old_filename, 3200, clim=[103000, 200000]) canvas_old.print_figure('../FinalReport/figures/TOT_X16a_old.jpg') fig_new = Figure(shape, dpi=150) canvas_new = FigureCanvas(fig_new) ax_new = fig_new.add_axes([0, 0, 1, 1]) ax_new.axis('off') plot_radar(ax_new, 'TOT_LO', 3200, clim=[136000, 233000]) canvas_new.print_figure('../FinalReport/figures/TOT_X16a_new.jpg')
def plot_DC(): shape = (36, 18) TOT_bounds = [6000, 9075, 200, 800] fig_TOT_ch1 = Figure(shape, dpi=150) canvas_TOT_ch1 = FigureCanvas(fig_TOT_ch1) ax_TOT_ch1 = fig_TOT_ch1.add_axes([0, 0, 1, 1]) ax_TOT_ch1.axis('off') old_filename = WAIS + '/targ/xtra/ICP3/CMP/pik1.RADnh3/TOT/JKB2d/X16a/MagLoResInco1' plot_radar(ax_TOT_ch1, old_filename, 3200, bounds=TOT_bounds, clim=[80000, 180000]) canvas_TOT_ch1.print_figure('../FinalReport/figures/TOT_ch1_DC.jpg') fig_TOT_ch2 = Figure(shape, dpi=150) canvas_TOT_ch2 = FigureCanvas(fig_TOT_ch2) ax_TOT_ch2 = fig_TOT_ch2.add_axes([0, 0, 1, 1]) ax_TOT_ch2.axis('off') plot_radar(ax_TOT_ch2, 'TOT_no_blanking', 3200, bounds=TOT_bounds, clim=[130000, 230000]) canvas_TOT_ch2.print_figure('../FinalReport/figures/TOT_ch2_DC.jpg') VCD_bounds = [9600, 10470, 200, 800] fig_VCD_ch1 = Figure(shape, dpi=150) canvas_VCD_ch1 = FigureCanvas(fig_VCD_ch1) ax_VCD_ch1 = fig_VCD_ch1.add_axes([0, 0, 1, 1]) ax_VCD_ch1.axis('off') old_filename = WAIS + '/targ/xtra/ICP4/CMP/pik1.RADnh3/VCD/JKB2g/DVD01a/MagLoResInco1' plot_radar(ax_VCD_ch1, old_filename, 3200, bounds=VCD_bounds, clim=[80000, 180000]) canvas_VCD_ch1.print_figure('../FinalReport/figures/VCD_ch1_DC.jpg') fig_VCD_ch2 = Figure(shape, dpi=150) canvas_VCD_ch2 = FigureCanvas(fig_VCD_ch2) ax_VCD_ch2 = fig_VCD_ch2.add_axes([0, 0, 1, 1]) ax_VCD_ch2.axis('off') plot_radar(ax_VCD_ch2, 'VCD_no_blanking', 3200, bounds=VCD_bounds, clim=[140000, 230000]) canvas_VCD_ch2.print_figure('../FinalReport/figures/VCD_ch2_DC.jpg')
def plot_all_utig_data(): ''' Plots all UTIG transects on a simple basemap, with transects color-coded by season. ''' fig = Figure((24, 20)) canvas = FigureCanvas(fig) ax = fig.add_axes([0,0,1,1]) bgs = deva.basemapUtilities.make_background_dict(fig, ax) bg = bgs['modis_simple'] bg.set_background() gls = deva.devaUtilities.make_grounding_line_dict() deva.devaUtilities.set_grounding_line(ax, gls, 'modis') ax.axis('equal') ax.set_xlim([-3000000, 3000000]) ax.set_ylim([-2500000, 2500000]) ax.tick_params(which='both', bottom=False, top=False, left=False, right=False) for side in ['bottom', 'top', 'left', 'right']: ax.spines[side].set_visible(False) transects = deva.devaUtilities.load_transects(antarctic=True) season_lookup = deva.utilities.SeasonLookup() for pst, data in transects.iteritems(): season,_ = season_lookup.get_season(pst) if season is None: print "No season found for %s" % (pst) continue elif season in ['ASE1', '2001', 'ICP1', 'ICP2', 'ICP3', 'ICP4', 'ICP5']: color = 'k' zorder = 3 elif season in ['ICP6']: color = 'darkgrey' zorder = 2 else: color = 'lightgrey' zorder = 1 ax.plot(data[:,1], data[:,2], color=color, linewidth=1.0, zorder=zorder) canvas.print_figure('../FinalReport/figures/all_data.png')
def plot_quiet_regions(): # Plot the region that the noise was calculated from... # For TOT... TOT_bounds, VCD_bounds, THW_bounds = find_quiet_regions() # TOT/JKB2d/X16a gives: # mag = 32809.224658469, phs = -0.90421798501485484 # VCD/JKB2g/DVD01a gives: # mag = 15720.217174332585, phs = -0.98350090576267946 # THW/SJB2/DRP02a gives: # 26158.900202734963, phs = 1.6808311318828895 TOT_fig = Figure((10, 8)) TOT_canvas = FigureCanvas(TOT_fig) TOT_ax = TOT_fig.add_axes([0, 0, 1, 1]) TOT_ax.axis('off') plot_radar(TOT_ax, 'TOT_LO', 3200, None, [135000, 234000]) xlim = TOT_ax.get_xlim() ylim = TOT_ax.get_ylim() TOT_ax.vlines(TOT_bounds[0:2], 0, 3200, colors='red', linewidth=3, linestyles='dashed') plot_bounding_box(TOT_ax, TOT_bounds, '', linewidth=4) TOT_ax.set_xlim(xlim) TOT_ax.set_ylim(ylim) TOT_canvas.print_figure('../FinalReport/figures/TOT_quiet_region.jpg') VCD_fig = Figure((10, 8)) VCD_canvas = FigureCanvas(VCD_fig) VCD_ax = VCD_fig.add_axes([0, 0, 1, 1]) VCD_ax.axis('off') plot_radar(VCD_ax, 'VCD_LO', 3200, None, [135000, 234000]) xlim = VCD_ax.get_xlim() ylim = VCD_ax.get_ylim() VCD_ax.vlines(VCD_bounds[0:2], 0, 3200, colors='red', linewidth=3, linestyles='dashed') plot_bounding_box(VCD_ax, VCD_bounds, '', linewidth=4) VCD_ax.set_xlim(xlim) VCD_ax.set_ylim(ylim) VCD_canvas.print_figure('../FinalReport/figures/VCD_quiet_region.jpg') THW_fig = Figure((10, 8)) THW_canvas = FigureCanvas(THW_fig) THW_ax = THW_fig.add_axes([0, 0, 1, 1]) THW_ax.axis('off') plot_radar(THW_ax, 'THW_LO', 3200, None, [135000, 234000]) xlim = THW_ax.get_xlim() ylim = THW_ax.get_ylim() THW_ax.vlines(THW_bounds[0:2], 0, 3200, colors='red', linewidth=3, linestyles='dashed') plot_bounding_box(THW_ax, THW_bounds, '', linewidth=4) THW_ax.set_xlim(xlim) THW_ax.set_ylim(ylim) THW_canvas.print_figure('../FinalReport/figures/THW_quiet_region.jpg')
def plot_DC(): shape = (36, 18) TOT_bounds = [6000, 9075, 200, 800] fig_TOT_ch1 = Figure(shape, dpi=150) canvas_TOT_ch1 = FigureCanvas(fig_TOT_ch1) ax_TOT_ch1 = fig_TOT_ch1.add_axes([0, 0, 1, 1]) ax_TOT_ch1.axis('off') old_filename = WAIS + '/targ/xtra/ICP3/CMP/pik1.RADnh3/TOT/JKB2d/X16a/MagLoResInco1' plot_radar(ax_TOT_ch1, old_filename, 3200, bounds=TOT_bounds, clim=[80000, 180000]) canvas_TOT_ch1.print_figure('../FinalReport/figures/TOT_ch1_DC.jpg') fig_TOT_ch2 = Figure(shape, dpi=150) canvas_TOT_ch2 = FigureCanvas(fig_TOT_ch2) ax_TOT_ch2 = fig_TOT_ch2.add_axes([0, 0, 1, 1]) ax_TOT_ch2.axis('off') plot_radar(ax_TOT_ch2, 'TOT_no_blanking', 3200, bounds=TOT_bounds, clim=[130000, 230000]) canvas_TOT_ch2.print_figure('../FinalReport/figures/TOT_ch2_DC.jpg') VCD_bounds = [9600, 10470, 200, 800] fig_VCD_ch1 = Figure(shape, dpi=150) canvas_VCD_ch1 = FigureCanvas(fig_VCD_ch1) ax_VCD_ch1 = fig_VCD_ch1.add_axes([0, 0, 1, 1]) ax_VCD_ch1.axis('off') old_filename = WAIS + '/targ/xtra/ICP4/CMP/pik1.RADnh3/VCD/JKB2g/DVD01a/MagLoResInco1' plot_radar(ax_VCD_ch1, old_filename, 3200, bounds=VCD_bounds, clim=[80000, 180000]) canvas_VCD_ch1.print_figure('../FinalReport/figures/VCD_ch1_DC.jpg') fig_VCD_ch2 = Figure(shape, dpi=150) canvas_VCD_ch2 = FigureCanvas(fig_VCD_ch2) ax_VCD_ch2 = fig_VCD_ch2.add_axes([0, 0, 1, 1]) ax_VCD_ch2.axis('off') plot_radar(ax_VCD_ch2, 'VCD_no_blanking', 3200, bounds=VCD_bounds, clim=[140000, 230000]) canvas_VCD_ch2.print_figure('../FinalReport/figures/VCD_ch2_DC.jpg')
class PlotDisplay(Component): ''' Class to create a display plot, using data and a key for plot type. ''' def __init__(self, data, ydata=None, plot_type=None, title=None, xlabel=None, ylabel=None, name="PlotDisplay", parent=None): ''' Initialize the class to create display. Parameters ---------- data : data array The data to be plotted. [Optional] plot_type : str Type of plot to produce (e.g. plot, barplot, etc). name : string Display window name. parent : PyQt instance Parent instance to associate to Display window. If None, then Qt owns, otherwise associated with parent PyQt instance. Notes ----- This class records the selected button and passes the change value back to variable. ''' super(PlotDisplay, self).__init__(name=name, parent=parent) self.setFocusPolicy(QtCore.Qt.ClickFocus) self.data = data self.ydata = ydata self.plot_type = plot_type # Set plot title and colorbar units to defaults self.title = title self.xlabel = xlabel self.ylabel = ylabel self.units = None self.limits = None # Find the PyArt colormap names self.cm_names = ["pyart_" + m for m in pyart.graph.cm.datad if not m.endswith("_r")] self.cm_names.sort() # Create a figure for output self._set_fig_ax() # Launch the GUI interface self.LaunchGUI() # Create the plot self._update_plot() ## self.NewRadar(None, None, True) self.show() #################### # GUI methods # #################### def LaunchGUI(self): '''Launches a GUI interface.''' # Create layout self.layout = QtGui.QGridLayout() self.layout.setSpacing(4) # Create the widget self.central_widget = QtGui.QWidget() self.setCentralWidget(self.central_widget) self._set_figure_canvas() self.central_widget.setLayout(self.layout) # Add buttons along display for user control self.addButtons() self.setUILayout() # Set the status bar to display messages self.statusbar = self.statusBar() ################################## # User display interface methods # ################################## def addButtons(self): '''Add a series of buttons for user control over display.''' # Create the Display controls self._add_displayBoxUI() def setUILayout(self): '''Setup the button/display UI layout.''' self.layout.addWidget(self.dispButton, 0, 1) ############################# # Functionality methods # ############################# def _open_LimsDialog(self): '''Open a dialog box to change display limits.''' from .limits import limits_dialog limits, cmap, change = limits_dialog(self.limits, self.cmap, self.name) if change == 1: self.cmap = cmap self.limits = limits self._update_plot() def _title_input(self): '''Retrieve new plot title.''' val, entry = common.string_dialog(self.title, "Plot Title", "Title:") if entry is True: self.title = val self._update_plot() def _units_input(self): '''Retrieve new plot units.''' val, entry = common.string_dialog(self.units, "Plot Units", "Units:") if entry is True: self.units = val self._update_plot() def _add_cmaps_to_button(self): '''Add a menu to change colormap used for plot.''' for cm_name in self.cm_names: cmapAction = self.dispCmapmenu.addAction(cm_name) cmapAction.setStatusTip("Use the %s colormap" % cm_name) cmapAction.triggered[()].connect( lambda cm_name=cm_name: self.cmapSelectCmd(cm_name)) self.dispCmap.setMenu(self.dispCmapmenu) def _add_displayBoxUI(self): '''Create the Display Options Button menu.''' self.dispButton = QtGui.QPushButton("Display Options") self.dispButton.setToolTip("Adjust display properties") self.dispButton.setFocusPolicy(QtCore.Qt.NoFocus) dispmenu = QtGui.QMenu(self) dispLimits = dispmenu.addAction("Adjust Display Limits") dispLimits.setToolTip("Set data, X, and Y range limits") dispTitle = dispmenu.addAction("Change Title") dispTitle.setToolTip("Change plot title") dispUnit = dispmenu.addAction("Change Units") dispUnit.setToolTip("Change units string") # toolZoomPan = dispmenu.addAction("Zoom/Pan") self.dispCmap = dispmenu.addAction("Change Colormap") self.dispCmapmenu = QtGui.QMenu("Change Cmap") self.dispCmapmenu.setFocusPolicy(QtCore.Qt.NoFocus) dispSaveFile = dispmenu.addAction("Save Image") dispSaveFile.setShortcut("Ctrl+S") dispSaveFile.setStatusTip("Save Image using dialog") self.dispHelp = dispmenu.addAction("Help") dispLimits.triggered[()].connect(self._open_LimsDialog) dispTitle.triggered[()].connect(self._title_input) dispUnit.triggered[()].connect(self._units_input) # toolZoomPan.triggered[()].connect(self.toolZoomPanCmd) dispSaveFile.triggered[()].connect(self._savefile) self.dispHelp.triggered[()].connect(self.displayHelp) self._add_cmaps_to_button() self.dispButton.setMenu(dispmenu) def displayHelp(self): text = "<b>Using the Simple Plot Feature</b><br><br>" text += "<i>Purpose</i>:<br>" text += "Display a plot.<br><br>" text += "The limits dialog is a common format that allows the user change:<br>" text += "<i>X and Y limits<br>" text += "Data limits</i><br>" text += "However, not all plots take each argument.<br>" text += "For example, a simple line plot has no data min/max data value.<br>" common.ShowLongText(text) ######################## # Selectionion methods # ######################## # def _create_plot(self): # ''' # Create a plot # ''' # # test for None # if self.Vradar.value is None: # self.fieldBox.clear() # self.tiltBox.clear() # return # # # Get the tilt angles # self.rTilts = self.Vradar.value.sweep_number['data'][:] # # Get field names # self.fieldnames = self.Vradar.value.fields.keys() # # # Check the file type and initialize limts # self._check_file_type() # # # Update field and tilt MenuBox # self._fillTiltBox() # self._fillFieldBox() # # self.units = None # if strong: # self._update_plot() # def NewLims(self, variable, value, strong): # ''' # Slot for 'ValueChanged' signal of # :py:class:`Vlims <artview.core.core.Variable>`. # # This will: # # * If strong update: update axes # ''' # if strong: # self._update_axes() # def NewCmap(self, variable, value, strong): # ''' # Slot for 'ValueChanged' signal of # :py:class:`Vcmap <artview.core.core.Variable>`. # # This will: # # * If strong update: update plot # ''' # if strong and self.Vradar.value is not None: # self._update_plot() def cmapSelectCmd(self, cm_name): '''Captures colormap selection and redraws.''' self.cmap['cmap'] = cm_name # self.Vcmap.value['cmap'] = cm_name # self.Vcmap.change(self.Vcmap.value) # def toolZoomPanCmd(self): # '''Creates and connects to a Zoom/Pan instance.''' # from .tools import ZoomPan # scale = 1.1 # self.tools['zoompan'] = ZoomPan( # self.limits, self.ax, # base_scale=scale, parent=self.parent) # self.tools['zoompan'].connect() #################### # Plotting methods # #################### def _set_fig_ax(self): '''Set the figure and axis to plot.''' self.XSIZE = 5 self.YSIZE = 5 self.fig = Figure(figsize=(self.XSIZE, self.YSIZE)) self.ax = self.fig.add_axes([0.2, 0.2, 0.7, 0.7]) # def _update_fig_ax(self): # '''Set the figure and axis to plot.''' # if self.plot_type in ("radarAirborne", "radarRhi"): # self.YSIZE = 5 # else: # self.YSIZE = 8 # xwidth = 0.7 # yheight = 0.7 * float(self.YSIZE) / float(self.XSIZE) # self.ax.set_position([0.2, 0.55-0.5*yheight, xwidth, yheight]) # self.cax.set_position([0.2, 0.10, xwidth, 0.02]) # self._update_axes() def _set_figure_canvas(self): '''Set the figure canvas to draw in window area.''' self.canvas = FigureCanvasQTAgg(self.fig) # Add the widget to the canvas self.layout.addWidget(self.canvas, 1, 0, 4, 3) def _update_plot(self): '''Draw/Redraw the plot.''' # Create the plot with PyArt PlotDisplay self.ax.cla() # Clear the plot axes # Reset to default title if user entered nothing w/ Title button if self.title == '': title = None else: title = self.title colorbar_flag=False self.cmap = {'vmin': self.data.min(), 'vmax': self.data.max(), 'cmap': 'pyart_RefDiff'} if self.plot_type == "hist": self.plot = self.ax.hist(self.data, bins=25, range = (self.cmap['vmin'], self.cmap['vmax']), figure=self.fig) self.ax.set_ylabel("Counts") if self.xlabel: self.ax.set_xlabel(self.xlabel) elif self.plot_type == "hist2d": # Check that y data was provided if self.ydata: y = self.ydata # Create Plot self.plot = self.ax.hist2d(self.data, y, bins=[25, 20], range=[[self.cmap['vmin'], self.cmap['vmax']], [y.min(), y.max()]], cmap=self.cm_name, figure=self.fig) colorbar_flag=True elif self.plot_type == "plot": # Check that y data was provided if self.ydata: y = self.ydata # Create Plot self.plot = self.ax.plot(self.data, y, figure=self.fig) # Set the axis labels if arguments passed if self.xlabel: self.ax.set_xlabel(self.xlabel) if self.ylabel: self.ax.set_ylabel(self.ylabel) # If limits exists, update the axes otherwise retrieve if self.limits: self._update_axes() else: self._get_axes_limits() # Set the title if passed if title is not None: self.ax.set_title(title) # If the colorbar flag is thrown, create it if colorbar_flag: # Clear the colorbar axes self.cax.cla() self.cax = self.fig.add_axes([0.2, 0.10, 0.7, 0.02]) norm = mlabNormalize(vmin=self.cmap['vmin'], vmax=self.cmap['vmax']) self.cbar = mlabColorbarBase(self.cax, cmap=self.cm_name, norm=norm, orientation='horizontal') # colorbar - use specified units or default depending on # what has or has not been entered if self.units is None or self.units == '': self.units = '' self.cbar.set_label(self.units) self.canvas.draw() def _update_axes(self): '''Change the Plot Axes.''' self.ax.set_xlim(self.limits['xmin'], self.limits['xmax']) self.ax.set_ylim(self.limits['ymin'], self.limits['ymax']) self.ax.figure.canvas.draw() def _get_axes_limits(self): '''Get the axes limits''' xlim = self.ax.get_xlim() ylim = self.ax.get_ylim() self.limits = {} self.limits['xmin'] = xlim[0] self.limits['xmax'] = xlim[1] self.limits['ymin'] = ylim[0] self.limits['ymax'] = ylim[1] ######################## # Image save methods # ######################## def _savefile(self, PTYPE=IMAGE_EXT): '''Save the current display using PyQt dialog interface.''' file_choices = "PNG (*.png)|*.png" path = unicode(QtGui.QFileDialog.getSaveFileName( self, 'Save file', ' ', file_choices)) if path: self.canvas.print_figure(path, dpi=DPI) self.statusbar.showMessage('Saved to %s' % path)
class MainApp(QtGui.QMainWindow, mainMenu.Ui_MainWindow, orbitsMenu.Ui_orbitsMenu, imagesMenu.Ui_imagesMenu, mcMenu.Ui_Frame): def __init__(self, parent = None): QtGui.QMainWindow.__init__(self, parent) # se crea la ventana principal self.ventana = mainMenu.Ui_MainWindow() self.ventana.setupUi(self) ## imagen principal self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) ## se le agrega a la figura la accion movimiento del mouse sobre ella self.figure.canvas.mpl_connect("motion_notify_event", self.mouse_move) self.main_frame = self.ventana.verticalLayout.addWidget(self.canvas) ## barra de edicion de grafica self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) self.ventana.verticalLayout.addWidget(self.mpl_toolbar) ## imagen zoom self.figure2 = plt.figure() self.canvas2 = FigureCanvas(self.figure2) self.main_frame2 = self.ventana.verticalLayout_3.addWidget(self.canvas2) ### acciones bar menu self.connect(self.ventana.action_Quit, QtCore.SIGNAL("triggered()"), QtCore.SLOT('close()')) #self.connect(self.ventana.action_Abrir,QtCore.SIGNAL("triggered()"),self.openFile) #self.connect(self.ventana.action_guardar,QtCore.SIGNAL("triggered()"),self.guardarPlot) self.connect(self.ventana.action_About, QtCore.SIGNAL("triggered()"), self.about) self.connect(self.ventana.actionMassiveCalc, QtCore.SIGNAL("triggered()"), self.showMC) # acciones combobox self.connect(self.ventana.comboBox, QtCore.SIGNAL("currentIndexChanged(int)"), self.putMenu) #### por defecto esta seleccionado orbitsMenu ###self.showOrbitsMenu() def putMenu(self): valorCB = str(self.ventana.comboBox.currentText()) if (valorCB == "SAC-D/Aquarius"): self.showOrbitsMenu() elif (valorCB == "None"): self.removeButtons() else: self.showImagesMenu() ###----------------------------Orbits Menu--------------------------------- def showOrbitsMenu(self): """ Función que carga el menú orbitas :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ # se borran las imagenes previas, textEdit y otros self.clear() # se elimina elementos creados previos si es que existen self.removeButtons() # se crea un objeto QWidget orbitsMenuQw = QtGui.QWidget() # se crea una instancia de la clase que crea el menu self.orbitsMenu = orbitsMenu.Ui_orbitsMenu() # se llama a la funcion que inserta los elementos self.orbitsMenu.setupUi(orbitsMenuQw) # se inserta el menu en la ventana principal self.ventana.verticalLayout_2.addWidget(orbitsMenuQw) # acciones de los elementos de orbitsMenu self.orbitsMenu.pushButton.clicked.connect(self.openTargz) # si se selecciona el nivel, se cargan los productos/bandas self.orbitsMenu.comboBox.currentIndexChanged.connect(self.putProductBand) self.orbitsMenu.comboBox_2.currentIndexChanged.connect(self.putMaps) self.orbitsMenu.comboBox_3.currentIndexChanged.connect(self.putColorbars) self.orbitsMenu.comboBox_4.currentIndexChanged.connect(self.activateButtonGraph) self.orbitsMenu.pushButton_2.clicked.connect(self.graph) self.orbitsMenu.pushButton_3.clicked.connect(self.savePlot) def openTargz(self): """ Abre archivos tar.gz pertenecientes a SAC-D/Aquarius (menú orbitas) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ # se borran las imagenes previas, textEdit y otros self.clear() # se desactiva el boton guardar self.orbitsMenu.pushButton_3.setEnabled(False) # lista archivos seleccionados (self.listFiles es una QStringList) self.listFiles = QtGui.QFileDialog.getOpenFileNames(self, "Select file/s tar.gz") # print self.listFiles.isEmpty() if (self.listFiles.isEmpty() is False): #self.ventana.textEdit.setText("# Opened Folder # \n"+self.listFiles + "\n") #listFile = os.listdir(self.folder) numFiles = self.listFiles.count() #print numFiles # se valida la existencia de archivos tar.gz flag = 0 for i in range(0, numFiles): if str(self.listFiles[i]).find(".tar.gz") != -1: flag = 1 if (flag != 1): reply = QtGui.QMessageBox.critical(self, 'Message', "tar.gz files from SAC-D/Aquarius mission") self.listFiles = QtGui.QFileDialog.getOpenFileNames(self, "Select file/s tar.gz") ## se cargan los niveles self.putLevels() self.activateButtonGraph() def putLevels(self): """ Función carga los niveles de procesamiento (menú orbitas) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ self.orbitsMenu.comboBox.clear() self.orbitsMenu.comboBox.addItem("None") self.orbitsMenu.comboBox.addItem("L1B") self.orbitsMenu.comboBox.addItem("L2") def putProductBand(self): """ Carga bandas/productos según nivel de procesamiento (menú orbitas) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ # se obtiene el valor del combobox de nivel de procesamiento text = self.orbitsMenu.comboBox.currentText() # se borra el combobox de las bandas self.orbitsMenu.comboBox_2.clear() # si se selecciona None if (text == "None"): self.orbitsMenu.comboBox_2.addItem("None") # si es L1B if (text == "L1B"): # se cargan los productos de nivel L1-B self.orbitsMenu.comboBox_2.addItem("None") self.orbitsMenu.comboBox_2.addItem("ka_h_antenna_temperature") self.orbitsMenu.comboBox_2.addItem("ka_v_antenna_temperature") self.orbitsMenu.comboBox_2.addItem("ka_n45_antenna_temperature") self.orbitsMenu.comboBox_2.addItem("ka_p45_antenna_temperature") if (text == "L2"): # se cargan los productos de nivel L2 self.orbitsMenu.comboBox_2.addItem("None") self.orbitsMenu.comboBox_2.addItem("columnar_water_vapor") self.orbitsMenu.comboBox_2.addItem("wind_speed") def putMaps(self): """ Carga tipos de mapa donde se puede graficar (menú orbitas) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ # se obtiene el valor del combobox banda/producto text = self.orbitsMenu.comboBox_2.currentText() # se borra el combobox de las bandas self.orbitsMenu.comboBox_3.clear() # si se selecciono anteriormente None if (text == "None"): self.orbitsMenu.comboBox_3.addItem("None") else: self.orbitsMenu.comboBox_3.addItem("None") self.orbitsMenu.comboBox_3.addItem("robin") self.orbitsMenu.comboBox_3.addItem("mill") self.orbitsMenu.comboBox_3.addItem("kav7") def putColorbars(self): ## coloca los tipos de color bar """ Función que carga las diferentes escalas de colores (menú orbitas) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ # se obtiene el valor del combobox banda/producto text = self.orbitsMenu.comboBox_3.currentText() # se borra el combobox de las bandas self.orbitsMenu.comboBox_4.clear() # si se selecciono anteriormente None if (text == "None"): self.orbitsMenu.comboBox_4.addItem("None") else: self.orbitsMenu.comboBox_4.addItem("None") self.orbitsMenu.comboBox_4.addItem("hot") self.orbitsMenu.comboBox_4.addItem("jet") self.orbitsMenu.comboBox_4.addItem("summer") self.orbitsMenu.comboBox_4.addItem("winter") def activateButtonGraph(self): """ Función que activa el boton graficar (menú orbitas) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ # se obtiene el valor del combobox banda/producto text = self.orbitsMenu.comboBox_4.currentText() # si se selecciono None if (text == "None"): self.orbitsMenu.pushButton_2.setEnabled(False) if ((self.listFiles.isEmpty() is False) and (text != "None")): self.orbitsMenu.pushButton_2.setEnabled(True) def graph(self): """ Función que grafica los archivos tar.gz seleccionados (menú orbitas) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ self.ventana.textEdit.append("Iniciando...") # se actualiza la interfaz para mostrar las acciones en el textEdit QtGui.QApplication.processEvents() # se borran las imagenes previas self.clear1() # se obtienen los valores seleccionados en OrbitsMenu listFiles = self.listFiles level = str(self.orbitsMenu.comboBox.currentText()) nameProduct = str(self.orbitsMenu.comboBox_2.currentText()) typeMap = str(self.orbitsMenu.comboBox_3.currentText()) nameCB = str(self.orbitsMenu.comboBox_4.currentText()) #print "path: " + path #print "level: " + level #print "nameProduct: " + nameProduct #print "nameCB: " + nameCB #print "typeMap: " + typeMap # se extraen los tar.gz en un archivo temporal pathHDF = processing.extractFiles(listFiles, self.ventana.textEdit) # se actualiza la interfaz para mostrar las acciones en el textEdit QtGui.QApplication.processEvents() #### muy importante #### # como utilizo plt para graficar solo puedo tener un solo objeto figure plt.close(self.figure2) # se genera el mapa mapa = visualization.generateMap(typeMap, level, self.ventana.textEdit) # se actualiza la interfaz para mostrar las acciones en el textEdit QtGui.QApplication.processEvents() # se obtiene la imagen visualization.graphSACDProduct(plt, self.figure, pathHDF, level, nameProduct, nameCB, mapa, self.ventana.textEdit) # para hacer mas pequenios los margenes self.figure.tight_layout() # se actualiza la interfaz para mostrar las acciones en el textEdit QtGui.QApplication.processEvents() # se elimina la carpeta temporal de los archivos descomprimidos processing.eliminateTmp(pathHDF, self.ventana.textEdit) # se actualiza la interfaz para mostrar las acciones en el textEdit QtGui.QApplication.processEvents() # se grafica self.figure.subplots_adjust(left = 0.05, right = 0.95, bottom = 0.05, top = 0.95) self.canvas.draw() # se activa el boton guardar grafica self.orbitsMenu.pushButton_3.setEnabled(True) return def savePlot(self): """ Guarda grafico creado en formato png (menú orbitas) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ file_choices = "PNG (*.png)|*.png" self.dpi = 100 path = unicode(QtGui.QFileDialog.getSaveFileName(self,'Save file', '',file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) return ###-------------------------Fin Orbits Menu-------------------------------- ###----------------------------Images Menu--------------------------------- def showImagesMenu(self): """ Función que carga el menú imágenes :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ # se borran las imagenes previas, textEdit y otros self.clear() ## se elimina elementos creados previos si es que existen self.removeButtons() # se crea un objeto QWidget imagesMenuQw = QtGui.QWidget() # se crea una instancia de la clase que crea el menu self.imagesMenu = imagesMenu.Ui_imagesMenu() # se llama a la funcion que inserta los elementos self.imagesMenu.setupUi(imagesMenuQw) # se inserta el menu en la ventana principal self.ventana.verticalLayout_2.addWidget(imagesMenuQw) # acciones de los elementos de imagesMenu self.imagesMenu.pushButton.clicked.connect(self.openFile) ## si se selecciona el mapa de colores se cargan las bandas self.imagesMenu.comboBox.currentIndexChanged.connect(self.putImage) self.imagesMenu.comboBox_2.currentIndexChanged.connect(self.putImage) self.imagesMenu.radioButton.clicked.connect(self.changeLatLon) self.imagesMenu.radioButton_2.clicked.connect(self.changeRowCol) # cuando se hace click en el boton extract self.imagesMenu.pushButton_2.clicked.connect(self.extract) def openFile(self): """ Abre imágenes GeoTiff (menú imagenes) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ # se borran las imagenes previas, textEdit y otros self.clear() # se desactiva el boton guardar #self.orbitsMenu.pushButton_3.setEnabled(False) #self.putTextComboBox() self.filename = QtGui.QFileDialog.getOpenFileName(self, 'Open File') if (self.filename != ""): self.ventana.textEdit.setText("# Opened File # \n" + self.filename) # carga imagen con rasterIO /GDAL compatible self.file_pointer = rasterIO.opengdalraster(str(self.filename)) driver, self.XSize, self.YSize, self.NBand, proj_wkt, geo = rasterIO.readrastermeta(self.file_pointer) self.lon0, self.lat0 = geo[0], geo[3] self.dlon, self.dlat = geo[1], geo[5] # rango de latitud y longitud max_lat = self.lat0 + self.YSize * self.dlat max_lon = self.lon0 + self.XSize * self.dlon # Show and Log figure metadata sms = "\n+ Metadata \n " + proj_wkt + "\n" sms += " - Size = " + str(self.YSize) + "," + str(self.XSize) + "\n" sms += " - Delta latitude = " + str(self.dlat) + "\n - Delta longitude = " + str(self.dlon) + "\n" sms += " - Latitude limits: \n" sms += " from = " + str(self.lat0) + "\n" sms += " to = " + str(max_lat) + "\n" sms += " - Longitude limits: \n" sms += " from = " + str(self.lon0) + "\n" sms += " to = " + str(max_lon) + "\n" self.ventana.textEdit.append(sms) # se muestra la imagen con el mapa de colores y la banda 1 self.putImage() # actualiza el combobox de bandas self.putBand() # se actualiza la interfaz para mostrar las acciones en el textEdit QtGui.QApplication.processEvents() def putBand(self): """ Función carga las bandas de la imagen (menú imagenes) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ colorMap = self.imagesMenu.comboBox.currentText() # se borra el combobox de las bandas self.imagesMenu.comboBox_2.clear() # se obtiene la cantidad de bandas de la imagen seleccionada bands = [str(b) for b in range(1, self.NBand + 1)] self.imagesMenu.comboBox_2.addItems(bands) def putImage(self): """ Función que muestra la imagen requerida (menú imagenes) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ self.clear1() # se obtiene el colormap seleccionado colorMap = str(self.imagesMenu.comboBox.currentText()) if (colorMap == " "): colorMap = "gist_earth" # se obtiene la banda seleccionada if (self.imagesMenu.comboBox_2.currentText() == ''): band = 1 else: band = int(self.imagesMenu.comboBox_2.currentText()) self.data = rasterIO.readrasterband(self.file_pointer, band) self.data = self.data.astype(np.float32) self.ax = self.figure.add_subplot(111) colorValue = eval("cm." + colorMap) image = self.ax.imshow(self.data, cmap=colorValue) # se inserta la barra de colores # el segundo parametro es el tamano de la barra de colores self.figure.colorbar(image, pad=0.01) # para hacer mas pequenios los margenes self.figure.tight_layout() # se actualiza la interfaz para mostrar las acciones en el textEdit QtGui.QApplication.processEvents() # se grafica self.canvas.draw() # se activa el boton extract self.imagesMenu.pushButton_2.setEnabled(True) def changeRowCol(self): """ Intercambia los nombres de los combobox (menú imagenes) Cambia label Latitude/Longitude por Row/column :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ self.imagesMenu.label_4.setText('Row') self.imagesMenu.label_5.setText('Column') def changeLatLon(self): """ Intercambia los nombres de los combobox (menú imagenes) Cambia label Row/column por Latitude/Longitude :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ self.imagesMenu.label_4.setText('Latitude') self.imagesMenu.label_5.setText('Longitude') def extract(self): """ Extrae el valor de un pixel de la imagen (menú imagenes) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ # Get the value from image if (self.imagesMenu.radioButton.isChecked()): # esta seleccionada "Lat/Lon" lat = float(self.imagesMenu.lineEdit.text()) lon = float(self.imagesMenu.lineEdit_2.text()) ## Image indexes row,col = module.getRowCol(lat,lon,self.lat0, self.lon0, self.dlat, self.dlon) else: row = float(self.imagesMenu.lineEdit.text()) col = float(self.imagesMenu.lineEdit_2.text()) ## Only to be logged lat, lon = module.getLatLon(row,col,self.lat0, self.lon0, self.dlat, self.dlon) ## Format the info to be logged sms = "\n+ Extract operation \n" sms += " Lat = " + str(lat) + "\n" sms += " Lon = " + str(lon) + "\n" sms += " Row = " + str(row) + "\n" sms += " Col = " + str(col) + "\n" self.ventana.textEdit.append(sms) if (self.YSize < row or row < 0) or (self.XSize < col or col < 0): # if row or col are out of bounds self.ventana.textEdit.append("\n Error: Row or column out of bouds") else: self.imagesMenu.lineEdit_3.setText(str(self.data[row][col])) self.ventana.textEdit.append(" Extracted value = " + str(self.data[row][col])) ###------------------------Fin Images Menu--------------------------------- ###------------------------------------------------------------------------ ###------------------------MassiveCalc------------------------------------- def showMC(self): """ Función que muestra la ventana de calculos masivos :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ # primero se crea el objeto frame self.frame = QtGui.QFrame() self.frame.setWindowTitle("Titi") # se instancia la clase de la ventana calculos masivos titi_calcs_app.CalcsApp(self.frame) # se le fija el tamaño a la ventana y se quita el resize width = 900 height = 600 self.frame.setFixedSize(width, height) self.frame.show() return ###------------------------Fin massiveCalc--------------------------------- ###------------------------------------------------------------------------ ###------------------------Funciones generales----------------------------- def removeButtons(self): """ Elimina widgets agregados dinamicamente (menú orbitas o imágenes) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ for cnt in range(self.ventana.verticalLayout_2.count()): value = str(self.ventana.verticalLayout_2.itemAt(cnt)) #print value if (value.find("QWidgetItem") != -1): self.ventana.verticalLayout_2.itemAt(cnt).widget().close() def mouse_move(self, event): """ Captura posición del mouse sobre imagen (menú imágenes) :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ valorCB = str(self.ventana.comboBox.currentText()) if not event.inaxes: return if (valorCB != "SAC-D/Aquarius"): # solo si se encuentra en imagesMenu #if (self.ventana.verticalLayout_3.count() == 0): ## no se agrego la figura de zoom aun ## entonces se agrega el figure x, y = event.xdata, event.ydata #self.ventana.label_2.setText(str(x) + " " + str(y)) if (0 <= x <= self.XSize) and (0 <= y <= self.YSize): # se obtiene el colormap seleccionado colorMap = str(self.imagesMenu.comboBox.currentText()) colorValue = eval("cm." + colorMap) ## Load small zoom self.figure2.clear() self.ax2 = self.figure2.add_subplot(111) image2 = self.ax2.imshow(self.data[y-8:y+8,x-8:x+8], cmap = colorValue) self.canvas2.draw() # statusBar col, row = int(x), int(y) lat, lon = module.getLatLon(row,col, self.lat0, self.lon0, self.dlat, self.dlon) sms = "Row,Col = [" + str(row) + "," + str(col) + "] | Lat,Lon = [" + str(lat) + "," + str(lon) + "]" sms += " | Value = " + str(self.data[row,col]) self.statusBar().showMessage(sms) def clear(self): """ Función que borra las gráficas y la barra de acciones :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ self.figure.clear() self.canvas.draw() self.figure2.clear() self.canvas2.draw() self.ventana.textEdit.clear() def clear1(self): """ Función que borra las gráficas :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ self.figure.clear() self.canvas.draw() self.figure2.clear() self.canvas2.draw() def about(self): """ Función que muestra cuadro de diálogo con info acerca del software :param self: instancia de la clase MainApp :type self: titi_app.MainApp :returns: Sin retorno :rtype: -- """ QtGui.QMessageBox.about(self, self.tr("Acerca de..."), self.tr("saTellITal Image viewer\n\n" "Autor: CENEHA - Centro de Estudios Hidroambientales \n" "E-mail: ceneha [at] fich.unl.edu.ar\n" "Version: 0.2 \n" "Fecha: May 2014"))
class View(QWidget): def __init__(self, parent = None): super(View, self).__init__(parent) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar = NavigationToolbar(self.canvas, self) self.navigationLayout = QHBoxLayout() layout = QHBoxLayout(self) self.navigations = [] self.addNavigation(True) addIcon = QIcon.fromTheme('list-add') addNaviButton = QPushButton(addIcon, 'Add navigation', self) addNaviButton.clicked.connect(self.addNavigation) self.maxFreq = QDoubleSpinBox(self) self.maxFreq.setValue(10.0) self.maxFreq.setVisible(False) spectrumIcon = QIcon.fromTheme('network-wireless') self.spectrum = QPushButton(spectrumIcon, 'Spectrum', self) self.spectrum.setCheckable(True) self.spectrum.clicked.connect(self.plot) self.spectrum.toggled.connect(self.maxFreq.setVisible) self.maxFreq.valueChanged.connect(self.plot) saveAll = QPushButton(QIcon.fromTheme('document-save'), '', self) saveAll.clicked.connect(self.savePlots) self.epicenterX = QDoubleSpinBox(self) self.epicenterX.setValue(0.0) self.epicenterX.setMaximum(float('inf')) self.epicenterX.setVisible(False) self.epicenterX.valueChanged.connect(self.plot) self.epicenterY = QDoubleSpinBox(self) self.epicenterY.setValue(0.0) self.epicenterY.setMaximum(float('inf')) self.epicenterY.setVisible(False) self.epicenterY.valueChanged.connect(self.plot) self.rotate = QPushButton('Rotate', self) self.rotate.setCheckable(True) self.rotate.clicked.connect(self.plot) self.rotate.toggled.connect(self.epicenterX.setVisible) self.rotate.toggled.connect(self.epicenterY.setVisible) toolLayout = QHBoxLayout() toolLayout.addWidget(addNaviButton) toolLayout.addWidget(self.spectrum) toolLayout.addWidget(self.maxFreq) toolLayout.addWidget(saveAll) toolLayout.addWidget(self.rotate) toolLayout.addWidget(self.epicenterX) toolLayout.addWidget(self.epicenterY) toolLayout.addWidget(toolbar) plotLayout = QVBoxLayout() plotLayout.addLayout(toolLayout) plotLayout.addWidget(self.canvas) layout.addLayout(self.navigationLayout) layout.addLayout(plotLayout) def addNavigation(self, noclose = False): navigation = Navigation.Navigation(noclose) navigation.activeItemChanged.connect(self.plot) navigation.close.connect(self.closeNavigation) navigation.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Minimum) self.navigationLayout.addWidget(navigation) self.navigations.append(navigation) def closeNavigation(self, widget): self.navigations.remove(widget) self.navigationLayout.removeWidget(widget) widget.deleteLater() self.plot() def plot(self): waveforms = [] numPlots = 0 names = set() for nav in self.navigations: for wf in nav.getActiveWaveforms(): numPlots = max(numPlots, len(wf.names)) names.update(set(wf.names)) waveforms.append(wf) self.figure.clear() if numPlots > 0: names = list(names) names.sort() numRows = math.ceil(math.sqrt(numPlots)); numCols = math.ceil(numPlots / numRows) subplots = dict() for i in range(0, len(names)): subplots[ names[i] ] = self.figure.add_subplot(numRows, numCols, i+1) for wf in waveforms: for name in wf.names: p = subplots[name] if self.spectrum.isChecked(): n = len(wf.waveforms[name]) dt = wf.time[1]-wf.time[0] # assume equally spaced samples f = scipy.fftpack.fftfreq(n, dt) W = dt * scipy.fftpack.fft(wf.waveforms[name]) maxFreqIndices = numpy.argwhere(f > self.maxFreq.value()) L = maxFreqIndices[0] if len(maxFreqIndices) > 0 else n/2 p.loglog(f[1:L], numpy.absolute(W[1:L])) p.set_xlabel('f [Hz]') else: twf = wf.waveforms[name] if self.rotate.isChecked(): epicenter = numpy.array([self.epicenterX.value(), self.epicenterY.value(), 0.0]) radial = wf.coordinates - epicenter phi = math.acos(radial[0] / numpy.linalg.norm(radial)) if name == 'u': twf = math.cos(phi) * wf.waveforms['u'] + math.sin(phi) * wf.waveforms['v'] elif name == 'v': twf = -math.sin(phi) * wf.waveforms['u'] + math.cos(phi) * wf.waveforms['v'] p.plot(wf.time, twf) p.set_xlabel('t (s)') p.set_ylabel(name) self.figure.tight_layout() self.canvas.draw() def savePlots(self): filetypes = self.canvas.get_supported_filetypes_grouped() defaultFiletype = self.canvas.get_default_filetype() filters = [] selectedFilter = '' for name, extensions in sorted(filetypes.iteritems()): filtr = '{0} ({1})'.format(name, ' '.join(['*.{0}'.format(ext) for ext in extensions])) if defaultFiletype in extensions: selectedFilter = filtr filters.append(filtr) fileName, filtr = QFileDialog.getSaveFileNameAndFilter(self, 'Choose a save location.', '', ';;'.join(filters), selectedFilter) fileName = os.path.splitext(str(fileName))[0] extension = re.search(r'\*(\.[a-zA-Z]+)', str(filtr)).group(1) maxRow = min([nav.numberOfRows() for nav in self.navigations]) for row in range(maxRow): for nav in self.navigations: nav.selectWaveformAt(row) self.plot() self.canvas.print_figure('{0}{1:03}{2}'.format(fileName, row+1, extension))
class Browse(QtGui.QMainWindow): '''Class to hold the GUI browse method''' def __init__(self, pathDir=None, airborne=False, rhi=False): '''Initialize the class to create the interface''' super(Browse, self).__init__() # Set some parameters self.dirIn = pathDir # Default field and tilt angle to plot self.field = 'reflectivity' self.tilt = 0 self.airborne = airborne self.rhi = rhi # Set size of plot self.XSIZE = PPI_XSIZE self.YSIZE = PPI_YSIZE self.XRNG = PPI_XRNG self.YRNG = PPI_YRNG if self.airborne: self.XSIZE = AIR_XSIZE self.YSIZE = AIR_YSIZE self.XRNG = AIR_XRNG self.YRNG = AIR_YRNG if self.rhi: self.XSIZE = RHI_XSIZE self.YSIZE = RHI_YSIZE self.XRNG = RHI_XRNG self.YRNG = RHI_YRNG # Set plot title and colorbar units to defaults self.title = None self.units = None # Initialize limits self._initialize_limits() # Set the default range rings self.RngRingList = ["None", "10 km", "20 km", "30 km", "50 km", "100 km"] self.RngRing = False # Find the PyArt colormap names # self.cm_names = pyart.graph.cm._cmapnames self.cm_names = [m for m in cm.datad if not m.endswith("_r")] self.cm_names.sort() # Create a figure for output self._set_fig_ax(nrows=1, ncols=1) # Initiate no tool useage self.ToolSelect = "No Tools" # Launch the GUI interface self.LaunchGUI() # Show an "Open" dialog box and return the path to the selected file self.showFileDialog() self.central_widget.setLayout(self.layout) self.show() self.pickPoint = self.fig.canvas.mpl_connect('button_press_event', self.onPick) # Allow advancement via left and right arrow keys # and tilt adjustment via the Up-Down arrow keys def keyPressEvent(self, event): if event.key()==QtCore.Qt.Key_Right: #self.slider.setValue(self.slider.value() + 1) self.AdvanceFileSelect(self.fileindex + 1) elif event.key()==QtCore.Qt.Key_Left: #self.slider.setValue(self.slider.value() - 1) self.AdvanceFileSelect(self.fileindex - 1) elif event.key()==QtCore.Qt.Key_Up: #self.slider.setValue(self.slider.value() - 1) self.TiltSelectCmd(self.tilt + 1) elif event.key()==QtCore.Qt.Key_Down: #self.slider.setValue(self.slider.value() - 1) self.TiltSelectCmd(self.tilt - 1) else: QtGui.QWidget.keyPressEvent(self, event) def onPick(self, event): '''Get value at the point selected by mouse click''' xdata = event.xdata # get event x location ydata = event.ydata # get event y location az = np.arctan2(xdata, ydata)*180./np.pi if az < 0: az = az + 360. rng = np.sqrt(xdata*xdata+ydata*ydata) azindex = np.argmin(np.abs(self.radar.azimuth['data'][self.radar.sweep_start_ray_index['data'][self.tilt]:self.radar.sweep_end_ray_index['data'][self.tilt]]-az))+self.radar.sweep_start_ray_index['data'][self.tilt] rngindex = np.argmin(np.abs(self.radar.range['data']-rng*1000.)) msg = 'x = %4.2f, y = %4.2f, Azimuth = %4.2f deg., Range = %4.3f km, %s = %4.2f %s'\ %(xdata, ydata, self.radar.azimuth['data'][azindex], self.radar.range['data'][rngindex]/1000., self.field, self.radar.fields[self.field]['data'][azindex][rngindex], self.units) self.statusBar().showMessage(msg) #################### # GUI methods # #################### def LaunchGUI(self): '''Launches a GUI interface.''' self.setAttribute(QtCore.Qt.WA_DeleteOnClose) # Initiate a counter, used so that Tilt and Field menus are # not increased every time there is a selection # Might be a more elegant way self.counter = 0 # Create the widget self.central_widget = QtGui.QWidget() self.setCentralWidget(self.central_widget) self.statusBar() # Create the menus self.CreateMenu() # Create the button controls limsb = QtGui.QPushButton("Adjust Limits") limsb.setToolTip("Set data, X, and Y range limits") limsb.clicked.connect(self.showLimsDialog) titleb = QtGui.QPushButton("Title") titleb.setToolTip("Change plot title") titleb.clicked.connect(self._title_input) unitsb = QtGui.QPushButton("Units") unitsb.setToolTip("Change units string") unitsb.clicked.connect(self._units_input) # Create the Tools ComboBox self.toolsBoxUI() # Create layout self.layout = QtGui.QGridLayout() self.layout.setSpacing(8) self.layout.addWidget(limsb, 0, 0) self.layout.addWidget(self.toolsBox, 0, 1) self.layout.addWidget(titleb, 0, 2) self.layout.addWidget(unitsb, 0, 3) # Create the Tilt buttons self.CreateTiltWidget() def showFileDialog(self): '''Open a dialog box to choose file''' self.qfilename = QtGui.QFileDialog.getOpenFileName(self, 'Open file', self.dirIn) self.filename = str(self.qfilename) self._openfile() def showLimsDialog(self): self.limsDialog = QtGui.QDialog() u = Ui_LimsDialog(self.limsDialog, self.limits) u.setupUi()#self.limsDialog, self.limits self.limsDialog.exec_() def LaunchTiltButton(self): if self.windowTilt is None: self.windowTilt = TiltButtons(tilts=self.rTilts) self.windowTilt.show() def toolsBoxUI(self): self.toolsBox = QtGui.QComboBox() self.toolsBox.setToolTip("Choose a tool to apply") self.toolsBox.addItem("No Tools") self.toolsBox.addItem("Zoom/Pan") self.toolsBox.addItem("Reset file defaults") self.toolsBox.activated[str].connect(self.comboAction) def comboAction(self, text): # Check to see if Zoom/Pan was selected, if so disconnect if self.ToolSelect == "Zoom/Pan": print "IN DISCONNECT" self.zp.disconnect() # Set the Tool to use self.ToolSelect = text if self.ToolSelect == "Reset file defaults": self._initialize_limits() self._update_plot() ###################### # Help methods # ###################### def _about(self): txOut = "This is a simple radar file browser to allow \ quicklooks using the DoE PyArt software" QtGui.QMessageBox.about(self, "About ARTView", txOut) def _get_RadarLongInfo(self): '''Print out the radar info to text box''' # Get the radar info form rada object and print it txOut = self.radar.info() print txOut # QtGui.QMessageBox.information(self, "Long Radar Info", str(txOut)) QtGui.QMessageBox.information(self, "Long Radar Info", "See terminal window") def _get_RadarShortInfo(self): '''Print out some basic info about the radar''' try: rname = self.radar.metadata['instrument_name'] except: rname = "Info not available" try: rlon = str(self.radar.longitude['data'][0]) except: rlon = "Info not available" try: rlat = str(self.radar.latitude['data'][0]) except: rlat = "Info not available" try: ralt = str(self.radar.altitude['data'][0]) raltu = self.radar.altitude['units'][0] except: ralt = "Info not available" raltu = " " try: maxr = str(self.radar.instrument_parameters['unambiguous_range']['data'][0]) maxru = self.radar.instrument_parameters['unambiguous_range']['units'][0] except: maxr = "Info not available" maxru = " " try: nyq = str(self.radar.instrument_parameters['nyquist_velocity']['data'][0]) nyqu = self.radar.instrument_parameters['nyquist_velocity']['units'][0] except: nyq = "Info not available" nyqu = " " try: bwh = str(self.radar.instrument_parameters['radar_beam_width_h']['data'][0]) bwhu = self.radar.instrument_parameters['radar_beam_width_h']['units'][0] except: bwh = "Info not available" bwhu = " " try: bwv = str(self.radar.instrument_parameters['radar_beam_width_v']['data'][0]) bwvu = self.radar.instrument_parameters['radar_beam_width_v']['units'][0] except: bwv = "Info not available" bwvu = " " try: pw = str(self.radar.instrument_parameters['pulse_width']['data'][0]) pwu = self.radar.instrument_parameters['pulse_width']['units'][0] except: pw = "Info not available" pwu = " " try: ngates = str(self.radar.ngates) except: ngates = "Info not available" try: nsweeps = str(self.radar.nsweeps) except: nsweeps = "Info not available" txOut = (('Radar Name: %s\n'% rname) +\ ('Radar longitude: %s\n'% rlon) + \ ('Radar latitude: %s\n'% rlat) + \ ('Radar altitude: %s %s\n'% (ralt, raltu)) + \ (' \n') + \ ('Unambiguous range: %s %s\n'% (maxr, maxru)) + \ ('Nyquist velocity: %s %s\n'% (nyq, nyqu)) + \ (' \n') + \ ('Radar Beamwidth, horiz: %s %s\n'% (bwh, bwhu)) + \ ('Radar Beamwidth, vert: %s %s\n'% (bwv, bwvu)) + \ ('Pulsewidth: %s %s \n'% (pw, pwu)) + \ (' \n') + \ ('Number of gates: %s\n'% ngates) + \ ('Number of sweeps: %s\n'% nsweeps)) QtGui.QMessageBox.information(self, "Short Radar Info", txOut) ###################### # Menu build methods # ###################### def CreateMenu(self): '''Create a selection menu''' self.menubar = self.menuBar() self.AddFileMenu() self.AddAboutMenu() self.AddPlotMenu() self.AddFileAdvanceMenu() def AddFileMenu(self): self.filemenu = self.menubar.addMenu('&File') openFile = QtGui.QAction('Open', self) openFile.setShortcut('Ctrl+O') openFile.setStatusTip('Open new File') openFile.triggered.connect(self.showFileDialog) quicksaveImage = QtGui.QAction('Quick Save Image', self) quicksaveImage.setShortcut('Ctrl+D') quicksaveImage.setStatusTip('Save Image to local directory with default name') quicksaveImage.triggered.connect(self._quick_savefile) saveImage = QtGui.QAction('Save Image', self) saveImage.setShortcut('Ctrl+S') saveImage.setStatusTip('Save Image using dialog') saveImage.triggered.connect(self._savefile) exitApp = QtGui.QAction('Close', self) exitApp.setShortcut('Ctrl+Q') exitApp.setStatusTip('Exit ARTView') exitApp.triggered.connect(self.close) self.filemenu.addAction(openFile) self.filemenu.addAction(quicksaveImage) self.filemenu.addAction(saveImage) self.filemenu.addAction(exitApp) def AddAboutMenu(self): '''Add Help item to menu bar''' self.aboutmenu = self.menubar.addMenu('About') self._aboutArtview = QtGui.QAction('ARTView', self) self._aboutArtview.setStatusTip('About ARTView') self._aboutArtview.triggered.connect(self._about) self.RadarShort = QtGui.QAction('Radar Short', self) self.RadarShort.setStatusTip('Print Short Radar Structure Info') self.RadarShort.triggered.connect(self._get_RadarShortInfo) self.RadarLong = QtGui.QAction('Radar Long', self) self.RadarLong.setStatusTip('Print Long Radar Structure Info') self.RadarLong.triggered.connect(self._get_RadarLongInfo) self.aboutmenu.addAction(self._aboutArtview) self.aboutmenu.addAction(self.RadarShort) self.aboutmenu.addAction(self.RadarLong) def AddPlotMenu(self): '''Add Plot item to menu bar''' self.plotmenu = self.menubar.addMenu('&Plot') # Add submenus self.fieldmenu = self.plotmenu.addMenu('Field') if self.airborne or self.rhi: pass else: self.tiltmenu = self.plotmenu.addMenu('Tilt') self.rngringmenu = self.plotmenu.addMenu('Set Range Rings') self.cmapmenu = self.plotmenu.addMenu('Colormap') def AddFileAdvanceMenu(self): '''Add an option to advance to next or previous file''' self.advancemenu = self.menubar.addMenu("Advance file") def AddTiltMenu(self): '''Add a menu to change tilt angles of current plot''' for ntilt in self.rTilts: TiltAction = self.tiltmenu.addAction("Tilt %d"%(ntilt+1)) TiltAction.triggered[()].connect(lambda ntilt=ntilt: self.TiltSelectCmd(ntilt)) def AddFieldMenu(self): '''Add a menu to change current plot field''' for nombre in self.fieldnames: FieldAction = self.fieldmenu.addAction(nombre) FieldAction.triggered[()].connect(lambda nombre=nombre: self.FieldSelectCmd(nombre)) def AddRngRingMenu(self): '''Add a menu to set range rings''' for RngRing in self.RngRingList: RingAction = self.rngringmenu.addAction(RngRing) RingAction.triggered[()].connect(lambda RngRing=RngRing: self.RngRingSelectCmd(RngRing)) def AddNextPrevMenu(self): '''Add an option to advance to next or previous file''' nextAction = self.advancemenu.addAction("Next") nextAction.triggered[()].connect(lambda findex=self.fileindex + 1: self.AdvanceFileSelect(findex)) prevAction = self.advancemenu.addAction("Previous") prevAction.triggered[()].connect(lambda findex=self.fileindex - 1: self.AdvanceFileSelect(findex)) firstAction = self.advancemenu.addAction("First") firstAction.triggered[()].connect(lambda findex=0: self.AdvanceFileSelect(findex)) lastAction = self.advancemenu.addAction("Last") lastAction.triggered[()].connect(lambda findex=(len(self.filelist) - 1): self.AdvanceFileSelect(findex)) def AddCmapMenu(self): '''Add a menu to change colormap used for plot''' for cm_name in self.cm_names: cmapAction = self.cmapmenu.addAction(cm_name) cmapAction.setStatusTip("Use the %s colormap"%cm_name) cmapAction.triggered[()].connect(lambda cm_name=cm_name: self.cmapSelectCmd(cm_name)) ######################## # Button methods # ######################## def CreateTiltWidget(self): '''Create a widget to store radio buttons to control tilt adjust''' self.radioBox = QtGui.QGroupBox("Tilt Selection") self.rBox_layout = QtGui.QVBoxLayout(self.radioBox) def SetTiltRadioButtons(self): '''Set a tilt selection using radio buttons''' # Need to first create each tilt button and connect a value when selected for ntilt in self.rTilts: tiltbutton = QtGui.QRadioButton("Tilt %d"%(ntilt+1), self.radioBox) QtCore.QObject.connect(tiltbutton, QtCore.SIGNAL("clicked()"), \ partial(self.TiltSelectCmd, ntilt)) self.rBox_layout.addWidget(tiltbutton) self.radioBox.setLayout(self.rBox_layout) return self.radioBox ############################# # Limit entry methods # ############################# def _lims_input(self, entry): '''Retrieve new limits input''' if entry['dmin'] is not None: self.limits['vmin'] = entry['dmin'] if entry['dmax'] is not None: self.limits['vmax'] = entry['dmax'] if entry['xmin'] is not None: self.limits['xmin'] = entry['xmin'] if entry['xmax'] is not None: self.limits['xmax'] = entry['xmax'] if entry['ymin'] is not None: self.limits['ymin'] = entry['ymin'] if entry['ymax'] is not None: self.limits['ymax'] = entry['ymax'] self._update_plot() def _title_input(self): '''Retrieve new plot title''' if self.title is None: old_val = '' else: old_val = self.title val, entry = QtGui.QInputDialog.getText(self, "Plot Title", \ "Title:", 0, old_val) if entry is True: self.title = val self._update_plot() def _units_input(self): '''Retrieve new plot units''' if self.units is None: old_val = '' else: old_val = self.units val, entry = QtGui.QInputDialog.getText(self, "Plot Units", \ "Units:", 0, old_val) if entry is True: self.units = val self._update_plot() ######################## # Selectionion methods # ######################## def TiltSelectCmd(self, ntilt): '''Captures a selection and redraws the field with new tilt''' print ntilt self.tilt = ntilt self._update_plot() def FieldSelectCmd(self, nombre): '''Captures a selection and redraws the new field''' self.field = nombre self._initialize_limits() self.units = None self._update_plot() def RngRingSelectCmd(self, ringSel): '''Captures selection and redraws the field with range rings''' if ringSel is "None": self.RngRing = False else: self.RngRing = True # Find the unambigous range of the radar try: unrng = int(self.radar.instrument_parameters['unambiguous_range']['data'][0]/1000) except: unrng = int(self.limits['xmax']) # Set the step if ringSel == '10 km': ringdel = 10 if ringSel == '20 km': ringdel = 20 if ringSel == '30 km': ringdel = 30 if ringSel == '50 km': ringdel = 50 if ringSel == '100 km': ringdel = 100 # Calculate an array of range rings self.RNG_RINGS = range(ringdel, unrng, ringdel) self._update_plot() def cmapSelectCmd(self, cm_name): '''Captures selection of new cmap and redraws''' self.CMAP = cm_name self._update_plot() def AdvanceFileSelect(self, findex): '''Captures a selection and redraws figure with new file''' if findex > len(self.filelist): print len(self.filelist) msg = "End of directory, cannot advance" self._ShowWarning(msg) findex = (len(self.filelist) - 1) return if findex < 0: msg = "Beginning of directory, must move forward" self._ShowWarning(msg) findex = 0 return self.fileindex = findex self.filename = self.dirIn + "/" + self.filelist[findex] self._openfile() ######################## # Menu display methods # ######################## def _openfile(self): '''Open a file via a file selection window''' print "Opening file " + self.filename # Update to current directory when file is chosen self.dirIn = os.path.dirname(self.filename) # Get a list of files in the working directory self.filelist = os.listdir(self.dirIn) self.fileindex = self.filelist.index(os.path.basename(self.filename)) # Read the data from file try: self.radar = pyart.io.read(self.filename) except: msg = "This is not a recognized radar file" self._ShowWarning(msg) return # In case the flags were not used at startup # Check to see if this is an aircraft or rhi file if self.counter == 0: self._check_file_type() self._set_figure_canvas() # Increment counter to know whether to renew Tilt and field menus # If > 1 then remove the pre-existing menus before redrawing self.counter += 1 if self.counter > 1: self.fieldmenu.clear() self.advancemenu.clear() if self.airborne or self.rhi: pass else: self.tiltmenu.clear() self.rngringmenu.clear() self.radioBox.deleteLater() # Get the tilt angles self.rTilts = self.radar.sweep_number['data'][:] # Get field names self.fieldnames = self.radar.fields.keys() # Set up the menus associated with scanning ground radars if self.airborne or self.rhi: pass else: self.CreateTiltWidget() self.layout.addWidget(self.SetTiltRadioButtons(), 1, 6, 6, 1) self.AddRngRingMenu() self.AddTiltMenu() self.AddFieldMenu() self.AddNextPrevMenu() self.AddCmapMenu() self.units = None self.title = None self._update_plot() #################### # Plotting methods # #################### def _set_fig_ax(self, nrows=1, ncols=1): '''Set the figure and axis to plot to''' self.fig = Figure(figsize=(self.XSIZE, self.YSIZE)) xwidth = 0.7 yheight = 0.7 * float(self.YSIZE)/float(self.XSIZE) self.ax = self.fig.add_axes([0.2, 0.2, xwidth, yheight]) self.cax = self.fig.add_axes([0.2,0.10, xwidth, 0.02]) # We want the axes cleared every time plot() is called #self.axes.hold(False) def _set_fig_ax_rhi(self): '''Change figure size and limits if RHI''' if self.rhi: self.XSIZE = RHI_XSIZE self.YSIZE = RHI_YSIZE self.limits['ymin'] = RHI_YRNG[0] self.limits['ymax'] = RHI_YRNG[1] if self.airborne: self.XSIZE = AIR_XSIZE self.YSIZE = AIR_YSIZE self.limits['xmin'] = AIR_XRNG[0] self.limits['xmax'] = AIR_XRNG[1] self.limits['ymin'] = AIR_YRNG[0] self.limits['ymax'] = AIR_YRNG[1] self.fig.set_size_inches(self.XSIZE, self.YSIZE) self._set_fig_ax() def _set_figure_canvas(self): '''Set the figure canvas to draw in window area''' self.canvas = FigureCanvasQTAgg(self.fig) # Add the widget to the canvas self.layout.addWidget(self.canvas, 1, 0, 7, 6) def _update_plot(self): '''Renew the plot''' # This is a bit of a hack to ensure that the viewer works with files # withouth "standard" output as defined by PyArt # Check to see if the field 'reflectivity' exists for the initial open self._check_default_field() # Create the plot with PyArt RadarDisplay # Always intitiates at lowest elevation angle self.ax.cla() # Reset to default title if user entered nothing w/ Title button if self.title == '': self.title = None # If Zoom/Pan selected, Set up the zoom/pan functionality if self.ToolSelect == "Zoom/Pan": scale = 1.1 self.zp = ZoomPan(self.ax, self.limits, base_scale = scale) #figZoom = self.zp.zoom() #figPan = self.zp.pan_factory(self.limits) self.zp.connect() if self.airborne: self.display = pyart.graph.RadarDisplay_Airborne(self.radar) self.plot = self.display.plot_sweep_grid(self.field, \ vmin=self.limits['vmin'], vmax=self.limits['vmax'],\ colorbar_flag=False, cmap=self.CMAP,\ ax=self.ax, title=self.title) self.display.set_limits(xlim=(self.limits['xmin'], self.limits['xmax']),\ ylim=(self.limits['ymin'], self.limits['ymax']),\ ax=self.ax) self.display.plot_grid_lines() else: self.display = pyart.graph.RadarDisplay(self.radar) if self.radar.scan_type != 'rhi': # Create Plot if self.tilt < len(self.rTilts): pass else: self.tilt = 0 self.plot = self.display.plot_ppi(self.field, self.tilt,\ vmin=self.limits['vmin'], vmax=self.limits['vmax'],\ colorbar_flag=False, cmap=self.CMAP,\ ax=self.ax, title=self.title) # Set limits self.display.set_limits(xlim=(self.limits['xmin'], self.limits['xmax']),\ ylim=(self.limits['ymin'], self.limits['ymax']),\ ax=self.ax) # Add range rings if self.RngRing: self.display.plot_range_rings(self.RNG_RINGS, ax=self.ax) # Add radar location self.display.plot_cross_hair(5., ax=self.ax) else: self.plot = self.display.plot_rhi(self.field, self.tilt,\ vmin=self.limits['vmin'], vmax=self.limits['vmax'],\ colorbar_flag=False, cmap=self.CMAP,\ ax=self.ax, title=self.title) self.display.set_limits(xlim=(self.limits['xmin'], self.limits['xmax']),\ ylim=(self.limits['ymin'], self.limits['ymax']),\ ax=self.ax) # Add range rings if self.RngRing: self.display.plot_range_rings(self.RNG_RINGS, ax=self.ax) norm = mlabNormalize(vmin=self.limits['vmin'],\ vmax=self.limits['vmax']) self.cbar=mlabColorbarBase(self.cax, cmap=self.CMAP,\ norm=norm, orientation='horizontal') # colorbar - use specified units or default depending on # what has or has not been entered if self.units is None or self.units == '': try: self.units = self.radar.fields[self.field]['units'] except: self.units = '' self.cbar.set_label(self.units) print "Plotting %s field, Tilt %d" % (self.field, self.tilt+1) self.canvas.draw() ######################### # Get and check methods # ######################### def _initialize_limits(self): if self.field == 'reflectivity': self.vminmax = (Z_LIMS[0], Z_LIMS[1]) self.CMAP = 'gist_ncar' elif self.field == 'DBZ': self.vminmax = (Z_LIMS[0], Z_LIMS[1]) self.CMAP = 'gist_ncar' elif self.field == 'velocity': self.vminmax = (VR_LIMS[0], VR_LIMS[1]) self.CMAP = 'RdBu_r' elif self.field == 'VEL': self.vminmax = (VR_LIMS[0], VR_LIMS[1]) self.CMAP = 'RdBu_r' elif self.field == 'differential_reflectivity': self.vminmax = (ZDR_LIMS[0], ZDR_LIMS[1]) self.CMAP = 'RdYlBu_r' elif self.field == 'cross_correlation_ratio': self.vminmax = (RHO_HV_LIMS[0], RHO_HV_LIMS[1]) self.CMAP = 'cool' elif self.field == 'differential_phase': self.vminmax = (KDP_LIMS[0], KDP_LIMS[1]) self.CMAP = 'YlOrBr' elif self.field == 'normalized_coherent_power': self.vminmax = (NCP_LIMS[0], NCP_LIMS[1]) self.CMAP = 'jet' elif self.field == 'spectrum_width': self.vminmax = (SW_LIMS[0], SW_LIMS[1]) self.CMAP = 'gist_ncar' elif self.field == 'specific_differential_phase': self.vminmax = (PHIDP_LIMS[0], PHIDP_LIMS[1]) self.CMAP = 'RdBu_r' elif self.field == 'total_power': self.vminmax = (TP_LIMS[0], TP_LIMS[1]) self.CMAP = 'jet' limit_strs = ('vmin', 'vmax', 'xmin', 'xmax', 'ymin', 'ymax') self.limits = {} # Now pull the default values self.limits['vmin'] = self.vminmax[0] self.limits['vmax'] = self.vminmax[1] self.limits['xmin'] = self.XRNG[0] self.limits['xmax'] = self.XRNG[1] self.limits['ymin'] = self.YRNG[0] self.limits['ymax'] = self.YRNG[1] # # def _build_cmap_dict(self): # # self.cmap_dict = {} # # self.cmap_dict['gist_ncar'] = matcm.get_cmap(name='gist_ncar') # # self.cmap_dict['RdBu_r'] = matcm.get_cmap(name='RdBu_r') # # self.cmap_dict['RdYlBu_r'] = matcm.get_cmap(name='RdYlBu_r # # self.cmap_dict['cool'] = matcm.get_cmap(name='cool # # self.cmap_dict['YlOrBr'] = matcm.get_cmap(name='YlOrBr # # self.cmap_dict['jet'] = matcm.get_cmap(name='jet # # self.cmap_dict[' # # self.cmap_dict[' def _check_default_field(self): '''Hack to perform a check on reflectivity to make it work with a larger number of files This should only occur upon start up with a new file''' if self.field == 'reflectivity': if self.field in self.fieldnames: pass elif 'CZ' in self.fieldnames: self.field = 'CZ' elif 'DZ' in self.fieldnames: self.field = 'DZ' elif 'dbz' in self.fieldnames: self.field = 'dbz' elif 'DBZ' in self.fieldnames: self.field = 'DBZ' elif 'dBZ' in self.fieldnames: self.field = 'dBZ' elif 'Z' in self.fieldnames: self.field = 'Z' elif 'DBZ_S'in self.fieldnames: self.field = 'DBZ_S' elif 'reflectivity_horizontal'in self.fieldnames: self.field = 'reflectivity_horizontal' def _check_file_type(self): '''Check file to see if the file is airborne or rhi''' if self.radar.scan_type != 'rhi': pass else: try: (self.radar.metadata['platform_type'] == 'aircraft_tail') or \ (self.radar.metadata['platform_type'] == 'aircraft') self.airborne = True except: self.rhi = True self._set_fig_ax_rhi() ######################## # Warning methods # ######################## def _ShowWarning(self, msg): '''Show a warning message''' flags = QtGui.QMessageBox.StandardButton() response = QtGui.QMessageBox.warning(self, "Warning!", msg, flags) if response == 0: print msg else: print "Warning Discarded!" ######################## # Image save methods # ######################## def _quick_savefile(self, PTYPE=IMAGE_EXT): '''Save the current display''' PNAME = self.display.generate_filename(self.field, self.tilt, ext=IMAGE_EXT) print "Creating "+ PNAME def _savefile(self, PTYPE=IMAGE_EXT): PBNAME = self.display.generate_filename(self.field, self.tilt, ext=IMAGE_EXT) file_choices = "PNG (*.png)|*.png" path = unicode(QtGui.QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=DPI) self.statusBar().showMessage('Saved to %s' % path)
class RadarDisplay(Component): ''' Class to create a display plot, using a returned Radar structure from the PyArt pyart.graph package. ''' Vradar = None #: see :ref:`shared_variable` Vfield = None #: see :ref:`shared_variable` Vtilt = None #: see :ref:`shared_variable` Vlims = None #: see :ref:`shared_variable` Vcmap = None #: see :ref:`shared_variable` Vgatefilter = None #: see :ref:`shared_variable` @classmethod def guiStart(self, parent=None): '''Graphical interface for starting this class''' args = _DisplayStart().startDisplay() return self(**args), True def __init__(self, Vradar, Vfield, Vtilt, Vlims=None, Vcmap=None, Vgatefilter=None, name="RadarDisplay", parent=None): ''' Initialize the class to create display. Parameters ---------- Vradar : :py:class:`~artview.core.core.Variable` instance Radar signal variable. Vfield : :py:class:`~artview.core.core.Variable` instance Field signal variable. Vtilt : :py:class:`~artview.core.core.Variable` instance Tilt signal variable. [Optional] Vlims : :py:class:`~artview.core.core.Variable` instance Limits signal variable. A value of None will instantiate a limits variable. Vcmap : :py:class:`~artview.core.core.Variable` instance Colormap signal variable. A value of None will instantiate a colormap variable. Vgatefilter : :py:class:`~artview.core.core.Variable` instance Gatefilter signal variable. A value of None will instantiate a empty variable. name : string Display window name. parent : PyQt instance Parent instance to associate to Display window. If None, then Qt owns, otherwise associated with parent PyQt instance. Notes ----- This class records the selected button and passes the change value back to variable. ''' super(RadarDisplay, self).__init__(name=name, parent=parent) self.setFocusPolicy(QtCore.Qt.ClickFocus) # Set up signal, so that DISPLAY can react to # external (or internal) changes in radar, field, # lims and tilt (expected to be Core.Variable instances) # The capital V so people remember using ".value" self.Vradar = Vradar self.Vfield = Vfield self.Vtilt = Vtilt if Vlims is None: self.Vlims = Variable(None) else: self.Vlims = Vlims if Vcmap is None: self.Vcmap = Variable(None) else: self.Vcmap = Vcmap if Vgatefilter is None: self.Vgatefilter = Variable(None) else: self.Vgatefilter = Vgatefilter self.sharedVariables = {"Vradar": self.NewRadar, "Vfield": self.NewField, "Vtilt": self.NewTilt, "Vlims": self.NewLims, "Vcmap": self.NewCmap, "Vgatefilter": self.NewGatefilter} # Connect the components self.connectAllVariables() self.plot_type = None # Set plot title and colorbar units to defaults self.title = self._get_default_title() self.units = self._get_default_units() # Set the default range rings self.RngRingList = ["None", "10 km", "20 km", "30 km", "50 km", "100 km"] self.RngRing = False # Find the PyArt colormap names # self.cm_names = [m for m in cm.datad if not m.endswith("_r")] self.cm_names = ["pyart_" + m for m in pyart.graph.cm.datad if not m.endswith("_r")] self.cm_names.sort() # Create tool dictionary self.tools = {} # Set up Default limits and cmap if Vlims is None: self._set_default_limits(strong=False) if Vcmap is None: self._set_default_cmap(strong=False) # Create a figure for output self._set_fig_ax() # Launch the GUI interface self.LaunchGUI() # Initialize radar variable self.NewRadar(None, None, True) self.show() def keyPressEvent(self, event): '''Allow tilt adjustment via the Up-Down arrow keys.''' if event.key() == QtCore.Qt.Key_Up: self.TiltSelectCmd(self.Vtilt.value + 1) elif event.key() == QtCore.Qt.Key_Down: self.TiltSelectCmd(self.Vtilt.value - 1) else: super(RadarDisplay, self).keyPressEvent(event) #################### # GUI methods # #################### def LaunchGUI(self): '''Launches a GUI interface.''' # Create layout self.layout = QtGui.QGridLayout() self.layout.setSpacing(8) # Create the widget self.central_widget = QtGui.QWidget() self.setCentralWidget(self.central_widget) self._set_figure_canvas() self.central_widget.setLayout(self.layout) # Add buttons along display for user control self.addButtons() self.setUILayout() # Set the status bar to display messages self.statusbar = self.statusBar() ################################## # User display interface methods # ################################## def addButtons(self): '''Add a series of buttons for user control over display.''' # Create the Display controls self._add_displayBoxUI() # Create the Tilt controls self._add_tiltBoxUI() # Create the Field controls self._add_fieldBoxUI() # Create the Tools controls self._add_toolsBoxUI() # Create the Informational label at top self._add_infolabel() def setUILayout(self): '''Setup the button/display UI layout.''' self.layout.addWidget(self.tiltBox, 0, 0) self.layout.addWidget(self.fieldBox, 0, 1) self.layout.addWidget(self.dispButton, 0, 2) self.layout.addWidget(self.toolsButton, 0, 3) self.layout.addWidget(self.infolabel, 0, 4) ############################# # Functionality methods # ############################# def _open_LimsDialog(self): '''Open a dialog box to change display limits.''' from .limits import limits_dialog limits, cmap, change = limits_dialog( self.Vlims.value, self.Vcmap.value, self.name) if change == 1: self.Vcmap.change(cmap) self.Vlims.change(limits) def _fillTiltBox(self): '''Fill in the Tilt Window Box with current elevation angles.''' self.tiltBox.clear() self.tiltBox.addItem("Tilt Window") # Loop through and create each tilt button elevs = self.Vradar.value.fixed_angle['data'][:] for i, ntilt in enumerate(self.rTilts): btntxt = "%2.1f deg (Tilt %d)" % (elevs[i], i+1) self.tiltBox.addItem(btntxt) def _fillFieldBox(self): '''Fill in the Field Window Box with current variable names.''' self.fieldBox.clear() self.fieldBox.addItem("Field Window") # Loop through and create each field button for field in self.fieldnames: self.fieldBox.addItem(field) def _tiltAction(self, text): '''Define action for Tilt Button selection.''' if text == "Tilt Window": self._open_tiltbuttonwindow() else: ntilt = int(text.split("(Tilt ")[1][:-1])-1 self.TiltSelectCmd(ntilt) def _fieldAction(self, text): '''Define action for Field Button selection.''' if text == "Field Window": self._open_fieldbuttonwindow() else: self.FieldSelectCmd(str(text)) def _GateFilterToggleAction(self): '''Define action for GateFilterToggle menu selection.''' if self.gatefilterToggle.isChecked(): self.gatefilterToggle.setText("GateFilter On") else: self.gatefilterToggle.setText("GateFilter Off") self._update_plot() def _title_input(self): '''Retrieve new plot title.''' val, entry = common.string_dialog_with_reset( self.title, "Plot Title", "Title:", self._get_default_title()) if entry is True: self.title = val self._update_plot() def _units_input(self): '''Retrieve new plot units.''' val, entry = common.string_dialog_with_reset( self.units, "Plot Units", "Units:", self._get_default_units()) if entry is True: self.units = val self._update_plot() def _open_tiltbuttonwindow(self): '''Open a TiltButtonWindow instance.''' from .level import LevelButtonWindow self.tiltbuttonwindow = LevelButtonWindow( self.Vtilt, plot_type=self.plot_type, Vcontainer=self.Vradar, name=self.name+" Tilt Selection", parent=self.parent) def _open_fieldbuttonwindow(self): '''Open a FieldButtonWindow instance.''' from .field import FieldButtonWindow self.fieldbuttonwindow = FieldButtonWindow( self.Vradar, self.Vfield, name=self.name+" Field Selection", parent=self.parent) def _add_RngRing_to_button(self): '''Add a menu to display range rings on plot.''' for RngRing in self.RngRingList: RingAction = self.dispRngRingmenu.addAction(RngRing) RingAction.setStatusTip("Apply Range Rings every %s" % RngRing) RingAction.triggered[()].connect( lambda RngRing=RngRing: self.RngRingSelectCmd(RngRing)) self.dispRngRing.setMenu(self.dispRngRingmenu) def _add_cmaps_to_button(self): '''Add a menu to change colormap used for plot.''' for cm_name in self.cm_names: cmapAction = self.dispCmapmenu.addAction(cm_name) cmapAction.setStatusTip("Use the %s colormap" % cm_name) cmapAction.triggered[()].connect( lambda cm_name=cm_name: self.cmapSelectCmd(cm_name)) self.dispCmap.setMenu(self.dispCmapmenu) def _add_displayBoxUI(self): '''Create the Display Options Button menu.''' self.dispButton = QtGui.QPushButton("Display Options") self.dispButton.setToolTip("Adjust display properties") self.dispButton.setFocusPolicy(QtCore.Qt.NoFocus) dispmenu = QtGui.QMenu(self) dispLimits = dispmenu.addAction("Adjust Display Limits") dispLimits.setToolTip("Set data, X, and Y range limits") self.gatefilterToggle = QtGui.QAction( 'GateFilter On', dispmenu, checkable=True, triggered=self._GateFilterToggleAction)#_update_plot) dispmenu.addAction(self.gatefilterToggle) self.gatefilterToggle.setChecked(True) dispTitle = dispmenu.addAction("Change Title") dispTitle.setToolTip("Change plot title") dispUnit = dispmenu.addAction("Change Units") dispUnit.setToolTip("Change units string") self.dispRngRing = dispmenu.addAction("Add Range Rings") self.dispRngRingmenu = QtGui.QMenu("Add Range Rings") self.dispRngRingmenu.setFocusPolicy(QtCore.Qt.NoFocus) self.dispCmap = dispmenu.addAction("Change Colormap") self.dispCmapmenu = QtGui.QMenu("Change Cmap") self.dispCmapmenu.setFocusPolicy(QtCore.Qt.NoFocus) dispQuickSave = dispmenu.addAction("Quick Save Image") dispQuickSave.setShortcut("Ctrl+D") dispQuickSave.setToolTip( "Save Image to local directory with default name") dispSaveFile = dispmenu.addAction("Save Image") dispSaveFile.setShortcut("Ctrl+S") dispSaveFile.setStatusTip("Save Image using dialog") dispLimits.triggered[()].connect(self._open_LimsDialog) dispTitle.triggered[()].connect(self._title_input) dispUnit.triggered[()].connect(self._units_input) dispQuickSave.triggered[()].connect(self._quick_savefile) dispSaveFile.triggered[()].connect(self._savefile) self._add_RngRing_to_button() self._add_cmaps_to_button() self.dispButton.setMenu(dispmenu) def _add_tiltBoxUI(self): '''Create the Tilt Selection ComboBox.''' self.tiltBox = QtGui.QComboBox() self.tiltBox.setFocusPolicy(QtCore.Qt.NoFocus) self.tiltBox.setToolTip("Select tilt elevation angle to display.\n" "'Tilt Window' will launch popup.\n" "Up/Down arrow keys Increase/Decrease tilt.") self.tiltBox.activated[str].connect(self._tiltAction) def _add_fieldBoxUI(self): '''Create the Field Selection ComboBox.''' self.fieldBox = QtGui.QComboBox() self.fieldBox.setFocusPolicy(QtCore.Qt.NoFocus) self.fieldBox.setToolTip("Select variable/field in data file.\n" "'Field Window' will launch popup.\n") self.fieldBox.activated[str].connect(self._fieldAction) def _add_toolsBoxUI(self): '''Create the Tools Button menu.''' self.toolsButton = QtGui.QPushButton("Toolbox") self.toolsButton.setFocusPolicy(QtCore.Qt.NoFocus) self.toolsButton.setToolTip("Choose a tool to apply") toolmenu = QtGui.QMenu(self) toolZoomPan = toolmenu.addAction("Zoom/Pan") toolValueClick = toolmenu.addAction("Click for Value") toolSelectRegion = toolmenu.addAction("Select a Region of Interest") toolReset = toolmenu.addAction("Reset Tools") toolDefault = toolmenu.addAction("Reset File Defaults") toolZoomPan.triggered[()].connect(self.toolZoomPanCmd) toolValueClick.triggered[()].connect(self.toolValueClickCmd) toolSelectRegion.triggered[()].connect(self.toolSelectRegionCmd) toolReset.triggered[()].connect(self.toolResetCmd) toolDefault.triggered[()].connect(self.toolDefaultCmd) self.toolsButton.setMenu(toolmenu) def _add_infolabel(self): '''Create an information label about the display''' self.infolabel = QtGui.QLabel("Radar: \n" "Field: \n" "Tilt: ", self) self.infolabel.setStyleSheet('color: red; font: italic 10px') self.infolabel.setToolTip("Filename not loaded") def _update_infolabel(self): if self.Vradar.value is None: return self.infolabel.setText("Radar: %s\n" "Field: %s\n" "Tilt: %d" % ( self.Vradar.value.metadata[ 'instrument_name'], self.Vfield.value, self.Vtilt.value+1)) if hasattr(self.Vradar.value, 'filename'): self.infolabel.setToolTip(self.Vradar.value.filename) ######################## # Selectionion methods # ######################## def NewRadar(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vradar <artview.core.core.Variable>`. This will: * Update fields and tilts lists and MenuBoxes * Check radar scan type and reset limits if needed * Reset units and title * If strong update: update plot ''' # test for None if self.Vradar.value is None: self.fieldBox.clear() self.tiltBox.clear() return # Get the tilt angles self.rTilts = self.Vradar.value.sweep_number['data'][:] # Get field names self.fieldnames = self.Vradar.value.fields.keys() # Check the file type and initialize limts self._check_file_type() # Update field and tilt MenuBox self._fillTiltBox() self._fillFieldBox() self.units = self._get_default_units() self.title = self._get_default_title() if strong: self._update_plot() self._update_infolabel() def NewField(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vfield <artview.core.core.Variable>`. This will: * Reset colormap * Reset units * Update fields MenuBox * If strong update: update plot ''' self._set_default_cmap(strong=False) self.units = self._get_default_units() self.title = self._get_default_title() idx = self.fieldBox.findText(value) self.fieldBox.setCurrentIndex(idx) if strong: self._update_plot() self._update_infolabel() def NewLims(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vlims <artview.core.core.Variable>`. This will: * If strong update: update axes ''' if strong: self._update_axes() def NewCmap(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vcmap <artview.core.core.Variable>`. This will: * If strong update: update plot ''' if strong: self._update_plot() def NewGatefilter(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vgatefilter <artview.core.core.Variable>`. This will: * If strong update: update plot ''' if strong: self._update_plot() def NewTilt(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vtilt <artview.core.core.Variable>`. This will: * Update tilt MenuBox * If strong update: update plot ''' # +1 since the first one is "Tilt Window" self.tiltBox.setCurrentIndex(value+1) if strong: self._update_plot() self._update_infolabel() def TiltSelectCmd(self, ntilt): ''' Captures tilt selection and update tilt :py:class:`~artview.core.core.Variable`. ''' if ntilt < 0: ntilt = len(self.rTilts)-1 elif ntilt >= len(self.rTilts): ntilt = 0 self.Vtilt.change(ntilt) def FieldSelectCmd(self, name): ''' Captures field selection and update field :py:class:`~artview.core.core.Variable`. ''' self.Vfield.change(name) def RngRingSelectCmd(self, ringSel): ''' Captures Range Ring selection and redraws the field with range rings. ''' if ringSel is "None": self.RngRing = False else: self.RngRing = True # Find the unambigous range of the radar try: unrng = int(self.Vradar.value.instrument_parameters[ 'unambiguous_range']['data'][0]/1000) except: unrng = int(self.Vlims.value['xmax']) # Set the step if ringSel == '10 km': ringdel = 10 if ringSel == '20 km': ringdel = 20 if ringSel == '30 km': ringdel = 30 if ringSel == '50 km': ringdel = 50 if ringSel == '100 km': ringdel = 100 # Calculate an array of range rings self.RNG_RINGS = range(ringdel, unrng, ringdel) self._update_plot() def cmapSelectCmd(self, cm_name): '''Captures colormap selection and redraws.''' CMAP = cm_name self.Vcmap.value['cmap'] = cm_name self.Vcmap.update() def toolZoomPanCmd(self): '''Creates and connects to a Zoom/Pan instance.''' from .tools import ZoomPan scale = 1.1 self.tools['zoompan'] = ZoomPan( self.Vlims, self.ax, base_scale=scale, parent=self.parent) self.tools['zoompan'].connect() def toolValueClickCmd(self): '''Creates and connects to Point-and-click value retrieval''' from .tools import ValueClick self.tools['valueclick'] = ValueClick( self.Vradar, self.Vtilt, self.Vfield, self.units, self.ax, self.statusbar, parent=self.parent) self.tools['valueclick'].connect() def toolSelectRegionCmd(self): '''Creates and connects to Region of Interest instance''' from .select_region import SelectRegion self.tools['select_region'] = SelectRegion( self, name=self.name + " SelectRegion", parent=self) def toolResetCmd(self): '''Reset tools via disconnect.''' from . import tools self.tools = tools.reset_tools(self.tools) def toolDefaultCmd(self): '''Restore the Display defaults.''' from . import tools self.tools, limits, cmap = tools.restore_default_display( self.tools, self.Vfield.value, self.plot_type) self.Vcmap.change(cmap) self.Vlims.change(limits) def getPathInteriorValues(self, path): ''' Return the bins values path. Parameters ---------- path : Matplotlib Path instance Returns ------- points: Points Points object containing all bins of the current radar and tilt inside path. Axes : 'x_disp', 'y_disp', 'ray_index', 'range_index', 'azimuth', 'range'. Fields: just current field Notes ----- If Vradar.value is None, returns None ''' from .tools import interior_radar radar = self.Vradar.value if radar is None: return None xy, idx = interior_radar(path, radar, self.Vtilt.value) xaxis = {'data': xy[:, 0] * 1000., 'long_name': 'X-coordinate in Cartesian system', 'axis': 'X', 'units': 'm'} yaxis = {'data': xy[:, 1] * 1000., 'long_name': 'Y-coordinate in Cartesian system', 'axis': 'Y', 'units': 'm'} azi = radar.azimuth.copy() azi['data'] = radar.azimuth['data'][idx[:, 0]] rng = radar.range.copy() rng['data'] = radar.range['data'][idx[:, 1]] field = radar.fields[self.Vfield.value].copy() field['data'] = radar.fields[self.Vfield.value]['data'][ idx[:, 0], idx[:, 1]] ray_idx = {'data': idx[:, 0], 'long_name': 'index in ray dimension'} rng_idx = {'data': idx[:, 1], 'long_name': 'index in range dimension'} axes = {'x_disp': xaxis, 'y_disp': yaxis, 'ray_index': ray_idx, 'range_index': rng_idx, 'azimuth': azi, 'range': rng} fields = {self.Vfield.value: field} points = Points(fields, axes, radar.metadata.copy(), xy.shape[0]) return points #################### # Plotting methods # #################### def _set_fig_ax(self): '''Set the figure and axis to plot.''' self.XSIZE = 8 self.YSIZE = 8 self.fig = Figure(figsize=(self.XSIZE, self.YSIZE)) self.ax = self.fig.add_axes([0.2, 0.2, 0.7, 0.7]) self.cax = self.fig.add_axes([0.2, 0.10, 0.7, 0.02]) # self._update_axes() def _update_fig_ax(self): '''Set the figure and axis to plot.''' if self.plot_type in ("radarAirborne", "radarRhi"): self.YSIZE = 5 else: self.YSIZE = 8 xwidth = 0.7 yheight = 0.7 # * float(self.YSIZE) / float(self.XSIZE) self.ax.set_position([0.2, 0.55-0.5*yheight, xwidth, yheight]) self.cax.set_position([0.2, 0.10, xwidth, 0.02]) self._update_axes() def _set_figure_canvas(self): '''Set the figure canvas to draw in window area.''' self.canvas = FigureCanvasQTAgg(self.fig) # Add the widget to the canvas self.layout.addWidget(self.canvas, 1, 0, 7, 6) def _update_plot(self): '''Draw/Redraw the plot.''' if self.Vradar.value is None: return # Create the plot with PyArt RadarDisplay self.ax.cla() # Clear the plot axes self.cax.cla() # Clear the colorbar axes if self.Vfield.value not in self.Vradar.value.fields.keys(): self.canvas.draw() self.statusbar.setStyleSheet("QStatusBar{padding-left:8px;" + "background:rgba(255,0,0,255);" + "color:black;font-weight:bold;}") self.statusbar.showMessage("Field not Found in Radar", msecs=5000) return else: self.statusbar.setStyleSheet("QStatusBar{padding-left:8px;" + "background:rgba(0,0,0,0);" + "color:black;font-weight:bold;}") self.statusbar.clearMessage() title = self.title limits = self.Vlims.value cmap = self.Vcmap.value if self.gatefilterToggle.isChecked(): gatefilter = self.Vgatefilter.value else: gatefilter = None if self.plot_type == "radarAirborne": self.display = pyart.graph.RadarDisplay_Airborne(self.Vradar.value) self.plot = self.display.plot_sweep_grid( self.Vfield.value, vmin=cmap['vmin'], vmax=cmap['vmax'], colorbar_flag=False, cmap=cmap['cmap'], mask_outside=True, gatefilter=gatefilter, ax=self.ax, fig=self.fig, title=title) self.display.plot_grid_lines() elif self.plot_type == "radarPpi": self.display = pyart.graph.RadarDisplay(self.Vradar.value) # Create Plot self.plot = self.display.plot_ppi( self.Vfield.value, self.Vtilt.value, vmin=cmap['vmin'], vmax=cmap['vmax'], colorbar_flag=False, cmap=cmap['cmap'], mask_outside=True, gatefilter=gatefilter, ax=self.ax, fig=self.fig, title=title) # Add range rings if self.RngRing: self.display.plot_range_rings(self.RNG_RINGS, ax=self.ax) # Add radar location self.display.plot_cross_hair(5., ax=self.ax) elif self.plot_type == "radarRhi": self.display = pyart.graph.RadarDisplay(self.Vradar.value) # Create Plot self.plot = self.display.plot_rhi( self.Vfield.value, self.Vtilt.value, vmin=cmap['vmin'], vmax=cmap['vmax'], colorbar_flag=False, cmap=cmap['cmap'], mask_outside=True, gatefilter=gatefilter, ax=self.ax, fig=self.fig, title=title) # Add range rings if self.RngRing: self.display.plot_range_rings(self.RNG_RINGS, ax=self.ax) self._update_axes() norm = mlabNormalize(vmin=cmap['vmin'], vmax=cmap['vmax']) self.cbar = mlabColorbarBase(self.cax, cmap=cmap['cmap'], norm=norm, orientation='horizontal') self.cbar.set_label(self.units) # print "Plotting %s field, Tilt %d in %s" % ( # self.Vfield.value, self.Vtilt.value+1, self.name) self.canvas.draw() def _update_axes(self): '''Change the Plot Axes.''' limits = self.Vlims.value self.ax.set_xlim(limits['xmin'], limits['xmax']) self.ax.set_ylim(limits['ymin'], limits['ymax']) self.ax.figure.canvas.draw() ######################### # Check methods # ######################### def _check_file_type(self): '''Check file to see if the file is airborne or rhi.''' radar = self.Vradar.value old_plot_type = self.plot_type if radar.scan_type != 'rhi': self.plot_type = "radarPpi" else: if 'platform_type' in radar.metadata: if (radar.metadata['platform_type'] == 'aircraft_tail' or radar.metadata['platform_type'] == 'aircraft'): self.plot_type = "radarAirborne" else: self.plot_type = "radarRhi" else: self.plot_type = "radarRhi" if self.plot_type != old_plot_type: print("Changed Scan types, reinitializing") self._set_default_limits() self._update_fig_ax() def _set_default_limits(self, strong=True): ''' Set limits to pre-defined default.''' from .limits import _default_limits limits, cmap = _default_limits( self.Vfield.value, self.plot_type) self.Vlims.change(limits, strong) def _set_default_cmap(self, strong=True): ''' Set colormap to pre-defined default.''' cmap = pyart.config.get_field_colormap(self.Vfield.value) d = {} d['cmap'] = cmap lims = pyart.config.get_field_limits(self.Vfield.value, self.Vradar.value, self.Vtilt.value) if lims != (None, None): d['vmin'] = lims[0] d['vmax'] = lims[1] else: d['vmin'] = -10 d['vmax'] = 65 self.Vcmap.change(d, strong) def _get_default_title(self): '''Get default title from pyart.''' if (self.Vradar.value is None or self.Vfield.value not in self.Vradar.value.fields): return '' return pyart.graph.common.generate_title(self.Vradar.value, self.Vfield.value, self.Vtilt.value) def _get_default_units(self): '''Get default units for current radar and field.''' if self.Vradar.value is not None: try: return self.Vradar.value.fields[self.Vfield.value]['units'] except: return '' else: return '' ######################## # Image save methods # ######################## def _quick_savefile(self, PTYPE=IMAGE_EXT): '''Save the current display via PyArt interface.''' imagename = self.display.generate_filename( self.Vfield.value, self.Vtilt.value, ext=IMAGE_EXT) self.canvas.print_figure(os.path.join(os.getcwd(), imagename), dpi=DPI) self.statusbar.showMessage( 'Saved to %s' % os.path.join(os.getcwd(), imagename)) def _savefile(self, PTYPE=IMAGE_EXT): '''Save the current display using PyQt dialog interface.''' PBNAME = self.display.generate_filename( self.Vfield.value, self.Vtilt.value, ext=IMAGE_EXT) file_choices = "PNG (*.png)|*.png" path = unicode(QtGui.QFileDialog.getSaveFileName( self, 'Save file', PBNAME, file_choices)) if path: self.canvas.print_figure(path, dpi=DPI) self.statusbar.showMessage('Saved to %s' % path) ######################## # get methods # ######################## def getPlotAxis(self): '''Get :py:class:`matplotlib.axes.Axes` instance of main plot.''' return self.ax def getStatusBar(self): '''Get :py:class:`PyQt4.QtGui.QStatusBar` instance.''' return self.statusbar def getField(self): '''Get current field.''' return self.Vfield.value def getUnits(self): '''Get current units.''' return self.units def getRadar(self): ''' get current radar ''' return self.Vradar.value
def plot_data_products(): # Define all bounds first s.t. we can draw the context boxes on the # dechirped image. # The raw bounds are for the full image raw_min_sample = 150 raw_max_sample = 2300 raw_bounds = [0, 9075, raw_min_sample, raw_max_sample] # The filtering needs to zoom in on the surface filter_bounds = [1700, 5900, 425, 650] # For the SNR improvements of incoherent stacking, look at the layers that just pop out. layer_bounds = [7400, 8580, 200, 1750] # For filtered, clim = [110000, 195000] and for stacked, clim=[140000, 225000] # Zooming in on the crevasses shows speckle nicely incoherent_bounds = [2880, 4900, 850, 1700] #The clim for this is best is the coherent is [150000, 234000] and incoherent is [160000, 234000] # Appropriate color limits depend on the processing used raw_clim = [25000, 90000] dechirped_clim = [115000, 200000] filtered_clim = [115000, 200000] # First, generate the raw figure that requires raw data + dechirped data # over the full transect. raw_shape = (50, 17) #raw_shape = (10, 17./5) # This is ugly ... # fig_raw = Figure(raw_shape, dpi=150) # canvas_raw = FigureCanvas(fig_raw) # ax_raw = fig_raw.add_axes([0, 0, 1, 1]) # ax_raw.axis('off') # plot_radar(ax_raw, 'TOT_raw', 3200, raw_bounds, raw_clim) # canvas_raw.print_figure('../FinalReport/figures/TOT_raw_full.jpg') multiple_bounds = [3050, 5325, 325, 2675] fig_multiples = Figure(raw_shape, dpi=150) canvas_multiples = FigureCanvas(fig_multiples) ax_multiples = fig_multiples.add_axes([0, 0, 1, 1]) ax_multiples.axis('off') plot_radar(ax_multiples, 'TOT_no_blanking', 3200, multiple_bounds, clim=[140000, 230000]) ax_multiples.text(3950, 900, 'surface multiple', color='red', fontsize=70, horizontalalignment='left', verticalalignment='bottom') ax_multiples.text(3950, 2380, 'basal multiple', color='red', fontsize=70, horizontalalignment='left', verticalalignment='top') canvas_multiples.print_figure('../FinalReport/figures/TOT_multiples.jpg')
class FlowEstimatorDialog(QtGui.QDialog, FORM_CLASS): def __init__(self, iface, parent=None): """Constructor.""" super(FlowEstimatorDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect #QDialog.__init__(self, None, Qt.WindowStaysOnTopHint) self.iface = iface self.setupUi(self) self.btnOk = self.buttonBox.button(QtGui.QDialogButtonBox.Ok) self.btnOk.setText("Save Data") self.btnClose = self.buttonBox.button(QtGui.QDialogButtonBox.Close) self.btnBrowse.clicked.connect(self.writeDirName) self.btnLoadTXT.clicked.connect(self.loadTxt) self.btnSampleLine.setEnabled(False) self.btnSampleSlope.setEnabled(False) self.calcType = 'Trap' # add matplotlib figure to dialog self.figure = Figure() self.axes = self.figure.add_subplot(111) self.figure.subplots_adjust(left=.1, bottom=0.15, right=.78, top=.9, wspace=None, hspace=.2) self.mplCanvas = FigureCanvas(self.figure) #self.widgetPlotToolbar = NavigationToolbar(self.mplCanvas, self.widgetPlot) #lstActions = self.widgetPlotToolbar.actions() #self.widgetPlotToolbar.removeAction(lstActions[7]) self.vLayout.addWidget(self.mplCanvas) self.vLayout.minimumSize() #self.vLayout.addWidget(self.widgetPlotToolbar) self.figure.patch.set_visible(False) # and configure matplotlib params # rcParams["font.serif"] = "Verdana, Arial, Liberation Serif" # rcParams["font.sans-serif"] = "Tahoma, Arial, Liberation Sans" # rcParams["font.cursive"] = "Courier New, Arial, Liberation Sans" # rcParams["font.fantasy"] = "Comic Sans MS, Arial, Liberation Sans" # rcParams["font.monospace"] = "Courier New, Liberation Mono" # #print self.cbDEM.changeEvent self.depth.valueChanged.connect(self.run) self.botWidth.valueChanged.connect(self.run) self.leftSS.valueChanged.connect(self.run) self.rightSS.valueChanged.connect(self.run) self.n.valueChanged.connect(self.run) self.slope.valueChanged.connect(self.run) self.cbWSE.valueChanged.connect(self.run) self.ft.clicked.connect(self.run) self.m.clicked.connect(self.run) self.cbUDwse.valueChanged.connect(self.run) self.manageGui() self.btnSampleLine.clicked.connect(self.sampleLine) self.btnSampleSlope.clicked.connect(self.sampleSlope) def manageGui(self): print 'manageGui' self.cbDEM.clear() if utils.getRasterLayerNames(): self.cbDEM.addItems(utils.getRasterLayerNames()) self.btnSampleLine.setEnabled(True) self.btnSampleSlope.setEnabled(True) self.run() # def refreshPlot(self): # self.axes.clear() def plotter(self): R, area, topWidth, Q, v, depth, xGround, yGround, yGround0, xWater, yWater, yWater0 = self.args self.axes.clear() formatter = ScalarFormatter(useOffset=False) self.axes.yaxis.set_major_formatter(formatter) self.axes.plot(xGround, yGround, 'k') #self.axes.fill_between(xGround, yGround, yGround0, where=yGround>yGround0, facecolor='0.9', interpolate=True) if Q != 0: self.axes.plot(xWater, yWater, 'blue') self.axes.fill_between(xWater, yWater, yWater0, where=yWater >= yWater0, facecolor='blue', interpolate=True, alpha=0.1) self.outText = 'R: {0:.2f} {5}\nArea: {1:,.2f} {5}$^2$\nTop Width: {2:.2f} {5}\nDepth: {6:,.2f} {5}\nQ: {3:,.2f} {5}$^3$/s\nVelocity {4:,.2f} {5}/s'.format( R, area, topWidth, Q, v, self.units, depth) self.axes.set_xlabel('Station, ' + self.units) self.axes.set_ylabel('Elevation, ' + self.units) self.axes.set_title('Cross Section') #self.axes.set_ylim(bottom=0) #self.axes.show() #print self.outText self.refreshPlotText() def refreshPlotText(self): self.axes.annotate(self.outText, xy=(.8, .35), xycoords='figure fraction') #at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") #self.axes.add_artist(at) self.mplCanvas.draw() def run(self): if self.ft.isChecked(): self.units = 'ft' else: self.units = 'm' if self.tabWidget.currentIndex() == 0: print 'calc trap channel' self.calcType = 'Trap' self.args = flowEstimator(self.depth.value(), self.n.value(), self.slope.value(), widthBottom=self.botWidth.value(), rightSS=self.rightSS.value(), leftSS=self.leftSS.value(), units=self.units) self.plotter() elif self.tabWidget.currentIndex() == 1: try: self.calcType = 'DEM' # print self.cbWSE.value(), self.n.value(), self.slope.value(), self.staElev, self.units self.args = flowEstimator(self.cbWSE.value(), self.n.value(), self.slope.value(), staElev=self.staElev, units=self.units) self.plotter() except: self.axes.clear() else: try: self.calcType = 'UD' self.args = flowEstimator(self.cbUDwse.value(), self.n.value(), self.slope.value(), staElev=self.staElev, units=self.units) self.plotter() except: self.axes.clear() def sampleLine(self): try: self.deactivate() except: pass self.btnSampleLine.setEnabled(False) self.sampleBtnCode = 'sampleLine' self.rubberBand() def sampleSlope(self): try: self.deactivate() except: pass self.btnSampleSlope.setEnabled(False) self.sampleBtnCode = 'sampleSlope' self.rubberBand() #============================================================================== # START rubberband and related functions from # https://github.com/etiennesky/profiletool #============================================================================== def rubberBand(self): print 'rubberband ' self.canvas = self.iface.mapCanvas() #Init classe variables if self.sampleBtnCode == 'sampleLine': self.tool = ProfiletoolMapTool( self.canvas, self.btnSampleLine) #the mouselistener else: self.tool = ProfiletoolMapTool( self.canvas, self.btnSampleSlope) #the mouselistener self.pointstoDraw = None #Polyline in mapcanvas CRS analysed self.dblclktemp = False #enable disctinction between leftclick and doubleclick self.selectionmethod = 0 #The selection method defined in option self.saveTool = self.canvas.mapTool( ) #Save the standard mapttool for restoring it at the end self.textquit0 = "Click for polyline and double click to end (right click to cancel then quit)" self.textquit1 = "Select the polyline in a vector layer (Right click to quit)" #Listeners of mouse self.connectTool() #init the mouse listener comportement and save the classic to restore it on quit self.canvas.setMapTool(self.tool) #init the temp layer where the polyline is draw self.polygon = False self.rubberband = QgsRubberBand(self.canvas, self.polygon) self.rubberband.setWidth(2) if self.sampleBtnCode == 'sampleLine': color = Qt.red else: color = Qt.blue self.rubberband.setColor(QColor(color)) #init the table where is saved the poyline self.pointstoDraw = [] self.pointstoCal = [] self.lastClicked = [[-9999999999.9, 9999999999.9]] # The last valid line we drew to create a free-hand profile self.lastFreeHandPoints = [] #Help about what doing if self.selectionmethod == 0: self.iface.mainWindow().statusBar().showMessage(self.textquit0) elif self.selectionmethod == 1: self.iface.mainWindow().statusBar().showMessage(self.textquit1) #************************************* Mouse listener actions *********************************************** def moved(self, position): #draw the polyline on the temp layer (rubberband) #print 'moved' if self.selectionmethod == 0: if len(self.pointstoDraw) > 0: #Get mouse coords mapPos = self.canvas.getCoordinateTransform().toMapCoordinates( position["x"], position["y"]) #Draw on temp layer if QGis.QGIS_VERSION_INT >= 10900: self.rubberband.reset(QGis.Line) else: self.rubberband.reset(self.polygon) for i in range(0, len(self.pointstoDraw)): self.rubberband.addPoint( QgsPoint(self.pointstoDraw[i][0], self.pointstoDraw[i][1])) self.rubberband.addPoint(QgsPoint(mapPos.x(), mapPos.y())) # if self.selectionmethod == 1: # return def rightClicked(self, position): #used to quit the current action print 'rightclicked' if self.selectionmethod == 0: if len(self.pointstoDraw) > 0: self.pointstoDraw = [] self.pointstoCal = [] self.rubberband.reset(self.polygon) else: self.cleaning() def leftClicked(self, position): #Add point to analyse print 'leftclicked' mapPos = self.canvas.getCoordinateTransform().toMapCoordinates( position["x"], position["y"]) newPoints = [[mapPos.x(), mapPos.y()]] if self.selectionmethod == 0: if newPoints == self.dblclktemp: self.dblclktemp = None return else: if len(self.pointstoDraw) == 0: self.rubberband.reset(self.polygon) self.pointstoDraw += newPoints def doubleClicked(self, position): print 'doubleclicked' if self.selectionmethod == 0: #Validation of line mapPos = self.canvas.getCoordinateTransform().toMapCoordinates( position["x"], position["y"]) newPoints = [[mapPos.x(), mapPos.y()]] self.pointstoDraw += newPoints #launch analyses self.iface.mainWindow().statusBar().showMessage( str(self.pointstoDraw)) if self.sampleBtnCode == 'sampleLine': self.staElev, error = self.doRubberbandProfile() if error: self.deactivate() else: self.doIrregularProfileFlowEstimator() self.btnSampleLine.setEnabled(True) self.deactivate() else: staElev, error = self.doRubberbandProfile() if error: self.deactivate() else: self.doRubberbandSlopeEstimator(staElev) self.btnSampleSlope.setEnabled(True) self.deactivate() #Reset self.lastFreeHandPoints = self.pointstoDraw self.pointstoDraw = [] #temp point to distinct leftclick and dbleclick self.dblclktemp = newPoints self.iface.mainWindow().statusBar().showMessage(self.textquit0) self.iface.mainWindow().activateWindow() return ###*********************************************** def connectTool(self): print 'connecting' QObject.connect(self.tool, SIGNAL("moved"), self.moved) # self.tool.moved.connect(self.moved) QObject.connect(self.tool, SIGNAL("rightClicked"), self.rightClicked) QObject.connect(self.tool, SIGNAL("leftClicked"), self.leftClicked) QObject.connect(self.tool, SIGNAL("doubleClicked"), self.doubleClicked) QObject.connect(self.tool, SIGNAL("deactivate"), self.deactivate) def deactivate(self): #enable clean exit of the plugin self.cleaning() QObject.disconnect(self.tool, SIGNAL("moved"), self.moved) QObject.disconnect(self.tool, SIGNAL("leftClicked"), self.leftClicked) QObject.disconnect(self.tool, SIGNAL("rightClicked"), self.rightClicked) QObject.disconnect(self.tool, SIGNAL("doubleClicked"), self.doubleClicked) # self.rubberband.reset(self.polygon) # self.iface.mainWindow().statusBar().showMessage("") # self.depth.setEnabled(True) # self.botWidth.setEnabled(True) # self.leftSS.setEnabled(True) # self.rightSS.setEnabled(True) # self.n.setEnabled(True) # self.slope.setEnabled(True) # self.cbWSE.setEnabled(True) # self.ft.setEnabled(True) # self.m.setEnabled(True) # self.cbDEM.setEnabled(True) def cleaning(self): #used on right click self.canvas.unsetMapTool(self.tool) self.canvas.setMapTool(self.saveTool) self.rubberband.reset(self.polygon) #self.rubberband.reset(self.polygon) self.iface.mainWindow().statusBar().showMessage("") #============================================================================== # END rubberband and related functions from # https://github.com/etiennesky/profiletool #============================================================================== def doRubberbandProfile(self): layerString = self.cbDEM.currentText() layer = utils.getRasterLayerByName(' '.join( layerString.split(' ')[:-1])) if layer.isValid(): self.xRes = layer.rasterUnitsPerPixelX() line = LineString(self.pointstoDraw[:-1]) xyzdList = utils.elevationSampler(line, self.xRes, layer) sta = xyzdList[-1] elev = xyzdList[-2] staElev = np.array(zip(sta, elev)) try: np.isnan(np.sum(staElev[:, 1])) return [staElev, None] except: QMessageBox.warning(self, 'Error', 'Sampled line not within bounds of DEM') #self.cleaning() return [staElev, 'error'] def doIrregularProfileFlowEstimator(self): thalweig = self.staElev[np.where( self.staElev[:, 1] == np.min(self.staElev[:, 1]))] thalweigX = thalweig[:, 0][0] minElev = thalweig[:, 1][0] + .01 try: lbMaxEl = self.staElev[np.where( self.staElev[:, 0] > thalweigX)][:, 1].max() except: QMessageBox.warning(self, 'Error', 'Channel not found') try: self.deactivate() except: pass return try: rbMaxEl = self.staElev[np.where( self.staElev[:, 0] < thalweigX)][:, 1].max() except: QMessageBox.warning(self, 'Error', 'Channel not found') try: self.deactivate() except: pass return maxElev = np.array([lbMaxEl, rbMaxEl]).min() - .01 WSE = maxElev WSE = (self.staElev[:, 1].max() - self.staElev[:, 1].min()) / 2. + self.staElev[:, 1].min() if self.tabWidget.currentIndex() == 1: self.cbWSE.setValue(WSE) self.cbWSE.setMinimum(minElev) self.cbWSE.setMaximum(maxElev) elif self.tabWidget.currentIndex() == 2: self.cbUDwse.setValue(WSE) self.cbUDwse.setMinimum(minElev) self.cbUDwse.setMaximum(maxElev) else: return self.run() def doRubberbandSlopeEstimator(self, staElev): slope = -(staElev[:, 1][-1] - staElev[:, 1][0]) / staElev[:, 0][-1] print slope self.axes.clear() formatter = ScalarFormatter(useOffset=False) self.axes.yaxis.set_major_formatter(formatter) self.axes.plot(staElev[:, 0], staElev[:, 1], 'k', label='Sampled DEM') x = np.array([staElev[0, 0], staElev[-1, 0]]) y = np.array([staElev[0, 1], staElev[-1, 1]]) self.axes.plot(x, y, label='Slope') self.axes.set_xlabel('Station, ' + self.units) self.axes.set_ylabel('Elevation, ' + self.units) self.axes.set_title('DEM Derived Slope = ' + slope.astype('|S8')) self.axes.legend() self.mplCanvas.draw() if slope <= 0: QMessageBox.warning( self, 'Error', 'Negative or zero slope\nPlease check sampled area\n\nWater flows downhill you know!' ) print 'error: negative slope' else: reply = QMessageBox.question( self, 'Message', 'DEM Derived Slope is {}\nWould you like to use this value?'. format(slope.astype('|S8')), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.Yes: self.slope.setValue(slope) else: pass def writeDirName(self): self.outputDir.clear() self.dirName = QFileDialog.getExistingDirectory( self, 'Select Output Directory') self.outputDir.setText(self.dirName) def loadTxt(self): filePath = QFileDialog.getOpenFileName( self, 'Select tab or space delimited text file containing station and elevation data' ) print filePath try: self.staElev = np.loadtxt(filePath) self.inputFile.setText(filePath) self.calcType = 'UD' self.doIrregularProfileFlowEstimator() except: QMessageBox.warning( self, 'Error', 'Please check that the text file is space or tab delimited and does not contain header information' ) def accept(self): # assign results to numpy array for quick csv dump outPath = self.outputDir.text() home = os.path.expanduser("~") if outPath == '': outPath = os.path.join(home, 'Desktop', 'QGIS2FlowEstimatorFiles') self.outputDir.setText(outPath) if not os.path.exists(outPath): os.makedirs(outPath) os.chdir(outPath) fileName = 'FlowEstimatorResults.txt' outFile = open(fileName, 'w') outHeader = '*' * 20 + '\nFlow Estimator - A QGIS plugin\nEstimates uniform, steady flow in a channel using Mannings equation\n' + '*' * 20 if self.calcType == 'DEM': proj4 = utils.getRasterLayerByName( self.cbDEM.currentText().split(' EPSG')[0]).crs().toProj4() outHeader += '\n' * 5 + 'Type:\tCross Section from DEM\nUnits:\t{0}\nDEM Layer:\t{1}\nProjection (Proj4 format):\t{2}\nChannel Slope:\t{3:.06f}\nMannings n:\t{4:.02f}\n\n\n\nstation\televation\n'.format( self.units, self.cbDEM.currentText(), proj4, self.slope.value(), self.n.value()) outFile.write(outHeader) np.savetxt(outFile, self.staElev, fmt='%.3f', delimiter='\t') wseMax = self.cbWSE.value() wseMin = self.cbWSE.minimum() elif self.calcType == 'UD': proj4 = utils.getRasterLayerByName( self.cbDEM.currentText().split(' EPSG')[0]).crs().toProj4() outHeader += '\n' * 5 + 'Type:\tUser Defined Cross Section\nUnits:\t{0}\nChannel Slope:\t{1:.06f}\nMannings n:\t{2:.02f}\n\n\n\nstation\televation\n'.format( self.units, self.slope.value(), self.n.value()) outFile.write(outHeader) np.savetxt(outFile, self.staElev, fmt='%.3f', delimiter='\t') wseMax = self.cbUDwse.value() wseMin = self.cbUDwse.minimum() else: outHeader += '\n' * 5 + 'Type:\tTrapizodal Channel\nUnits:\t{0}\nChannel Slope:\t{1:.06f}\nMannings n:\t{2:.02f}\nBottom Width:\t{3:.02f}\nRight Side Slope:\t{4:.02f}\nLeft Side Slope:\t{5:.02f}\n'.format( self.units, self.slope.value(), self.n.value(), self.botWidth.value(), self.rightSS.value(), self.leftSS.value()) outFile.write(outHeader) wseMax = self.depth.value() wseMin = 0.0 self.mplCanvas.print_figure('FlowEstimatorResultsXSFigure') outHeader = '\n\n\n\n\n\n\nwater surface elevation\tflow\tvelocity\tR\tarea\ttop width\tdepth\n' outFile.write(outHeader) ###do loop here step = 0.1 wseList = [] qList = [] for wse in utils.frange(wseMin, wseMax, step): if self.calcType == 'DEM' or self.calcType == 'UD': args = flowEstimator(wse, self.n.value(), self.slope.value(), staElev=self.staElev, units=self.units) else: args = flowEstimator(wse, self.n.value(), self.slope.value(), widthBottom=self.botWidth.value(), rightSS=self.rightSS.value(), leftSS=self.leftSS.value(), units=self.units) R, area, topWidth, Q, v, depth, xGround, yGround, yGround0, xWater, yWater, yWater0 = args data = '{0}\t{1:.02f}\t{2:.02f}\t{3:.02f}\t{4:.02f}\t{5:.02f}\t{6:.02f}\n'.format( wse, Q, v, R, area, topWidth, depth) outFile.write(data) wseList.append(wse) qList.append(Q) self.axes.clear() formatter = ScalarFormatter(useOffset=False) self.axes.yaxis.set_major_formatter(formatter) self.axes.plot(qList, wseList, 'k', label='Rating Curve') self.axes.set_ylabel('Water Surface Elevation, ' + self.units) self.axes.set_xlabel('Discharge, {0}$^3$/s'.format(self.units)) self.axes.set_title('Rating Curve') self.axes.grid() self.mplCanvas.draw() self.mplCanvas.print_figure('FlowEstimatorRatingCurve') outFile.close() self.iface.messageBar().pushMessage( "Flow Estimator", 'Output files located here {}. Please delete when finished'. format(outPath), duration=30)
class DrainageChannelBuilderDialog(QtGui.QDialog, FORM_CLASS): def __init__(self, iface, parent=None): """Constructor.""" super(DrainageChannelBuilderDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.iface = iface self.setupUi(self) self.resWarning = False self.btnOk = self.buttonBox.button(QtGui.QDialogButtonBox.Ok) self.btnOk.setText(self.tr("Run 2D")) self.btnClose = self.buttonBox.button(QtGui.QDialogButtonBox.Close) self.cbDEM.currentIndexChanged.connect(self.updateRasterRes) self.cbDEM.currentIndexChanged.connect(self.checkLayerExtents) self.cbCL.currentIndexChanged.connect(self.checkLayerExtents) self.spinElevStart.valueChanged.connect(self.calcDepth) self.spinElevEnd.valueChanged.connect(self.calcDepth) self.spinRightSideSlope.valueChanged.connect(self.updateMaxBankWidth) self.spinLeftSideSlope.valueChanged.connect(self.updateMaxBankWidth) self.spinWidth.valueChanged.connect(self.checkRes) self.spinRes.valueChanged.connect(self.checkRes) self.browseBtn.clicked.connect(self.writeDirName) self.btn1Dsave.clicked.connect(self.writeOut1Dresults) # add matplotlib figure to dialog self.figure = Figure() self.axes = self.figure.add_subplot(111) self.figure.subplots_adjust(left=.1, bottom=0.1, right=.95, top=.9, wspace=None, hspace=.2) self.canvas = FigureCanvas(self.figure) self.widgetPlotToolbar = NavigationToolbar(self.canvas, self.widgetPlot) lstActions = self.widgetPlotToolbar.actions() self.widgetPlotToolbar.removeAction(lstActions[7]) self.gPlot.addWidget(self.canvas) self.gPlot.addWidget(self.widgetPlotToolbar) self.figure.patch.set_visible(False) # and configure matplotlib params rcParams["font.serif"] = "Verdana, Arial, Liberation Serif" rcParams["font.sans-serif"] = "Tahoma, Arial, Liberation Sans" rcParams["font.cursive"] = "Courier New, Arial, Liberation Sans" rcParams["font.fantasy"] = "Comic Sans MS, Arial, Liberation Sans" rcParams["font.monospace"] = "Courier New, Liberation Mono" self.manageGui() def manageGui(self): print 'manageGui' self.cbCL.clear() self.cbCL.addItems(utils.getLineLayerNames()) self.cbDEM.clear() self.cbDEM.addItems(utils.getRasterLayerNames()) def refreshPlot(self): self.axes.clear() results1D = utils.getPlotArray(self.vLayer, self.rLayer, self.spinElevStart.value(), self.spinElevEnd.value(), self.xRes) self.stationA = results1D[0, :] self.zExistA = results1D[1, :] self.zPropA = results1D[2, :] self.xA = results1D[3, :] self.yA = results1D[4, :] self.calcCutAvgAreaEndMeth() self.axes.plot(self.stationA, self.zExistA) self.axes.plot(self.stationA, self.zPropA) self.axes.grid() formatter = ScalarFormatter(useOffset=False) self.axes.yaxis.set_major_formatter(formatter) self.axes.set_ylabel(unicode(self.tr("Elevation, z field units"))) self.axes.set_xlabel(unicode(self.tr('Station, layer units'))) self.axes.set_title( unicode( self.tr('Channel {} Profile and 1D Calculation Results'.format( self.vLayer.name())))) at = AnchoredText( self.outText, prop=dict(size=12), frameon=True, loc=1, ) at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") self.axes.add_artist(at) self.canvas.draw() def refreshPlotText(self): at = AnchoredText( self.outText, prop=dict(size=12), frameon=True, loc=1, ) at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") self.axes.add_artist(at) self.canvas.draw() def calcCutAvgAreaEndMeth(self): self.depth1D = self.zExistA - self.zPropA # find max bank width based on CL depth self.maxCLdepth = np.max(self.depth1D) leftSlope = self.spinLeftSideSlope.value() rightSlope = self.spinRightSideSlope.value() width = self.spinWidth.value() self.area = self.depth1D * (width + self.depth1D * leftSlope / 2.0 + self.depth1D * rightSlope / 2.0) self.length = self.stationA[1:] - self.stationA[:-1] self.vol1D = (self.area[1:] + self.area[:-1]) * self.length / 2.0 self.vol1D = np.append(0, self.vol1D) self.totVol1D = np.sum(self.vol1D) self.maxDepth1D = np.max(self.depth1D) self.totLength = np.max(self.stationA) self.channelSlope = (self.zPropA[-1] - self.zPropA[0]) / self.totLength self.outText = 'Length: {2:.2f} layer units\nChannel Slope: {3:.6f}\n1D Max. Cut Depth: {0:,.2f} layer units\n1D Tot. Vol.: {1:,.2f} layer units$^3$'.format( self.maxDepth1D, self.totVol1D, self.totLength, self.channelSlope) #self.canvas.draw() def writeOut1Dresults(self): # assign results to numpy array for quick csv dump self.out1Dresults = np.zeros((self.stationA.size, 8)) self.out1Dresults[:, 0] = self.stationA self.out1Dresults[:, 1] = self.vol1D self.out1Dresults[:, 2] = self.zExistA self.out1Dresults[:, 3] = self.zPropA self.out1Dresults[:, 4] = self.depth1D self.out1Dresults[:, 5] = self.area self.out1Dresults[:, 6] = self.xA self.out1Dresults[:, 7] = self.yA outPath = self.outputDir.text() home = os.path.expanduser("~") if outPath == '': outPath = os.path.join(home, 'Desktop', 'QGIS2DrainageChannelBuilderFiles') self.outputDir.setText(outPath) if not os.path.exists(outPath): os.makedirs(outPath) os.chdir(outPath) fileName = 'Channel1Dresults_{}.txt'.format(self.vLayer.name()) outHeader = 'DEM Layer:\t{0}\nChannel Centerline:\t{1}\nProjection (Proj4 format):\t{13}\nChannel Bottom Width:\t{3}\nChannel Start Elevation:\t{4}\nChannel End Elevation:\t{5}\nChannel Slope:\t{12:.06f}\nLeft Side Slope:\t{6}\nRight Side Slope:\t{7}\nLength:\t{9:,.2f}\n1D Max. Cut Depth:\t{10:,.2f}\n1D Tot. Vol:\t{11:,.2f}\nNote:\tAll units of length correspond to layer units, all units of area and volume are a combination of layer units and raster elevation units\n\nstation\tvol\tzExist\tzProp\tdepth\tcutArea\tx\ty\n'.format( self.cbDEM.currentText(), self.cbCL.currentText(), self.spinRes.value(), self.spinWidth.value(), self.spinElevStart.value(), self.spinElevEnd.value(), self.spinLeftSideSlope.value(), self.spinRightSideSlope.value(), self.spinBankWidth.value(), self.totLength, self.maxDepth1D, self.totVol1D, self.channelSlope, self.rLayer.crs().toProj4()) np.savetxt(fileName, self.out1Dresults, fmt='%.3f', header=outHeader, delimiter='\t') self.canvas.print_figure('Channel1DresultsFigure_{}'.format( self.vLayer.name())) def updateMaxBankWidth(self): self.maxBankWidth = self.maxCLdepth * np.max( np.array([ self.spinLeftSideSlope.value(), self.spinRightSideSlope.value() ])) self.spinBankWidth.setValue(self.maxBankWidth + self.spinRes.value() / 2.) self.calcCutAvgAreaEndMeth() self.refreshPlotText() def writeDirName(self): self.outputDir.clear() self.dirName = QtGui.QFileDialog.getExistingDirectory( self, 'Select Output Directory') self.outputDir.setText(self.dirName) self.checkBoxLoadLayers.setCheckState(True) def updateRasterRes(self): layer = utils.getRasterLayerByName( self.cbDEM.currentText().split(' EPSG')[0]) if layer.isValid(): self.xRes = layer.rasterUnitsPerPixelX() self.yRes = layer.rasterUnitsPerPixelY() self.labelDEMres.setText( 'DEM Layer Resolution = {:.2f} X {:.2f} Y'.format( self.xRes, self.yRes)) def checkRes(self): if self.spinWidth.value() <= self.spinRes.value() * 10: self.resWarning = True self.errOutput() else: self.resWarning = False self.errOutput() self.calcCutAvgAreaEndMeth() self.refreshPlotText() def checkLayerExtents(self): self.rLayer = utils.getRasterLayerByName( self.cbDEM.currentText().split(' EPSG')[0]) self.vLayer = utils.getVectorLayerByName( self.cbCL.currentText().split(' EPSG')[0]) try: if self.rLayer.isValid() and self.vLayer.isValid(): if self.rLayer.extent().xMaximum() >= self.vLayer.extent( ).xMaximum() and self.rLayer.extent().xMinimum( ) <= self.vLayer.extent().xMinimum() and self.rLayer.extent( ).yMaximum() >= self.vLayer.extent().yMaximum( ) and self.rLayer.extent().yMinimum() <= self.vLayer.extent( ).yMinimum(): self.layersOverlap = True self.calcElev() self.btn1Dsave.setEnabled(True) self.btnOk.setEnabled(True) self.errOutput() else: self.btnOk.setEnabled(False) self.spinElevStart.setEnabled(False) self.spinElevEnd.setEnabled(False) self.layersOverlap = False self.errOutput() except: self.btnOk.setEnabled(False) self.spinElevStart.setEnabled(False) self.spinElevEnd.setEnabled(False) self.btn1Dsave.setEnabled(False) self.overlayError = True self.layersOverlap = False self.errOutput() pass def calcDepth(self): if self.layersOverlap: self.layersOverlap = utils.calcDepth(self) if self.layersOverlap: self.refreshPlot() def calcElev(self): if self.layersOverlap: self.spinElevStart.setEnabled(True) self.spinElevEnd.setEnabled(True) self.spinElevStart.setValue(utils.calcElev(self)[0]) self.spinElevEnd.setValue(utils.calcElev(self)[1]) self.refreshPlot() def updateProgressText(self, message): self.labelProgress.setText(message) def updateOutputText(self, message): self.textBrowser.setPlainText(message) def workerError(self, e, exception_string): print 'workerError\n{}\n'.format(exception_string) QgsMessageLog.logMessage( 'Worker thread raised an exception:\n{}'.format(exception_string), level=QgsMessageLog.CRITICAL) self.iface.messageBar().pushMessage( "Drainge Channel Builder", 'It didnt work, see log for details', level=QgsMessageBar.CRITICAL, duration=3) self.progressBar.setMaximum(100) self.btnOk.setEnabled(True) self.btn1Dsave.setEnabled(True) def stopWorker(self): self.worker.kill() if self.worker is not None: self.worker.deleteLater() self.thread.quit() self.thread.wait() self.thread.deleteLater() def errOutput(self): if self.resWarning and not self.layersOverlap: self.labelErrMessage.setText( 'Error: Vector is not completely within raster domain\nWarning: For best 2D results, channel bottom width should be at least ten times greater than 2D grid calculation resolution' ) elif self.resWarning: self.labelErrMessage.setText( 'Warning: For best 2D results, channel bottom width should be at least ten times greater than 2D grid calculation resolution' ) elif not self.layersOverlap: self.labelErrMessage.setText( 'Error: Vector is not completely within raster domain') else: self.labelErrMessage.setText('') def workerFinished(self, values): # print 'values = ',values self.stopWorker() self.values = values maxCut = values[0][0] avgCut = values[0][1] totVol = values[0][2] self.dirName = values[0][3] self.outputDir.setText(self.dirName) demChannelPath = values[0][4] demCutDepth = values[0][5] length = values[0][6] outText = 'Summary of 2D Results:\nTot. Vol.\t{2:,.2f}\nLength\t{3:,.2f}\nMax. Cut Depth\t{0:.2f}\nAvg. Cut Depth\t{1:.2f}'.format( maxCut, avgCut, totVol, length) self.updateOutputText(outText) # for layer in self.layers: # utils.addSlLayer(self.iface,self.dbase,layer) self.iface.messageBar().pushMessage( "Drainge Channel Builder", 'Channel elevation DEM, channel depth of cut DEM, and channel grid points located at {}. Please delete when finished' .format(self.dirName), duration=30) # set channel dem qml by coping dem layer style to channel dem qml self.rLayer.saveNamedStyle( os.path.join(self.dirName, demChannelPath.split('.tif')[0] + '.qml')) # set depth qml w/ function xmlString = utils.depthQMLwriter(maxCut) demCutDepthQML = open(demCutDepth.split('.tif')[0] + '.qml', 'w') demCutDepthQML.write(xmlString) demCutDepthQML.close() if self.checkBoxLoadLayers.checkState(): for fileName in [demChannelPath, demCutDepth]: fileInfo = QFileInfo(fileName) baseName = fileInfo.baseName() layer = QgsRasterLayer(fileName, baseName) if not layer.isValid(): print "Layer failed to load!" QgsMapLayerRegistry.instance().addMapLayer(layer) self.progressBar.setMaximum(100) self.cbCL.setEnabled(True) self.cbDEM.setEnabled(True) self.spinRes.setEnabled(True) self.spinWidth.setEnabled(True) self.spinElevStart.setEnabled(True) self.spinElevEnd.setEnabled(True) self.spinLeftSideSlope.setEnabled(True) self.spinRightSideSlope.setEnabled(True) self.spinBankWidth.setEnabled(True) self.browseBtn.setEnabled(True) self.btn1Dsave.setEnabled(True) self.btnClose.setEnabled(True) self.btnOk.setEnabled(True) self.writeOut1Dresults() fileName = 'Channel2DresultsSummary_{}.txt'.format(self.vLayer.name()) outText = 'DEM Layer:\t{0}\nChannel Centerline:\t{1}\nProjection (Proj4 format):\t{14}\n2D Grid Calc Res.:\t{2}\nChannel Bottom Width:\t{3}\nChannel Start Elevation:\t{4}\nChannel End Elevation:\t{5}\nChannel Slope:\t{13:.06f}\nLeft Side Slope:\t{6}\nRight Side Slope:\t{7}\n2D Maximum Bank Width:\t{8}\n\nLength:\t{9:,.2f}\n2D Max. Cut Depth:\t{10:,.2f}\n2D Avg. Cut Depth:\t{11:,.2f}\n2D Tot. Vol:\t{12:,.2f}\n\nNote:\tAll units of length correspond to layer units, all units of area and volume are a combination of layer units and raster elevation units\n\n'.format( self.cbDEM.currentText(), self.cbCL.currentText(), self.spinRes.value(), self.spinWidth.value(), self.spinElevStart.value(), self.spinElevEnd.value(), self.spinLeftSideSlope.value(), self.spinRightSideSlope.value(), self.spinBankWidth.value(), self.totLength, maxCut, avgCut, totVol, self.channelSlope, self.rLayer.crs().toProj4()) outFile = open(fileName, 'w') outFile.write(outText) outFile.close() self.checkBoxLoadLayers.setCheckState(False) self.iface.mapCanvas().refresh() def accept(self): print 'accepted' args = [ self.rLayer, self.vLayer, self.spinWidth.value(), self.spinRes.value(), self.spinElevStart.value(), self.spinElevEnd.value(), self.spinLeftSideSlope.value(), self.spinRightSideSlope.value(), self.spinBankWidth.value(), self.outputDir.text() ] self.thread = QThread() # create a new worker instance self.worker = DrainageChannelThread.DrainageChannelBuilder(args) # create a new worker instance self.worker.moveToThread(self.thread) self.worker.updateProgressText.connect(self.updateProgressText) self.worker.workerFinished.connect(self.workerFinished) self.worker.error.connect(self.workerError) self.btnClose.setEnabled(False) self.cbCL.setEnabled(False) self.cbDEM.setEnabled(False) self.spinRes.setEnabled(False) self.spinWidth.setEnabled(False) self.spinElevStart.setEnabled(False) self.spinElevEnd.setEnabled(False) self.spinLeftSideSlope.setEnabled(False) self.spinRightSideSlope.setEnabled(False) self.spinBankWidth.setEnabled(False) self.browseBtn.setEnabled(False) self.btn1Dsave.setEnabled(False) self.btnOk.setEnabled(False) self.tabWidgetOutput.setCurrentWidget(self.tabOutputSummary) #self.buttonBox.rejected.disconnect(self.reject) self.progressBar.setMaximum(0) self.thread.started.connect(self.worker.run) self.thread.start() def reject(self): print 'rejected' QtGui.QDialog.reject(self)
class MatplotlibExample(QtGui.QMainWindow): """HOW TO EMBED MATPLOTLIB WITH PYSIDE""" here = os.path.abspath(os.path.join( os.path.dirname(__file__))) # to be overloaded media = os.path.abspath(os.path.join(here, "media")) def __init__(self, parent=None): QtGui.QMainWindow.__init__(self, parent) self.x = None self.y = None self.last_idx = None # matplotlib stuff self.dpi = 200 self.figure = None self.canvas = None self.axes = None self.mpl_toolbar = None # PySide stuff self.main_frame = None self.file_menu = None self.help_menu = None self.textbox = None self.draw_button = None self.grid_ck = None self.slider = None # create ui self.setWindowTitle('PySide with matplotlib') self.center() self.create_menu() self.create_main_frame() self.on_draw() def create_menu(self): """Menu creation""" # file menu self.file_menu = self.menuBar().addMenu("&File") load_file_action = self.create_action("&Save plot", shortcut="Ctrl+S", slot=self.save_plot, tip="Save the plot") self.file_menu.addAction(load_file_action) quit_action = self.create_action("&Quit", slot=self.close, shortcut="Ctrl+Q", tip="Close the app") self.file_menu.addAction(quit_action) # help menu self.help_menu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut='F1', slot=self.on_about, tip='About this app') self.help_menu.addAction(about_action) def create_action(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): """Helper function to create an action""" action = QtGui.QAction(text, self) if icon is not None: action.setIcon( QtGui.QIcon(os.path.join(self.media, "%s.png" % icon))) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) if slot is not None: action.triggered.connect(slot) if checkable: action.setCheckable(True) return action def create_main_frame(self): self.main_frame = QtGui.QWidget() # Create Matplotlib figure and canvas self.figure = Figure((6.0, 4.0), dpi=self.dpi) # inches, dots-per-inch self.canvas = FigureCanvas(self.figure) self.canvas.setParent(self.main_frame) self.canvas.setFocusPolicy( QtCore.Qt.ClickFocus) # key for press events!!! self.canvas.setFocus() # we use add_subplot (so that the subplot configuration tool in the navigation toolbar works) self.axes = self.figure.add_subplot(111) # Bind events self.canvas.mpl_connect('pick_event', self.on_pick) self.canvas.mpl_connect('button_press_event', self.on_mouse_press) self.canvas.mpl_connect('key_press_event', self.on_key_press) # Create the navigation toolbar, tied to the canvas self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) # other GUI controls self.textbox = QtGui.QLineEdit() self.textbox.setMinimumWidth(200) self.textbox.editingFinished.connect(self.on_draw) self.textbox.setText('1 3 2 6 3 2 4') self.draw_button = QtGui.QPushButton("&Draw") self.draw_button.clicked.connect(self.on_draw) # grid self.grid_ck = QtGui.QCheckBox("Show &Grid") self.grid_ck.setChecked(False) self.grid_ck.stateChanged.connect(self.on_draw) # slider slider_label = QtGui.QLabel('Plot width (%):') self.slider = QtGui.QSlider(QtCore.Qt.Horizontal) self.slider.setRange(1, 100) self.slider.setValue(20) self.slider.setTracking(True) self.slider.setTickPosition(QtGui.QSlider.TicksBothSides) self.slider.valueChanged.connect(self.on_draw) # layouts hbox = QtGui.QHBoxLayout() for w in [ self.textbox, self.draw_button, self.grid_ck, slider_label, self.slider ]: hbox.addWidget(w) hbox.setAlignment(w, QtCore.Qt.AlignVCenter) vbox = QtGui.QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) vbox.addLayout(hbox) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def center(self): qr = self.frameGeometry() cp = QtGui.QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def save_plot(self): flt = "PNG (*.png)|*.png" path = QtGui.QFileDialog.getSaveFileName(self, 'Save file', '', flt) if path: self.canvas.print_figure(path, dpi=self.dpi) def on_about(self): msg = """PySide with matplotlib: - navigation bar - grid toggle - interactivity ('Draw' button, slider, click on bar) - plot saving """ QtGui.QMessageBox.about(self, "About the demo", msg.strip()) def on_pick(self, event): """Manage pick event""" if event.ind is None: return m_x = event.mouseevent.xdata m_y = event.mouseevent.ydata msg = "Click event:\n" msg += "ind: %s\n" % event.ind msg += "mouse.xdata/ydata: %s, %s\n" % (m_x, m_y) # click location msg += "xydata:%s\n" % event.artist.get_xydata() print(msg) # in case of multiple selection distances = np.hypot(m_x - self.x[event.ind], m_y - self.y[event.ind]) idx_min = distances.argmin() self.last_idx = event.ind[idx_min] self.update_plot() def on_key_press(self, event): """Manage press event""" print("pressed key: %s" % event.key) def on_mouse_press(self, event): """Manage press event""" print("pressed mouse: %s" % event.key) def on_draw(self): """Redraws the figure""" self.y = [int(d) for d in self.textbox.text().split()] self.x = range(len(self.y)) # clear the axes and redraw self.axes.clear() self.axes.grid(self.grid_ck.isChecked()) self.axes.plot(self.x, self.y, linewidth=self.slider.value() / 100.0, alpha=0.5, picker=3) self.canvas.draw() def update_plot(self): if self.last_idx is None: return print("update") self.on_draw() self.axes.text(self.x[self.last_idx], self.y[self.last_idx], 'x=%1.3f\ny=%1.3f' % (self.x[self.last_idx], self.y[self.last_idx]), va='top') self.canvas.draw()
def plot_quiet_regions(): # Plot the region that the noise was calculated from... # For TOT... TOT_bounds, VCD_bounds, THW_bounds = find_quiet_regions() # TOT/JKB2d/X16a gives: # mag = 32809.224658469, phs = -0.90421798501485484 # VCD/JKB2g/DVD01a gives: # mag = 15720.217174332585, phs = -0.98350090576267946 # THW/SJB2/DRP02a gives: # 26158.900202734963, phs = 1.6808311318828895 TOT_fig = Figure((10, 8)) TOT_canvas = FigureCanvas(TOT_fig) TOT_ax = TOT_fig.add_axes([0, 0, 1, 1]) TOT_ax.axis('off') plot_radar(TOT_ax, 'TOT_LO', 3200, None, [135000, 234000]) xlim = TOT_ax.get_xlim() ylim = TOT_ax.get_ylim() TOT_ax.vlines(TOT_bounds[0:2], 0, 3200, colors='red', linewidth=3, linestyles='dashed') plot_bounding_box(TOT_ax, TOT_bounds, '', linewidth=4) TOT_ax.set_xlim(xlim) TOT_ax.set_ylim(ylim) TOT_canvas.print_figure('../FinalReport/figures/TOT_quiet_region.jpg') VCD_fig = Figure((10, 8)) VCD_canvas = FigureCanvas(VCD_fig) VCD_ax = VCD_fig.add_axes([0, 0, 1, 1]) VCD_ax.axis('off') plot_radar(VCD_ax, 'VCD_LO', 3200, None, [135000, 234000]) xlim = VCD_ax.get_xlim() ylim = VCD_ax.get_ylim() VCD_ax.vlines(VCD_bounds[0:2], 0, 3200, colors='red', linewidth=3, linestyles='dashed') plot_bounding_box(VCD_ax, VCD_bounds, '', linewidth=4) VCD_ax.set_xlim(xlim) VCD_ax.set_ylim(ylim) VCD_canvas.print_figure('../FinalReport/figures/VCD_quiet_region.jpg') THW_fig = Figure((10, 8)) THW_canvas = FigureCanvas(THW_fig) THW_ax = THW_fig.add_axes([0, 0, 1, 1]) THW_ax.axis('off') plot_radar(THW_ax, 'THW_LO', 3200, None, [135000, 234000]) xlim = THW_ax.get_xlim() ylim = THW_ax.get_ylim() THW_ax.vlines(THW_bounds[0:2], 0, 3200, colors='red', linewidth=3, linestyles='dashed') plot_bounding_box(THW_ax, THW_bounds, '', linewidth=4) THW_ax.set_xlim(xlim) THW_ax.set_ylim(ylim) THW_canvas.print_figure('../FinalReport/figures/THW_quiet_region.jpg')
class AppForm(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle('Templar_2') self.create_menu() self.create_main_frame() self.create_status_bar() self.dacStatus = 'off' self.dramStatus = 'off' self.tapStatus = 'off' self.socketStatus = 'off' self.ch_all = [] self.attens = numpy.array([1. for i in range(256)]) #self.freqRes = 1953.125 #self.freqRes = 7812.5 self.sampleRate = 550e6 N_lut_entries = 2**16 self.freqRes = self.sampleRate/N_lut_entries #self.iq_centers = numpy.array([0.+0j]*256) def openClient(self): self.status_text.setText('connecting...') self.roach = corr.katcp_wrapper.FpgaClient(self.textbox_roachIP.text(),7147) time.sleep(2) self.status_text.setText('connecting...done') self.button_openClient.setDisabled(True) def programRFswitches(self, regStr = '10110'): # 5 bit word: LO_int/ext, RF_loop, LO_source(doubler), BB_loop, Ck_int/ext #regStr = self.textbox_rfSwReg.text() print int(regStr[0]), int(regStr[1]), int(regStr[2]),int(regStr[3]), int(regStr[4]) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('if_switch', 1) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[0])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[0])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[0])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[1])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[1])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[1])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[2])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[2])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[2])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[3])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[3])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[3])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[4])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[4])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[4])<<2)+(0<<1)+(0<<0)) # Now clock out the data written to the reg. self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('if_switch', 0) def programAttenuators(self, atten_in_desired, atten_out_desired): # There are eight settings for each attenuator: # 0db, -0.5, -1, -2, -4, -8, -16, and -31.5, which # are listed in order in "attenuations." #atten_in_desired = float(self.textbox_atten_in.text()) atten_in = 63 - int(atten_in_desired*2) #atten_out_desired = float(self.textbox_atten_out.text()) if atten_out_desired <= 31.5: atten_out0 = 63 atten_out1 = 63 - int(atten_out_desired*2) else: atten_out0 = 63 - int((atten_out_desired-31.5)*2) atten_out1 = 0 reg = numpy.binary_repr((atten_in<<12)+(atten_out0<<6)+(atten_out1<<0)) b = '0'*(18-len(reg)) + reg print reg, len(reg) self.roach.write_int('regs', (0<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('if_switch', 1) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[0])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[0])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[0])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[1])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[1])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[1])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[2])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[2])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[2])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[3])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[3])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[3])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[4])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[4])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[4])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[5])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[5])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[5])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[6])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[6])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[6])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[7])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[7])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[7])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[8])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[8])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[8])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[9])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[9])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[9])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[10])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[10])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[10])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[11])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[11])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[11])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[12])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[12])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[12])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[13])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[13])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[13])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[14])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[14])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[14])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[15])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[15])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[15])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[16])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[16])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[16])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[17])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[17])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[17])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('if_switch', 0) def programLO(self, freq=3.2e9, sweep_freq=0): f_pfd = 10e6 if sweep_freq: f = freq else: f = float(self.textbox_loFreq.text()) if f >= 4.4e9: f = f/2 INT = int(f)/int(f_pfd) MOD = 2000 FRAC = int(round(MOD*(f/f_pfd-INT))) if FRAC != 0: gcd = fractions.gcd(MOD,FRAC) if gcd != 1: MOD = MOD/gcd FRAC = int(FRAC/gcd) PHASE = 1 R = 1 power = 3 aux_power = 3 MUX = 3 LOCK_DETECT = 1 reg5 = (LOCK_DETECT<<22) + (1<<2) + (1<<0) reg4 = (1<<23) + (1<<18) + (1<<16) + (1<<8) + (aux_power<<6) + (1<<5) + (power<<3) + (1<<2) reg3 = (1<<10) + (1<<7) + (1<<5) + (1<<4) + (1<<1) + (1<<0) reg2 = (MUX<<26) + (R<<14) + (1<<11) + (1<<10) + (1<<9) + (1<<7) + (1<<6) + (1<<1) reg1 = (1<<27) + (PHASE<<15) + (MOD<<3) + (1<<0) reg0 = (INT<<15) + (FRAC<<3) regs = [reg5, reg4, reg3, reg2, reg1, reg0] for r in regs: self.roach.write_int('SER_DI', r) self.roach.write_int('LO_SLE', 1) self.roach.write_int('start', 1) self.roach.write_int('start', 0) self.roach.write_int('LO_SLE', 0) def define_LUTs(self): self.iq_centers = numpy.array([0.+0j]*256) self.define_DAC_LUT() self.define_DDS_LUT() self.write_LUTs() #self.loadLUTs() print "luts defined and loaded." def freqCombLUT(self, echo, freq, sampleRate, resolution, amplitude=[1.]*256, phase=[0.]*256, random_phase = 'yes'): offset = int(self.textbox_offset.text()) amp_full_scale = 2**15-1 N_freqs = len(freq) size = int(sampleRate/resolution) I, Q = numpy.array([0.]*size), numpy.array([0.]*size) single_I, single_Q = numpy.array([0.]*size), numpy.array([0.]*size) numpy.random.seed(1000) for n in range(N_freqs): if random_phase == 'yes': phase[n] = numpy.random.uniform(0, 2*numpy.pi) #start_x = phase[n]+2*numpy.pi*freq[n]*offset/sampleRate #end_x = phase[n]+2*numpy.pi*freq[n]*(size+offset)/sampleRate #x = numpy.linspace(start_x, end_x, size) #start_y = phase[n] #end_y = phase[n]+2*numpy.pi*freq[n]*(size)/sampleRate #y = numpy.linspace(start_y, end_y, size) x = [2*numpy.pi*freq[n]*(t+offset)/sampleRate+phase[n] for t in range(size)] y = [2*numpy.pi*freq[n]*t/sampleRate+phase[n] for t in range(size)] single_I = amplitude[n]*numpy.cos(x) single_Q = amplitude[n]*numpy.sin(y) I = I + single_I Q = Q + single_Q a = numpy.array([abs(I).max(), abs(Q).max()]) I = numpy.array([int(i*amp_full_scale/a.max()) for i in I]) Q = numpy.array([int(q*amp_full_scale/a.max()) for q in Q]) if echo == 'yes': print 'scale factor: ', a.max() self.scale_factor = a.max() print 'Set atten_out to: ', 20*numpy.log10(self.previous_scale_factor/self.scale_factor) + self.minimumAttenuation return I, Q def define_DAC_LUT(self): freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) f_base = float(self.textbox_loFreq.text()) resFreqs = numpy.array(freqs) for n in range(len(freqs)): if freqs[n] < f_base: freqs[n] = freqs[n] + self.sampleRate self.freqs_dac = [round((f-f_base)/self.freqRes)*self.freqRes for f in freqs] atten_min = self.attens.min() print numpy.array(self.freqs_dac) + f_base print "minimum attenuation: ", self.minimumAttenuation amplitudes = [10**(+(atten_min-a)/20.) for a in self.attens] self.I_dac, self.Q_dac = self.freqCombLUT('yes', self.freqs_dac, self.sampleRate, self.freqRes, amplitudes) print 'done defining DAC freqs.' def define_DDS_LUT(self, phase = [0.]*256): fft_len=2**9 ch_shift = int(self.textbox_dds_shift.text()) freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) f_base = float(self.textbox_loFreq.text()) for n in range(len(freqs)): if freqs[n] < f_base: freqs[n] = freqs[n] + self.sampleRate#512e6 freqs_dds = [0 for j in range(256)] for n in range(len(freqs)): freqs_dds[n] = round((freqs[n]-f_base)/self.freqRes)*self.freqRes freq_residuals = self.select_bins(freqs_dds) L = int(self.sampleRate/self.freqRes) self.I_dds, self.Q_dds = [0.]*L, [0.]*L for m in range(256): #I, Q = self.freqCombLUT('no', [freq_residuals[m]], 2e6, self.freqRes, [1.], [phase[m]], 'no') I, Q = self.freqCombLUT('no', [freq_residuals[m]], self.sampleRate/fft_len*2, self.freqRes, [1.], [phase[m]], 'no') for j in range(len(I)/2): self.I_dds[j*512+2*((m+ch_shift)%256)] = I[2*j] self.I_dds[j*512+2*((m+ch_shift)%256)+1] = I[2*j+1] self.Q_dds[j*512+2*((m+ch_shift)%256)] = Q[2*j] self.Q_dds[j*512+2*((m+ch_shift)%256)+1] = Q[2*j+1] print "done defing dds freqs. " def select_bins(self, readout_freqs): fft_len = 2**9 bins = '' i = 0 residuals = [] for f in readout_freqs: fft_bin = int(round(f*fft_len/self.sampleRate)) fft_freq = fft_bin*self.sampleRate/fft_len freq_residual = round((f - fft_freq)/self.freqRes)*self.freqRes residuals.append(freq_residual) bins = bins + struct.pack('>l', fft_bin) self.roach.write_int('bins', fft_bin) self.roach.write_int('load_bins', (i<<1) + (1<<0)) self.roach.write_int('load_bins', (i<<1) + (0<<0)) i = i + 1 return residuals def write_LUTs(self): if self.dacStatus == 'off': self.roach.write_int('startDAC', 0) else: self.toggleDAC() binaryData = '' print len(self.I_dac) self.axes0.clear() self.axes0.plot(range(len(self.I_dac)),self.I_dac,'r-') self.axes0.plot(range(len(self.I_dds)),self.I_dds,'b-') self.canvas.draw() for n in range(len(self.I_dac)/2): i_dac_0 = struct.pack('>h', self.I_dac[2*n]) i_dac_1 = struct.pack('>h', self.I_dac[2*n+1]) i_dds_0 = struct.pack('>h', self.I_dds[2*n]) i_dds_1 = struct.pack('>h', self.I_dds[2*n+1]) q_dac_0 = struct.pack('>h', self.Q_dac[2*n]) q_dac_1 = struct.pack('>h', self.Q_dac[2*n+1]) q_dds_0 = struct.pack('>h', self.Q_dds[2*n]) q_dds_1 = struct.pack('>h', self.Q_dds[2*n+1]) binaryData = binaryData + q_dds_1 + q_dds_0 + q_dac_1 + q_dac_0 + i_dds_1 + i_dds_0 + i_dac_1 + i_dac_0 self.roach.write('dram_memory', binaryData) # Write LUTs to file. saveDir = str(self.textbox_saveDir.text()) f = open(saveDir + 'luts.dat', 'w') f.write(binaryData) f.close() def loadIQcenters(self): saveDir = str(self.textbox_saveDir.text()) centers_for_file = [[0., 0.]]*256 for ch in range(256): I_c = int(self.iq_centers[ch].real/2**3) Q_c = int(self.iq_centers[ch].imag/2**3) center = (I_c<<16) + (Q_c<<0) self.roach.write_int('conv_phase_centers', center) self.roach.write_int('conv_phase_load_centers', (ch<<1)+(1<<0)) self.roach.write_int('conv_phase_load_centers', 0) centers_for_file[ch] = [self.iq_centers[ch].real, self.iq_centers[ch].imag] numpy.savetxt(saveDir+'centers.dat', centers_for_file) def findIQcenters(self, I, Q): I_0 = (I.max()+I.min())/2. Q_0 = (Q.max()+Q.min())/2. return complex(I_0, Q_0) def rotateLoops(self): print "Calculating loop rotations..." dac_freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) N_freqs = len(dac_freqs) phase = [0.]*256 self.roach.write_int('startAccumulator', 0) self.roach.write_int('avgIQ_ctrl', 1) self.roach.write_int('avgIQ_ctrl', 0) self.roach.write_int('startAccumulator', 1) time.sleep(0.001) data = self.roach.read('avgIQ_bram', 4*2*256) for n in range(N_freqs): I = struct.unpack('>l', data[4*n:4*n+4])[0]-self.iq_centers[n].real Q = struct.unpack('>l', data[4*(n+256):4*(n+256)+4])[0]-self.iq_centers[n].imag phase[n] = numpy.arctan2(Q, I) self.define_DDS_LUT(phase) self.write_LUTs() """ Since the DAC is toggled OFF in write_LUTs, it needs to be turned back on with the following. """ self.toggleDAC() self.sweepLO() def sweepLO(self): atten_in = float(self.textbox_atten_in.text()) saveDir = str(self.textbox_saveDir.text()) savefile = saveDir + 'ps_' + time.strftime("%Y%m%d-%H%M%S",time.localtime()) + '.h5' dac_freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) self.N_freqs = len(dac_freqs) f_base = float(self.textbox_loFreq.text()) if f_base >= 4.4e9: self.programRFswitches('10010') print 'LO doubled.' else: self.programRFswitches('10110') print 'LO normal operation.' loSpan = float(self.textbox_loSpan.text()) df = 1e4 steps = int(loSpan/df) print "LO steps: ", steps lo_freqs = [f_base+i*df-0.5*steps*df for i in range(steps)] atten_start = int(self.textbox_powerSweepStart.text()) atten_stop = int(self.textbox_powerSweepStop.text()) if atten_start <= atten_stop: attens = [i for i in range(atten_start, atten_stop+1)] else: attens = [i for i in range(atten_start, atten_stop-1, -1)] for a in attens: print a self.programAttenuators(atten_in, a) time.sleep(0.5) f_span = [] l = 0 self.f_span = [[0]*steps]*self.N_freqs for f in dac_freqs: f_span = f_span + [f-0.5*steps*df+n*df for n in range(steps)] self.f_span[l] = [f-0.5*steps*df+n*df for n in range(steps)] l = l + 1 I = numpy.zeros(self.N_freqs*steps, dtype='float32') Q = numpy.zeros(self.N_freqs*steps, dtype='float32') I_std = numpy.zeros(self.N_freqs*steps, dtype='float32') Q_std = numpy.zeros(self.N_freqs*steps, dtype='float32') self.I, self.Q = numpy.array([[0.]*steps]*self.N_freqs),numpy.array([[0.]*steps]*self.N_freqs) for i in range(steps): #print i self.programLO(lo_freqs[i],1) self.roach.write_int('startAccumulator', 0) self.roach.write_int('avgIQ_ctrl', 1) self.roach.write_int('avgIQ_ctrl', 0) self.roach.write_int('startAccumulator', 1) time.sleep(0.001) data = self.roach.read('avgIQ_bram', 4*2*256) for j in range(self.N_freqs): I[j*steps+i] = struct.unpack('>l', data[4*j:4*j+4])[0] Q[j*steps+i] = struct.unpack('>l', data[4*(j+256):4*(j+256)+4])[0] I_std[j*steps+i] = 0 Q_std[j*steps+i] = 0 self.I[j, i] = I[j*steps+i] self.Q[j, i] = Q[j*steps+i] self.programLO(f_base,1) # Find IQ centers. self.I_on_res, self.Q_on_res = [0.]*self.N_freqs, [0.]*self.N_freqs self.roach.write_int('startAccumulator', 0) self.roach.write_int('avgIQ_ctrl', 1) self.roach.write_int('avgIQ_ctrl', 0) self.roach.write_int('startAccumulator', 1) time.sleep(0.001) data = self.roach.read('avgIQ_bram', 4*2*256) for j in range(self.N_freqs): self.I_on_res[j] = struct.unpack('>l', data[4*j:4*j+4])[0] self.Q_on_res[j] = struct.unpack('>l', data[4*(j+256):4*(j+256)+4])[0] self.iq_centers[j] = self.findIQcenters(I[j*steps:j*steps+steps],Q[j*steps:j*steps+steps]) N = steps*self.N_freqs for n in range(self.N_freqs): w = iqsweep.IQsweep() w.f0 = dac_freqs[n]/1e9 w.span = steps*df/1e6 w.fsteps = steps #w.atten1 = a w.atten1 = a + self.attens[n] #w.atten1 = atten # w.atten2 is actually the "scale factor" used in the DAC LUT # generation. w.atten2 = 0 w.scale = self.scale_factor w.PreadoutdB = -w.atten1 - 20*numpy.log10(self.scale_factor) w.Tstart = 0.100 w.Tend = 0.100 w.I0 = 0.0 w.Q0 = 0.0 w.resnum = n w.freq = numpy.array(f_span[n*steps:(n+1)*steps], dtype='float32')/1e9 w.I = I[n*steps:(n+1)*steps] w.Q = Q[n*steps:(n+1)*steps] w.Isd = I_std[n*steps:(n+1)*steps] w.Qsd = Q_std[n*steps:(n+1)*steps] w.time = time.time() w.savenoise = 0 w.Save(savefile,'r0', 'a') self.axes0.clear() self.axes1.clear() self.axes0.semilogy(f_span, (I**2 + Q**2)**.5, '.-') self.axes1.plot(I, Q, '.-', self.iq_centers.real[0:self.N_freqs], self.iq_centers.imag[0:self.N_freqs], '.', self.I_on_res, self.Q_on_res, '.') self.canvas.draw() def toggleDAC(self): if self.dacStatus == 'off': print "Starting DAC...", self.roach.write_int('startDAC', 1) time.sleep(1) while self.roach.read_int('DRAM_LUT_rd_valid') != 0: self.roach.write_int('startDAC', 0) time.sleep(0.25) self.roach.write_int('startDAC', 1) time.sleep(1) print ".", f_base = float(self.textbox_loFreq.text()) self.programLO(f_base,1) self.button_startDAC.setText('Stop DAC') self.dacStatus = 'on' self.status_text.setText('DAC turned on. ') print "done" else: print "Stopping DAC..." self.roach.write_int('startDAC', 0) self.button_startDAC.setText('Start DAC') self.dacStatus = 'off' print "done" self.status_text.setText('DAC turned off. ') def loadFreqsAttens(self): f_base = float(self.textbox_loFreq.text()) freqFile =str(self.textbox_freqFile.text()) x = numpy.loadtxt(freqFile) x_string = '' self.previous_scale_factor = x[0,0] N_freqs = len(x[1:,0]) for l in x[1:,0]: x_string = x_string + str(l*1e9) + '\n' self.iq_centers = numpy.array([0.+0j]*256) for n in range(N_freqs): #for n in range(256): self.iq_centers[n] = complex(x[n+1,1], x[n+1,2]) self.attens = x[1:,3] self.minimumAttenuation = numpy.array(x[1:,3]).min() self.textedit_DACfreqs.setText(x_string) def loadLUTs(self): self.scale_factor = 1. self.iq_centers = numpy.array([0.+0j]*256) # Loads the DAC and DDS LUTs from file. As well as the IQ loop centers. if self.dacStatus == 'off': self.roach.write_int('startDAC', 0) else: self.toggleDAC() saveDir = str(self.textbox_saveDir.text()) f = open(saveDir+'luts.dat', 'r') binaryData = f.read() self.roach.write('dram_memory', binaryData) x = numpy.loadtxt(saveDir+'centers.dat') N_freqs = len(x[:,0]) for n in range(N_freqs): self.iq_centers[n] = complex(x[n,0], x[n,1]) # Select and write bins for first stage of channelizer. freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) f_base = float(self.textbox_loFreq.text()) for n in range(len(freqs)): if freqs[n] < f_base: freqs[n] = freqs[n] + self.sampleRate#512e6 freqs_dds = [0 for j in range(256)] for n in range(len(freqs)): freqs_dds[n] = round((freqs[n]-f_base)/self.freqRes)*self.freqRes freq_residuals = self.select_bins(freqs_dds) print 'LUTs and IQ centers loaded from file.' def channelIncUp(self): ch = int(self.textbox_channel.text()) ch = ch + 1 ch = ch%self.N_freqs self.textbox_channel.setText(str(ch)) self.axes0.clear() self.axes1.clear() self.axes0.semilogy(self.f_span[ch], (self.I[ch]**2 + self.Q[ch]**2)**.5, '.-') self.axes1.plot(self.I[ch], self.Q[ch], '.-', self.iq_centers.real[ch], self.iq_centers.imag[ch], '.', self.I_on_res[ch], self.Q_on_res[ch], '.') self.canvas.draw() def channelIncDown(self): ch = int(self.textbox_channel.text()) ch = ch - 1 ch = ch%self.N_freqs self.textbox_channel.setText(str(ch)) self.axes0.clear() self.axes1.clear() self.axes0.semilogy(self.f_span[ch], (self.I[ch]**2 + self.Q[ch]**2)**.5, '.-') self.axes1.plot(self.I[ch], self.Q[ch], '.-', self.iq_centers.real[ch], self.iq_centers.imag[ch], '.', self.I_on_res[ch], self.Q_on_res[ch], '.') self.canvas.draw() def changeCenter(self, event): I = event.xdata Q = event.ydata ch = int(self.textbox_channel.text()) print ch self.iq_centers.real[ch] = I self.iq_centers.imag[ch] = Q self.axes1.plot(I, Q, '.') self.canvas.draw() def create_main_frame(self): self.main_frame = QWidget() # Create the mpl Figure and FigCanvas objects. self.dpi = 100 self.fig = Figure((9.0, 5.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) self.axes0 = self.fig.add_subplot(121) self.axes1 = self.fig.add_subplot(122) cid=self.canvas.mpl_connect('button_press_event', self.changeCenter) # Create the navigation toolbar, tied to the canvas self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) # Roach board's IP address self.textbox_roachIP = QLineEdit('10.0.0.10') self.textbox_roachIP.setMaximumWidth(200) label_roachIP = QLabel('Roach IP Address:') # Start connection to roach. self.button_openClient = QPushButton("(1)Open Client") self.button_openClient.setMaximumWidth(100) self.connect(self.button_openClient, SIGNAL('clicked()'), self.openClient) # LO frequency. self.textbox_loFreq = QLineEdit('3.5e9') self.textbox_loFreq.setMaximumWidth(100) label_loFreq = QLabel('LO frequency:') # Sweep span self.textbox_loSpan = QLineEdit('0.5e6') self.textbox_loSpan.setMaximumWidth(50) label_loSpan = QLabel('LO Span:') # Frequency span shift # A span shift of 0.75 shifts 75% of sweep span to the lower portion of the range. self.textbox_spanShift = QLineEdit('0.5') self.textbox_spanShift.setMaximumWidth(50) label_spanShift = QLabel('Span shift') # DAC Frequencies. self.textedit_DACfreqs = QTextEdit() self.textedit_DACfreqs.setMaximumWidth(170) self.textedit_DACfreqs.setMaximumHeight(100) label_DACfreqs = QLabel('DAC Freqs:') # Input attenuation. self.textbox_atten_in = QLineEdit('0') self.textbox_atten_in.setMaximumWidth(50) label_atten_in = QLabel('Input atten.:') # offset in lut self.textbox_offset = QLineEdit('0') self.textbox_offset.setMaximumWidth(50) label_offset = QLabel('DAC sync. lag:') # offset in lut self.textbox_dds_shift = QLineEdit('144') self.textbox_dds_shift.setMaximumWidth(50) label_dds_shift = QLabel('DDS sync. lag:') # Power sweep range. self.textbox_powerSweepStart = QLineEdit('16') self.textbox_powerSweepStart.setMaximumWidth(50) label_powerSweepStart = QLabel('Start atten.:') self.textbox_powerSweepStop = QLineEdit('16') self.textbox_powerSweepStop.setMaximumWidth(50) label_powerSweepStop = QLabel('Stop atten:') # Save directory self.textbox_saveDir = QLineEdit('/home/sean/data/20120713/freqs.txt') self.textbox_saveDir.setMaximumWidth(250) label_saveDir = QLabel('Save directory:') label_saveDir.setMaximumWidth(150) # File with frequencies/attens self.textbox_freqFile = QLineEdit('/home/sean/data/20120713/freqs.txt') self.textbox_freqFile.setMaximumWidth(200) # Load freqs and attens from file. self.button_loadFreqsAttens = QPushButton("(2)Load freqs/attens") self.button_loadFreqsAttens.setMaximumWidth(200) self.connect(self.button_loadFreqsAttens, SIGNAL('clicked()'), self.loadFreqsAttens) # Rotate IQ loops. self.button_rotateLoops = QPushButton("(6)Rot. Loops") self.button_rotateLoops.setMaximumWidth(150) self.connect(self.button_rotateLoops, SIGNAL('clicked()'), self.rotateLoops) # Translate IQ loops. self.button_translateLoops = QPushButton("(7)Trans. Loops") self.button_translateLoops.setMaximumWidth(150) self.connect(self.button_translateLoops, SIGNAL('clicked()'), self.loadIQcenters) # DAC start button. self.button_startDAC = QPushButton("(4)Start DAC") self.button_startDAC.setMaximumWidth(200) self.connect(self.button_startDAC, SIGNAL('clicked()'), self.toggleDAC) # define DAC/DDS frequencies and load LUTs. self.button_define_LUTs= QPushButton("(3)Define LUTs") self.button_define_LUTs.setMaximumWidth(200) self.connect(self.button_define_LUTs, SIGNAL('clicked()'), self.define_LUTs) # Sweep LO self.button_sweepLO = QPushButton("(5)Sweep LO") self.button_sweepLO.setMaximumWidth(340) self.connect(self.button_sweepLO, SIGNAL('clicked()'), self.sweepLO) # Channel increment up 1. self.button_channelIncUp = QPushButton("+") self.button_channelIncUp.setMaximumWidth(50) self.connect(self.button_channelIncUp, SIGNAL('clicked()'), self.channelIncUp) # Channel increment down 1. self.button_channelIncDown = QPushButton("-") self.button_channelIncDown.setMaximumWidth(50) self.connect(self.button_channelIncDown, SIGNAL('clicked()'), self.channelIncDown) # Channel to measure self.textbox_channel = QLineEdit('0') self.textbox_channel.setMaximumWidth(40) label_channel = QLabel('Ch:') label_channel.setMaximumWidth(50) # Add widgets to window. gbox0 = QVBoxLayout() hbox00 = QHBoxLayout() hbox00.addWidget(self.textbox_roachIP) hbox00.addWidget(self.button_openClient) gbox0.addLayout(hbox00) hbox01 = QHBoxLayout() hbox01.addWidget(self.textbox_freqFile) hbox01.addWidget(self.button_loadFreqsAttens) gbox0.addLayout(hbox01) hbox02 = QHBoxLayout() hbox02.addWidget(label_saveDir) hbox02.addWidget(self.textbox_saveDir) gbox0.addLayout(hbox02) hbox03 = QHBoxLayout() hbox03.addWidget(label_loFreq) hbox03.addWidget(self.textbox_loFreq) gbox0.addLayout(hbox03) gbox1 = QVBoxLayout() gbox1.addWidget(label_DACfreqs) gbox1.addWidget(self.textedit_DACfreqs) hbox10 = QHBoxLayout() hbox10.addWidget(label_offset) hbox10.addWidget(self.textbox_offset) hbox11 = QHBoxLayout() hbox11.addWidget(label_dds_shift) hbox11.addWidget(self.textbox_dds_shift) gbox1.addLayout(hbox10) gbox1.addLayout(hbox11) gbox1.addWidget(self.button_define_LUTs) gbox1.addWidget(self.button_startDAC) gbox2 = QVBoxLayout() hbox20 = QHBoxLayout() hbox20.addWidget(label_atten_in) hbox20.addWidget(self.textbox_atten_in) hbox20.addWidget(label_powerSweepStart) hbox20.addWidget(self.textbox_powerSweepStart) hbox20.addWidget(label_powerSweepStop) hbox20.addWidget(self.textbox_powerSweepStop) gbox2.addLayout(hbox20) hbox21 = QHBoxLayout() hbox21.addWidget(label_loSpan) hbox21.addWidget(self.textbox_loSpan) hbox21.addWidget(self.button_sweepLO) gbox2.addLayout(hbox21) hbox22 = QHBoxLayout() hbox22.addWidget(label_channel) hbox22.addWidget(self.textbox_channel) hbox22.addWidget(self.button_channelIncDown) hbox22.addWidget(self.button_channelIncUp) gbox2.addLayout(hbox22) hbox23 = QHBoxLayout() hbox23.addWidget(self.button_rotateLoops) hbox23.addWidget(self.button_translateLoops) gbox2.addLayout(hbox23) hbox = QHBoxLayout() hbox.addLayout(gbox0) hbox.addLayout(gbox1) hbox.addLayout(gbox2) vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) vbox.addLayout(hbox) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def create_status_bar(self): self.status_text = QLabel("Awaiting orders.") self.statusBar().addWidget(self.status_text, 1) def create_menu(self): self.file_menu = self.menuBar().addMenu("&File") load_file_action = self.create_action("&Save plot", shortcut="Ctrl+S", slot=self.save_plot, tip="Save the plot") quit_action = self.create_action("&Quit", slot=self.close, shortcut="Ctrl+Q", tip="Close the application") self.add_actions(self.file_menu, (load_file_action, None, quit_action)) self.help_menu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut='F1', slot=self.on_about, tip='About the demo') self.add_actions(self.help_menu, (about_action,)) def add_actions(self, target, actions): for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def create_action( self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def save_plot(self): file_choices = "PNG (*.png)|*.png" path = unicode(QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def on_about(self): msg = """ Message to user goes here. """ QMessageBox.about(self, "MKID-ROACH software demo", msg.strip())
def plot_LO_fft(): # pik1_utils does all of this ... filename = WAIS + '/orig/xlob/TOT/JKB2d/X16a/RADnh3/bxds' trace_start, trace_end = 405450, 415450 # This is 8109 - 8309 in pik1 traces correction = pik1_utils.find_LO_params(filename, 2, 3437, trace_start, trace_end) print "LO correction: ", correction print "mag = %r, phs = %r" % (np.abs(correction), np.angle(correction)) # Plot the fft of the full traces input_sweeps = pik1_utils.load_raw_nh3_subset_gen( filename, 2, np.arange(trace_start, trace_end), 3437, 3200, 200) input_stacked = pik1_utils.coherent_stack_gen(input_sweeps, 50) radar_data = np.array([trace for trace in input_stacked]) radar_fft = np.fft.fft(radar_data, n=3200) fftfreq3200 = np.fft.fftfreq(3200, 1 / 50.) fig2 = Figure((10, 8)) canvas2 = FigureCanvas(fig2) ax2 = fig2.add_axes([0, 0, 1, 1]) ax2.imshow(np.abs(radar_fft), cmap='gray', aspect='auto') canvas2.print_figure('../FinalReport/figures/data_fft.jpg') # Plot the fft of the bottom portion only input_sweeps = pik1_utils.load_raw_nh3_subset_gen( filename, 2, np.arange(trace_start, trace_end), 3437, 3437, 200) input_stacked = pik1_utils.coherent_stack_gen(input_sweeps, 50) noise_data = np.array([trace[-800:] for trace in input_stacked]) noise_fft = np.fft.fft(noise_data, n=800) fftfreq800 = np.fft.fftfreq(800, 1 / 50.) fig3 = Figure((10, 8)) canvas3 = FigureCanvas(fig3) ax3 = fig3.add_axes([0, 0, 1, 1]) ax3.imshow(np.abs(noise_fft), cmap='gray', aspect='auto') canvas3.print_figure('../FinalReport/figures/noise_fft.jpg') # TODO: Maybe just to nice clean line plots showing full transect and bottom? fig4 = Figure((15, 5)) canvas4 = FigureCanvas(fig4) ax4 = fig4.add_axes([0.01, 0.2, 0.98, 0.8]) abs_noise_fft = np.sum(np.abs(noise_fft), axis=0) # cancel out constant term. abs_noise_fft[0] = 0 plot_noise_fft = abs_noise_fft / np.max(abs_noise_fft) ax4.plot(fftfreq800[1:400], plot_noise_fft[1:400], color='black') ax4.plot(fftfreq800[401:800], plot_noise_fft[401:800], color='black') # Cancel out the two biggest peaks. abs_noise_fft[160] = 0 abs_noise_fft[640] = 0 plot_noise_fft2 = abs_noise_fft / np.max(abs_noise_fft) ax4.plot(fftfreq800[1:400], plot_noise_fft2[1:400], color='darkgrey') ax4.plot(fftfreq800[401:800], plot_noise_fft2[401:800], color='darkgrey') abs_radar_fft = np.sum(np.abs(radar_fft), axis=0) plot_radar_fft = abs_radar_fft / np.max(abs_radar_fft) # Plot in two chunks to avoid the awkward line across the figure. ax4.plot(fftfreq3200[1:1600], plot_radar_fft[1:1600], color='red') ax4.plot(fftfreq3200[1601:3200], plot_radar_fft[1601:3200], color='red') ax.set_ylim([0, 1.05]) ax4.set_xlim([-25, 25]) ax4.tick_params(which='both', bottom=True, top=False, left=False, right=False, labelbottom=True, labeltop=False, labelleft=False, labelright=False, labelsize=18) for side in ['top', 'left', 'right']: ax4.spines[side].set_visible(False) ax4.set_xlabel('Frequency (MHz)', fontsize=24) canvas4.print_figure('../FinalReport/figures/LO_fft.jpg')
class AppForm(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle('Channelizer 2') self.create_menu() self.create_main_frame() self.create_status_bar() self.dacStatus = 'off' self.dramStatus = 'off' self.tapStatus = 'off' self.socketStatus = 'off' self.ch_all = [] self.attens = numpy.array([1. for i in range(256)]) self.sampleRate = 550e6 N_lut_entries = 2**16 self.freqRes = self.sampleRate/N_lut_entries #self.freqRes = 7812.5 self.zeroChannels = [0]*256 self.thresholds, self.medians = numpy.array([0.]*256), numpy.array([0.]*256) def openClient(self): self.roach = corr.katcp_wrapper.FpgaClient(self.textbox_roachIP.text(),7147) time.sleep(2) self.status_text.setText('connection established') self.button_openClient.setDisabled(True) def loadFIRcoeffs(self): N_freqs = len(map(float, unicode(self.textedit_DACfreqs.toPlainText()).split())) taps = 26 for ch in range(N_freqs): # If the dark count rate is very high, the channel will be zeroed. if self.zeroChannels[ch]: lpf = numpy.array([0.]*taps)*(2**11-1) else: print ch #lpf = numpy.array([1.]+[0]*(taps-1))*(2**11-1) # 26 tap, 25 us matched fir lpf = numpy.array([0.0875788844768 , 0.0840583257978 , 0.0810527406206 , 0.0779008825067 , 0.075106964962 , 0.0721712998256 , 0.0689723729398 , 0.066450095496 , 0.0638302570705 , 0.0613005685486 , 0.0589247737004 , 0.0565981917436 , 0.0544878914297 , 0.0524710948658 , 0.0503447054014 , 0.0483170854189 , 0.0463121066637 , 0.044504238059 , 0.0428469827102 , 0.0410615366471 , 0.0395570640218 , 0.0380071830756 , 0.0364836787854 , 0.034960959124 , 0.033456372241 , 0.0321854467182])*(2**11-1) #lpf = lpf[::-1] # 26 tap, lpf, 250 kHz, #lpf = numpy.array([-0 , 0.000166959420533 , 0.00173811663844 , 0.00420937801998 , 0.00333739357391 , -0.0056305703275 , -0.0212738104942 , -0.0318529375832 , -0.0193635986879 , 0.0285916612022 , 0.106763943766 , 0.18981814328 , 0.243495321192 , 0.243495321192 , 0.18981814328 , 0.106763943766 , 0.0285916612022 , -0.0193635986879 , -0.0318529375832 , -0.0212738104942 , -0.0056305703275 , 0.00333739357391 , 0.00420937801998 , 0.00173811663844 , 0.000166959420533 , -0])*(2**11-1) # 26 tap, lpf, 125 kHz. #lpf = numpy.array([0 , -0.000431898216436 , -0.00157886921107 , -0.00255492263971 , -0.00171727439076 , 0.00289724121972 , 0.0129123447233 , 0.0289345497995 , 0.0500906370566 , 0.0739622085341 , 0.0969821586979 , 0.115211955161 , 0.125291869266 , 0.125291869266 , 0.115211955161 , 0.0969821586979 , 0.0739622085341 , 0.0500906370566 , 0.0289345497995 , 0.0129123447233 , 0.00289724121972 , -0.00171727439076 , -0.00255492263971 , -0.00157886921107 , -0.000431898216436 , -0])*(2**11-1) # Generic 40 tap matched filter for 25 us lifetime pulse #lpf = numpy.array([0.153725595011 , 0.141052390733 , 0.129753816201 , 0.119528429291 , 0.110045314901 , 0.101336838027 , 0.0933265803805 , 0.0862038188673 , 0.0794067694409 , 0.0729543134914 , 0.0674101836798 , 0.0618283869464 , 0.0567253144676 , 0.0519730940444 , 0.047978953698 , 0.043791412767 , 0.0404560656757 , 0.0372466775252 , 0.0345000956808 , 0.0319243455811 , 0.0293425115323 , 0.0268372778298 , 0.0245216835234 , 0.0226817116475 , 0.0208024488535 , 0.0189575043357 , 0.0174290665862 , 0.0158791788119 , 0.0144611054123 , 0.0132599563305 , 0.0121083419203 , 0.0109003580368 , 0.0100328742978 , 0.00939328253743 , 0.00842247241585 , 0.00789304712484 , 0.00725494259117 , 0.00664528407122 , 0.00606688645845 , 0.00552041438208])*(2**11-1) #lpf = lpf[::-1] for n in range(taps/2): coeff0 = int(lpf[2*n]) coeff1 = int(lpf[2*n+1]) coeff0 = numpy.binary_repr(int(lpf[2*n]), 12) coeff1 = numpy.binary_repr(int(lpf[2*n+1]), 12) coeffs = int(coeff1+coeff0, 2) coeffs_bin = struct.pack('>l', coeffs) register_name = 'FIR_b' + str(2*n) + 'b' + str(2*n+1) self.roach.write(register_name, coeffs_bin) self.roach.write_int('FIR_load_coeff', (ch<<1) + (1<<0)) self.roach.write_int('FIR_load_coeff', (ch<<1) + (0<<0)) # Inactive channels will also be zeroed. lpf = numpy.array([0.]*taps) for ch in range(N_freqs, 256): for n in range(taps/2): #coeffs = struct.pack('>h', lpf[2*n]) + struct.pack('>h', lpf[2*n+1]) coeffs = struct.pack('>h', lpf[2*n+1]) + struct.pack('>h', lpf[2*n]) register_name = 'FIR_b' + str(2*n) + 'b' + str(2*n+1) self.roach.write(register_name, coeffs) self.roach.write_int('FIR_load_coeff', (ch<<1) + (1<<0)) self.roach.write_int('FIR_load_coeff', (ch<<1) + (0<<0)) print 'done loading fir.' def find_nearest(self, array, value): idx=(numpy.abs(array-value)).argmin() return idx def loadThresholds(self): """ Takes two time streams and concatenates together for a longer sample. median is used instead of mean. """ x = raw_input('Is the lamp off? ') Nsigma = float(self.textbox_Nsigma.text()) N_freqs = len(map(float, unicode(self.textedit_DACfreqs.toPlainText()).split())) self.thresholds, self.medians = numpy.array([0.]*N_freqs), numpy.array([0.]*N_freqs) steps = int(self.textbox_timeLengths.text()) L = 2**10 for ch in range(N_freqs): bin_data_phase = '' for n in range(steps): self.roach.write_int('ch_we', ch) self.roach.write_int('startSnap', 0) self.roach.write_int('snapPhase_ctrl', 1) self.roach.write_int('snapPhase_ctrl', 0) self.roach.write_int('startSnap', 1) time.sleep(0.001) bin_data_phase = bin_data_phase + self.roach.read('snapPhase_bram', 4*L) phase = [] for m in range(steps*L): phase.append(struct.unpack('>h', bin_data_phase[m*4+2:m*4+4])[0]) phase.append(struct.unpack('>h', bin_data_phase[m*4+0:m*4+2])[0]) phase = numpy.array(phase) #phase_avg = numpy.median(phase) #sigma = phase.std() n,bins= numpy.histogram(phase,bins=100) n = numpy.array(n,dtype='float32')/numpy.sum(n) tot = numpy.zeros(len(bins)) for i in xrange(len(bins)): tot[i] = numpy.sum(n[:i]) bins1 = .5*(bins[1:]+bins[:-1]) med = bins[self.find_nearest(tot,0.5)] thresh = bins[self.find_nearest(tot,0.05)] threshold = int(med-Nsigma*abs(med-thresh)) scale_to_angle = 360./2**16*4/numpy.pi #threshold = int((phase_avg - Nsigma*sigma)) # -25736 = -180 degrees if threshold < -25736: threshold = -25736 self.thresholds[ch] = scale_to_angle*threshold self.medians[ch] = scale_to_angle*med self.roach.write_int('capture_threshold', threshold) self.roach.write_int('capture_load_thresh', (ch<<1)+(1<<0)) self.roach.write_int('capture_load_thresh', (ch<<1)+(0<<0)) print "channel: ", ch, "median: ", scale_to_angle*med, "threshold: ", scale_to_angle*threshold #print "channel: ", ch, "avg: ", scale_to_angle*phase_avg, "sigma: ", scale_to_angle*sigma, "threshold: ", scale_to_angle*threshold def snapshot(self): ch_we = int(self.textbox_channel.text()) self.roach.write_int('ch_we', ch_we) #print self.roach.read_int('ch_we') steps = int(self.textbox_timeLengths.text()) L = 2**10 bin_data_phase = '' for n in range(steps): self.roach.write_int('startSnap', 0) self.roach.write_int('snapPhase_ctrl', 1) self.roach.write_int('snapPhase_ctrl', 0) self.roach.write_int('startSnap', 1) time.sleep(0.001) bin_data_phase = bin_data_phase + self.roach.read('snapPhase_bram', 4*L) phase = [] for m in range(steps*L): phase.append(struct.unpack('>h', bin_data_phase[m*4+2:m*4+4])[0]) phase.append(struct.unpack('>h', bin_data_phase[m*4+0:m*4+2])[0]) phase = numpy.array(phase)*360./2**16*4/numpy.pi self.axes1.clear() self.axes1.plot(phase, '.-', [self.thresholds[ch_we]]*2*L*steps, 'r.', [self.medians[ch_we]]*2*L*steps, 'g.') self.canvas.draw() def readPulses(self): scale_to_degrees = 360./2**12*4/numpy.pi channel_count = [0]*256 p1 = [[]]*256 timestamp = [[]]*256 for i in range(256): p1[i] = [0] timestamp[i] = [0] seconds = int(self.textbox_seconds.text()) self.roach.write_int('startBuffer', 1) for n in range(seconds): addr0 = self.roach.read_int('pulses_addr') time.sleep(1.0) addr1 = self.roach.read_int('pulses_addr') bin_data_0 = self.roach.read('pulses_bram0', 4*2**14) bin_data_1 = self.roach.read('pulses_bram1', 4*2**14) if addr1 >= addr0: total_counts = addr1-addr0 for n in range(addr0, addr1): raw_data_1 = struct.unpack('>L', bin_data_1[n*4:n*4+4])[0] raw_data_0 = struct.unpack('>L', bin_data_0[n*4:n*4+4])[0] ch = raw_data_1/2**24 channel_count[ch] = channel_count[ch] + 1 p1[ch].append((raw_data_1%2**12 - 2**11)*scale_to_degrees) timestamp[ch].append(raw_data_0%2**20) else: total_counts = addr1+2**14-addr0 for n in range(addr0, 2**14): raw_data_1 = struct.unpack('>L', bin_data_1[n*4:n*4+4])[0] raw_data_0 = struct.unpack('>L', bin_data_0[n*4:n*4+4])[0] ch = raw_data_1/2**24 channel_count[ch] = channel_count[ch] + 1 p1[ch].append((raw_data_1%2**12 - 2**11)*scale_to_degrees) timestamp[ch].append(raw_data_0%2**20) for n in range(0, addr1): raw_data_1 = struct.unpack('>L', bin_data_1[n*4:n*4+4])[0] raw_data_0 = struct.unpack('>L', bin_data_0[n*4:n*4+4])[0] ch = raw_data_1/2**24 channel_count[ch] = channel_count[ch] + 1 p1[ch].append((raw_data_1%2**12 - 2**11)*scale_to_degrees) timestamp[ch].append(raw_data_0%2**20) print total_counts self.roach.write_int('startBuffer', 0) print channel_count ch = int(self.textbox_channel.text()) numpy.savetxt('/home/sean/data/restest/test.dat', p1[ch]) # With lamp off, run "readPulses." If count rate is above 50, it's anamolous # and it's FIR should be set to 0. #for n in range(256): # if channel_count[n] > 100: # self.zeroChannels[n] = 1 #x = numpy.arange(-270, 0, 3) #y = numpy.histogram(data, 90) self.axes0.clear() self.axes0.plot(timestamp[ch], '.') self.canvas.draw() def channelInc(self): ch_we = int(self.textbox_channel.text()) ch_we = ch_we + 1 self.textbox_channel.setText(str(ch_we)) def toggleDAC(self): if self.dacStatus == 'off': print "Starting LUT...", self.roach.write_int('startDAC', 1) time.sleep(1) while self.roach.read_int('DRAM_LUT_rd_valid') != 0: self.roach.write_int('startDAC', 0) time.sleep(0.25) self.roach.write_int('startDAC', 1) time.sleep(1) print ".", print "done." #self.button_startDAC.setText('Stop DAC') self.dacStatus = 'on' self.status_text.setText('DAC turned on. ') else: self.roach.write_int('startDAC', 0) #self.button_startDAC.setText('Start DAC') self.dacStatus = 'off' self.status_text.setText('DAC turned off. ') def loadIQcenters(self): for ch in range(256): I_c = int(self.iq_centers[ch].real/2**3) Q_c = int(self.iq_centers[ch].imag/2**3) center = (I_c<<16) + (Q_c<<0) self.roach.write_int('conv_phase_centers', center) self.roach.write_int('conv_phase_load_centers', (ch<<1)+(1<<0)) self.roach.write_int('conv_phase_load_centers', 0) def select_bins(self, readout_freqs): fft_len = 2**9 bins = '' i = 0 residuals = [] for f in readout_freqs: fft_bin = int(round(f*fft_len/self.sampleRate)) fft_freq = fft_bin*self.sampleRate/fft_len freq_residual = round((f - fft_freq)/self.freqRes)*self.freqRes residuals.append(freq_residual) bins = bins + struct.pack('>l', fft_bin) self.roach.write_int('bins', fft_bin) self.roach.write_int('load_bins', (i<<1) + (1<<0)) self.roach.write_int('load_bins', (i<<1) + (0<<0)) i = i + 1 self.status_text.setText('done writing LUTs. ') return residuals def loadLUTs(self): self.scale_factor = 1. self.iq_centers = numpy.array([0.+0j]*256) # Loads the DAC and DDS LUTs from file. As well as the IQ loop centers. if self.dacStatus == 'off': self.roach.write_int('startDAC', 0) else: self.toggleDAC() saveDir = str(self.textbox_lutDir.text()) #saveDir = str(os.environ['PWD'] + '/'+ self.textbox_lutDir.text()) f = open(saveDir+'luts.dat', 'r') binaryData = f.read() self.roach.write('dram_memory', binaryData) x = numpy.loadtxt(saveDir+'centers.dat') N_freqs = len(x[:,0]) for n in range(N_freqs): self.iq_centers[n] = complex(x[n,0], x[n,1]) # Select and write bins for first stage of channelizer. freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) f_base = float(self.textbox_loFreq.text()) for n in range(len(freqs)): if freqs[n] < f_base: freqs[n] = freqs[n] + 512e6 freqs_dds = [0 for j in range(256)] for n in range(len(freqs)): freqs_dds[n] = round((freqs[n]-f_base)/self.freqRes)*self.freqRes freq_residuals = self.select_bins(freqs_dds) print 'LUTs and IQ centers loaded from file.' self.loadIQcenters() self.toggleDAC() def importFreqs(self): freqFile =str(self.textbox_freqFile.text()) x = numpy.loadtxt(freqFile) x_string = '' self.previous_scale_factor = x[0,0] N_freqs = len(x[1:,0]) for l in x[1:,0]: x_string = x_string + str(l*1e9) + '\n' self.iq_centers = numpy.array([0.+0j]*256) for n in range(N_freqs): #for n in range(256): self.iq_centers[n] = complex(x[n+1,1], x[n+1,2]) self.attens = x[1:,3] self.textedit_DACfreqs.setText(x_string) self.zeroChannels = [0]*256 def importFIRcoeffs(self): coeffsFile =str(self.textbox_coeffsFile.text()) x = numpy.loadtxt(coeffsFile) def file_dialog(self): print 'add dialog box here' #self.newdatadir = QFileDialog.getExistingDirectory(self, str("Choose SaveDirectory"), "",QFileDialog.ShowDirsOnly) #if len(self.newdatadir) > 0: # self.datadir = self.newdatadir # print self.datadir #self.ui.data_directory_lineEdit.setText(self.datadir) #put new path name in line edit # self.button_saveDir.setText(str(self.datadir)) def create_main_frame(self): self.main_frame = QWidget() # Create the mpl Figure and FigCanvas objects. self.dpi = 100 self.fig = Figure((9.0, 5.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) self.axes0 = self.fig.add_subplot(121) self.axes1 = self.fig.add_subplot(122) # Create the navigation toolbar, tied to the canvas self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) # Roach board's IP address self.textbox_roachIP = QLineEdit('10.0.0.10') self.textbox_roachIP.setMaximumWidth(200) label_roachIP = QLabel('Roach IP Address:') # Start connection to roach. self.button_openClient = QPushButton("(1)Open Client") self.button_openClient.setMaximumWidth(100) self.connect(self.button_openClient, SIGNAL('clicked()'), self.openClient) # DAC Frequencies. self.textedit_DACfreqs = QTextEdit() self.textedit_DACfreqs.setMaximumWidth(170) self.textedit_DACfreqs.setMaximumHeight(100) label_DACfreqs = QLabel('DAC Freqs:') # File with frequencies/attens self.textbox_freqFile = QLineEdit('/home/sean/data/20120812adr/freqs0.txt') self.textbox_freqFile.setMaximumWidth(200) # Import freqs from file. self.button_importFreqs = QPushButton("(2)Load freqs") self.button_importFreqs.setMaximumWidth(200) self.connect(self.button_importFreqs, SIGNAL('clicked()'), self.importFreqs) # File with FIR coefficients self.textbox_coeffsFile = QLineEdit('/home/sean/data/20110803/r0/') self.textbox_coeffsFile.setMaximumWidth(200) # Import FIR coefficients from file. self.button_importFIRcoeffs = QPushButton("Import FIR coeffs.") self.button_importFIRcoeffs.setMaximumWidth(200) self.connect(self.button_importFIRcoeffs, SIGNAL('clicked()'), self.importFIRcoeffs) # Channel increment by 1. self.button_channelInc = QPushButton("Channel++") self.button_channelInc.setMaximumWidth(100) self.connect(self.button_channelInc, SIGNAL('clicked()'), self.channelInc) # Load FIR coefficients. self.button_loadFIRcoeffs = QPushButton("(3)load FIR") self.button_loadFIRcoeffs.setMaximumWidth(170) self.connect(self.button_loadFIRcoeffs, SIGNAL('clicked()'), self.loadFIRcoeffs) # Load thresholds. self.button_loadThresholds = QPushButton("(4)load thresholds") self.button_loadThresholds.setMaximumWidth(170) self.connect(self.button_loadThresholds, SIGNAL('clicked()'), self.loadThresholds) # Channel to measure self.textbox_channel = QLineEdit('0') self.textbox_channel.setMaximumWidth(50) # threshold N*sigma self.textbox_Nsigma = QLineEdit('5.0') self.textbox_Nsigma.setMaximumWidth(50) label_Nsigma = QLabel('sigma ') # Time snapshot of a single channel self.button_snapshot = QPushButton("snapshot") self.button_snapshot.setMaximumWidth(170) self.connect(self.button_snapshot, SIGNAL('clicked()'), self.snapshot) # Read pulses self.button_readPulses = QPushButton("Read pulses") self.button_readPulses.setMaximumWidth(170) self.connect(self.button_readPulses, SIGNAL('clicked()'), self.readPulses) # Seconds for "read pulses." self.textbox_seconds = QLineEdit('10') self.textbox_seconds.setMaximumWidth(50) # lengths of 1 ms for defining thresholds. self.textbox_timeLengths = QLineEdit('10') self.textbox_timeLengths.setMaximumWidth(50) label_timeLengths = QLabel('mSeconds ') # Add widgets to window. gbox0 = QVBoxLayout() hbox00 = QHBoxLayout() hbox00.addWidget(self.textbox_roachIP) hbox00.addWidget(self.button_openClient) gbox0.addLayout(hbox00) hbox01 = QHBoxLayout() hbox01.addWidget(self.textbox_freqFile) hbox01.addWidget(self.button_importFreqs) gbox0.addLayout(hbox01) hbox02 = QHBoxLayout() hbox02.addWidget(self.textbox_coeffsFile) hbox02.addWidget(self.button_importFIRcoeffs) hbox02.addWidget(self.button_loadFIRcoeffs) gbox0.addLayout(hbox02) hbox03 = QHBoxLayout() hbox03.addWidget(self.textbox_timeLengths) hbox03.addWidget(label_timeLengths) hbox03.addWidget(self.textbox_Nsigma) hbox03.addWidget(label_Nsigma) hbox03.addWidget(self.button_loadThresholds) gbox0.addLayout(hbox03) gbox1 = QVBoxLayout() gbox1.addWidget(label_DACfreqs) gbox1.addWidget(self.textedit_DACfreqs) gbox2 = QVBoxLayout() hbox20 = QHBoxLayout() hbox20.addWidget(self.textbox_channel) hbox20.addWidget(self.button_channelInc) gbox2.addLayout(hbox20) gbox2.addWidget(self.button_snapshot) hbox21 = QHBoxLayout() hbox21.addWidget(self.textbox_seconds) hbox21.addWidget(self.button_readPulses) gbox2.addLayout(hbox21) hbox = QHBoxLayout() hbox.addLayout(gbox0) hbox.addLayout(gbox1) hbox.addLayout(gbox2) vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) vbox.addLayout(hbox) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def create_status_bar(self): self.status_text = QLabel("Awaiting orders.") self.statusBar().addWidget(self.status_text, 1) def create_menu(self): self.file_menu = self.menuBar().addMenu("&File") load_file_action = self.create_action("&Save plot",shortcut="Ctrl+S", slot=self.save_plot, tip="Save the plot") quit_action = self.create_action("&Quit", slot=self.close, shortcut="Ctrl+Q", tip="Close the application") self.add_actions(self.file_menu, (load_file_action, None, quit_action)) self.help_menu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut='F1', slot=self.on_about, tip='About the demo') self.add_actions(self.help_menu, (about_action,)) def add_actions(self, target, actions): for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def create_action( self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def save_plot(self): file_choices = "PNG (*.png)|*.png" path = unicode(QFileDialog.getSaveFileName(self, 'Save file', '',file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def on_about(self): msg = """ Message to user goes here. """ QMessageBox.about(self, "MKID-ROACH software demo", msg.strip())
def plot_trace(): # Trace 3900 in pik1 looks pretty nice. trace_idx = 3900 num_in_samples = 3437 num_out_samples = 3200 raw_y = 2 #(where to plot the raw data ...) raw_filename = WAIS + '/orig/xlob/TOT/JKB2d/X16a/RADnh3/bxds' # For plotting, I tried raw dB of trace ... I don't like it as much # If I try it again, gotta be careful - abs(elem) can cause overflow errors if we don't cast to float first! sweep_gen = pik1_utils.load_raw_nh3_subset_gen(raw_filename, 2, [50 * trace_idx], num_in_samples, num_out_samples, 0) raw_trace = [elem for elem in sweep_gen][0] raw_min = np.min(raw_trace) raw_range = 1.0 * np.max(raw_trace) - np.min(raw_trace) norm_raw_trace_ch2 = [ raw_y + (1.0 * elem - raw_min) / raw_range for elem in raw_trace ] sweep_gen = pik1_utils.load_raw_nh3_subset_gen(raw_filename, 1, [50 * trace_idx], num_in_samples, num_out_samples, 0) raw_trace = [elem for elem in sweep_gen][0] raw_min = np.min(raw_trace) raw_range = 1.0 * np.max(raw_trace) - np.min(raw_trace) norm_raw_trace_ch1 = [ raw_y + 1 + (1.0 * elem - raw_min) / raw_range for elem in raw_trace ] pik1_filename = 'TOT_LO' num_traces = pik1_utils.get_num_traces(pik1_filename, num_in_samples) pik1_data = np.memmap(pik1_filename, '>i4', mode='r', shape=(num_traces, num_out_samples)) processed_trace = pik1_data[trace_idx, :] trace_min = np.min(processed_trace) trace_range = np.max(processed_trace) - np.min(processed_trace) norm_processed_trace = [ 1.0 * (elem - trace_min) / trace_range for elem in processed_trace ] fig = Figure((30, 15)) canvas = FigureCanvas(fig) ax = fig.add_axes([0, 0, 1, 1]) ax.minorticks_off() ax.tick_params(which='both', bottom=False, top=False, left=False, right=False, labelbottom=False, labeltop=False, labelleft=False, labelright=False) for side in ['bottom', 'top', 'left', 'right']: ax.spines[side].set_visible(False) ax.set_xlim([0, 3200]) ylim = [0, raw_y + 2] ax.set_ylim(ylim) bar_y1 = 1.15 bar_y2 = 1.25 bar_y3 = 1.35 text_y1 = 1.0 text_y2 = 1.3 text_y3 = 1.4 fontsize = 50 xshift = 66 # WTF. Why do I have to offset the raw pulse to make it line up with the dechirped? # reference chirp 50-100 ax.plot([50, 100], [1.65, 1.65], color='red', linewidth=15) ax.text(25, 1.7, 'chirp', fontsize=50, color='red', horizontalalignment='left', verticalalignment='bottom') # "main bang" ax.plot([100, 540], [1.35, 1.35], '--', color='red', linewidth=15) ax.plot([100, 400], [1.35, 1.35], color='red', linewidth=15) ax.text(250, 1.4, 'main bang', fontsize=50, color='red', horizontalalignment='center', verticalalignment='bottom') # hardware blanking from 1-134 (in theory ... I don't trust this from the resulting plots ax.plot([xshift + 0, xshift + 134], [1.15, 1.15], color='red', linewidth=15) ax.text(xshift, 0.9, 'HW blanking', fontsize=50, color='red', horizontalalignment='left', verticalalignment='bottom') # software blanking from 1-200 ax.plot([0, 200], [0.85, 0.85], color='red', linewidth=15) ax.text(25, 0.6, 'SW blanking', fontsize=50, color='red', horizontalalignment='left', verticalalignment='bottom') # Surface at 555 ax.plot([555, 555], ylim, color='lightgray', linewidth=15) ax.text(575, 1.3, 'ice surface', fontsize=50, color='black', horizontalalignment='left', verticalalignment='bottom') # surface multiple at 950 ax.plot([950, 950], ylim, color='lightgray', linewidth=15) ax.text(970, 0.9, 'surface \nmultiple', fontsize=50, color='black', horizontalalignment='left', verticalalignment='bottom') # Crevasses at 1262 ax.plot([1262, 1262], ylim, color='lightgray', linewidth=15) ax.text(1282, 1.7, 'crevasse', fontsize=50, color='black', horizontalalignment='left', verticalalignment='bottom') # water at 1378 ax.plot([1378, 1378], ylim, color='lightgray', linewidth=15) ax.text(1398, 0.85, 'water', fontsize=50, color='black', horizontalalignment='left', verticalalignment='bottom') # off-nadir energy at 1400 - 1850 ax.plot([1400, 2100], [bar_y2, bar_y2], '--', color='red', linewidth=15) ax.plot([1500, 1850], [bar_y2, bar_y2], color='red', linewidth=15) ax.text(1750, text_y2, 'off-nadir energy', fontsize=50, color='red', horizontalalignment='center', verticalalignment='bottom') # bed multiple at 2204 ax.plot([2204, 2204], ylim, color='lightgray', linewidth=15) ax.text(2224, 0.5, 'bed multiple', fontsize=50, color='black', horizontalalignment='left', verticalalignment='bottom') # noise at 2400 - 3200 (Can I call this receiver noise?) ax.plot([2250, 3200], [bar_y2, bar_y2], '--', color='red', linewidth=15) ax.plot([2400, 3200], [bar_y2, bar_y2], color='red', linewidth=15) ax.text(2725, text_y2, 'receiver noise', fontsize=50, color='red', horizontalalignment='center', verticalalignment='bottom') # Ideas: # * have 3 different y values, for things that apply to one, the other, or both? # * shade the background to make it clearer what's going on? # or at least draw light vertical grey bars for the point events? # * Be sure to show where this trace comes from on a transect ... # ax1 = fig.add_axes([0.0, 0.05, 0.5, 0.9]) # ax1.set_ylim([3200, 0]) # ax1.minorticks_off() # ax1.tick_params(which='both', # bottom=False, top=False, left=False, right=False, # labelbottom=False, labeltop=False, labelleft=False, labelright=False) # for side in ['bottom', 'top', 'left', 'right']: # ax1.spines[side].set_visible(False) # ax2 = fig.add_axes([0.5, 0.05, 0.5, 0.9]) # ax2.plot(processed_trace, range(len(processed_trace)), 'k', markersize=1) # ax2.set_ylim([3200, 0]) # ax2.minorticks_on() # ax2.tick_params(which='both', direction='inout', labelsize=18, # bottom=False, top=False, left=True, right=False, # labelbottom=False, labeltop=False, labelleft=True, labelright=False) # for side in ['bottom', 'top', 'right']: # ax2.spines[side].set_visible(False) ax.plot(range(len(norm_processed_trace)), norm_processed_trace, 'k.', markersize=6) #ax.plot(range(len(norm_processed_trace)), norm_processed_trace, color='lightgrey', linewidth=1) ax.plot(np.arange(xshift, xshift + len(norm_raw_trace_ch2)), norm_raw_trace_ch2, 'k.', markersize=6) ax.plot(np.arange(xshift, xshift + len(norm_raw_trace_ch1)), norm_raw_trace_ch1, 'k.', markersize=6) canvas.print_figure('../FinalReport/figures/trace.jpg')
class CutePlot(QMainWindow): def __init__(self, parent=None): super(CutePlot, self).__init__(parent) # Default values for lower and upper bound self.LB_default = -10 self.UB_default = 10 # Create main plot area + menus + status bar self.create_main_frame() #self.textbox.setText() self.LB_UB_defaults() self.on_draw() self.statusBar() self.setWindowTitle('Graficador') self.create_menu() self.guardarImagen() def LB_UB_defaults(self): # Set default values for lower bound and upper bound self.lowerbound.setText(str(self.LB_default)) self.upperbound.setText(str(self.UB_default)) def create_main_frame(self): self.main_frame = QWidget() # 7x5 inches, 80 dots-per-inch self.dpi = 80 self.fig = Figure((7, 5), dpi = self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) self.guardarImagen() self.is_data = False self.axes = self.fig.add_subplot(111) # axis_state keeps track of how many subplots are present # axis_state = 0: main plot only # axis_state = 1: horizontal split (quadrants 1 and 2) # axis_state = 2: vertical split (quadrants 1 and 4) # axis_state = 3: show all 4 subplots self.axis_state = 0 self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) # f(x) textbox self.title = QLabel('<font size=4><em>f</em> (<em>x </em>) =</font>') self.textbox = QLineEdit() self.textbox.setMinimumWidth(200) self.connect(self.textbox, SIGNAL('returnPressed()'), self.on_draw) # Lowerbound and upperbound textboxes self.LB_title = QLabel('<font size=4>Min:</font>') self.lowerbound = QLineEdit() self.lowerbound.setMaximumWidth(30) self.connect(self.lowerbound, SIGNAL('returnPressed()'), self.on_draw) self.UB_title = QLabel('<font size=4>Max:</font>') self.upperbound = QLineEdit() self.upperbound.setMaximumWidth(30) self.connect(self.upperbound, SIGNAL('returnPressed()'), self.on_draw) # Plot button self.draw_button = QPushButton("&Plot") self.connect(self.draw_button, SIGNAL('clicked()'), self.on_draw) # Hold checkbox self.hold_cb = QCheckBox("&Hold") self.hold_cb.setChecked(False) self.connect(self.hold_cb, SIGNAL('stateChanged(int)'), self.on_minor_change) self.hold_cb.setToolTip('Prevent new plots from replacing old ones') self.hold_cb.setStatusTip('Prevent new plots from replacing old ones') # Log-x and log-y checkboxes self.logx_cb = QCheckBox("Log-&x") self.logx_cb.setChecked(False) self.connect(self.logx_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.logx_cb.setToolTip('Change x-axis to logarithmic scale') self.logx_cb.setStatusTip('Change x-axis to logarithmic scale') self.logy_cb = QCheckBox("Log-&y") self.logy_cb.setChecked(False) self.connect(self.logy_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.logy_cb.setToolTip('Change y-axis to logarithmic scale') self.logy_cb.setStatusTip('Change y-axis to logarithmic scale') # Truncated-log checkbox self.trunc_cb = QCheckBox("Show &Negative") self.trunc_cb.setChecked(False) self.connect(self.trunc_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.trunc_cb.setToolTip('Plot negative values of log-transformed functions') self.trunc_cb.setStatusTip('Plot negative values of log-transformed functions') # Grid checkbox self.grid_cb = QCheckBox("&Grid") self.grid_cb.setChecked(False) self.connect(self.grid_cb, SIGNAL('stateChanged(int)'), self.on_minor_change) self.grid_cb.setToolTip('Show grid') self.grid_cb.setStatusTip('Show grid') # Grid layout grid = QGridLayout() grid.setSpacing(10) gridCol = 0 for w in [self.title, self.textbox, self.LB_title, self.lowerbound, self.UB_title, self.upperbound, self.draw_button]: grid.addWidget(w, 0, gridCol) gridCol += 1 grid2 = QGridLayout() grid2.setSpacing(10) gridCol = 0 for w in [self.logx_cb, self.logy_cb, self.trunc_cb, self.hold_cb, self.grid_cb]: grid2.addWidget(w, 0, gridCol) gridCol += 1 vbox = QVBoxLayout() vbox.addLayout(grid) vbox.addLayout(grid2) vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def on_minor_change(self): self.on_draw(self.is_data) def on_draw(self, *args): # Get x-domain from user input self.LB_input = unicode(self.lowerbound.text()) self.UB_input = unicode(self.upperbound.text()) # Message box error if the domain inputs aren't int or float types # If float, round to the nearest 0.1 round_to = 10 try: self.LB_float = int(self.LB_input)*round_to self.UB_float = int(self.UB_input)*round_to except: self.LB_UB_defaults() QMessageBox.question(self, 'Error', '<center>Minimum and maximum values must be<br />\ integer or floating-point numbers.</center>', QMessageBox.Ok) # Make sure UB > LB if self.UB_float <= self.LB_float: self.LB_UB_defaults() QMessageBox.question(self, 'Error', '<center>Maximum must be greater\ than minimum value.</center>', QMessageBox.Ok) # If plotting a function, then get x and y values if len(args) == 0: self.is_data = False # Set x values (/round_to is to use range() with floating-point numbers) self.input_x = range(self.LB_float, self.UB_float + 1) self.input_x = [i/float(round_to) for i in self.input_x] # Calculate f(x) values for specified function fx = unicode(self.textbox.text()) # If the f(x) field is empty, then default to y = 0 plot if fx == '': self.y = [0 for i in self.input_x] # Otherwise, evaluate the specified function and get ready to plot else: # Replace exp with numbers fx = fx.replace('exp', str(exp(1)) + '**') # Allow users to enter ^ for powers (replace ^ with **) fx = fx.replace('^', '**') # Try and evaluate; if there is an error, then shift slightly to the right try: self.y = [eval(fx) for x in self.input_x] except: fx = fx.replace('x', '(x + 10**(-6))') self.y = [eval(fx) for x in self.input_x] self.plot_symbol = '-' if self.is_data: self.plot_symbol = 'o' # If the hold box is checked, then new plots do not erase old ones new_state = self.quad_check() if self.axis_state == 0: self.axes.hold(self.hold_cb.isChecked()) else: if self.hold_cb.isChecked(): # If 'hold' is checked, see what quadrants will be shown # - if the quadrant state changes, remove subplots # - otherwise retain subplots if self.axis_state == 0 and new_state == 0: self.axes.hold(self.hold_cb.isChecked()) elif self.axis_state == 3 and new_state == 3: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q2.hold(self.hold_cb.isChecked()) self.axes_Q3.hold(self.hold_cb.isChecked()) self.axes_Q4.hold(self.hold_cb.isChecked()) elif self.axis_state == 1 and new_state == 1: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q2.hold(self.hold_cb.isChecked()) elif self.axis_state == 2 and new_state == 2: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q4.hold(self.hold_cb.isChecked()) else: self.remove_subplots() else: self.remove_subplots() # If show negative box is unchecked if not self.trunc_cb.isChecked(): self.add_main() self.axes.plot(self.input_x, self.y, self.plot_symbol) if not self.logx_cb.isChecked() and not self.logy_cb.isChecked(): self.axes.set_xscale('linear') self.axes.set_yscale('linear') elif self.logx_cb.isChecked() and not self.logy_cb.isChecked(): self.axes.set_xscale('log') self.axes.set_yscale('linear') elif not self.logx_cb.isChecked() and self.logy_cb.isChecked(): self.axes.set_xscale('linear') self.axes.set_yscale('log') else: self.axes.set_xscale('log') self.axes.set_yscale('log') else: # Linear plot #if not self.logx_cb.isChecked() and not self.logy_cb.isChecked(): if new_state == 0: self.add_main() self.axes.plot(self.input_x,self.y,self.plot_symbol) # Log x, linear y plot #elif self.logx_cb.isChecked() and not self.logy_cb.isChecked(): elif new_state == 1: if not self.trunc_cb.isChecked(): self.add_main() self.axes.semilogx(self.input_x,self.y,self.plot_symbol) else: self.trunc_logx() # Linear x, log y plot #elif not self.logx_cb.isChecked() and self.logy_cb.isChecked(): elif new_state == 2: if not self.trunc_cb.isChecked(): self.add_main() self.axes.semilogy(self.input_x,self.y,self.plot_symbol) else: self.trunc_logy() # Log-log plot else: if not self.trunc_cb.isChecked(): self.add_main() self.axes.loglog(self.input_x,self.y,self.plot_symbol) else: self.trunc_loglog() # Add grid if grid checkbox is checked if self.axis_state == 0: self.axes.grid(self.grid_cb.isChecked()) else: if hasattr(self, 'axes_Q1'): self.axes_Q1.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q2'): self.axes_Q2.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q3'): self.axes_Q3.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q4'): self.axes_Q4.grid(self.grid_cb.isChecked()) self.axes.set_xlabel('$x$') self.axes.set_ylabel('$f(x)$') self.canvas.draw() self.guardarImagen() def remove_subplots(self): # Remove all subplots and axis flip flags if hasattr(self, 'axes_Q1'): self.fig.delaxes(self.axes_Q1) del self.axes_Q1 if hasattr(self, 'axes_Q2'): self.fig.delaxes(self.axes_Q2) del self.axes_Q2 if hasattr(self, 'flip_Q2'): del self.flip_Q2 if hasattr(self, 'axes_Q3'): self.fig.delaxes(self.axes_Q3) del self.axes_Q3 del self.flip_Q3 if hasattr(self, 'axes_Q4'): self.fig.delaxes(self.axes_Q4) del self.axes_Q4 if hasattr(self, 'flip_Q4'): del self.flip_Q4 def add_main(self): # Reinsert the main plot if self.axis_state > 0: self.remove_subplots() self.axes = self.fig.add_subplot(111) self.axis_state = 0 def create_menu(self): exitAction = QAction('Quit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit application') exitAction.triggered.connect(self.close) menubar = self.menuBar() fileMenu = menubar.addMenu('&File') save_plot_action = self.create_action("&Save plot", shortcut = "Ctrl+S", slot = self.save_plot, tip = "Save image to file") import_data_action = self.create_action("&Import data", shortcut = "Ctrl+I", slot = self.import_data, tip = "Import data from file") fileMenu.addAction(save_plot_action) fileMenu.addAction(import_data_action) fileMenu.addAction(exitAction) helpMenu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut = 'F1', slot = self.on_about, tip = 'About CutePlot') helpMenu.addAction(about_action) def create_action(self, text, slot = None, shortcut = None, icon = None, tip = None, checkable = False, signal = "triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def save_plot(self): file_choices = "PNG (*.png)" path = unicode(QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi = self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def import_data(self): file_choices = "*.csv;;*.txt;;*.tab;;*.dat;;*.*" self.path = QFileDialog.getOpenFileName(self, 'Import data', '', file_choices) if self.path: datafile = open(self.path[0], 'r') if datafile: self.is_data = True delimiter = ',' input_xy = [map(float, line.strip().split(delimiter)) for line in datafile] self.input_x, self.y = [[row[col] for row in input_xy] for col in [0, 1]] datafile.close() self.statusBar().showMessage('Imported data', 2000) self.on_draw(self.is_data) def on_about(self): msg = """<center><b>CutePlot v. 0.1</b></center> <center>Free, open-source plotting program,<br /> written in Python (PySide/Qt + matplotlib).</center> <center>(c) Jack Peterson, 2012</center> """ QMessageBox.about(self, "About", msg.strip()) def quad_check(self): # Q = quadrant Q1 = False Q2 = False Q3 = False Q4 = False # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1 = True elif self.input_x[j] < 0 and self.y[j] > 0: Q2 = True elif self.input_x[j] < 0 and self.y[j] < 0: Q3 = True elif self.input_x[j] > 0 and self.y[j] < 0: Q4 = True if (Q3 or (Q2 and Q4) or ((Q2 or Q4) and self.axis_state == 3)) and self.logx_cb.isChecked() and self.logy_cb.isChecked(): new_state = 3 elif (Q2 and self.logx_cb.isChecked()) or (self.hold_cb.isChecked() and self.axis_state == 1): new_state = 1 elif (Q4 and self.logy_cb.isChecked()) or (self.hold_cb.isChecked() and self.axis_state == 2): new_state = 2 else: new_state = 0 return new_state def trunc_logx(self): # Q = quadrant Q1_x = [] Q1_y = [] Q2_x = [] Q2_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] > 0: Q2_x.append(-self.input_x[j]) Q2_y.append(self.y[j]) # If only Q1 is populated, then use an ordinary semilogx plot if Q2_x == [] and not self.hold_cb.isChecked(): self.add_main() self.axes.semilogx(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) if self.axis_state == 2 or self.axis_state == 3: self.axis_state = 3 else: self.axis_state = 1 # Create 2 subplots self.axes_Q1 = self.fig.add_subplot(122) self.axes_Q2 = self.fig.add_subplot(121) self.axes_Q1.autoscale(enable = True) self.axes_Q2.autoscale(enable = True) self.axes_Q1.semilogx(Q1_x, Q1_y, self.plot_symbol) self.axes_Q2.semilogx(Q2_x, Q2_y, self.plot_symbol) # Reverse Q2 x-axis if not hasattr(self, 'flip_Q2'): self.flip_Q2 = True self.axes_Q2.set_xlim(self.axes_Q2.get_xlim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_xticklabels([]) else: try: x_UB_Q1 = int(ceil(log10(max(Q1_x)))) x_LB_Q1 = int(floor(log10(min(Q1_x)))) except: x_UB_Q1 = 2 x_LB_Q1 = -1 Q1_xlabels = [] for i in range(x_LB_Q1, x_UB_Q1 + 1): Q1_xlabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_xticklabels(Q1_xlabels) self.axes_Q1.xaxis.tick_bottom() self.axes_Q1.yaxis.tick_right() # Q2 axes if Q2_x == [] and not self.hold_cb.isChecked(): self.axes_Q2.set_xticklabels([]) else: try: x_UB_Q2 = int(ceil(log10(max(Q2_x)))) x_LB_Q2 = int(floor(log10(min(Q2_x)))) except: x_UB_Q2 = 2 x_LB_Q2 = -1 Q2_xlabels = [] for i in range(x_LB_Q2, x_UB_Q2 + 1): Q2_xlabels.append('$-10^{%s}$' % str(i)) self.axes_Q2.set_xticklabels(Q2_xlabels) self.axes_Q2.xaxis.tick_bottom() self.axes_Q2.yaxis.tick_left() def trunc_logy(self): # Q = quadrant Q1_x = [] Q1_y = [] Q4_x = [] Q4_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] > 0 and self.y[j] < 0: Q4_x.append(self.input_x[j]) Q4_y.append(-self.y[j]) # If only Q1 is populated, then use an ordinary semilogy plot if Q4_x == [] and not self.hold_cb.isChecked(): self.add_main() self.axes.semilogy(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) if self.axis_state == 1 or self.axis_state == 3: self.axis_state = 3 else: self.axis_state = 2 # Create 2 subplots self.axes_Q1 = self.fig.add_subplot(211) self.axes_Q4 = self.fig.add_subplot(212) self.axes_Q1.autoscale(enable = True) self.axes_Q4.autoscale(enable = True) self.axes_Q1.semilogy(Q1_x, Q1_y, self.plot_symbol) self.axes_Q4.semilogy(Q4_x, Q4_y, self.plot_symbol) # Reverse Q4 y-axis if not hasattr(self, 'flip_Q4'): self.flip_Q4 = True self.axes_Q4.set_ylim(self.axes_Q4.get_ylim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_yticklabels([]) else: try: y_UB_Q1 = int(ceil(log10(max(Q1_y)))) y_LB_Q1 = int(floor(log10(min(Q1_y)))) except: y_UB_Q1 = 2 y_LB_Q1 = -1 Q1_ylabels = [] for i in range(y_LB_Q1, y_UB_Q1 + 1): Q1_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_yticklabels(Q1_ylabels) self.axes_Q1.xaxis.tick_top() self.axes_Q1.yaxis.tick_right() # Q4 axes if Q4_x == [] and not self.hold_cb.isChecked(): self.axes_Q4.set_yticklabels([]) else: try: y_UB_Q4 = int(ceil(log10(max(Q4_y)))) y_LB_Q4 = int(floor(log10(min(Q4_y)))) except: y_UB_Q4 = 2 y_LB_Q4 = -1 Q4_ylabels = [] for i in range(y_LB_Q4, y_UB_Q4 + 1): Q4_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q4.set_yticklabels(Q4_ylabels) self.axes_Q4.xaxis.tick_bottom() self.axes_Q4.yaxis.tick_right() def trunc_loglog(self): # Q = quadrant Q1_x = [] Q1_y = [] Q2_x = [] Q2_y = [] Q3_x = [] Q3_y = [] Q4_x = [] Q4_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] > 0: Q2_x.append(-self.input_x[j]) Q2_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] < 0: Q3_x.append(-self.input_x[j]) Q3_y.append(-self.y[j]) elif self.input_x[j] > 0 and self.y[j] < 0: Q4_x.append(self.input_x[j]) Q4_y.append(-self.y[j]) # If only Q1 is populated, then use an ordinary loglog plot if Q2_x == [] and Q3_x == [] and Q4_x == [] and not self.hold_cb.isChecked(): self.add_main() self.axes.loglog(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) self.axis_state = 3 # Create 4 subplots self.axes_Q1 = self.fig.add_subplot(222) self.axes_Q2 = self.fig.add_subplot(221) self.axes_Q3 = self.fig.add_subplot(223) self.axes_Q4 = self.fig.add_subplot(224) self.axes_Q1.autoscale(enable = True) self.axes_Q2.autoscale(enable = True) self.axes_Q3.autoscale(enable = True) self.axes_Q4.autoscale(enable = True) self.axes_Q1.loglog(Q1_x, Q1_y, self.plot_symbol) self.axes_Q2.loglog(Q2_x, Q2_y, self.plot_symbol) self.axes_Q3.loglog(Q3_x, Q3_y, self.plot_symbol) self.axes_Q4.loglog(Q4_x, Q4_y, self.plot_symbol) if not hasattr(self, 'flip_Q3'): self.flip_Q3 = True # Reverse Q2 x-axis self.axes_Q2.set_xlim(self.axes_Q2.get_xlim()[::-1]) # Reverse Q3 x- and y-axes self.axes_Q3.set_xlim(self.axes_Q3.get_xlim()[::-1]) self.axes_Q3.set_ylim(self.axes_Q3.get_ylim()[::-1]) # Reverse Q4 y-axis self.axes_Q4.set_ylim(self.axes_Q4.get_ylim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_xticklabels([]) self.axes_Q1.set_yticklabels([]) else: try: x_UB_Q1 = int(ceil(log10(max(Q1_x)))) y_UB_Q1 = int(ceil(log10(max(Q1_y)))) x_LB_Q1 = int(floor(log10(min(Q1_x)))) y_LB_Q1 = int(floor(log10(min(Q1_y)))) except: x_UB_Q1 = 2 y_UB_Q1 = 2 x_LB_Q1 = -1 y_LB_Q1 = -1 Q1_xlabels = [] Q1_ylabels = [] for i in range(x_LB_Q1, x_UB_Q1 + 1): Q1_xlabels.append('$10^{%s}$' % str(i)) for i in range(y_LB_Q1, y_UB_Q1 + 1): Q1_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_xticklabels(Q1_xlabels) self.axes_Q1.set_yticklabels(Q1_ylabels) self.axes_Q1.xaxis.tick_top() self.axes_Q1.yaxis.tick_right() # Q2 axes if Q2_x == [] and not self.hold_cb.isChecked(): self.axes_Q2.set_xticklabels([]) self.axes_Q2.set_yticklabels([]) else: try: x_UB_Q2 = int(ceil(log10(max(Q2_x)))) y_UB_Q2 = int(ceil(log10(max(Q2_y)))) x_LB_Q2 = int(floor(log10(min(Q2_x)))) y_LB_Q2 = int(floor(log10(min(Q2_y)))) except: x_UB_Q2 = 2 y_UB_Q2 = 2 x_LB_Q2 = -1 y_LB_Q2 = -1 Q2_xlabels = [] Q2_ylabels = [] for i in range(x_LB_Q2, x_UB_Q2 + 1): Q2_xlabels.append('$-10^{%s}$' % str(i)) for i in range(y_LB_Q2, y_UB_Q2 + 1): Q2_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q2.set_xticklabels(Q2_xlabels) self.axes_Q2.set_yticklabels(Q2_ylabels) self.axes_Q2.xaxis.tick_top() self.axes_Q2.yaxis.tick_left() # Q3 axes if Q3_x == [] and not self.hold_cb.isChecked(): self.axes_Q3.set_xticklabels([]) self.axes_Q3.set_yticklabels([]) else: try: x_UB_Q3 = int(ceil(log10(max(Q3_x)))) y_UB_Q3 = int(ceil(log10(max(Q3_y)))) x_LB_Q3 = int(floor(log10(min(Q3_x)))) y_LB_Q3 = int(floor(log10(min(Q3_y)))) except: x_UB_Q3 = 2 y_UB_Q3 = 2 x_LB_Q3 = -1 y_LB_Q3 = -1 Q3_xlabels = [] Q3_ylabels = [] for i in range(x_LB_Q3, x_UB_Q3 + 1): Q3_xlabels.append('$-10^{%s}$' % str(i)) for i in range(y_LB_Q3, y_UB_Q3 + 1): Q3_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q3.set_xticklabels(Q3_xlabels) self.axes_Q3.set_yticklabels(Q3_ylabels) self.axes_Q3.xaxis.tick_bottom() self.axes_Q3.yaxis.tick_left() # Q4 axes if Q4_x == [] and not self.hold_cb.isChecked(): self.axes_Q4.set_xticklabels([]) self.axes_Q4.set_yticklabels([]) else: try: x_UB_Q4 = int(ceil(log10(max(Q4_x)))) y_UB_Q4 = int(ceil(log10(max(Q4_y)))) x_LB_Q4 = int(floor(log10(min(Q4_x)))) y_LB_Q4 = int(floor(log10(min(Q4_y)))) except: x_UB_Q4 = 2 y_UB_Q4 = 2 x_LB_Q4 = -1 y_LB_Q4 = -1 Q4_xlabels = [] Q4_ylabels = [] for i in range(x_LB_Q4, x_UB_Q4 + 1): Q4_xlabels.append('$10^{%s}$' % str(i)) for i in range(y_LB_Q4, y_UB_Q4 + 1): Q4_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q4.set_xticklabels(Q4_xlabels) self.axes_Q4.set_yticklabels(Q4_ylabels) self.axes_Q4.xaxis.tick_bottom() self.axes_Q4.yaxis.tick_right() def guardarImagen(self): path = os.path.abspath("untitled.png") self.canvas.resize(460,261 ) self.canvas.print_figure(path, dpi = self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) self.canvas.resize(560,361 )
def plot_filter_freqs(): ''' Shows the various frequencies within the system, and what the filter does. ''' # Each bin is one sample; sampled at 50Mhz nbins = 3200 # Time of each sample, in us tt = np.arange(0, nbins / 50., 1 / 50.) # 70MHz local oscillator signal y70 = np.sin(2 * np.pi * tt * 70) fft70 = np.fft.fft(y70, n=nbins) # 140MHz - LO's first harmonic y140 = np.sin(2 * np.pi * tt * 140) fft140 = np.fft.fft(y140, n=nbins) # Reference chirp used for actual pik1 dechirping ref_chirp = pik1_utils.generate_reference_chirp() y_ref = np.zeros(nbins) y_ref[100:100 + len(ref_chirp)] = ref_chirp fft_ref = np.fft.fft(y_ref, n=nbins) # theoretical reference chirp theoretical_chirp = pik1_utils.generate_theoretical_chirp() y_theory = np.zeros(nbins) y_theory[100:100 + len(theoretical_chirp)] = theoretical_chirp fft_theory = np.fft.fft(y_theory, n=nbins) # Frequency of fft bins, in MHz fftfreq = np.fft.fftfreq(nbins, 1 / 50.) # ideal chirp frequency content fft_ideal = [ 1.0 if 2.5 <= np.abs(elem) <= 17.5 else 0.0 for elem in fftfreq ] # Hanning filter hfilter = pik1_utils.generate_hfilter(nbins) fig = Figure((15, 3.5)) canvas = FigureCanvas(fig) ax = fig.add_axes([0.05, 0.25, 0.9, 0.7]) ax.plot(fftfreq, np.abs(fft_ref) / np.max(np.abs(fft_ref)), linewidth=1, color='black', label='pik1 reference chirp') ax.plot(fftfreq, np.abs(fft_theory) / np.max(np.abs(fft_theory)), linewidth=2, color='darkgrey', label='theoretical reference chirp') ax.plot(fftfreq, np.abs(fft_ideal) / np.max(np.abs(fft_ideal)), linewidth=2, color='lightgrey', label='theoretical reference chirp') ax.plot(fftfreq, np.abs(fft70) / np.max(np.abs(fft70)), linewidth=2, color='blue', label='FFT of 70MHz signal') ax.plot(fftfreq, np.abs(fft140) / np.max(np.abs(fft140)), linewidth=2, color='blue', label='FFT of 1st harmonic of LO') ax.plot(fftfreq, hfilter / np.max(hfilter), linewidth=2, color='red', label='Hamming filter') ax.set_ylim([0, 1.05]) ax.set_xlim([-25, 25]) ax.set_xlabel('Frequency (MHz)', fontsize=24) #ax.legend() ax.tick_params(which='both', bottom=True, top=False, left=False, right=False, labelbottom=True, labeltop=False, labelleft=False, labelright=False, labelsize=18) for side in ['top', 'left', 'right']: ax.spines[side].set_visible(False) canvas.print_figure('../FinalReport/figures/filter_frequencies.png')
class View(QWidget): def __init__(self, parent = None): super(View, self).__init__(parent) self.__watchdog = Watchdog.Watchdog() self.__watchdog.fileChanged.connect(self.refreshAll) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar = NavigationToolbar(self.canvas, self) self.navigationLayout = QHBoxLayout() layout = QHBoxLayout(self) self.navigations = [] self.addNavigation(True) self.filters = [ Filters.Lowpass(), Filters.Deconvolve(), Filters.Rotate() ] filterLayout = QVBoxLayout() for f in self.filters: filterLayout.addWidget(f) f.filterChanged.connect(self.plot) filterLayout.addStretch() addIcon = QIcon.fromTheme('list-add') addNaviButton = QPushButton(addIcon, 'Add navigation', self) addNaviButton.clicked.connect(self.addNavigation) self.maxFreq = QDoubleSpinBox(self) self.maxFreq.setValue(10.0) self.maxFreq.setVisible(False) self.maxFreq.valueChanged.connect(self.plot) spectrumIcon = QIcon.fromTheme('network-wireless') self.spectrum = QPushButton(spectrumIcon, 'Spectrum', self) self.spectrum.setCheckable(True) self.spectrum.clicked.connect(self.plot) self.spectrum.toggled.connect(self.maxFreq.setVisible) self.diff = QPushButton('Diff', self) self.diff.setCheckable(True) self.diff.clicked.connect(self.plot) self.diff.clicked.connect(self.spectrum.setHidden) self.spectrum.toggled.connect(self.diff.setHidden) autoRefresh = QPushButton(QIcon.fromTheme('view-refresh'), 'Auto', self) autoRefresh.setCheckable(True) autoRefresh.clicked.connect(self.__watchdog.toggle) saveAll = QPushButton(QIcon.fromTheme('document-save'), '', self) saveAll.clicked.connect(self.savePlots) toolLayout = QHBoxLayout() toolLayout.addWidget(addNaviButton) toolLayout.addWidget(self.diff) toolLayout.addWidget(self.spectrum) toolLayout.addWidget(self.maxFreq) toolLayout.addWidget(autoRefresh) toolLayout.addWidget(saveAll) toolLayout.addWidget(toolbar) plotLayout = QVBoxLayout() plotLayout.addLayout(toolLayout) plotLayout.addWidget(self.canvas) layout.addLayout(self.navigationLayout) layout.addLayout(plotLayout) layout.addLayout(filterLayout) def addNavigation(self, noclose = False): navigation = Navigation.Navigation(noclose) navigation.activeItemChanged.connect(self.plot) navigation.folderChanged.connect(self.navigationFolderChanged) navigation.close.connect(self.closeNavigation) navigation.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Minimum) self.navigationLayout.addWidget(navigation) self.navigations.append(navigation) def navigationFolderChanged(self, oldFolder, newFolder): self.__watchdog.removeFolder(oldFolder) self.__watchdog.addFolder(newFolder) def closeNavigation(self, widget): self.navigations.remove(widget) self.navigationLayout.removeWidget(widget) widget.deleteLater() self.plot() def refreshAll(self): for navigation in self.navigations: navigation.refreshFolder() def plot(self): wfc = [wf for nav in self.navigations for wf in nav.getActiveWaveforms()] for filt in self.filters: if filt.isChecked(): for wf in wfc: filt.apply(wf) if self.diff.isChecked() and len(wfc) > 0: wf0 = wfc.pop() for nWf, wf in enumerate(wfc): wfc[nWf].subtract(wf0) names = set([name for wf in wfc for name in wf.waveforms.iterkeys()]) numPlots = len(names) self.figure.clear() if numPlots > 0: names = list(names) names.sort() numRows = math.ceil(math.sqrt(numPlots)); numCols = math.ceil(numPlots / numRows) subplots = dict() for i in range(len(names)): subplots[ names[i] ] = self.figure.add_subplot(numRows, numCols, i+1) for nWf, wf in enumerate(wfc): for name, waveform in wf.waveforms.iteritems(): p = subplots[name] if self.spectrum.isChecked(): n = len(waveform) dt = wf.time[1]-wf.time[0] # assume equally spaced samples f = scipy.fftpack.fftfreq(n, dt) W = dt * scipy.fftpack.fft(waveform) maxFreqIndices = numpy.argwhere(f > self.maxFreq.value()) L = maxFreqIndices[0] if len(maxFreqIndices) > 0 else n/2 p.loglog(f[1:L], numpy.absolute(W[1:L]), label=str(nWf)) p.set_xlabel('f [Hz]') elif self.diff.isChecked(): p.plot(wf.time, waveform, label='{}-0'.format(nWf+1)) p.set_xlabel('t (s)') else: p.plot(wf.time, waveform, label=str(nWf)) p.set_xlabel('t (s)') p.set_ylabel(name) self.figure.tight_layout() for i in range(len(names)): subplots[ names[i] ].legend(prop={'size':8}, frameon=False) self.canvas.draw() def savePlots(self): filetypes = self.canvas.get_supported_filetypes_grouped() defaultFiletype = self.canvas.get_default_filetype() filters = [] selectedFilter = '' for name, extensions in sorted(filetypes.iteritems()): filtr = '{0} ({1})'.format(name, ' '.join(['*.{0}'.format(ext) for ext in extensions])) if defaultFiletype in extensions: selectedFilter = filtr filters.append(filtr) fileName, filtr = QFileDialog.getSaveFileNameAndFilter(self, 'Choose a save location.', '', ';;'.join(filters), selectedFilter) fileName = os.path.splitext(str(fileName))[0] extension = re.search(r'\*(\.[a-zA-Z]+)', str(filtr)).group(1) maxRow = min([nav.numberOfRows() for nav in self.navigations]) for row in range(maxRow): for nav in self.navigations: nav.selectWaveformAt(row) self.plot() self.canvas.print_figure('{0}{1:03}{2}'.format(fileName, row+1, extension))
class InteractiveDetector(QMainWindow): """ Click on a point to select and highlight it -- the data that generated the point will be shown in the lower axes. Use the 'n' and 'p' keys to browse through the next and previous points @see http://matplotlib.org/examples/event_handling/data_browser.html @see http://matplotlib.org/examples/event_handling/poly_editor.html """ def __init__(self, Seq, hpe, importer, filename_detection, filename_log, subset_idxs, start_idx, parent=None): super(InteractiveDetector, self).__init__(parent) self.setWindowTitle('Detection Tool') self.dpi = 100 self.panned = False self.lastind = 0 self._ind = None self.epsilon = 15 self.press = (0, 0, 0, 0) self.hpe = hpe self.importer = importer self.path_prefix = '' if filename_log is not None: if os.path.exists(filename_log): self.file_log = open(filename_log, 'a') else: self.file_log = open(filename_log, 'w') else: self.file_log = None self.start_time = time.time() self.subset_idxs = subset_idxs if len(self.subset_idxs) > 0: self.curFrame = self.subset_idxs[0] else: self.curFrame = 0 self._seq = Seq self.plots_xy = [] self.plots_xz = [] self.plots_yz = [] self.plots_color = [] self.getCurrentStatus(filename_detection) if start_idx != 0: if len(self.subset_idxs) > 0: if start_idx in self.subset_idxs: self.curFrame = start_idx else: raise UserWarning("Unknown start_idx, not in subset!") else: self.curFrame = start_idx if ~numpy.allclose(self._seq.data[self.curFrame].com, 0.): self.curData = self.importer.joint3DToImg( self._seq.data[self.curFrame].com).reshape((1, 3)) else: self.curData = numpy.zeros((1, 3)) self.filename_det = filename_detection self.initGUI() self.connectEvents() self.showCurrent() def initGUI(self): self.main_frame = QWidget(self) # XY plot and toolbar self.fig_xy = Figure((5.0, 4.0), dpi=self.dpi) self.canvas_xy = FigureCanvas(self.fig_xy) self.canvas_xy.setParent(self.main_frame) self.ax_xy = self.fig_xy.add_subplot(111) self.ax_xy.set_title('Click and drag a point to move it') box = self.ax_xy.get_position() self.ax_xy.set_position( [box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9]) self.mpl_toolbar_xy = NavigationToolbar(self.canvas_xy, self.main_frame) def pan_callback(): self.panned = True self.fig_xy.canvas.toolbar.actions()[4].triggered.connect(pan_callback) # XZ, YZ plot and toolbar self.fig_xz_yz = Figure((5.0, 4.0), dpi=self.dpi) self.canvas_xz_yz = FigureCanvas(self.fig_xz_yz) self.canvas_xz_yz.setParent(self.main_frame) gs = matplotlib.gridspec.GridSpec(2, 1) self.ax_xz = self.fig_xz_yz.add_subplot(gs[0]) self.ax_xz.axes.set_frame_on(False) self.ax_yz = self.fig_xz_yz.add_subplot(gs[1]) self.ax_yz.axes.set_frame_on(False) self.mpl_toolbar_xz_yz = NavigationToolbar(self.canvas_xz_yz, self.main_frame) # optional color plot and toolbar if hasattr(self._seq.data[self.curFrame], 'colorFileName'): self.fig_color = Figure((5.0, 4.0), dpi=self.dpi) self.canvas_color = FigureCanvas(self.fig_color) self.canvas_color.setParent(self.main_frame) self.ax_color = self.fig_color.add_subplot(111) self.ax_color.axes.set_frame_on(False) self.mpl_toolbar_color = NavigationToolbar(self.canvas_color, self.main_frame) else: self.ax_color = None # Layout with posebit controls hbox_pb_controls = QHBoxLayout() self.prevButton = QPushButton('Previous', self.main_frame) self.connect(self.prevButton, SIGNAL('clicked()'), self.prevButton_callback) hbox_pb_controls.addWidget(self.prevButton) self.pointcloudButton = QPushButton('3D View', self.main_frame) self.connect(self.pointcloudButton, SIGNAL('clicked()'), self.pointcloudButton_callback) hbox_pb_controls.addWidget(self.pointcloudButton) self.nextButton = QPushButton('Next', self.main_frame) self.connect(self.nextButton, SIGNAL('clicked()'), self.nextButton_callback) hbox_pb_controls.addWidget(self.nextButton) hbox_plots = QHBoxLayout() vbox_xy = QVBoxLayout() vbox_xy.addWidget(self.canvas_xy) vbox_xy.addWidget(self.mpl_toolbar_xy) hbox_plots.addLayout(vbox_xy) vbox_xz_yz = QVBoxLayout() vbox_xz_yz.addWidget(self.canvas_xz_yz) vbox_xz_yz.addWidget(self.mpl_toolbar_xz_yz) hbox_plots.addLayout(vbox_xz_yz) if self.ax_color is not None: vbox_color = QVBoxLayout() vbox_color.addWidget(self.canvas_color) vbox_color.addWidget(self.mpl_toolbar_color) hbox_plots.addLayout(vbox_color) vbox_all = QVBoxLayout() vbox_all.addLayout(hbox_plots) sep = QFrame(self.main_frame) sep.setFrameShape(QFrame.HLine) sep.setFrameShadow(QFrame.Sunken) vbox_all.addWidget(sep) vbox_all.addLayout(hbox_pb_controls) self.main_frame.setLayout(vbox_all) self.setCentralWidget(self.main_frame) def showCurrent(self): dm = self.importer.loadDepthMap(self.path_prefix + self._seq.data[self.curFrame].fileName) if ~numpy.allclose(self.curData, 0.): dm[dm < self.curData[0, 2] - self._seq.config['cube'][2] / 2.] = self.curData[0, 2] + self._seq.config['cube'][2] / 2. dm[dm > self.curData[0, 2] + self._seq.config['cube'][2] / 2.] = self.curData[0, 2] + self._seq.config['cube'][2] / 2. # XY plot if self.panned is False: xstart = numpy.where(~numpy.isclose( dm, self.curData[0, 2] + self._seq.config['cube'][2] / 2.))[1].min() xend = numpy.where(~numpy.isclose( dm, self.curData[0, 2] + self._seq.config['cube'][2] / 2.))[1].max() ystart = numpy.where(~numpy.isclose( dm, self.curData[0, 2] + self._seq.config['cube'][2] / 2.))[0].min() yend = numpy.where(~numpy.isclose( dm, self.curData[0, 2] + self._seq.config['cube'][2] / 2.))[0].max() else: xstart, xend = self.ax_xy.get_xlim() yend, ystart = self.ax_xy.get_ylim( ) # axis is flipped after imshow self.ax_xy.cla() self.text = self.ax_xy.text(0.05, 0.95, 'joint: none', transform=self.ax_xy.transAxes, va='top') self.frame = self.ax_xy.text(0.55, 0.95, 'frame: none', transform=self.ax_xy.transAxes, va='top') self.selected, = self.ax_xy.plot([0], [0], 'o', ms=12, alpha=0.4, color='yellow', visible=False) self.frame.set_text('frame: {}/{}'.format(self.curFrame, len(self._seq.data) - 1)) self.ax_xy.imshow(dm, cmap='CMRmap', interpolation='none') self.ax_xy.set_xlabel('x') self.ax_xy.set_ylabel('y') self.plots_xy = [] for i in range(self.curData.shape[0]): d, = self.ax_xy.plot(self.curData[i, 0], self.curData[i, 1], c='r', marker='o', markersize=8) self.plots_xy.append(d) self.ax_xy.set_xlim([xstart, xend]) self.ax_xy.set_ylim([yend, ystart]) def format_coord(x, y): numrows, numcols = dm.shape col = int(numpy.rint(x)) row = int(numpy.rint(y)) if 0 <= col < numcols and 0 <= row < numrows: z = dm[row, col] return 'x=%d,y=%d,z=%d' % (x, y, z) else: return 'x=%d,y=%d' % (x, y) self.ax_xy.format_coord = format_coord # XZ plot self.ax_xz.cla() xz = numpy.min(dm, axis=0) self.ax_xz.plot(numpy.arange(dm.shape[1]), xz) self.ax_xz.set_xlabel('x') self.ax_xz.set_ylabel('z') pts = numpy.column_stack( (numpy.arange(dm.shape[1] + 2), numpy.concatenate([numpy.asarray([0]), xz, numpy.asarray([0])]))) polygon = Polygon(pts, True) p = PatchCollection([polygon], cmap='jet', alpha=0.4) p.set_array(numpy.array([50, 50, 50, 50])) self.ax_xz.add_collection(p) self.plots_xz = [] for i in range(self.curData.shape[0]): d, = self.ax_xz.plot(self.curData[i, 0], self.curData[i, 2], c='r', marker='o', markersize=8) self.plots_xz.append(d) # YZ plot self.ax_yz.cla() yz = numpy.min(dm, axis=1) self.ax_yz.plot(numpy.arange(dm.shape[0]), yz) self.ax_yz.set_xlabel('y') self.ax_yz.set_ylabel('z') pts = numpy.column_stack( (numpy.arange(dm.shape[0] + 2), numpy.concatenate([numpy.asarray([0]), yz, numpy.asarray([0])]))) polygon = Polygon(pts, True) p = PatchCollection([polygon], cmap='jet', alpha=0.4) p.set_array(numpy.array([50, 50, 50, 50])) self.ax_yz.add_collection(p) self.plots_yz = [] for i in range(self.curData.shape[0]): d, = self.ax_yz.plot(self.curData[i, 1], self.curData[i, 2], c='r', marker='o', markersize=8) self.plots_yz.append(d) # color image if self.ax_color: color = self.importer.loadColorImage( self.path_prefix + self._seq.data[self.curFrame].colorFileName) # XY plot self.ax_color.cla() if len(color.shape) == 3: self.ax_color.imshow(color.astype('uint8'), interpolation='none') else: self.ax_color.imshow(color, interpolation='none', cmap='gray') self.ax_color.set_xlabel('u') self.ax_color.set_ylabel('v') self.plots_color = [] joint2D = self.importer.jointsDpt2DToCol2D(self.curData) for i in range(joint2D.shape[0]): d, = self.ax_color.plot(joint2D[i, 0], joint2D[i, 1], c='r', marker='o', markersize=8) self.plots_color.append(d) self.update() def showContext(self): dm = self.importer.loadDepthMap(self.path_prefix + self._seq.data[self.curFrame].fileName) dm[dm < self.curData[0, 2] - self._seq.config['cube'][2] / 2.] = \ self.curData[0, 2] + self._seq.config['cube'][2] / 2. dm[dm > self.curData[0, 2] + self._seq.config['cube'][2] / 2.] = \ self.curData[0, 2] + self._seq.config['cube'][2] / 2. self.ax_xz.cla() xz = dm[int(numpy.rint(self.curData[self.lastind, 1]))] self.ax_xz.plot(numpy.arange(dm.shape[1]), xz) self.plots_xz = [] d, = self.ax_xz.plot(self.curData[self.lastind, 0], self.curData[self.lastind, 2], c='r', marker='o', markersize=8) self.plots_xz.append(d) self.ax_xz.set_xlim([ self.curData[self.lastind, 0] - self._seq.data[self.curFrame].dpt.shape[0] // 4, self.curData[self.lastind, 0] + self._seq.data[self.curFrame].dpt.shape[0] // 4 ]) self.ax_xz.set_ylim([ self.curData[self.lastind, 2] - self._seq.config['cube'][2] // 4, self.curData[self.lastind, 2] + self._seq.config['cube'][2] // 4 ]) self.ax_xz.set_xlabel('x') self.ax_xz.set_ylabel('z') self.ax_yz.cla() yz = dm[:, int(numpy.rint(self.curData[self.lastind, 0]))] self.ax_yz.plot(numpy.arange(dm.shape[0]), yz) self.plots_yz = [] d, = self.ax_yz.plot(self.curData[self.lastind, 1], self.curData[self.lastind, 2], c='r', marker='o', markersize=8) self.plots_yz.append(d) self.ax_yz.set_xlim([ self.curData[self.lastind, 1] - self._seq.data[self.curFrame].dpt.shape[1] // 4, self.curData[self.lastind, 1] + self._seq.data[self.curFrame].dpt.shape[1] // 4 ]) self.ax_yz.set_ylim([ self.curData[self.lastind, 2] - self._seq.config['cube'][2] // 4, self.curData[self.lastind, 2] + self._seq.config['cube'][2] // 4 ]) self.ax_yz.set_xlabel('y') self.ax_yz.set_ylabel('z') self.update() def getCurrentStatus(self, filename_detections): pbar = pb.ProgressBar( maxval=len(self._seq.data), widgets=['Loading last status', pb.Percentage(), pb.Bar()]) pbar.start() cache_str = '' with open(filename_detections, "r") as inputfile: cache_str = inputfile.readlines() for i in xrange(len(self._seq.data)): pbar.update(i) if len(self.subset_idxs) > 0: if i not in self.subset_idxs: break hd = HandDetector(numpy.zeros((1, 1)), 0., 0.) # dummy object com = numpy.asarray( hd.detectFromCache(filename_detections, self._seq.data[i].fileName, cache_str)) if numpy.allclose(com[2], 0.): self.curFrame = i break else: self._seq.data[i] = self._seq.data[i]._replace( com=self.importer.jointImgTo3D(com.reshape((3, )))) # redo last pose, it might be set to default and saved if self.curFrame > 0: if len(self.subset_idxs) > 0: if self.subset_idxs.index(self.curFrame) - 1 >= 0: self.curFrame = self.subset_idxs[ self.subset_idxs.index(self.curFrame) - 1] else: self.curFrame -= 1 def saveCurrent(self): self._seq.data[self.curFrame] = self._seq.data[self.curFrame]._replace( com=self.importer.jointImgTo3D(self.curData.reshape((3, )))) if self.filename_det is not None: self.importer.saveSequenceDetections(self._seq, self.filename_det) # save log file if self.file_log is not None: self.file_log.write('{}, {}, {}@{}, {}\n'.format( self._seq.data[self.curFrame].fileName, time.strftime("%d.%m.%Y %H:%M:%S +0000", time.gmtime()), getpass.getuser(), os.uname()[1], time.time() - self.start_time)) self.file_log.flush() def connectEvents(self): self.canvas_xy.mpl_connect('button_press_event', self.button_press_callback) self.canvas_xy.mpl_connect('button_release_event', self.button_release_callback) self.canvas_xy.mpl_connect('motion_notify_event', self.motion_notify_callback) def save_plot(self): file_choices = "PNG (*.png)|*.png" path = unicode( QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas_xy.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def pointcloudButton_callback(self): self.pointcloud() def nextButton_callback(self): self.next() def prevButton_callback(self): self.prev() def keyPressEvent(self, event): if self.lastind is None: return if event.text() == 'q': QApplication.instance().quit() elif event.text() == 'p': # previous self.prev() print 'prev' elif event.text() == 'n': # next self.next() print 'next' elif event.text() == '3': # pointcloud self.pointcloud() print '3D' elif event.text() == 'r': # reload self.panned = False self.showCurrent() print 'reload' elif event.text() == 's': # save if self.filename_det is not None: self.importer.saveSequenceDetections(self._seq, self.filename_det) print 'saved' elif event.text() == 'a': # snap to z if self.lastind is not None: self.snap_z(self.lastind) print 'align' self.showContext() elif event.text() == 'z': # snap to z self.snap_z() print 'snap' self.showContext() elif event.key() == Qt.Key_Plus: self.curData[self.lastind, 2] += 5. print "z+=5" self.showContext() elif event.key() == Qt.Key_Minus: self.curData[self.lastind, 2] -= 5. print "z-=5" self.showContext() elif event.key() == Qt.Key_Up: self.curData[self.lastind, 1] -= 1. print "x-=1" self.showCurrent() elif event.key() == Qt.Key_Down: self.curData[self.lastind, 1] += 1. print "x+=1" self.showCurrent() elif event.key() == Qt.Key_Right: self.curData[self.lastind, 0] += 1. print "y+=1" self.showCurrent() elif event.key() == Qt.Key_Left: self.curData[self.lastind, 0] -= 1. print "y-=1" self.showCurrent() else: return self.update() def snap_z(self, idx=None): dm = self.importer.loadDepthMap(self.path_prefix + self._seq.data[self.curFrame].fileName) if idx is None: for i in range(self.curData.shape[0]): self.curData[i, 2] = dm[int(numpy.rint(self.curData[i, 1])), int(numpy.rint(self.curData[i, 0]))] else: self.curData[idx, 2] = dm[int(numpy.rint(self.curData[idx, 1])), int(numpy.rint(self.curData[idx, 0]))] def pointcloud(self): dm = self.importer.loadDepthMap(self.path_prefix + self._seq.data[self.curFrame].fileName) cur3D = self.importer.jointImgTo3D(self.curData[0]).reshape((1, 3)) hd = HandDetector(dm, self.importer.fx, self.importer.fy, importer=self.importer) dpt, M, com = hd.cropArea3D(self.curData[0].reshape((3, )), size=self._seq.config['cube'], docom=False) self.hpe.plotResult3D(dpt, M, cur3D, cur3D, showGT=False, niceColors=False) def next(self): self.panned = False self.saveCurrent() if len(self.subset_idxs) > 0: if len(self.subset_idxs) > self.subset_idxs.index( self.curFrame) + 1: self.curFrame = self.subset_idxs[ self.subset_idxs.index(self.curFrame) + 1] else: print "Done" QApplication.instance().quit() else: if self.curFrame < len(self._seq.data) - 1: self.curFrame += 1 else: print "Done" QApplication.instance().quit() # reset timer self.start_time = time.time() # if there is no data, use the previous data to initialize com = self._seq.data[self.curFrame].com if numpy.allclose(com, 0.): prevFrame = self.curFrame if len(self.subset_idxs) > 0: if self.subset_idxs.index(self.curFrame) - 1 >= 0: prevFrame = self.subset_idxs[ self.subset_idxs.index(self.curFrame) - 1] else: if self.curFrame > 0: prevFrame = self.curFrame - 1 prev_com = self._seq.data[prevFrame].com if numpy.allclose(prev_com, 0.): self.curData = numpy.zeros((1, 3)) else: self.curData = self.importer.joint3DToImg(prev_com).reshape( (1, 3)) else: self.curData = self.importer.joint3DToImg(com).reshape((1, 3)) self.showCurrent() def prev(self): self.panned = False self.saveCurrent() if len(self.subset_idxs) > 0: if self.subset_idxs.index(self.curFrame) - 1 >= 0: self.curFrame = self.subset_idxs[ self.subset_idxs.index(self.curFrame) - 1] else: print "First already selected!" else: if self.curFrame > 0: self.curFrame -= 1 else: print "First already selected!" # reset timer self.start_time = time.time() prev_com = self._seq.data[self.curFrame].com if numpy.allclose(prev_com, 0.): self.curData = numpy.zeros((1, 3)) else: self.curData = self.importer.joint3DToImg(prev_com).reshape((1, 3)) self.showCurrent() def update(self): if self.lastind is None: return self.selected.set_visible(True) if self._ind is not None: self.selected.set_data(self.curData[self._ind, 0], self.curData[self._ind, 1]) self.text.set_text('joint: %d' % self.lastind) self.ax_xy.get_figure().canvas.draw() self.ax_xz.get_figure().canvas.draw() self.ax_yz.get_figure().canvas.draw() if self.ax_color: if self._ind is not None: j2D = self.importer.jointsDpt2DToCol2D(self.curData) self.plots_color[self._ind].set_data(j2D[self._ind, 0], j2D[self._ind, 1]) self.ax_color.get_figure().canvas.draw() def get_ind_under_point(self, event): """ get the index of the vertex under point if within epsilon tolerance :param event: qt event :return: index of selected point """ # display coords distances = numpy.hypot(event.xdata - self.curData[:, 0], event.ydata - self.curData[:, 1]) indmin = distances.argmin() if distances[indmin] >= self.epsilon: ind = None else: ind = indmin self.lastind = ind return ind def button_press_callback(self, event): """ whenever a mouse button is pressed :param event: qt event :return: """ if event.inaxes is None: return if event.button != 1: return self._ind = self.get_ind_under_point(event) print "got joint id", self._ind # if there is only one joint if self._ind is None and self.curData.shape[0] == 1: self._ind = 0 self.lastind = 0 self.plots_xy[self._ind].set_data([event.xdata, event.ydata]) if self._ind is not None: x0, y0 = self.plots_xy[self._ind].get_data() self.press = x0, y0, event.xdata, event.ydata self.update() def button_release_callback(self, event): """ whenever a mouse button is released :param event: qt event :return: """ if event.button != 1: return if self._ind is None: return x, y = self.plots_xy[self._ind].get_data() self.curData[self._ind, 0] = x self.curData[self._ind, 1] = y # set to closest valid depth if at invalid depth dm = self.importer.loadDepthMap(self.path_prefix + self._seq.data[self.curFrame].fileName) if numpy.isclose(dm[int(y), int(x)], self.importer.getDepthMapNV()): validmsk = ~numpy.isclose(dm, self.importer.getDepthMapNV()) pts = numpy.asarray(numpy.where(validmsk)).transpose() deltas = pts - numpy.asarray([int(y), int(x)]) dist = numpy.einsum('ij,ij->i', deltas, deltas) pos = numpy.argmin(dist) self.curData[self._ind, 2] = dm[pts[pos][0], pts[pos][1]] + 10 # add offset to depth else: self.curData[self._ind, 2] = dm[int(y), int(x)] + 10 # add offset to depth self._ind = None self.showCurrent() self.showContext() def motion_notify_callback(self, event): """ on mouse movement :param event: qt event :return: """ if self._ind is None: return if event.inaxes is None: return if event.button != 1: return x0, y0, xpress, ypress = self.press dx = event.xdata - xpress dy = event.ydata - ypress self.selected.set_data(x0 + dx, y0 + dy) self.plots_xy[self._ind].set_data(x0 + dx, y0 + dy) self.ax_xy.get_figure().canvas.draw()
class MainWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.createGUI() def createGUI(self): """Crea y configura todos los elementos de la itefaz""" self.arduino = ReadArduino() if len(self.arduino.arduinos) < 1: mensaje = QMessageBox(self) mensaje.setText('No hay algun arduino para trabajar') mensaje.setWindowTitle('Hardware no disponible') mensaje.setIcon(QMessageBox.Critical) mensaje.exec_() exit() self.arduino.start() # Generacion de elementos principales de la grafica self.dpi = 100 self.fig = Figure((6.0, 5.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) #self.canvas.setParent(self.frame) # Configuracion de la grafica self.axes = self.fig.add_subplot(111) self.axes.set_axis_bgcolor('black') self.axes.grid(True, color='gray') # Declaracion de interfaz self.pause = QPushButton('Pausar') self.pause.setDisabled(True) self.pause.clicked.connect(self.doPause) self.comenzar = QPushButton('Comenzar') self.comenzar.setCheckable(True) self.comenzar.toggled.connect(self.togglePrueba) self.guardar = QPushButton('Guardar') self.guardar.clicked.connect(self.doGuardar) self.nombreL = QLabel('Nombre de la prueba') self.nombre = QLineEdit() self.tiempoPrueba = QSpinBox() self.tiempoPrueba.setMinimum(1) self.tiempoPrueba.setDisabled(True) self.grupoTiempo = QGroupBox('Manejo de tiempo') self.grupoSignal = QGroupBox(u'Señales') self.politicaTiempo = QButtonGroup() self.tiempoFijo = QRadioButton('Tiempo fijo') self.tiempoIlimitado = QRadioButton('Tiempo ilimitado') self.tiempoIlimitado.setChecked(True) self.tiempoIlimitado.toggled.connect(self.cambiaPolitica) self.led0 = QPushButton('0') self.led1 = QPushButton('1') self.led2 = QPushButton('2') self.led3 = QPushButton('3') self.led0.setCheckable(True) self.led1.setCheckable(True) self.led2.setCheckable(True) self.led3.setCheckable(True) self.led0.toggled.connect(self.arduino.led0) self.led1.toggled.connect(self.arduino.led1) self.led2.toggled.connect(self.arduino.led2) self.led3.toggled.connect(self.arduino.led3) # Configuracion de la interfaz gt = QGridLayout() gt.addWidget(self.tiempoIlimitado, 0, 0) gt.addWidget(self.tiempoFijo, 1, 0) gt.addWidget(self.tiempoPrueba, 1, 1) gt.addWidget(self.pause, 0, 2, 2, 1) self.grupoTiempo.setLayout(gt) gs = QGridLayout() gs.addWidget(self.led0, 2, 2) gs.addWidget(self.led1, 2, 3) gs.addWidget(self.led2, 3, 2) gs.addWidget(self.led3, 3, 3) self.grupoSignal.setLayout(gs) gp = QGridLayout() gp.addWidget(self.nombreL, 1, 0) gp.addWidget(self.nombre, 1, 1) gp.addWidget(self.comenzar, 1, 2) gp.addWidget(self.guardar, 1, 3) gp.addWidget(self.grupoTiempo, 2, 0, 1, 2) gp.addWidget(self.grupoSignal, 2, 2, 1, 2) gp.addWidget(self.canvas, 0, 0, 1, 4) self.frame = QWidget() self.frame.setLayout(gp) self.setCentralWidget(self.frame) self.data = [] self.line = self.axes.plot( self.data, linewidth=1, color=(1, 1, 0), )[0] # Manejo de tiempo self.tiempoTrancurrido = 0 self.contadorActializa = QTimer() self.contadorActializa.timeout.connect(self.refresh) self.lecturasXSegundo = 20 self.contadorActializa.setInterval(1000 / self.lecturasXSegundo) self.contadorPrincipal = QTimer() self.contadorPrincipal.timeout.connect(self.comenzar.toggle) self.draw_chart() def detenerPrueba(self): self.contadorActializa.stop() self.contadorPrincipal.stop() self.comenzar.setText('Comenzar') self.pause.setText('Pausar') self.pause.setDisabled(True) def comenzarPrueba(self): self.data = [] self.tiempoTrancurrido = 0 self.draw_chart() if self.tiempoFijo.isChecked(): self.contadorPrincipal.setInterval( self.tiempoPrueba.value() * 1000) else: self.contadorPrincipal.setInterval(525600000) # Un año self.contadorActializa.start() self.contadorPrincipal.start() self.comenzar.setText('Detener') self.pause.setDisabled(False) def togglePrueba(self): """Metodo para comenzar y detener la prueba actual""" if self.comenzar.isChecked(): self.comenzarPrueba() else: self.detenerPrueba() def cambiaPolitica(self): """Habilita la interfaz para manejo manual del tiempo""" if self.tiempoIlimitado.isChecked(): self.tiempoPrueba.setDisabled(True) else: self.tiempoPrueba.setEnabled(True) def doGuardar(self): """Verifica la existencia de datos y los guarda en png, cvs y pdf""" if self.nombre.text() == '': mensaje = QMessageBox(self) mensaje.setText('Ingrese un nombre para la prueba') mensaje.setWindowTitle('Error al guardar') mensaje.setIcon(QMessageBox.Critical) mensaje.exec_() elif len(self.data) == 0: mensaje = QMessageBox(self) mensaje.setText('No hay datos para guardar') mensaje.setWindowTitle('Error al guardar') mensaje.setIcon(QMessageBox.Critical) mensaje.exec_() else: nombre = str(self.nombre.text().toUtf8()) archivo = open(nombre + '.csv', 'w') archivo.write(str(self.data)[1:-1] + '\n') archivo.close() pdf = PdfPages(nombre + '.pdf') self.fig.savefig(pdf, format='pdf') pdf.close() self.canvas.print_figure(nombre + '.png', dpi=self.dpi) mensaje = QMessageBox(self) mensaje.setText('La prueba a sido guardada correctamente') mensaje.setWindowTitle('Guardado con exito') mensaje.setIcon(QMessageBox.Information) mensaje.exec_() def doPause(self): """Maneja las pausas de la aplicación""" if self.contadorActializa.isActive(): self.contadorActializa.stop() self.contadorPrincipal.stop() self.pause.setText('Reanudar') else: self.contadorActializa.start() # Recalcula el tiempo restante de la prueba self.contadorPrincipal.setInterval( self.tiempoPrueba.value() * 1000 - self.tiempoTrancurrido) self.contadorPrincipal.start() self.pause.setText('Pausar') def refresh(self): """Agrega un dato al conjunto y regenera la gráfica""" self.tiempoTrancurrido += 1000 / self.lecturasXSegundo self.data.append(int(self.arduino.data)) self.draw_chart() def draw_chart(self): """Regenera la grafica""" count = round(len(self.data) * 1.0 / self.lecturasXSegundo, 3) xmax = count if count > 3 else 3 xmin = 0 self.axes.set_xbound(lower=xmin, upper=xmax) self.axes.set_ybound(lower=0, upper=1023) self.line.set_xdata(np.arange(0, count, 1.0 / self.lecturasXSegundo)) self.line.set_ydata(np.array(self.data)) self.canvas.draw() def closeEvent(self, event): self.arduino.stop()
class StatisticsWindow(QWidget): def __init__(self,parent=None): QWidget.__init__(self,parent) self.setWindowFlags(Qt.Window) self.setWindowTitle('Statistics:') self.parent=parent self.figure,self.axes = plt.subplots(2,1,sharex=True) self.figure.set_size_inches((5.0,4.0)) self.canvas=FigureCanvas(self.figure) self.canvas.setParent(self) layout = QVBoxLayout() layout.addWidget(self.canvas) #commented away as it causes segmentation faults in my ubuntu, even though #it works fine in my windows 7 computer. #self.mpl_toolbar = NavigationToolBar(self.canvas,self) #layout.addWidget(self.mpl_toolbar) self.setLayout(layout) self.dataChanged() def draw(self): self.canvas.draw() def closeEvent(self,event): self.parent.closeStatWindow() self.close() def exportStats(self): dialog = QFileDialog(self, "Choose a filename and type to save to, suffix declares type","./stats.csv") filetypes = self.canvas.get_supported_filetypes_grouped().items() filetypes.append(('Spreadsheet',['csv'])) filetypes.sort() default_filetype = 'csv' filters = [] selectedFilter = None for name, exts in filetypes: exts_list = " ".join(['*.%s' % ext for ext in exts]) filter = '%s (%s)' % (name, exts_list) if default_filetype in exts: selectedFilter = filter filters.append(filter) filters = ';;'.join(filters) dialog.setFileMode(QFileDialog.AnyFile) dialog.setNameFilter(filters) dialog.selectNameFilter(selectedFilter) dialog.setViewMode(QFileDialog.Detail) dialog.setAcceptMode(QFileDialog.AcceptSave) if not dialog.exec_(): return fname = dialog.selectedFiles() if not fname[0]: return fname=fname[0] if not '.' in fname: fname+='.csv' self.dataChanged() if '.csv' in fname: with open(fname, 'wb') as f: w = csv.writer(f, delimiter=';', quoting=csv.QUOTE_NONE) w.writerow(["Total Eaten:", "Total Walked:"]) for i in range(len(self.parent.statEaten)): w.writerow([self.parent.statEaten[i], self.parent.statWalked[i]]) else: try: self.canvas.print_figure( unicode(fname) ) except Exception, e: QMessageBox.critical( self, "Error saving file", str(e), QMessageBox.Ok, QMessageBox.NoButton)
class AppForm(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle('Channelizer 2') self.create_menu() self.create_main_frame() self.create_status_bar() self.dacStatus = 'off' self.dramStatus = 'off' self.tapStatus = 'off' self.socketStatus = 'off' self.numFreqs=0 self.ch_all = [] self.attens = numpy.array([1. for i in range(256)]) self.freqRes = 7812.5 self.sampleRate = 512e6 self.zeroChannels = [0]*256 #writing threshold to register self.thresholds, self.medians = numpy.array([0.]*256), numpy.array([0.]*256) self.customThresholds = numpy.array([360.]*256) self.customResonators=numpy.array([[0.0,-1]]*256) #customResonator[ch]=[freq,atten] def openClient(self): self.roach = corr.katcp_wrapper.FpgaClient(self.textbox_roachIP.text(),7147) time.sleep(2) self.status_text.setText('connection established') print 'Connected to ',self.textbox_roachIP.text() self.button_openClient.setDisabled(True) def loadFIRcoeffs(self): N_freqs = len(map(float, unicode(self.textedit_DACfreqs.toPlainText()).split())) taps = 26 for ch in range(N_freqs): # If the resonator's attenuation is >=99 then its FIR should be zeroed if self.zeroChannels[ch]: lpf = numpy.array([0.]*taps)*(2**11-1) print 'deleted ch ',ch else: lpf = numpy.array(self.fir)*(2**11-1) print ch #lpf = numpy.array([1.]+[0]*(taps-1))*(2**11-1) # 26 tap, 25 us matched fir #lpf = numpy.array([0.0875788844768 , 0.0840583257978 , 0.0810527406206 , 0.0779008825067 , 0.075106964962 , 0.0721712998256 , 0.0689723729398 , 0.066450095496 , 0.0638302570705 , 0.0613005685486 , 0.0589247737004 , 0.0565981917436 , 0.0544878914297 , 0.0524710948658 , 0.0503447054014 , 0.0483170854189 , 0.0463121066637 , 0.044504238059 , 0.0428469827102 , 0.0410615366471 , 0.0395570640218 , 0.0380071830756 , 0.0364836787854 , 0.034960959124 , 0.033456372241 , 0.0321854467182])*(2**11-1) #26 tap, 20 us matched fir #lpf = numpy.array([ 0.102806030245 , 0.097570344415 , 0.0928789946181 , 0.0885800360545 , 0.0841898850361 , 0.079995145104 , 0.0761649967857 , 0.0724892663141 , 0.0689470889358 , 0.0657584886557 , 0.0627766233242 , 0.0595952531565 , 0.0566356208278 , 0.053835736579 , 0.0510331408751 , 0.048623806127 , 0.0461240096904 , 0.0438134132285 , 0.0418265743203 , 0.0397546477453 , 0.0377809254888 , 0.0358044897245 , 0.0338686929847 , 0.0321034547839 , 0.0306255734188 , 0.0291036235859 ])*(2**11-1) #26 tap, 30 us matched fir #lpf = numpy.array([ 0.0781747107378 , 0.0757060398243 , 0.0732917718492 , 0.0708317694778 , 0.0686092845217 , 0.0665286923521 , 0.0643467681477 , 0.0621985982971 , 0.0600681642401 , 0.058054873199 , 0.0562486467178 , 0.0542955553149 , 0.0527148880657 , 0.05096365681 , 0.0491121116212 , 0.0474936094733 , 0.0458638771941 , 0.0443219286645 , 0.0429290438102 , 0.0415003391096 , 0.0401174498302 , 0.0386957715665 , 0.0374064708747 , 0.0362454802408 , 0.0350170176804 , 0.033873302383 ])*(2**11-1) #lpf = lpf[::-1] # 26 tap, lpf, 250 kHz, #lpf = numpy.array([-0 , 0.000166959420533 , 0.00173811663844 , 0.00420937801998 , 0.00333739357391 , -0.0056305703275 , -0.0212738104942 , -0.0318529375832 , -0.0193635986879 , 0.0285916612022 , 0.106763943766 , 0.18981814328 , 0.243495321192 , 0.243495321192 , 0.18981814328 , 0.106763943766 , 0.0285916612022 , -0.0193635986879 , -0.0318529375832 , -0.0212738104942 , -0.0056305703275 , 0.00333739357391 , 0.00420937801998 , 0.00173811663844 , 0.000166959420533 , -0])*(2**11-1) # 26 tap, lpf, 125 kHz. #lpf = numpy.array([0 , -0.000431898216436 , -0.00157886921107 , -0.00255492263971 , -0.00171727439076 , 0.00289724121972 , 0.0129123447233 , 0.0289345497995 , 0.0500906370566 , 0.0739622085341 , 0.0969821586979 , 0.115211955161 , 0.125291869266 , 0.125291869266 , 0.115211955161 , 0.0969821586979 , 0.0739622085341 , 0.0500906370566 , 0.0289345497995 , 0.0129123447233 , 0.00289724121972 , -0.00171727439076 , -0.00255492263971 , -0.00157886921107 , -0.000431898216436 , -0])*(2**11-1) # Generic 40 tap matched filter for 25 us lifetime pulse #lpf = numpy.array([0.153725595011 , 0.141052390733 , 0.129753816201 , 0.119528429291 , 0.110045314901 , 0.101336838027 , 0.0933265803805 , 0.0862038188673 , 0.0794067694409 , 0.0729543134914 , 0.0674101836798 , 0.0618283869464 , 0.0567253144676 , 0.0519730940444 , 0.047978953698 , 0.043791412767 , 0.0404560656757 , 0.0372466775252 , 0.0345000956808 , 0.0319243455811 , 0.0293425115323 , 0.0268372778298 , 0.0245216835234 , 0.0226817116475 , 0.0208024488535 , 0.0189575043357 , 0.0174290665862 , 0.0158791788119 , 0.0144611054123 , 0.0132599563305 , 0.0121083419203 , 0.0109003580368 , 0.0100328742978 , 0.00939328253743 , 0.00842247241585 , 0.00789304712484 , 0.00725494259117 , 0.00664528407122 , 0.00606688645845 , 0.00552041438208])*(2**11-1) #lpf = lpf[::-1] for n in range(taps/2): coeff0 = int(lpf[2*n]) coeff1 = int(lpf[2*n+1]) coeff0 = numpy.binary_repr(int(lpf[2*n]), 12) coeff1 = numpy.binary_repr(int(lpf[2*n+1]), 12) coeffs = int(coeff1+coeff0, 2) coeffs_bin = struct.pack('>l', coeffs) register_name = 'FIR_b' + str(2*n) + 'b' + str(2*n+1) self.roach.write(register_name, coeffs_bin) self.roach.write_int('FIR_load_coeff', (ch<<1) + (1<<0)) self.roach.write_int('FIR_load_coeff', (ch<<1) + (0<<0)) # Inactive channels will also be zeroed. lpf = numpy.array([0.]*taps) for ch in range(N_freqs, 256): for n in range(taps/2): #coeffs = struct.pack('>h', lpf[2*n]) + struct.pack('>h', lpf[2*n+1]) coeffs = struct.pack('>h', lpf[2*n+1]) + struct.pack('>h', lpf[2*n]) register_name = 'FIR_b' + str(2*n) + 'b' + str(2*n+1) self.roach.write(register_name, coeffs) self.roach.write_int('FIR_load_coeff', (ch<<1) + (1<<0)) self.roach.write_int('FIR_load_coeff', (ch<<1) + (0<<0)) print 'done loading fir.' self.status_text.setText('FIRs loaded') def find_nearest(self, array, value): idx=(numpy.abs(array-value)).argmin() return idx def loadCustomThresholds(self): freqFile =str(self.textbox_freqFile.text()) if freqFile[-8:] == '_NEW.txt': freqFile=freqFile[:-8]+'_THRESHOLD.txt' else: freqFile=freqFile[:-4]+'_THRESHOLD.txt' try: x=numpy.loadtxt(freqFile) self.customThresholds = numpy.array([360.]*256) if type(x[0]) == numpy.ndarray: for arr in x: self.customThresholds[int(arr[0])]=arr[1] else: self.customThresholds[int(x[0])]=x[1] print 'Custom Thresholds loaded from',freqFile except IOError: #No custom thresholds to load pass def rmCustomThreshold(self): ch = int(self.textbox_channel.text()) if self.customThresholds[ch] != 360.0: self.customThresholds[ch]=360.0 print "Custom Threshold from channel",ch,"removed." #self.loadSingleThreshold(ch) scale_to_angle = 360./2**16*4/numpy.pi self.roach.write_int('capture_threshold', int(self.thresholds[ch]/scale_to_angle)) self.roach.write_int('capture_load_thresh', (ch<<1)+(1<<0)) self.roach.write_int('capture_load_thresh', (ch<<1)+(0<<0)) print "Old threshold updated to roach" freqFile =str(self.textbox_freqFile.text()) if freqFile[-8:] == '_NEW.txt': freqFile=freqFile[:-8]+'_THRESHOLD.txt' else: freqFile=freqFile[:-4]+'_THRESHOLD.txt' try: x=numpy.loadtxt(freqFile) f=open(freqFile,'w') if type(x[0]) == numpy.ndarray: for arr in x: #print 'arr',arr if arr[0]!=ch: f.write(str(int(arr[0]))+'\t'+str(float(arr[1]))+'\n') else: if x[0]!=ch: f.write(str(int(x[0]))+'\t'+str(float(x[1]))+'\n') print "Removed Custom Threshold on channel ",ch," from ",freqFile self.status_text.setText("Removed Custom Threshold on channel "+str(ch)+" from "+str(freqFile)) f.close() except IOError: print 'Unable to remove custom threshold from channel',ch else: print "No custom threshold set for channel",ch def setCustomThreshold(self,event): scale_to_angle = 360./2**16*4/numpy.pi ch = int(self.textbox_channel.text()) newThreshold = event.ydata #print "Threshold selected:",newThreshold if event.ydata != None and self.mpl_toolbar.mode == '': self.loadSingleThreshold(ch) #resets median newThreshold = newThreshold - self.medians[ch] #for threshold adjusting firmware only! self.customThresholds[ch] = newThreshold newThreshold = int(newThreshold/scale_to_angle) #print "writing threshold to register:",newThreshold #print "median for channel ",ch,": ",self.medians[ch] #self.customThresholds[ch] = newThreshold #print "new threshold: ",newThreshold #print "old threshold: ",self.thresholds[ch]/scale_to_angle self.roach.write_int('capture_threshold', newThreshold) self.roach.write_int('capture_load_thresh', (ch<<1)+(1<<0)) self.roach.write_int('capture_load_thresh', (ch<<1)+(0<<0)) print "channel: ", ch, "median: ", self.medians[ch], "new threshold: ", scale_to_angle*newThreshold #print self.customThresholds[ch] #self.loadSingleThreshold(ch) freqFile =str(self.textbox_freqFile.text()) if freqFile[-8:] == '_NEW.txt': freqFile=freqFile[:-8]+'_THRESHOLD.txt' else: freqFile=freqFile[:-4]+'_THRESHOLD.txt' try: f=open(freqFile,'a') f.write(str(int(ch))+'\t'+str(float(self.customThresholds[ch]))+'\n') f.close() print 'Saved custom threshold to',freqFile except IOError: print 'ERROR: There was a problem saving thresholds to',freqFile def loadThresholds(self): """ Takes two time streams and concatenates together for a longer sample. median is used instead of mean. """ x = raw_input('Is the lamp off? ') Nsigma = float(self.textbox_Nsigma.text()) N_freqs = len(map(float, unicode(self.textedit_DACfreqs.toPlainText()).split())) self.thresholds, self.medians = numpy.array([0.]*N_freqs), numpy.array([0.]*N_freqs) #for ch in range(N_freqs): #print 'attempting to load channel: ',ch # self.loadSingleThreshold(ch) steps = int(self.textbox_timeLengths.text()) L = 2**10 scale_to_angle = 360./2**16*4/numpy.pi for ch in range(N_freqs): bin_data_phase = '' for n in range(steps): self.roach.write_int('ch_we', ch) self.roach.write_int('startSnap', 0) self.roach.write_int('snapPhase_ctrl', 1) self.roach.write_int('snapPhase_ctrl', 0) self.roach.write_int('startSnap', 1) time.sleep(0.001) bin_data_phase = bin_data_phase + self.roach.read('snapPhase_bram', 4*L) phase = [] for m in range(steps*L): phase.append(struct.unpack('>h', bin_data_phase[m*4+2:m*4+4])[0]) phase.append(struct.unpack('>h', bin_data_phase[m*4+0:m*4+2])[0]) phase = numpy.array(phase) #phase_avg = numpy.median(phase) #sigma = phase.std() n,bins= numpy.histogram(phase,bins=100) n = numpy.array(n,dtype='float32')/numpy.sum(n) tot = numpy.zeros(len(bins)) for i in xrange(len(bins)): tot[i] = numpy.sum(n[:i]) bins1 = .5*(bins[1:]+bins[:-1]) med = bins[self.find_nearest(tot,0.5)] thresh = bins[self.find_nearest(tot,0.05)] #threshold = int(med-Nsigma*abs(med-thresh)) threshold = int(-Nsigma*abs(med-thresh)) #for threshold adjusting firmware! #threshold = int((phase_avg - Nsigma*sigma)) # -25736 = -180 degrees if threshold < -25736: threshold = -25736 self.thresholds[ch] = scale_to_angle*threshold self.medians[ch] = scale_to_angle*med if self.customThresholds[ch] != 360.0: threshold = self.customThresholds[ch]/scale_to_angle if threshold < -25736: threshold = -25736 print 'Channel '+str(ch)+' has a custom threshold' self.roach.write_int('capture_threshold', threshold) self.roach.write_int('capture_load_thresh', (ch<<1)+(1<<0)) self.roach.write_int('capture_load_thresh', (ch<<1)+(0<<0)) print "channel: ", ch, "median: ", scale_to_angle*med, "threshold: ", scale_to_angle*threshold #print "channel: ", ch, "avg: ", scale_to_angle*phase_avg, "sigma: ", scale_to_angle*sigma, "threshold: ", scale_to_angle*threshold self.status_text.setText('Thresholds loaded') print 'Done loading thresholds' def loadSingleThreshold(self,ch): #print 'ch: ',ch L = 2**10 scale_to_angle = 360./2**16*4/numpy.pi Nsigma = float(self.textbox_Nsigma.text()) bin_data_phase = '' steps = int(self.textbox_timeLengths.text()) for n in range(steps): self.roach.write_int('ch_we', ch) self.roach.write_int('startSnap', 0) self.roach.write_int('snapPhase_ctrl', 1) self.roach.write_int('snapPhase_ctrl', 0) self.roach.write_int('startSnap', 1) time.sleep(0.001) bin_data_phase = bin_data_phase + self.roach.read('snapPhase_bram', 4*L) phase = [] for m in range(steps*L): phase.append(struct.unpack('>h', bin_data_phase[m*4+2:m*4+4])[0]) phase.append(struct.unpack('>h', bin_data_phase[m*4+0:m*4+2])[0]) phase = numpy.array(phase) #phase_avg = numpy.median(phase) #sigma = phase.std() n,bins= numpy.histogram(phase,bins=100) n = numpy.array(n,dtype='float32')/numpy.sum(n) tot = numpy.zeros(len(bins)) for i in xrange(len(bins)): tot[i] = numpy.sum(n[:i]) bins1 = .5*(bins[1:]+bins[:-1]) med = bins[self.find_nearest(tot,0.5)] thresh = bins[self.find_nearest(tot,0.05)] #threshold = int(med-Nsigma*abs(med-thresh)) threshold = int(-Nsigma*abs(med-thresh)) #for threshold adjusting firmware! #threshold = int((phase_avg - Nsigma*sigma)) # -25736 = -180 degrees if threshold < -25736: threshold = -25736 self.thresholds[ch] = scale_to_angle*threshold self.medians[ch] = scale_to_angle*med if self.customThresholds[ch] != 360.0: threshold = self.customThresholds[ch]/scale_to_angle if threshold < -25736: threshold = -25736 print 'Channel '+str(ch)+' has a custom threshold' self.roach.write_int('capture_threshold', threshold) self.roach.write_int('capture_load_thresh', (ch<<1)+(1<<0)) self.roach.write_int('capture_load_thresh', (ch<<1)+(0<<0)) print "channel: ", ch, "median: ", scale_to_angle*med, "threshold: ", scale_to_angle*threshold #print "channel: ", ch, "avg: ", scale_to_angle*phase_avg, "sigma: ", scale_to_angle*sigma, "threshold: ", scale_to_angle*threshold def snapshot(self): self.displayResonatorProperties() ch_we = int(self.textbox_channel.text()) self.roach.write_int('ch_we', ch_we) #print self.roach.read_int('ch_we') steps = int(self.textbox_snapSteps.text()) L = 2**10 bin_data_phase = '' for n in range(steps): self.roach.write_int('startSnap', 0) self.roach.write_int('snapPhase_ctrl', 1) self.roach.write_int('snapPhase_ctrl', 0) self.roach.write_int('startSnap', 1) time.sleep(0.001) bin_data_phase = bin_data_phase + self.roach.read('snapPhase_bram', 4*L) phase = [] for m in range(steps*L): phase.append(struct.unpack('>h', bin_data_phase[m*4+2:m*4+4])[0]) phase.append(struct.unpack('>h', bin_data_phase[m*4+0:m*4+2])[0]) phase = numpy.array(phase)*360./2**16*4/numpy.pi self.axes1.clear() #self.axes1.plot(phase, '.-', [self.thresholds[ch_we]]*2*L*steps, 'r.', [self.medians[ch_we]]*2*L*steps, 'g.') saveDir = str('') if saveDir != '': phasefilename = saveDir + 'snapshot_'+time.strftime("%Y%m%d-%H%M%S",time.localtime()) + str(self.textbox_roachIP.text())+'.txt' numpy.savetxt(phasefilename,phase,fmt='%.8e') if steps <= 1000: self.axes1.plot(phase,'.-') med=numpy.median(phase) print 'ch:',ch_we,'median:',med, thresh=self.thresholds[ch_we] if self.customThresholds[ch_we] != 360.0: thresh=self.customThresholds[ch_we] print "Custom Threshold: ", thresh, self.axes1.plot([thresh+med]*2*L*steps,'r.',[med]*2*L*steps,'g.',alpha=1) med=self.medians[ch_we] self.axes1.plot([thresh+med]*2*L*steps,'y.',[med]*2*L*steps,'y.',alpha=0.2) print "Threshold: ",self.thresholds[ch_we] #print "Channel: ",ch_we," median: " ,self.medians[ch_we], #if self.customThresholds[ch_we] != 360.0: # self.axes1.plot(phase, '.-', [self.customThresholds[ch_we]+self.medians[ch_we]]*2*L*steps, 'r.', [self.medians[ch_we]]*2*L*steps, 'g.',alpha=0.3) # print "Custom Threshold: ",self.customThresholds[ch_we]," Threshold: ",self.thresholds[ch_we] #else: # self.axes1.plot(phase, '.-', [self.thresholds[ch_we]+self.medians[ch_we]]*2*L*steps, 'r.', [self.medians[ch_we]]*2*L*steps, 'g.',alpha=0) # print "Threshold: ",self.thresholds[ch_we] #print " " self.canvas.draw() print "snapshot taken" def longsnapshot(self): self.displayResonatorProperties() ch_we = int(self.textbox_channel.text()) self.roach.write_int('ch_we', ch_we) #print self.roach.read_int('ch_we') steps = int(self.textbox_longsnapSteps.text()) L = 2**10 numQDRSamples=2**19 numBytesPerSample=4 nLongsnapSamples = numQDRSamples*2*steps # 2 16-bit samples per 32-bit QDR word bin_data_phase = '' qdr_data_str = '' for n in range(steps): self.roach.write_int('snapPhase_ctrl', 0) self.roach.write_int('snapqdr_ctrl',0) self.roach.write_int('startSnap', 0) self.roach.write_int('snapqdr_ctrl',1) self.roach.write_int('snapPhase_ctrl', 1) self.roach.write_int('startSnap', 1) time.sleep(2) bin_data_phase = bin_data_phase + self.roach.read('snapPhase_bram', 4*L) qdr_data_str = qdr_data_str + self.roach.read('qdr0_memory',numQDRSamples*numBytesPerSample) self.roach.write_int('snapPhase_ctrl', 0) self.roach.write_int('snapqdr_ctrl',0) self.roach.write_int('startSnap', 0) phase = [] for m in range(steps*L): phase.append(struct.unpack('>h', bin_data_phase[m*4+2:m*4+4])[0]) phase.append(struct.unpack('>h', bin_data_phase[m*4+0:m*4+2])[0]) qdr_values = struct.unpack('>%dh'%(nLongsnapSamples),qdr_data_str) qdr_phase_values = numpy.array(qdr_values)*360./2**16*4/numpy.pi phase = numpy.array(phase)*360./2**16*4/numpy.pi fqdr = open('ch_out_%d.txt'%ch_we,'w') for q in qdr_phase_values: fqdr.write(str(q)+'\n') fsnap = open('ch_snap_%d.txt'%ch_we,'w') for q in phase: fsnap.write(str(q)+'\n') self.axes1.clear() #self.axes1.plot(phase, '.-', [self.thresholds[ch_we]]*2*L*steps, 'r.', [self.medians[ch_we]]*2*L*steps, 'g.') saveDir = str('') if saveDir != '': phasefilename = saveDir + 'snapshot_'+time.strftime("%Y%m%d-%H%M%S",time.localtime()) + str(self.textbox_roachIP.text())+'.txt' numpy.savetxt(phasefilename,phase,fmt='%.8e') med=numpy.median(phase) print 'ch:',ch_we,'median:',med, thresh=self.thresholds[ch_we] if self.customThresholds[ch_we] != 360.0: thresh=self.customThresholds[ch_we] print "Custom Threshold: ", thresh, if steps < 2: self.axes1.plot(qdr_phase_values,'b-') self.axes1.plot([thresh+med]*2*numQDRSamples*steps,'r-',[med]*2*numQDRSamples*steps,'g-',alpha=1) med=self.medians[ch_we] self.axes1.plot([thresh+med]*2*numQDRSamples*steps,'y-',[med]*2*numQDRSamples*steps,'y-',alpha=0.2) nFFTAverages = 100 nSamplesPerFFT = nLongsnapSamples/nFFTAverages noiseFFT = numpy.zeros(nSamplesPerFFT) noiseFFTFreqs = numpy.fft.fftfreq(nSamplesPerFFT) for iAvg in xrange(nFFTAverages): noise = numpy.fft.fft(qdr_phase_values[iAvg*nSamplesPerFFT:(iAvg+1)*nSamplesPerFFT]) noise = numpy.abs(noise)**2 noiseFFT += noise noiseFFT /= nFFTAverages self.axes0.clear() nFFTfreqs = len(noiseFFTFreqs)/2 noiseFFTFreqs *= 1e6 #convert MHz to Hz self.axes0.loglog(noiseFFTFreqs[1:nFFTfreqs],noiseFFT[1:nFFTfreqs]) self.axes0.set_xlabel('Freq (Hz)') self.axes0.set_ylabel('FFT of snapshot, nAverages=%d'%nFFTAverages) print "Threshold: ",self.thresholds[ch_we] self.canvas.draw() print "longsnapshot taken" def readPulses(self): scale_to_degrees = 360./2**12*4/numpy.pi channel_count = [0]*256 p1 = [[] for n in range(256)] timestamp = [[] for n in range(256)] baseline = [[] for n in range(256)] peaks = [[] for n in range(256)] seconds = int(self.textbox_seconds.text()) steps = seconds*10 self.roach.write_int('startBuffer', 1) time.sleep(1) for n in range(steps): addr0 = self.roach.read_int('pulses_addr') time.sleep(0.1) addr1 = self.roach.read_int('pulses_addr') bin_data_0 = self.roach.read('pulses_bram0', 4*2**14) bin_data_1 = self.roach.read('pulses_bram1', 4*2**14) if addr1 >= addr0: total_counts = addr1-addr0 for n in range(addr0, addr1): raw_data_1 = struct.unpack('>L', bin_data_1[n*4:n*4+4])[0] raw_data_0 = struct.unpack('>L', bin_data_0[n*4:n*4+4])[0] ch = raw_data_1/2**24 channel_count[ch] = channel_count[ch] + 1 timestamp[ch].append(raw_data_0%2**20) baseline[ch].append((raw_data_0>>20)%2**12) peaks[ch].append((raw_data_1>>12)%2**12) else: total_counts = addr1+2**14-addr0 for n in range(addr0, 2**14): raw_data_1 = struct.unpack('>L', bin_data_1[n*4:n*4+4])[0] raw_data_0 = struct.unpack('>L', bin_data_0[n*4:n*4+4])[0] ch = raw_data_1/2**24 channel_count[ch] = channel_count[ch] + 1 p1[ch].append((raw_data_1%2**12 - 2**11)*scale_to_degrees) timestamp[ch].append(raw_data_0%2**20) baseline[ch].append((raw_data_0>>20)%2**12) peaks[ch].append((raw_data_1>>12)%2**12) for n in range(0, addr1): raw_data_1 = struct.unpack('>L', bin_data_1[n*4:n*4+4])[0] raw_data_0 = struct.unpack('>L', bin_data_0[n*4:n*4+4])[0] ch = raw_data_1/2**24 channel_count[ch] = channel_count[ch] + 1 p1[ch].append((raw_data_1%2**12 - 2**11)*scale_to_degrees) timestamp[ch].append(raw_data_0%2**20) baseline[ch].append((raw_data_0>>20)%2**12) peaks[ch].append((raw_data_1>>12)%2**12) print total_counts self.roach.write_int('startBuffer', 0) print 'sorted indices by counts', numpy.argsort(channel_count)[::-1] print 'sorted by counts', numpy.sort(channel_count)[::-1] print 'total counts by channel: ',channel_count channel_count = numpy.array(channel_count) ch = int(self.textbox_channel.text()) numpy.savetxt('/home/sean/data/restest/test.dat', p1[ch]) # With lamp off, run "readPulses." If count rate is above 50, it's anamolous # and it's FIR should be set to 0. #for n in range(256): # if channel_count[n] > 100: # self.zeroChannels[n] = 1 #x = numpy.arange(-270, 0, 3) #y = numpy.histogram(data, 90) base = numpy.array(baseline[ch],dtype='float') base = base/2.0**9-4.0 base = base*180.0/numpy.pi peaksCh = numpy.array(peaks[ch],dtype = 'float') peaksCh = peaksCh/2.0**9-4.0 peaksCh = peaksCh*180.0/numpy.pi peaksSubBase = peaksCh-base print 'count rate:',len(base) print 'mean baseline:',numpy.mean(base) print 'median baseline:',numpy.median(base) print 'stddev baseline:',numpy.std(base) self.axes0.clear() #self.axes0.plot(timestamp[ch], '.') self.axes0.plot(base, 'g.-') self.axes0.plot(peaksCh, 'b.-') self.axes0.plot(peaksSubBase, 'r.-') self.canvas.draw() def channelInc(self): ch_we = int(self.textbox_channel.text()) ch_we = ch_we + 1 if ch_we >=self.numFreqs: ch_we=0 self.textbox_channel.setText(str(ch_we)) def toggleDAC(self): if self.dacStatus == 'off': print "Starting LUT...", self.roach.write_int('startDAC', 1) time.sleep(1) while self.roach.read_int('DRAM_LUT_rd_valid') != 0: self.roach.write_int('startDAC', 0) time.sleep(0.25) self.roach.write_int('startDAC', 1) time.sleep(1) print ".", print "done." #self.button_startDAC.setText('Stop DAC') self.dacStatus = 'on' self.status_text.setText('DAC turned on. ') else: self.roach.write_int('startDAC', 0) #self.button_startDAC.setText('Start DAC') self.dacStatus = 'off' self.status_text.setText('DAC turned off. ') def loadIQcenters(self): for ch in range(256): I_c = int(self.iq_centers[ch].real/2**3) Q_c = int(self.iq_centers[ch].imag/2**3) center = (I_c<<16) + (Q_c<<0) self.roach.write_int('conv_phase_centers', center) self.roach.write_int('conv_phase_load_centers', (ch<<1)+(1<<0)) self.roach.write_int('conv_phase_load_centers', 0) def select_bins(self, readout_freqs): fft_len = 2**9 bins = '' i = 0 residuals = [] for f in readout_freqs: fft_bin = int(round(f*fft_len/self.sampleRate)) fft_freq = fft_bin*self.sampleRate/fft_len freq_residual = round((f - fft_freq)/self.freqRes)*self.freqRes residuals.append(freq_residual) bins = bins + struct.pack('>l', fft_bin) self.roach.write_int('bins', fft_bin) self.roach.write_int('load_bins', (i<<1) + (1<<0)) self.roach.write_int('load_bins', (i<<1) + (0<<0)) i = i + 1 self.status_text.setText('done writing LUTs. ') return residuals def loadLUTs(self): self.scale_factor = 1. self.iq_centers = numpy.array([0.+0j]*256) # Loads the DAC and DDS LUTs from file. As well as the IQ loop centers. if self.dacStatus == 'off': self.roach.write_int('startDAC', 0) else: self.toggleDAC() saveDir = str(self.textbox_lutDir.text()) #saveDir = str(os.environ['PWD'] + '/'+ self.textbox_lutDir.text()) f = open(saveDir+'luts.dat', 'r') binaryData = f.read() self.roach.write('dram_memory', binaryData) x = numpy.loadtxt(saveDir+'centers.dat') N_freqs = len(x[:,0]) for n in range(N_freqs): self.iq_centers[n] = complex(x[n,0], x[n,1]) # Select and write bins for first stage of channelizer. freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) f_base = float(self.textbox_loFreq.text()) for n in range(len(freqs)): if freqs[n] < f_base: freqs[n] = freqs[n] + 512e6 freqs_dds = [0 for j in range(256)] for n in range(len(freqs)): freqs_dds[n] = round((freqs[n]-f_base)/self.freqRes)*self.freqRes freq_residuals = self.select_bins(freqs_dds) print 'LUTs and IQ centers loaded from file.' self.loadIQcenters() self.toggleDAC() def importFreqs(self): freqFile =str(self.textbox_freqFile.text()) self.loadCustomAtten() try: x = numpy.loadtxt(freqFile) x_string = '' for i in range(len(self.customResonators)): if self.customResonators[i][1]!=-1: x[i+1,0]=self.customResonators[i][0] x[i+1,3]=self.customResonators[i][1] self.previous_scale_factor = x[0,0] N_freqs = len(x[1:,0]) self.numFreqs=N_freqs for l in x[1:,0]: x_string = x_string + str(l*1e9) + '\n' self.iq_centers = numpy.array([0.+0j]*256) for n in range(N_freqs): #for n in range(256): self.iq_centers[n] = complex(x[n+1,1], x[n+1,2]) self.attens = x[1:,3] self.textedit_DACfreqs.setText(x_string) self.findDeletedResonators() print 'Freq/Atten loaded from',freqFile self.status_text.setText('Freq/Atten loaded') self.loadCustomThresholds() except IOError: print 'No such file or directory:',freqFile self.status_text.setText('IOError') def findDeletedResonators(self): for i in range(len(self.customResonators)): if self.customResonators[i][1] >=99: self.zeroChannels[i] = 1 else: self.zeroChannels[i] = 0 #needed so you can undelete resonators def loadCustomAtten(self): freqFile =str(self.textbox_freqFile.text()) newFreqFile = freqFile[:-4] + '_NEW.txt' try: y=numpy.loadtxt(newFreqFile) self.customResonators=numpy.array([[0.0,-1]]*256) if type(y[0]) == numpy.ndarray: for arr in y: self.customResonators[int(arr[0])]=arr[1:3] else: self.customResonators[int(y[0])]=y[1:3] print 'Loaded custom resonator freq/atten from',newFreqFile except IOError: pass def displayResonatorProperties(self): ch=int(self.textbox_channel.text()) freqFile =str(self.textbox_freqFile.text()) x = numpy.loadtxt(freqFile) #self.loadCustomAtten() for i in range(len(self.customResonators)): if self.customResonators[i][1]!=-1: x[i+1,0]=self.customResonators[i][0] x[i+1,3]=self.customResonators[i][1] #print 'atten: '+str(x[ch,3]) self.label_attenuation.setText('attenuation: ' + str(int(x[ch+1,3]))) self.label_frequency.setText('freq (GHz): ' + str(float(x[ch+1,0]))) self.label_median.setText('median: '+str(self.medians[ch])) if self.customThresholds[ch] != 360.0: self.label_threshold.setText('threshold: '+str(self.customThresholds[ch])) else: self.label_threshold.setText('threshold: '+str(self.thresholds[ch])) def importFIRcoeffs(self): coeffsFile =str(self.textbox_coeffsFile.text()) self.fir = numpy.loadtxt(coeffsFile) print self.fir def file_dialog(self): print 'add dialog box here' #self.newdatadir = QFileDialog.getExistingDirectory(self, str("Choose SaveDirectory"), "",QFileDialog.ShowDirsOnly) #if len(self.newdatadir) > 0: # self.datadir = self.newdatadir # print self.datadir #self.ui.data_directory_lineEdit.setText(self.datadir) #put new path name in line edit # self.button_saveDir.setText(str(self.datadir)) def create_main_frame(self): self.main_frame = QWidget() # Create the mpl Figure and FigCanvas objects. self.dpi = 100 self.fig = Figure((9.0, 5.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) self.axes0 = self.fig.add_subplot(121) self.axes1 = self.fig.add_subplot(122) cid=self.canvas.mpl_connect('button_press_event', self.setCustomThreshold) # Create the navigation toolbar, tied to the canvas self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) # Roach board's IP address self.textbox_roachIP = QLineEdit('10.0.0.1%d'%roachNum) self.textbox_roachIP.setMaximumWidth(200) label_roachIP = QLabel('Roach IP Address:') # Start connection to roach. self.button_openClient = QPushButton("(1)Open Client") self.button_openClient.setMaximumWidth(100) self.connect(self.button_openClient, SIGNAL('clicked()'), self.openClient) # DAC Frequencies. self.textedit_DACfreqs = QTextEdit() self.textedit_DACfreqs.setMaximumWidth(170) self.textedit_DACfreqs.setMaximumHeight(100) label_DACfreqs = QLabel('DAC Freqs:') # File with frequencies/attens self.textbox_freqFile = QLineEdit('/home/sean/data/20121006/ps_freq%d.txt'%roachNum) self.textbox_freqFile.setMaximumWidth(200) # Import freqs from file. self.button_importFreqs = QPushButton("(2)Load freqs") self.button_importFreqs.setMaximumWidth(200) self.connect(self.button_importFreqs, SIGNAL('clicked()'), self.importFreqs) # File with FIR coefficients self.textbox_coeffsFile = QLineEdit('/home/sean/data/common/matched_r4p46.txt') self.textbox_coeffsFile.setMaximumWidth(200) # Import FIR coefficients from file. self.button_importFIRcoeffs = QPushButton("Import FIR coeffs.") self.button_importFIRcoeffs.setMaximumWidth(200) self.connect(self.button_importFIRcoeffs, SIGNAL('clicked()'), self.importFIRcoeffs) # Channel increment by 1. self.button_channelInc = QPushButton("Channel++") self.button_channelInc.setMaximumWidth(100) self.connect(self.button_channelInc, SIGNAL('clicked()'), self.channelInc) # Load FIR coefficients. self.button_loadFIRcoeffs = QPushButton("(3)load FIR") self.button_loadFIRcoeffs.setMaximumWidth(170) self.connect(self.button_loadFIRcoeffs, SIGNAL('clicked()'), self.loadFIRcoeffs) # Load thresholds. self.button_loadThresholds = QPushButton("(4)load thresholds") self.button_loadThresholds.setMaximumWidth(170) self.connect(self.button_loadThresholds, SIGNAL('clicked()'), self.loadThresholds) # remove custom threshold button self.button_rmCustomThreshold = QPushButton("remove custom threshold") self.button_rmCustomThreshold.setMaximumWidth(170) self.connect(self.button_rmCustomThreshold, SIGNAL('clicked()'), self.rmCustomThreshold) # Channel to measure self.textbox_channel = QLineEdit('0') self.textbox_channel.setMaximumWidth(50) # threshold N*sigma self.textbox_Nsigma = QLineEdit('2.5') self.textbox_Nsigma.setMaximumWidth(50) label_Nsigma = QLabel('sigma ') # Time snapshot of a single channel self.button_snapshot = QPushButton("snapshot") self.button_snapshot.setMaximumWidth(170) self.connect(self.button_snapshot, SIGNAL('clicked()'), self.snapshot) # Long time snapshot of a single channel self.button_longsnapshot = QPushButton("longsnapshot") self.button_longsnapshot.setMaximumWidth(170) self.connect(self.button_longsnapshot, SIGNAL('clicked()'), self.longsnapshot) # Read pulses self.button_readPulses = QPushButton("Read pulses") self.button_readPulses.setMaximumWidth(170) self.connect(self.button_readPulses, SIGNAL('clicked()'), self.readPulses) # Seconds for "read pulses." self.textbox_seconds = QLineEdit('1') self.textbox_seconds.setMaximumWidth(50) # lengths of 2 ms for defining thresholds. self.textbox_timeLengths = QLineEdit('10') self.textbox_timeLengths.setMaximumWidth(50) label_timeLengths = QLabel('* 2 msec ') # lengths of 2 ms steps to combine in a snapshot. self.textbox_snapSteps = QLineEdit('10') self.textbox_snapSteps.setMaximumWidth(50) label_snapSteps = QLabel('* 2 msec') # lengths of 2 ms steps to combine in a snapshot. self.textbox_longsnapSteps = QLineEdit('1') self.textbox_longsnapSteps.setMaximumWidth(50) label_longsnapSteps = QLabel('* sec') #median self.label_median = QLabel('median: 0.0000') self.label_median.setMaximumWidth(170) #threshold self.label_threshold = QLabel('threshold: 0.0000') self.label_threshold.setMaximumWidth(170) #attenuation self.label_attenuation = QLabel('attenuation: 0') self.label_attenuation.setMaximumWidth(170) #frequency self.label_frequency = QLabel('freq (GHz): 0.0000') self.label_frequency.setMaximumWidth(170) # Add widgets to window. gbox0 = QVBoxLayout() hbox00 = QHBoxLayout() hbox00.addWidget(self.textbox_roachIP) hbox00.addWidget(self.button_openClient) gbox0.addLayout(hbox00) hbox01 = QHBoxLayout() hbox01.addWidget(self.textbox_freqFile) hbox01.addWidget(self.button_importFreqs) gbox0.addLayout(hbox01) hbox02 = QHBoxLayout() hbox02.addWidget(self.textbox_coeffsFile) hbox02.addWidget(self.button_importFIRcoeffs) hbox02.addWidget(self.button_loadFIRcoeffs) gbox0.addLayout(hbox02) hbox03 = QHBoxLayout() hbox03.addWidget(self.textbox_timeLengths) hbox03.addWidget(label_timeLengths) hbox03.addWidget(self.textbox_Nsigma) hbox03.addWidget(label_Nsigma) hbox03.addWidget(self.button_loadThresholds) gbox0.addLayout(hbox03) gbox1 = QVBoxLayout() gbox1.addWidget(label_DACfreqs) gbox1.addWidget(self.textedit_DACfreqs) gbox2 = QVBoxLayout() hbox20 = QHBoxLayout() hbox20.addWidget(self.textbox_channel) hbox20.addWidget(self.button_channelInc) gbox2.addLayout(hbox20) hbox21 = QHBoxLayout() hbox21.addWidget(self.button_snapshot) hbox21.addWidget(self.textbox_snapSteps) hbox21.addWidget(label_snapSteps) gbox2.addLayout(hbox21) hbox22 = QHBoxLayout() hbox22.addWidget(self.button_longsnapshot) hbox22.addWidget(self.textbox_longsnapSteps) hbox22.addWidget(label_longsnapSteps) gbox2.addLayout(hbox22) gbox2.addWidget(self.button_rmCustomThreshold) hbox23 = QHBoxLayout() hbox23.addWidget(self.textbox_seconds) hbox23.addWidget(self.button_readPulses) gbox2.addLayout(hbox23) gbox3 = QVBoxLayout() gbox3.addWidget(self.label_median) gbox3.addWidget(self.label_threshold) gbox3.addWidget(self.label_attenuation) gbox3.addWidget(self.label_frequency) hbox = QHBoxLayout() hbox.addLayout(gbox0) hbox.addLayout(gbox1) hbox.addLayout(gbox2) hbox.addLayout(gbox3) vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) vbox.addLayout(hbox) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def create_status_bar(self): self.status_text = QLabel("Awaiting orders.") self.statusBar().addWidget(self.status_text, 1) def create_menu(self): self.file_menu = self.menuBar().addMenu("&File") load_file_action = self.create_action("&Save plot",shortcut="Ctrl+S", slot=self.save_plot, tip="Save the plot") quit_action = self.create_action("&Quit", slot=self.close, shortcut="Ctrl+Q", tip="Close the application") self.add_actions(self.file_menu, (load_file_action, None, quit_action)) self.help_menu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut='F1', slot=self.on_about, tip='About the demo') self.add_actions(self.help_menu, (about_action,)) def add_actions(self, target, actions): for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def create_action( self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def save_plot(self): file_choices = "PNG (*.png)|*.png" path = unicode(QFileDialog.getSaveFileName(self, 'Save file', '',file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def on_about(self): msg = """ Message to user goes here. """ QMessageBox.about(self, "MKID-ROACH software demo", msg.strip())
class QArkMplPlotWidget(QtGui.QWidget): """ """ VIEW_MODE__PLOT = 1 VIEW_MODE__IMAGESHOW = 2 VIEW_MODE__MATRIXSHOW = 4 VIEW_MODE__WIREFRAME = 8 VIEW_MODE__SURFACE = 16 VIEW_MODE__COLORMESH = 32 def __init__(self, parent=None): """ """ super(QArkMplPlotWidget, self).__init__(parent=parent) self.initUi() self.initConnection() self.t_axes = {} def initUi(self): """ """ self.setObjectName(_fromUtf8("qArkMplPlotWidget")) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) vbox = QtGui.QVBoxLayout() #-------------------------------------------------------------------------------- # Plot zone #-------------------------------------------------------------------------------- self.dpi = 70 #self.figure = plt.Figure((10.0, 10.0), dpi=self.dpi) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) print(self) self.canvas.setParent(self) self.mpl_toolbar = NavigationToolbar(self.canvas, self) vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) self.setLayout(vbox) def initConnection(self): """ """ pass def initAxe(self, _u_c, _u_mode): if _u_mode is self.__class__.VIEW_MODE__PLOT: self.t_axes[_u_c] = self.fig.add_subplot(_u_c) elif _u_mode is self.__class__.VIEW_MODE__IMAGESHOW: self.t_axes[_u_c] = self.fig.add_subplot(_u_c) elif _u_mode is self.__class__.VIEW_MODE__MATRIXSHOW: self.t_axes[_u_c] = self.fig.add_subplot(_u_c) elif _u_mode is self.__class__.VIEW_MODE__WIREFRAME: self.t_axes[_u_c] = self.fig.add_subplot(_u_c, projection='3d') elif _u_mode is self.__class__.VIEW_MODE__SURFACE: self.t_axes[_u_c] = self.fig.add_subplot(_u_c, projection='3d') elif _u_mode is self.__class__.VIEW_MODE__COLORMESH: self.t_axes[_u_c] = self.fig.add_subplot(_u_c) return self.t_axes[_u_c] def enableCurrentFigure(self): plt.figure(self.figure.number) def displayPlot(self): self.canvas.draw() def savePlot(self, _s_filename): self.canvas.print_figure(_s_filename) def setPlotter(self, _o_plotter): self.o_plotter = _o_plotter self.enableCurrentFigure() self.o_plotter.setFigure(self.figure) self.o_plotter.plot() self.displayPlot() @QtCore.pyqtSlot() def updatePlot(self): self.o_plotter.plot() self.displayPlot()
class GNSSAzEl_window(QtGui.QMainWindow, Ui_GNSSAzElWindow): def __init__(self): super(GNSSAzEl_window, self).__init__() self.setupUi(self) self.init_Ui() def init_Ui(self): self.create_menuActions() self.btn_close.clicked.connect(self.close) self.dpi = 100 self.fig = Figure((6.6, 6.6), dpi=self.dpi) self.fig.patch.set_facecolor('none') self.canvas = FigureCanvas(self.fig) # Position as left, top, width, height self.canvas.setGeometry(QtCore.QRect(40, 60, 600, 600)) self.canvas.setParent(self) self.drawAzElPlt() self.checkBoxGPS.clicked.connect(self.refreshAzElPlt) self.checkBoxGLONASS.clicked.connect(self.refreshAzElPlt) self.checkBoxGALILEO.clicked.connect(self.refreshAzElPlt) self.checkBoxBEIDOU.clicked.connect(self.refreshAzElPlt) self.refreshtimer = QtCore.QTimer() self.refreshtimer.start(5000) #ms self.refreshtimer.timeout.connect(self.refreshAzElPlt) self.btn_close.clicked.connect(self.refreshtimer.stop) def drawAzElPlt(self): """ Draws the GNSS Az-El plot. """ self.grid = rc('grid', color='green', linewidth=0.5, linestyle='-') self.grid = rc('xtick', labelsize=15) self.grid = rc('ytick', labelsize=10) self.ax = self.fig.add_axes([0.1, 0.1, 0.8, 0.8], projection='polar', axisbg='#d5de9c') self.ax.set_rmax(90) self.ax.set_rgrids([10, 20, 30, 40, 50, 60, 70, 80, 90], angle=0) self.ax.set_theta_offset(0.5 * math.pi) self.ax.set_theta_direction(-1) self.ax.set_title( "GNSS satellites on the local horizon", va='bottom', ) self.ax.set_yticklabels(map(str, range(80, 0, -10))) self.ax.set_xticklabels(['N', '', 'E', '', 'S', '', 'W', '']) self.ax.set_autoscalex_on(False) self.ax.set_autoscaley_on(False) if self.checkBoxGPS.isChecked() == True: [GPSname, phi_GPS, r_GPS] = satellites.SatCompute( 'visible', 'GPS') # fills the list with all GPS satellites GPSname = [ j.split()[-2].replace("(", "") + j.split()[-1].replace(")", "") for j in GPSname ] # shortens the displayed satellites' names on the plot self.ax.plot(phi_GPS, r_GPS, 'ro', label='GPS') for i, txt in enumerate(GPSname): self.ax.annotate(txt, (phi_GPS[i], r_GPS[i])) if self.checkBoxGLONASS.isChecked() == True: [GLONASSname, phi_GLONASS, r_GLONASS] = satellites.SatCompute( 'visible', 'COSMOS') # fills the list with all GLONASS satellites GLONASSname = [j[13:16] for j in GLONASSname] self.ax.plot(phi_GLONASS, r_GLONASS, 'bo', label='GLONASS') for i, txt in enumerate(GLONASSname): self.ax.annotate(txt, (phi_GLONASS[i], r_GLONASS[i])) if self.checkBoxGALILEO.isChecked() == True: [GALILEOname, phi_GALILEO, r_GALILEO] = satellites.SatCompute( 'visible', 'GSAT') # fills the list with all GALILEO satellites GALILEOname = [ j.split()[-2].replace("(", "") + j.split()[-1].replace(")", "") for j in GALILEOname ] self.ax.plot(phi_GALILEO, r_GALILEO, 'go', label='GALILEO') for i, txt in enumerate(GALILEOname): self.ax.annotate(txt, (phi_GALILEO[i], r_GALILEO[i])) if self.checkBoxBEIDOU.isChecked() == True: [BEIDOUname, phi_BEIDOU, r_BEIDOU] = satellites.SatCompute( 'visible', 'BEIDOU') # fills the list with all BEIDOU satellites BEIDOUname = [j.split()[-1] for j in BEIDOUname] self.ax.plot(phi_BEIDOU, r_BEIDOU, 'ko', label='BEIDOU') for i, txt in enumerate(BEIDOUname): self.ax.annotate(txt, (phi_BEIDOU[i], r_BEIDOU[i])) self.canvas.draw() def refreshAzElPlt(self): """ Refreshes the drawn plot """ self.ax.clear() self.drawAzElPlt() def create_menuActions(self): """ Creates actions for the menubar of the GNSS Az-El window. :return: Nothing """ save_file_action = self.create_action( "&Save current view", shortcut="Ctrl+S", slot=self.save_AzElPlot, tip="Saves current Azimuth-Elevation view as an image.") quit_action = self.create_action("&Quit", slot=self.close, shortcut="Ctrl+Q", tip="Closes current window") about_action = self.create_action( "&About", shortcut='F1', slot=self.on_about, tip='Displays additional information.') self.add_actions(self.file_menu, (save_file_action, None, quit_action)) self.add_actions(self.help_menu, (about_action, )) def add_actions(self, target, actions): for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def create_action(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QtGui.QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, QtCore.SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def save_AzElPlot(self): """ Saves the current GNSS Az-El plot """ file_choices = "PNG (*.png)|*.png" path = unicode( QtGui.QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def on_about(self): """ A message of the help option """ msg = """ This window displays current positions of GNSS satellites on the local horizon. Positions computed using two-line element set (TLE) data. """ QtGui.QMessageBox.about(self, "GNSS Az-El view", msg.strip())
class MplFigureCellWidget(QCellWidget): """ MplFigureCellWidget is the actual QWidget taking the FigureManager as a child for displaying figures """ def __init__(self, parent=None): """ MplFigureCellWidget(parent: QWidget) -> MplFigureCellWidget Initialize the widget with its central layout """ QCellWidget.__init__(self, parent) self.setFocusPolicy(QtCore.Qt.StrongFocus) centralLayout = QtGui.QVBoxLayout() self.setLayout(centralLayout) centralLayout.setMargin(0) centralLayout.setSpacing(0) # self.figManager = pylab.get_current_fig_manager() # self.figManager = None # self.figNumber = None self.canvas = None self.figure = None self.figManager = None self.toolBarType = MplFigureCellToolBar self.mplToolbar = None # def getFigManager(self): # if self.figNumber is not None: # pylab.figure(self.figNumber) # return pylab.get_current_fig_manager() # return None # def getFigure(self): # if self.figNumber is not None: # return pylab.figure(self.figNumber) # return None def updateContents(self, inputPorts): """ updateContents(inputPorts: tuple) -> None Update the widget contents based on the input data """ (fig, ) = inputPorts if not self.figure or self.figure.number != fig.figInstance.number: if self.layout().count() > 0: self.layout().removeWidget(self.canvas) self.figure = fig.figInstance # self.figure.set_size_inches(8.0,6.0) self.canvas = FigureCanvasQTAgg(self.figure) self.mplToolbar = MplNavigationToolbar(self.canvas, None) self.canvas.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) # self.figManager = FigureManagerBase(self.canvas, self.figure.number) self.layout().addWidget(self.canvas) # Update the new figure canvas # self.canvas.draw() # self.layout().addWidget(self.getFigManager().window) # Update the new figure canvas # self.getFigManager().canvas.draw() # # Replace the old one with the new one # if newFigManager!=self.figManager: # # Remove the old figure manager # if self.figManager: # self.figManager.window.hide() # self.layout().removeWidget(self.figManager.window) # # Add the new one in # self.layout().addWidget(newFigManager.window) # # Destroy the old one if possible # if self.figManager: # try: # pylab.close(self.figManager.canvas.figure) # # There is a bug in Matplotlib backend_qt4. It is a # # wrong command for Qt4. Just ignore it and continue # # to destroy the widget # except: # pass # self.figManager.window.deleteLater() # del self.figManager # # Save back the manager # self.figManager = newFigManager # self.update() def keyPressEvent(self, event): print "KEY PRESS:", event.key() self.canvas.keyPressEvent(event) def keyReleaseEvent(self, event): print "KEY RELEASE:", event.key() self.canvas.keyReleaseEvent(event) def deleteLater(self): """ deleteLater() -> None Overriding PyQt deleteLater to free up resources """ # Destroy the old one if possible if self.figure is not None: # self.getFigManager().window.deleteLater() print "pylab:", pylab print "self.figure:", self.figure pylab.close(self.figure) # if self.figManager: # try: # pylab.close(self.figManager.canvas.figure) # # There is a bug in Matplotlib backend_qt4. It is a # # wrong command for Qt4. Just ignore it and continue # # to destroy the widget # except: # pass # self.figManager.window.deleteLater() QCellWidget.deleteLater(self) def grabWindowPixmap(self): """ grabWindowPixmap() -> QPixmap Widget special grabbing function """ # pylab.figure(self.figNumber) # figManager = pylab.get_current_fig_manager() return QtGui.QPixmap.grabWidget(self.canvas) def dumpToFile(self, filename): previous_size = tuple(self.figure.get_size_inches()) self.figure.set_size_inches(8.0,6.0) self.canvas.print_figure(filename) self.figure.set_size_inches(previous_size[0],previous_size[1]) self.canvas.draw() def saveToPDF(self, filename): previous_size = tuple(self.figure.get_size_inches()) self.figure.set_size_inches(8.0,6.0) self.canvas.print_figure(filename) self.figure.set_size_inches(previous_size[0],previous_size[1]) self.canvas.draw()
class GridDisplay(Component): ''' Class to create a display plot, using a Grid structure. ''' Vgrid = None #: see :ref:`shared_variable` Vfield = None #: see :ref:`shared_variable` VlevelZ = None \ #: see :ref:`shared_variable`, only used if plot_type="gridZ" VlevelY = None \ #: see :ref:`shared_variable`, only used if plot_type="gridY" VlevelX = None \ #: see :ref:`shared_variable`, only used if plot_type="gridX" Vcmap = None #: see :ref:`shared_variable` @classmethod def guiStart(self, parent=None): '''Graphical interface for starting this class''' args = _DisplayStart().startDisplay() return self(**args), True def __init__(self, Vgrid, Vfield, VlevelZ=None, VlevelY=None, VlevelX=None, Vlims=None, Vcmap=None, plot_type="gridZ", name="Display", parent=None): ''' Initialize the class to create display. Parameters ---------- Vgrid : :py:class:`~artview.core.core.Variable` instance grid signal variable. Vfield : :py:class:`~artview.core.core.Variable` instance Field signal variable. [Optional] VlevelZ : :py:class:`~artview.core.core.Variable` instance Signal variable for vertical level, only used if plot_type="gridZ". If None start with value zero. VlevelY : :py:class:`~artview.core.core.Variable` instance Signal variable for latitudinal level, only used if plot_type="gridY". If None start with value zero. VlevelX : :py:class:`~artview.core.core.Variable` instance Signal variable for longitudinal level, only used if plot_type="gridX". If None start with value zero. Vlims : :py:class:`~artview.core.core.Variable` instance Limits signal variable. A value of None will instantiate a limits variable. Vcmap : :py:class:`~artview.core.core.Variable` instance Colormap signal variable. A value of None will instantiate a colormap variable. plot_type : "gridZ", "gridY" or "gridX" Define plot type, "gridZ" will plot a Z level, that is a XY plane. Analog for "gridY" and "gridZ" name : string Display window name. parent : PyQt instance Parent instance to associate to Display window. If None, then Qt owns, otherwise associated with parent PyQt instance. Notes ----- This class records the selected button and passes the change value back to variable. ''' super(GridDisplay, self).__init__(name=name, parent=parent) self.setFocusPolicy(QtCore.Qt.ClickFocus) self.basemap = None # Set up signal, so that DISPLAY can react to # external (or internal) changes in grid, field, # lims and level (expected to be Core.Variable instances) # The capital V so people remember using ".value" self.Vgrid = Vgrid self.Vfield = Vfield if VlevelZ is None: self.VlevelZ = Variable(0) else: self.VlevelZ = VlevelZ if VlevelY is None: self.VlevelY = Variable(0) else: self.VlevelY = VlevelY if VlevelX is None: self.VlevelX = Variable(0) else: self.VlevelX = VlevelX if Vlims is None: self.Vlims = Variable(None) else: self.Vlims = Vlims if Vcmap is None: self.Vcmap = Variable(None) else: self.Vcmap = Vcmap self.sharedVariables = {"Vgrid": self.Newgrid, "Vfield": self.NewField, "Vlims": self.NewLims, "Vcmap": self.NewCmap, } self.change_plot_type(plot_type) # Connect the components self.connectAllVariables() # Set plot title and colorbar units to defaults self.title = self._get_default_title() self.units = self._get_default_units() # set default latlon lines self.lat_lines = np.linspace(-90, 90, num=181) self.lon_lines = np.linspace(-180, 180, num=361) # Find the PyArt colormap names self.cm_names = ["pyart_" + m for m in pyart.graph.cm.datad if not m.endswith("_r")] self.cm_names.sort() # Create tool dictionary self.tools = {} # Set up Default limits and cmap if Vlims is None: self._set_default_limits(strong=False) if Vcmap is None: self._set_default_cmap(strong=False) # Create a figure for output self._set_fig_ax() # Launch the GUI interface self.LaunchGUI() # Initialize grid variable self.Newgrid(None, None, True) self._update_fig_ax() self.show() def keyPressEvent(self, event): '''Allow level adjustment via the Up-Down arrow keys.''' if event.key() == QtCore.Qt.Key_Up: self.LevelSelectCmd(self.Vlevel.value + 1) elif event.key() == QtCore.Qt.Key_Down: self.LevelSelectCmd(self.Vlevel.value - 1) else: super(GridDisplay, self).keyPressEvent(event) #################### # GUI methods # #################### def LaunchGUI(self): '''Launches a GUI interface.''' # Create layout self.layout = QtGui.QGridLayout() self.layout.setSpacing(8) # Create the widget self.central_widget = QtGui.QWidget() self.setCentralWidget(self.central_widget) self._set_figure_canvas() self.central_widget.setLayout(self.layout) # Add buttons along display for user control self.addButtons() self.setUILayout() # Set the status bar to display messages self.statusbar = self.statusBar() ################################## # User display interface methods # ################################## def addButtons(self): '''Add a series of buttons for user control over display.''' # Create the Display controls self._add_displayBoxUI() # Create the Level controls self._add_levelBoxUI() # Create the Field controls self._add_fieldBoxUI() # Create the Tools controls self._add_toolsBoxUI() # Create the Informational label at top self._add_infolabel() def setUILayout(self): '''Setup the button/display UI layout.''' self.layout.addWidget(self.levelBox, 0, 0) self.layout.addWidget(self.fieldBox, 0, 1) self.layout.addWidget(self.dispButton, 0, 2) self.layout.addWidget(self.toolsButton, 0, 3) self.layout.addWidget(self.infolabel, 0, 4) ############################# # Functionality methods # ############################# def _open_LimsDialog(self): '''Open a dialog box to change display limits.''' from .limits import limits_dialog limits, cmap, change = limits_dialog(self.Vlims.value, self.Vcmap.value, self.name) if change == 1: self.Vcmap.change(cmap, False) self.Vlims.change(limits) def _fillLevelBox(self): '''Fill in the Level Window Box with current levels.''' self.levelBox.clear() self.levelBox.addItem("Level Window") # Loop through and create each level button if self.plot_type == "gridZ": levels = self.Vgrid.value.axes['z_disp']['data'] elif self.plot_type == "gridY": levels = self.Vgrid.value.axes['y_disp']['data'] elif self.plot_type == "gridX": levels = self.Vgrid.value.axes['x_disp']['data'] for nlevel in range(len(levels)): btntxt = "%2.1f m (level %d)" % (levels[nlevel], nlevel+1) self.levelBox.addItem(btntxt) def _fillFieldBox(self): '''Fill in the Field Window Box with current variable names.''' self.fieldBox.clear() self.fieldBox.addItem("Field Window") # Loop through and create each field button for field in self.fieldnames: self.fieldBox.addItem(field) def _levelAction(self, text): '''Define action for Level Button selection.''' if text == "Level Window": self._open_levelbuttonwindow() else: nlevel = int(text.split("(level ")[1][:-1])-1 self.LevelSelectCmd(nlevel) def _fieldAction(self, text): '''Define action for Field Button selection.''' if text == "Field Window": self._open_fieldbuttonwindow() else: self.FieldSelectCmd(str(text)) def _title_input(self): '''Retrieve new plot title.''' val, entry = common.string_dialog_with_reset( self.title, "Plot Title", "Title:", self._get_default_title()) if entry is True: self.title = val self._update_plot() def _units_input(self): '''Retrieve new plot units.''' val, entry = common.string_dialog_with_reset( self.units, "Plot Units", "Units:", self._get_default_units()) if entry is True: self.units = val self._update_plot() def _open_levelbuttonwindow(self): '''Open a LevelButtonWindow instance.''' from .level import LevelButtonWindow if self.plot_type == "gridZ": self.levelbuttonwindow = LevelButtonWindow( self.Vlevel, self.plot_type, Vcontainer=self.Vgrid, controlType="radio", name=self.name+" Level Selection", parent=self.parent) else: self.levelbuttonwindow = LevelButtonWindow( self.Vlevel, self.plot_type, Vcontainer=self.Vgrid, controlType="slider", name=self.name+" Level Selection", parent=self.parent) def _open_fieldbuttonwindow(self): '''Open a FieldButtonWindow instance.''' from .field import FieldButtonWindow self.fieldbuttonwindow = FieldButtonWindow( self.Vgrid, self.Vfield, name=self.name+" Field Selection", parent=self.parent) def _add_cmaps_to_button(self): '''Add a menu to change colormap used for plot.''' for cm_name in self.cm_names: cmapAction = self.dispCmapmenu.addAction(cm_name) cmapAction.setStatusTip("Use the %s colormap" % cm_name) cmapAction.triggered[()].connect( lambda cm_name=cm_name: self.cmapSelectCmd(cm_name)) self.dispCmap.setMenu(self.dispCmapmenu) def _add_displayBoxUI(self): '''Create the Display Options Button menu.''' self.dispButton = QtGui.QPushButton("Display Options") self.dispButton.setToolTip("Adjust display properties") self.dispButton.setFocusPolicy(QtCore.Qt.NoFocus) dispmenu = QtGui.QMenu(self) dispLimits = dispmenu.addAction("Adjust Display Limits") dispLimits.setToolTip("Set data, X, and Y range limits") dispTitle = dispmenu.addAction("Change Title") dispTitle.setToolTip("Change plot title") dispUnit = dispmenu.addAction("Change Units") dispUnit.setToolTip("Change units string") self.dispCmap = dispmenu.addAction("Change Colormap") self.dispCmapmenu = QtGui.QMenu("Change Cmap") self.dispCmapmenu.setFocusPolicy(QtCore.Qt.NoFocus) dispQuickSave = dispmenu.addAction("Quick Save Image") dispQuickSave.setShortcut("Ctrl+D") dispQuickSave.setToolTip( "Save Image to local directory with default name") dispSaveFile = dispmenu.addAction("Save Image") dispSaveFile.setShortcut("Ctrl+S") dispSaveFile.setStatusTip("Save Image using dialog") dispLimits.triggered[()].connect(self._open_LimsDialog) dispTitle.triggered[()].connect(self._title_input) dispUnit.triggered[()].connect(self._units_input) dispQuickSave.triggered[()].connect(self._quick_savefile) dispSaveFile.triggered[()].connect(self._savefile) self._add_cmaps_to_button() self.dispButton.setMenu(dispmenu) def _add_levelBoxUI(self): '''Create the Level Selection ComboBox.''' self.levelBox = QtGui.QComboBox() self.levelBox.setFocusPolicy(QtCore.Qt.NoFocus) self.levelBox.setToolTip( "Select level slice to display.\n" "'Level Window' will launch popup.\n" "Up/Down arrow keys Increase/Decrease level.") self.levelBox.activated[str].connect(self._levelAction) def _add_fieldBoxUI(self): '''Create the Field Selection ComboBox.''' self.fieldBox = QtGui.QComboBox() self.fieldBox.setFocusPolicy(QtCore.Qt.NoFocus) self.fieldBox.setToolTip("Select variable/field in data file.\n" "'Field Window' will launch popup.\n") self.fieldBox.activated[str].connect(self._fieldAction) def _add_toolsBoxUI(self): '''Create the Tools Button menu.''' self.toolsButton = QtGui.QPushButton("Toolbox") self.toolsButton.setFocusPolicy(QtCore.Qt.NoFocus) self.toolsButton.setToolTip("Choose a tool to apply") toolmenu = QtGui.QMenu(self) toolZoomPan = toolmenu.addAction("Zoom/Pan") toolValueClick = toolmenu.addAction("Click for Value") toolSelectRegion = toolmenu.addAction("Select a Region of Interest") toolReset = toolmenu.addAction("Reset Tools") toolDefault = toolmenu.addAction("Reset File Defaults") toolZoomPan.triggered[()].connect(self.toolZoomPanCmd) toolValueClick.triggered[()].connect(self.toolValueClickCmd) toolSelectRegion.triggered[()].connect(self.toolSelectRegionCmd) toolReset.triggered[()].connect(self.toolResetCmd) toolDefault.triggered[()].connect(self.toolDefaultCmd) self.toolsButton.setMenu(toolmenu) def _add_infolabel(self): '''Create an information label about the display''' self.infolabel = QtGui.QLabel("Grid: \n" "Field: \n" "Level: ", self) self.infolabel.setStyleSheet('color: red; font: italic 10px') self.infolabel.setToolTip("Filename not loaded") def _update_infolabel(self): if self.Vgrid.value is None: return self.infolabel.setText( "Grid: %s\n" "Field: %s\n" "Level: %d" % (self.Vgrid.value.metadata['instrument_name'], self.Vfield.value, self.Vlevel.value+1)) if hasattr(self.Vgrid.value, 'filename'): self.infolabel.setToolTip(self.Vgrid.value.filename) ######################## # Selectionion methods # ######################## def Newgrid(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vgrid <artview.core.core.Variable>`. This will: * Update fields and levels lists and MenuBoxes * Check grid scan type and reset limits if needed * Reset units and title * If strong update: update plot ''' # test for None if self.Vgrid.value is None: self.fieldBox.clear() self.levelBox.clear() return # Get field names self.fieldnames = self.Vgrid.value.fields.keys() # Check the file type and initialize limts self._check_file_type() # Update field and level MenuBox self._fillLevelBox() self._fillFieldBox() self.units = self._get_default_units() self.title = self._get_default_title() if strong: self._update_plot() self._update_infolabel() def NewField(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vfield <artview.core.core.Variable>`. This will: * Reset colormap * Reset units * Update fields MenuBox * If strong update: update plot ''' self._set_default_cmap(strong=False) self.units = self._get_default_units() self.title = self._get_default_title() idx = self.fieldBox.findText(value) self.fieldBox.setCurrentIndex(idx) if strong: self._update_plot() self._update_infolabel() def NewLims(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vlims <artview.core.core.Variable>`. This will: * If strong update: update axes ''' if strong: self._update_axes() def NewCmap(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vcmap <artview.core.core.Variable>`. This will: * If strong update: update plot ''' if strong: self._update_plot() def NewLevel(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vlevel* <artview.core.core.Variable>`. This will: * Update level MenuBox * If strong update: update plot ''' # +1 since the first one is "Level Window" self.levelBox.setCurrentIndex(value+1) if strong: self._update_plot() self._update_infolabel() def LevelSelectCmd(self, nlevel): ''' Captures Level selection and update Level :py:class:`~artview.core.core.Variable`. ''' if nlevel < 0: nlevel = len(self.levels)-1 elif nlevel >= len(self.levels): nlevel = 0 self.Vlevel.change(nlevel) def FieldSelectCmd(self, name): ''' Captures field selection and update field :py:class:`~artview.core.core.Variable`. ''' self.Vfield.change(name) def cmapSelectCmd(self, cm_name): '''Captures colormap selection and redraws.''' CMAP = cm_name self.Vcmap.value['cmap'] = cm_name self.Vcmap.update() def toolZoomPanCmd(self): '''Creates and connects to a Zoom/Pan instance.''' from .tools import ZoomPan scale = 1.1 self.tools['zoompan'] = ZoomPan( self.Vlims, self.ax, base_scale=scale, parent=self.parent) self.tools['zoompan'].connect() def toolValueClickCmd(self): '''Creates and connects to Point-and-click value retrieval''' from .pick_value import ValueClick self.tools['valueclick'] = ValueClick( self, name=self.name + "ValueClick", parent=self) def toolSelectRegionCmd(self): '''Creates and connects to Region of Interest instance.''' from .select_region import SelectRegion self.tools['select_region'] = SelectRegion( self, name=self.name + " SelectRegion", parent=self) def toolResetCmd(self): '''Reset tools via disconnect.''' from . import tools self.tools = tools.reset_tools(self.tools) def toolDefaultCmd(self): '''Restore the Display defaults.''' from . import tools self._set_default_limits() self._set_default_cmap() def getPathInteriorValues(self, path): ''' Return the bins values path. Parameters ---------- path : Matplotlib Path instance Returns ------- points: Points Points object containing all bins of the current grid and level inside path. Axes : 'x_disp', 'y_disp', 'x_disp', 'x_index', 'y_index', 'z_index'. Fields: just current field Notes ----- If Vgrid.value is None, returns None ''' from .tools import interior_grid grid = self.Vgrid.value if grid is None: return None xy, idx = interior_grid(path, grid, self.basemap, self.Vlevel.value, self.plot_type) if self.plot_type == "gridZ": x = xy[:, 0] y = xy[:, 1] z = np.ones_like(xy[:, 0]) * self.levels[self.VlevelZ.value] x_idx = idx[:, 0] y_idx = idx[:, 1] z_idx = np.ones_like(idx[:, 0]) * self.VlevelZ.value elif self.plot_type == "gridY": x = xy[:, 0] * 1000. z = xy[:, 1] * 1000. y = np.ones_like(xy[:, 0]) * self.levels[self.VlevelY.value] x_idx = idx[:, 0] z_idx = idx[:, 1] y_idx = np.ones_like(idx[:, 0]) * self.VlevelY.value elif self.plot_type == "gridX": z = xy[:, 0] * 1000. y = xy[:, 1] * 1000. x = np.ones_like(xy[:, 0]) * self.levels[self.VlevelX.value] z_idx = idx[:, 0] y_idx = idx[:, 1] x_idx = np.ones_like(idx[:, 0]) * self.VlevelX.value xaxis = {'data': x, 'long_name': 'X-coordinate in Cartesian system', 'axis': 'X', 'units': 'm'} yaxis = {'data': y, 'long_name': 'Y-coordinate in Cartesian system', 'axis': 'Y', 'units': 'm'} zaxis = {'data': z, 'long_name': 'Z-coordinate in Cartesian system', 'axis': 'Z', 'units': 'm'} field = grid.fields[self.Vfield.value].copy() field['data'] = grid.fields[self.Vfield.value]['data'][ z_idx, y_idx, x_idx] x_idx = {'data': x_idx, 'long_name': 'index in nx dimension'} y_idx = {'data': y_idx, 'long_name': 'index in ny dimension'} z_idx = {'data': z_idx, 'long_name': 'index in nz dimension'} axes = {'x_disp': xaxis, 'y_disp': yaxis, 'z_disp': zaxis, 'x_index': x_idx, 'y_index': y_idx, 'z_index': z_idx, } fields = {self.Vfield.value: field} points = Points(fields, axes, grid.metadata.copy(), xy.shape[0]) return points def getNearestPoints(self, xdata, ydata): ''' Return the bins values nearest to point. Parameters ---------- xdata, ydata : float Returns ------- x, y, z, value, x_idx, y_idx, z_idx: ndarray Truplet of 1arrays containing x,y,z coordinate, current field value, x, y and z index. Notes ----- If Vgrid.value is None, returns None ''' from .tools import nearest_point_grid grid = self.Vgrid.value # map center lat0 = self.Vgrid.value.axes['lat']['data'][0] lon0 = self.Vgrid.value.axes['lon']['data'][0] if grid is None: return (np.array([]),)*7 if self.plot_type == "gridZ": idx = nearest_point_grid( grid, self.basemap, self.levels[self.VlevelZ.value], ydata, xdata) elif self.plot_type == "gridY": idx = nearest_point_grid( grid, self.basemap, ydata * 1000., self.levels[self.VlevelY.value], xdata * 1000.) elif self.plot_type == "gridX": idx = nearest_point_grid( grid, self.basemap, ydata * 1000., xdata * 1000., self.levels[self.VlevelX.value]) aux = (grid.axes['x_disp']['data'][idx[:, 2]], grid.axes['y_disp']['data'][idx[:, 1]], grid.axes['z_disp']['data'][idx[:, 0]], grid.fields[self.Vfield.value]['data'][idx[:, 0], idx[:, 1], idx[:, 2]], idx[:, 2], idx[:, 1], idx[:, 0]) return aux #################### # Plotting methods # #################### def _set_fig_ax(self): '''Set the figure and axis to plot.''' self.XSIZE = 8 self.YSIZE = 8 self.fig = Figure(figsize=(self.XSIZE, self.YSIZE)) self.ax = self.fig.add_axes([0.2, 0.2, 0.7, 0.7]) self.cax = self.fig.add_axes([0.2, 0.10, 0.7, 0.02]) # self._update_axes() def _update_fig_ax(self): '''Set the figure and axis to plot.''' if self.plot_type in ("gridX", "gridY"): self.YSIZE = 5 else: self.YSIZE = 8 xwidth = 0.7 yheight = 0.7 self.ax.set_position([0.15, 0.15, xwidth, yheight]) self.cax.set_position([0.15+xwidth, 0.15, 0.02, yheight]) self._update_axes() def _set_figure_canvas(self): '''Set the figure canvas to draw in window area.''' self.canvas = FigureCanvasQTAgg(self.fig) # Add the widget to the canvas self.layout.addWidget(self.canvas, 1, 0, 7, 6) def _update_plot(self): '''Draw/Redraw the plot.''' if self.Vgrid.value is None: return # Create the plot with PyArt GridMapDisplay self.ax.cla() # Clear the plot axes self.cax.cla() # Clear the colorbar axes if self.Vfield.value not in self.Vgrid.value.fields.keys(): self.canvas.draw() self.statusbar.setStyleSheet("QStatusBar{padding-left:8px;" + "background:rgba(255,0,0,255);" + "color:black;font-weight:bold;}") self.statusbar.showMessage("Field not Found in Radar", msecs=5000) return else: self.statusbar.setStyleSheet("QStatusBar{padding-left:8px;" + "background:rgba(0,0,0,0);" + "color:black;font-weight:bold;}") self.statusbar.clearMessage() title = self.title limits = self.Vlims.value cmap = self.Vcmap.value self.display = pyart.graph.GridMapDisplay(self.Vgrid.value) # Create Plot if self.plot_type == "gridZ": self.display.plot_basemap( self.lat_lines, self.lon_lines, ax=self.ax) self.basemap = self.display.get_basemap() self.plot = self.display.plot_grid( self.Vfield.value, self.VlevelZ.value, vmin=cmap['vmin'], vmax=cmap['vmax'], cmap=cmap['cmap'], colorbar_flag=False, title=title, ax=self.ax, fig=self.fig) elif self.plot_type == "gridY": self.basemap = None self.plot = self.display.plot_latitudinal_level( self.Vfield.value, self.VlevelY.value, vmin=cmap['vmin'], vmax=cmap['vmax'], cmap=cmap['cmap'], colorbar_flag=False, title=title, ax=self.ax, fig=self.fig) elif self.plot_type == "gridX": self.basemap = None self.plot = self.display.plot_longitudinal_level( self.Vfield.value, self.VlevelX.value, vmin=cmap['vmin'], vmax=cmap['vmax'], cmap=cmap['cmap'], colorbar_flag=False, title=title, ax=self.ax, fig=self.fig) limits = self.Vlims.value x = self.ax.get_xlim() y = self.ax.get_ylim() limits['xmin'] = x[0] limits['xmax'] = x[1] limits['ymin'] = y[0] limits['ymax'] = y[1] self._update_axes() norm = mlabNormalize(vmin=cmap['vmin'], vmax=cmap['vmax']) self.cbar = mlabColorbarBase(self.cax, cmap=cmap['cmap'], norm=norm, orientation='vertical') self.cbar.set_label(self.units) if self.plot_type == "gridZ": print("Plotting %s field, Z level %d in %s" % ( self.Vfield.value, self.VlevelZ.value+1, self.name)) elif self.plot_type == "gridY": print("Plotting %s field, Y level %d in %s" % ( self.Vfield.value, self.VlevelY.value+1, self.name)) elif self.plot_type == "gridX": print("Plotting %s field, X level %d in %s" % ( self.Vfield.value, self.VlevelX.value+1, self.name)) self.canvas.draw() def _update_axes(self): '''Change the Plot Axes.''' limits = self.Vlims.value self.ax.set_xlim(limits['xmin'], limits['xmax']) self.ax.set_ylim(limits['ymin'], limits['ymax']) self.ax.figure.canvas.draw() ######################### # Check methods # ######################### def _set_default_limits(self, strong=True): '''Set limits to pre-defined default.''' limits = self.Vlims.value if limits is None: limits = {} if self.Vgrid.value is None: limits['xmin'] = 0 limits['xmax'] = 1 limits['ymin'] = 0 limits['ymax'] = 1 elif self.plot_type == "gridZ": if self.basemap is not None: limits['xmin'] = self.basemap.llcrnrx limits['xmax'] = self.basemap.urcrnrx limits['ymin'] = self.basemap.llcrnry limits['ymax'] = self.basemap.urcrnry else: limits['xmin'] = -150 limits['xmax'] = 150 limits['ymin'] = -150 limits['ymax'] = 150 elif self.plot_type == "gridY": limits['xmin'] = (self.Vgrid.value.axes['x_disp']['data'][0] / 1000.) limits['xmax'] = (self.Vgrid.value.axes['x_disp']['data'][-1] / 1000.) limits['ymin'] = (self.Vgrid.value.axes['z_disp']['data'][0] / 1000.) limits['ymax'] = (self.Vgrid.value.axes['z_disp']['data'][-1] / 1000.) elif self.plot_type == "gridX": limits['xmin'] = (self.Vgrid.value.axes['y_disp']['data'][0] / 1000.) limits['xmax'] = (self.Vgrid.value.axes['y_disp']['data'][-1] / 1000.) limits['ymin'] = (self.Vgrid.value.axes['z_disp']['data'][0] / 1000.) limits['ymax'] = (self.Vgrid.value.axes['z_disp']['data'][-1] / 1000.) self.Vlims.change(limits, strong) def _set_default_cmap(self, strong=True): '''Set colormap to pre-defined default.''' cmap = pyart.config.get_field_colormap(self.Vfield.value) d = {} d['cmap'] = cmap lims = pyart.config.get_field_limits(self.Vfield.value, self.Vgrid.value) if lims != (None, None): d['vmin'] = lims[0] d['vmax'] = lims[1] else: d['vmin'] = -10 d['vmax'] = 65 self.Vcmap.change(d, strong) def _get_default_title(self): '''Get default title from pyart.''' if ( self.Vgrid.value is None or self.Vfield.value not in self.Vgrid.value.fields): return '' if self.plot_type == "gridZ": return pyart.graph.common.generate_grid_title(self.Vgrid.value, self.Vfield.value, self.Vlevel.value) elif self.plot_type == "gridY": return pyart.graph.common.generate_latitudinal_level_title( self.Vgrid.value, self.Vfield.value, self.Vlevel.value) elif self.plot_type == "gridX": return pyart.graph.common.generate_longitudinal_level_title( self.Vgrid.value, self.Vfield.value, self.Vlevel.value) def _get_default_units(self): '''Get default units for current grid and field.''' if self.Vgrid.value is not None: try: return self.Vgrid.value.fields[self.Vfield.value]['units'] except: return '' else: return '' def _check_file_type(self): '''Check file to see if the file type.''' # self._update_fig_ax() return def change_plot_type(self, plot_type): '''Change plot type.''' # remove shared variables for key in ("VlevelZ", "VlevelY", "VlevelX"): if key in self.sharedVariables.keys(): del self.sharedVariables[key] if plot_type == "gridZ": self.sharedVariables["VlevelZ"] = self.NewLevel elif plot_type == "gridY": self.sharedVariables["VlevelY"] = self.NewLevel elif plot_type == "gridX": self.sharedVariables["VlevelX"] = self.NewLevel else: import warnings warnings.warn('Invalid Plot type %s, reseting to gridZ' % plot_type) self.sharedVariables["VlevelZ"] = self.NewLevel plot_type = "gridZ" self.plot_type = plot_type ######################## # Image save methods # ######################## def _quick_savefile(self, PTYPE=IMAGE_EXT): '''Save the current display via PyArt interface.''' imagename = self.display.generate_filename( self.Vfield.value, self.Vlevel.value, ext=IMAGE_EXT) self.canvas.print_figure(os.path.join(os.getcwd(), imagename), dpi=DPI) self.statusbar.showMessage('Saved to %s' % os.path.join(os.getcwd(), imagename)) def _savefile(self, PTYPE=IMAGE_EXT): '''Save the current display using PyQt dialog interface.''' imagename = self.display.generate_filename( self.Vfield.value, self.Vlevel.value, ext=IMAGE_EXT) file_choices = "PNG (*.png)|*.png" path = unicode(QtGui.QFileDialog.getSaveFileName( self, 'Save file', imagename, file_choices)) if path: self.canvas.print_figure(path, dpi=DPI) self.statusbar.showMessage('Saved to %s' % path) ######################## # get methods # ######################## def getPlotAxis(self): ''' get :py:class:`matplotlib.axes.Axes` instance of main plot ''' return self.ax def getStatusBar(self): ''' get :py:class:`PyQt4.QtGui.QStatusBar` instance''' return self.statusbar def getField(self): ''' get current field ''' return self.Vfield.value def getUnits(self): ''' get current units ''' return self.units ######################## # Properties # ######################## @property def Vlevel(self): '''Alias to VlevelZ, VlevelY or VlevelX depending on plot_type.''' if self.plot_type == "gridZ": return self.VlevelZ elif self.plot_type == "gridY": return self.VlevelY elif self.plot_type == "gridX": return self.VlevelX else: return None @property def levels(self): '''Values from the axes of grid, depending on plot_type.''' if self.plot_type == "gridZ": return self.Vgrid.value.axes['z_disp']['data'][:] elif self.plot_type == "gridY": return self.Vgrid.value.axes['y_disp']['data'][:] elif self.plot_type == "gridX": return self.Vgrid.value.axes['x_disp']['data'][:] else: return None
class Stock(QMainWindow): """ main Stock class """ def __init__(self, parent=None): log(NAME, 'debug') QMainWindow.__init__(self, parent) self.comboBox = QComboBox() self.setWindowTitle(NAME) self.create_menu() self.create_main_frame() self.create_status_bar() self.plotColor = 'r' self.plotStyle = '' self.noOfFiles = 30 self.mDict = dict() self.getData() self.comboBox.addItems(sorted(self.mDict.keys())) self.on_draw() log('Initialized ' + str(self.noOfFiles) + ' files...') def on_draw(self): comboChoose = self.comboBox.currentText() cashName, mtply, values = self.getCash(comboChoose) self.data = map(float, values) self.axes.clear() self.axes.grid(self.grid_cb.isChecked()) _y = self.data _x = range(len(self.data)) self.axes.plot(_x, _y, self.plotColor + self.plotStyle, label=str(cashName)) self.axes.legend() self.canvas.draw() def create_menu(self): # ------------------ FILE MENU -------------------- self.file_menu = self.menuBar().addMenu("&File") load_file_action = self.create_action("&Save plot", shortcut="Ctrl+S", slot=self.save_plot, tip="Save the plot") quit_action = self.create_action("&Quit", slot=self.close, shortcut="Ctrl+Q", tip="Close the application") self.add_actions(self.file_menu, (load_file_action, None, quit_action)) # ------------------ PREFS MENU -------------------- self.pref_menu = self.menuBar().addMenu('&Preferences') pref_action = self.create_action('&Options', shortcut='Ctrl+O', slot = self.prefs, tip = 'Configure options') self.add_actions(self.pref_menu, (pref_action,)) # ------------------ HELP MENU -------------------- self.help_menu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut='F1', slot=self.on_about, tip='About this application') self.add_actions(self.help_menu, (about_action,)) def create_main_frame(self): self.main_frame = QWidget() # Create the mpl Figure and FigCanvas objects. # 5x4 inches, 100 dots-per-inch # self.dpi = 100 self.fig = Figure((5.0, 4.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) # Since we have only one plot, we can use add_axes # instead of add_subplot, but then the subplot # configuration tool in the navigation toolbar wouldn't # work. # self.axes = self.fig.add_subplot(111) # Bind the 'pick' event for clicking on one of the bars # self.canvas.mpl_connect('pick_event', self.on_pick) # Create the navigation toolbar, tied to the canvas # self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) # Other GUI controls # self.connect(self.comboBox, SIGNAL('currentIndexChanged(const QString&)'), self.on_draw) self.draw_button = QPushButton("&Draw") self.connect(self.draw_button, SIGNAL('clicked()'), self.on_draw) self.grid_cb = QCheckBox("Show &Grid") self.grid_cb.setChecked(False) self.connect(self.grid_cb, SIGNAL('stateChanged(int)'), self.on_draw) # # Layout with box sizers # hbox = QHBoxLayout() for w in [ self.comboBox, self.draw_button, self.grid_cb]: hbox.addWidget(w) hbox.setAlignment(w, Qt.AlignVCenter) vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) vbox.addLayout(hbox) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def create_status_bar(self): self.status_text = QLabel(NAME) self.statusBar().addWidget(self.status_text, 1) def create_action( self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def save_plot(self): file_choices = "PNG (*.png)|*.png" path = unicode(QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def prefs(self): self.statusBar().showMessage('Preferences') # # Pass the parent - self (the calling form) to the dialog # to take advantage of the fact that by default PyQt centers a dialog over # its parent and also because the dialogs that have a parent do not get a # separate entry in the taskbar. # dialog = PropertitesDlg(self) # # Mark current color as selected # for k,v in PropertitesDlg.COLORS.items(): if v == self.plotColor: _colorStr = k break _indexC = dialog.colorComboBox.findText(_colorStr) if _indexC != -1: dialog.colorComboBox.setCurrentIndex(_indexC) # # Mark current line style as selected # for k,v in PropertitesDlg.STYLES.items(): if v == self.plotStyle: _styleStr = k break _indexS = dialog.styleComboBox.findText(_styleStr) if _indexS != -1: dialog.styleComboBox.setCurrentIndex(_indexS) # # Open a properties dumb dialog and grab results # if dialog.exec_(): self.plotColor = PropertitesDlg.COLORS.get(str(dialog.colorComboBox.currentText())) self.plotStyle = PropertitesDlg.STYLES.get(str(dialog.styleComboBox.currentText())) self.on_draw() def add_actions(self, target, actions): for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def on_pick(self, event): # The event received here is of the type # matplotlib.backend_bases.PickEvent # # It carries lots of information, of which we're using # only a small amount here. # box_points = event.artist.get_bbox().get_points() msg = "You've clicked on a bar with coords:\n %s" % box_points QMessageBox.information(self, "Click!", msg) def on_about(self): msg = '\n' + NAME + '\n\n' + AUTHOR + '\n' + HISTORY + '\n' QMessageBox.about(self, "About", msg.strip()) def getData(self): _mDict = dict() log('Retrieving XML data from nbp.pl...', 'debug') try: fList = urllib.urlopen('http://www.nbp.pl/kursy/xml/dir.txt') lines = fList.readlines() aLines = [ a for a in lines if a.startswith('a') ] # list of files, which starts with 'a' for line in aLines[len(aLines) - self.noOfFiles:]: line = line.strip() log('Retrieving XML data from http://www.nbp.pl/kursy/xml/' + line + '.xml', 'debug') xmlFile = urllib.urlopen('http://www.nbp.pl/kursy/xml/' + line + '.xml') xmlData = xml.dom.minidom.parse(xmlFile) nodeList = xmlData.getElementsByTagName('numer_tabeli') number = nodeList[0].childNodes[0].nodeValue log('Table number: ' + number, 'debug') nodeList = xmlData.getElementsByTagName('data_publikacji') data = nodeList[0].childNodes[0].nodeValue log('Date of publication: ' + data ,'debug') #cNodes = xmlData.childNodes for i in xmlData.getElementsByTagName('pozycja'): code = i.getElementsByTagName('kod_waluty')[0].childNodes[0].nodeValue name = i.getElementsByTagName('nazwa_waluty')[0].childNodes[0].nodeValue.encode("iso-8859-15", "replace") mtply = i.getElementsByTagName('przelicznik')[0].childNodes[0].nodeValue value = i.getElementsByTagName('kurs_sredni')[0].childNodes[0].nodeValue.replace(',', '.') try: _mDict[code].append(value) except KeyError: _mDict[code] = [] _mDict[code].append(name) _mDict[code].append(mtply) _mDict[code].append(value) log(_mDict, 'debug') except Exception, err: log('Exception caught: ' + str(err)) sys.exit(1) self.mDict = _mDict
class EsaEpixForm(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle('ePix ESA Live Display') self.create_menu() self.create_main_frame() self.create_status_bar() self.textbox.setText('1 2 3 4') self.on_draw() def on_about(self): msg = """ ESA ePix online display """ QMessageBox.about(self, "About the app", msg.strip()) def on_pick(self, event): # The event received here is of the type # matplotlib.backend_bases.PickEvent # # It carries lots of information, of which we're using # only a small amount here. # box_points = event.artist.get_bbox().get_points() msg = "You've clicked on a bar with coords:\n %s" % box_points QMessageBox.information(self, "Click!", msg) def on_draw(self): """ Redraws the figure """ str = unicode(self.textbox.text()) self.data = map(int, str.split()) x = range(len(self.data)) # clear the axes and redraw the plot anew # self.axes.clear() self.axes.grid(self.grid_cb.isChecked()) self.axes.bar( left=x, height=self.data, width=self.slider.value() / 100.0, align='center', alpha=0.44, picker=5) self.canvas.draw() def create_menu(self): self.file_menu = self.menuBar().addMenu("&File") load_file_action = self.create_action("&Save plot", shortcut="Ctrl+S", slot=self.save_plot, tip="Save the plot") quit_action = self.create_action("&Quit", shortcut="Ctrl+Q", slot=self.close, tip="Close the application") #self.add_actions(self.file_menu, # (load_file_action, None, quit_action)) self.add_actions(self.file_menu, (quit_action,load_file_action)) self.help_menu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut='F1', slot=self.on_about, tip='About this thing') self.add_actions(self.help_menu, (about_action,)) def create_main_frame(self): self.main_frame = QWidget() # Create the mpl Figure and FigCanvas objects. # 5x4 inches, 100 dots-per-inch # self.dpi = 100 self.fig = Figure((5.0, 4.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) # Since we have only one plot, we can use add_axes # instead of add_subplot, but then the subplot # configuration tool in the navigation toolbar wouldn't # work. # self.axes = self.fig.add_subplot(111) # Bind the 'pick' event for clicking on one of the bars # self.canvas.mpl_connect('pick_event', self.on_pick) # Create the navigation toolbar, tied to the canvas # self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) # Other GUI controls # self.textbox = QLineEdit() self.textbox.setMinimumWidth(200) self.connect(self.textbox, SIGNAL('editingFinished ()'), self.on_draw) self.draw_button = QPushButton("&Draw") self.connect(self.draw_button, SIGNAL('clicked()'), self.on_draw) self.grid_cb = QCheckBox("Show &Grid") self.grid_cb.setChecked(False) self.connect(self.grid_cb, SIGNAL('stateChanged(int)'), self.on_draw) slider_label = QLabel('Bar width (%):') self.slider = QSlider(Qt.Horizontal) self.slider.setRange(1, 100) self.slider.setValue(20) self.slider.setTracking(True) self.slider.setTickPosition(QSlider.TicksBothSides) self.connect(self.slider, SIGNAL('valueChanged(int)'), self.on_draw) # # Layout with box sizers # hbox = QHBoxLayout() for w in [ self.textbox, self.draw_button, self.grid_cb, slider_label, self.slider]: hbox.addWidget(w) hbox.setAlignment(w, Qt.AlignVCenter) vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) vbox.addLayout(hbox) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def save_plot(self): file_choices = "PNG (*.png)|*.png" path = unicode(QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def create_status_bar(self): self.status_text = QLabel("This is a status text") self.statusBar().addWidget(self.status_text, 1) def add_actions(self, target, actions): for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def create_action( self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action
class highTemplar(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) #print '1' self.setWindowTitle('High Templar Resonator Setup') self.create_menu() self.create_main_frame() self.create_status_bar() #print '2' #self.disableCommandButtons() self.numThreadsRunning=0 self.currentChannel=[0,0,0,0,0,0,0,0] #self.roachStatus=[0,0,0,0,0,0,0,0] self.connected = 0 self.roach0=Roach(0) self.roach1=Roach(1) self.roach2=Roach(2) self.roach3=Roach(3) self.roach4=Roach(4) self.roach5=Roach(5) self.roach6=Roach(6) self.roach7=Roach(7) self.roaches=[self.roach0,self.roach1,self.roach2,self.roach3,self.roach4,self.roach5,self.roach6,self.roach7] #print '3' self.showControls() self.threadPool=[] for roach in self.roaches: self.threadPool.append(RoachThread(roach)) pass for thread in self.threadPool: self.connect(thread,QtCore.SIGNAL("connectedRoach(int)"),self.catchRoachConnectionSignal) self.connect(thread,QtCore.SIGNAL("loadedFrequency(int)"),self.catchRoachThreadSignal) self.connect(thread,QtCore.SIGNAL("loadedLUT(int)"),self.catchRoachThreadSignal) self.connect(thread,QtCore.SIGNAL("swept(int)"),self.catchRoachThreadSignal) self.connect(thread,QtCore.SIGNAL("rotated(int)"),self.catchRoachThreadSignal) self.connect(thread,QtCore.SIGNAL("centered(int)"),self.catchRoachThreadSignal) self.connect(thread,QtCore.SIGNAL("loadedFIR(int)"),self.catchRoachThreadSignal) self.connect(thread,QtCore.SIGNAL("loadedThreshold(int)"),self.catchRoachThreadSignal) thread.setTerminationEnabled(False) self.status_text.setText('Connecting Roaches...') QTimer.singleShot(0, self.startThreads) def startThreads(self): for thread in self.threadPool: thread.start() #thread.running() # self.emit(QtCore.SIGNAL("startThread()")) #for roach in self.roaches: # roach.startThread() def catchRoachConnectionSignal(self,roachNum): self.connected+=1 self.colorCommandButtons(roach=[roachNum],color='red') self.roaches[roachNum].setStatus(1) #print 'here 1' #for roach in self.roaches: # if roach.getState() == RoachState.connect: # connected+=1 # #print 'roach',roach.getNum() # self.colorCommandButtons(roach=[int(roach.getNum())],color='red') if self.connected == len(self.roaches): print '...Done connecting roaches' #print 'l:', len(self.commandButtons) self.colorCommandButtons(roach=[len(self.commandButtons)-1],color='blue') self.status_text.setText('All Roaches Connected') def catchRoachThreadSignal(self,roachNum): #print 'here:',roachNum if self.roaches[roachNum].getError() == 0: self.colorCommandButtons(roach=[roachNum],command=[self.roaches[roachNum].getState()],color='green') self.status_text.setText('Roach: '+str(roachNum)+' command: '+ RoachState.parseState(self.roaches[roachNum].getState())+' completed') self.roaches[roachNum].setStatus(1) else: print 'Error on roach',roachNum,':',self.roaches[roachNum].getError() self.colorCommandButtons(roach=[roachNum],command=[self.roaches[roachNum].getNextState()],color='error') self.roaches[roachNum].emptyCommandQueue() self.roaches[roachNum].setStatus(1) self.roaches[roachNum].resetError() def catchRoachLoadFreqSignal(self,s): print 's:',s self.numThreadsRunning-=1 if self.numThreadsRunning == 0: self.threadPool=[] self.enableCommandButtons() if self.checkRoachErrors(RoachState.loadFreq)==0: print 'roaches finished loading freq file!' self.status_text.setText('roaches finished loading freq file') else: #errors! pass def commandButtonClicked(self): source = self.sender() print 'Roach: ',source.roach,'Command: ',RoachState.parseState(source.command) for i in source.roach: if self.roaches[i].hasCommand(): print 'Roach still working...' else: self.commandRoach(i,self.roaches[i].getState(),source.command) #print 'command ready', i, source.command #if source.command == 'loadFreq': # self.disableCommandButtons() # self.status_text.setText('Loading Frequency Files for Roach:'+str(source.roach)) # print 'Loading Frequency Files for Roach:',str(source.roach) # #This is where the GUI grabs the freqFile and gives it to the roaches # #freqFile=self.label_freqFile_0.text() # #roach0.setFreqFile(freqFile) # #print 'nums:',source.roach # self.status=0 # for i in source.roach: # #print 'i:',i # self.threadPool.append(RoachThread(self.roaches[i],RoachState.loadFreq)) # self.connect(self.threadPool[len(self.threadPool)-1],QtCore.SIGNAL("loadedFrequency()"),self.catchRoachLoadFreqSignal) # self.threadPool[len(self.threadPool)-1].start() # self.numThreadsRunning+=1 #else: # print 'No such command',source.command, 'from roach',source.roach def commandRoach(self,roachNum,state,command): """State Machine""" #do everything between current state and new state for com in range(state+1,command): self.roaches[roachNum].pushCommand(com) self.colorCommandButtons(roach=[roachNum],command=[com],color='cyan') print 'pushed',RoachState.parseState(com) self.roaches[roachNum].pushCommand(command) self.colorCommandButtons(roach=[roachNum],command=[command],color='cyan') print 'pushed',RoachState.parseState(command) for com in range(command+1,RoachState.loadThreshold+1): self.colorCommandButtons(roach=[roachNum],command=[com],color='red') #if command == RoachState.loadFreq: # self.roaches[roachNum].pushCommand(RoachState.loadFreq) #elif command == RoachState.defineLUT: # if self.roaches[roachNum].getState() == RoachState.connect: # self.roaches[roachNum].pushCommand(RoachState.loadFreq) # self.roaches[roachNum].pushCommand(RoachState.defineLUT) #elif command == RoachState.sweep: # if self.roaches[roachNum].getState() == RoachState.connect: # self.roaches[roachNum].pushCommand(RoachState.loadFreq) # self.roaches[roachNum].pushCommand(RoachState.defineLUT) # elif self.roaches[roachNum].getState() == RoachState.loadFreq: # self.roaches[roachNum].pushCommand(RoachState.defineLUT) # self.roaches[roachNum].pushCommand(RoachState.sweep) #elif command == RoachState.rotate: # pass #elif command == RoachState.center: # pass #elif command == RoachState.loadFIR: # pass #elif command == RoachState.loadThreshold: # pass #else: # print 'no code iplemented yet' # self.roaches[roachNum].setError(9) #self.roaches[roachNum].setStatus(1) def checkRoachErrors(self,command): errors=0 for roach in self.roaches: if roach.getState == command and roach.getStatus==0: print 'roach',roach.getNum,'failed command',command errors+=1 roach.setStatus(1) return errors def radioRoachChanged(self): source=self.sender() if source.isChecked(): #time.sleep(.1) #self.showControls(source.roach) pass else: #signal from the radio roach that was turned off self.saveControls(source.roach) def showControls(self): """change plot, resonator characteristics etc""" #print 'showing' roachNum=-1 for radio in self.radioRoaches: if radio.isChecked(): roachNum = radio.roach #print 'changing',roachNum roach=self.roaches[int(roachNum)] self.textbox_loFreq.setText('%e'%roach.getLoFreq()) self.textbox_freqFile.setText(str(roach.getFreqFile())) self.textbox_saveDir.setText(str(roach.getSaveDir())) self.textbox_loSpan.setText('%e'%float(roach.getLoSpan())) self.textbox_inputAtten.setText(str(roach.getInputAtten())) self.textbox_startAtten.setText(str(roach.getStartAtten())) self.textbox_stopAtten.setText(str(roach.getStopAtten())) self.textbox_ddsSyncLag.setText(str(roach.getDdsSyncLag())) #print 'done' def saveControls(self,roachNum): """save current resonator characteristics etc""" roach=self.roaches[int(roachNum)] roach.setLoFreq(float(self.textbox_loFreq.text())) roach.setFreqFile(self.textbox_freqFile.text()) roach.setSaveDir(self.textbox_saveDir.text()) roach.setLoSpan(float(self.textbox_loSpan.text())) roach.setInputAtten(int(self.textbox_inputAtten.text())) roach.setStartAtten(int(self.textbox_startAtten.text())) roach.setStopAtten(int(self.textbox_stopAtten.text())) roach.setDdsSyncLag(int(self.textbox_ddsSyncLag.text())) #print 'saved',roachNum self.showControls() def deleteResonator(self): print 'delete resonator' def updateResonator(self): print 'update resonator' self.roaches[0].pushCommand(RoachState.loadFreq) #self.roaches[0].pushCommand(RoachState.defineLUT) def colorCommandButtons(self,roach=[0,1,2,3,4,5,6,7,8],command=[RoachState.loadFreq,RoachState.defineLUT,RoachState.sweep,RoachState.rotate,RoachState.center,RoachState.loadFIR,RoachState.loadThreshold],color='red'): for roachNum in roach: for com in command: if color == 'red': self.commandButtons[roachNum][com-1].setEnabled(True) self.commandButtons[roachNum][com-1].setPalette(self.redButtonPalette) if color == 'green': self.commandButtons[roachNum][com-1].setEnabled(True) self.commandButtons[roachNum][com-1].setPalette(self.greenButtonPalette) if color == 'blue': self.commandButtons[roachNum][com-1].setEnabled(True) self.commandButtons[roachNum][com-1].setPalette(self.blueButtonPalette) if color == 'gray': self.commandButtons[roachNum][com-1].setEnabled(False) self.commandButtons[roachNum][com-1].setPalette(self.grayButtonPalette) if color == 'error': self.commandButtons[roachNum][com-1].setEnabled(True) self.commandButtons[roachNum][com-1].setPalette(self.errorButtonPalette) if color == 'cyan': self.commandButtons[roachNum][com-1].setEnabled(True) self.commandButtons[roachNum][com-1].setPalette(self.cyanButtonPalette) # def disableCommandButtons(self): # for arr in self.commandButtons: # for button in arr: # button.setEnabled(False) # button.setPalette(self.grayButtonPalette) # def enableCommandButtons(self,roach=[0,1,2,3,4,5,6,7,8],command=[RoachState.loadFreq,RoachState.defineLUT,RoachState.sweep,RoachState.rotate,RoachState.center,RoachState.loadFIR,RoachState.loadThreshold],color='red'): # for roachNum in roach: # for com in command: # self.commandButtons[roachNum][com-1].setEnabled(True) # if color == 'red': # self.commandButtons[roachNum][com-1].setPalette(self.redButtonPalette) # if color == 'green': # self.commandButtons[roachNum][com-1].setPalette(self.greenButtonPalette) # def finishedCommandButtons(self,roach=[0,1,2,3,4,5,6,7,8],command=[RoachState.loadFreq,RoachState.defineLUT,RoachState.sweep,RoachState.rotate,RoachState.center,RoachState.loadFIR,RoachState.loadThreshold],color='red'): # for roachNum in roach: # for com in command: # self.commandButtons[roachNum][com-1].setEnabled(True) # if color == 'red': # self.commandButtons[roachNum][com-1].setPalette(self.redButtonPalette) # if color == 'green': # self.commandButtons[roachNum][com-1].setPalette(self.greenButtonPalette) #for arr in self.commandButtons: # for button in arr: # button.setEnabled(True) # button.setPalette(self.redButtonPalette) def create_main_frame(self): self.main_frame = QWidget() # Create the mpl Figure and FigCanvas objects. self.dpi = 100 self.fig = Figure((9.0, 5.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) self.axes0 = self.fig.add_subplot(121) self.axes1 = self.fig.add_subplot(122) #cid=self.canvas.mpl_connect('button_press_event', self.changeCenter) # Create the navigation toolbar, tied to the canvas #self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) self.mpl_toolbar = customNavToolBar(self.canvas,self.main_frame) #button color palettes self.greenButtonPalette = QPalette() self.greenButtonPalette.setColor(QPalette.Button,Qt.green) self.redButtonPalette = QPalette() self.redButtonPalette.setColor(QPalette.Button,Qt.darkRed) self.grayButtonPalette = QPalette() self.grayButtonPalette.setColor(QPalette.Button,Qt.gray) self.blueButtonPalette = QPalette() self.blueButtonPalette.setColor(QPalette.Button,Qt.blue) self.errorButtonPalette = QPalette() self.errorButtonPalette.setColor(QPalette.Button,Qt.red) self.cyanButtonPalette = QPalette() self.cyanButtonPalette.setColor(QPalette.Button,Qt.darkCyan) #Command Labels: self.label_loadFreq = QLabel('Load Freq/Atten:') self.label_defineLUT = QLabel("Define LUT's:") self.label_sweep = QLabel('Sweep:') self.label_rotate = QLabel('Rotate Loops:') self.label_center = QLabel('Center Loops:') self.label_loadFIR = QLabel("Load FIR's:") self.label_loadThreshold = QLabel('Load Thresholds:') self.commandLabels=[self.label_loadFreq,self.label_defineLUT,self.label_sweep,self.label_rotate,self.label_center,self.label_loadFIR,self.label_loadThreshold] for label in self.commandLabels: label.setMaximumWidth(110) label.setMinimumWidth(110) #Roach Labels self.label_roachNum = QLabel('Roach:') self.label_roach0 = QLabel('roach 0') self.label_roach1 = QLabel('roach 1') self.label_roach2 = QLabel('roach 2') self.label_roach3 = QLabel('roach 3') self.label_roach4 = QLabel('roach 4') self.label_roach5 = QLabel('roach 5') self.label_roach6 = QLabel('roach 6') self.label_roach7 = QLabel('roach 7') self.label_allRoaches = QLabel('All') self.roachLabels=[self.label_roachNum,self.label_roach0,self.label_roach1,self.label_roach2,self.label_roach3,self.label_roach4,self.label_roach5,self.label_roach6,self.label_roach7,self.label_allRoaches] for label in self.roachLabels[1:]: label.setMaximumWidth(50) label.setMinimumWidth(50) self.label_roachNum.setMaximumWidth(110) self.label_roachNum.setMinimumWidth(110) #Roach 0 command buttons self.button_loadFreq_0 = QPushButton() self.button_defineLUT_0 = QPushButton() self.button_sweep_0 = QPushButton() self.button_rotate_0 = QPushButton() self.button_center_0 = QPushButton() self.button_loadFIR_0 = QPushButton() self.button_loadThreshold_0 = QPushButton() roach0CommandButtons =[self.button_loadFreq_0,self.button_defineLUT_0,self.button_sweep_0,self.button_rotate_0,self.button_center_0,self.button_loadFIR_0,self.button_loadThreshold_0] #Roach 1 command buttons self.button_loadFreq_1 = QPushButton() self.button_defineLUT_1 = QPushButton() self.button_sweep_1 = QPushButton() self.button_rotate_1 = QPushButton() self.button_center_1 = QPushButton() self.button_loadFIR_1 = QPushButton() self.button_loadThreshold_1 = QPushButton() roach1CommandButtons =[self.button_loadFreq_1,self.button_defineLUT_1,self.button_sweep_1,self.button_rotate_1,self.button_center_1,self.button_loadFIR_1,self.button_loadThreshold_1] #Roach 2 command buttons self.button_loadFreq_2 = QPushButton() self.button_defineLUT_2 = QPushButton() self.button_sweep_2 = QPushButton() self.button_rotate_2 = QPushButton() self.button_center_2 = QPushButton() self.button_loadFIR_2 = QPushButton() self.button_loadThreshold_2 = QPushButton() roach2CommandButtons =[self.button_loadFreq_2,self.button_defineLUT_2,self.button_sweep_2,self.button_rotate_2,self.button_center_2,self.button_loadFIR_2,self.button_loadThreshold_2] #Roach 3 command buttons self.button_loadFreq_3 = QPushButton() self.button_defineLUT_3 = QPushButton() self.button_sweep_3 = QPushButton() self.button_rotate_3 = QPushButton() self.button_center_3 = QPushButton() self.button_loadFIR_3 = QPushButton() self.button_loadThreshold_3 = QPushButton() roach3CommandButtons =[self.button_loadFreq_3,self.button_defineLUT_3,self.button_sweep_3,self.button_rotate_3,self.button_center_3,self.button_loadFIR_3,self.button_loadThreshold_3] #Roach 4 command buttons self.button_loadFreq_4 = QPushButton() self.button_defineLUT_4 = QPushButton() self.button_sweep_4 = QPushButton() self.button_rotate_4 = QPushButton() self.button_center_4 = QPushButton() self.button_loadFIR_4 = QPushButton() self.button_loadThreshold_4 = QPushButton() roach4CommandButtons =[self.button_loadFreq_4,self.button_defineLUT_4,self.button_sweep_4,self.button_rotate_4,self.button_center_4,self.button_loadFIR_4,self.button_loadThreshold_4] #Roach 5 command buttons self.button_loadFreq_5 = QPushButton() self.button_defineLUT_5 = QPushButton() self.button_sweep_5 = QPushButton() self.button_rotate_5 = QPushButton() self.button_center_5 = QPushButton() self.button_loadFIR_5 = QPushButton() self.button_loadThreshold_5 = QPushButton() roach5CommandButtons =[self.button_loadFreq_5,self.button_defineLUT_5,self.button_sweep_5,self.button_rotate_5,self.button_center_5,self.button_loadFIR_5,self.button_loadThreshold_5] #Roach 6 command buttons self.button_loadFreq_6 = QPushButton() self.button_defineLUT_6 = QPushButton() self.button_sweep_6 = QPushButton() self.button_rotate_6 = QPushButton() self.button_center_6 = QPushButton() self.button_loadFIR_6 = QPushButton() self.button_loadThreshold_6 = QPushButton() roach6CommandButtons =[self.button_loadFreq_6,self.button_defineLUT_6,self.button_sweep_6,self.button_rotate_6,self.button_center_6,self.button_loadFIR_6,self.button_loadThreshold_6] #Roach 7 command buttons self.button_loadFreq_7 = QPushButton() self.button_defineLUT_7 = QPushButton() self.button_sweep_7 = QPushButton() self.button_rotate_7 = QPushButton() self.button_center_7 = QPushButton() self.button_loadFIR_7 = QPushButton() self.button_loadThreshold_7 = QPushButton() roach7CommandButtons =[self.button_loadFreq_7,self.button_defineLUT_7,self.button_sweep_7,self.button_rotate_7,self.button_center_7,self.button_loadFIR_7,self.button_loadThreshold_7] #all Roaches command buttons self.button_loadFreq = QPushButton() self.button_defineLUT = QPushButton() self.button_sweep = QPushButton() self.button_rotate = QPushButton() self.button_center = QPushButton() self.button_loadFIR = QPushButton() self.button_loadThreshold = QPushButton() roachCommandButtons =[self.button_loadFreq,self.button_defineLUT,self.button_sweep,self.button_rotate,self.button_center,self.button_loadFIR,self.button_loadThreshold] #Matrix of roach command buttons self.commandButtons=[roach0CommandButtons,roach1CommandButtons,roach2CommandButtons,roach3CommandButtons,roach4CommandButtons,roach5CommandButtons,roach6CommandButtons,roach7CommandButtons,roachCommandButtons] for i in range(len(self.commandLabels)): for j in range(len(self.commandButtons)): self.commandButtons[j][i].setMaximumWidth(50) self.commandButtons[j][i].setMaximumHeight(50) self.commandButtons[j][i].setMinimumHeight(50) self.commandButtons[j][i].setEnabled(False) self.commandButtons[j][i].setMinimumWidth(50) self.commandButtons[j][i].setPalette(self.grayButtonPalette) self.commandButtons[j][i].roach = [j] if j==len(self.commandButtons)-1: self.commandButtons[j][i].roach = [0,1,2,3,4,5,6,7] #button forall roaches self.connect(self.commandButtons[j][i],SIGNAL('clicked()'),self.commandButtonClicked) if i==0: self.commandButtons[j][i].command = RoachState.loadFreq if i==1: self.commandButtons[j][i].command = RoachState.defineLUT if i==2: self.commandButtons[j][i].command = RoachState.sweep if i==3: self.commandButtons[j][i].command = RoachState.rotate if i==4: self.commandButtons[j][i].command = RoachState.center if i==5: self.commandButtons[j][i].command = RoachState.loadFIR if i==6: self.commandButtons[j][i].command = RoachState.loadThreshold #Roach radio buttons self.radio_roach_0 = QRadioButton('Roach 0') self.radio_roach_0.setChecked(True) self.radio_roach_1 = QRadioButton('Roach 1') self.radio_roach_2 = QRadioButton('Roach 2') self.radio_roach_3 = QRadioButton('Roach 3') self.radio_roach_4 = QRadioButton('Roach 4') self.radio_roach_5 = QRadioButton('Roach 5') self.radio_roach_6 = QRadioButton('Roach 6') self.radio_roach_7 = QRadioButton('Roach 7') self.radioRoaches = [self.radio_roach_0,self.radio_roach_1,self.radio_roach_2,self.radio_roach_3,self.radio_roach_4,self.radio_roach_5,self.radio_roach_6,self.radio_roach_7] for i in range(len(self.radioRoaches)): self.connect(self.radioRoaches[i],SIGNAL('toggled(bool)'),self.radioRoachChanged) self.radioRoaches[i].roach=i #Channel Characteristics labels and textboxes self.label_channel = QLabel('CH: ') self.textbox_channel = QLineEdit('0') #atten spinbox, freq textbox, submit button self.spinbox_attenuation = QSpinBox() self.spinbox_attenuation.setMaximum(MAX_ATTEN) self.label_attenuation = QLabel('Atten:') self.textbox_freq = QLineEdit('0') self.textbox_freq.setMaximumWidth(130) self.label_freq = QLabel('Freq (GHz):') #median textbox, threshold textbox, submit button self.textbox_med = QLineEdit('0.0') self.textbox_med.setMaximumWidth(130) self.label_med = QLabel('median:') self.textbox_threshold = QLineEdit('0.0') self.textbox_threshold.setMaximumWidth(130) self.label_threshold = QLabel('threshold:') #update resonator button, remove resonator button self.button_deleteResonator = QPushButton('Remove Resonator') self.button_deleteResonator.setMaximumWidth(170) self.connect(self.button_deleteResonator, SIGNAL('clicked()'), self.deleteResonator) self.button_updateResonator = QPushButton("Update Resonator") self.button_updateResonator.setMaximumWidth(170) self.connect(self.button_updateResonator, SIGNAL('clicked()'), self.updateResonator) #Roach Properties self.label_freqFile = QLabel('Freq/Atten file:') self.label_freqFile.setMaximumWidth(100) self.textbox_freqFile = QLineEdit('ps_freq0.txt') self.textbox_freqFile.setMaximumWidth(200) self.label_saveDir = QLabel('Save Dir:') self.label_saveDir.setMaximumWidth(100) self.textbox_saveDir = QLineEdit('/home/sean/data/') self.textbox_saveDir.setMaximumWidth(200) self.label_loFreq = QLabel('LO Freq:') self.label_loFreq.setMaximumWidth(100) self.textbox_loFreq = QLineEdit('0') self.textbox_loFreq.setMaximumWidth(100) self.label_ddsSyncLag = QLabel('DDS Sync Lag:') self.label_ddsSyncLag.setMaximumWidth(100) self.textbox_ddsSyncLag = QLineEdit('151') self.textbox_ddsSyncLag.setMaximumWidth(100) self.label_inputAtten = QLabel('Input Atten:') self.label_inputAtten.setMaximumWidth(100) self.textbox_inputAtten = QLineEdit('5') self.textbox_inputAtten.setMaximumWidth(100) self.label_startAtten = QLabel('Start Atten:') self.label_startAtten.setMaximumWidth(100) self.textbox_startAtten = QLineEdit('10') self.textbox_startAtten.setMaximumWidth(100) self.label_stopAtten = QLabel('Stop Atten:') self.label_stopAtten.setMaximumWidth(100) self.textbox_stopAtten = QLineEdit('10') self.textbox_stopAtten.setMaximumWidth(100) self.label_loSpan = QLabel('LO Span:') self.label_loSpan.setMaximumWidth(100) self.textbox_loSpan = QLineEdit('0.5e9') self.textbox_loSpan.setMaximumWidth(100) #command buttons/labels vbox0=QVBoxLayout(spacing = 10) #change spacing until looks good hbox00=QHBoxLayout(spacing=10) for label in self.roachLabels: hbox00.addWidget(label) vbox0.addLayout(hbox00) hbox01=QHBoxLayout(spacing=35) hbox02=QHBoxLayout(spacing=35) hbox03=QHBoxLayout(spacing=35) hbox04=QHBoxLayout(spacing=35) hbox05=QHBoxLayout(spacing=35) hbox06=QHBoxLayout(spacing=35) hbox07=QHBoxLayout(spacing=35) hbox08=QHBoxLayout(spacing=35) hboxes=[hbox01,hbox02,hbox03,hbox04,hbox05,hbox06,hbox07,hbox08] for i in range(len(self.commandLabels)): hboxes[i].addWidget(self.commandLabels[i]) for j in range(len(self.commandButtons)): hboxes[i].addWidget(self.commandButtons[j][i]) vbox0.addLayout(hboxes[i]) #radio buttons vbox1 = QVBoxLayout() hbox10 = QHBoxLayout() for i in range(len(self.radioRoaches)/2+int(len(self.radioRoaches))%2): hbox10.addWidget(self.radioRoaches[i]) hbox11 = QHBoxLayout() for i in range(len(self.radioRoaches)/2): hbox11.addWidget(self.radioRoaches[i+len(self.radioRoaches)/2+int(len(self.radioRoaches))%2]) vbox1.addLayout(hbox10) vbox1.addLayout(hbox11) #figures vbox1.addWidget(self.canvas) vbox1.addWidget(self.mpl_toolbar) #channel characteristics hbox12=QHBoxLayout() hbox12.addWidget(self.label_channel) hbox12.addWidget(self.textbox_channel) hbox12.addWidget(self.button_updateResonator) hbox12.addWidget(self.button_deleteResonator) vbox1.addLayout(hbox12) hbox13 = QHBoxLayout() hbox13.addWidget(self.label_attenuation) hbox13.addWidget(self.spinbox_attenuation) hbox13.addWidget(self.label_freq) hbox13.addWidget(self.textbox_freq) hbox13.addWidget(self.label_med) hbox13.addWidget(self.textbox_med) hbox13.addWidget(self.label_threshold) hbox13.addWidget(self.textbox_threshold) vbox1.addLayout(hbox13) #roach properties hbox14=QHBoxLayout() hbox14.addWidget(self.label_loFreq) hbox14.addWidget(self.textbox_loFreq) hbox14.addWidget(self.label_freqFile) hbox14.addWidget(self.textbox_freqFile) hbox14.addWidget(self.label_saveDir) hbox14.addWidget(self.textbox_saveDir) vbox1.addLayout(hbox14) hbox15=QHBoxLayout() hbox15.addWidget(self.label_loSpan) hbox15.addWidget(self.textbox_loSpan) hbox15.addWidget(self.label_inputAtten) hbox15.addWidget(self.textbox_inputAtten) hbox15.addWidget(self.label_startAtten) hbox15.addWidget(self.textbox_startAtten) hbox15.addWidget(self.label_stopAtten) hbox15.addWidget(self.textbox_stopAtten) vbox1.addLayout(hbox15) hbox16=QHBoxLayout() hbox16.addWidget(self.label_ddsSyncLag) hbox16.addWidget(self.textbox_ddsSyncLag) vbox1.addLayout(hbox16) hbox = QHBoxLayout() hbox.addLayout(vbox0) hbox.addLayout(vbox1) vbox = QVBoxLayout() vbox.addLayout(hbox) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def create_status_bar(self): self.status_text = QLabel("Awaiting orders.") self.statusBar().addWidget(self.status_text, 1) def create_menu(self): self.file_menu = self.menuBar().addMenu("&File") load_file_action = self.create_action("&Save plot",shortcut="Ctrl+S",slot=self.save_plot, tip="Save the plot") quit_action = self.create_action("&Quit", slot=self.close,shortcut="Ctrl+Q", tip="Close the application") self.add_actions(self.file_menu, (load_file_action, None, quit_action)) self.help_menu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut='F1',slot=self.on_about, tip='About the demo') self.add_actions(self.help_menu, (about_action,)) def add_actions(self, target, actions): for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def create_action( self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def save_plot(self): file_choices = "PNG (*.png)|*.png" path = unicode(QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def on_about(self): msg = """ Message to user goes here. """ QMessageBox.about(self, "MKID-ROACH software demo", msg.strip()) def closeEvent(self, event): for roach in self.roaches: roach.closeThread(1) #time.sleep(1) #for thread in self.threadPool: #thread.setTerminationEnabled(True) #thread.closeThread() time.sleep(.1) QtCore.QCoreApplication.instance().quit
class AppForm(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle('Templar_noise') self.create_menu() self.create_main_frame() self.create_status_bar() self.dacStatus = 'off' self.dramStatus = 'off' self.tapStatus = 'off' self.socketStatus = 'off' self.ch_all = [] self.attens = numpy.array([1. for i in range(256)]) self.freqRes = 7812.5 self.sampleRate = 512e6 self.iq_centers = numpy.array([0.+0j]*256) def openClient(self): self.roach = corr.katcp_wrapper.FpgaClient(self.textbox_roachIP.text(),7147) time.sleep(2) self.status_text.setText('connection established') self.button_openClient.setDisabled(True) def programRFswitches(self, regStr = '10110'): # 5 bit word: LO_int/ext, RF_loop, LO_source(doubler), BB_loop, Ck_int/ext #regStr = self.textbox_rfSwReg.text() print int(regStr[0]), int(regStr[1]), int(regStr[2]),int(regStr[3]), int(regStr[4]) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('if_switch', 1) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[0])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[0])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[0])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[1])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[1])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[1])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[2])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[2])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[2])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[3])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[3])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[3])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[4])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[4])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(int(regStr[4])<<2)+(0<<1)+(0<<0)) # Now clock out the data written to the reg. self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(1<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('if_switch', 0) def programAttenuators(self, atten_in_desired, atten_out_desired): # There are eight settings for each attenuator: # 0db, -0.5, -1, -2, -4, -8, -16, and -31.5, which # are listed in order in "attenuations." #atten_in_desired = float(self.textbox_atten_in.text()) atten_in = 63 - int(atten_in_desired*2) #atten_out_desired = float(self.textbox_atten_out.text()) if atten_out_desired <= 31.5: atten_out0 = 63 atten_out1 = 63 - int(atten_out_desired*2) else: atten_out0 = 63 - int((atten_out_desired-31.5)*2) atten_out1 = 0 """ self.roach.write_int('SER_DI', (atten_in<<26)+(atten_out0<<20)+(atten_out1<<14)) self.roach.write_int('SWAT_LE', 1) self.roach.write_int('start', 1) self.roach.write_int('start', 0) self.roach.write_int('SWAT_LE', 0) self.status_text.setText('Attenuators programmed. ') """ reg = numpy.binary_repr((atten_in<<12)+(atten_out0<<6)+(atten_out1<<0)) b = '0'*(18-len(reg)) + reg print reg, len(reg) self.roach.write_int('regs', (0<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('if_switch', 1) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[0])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[0])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[0])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[1])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[1])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[1])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[2])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[2])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[2])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[3])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[3])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[3])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[4])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[4])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[4])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[5])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[5])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[5])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[6])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[6])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[6])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[7])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[7])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[7])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[8])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[8])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[8])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[9])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[9])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[9])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[10])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[10])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[10])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[11])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[11])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[11])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[12])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[12])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[12])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[13])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[13])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[13])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[14])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[14])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[14])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[15])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[15])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[15])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[16])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[16])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[16])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[17])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[17])<<2)+(1<<1)+(0<<0)) self.roach.write_int('regs', (0<<4)+(1<<3)+(int(b[17])<<2)+(0<<1)+(0<<0)) self.roach.write_int('regs', (1<<4)+(1<<3)+(0<<2)+(0<<1)+(0<<0)) self.roach.write_int('if_switch', 0) def programLO(self, freq=3.2e9, sweep_freq=0): f_pfd = 10e6 if sweep_freq: f = freq else: f = float(self.textbox_loFreq.text()) if f >= 4.4e9: f = f/2 INT = int(f)/int(f_pfd) MOD = 2000 FRAC = int(round(MOD*(f/f_pfd-INT))) if FRAC != 0: gcd = fractions.gcd(MOD,FRAC) if gcd != 1: MOD = MOD/gcd FRAC = int(FRAC/gcd) PHASE = 1 R = 1 power = 3 aux_power = 3 MUX = 3 LOCK_DETECT = 1 reg5 = (LOCK_DETECT<<22) + (1<<2) + (1<<0) reg4 = (1<<23) + (1<<18) + (1<<16) + (1<<8) + (aux_power<<6) + (1<<5) + (power<<3) + (1<<2) reg3 = (1<<10) + (1<<7) + (1<<5) + (1<<4) + (1<<1) + (1<<0) reg2 = (MUX<<26) + (R<<14) + (1<<11) + (1<<10) + (1<<9) + (1<<7) + (1<<6) + (1<<1) reg1 = (1<<27) + (PHASE<<15) + (MOD<<3) + (1<<0) reg0 = (INT<<15) + (FRAC<<3) regs = [reg5, reg4, reg3, reg2, reg1, reg0] for r in regs: self.roach.write_int('SER_DI', r) self.roach.write_int('LO_SLE', 1) self.roach.write_int('start', 1) self.roach.write_int('start', 0) self.roach.write_int('LO_SLE', 0) self.status_text.setText('LO programmed. ') def freqCombLUT(self, echo, freq, sampleRate, resolution, amplitude=[1.]*256, phase=[0.]*256, random_phase = 'yes'): offset = int(self.textbox_offset.text()) amp_full_scale = 2**15-1 N_freqs = len(freq) size = int(sampleRate/resolution) I, Q = numpy.array([0.]*size), numpy.array([0.]*size) single_I, single_Q = numpy.array([0.]*size), numpy.array([0.]*size) #numpy.random.seed(1000) for n in range(N_freqs): if random_phase == 'yes': phase[n] = numpy.random.uniform(0, 2*numpy.pi) x = [2*numpy.pi*freq[n]*(t+offset)/sampleRate+phase[n] for t in range(size)] y = [2*numpy.pi*freq[n]*t/sampleRate+phase[n] for t in range(size)] single_I = amplitude[n]*numpy.cos(x) single_Q = amplitude[n]*numpy.sin(y) I = I + single_I Q = Q + single_Q a = numpy.array([abs(I).max(), abs(Q).max()]) I = numpy.array([int(i*amp_full_scale/a.max()) for i in I]) Q = numpy.array([int(q*amp_full_scale/a.max()) for q in Q]) if echo == 'yes': print 'scale factor: ', a.max() self.scale_factor = a.max() print 'Set atten_out to: ', 20*numpy.log10(self.previous_scale_factor/self.scale_factor) + self.minimumAttenuation return I, Q def define_DAC_LUT(self): freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) f_base = float(self.textbox_loFreq.text()) for n in range(len(freqs)): if freqs[n] < f_base: freqs[n] = freqs[n] + 512e6 self.freqs_dac = [round((f-f_base)/self.freqRes)*self.freqRes for f in freqs] atten_min = self.attens.min() print "minimum attenuation: ", self.minimumAttenuation amplitudes = [10**(+(atten_min-a)/20.) for a in self.attens] self.I_dac, self.Q_dac = self.freqCombLUT('yes', self.freqs_dac, self.sampleRate, self.freqRes, amplitudes) self.status_text.setText('done defining DAC freqs. ') def defne_noiseRO(self, freq=10e6): sampleRate = 512e6 size = int(512e6/7812.5) start_x = 2*numpy.pi*freq/sampleRate end_x = 2*numpy.pi*freq*size/sampleRate x = numpy.linspace(start_x, end_x, size) start_y = 0 end_y = 0 + 2*numpy.pi*freq*(size)/sampleRate y = numpy.linspace(start_y, end_y, size) I = numpy.cos(x) Q = numpy.sin(y) sigma = 10. iq = numpy.array([0+0j]*size) for i in range(size): I[i] = I[i] + random.gauss(0, sigma) Q[i] = Q[i] + random.gauss(0, sigma) amp_full_scale = 2**15-1 a = numpy.array([abs(I).max(), abs(Q).max()]) I = numpy.array([int(i*amp_full_scale/a.max()) for i in I]) Q = numpy.array([int(q*amp_full_scale/a.max()) for q in Q]) return I, Q def noiseRO(self): freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) f_base = float(self.textbox_loFreq.text()) for n in range(len(freqs)): if freqs[n] < f_base: freqs[n] = freqs[n] + 512e6 self.freqs_dac = [round((f-f_base)/self.freqRes)*self.freqRes for f in freqs] atten_min = self.attens.min() amplitudes = [10**(+(atten_min-a)/20.) for a in self.attens] self.I_dac, self.Q_dac = self.define_noiseRO(self.freqs_dac[0]) self.status_text.setText('done defining noise RO. ') def define_DDS_LUT(self, phase = [0.]*256): ch_shift = 147 # This number should be verified in the utility ddc2x_v*.py freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) f_base = float(self.textbox_loFreq.text()) for n in range(len(freqs)): if freqs[n] < f_base: freqs[n] = freqs[n] + 512e6 freqs_dds = [0 for j in range(256)] for n in range(len(freqs)): freqs_dds[n] = round((freqs[n]-f_base)/self.freqRes)*self.freqRes freq_residuals = self.select_bins(freqs_dds) L = int(self.sampleRate/self.freqRes) self.I_dds, self.Q_dds = [0.]*L, [0.]*L for m in range(256): I, Q = self.freqCombLUT('no', [freq_residuals[m]], 2e6, self.freqRes, [1.], [phase[m]], 'no') for j in range(len(I)/2): self.I_dds[j*512+2*((m+ch_shift)%256)] = I[2*j] self.I_dds[j*512+2*((m+ch_shift)%256)+1] = I[2*j+1] self.Q_dds[j*512+2*((m+ch_shift)%256)] = Q[2*j] self.Q_dds[j*512+2*((m+ch_shift)%256)+1] = Q[2*j+1] self.axes0.clear() self.axes0.plot(self.I_dds, '.', self.Q_dds, '.') self.canvas.draw() print "done defing dds freqs. " def select_bins(self, readout_freqs): fft_len = 2**9 bins = '' i = 0 residuals = [] for f in readout_freqs: fft_bin = int(round(f*fft_len/self.sampleRate)) fft_freq = fft_bin*self.sampleRate/fft_len freq_residual = round((f - fft_freq)/self.freqRes)*self.freqRes residuals.append(freq_residual) bins = bins + struct.pack('>l', fft_bin) self.roach.write_int('bins', fft_bin) self.roach.write_int('load_bins', (i<<1) + (1<<0)) self.roach.write_int('load_bins', (i<<1) + (0<<0)) i = i + 1 self.status_text.setText('done writing LUTs. ') return residuals def write_LUTs(self): if self.dacStatus == 'off': self.roach.write_int('startDAC', 0) else: self.toggleDAC() binaryData = '' print len(self.I_dac) for n in range(len(self.I_dac)/2): i_dac_0 = struct.pack('>h', self.I_dac[2*n]) i_dac_1 = struct.pack('>h', self.I_dac[2*n+1]) i_dds_0 = struct.pack('>h', self.I_dds[2*n]) i_dds_1 = struct.pack('>h', self.I_dds[2*n+1]) q_dac_0 = struct.pack('>h', self.Q_dac[2*n]) q_dac_1 = struct.pack('>h', self.Q_dac[2*n+1]) q_dds_0 = struct.pack('>h', self.Q_dds[2*n]) q_dds_1 = struct.pack('>h', self.Q_dds[2*n+1]) binaryData = binaryData + q_dds_1 + q_dds_0 + q_dac_1 + q_dac_0 + i_dds_1 + i_dds_0 + i_dac_1 + i_dac_0 self.roach.write('dram_memory', binaryData) # Write LUTs to file. saveDir = str(self.textbox_saveDir.text()) f = open(saveDir + 'luts.dat', 'w') f.write(binaryData) f.close() def loadIQcenters(self): saveDir = str(self.textbox_saveDir.text()) #saveDir = str(os.environ['PWD'] + '/'+ self.textbox_saveDir.text()) centers_for_file = [[0., 0.]]*256 for ch in range(256): I_c = int(self.iq_centers[ch].real/2**3) Q_c = int(self.iq_centers[ch].imag/2**3) center = (I_c<<16) + (Q_c<<0) self.roach.write_int('conv_phase_centers', center) self.roach.write_int('conv_phase_load_centers', (ch<<1)+(1<<0)) self.roach.write_int('conv_phase_load_centers', 0) centers_for_file[ch] = [self.iq_centers[ch].real, self.iq_centers[ch].imag] numpy.savetxt(saveDir+'centers.dat', centers_for_file) def findIQcenters(self, I, Q): I_0 = (I.max()+I.min())/2. Q_0 = (Q.max()+Q.min())/2. return complex(I_0, Q_0) def rotateLoops(self): print "Calculating loop rotations..." L = 2**15 bin_data_phase = '' self.roach.write_int('startSnap', 0) self.roach.write_int('snapI_ctrl', 1) self.roach.write_int('snapI_ctrl', 0) self.roach.write_int('snapQ_ctrl', 1) self.roach.write_int('snapQ_ctrl', 0) self.roach.write_int('startSnap', 1) time.sleep(0.1) bin_data_I = self.roach.read('snapI_bram', 4*L) bin_data_Q = self.roach.read('snapQ_bram', 4*L) I = numpy.array([0]*L) Q = numpy.array([0]*L) for m in range(L): I[m] = struct.unpack('>l', bin_data_I[m*4:m*4+4])[0]/2**14 - self.iq_centers[0].real Q[m] = struct.unpack('>l', bin_data_Q[m*4:m*4+4])[0]/2**14 - self.iq_centers[1].imag phase = [0.]*256 phase[0] = numpy.arctan2(Q.mean(), I.mean()) self.define_DDS_LUT(phase) self.write_LUTs() print "done." """ def sweepLO(self): atten_in = float(self.textbox_atten_in.text()) saveDir = str(self.textbox_saveDir.text()) savefile = saveDir + 'ps_' + time.strftime("%Y%m%d-%H%M%S",time.localtime()) + '.h5' dac_freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) self.N_freqs = len(dac_freqs) f_base = float(self.textbox_loFreq.text()) if f_base >= 4.4e9: self.programRFswitches('10010') print 'LO doubled.' else: #self.programRFswitches('10100') self.programRFswitches('10110') print 'LO normal operation.' loSpan = float(self.textbox_loSpan.text()) # spanShift = 0.5 --> a sweep centered on the resonant freq. spanShift = float(self.textbox_spanShift.text()) df = 1e4 steps = int(loSpan/df) print "LO steps: ", steps # Frequency span is off-center to account for freq. shift to higher values for atten -> inf. lo_freqs = [f_base+i*df-spanShift*steps*df for i in range(steps)] atten_start = int(self.textbox_powerSweepStart.text()) atten_stop = int(self.textbox_powerSweepStop.text()) attens = [i for i in range(atten_start, atten_stop+1)] self.roach.write_int('ch_we0', 1) self.roach.write_int('ch_we1', 1) for a in attens: print a self.programAttenuators(atten_in, a) time.sleep(0.5) f_span = [] l = 0 self.f_span = [[0]*steps]*self.N_freqs for f in dac_freqs: f_span = f_span + [f-spanShift*steps*df+n*df for n in range(steps)] self.f_span[l] = [f-spanShift*steps*df+n*df for n in range(steps)] l = l + 1 I = numpy.zeros(self.N_freqs*steps, dtype='float32') Q = numpy.zeros(self.N_freqs*steps, dtype='float32') self.I, self.Q = numpy.array([[0.]*steps]*self.N_freqs),numpy.array([[0.]*steps]*self.N_freqs) for i in range(steps): self.programLO(lo_freqs[i],1) L = 2**15 bin_data_phase = '' self.roach.write_int('startSnap', 0) self.roach.write_int('snapI_ctrl', 1) self.roach.write_int('snapI_ctrl', 0) self.roach.write_int('snapQ_ctrl', 1) self.roach.write_int('snapQ_ctrl', 0) self.roach.write_int('startSnap', 1) time.sleep(0.1) bin_data_I = self.roach.read('snapI_bram', 4*L) bin_data_Q = self.roach.read('snapQ_bram', 4*L) I = numpy.array([0]*L) Q = numpy.array([0]*L) for m in range(L): I[m] = struct.unpack('>l', bin_data_I[m*4:m*4+4])[0]/2**14 Q[m] = struct.unpack('>l', bin_data_Q[m*4:m*4+4])[0]/2**14 self.I[0,i] = I.mean() self.Q[0,i] = Q.mean() self.programLO(f_base,1) # Find IQ centers. self.I_on_res, self.Q_on_res = [0.]*self.N_freqs, [0.]*self.N_freqs bin_data_phase = '' self.roach.write_int('startSnap', 0) self.roach.write_int('snapI_ctrl', 1) self.roach.write_int('snapI_ctrl', 0) self.roach.write_int('snapQ_ctrl', 1) self.roach.write_int('snapQ_ctrl', 0) self.roach.write_int('startSnap', 1) time.sleep(0.1) bin_data_I = self.roach.read('snapI_bram', 4*L) bin_data_Q = self.roach.read('snapQ_bram', 4*L) I = numpy.array([0]*L) Q = numpy.array([0]*L) for m in range(L): I[m] = struct.unpack('>l', bin_data_I[m*4:m*4+4])[0]/2**14 Q[m] = struct.unpack('>l', bin_data_Q[m*4:m*4+4])[0]/2**14 self.I_on_res[0] = I.mean() self.Q_on_res[0] = Q.mean() self.iq_centers[0] = self.findIQcenters(self.I[0,:],self.Q[0,:]) N = steps*self.N_freqs self.axes0.clear() self.axes1.clear() self.axes0.semilogy((self.I[0,:]**2 + self.Q[0,:]**2)**.5, '.-') #self.axes0.semilogy(f_span, (self.I[0,:]**2 + self.Q[0,:]**2)**.5, '.-') #self.axes0.semilogy(f_span, (I**2 + Q**2)**.5, '.-') self.axes1.plot(self.I[0,:], self.Q[0,:], '.-', self.iq_centers.real[0:self.N_freqs], self.iq_centers.imag[0:self.N_freqs], '.', self.I_on_res, self.Q_on_res, '.') #self.axes1.plot(f_span[0:N-1], dist, '.-') #self.axes1.set_xlim((-100000,100000)) #self.axes1.set_ylim((-100000,100000)) self.canvas.draw() """ def sweepLO(self): atten_in = float(self.textbox_atten_in.text()) saveDir = str(self.textbox_saveDir.text()) #saveDir = str(os.environ['PWD'] + '/'+ self.textbox_saveDir.text()) savefile = saveDir + 'ps_' + time.strftime("%Y%m%d-%H%M%S",time.localtime()) + '.h5' dac_freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) self.N_freqs = len(dac_freqs) f_base = float(self.textbox_loFreq.text()) if f_base >= 4.4e9: self.programRFswitches('10010') print 'LO doubled.' else: #self.programRFswitches('10100') self.programRFswitches('10110') print 'LO normal operation.' loSpan = float(self.textbox_loSpan.text()) # spanShift = 0.5 --> a sweep centered on the resonant freq. spanShift = float(self.textbox_spanShift.text()) df = 1e4 steps = int(loSpan/df) print "LO steps: ", steps # Frequency span is off-center to account for freq. shift to higher values for atten -> inf. lo_freqs = [f_base+i*df-spanShift*steps*df for i in range(steps)] atten_start = int(self.textbox_powerSweepStart.text()) atten_stop = int(self.textbox_powerSweepStop.text()) attens = [i for i in range(atten_start, atten_stop+1)] for a in attens: print a self.programAttenuators(atten_in, a) time.sleep(0.5) f_span = [] l = 0 self.f_span = [[0]*steps]*self.N_freqs for f in dac_freqs: f_span = f_span + [f-spanShift*steps*df+n*df for n in range(steps)] self.f_span[l] = [f-spanShift*steps*df+n*df for n in range(steps)] l = l + 1 I = numpy.zeros(self.N_freqs*steps, dtype='float32') Q = numpy.zeros(self.N_freqs*steps, dtype='float32') I_std = numpy.zeros(self.N_freqs*steps, dtype='float32') Q_std = numpy.zeros(self.N_freqs*steps, dtype='float32') self.I, self.Q = numpy.array([[0.]*steps]*self.N_freqs),numpy.array([[0.]*steps]*self.N_freqs) for i in range(steps): #print i self.programLO(lo_freqs[i],1) self.roach.write_int('startAccumulator', 0) self.roach.write_int('avgIQ_ctrl', 1) self.roach.write_int('avgIQ_ctrl', 0) self.roach.write_int('startAccumulator', 1) time.sleep(0.001) data = self.roach.read('avgIQ_bram', 4*2*256) for j in range(self.N_freqs): I[j*steps+i] = struct.unpack('>l', data[4*j:4*j+4])[0] Q[j*steps+i] = struct.unpack('>l', data[4*(j+256):4*(j+256)+4])[0] I_std[j*steps+i] = 0 Q_std[j*steps+i] = 0 self.I[j, i] = I[j*steps+i] self.Q[j, i] = Q[j*steps+i] self.programLO(f_base,1) # Find IQ centers. self.I_on_res, self.Q_on_res = [0.]*self.N_freqs, [0.]*self.N_freqs self.roach.write_int('startAccumulator', 0) self.roach.write_int('avgIQ_ctrl', 1) self.roach.write_int('avgIQ_ctrl', 0) self.roach.write_int('startAccumulator', 1) time.sleep(0.001) data = self.roach.read('avgIQ_bram', 4*2*256) for j in range(self.N_freqs): self.I_on_res[j] = struct.unpack('>l', data[4*j:4*j+4])[0] self.Q_on_res[j] = struct.unpack('>l', data[4*(j+256):4*(j+256)+4])[0] self.iq_centers[j] = self.findIQcenters(I[j*steps:j*steps+steps],Q[j*steps:j*steps+steps]) numpy.savetxt('centers.dat', [self.iq_centers.real, self.iq_centers.imag]) self.axes1.clear() self.axes0.semilogy(f_span, (I**2 + Q**2)**.5, '.-') self.axes1.plot(I, Q, '.-', self.iq_centers.real[0:self.N_freqs], self.iq_centers.imag[0:self.N_freqs], '.', self.I_on_res, self.Q_on_res, '.') self.canvas.draw() def toggleDAC(self): if self.dacStatus == 'off': print "Starting LUT...", self.roach.write_int('startDAC', 1) time.sleep(1) while self.roach.read_int('DRAM_LUT_rd_valid') != 0: print self.roach.read_int('DRAM_LUT_rd_valid') self.roach.write_int('startDAC', 0) time.sleep(0.35) self.roach.write_int('startDAC', 1) time.sleep(1) print ".", print "done." self.button_startDAC.setText('Stop DAC') self.dacStatus = 'on' self.status_text.setText('DAC turned on. ') else: self.roach.write_int('startDAC', 0) self.button_startDAC.setText('Start DAC') self.dacStatus = 'off' self.status_text.setText('DAC turned off. ') def loadFreqsAttens(self): f_base = float(self.textbox_loFreq.text()) freqFile =str(self.textbox_freqFile.text()) x = numpy.loadtxt(freqFile) x_string = '' self.previous_scale_factor = x[0,0] N_freqs = len(x[1:,0]) for l in x[1:,0]: x_string = x_string + str(l*1e9) + '\n' self.iq_centers = numpy.array([0.+0j]*256) for n in range(N_freqs): #for n in range(256): self.iq_centers[n] = complex(x[n+1,1], x[n+1,2]) self.attens = x[1:,3] self.minimumAttenuation = numpy.array(x[1:,3]).min() self.textedit_DACfreqs.setText(x_string) def loadLUTs(self): self.scale_factor = 1. self.iq_centers = numpy.array([0.+0j]*256) # Loads the DAC and DDS LUTs from file. As well as the IQ loop centers. if self.dacStatus == 'off': self.roach.write_int('startDAC', 0) else: self.toggleDAC() saveDir = str(self.textbox_saveDir.text()) #saveDir = str(os.environ['PWD'] + '/'+ self.textbox_saveDir.text()) f = open(saveDir+'luts.dat', 'r') binaryData = f.read() self.roach.write('dram_memory', binaryData) x = numpy.loadtxt(saveDir+'centers.dat') N_freqs = len(x[:,0]) for n in range(N_freqs): self.iq_centers[n] = complex(x[n,0], x[n,1]) # Select and write bins for first stage of channelizer. freqs = map(float, unicode(self.textedit_DACfreqs.toPlainText()).split()) f_base = float(self.textbox_loFreq.text()) for n in range(len(freqs)): if freqs[n] < f_base: freqs[n] = freqs[n] + 512e6 freqs_dds = [0 for j in range(256)] for n in range(len(freqs)): freqs_dds[n] = round((freqs[n]-f_base)/self.freqRes)*self.freqRes freq_residuals = self.select_bins(freqs_dds) print 'LUTs and IQ centers loaded from file.' def channelIncUp(self): ch = int(self.textbox_channel.text()) ch = ch + 1 ch = ch%self.N_freqs self.textbox_channel.setText(str(ch)) self.axes0.clear() self.axes1.clear() self.axes0.semilogy(self.f_span[ch], (self.I[ch]**2 + self.Q[ch]**2)**.5, '.-') self.axes1.plot(self.I[ch], self.Q[ch], '.-', self.iq_centers.real[ch], self.iq_centers.imag[ch], '.', self.I_on_res[ch], self.Q_on_res[ch], '.') #self.axes1.set_xlim((-10000,10000)) #self.axes1.set_ylim((-10000,10000)) self.canvas.draw() def channelIncDown(self): ch = int(self.textbox_channel.text()) ch = ch - 1 ch = ch%self.N_freqs self.textbox_channel.setText(str(ch)) self.axes0.clear() self.axes1.clear() self.axes0.semilogy(self.f_span[ch], (self.I[ch]**2 + self.Q[ch]**2)**.5, '.-') self.axes1.plot(self.I[ch], self.Q[ch], '.-', self.iq_centers.real[ch], self.iq_centers.imag[ch], '.', self.I_on_res[ch], self.Q_on_res[ch], '.') #self.axes1.set_xlim((-10000,10000)) #self.axes1.set_ylim((-10000,10000)) self.canvas.draw() def changeCenter(self, event): I = event.xdata Q = event.ydata #ch = int(self.textbox_channel.text()) #print ch #self.iq_centers.real[ch] = I #self.iq_centers.imag[ch] = Q #self.axes1.plot(I, Q, '.') #self.canvas.draw() def snapshot(self): for r in range(self.N_freqs): self.roach.write_int('ch_we'+str(r), r) #self.roach.write_int('ch_we'+str(r), 1) for r in range(4-self.N_freqs): self.roach.write_int('ch_we'+str(r), 0) steps = int(self.textbox_noiseSteps.text()) L = 2**15 I, Q = [], [] for n in range(steps): print n self.roach.write_int('startSnap', 0) self.roach.write_int('snapI_ctrl', 1) self.roach.write_int('snapI_ctrl', 0) self.roach.write_int('snapQ_ctrl', 1) self.roach.write_int('snapQ_ctrl', 0) self.roach.write_int('startSnap', 1) time.sleep(0.1) bin_data_I = self.roach.read('snapI_bram', 4*L) bin_data_Q = self.roach.read('snapQ_bram', 4*L) for m in range(L*2):#two 16 bit values in each 32-bit word #low order bits are an earlier value i = struct.unpack('>h', bin_data_I[m*2:m*2+2])[0] q = struct.unpack('>h', bin_data_Q[m*2:m*2+2])[0] I.append(i) Q.append(q) numpy.savetxt('test.dat', [I,Q]) self.axes1.clear() self.axes1.plot(I,'.-', Q, '.-') self.canvas.draw() def create_main_frame(self): self.main_frame = QWidget() # Create the mpl Figure and FigCanvas objects. self.dpi = 100 self.fig = Figure((9.0, 5.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) self.axes0 = self.fig.add_subplot(121) #self.axes3 = self.axes0.twinx() self.axes1 = self.fig.add_subplot(122) cid=self.canvas.mpl_connect('button_press_event', self.changeCenter) # Create the navigation toolbar, tied to the canvas self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) # Roach board's IP address self.textbox_roachIP = QLineEdit('10.0.0.14') self.textbox_roachIP.setMaximumWidth(200) label_roachIP = QLabel('Roach IP Address:') # Start connection to roach. self.button_openClient = QPushButton("&Open Client") self.button_openClient.setMaximumWidth(100) self.connect(self.button_openClient, SIGNAL('clicked()'), self.openClient) # LO frequency. self.textbox_loFreq = QLineEdit('5.07e9') self.textbox_loFreq.setMaximumWidth(100) label_loFreq = QLabel('LO frequency:') # Program LO. self.button_programLO = QPushButton("&Prog.") self.button_programLO.setMaximumWidth(100) self.connect(self.button_programLO, SIGNAL('clicked()'), self.programLO) label_programLO = QLabel('') # Sweep span self.textbox_loSpan = QLineEdit('0.5e6') self.textbox_loSpan.setMaximumWidth(50) label_loSpan = QLabel('LO Span') # Frequency span shift # A span shift of 0.75 shifts 75% of sweep span to the lower portion of the range. self.textbox_spanShift = QLineEdit('0.5') self.textbox_spanShift.setMaximumWidth(50) label_spanShift = QLabel('Span shift') # DAC Frequencies. self.textedit_DACfreqs = QTextEdit() self.textedit_DACfreqs.setMaximumWidth(170) self.textedit_DACfreqs.setMaximumHeight(100) label_DACfreqs = QLabel('DAC Freqs:') # Input attenuation. self.textbox_atten_in = QLineEdit('0') self.textbox_atten_in.setMaximumWidth(50) label_atten_in = QLabel('atten. (in)') # offset in lut self.textbox_offset = QLineEdit('0') self.textbox_offset.setMaximumWidth(50) # Power sweep range. self.textbox_powerSweepStart = QLineEdit('16') self.textbox_powerSweepStart.setMaximumWidth(50) label_powerSweepStart = QLabel('Start Atten') self.textbox_powerSweepStop = QLineEdit('16') self.textbox_powerSweepStop.setMaximumWidth(50) label_powerSweepStop = QLabel('Stop Atten') # Save directory self.textbox_saveDir = QLineEdit('/home/sean/data/20111115/r0/') self.textbox_saveDir.setMaximumWidth(50) label_saveDir = QLabel('Save directory') label_saveDir.setMaximumWidth(150) # File with frequencies/attens self.textbox_freqFile = QLineEdit('freqs.txt') self.textbox_freqFile.setMaximumWidth(200) # Load freqs and attens from file. self.button_loadFreqsAttens = QPushButton("Load freqs/attens") self.button_loadFreqsAttens.setMaximumWidth(200) self.connect(self.button_loadFreqsAttens, SIGNAL('clicked()'), self.loadFreqsAttens) # Load freqs onto FPGA. self.button_write_LUTs = QPushButton("&Load LUTs") self.button_write_LUTs.setMaximumWidth(200) self.connect(self.button_write_LUTs, SIGNAL('clicked()'), self.write_LUTs) # Define noise readout. self.button_noiseRO = QPushButton("noise RO") self.button_noiseRO.setMaximumWidth(200) self.connect(self.button_noiseRO, SIGNAL('clicked()'), self.noiseRO) # Take snapshot. self.button_snapshot = QPushButton("Snapshot") self.button_snapshot.setMaximumWidth(200) self.connect(self.button_snapshot, SIGNAL('clicked()'), self.snapshot) # Noise data steps self.textbox_noiseSteps = QLineEdit('1') self.textbox_noiseSteps.setMaximumWidth(50) # Rotate IQ loops. self.button_rotateLoops = QPushButton("Rot. Loops") self.button_rotateLoops.setMaximumWidth(200) self.connect(self.button_rotateLoops, SIGNAL('clicked()'), self.rotateLoops) # Translate IQ loops. self.button_translateLoops = QPushButton("Trans. Loops") self.button_translateLoops.setMaximumWidth(200) self.connect(self.button_translateLoops, SIGNAL('clicked()'), self.loadIQcenters) # DAC start button. self.button_startDAC = QPushButton("&Start DAC") self.button_startDAC.setMaximumWidth(200) self.connect(self.button_startDAC, SIGNAL('clicked()'), self.toggleDAC) # define DDS frequencies. self.button_define_DDS_LUT= QPushButton("&Define DDS") self.button_define_DDS_LUT.setMaximumWidth(200) self.connect(self.button_define_DDS_LUT, SIGNAL('clicked()'), self.define_DDS_LUT) # define DAC frequencies. self.button_define_DAC_LUT= QPushButton("&Define DAC LUT") self.button_define_DAC_LUT.setMaximumWidth(200) self.connect(self.button_define_DAC_LUT, SIGNAL('clicked()'), self.define_DAC_LUT) # Sweep LO self.button_sweepLO = QPushButton("Sweep LO") self.button_sweepLO.setMaximumWidth(340) self.connect(self.button_sweepLO, SIGNAL('clicked()'), self.sweepLO) # load LUT and IQ centers from file self.button_loadLUTs = QPushButton("Load LUTs and centers") self.button_loadLUTs.setMaximumWidth(340) self.connect(self.button_loadLUTs, SIGNAL('clicked()'), self.loadLUTs) # Channel increment up 1. self.button_channelIncUp = QPushButton("+") self.button_channelIncUp.setMaximumWidth(50) self.connect(self.button_channelIncUp, SIGNAL('clicked()'), self.channelIncUp) # Channel increment down 1. self.button_channelIncDown = QPushButton("-") self.button_channelIncDown.setMaximumWidth(50) self.connect(self.button_channelIncDown, SIGNAL('clicked()'), self.channelIncDown) # Channel to measure self.textbox_channel = QLineEdit('0') self.textbox_channel.setMaximumWidth(100) # Add widgets to window. gbox0 = QVBoxLayout() hbox01 = QHBoxLayout() hbox01.addWidget(self.textbox_roachIP) hbox01.addWidget(self.button_openClient) gbox0.addLayout(hbox01) gbox030 = QVBoxLayout() gbox030.addWidget(label_loFreq) gbox030.addWidget(self.textbox_loFreq) gbox032 = QVBoxLayout() gbox032.addWidget(label_programLO) gbox032.addWidget(self.button_programLO) gbox033 = QVBoxLayout() gbox033.addWidget(label_loSpan) gbox033.addWidget(self.textbox_loSpan) gbox034 = QVBoxLayout() gbox034.addWidget(label_spanShift) gbox034.addWidget(self.textbox_spanShift) gbox12 = QVBoxLayout() gbox12.addWidget(label_atten_in) gbox12.addWidget(self.textbox_atten_in) gbox10 = QVBoxLayout() gbox10.addWidget(label_powerSweepStart) gbox10.addWidget(self.textbox_powerSweepStart) gbox11 = QVBoxLayout() gbox11.addWidget(label_powerSweepStop) gbox11.addWidget(self.textbox_powerSweepStop) hbox11 = QHBoxLayout() hbox11.addLayout(gbox12) hbox11.addLayout(gbox10) hbox11.addLayout(gbox11) hbox11.addWidget(self.textbox_offset) hbox03 = QHBoxLayout() hbox03.addLayout(gbox030) hbox03.addLayout(gbox032) hbox03.addLayout(gbox033) hbox03.addLayout(gbox034) gbox0.addLayout(hbox03) gbox0.addLayout(hbox11) gbox1 = QVBoxLayout() gbox1.addWidget(label_DACfreqs) gbox1.addWidget(self.textedit_DACfreqs) hbox12 = QHBoxLayout() hbox12.addWidget(label_saveDir) hbox12.addWidget(self.textbox_saveDir) gbox1.addLayout(hbox12) hbox13 = QHBoxLayout() hbox13.addWidget(self.textbox_channel) hbox13.addWidget(self.button_channelIncDown) hbox13.addWidget(self.button_channelIncUp) gbox1.addLayout(hbox13) gbox2 = QVBoxLayout() hbox23 = QHBoxLayout() hbox23.addWidget(self.textbox_freqFile) hbox23.addWidget(self.button_loadFreqsAttens) gbox2.addLayout(hbox23) hbox20 = QHBoxLayout() hbox20.addWidget(self.button_define_DAC_LUT) hbox20.addWidget(self.button_define_DDS_LUT) gbox2.addLayout(hbox20) hbox25 = QHBoxLayout() hbox25.addWidget(self.button_noiseRO) hbox25.addWidget(self.textbox_noiseSteps) hbox25.addWidget(self.button_snapshot) gbox2.addLayout(hbox25) hbox24 = QHBoxLayout() hbox24.addWidget(self.button_write_LUTs) hbox24.addWidget(self.button_startDAC) gbox2.addLayout(hbox24) gbox2.addWidget(self.button_sweepLO) hbox22 = QHBoxLayout() hbox22.addWidget(self.button_rotateLoops) hbox22.addWidget(self.button_translateLoops) gbox2.addLayout(hbox22) gbox2.addWidget(self.button_loadLUTs) hbox = QHBoxLayout() hbox.addLayout(gbox0) hbox.addLayout(gbox1) hbox.addLayout(gbox2) vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) vbox.addLayout(hbox) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def create_status_bar(self): self.status_text = QLabel("Awaiting orders.") self.statusBar().addWidget(self.status_text, 1) def create_menu(self): self.file_menu = self.menuBar().addMenu("&File") load_file_action = self.create_action("&Save plot", shortcut="Ctrl+S", slot=self.save_plot, tip="Save the plot") quit_action = self.create_action("&Quit", slot=self.close, shortcut="Ctrl+Q", tip="Close the application") self.add_actions(self.file_menu, (load_file_action, None, quit_action)) self.help_menu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut='F1', slot=self.on_about, tip='About the demo') self.add_actions(self.help_menu, (about_action,)) def add_actions(self, target, actions): for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def create_action( self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def save_plot(self): file_choices = "PNG (*.png)|*.png" path = unicode(QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def on_about(self): msg = """ Message to user goes here. """ QMessageBox.about(self, "MKID-ROACH software demo", msg.strip())
class CutePlot(QMainWindow): def __init__(self, parent=None): super(CutePlot, self).__init__(parent) # Default values for lower and upper bound self.LB_default = -10 self.UB_default = 10 # Create main plot area + menus + status bar self.create_main_frame() #self.textbox.setText() self.LB_UB_defaults() self.on_draw() self.statusBar() self.setWindowTitle('Graficador') self.create_menu() self.guardarImagen() def LB_UB_defaults(self): # Set default values for lower bound and upper bound self.lowerbound.setText(str(self.LB_default)) self.upperbound.setText(str(self.UB_default)) def create_main_frame(self): self.main_frame = QWidget() # 7x5 inches, 80 dots-per-inch self.dpi = 80 self.fig = Figure((5, 3), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) self.is_data = False self.axes = self.fig.add_subplot(111) # axis_state keeps track of how many subplots are present # axis_state = 0: main plot only # axis_state = 1: horizontal split (quadrants 1 and 2) # axis_state = 2: vertical split (quadrants 1 and 4) # axis_state = 3: show all 4 subplots self.axis_state = 0 self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) # f(x) textbox self.title = QLabel('<font size=4><em>f</em> (<em>x </em>) =</font>') self.textbox = QLineEdit() self.textbox.setMinimumWidth(200) self.connect(self.textbox, SIGNAL('returnPressed()'), self.on_draw) # Lowerbound and upperbound textboxes self.LB_title = QLabel('<font size=4>Min:</font>') self.lowerbound = QLineEdit() self.lowerbound.setMaximumWidth(30) self.connect(self.lowerbound, SIGNAL('returnPressed()'), self.on_draw) self.UB_title = QLabel('<font size=4>Max:</font>') self.upperbound = QLineEdit() self.upperbound.setMaximumWidth(30) self.connect(self.upperbound, SIGNAL('returnPressed()'), self.on_draw) # Plot button self.draw_button = QPushButton("&Plot") self.connect(self.draw_button, SIGNAL('clicked()'), self.on_draw) # Hold checkbox self.hold_cb = QCheckBox("&Hold") self.hold_cb.setChecked(False) self.connect(self.hold_cb, SIGNAL('stateChanged(int)'), self.on_minor_change) self.hold_cb.setToolTip('Prevent new plots from replacing old ones') self.hold_cb.setStatusTip('Prevent new plots from replacing old ones') # Log-x and log-y checkboxes self.logx_cb = QCheckBox("Log-&x") self.logx_cb.setChecked(False) self.connect(self.logx_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.logx_cb.setToolTip('Change x-axis to logarithmic scale') self.logx_cb.setStatusTip('Change x-axis to logarithmic scale') self.logy_cb = QCheckBox("Log-&y") self.logy_cb.setChecked(False) self.connect(self.logy_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.logy_cb.setToolTip('Change y-axis to logarithmic scale') self.logy_cb.setStatusTip('Change y-axis to logarithmic scale') # Truncated-log checkbox self.trunc_cb = QCheckBox("Show &Negative") self.trunc_cb.setChecked(False) self.connect(self.trunc_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.trunc_cb.setToolTip( 'Plot negative values of log-transformed functions') self.trunc_cb.setStatusTip( 'Plot negative values of log-transformed functions') # Grid checkbox self.grid_cb = QCheckBox("&Grid") self.grid_cb.setChecked(False) self.connect(self.grid_cb, SIGNAL('stateChanged(int)'), self.on_minor_change) self.grid_cb.setToolTip('Show grid') self.grid_cb.setStatusTip('Show grid') # Grid layout grid = QGridLayout() grid.setSpacing(10) gridCol = 0 for w in [ self.title, self.textbox, self.LB_title, self.lowerbound, self.UB_title, self.upperbound, self.draw_button ]: grid.addWidget(w, 0, gridCol) gridCol += 1 grid2 = QGridLayout() grid2.setSpacing(10) gridCol = 0 for w in [ self.logx_cb, self.logy_cb, self.trunc_cb, self.hold_cb, self.grid_cb ]: grid2.addWidget(w, 0, gridCol) gridCol += 1 vbox = QVBoxLayout() vbox.addLayout(grid) vbox.addLayout(grid2) vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def on_minor_change(self): self.on_draw(self.is_data) def on_draw(self, *args): # Get x-domain from user input self.LB_input = unicode(self.lowerbound.text()) self.UB_input = unicode(self.upperbound.text()) # Message box error if the domain inputs aren't int or float types # If float, round to the nearest 0.1 round_to = 10 try: self.LB_float = int(self.LB_input) * round_to self.UB_float = int(self.UB_input) * round_to except: self.LB_UB_defaults() QMessageBox.question( self, 'Error', '<center>Minimum and maximum values must be<br />\ integer or floating-point numbers.</center>', QMessageBox.Ok) # Make sure UB > LB if self.UB_float <= self.LB_float: self.LB_UB_defaults() QMessageBox.question( self, 'Error', '<center>Maximum must be greater\ than minimum value.</center>', QMessageBox.Ok) # If plotting a function, then get x and y values if len(args) == 0: self.is_data = False # Set x values (/round_to is to use range() with floating-point numbers) self.input_x = range(self.LB_float, self.UB_float + 1) self.input_x = [i / float(round_to) for i in self.input_x] # Calculate f(x) values for specified function fx = unicode(self.textbox.text()) # If the f(x) field is empty, then default to y = 0 plot if fx == '': self.y = [0 for i in self.input_x] # Otherwise, evaluate the specified function and get ready to plot else: # Replace exp with numbers fx = fx.replace('exp', str(exp(1)) + '**') # Allow users to enter ^ for powers (replace ^ with **) fx = fx.replace('^', '**') # Try and evaluate; if there is an error, then shift slightly to the right try: self.y = [eval(fx) for x in self.input_x] except: fx = fx.replace('x', '(x + 10**(-6))') self.y = [eval(fx) for x in self.input_x] self.plot_symbol = '-' if self.is_data: self.plot_symbol = 'o' # If the hold box is checked, then new plots do not erase old ones new_state = self.quad_check() if self.axis_state == 0: self.axes.hold(self.hold_cb.isChecked()) else: if self.hold_cb.isChecked(): # If 'hold' is checked, see what quadrants will be shown # - if the quadrant state changes, remove subplots # - otherwise retain subplots if self.axis_state == 0 and new_state == 0: self.axes.hold(self.hold_cb.isChecked()) elif self.axis_state == 3 and new_state == 3: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q2.hold(self.hold_cb.isChecked()) self.axes_Q3.hold(self.hold_cb.isChecked()) self.axes_Q4.hold(self.hold_cb.isChecked()) elif self.axis_state == 1 and new_state == 1: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q2.hold(self.hold_cb.isChecked()) elif self.axis_state == 2 and new_state == 2: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q4.hold(self.hold_cb.isChecked()) else: self.remove_subplots() else: self.remove_subplots() # If show negative box is unchecked if not self.trunc_cb.isChecked(): self.add_main() self.axes.plot(self.input_x, self.y, self.plot_symbol) if not self.logx_cb.isChecked() and not self.logy_cb.isChecked(): self.axes.set_xscale('linear') self.axes.set_yscale('linear') elif self.logx_cb.isChecked() and not self.logy_cb.isChecked(): self.axes.set_xscale('log') self.axes.set_yscale('linear') elif not self.logx_cb.isChecked() and self.logy_cb.isChecked(): self.axes.set_xscale('linear') self.axes.set_yscale('log') else: self.axes.set_xscale('log') self.axes.set_yscale('log') else: # Linear plot #if not self.logx_cb.isChecked() and not self.logy_cb.isChecked(): if new_state == 0: self.add_main() self.axes.plot(self.input_x, self.y, self.plot_symbol) # Log x, linear y plot #elif self.logx_cb.isChecked() and not self.logy_cb.isChecked(): elif new_state == 1: if not self.trunc_cb.isChecked(): self.add_main() self.axes.semilogx(self.input_x, self.y, self.plot_symbol) else: self.trunc_logx() # Linear x, log y plot #elif not self.logx_cb.isChecked() and self.logy_cb.isChecked(): elif new_state == 2: if not self.trunc_cb.isChecked(): self.add_main() self.axes.semilogy(self.input_x, self.y, self.plot_symbol) else: self.trunc_logy() # Log-log plot else: if not self.trunc_cb.isChecked(): self.add_main() self.axes.loglog(self.input_x, self.y, self.plot_symbol) else: self.trunc_loglog() # Add grid if grid checkbox is checked if self.axis_state == 0: self.axes.grid(self.grid_cb.isChecked()) else: if hasattr(self, 'axes_Q1'): self.axes_Q1.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q2'): self.axes_Q2.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q3'): self.axes_Q3.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q4'): self.axes_Q4.grid(self.grid_cb.isChecked()) self.axes.set_xlabel('$x$') self.axes.set_ylabel('$f(x)$') self.canvas.draw() self.guardarImagen() def remove_subplots(self): # Remove all subplots and axis flip flags if hasattr(self, 'axes_Q1'): self.fig.delaxes(self.axes_Q1) del self.axes_Q1 if hasattr(self, 'axes_Q2'): self.fig.delaxes(self.axes_Q2) del self.axes_Q2 if hasattr(self, 'flip_Q2'): del self.flip_Q2 if hasattr(self, 'axes_Q3'): self.fig.delaxes(self.axes_Q3) del self.axes_Q3 del self.flip_Q3 if hasattr(self, 'axes_Q4'): self.fig.delaxes(self.axes_Q4) del self.axes_Q4 if hasattr(self, 'flip_Q4'): del self.flip_Q4 def add_main(self): # Reinsert the main plot if self.axis_state > 0: self.remove_subplots() self.axes = self.fig.add_subplot(111) self.axis_state = 0 def create_menu(self): exitAction = QAction('Quit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit application') exitAction.triggered.connect(self.close) menubar = self.menuBar() fileMenu = menubar.addMenu('&File') save_plot_action = self.create_action("&Save plot", shortcut="Ctrl+S", slot=self.save_plot, tip="Save image to file") import_data_action = self.create_action("&Import data", shortcut="Ctrl+I", slot=self.import_data, tip="Import data from file") fileMenu.addAction(save_plot_action) fileMenu.addAction(import_data_action) fileMenu.addAction(exitAction) helpMenu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut='F1', slot=self.on_about, tip='About CutePlot') helpMenu.addAction(about_action) def create_action(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def save_plot(self): file_choices = "PNG (*.png)" path = unicode( QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def import_data(self): file_choices = "*.csv;;*.txt;;*.tab;;*.dat;;*.*" self.path = QFileDialog.getOpenFileName(self, 'Import data', '', file_choices) if self.path: datafile = open(self.path[0], 'r') if datafile: self.is_data = True delimiter = ',' input_xy = [ map(float, line.strip().split(delimiter)) for line in datafile ] self.input_x, self.y = [[row[col] for row in input_xy] for col in [0, 1]] datafile.close() self.statusBar().showMessage('Imported data', 2000) self.on_draw(self.is_data) def on_about(self): msg = """<center><b>CutePlot v. 0.1</b></center> <center>Free, open-source plotting program,<br /> written in Python (PySide/Qt + matplotlib).</center> <center>(c) Jack Peterson, 2012</center> """ QMessageBox.about(self, "About", msg.strip()) def quad_check(self): # Q = quadrant Q1 = False Q2 = False Q3 = False Q4 = False # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1 = True elif self.input_x[j] < 0 and self.y[j] > 0: Q2 = True elif self.input_x[j] < 0 and self.y[j] < 0: Q3 = True elif self.input_x[j] > 0 and self.y[j] < 0: Q4 = True if (Q3 or (Q2 and Q4) or ((Q2 or Q4) and self.axis_state == 3) ) and self.logx_cb.isChecked() and self.logy_cb.isChecked(): new_state = 3 elif (Q2 and self.logx_cb.isChecked()) or (self.hold_cb.isChecked() and self.axis_state == 1): new_state = 1 elif (Q4 and self.logy_cb.isChecked()) or (self.hold_cb.isChecked() and self.axis_state == 2): new_state = 2 else: new_state = 0 return new_state def trunc_logx(self): # Q = quadrant Q1_x = [] Q1_y = [] Q2_x = [] Q2_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] > 0: Q2_x.append(-self.input_x[j]) Q2_y.append(self.y[j]) # If only Q1 is populated, then use an ordinary semilogx plot if Q2_x == [] and not self.hold_cb.isChecked(): self.add_main() self.axes.semilogx(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) if self.axis_state == 2 or self.axis_state == 3: self.axis_state = 3 else: self.axis_state = 1 # Create 2 subplots self.axes_Q1 = self.fig.add_subplot(122) self.axes_Q2 = self.fig.add_subplot(121) self.axes_Q1.autoscale(enable=True) self.axes_Q2.autoscale(enable=True) self.axes_Q1.semilogx(Q1_x, Q1_y, self.plot_symbol) self.axes_Q2.semilogx(Q2_x, Q2_y, self.plot_symbol) # Reverse Q2 x-axis if not hasattr(self, 'flip_Q2'): self.flip_Q2 = True self.axes_Q2.set_xlim(self.axes_Q2.get_xlim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_xticklabels([]) else: try: x_UB_Q1 = int(ceil(log10(max(Q1_x)))) x_LB_Q1 = int(floor(log10(min(Q1_x)))) except: x_UB_Q1 = 2 x_LB_Q1 = -1 Q1_xlabels = [] for i in range(x_LB_Q1, x_UB_Q1 + 1): Q1_xlabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_xticklabels(Q1_xlabels) self.axes_Q1.xaxis.tick_bottom() self.axes_Q1.yaxis.tick_right() # Q2 axes if Q2_x == [] and not self.hold_cb.isChecked(): self.axes_Q2.set_xticklabels([]) else: try: x_UB_Q2 = int(ceil(log10(max(Q2_x)))) x_LB_Q2 = int(floor(log10(min(Q2_x)))) except: x_UB_Q2 = 2 x_LB_Q2 = -1 Q2_xlabels = [] for i in range(x_LB_Q2, x_UB_Q2 + 1): Q2_xlabels.append('$-10^{%s}$' % str(i)) self.axes_Q2.set_xticklabels(Q2_xlabels) self.axes_Q2.xaxis.tick_bottom() self.axes_Q2.yaxis.tick_left() def trunc_logy(self): # Q = quadrant Q1_x = [] Q1_y = [] Q4_x = [] Q4_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] > 0 and self.y[j] < 0: Q4_x.append(self.input_x[j]) Q4_y.append(-self.y[j]) # If only Q1 is populated, then use an ordinary semilogy plot if Q4_x == [] and not self.hold_cb.isChecked(): self.add_main() self.axes.semilogy(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) if self.axis_state == 1 or self.axis_state == 3: self.axis_state = 3 else: self.axis_state = 2 # Create 2 subplots self.axes_Q1 = self.fig.add_subplot(211) self.axes_Q4 = self.fig.add_subplot(212) self.axes_Q1.autoscale(enable=True) self.axes_Q4.autoscale(enable=True) self.axes_Q1.semilogy(Q1_x, Q1_y, self.plot_symbol) self.axes_Q4.semilogy(Q4_x, Q4_y, self.plot_symbol) # Reverse Q4 y-axis if not hasattr(self, 'flip_Q4'): self.flip_Q4 = True self.axes_Q4.set_ylim(self.axes_Q4.get_ylim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_yticklabels([]) else: try: y_UB_Q1 = int(ceil(log10(max(Q1_y)))) y_LB_Q1 = int(floor(log10(min(Q1_y)))) except: y_UB_Q1 = 2 y_LB_Q1 = -1 Q1_ylabels = [] for i in range(y_LB_Q1, y_UB_Q1 + 1): Q1_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_yticklabels(Q1_ylabels) self.axes_Q1.xaxis.tick_top() self.axes_Q1.yaxis.tick_right() # Q4 axes if Q4_x == [] and not self.hold_cb.isChecked(): self.axes_Q4.set_yticklabels([]) else: try: y_UB_Q4 = int(ceil(log10(max(Q4_y)))) y_LB_Q4 = int(floor(log10(min(Q4_y)))) except: y_UB_Q4 = 2 y_LB_Q4 = -1 Q4_ylabels = [] for i in range(y_LB_Q4, y_UB_Q4 + 1): Q4_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q4.set_yticklabels(Q4_ylabels) self.axes_Q4.xaxis.tick_bottom() self.axes_Q4.yaxis.tick_right() def trunc_loglog(self): # Q = quadrant Q1_x = [] Q1_y = [] Q2_x = [] Q2_y = [] Q3_x = [] Q3_y = [] Q4_x = [] Q4_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] > 0: Q2_x.append(-self.input_x[j]) Q2_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] < 0: Q3_x.append(-self.input_x[j]) Q3_y.append(-self.y[j]) elif self.input_x[j] > 0 and self.y[j] < 0: Q4_x.append(self.input_x[j]) Q4_y.append(-self.y[j]) # If only Q1 is populated, then use an ordinary loglog plot if Q2_x == [] and Q3_x == [] and Q4_x == [] and not self.hold_cb.isChecked( ): self.add_main() self.axes.loglog(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) self.axis_state = 3 # Create 4 subplots self.axes_Q1 = self.fig.add_subplot(222) self.axes_Q2 = self.fig.add_subplot(221) self.axes_Q3 = self.fig.add_subplot(223) self.axes_Q4 = self.fig.add_subplot(224) self.axes_Q1.autoscale(enable=True) self.axes_Q2.autoscale(enable=True) self.axes_Q3.autoscale(enable=True) self.axes_Q4.autoscale(enable=True) self.axes_Q1.loglog(Q1_x, Q1_y, self.plot_symbol) self.axes_Q2.loglog(Q2_x, Q2_y, self.plot_symbol) self.axes_Q3.loglog(Q3_x, Q3_y, self.plot_symbol) self.axes_Q4.loglog(Q4_x, Q4_y, self.plot_symbol) if not hasattr(self, 'flip_Q3'): self.flip_Q3 = True # Reverse Q2 x-axis self.axes_Q2.set_xlim(self.axes_Q2.get_xlim()[::-1]) # Reverse Q3 x- and y-axes self.axes_Q3.set_xlim(self.axes_Q3.get_xlim()[::-1]) self.axes_Q3.set_ylim(self.axes_Q3.get_ylim()[::-1]) # Reverse Q4 y-axis self.axes_Q4.set_ylim(self.axes_Q4.get_ylim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_xticklabels([]) self.axes_Q1.set_yticklabels([]) else: try: x_UB_Q1 = int(ceil(log10(max(Q1_x)))) y_UB_Q1 = int(ceil(log10(max(Q1_y)))) x_LB_Q1 = int(floor(log10(min(Q1_x)))) y_LB_Q1 = int(floor(log10(min(Q1_y)))) except: x_UB_Q1 = 2 y_UB_Q1 = 2 x_LB_Q1 = -1 y_LB_Q1 = -1 Q1_xlabels = [] Q1_ylabels = [] for i in range(x_LB_Q1, x_UB_Q1 + 1): Q1_xlabels.append('$10^{%s}$' % str(i)) for i in range(y_LB_Q1, y_UB_Q1 + 1): Q1_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_xticklabels(Q1_xlabels) self.axes_Q1.set_yticklabels(Q1_ylabels) self.axes_Q1.xaxis.tick_top() self.axes_Q1.yaxis.tick_right() # Q2 axes if Q2_x == [] and not self.hold_cb.isChecked(): self.axes_Q2.set_xticklabels([]) self.axes_Q2.set_yticklabels([]) else: try: x_UB_Q2 = int(ceil(log10(max(Q2_x)))) y_UB_Q2 = int(ceil(log10(max(Q2_y)))) x_LB_Q2 = int(floor(log10(min(Q2_x)))) y_LB_Q2 = int(floor(log10(min(Q2_y)))) except: x_UB_Q2 = 2 y_UB_Q2 = 2 x_LB_Q2 = -1 y_LB_Q2 = -1 Q2_xlabels = [] Q2_ylabels = [] for i in range(x_LB_Q2, x_UB_Q2 + 1): Q2_xlabels.append('$-10^{%s}$' % str(i)) for i in range(y_LB_Q2, y_UB_Q2 + 1): Q2_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q2.set_xticklabels(Q2_xlabels) self.axes_Q2.set_yticklabels(Q2_ylabels) self.axes_Q2.xaxis.tick_top() self.axes_Q2.yaxis.tick_left() # Q3 axes if Q3_x == [] and not self.hold_cb.isChecked(): self.axes_Q3.set_xticklabels([]) self.axes_Q3.set_yticklabels([]) else: try: x_UB_Q3 = int(ceil(log10(max(Q3_x)))) y_UB_Q3 = int(ceil(log10(max(Q3_y)))) x_LB_Q3 = int(floor(log10(min(Q3_x)))) y_LB_Q3 = int(floor(log10(min(Q3_y)))) except: x_UB_Q3 = 2 y_UB_Q3 = 2 x_LB_Q3 = -1 y_LB_Q3 = -1 Q3_xlabels = [] Q3_ylabels = [] for i in range(x_LB_Q3, x_UB_Q3 + 1): Q3_xlabels.append('$-10^{%s}$' % str(i)) for i in range(y_LB_Q3, y_UB_Q3 + 1): Q3_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q3.set_xticklabels(Q3_xlabels) self.axes_Q3.set_yticklabels(Q3_ylabels) self.axes_Q3.xaxis.tick_bottom() self.axes_Q3.yaxis.tick_left() # Q4 axes if Q4_x == [] and not self.hold_cb.isChecked(): self.axes_Q4.set_xticklabels([]) self.axes_Q4.set_yticklabels([]) else: try: x_UB_Q4 = int(ceil(log10(max(Q4_x)))) y_UB_Q4 = int(ceil(log10(max(Q4_y)))) x_LB_Q4 = int(floor(log10(min(Q4_x)))) y_LB_Q4 = int(floor(log10(min(Q4_y)))) except: x_UB_Q4 = 2 y_UB_Q4 = 2 x_LB_Q4 = -1 y_LB_Q4 = -1 Q4_xlabels = [] Q4_ylabels = [] for i in range(x_LB_Q4, x_UB_Q4 + 1): Q4_xlabels.append('$10^{%s}$' % str(i)) for i in range(y_LB_Q4, y_UB_Q4 + 1): Q4_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q4.set_xticklabels(Q4_xlabels) self.axes_Q4.set_yticklabels(Q4_ylabels) self.axes_Q4.xaxis.tick_bottom() self.axes_Q4.yaxis.tick_right() def guardarImagen(self): path = os.path.abspath("untitled.png") self.canvas.resize(460, 261) self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) self.canvas.resize(560, 361)
class GridDisplay(Component): ''' Class to create a display plot, using a Grid structure. ''' Vgrid = None #: see :ref:`shared_variable` Vfield = None #: see :ref:`shared_variable` VlevelZ = None \ #: see :ref:`shared_variable`, only used if plot_type="gridZ" VlevelY = None \ #: see :ref:`shared_variable`, only used if plot_type="gridY" VlevelX = None \ #: see :ref:`shared_variable`, only used if plot_type="gridX" Vcmap = None #: see :ref:`shared_variable` @classmethod def guiStart(self, parent=None): '''Graphical interface for starting this class''' args = _DisplayStart().startDisplay() return self(**args), True def __init__(self, Vgrid, Vfield, VlevelZ=None, VlevelY=None, VlevelX=None, Vlims=None, Vcmap=None, plot_type="gridZ", name="Display", parent=None): ''' Initialize the class to create display. Parameters ---------- Vgrid : :py:class:`~artview.core.core.Variable` instance grid signal variable. Vfield : :py:class:`~artview.core.core.Variable` instance Field signal variable. [Optional] VlevelZ : :py:class:`~artview.core.core.Variable` instance Signal variable for vertical level, only used if plot_type="gridZ". If None start with value zero. VlevelY : :py:class:`~artview.core.core.Variable` instance Signal variable for latitudinal level, only used if plot_type="gridY". If None start with value zero. VlevelX : :py:class:`~artview.core.core.Variable` instance Signal variable for longitudinal level, only used if plot_type="gridX". If None start with value zero. Vlims : :py:class:`~artview.core.core.Variable` instance Limits signal variable. A value of None will instantiate a limits variable. Vcmap : :py:class:`~artview.core.core.Variable` instance Colormap signal variable. A value of None will instantiate a colormap variable. plot_type : "gridZ", "gridY" or "gridX" Define plot type, "gridZ" will plot a Z level, that is a XY plane. Analog for "gridY" and "gridZ" name : string Display window name. parent : PyQt instance Parent instance to associate to Display window. If None, then Qt owns, otherwise associated with parent PyQt instance. Notes ----- This class records the selected button and passes the change value back to variable. ''' super(GridDisplay, self).__init__(name=name, parent=parent) self.setFocusPolicy(QtCore.Qt.ClickFocus) # Set up signal, so that DISPLAY can react to # external (or internal) changes in grid, field, # lims and level (expected to be Core.Variable instances) # The capital V so people remember using ".value" self.Vgrid = Vgrid self.Vfield = Vfield if VlevelZ is None: self.VlevelZ = Variable(0) else: self.VlevelZ = VlevelZ if VlevelY is None: self.VlevelY = Variable(0) else: self.VlevelY = VlevelY if VlevelX is None: self.VlevelX = Variable(0) else: self.VlevelX = VlevelX if Vlims is None: self.Vlims = Variable(None) else: self.Vlims = Vlims if Vcmap is None: self.Vcmap = Variable(None) else: self.Vcmap = Vcmap self.sharedVariables = {"Vgrid": self.Newgrid, "Vfield": self.NewField, "Vlims": self.NewLims, "Vcmap": self.NewCmap,} self.change_plot_type(plot_type) # Connect the components self.connectAllVariables() # Set plot title and colorbar units to defaults # TODO convert title to grid #self.title = None self.units = None # set default latlon lines self.lat_lines = np.linspace(-90, 90, num=181) self.lon_lines = np.linspace(-180, 180, num=361) # Find the PyArt colormap names self.cm_names = ["pyart_" + m for m in pyart.graph.cm.datad if not m.endswith("_r")] self.cm_names.sort() # Create tool dictionary self.tools = {} # Set up Default limits and cmap if Vlims is None: self._set_default_limits(strong=False) if Vcmap is None: self._set_default_cmap(strong=False) # Create a figure for output self._set_fig_ax() # Launch the GUI interface self.LaunchGUI() # Initialize grid variable self.Newgrid(None, None, True) self.show() def keyPressEvent(self, event): '''Allow level adjustment via the Up-Down arrow keys.''' if event.key() == QtCore.Qt.Key_Up: self.LevelSelectCmd(self.Vlevel.value + 1) elif event.key() == QtCore.Qt.Key_Down: self.LevelSelectCmd(self.Vlevel.value - 1) else: super(GridDisplay, self).keyPressEvent(event) #################### # GUI methods # #################### def LaunchGUI(self): '''Launches a GUI interface.''' # Create layout self.layout = QtGui.QGridLayout() self.layout.setSpacing(8) # Create the widget self.central_widget = QtGui.QWidget() self.setCentralWidget(self.central_widget) self._set_figure_canvas() self.central_widget.setLayout(self.layout) # Add buttons along display for user control self.addButtons() self.setUILayout() # Set the status bar to display messages self.statusbar = self.statusBar() ################################## # User display interface methods # ################################## def addButtons(self): '''Add a series of buttons for user control over display.''' # Create the Display controls self._add_displayBoxUI() # Create the Level controls self._add_levelBoxUI() # Create the Field controls self._add_fieldBoxUI() # Create the Tools controls self._add_toolsBoxUI() def setUILayout(self): '''Setup the button/display UI layout.''' self.layout.addWidget(self.levelBox, 0, 0) self.layout.addWidget(self.fieldBox, 0, 1) self.layout.addWidget(self.dispButton, 0, 2) self.layout.addWidget(self.toolsButton, 0, 3) ############################# # Functionality methods # ############################# def _open_LimsDialog(self): '''Open a dialog box to change display limits.''' from .limits import limits_dialog limits, cmap, change = limits_dialog(self.Vlims.value, self.Vcmap.value, self.name) if change == 1: self.Vcmap.change(cmap, False) print(limits) self.Vlims.change(limits) print(self.Vlims.value) def _fillLevelBox(self): '''Fill in the Level Window Box with current levels.''' self.levelBox.clear() self.levelBox.addItem("Level Window") # Loop through and create each level button if self.plot_type == "gridZ": levels = self.Vgrid.value.axes['z_disp']['data'] elif self.plot_type == "gridY": levels = self.Vgrid.value.axes['y_disp']['data'] elif self.plot_type == "gridX": levels = self.Vgrid.value.axes['x_disp']['data'] for nlevel in range(len(levels)): btntxt = "%2.1f m (level %d)"%(levels[nlevel], nlevel+1) self.levelBox.addItem(btntxt) def _fillFieldBox(self): '''Fill in the Field Window Box with current variable names.''' self.fieldBox.clear() self.fieldBox.addItem("Field Window") # Loop through and create each field button for field in self.fieldnames: self.fieldBox.addItem(field) def _levelAction(self, text): '''Define action for Level Button selection.''' if text == "Level Window": self._open_levelbuttonwindow() else: nlevel = int(text.split("(level ")[1][:-1])-1 self.LevelSelectCmd(nlevel) def _fieldAction(self, text): '''Define action for Field Button selection.''' if text == "Field Window": self._open_fieldbuttonwindow() else: self.FieldSelectCmd(str(text)) def _title_input(self): '''Retrieve new plot title.''' val, entry = common.string_dialog(self.title, "Plot Title", "Title:") if entry is True: self.title = val self._update_plot() def _units_input(self): '''Retrieve new plot units.''' val, entry = common.string_dialog(self.units, "Plot Units", "Units:") if entry is True: self.units = val self._update_plot() def _open_levelbuttonwindow(self): '''Open a LevelButtonWindow instance.''' from .level import LevelButtonWindow self.levelbuttonwindow = LevelButtonWindow( self.Vlevel, self.plot_type, Vcontainer=self.Vgrid, name=self.name+" Level Selection", parent=self.parent) def _open_fieldbuttonwindow(self): '''Open a FieldButtonWindow instance.''' from .field import FieldButtonWindow self.fieldbuttonwindow = FieldButtonWindow( self.Vgrid, self.Vfield, name=self.name+" Field Selection", parent=self.parent) def _add_cmaps_to_button(self): '''Add a menu to change colormap used for plot.''' for cm_name in self.cm_names: cmapAction = self.dispCmapmenu.addAction(cm_name) cmapAction.setStatusTip("Use the %s colormap" % cm_name) cmapAction.triggered[()].connect( lambda cm_name=cm_name: self.cmapSelectCmd(cm_name)) self.dispCmap.setMenu(self.dispCmapmenu) def _add_displayBoxUI(self): '''Create the Display Options Button menu.''' self.dispButton = QtGui.QPushButton("Display Options") self.dispButton.setToolTip("Adjust display properties") self.dispButton.setFocusPolicy(QtCore.Qt.NoFocus) dispmenu = QtGui.QMenu(self) dispLimits = dispmenu.addAction("Adjust Display Limits") dispLimits.setToolTip("Set data, X, and Y range limits") # TODO convert me to grid #dispTitle = dispmenu.addAction("Change Title") #dispTitle.setToolTip("Change plot title") dispUnit = dispmenu.addAction("Change Units") dispUnit.setToolTip("Change units string") self.dispCmap = dispmenu.addAction("Change Colormap") self.dispCmapmenu = QtGui.QMenu("Change Cmap") self.dispCmapmenu.setFocusPolicy(QtCore.Qt.NoFocus) #dispQuickSave = dispmenu.addAction("Quick Save Image") #dispQuickSave.setShortcut("Ctrl+D") #dispQuickSave.setStatusTip( # "Save Image to local directory with default name") #dispSaveFile = dispmenu.addAction("Save Image") #dispSaveFile.setShortcut("Ctrl+S") #dispSaveFile.setStatusTip("Save Image using dialog") dispLimits.triggered[()].connect(self._open_LimsDialog) #dispTitle.triggered[()].connect(self._title_input) dispUnit.triggered[()].connect(self._units_input) #dispQuickSave.triggered[()].connect(self._quick_savefile) #dispSaveFile.triggered[()].connect(self._savefile) self._add_cmaps_to_button() self.dispButton.setMenu(dispmenu) def _add_levelBoxUI(self): '''Create the Level Selection ComboBox.''' self.levelBox = QtGui.QComboBox() self.levelBox.setFocusPolicy(QtCore.Qt.NoFocus) self.levelBox.setToolTip("Choose level") self.levelBox.activated[str].connect(self._levelAction) def _add_fieldBoxUI(self): '''Create the Field Selection ComboBox.''' self.fieldBox = QtGui.QComboBox() self.fieldBox.setFocusPolicy(QtCore.Qt.NoFocus) self.fieldBox.setToolTip("Choose variable/field") self.fieldBox.activated[str].connect(self._fieldAction) def _add_toolsBoxUI(self): '''Create the Tools Button menu.''' self.toolsButton = QtGui.QPushButton("Toolbox") self.toolsButton.setFocusPolicy(QtCore.Qt.NoFocus) self.toolsButton.setToolTip("Choose a tool to apply") toolmenu = QtGui.QMenu(self) toolZoomPan = toolmenu.addAction("Zoom/Pan") toolValueClick = toolmenu.addAction("Click for Value") toolSelectRegion = toolmenu.addAction("Select a Region of Interest") toolCustom = toolmenu.addAction("Use Custom Tool") toolDefault = toolmenu.addAction("Reset File Defaults") toolZoomPan.triggered[()].connect(self.toolZoomPanCmd) toolValueClick.triggered[()].connect(self.toolValueClickCmd) toolSelectRegion.triggered[()].connect(self.toolSelectRegionCmd) toolCustom.triggered[()].connect(self.toolCustomCmd) toolDefault.triggered[()].connect(self.toolDefaultCmd) self.toolsButton.setMenu(toolmenu) ######################## # Selectionion methods # ######################## def Newgrid(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vgrid <artview.core.core.Variable>`. This will: * Update fields and levels lists and MenuBoxes * Check grid scan type and reset limits if needed * Reset units and title * If strong update: update plot ''' # test for None if self.Vgrid.value is None: self.fieldBox.clear() self.levelBox.clear() return # Get field names self.fieldnames = self.Vgrid.value.fields.keys() # Check the file type and initialize limts self._check_file_type() # Update field and level MenuBox self._fillLevelBox() self._fillFieldBox() self.units = None self.title = None if strong: self._update_plot() def NewField(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vfield <artview.core.core.Variable>`. This will: * Reset colormap * Reset units * Update fields MenuBox * If strong update: update plot ''' self._set_default_cmap(strong=False) self.units = None idx = self.fieldBox.findText(value) self.fieldBox.setCurrentIndex(idx) if strong and self.Vgrid.value is not None: self._update_plot() def NewLims(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vlims <artview.core.core.Variable>`. This will: * If strong update: update axes ''' if strong: self._update_axes() def NewCmap(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vcmap <artview.core.core.Variable>`. This will: * If strong update: update plot ''' if strong and self.Vgrid.value is not None: self._update_plot() def NewLevel(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vlevel* <artview.core.core.Variable>`. This will: * Update level MenuBox * If strong update: update plot ''' # +1 since the first one is "Level Window" self.levelBox.setCurrentIndex(value+1) if strong and self.Vgrid.value is not None: self._update_plot() def LevelSelectCmd(self, nlevel): ''' Captures Level selection and update Level :py:class:`~artview.core.core.Variable`. ''' if nlevel < 0: nlevel = len(self.levels)-1 elif nlevel >= len(self.levels): nlevel = 0 self.Vlevel.change(nlevel) def FieldSelectCmd(self, name): ''' Captures field selection and update field :py:class:`~artview.core.core.Variable`. ''' self.Vfield.change(name) def cmapSelectCmd(self, cm_name): '''Captures colormap selection and redraws.''' CMAP = cm_name self.Vcmap.value['cmap'] = cm_name self.Vcmap.change(self.Vcmap.value) def toolZoomPanCmd(self): '''Creates and connects to a Zoom/Pan instance.''' from .tools import ZoomPan scale = 1.1 self.tools['zoompan'] = ZoomPan( self.Vlims, self.ax, base_scale=scale, parent=self.parent) self.tools['zoompan'].connect() def toolValueClickCmd(self): '''Creates and connects to Point-and-click value retrieval''' from .pick_value import ValueClick self.tools['valueclick'] = ValueClick( self, name=self.name + "ValueClick", parent=self) def toolSelectRegionCmd(self): '''Creates and connects to Region of Interest instance.''' from .select_region import SelectRegion self.tools['select_region'] = SelectRegion(self, name=self.name + " SelectRegion", parent=self) def toolCustomCmd(self): '''Allow user to activate self-defined tool.''' from . import tools tools.custom_tool(self.tools) def toolDefaultCmd(self): '''Restore the Display defaults.''' from . import tools self.tools, limits, cmap = tools.restore_default_display( self.tools, self.Vfield.value, self.scan_type) self.Vcmap.change(cmap) self.Vlims.change(limits) def getPathInteriorValues(self, path): ''' Return the bins values path. Parameters ---------- path : Matplotlib Path instance Returns ------- x, y, azi, range, value, ray_idx, range_inx: ndarray Truplet of 1arrays containing x,y coordinate, azimuth, range, current field value, ray index and range index for all bin of the current grid and tilt inside path. Notes ----- If Vgrid.value is None, returns None ''' # TODO convert to grid from .tools import interior_grid grid = self.Vgrid.value if grid is None: return (np.array([]),)*7 xy, idx = interior_grid(path, grid, self.Vlevel.value, self.plot_type) aux = (xy[:, 0], xy[:, 1], grid.azimuth['data'][idx[:, 0]], grid.range['data'][idx[:, 1]] / 1000., grid.fields[self.Vfield.value]['data'][idx[:, 0], idx[:, 1]], idx[:, 0], idx[:, 1]) return aux def getNearestPoints(self, xdata, ydata): ''' Return the bins values nearest to point. Parameters ---------- xdata, ydata : float Returns ------- x, y, z, value, x_idx, y_idx, z_idx: ndarray Truplet of 1arrays containing x,y,z coordinate, current field value, x, y and z index. Notes ----- If Vgrid.value is None, returns None ''' from .tools import nearest_point_grid grid = self.Vgrid.value if grid is None: return (np.array([]),)*7 if self.plot_type == "gridZ": idx = nearest_point_grid(grid, self.levels[self.VlevelZ.value], ydata, xdata) elif self.plot_type == "gridY": idx = nearest_point_grid(grid, ydata, self.levels[self.VlevelY.value], x_data) elif self.plot_type == "gridX": idx = nearest_point_grid(grid, ydata, x_data, self.levels[self.VlevelX.value]) aux = (grid.axes['x_disp']['data'][idx[:,2]], grid.axes['y_disp']['data'][idx[:,1]], grid.axes['z_disp']['data'][idx[:,0]], grid.fields[self.Vfield.value]['data'][idx[:, 0], idx[:, 1], idx[:, 2]], idx[:, 2], idx[:, 1], idx[:, 0]) return aux #################### # Plotting methods # #################### def _set_fig_ax(self): '''Set the figure and axis to plot.''' self.XSIZE = 8 self.YSIZE = 8 self.fig = Figure(figsize=(self.XSIZE, self.YSIZE)) self.ax = self.fig.add_axes([0.2, 0.2, 0.7, 0.7]) self.cax = self.fig.add_axes([0.2, 0.10, 0.7, 0.02]) #self._update_axes() def _update_fig_ax(self): '''Set the figure and axis to plot.''' if self.plot_type in ("gridX", "gridY"): self.YSIZE = 5 else: self.YSIZE = 8 xwidth = 0.7 yheight = 0.7 * float(self.YSIZE) / float(self.XSIZE) self.ax.set_position([0.2, 0.55-0.5*yheight, xwidth, yheight]) self.cax.set_position([0.2, 0.10, xwidth, 0.02]) self._update_axes() def _set_figure_canvas(self): '''Set the figure canvas to draw in window area.''' self.canvas = FigureCanvasQTAgg(self.fig) # Add the widget to the canvas self.layout.addWidget(self.canvas, 1, 0, 7, 6) def _update_plot(self): '''Draw/Redraw the plot.''' self._check_default_field() # Create the plot with PyArt GridMapDisplay self.ax.cla() # Clear the plot axes self.cax.cla() # Clear the colorbar axes if self.Vfield.value not in self.Vgrid.value.fields.keys(): self.canvas.draw() return # Reset to default title if user entered nothing w/ Title button # TODO convert title to grid #if self.title == '': # title = None #else: # title = self.title limits = self.Vlims.value cmap = self.Vcmap.value self.display = pyart.graph.GridMapDisplay(self.Vgrid.value) # Create Plot if self.plot_type == "gridZ": self.display.plot_basemap(self.lat_lines, self.lon_lines, ax=self.ax) self.plot = self.display.plot_grid( self.Vfield.value, self.VlevelZ.value, vmin=cmap['vmin'], vmax=cmap['vmax'],cmap=cmap['cmap']) elif self.plot_type == "gridY": self.plot = self.display.plot_latitude_slice( self.Vfield.value, vmin=cmap['vmin'], vmax=cmap['vmax'],cmap=cmap['cmap']) elif self.plot_type == "gridX": self.plot = self.display.plot_longitude_slice( self.Vfield.value, vmin=cmap['vmin'], vmax=cmap['vmax'],cmap=cmap['cmap']) limits = self.Vlims.value x = self.ax.get_xlim() y = self.ax.get_ylim() limits['xmin'] = x[0] limits['xmax'] = x[1] limits['ymin'] = y[0] limits['ymax'] = y[1] self._update_axes() norm = mlabNormalize(vmin=cmap['vmin'], vmax=cmap['vmax']) self.cbar = mlabColorbarBase(self.cax, cmap=cmap['cmap'], norm=norm, orientation='horizontal') # colorbar - use specified units or default depending on # what has or has not been entered if self.units is None or self.units == '': try: self.units = self.Vgrid.value.fields[self.field]['units'] except: self.units = '' self.cbar.set_label(self.units) if self.plot_type == "gridZ": print("Plotting %s field, Z level %d in %s" % ( self.Vfield.value, self.VlevelZ.value+1, self.name)) elif self.plot_type == "gridY": print("Plotting %s field, Y level %d in %s" % ( self.Vfield.value, self.VlevelY.value+1, self.name)) elif self.plot_type == "gridX": print("Plotting %s field, X level %d in %s" % ( self.Vfield.value, self.VlevelX.value+1, self.name)) self.canvas.draw() def _update_axes(self): '''Change the Plot Axes.''' limits = self.Vlims.value self.ax.set_xlim(limits['xmin'], limits['xmax']) self.ax.set_ylim(limits['ymin'], limits['ymax']) self.ax.figure.canvas.draw() ######################### # Check methods # ######################### def _check_default_field(self): ''' Hack to perform a check on reflectivity to make it work with #a larger number of files as there are many nomenclature is the weather radar world. This should only occur upon start up with a new file. ''' if self.Vfield.value == pyart.config.get_field_name('reflectivity'): if self.Vfield.value in self.fieldnames: pass elif 'CZ' in self.fieldnames: self.Vfield.change('CZ', False) elif 'DZ' in self.fieldnames: self.Vfield.change('DZ', False) elif 'dbz' in self.fieldnames: self.Vfield.change('dbz', False) elif 'DBZ' in self.fieldnames: self.Vfield.change('DBZ', False) elif 'dBZ' in self.fieldnames: self.Vfield.change('dBZ', False) elif 'Z' in self.fieldnames: self.Vfield.change('Z', False) elif 'DBZ_S' in self.fieldnames: self.Vfield.change('DBZ_S', False) elif 'reflectivity_horizontal'in self.fieldnames: self.Vfield.change('reflectivity_horizontal', False) elif 'DBZH' in self.fieldnames: self.Vfield.change('DBZH', False) else: msg = "Could not find the field name.\n\ You can add an additional name by modifying the\n\ 'check_default_field' function in plot.py\n\ Please send a note to ARTView folks to add this name\n\ Thanks!" common.ShowWarning(msg) def _set_default_limits(self, strong=True): '''Set limits to pre-defined default.''' # TODO convert me to grid from .limits import _default_limits limits, cmap = _default_limits( self.Vfield.value, "PPI") self.Vlims.change(limits, strong) def _set_default_cmap(self, strong=True): '''Set colormap to pre-defined default.''' # TODO convert me to grid from .limits import _default_limits limits, cmap = _default_limits( self.Vfield.value, "PPI") self.Vcmap.change(cmap, strong) def _check_file_type(self): '''Check file to see if the file type.''' #self._update_fig_ax() return def change_plot_type(self, plot_type): '''Change plot type.''' # remove shared variables for key in ("VlevelZ","VlevelY","VlevelX"): if key in self.sharedVariables.keys(): del self.sharedVariables[key] if plot_type == "gridZ": self.sharedVariables["VlevelZ"] = self.NewLevel elif plot_type == "gridY": self.sharedVariables["VlevelY"] = self.NewLevel elif plot_type == "gridX": self.sharedVariables["VlevelX"] = self.NewLevel else: import warnings warnings.warn('Invalid Plot type %s, reseting to gridZ'%plot_type) self.sharedVariables["VlevelZ"] = self.NewLevel plot_type = "gridZ" self.plot_type = plot_type ######################## # Image save methods # ######################## # TODO convert me to grid - Update when PyArt updates this interface def _quick_savefile(self, PTYPE=IMAGE_EXT): '''Save the current display via PyArt interface.''' imagename = self.display.generate_filename( self.Vfield.value, self.Vlevel.value, ext=IMAGE_EXT) self.canvas.print_figure(os.path.join(os.getcwd(), imagename), dpi=DPI) self.statusbar.showMessage('Saved to %s' % os.path.join(os.getcwd(), imagename)) def _savefile(self, PTYPE=IMAGE_EXT): '''Save the current display using PyQt dialog interface.''' PBNAME = self.display.generate_filename( self.Vfield.value, self.Vlevel.value, ext=IMAGE_EXT) file_choices = "PNG (*.png)|*.png" path = unicode(QtGui.QFileDialog.getSaveFileName( self, 'Save file', PBNAME, file_choices)) if path: self.canvas.print_figure(path, dpi=DPI) self.statusbar.showMessage('Saved to %s' % path) ######################## # get methods # ######################## def getPlotAxis(self): ''' get :py:class:`matplotlib.axes.Axes` instance of main plot ''' return self.ax def getStatusBar(self): ''' get :py:class:`PyQt4.QtGui.QStatusBar` instance''' return self.statusbar def getField(self): ''' get current field ''' return self.Vfield.value def getUnits(self): ''' get current units ''' return self.units ######################## # Properties # ######################## @property def Vlevel(self): '''Alias to VlevelZ, VlevelY or VlevelX depending on plot_type.''' if self.plot_type == "gridZ": return self.VlevelZ elif self.plot_type == "gridY": return self.VlevelY elif self.plot_type == "gridX": return self.VlevelX else: return None @property def levels(self): '''Values from the axes of grid, depending on plot_type.''' if self.plot_type == "gridZ": return self.Vgrid.value.axes['z_disp']['data'][:] elif self.plot_type == "gridY": return self.Vgrid.value.axes['y_disp']['data'][:] elif self.plot_type == "gridX": return self.Vgrid.value.axes['x_disp']['data'][:] else: return None
class MainWindow(QMainWindow, Ui_MainWindow): '''Main application window''' selectedEdgeChanged = pyqtSignal(object) selectedEdgeChangedList = pyqtSignal(object) #when edge selected in QListWidget unitCellChanged = pyqtSignal() latticeVisibleChanged = pyqtSignal(object) # used to bind with mpl.event arrowsVisibleChanged = pyqtSignal(object) # used to bind with mpl.event def __init__(self, fileName=None, TEXT_MODE=True): super(MainWindow, self).__init__() self.setupUi(self) self.prefFileName = os.path.dirname(__file__)+'/../resources/preferences.xml' self.SETTINGS = ET.parse(self.prefFileName).getroot() self.CURRENT_THEME = DealXML.get_child_by_name(self.SETTINGS,"THEME","Current theme") self.TEXT_MODE = TEXT_MODE self.size = (2,2,2) self.spinBox_sizeL.setValue(self.size[0]) self.spinBox_sizeW.setValue(self.size[1]) self.spinBox_sizeH.setValue(self.size[2]) self.spinBox_type.clear() self.radioButton_output.setChecked(TEXT_MODE) self.setup_mpl_canvas() # initialize canvas path = self.prefFileName if fileName is None else fileName self.importXML_fromFile(path) self.fileNameXML = fileName self.label_fileNameXML.setText("XML library file: " + self.getFileLabelText()) self.msb_noActiveEdge = QMessageBox() self.msb_noActiveEdge.setIcon(QMessageBox.Critical) self.msb_noActiveEdge.setWindowTitle("Message") self.msb_noActiveEdge.setStandardButtons(QMessageBox.Ok) self.msb_noActiveEdge.setText("No edge is selected") # setup signals and slots self.btnEditXML.clicked.connect(self.editXML_callback) self.spinBox_sizeL.valueChanged.connect(self.changeSize_callback) self.spinBox_sizeW.valueChanged.connect(self.changeSize_callback) self.spinBox_sizeH.valueChanged.connect(self.changeSize_callback) self.btnDel.clicked.connect(self.delteEdge_callback) self.btnClear.clicked.connect(self.gee.clearEdges_callback) self.btnChangeType.clicked.connect(self.changeType_callback) self.btnLength.clicked.connect(self.addDistEdges_callback) self.listEdges.currentItemChanged.connect(self.selectEdgeList_callback) self.radioButton_output.toggled.connect(self.change_textMode) self.selectedEdgeChanged.connect(self.selectEdgeSignal_slot) self.unitCellChanged.connect(self.update_listEdges) self.setup_menu() if self.TEXT_MODE: print(self.gee.__doc__) def setup_menu(self): '''setup slot for menu actions''' # configure menuFile self.action_ImportXML.triggered.connect(self.importXMLdlg_callback) self.action_ImportCryst.triggered.connect(self.importCryst_callback) self.action_SaveXML.triggered.connect(self.saveXML_callback) self.action_SaveXML_as.triggered.connect(self.saveXML_as_callback) self.action_ExportIMG.triggered.connect(self.exportIMG_callback) self.action_ExportAnim.triggered.connect(self.exportAnim_callback) self.action_Quit.triggered.connect(self.quit_callback) # configure menuEdit self.action_EditXML.triggered.connect(self.editXML_callback) self.action_AddSimEdges.triggered.connect(self.addSimEdges_callback) self.action_AddDistEdges.triggered.connect(self.addDistEdges_callback) self.action_ChangeType.triggered.connect(self.menuChangeType_callback) self.action_DelEdge.triggered.connect(self.delteEdge_callback) self.action_ClearEdges.triggered.connect(self.gee.clearEdges_callback) self.action_Pref.triggered.connect(self.preferences_callback) # configure menuHelo self.action_About.triggered.connect(self.about_callback) self.action_Doc.triggered.connect(self.doc_callback) def setup_mpl_canvas(self): ''' setup matplotlib manipulation pane widget for displaying and editing lattice graph ''' self.dpi = 100 self.fig = Figure((5.0, 5.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.ax = self.fig.gca(projection='3d') self.fig.subplots_adjust(left=0, bottom=0, right=1, top=1) self.canvas.setParent(self.mplWidget) self.mplLayout.addWidget(self.canvas) self.canvas.setFocusPolicy(Qt.ClickFocus) self.canvas.setFocus() # add aniation button self.btnPlay = QPushButton("Animate") self.btnPlay.setStatusTip("Open animation manager.") self.btnPlay.clicked.connect(self.exportAnim_callback) self.btnPlay.setFocusPolicy(Qt.NoFocus) mplHbox = QHBoxLayout() mplHbox.addWidget(self.btnPlay) mplHbox.addStretch() mplVbox = QVBoxLayout() mplVbox.addLayout(mplHbox) mplVbox.addStretch() self.canvas.setLayout(mplVbox) def importXML_fromFile(self, path): '''import lattice graph form xml file''' self.fileNameXML = path self.parser = ParseXML(fileName = self.fileNameXML) LG_name_list = self.parser.get_LATTICEGRAPH_names() if len(LG_name_list) > 1: self.dlgSelectLG = DialogSelectLG(self, LG_name_list) self.dlgSelectLG.show() else: self.importXml(LG_name_list[0]) def importXml(self, LG_name): '''import lattice graph with given name from predefined parser - ParseXML object''' self.LATTICEGRAPH_name = LG_name self.label_fileNameXML.setText("XML library file: "+self.getFileLabelText()) self.label_LG_name.setText("Lattice graph name: "+self.LATTICEGRAPH_name) if self.parser is None: raise ValueError("Parser is not defined") self.lattice, self.UC = self.parser.parse_LATTICEGRAPH(self.LATTICEGRAPH_name) self.cluster = CrystalCluster(self.UC, self.lattice, self.size) self.ax.clear() self.gee = GraphEdgesEditor(self.ax, self.cluster, parent=self, display_report=True) self.canvas.draw() self.update_listEdges() self.unitCellChanged.emit() def update_listEdges(self): '''is used to update QListWidget when unit cell is changed''' self.initialization = True # block QListWidget valuechanged callback self.listEdges.clear() defaultListItem = QListWidgetItem('') self.listEdges_idToItem = {None: defaultListItem} self.listEdges_ItemToId = {defaultListItem.text(): None} for key, edge in self.gee.UC.edges.items(): newItem = QListWidgetItem(str(edge)) self.listEdges.addItem(newItem) self.listEdges_idToItem[key] = newItem self.listEdges_ItemToId[newItem.text()] = key self.listEdges.addItem(defaultListItem) self.listEdges.setCurrentItem(defaultListItem) self.initialization = False # relieze QListWidget valuechanged callback def changeSize_callback(self): '''called when cluter size in spinBox is chanaged''' self.size = (self.spinBox_sizeL.value(), self.spinBox_sizeW.value(), self.spinBox_sizeH.value()) self.gee.reset_size(self.size) def changeType_callback(self): '''called when value of self.spinBox_type is changed''' if self.gee.e_active_ind is None: self.msb_noActiveEdge.exec_() else: self.gee.change_active_edge_type(self.spinBox_type.value()) def selectEdgeList_callback(self, selectedItem): '''called when edge is selected in QListWidget''' if not self.initialization: activeEdge_id = self.listEdges_ItemToId[selectedItem.text()] self.gee.select_edge(activeEdge_id) self.selectedEdgeChangedList.emit(activeEdge_id) if activeEdge_id is None: msg = " active edge unselected" self.spinBox_type.clear() else: msg = " selected edge: {}".format(self.cluster.UC.edges[activeEdge_id]) _type = self.cluster.UC.edges[activeEdge_id].type self.spinBox_type.setValue(_type) self.statusBar().showMessage(msg, 2000) if self.TEXT_MODE: print(msg) def selectEdgeSignal_slot(self, activeEdge_id): '''Process selecting edge signal''' activeItem = self.listEdges_idToItem[activeEdge_id] self.listEdges.setCurrentItem(activeItem) def change_textMode(self, _bool): '''turn on/off printing actions into terminal''' self.TEXT_MODE = _bool self.gee.display_report = _bool msg = " displaying actions in terminal is turned {}".format("on" if _bool else "off") self.statusBar().showMessage(msg, 2000) print(msg) def getFileLabelText(self): '''Returns the label string of the xml library file''' if self.fileNameXML is None: return "None" else: fileName = os.path.basename(self.fileNameXML) dirName = os.path.basename(os.path.dirname(self.fileNameXML)) return os.path.join("...", dirName, fileName) def importXMLdlg_callback(self): '''when import acttion is activated''' output = QFileDialog.getOpenFileName(self, 'Open xml library containing Lattice Graph', filter = "XML files (*.xml);;All files (*.*)") path = getPathString(output) if path != "": self.importXML_fromFile(path) def importCryst_callback(self): '''import crystal providing lattice and unit cell parameters''' self.dlgImportCryst = DialogImportCryst(self) self.dlgImportCryst.show() def saveXML_callback(self): '''save changes to lattice graph xml library file''' if self.fileNameXML == None: self.saveXML_as_callback() else: self.exporter = ExportXML(self.gee.cluster.lattice, self.gee.cluster.UC, self.LATTICEGRAPH_name) self.exporter.export_to_lib(self.fileNameXML) def saveXML_as_callback(self): '''save lattice graph to xml library file''' dialog = DialogExportLG(self, self.LATTICEGRAPH_name, self.cluster.lattice.atrib["BOUNDARY"]) if dialog.exec_(): self.LATTICEGRAPH_name = str(dialog.lineEdit_LGname.text()) self.gee.cluster.lattice.atrib["BOUNDARY"]= \ str(dialog.comboBox_boundary.currentText()) output = QFileDialog.getSaveFileName(self, filter="XML files (*.xml)") path = getPathString(output) # if not canceled if path != '': self.fileNameXML = path self.exporter = ExportXML(self.gee.cluster.lattice, self.gee.cluster.UC, self.LATTICEGRAPH_name) self.exporter.export_to_lib(self.fileNameXML) self.label_fileNameXML.setText("XML library file: "+self.getFileLabelText()) def exportIMG_callback(self): '''Savve image of the Heisenberg model (lattice graph)''' output = QFileDialog.getSaveFileName(self,caption='Save model image', filter="Images (*.png *.xpm *.jpg);;All files (*.*)") path = getPathString(output) if path != '': self.exportIMG(path) def exportIMG(self, path): '''Savve image of the Heisenberg model (lattice graph)''' self.canvas.print_figure(path, dpi=self.dpi, bbox_inches='tight', pad_inches=0) self.statusBar().showMessage('Saved to %s' % path, 2000) def exportAnim_callback(self): '''animate lattice graph mpl_pane and open animation manager''' self.dlgExportAnim = QDialogAnimManager(self.ax) self.dlgExportAnim.show() # disable animated GraphEdgeEditor artists self.gee.sc_active.set_visible(False) self.gee.new_edge.set_visible(False) # enabele animated GraphEdgeEditor artists self.dlgExportAnim.closed.connect(self.gee.sc_active.set_visible) self.dlgExportAnim.closed.connect(self.gee.new_edge.set_visible) def quit_callback(self): self.close() def editXML_callback(self): ''' open lattice graph xml code editor''' self.dlgEditXML = DialogEditXML(self) self.dlgEditXML.show() if self.TEXT_MODE: print(" open lattice graph xml code editor") def addSimEdges_callback(self): '''search for and add edges that have the same length as selected one''' if self.gee.e_active_ind is None: self.msb_noActiveEdge.exec_() else: self.gee.searchActiveDistEdge_callback() def addDistEdges_callback(self): '''opens edge length manipulation manager''' self.gee.select_edge(None) self.selectEdgeSignal_slot(None) self.dlgDistSearch = DialogDistSearch(self) self.dlgDistSearch.show() def menuChangeType_callback(self): '''change selected edge type''' if self.gee.e_active_ind is None: self.msb_noActiveEdge.exec_() else: self.dlg = DialogChangeEdgeType(self) self.dlg.show() def delteEdge_callback(self): '''delete selected edge''' if self.gee.e_active_ind is None: self.msb_noActiveEdge.exec_() else: self.gee.delete_active_edge_callback() def preferences_callback(self): '''Calls preference dialog''' self.dlgPref = MyDialogPreferences(parent = self) self.dlgPref.applySignal.connect(self.applyPref_callback) self.arrowsVisibleChanged.connect(self.dlgPref.prefWidget.checkBox_arrows.setChecked) self.latticeVisibleChanged.connect(self.dlgPref.prefWidget.checkBox_lattice.setChecked) self.dlgPref.show() def applyPref_callback(self): '''when apply button is cklicked in DialogPreferences''' self.gee.initialize_theme(self.CURRENT_THEME) self.gee.set_artists_properties() def about_callback(self): '''display app help''' self.msg = QMessageBox() self.msg.setIcon(QMessageBox.Information) self.msg.setTextFormat(Qt.RichText) text = ''' <b>Lattice graph designer 1.0a1</b> <br> Copyright © 2017, Ivan Luchko and Project Contributors <br> Licensed under the terms of the MIT License <br><br> Lattice graph designer is a tool which allows to visualize and create a lattice graph model using the intuitive GUI and interactive 3D drag-and-drop graph manipulation pane. <br><br> It was primarily created for the <a href="http://alps.comp-phys.org">ALPS project</a> to deal with a lattice graph of the <a href="https://en.wikipedia.org/wiki/Heisenberg_model_(quantum)">Heisenberg model</a> defined in <a href="http://alps.comp-phys.org/mediawiki/index.php/Tutorials:LatticeHOWTO"> ALPS xml graph format</a>. <br><br> Support of the other formats and projects can be extended. <br><br> For bug reports and feature requests, please go to our <a href="https://github.com/luchko/latticegraph_designer">Github website</a>. ''' self.msg.setText(text) self.msg.setWindowTitle("About Lattice graph designer") self.msg.setStandardButtons(QMessageBox.Ok) self.msg.exec_() def doc_callback(self): '''open documentation''' webbrowser.open_new_tab("https://latticegraph-designer.readthedocs.io")
class Display(QtGui.QMainWindow): '''Class that plots a Radar structure using pyart.graph''' def __init__(self, Vradar, Vfield, Vtilt, airborne=False, rhi=False, name="Display", parent=None): '''Initialize the class to create the interface''' super(Display, self).__init__(parent) self.parent = parent self.name = name self.setWindowTitle(name) #XXX set up signal, so that DISPLAY can react to external (or internal) changes in radar,field and tilt #XXX radar,field and tilt are expected to be Core.Variable instances #XXX I use the capital V so people remember using ".value" self.Vradar = Vradar QtCore.QObject.connect(Vradar,QtCore.SIGNAL("ValueChanged"),self.NewRadar) self.Vfield = Vfield QtCore.QObject.connect(Vfield,QtCore.SIGNAL("ValueChanged"),self.NewField) self.Vtilt = Vtilt QtCore.QObject.connect(Vtilt,QtCore.SIGNAL("ValueChanged"),self.NewTilt) self.airborne = airborne self.rhi = rhi # Set size of plot self.XSIZE = PPI_XSIZE self.YSIZE = PPI_YSIZE self.XRNG = PPI_XRNG self.YRNG = PPI_YRNG if self.airborne: self.XSIZE = AIR_XSIZE self.YSIZE = AIR_YSIZE self.XRNG = AIR_XRNG self.YRNG = AIR_YRNG if self.rhi: self.XSIZE = RHI_XSIZE self.YSIZE = RHI_YSIZE self.XRNG = RHI_XRNG self.YRNG = RHI_YRNG # Set plot title and colorbar units to defaults self.title = None self.units = None # Initialize limits self._initialize_limits() # Set the default range rings self.RngRingList = ["None", "10 km", "20 km", "30 km", "50 km", "100 km"] self.RngRing = False # Find the PyArt colormap names # self.cm_names = pyart.graph.cm._cmapnames self.cm_names = [m for m in cm.datad if not m.endswith("_r")] self.cm_names.sort() # Create a figure for output self._set_fig_ax(nrows=1, ncols=1) # Initiate no tool useage self.ToolSelect = "No Tools" #XXX this is problably not the right way of doing this # Launch the GUI interface self.LaunchGUI() #XXX almost empty self.NewRadar(None, None) #XXX initialise radar self.show() self.pickPoint = self.fig.canvas.mpl_connect('button_press_event', self.onPick) # Allow advancement via left and right arrow keys # and tilt adjustment via the Up-Down arrow keys def keyPressEvent(self, event): if event.key()==QtCore.Qt.Key_Up: self.TiltSelectCmd(self.Vtilt.value + 1) #XXX Display control de tilt, but not the file elif event.key()==QtCore.Qt.Key_Down: self.TiltSelectCmd(self.Vtilt.value - 1) #XXX Display control de tilt, but not the file else: if self.parent==None: QtGui.QWidget.keyPressEvent(self, event) else: self.parent.keyPressEvent(event) #XXX send event to parent to handel it, I consider not having a pygt form of doing this a limitation def onPick(self, event): '''Get value at the point selected by mouse click''' xdata = event.xdata # get event x location ydata = event.ydata # get event y location az = np.arctan2(xdata, ydata)*180./np.pi radar = self.Vradar.value #keep equantions clean if az < 0: az = az + 360. rng = np.sqrt(xdata*xdata+ydata*ydata) azindex = np.argmin(np.abs(radar.azimuth['data'][radar.sweep_start_ray_index['data'][self.Vtilt.value]:radar.sweep_end_ray_index['data'][self.Vtilt.value]]-az))+radar.sweep_start_ray_index['data'][self.Vtilt.value] rngindex = np.argmin(np.abs(radar.range['data']-rng*1000.)) msg = 'x = %4.2f, y = %4.2f, Azimuth = %4.2f deg., Range = %4.2f km, %s = %4.2f %s'\ %(xdata, ydata, radar.azimuth['data'][azindex], radar.range['data'][rngindex]/1000., self.Vfield.value, radar.fields[self.Vfield.value]['data'][azindex][rngindex], self.units) self.statusBar().showMessage(msg) #################### # GUI methods # #################### def LaunchGUI(self): '''Launches a GUI interface.''' # Create layout self.layout = QtGui.QGridLayout() self.layout.setSpacing(8) # Create the widget self.central_widget = QtGui.QWidget() self.setCentralWidget(self.central_widget) # self.statusBar() self.central_widget.setLayout(self.layout) #self.setLayout(self.layout) # Create the Tilt buttons #self.CreateTiltWidget() # Create the Tools ComboBox # self.toolsBoxUI() self.addButtons() ######################## # Button methods # ######################## def addButtons(self): '''If not BASIC mode, then add functionality buttons''' # Create the button controls limsb = QtGui.QPushButton("Adjust Limits") limsb.setToolTip("Set data, X, and Y range limits") # limsb.clicked.connect(self.showLimsDialog) titleb = QtGui.QPushButton("Title") titleb.setToolTip("Change plot title") titleb.clicked.connect(self._title_input) unitsb = QtGui.QPushButton("Units") unitsb.setToolTip("Change units string") unitsb.clicked.connect(self._units_input) tiltsb = QtGui.QPushButton("Tilt Select") tiltsb.setToolTip("Choose tilt elevation angle") tiltsb.clicked.connect(self._open_tiltbuttonwindow) self.layout.addWidget(limsb, 0, 0) # self.layout.addWidget(self.toolsBox, 0, 1) self.layout.addWidget(titleb, 0, 2) self.layout.addWidget(unitsb, 0, 3) self.layout.addWidget(tiltsb, 0, 4) ############################# # Functionality methods # ############################# def _lims_input(self, entry): '''Retrieve new limits input''' if entry['dmin'] is not None: self.limits['vmin'] = entry['dmin'] if entry['dmax'] is not None: self.limits['vmax'] = entry['dmax'] if entry['xmin'] is not None: self.limits['xmin'] = entry['xmin'] if entry['xmax'] is not None: self.limits['xmax'] = entry['xmax'] if entry['ymin'] is not None: self.limits['ymin'] = entry['ymin'] if entry['ymax'] is not None: self.limits['ymax'] = entry['ymax'] self._update_plot() def _title_input(self): '''Retrieve new plot title''' if self.title is None: old_val = '' else: old_val = self.title val, entry = QtGui.QInputDialog.getText(self, "Plot Title", \ "Title:", 0, old_val) if entry is True: self.title = val self._update_plot() def _units_input(self): '''Retrieve new plot units''' if self.units is None: old_val = '' else: old_val = self.units val, entry = QtGui.QInputDialog.getText(self, "Plot Units", \ "Units:", 0, old_val) if entry is True: self.units = val self._update_plot() def _open_tiltbuttonwindow(self): '''Open a TiltButtonWindow instance''' self.tiltbuttonwindow = TiltButtonWindow(self.Vradar, self.Vtilt, \ name=self.name+" Tilt Selection", parent=self.parent) ######################## # Selectionion methods # ######################## def NewRadar(self,variable,value): # In case the flags were not used at startup self._check_file_type() self._set_figure_canvas() # Get the tilt angles self.rTilts = self.Vradar.value.sweep_number['data'][:] # Get field names self.fieldnames = self.Vradar.value.fields.keys() # Set up the menus associated with scanning ground radars if self.airborne or self.rhi: pass else: pass self.units = None self.title = None self._update_plot() def NewField(self,variable,value): self._initialize_limits() self.units = None self._update_plot() def NewTilt(self,variable,value): self._update_plot() def TiltSelectCmd(self, ntilt): '''Captures a selection and redraws the field with new tilt''' print ntilt self.Vtilt.change(ntilt) #XXX tilt is changed and signal sended, so this and other classes do what they need to do def FieldSelectCmd(self, nombre): '''Captures a selection and redraws the new field''' self.Vfield.change(nombre) def RngRingSelectCmd(self, ringSel): '''Captures selection and redraws the field with range rings''' if ringSel is "None": self.RngRing = False else: self.RngRing = True # Find the unambigous range of the radar try: unrng = int(self.radar.instrument_parameters['unambiguous_range']['data'][0]/1000) except: unrng = int(self.limits['xmax']) # Set the step if ringSel == '10 km': ringdel = 10 if ringSel == '20 km': ringdel = 20 if ringSel == '30 km': ringdel = 30 if ringSel == '50 km': ringdel = 50 if ringSel == '100 km': ringdel = 100 # Calculate an array of range rings self.RNG_RINGS = range(ringdel, unrng, ringdel) self._update_plot() def cmapSelectCmd(self, cm_name): '''Captures selection of new cmap and redraws''' self.CMAP = cm_name self._update_plot() #################### # Plotting methods # #################### def _set_fig_ax(self, nrows=1, ncols=1): '''Set the figure and axis to plot to''' self.fig = Figure(figsize=(self.XSIZE, self.YSIZE)) xwidth = 0.7 yheight = 0.7 * float(self.YSIZE)/float(self.XSIZE) self.ax = self.fig.add_axes([0.2, 0.2, xwidth, yheight]) self.cax = self.fig.add_axes([0.2,0.10, xwidth, 0.02]) # We want the axes cleared every time plot() is called #self.axes.hold(False) def _set_fig_ax_rhi(self): '''Change figure size and limits if RHI''' if self.rhi: self.XSIZE = RHI_XSIZE self.YSIZE = RHI_YSIZE self.limits['ymin'] = RHI_YRNG[0] self.limits['ymax'] = RHI_YRNG[1] if self.airborne: self.XSIZE = AIR_XSIZE self.YSIZE = AIR_YSIZE self.limits['xmin'] = AIR_XRNG[0] self.limits['xmax'] = AIR_XRNG[1] self.limits['ymin'] = AIR_YRNG[0] self.limits['ymax'] = AIR_YRNG[1] self.fig.set_size_inches(self.XSIZE, self.YSIZE) self._set_fig_ax() def _set_figure_canvas(self): '''Set the figure canvas to draw in window area''' self.canvas = FigureCanvasQTAgg(self.fig) # Add the widget to the canvas self.layout.addWidget(self.canvas, 1, 0, 7, 6) def _update_plot(self): '''Renew the plot''' # This is a bit of a hack to ensure that the viewer works with files # withouth "standard" output as defined by PyArt # Check to see if the field 'reflectivity' exists for the initial open self._check_default_field() # Create the plot with PyArt RadarDisplay # Always intitiates at lowest elevation angle self.ax.cla() # Reset to default title if user entered nothing w/ Title button if self.title == '': self.title = None # If Zoom/Pan selected, Set up the zoom/pan functionality if self.ToolSelect == "Zoom/Pan": scale = 1.1 self.zp = ZoomPan(self.ax, self.limits, base_scale = scale) #figZoom = self.zp.zoom() #figPan = self.zp.pan_factory(self.limits) self.zp.connect() if self.airborne: self.display = pyart.graph.RadarDisplay_Airborne(self.Vradar.value) self.plot = self.display.plot_sweep_grid(self.Vfield.value, \ vmin=self.limits['vmin'], vmax=self.limits['vmax'],\ colorbar_flag=False, cmap=self.CMAP,\ ax=self.ax, title=self.title) self.display.set_limits(xlim=(self.limits['xmin'], self.limits['xmax']),\ ylim=(self.limits['ymin'], self.limits['ymax']),\ ax=self.ax) self.display.plot_grid_lines() else: self.display = pyart.graph.RadarDisplay(self.Vradar.value) if self.Vradar.value.scan_type != 'rhi': # Create Plot if self.Vtilt.value < len(self.rTilts): pass else: self.Vtilt.change(0) self.plot = self.display.plot_ppi(self.Vfield.value, self.Vtilt.value,\ vmin=self.limits['vmin'], vmax=self.limits['vmax'],\ colorbar_flag=False, cmap=self.CMAP,\ ax=self.ax, title=self.title) # Set limits self.display.set_limits(xlim=(self.limits['xmin'], self.limits['xmax']),\ ylim=(self.limits['ymin'], self.limits['ymax']),\ ax=self.ax) # Add range rings if self.RngRing: self.display.plot_range_rings(self.RNG_RINGS, ax=self.ax) # Add radar location self.display.plot_cross_hair(5., ax=self.ax) else: self.plot = self.display.plot_rhi(self.Vfield.value, self.Vtilt.value,\ vmin=self.limits['vmin'], vmax=self.limits['vmax'],\ colorbar_flag=False, cmap=self.CMAP,\ ax=self.ax, title=self.title) self.display.set_limits(xlim=(self.limits['xmin'], self.limits['xmax']),\ ylim=(self.limits['ymin'], self.limits['ymax']),\ ax=self.ax) # Add range rings if self.RngRing: self.display.plot_range_rings(self.RNG_RINGS, ax=self.ax) norm = mlabNormalize(vmin=self.limits['vmin'],\ vmax=self.limits['vmax']) self.cbar=mlabColorbarBase(self.cax, cmap=self.CMAP,\ norm=norm, orientation='horizontal') # colorbar - use specified units or default depending on # what has or has not been entered if self.units is None or self.units == '': try: self.units = self.Vradar.value.fields[self.field]['units'] except: self.units = '' self.cbar.set_label(self.units) print "Plotting %s field, Tilt %d" % (self.Vfield.value, self.Vtilt.value+1) self.canvas.draw() ######################### # Get and check methods # ######################### def _initialize_limits(self): field = self.Vfield.value if field == 'reflectivity': self.vminmax = (Z_LIMS[0], Z_LIMS[1]) self.CMAP = 'gist_ncar' elif field == 'DBZ' or field == 'DBZH': self.vminmax = (Z_LIMS[0], Z_LIMS[1]) self.CMAP = 'gist_ncar' elif field == 'velocity': self.vminmax = (VR_LIMS[0], VR_LIMS[1]) self.CMAP = 'RdBu_r' elif field == 'VEL': self.vminmax = (VR_LIMS[0], VR_LIMS[1]) self.CMAP = 'RdBu_r' elif field == 'differential_reflectivity': self.vminmax = (ZDR_LIMS[0], ZDR_LIMS[1]) self.CMAP = 'RdYlBu_r' elif field == 'cross_correlation_ratio': self.vminmax = (RHO_HV_LIMS[0], RHO_HV_LIMS[1]) self.CMAP = 'cool' elif field == 'differential_phase': self.vminmax = (KDP_LIMS[0], KDP_LIMS[1]) self.CMAP = 'YlOrBr' elif field == 'normalized_coherent_power': self.vminmax = (NCP_LIMS[0], NCP_LIMS[1]) self.CMAP = 'jet' elif field == 'spectrum_width': self.vminmax = (SW_LIMS[0], SW_LIMS[1]) self.CMAP = 'gist_ncar' elif field == 'specific_differential_phase': self.vminmax = (PHIDP_LIMS[0], PHIDP_LIMS[1]) self.CMAP = 'RdBu_r' elif field == 'total_power': self.vminmax = (TP_LIMS[0], TP_LIMS[1]) self.CMAP = 'jet' limit_strs = ('vmin', 'vmax', 'xmin', 'xmax', 'ymin', 'ymax') self.limits = {} # Now pull the default values self.limits['vmin'] = self.vminmax[0] self.limits['vmax'] = self.vminmax[1] self.limits['xmin'] = self.XRNG[0] self.limits['xmax'] = self.XRNG[1] self.limits['ymin'] = self.YRNG[0] self.limits['ymax'] = self.YRNG[1] # # def _build_cmap_dict(self): # # self.cmap_dict = {} # # self.cmap_dict['gist_ncar'] = matcm.get_cmap(name='gist_ncar') # # self.cmap_dict['RdBu_r'] = matcm.get_cmap(name='RdBu_r') # # self.cmap_dict['RdYlBu_r'] = matcm.get_cmap(name='RdYlBu_r # # self.cmap_dict['cool'] = matcm.get_cmap(name='cool # # self.cmap_dict['YlOrBr'] = matcm.get_cmap(name='YlOrBr # # self.cmap_dict['jet'] = matcm.get_cmap(name='jet # # self.cmap_dict[' # # self.cmap_dict[' def _check_default_field(self): '''Hack to perform a check on reflectivity to make it work with a larger number of files This should only occur upon start up with a new file''' if self.Vfield.value == 'reflectivity': if self.Vfield.value in self.fieldnames: pass elif 'CZ' in self.fieldnames: self.Vfield.change('CZ') elif 'DZ' in self.fieldnames: self.Vfield.change('DZ') elif 'dbz' in self.fieldnames: self.Vfield.change('dbz') elif 'DBZ' in self.fieldnames: self.Vfield.change('DBZ') elif 'dBZ' in self.fieldnames: self.Vfield.change('dBZ') elif 'Z' in self.fieldnames: self.Vfield.change('Z') elif 'DBZ_S'in self.fieldnames: self.Vfield.change('DBZ_S') elif 'reflectivity_horizontal'in self.fieldnames: self.Vfield.change('reflectivity_horizontal') def _check_file_type(self): '''Check file to see if the file is airborne or rhi''' if self.Vradar.value.scan_type != 'rhi': pass else: try: (self.Vradar.value.metadata['platform_type'] == 'aircraft_tail') or \ (self.Vradar.value.metadata['platform_type'] == 'aircraft') self.airborne = True except: self.rhi = True self._set_fig_ax_rhi() ######################## # Warning methods # ######################## def _ShowWarning(self, msg): '''Show a warning message''' flags = QtGui.QMessageBox.StandardButton() response = QtGui.QMessageBox.warning(self, "Warning!", msg, flags) if response == 0: print msg else: print "Warning Discarded!" ######################## # Image save methods # ######################## def _quick_savefile(self, PTYPE=IMAGE_EXT): '''Save the current display''' PNAME = self.display.generate_filename(self.Vfield.value, self.Vtilt.value, ext=IMAGE_EXT) print "Creating "+ PNAME def _savefile(self, PTYPE=IMAGE_EXT): PBNAME = self.display.generate_filename(self.Vfield.value, self.Vtilt.value, ext=IMAGE_EXT) file_choices = "PNG (*.png)|*.png" path = unicode(QtGui.QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=DPI) self.statusBar().showMessage('Saved to %s' % path)
class PointsDisplay(Component): ''' Class to create a display plot, using data from a Points instance. ''' Vpoints = None #: see :ref:`shared_variable` Vfield = None #: see :ref:`shared_variable` Vlims = None #: see :ref:`shared_variable` Vcmap = None #: see :ref:`shared_variable` @classmethod def guiStart(self, parent=None): '''Graphical interface for starting this class''' kwargs, independent = \ common._SimplePluginStart("PointsDisplay").startDisplay() kwargs['parent'] = parent return self(**kwargs), independent def __init__(self, Vpoints=None, Vfield=None, Vlims=None, Vcmap=None, plot_type="histogram", name="PointsDisplay", parent=None): ''' Initialize the class to create display. Parameters ---------- [Optional] Vpoints : :py:class:`~artview.core.core.Variable` instance Points signal variable. If None start new one with None. Vfield : :py:class:`~artview.core.core.Variable` instance Field signal variable. If None start new one with empty string. Vlims : :py:class:`~artview.core.core.Variable` instance Limits signal variable. A value of None will instantiate a limits variable. Vcmap : :py:class:`~artview.core.core.Variable` instance Colormap signal variable. A value of None will instantiate a colormap variable. plot_type : str Type of plot to produce (e.g. histogram, statistics, table). name : string Display window name. parent : PyQt instance Parent instance to associate to Display window. If None, then Qt owns, otherwise associated with parent PyQt instance. ''' super(PointsDisplay, self).__init__(name=name, parent=parent) self.setFocusPolicy(QtCore.Qt.ClickFocus) if Vpoints is None: self.Vpoints = Variable(None) else: self.Vpoints = Vpoints if Vfield is None: self.Vfield = Variable('') else: self.Vfield = Vfield if Vlims is None: self.Vlims = Variable({}) else: self.Vlims = Vlims if Vcmap is None: self.Vcmap = Variable(None) else: self.Vcmap = Vcmap self.sharedVariables = {"Vpoints": self.NewPoints, "Vfield": self.NewField, "Vlims": self.NewLims, "Vcmap": self.NewCmap, } # Connect the components self.connectAllVariables() # Set plot title and colorbar units to defaults self.title = self._get_default_title() self.units = self._get_default_units() # Find the PyArt colormap names self.cm_names = ["pyart_" + m for m in pyart.graph.cm.datad if not m.endswith("_r")] self.cm_names.sort() # Create a figure for output self._set_fig_ax() # Launch the GUI interface self.LaunchGUI() # Set up Default limits and cmap if Vcmap is None: self._set_default_cmap(strong=False) if Vlims is None: self._set_default_limits(strong=False) self.plot_type = None self.changePlotType(plot_type) self.show() #################### # GUI methods # #################### def LaunchGUI(self): '''Launches a GUI interface.''' # Create layout self.layout = QtGui.QVBoxLayout() # Create the widget self.central_widget = QtGui.QWidget() self.setCentralWidget(self.central_widget) self._set_figure_canvas() self.central_widget.setLayout(self.layout) # Add Menu self.addStandardMenu() # Set the status bar to display messages self.statusbar = self.statusBar() def addStandardMenu(self): '''Add Standard Menus.''' self.menubar = self.menuBar() self.filemenu = self.menubar.addMenu('File') openCSV = self.filemenu.addAction('Open Tabular Data') openCSV.setStatusTip('Open a Region Data CSV file') openCSV.triggered.connect(self.openTable) saveCSV = self.filemenu.addAction('Save Tabular Data') saveCSV.setStatusTip('Save a Region Data CSV file') saveCSV.triggered.connect(self.saveTable) self.plotTypeMenu = self.menubar.addMenu('Plot Type') hist = self.plotTypeMenu.addAction('Histogram') hist.setStatusTip('Plot histogram of Data ') hist.triggered.connect(lambda: self.changePlotType('histogram')) stats = self.plotTypeMenu.addAction('Statistics') stats.setStatusTip('Show basic statistics of Data') stats.triggered.connect(lambda: self.changePlotType('statistics')) table = self.plotTypeMenu.addAction('Table') table.setStatusTip('Show data in a Table') table.triggered.connect(lambda: self.changePlotType('table')) self.displayMenu = self.menubar.addMenu('Display') ################################## # User display interface methods # ################################## ############################# # Functionality methods # ############################# def changePlotType(self, plot_type): try: if self.plot_type == 'histogram': self.layout.removeWidget(self.canvas) self.canvas.hide() elif self.plot_type == 'statistics': self.layout.removeWidget(self.statistics) self.statistics.close() elif self.plot_type == 'table': self.layout.removeWidget(self.table) self.table.close() except: pass self.plot_type = plot_type self.displayMenu.clear() if plot_type == 'histogram': self._fill_histogram_menu() # Add the widget to the canvas self.layout.addWidget(self.canvas, 0) self.canvas.show() elif plot_type == 'statistics': pass elif plot_type == 'table': pass self._update_plot() def _open_LimsDialog(self): '''Open a dialog box to change display limits.''' from .limits import limits_dialog limits, cmap, change = limits_dialog( self.Vlims.value, self.Vcmap.value, self.name) if change == 1: self.Vcmap.change(cmap) self.Vlims.change(limits) def _title_input(self): '''Retrieve new plot title.''' val, entry = common.string_dialog_with_reset( self.title, "Plot Title", "Title:", self._get_default_title()) if entry is True: self.title = val self._update_plot() def _units_input(self): '''Retrieve new plot units.''' val, entry = common.string_dialog_with_reset( self.units, "Plot Units", "Units:", self._get_default_units()) if entry is True: self.units = val self._update_plot() def _fill_histogram_menu(self): '''Create the Display Options Button menu.''' self.dispButton = QtGui.QPushButton("Display Options") self.dispButton.setToolTip("Adjust display properties") self.dispButton.setFocusPolicy(QtCore.Qt.NoFocus) dispmenu = QtGui.QMenu(self) dispLimits = self.displayMenu.addAction("Adjust Display Limits") dispLimits.setToolTip("Set data, X, and Y range limits") # dispTitle = dispmenu.addAction("Change Title") # dispTitle.setToolTip("Change plot title") # dispUnit = dispmenu.addAction("Change Units") # dispUnit.setToolTip("Change units string") dispSaveFile = self.displayMenu.addAction("Save Image") dispSaveFile.setShortcut("Ctrl+S") dispSaveFile.setStatusTip("Save Image using dialog") # dispHelp = self.displayMenu.addAction("Help") dispLimits.triggered.connect(self._open_LimsDialog) # dispTitle.triggered.connect(self._title_input) # dispUnit.triggered.connect(self._units_input) dispSaveFile.triggered.connect(self._savefile) # dispHelp.triggered.connect(self._displayHelp) #XXX help is out dated def _displayHelp(self): text = ( "<b>Using the PlotPoints Display</b><br><br>" "<i>Purpose</i>:<br>" "Display a plot of selected points.<br><br>" "The limits dialog is a common format that allows the user " "change:<br>" "<i>X and Y limits<br>" "Data limits</i><br>" "However, not all plots take each argument.<br>" "For example, a simple line plot has no data min/max data " "value.<br>") common.ShowLongText(text, set_html=True) def NewPoints(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vradar <artview.core.core.Variable>`. This will: * Update fields and tilts lists and MenuBoxes * Check radar scan type and reset limits if needed * Reset units and title * If strong update: update plot ''' # test for None if self.Vpoints.value is None: # self.fieldBox.clear() return # Get field names self.fieldnames = self.Vpoints.value.fields.keys() # self._fillFieldBox() self.units = self._get_default_units() self.title = self._get_default_title() if strong: self._update_plot() # self._update_infolabel() def NewField(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vfield <artview.core.core.Variable>`. This will: * Reset colormap * Reset units * Update fields MenuBox * If strong update: update plot ''' self._set_default_cmap(strong=False) self.units = self._get_default_units() self.title = self._get_default_title() # idx = self.fieldBox.findText(value) # self.fieldBox.setCurrentIndex(idx) if strong: self._update_plot() # self._update_infolabel() def NewLims(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vlims <artview.core.core.Variable>`. This will: * If strong update: update axes ''' if strong: self._update_axes() def NewCmap(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vcmap <artview.core.core.Variable>`. This will: * If strong update: update plot ''' if strong: pass # self._update_plot() ######################## # Selectionion methods # ######################## #################### # Plotting methods # #################### def _set_fig_ax(self): '''Set the figure and axis to plot.''' self.XSIZE = 5 self.YSIZE = 5 self.fig = Figure(figsize=(self.XSIZE, self.YSIZE)) self.ax = self.fig.add_axes([0.2, 0.2, 0.7, 0.7]) def _set_figure_canvas(self): '''Set the figure canvas to draw in window area.''' self.canvas = FigureCanvasQTAgg(self.fig) def _update_plot(self): '''Draw/Redraw the plot.''' if self.Vpoints.value is None: return # Create the plot with PyArt PlotDisplay self.ax.cla() # Clear the plot axes # Reset to default title if user entered nothing w/ Title button colorbar_flag = False points = self.Vpoints.value field = self.Vfield.value cmap = self.Vcmap.value if field not in points.fields.keys(): self.canvas.draw() self.statusbar.setStyleSheet("QStatusBar{padding-left:8px;" + "background:rgba(255,0,0,255);" + "color:black;font-weight:bold;}") self.statusbar.showMessage("Field not Found", msecs=5000) return else: self.statusbar.setStyleSheet("QStatusBar{padding-left:8px;" + "background:rgba(0,0,0,0);" + "color:black;font-weight:bold;}") self.statusbar.clearMessage() if self.plot_type == "histogram": self.plot = self.ax.hist( points.fields[field]['data'], bins=25, range=(cmap['vmin'], cmap['vmax']), figure=self.fig) self.ax.set_ylabel("Counts") # If limits exists, update the axes otherwise retrieve # self._update_axes() self._update_limits() # If the colorbar flag is thrown, create it if colorbar_flag: # Clear the colorbar axes self.cax.cla() self.cax = self.fig.add_axes([0.2, 0.10, 0.7, 0.02]) norm = mlabNormalize(vmin=cmap['vmin'], vmax=cmap['vmax']) self.cbar = mlabColorbarBase(self.cax, cmap=self.cm_name, norm=norm, orientation='horizontal') # colorbar - use specified units or default depending on # what has or has not been entered self.cbar.set_label(self.units) self.canvas.draw() elif self.plot_type == 'statistics': if (self.Vpoints.value is None or self.Vfield.value not in self.Vpoints.value.fields): common.ShowWarning("Please select Region and Field first") else: points = self.Vpoints.value field = self.Vfield.value SelectRegionstats = common._array_stats( points.fields[field]['data']) text = "<b>Basic statistics for the selected Region</b><br><br>" for stat in SelectRegionstats: text += ("<i>%s</i>: %5.2f<br>" % (stat, SelectRegionstats[stat])) self.statistics = QtGui.QDialog() layout = QtGui.QGridLayout(self.statistics) self.statistics = QtGui.QTextEdit("") self.statistics.setAcceptRichText(True) self.statistics.setReadOnly(True) self.statistics.setText(text) self.layout.addWidget(self.statistics, 0) elif self.plot_type == "table": if self.Vpoints.value is not None: # Instantiate Table self.table = common.CreateTable(self.Vpoints.value) self.layout.addWidget(self.table, 0) self.table.display() # Show the table self.table.show() else: common.ShowWarning("Please select or open Region first") def _update_axes(self): '''Change the Plot Axes.''' limits = self.Vlims.value self.ax.set_xlim(limits['xmin'], limits['xmax']) self.ax.set_ylim(limits['ymin'], limits['ymax']) self.canvas.draw() def _update_limits(self): limits = self.Vlims.value ax = self.ax.get_xlim() limits['xmin'] = ax[0] limits['xmax'] = ax[1] ax = self.ax.get_ylim() limits['ymin'] = ax[0] limits['ymax'] = ax[1] self.Vlims.update() def _set_default_cmap(self, strong=True): ''' Set colormap to pre-defined default.''' cmap = pyart.config.get_field_colormap(self.Vfield.value) d = {} d['cmap'] = cmap lims = pyart.config.get_field_limits(self.Vfield.value, self.Vpoints.value) if lims != (None, None): d['vmin'] = lims[0] d['vmax'] = lims[1] else: d['vmin'] = -10 d['vmax'] = 65 self.Vcmap.change(d, False) def _set_default_limits(self, strong=True): ''' Set limits to pre-defined default.''' cmap = self.Vcmap.value d = {} d['xmin'] = cmap['vmin'] d['xmax'] = cmap['vmax'] d['ymin'] = 0 d['ymax'] = 1000 self.Vlims.change(d, False) def _get_default_title(self): '''Get default title.''' if (self.Vpoints.value is None or self.Vfield.value not in self.Vpoints.value.fields): return '' return 'Points Plot' # pyart.graph.common.generate_title(self.Vpoints.value, # self.Vfield.value, # 0) def _get_default_units(self): '''Get default units for current radar and field.''' if self.Vpoints.value is not None: try: return self.Vpoints.value.fields[self.Vfield.value]['units'] except: return '' else: return '' ######################## # Image save methods # ######################## def _savefile(self, PTYPE=IMAGE_EXT): '''Save the current display using PyQt dialog interface.''' file_choices = "PNG (*.png)|*.png" path = unicode(QtGui.QFileDialog.getSaveFileName( self, 'Save file', ' ', file_choices)) if path: self.canvas.print_figure(path, dpi=DPI) self.statusbar.showMessage('Saved to %s' % path) def openTable(self): '''Open a saved table of SelectRegion points from a CSV file.''' path = QtGui.QFileDialog.getOpenFileName( self, 'Open File', '', 'CSV(*.csv)') if path == '': return points = read_points_csv(path) self.Vpoints.change(points) def saveTable(self): '''Save a Table of SelectRegion points to a CSV file.''' points = self.Vpoints.value if points is not None: fsuggest = ('SelectRegion_' + self.Vfield.value + '_' + str(points.axes['x_disp']['data'][:].mean()) + '_' + str(points.axes['y_disp']['data'][:].mean())+'.csv') path = QtGui.QFileDialog.getSaveFileName( self, 'Save CSV Table File', fsuggest, 'CSV(*.csv)') if not path.isEmpty(): write_points_csv(path, points) else: common.ShowWarning("No gate selected, no data to save!")
class View(QWidget): def __init__(self, parent = None): super(View, self).__init__(parent) self.__watchdog = Watchdog.Watchdog() self.__watchdog.fileChanged.connect(self.refreshAll) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar = NavigationToolbar(self.canvas, self) self.navigationLayout = QHBoxLayout() layout = QHBoxLayout(self) self.navigations = [] self.addNavigation(True) self.filters = [ Filters.Lowpass(), Filters.Deconvolve(), Filters.Rotate() ] filterLayout = QVBoxLayout() for f in self.filters: filterLayout.addWidget(f) f.filterChanged.connect(self.plot) filterLayout.addStretch() addIcon = QIcon.fromTheme('list-add') addNaviButton = QPushButton(addIcon, 'Add navigation', self) addNaviButton.clicked.connect(self.addNavigation) self.maxFreq = QDoubleSpinBox(self) self.maxFreq.setValue(10.0) self.maxFreq.setVisible(False) self.maxFreq.valueChanged.connect(self.plot) spectrumIcon = QIcon.fromTheme('network-wireless') self.spectrum = QPushButton(spectrumIcon, 'Spectrum', self) self.spectrum.setCheckable(True) self.spectrum.clicked.connect(self.plot) self.spectrum.toggled.connect(self.maxFreq.setVisible) self.diff = QPushButton('Diff', self) self.diff.setCheckable(True) self.diff.clicked.connect(self.plot) self.diff.clicked.connect(self.spectrum.setHidden) self.spectrum.toggled.connect(self.diff.setHidden) autoRefresh = QPushButton(QIcon.fromTheme('view-refresh'), 'Auto', self) autoRefresh.setCheckable(True) autoRefresh.clicked.connect(self.__watchdog.toggle) saveAll = QPushButton(QIcon.fromTheme('document-save'), '', self) saveAll.clicked.connect(self.savePlots) toolLayout = QHBoxLayout() toolLayout.addWidget(addNaviButton) toolLayout.addWidget(self.diff) toolLayout.addWidget(self.spectrum) toolLayout.addWidget(self.maxFreq) toolLayout.addWidget(autoRefresh) toolLayout.addWidget(saveAll) toolLayout.addWidget(toolbar) plotLayout = QVBoxLayout() plotLayout.addLayout(toolLayout) plotLayout.addWidget(self.canvas) layout.addLayout(self.navigationLayout) layout.addLayout(plotLayout) layout.addLayout(filterLayout) def addNavigation(self, noclose = False): navigation = Navigation.Navigation(noclose) navigation.activeItemChanged.connect(self.plot) navigation.folderChanged.connect(self.navigationFolderChanged) navigation.close.connect(self.closeNavigation) navigation.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Minimum) self.navigationLayout.addWidget(navigation) self.navigations.append(navigation) def navigationFolderChanged(self, oldFolder, newFolder): self.__watchdog.removeFolder(oldFolder) self.__watchdog.addFolder(newFolder) def closeNavigation(self, widget): self.navigations.remove(widget) self.navigationLayout.removeWidget(widget) widget.deleteLater() self.plot() def refreshAll(self): for navigation in self.navigations: navigation.refreshFolder() def plot(self): wfc = [wf for nav in self.navigations for wf in nav.getActiveWaveforms()] for filt in self.filters: if filt.isChecked(): for wf in wfc: filt.apply(wf) if self.diff.isChecked() and len(wfc) > 0: wf0 = wfc.pop() for nWf, wf in enumerate(wfc): wfc[nWf].subtract(wf0) names = set([name for wf in wfc for name in wf.waveforms.keys()]) numPlots = len(names) self.figure.clear() if numPlots > 0: names = list(names) names.sort() numRows = math.ceil(math.sqrt(numPlots)); numCols = math.ceil(numPlots / numRows) subplots = dict() for i in range(len(names)): subplots[ names[i] ] = self.figure.add_subplot(numRows, numCols, i+1) for nWf, wf in enumerate(wfc): for name, waveform in wf.waveforms.items(): p = subplots[name] if self.spectrum.isChecked(): n = len(waveform) dt = wf.time[1]-wf.time[0] # assume equally spaced samples f = scipy.fftpack.fftfreq(n, dt) W = dt * scipy.fftpack.fft(waveform) maxFreqIndices = numpy.argwhere(f > self.maxFreq.value()) L = maxFreqIndices[0,0] if numpy.size(maxFreqIndices) > 0 else n/2 p.loglog(f[1:L], numpy.absolute(W[1:L]), label=str(nWf)) p.set_xlabel('f [Hz]') elif self.diff.isChecked(): p.plot(wf.time, waveform, label='{}-0'.format(nWf+1)) p.set_xlabel('t (s)') else: p.plot(wf.time, waveform, label=str(nWf)) p.set_xlabel('t (s)') p.set_ylabel(name) self.figure.tight_layout() for i in range(len(names)): subplots[ names[i] ].legend(prop={'size':8}, frameon=False) self.canvas.draw() def savePlots(self): filetypes = self.canvas.get_supported_filetypes_grouped() defaultFiletype = self.canvas.get_default_filetype() filters = [] selectedFilter = '' for name, extensions in sorted(filetypes.items()): filtr = '{0} ({1})'.format(name, ' '.join(['*.{0}'.format(ext) for ext in extensions])) if defaultFiletype in extensions: selectedFilter = filtr filters.append(filtr) fileName, filtr = QFileDialog.getSaveFileNameAndFilter(self, 'Choose a save location.', '', ';;'.join(filters), selectedFilter) fileName = os.path.splitext(str(fileName))[0] extension = re.search(r'\*(\.[a-zA-Z]+)', str(filtr)).group(1) maxRow = min([nav.numberOfRows() for nav in self.navigations]) for row in range(maxRow): for nav in self.navigations: nav.selectWaveformAt(row) self.plot() self.canvas.print_figure('{0}{1:03}{2}'.format(fileName, row+1, extension))
class PlotDisplay(Component): ''' Class to create a display plot, using data and a key for plot type. ''' def __init__(self, data, ydata=None, plot_type=None, title=None, xlabel=None, ylabel=None, name="PlotDisplay", parent=None): ''' Initialize the class to create display. Parameters ---------- data : data array The data to be plotted. [Optional] plot_type : str Type of plot to produce (e.g. plot, barplot, etc). name : string Display window name. parent : PyQt instance Parent instance to associate to Display window. If None, then Qt owns, otherwise associated with parent PyQt instance. Notes ----- This class records the selected button and passes the change value back to variable. ''' super(PlotDisplay, self).__init__(name=name, parent=parent) self.setFocusPolicy(QtCore.Qt.ClickFocus) self.data = data self.ydata = ydata self.plot_type = plot_type # Set plot title and colorbar units to defaults self.title = title self.xlabel = xlabel self.ylabel = ylabel self.units = None self.limits = None # Find the PyArt colormap names self.cm_names = ["pyart_" + m for m in pyart.graph.cm.datad if not m.endswith("_r")] self.cm_names.sort() # Create a figure for output self._set_fig_ax() # Launch the GUI interface self.LaunchGUI() # Create the plot self._update_plot() # self.NewRadar(None, None, True) self.show() #################### # GUI methods # #################### def LaunchGUI(self): '''Launches a GUI interface.''' # Create layout self.layout = QtGui.QGridLayout() self.layout.setSpacing(4) # Create the widget self.central_widget = QtGui.QWidget() self.setCentralWidget(self.central_widget) self._set_figure_canvas() self.central_widget.setLayout(self.layout) # Add buttons along display for user control self.addButtons() self.setUILayout() # Set the status bar to display messages self.statusbar = self.statusBar() ################################## # User display interface methods # ################################## def addButtons(self): '''Add a series of buttons for user control over display.''' # Create the Display controls self._add_displayBoxUI() def setUILayout(self): '''Setup the button/display UI layout.''' self.layout.addWidget(self.dispButton, 0, 1) ############################# # Functionality methods # ############################# def _open_LimsDialog(self): '''Open a dialog box to change display limits.''' from .limits import limits_dialog limits, cmap, change = limits_dialog(self.limits, self.cmap, self.name) if change == 1: self.cmap = cmap self.limits = limits self._update_plot() def _title_input(self): '''Retrieve new plot title.''' val, entry = common.string_dialog(self.title, "Plot Title", "Title:") if entry is True: self.title = val self._update_plot() def _units_input(self): '''Retrieve new plot units.''' val, entry = common.string_dialog(self.units, "Plot Units", "Units:") if entry is True: self.units = val self._update_plot() def _add_cmaps_to_button(self): '''Add a menu to change colormap used for plot.''' for cm_name in self.cm_names: cmapAction = self.dispCmapmenu.addAction(cm_name) cmapAction.setStatusTip("Use the %s colormap" % cm_name) cmapAction.triggered[()].connect( lambda cm_name=cm_name: self.cmapSelectCmd(cm_name)) self.dispCmap.setMenu(self.dispCmapmenu) def _add_displayBoxUI(self): '''Create the Display Options Button menu.''' self.dispButton = QtGui.QPushButton("Display Options") self.dispButton.setToolTip("Adjust display properties") self.dispButton.setFocusPolicy(QtCore.Qt.NoFocus) dispmenu = QtGui.QMenu(self) dispLimits = dispmenu.addAction("Adjust Display Limits") dispLimits.setToolTip("Set data, X, and Y range limits") dispTitle = dispmenu.addAction("Change Title") dispTitle.setToolTip("Change plot title") dispUnit = dispmenu.addAction("Change Units") dispUnit.setToolTip("Change units string") # toolZoomPan = dispmenu.addAction("Zoom/Pan") self.dispCmap = dispmenu.addAction("Change Colormap") self.dispCmapmenu = QtGui.QMenu("Change Cmap") self.dispCmapmenu.setFocusPolicy(QtCore.Qt.NoFocus) dispSaveFile = dispmenu.addAction("Save Image") dispSaveFile.setShortcut("Ctrl+S") dispSaveFile.setStatusTip("Save Image using dialog") self.dispHelp = dispmenu.addAction("Help") dispLimits.triggered[()].connect(self._open_LimsDialog) dispTitle.triggered[()].connect(self._title_input) dispUnit.triggered[()].connect(self._units_input) # toolZoomPan.triggered[()].connect(self.toolZoomPanCmd) dispSaveFile.triggered[()].connect(self._savefile) self.dispHelp.triggered[()].connect(self.displayHelp) self._add_cmaps_to_button() self.dispButton.setMenu(dispmenu) def displayHelp(self): text = ( "<b>Using the Simple Plot Feature</b><br><br>" "<i>Purpose</i>:<br>" "Display a plot.<br><br>" "The limits dialog is a common format that allows the user " "change:<br>" "<i>X and Y limits<br>" "Data limits</i><br>" "However, not all plots take each argument.<br>" "For example, a simple line plot has no data min/max data " "value.<br>") common.ShowLongText(text) ######################## # Selectionion methods # ######################## # def _create_plot(self): # ''' # Create a plot # ''' # # test for None # if self.Vradar.value is None: # self.fieldBox.clear() # self.tiltBox.clear() # return # # # Get the tilt angles # self.rTilts = self.Vradar.value.sweep_number['data'][:] # # Get field names # self.fieldnames = self.Vradar.value.fields.keys() # # # Check the file type and initialize limts # self._check_file_type() # # # Update field and tilt MenuBox # self._fillTiltBox() # self._fillFieldBox() # # self.units = None # if strong: # self._update_plot() # def NewLims(self, variable, value, strong): # ''' # Slot for 'ValueChanged' signal of # :py:class:`Vlims <artview.core.core.Variable>`. # # This will: # # * If strong update: update axes # ''' # if strong: # self._update_axes() # def NewCmap(self, variable, value, strong): # ''' # Slot for 'ValueChanged' signal of # :py:class:`Vcmap <artview.core.core.Variable>`. # # This will: # # * If strong update: update plot # ''' # if strong and self.Vradar.value is not None: # self._update_plot() def cmapSelectCmd(self, cm_name): '''Captures colormap selection and redraws.''' self.cmap['cmap'] = cm_name # self.Vcmap.value['cmap'] = cm_name # self.Vcmap.update() # def toolZoomPanCmd(self): # '''Creates and connects to a Zoom/Pan instance.''' # from .tools import ZoomPan # scale = 1.1 # self.tools['zoompan'] = ZoomPan( # self.limits, self.ax, # base_scale=scale, parent=self.parent) # self.tools['zoompan'].connect() #################### # Plotting methods # #################### def _set_fig_ax(self): '''Set the figure and axis to plot.''' self.XSIZE = 5 self.YSIZE = 5 self.fig = Figure(figsize=(self.XSIZE, self.YSIZE)) self.ax = self.fig.add_axes([0.2, 0.2, 0.7, 0.7]) # def _update_fig_ax(self): # '''Set the figure and axis to plot.''' # if self.plot_type in ("radarAirborne", "radarRhi"): # self.YSIZE = 5 # else: # self.YSIZE = 8 # xwidth = 0.7 # yheight = 0.7 * float(self.YSIZE) / float(self.XSIZE) # self.ax.set_position([0.2, 0.55-0.5*yheight, xwidth, yheight]) # self.cax.set_position([0.2, 0.10, xwidth, 0.02]) # self._update_axes() def _set_figure_canvas(self): '''Set the figure canvas to draw in window area.''' self.canvas = FigureCanvasQTAgg(self.fig) # Add the widget to the canvas self.layout.addWidget(self.canvas, 1, 0, 4, 3) def _update_plot(self): '''Draw/Redraw the plot.''' # Create the plot with PyArt PlotDisplay self.ax.cla() # Clear the plot axes # Reset to default title if user entered nothing w/ Title button if self.title == '': title = None else: title = self.title colorbar_flag = False self.cmap = {'vmin': self.data.min(), 'vmax': self.data.max(), 'cmap': 'pyart_RefDiff'} if self.plot_type == "hist": self.plot = self.ax.hist( self.data, bins=25, range=(self.cmap['vmin'], self.cmap['vmax']), figure=self.fig) self.ax.set_ylabel("Counts") if self.xlabel: self.ax.set_xlabel(self.xlabel) elif self.plot_type == "hist2d": # Check that y data was provided if self.ydata: y = self.ydata # Create Plot self.plot = self.ax.hist2d( self.data, y, bins=[25, 20], range=([self.cmap['vmin'], self.cmap['vmax']], [y.min(), y.max()]), cmap=self.cm_name, figure=self.fig) colorbar_flag = True elif self.plot_type == "plot": # Check that y data was provided if self.ydata: y = self.ydata # Create Plot self.plot = self.ax.plot(self.data, y, figure=self.fig) # Set the axis labels if arguments passed if self.xlabel: self.ax.set_xlabel(self.xlabel) if self.ylabel: self.ax.set_ylabel(self.ylabel) # If limits exists, update the axes otherwise retrieve if self.limits: self._update_axes() else: self._get_axes_limits() # Set the title if passed if title is not None: self.ax.set_title(title) # If the colorbar flag is thrown, create it if colorbar_flag: # Clear the colorbar axes self.cax.cla() self.cax = self.fig.add_axes([0.2, 0.10, 0.7, 0.02]) norm = mlabNormalize(vmin=self.cmap['vmin'], vmax=self.cmap['vmax']) self.cbar = mlabColorbarBase(self.cax, cmap=self.cm_name, norm=norm, orientation='horizontal') # colorbar - use specified units or default depending on # what has or has not been entered if self.units is None or self.units == '': self.units = '' self.cbar.set_label(self.units) self.canvas.draw() def _update_axes(self): '''Change the Plot Axes.''' self.ax.set_xlim(self.limits['xmin'], self.limits['xmax']) self.ax.set_ylim(self.limits['ymin'], self.limits['ymax']) self.ax.figure.canvas.draw() def _get_axes_limits(self): '''Get the axes limits''' xlim = self.ax.get_xlim() ylim = self.ax.get_ylim() self.limits = {} self.limits['xmin'] = xlim[0] self.limits['xmax'] = xlim[1] self.limits['ymin'] = ylim[0] self.limits['ymax'] = ylim[1] ######################## # Image save methods # ######################## def _savefile(self, PTYPE=IMAGE_EXT): '''Save the current display using PyQt dialog interface.''' file_choices = "PNG (*.png)|*.png" path = unicode(QtGui.QFileDialog.getSaveFileName( self, 'Save file', ' ', file_choices)) if path: self.canvas.print_figure(path, dpi=DPI) self.statusbar.showMessage('Saved to %s' % path)
class MplFigureCellWidget(QCellWidget): """ MplFigureCellWidget is the actual QWidget taking the FigureManager as a child for displaying figures """ save_formats = ["Portable Document Format (*.pdf)", "Portable Network Graphic (*.png)", "PostScript (*.ps *.eps)", "Raw images (*.raw *.rgba)", "Scalable Vector Graphics (*.svg *.svgz)"] def __init__(self, parent=None): """ MplFigureCellWidget(parent: QWidget) -> MplFigureCellWidget Initialize the widget with its central layout """ QCellWidget.__init__(self, parent) self.setFocusPolicy(QtCore.Qt.StrongFocus) centralLayout = QtGui.QVBoxLayout() self.setLayout(centralLayout) centralLayout.setMargin(0) centralLayout.setSpacing(0) self.canvas = None self.figure = None self.figManager = None self.toolBarType = MplFigureCellToolBar self.mplToolbar = None def updateContents(self, inputPorts): """ updateContents(inputPorts: tuple) -> None Update the widget contents based on the input data """ (fig, ) = inputPorts if not self.figure or self.figure.number != fig.figInstance.number: if self.layout().count() > 0: self.layout().removeWidget(self.canvas) self.figure = fig.figInstance self.canvas = FigureCanvasQTAgg(self.figure) self.mplToolbar = MplNavigationToolbar(self.canvas, None) self.canvas.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) self.layout().addWidget(self.canvas) def keyPressEvent(self, event): print "KEY PRESS:", event.key() self.canvas.keyPressEvent(event) def keyReleaseEvent(self, event): print "KEY RELEASE:", event.key() self.canvas.keyReleaseEvent(event) def deleteLater(self): """ deleteLater() -> None Overriding PyQt deleteLater to free up resources """ # Destroy the old one if possible if self.figure is not None: print "pylab:", pylab print "self.figure:", self.figure pylab.close(self.figure) QCellWidget.deleteLater(self) def grabWindowPixmap(self): """ grabWindowPixmap() -> QPixmap Widget special grabbing function """ return QtGui.QPixmap.grabWidget(self.canvas) def dumpToFile(self, filename): previous_size = tuple(self.figure.get_size_inches()) self.figure.set_size_inches(8.0,6.0) self.canvas.print_figure(filename) self.figure.set_size_inches(previous_size[0],previous_size[1]) self.canvas.draw() def saveToPDF(self, filename): previous_size = tuple(self.figure.get_size_inches()) self.figure.set_size_inches(8.0,6.0) self.canvas.print_figure(filename) self.figure.set_size_inches(previous_size[0],previous_size[1]) self.canvas.draw()
class MplFigureCellWidget(QCellWidget): """ MplFigureCellWidget is the actual QWidget taking the FigureManager as a child for displaying figures """ save_formats = ["Portable Document Format (*.pdf)", "Portable Network Graphic (*.png)", "PostScript (*.ps *.eps)", "Raw images (*.raw *.rgba)", "Scalable Vector Graphics (*.svg *.svgz)"] def __init__(self, parent=None): """ MplFigureCellWidget(parent: QWidget) -> MplFigureCellWidget Initialize the widget with its central layout """ QCellWidget.__init__(self, parent) self.setFocusPolicy(QtCore.Qt.StrongFocus) centralLayout = QtGui.QVBoxLayout() self.setLayout(centralLayout) centralLayout.setMargin(0) centralLayout.setSpacing(0) self.canvas = None self.figure = None self.figManager = None self.toolBarType = MplFigureCellToolBar self.mplToolbar = None def updateContents(self, inputPorts): """ updateContents(inputPorts: tuple) -> None Update the widget contents based on the input data """ (figInstance, ) = inputPorts if not self.figure or self.figure.number != figInstance.number: if self.layout().count() > 0: self.layout().removeWidget(self.canvas) self.figure = figInstance self.canvas = FigureCanvasQTAgg(self.figure) self.mplToolbar = MplNavigationToolbar(self.canvas, None) self.canvas.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) self.layout().addWidget(self.canvas) def keyPressEvent(self, event): self.canvas.keyPressEvent(event) def keyReleaseEvent(self, event): self.canvas.keyReleaseEvent(event) def deleteLater(self): """ deleteLater() -> None Overriding PyQt deleteLater to free up resources """ # Destroy the old one if possible if self.figure is not None: pylab.close(self.figure) QCellWidget.deleteLater(self) def grabWindowPixmap(self): """ grabWindowPixmap() -> QPixmap Widget special grabbing function """ return QtGui.QPixmap.grabWidget(self.canvas) def dumpToFile(self, filename): previous_size = tuple(self.figure.get_size_inches()) self.figure.set_size_inches(8.0,6.0) self.canvas.print_figure(filename) self.figure.set_size_inches(previous_size[0],previous_size[1]) self.canvas.draw() def saveToPDF(self, filename): previous_size = tuple(self.figure.get_size_inches()) self.figure.set_size_inches(8.0,6.0) self.canvas.print_figure(filename) self.figure.set_size_inches(previous_size[0],previous_size[1]) self.canvas.draw()
class RadarDisplay(Component): ''' Class to create a display plot, using a returned Radar structure from the PyArt pyart.graph package. ''' Vradar = None # : see :ref:`shared_variable` Vfield = None # : see :ref:`shared_variable` Vtilt = None # : see :ref:`shared_variable` Vlims = None # : see :ref:`shared_variable` Vcmap = None # : see :ref:`shared_variable` @classmethod def guiStart(self, parent=None): '''Graphical interface for starting this class''' args = _DisplayStart().startDisplay() return self(**args), True def __init__(self, Vradar, Vfield, Vtilt, Vlims=None, Vcmap=None, name="RadarDisplay", parent=None): ''' Initialize the class to create display. Parameters ---------- Vradar : :py:class:`~artview.core.core.Variable` instance Radar signal variable. Vfield : :py:class:`~artview.core.core.Variable` instance Field signal variable. Vtilt : :py:class:`~artview.core.core.Variable` instance Tilt signal variable. [Optional] Vlims : :py:class:`~artview.core.core.Variable` instance Limits signal variable. A value of None will instantiate a limits variable. Vcmap : :py:class:`~artview.core.core.Variable` instance Colormap signal variable. A value of None will instantiate a colormap variable. name : string Display window name. parent : PyQt instance Parent instance to associate to Display window. If None, then Qt owns, otherwise associated with parent PyQt instance. Notes ----- This class records the selected button and passes the change value back to variable. ''' super(RadarDisplay, self).__init__(name=name, parent=parent) self.setFocusPolicy(QtCore.Qt.ClickFocus) # Set up signal, so that DISPLAY can react to # external (or internal) changes in radar, field, # lims and tilt (expected to be Core.Variable instances) # The capital V so people remember using ".value" self.Vradar = Vradar self.Vfield = Vfield self.Vtilt = Vtilt if Vlims is None: self.Vlims = Variable(None) else: self.Vlims = Vlims if Vcmap is None: self.Vcmap = Variable(None) else: self.Vcmap = Vcmap self.sharedVariables = {"Vradar": self.NewRadar, "Vfield": self.NewField, "Vtilt": self.NewTilt, "Vlims": self.NewLims, "Vcmap": self.NewCmap, } # Connect the components self.connectAllVariables() self.plot_type = None # Set plot title and colorbar units to defaults self.title = None self.units = None # Set the default range rings self.RngRingList = ["None", "10 km", "20 km", "30 km", "50 km", "100 km"] self.RngRing = False # Find the PyArt colormap names # self.cm_names = [m for m in cm.datad if not m.endswith("_r")] self.cm_names = ["pyart_" + m for m in pyart.graph.cm.datad if not m.endswith("_r")] self.cm_names.sort() # Create tool dictionary self.tools = {} # Set up Default limits and cmap if Vlims is None: self._set_default_limits(strong=False) if Vcmap is None: self._set_default_cmap(strong=False) # Create a figure for output self._set_fig_ax() # Launch the GUI interface self.LaunchGUI() # Initialize radar variable self.NewRadar(None, None, True) self.show() def keyPressEvent(self, event): '''Allow tilt adjustment via the Up-Down arrow keys.''' if event.key() == QtCore.Qt.Key_Up: self.TiltSelectCmd(self.Vtilt.value + 1) elif event.key() == QtCore.Qt.Key_Down: self.TiltSelectCmd(self.Vtilt.value - 1) else: super(RadarDisplay, self).keyPressEvent(event) #################### # GUI methods # #################### def LaunchGUI(self): '''Launches a GUI interface.''' # Create layout self.layout = QtGui.QGridLayout() self.layout.setSpacing(8) # Create the widget self.central_widget = QtGui.QWidget() self.setCentralWidget(self.central_widget) self._set_figure_canvas() self.central_widget.setLayout(self.layout) # Add buttons along display for user control self.addButtons() self.setUILayout() # Set the status bar to display messages self.statusbar = self.statusBar() ################################## # User display interface methods # ################################## def addButtons(self): '''Add a series of buttons for user control over display.''' # Create the Display controls self._add_displayBoxUI() # Create the Tilt controls self._add_tiltBoxUI() # Create the Field controls self._add_fieldBoxUI() # Create the Tools controls self._add_toolsBoxUI() # Create the Informational label at top self._add_infolabel() def setUILayout(self): '''Setup the button/display UI layout.''' self.layout.addWidget(self.tiltBox, 0, 0) self.layout.addWidget(self.fieldBox, 0, 1) self.layout.addWidget(self.dispButton, 0, 2) self.layout.addWidget(self.toolsButton, 0, 3) self.layout.addWidget(self.infolabel, 0, 4) ############################# # Functionality methods # ############################# def _open_LimsDialog(self): '''Open a dialog box to change display limits.''' from .limits import limits_dialog limits, cmap, change = limits_dialog( self.Vlims.value, self.Vcmap.value, self.name) if change == 1: self.Vcmap.change(cmap) self.Vlims.change(limits) def _fillTiltBox(self): '''Fill in the Tilt Window Box with current elevation angles.''' self.tiltBox.clear() self.tiltBox.addItem("Tilt Window") # Loop through and create each tilt button elevs = self.Vradar.value.fixed_angle['data'][:] for i, ntilt in enumerate(self.rTilts): btntxt = "%2.1f deg (Tilt %d)" % (elevs[i], i+1) self.tiltBox.addItem(btntxt) def _fillFieldBox(self): '''Fill in the Field Window Box with current variable names.''' self.fieldBox.clear() self.fieldBox.addItem("Field Window") # Loop through and create each field button for field in self.fieldnames: self.fieldBox.addItem(field) def _tiltAction(self, text): '''Define action for Tilt Button selection.''' if text == "Tilt Window": self._open_tiltbuttonwindow() else: ntilt = int(text.split("(Tilt ")[1][:-1])-1 self.TiltSelectCmd(ntilt) def _fieldAction(self, text): '''Define action for Field Button selection.''' if text == "Field Window": self._open_fieldbuttonwindow() else: self.FieldSelectCmd(str(text)) def _title_input(self): '''Retrieve new plot title.''' val, entry = common.string_dialog(self.title, "Plot Title", "Title:") if entry is True: self.title = val self._update_plot() def _units_input(self): '''Retrieve new plot units.''' val, entry = common.string_dialog(self.units, "Plot Units", "Units:") if entry is True: self.units = val self._update_plot() def _open_tiltbuttonwindow(self): '''Open a TiltButtonWindow instance.''' from .level import LevelButtonWindow self.tiltbuttonwindow = LevelButtonWindow( self.Vtilt, plot_type=self.plot_type, Vcontainer=self.Vradar, name=self.name+" Tilt Selection", parent=self.parent) def _open_fieldbuttonwindow(self): '''Open a FieldButtonWindow instance.''' from .field import FieldButtonWindow self.fieldbuttonwindow = FieldButtonWindow( self.Vradar, self.Vfield, name=self.name+" Field Selection", parent=self.parent) def _add_RngRing_to_button(self): '''Add a menu to display range rings on plot.''' for RngRing in self.RngRingList: RingAction = self.dispRngRingmenu.addAction(RngRing) RingAction.setStatusTip("Apply Range Rings every %s" % RngRing) RingAction.triggered[()].connect( lambda RngRing=RngRing: self.RngRingSelectCmd(RngRing)) self.dispRngRing.setMenu(self.dispRngRingmenu) def _add_cmaps_to_button(self): '''Add a menu to change colormap used for plot.''' for cm_name in self.cm_names: cmapAction = self.dispCmapmenu.addAction(cm_name) cmapAction.setStatusTip("Use the %s colormap" % cm_name) cmapAction.triggered[()].connect( lambda cm_name=cm_name: self.cmapSelectCmd(cm_name)) self.dispCmap.setMenu(self.dispCmapmenu) def _add_displayBoxUI(self): '''Create the Display Options Button menu.''' self.dispButton = QtGui.QPushButton("Display Options") self.dispButton.setToolTip("Adjust display properties") self.dispButton.setFocusPolicy(QtCore.Qt.NoFocus) dispmenu = QtGui.QMenu(self) dispLimits = dispmenu.addAction("Adjust Display Limits") dispLimits.setToolTip("Set data, X, and Y range limits") dispTitle = dispmenu.addAction("Change Title") dispTitle.setToolTip("Change plot title") dispUnit = dispmenu.addAction("Change Units") dispUnit.setToolTip("Change units string") self.dispRngRing = dispmenu.addAction("Add Range Rings") self.dispRngRingmenu = QtGui.QMenu("Add Range Rings") self.dispRngRingmenu.setFocusPolicy(QtCore.Qt.NoFocus) self.dispCmap = dispmenu.addAction("Change Colormap") self.dispCmapmenu = QtGui.QMenu("Change Cmap") self.dispCmapmenu.setFocusPolicy(QtCore.Qt.NoFocus) dispQuickSave = dispmenu.addAction("Quick Save Image") dispQuickSave.setShortcut("Ctrl+D") dispQuickSave.setToolTip( "Save Image to local directory with default name") dispSaveFile = dispmenu.addAction("Save Image") dispSaveFile.setShortcut("Ctrl+S") dispSaveFile.setStatusTip("Save Image using dialog") dispLimits.triggered[()].connect(self._open_LimsDialog) dispTitle.triggered[()].connect(self._title_input) dispUnit.triggered[()].connect(self._units_input) dispQuickSave.triggered[()].connect(self._quick_savefile) dispSaveFile.triggered[()].connect(self._savefile) self._add_RngRing_to_button() self._add_cmaps_to_button() self.dispButton.setMenu(dispmenu) def _add_tiltBoxUI(self): '''Create the Tilt Selection ComboBox.''' self.tiltBox = QtGui.QComboBox() self.tiltBox.setFocusPolicy(QtCore.Qt.NoFocus) self.tiltBox.setToolTip("Select tilt elevation angle to display.\n" "'Tilt Window' will launch popup.\n" "Up/Down arrow keys Increase/Decrease tilt.") self.tiltBox.activated[str].connect(self._tiltAction) def _add_fieldBoxUI(self): '''Create the Field Selection ComboBox.''' self.fieldBox = QtGui.QComboBox() self.fieldBox.setFocusPolicy(QtCore.Qt.NoFocus) self.fieldBox.setToolTip("Select variable/field in data file.\n" "'Field Window' will launch popup.\n") self.fieldBox.activated[str].connect(self._fieldAction) def _add_toolsBoxUI(self): '''Create the Tools Button menu.''' self.toolsButton = QtGui.QPushButton("Toolbox") self.toolsButton.setFocusPolicy(QtCore.Qt.NoFocus) self.toolsButton.setToolTip("Choose a tool to apply") toolmenu = QtGui.QMenu(self) toolZoomPan = toolmenu.addAction("Zoom/Pan") toolValueClick = toolmenu.addAction("Click for Value") toolSelectRegion = toolmenu.addAction("Select a Region of Interest") toolCustom = toolmenu.addAction("Use Custom Tool") toolReset = toolmenu.addAction("Reset Tools") toolDefault = toolmenu.addAction("Reset File Defaults") toolZoomPan.triggered[()].connect(self.toolZoomPanCmd) toolValueClick.triggered[()].connect(self.toolValueClickCmd) toolSelectRegion.triggered[()].connect(self.toolSelectRegionCmd) toolCustom.triggered[()].connect(self.toolCustomCmd) toolReset.triggered[()].connect(self.toolResetCmd) toolDefault.triggered[()].connect(self.toolDefaultCmd) self.toolsButton.setMenu(toolmenu) def _add_infolabel(self): '''Create an information label about the display''' self.infolabel = QtGui.QLabel("Radar: \n" "Field: \n" "Tilt: ", self) self.infolabel.setStyleSheet('color: red; font: italic 10px') self.infolabel.setToolTip("Filename not loaded") def _update_infolabel(self): self.infolabel.setText("Radar: %s\n" "Field: %s\n" "Tilt: %d" % ( self.Vradar.value.metadata[ 'instrument_name'], self.Vfield.value, self.Vtilt.value+1)) if hasattr(self.Vradar.value, 'filename'): self.infolabel.setToolTip(self.Vradar.value.filename) ######################## # Selectionion methods # ######################## def NewRadar(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vradar <artview.core.core.Variable>`. This will: * Update fields and tilts lists and MenuBoxes * Check radar scan type and reset limits if needed * Reset units and title * If strong update: update plot ''' # test for None if self.Vradar.value is None: self.fieldBox.clear() self.tiltBox.clear() return # Get the tilt angles self.rTilts = self.Vradar.value.sweep_number['data'][:] # Get field names self.fieldnames = self.Vradar.value.fields.keys() # Check the file type and initialize limts self._check_file_type() # Update field and tilt MenuBox self._fillTiltBox() self._fillFieldBox() self.units = None self.title = None if strong: self._update_plot() self._update_infolabel() def NewField(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vfield <artview.core.core.Variable>`. This will: * Reset colormap * Reset units * Update fields MenuBox * If strong update: update plot ''' self._set_default_cmap(strong=False) self.units = None idx = self.fieldBox.findText(value) self.fieldBox.setCurrentIndex(idx) if strong and self.Vradar.value is not None: self._update_plot() self._update_infolabel() def NewLims(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vlims <artview.core.core.Variable>`. This will: * If strong update: update axes ''' if strong: self._update_axes() def NewCmap(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vcmap <artview.core.core.Variable>`. This will: * If strong update: update plot ''' if strong and self.Vradar.value is not None: self._update_plot() def NewTilt(self, variable, value, strong): ''' Slot for 'ValueChanged' signal of :py:class:`Vtilt <artview.core.core.Variable>`. This will: * Update tilt MenuBox * If strong update: update plot ''' # +1 since the first one is "Tilt Window" self.tiltBox.setCurrentIndex(value+1) if strong and self.Vradar.value is not None: self._update_plot() self._update_infolabel() def TiltSelectCmd(self, ntilt): ''' Captures tilt selection and update tilt :py:class:`~artview.core.core.Variable`. ''' if ntilt < 0: ntilt = len(self.rTilts)-1 elif ntilt >= len(self.rTilts): ntilt = 0 self.Vtilt.change(ntilt) def FieldSelectCmd(self, name): ''' Captures field selection and update field :py:class:`~artview.core.core.Variable`. ''' self.Vfield.change(name) def RngRingSelectCmd(self, ringSel): ''' Captures Range Ring selection and redraws the field with range rings. ''' if ringSel is "None": self.RngRing = False else: self.RngRing = True # Find the unambigous range of the radar try: unrng = int(self.Vradar.value.instrument_parameters[ 'unambiguous_range']['data'][0]/1000) except: unrng = int(self.Vlims.value['xmax']) # Set the step if ringSel == '10 km': ringdel = 10 if ringSel == '20 km': ringdel = 20 if ringSel == '30 km': ringdel = 30 if ringSel == '50 km': ringdel = 50 if ringSel == '100 km': ringdel = 100 # Calculate an array of range rings self.RNG_RINGS = range(ringdel, unrng, ringdel) if self.Vradar.value is not None: self._update_plot() def cmapSelectCmd(self, cm_name): '''Captures colormap selection and redraws.''' CMAP = cm_name self.Vcmap.value['cmap'] = cm_name self.Vcmap.change(self.Vcmap.value) def toolZoomPanCmd(self): '''Creates and connects to a Zoom/Pan instance.''' from .tools import ZoomPan scale = 1.1 self.tools['zoompan'] = ZoomPan( self.Vlims, self.ax, base_scale=scale, parent=self.parent) self.tools['zoompan'].connect() def toolValueClickCmd(self): '''Creates and connects to Point-and-click value retrieval''' from .tools import ValueClick self.tools['valueclick'] = ValueClick( self.Vradar, self.Vtilt, self.Vfield, self.units, self.ax, self.statusbar, parent=self.parent) self.tools['valueclick'].connect() def toolSelectRegionCmd(self): '''Creates and connects to Region of Interest instance''' from .select_region import SelectRegion self.tools['select_region'] = SelectRegion( self, name=self.name + " SelectRegion", parent=self) def toolCustomCmd(self): '''Allow user to activate self-defined tool.''' from . import tools tools.custom_tool(self.tools) def toolResetCmd(self): '''Reset tools via disconnect.''' from . import tools self.tools = tools.reset_tools(self.tools) def toolDefaultCmd(self): '''Restore the Display defaults.''' from . import tools self.tools, limits, cmap = tools.restore_default_display( self.tools, self.Vfield.value, self.plot_type) self.Vcmap.change(cmap) self.Vlims.change(limits) def getPathInteriorValues(self, path): ''' Return the bins values path. Parameters ---------- path : Matplotlib Path instance Returns ------- x, y, azi, range, value, ray_idx, range_inx: ndarray Truplet of 1arrays containing x,y coordinate, azimuth, range, current field value, ray index and range index for all bin of the current radar and tilt inside path. Notes ----- If Vradar.value is None, returns None ''' from .tools import interior_radar radar = self.Vradar.value if radar is None: return (np.array([]),)*7 xy, idx = interior_radar(path, radar, self.Vtilt.value) aux = (xy[:, 0], xy[:, 1], radar.azimuth['data'][idx[:, 0]], radar.range['data'][idx[:, 1]] / 1000., radar.fields[self.Vfield.value]['data'][idx[:, 0], idx[:, 1]], idx[:, 0], idx[:, 1]) return aux #################### # Plotting methods # #################### def _set_fig_ax(self): '''Set the figure and axis to plot.''' self.XSIZE = 8 self.YSIZE = 8 self.fig = Figure(figsize=(self.XSIZE, self.YSIZE)) self.ax = self.fig.add_axes([0.2, 0.2, 0.7, 0.7]) self.cax = self.fig.add_axes([0.2, 0.10, 0.7, 0.02]) # self._update_axes() def _update_fig_ax(self): '''Set the figure and axis to plot.''' if self.plot_type in ("radarAirborne", "radarRhi"): self.YSIZE = 5 else: self.YSIZE = 8 xwidth = 0.7 yheight = 0.7 # * float(self.YSIZE) / float(self.XSIZE) self.ax.set_position([0.2, 0.55-0.5*yheight, xwidth, yheight]) self.cax.set_position([0.2, 0.10, xwidth, 0.02]) self._update_axes() def _set_figure_canvas(self): '''Set the figure canvas to draw in window area.''' self.canvas = FigureCanvasQTAgg(self.fig) # Add the widget to the canvas self.layout.addWidget(self.canvas, 1, 0, 7, 6) def _update_plot(self): '''Draw/Redraw the plot.''' # Create the plot with PyArt RadarDisplay self.ax.cla() # Clear the plot axes self.cax.cla() # Clear the colorbar axes if self.Vfield.value not in self.Vradar.value.fields.keys(): self.canvas.draw() self.statusbar.setStyleSheet("QStatusBar{padding-left:8px;" + "background:rgba(255,0,0,255);" + "color:black;font-weight:bold;}") self.statusbar.showMessage("Field not Found in Radar", msecs=5000) return else: self.statusbar.setStyleSheet("QStatusBar{padding-left:8px;" + "background:rgba(0,0,0,0);" + "color:black;font-weight:bold;}") self.statusbar.clearMessage() # Reset to default title if user entered nothing w/ Title button if self.title == '': title = None else: title = self.title limits = self.Vlims.value cmap = self.Vcmap.value if self.plot_type == "radarAirborne": self.display = pyart.graph.RadarDisplay_Airborne(self.Vradar.value) self.plot = self.display.plot_sweep_grid( self.Vfield.value, vmin=cmap['vmin'], vmax=cmap['vmax'], colorbar_flag=False, cmap=cmap['cmap'], ax=self.ax, fig=self.fig, title=title) self.display.plot_grid_lines() elif self.plot_type == "radarPpi": self.display = pyart.graph.RadarDisplay(self.Vradar.value) # Create Plot self.plot = self.display.plot_ppi( self.Vfield.value, self.Vtilt.value, vmin=cmap['vmin'], vmax=cmap['vmax'], colorbar_flag=False, cmap=cmap['cmap'], ax=self.ax, fig=self.fig, title=self.title) # Add range rings if self.RngRing: self.display.plot_range_rings(self.RNG_RINGS, ax=self.ax) # Add radar location self.display.plot_cross_hair(5., ax=self.ax) elif self.plot_type == "radarRhi": self.display = pyart.graph.RadarDisplay(self.Vradar.value) # Create Plot self.plot = self.display.plot_rhi( self.Vfield.value, self.Vtilt.value, vmin=cmap['vmin'], vmax=cmap['vmax'], colorbar_flag=False, cmap=cmap['cmap'], ax=self.ax, fig=self.fig, title=self.title) # Add range rings if self.RngRing: self.display.plot_range_rings(self.RNG_RINGS, ax=self.ax) self._update_axes() norm = mlabNormalize(vmin=cmap['vmin'], vmax=cmap['vmax']) self.cbar = mlabColorbarBase(self.cax, cmap=cmap['cmap'], norm=norm, orientation='horizontal') # colorbar - use specified units or default depending on # what has or has not been entered if self.units is None or self.units == '': try: self.units = self.Vradar.value.fields[self.field]['units'] except: self.units = '' self.cbar.set_label(self.units) # print "Plotting %s field, Tilt %d in %s" % ( # self.Vfield.value, self.Vtilt.value+1, self.name) self.canvas.draw() def _update_axes(self): '''Change the Plot Axes.''' limits = self.Vlims.value self.ax.set_xlim(limits['xmin'], limits['xmax']) self.ax.set_ylim(limits['ymin'], limits['ymax']) self.ax.figure.canvas.draw() ######################### # Check methods # ######################### def _check_file_type(self): '''Check file to see if the file is airborne or rhi.''' radar = self.Vradar.value old_plot_type = self.plot_type if radar.scan_type != 'rhi': self.plot_type = "radarPpi" else: if 'platform_type' in radar.metadata: if (radar.metadata['platform_type'] == 'aircraft_tail' or radar.metadata['platform_type'] == 'aircraft'): self.plot_type = "radarAirborne" else: self.plot_type = "radarRhi" else: self.plot_type = "radarRhi" if self.plot_type != old_plot_type: print("Changed Scan types, reinitializing") self._set_default_limits() self._update_fig_ax() def _set_default_limits(self, strong=True): ''' Set limits to pre-defined default.''' from .limits import _default_limits limits, cmap = _default_limits( self.Vfield.value, self.plot_type) self.Vlims.change(limits, strong) def _set_default_cmap(self, strong=True): ''' Set colormap to pre-defined default.''' from .limits import _default_limits limits, cmap = _default_limits( self.Vfield.value, self.plot_type) self.Vcmap.change(cmap, strong) ######################## # Image save methods # ######################## def _quick_savefile(self, PTYPE=IMAGE_EXT): '''Save the current display via PyArt interface.''' imagename = self.display.generate_filename( self.Vfield.value, self.Vtilt.value, ext=IMAGE_EXT) self.canvas.print_figure(os.path.join(os.getcwd(), imagename), dpi=DPI) self.statusbar.showMessage( 'Saved to %s' % os.path.join(os.getcwd(), imagename)) def _savefile(self, PTYPE=IMAGE_EXT): '''Save the current display using PyQt dialog interface.''' PBNAME = self.display.generate_filename( self.Vfield.value, self.Vtilt.value, ext=IMAGE_EXT) file_choices = "PNG (*.png)|*.png" path = unicode(QtGui.QFileDialog.getSaveFileName( self, 'Save file', PBNAME, file_choices)) if path: self.canvas.print_figure(path, dpi=DPI) self.statusbar.showMessage('Saved to %s' % path) ######################## # get methods # ######################## def getPlotAxis(self): ''' get :py:class:`matplotlib.axes.Axes` instance of main plot ''' return self.ax def getStatusBar(self): ''' get :py:class:`PyQt4.QtGui.QStatusBar` instance''' return self.statusbar def getField(self): ''' get current field ''' return self.Vfield.value def getUnits(self): ''' get current units ''' return self.units
class MainWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle("PyQt & matplotlib Test") self.createActions() self.createMenus() self.createMainFrame() self.createStatusBar() self.onDraw() def createActions(self): self.saveFileAction=QAction("&Save Plot", self, shortcut="Ctrl+S", triggered=self.savePlot, toolTip="Save the plot") self.quitAction=QAction("&Quit", self, shortcut="Ctrl+Q", triggered=self.close, toolTip="Close the application") self.aboutAction=QAction("&About", self, shortcut="F1", triggered=self.onAbout, toolTip="About this test") def createMenus(self): self.fileMenu=self.menuBar().addMenu("&File") self.fileMenu.addAction(self.saveFileAction) self.fileMenu.addSeparator() self.fileMenu.addAction(self.quitAction) self.helpMenu=self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.aboutAction) def createMainFrame(self): mainWidget=QWidget(self) QVBoxLayout(mainWidget) # matplotlib widget setup self.figure=Figure((5.0, 4.0), dpi=100) self.canvas=FigureCanvas(self.figure) self.canvas.setParent(mainWidget) mainWidget.layout().addWidget(self.canvas) self.axes=self.figure.add_subplot(111) self.canvas.mpl_connect('pick_event', self.onPick) self.canvas.mpl_connect('motion_notify_event', self.onMove) self.mplToolbar=NavToolbar(self.canvas, mainWidget) mainWidget.layout().addWidget(self.mplToolbar) # other widget setup controlLayout=QHBoxLayout() self.textbox=QLineEdit('1 2 3 4', editingFinished=self.onDraw, minimumWidth=200) controlLayout.addWidget(self.textbox, Qt.AlignVCenter) drawButton=QPushButton('&Draw', clicked=self.onDraw) controlLayout.addWidget(drawButton, Qt.AlignVCenter) self.showGrid=QCheckBox("Show &Grid", checked=False, stateChanged=self.onDraw) controlLayout.addWidget(self.showGrid, Qt.AlignVCenter) controlLayout.addWidget(QLabel("Bar Width (%)"), Qt.AlignVCenter) self.slider=QSlider(Qt.Horizontal, minimum=1, maximum=100, value=20, tracking=True, tickPosition=QSlider.TicksBothSides, valueChanged=self.onDraw) controlLayout.addWidget(self.slider, Qt.AlignVCenter) mainWidget.layout().addLayout(controlLayout) # test layout 1 testLayout1=QHBoxLayout() testLayout1.addWidget(QLabel("x:", self)) self.xValue=QLineEdit("0", self) testLayout1.addWidget(self.xValue) testLayout1.addWidget(QLabel("y:", self)) self.yValue=QLineEdit("0", self) testLayout1.addWidget(self.yValue) testLayout1.addWidget(QLabel("pixels")) mainWidget.layout().addLayout(testLayout1) self.setCentralWidget(mainWidget) # test layout 2 testLayout2=QHBoxLayout() testLayout2.addWidget(QLabel("x:", self)) self.xdValue=QLineEdit("0", self) testLayout2.addWidget(self.xdValue) testLayout2.addWidget(QLabel("y:", self)) self.ydValue=QLineEdit("0", self) testLayout2.addWidget(self.ydValue) testLayout2.addWidget(QLabel("data")) mainWidget.layout().addLayout(testLayout2) def createStatusBar(self): self.statusBar().addWidget(QLabel("This is a test"), 1) @pyqtSlot() def savePlot(self): path=str(QFileDialog.getSaveFileName(self, 'Save Plot...', '', FILE_FORMATS_STR)) print "Save Path:", path if path: self.canvas.print_figure(path) @pyqtSlot() def onAbout(self): QMessageBox.about( self, "About this test", """ <p>A test of using PyQt with matplotlib:</p> <ul> <li>Use the matplotlib navigation bar</li> <li>Add values to the text box and press Enter (or click "Draw")</li> <li>Show or hide the grid</li> <li>Drag the slider to modify the width of the bars</li> <li>Save the plot to a file using the File menu</li> <li>Click on a bar to receive an informative message</li> </ul> """) def onPick(self, event): QMessageBox.information( self, "Click!", "You've clicked on a bar with coords:\n%s" % event.artist.get_bbox().get_points()) def onMove(self, event): self.xValue.setText("%d" % event.x) self.yValue.setText("%d" % event.y) if event.inaxes: self.xdValue.setText("%f" % event.xdata) self.ydValue.setText("%f" % event.ydata) @pyqtSlot() def onDraw(self): data=map(int, str(self.textbox.text()).split()) x=range(len(data)) e=0.5*(randn(len(data))) self.axes.clear() self.axes.grid(self.showGrid.isChecked()) self.axes.bar( left=x, height=data, width=self.slider.value()/100.0, align='center', picker=5, alpha=0.5) self.axes.errorbar(x, data, e, fmt='o', ms=0) self.canvas.draw()