def __spacetime_diagram_o_frame(self): # from (x',t') to (x,t) def tr(x_prime, t_prime): x_prime, t_prime = np.asarray(x_prime), np.asarray(t_prime) return self.lorentz_transformations.transform( x_prime, t_prime, -self.velocity) # form (x,t) to (x',t') def inv_tr(x, t): x, t = np.asarray(x), np.asarray(t) return self.lorentz_transformations.transform(x, t, self.velocity) grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax = Subplot(self.fig, 1, 2, 1, grid_helper=grid_helper) if self.showOPrime \ else Subplot(self.fig, 1, 1, 1, grid_helper=grid_helper) self.fig.add_subplot(ax) ax.set_xlabel("x", loc="center") ax.set_ylabel("t", loc="center") # O' x axis ax.axis["x1"] = x1 = ax.new_floating_axis(1, 0) x1.label.set_text("x'") # O' t axis ax.axis["t1"] = t1 = ax.new_floating_axis(0, 0) t1.label.set_text("t'") self.__add_x_and_y_axis(ax) ax.format_coord = self.__format_coord_o_frame self.__remove_ticks(ax, x1, t1) self.world_lines_plotter.plot(plt, ax, self.velocity)
def setup_content(self): # Create subplot and add it to the figure: self.plot = Subplot(self.figure, 211, axisbg=(1.0, 1.0, 1.0, 0.0)) self.plot.set_autoscale_on(False) self.figure.add_axes(self.plot) # Connect events: self.canvas.mpl_connect('draw_event', self.fix_after_drawing) self.canvas.mpl_connect('resize_event', self.fix_after_drawing) self.update()
def setup_content(self, status_callback, marker_callback): # Create subplot and add it to the figure: self.plot = Subplot(self.figure, 211, facecolor=(1.0, 1.0, 1.0, 0.0)) self.plot.set_autoscale_on(False) self.figure.add_axes(self.plot) # Connect events: self.canvas.mpl_connect('draw_event', self.fix_after_drawing) self.canvas.mpl_connect('resize_event', self.fix_after_drawing) self.mtc = MotionTracker(self, status_callback) self.cc = ClickCatcher(self, marker_callback)
def plotISIDistributions(sessions,groups=None,sessionTypes=None,samplingRate=30000.0,save=False,fname=None,figsize=(10,6)): """Plots the isi distributions for all the cells in the given sessions""" fig = plt.figure(figsize=figsize) fig.subplots_adjust(left=0.05,right=.95) ISIs = {} ncells = 0 if sessionTypes != None: sessionTypes = dict(zip(sessions,sessionTypes)) for g in groups: for s in sessions: dataFile = h5py.File(os.path.expanduser('~/Documents/research/data/spikesorting/hmm/p=1e-20/%sg%.4d.hdf5' % (s,g)),'r') try: for c in dataFile['unitTimePoints'].keys(): isi = np.log(np.diff(dataFile['unitTimePoints'][c][:]/(samplingRate/1000))) cn = 'g%dc%d' % (g,int(c)) if cn in ISIs: ISIs[cn]['%s' %(s,)] = isi else: ISIs[cn] = {'%s' %(s,): isi} finally: dataFile.close() i = 1 ncells = len(ISIs.keys()) for c in ISIs.keys(): ax = Subplot(fig,1,ncells,i) formatAxis(ax) fig.add_axes(ax) ax.set_title(c) for k,v in ISIs[c].items(): if sessionTypes != None: L = sessionTypes[k] else: L = k n,b = np.histogram(v,bins=20,normed=True) plt.plot(b[:-1],n,label=L) i+=1 ax.set_xlabel('ISI [ms]') xl,xh = int(np.round((b[0]-0.5)*2))/2,int(np.round((b[-1]+0.5)*2))/2 xl = -0.5 dx = np.round(10.0*(xh-xl)/4.0)/10 xt_ = np.arange(xl,xh+1,dx) ax.set_xticks(xt_) ax.set_xticklabels(map(lambda s: r'$10^{%.1f}$' % (s,),xt_)) fig.axes[-1].legend() if save: if fname == None: fname = os.path.expanduser('~/Documents/research/figures/isi_comparison.pdf') fig.savefig(fname,bbox='tight')
def setup_axes1(self, fig, T_ticks, subplotshape=None): """ A simple one. """ deg = -45. self.tr = Affine2D().rotate_deg(deg) theta_ticks = [] #np.arange(theta_min, theta_max, d_T) grid_helper = GridHelperCurveLinear( self.tr, grid_locator1=FixedLocator(T_ticks), grid_locator2=FixedLocator(theta_ticks)) if subplotshape is None: subplotshape = (1, 1, 1) ax1 = Subplot(fig, *subplotshape, grid_helper=grid_helper) # ax1 will have a ticks and gridlines defined by the given # transform (+ transData of the Axes). Note that the transform of # the Axes itself (i.e., transData) is not affected by the given # transform. fig.add_subplot(ax1) # SW, SE, NE, NW corners = np.array([[-25., -20.], [30., 40.], [-40., 120.], [-105., 60.]]) corners_t = self._tf(corners[:, 0], corners[:, 1]) # ax1.set_aspect(1.) x_min, x_max = self.x_range ax1.set_xlim(x_min, x_max) ax1.set_ylim(*self.y_range) ax1.set_xlabel('Temperature [C]') ax1.set_aspect(1) #ax1.axis["t"]=ax1.new_floating_axis(0, 0.) #T_axis = ax1.axis['t'] #theta_axis = ax1.axis["t2"]=ax1.new_floating_axis(1, 0.) # plot.draw() # plot.show() self.ax1 = ax1
def standard_axis(fig, subplot, sharex=None, sharey=None, hasx=False, hasy=True): sys.stderr.write("This method is deprecated. " "Use stfio_plot.StandardAxis instead.\n") try: iter(subplot) if isinstance(subplot, matplotlib.gridspec.GridSpec): ax1 = Subplot(fig, subplot, frameon=False, sharex=sharex, sharey=sharey) else: ax1 = Subplot(fig, subplot[0], subplot[1], subplot[2], frameon=False, sharex=sharex, sharey=sharey) except: ax1 = Subplot(fig, subplot, frameon=False, sharex=sharex, sharey=sharey) fig.add_axes(ax1) ax1.axis["right"].set_visible(False) ax1.axis["top"].set_visible(False) if not hasx: ax1.axis["bottom"].set_visible(False) if not hasy: ax1.axis["left"].set_visible(False) return ax1
def plotXcorr(qdata,save=False,fname='hmmSortingUnits.pdf'): unitTimePoints = qdata['unitTimePoints'] samplingRate = qdata.get('samplingRate',30000.0) units = unitTimePoints.keys() nunits = len(units) xsize = max(10,nunits*2) fig = plt.figure(figsize=(xsize,(6.0/8)*xsize) ) fig.subplots_adjust(left=0.03,right=0.97,bottom=0.03,top=0.97) i = 1 if not 'XCorr' in qdata: if isinstance(qdata,dict): qdata['XCorr'] = {} else: qdata.create_group('XCorr') for k1 in xrange(len(units)-1) : if not units[k1] in qdata['XCorr']: qdata['XCorr'].create_group(units[k1]) for k2 in xrange(k1+1,len(units)): if not units[k2] in qdata['XCorr'][units[k1]]: T1 = unitTimePoints[units[k1]][:]/(samplingRate/1000) T2 = unitTimePoints[units[k2]][:]/(samplingRate/1000) #compute differences less than 50 ms C = pdist_threshold2(T1,T2,50) qdata['XCorr'][units[k1]].create_dataset(units[k2],data=C,compression=2,fletcher32=True,shuffle=True) else: C = qdata['XCorr'][units[k1]][units[k2]][:] n,b = np.histogram(C,np.arange(-50,50),normed=True) ax = Subplot(fig,nunits-1,nunits,k1*nunits+k2) fig.add_axes(ax) formatAxis(ax) ax.plot(b[:-1],n,'k') ax.fill_betweenx([0,n.max()],-1.0,1.0,color='r',alpha=0.3) if not (k1 == len(units)-2 and k2 == len(units)-1): ax.set_xticklabels('') ax.set_yticklabels('') if save: fig.savefig(os.path.expanduser('~/Documents/research/figures/SpikeSorting/hmm/%s' %( fname,)),bbox='tight') else: plt.draw()
def create_axes_nonorthogonal(self, transform): self.clear_figure() self.set_nonorthogonal_transform(transform) self.ax = CurveLinearSubPlot(self.fig, 1, 1, 1, grid_helper=GridHelperCurveLinear( (self.nonortho_tr, transform.inv_tr))) self.set_grid_on() self.fig.add_subplot(self.ax) self.plot_MDH = self.plot_MDH_nonorthogonal self.canvas.draw_idle()
def setup_axes1(self, fig, T_ticks, subplotshape=None): """ A simple one. """ deg = -45. self.tr = Affine2D().rotate_deg(deg) theta_ticks = [] #np.arange(theta_min, theta_max, d_T) grid_helper = GridHelperCurveLinear(self.tr, grid_locator1=FixedLocator(T_ticks), grid_locator2=FixedLocator(theta_ticks)) if subplotshape is None: subplotshape = (1,1,1) ax1 = Subplot(fig, *subplotshape, grid_helper=grid_helper) # ax1 will have a ticks and gridlines defined by the given # transform (+ transData of the Axes). Note that the transform of # the Axes itself (i.e., transData) is not affected by the given # transform. fig.add_subplot(ax1) # SW, SE, NE, NW corners = np.array([[-25., -20.], [30., 40.], [-40., 120.], [-105., 60.]]) corners_t = self._tf(corners[:,0], corners[:,1]) # ax1.set_aspect(1.) x_min, x_max = self.x_range ax1.set_xlim(x_min, x_max) ax1.set_ylim(*self.y_range) ax1.set_xlabel('Temperature [C]') ax1.set_aspect(1) #ax1.axis["t"]=ax1.new_floating_axis(0, 0.) #T_axis = ax1.axis['t'] #theta_axis = ax1.axis["t2"]=ax1.new_floating_axis(1, 0.) # plot.draw() # plot.show() self.ax1 = ax1
def create_axes_nonorthogonal(self, transform): self.clear_figure() self.set_nonorthogonal_transform(transform) self.ax = CurveLinearSubPlot(self.fig, 1, 1, 1, grid_helper=GridHelperCurveLinear( (transform.tr, transform.inv_tr))) # don't redraw on zoom as the data is rebinned and has to be redrawn again anyway self.enable_zoom_on_mouse_scroll(redraw=False) self.set_grid_on() self.fig.add_subplot(self.ax) self.plot_MDH = self.plot_MDH_nonorthogonal self.canvas.draw_idle()
def plot_hpxmap_hist(hpxmap, raRange=[-180, 180], decRange=[-90, 90], lonRef=0.0, cbar_kwargs=dict(), hpxmap_kwargs=dict(), hist_kwargs=dict(), fit_gaussian=False, figsize=(10, 4)): hist_defaults = dict(peak=True) set_defaults(hist_kwargs, hist_defaults) if isinstance(hpxmap, basestring): hpxmap = healpy.read_map(f) fig = plt.figure(10, figsize=figsize) fig.clf() gridspec = plt.GridSpec(1, 3) bmap = FgcmBasemap(lonRef=lonRef, raMin=raRange[0], raMax=raRange[1], decMin=decRange[0], decMax=decRange[1]) bmap.create_axes(rect=gridspec[0:2]) im = bmap.draw_hpxmap(hpxmap, **hpxmap_kwargs) bmap.draw_inset_colorbar(**cbar_kwargs) ax1 = plt.gca() ax1.axis['right'].major_ticklabels.set_visible(False) ax1.axis['top'].major_ticklabels.set_visible(False) ax2 = Subplot(fig, gridspec[2]) fig.add_subplot(ax2) plt.sca(ax2) ret = draw_hist(hpxmap, fit_gaussian=fit_gaussian, **hist_kwargs) ax2.yaxis.set_major_locator(MaxNLocator(6, prune='both')) ax2.xaxis.set_major_locator(MaxNLocator(5)) ax2.axis['left'].major_ticklabels.set_visible(False) ax2.axis['right'].major_ticklabels.set_visible(True) ax2.axis['right'].label.set_visible(True) ax2.axis['right'].label.set_text(r'Normalized Area (a.u.)') ax2.axis['bottom'].label.set_visible(True) plt.subplots_adjust(bottom=0.15, top=0.95) return fig, [ax1, ax2], ret
def curvelinear_test1(fig): """ Grid for custom transform. """ def tr(x, y): x, y = numpy.asarray(x), numpy.asarray(y) return x, y - (2 * x) # return x + (5 * y), (7 * y) + (3 * x) def inv_tr(x, y): x, y = numpy.asarray(x), numpy.asarray(y) return x, y + (2 * x) grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax1 = Subplot(fig, 1, 1, 1, grid_helper=grid_helper) # ax1 will have a ticks and gridlines defined by the given # transform (+ transData of the Axes). Note that the transform of # the Axes itself (i.e., transData) is not affected by the given # transform. fig.add_subplot(ax1) xx, yy = tr([0, 1], [0, 2]) ax1.plot(xx, yy, linewidth=2.0) ax1.set_aspect(1) ax1.set_xlim(-3, 3) ax1.set_ylim(-3, 3) ax1.axis["t"] = ax1.new_floating_axis( 0, 0 ) # first argument appears to be slope, second argument appears to be starting point on vertical ax1.axis["t2"] = ax1.new_floating_axis(1, 0) ax1.axhline(y=0, color='r') ax1.axvline(x=0, color='r') ax1.grid(True, zorder=0)
class DGSPlannerGUI(QtGui.QWidget): def __init__(self, ol=None, parent=None): # pylint: disable=unused-argument,super-on-old-class super(DGSPlannerGUI, self).__init__(parent) #OrientedLattice if ValidateOL(ol): self.ol = ol else: self.ol = mantid.geometry.OrientedLattice() self.masterDict = dict() #holds info about instrument and ranges self.updatedInstrument = False self.updatedOL = False self.wg = None #workspace group self.instrumentWidget = InstrumentSetupWidget.InstrumentSetupWidget( self) self.setLayout(QtGui.QHBoxLayout()) controlLayout = QtGui.QVBoxLayout() controlLayout.addWidget(self.instrumentWidget) self.ublayout = QtGui.QHBoxLayout() self.classic = ClassicUBInputWidget.ClassicUBInputWidget(self.ol) self.ublayout.addWidget(self.classic, alignment=QtCore.Qt.AlignTop, stretch=1) self.matrix = MatrixUBInputWidget.MatrixUBInputWidget(self.ol) self.ublayout.addWidget(self.matrix, alignment=QtCore.Qt.AlignTop, stretch=1) controlLayout.addLayout(self.ublayout) self.dimensionWidget = DimensionSelectorWidget.DimensionSelectorWidget( self) controlLayout.addWidget(self.dimensionWidget) plotControlLayout = QtGui.QGridLayout() self.plotButton = QtGui.QPushButton("Plot", self) self.oplotButton = QtGui.QPushButton("Overplot", self) self.helpButton = QtGui.QPushButton("?", self) self.colorLabel = QtGui.QLabel('Color by angle', self) self.colorButton = QtGui.QCheckBox(self) self.colorButton.toggle() self.aspectLabel = QtGui.QLabel('Aspect ratio 1:1', self) self.aspectButton = QtGui.QCheckBox(self) self.saveButton = QtGui.QPushButton("Save Figure", self) plotControlLayout.addWidget(self.plotButton, 0, 0) plotControlLayout.addWidget(self.oplotButton, 0, 1) plotControlLayout.addWidget(self.colorLabel, 0, 2, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.colorButton, 0, 3) plotControlLayout.addWidget(self.aspectLabel, 0, 4, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.aspectButton, 0, 5) plotControlLayout.addWidget(self.helpButton, 0, 6) plotControlLayout.addWidget(self.saveButton, 0, 7) controlLayout.addLayout(plotControlLayout) self.layout().addLayout(controlLayout) #figure self.figure = Figure() self.figure.patch.set_facecolor('white') self.canvas = FigureCanvas(self.figure) self.grid_helper = GridHelperCurveLinear((self.tr, self.inv_tr)) self.trajfig = Subplot(self.figure, 1, 1, 1, grid_helper=self.grid_helper) self.trajfig.hold(True) self.figure.add_subplot(self.trajfig) self.layout().addWidget(self.canvas) self.needToClear = False self.saveDir = '' #connections self.matrix.UBmodel.changed.connect(self.updateUB) self.matrix.UBmodel.changed.connect(self.classic.updateOL) self.classic.changed.connect(self.matrix.UBmodel.updateOL) self.classic.changed.connect(self.updateUB) self.instrumentWidget.changed.connect(self.updateParams) self.dimensionWidget.changed.connect(self.updateParams) self.plotButton.clicked.connect(self.updateFigure) self.oplotButton.clicked.connect(self.updateFigure) self.helpButton.clicked.connect(self.help) self.saveButton.clicked.connect(self.save) #force an update of values self.instrumentWidget.updateAll() self.dimensionWidget.updateChanges() #help self.assistantProcess = QtCore.QProcess(self) # pylint: disable=protected-access self.collectionFile = os.path.join(mantid._bindir, '../docs/qthelp/MantidProject.qhc') version = ".".join(mantid.__version__.split(".")[:2]) self.qtUrl = 'qthelp://org.sphinx.mantidproject.' + version + '/doc/interfaces/DGSPlanner.html' self.externalUrl = 'http://docs.mantidproject.org/nightly/interfaces/DGSPlanner.html' #control for cancel button self.iterations = 0 self.progress_canceled = False @QtCore.pyqtSlot(mantid.geometry.OrientedLattice) def updateUB(self, ol): self.ol = ol self.updatedOL = True self.trajfig.clear() @QtCore.pyqtSlot(dict) def updateParams(self, d): if self.sender() is self.instrumentWidget: self.updatedInstrument = True if d.has_key('dimBasis') and self.masterDict.has_key( 'dimBasis') and d['dimBasis'] != self.masterDict['dimBasis']: self.needToClear = True if d.has_key('dimIndex') and self.masterDict.has_key( 'dimIndex') and d['dimIndex'] != self.masterDict['dimIndex']: self.needToClear = True self.masterDict.update(copy.deepcopy(d)) def help(self): try: import pymantidplot pymantidplot.proxies.showCustomInterfaceHelp('DGSPlanner') except ImportError: self.assistantProcess.close() self.assistantProcess.waitForFinished() helpapp = QtCore.QLibraryInfo.location( QtCore.QLibraryInfo.BinariesPath) + QtCore.QDir.separator() helpapp += 'assistant' args = [ '-enableRemoteControl', '-collectionFile', self.collectionFile, '-showUrl', self.qtUrl ] if os.path.isfile(helpapp): self.assistantProcess.close() self.assistantProcess.waitForFinished() self.assistantProcess.start(helpapp, args) else: QtGui.QDesktopServices.openUrl(QtCore.QUrl(self.externalUrl)) def closeEvent(self, event): self.assistantProcess.close() self.assistantProcess.waitForFinished() event.accept() # pylint: disable=too-many-locals def updateFigure(self): # pylint: disable=too-many-branches if self.updatedInstrument or self.progress_canceled: self.progress_canceled = False #get goniometer settings first gonioAxis0values = numpy.arange( self.masterDict['gonioMinvals'][0], self.masterDict['gonioMaxvals'][0] + 0.1 * self.masterDict['gonioSteps'][0], self.masterDict['gonioSteps'][0]) gonioAxis1values = numpy.arange( self.masterDict['gonioMinvals'][1], self.masterDict['gonioMaxvals'][1] + 0.1 * self.masterDict['gonioSteps'][1], self.masterDict['gonioSteps'][1]) gonioAxis2values = numpy.arange( self.masterDict['gonioMinvals'][2], self.masterDict['gonioMaxvals'][2] + 0.1 * self.masterDict['gonioSteps'][2], self.masterDict['gonioSteps'][2]) self.iterations = len(gonioAxis0values) * len( gonioAxis1values) * len(gonioAxis2values) if self.iterations > 10: reply = QtGui.QMessageBox.warning( self, 'Goniometer', "More than 10 goniometer settings. This might be long.\n" "Are you sure you want to proceed?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.No: return if self.wg != None: mantid.simpleapi.DeleteWorkspace(self.wg) mantid.simpleapi.LoadEmptyInstrument( mantid.api.ExperimentInfo.getInstrumentFilename( self.masterDict['instrument']), OutputWorkspace="__temp_instrument") if self.masterDict['instrument'] == 'HYSPEC': mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='msd', LogText='1798.5', LogType='Number Series') mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='s2', LogText=str( self.masterDict['S2']), LogType='Number Series') mantid.simpleapi.LoadInstrument(Workspace="__temp_instrument", RewriteSpectraMap=True, InstrumentName="HYSPEC") #masking if self.masterDict.has_key('maskFilename') and len( self.masterDict['maskFilename'].strip()) > 0: try: __maskWS = mantid.simpleapi.Load( self.masterDict['maskFilename']) mantid.simpleapi.MaskDetectors( Workspace="__temp_instrument", MaskedWorkspace=__maskWS) except (ValueError, RuntimeError) as e: reply = QtGui.QMessageBox.critical( self, 'Error', "The following error has occured in loading the mask:\n" + str(e) + "\nDo you want to continue without mask?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.No: return if self.masterDict['makeFast']: sp = range( mantid.mtd["__temp_instrument"].getNumberHistograms()) tomask = sp[::4] + sp[1::4] + sp[2::4] mantid.simpleapi.MaskDetectors("__temp_instrument", SpectraList=tomask) i = 0 groupingStrings = [] progressDialog = QtGui.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: name = "__temp_instrument" + str(i) i += 1 progressDialog.setValue(i) progressDialog.setLabelText( "Creating workspace %d of %d..." % (i, self.iterations)) QtGui.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled = True progressDialog.close() return groupingStrings.append(name) mantid.simpleapi.CloneWorkspace("__temp_instrument", OutputWorkspace=name) mantid.simpleapi.SetGoniometer( Workspace=name, Axis0=str(g0) + "," + self.masterDict['gonioDirs'][0] + "," + str(self.masterDict['gonioSenses'][0]), Axis1=str(g1) + "," + self.masterDict['gonioDirs'][1] + "," + str(self.masterDict['gonioSenses'][1]), Axis2=str(g2) + "," + self.masterDict['gonioDirs'][2] + "," + str(self.masterDict['gonioSenses'][2])) progressDialog.close() mantid.simpleapi.DeleteWorkspace("__temp_instrument") self.wg = mantid.simpleapi.GroupWorkspaces( groupingStrings, OutputWorkspace="__temp_instrument") self.updatedInstrument = False #set the UB if self.updatedOL or not self.wg[0].sample().hasOrientedLattice(): mantid.simpleapi.SetUB(self.wg, UB=self.ol.getUB()) self.updatedOL = False #calculate coverage dimensions = ['Q1', 'Q2', 'Q3', 'DeltaE'] progressDialog = QtGui.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") for i in range(self.iterations): progressDialog.setValue(i) progressDialog.setLabelText("Calculating orientation %d of %d..." % (i, self.iterations)) QtGui.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled = True progressDialog.close() return __mdws = mantid.simpleapi.CalculateCoverageDGS( self.wg[i], Q1Basis=self.masterDict['dimBasis'][0], Q2Basis=self.masterDict['dimBasis'][1], Q3Basis=self.masterDict['dimBasis'][2], IncidentEnergy=self.masterDict['Ei'], Dimension1=dimensions[self.masterDict['dimIndex'][0]], Dimension1Min=float2Input(self.masterDict['dimMin'][0]), Dimension1Max=float2Input(self.masterDict['dimMax'][0]), Dimension1Step=float2Input(self.masterDict['dimStep'][0]), Dimension2=dimensions[self.masterDict['dimIndex'][1]], Dimension2Min=float2Input(self.masterDict['dimMin'][1]), Dimension2Max=float2Input(self.masterDict['dimMax'][1]), Dimension2Step=float2Input(self.masterDict['dimStep'][1]), Dimension3=dimensions[self.masterDict['dimIndex'][2]], Dimension3Min=float2Input(self.masterDict['dimMin'][2]), Dimension3Max=float2Input(self.masterDict['dimMax'][2]), Dimension4=dimensions[self.masterDict['dimIndex'][3]], Dimension4Min=float2Input(self.masterDict['dimMin'][3]), Dimension4Max=float2Input(self.masterDict['dimMax'][3])) if i == 0: intensity = __mdws.getSignalArray( )[:, :, 0, 0] * 1. #to make it writeable else: if self.colorButton.isChecked(): tempintensity = __mdws.getSignalArray()[:, :, 0, 0] intensity[numpy.where(tempintensity > 0)] = i + 1. else: tempintensity = __mdws.getSignalArray()[:, :, 0, 0] intensity[numpy.where(tempintensity > 0)] = 1. progressDialog.close() x = numpy.linspace( __mdws.getDimension(0).getMinimum(), __mdws.getDimension(0).getMaximum(), intensity.shape[0]) y = numpy.linspace( __mdws.getDimension(1).getMinimum(), __mdws.getDimension(1).getMaximum(), intensity.shape[1]) Y, X = numpy.meshgrid(y, x) xx, yy = self.tr(X, Y) Z = numpy.ma.masked_array(intensity, intensity == 0) Z = Z[:-1, :-1] #plotting if self.sender() is self.plotButton or self.needToClear: self.figure.clear() self.trajfig.clear() self.figure.add_subplot(self.trajfig) self.needToClear = False self.trajfig.pcolorfast(xx, yy, Z) if self.aspectButton.isChecked(): self.trajfig.set_aspect(1.) else: self.trajfig.set_aspect('auto') self.trajfig.set_xlabel(self.masterDict['dimNames'][0]) self.trajfig.set_ylabel(self.masterDict['dimNames'][1]) self.trajfig.grid(True) self.canvas.draw() mantid.simpleapi.DeleteWorkspace(__mdws) def save(self): fileName = str( QtGui.QFileDialog.getSaveFileName(self, 'Save Plot', self.saveDir, '*.png')) data = "Instrument " + self.masterDict['instrument'] + '\n' if self.masterDict['instrument'] == 'HYSPEC': data += "S2 = " + str(self.masterDict['S2']) + '\n' data += "Ei = " + str(self.masterDict['Ei']) + ' meV\n' data += "Goniometer values:\n" gonioAxis0values = numpy.arange( self.masterDict['gonioMinvals'][0], self.masterDict['gonioMaxvals'][0] + 0.1 * self.masterDict['gonioSteps'][0], self.masterDict['gonioSteps'][0]) gonioAxis1values = numpy.arange( self.masterDict['gonioMinvals'][1], self.masterDict['gonioMaxvals'][1] + 0.1 * self.masterDict['gonioSteps'][1], self.masterDict['gonioSteps'][1]) gonioAxis2values = numpy.arange( self.masterDict['gonioMinvals'][2], self.masterDict['gonioMaxvals'][2] + 0.1 * self.masterDict['gonioSteps'][2], self.masterDict['gonioSteps'][2]) for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: data += " " + self.masterDict['gonioLabels'][ 0] + " = " + str(g0) data += " " + self.masterDict['gonioLabels'][ 1] + " = " + str(g1) data += " " + self.masterDict['gonioLabels'][ 2] + " = " + str(g2) + '\n' data += "Lattice parameters:\n" data += " a = " + str(self.ol.a()) + " b = " + str( self.ol.b()) + " c = " + str(self.ol.c()) + '\n' data += " alpha = " + str(self.ol.alpha()) + " beta = " + str( self.ol.beta()) + " gamma = " + str(self.ol.gamma()) + '\n' data += "Orientation vectors:\n" data += " u = " + str(self.ol.getuVector()) + '\n' data += " v = " + str(self.ol.getvVector()) + '\n' data+="Integrated "+self.masterDict['dimNames'][2]+" between "+\ str(self.masterDict['dimMin'][2])+" and "+str(self.masterDict['dimMax'][2])+'\n' data+="Integrated "+self.masterDict['dimNames'][3]+" between "+\ str(self.masterDict['dimMin'][3])+" and "+str(self.masterDict['dimMax'][3])+'\n' info = self.figure.text(0.2, 0, data, verticalalignment='top') self.figure.savefig(fileName, bbox_inches='tight', additional_artists=info) self.saveDir = os.path.dirname(fileName) def tr(self, x, y): x, y = numpy.asarray(x), numpy.asarray(y) #one of the axes is energy if self.masterDict['dimIndex'][0] == 3 or self.masterDict['dimIndex'][ 1] == 3: return x, y else: h1, k1, l1 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][0]].split(',')) h2, k2, l2 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][1]].split(',')) angle = numpy.radians(self.ol.recAngle(h1, k1, l1, h2, k2, l2)) return 1. * x + numpy.cos(angle) * y, numpy.sin(angle) * y def inv_tr(self, x, y): x, y = numpy.asarray(x), numpy.asarray(y) #one of the axes is energy if self.masterDict['dimIndex'][0] == 3 or self.masterDict['dimIndex'][ 1] == 3: return x, y else: h1, k1, l1 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][0]].split(',')) h2, k2, l2 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][1]].split(',')) angle = numpy.radians(self.ol.recAngle(h1, k1, l1, h2, k2, l2)) return 1. * x - y / numpy.tan(angle), y / numpy.sin(angle)
def __init__(self, parent=None, window_flags=None, ol=None): # pylint: disable=unused-argument,super-on-old-class super(DGSPlannerGUI, self).__init__(parent) if window_flags: self.setWindowFlags(window_flags) # OrientedLattice if ValidateOL(ol): self.ol = ol else: self.ol = mantid.geometry.OrientedLattice() self.masterDict = dict() # holds info about instrument and ranges self.updatedInstrument = False self.instrumentWAND = False self.updatedOL = False self.wg = None # workspace group self.instrumentWidget = InstrumentSetupWidget.InstrumentSetupWidget( self) self.setLayout(QtWidgets.QHBoxLayout()) controlLayout = QtWidgets.QVBoxLayout() geometryBox = QtWidgets.QGroupBox("Instrument Geometry") plotBox = QtWidgets.QGroupBox("Plot Axes") geometryBoxLayout = QtWidgets.QVBoxLayout() geometryBoxLayout.addWidget(self.instrumentWidget) geometryBox.setLayout(geometryBoxLayout) controlLayout.addWidget(geometryBox) self.ublayout = QtWidgets.QHBoxLayout() self.classic = ClassicUBInputWidget.ClassicUBInputWidget(self.ol) self.ublayout.addWidget(self.classic, alignment=QtCore.Qt.AlignTop, stretch=1) self.matrix = MatrixUBInputWidget.MatrixUBInputWidget(self.ol) self.ublayout.addWidget(self.matrix, alignment=QtCore.Qt.AlignTop, stretch=1) sampleBox = QtWidgets.QGroupBox("Sample") sampleBox.setLayout(self.ublayout) controlLayout.addWidget(sampleBox) self.dimensionWidget = DimensionSelectorWidget.DimensionSelectorWidget( self) plotBoxLayout = QtWidgets.QVBoxLayout() plotBoxLayout.addWidget(self.dimensionWidget) plotControlLayout = QtWidgets.QGridLayout() self.plotButton = QtWidgets.QPushButton("Plot", self) self.oplotButton = QtWidgets.QPushButton("Overplot", self) self.helpButton = QtWidgets.QPushButton("?", self) self.colorLabel = QtWidgets.QLabel('Color by angle', self) self.colorButton = QtWidgets.QCheckBox(self) self.colorButton.toggle() self.aspectLabel = QtWidgets.QLabel('Aspect ratio 1:1', self) self.aspectButton = QtWidgets.QCheckBox(self) self.saveButton = QtWidgets.QPushButton("Save Figure", self) plotControlLayout.addWidget(self.plotButton, 0, 0) plotControlLayout.addWidget(self.oplotButton, 0, 1) plotControlLayout.addWidget(self.colorLabel, 0, 2, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.colorButton, 0, 3) plotControlLayout.addWidget(self.aspectLabel, 0, 4, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.aspectButton, 0, 5) plotControlLayout.addWidget(self.helpButton, 0, 6) plotControlLayout.addWidget(self.saveButton, 0, 7) plotBoxLayout.addLayout(plotControlLayout) plotBox = QtWidgets.QGroupBox("Plot Axes") plotBox.setLayout(plotBoxLayout) controlLayout.addWidget(plotBox) self.layout().addLayout(controlLayout) # figure self.figure = Figure() self.figure.patch.set_facecolor('white') self.canvas = FigureCanvas(self.figure) self.grid_helper = GridHelperCurveLinear((self.tr, self.inv_tr)) self.trajfig = Subplot(self.figure, 1, 1, 1, grid_helper=self.grid_helper) if matplotlib.compare_versions('2.1.0', matplotlib.__version__): self.trajfig.hold( True) # hold is deprecated since 2.1.0, true by default self.figure.add_subplot(self.trajfig) self.toolbar = MantidNavigationToolbar(self.canvas, self) figureLayout = QtWidgets.QVBoxLayout() figureLayout.addWidget(self.toolbar, 0) figureLayout.addWidget(self.canvas, 1) self.layout().addLayout(figureLayout) self.needToClear = False self.saveDir = '' # connections self.matrix.UBmodel.changed.connect(self.updateUB) self.matrix.UBmodel.changed.connect(self.classic.updateOL) self.classic.changed.connect(self.matrix.UBmodel.updateOL) self.classic.changed.connect(self.updateUB) self.instrumentWidget.changed.connect(self.updateParams) self.instrumentWidget.getInstrumentComboBox().activated[str].connect( self.instrumentUpdateEvent) self.instrumentWidget.getEditEi().textChanged.connect( self.eiWavelengthUpdateEvent) self.dimensionWidget.changed.connect(self.updateParams) self.plotButton.clicked.connect(self.updateFigure) self.oplotButton.clicked.connect(self.updateFigure) self.helpButton.clicked.connect(self.help) self.saveButton.clicked.connect(self.save) # force an update of values self.instrumentWidget.updateAll() self.dimensionWidget.updateChanges() # help self.assistant_process = QtCore.QProcess(self) # pylint: disable=protected-access self.mantidplot_name = 'DGS Planner' # control for cancel button self.iterations = 0 self.progress_canceled = False # register startup mantid.UsageService.registerFeatureUsage( mantid.kernel.FeatureType.Interface, "DGSPlanner", False)
# http://matplotlib.org/examples/axes_grid/demo_curvelinear_grid.html from mpl_toolkits.axisartist import Subplot from mpl_toolkits.axisartist.grid_helper_curvelinear import \ GridHelperCurveLinear def tr(x, y): # source (data) to target (rectilinear plot) coordinates x, y = numpy.asarray(x), numpy.asarray(y) return x + 0.2 * y, y - x def inv_tr(x, y): x, y = numpy.asarray(x), numpy.asarray(y) return x - 0.2 * y, y + x grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax6 = Subplot(fig, nrow, ncol, 6, grid_helper=grid_helper) fig.add_subplot(ax6) ax6.set_title('non-ortho axes') xx, yy = tr([3, 6], [5.0, 10.]) ax6.plot(xx, yy) ax6.set_aspect(1.) ax6.set_xlim(0, 10.) ax6.set_ylim(0, 10.) ax6.axis["t"] = ax6.new_floating_axis(0, 3.) ax6.axis["t2"] = ax6.new_floating_axis(1, 7.) ax6.grid(True) plt.show()
def curvelinear_test1(fig): """ grid for custom transform. """ def tr(x, y): x, y = np.asarray(x), np.asarray(y) return x, y - x def inv_tr(x, y): x, y = np.asarray(x), np.asarray(y) return x, y + x grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax1 = Subplot(fig, 1, 2, 1, grid_helper=grid_helper) # ax1 will have a ticks and gridlines defined by the given # transform (+ transData of the Axes). Note that the transform of # the Axes itself (i.e., transData) is not affected by the given # transform. fig.add_subplot(ax1) xx, yy = tr([3, 6], [5.0, 10.]) ax1.plot(xx, yy, linewidth=2.0) ax1.set_aspect(1.) ax1.set_xlim(0, 10.) ax1.set_ylim(0, 10.) ax1.axis["t"] = ax1.new_floating_axis(0, 3.) ax1.axis["t2"] = ax1.new_floating_axis(1, 7.) ax1.grid(True, zorder=0)
def __init__(self,ol=None,parent=None): # pylint: disable=unused-argument,super-on-old-class super(DGSPlannerGUI,self).__init__(parent) #OrientedLattice if ValidateOL(ol): self.ol=ol else: self.ol=mantid.geometry.OrientedLattice() self.masterDict=dict() #holds info about instrument and ranges self.updatedInstrument=False self.updatedOL=False self.wg=None #workspace group self.instrumentWidget=InstrumentSetupWidget.InstrumentSetupWidget(self) self.setLayout(QtGui.QHBoxLayout()) controlLayout=QtGui.QVBoxLayout() controlLayout.addWidget(self.instrumentWidget) self.ublayout=QtGui.QHBoxLayout() self.classic=ClassicUBInputWidget.ClassicUBInputWidget(self.ol) self.ublayout.addWidget(self.classic,alignment=QtCore.Qt.AlignTop,stretch=1) self.matrix=MatrixUBInputWidget.MatrixUBInputWidget(self.ol) self.ublayout.addWidget(self.matrix,alignment=QtCore.Qt.AlignTop,stretch=1) controlLayout.addLayout(self.ublayout) self.dimensionWidget=DimensionSelectorWidget.DimensionSelectorWidget(self) controlLayout.addWidget(self.dimensionWidget) plotControlLayout=QtGui.QGridLayout() self.plotButton=QtGui.QPushButton("Plot",self) self.oplotButton=QtGui.QPushButton("Overplot",self) self.helpButton=QtGui.QPushButton("?",self) self.colorLabel=QtGui.QLabel('Color by angle',self) self.colorButton=QtGui.QCheckBox(self) self.colorButton.toggle() self.aspectLabel=QtGui.QLabel('Aspect ratio 1:1',self) self.aspectButton=QtGui.QCheckBox(self) self.saveButton=QtGui.QPushButton("Save Figure",self) plotControlLayout.addWidget(self.plotButton,0,0) plotControlLayout.addWidget(self.oplotButton,0,1) plotControlLayout.addWidget(self.colorLabel,0,2,QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.colorButton,0,3) plotControlLayout.addWidget(self.aspectLabel,0,4,QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.aspectButton,0,5) plotControlLayout.addWidget(self.helpButton,0,6) plotControlLayout.addWidget(self.saveButton,0,7) controlLayout.addLayout(plotControlLayout) self.layout().addLayout(controlLayout) #figure self.figure=Figure() self.figure.patch.set_facecolor('white') self.canvas=FigureCanvas(self.figure) self.grid_helper = GridHelperCurveLinear((self.tr, self.inv_tr)) self.trajfig = Subplot(self.figure, 1, 1, 1, grid_helper=self.grid_helper) self.trajfig.hold(True) self.figure.add_subplot(self.trajfig) self.layout().addWidget(self.canvas) self.needToClear=False self.saveDir='' #connections self.matrix.UBmodel.changed.connect(self.updateUB) self.matrix.UBmodel.changed.connect(self.classic.updateOL) self.classic.changed.connect(self.matrix.UBmodel.updateOL) self.classic.changed.connect(self.updateUB) self.instrumentWidget.changed.connect(self.updateParams) self.dimensionWidget.changed.connect(self.updateParams) self.plotButton.clicked.connect(self.updateFigure) self.oplotButton.clicked.connect(self.updateFigure) self.helpButton.clicked.connect(self.help) self.saveButton.clicked.connect(self.save) #force an update of values self.instrumentWidget.updateAll() self.dimensionWidget.updateChanges() #help self.assistantProcess = QtCore.QProcess(self) # pylint: disable=protected-access self.collectionFile=os.path.join(mantid._bindir,'../docs/qthelp/MantidProject.qhc') version = ".".join(mantid.__version__.split(".")[:2]) self.qtUrl='qthelp://org.sphinx.mantidproject.'+version+'/doc/interfaces/DGSPlanner.html' self.externalUrl='http://docs.mantidproject.org/nightly/interfaces/DGSPlanner.html' #control for cancel button self.iterations=0 self.progress_canceled=False #register startup mantid.UsageService.registerFeatureUsage("Interface","DGSPlanner",False)
class MainPlotController(object): """ A controller for the main plot canvas. Sets up the widgets and has image exporting functionality. """ file_filters = ("Portable Network Graphics (PNG)", "*.png"), \ ("Scalable Vector Graphics (SVG)", "*.svg"), \ ("Portable Document Format (PDF)", "*.pdf") _canvas = None @property def canvas(self): if not self._canvas: self.setup_figure() self.setup_canvas() self.setup_content() return self._canvas # ------------------------------------------------------------ # View integration getters # ------------------------------------------------------------ def get_toolbar_widget(self, window): return NavigationToolbar(self.canvas, window) def get_canvas_widget(self): return self.canvas # ------------------------------------------------------------ # Initialization and other internals # ------------------------------------------------------------ def __init__(self, status_callback, marker_callback, *args, **kwargs): self.setup_layout_cache() self.setup_figure() self.setup_canvas() self.setup_content(status_callback, marker_callback) def setup_layout_cache(self): self.position_setup = PositionSetup() self.labels = list() self.marker_lbls = list() self._proxies = dict() self.scale = 1.0 self.stats = False self._last_pos = None def setup_figure(self): self.figure = Figure(dpi=72, facecolor="#FFFFFF", linewidth=0) self.figure.subplots_adjust(hspace=0.0, wspace=0.0) def setup_canvas(self): self._canvas = FigureCanvasGTK(self.figure) def setup_content(self, status_callback, marker_callback): # Create subplot and add it to the figure: self.plot = Subplot(self.figure, 211, facecolor=(1.0, 1.0, 1.0, 0.0)) self.plot.set_autoscale_on(False) self.figure.add_axes(self.plot) # Connect events: self.canvas.mpl_connect('draw_event', self.fix_after_drawing) self.canvas.mpl_connect('resize_event', self.fix_after_drawing) self.mtc = MotionTracker(self, status_callback) self.cc = ClickCatcher(self, marker_callback) #self.update() # ------------------------------------------------------------ # Update methods # ------------------------------------------------------------ def draw(self): self._last_pos = self.fix_before_drawing() self.figure.canvas.draw() def fix_after_drawing(self, *args): _new_pos = self.fix_before_drawing() if _new_pos != self._last_pos: self._last_pos = _new_pos self._redraw_later() return False def _redraw_later(self): self.timer = self.figure.canvas.new_timer(interval=10) self.timer.single_shot = True self.timer.add_callback(lambda: self.figure.canvas.draw_idle()) self.timer.start() def fix_before_drawing(self, *args): """ Fixes alignment issues due to longer labels or smaller windows Is executed after an initial draw event, since we can then retrieve the actual label dimensions and shift/resize the plot accordingly. """ renderer = get_renderer(self.figure) if not renderer or not self._canvas.get_realized(): return False # Fix left side for wide specimen labels: if len(self.labels) > 0: bbox = self._get_joint_bbox(self.labels, renderer) if bbox is not None: self.position_setup.left = self.position_setup.default_left + bbox.width # Fix top for high marker labels: if len(self.marker_lbls) > 0: bbox = self._get_joint_bbox( [label for label, flag, _ in self.marker_lbls if flag], renderer) if bbox is not None: self.position_setup.top = self.position_setup.default_top - bbox.height # Fix bottom for x-axis label: bottom_label = self.plot.axis["bottom"].label if bottom_label is not None: bbox = self._get_joint_bbox([bottom_label], renderer) if bbox is not None: self.position_setup.bottom = self.position_setup.default_bottom + ( bbox.ymax - bbox.ymin) * 2.0 # somehow we need this? # Calculate new plot position & set it: plot_pos = self.position_setup.position self.plot.set_position(plot_pos) # Adjust specimen label position for label in self.labels: label.set_x(plot_pos[0] - 0.025) # Adjust marker label position for label, flag, y_offset in self.marker_lbls: if flag: newy = plot_pos[1] + plot_pos[3] + y_offset - 0.025 label.set_y(newy) _new_pos = self.position_setup.to_string() return _new_pos def update(self, clear=False, project=None, specimens=None): """ Updates the entire plot with the given information. """ if clear: self.plot.cla() if project and specimens: self.labels, self.marker_lbls = plot_specimens( self.plot, self.position_setup, self.cc, project, specimens) # get mixtures for the selected specimens: plot_mixtures(self.plot, project, [ mixture for mixture in project.mixtures if any(specimen in mixture.specimens for specimen in specimens) ]) update_axes(self.plot, self.position_setup, project, specimens) self.draw() # ------------------------------------------------------------ # Plot position and size calculations # ------------------------------------------------------------ def _get_joint_bbox(self, container, renderer): bboxes = [] try: for text in container: bbox = text.get_window_extent(renderer=renderer) # the figure transform goes from relative coords->pixels and we # want the inverse of that bboxi = bbox.inverse_transformed(self.figure.transFigure) bboxes.append(bboxi) except (RuntimeError, ValueError): logger.exception( "Caught unhandled exception when joining boundig boxes") return None # don't continue # this is the bbox that bounds all the bboxes, again in relative # figure coords if len(bboxes) > 0: bbox = transforms.Bbox.union(bboxes) return bbox else: return None # ------------------------------------------------------------ # Graph exporting # ------------------------------------------------------------ def save(self, parent=None, current_name="graph", size="auto", num_specimens=1, offset=0.75): """ Displays a save dialog to export an image from the current plot. """ # Parse arguments: width, height = 0, 0 if size == "auto": descr, width, height, dpi = settings.OUTPUT_PRESETS[0] else: width, height, dpi = list( map(float, size.replace("@", "x").split("x"))) # Load gui: builder = Gtk.Builder() builder.add_from_file( resource_filename("pyxrd.specimen", "glade/save_graph_size.glade") ) # FIXME move this to this namespace!! size_expander = builder.get_object("size_expander") cmb_presets = builder.get_object("cmb_presets") # Setup combo with presets: cmb_store = Gtk.ListStore(str, int, int, float) for row in settings.OUTPUT_PRESETS: cmb_store.append(row) cmb_presets.clear() cmb_presets.set_model(cmb_store) cell = Gtk.CellRendererText() cmb_presets.pack_start(cell, True) cmb_presets.add_attribute(cell, 'text', 0) def on_cmb_changed(cmb, *args): itr = cmb.get_active_iter() w, h, d = cmb_store.get(itr, 1, 2, 3) entry_w.set_text(str(w)) entry_h.set_text(str(h)) entry_dpi.set_text(str(d)) cmb_presets.connect('changed', on_cmb_changed) # Setup input boxes: entry_w = builder.get_object("entry_width") entry_h = builder.get_object("entry_height") entry_dpi = builder.get_object("entry_dpi") entry_w.set_text(str(width)) entry_h.set_text(str(height)) entry_dpi.set_text(str(dpi)) # What to do when the user wants to save this: def on_accept(dialog): # Get the width, height & dpi width = float(entry_w.get_text()) height = float(entry_h.get_text()) dpi = float(entry_dpi.get_text()) i_width, i_height = width / dpi, height / dpi # Save it all right! self.save_figure(dialog.filename, dpi, i_width, i_height) # Ask the user where, how and if he wants to save: DialogFactory.get_save_dialog( "Save Graph", parent=parent, filters=self.file_filters, current_name=current_name, extra_widget=size_expander).run(on_accept) def save_figure(self, filename, dpi, i_width, i_height): """ Save the current plot Arguments: filename: the filename to save to (either .png, .pdf or .svg) dpi: Dots-Per-Inch resolution i_width: the width in inch i_height: the height in inch """ # Get original settings: original_dpi = self.figure.get_dpi() original_width, original_height = self.figure.get_size_inches() # Set everything according to the user selection: self.figure.set_dpi(dpi) self.figure.set_size_inches((i_width, i_height)) self.figure.canvas.draw() # replot bbox_inches = matplotlib.transforms.Bbox.from_bounds( 0, 0, i_width, i_height) # Save the figure: self.figure.savefig(filename, dpi=dpi, bbox_inches=bbox_inches) # Put everything back the way it was: self.figure.set_dpi(original_dpi) self.figure.set_size_inches((original_width, original_height)) self.figure.canvas.draw() # replot pass # end of class
class Tephigram(object): """ Generate a tephigram of one or more pressure and temperature data sets. """ def __init__(self, figure=None, isotherm_locator=None, dry_adiabat_locator=None, anchor=None): """ Initialise the tephigram transformation and plot axes. Kwargs: * figure: An existing :class:`matplotlib.figure.Figure` instance for the tephigram plot. If a figure is not provided, a new figure will be created by default. * isotherm_locator: A :class:`edson.Locator` instance or a numeric step size for the isotherm lines. * dry_adiabat_locator: A :class:`edson.Locator` instance or a numeric step size for the dry adiabat lines. * anchor: A sequence of two pressure, temperature pairs specifying the extent of the tephigram plot in terms of the bottom left hand corner and the top right hand corner. Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. For example: .. plot:: :include-source: import matplotlib.pyplot as plt import os.path import edson from edson import Tephigram dew_point = os.path.join(edson.RESOURCES_DIR, 'tephigram', 'dews.txt') dry_bulb = os.path.join(edson.RESOURCES_DIR, 'tephigram', 'temps.txt') dew_data, temp_data = edson.loadtxt(dew_point, dry_bulb) dews = zip(dew_data.pressure, dew_data.temperature) temps = zip(temp_data.pressure, temp_data.temperature) tephi = Tephigram() tephi.plot(dews, label='Dew-point', color='blue', linewidth=2, marker='s') tephi.plot(temps, label='Dry-bulb', color='red', linewidth=2, marker='o') plt.show() """ if not figure: # Create a default figure. self.figure = plt.figure(0, figsize=(9, 9)) else: self.figure = figure # Configure the locators. if isotherm_locator and not isinstance(isotherm_locator, Locator): if not isinstance(isotherm_locator, numbers.Number): raise ValueError('Invalid isotherm locator') locator_isotherm = Locator(isotherm_locator) else: locator_isotherm = isotherm_locator if dry_adiabat_locator and not isinstance(dry_adiabat_locator, Locator): if not isinstance(dry_adiabat_locator, numbers.Number): raise ValueError('Invalid dry adiabat locator') locator_theta = Locator(dry_adiabat_locator) else: locator_theta = dry_adiabat_locator # Define the tephigram coordinate-system transformation. self.tephi_transform = transforms.TephiTransform() grid_helper1 = GridHelperCurveLinear(self.tephi_transform, tick_formatter1=_FormatterIsotherm(), grid_locator1=locator_isotherm, tick_formatter2=_FormatterTheta(), grid_locator2=locator_theta) self.axes = Subplot(self.figure, 1, 1, 1, grid_helper=grid_helper1) self.transform = self.tephi_transform + self.axes.transData self.axes.axis['isotherm'] = self.axes.new_floating_axis(1, 0) self.axes.axis['theta'] = self.axes.new_floating_axis(0, 0) self.axes.axis['left'].get_helper().nth_coord_ticks = 0 self.axes.axis['left'].toggle(all=True) self.axes.axis['bottom'].get_helper().nth_coord_ticks = 1 self.axes.axis['bottom'].toggle(all=True) self.axes.axis['top'].get_helper().nth_coord_ticks = 0 self.axes.axis['top'].toggle(all=False) self.axes.axis['right'].get_helper().nth_coord_ticks = 1 self.axes.axis['right'].toggle(all=True) self.axes.gridlines.set_linestyle('solid') self.figure.add_subplot(self.axes) # Configure default axes. axis = self.axes.axis['left'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('baseline') axis.major_ticklabels.set_rotation(135) axis = self.axes.axis['right'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('baseline') axis.major_ticklabels.set_rotation(-135) self.axes.axis['top'].major_ticklabels.set_fontsize(10) axis = self.axes.axis['bottom'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_ha('left') axis.major_ticklabels.set_va('top') axis.major_ticklabels.set_rotation(-45) # Isotherms: lines of constant temperature (degC). axis = self.axes.axis['isotherm'] axis.set_axis_direction('right') axis.set_axislabel_direction('-') axis.major_ticklabels.set_rotation(90) axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('bottom') axis.major_ticklabels.set_color('grey') axis.major_ticklabels.set_visible(False) # turned-off # Dry adiabats: lines of constant potential temperature (degC). axis = self.axes.axis['theta'] axis.set_axis_direction('right') axis.set_axislabel_direction('+') axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('bottom') axis.major_ticklabels.set_color('grey') axis.major_ticklabels.set_visible(False) # turned-off axis.line.set_linewidth(3) axis.line.set_linestyle('--') # Lock down the aspect ratio. self.axes.set_aspect(1.) self.axes.grid(True) # Initialise the text formatter for the navigation status bar. self.axes.format_coord = self._status_bar # Factor in the tephigram transform. ISOBAR_TEXT['transform'] = self.transform WET_ADIABAT_TEXT['transform'] = self.transform MIXING_RATIO_TEXT['transform'] = self.transform # Create plot collections for the tephigram isopleths. func = partial(isopleths.isobar, MIN_THETA, MAX_THETA, self.axes, self.transform, ISOBAR_LINE) self._isobars = _PlotCollection(self.axes, ISOBAR_SPEC, MAX_PRESSURE, func, ISOBAR_TEXT, fixed=ISOBAR_FIXED, minimum=MIN_PRESSURE) func = partial(isopleths.wet_adiabat, MAX_PRESSURE, MIN_TEMPERATURE, self.axes, self.transform, WET_ADIABAT_LINE) self._wet_adiabats = _PlotCollection(self.axes, WET_ADIABAT_SPEC, MAX_WET_ADIABAT, func, WET_ADIABAT_TEXT, fixed=WET_ADIABAT_FIXED, minimum=MIN_WET_ADIABAT, xfocus=True) func = partial(isopleths.mixing_ratio, MIN_PRESSURE, MAX_PRESSURE, self.axes, self.transform, MIXING_RATIO_LINE) self._mixing_ratios = _PlotCollection(self.axes, MIXING_RATIO_SPEC, MIXING_RATIOS, func, MIXING_RATIO_TEXT, fixed=MIXING_RATIO_FIXED) # Initialise for the tephigram plot event handler. plt.connect('motion_notify_event', _handler) self.axes.tephigram = True self.axes.tephigram_original_delta_xlim = self.original_delta_xlim = DEFAULT_WIDTH self.axes.tephigram_transform = self.tephi_transform self.axes.tephigram_inverse = self.tephi_transform.inverted() self.axes.tephigram_isopleths = [self._isobars, self._wet_adiabats, self._mixing_ratios] # The tephigram profiles. self._profiles = [] self.axes.tephigram_profiles = self._profiles # Center the plot around the anchor extent. self._anchor = anchor if self._anchor is not None: self._anchor = np.asarray(anchor) if self._anchor.ndim != 2 or self._anchor.shape[-1] != 2 or \ len(self._anchor) != 2: msg = 'Invalid anchor, expecting [(bottom-left-pressure, ' \ 'bottom-left-temperature), (top-right-pressure, ' \ 'top-right-temperature)]' raise ValueError(msg) (bottom_pressure, bottom_temp), \ (top_pressure, top_temp) = self._anchor if (bottom_pressure - top_pressure) < 0: raise ValueError('Invalid anchor pressure range') if (bottom_temp - top_temp) < 0: raise ValueError('Invalid anchor temperature range') self._anchor = isopleths.Profile(anchor, self.axes) self._anchor.plot(visible=False) xlim, ylim = self._calculate_extents() self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) def plot(self, data, **kwargs): """ Plot the environmental lapse rate profile of the pressure and temperature data points. The pressure and temperature data points are transformed into potential temperature and temperature data points before plotting. By default, the tephigram will automatically center the plot around all profiles. .. warning:: Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. Args: * data: pressure and temperature pair data points. .. note:: All keyword arguments are passed through to :func:`matplotlib.pyplot.plot`. For example: .. plot:: :include-source: import matplotlib.pyplot as plt from edson import Tephigram tephi = Tephigram() data = [[1006, 26.4], [924, 20.3], [900, 19.8], [850, 14.5], [800, 12.9], [755, 8.3]] profile = tephi.plot(data, color='red', linestyle='--', linewidth=2, marker='o') barbs = [(10, 45, 900), (20, 60, 850), (25, 90, 800)] profile.barbs(barbs) plt.show() For associating wind barbs with an environmental lapse rate profile, see :meth:`~edson.isopleths.Profile.barbs`. """ profile = isopleths.Profile(data, self.axes) profile.plot(**kwargs) self._profiles.append(profile) # Center the tephigram plot around all the profiles. if self._anchor is None: xlim, ylim = self._calculate_extents(xfactor=.25, yfactor=.05) self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) # Refresh the tephigram plot isopleths. _refresh_isopleths(self.axes) # Show the plot legend. if 'label' in kwargs: font_properties = FontProperties(size='x-small') plt.legend(loc='upper left', fancybox=True, shadow=True, prop=font_properties) return profile def _status_bar(self, x_point, y_point): """Generate text for the interactive backend navigation status bar.""" temperature, theta = transforms.xy_to_temperature_theta(x_point, y_point) pressure, _ = transforms.temperature_theta_to_pressure_temperature(temperature, theta) xlim = self.axes.get_xlim() zoom = (xlim[1] - xlim[0]) / self.original_delta_xlim text = "T:%.2f, theta:%.2f, phi:%.2f (zoom:%.3f)" % (float(temperature), float(theta), float(pressure), zoom) return text def _calculate_extents(self, xfactor=None, yfactor=None): min_x = min_y = 1e10 max_x = max_y = -1e-10 profiles = self._profiles if self._anchor is not None: profiles = [self._anchor] for profile in profiles: xy_points = self.tephi_transform.transform(np.concatenate((profile.temperature.reshape(-1, 1), profile.theta.reshape(-1, 1)), axis=1)) x_points = xy_points[:, 0] y_points = xy_points[:, 1] min_x, min_y = np.min([min_x, np.min(x_points)]), np.min([min_y, np.min(y_points)]) max_x, max_y = np.max([max_x, np.max(x_points)]), np.max([max_y, np.max(y_points)]) if xfactor is not None: delta_x = max_x - min_x min_x, max_x = min_x - xfactor * delta_x, max_x + xfactor * delta_x if yfactor is not None: delta_y = max_y - min_y min_y, max_y = min_y - yfactor * delta_y, max_y + yfactor * delta_y return ([min_x, max_x], [min_y, max_y])
class MainPlotController (PlotController): """ A controller for the main plot canvas. """ # ------------------------------------------------------------ # Initialization and other internals # ------------------------------------------------------------ def __init__(self, app_controller, *args, **kwargs): self.labels = list() self.marker_lbls = list() self.scale = 1.0 self.stats = False self.position_setup = PositionSetup() self.app_controller = app_controller PlotController.__init__(self, *args, **kwargs) def setup_content(self): # Create subplot and add it to the figure: self.plot = Subplot(self.figure, 211, axisbg=(1.0, 1.0, 1.0, 0.0)) self.plot.set_autoscale_on(False) self.figure.add_axes(self.plot) # Connect events: self.canvas.mpl_connect('draw_event', self.fix_after_drawing) self.canvas.mpl_connect('resize_event', self.fix_after_drawing) self.update() # ------------------------------------------------------------ # Update methods # ------------------------------------------------------------ def update(self, clear=False, project=None, specimens=None): """ Updates the entire plot with the given information. """ if clear: self.plot.cla() if project and specimens: self.labels, self.marker_lbls = plot_specimens( self.plot, self.position_setup, project, specimens ) # get mixtures for the selected specimens: plot_mixtures(self.plot, project, [ mixture for mixture in project.mixtures if any(specimen in mixture.specimens for specimen in specimens) ]) update_axes( self.plot, self.position_setup, project, specimens ) self.draw() # ------------------------------------------------------------ # Plot position and size calculations # ------------------------------------------------------------ def _get_joint_bbox(self, container): renderer = self._find_renderer() bboxes = [] try: for text in container: bbox = text.get_window_extent(renderer=renderer) # the figure transform goes from relative coords->pixels and we # want the inverse of that bboxi = bbox.inverse_transformed(self.figure.transFigure) bboxes.append(bboxi) except RuntimeError: logger.exception("Caught unhandled exception when joining boundig boxes") return None # don't continue # this is the bbox that bounds all the bboxes, again in relative # figure coords if len(bboxes) > 0: bbox = transforms.Bbox.union(bboxes) return bbox else: return None def _find_renderer(self): if hasattr(self.figure.canvas, "get_renderer"): #Some backends, such as TkAgg, have the get_renderer method, which #makes this easy. renderer = self.figure.canvas.get_renderer() else: #Others don't, but since this is called in the 'fix_after_drawing', # a renderer has been created, so we can get it like this: renderer = self.figure._cachedRenderer return renderer def fix_after_drawing(self, *args): """ Fixes alignment issues due to longer labels or smaller windows Is executed after an initial draw event, since we can then retrieve the actual label dimensions and shift/resize the plot accordingly. """ # Fix left side for wide specimen labels: if len(self.labels) > 0: bbox = self._get_joint_bbox(self.labels) self.position_setup.left = 0.05 + bbox.width # Fix top for high marker labels: if len(self.marker_lbls) > 0: bbox = self._get_joint_bbox([ label for label, flag, _ in self.marker_lbls if flag ]) if bbox is not None: self.position_setup.top = 1.0 - (0.05 + bbox.height) #Figure top - marker margin # Fix bottom for x-axis label: bottom_label = self.plot.axis["bottom"].label if bottom_label is not None: bbox = self._get_joint_bbox([bottom_label]) if bbox is not None: self.position_setup.bottom = 0.10 - min(bbox.ymin, 0.0) # Calculate new plot position & set it: plot_pos = self.position_setup.position self.plot.set_position(plot_pos) # Adjust specimen label position for label in self.labels: label.set_x(plot_pos[0] - 0.025) # Adjust marker label position for label, flag, y_offset in self.marker_lbls: if flag: label.set_y(plot_pos[1] + plot_pos[3] + y_offset - 0.025) self.figure.canvas.draw() return False pass # end of class
def __init__(self, figure=None, isotherm_locator=None, dry_adiabat_locator=None, anchor=None): """ Initialise the tephigram transformation and plot axes. Kwargs: * figure: An existing :class:`matplotlib.figure.Figure` instance for the tephigram plot. If a figure is not provided, a new figure will be created by default. * isotherm_locator: A :class:`edson.Locator` instance or a numeric step size for the isotherm lines. * dry_adiabat_locator: A :class:`edson.Locator` instance or a numeric step size for the dry adiabat lines. * anchor: A sequence of two pressure, temperature pairs specifying the extent of the tephigram plot in terms of the bottom left hand corner and the top right hand corner. Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. For example: .. plot:: :include-source: import matplotlib.pyplot as plt import os.path import edson from edson import Tephigram dew_point = os.path.join(edson.RESOURCES_DIR, 'tephigram', 'dews.txt') dry_bulb = os.path.join(edson.RESOURCES_DIR, 'tephigram', 'temps.txt') dew_data, temp_data = edson.loadtxt(dew_point, dry_bulb) dews = zip(dew_data.pressure, dew_data.temperature) temps = zip(temp_data.pressure, temp_data.temperature) tephi = Tephigram() tephi.plot(dews, label='Dew-point', color='blue', linewidth=2, marker='s') tephi.plot(temps, label='Dry-bulb', color='red', linewidth=2, marker='o') plt.show() """ if not figure: # Create a default figure. self.figure = plt.figure(0, figsize=(9, 9)) else: self.figure = figure # Configure the locators. if isotherm_locator and not isinstance(isotherm_locator, Locator): if not isinstance(isotherm_locator, numbers.Number): raise ValueError('Invalid isotherm locator') locator_isotherm = Locator(isotherm_locator) else: locator_isotherm = isotherm_locator if dry_adiabat_locator and not isinstance(dry_adiabat_locator, Locator): if not isinstance(dry_adiabat_locator, numbers.Number): raise ValueError('Invalid dry adiabat locator') locator_theta = Locator(dry_adiabat_locator) else: locator_theta = dry_adiabat_locator # Define the tephigram coordinate-system transformation. self.tephi_transform = transforms.TephiTransform() grid_helper1 = GridHelperCurveLinear(self.tephi_transform, tick_formatter1=_FormatterIsotherm(), grid_locator1=locator_isotherm, tick_formatter2=_FormatterTheta(), grid_locator2=locator_theta) self.axes = Subplot(self.figure, 1, 1, 1, grid_helper=grid_helper1) self.transform = self.tephi_transform + self.axes.transData self.axes.axis['isotherm'] = self.axes.new_floating_axis(1, 0) self.axes.axis['theta'] = self.axes.new_floating_axis(0, 0) self.axes.axis['left'].get_helper().nth_coord_ticks = 0 self.axes.axis['left'].toggle(all=True) self.axes.axis['bottom'].get_helper().nth_coord_ticks = 1 self.axes.axis['bottom'].toggle(all=True) self.axes.axis['top'].get_helper().nth_coord_ticks = 0 self.axes.axis['top'].toggle(all=False) self.axes.axis['right'].get_helper().nth_coord_ticks = 1 self.axes.axis['right'].toggle(all=True) self.axes.gridlines.set_linestyle('solid') self.figure.add_subplot(self.axes) # Configure default axes. axis = self.axes.axis['left'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('baseline') axis.major_ticklabels.set_rotation(135) axis = self.axes.axis['right'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('baseline') axis.major_ticklabels.set_rotation(-135) self.axes.axis['top'].major_ticklabels.set_fontsize(10) axis = self.axes.axis['bottom'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_ha('left') axis.major_ticklabels.set_va('top') axis.major_ticklabels.set_rotation(-45) # Isotherms: lines of constant temperature (degC). axis = self.axes.axis['isotherm'] axis.set_axis_direction('right') axis.set_axislabel_direction('-') axis.major_ticklabels.set_rotation(90) axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('bottom') axis.major_ticklabels.set_color('grey') axis.major_ticklabels.set_visible(False) # turned-off # Dry adiabats: lines of constant potential temperature (degC). axis = self.axes.axis['theta'] axis.set_axis_direction('right') axis.set_axislabel_direction('+') axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('bottom') axis.major_ticklabels.set_color('grey') axis.major_ticklabels.set_visible(False) # turned-off axis.line.set_linewidth(3) axis.line.set_linestyle('--') # Lock down the aspect ratio. self.axes.set_aspect(1.) self.axes.grid(True) # Initialise the text formatter for the navigation status bar. self.axes.format_coord = self._status_bar # Factor in the tephigram transform. ISOBAR_TEXT['transform'] = self.transform WET_ADIABAT_TEXT['transform'] = self.transform MIXING_RATIO_TEXT['transform'] = self.transform # Create plot collections for the tephigram isopleths. func = partial(isopleths.isobar, MIN_THETA, MAX_THETA, self.axes, self.transform, ISOBAR_LINE) self._isobars = _PlotCollection(self.axes, ISOBAR_SPEC, MAX_PRESSURE, func, ISOBAR_TEXT, fixed=ISOBAR_FIXED, minimum=MIN_PRESSURE) func = partial(isopleths.wet_adiabat, MAX_PRESSURE, MIN_TEMPERATURE, self.axes, self.transform, WET_ADIABAT_LINE) self._wet_adiabats = _PlotCollection(self.axes, WET_ADIABAT_SPEC, MAX_WET_ADIABAT, func, WET_ADIABAT_TEXT, fixed=WET_ADIABAT_FIXED, minimum=MIN_WET_ADIABAT, xfocus=True) func = partial(isopleths.mixing_ratio, MIN_PRESSURE, MAX_PRESSURE, self.axes, self.transform, MIXING_RATIO_LINE) self._mixing_ratios = _PlotCollection(self.axes, MIXING_RATIO_SPEC, MIXING_RATIOS, func, MIXING_RATIO_TEXT, fixed=MIXING_RATIO_FIXED) # Initialise for the tephigram plot event handler. plt.connect('motion_notify_event', _handler) self.axes.tephigram = True self.axes.tephigram_original_delta_xlim = self.original_delta_xlim = DEFAULT_WIDTH self.axes.tephigram_transform = self.tephi_transform self.axes.tephigram_inverse = self.tephi_transform.inverted() self.axes.tephigram_isopleths = [self._isobars, self._wet_adiabats, self._mixing_ratios] # The tephigram profiles. self._profiles = [] self.axes.tephigram_profiles = self._profiles # Center the plot around the anchor extent. self._anchor = anchor if self._anchor is not None: self._anchor = np.asarray(anchor) if self._anchor.ndim != 2 or self._anchor.shape[-1] != 2 or \ len(self._anchor) != 2: msg = 'Invalid anchor, expecting [(bottom-left-pressure, ' \ 'bottom-left-temperature), (top-right-pressure, ' \ 'top-right-temperature)]' raise ValueError(msg) (bottom_pressure, bottom_temp), \ (top_pressure, top_temp) = self._anchor if (bottom_pressure - top_pressure) < 0: raise ValueError('Invalid anchor pressure range') if (bottom_temp - top_temp) < 0: raise ValueError('Invalid anchor temperature range') self._anchor = isopleths.Profile(anchor, self.axes) self._anchor.plot(visible=False) xlim, ylim = self._calculate_extents() self.axes.set_xlim(xlim) self.axes.set_ylim(ylim)
x_T_levels = [x_from_Tp(Ti, p_all) for Ti in T_levels] x_thetas = [x_from_Tp(Bolton.theta_dry(theta_i, p_all),p_all) for theta_i in theta_levels] x_mixing_ratios = [x_from_Tp((Bolton.mixing_ratio_line(p_all,MRi)+C_to_K),p_all) for MRi in mixing_ratios] mesh_T, mesh_p = np.meshgrid( np.arange(-60.0, T_levels.max()-C_to_K+0.1, 0.1), p_all) theta_ep_mesh = Bolton.theta_ep_field(mesh_T, mesh_p) # Plotting Code! skew_grid_helper = GridHelperCurveLinear((from_thermo, to_thermo)) fig = plt.figure() ax = Subplot(fig, 1, 1, 1, grid_helper=skew_grid_helper) fig.add_subplot(ax) for yi in y_p_levels: ax.plot((x_min, x_max), (yi,yi), color = (1.0, 0.8, 0.8)) for x_T in x_T_levels: ax.plot(x_T, y_all_p, color=(1.0, 0.5, 0.5)) for x_theta in x_thetas: ax.plot(x_theta, y_all_p, color=(1.0, 0.7, 0.7)) for x_mixing_ratio in x_mixing_ratios: good = p_all >= 600 # restrict mixing ratio lines to below 600 mb ax.plot(x_mixing_ratio[good], y_all_p[good], color = (0.8, 0.8, 0.6))
class Tephigram: """ Generate a tephigram of one or more pressure and temperature data sets. """ def __init__( self, figure=None, isotherm_locator=None, dry_adiabat_locator=None, anchor=None, ): """ Initialise the tephigram transformation and plot axes. Kwargs: * figure: An existing :class:`matplotlib.figure.Figure` instance for the tephigram plot. If a figure is not provided, a new figure will be created by default. * isotherm_locator: A :class:`tephi.Locator` instance or a numeric step size for the isotherm lines. * dry_adiabat_locator: A :class:`tephi.Locator` instance or a numeric step size for the dry adiabat lines. * anchor: A sequence of two pressure, temperature pairs specifying the extent of the tephigram plot in terms of the bottom left hand corner and the top right hand corner. Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. For example: .. plot:: :include-source: import matplotlib.pyplot as plt from numpy import column_stack import os.path import tephi from tephi import Tephigram dew_point = os.path.join(tephi.DATA_DIR, 'dews.txt') dry_bulb = os.path.join(tephi.DATA_DIR, 'temps.txt') dew_data, temp_data = tephi.loadtxt(dew_point, dry_bulb) dews = column_stack((dew_data.pressure, dew_data.temperature)) temps = column_stack((temp_data.pressure, temp_data.temperature)) tpg = Tephigram() tpg.plot(dews, label='Dew-point', color='blue', linewidth=2) tpg.plot(temps, label='Dry-bulb', color='red', linewidth=2) plt.show() """ if not figure: # Create a default figure. self.figure = plt.figure(0, figsize=(9, 9)) else: self.figure = figure # Configure the locators. if isotherm_locator and not isinstance(isotherm_locator, Locator): if not isinstance(isotherm_locator, numbers.Number): raise ValueError("Invalid isotherm locator") locator_isotherm = Locator(isotherm_locator) else: locator_isotherm = isotherm_locator if dry_adiabat_locator and not isinstance(dry_adiabat_locator, Locator): if not isinstance(dry_adiabat_locator, numbers.Number): raise ValueError("Invalid dry adiabat locator") locator_theta = Locator(dry_adiabat_locator) else: locator_theta = dry_adiabat_locator # Define the tephigram coordinate-system transformation. self.tephi_transform = transforms.TephiTransform() ghelper = GridHelperCurveLinear( self.tephi_transform, tick_formatter1=_FormatterIsotherm(), grid_locator1=locator_isotherm, tick_formatter2=_FormatterTheta(), grid_locator2=locator_theta, ) self.axes = Subplot(self.figure, 1, 1, 1, grid_helper=ghelper) self.transform = self.tephi_transform + self.axes.transData self.axes.axis["isotherm"] = self.axes.new_floating_axis(1, 0) self.axes.axis["theta"] = self.axes.new_floating_axis(0, 0) self.axes.axis["left"].get_helper().nth_coord_ticks = 0 self.axes.axis["left"].toggle(all=True) self.axes.axis["bottom"].get_helper().nth_coord_ticks = 1 self.axes.axis["bottom"].toggle(all=True) self.axes.axis["top"].get_helper().nth_coord_ticks = 0 self.axes.axis["top"].toggle(all=False) self.axes.axis["right"].get_helper().nth_coord_ticks = 1 self.axes.axis["right"].toggle(all=True) self.axes.gridlines.set_linestyle("solid") self.figure.add_subplot(self.axes) # Configure default axes. axis = self.axes.axis["left"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("baseline") axis.major_ticklabels.set_rotation(135) axis = self.axes.axis["right"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("baseline") axis.major_ticklabels.set_rotation(-135) self.axes.axis["top"].major_ticklabels.set_fontsize(10) axis = self.axes.axis["bottom"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_ha("left") axis.major_ticklabels.set_va("top") axis.major_ticklabels.set_rotation(-45) # Isotherms: lines of constant temperature (degC). axis = self.axes.axis["isotherm"] axis.set_axis_direction("right") axis.set_axislabel_direction("-") axis.major_ticklabels.set_rotation(90) axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("bottom") axis.major_ticklabels.set_color("grey") axis.major_ticklabels.set_visible(False) # turned-off # Dry adiabats: lines of constant potential temperature (degC). axis = self.axes.axis["theta"] axis.set_axis_direction("right") axis.set_axislabel_direction("+") axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("bottom") axis.major_ticklabels.set_color("grey") axis.major_ticklabels.set_visible(False) # turned-off axis.line.set_linewidth(3) axis.line.set_linestyle("--") # Lock down the aspect ratio. self.axes.set_aspect(1.0) self.axes.grid(True) # Initialise the text formatter for the navigation status bar. self.axes.format_coord = self._status_bar # Factor in the tephigram transform. ISOBAR_TEXT["transform"] = self.transform WET_ADIABAT_TEXT["transform"] = self.transform MIXING_RATIO_TEXT["transform"] = self.transform # Create plot collections for the tephigram isopleths. func = partial( isopleths.isobar, MIN_THETA, MAX_THETA, self.axes, self.transform, ISOBAR_LINE, ) self._isobars = _PlotCollection( self.axes, ISOBAR_SPEC, MAX_PRESSURE, func, ISOBAR_TEXT, fixed=ISOBAR_FIXED, minimum=MIN_PRESSURE, ) func = partial( isopleths.wet_adiabat, MAX_PRESSURE, MIN_TEMPERATURE, self.axes, self.transform, WET_ADIABAT_LINE, ) self._wet_adiabats = _PlotCollection( self.axes, WET_ADIABAT_SPEC, MAX_WET_ADIABAT, func, WET_ADIABAT_TEXT, fixed=WET_ADIABAT_FIXED, minimum=MIN_WET_ADIABAT, xfocus=True, ) func = partial( isopleths.mixing_ratio, MIN_PRESSURE, MAX_PRESSURE, self.axes, self.transform, MIXING_RATIO_LINE, ) self._mixing_ratios = _PlotCollection( self.axes, MIXING_RATIO_SPEC, MIXING_RATIOS, func, MIXING_RATIO_TEXT, fixed=MIXING_RATIO_FIXED, ) # Initialise for the tephigram plot event handler. plt.connect("motion_notify_event", _handler) self.axes.tephigram = True self.axes.tephigram_original_delta_xlim = DEFAULT_WIDTH self.original_delta_xlim = DEFAULT_WIDTH self.axes.tephigram_transform = self.tephi_transform self.axes.tephigram_inverse = self.tephi_transform.inverted() self.axes.tephigram_isopleths = [ self._isobars, self._wet_adiabats, self._mixing_ratios, ] # The tephigram profiles. self._profiles = [] self.axes.tephigram_profiles = self._profiles # Center the plot around the anchor extent. self._anchor = anchor if self._anchor is not None: self._anchor = np.asarray(anchor) if (self._anchor.ndim != 2 or self._anchor.shape[-1] != 2 or len(self._anchor) != 2): msg = ("Invalid anchor, expecting [(bottom-left-pressure, " "bottom-left-temperature), (top-right-pressure, " "top-right-temperature)]") raise ValueError(msg) ( (bottom_pressure, bottom_temp), (top_pressure, top_temp), ) = self._anchor if (bottom_pressure - top_pressure) < 0: raise ValueError("Invalid anchor pressure range") if (bottom_temp - top_temp) < 0: raise ValueError("Invalid anchor temperature range") self._anchor = isopleths.Profile(anchor, self.axes) self._anchor.plot(visible=False) xlim, ylim = self._calculate_extents() self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) def plot(self, data, **kwargs): """ Plot the environmental lapse rate profile of the pressure and temperature data points. The pressure and temperature data points are transformed into potential temperature and temperature data points before plotting. By default, the tephigram will automatically center the plot around all profiles. .. warning:: Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. Args: * data: pressure and temperature pair data points. .. note:: All keyword arguments are passed through to :func:`matplotlib.pyplot.plot`. For example: .. plot:: :include-source: import matplotlib.pyplot as plt from tephi import Tephigram tpg = Tephigram() data = [[1006, 26.4], [924, 20.3], [900, 19.8], [850, 14.5], [800, 12.9], [755, 8.3]] profile = tpg.plot(data, color='red', linestyle='--', linewidth=2, marker='o') barbs = [(10, 45, 900), (20, 60, 850), (25, 90, 800)] profile.barbs(barbs) plt.show() For associating wind barbs with an environmental lapse rate profile, see :meth:`~tephi.isopleths.Profile.barbs`. """ profile = isopleths.Profile(data, self.axes) profile.plot(**kwargs) self._profiles.append(profile) # Center the tephigram plot around all the profiles. if self._anchor is None: xlim, ylim = self._calculate_extents(xfactor=0.25, yfactor=0.05) self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) # Refresh the tephigram plot isopleths. _refresh_isopleths(self.axes) # Show the plot legend. if "label" in kwargs: font_properties = FontProperties(size="x-small") plt.legend( loc="upper left", fancybox=True, shadow=True, prop=font_properties, ) return profile def _status_bar(self, x_point, y_point): """Generate text for the interactive backend navigation status bar.""" temperature, theta = transforms.convert_xy2Tt(x_point, y_point) pressure, _ = transforms.convert_Tt2pT(temperature, theta) xlim = self.axes.get_xlim() zoom = (xlim[1] - xlim[0]) / self.original_delta_xlim msg = "T:{:.2f}, theta:{:.2f}, phi:{:.2f} (zoom:{:.3f})" text = msg.format(float(temperature), float(theta), float(pressure), zoom) return text def _calculate_extents(self, xfactor=None, yfactor=None): min_x = min_y = 1e10 max_x = max_y = -1e-10 profiles = self._profiles transform = self.tephi_transform.transform if self._anchor is not None: profiles = [self._anchor] for profile in profiles: temperature = profile.temperature.reshape(-1, 1) theta = profile.theta.reshape(-1, 1) xy_points = transform(np.concatenate((temperature, theta), axis=1)) x_points = xy_points[:, 0] y_points = xy_points[:, 1] min_x = np.min([min_x, np.min(x_points)]) min_y = np.min([min_y, np.min(y_points)]) max_x = np.max([max_x, np.max(x_points)]) max_y = np.max([max_y, np.max(y_points)]) if xfactor is not None: delta_x = max_x - min_x min_x, max_x = min_x - xfactor * delta_x, max_x + xfactor * delta_x if yfactor is not None: delta_y = max_y - min_y min_y, max_y = min_y - yfactor * delta_y, max_y + yfactor * delta_y return ([min_x, max_x], [min_y, max_y])
class MainPlotController(object): """ A controller for the main plot canvas. Sets up the widgets and has image exporting functionality. """ file_filters = ("Portable Network Graphics (PNG)", "*.png"), \ ("Scalable Vector Graphics (SVG)", "*.svg"), \ ("Portable Document Format (PDF)", "*.pdf") _canvas = None @property def canvas(self): if not self._canvas: self.setup_figure() self.setup_canvas() self.setup_content() return self._canvas # ------------------------------------------------------------ # View integration getters # ------------------------------------------------------------ def get_toolbar_widget(self, window): return NavigationToolbar(self.canvas, window) def get_canvas_widget(self): return self.canvas # ------------------------------------------------------------ # Initialization and other internals # ------------------------------------------------------------ def __init__(self, status_callback, marker_callback, *args, **kwargs): self.setup_layout_cache() self.setup_figure() self.setup_canvas() self.setup_content(status_callback, marker_callback) def setup_layout_cache(self): self.position_setup = PositionSetup() self.labels = list() self.marker_lbls = list() self._proxies = dict() self.scale = 1.0 self.stats = False self._last_pos = None def setup_figure(self): self.figure = Figure(dpi=72, facecolor="#FFFFFF", linewidth=0) self.figure.subplots_adjust(hspace=0.0, wspace=0.0) def setup_canvas(self): self._canvas = FigureCanvasGTK(self.figure) def setup_content(self, status_callback, marker_callback): # Create subplot and add it to the figure: self.plot = Subplot(self.figure, 211, facecolor=(1.0, 1.0, 1.0, 0.0)) self.plot.set_autoscale_on(False) self.figure.add_axes(self.plot) # Connect events: self.canvas.mpl_connect('draw_event', self.fix_after_drawing) self.canvas.mpl_connect('resize_event', self.fix_after_drawing) self.mtc = MotionTracker(self, status_callback) self.cc = ClickCatcher(self, marker_callback) #self.update() # ------------------------------------------------------------ # Update methods # ------------------------------------------------------------ def draw(self): self._last_pos = self.fix_before_drawing() self.figure.canvas.draw() def fix_after_drawing(self, *args): _new_pos = self.fix_before_drawing() if _new_pos != self._last_pos: self.figure.canvas.draw() self._last_pos = _new_pos return False def fix_before_drawing(self, *args): """ Fixes alignment issues due to longer labels or smaller windows Is executed after an initial draw event, since we can then retrieve the actual label dimensions and shift/resize the plot accordingly. """ renderer = get_renderer(self.figure) if not renderer or not self._canvas.get_realized(): return False # Fix left side for wide specimen labels: if len(self.labels) > 0: bbox = self._get_joint_bbox(self.labels, renderer) if bbox is not None: self.position_setup.left = self.position_setup.default_left + bbox.width # Fix top for high marker labels: if len(self.marker_lbls) > 0: bbox = self._get_joint_bbox([ label for label, flag, _ in self.marker_lbls if flag ], renderer) if bbox is not None: self.position_setup.top = self.position_setup.default_top - bbox.height # Fix bottom for x-axis label: bottom_label = self.plot.axis["bottom"].label if bottom_label is not None: bbox = self._get_joint_bbox([bottom_label], renderer) if bbox is not None: self.position_setup.bottom = self.position_setup.default_bottom + (bbox.ymax - bbox.ymin) * 2.0 # somehow we need this? # Calculate new plot position & set it: plot_pos = self.position_setup.position self.plot.set_position(plot_pos) # Adjust specimen label position for label in self.labels: label.set_x(plot_pos[0] - 0.025) # Adjust marker label position for label, flag, y_offset in self.marker_lbls: if flag: newy = plot_pos[1] + plot_pos[3] + y_offset - 0.025 label.set_y(newy) _new_pos = self.position_setup.to_string() return _new_pos def update(self, clear=False, project=None, specimens=None): """ Updates the entire plot with the given information. """ if clear: self.plot.cla() if project and specimens: self.labels, self.marker_lbls = plot_specimens( self.plot, self.position_setup, self.cc, project, specimens ) # get mixtures for the selected specimens: plot_mixtures(self.plot, project, [ mixture for mixture in project.mixtures if any(specimen in mixture.specimens for specimen in specimens) ]) update_axes( self.plot, self.position_setup, project, specimens ) self.draw() # ------------------------------------------------------------ # Plot position and size calculations # ------------------------------------------------------------ def _get_joint_bbox(self, container, renderer): bboxes = [] try: for text in container: bbox = text.get_window_extent(renderer=renderer) # the figure transform goes from relative coords->pixels and we # want the inverse of that bboxi = bbox.inverse_transformed(self.figure.transFigure) bboxes.append(bboxi) except (RuntimeError, ValueError): logger.exception("Caught unhandled exception when joining boundig boxes") return None # don't continue # this is the bbox that bounds all the bboxes, again in relative # figure coords if len(bboxes) > 0: bbox = transforms.Bbox.union(bboxes) return bbox else: return None # ------------------------------------------------------------ # Graph exporting # ------------------------------------------------------------ def save(self, parent=None, current_name="graph", size="auto", num_specimens=1, offset=0.75): """ Displays a save dialog to export an image from the current plot. """ # Parse arguments: width, height = 0, 0 if size == "auto": descr, width, height, dpi = settings.OUTPUT_PRESETS[0] else: width, height, dpi = list(map(float, size.replace("@", "x").split("x"))) # Load gui: builder = Gtk.Builder() builder.add_from_file(resource_filename("pyxrd.specimen", "glade/save_graph_size.glade")) # FIXME move this to this namespace!! size_expander = builder.get_object("size_expander") cmb_presets = builder.get_object("cmb_presets") # Setup combo with presets: cmb_store = Gtk.ListStore(str, int, int, float) for row in settings.OUTPUT_PRESETS: cmb_store.append(row) cmb_presets.clear() cmb_presets.set_model(cmb_store) cell = Gtk.CellRendererText() cmb_presets.pack_start(cell, True) cmb_presets.add_attribute(cell, 'text', 0) def on_cmb_changed(cmb, *args): itr = cmb.get_active_iter() w, h, d = cmb_store.get(itr, 1, 2, 3) entry_w.set_text(str(w)) entry_h.set_text(str(h)) entry_dpi.set_text(str(d)) cmb_presets.connect('changed', on_cmb_changed) # Setup input boxes: entry_w = builder.get_object("entry_width") entry_h = builder.get_object("entry_height") entry_dpi = builder.get_object("entry_dpi") entry_w.set_text(str(width)) entry_h.set_text(str(height)) entry_dpi.set_text(str(dpi)) # What to do when the user wants to save this: def on_accept(dialog): # Get the width, height & dpi width = float(entry_w.get_text()) height = float(entry_h.get_text()) dpi = float(entry_dpi.get_text()) i_width, i_height = width / dpi, height / dpi # Save it all right! self.save_figure(dialog.filename, dpi, i_width, i_height) # Ask the user where, how and if he wants to save: DialogFactory.get_save_dialog( "Save Graph", parent=parent, filters=self.file_filters, current_name=current_name, extra_widget=size_expander ).run(on_accept) def save_figure(self, filename, dpi, i_width, i_height): """ Save the current plot Arguments: filename: the filename to save to (either .png, .pdf or .svg) dpi: Dots-Per-Inch resolution i_width: the width in inch i_height: the height in inch """ # Get original settings: original_dpi = self.figure.get_dpi() original_width, original_height = self.figure.get_size_inches() # Set everything according to the user selection: self.figure.set_dpi(dpi) self.figure.set_size_inches((i_width, i_height)) self.figure.canvas.draw() # replot bbox_inches = matplotlib.transforms.Bbox.from_bounds(0, 0, i_width, i_height) # Save the figure: self.figure.savefig(filename, dpi=dpi, bbox_inches=bbox_inches) # Put everything back the way it was: self.figure.set_dpi(original_dpi) self.figure.set_size_inches((original_width, original_height)) self.figure.canvas.draw() # replot pass # end of class
s=4, marker='s', vmin=vmin, vmax=vmax, latlon=True, rasterized=True) smap.draw_inset_colorbar(label=r'mag arcsec$^{-2}$') ax1 = plt.gca() #ax1.annotate(f"{band}-band",(0.05,0.9),xycoords='axes fraction', # fontsize=14) ax1.axis['right'].major_ticklabels.set_visible(False) #ax1.axis['top'].major_ticklabels.set_visible(False) # Plot histogram ax2 = Subplot(fig, gridspec[2]) fig.add_subplot(ax2) plt.sca(ax2) #fig.add_subplot(gridspec[2]) bins = np.linspace(x['sb'].min(), x['sb'].max(), 35) ret = draw_hist(hpxmap, label=band, color=v, peak=False, bins=bins, density=True) ax2.yaxis.set_major_locator(MaxNLocator(6, prune='both')) ax2.axis['left'].major_ticklabels.set_visible(True) ax2.axis['right'].major_ticklabels.set_visible(False) ax2.axis['left'].label.set_visible(True)
def curvelinear_test1(fig): """ grid for custom transform. """ def tr(x, y): x, y = np.asarray(x), np.asarray(y) return x, y-x def inv_tr(x,y): x, y = np.asarray(x), np.asarray(y) return x, y+x grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax1 = Subplot(fig, 1, 2, 1, grid_helper=grid_helper) fig.add_subplot(ax1) xx, yy = tr([3, 6], [5.0, 10.]) ax1.plot(xx, yy) ax1.set_aspect(1.) ax1.set_xlim(0, 10.) ax1.set_ylim(0, 10.) ax1.axis["t"]=ax1.new_floating_axis(0, 3.) ax1.axis["t2"]=ax1.new_floating_axis(1, 7.) ax1.grid(True)
class DGSPlannerGUI(QtGui.QWidget): def __init__(self,ol=None,parent=None): # pylint: disable=unused-argument,super-on-old-class super(DGSPlannerGUI,self).__init__(parent) #OrientedLattice if ValidateOL(ol): self.ol=ol else: self.ol=mantid.geometry.OrientedLattice() self.masterDict=dict() #holds info about instrument and ranges self.updatedInstrument=False self.updatedOL=False self.wg=None #workspace group self.instrumentWidget=InstrumentSetupWidget.InstrumentSetupWidget(self) self.setLayout(QtGui.QHBoxLayout()) controlLayout=QtGui.QVBoxLayout() controlLayout.addWidget(self.instrumentWidget) self.ublayout=QtGui.QHBoxLayout() self.classic=ClassicUBInputWidget.ClassicUBInputWidget(self.ol) self.ublayout.addWidget(self.classic,alignment=QtCore.Qt.AlignTop,stretch=1) self.matrix=MatrixUBInputWidget.MatrixUBInputWidget(self.ol) self.ublayout.addWidget(self.matrix,alignment=QtCore.Qt.AlignTop,stretch=1) controlLayout.addLayout(self.ublayout) self.dimensionWidget=DimensionSelectorWidget.DimensionSelectorWidget(self) controlLayout.addWidget(self.dimensionWidget) plotControlLayout=QtGui.QGridLayout() self.plotButton=QtGui.QPushButton("Plot",self) self.oplotButton=QtGui.QPushButton("Overplot",self) self.helpButton=QtGui.QPushButton("?",self) self.colorLabel=QtGui.QLabel('Color by angle',self) self.colorButton=QtGui.QCheckBox(self) self.colorButton.toggle() self.aspectLabel=QtGui.QLabel('Aspect ratio 1:1',self) self.aspectButton=QtGui.QCheckBox(self) self.saveButton=QtGui.QPushButton("Save Figure",self) plotControlLayout.addWidget(self.plotButton,0,0) plotControlLayout.addWidget(self.oplotButton,0,1) plotControlLayout.addWidget(self.colorLabel,0,2,QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.colorButton,0,3) plotControlLayout.addWidget(self.aspectLabel,0,4,QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.aspectButton,0,5) plotControlLayout.addWidget(self.helpButton,0,6) plotControlLayout.addWidget(self.saveButton,0,7) controlLayout.addLayout(plotControlLayout) self.layout().addLayout(controlLayout) #figure self.figure=Figure() self.figure.patch.set_facecolor('white') self.canvas=FigureCanvas(self.figure) self.grid_helper = GridHelperCurveLinear((self.tr, self.inv_tr)) self.trajfig = Subplot(self.figure, 1, 1, 1, grid_helper=self.grid_helper) self.trajfig.hold(True) self.figure.add_subplot(self.trajfig) self.layout().addWidget(self.canvas) self.needToClear=False self.saveDir='' #connections self.matrix.UBmodel.changed.connect(self.updateUB) self.matrix.UBmodel.changed.connect(self.classic.updateOL) self.classic.changed.connect(self.matrix.UBmodel.updateOL) self.classic.changed.connect(self.updateUB) self.instrumentWidget.changed.connect(self.updateParams) self.dimensionWidget.changed.connect(self.updateParams) self.plotButton.clicked.connect(self.updateFigure) self.oplotButton.clicked.connect(self.updateFigure) self.helpButton.clicked.connect(self.help) self.saveButton.clicked.connect(self.save) #force an update of values self.instrumentWidget.updateAll() self.dimensionWidget.updateChanges() #help self.assistantProcess = QtCore.QProcess(self) # pylint: disable=protected-access self.collectionFile=os.path.join(mantid._bindir,'../docs/qthelp/MantidProject.qhc') version = ".".join(mantid.__version__.split(".")[:2]) self.qtUrl='qthelp://org.sphinx.mantidproject.'+version+'/doc/interfaces/DGSPlanner.html' self.externalUrl='http://docs.mantidproject.org/nightly/interfaces/DGSPlanner.html' #control for cancel button self.iterations=0 self.progress_canceled=False #register startup mantid.UsageService.registerFeatureUsage("Interface","DGSPlanner",False) @QtCore.pyqtSlot(mantid.geometry.OrientedLattice) def updateUB(self,ol): self.ol=ol self.updatedOL=True self.trajfig.clear() @QtCore.pyqtSlot(dict) def updateParams(self,d): if self.sender() is self.instrumentWidget: self.updatedInstrument=True if 'dimBasis' in d and 'dimBasis' in self.masterDict and d['dimBasis']!=self.masterDict['dimBasis']: self.needToClear=True if 'dimIndex' in d and 'dimIndex' in self.masterDict and d['dimIndex']!=self.masterDict['dimIndex']: self.needToClear=True self.masterDict.update(copy.deepcopy(d)) def help(self): try: import pymantidplot pymantidplot.proxies.showCustomInterfaceHelp('DGSPlanner') except ImportError: self.assistantProcess.close() self.assistantProcess.waitForFinished() helpapp = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.BinariesPath) + QtCore.QDir.separator() helpapp += 'assistant' args = ['-enableRemoteControl', '-collectionFile',self.collectionFile,'-showUrl',self.qtUrl] if os.path.isfile(helpapp) and os.path.isfile(self.collectionFile): self.assistantProcess.close() self.assistantProcess.waitForFinished() self.assistantProcess.start(helpapp, args) else: mqt.MantidQt.API.MantidDesktopServices.openUrl(QtCore.QUrl(self.externalUrl)) def closeEvent(self,event): self.assistantProcess.close() self.assistantProcess.waitForFinished() event.accept() # pylint: disable=too-many-locals def updateFigure(self): # pylint: disable=too-many-branches if self.updatedInstrument or self.progress_canceled: self.progress_canceled=False #get goniometer settings first gonioAxis0values=numpy.arange(self.masterDict['gonioMinvals'][0],self.masterDict['gonioMaxvals'][0] +0.1*self.masterDict['gonioSteps'][0],self.masterDict['gonioSteps'][0]) gonioAxis1values=numpy.arange(self.masterDict['gonioMinvals'][1],self.masterDict['gonioMaxvals'][1] +0.1*self.masterDict['gonioSteps'][1],self.masterDict['gonioSteps'][1]) gonioAxis2values=numpy.arange(self.masterDict['gonioMinvals'][2],self.masterDict['gonioMaxvals'][2] +0.1*self.masterDict['gonioSteps'][2],self.masterDict['gonioSteps'][2]) self.iterations=len(gonioAxis0values)*len(gonioAxis1values)*len(gonioAxis2values) if self.iterations>10: reply = QtGui.QMessageBox.warning(self, 'Goniometer',"More than 10 goniometer settings. This might be long.\n" "Are you sure you want to proceed?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply==QtGui.QMessageBox.No: return if self.wg is not None: mantid.simpleapi.DeleteWorkspace(self.wg) mantid.simpleapi.LoadEmptyInstrument(mantid.api.ExperimentInfo.getInstrumentFilename(self.masterDict['instrument']), OutputWorkspace="__temp_instrument") if self.masterDict['instrument']=='HYSPEC': mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument",LogName='msd',LogText='1798.5',LogType='Number Series') mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument",LogName='s2', LogText=str(self.masterDict['S2']),LogType='Number Series') mantid.simpleapi.LoadInstrument(Workspace="__temp_instrument", RewriteSpectraMap=True, InstrumentName="HYSPEC") #masking if 'maskFilename' in self.masterDict and len(self.masterDict['maskFilename'].strip())>0: try: __maskWS=mantid.simpleapi.Load(self.masterDict['maskFilename']) mantid.simpleapi.MaskDetectors(Workspace="__temp_instrument",MaskedWorkspace=__maskWS) except (ValueError,RuntimeError) as e: reply = QtGui.QMessageBox.critical(self, 'Error',"The following error has occured in loading the mask:\n"+ str(e)+"\nDo you want to continue without mask?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply==QtGui.QMessageBox.No: return if self.masterDict['makeFast']: sp=range(mantid.mtd["__temp_instrument"].getNumberHistograms()) tomask=sp[::4]+sp[1::4]+sp[2::4] mantid.simpleapi.MaskDetectors("__temp_instrument",SpectraList=tomask) i=0 groupingStrings=[] progressDialog = QtGui.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: name="__temp_instrument"+str(i) i+=1 progressDialog.setValue(i) progressDialog.setLabelText("Creating workspace %d of %d..." % (i, self.iterations)) QtGui.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled=True progressDialog.close() return groupingStrings.append(name) mantid.simpleapi.CloneWorkspace("__temp_instrument",OutputWorkspace=name) mantid.simpleapi.SetGoniometer(Workspace=name, Axis0=str(g0)+","+self.masterDict['gonioDirs'][0]+ ","+str(self.masterDict['gonioSenses'][0]), Axis1=str(g1)+","+self.masterDict['gonioDirs'][1]+ ","+str(self.masterDict['gonioSenses'][1]), Axis2=str(g2)+","+self.masterDict['gonioDirs'][2]+ ","+str(self.masterDict['gonioSenses'][2])) progressDialog.close() mantid.simpleapi.DeleteWorkspace("__temp_instrument") self.wg=mantid.simpleapi.GroupWorkspaces(groupingStrings,OutputWorkspace="__temp_instrument") self.updatedInstrument=False #set the UB if self.updatedOL or not self.wg[0].sample().hasOrientedLattice(): mantid.simpleapi.SetUB(self.wg,UB=self.ol.getUB()) self.updatedOL=False #calculate coverage dimensions=['Q1','Q2','Q3','DeltaE'] progressDialog = QtGui.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") for i in range(self.iterations): progressDialog.setValue(i) progressDialog.setLabelText("Calculating orientation %d of %d..." % (i, self.iterations)) QtGui.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled=True progressDialog.close() return __mdws=mantid.simpleapi.CalculateCoverageDGS(self.wg[i], Q1Basis=self.masterDict['dimBasis'][0], Q2Basis=self.masterDict['dimBasis'][1], Q3Basis=self.masterDict['dimBasis'][2], IncidentEnergy=self.masterDict['Ei'], Dimension1=dimensions[self.masterDict['dimIndex'][0]], Dimension1Min=float2Input(self.masterDict['dimMin'][0]), Dimension1Max=float2Input(self.masterDict['dimMax'][0]), Dimension1Step=float2Input(self.masterDict['dimStep'][0]), Dimension2=dimensions[self.masterDict['dimIndex'][1]], Dimension2Min=float2Input(self.masterDict['dimMin'][1]), Dimension2Max=float2Input(self.masterDict['dimMax'][1]), Dimension2Step=float2Input(self.masterDict['dimStep'][1]), Dimension3=dimensions[self.masterDict['dimIndex'][2]], Dimension3Min=float2Input(self.masterDict['dimMin'][2]), Dimension3Max=float2Input(self.masterDict['dimMax'][2]), Dimension4=dimensions[self.masterDict['dimIndex'][3]], Dimension4Min=float2Input(self.masterDict['dimMin'][3]), Dimension4Max=float2Input(self.masterDict['dimMax'][3])) if i==0: intensity=__mdws.getSignalArray()[:,:,0,0]*1. #to make it writeable else: if self.colorButton.isChecked(): tempintensity= __mdws.getSignalArray()[:,:,0,0] intensity[numpy.where( tempintensity>0)]=i+1. else: tempintensity= __mdws.getSignalArray()[:,:,0,0] intensity[numpy.where( tempintensity>0)]=1. progressDialog.close() x = numpy.linspace(__mdws.getDimension(0).getMinimum(), __mdws.getDimension(0).getMaximum(),intensity.shape[0] ) y = numpy.linspace(__mdws.getDimension(1).getMinimum(), __mdws.getDimension(1).getMaximum(),intensity.shape[1] ) Y,X = numpy.meshgrid(y,x) xx, yy = self.tr(X, Y) Z=numpy.ma.masked_array(intensity,intensity==0) Z = Z[:-1, :-1] #plotting if self.sender() is self.plotButton or self.needToClear: self.figure.clear() self.trajfig.clear() self.figure.add_subplot(self.trajfig) self.needToClear=False self.trajfig.pcolorfast(xx,yy,Z) if self.aspectButton.isChecked(): self.trajfig.set_aspect(1.) else: self.trajfig.set_aspect('auto') self.trajfig.set_xlabel(self.masterDict['dimNames'][0]) self.trajfig.set_ylabel(self.masterDict['dimNames'][1]) self.trajfig.grid(True) self.canvas.draw() mantid.simpleapi.DeleteWorkspace(__mdws) def save(self): fileName = str(QtGui.QFileDialog.getSaveFileName(self, 'Save Plot', self.saveDir,'*.png')) data = "Instrument "+self.masterDict['instrument']+'\n' if self.masterDict['instrument']=='HYSPEC': data+= "S2 = "+str(self.masterDict['S2'])+'\n' data+= "Ei = "+str(self.masterDict['Ei'])+' meV\n' data+= "Goniometer values:\n" gonioAxis0values=numpy.arange(self.masterDict['gonioMinvals'][0],self.masterDict['gonioMaxvals'][0] +0.1*self.masterDict['gonioSteps'][0],self.masterDict['gonioSteps'][0]) gonioAxis1values=numpy.arange(self.masterDict['gonioMinvals'][1],self.masterDict['gonioMaxvals'][1] +0.1*self.masterDict['gonioSteps'][1],self.masterDict['gonioSteps'][1]) gonioAxis2values=numpy.arange(self.masterDict['gonioMinvals'][2],self.masterDict['gonioMaxvals'][2] +0.1*self.masterDict['gonioSteps'][2],self.masterDict['gonioSteps'][2]) for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: data+=" "+self.masterDict['gonioLabels'][0]+" = "+str(g0) data+=" "+self.masterDict['gonioLabels'][1]+" = "+str(g1) data+=" "+self.masterDict['gonioLabels'][2]+" = "+str(g2)+'\n' data+= "Lattice parameters:\n" data+=" a = "+str(self.ol.a())+" b = "+str(self.ol.b())+" c = "+str(self.ol.c())+'\n' data+=" alpha = "+str(self.ol.alpha())+" beta = "+str(self.ol.beta())+" gamma = "+str(self.ol.gamma())+'\n' data+= "Orientation vectors:\n" data+=" u = "+str(self.ol.getuVector())+'\n' data+=" v = "+str(self.ol.getvVector())+'\n' data+="Integrated "+self.masterDict['dimNames'][2]+" between "+\ str(self.masterDict['dimMin'][2])+" and "+str(self.masterDict['dimMax'][2])+'\n' data+="Integrated "+self.masterDict['dimNames'][3]+" between "+\ str(self.masterDict['dimMin'][3])+" and "+str(self.masterDict['dimMax'][3])+'\n' info=self.figure.text(0.2,0,data,verticalalignment='top') self.figure.savefig(fileName,bbox_inches='tight',additional_artists=info) self.saveDir=os.path.dirname(fileName) def tr(self,x, y): x, y = numpy.asarray(x), numpy.asarray(y) #one of the axes is energy if self.masterDict['dimIndex'][0]==3 or self.masterDict['dimIndex'][1]==3: return x,y else: h1,k1,l1=(float(temp) for temp in self.masterDict['dimBasis'][self.masterDict['dimIndex'][0]].split(',')) h2,k2,l2=(float(temp) for temp in self.masterDict['dimBasis'][self.masterDict['dimIndex'][1]].split(',')) angle=numpy.radians(self.ol.recAngle(h1,k1,l1,h2,k2,l2)) return 1.*x+numpy.cos(angle)*y, numpy.sin(angle)*y def inv_tr(self,x,y): x, y = numpy.asarray(x), numpy.asarray(y) #one of the axes is energy if self.masterDict['dimIndex'][0]==3 or self.masterDict['dimIndex'][1]==3: return x,y else: h1,k1,l1=(float(temp) for temp in self.masterDict['dimBasis'][self.masterDict['dimIndex'][0]].split(',')) h2,k2,l2=(float(temp) for temp in self.masterDict['dimBasis'][self.masterDict['dimIndex'][1]].split(',')) angle=numpy.radians(self.ol.recAngle(h1,k1,l1,h2,k2,l2)) return 1.*x-y/numpy.tan(angle), y/numpy.sin(angle)
class SliceViewerDataView(QWidget): """The view for the data portion of the sliceviewer""" def __init__(self, presenter: IDataViewSubscriber, dims_info, can_normalise, parent=None, conf=None): super().__init__(parent) self.presenter = presenter self.image = None self.line_plots_active = False self.can_normalise = can_normalise self.nonortho_transform = None self.conf = conf self._line_plots = None self._image_info_tracker = None self._region_selection_on = False self._orig_lims = None # Dimension widget self.dimensions_layout = QGridLayout() self.dimensions = DimensionWidget(dims_info, parent=self) self.dimensions.dimensionsChanged.connect(self.presenter.dimensions_changed) self.dimensions.valueChanged.connect(self.presenter.slicepoint_changed) self.dimensions_layout.addWidget(self.dimensions, 1, 0, 1, 1) self.colorbar_layout = QVBoxLayout() self.colorbar_layout.setContentsMargins(0, 0, 0, 0) self.colorbar_layout.setSpacing(0) self.image_info_widget = ImageInfoWidget(self) self.image_info_widget.setToolTip("Information about the selected pixel") self.track_cursor = QCheckBox("Track Cursor", self) self.track_cursor.setToolTip( "Update the image readout table when the cursor is over the plot. " "If unticked the table will update only when the plot is clicked") self.dimensions_layout.setHorizontalSpacing(10) self.dimensions_layout.addWidget(self.track_cursor, 0, 1, Qt.AlignRight) self.dimensions_layout.addWidget(self.image_info_widget, 1, 1) self.track_cursor.setChecked(True) self.track_cursor.stateChanged.connect(self.on_track_cursor_state_change) # normalization options if can_normalise: self.norm_label = QLabel("Normalization") self.colorbar_layout.addWidget(self.norm_label) self.norm_opts = QComboBox() self.norm_opts.addItems(["None", "By bin width"]) self.norm_opts.setToolTip("Normalization options") self.colorbar_layout.addWidget(self.norm_opts) # MPL figure + colorbar self.fig = Figure() self.ax = None self.image = None self._grid_on = False self.fig.set_facecolor(self.palette().window().color().getRgbF()) self.canvas = SliceViewerCanvas(self.fig) self.canvas.mpl_connect('button_release_event', self.mouse_release) self.canvas.mpl_connect('button_press_event', self.presenter.canvas_clicked) self.colorbar_label = QLabel("Colormap") self.colorbar_layout.addWidget(self.colorbar_label) norm_scale = self.get_default_scale_norm() self.colorbar = ColorbarWidget(self, norm_scale) self.colorbar.cmap.setToolTip("Colormap options") self.colorbar.crev.setToolTip("Reverse colormap") self.colorbar.norm.setToolTip("Colormap normalisation options") self.colorbar.powerscale.setToolTip("Power colormap scale") self.colorbar.cmax.setToolTip("Colormap maximum limit") self.colorbar.cmin.setToolTip("Colormap minimum limit") self.colorbar.autoscale.setToolTip("Automatically changes colormap limits when zooming on the plot") self.colorbar_layout.addWidget(self.colorbar) self.colorbar.colorbarChanged.connect(self.update_data_clim) self.colorbar.scaleNormChanged.connect(self.scale_norm_changed) # make width larger to fit image readout table self.colorbar.setMaximumWidth(200) # MPL toolbar self.toolbar_layout = QHBoxLayout() self.mpl_toolbar = SliceViewerNavigationToolbar(self.canvas, self, False) self.mpl_toolbar.gridClicked.connect(self.toggle_grid) self.mpl_toolbar.linePlotsClicked.connect(self.on_line_plots_toggle) self.mpl_toolbar.regionSelectionClicked.connect(self.on_region_selection_toggle) self.mpl_toolbar.homeClicked.connect(self.on_home_clicked) self.mpl_toolbar.nonOrthogonalClicked.connect(self.on_non_orthogonal_axes_toggle) self.mpl_toolbar.zoomPanClicked.connect(self.presenter.zoom_pan_clicked) self.mpl_toolbar.zoomPanFinished.connect(self.on_data_limits_changed) self.toolbar_layout.addWidget(self.mpl_toolbar) # Status bar self.status_bar = QStatusBar(parent=self) self.status_bar.setStyleSheet('QStatusBar::item {border: None;}') # Hide spacers between button and label self.status_bar_label = QLabel() self.help_button = QToolButton() self.help_button.setText("?") self.status_bar.addWidget(self.help_button) self.status_bar.addWidget(self.status_bar_label) # layout layout = QGridLayout(self) layout.setSpacing(1) layout.addLayout(self.dimensions_layout, 0, 0, 1, 2) layout.addLayout(self.toolbar_layout, 1, 0, 1, 1) layout.addLayout(self.colorbar_layout, 1, 1, 3, 1) layout.addWidget(self.canvas, 2, 0, 1, 1) layout.addWidget(self.status_bar, 3, 0, 1, 1) layout.setRowStretch(2, 1) @property def grid_on(self): return self._grid_on @property def line_plotter(self): return self._line_plots @property def nonorthogonal_mode(self): return self.nonortho_transform is not None def create_axes_orthogonal(self, redraw_on_zoom=False): """Create a standard set of orthogonal axes :param redraw_on_zoom: If True then when scroll zooming the canvas is redrawn immediately """ self.clear_figure() self.nonortho_transform = None self.ax = self.fig.add_subplot(111, projection='mantid') self.enable_zoom_on_mouse_scroll(redraw_on_zoom) if self.grid_on: self.ax.grid(self.grid_on) if self.line_plots_active: self.add_line_plots() self.plot_MDH = self.plot_MDH_orthogonal self.canvas.draw_idle() def create_axes_nonorthogonal(self, transform): self.clear_figure() self.set_nonorthogonal_transform(transform) self.ax = CurveLinearSubPlot(self.fig, 1, 1, 1, grid_helper=GridHelperCurveLinear( (transform.tr, transform.inv_tr))) # don't redraw on zoom as the data is rebinned and has to be redrawn again anyway self.enable_zoom_on_mouse_scroll(redraw=False) self.set_grid_on() self.fig.add_subplot(self.ax) self.plot_MDH = self.plot_MDH_nonorthogonal self.canvas.draw_idle() def enable_zoom_on_mouse_scroll(self, redraw): """Enable zoom on scroll the mouse wheel for the created axes :param redraw: Pass through to redraw option in enable_zoom_on_scroll """ self.canvas.enable_zoom_on_scroll(self.ax, redraw=redraw, toolbar=self.mpl_toolbar, callback=self.on_data_limits_changed) def add_line_plots(self, toolcls, exporter): """Assuming line plots are currently disabled, enable them on the current figure The image axes must have been created first. :param toolcls: Use this class to handle creating the plots :param exporter: Object defining methods to export cuts/roi """ if self.line_plots_active: return self.line_plots_active = True self._line_plots = toolcls(LinePlots(self.ax, self.colorbar), exporter) self.status_bar_label.setText(self._line_plots.status_message()) self.canvas.setFocus() self.mpl_toolbar.set_action_checked(ToolItemText.LINEPLOTS, True, trigger=False) def switch_line_plots_tool(self, toolcls, exporter): """Assuming line plots are currently enabled then switch the tool used to generate the plot curves. :param toolcls: Use this class to handle creating the plots """ if not self.line_plots_active: return # Keep the same set of line plots axes but swap the selection tool plotter = self._line_plots.plotter plotter.delete_line_plot_lines() self._line_plots.disconnect() self._line_plots = toolcls(plotter, exporter) self.status_bar_label.setText(self._line_plots.status_message()) self.canvas.setFocus() self.canvas.draw_idle() def remove_line_plots(self): """Assuming line plots are currently enabled, remove them from the current figure """ if not self.line_plots_active: return self._line_plots.plotter.close() self.status_bar_label.clear() self._line_plots = None self.line_plots_active = False def plot_MDH_orthogonal(self, ws, **kwargs): """ clears the plot and creates a new one using a MDHistoWorkspace """ self.clear_image() self.image = self.ax.imshow(ws, origin='lower', aspect='auto', transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), **kwargs) # ensure the axes data limits are updated to match the # image. For example if the axes were zoomed and the # swap dimensions was clicked we need to restore the # appropriate extents to see the image in the correct place extent = self.image.get_extent() self.ax.set_xlim(extent[0], extent[1]) self.ax.set_ylim(extent[2], extent[3]) # Set the original data limits which get passed to the ImageInfoWidget so that # the mouse projection to data space is correct for MDH workspaces when zoomed/changing slices self._orig_lims = self.get_axes_limits() self.on_track_cursor_state_change(self.track_cursor_checked()) self.draw_plot() def plot_MDH_nonorthogonal(self, ws, **kwargs): self.clear_image() self.image = pcolormesh_nonorthogonal(self.ax, ws, self.nonortho_transform.tr, transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), **kwargs) self.on_track_cursor_state_change(self.track_cursor_checked()) # swapping dimensions in nonorthogonal mode currently resets back to the # full data limits as the whole axes has been recreated so we don't have # access to the original limits # pcolormesh clears any grid that was previously visible if self.grid_on: self.ax.grid(self.grid_on) self.draw_plot() def plot_matrix(self, ws, **kwargs): """ clears the plot and creates a new one using a MatrixWorkspace keeping the axes limits that have already been set """ # ensure view is correct if zoomed in while swapping dimensions # compute required extent and just have resampling imshow deal with it old_extent = None if self.image is not None: old_extent = self.image.get_extent() if self.image.transpose != self.dimensions.transpose: e1, e2, e3, e4 = old_extent old_extent = e3, e4, e1, e2 self.clear_image() self.image = self.ax.imshow(ws, origin='lower', aspect='auto', interpolation='none', transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), extent=old_extent, **kwargs) self.on_track_cursor_state_change(self.track_cursor_checked()) self.draw_plot() def clear_image(self): """Removes any image from the axes""" if self.image is not None: if self.line_plots_active: self._line_plots.plotter.delete_line_plot_lines() self.image_info_widget.cursorAt(DBLMAX, DBLMAX, DBLMAX) if hasattr(self.ax, "remove_artists_if"): self.ax.remove_artists_if(lambda art: art == self.image) else: self.image.remove() self.image = None def clear_figure(self): """Removes everything from the figure""" if self.line_plots_active: self._line_plots.plotter.close() self.line_plots_active = False self.image = None self.canvas.disable_zoom_on_scroll() self.fig.clf() self.ax = None def draw_plot(self): self.ax.set_title('') self.canvas.draw() if self.image: self.colorbar.set_mappable(self.image) self.colorbar.update_clim() self.mpl_toolbar.update() # clear nav stack if self.line_plots_active: self._line_plots.plotter.delete_line_plot_lines() self._line_plots.plotter.update_line_plot_labels() def export_region(self, limits, cut): """ React to a region selection that should be exported :param limits: 2-tuple of ((left, right), (bottom, top)) :param cut: A str denoting which cuts to export. """ self.presenter.export_region(limits, cut) def update_plot_data(self, data): """ This just updates the plot data without creating a new plot. The extents can change if the data has been rebinned. """ if self.nonortho_transform: self.image.set_array(data.T.ravel()) else: self.image.set_data(data.T) self.colorbar.update_clim() def track_cursor_checked(self): return self.track_cursor.isChecked() if self.track_cursor else False def on_track_cursor_state_change(self, state): """ Called to notify the current state of the track cursor box """ if self._image_info_tracker is not None: self._image_info_tracker.disconnect() if self._line_plots is not None and not self._region_selection_on: self._line_plots.disconnect() self._image_info_tracker = ImageInfoTracker(image=self.image, transform=self.nonortho_transform, do_transform=self.nonorthogonal_mode, widget=self.image_info_widget, cursor_transform=self._orig_lims) if state: self._image_info_tracker.connect() if self._line_plots and not self._region_selection_on: self._line_plots.connect() else: self._image_info_tracker.disconnect() if self._line_plots and not self._region_selection_on: self._line_plots.disconnect() def on_home_clicked(self): """Reset the view to encompass all of the data""" self.presenter.show_all_data_clicked() def on_line_plots_toggle(self, state): """Switch state of the line plots""" self.presenter.line_plots(state) def on_region_selection_toggle(self, state): """Switch state of the region selection""" self.presenter.region_selection(state) self._region_selection_on = state # If state is off and track cursor is on, make sure line plots are re-connected to move cursor if not state and self.track_cursor_checked(): if self._line_plots: self._line_plots.connect() def on_non_orthogonal_axes_toggle(self, state): """ Switch state of the non-orthognal axes on/off """ self.presenter.nonorthogonal_axes(state) def on_data_limits_changed(self): """ React to when the data limits have changed """ self.presenter.data_limits_changed() def deactivate_and_disable_tool(self, tool_text): """Deactivate a tool as if the control had been pressed and disable the functionality""" self.deactivate_tool(tool_text) self.disable_tool_button(tool_text) def activate_tool(self, tool_text): """Activate a given tool as if the control had been pressed""" self.mpl_toolbar.set_action_checked(tool_text, True) def deactivate_tool(self, tool_text): """Deactivate a given tool as if the tool button had been pressed""" self.mpl_toolbar.set_action_checked(tool_text, False) def enable_tool_button(self, tool_text): """Set a given tool button enabled so it can be interacted with""" self.mpl_toolbar.set_action_enabled(tool_text, True) def disable_tool_button(self, tool_text): """Set a given tool button disabled so it cannot be interacted with""" self.mpl_toolbar.set_action_enabled(tool_text, False) def get_axes_limits(self): """ Return the limits on the image axes transformed into the nonorthogonal frame if appropriate """ if self.image is None: return None else: xlim, ylim = self.ax.get_xlim(), self.ax.get_ylim() if self.nonorthogonal_mode: inv_tr = self.nonortho_transform.inv_tr # viewing axis y not aligned with plot axis xmin_p, ymax_p = inv_tr(xlim[0], ylim[1]) xmax_p, ymin_p = inv_tr(xlim[1], ylim[0]) xlim, ylim = (xmin_p, xmax_p), (ymin_p, ymax_p) return xlim, ylim def get_full_extent(self): """ Return the full extent of image - only applicable for plots of matrix workspaces """ if self.image and isinstance(self.image, samplingimage.SamplingImage): return self.image.get_full_extent() else: return None def set_axes_limits(self, xlim, ylim): """ Set the view limits on the image axes to the given extents. Assume the limits are in the orthogonal frame. :param xlim: 2-tuple of (xmin, xmax) :param ylim: 2-tuple of (ymin, ymax) """ self.ax.set_xlim(xlim) self.ax.set_ylim(ylim) def set_grid_on(self): """ If not visible sets the grid visibility """ if not self._grid_on: self._grid_on = True self.mpl_toolbar.set_action_checked(ToolItemText.GRID, state=self._grid_on) def set_nonorthogonal_transform(self, transform): """ Set the transform for nonorthogonal axes mode :param transform: An object with a tr method to transform from nonorthognal coordinates to display coordinates """ self.nonortho_transform = transform def show_temporary_status_message(self, msg, timeout_ms): """ Show a message in the status bar that disappears after a set period :param msg: A str message to display :param timeout_ms: Timeout in milliseconds to display the message for """ self.status_bar.showMessage(msg, timeout_ms) def toggle_grid(self, state): """ Toggle the visibility of the grid on the axes """ self._grid_on = state self.ax.grid(self._grid_on) self.canvas.draw_idle() def mouse_release(self, event): if event.inaxes != self.ax: return self.canvas.setFocus() if event.button == 1: self._image_info_tracker.on_cursor_at(event.xdata, event.ydata) if self.line_plots_active and not self._region_selection_on: self._line_plots.on_cursor_at(event.xdata, event.ydata) if event.button == 3: self.on_home_clicked() def deactivate_zoom_pan(self): self.deactivate_tool(ToolItemText.PAN) self.deactivate_tool(ToolItemText.ZOOM) def update_data_clim(self): self.image.set_clim(self.colorbar.colorbar.mappable.get_clim()) if self.line_plots_active: self._line_plots.plotter.update_line_plot_limits() self.canvas.draw_idle() def set_normalization(self, ws, **kwargs): normalize_by_bin_width, _ = get_normalize_by_bin_width(ws, self.ax, **kwargs) is_normalized = normalize_by_bin_width or ws.isDistribution() self.presenter.normalization = is_normalized if is_normalized: self.norm_opts.setCurrentIndex(1) else: self.norm_opts.setCurrentIndex(0) def get_default_scale_norm(self): scale = 'Linear' if self.conf is None: return scale if self.conf.has(SCALENORM): scale = self.conf.get(SCALENORM) if scale == 'Power' and self.conf.has(POWERSCALE): exponent = self.conf.get(POWERSCALE) scale = (scale, exponent) scale = "SymmetricLog10" if scale == 'Log' else scale return scale def scale_norm_changed(self): if self.conf is None: return scale = self.colorbar.norm.currentText() self.conf.set(SCALENORM, scale) if scale == 'Power': exponent = self.colorbar.powerscale_value self.conf.set(POWERSCALE, exponent)
def plot2d(data, variables, filename): # default vales logscale_x = False logscale_y = False logable = True grid_bool = False axistop_bool = False axisright_bool = False axisbottom_bool = True axisleft_bool = True legend_outside = True colors = {} for i in range(0, len(variables)): colors[variables[i]] = ['w', 'b', 'g', 'r', 'c', 'm', 'y', 'k'][i] # intern parameters plot_range = 1. / 8 # means of two data series differ by this factor, # the scalng is defined ill # prepare data ---> # check for appropriate format of input if (isinstance(variables, list)): for i in variables: if (not isinstance(i, str)): print('second argument must be a list of str') return None else: print('second argument must be a list') if (not isinstance(filename, str)): print('third argument must be of type str') return None if (not isinstance(data, list)): print('first argument must be of type list') return None for j in data[0]: if (not isinstance(j, float) and not isinstance(j, int)): print('data must be passed as a list of int or float') return None for i in data[1:]: for j in i: if (not isinstance(j, float) and not isinstance(j, int)): print('data must be passed as list of int or float') return None elif j <= 0 and logable: print('logscale impossible') logable = False # to avoid using logscale #get number of rows and colums of data ncols = len(data) nrows = len(data[0]) #check wether all rows have the same size and create df df = {} # initialize empty dictionary similar to dataframe for i in range(0, ncols): if not len(data[i]) == nrows: print('missing values in data set') return None else: df[variables[i]] = data[i] # <--- prepare data # ---> analyse data # get lower and upper bounds of each variable bounds = {} for i in variables: [bounds['lower', i], bounds['upper', i]] = [min(df[i]), max(df[i])] # check whether magnitudes are approximately equal and use logscale if not means = [0] * (ncols - 1) for i in range(1, ncols): # compute means of the columns means[i - 1] = np.mean(df[variables[i]]) if min(means) / max( means ) < plot_range and logable: # if variables have different magnitudes set print('ill scaling - use logscale') # logscale logscale_y = True for i in range( 1, ncols ): # if one variable changes magnitude drastically set logscale if ((max(df[variables[i]]) - min(df[variables[i]])) != 0): temp = means[i - 1] / (max(df[variables[i]]) - min(df[variables[i]])) if temp < plot_range and logable and not logscale_y: print('very ill scaling - use logscale') logscale_y = True # <--- analyse data # plot ---> fig = plt.figure(1) ax = Subplot(fig, 111) fig.add_subplot(ax) for i in variables[1:]: ax.scatter(df[variables[0]], df[i], label='$' + i + '$', color=colors[i]) # font # labels and legend ax.legend(loc='best') plt.xlabel(variables[0], **hfont) if legend_outside: box = ax.get_position() ax.set_position([box.x0, box.y0, box.width * 0.8, box.height]) ax.legend(loc='center left', bbox_to_anchor=(1, 0.5)) # logscale if logscale_y: plt.yscale('log') if logscale_x: plt.xscale('log') # grid plt.grid(grid_bool) # visible axis ax.axis["right"].set_visible(axisright_bool) ax.axis["top"].set_visible(axistop_bool) ax.axis["bottom"].set_visible(axisbottom_bool) ax.axis["left"].set_visible(axisleft_bool) # <--- plot # save plot ---> pdfdirectory = filename + '.pdf' pgfdirectory = filename + '.pgf' fig.savefig(pdfdirectory) plt.savefig(pgfdirectory) # <--- save plot print('run successful') return None
def plotSpikes(qdata,save=False,fname='hmmSorting.pdf',tuning=False,figsize=(10,6)): allSpikes = qdata['allSpikes'] unitSpikes = qdata['unitSpikes'] spikeIdx = qdata['spikeIdx'] spikeIdx = qdata['unitTimePoints'] units = qdata['unitTimePoints'] spikeForms = qdata['spikeForms'] channels = qdata['channels'] uniqueIdx = qdata['uniqueIdx'] samplingRate = qdata.get('samplingRate',30000.0) """ mustClose = False if isinstance(dataFile,str): dataFile = h5py.File(dataFile,'r') mustClose = True data = dataFile['data'][:] """ keys = np.array(units.keys()) x = np.arange(32)[None,:] + 42*np.arange(spikeForms.shape[1])[:,None] xt = np.linspace(0,31,spikeForms.shape[-1])[None,:] + 42*np.arange(spikeForms.shape[1])[:,None] xch = 10 + 42*np.arange(len(channels)) for c in units.keys(): ymin,ymax = (5000,-5000) fig = plt.figure(figsize=figsize) fig.subplots_adjust(hspace=0.3) print "Unit: %s " %(str(c),) print "\t Plotting waveforms..." sys.stdout.flush() #allspikes = data[units[c][:,None]+np.arange(-10,22)[None,:],:] #allspikes = allSpikes[spikeIdx[c]] allspikes = qdata['unitSpikes'][c] otherunits = keys[keys!=c] #nonOverlapIdx = np.prod(np.array([~np.lib.arraysetops.in1d(spikeIdx[c],spikeIdx[c1]) for c1 in otherunits]),axis=0).astype(np.bool) #nonOverlapIdx = np.prod(np.array([pdist_threshold(spikeIdx[c],spikeIdx[c1],3) for c1 in otherunits]),axis=0).astype(np.bool) #nonOverlapIdx = uniqueIdx[c] nonOverlapIdx = qdata['nonOverlapIdx'][c] overlapIdx = np.lib.arraysetops.setdiff1d(np.arange(qdata['unitTimePoints'][c].shape[0]),nonOverlapIdx) #allspikes = allSpikes[np.lib.arraysetops.union1d(nonOverlapIdx,overlapIdx)] ax = Subplot(fig,2,3,1) fig.add_axes(ax) formatAxis(ax) #plt.plot(x.T,sp,'b') m = allspikes[:].mean(0) s = allspikes[:].std(0) plt.plot(x.T,m,'k',lw=1.5) #find the minimum point for this template ich = spikeForms[int(c)].min(1).argmin() ix = spikeForms[int(c)][ich,:].argmin() #plt.plot(x.T,spikeForms[int(c)][:,ix-10:ix+22].T,'r') plt.plot(x.T,np.roll(spikeForms[int(c)],10-ix,axis=1)[:,:32].T,'r') for i in xrange(x.shape[0]): plt.fill_between(x[i],m[:,i]-s[:,i],m[:,i]+s[:,i],color='b',alpha=0.5) yl = ax.get_ylim() ymin = min(ymin,yl[0]) ymax = max(ymax,yl[1]) ax.set_title('All spikes (%d)' % (allspikes.shape[0],)) ax = Subplot(fig,2,3,2) fig.add_axes(ax) formatAxis(ax) if len(nonOverlapIdx)>0: m = allspikes[:][nonOverlapIdx,:,:].mean(0) s = allspikes[:][nonOverlapIdx,:,:].std(0) plt.plot(x.T,m,'k',lw=1.5) for i in xrange(x.shape[0]): plt.fill_between(x[i],m[:,i]-s[:,i],m[:,i]+s[:,i],color='b',alpha=0.5) #plt.plot(x.T,spikeForms[int(c)][:,ix-10:ix+22].T,'r') plt.plot(x.T,np.roll(spikeForms[int(c)],10-ix,axis=1)[:,:32].T,'r') yl = ax.get_ylim() ymin = min(ymin,yl[0]) ymax = max(ymax,yl[1]) #for sp in allspikes[nonOverlapIdx,:,:]: # plt.plot(x.T,sp,'r') ax.set_title('Non-overlap spikes (%d)' %(nonOverlapIdx.shape[0],)) ax = Subplot(fig,2,3,3) fig.add_axes(ax) formatAxis(ax) if len(overlapIdx)>0: m = allspikes[:][overlapIdx,:,:].mean(0) s = allspikes[:][overlapIdx,:,:].std(0) plt.plot(x.T,m,'k',lw=1.5) for i in xrange(x.shape[0]): plt.fill_between(x[i],m[:,i]-s[:,i],m[:,i]+s[:,i],color='b',alpha=0.5) #plt.plot(x.T,spikeForms[int(c)][:,ix-10:ix+22].T,'r') plt.plot(x.T,np.roll(spikeForms[int(c)],10-ix,axis=1)[:,:32].T,'r') yl = ax.get_ylim() ymin = min(ymin,yl[0]) ymax = max(ymax,yl[1]) #for sp in allspikes[~nonOverlapIdx,:,:]: # plt.plot(x.T,sp,'g') ax.set_title('Overlap spikes (%d)' % ((overlapIdx).shape[0],)) for a in fig.axes: a.set_ylim((ymin,ymax)) a.set_xticks(xch) a.set_xticklabels(map(str,channels)) a.set_xlabel('Channels') for a in fig.axes[1:]: a.set_yticklabels([]) fig.axes[0].set_ylabel('Amplitude') """ isi distribution """ print "\t ISI distribution..." sys.stdout.flush() timepoints = qdata['unitTimePoints'][c][:]/(samplingRate/1000) if len(timepoints)<2: print "Too few spikes. Aborting..." continue isi = np.log(np.diff(timepoints)) n,b = np.histogram(isi,100) ax = Subplot(fig,2,3,4) fig.add_axes(ax) formatAxis(ax) ax.plot(b[:-1],n,'k') yl = ax.get_ylim() ax.vlines(0.0,0,yl[1],'r',lw=1.5) ax.set_xlabel('ISI [ms]') #get xticklabels xl,xh = int(np.round((b[0]-0.5)*2))/2,int(np.round((b[-1]+0.5)*2))/2 xl = -0.5 dx = np.round(10.0*(xh-xl)/5.0)/10 xt_ = np.arange(xl,xh+1,dx) ax.set_xticks(xt_) ax.set_xticklabels(map(lambda s: r'$10^{%.1f}$' % (s,),xt_)) """ auto-correlogram """ print "\t auto-correllogram..." sys.stdout.flush() if not 'autoCorr' in qdata: if isinstance(qdata,dict): qdata['autoCorr'] = {} else: qdata.create_group('autoCorr') if not c in qdata['autoCorr']: C = pdist_threshold2(timepoints,timepoints,50) qdata['autoCorr'][c] = C if not isinstance(qdata,dict): qdata.flush() else: C = qdata['autoCorr'][c][:] n,b = np.histogram(C[C!=0],np.arange(-50,50)) ax = Subplot(fig,2,3,5) fig.add_axes(ax) formatAxis(ax) ax.plot(b[:-1],n,'k') ax.fill_betweenx([0,n.max()],-1.0,1.0,color='r',alpha=0.3) ax.set_xlabel('Lag [ms]') if tuning: print "\tPlotting tuning..." sys.stdout.flush() #attempt to get tuning for the current session, based on PWD stimCounts,isiCounts,angles,spikedata = gt.getTuning(sptrain=timepoints) #reshape to number of orientations X number of reps, collapsing #across everything else #angles = np.append(angles,[angles[0]]) C = stimCounts['0'].transpose((1,0,2,3)) C = C.reshape(C.shape[0],C.size/C.shape[0]) ax = plt.subplot(2,3,6,polar=True) ax.errorbar(angles*np.pi/180,C.mean(1),C.std(1)) if save: if not os.path.isabs(fname): fn = os.path.expanduser('~/Documents/research/figures/SpikeSorting/hmm/%s' % (fname.replace('.pdf','Unit%s.pdf' %(str(c),)),)) else: fn = fname.replace('.pdf','Unit%s.pdf' %(str(c),)) fig.savefig(fn,bbox='tight') if not save: plt.draw() """
def plot_univariate_inequality(xdata, inclusive=False, raycolor='blue', bgcolor='white', xlim=None, figsize=(5, 0.5), figure=None, title=None, **kwargs): """Plot a chart of a univariate inequality ray with matplotlib Args: xdata (list, tuple): endpoints of the ray: ``[start, ..., end]`` Kwargs: inclusive (bool): if True, draw a filled circle; if False, draw a circle without fill raycolor (str): matplotlib color of the line and markers bgcolor (str): matplotlib color for the background and circle without fill xlim (None, True, or list/tuple): if True, calculate defaults for xmin and xmax of the x-axis figsize (tuple): matplotlib figsize figure (None or matplotlib.figure.Figure): Figure to add a plot to title (None or str): Title to add the the plot kwargs: all other kwargs will be passed to `plt.figure()` Returns: matplotlib.figure.Figure: inequality figure """ _xdata = sorted(xdata) xstart = _xdata[0] xend = _xdata[-1] lefttoright = xstart <= xend lefttoright2 = xdata[0] <= xdata[-1] fig1 = figure or plt.figure(figsize=figsize, facecolor=bgcolor, **kwargs) ax1 = Subplot(fig1, 111) fig1.add_subplot(ax1) style = {} style['linewidth'] = 4 style['linestyle'] = 'solid' style['color'] = raycolor style['marker'] = 'o' style['markersize'] = 12 style['markevery'] = [0] if lefttoright else [-1] style['markerfacecolor'] = raycolor if inclusive else bgcolor style['markeredgecolor'] = raycolor style['markeredgewidth'] = 4 arrowstyle = {} arrowstyle['marker'] = '>' if lefttoright2 else '<' arrowstyle['markersize'] = 12 arrowstyle['markerfacecolor'] = raycolor arrowstyle['markeredgecolor'] = raycolor arrowy = xdata[-1] if lefttoright else xdata[0] ax1.set(ylim=(-0.5, 1)) if xlim is True: xlim = (float(xstart - 1), float(xend + 0.5)) if xlim: ax1.set(xlim=xlim) ax1.axis["left"].set_visible(False) ax1.axis["right"].set_visible(False) ax1.axis["top"].set_visible(False) #ax1.axis["bottom"].set_axisline_style(axisline_style="->", size=2) ax1.plot(1, -0.5, ">k", transform=ax1.get_yaxis_transform(), clip_on=False) ax1.plot(0, -0.5, "<k", transform=ax1.get_yaxis_transform(), clip_on=False) _x = [xval for xval in xdata] _y = [0 for xval in xdata] plt.plot(_x, _y, **style) if title: ax1.set_title(title) plt.plot(arrowy, 0, **arrowstyle) return fig1
class SliceViewerDataView(QWidget): """The view for the data portion of the sliceviewer""" def __init__(self, presenter, dims_info, can_normalise, parent=None): super().__init__(parent) self.presenter = presenter self.image = None self.line_plots = False self.can_normalise = can_normalise self.nonortho_tr = None # Dimension widget self.dimensions_layout = QHBoxLayout() self.dimensions = DimensionWidget(dims_info, parent=self) self.dimensions.dimensionsChanged.connect( self.presenter.dimensions_changed) self.dimensions.valueChanged.connect(self.presenter.slicepoint_changed) self.dimensions_layout.addWidget(self.dimensions) self.colorbar_layout = QVBoxLayout() # normalization options if can_normalise: self.norm_layout = QHBoxLayout() self.norm_label = QLabel("Normalization =") self.norm_layout.addWidget(self.norm_label) self.norm_opts = QComboBox() self.norm_opts.addItems(["None", "By bin width"]) self.norm_opts.setToolTip("Normalization options") self.norm_layout.addWidget(self.norm_opts) self.colorbar_layout.addLayout(self.norm_layout) # MPL figure + colorbar self.mpl_layout = QHBoxLayout() self.fig = Figure() self.ax = None self._grid_on = False self.fig.set_facecolor(self.palette().window().color().getRgbF()) self.canvas = FigureCanvas(self.fig) self.canvas.mpl_connect('motion_notify_event', self.mouse_move) self.create_axes_orthogonal() self.mpl_layout.addWidget(self.canvas) self.colorbar = ColorbarWidget(self) self.colorbar_layout.addWidget(self.colorbar) self.colorbar.colorbarChanged.connect(self.update_data_clim) self.colorbar.colorbarChanged.connect(self.update_line_plot_limits) self.mpl_layout.addLayout(self.colorbar_layout) # MPL toolbar self.mpl_toolbar = SliceViewerNavigationToolbar(self.canvas, self) self.mpl_toolbar.gridClicked.connect(self.toggle_grid) self.mpl_toolbar.linePlotsClicked.connect(self.line_plots_toggle) self.mpl_toolbar.plotOptionsChanged.connect( self.colorbar.mappable_changed) self.mpl_toolbar.nonOrthogonalClicked.connect( self.non_orthogonal_axes_toggle) # layout self.layout = QGridLayout(self) self.layout.addLayout(self.dimensions_layout, 0, 0) self.layout.addWidget(self.mpl_toolbar, 1, 0) self.layout.addLayout(self.mpl_layout, 2, 0) @property def grid_on(self): return self._grid_on @property def nonorthogonal_mode(self): return self.nonortho_tr is not None def create_axes_orthogonal(self): self.clear_figure() self.nonortho_tr = None self.ax = self.fig.add_subplot(111, projection='mantid') if self.grid_on: self.ax.grid() if self.line_plots: self.add_line_plots() self.plot_MDH = self.plot_MDH_orthogonal self.canvas.draw_idle() def create_axes_nonorthogonal(self, transform): self.clear_figure() self.set_nonorthogonal_transform(transform) self.ax = CurveLinearSubPlot(self.fig, 1, 1, 1, grid_helper=GridHelperCurveLinear( (self.nonortho_tr, transform.inv_tr))) self.set_grid_on() self.fig.add_subplot(self.ax) self.plot_MDH = self.plot_MDH_nonorthogonal self.canvas.draw_idle() def add_line_plots(self): """Assuming line plots are currently disabled, enable them on the current figure The image axes must have been created first. """ if self.line_plots: return image_axes = self.ax if image_axes is None: return # Create a new GridSpec and reposition the existing image Axes gs = gridspec.GridSpec(2, 2, width_ratios=[1, 4], height_ratios=[4, 1], wspace=0.0, hspace=0.0) image_axes.set_position(gs[1].get_position(self.fig)) image_axes.xaxis.set_visible(False) image_axes.yaxis.set_visible(False) self.axx = self.fig.add_subplot(gs[3], sharex=image_axes) self.axx.yaxis.tick_right() self.axy = self.fig.add_subplot(gs[0], sharey=image_axes) self.axy.xaxis.tick_top() self.mpl_toolbar.update() # sync list of axes in navstack self.canvas.draw_idle() def remove_line_plots(self): """Assuming line plots are currently enabled, remove them from the current figure """ if not self.line_plots: return image_axes = self.ax if image_axes is None: return self.clear_line_plots() all_axes = self.fig.axes # The order is defined by the order of the add_subplot calls so we always want to remove # the last two Axes. Do it backwards to cope with the container size change all_axes[2].remove() all_axes[1].remove() gs = gridspec.GridSpec(1, 1) image_axes.set_position(gs[0].get_position(self.fig)) image_axes.xaxis.set_visible(True) image_axes.yaxis.set_visible(True) self.axx, self.axy = None, None self.mpl_toolbar.update() # sync list of axes in navstack self.canvas.draw_idle() def plot_MDH_orthogonal(self, ws, **kwargs): """ clears the plot and creates a new one using a MDHistoWorkspace """ self.clear_image() self.image = self.ax.imshow(ws, origin='lower', aspect='auto', transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), **kwargs) self.draw_plot() def plot_MDH_nonorthogonal(self, ws, **kwargs): self.clear_image() self.image = pcolormesh_nonorthogonal( self.ax, ws, self.nonortho_tr, transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), **kwargs) # pcolormesh clears any grid that was previously visible if self.grid_on: self.ax.grid() self.draw_plot() def plot_matrix(self, ws, **kwargs): """ clears the plot and creates a new one using a MatrixWorkspace """ self.clear_image() self.image = imshow_sampling(self.ax, ws, origin='lower', aspect='auto', interpolation='none', transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), **kwargs) self.image._resample_image() self.draw_plot() def clear_image(self): """Removes any image from the axes""" if self.image is not None: self.image.remove() self.image = None def clear_figure(self): """Removes everything from the figure""" self.image = None self._grid_on = False self.fig.clf() def draw_plot(self): self.ax.set_title('') self.colorbar.set_mappable(self.image) self.colorbar.update_clim() self.mpl_toolbar.update() # clear nav stack self.clear_line_plots() self.canvas.draw_idle() def update_plot_data(self, data): """ This just updates the plot data without creating a new plot """ if self.nonortho_tr: self.image.set_array(data.T.ravel()) else: self.image.set_data(data.T) self.colorbar.update_clim() def line_plots_toggle(self, state): self.presenter.line_plots(state) self.line_plots = state def non_orthogonal_axes_toggle(self, state): """ Switch state of the non-orthognal axes on/off """ self.presenter.nonorthogonal_axes(state) def enable_lineplots_button(self): """ Enables line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.LINEPLOTS, True) def disable_lineplots_button(self): """ Disabled line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.LINEPLOTS, False) def enable_peaks_button(self): """ Enables line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.OVERLAYPEAKS, True) def disable_peaks_button(self): """ Disables line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.OVERLAYPEAKS, False) def disable_nonorthogonal_axes_button(self): """ Disables non-orthorognal axes functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.NONORTHOGONAL_AXES, False) def clear_line_plots(self): try: # clear old plots del self.xfig del self.yfig except AttributeError: pass def update_data_clim(self): self.image.set_clim(self.colorbar.colorbar.mappable.get_clim()) self.canvas.draw_idle() def update_line_plot_limits(self): if self.line_plots: self.axx.set_ylim(self.colorbar.cmin_value, self.colorbar.cmax_value) self.axy.set_xlim(self.colorbar.cmin_value, self.colorbar.cmax_value) def set_grid_on(self): """ If not visible sets the grid visibility """ if not self._grid_on: self.toggle_grid() def set_nonorthogonal_transform(self, transform): """ Set the transform for nonorthogonal axes mode :param transform: An object with a tr method to transform from nonorthognal coordinates to display coordinates """ self.nonortho_tr = transform.tr def toggle_grid(self): """ Toggle the visibility of the grid on the axes """ self.ax.grid() self._grid_on = not self._grid_on self.canvas.draw_idle() def mouse_move(self, event): if self.line_plots and event.inaxes == self.ax: self.update_line_plots(event.xdata, event.ydata) def plot_x_line(self, x, y): try: self.xfig[0].set_data(x, y) except (AttributeError, IndexError): self.axx.clear() self.xfig = self.axx.plot(x, y) self.axx.set_xlabel(self.ax.get_xlabel()) self.update_line_plot_limits() self.canvas.draw_idle() def plot_y_line(self, x, y): try: self.yfig[0].set_data(y, x) except (AttributeError, IndexError): self.axy.clear() self.yfig = self.axy.plot(y, x) self.axy.set_ylabel(self.ax.get_ylabel()) self.update_line_plot_limits() self.canvas.draw_idle() def update_line_plots(self, x, y): xmin, xmax, ymin, ymax = self.image.get_extent() arr = self.image.get_array() data_extent = Bbox([[ymin, xmin], [ymax, xmax]]) array_extent = Bbox([[0, 0], arr.shape[:2]]) trans = BboxTransform(boxin=data_extent, boxout=array_extent) point = trans.transform_point([y, x]) if any(np.isnan(point)): return i, j = point.astype(int) if 0 <= i < arr.shape[0]: self.plot_x_line(np.linspace(xmin, xmax, arr.shape[1]), arr[i, :]) if 0 <= j < arr.shape[1]: self.plot_y_line(np.linspace(ymin, ymax, arr.shape[0]), arr[:, j]) def set_normalization(self, ws, **kwargs): normalize_by_bin_width, _ = get_normalize_by_bin_width( ws, self.ax, **kwargs) is_normalized = normalize_by_bin_width or ws.isDistribution() if is_normalized: self.presenter.normalization = mantid.api.MDNormalization.VolumeNormalization self.norm_opts.setCurrentIndex(1) else: self.presenter.normalization = mantid.api.MDNormalization.NoNormalization self.norm_opts.setCurrentIndex(0)
class SliceViewerDataView(QWidget): """The view for the data portion of the sliceviewer""" def __init__(self, presenter, dims_info, can_normalise, parent=None): super().__init__(parent) self.presenter = presenter self.image = None self.line_plots = False self.can_normalise = can_normalise self.nonortho_tr = None self.ws_type = dims_info[0]['type'] # Dimension widget self.dimensions_layout = QGridLayout() self.dimensions = DimensionWidget(dims_info, parent=self) self.dimensions.dimensionsChanged.connect( self.presenter.dimensions_changed) self.dimensions.valueChanged.connect(self.presenter.slicepoint_changed) self.dimensions_layout.addWidget(self.dimensions, 1, 0, 1, 1) self.colorbar_layout = QVBoxLayout() self.colorbar_layout.setContentsMargins(0, 0, 0, 0) self.colorbar_layout.setSpacing(0) self.image_info_widget = ImageInfoWidget(self.ws_type, self) self.track_cursor = QCheckBox("Track Cursor", self) self.track_cursor.setToolTip( "Update the image readout table when the cursor is over the plot. " "If unticked the table will update only when the plot is clicked") if self.ws_type == 'MDE': self.colorbar_layout.addWidget(self.image_info_widget, alignment=Qt.AlignCenter) self.colorbar_layout.addWidget(self.track_cursor) else: self.dimensions_layout.setHorizontalSpacing(10) self.dimensions_layout.addWidget(self.track_cursor, 0, 1, Qt.AlignRight) self.dimensions_layout.addWidget(self.image_info_widget, 1, 1) self.track_cursor.setChecked(True) # normalization options if can_normalise: self.norm_label = QLabel("Normalization") self.colorbar_layout.addWidget(self.norm_label) self.norm_opts = QComboBox() self.norm_opts.addItems(["None", "By bin width"]) self.norm_opts.setToolTip("Normalization options") self.colorbar_layout.addWidget(self.norm_opts) # MPL figure + colorbar self.fig = Figure() self.ax = None self.axx, self.axy = None, None self.image = None self._grid_on = False self.fig.set_facecolor(self.palette().window().color().getRgbF()) self.canvas = SliceViewerCanvas(self.fig) self.canvas.mpl_connect('motion_notify_event', self.mouse_move) self.canvas.mpl_connect('axes_leave_event', self.mouse_outside_image) self.canvas.mpl_connect('button_press_event', self.mouse_click) self.canvas.mpl_connect('button_release_event', self.mouse_release) self.colorbar_label = QLabel("Colormap") self.colorbar_layout.addWidget(self.colorbar_label) self.colorbar = ColorbarWidget(self) self.colorbar_layout.addWidget(self.colorbar) self.colorbar.colorbarChanged.connect(self.update_data_clim) self.colorbar.colorbarChanged.connect(self.update_line_plot_limits) # make width larger to fit image readout table if self.ws_type == 'MDE': self.colorbar.setMaximumWidth(155) # MPL toolbar self.toolbar_layout = QHBoxLayout() self.mpl_toolbar = SliceViewerNavigationToolbar( self.canvas, self, False) self.mpl_toolbar.gridClicked.connect(self.toggle_grid) self.mpl_toolbar.linePlotsClicked.connect(self.on_line_plots_toggle) self.mpl_toolbar.homeClicked.connect(self.on_home_clicked) self.mpl_toolbar.plotOptionsChanged.connect( self.colorbar.mappable_changed) self.mpl_toolbar.nonOrthogonalClicked.connect( self.on_non_orthogonal_axes_toggle) self.mpl_toolbar.zoomPanFinished.connect(self.on_data_limits_changed) self.toolbar_layout.addWidget(self.mpl_toolbar) # layout layout = QGridLayout(self) layout.setSpacing(1) layout.addLayout(self.dimensions_layout, 0, 0, 1, 2) layout.addLayout(self.toolbar_layout, 1, 0, 1, 2) layout.addWidget(self.canvas, 2, 0, 1, 1) layout.addLayout(self.colorbar_layout, 1, 1, 2, 1) layout.setRowStretch(2, 1) @property def grid_on(self): return self._grid_on @property def nonorthogonal_mode(self): return self.nonortho_tr is not None def create_axes_orthogonal(self, redraw_on_zoom=False): """Create a standard set of orthogonal axes :param redraw_on_zoom: If True then when scroll zooming the canvas is redrawn immediately """ self.clear_figure() self.nonortho_tr = None self.ax = self.fig.add_subplot(111, projection='mantid') self.enable_zoom_on_mouse_scroll(redraw_on_zoom) if self.grid_on: self.ax.grid(self.grid_on) if self.line_plots: self.add_line_plots() self.plot_MDH = self.plot_MDH_orthogonal self.canvas.draw_idle() def create_axes_nonorthogonal(self, transform): self.clear_figure() self.set_nonorthogonal_transform(transform) self.ax = CurveLinearSubPlot(self.fig, 1, 1, 1, grid_helper=GridHelperCurveLinear( (self.nonortho_tr, transform.inv_tr))) # don't redraw on zoom as the data is rebinned and has to be redrawn again anyway self.enable_zoom_on_mouse_scroll(redraw=False) self.set_grid_on() self.fig.add_subplot(self.ax) self.plot_MDH = self.plot_MDH_nonorthogonal self.canvas.draw_idle() def enable_zoom_on_mouse_scroll(self, redraw): """Enable zoom on scroll the mouse wheel for the created axes :param redraw: Pass through to redraw option in enable_zoom_on_scroll """ self.canvas.enable_zoom_on_scroll(self.ax, redraw=redraw, toolbar=self.mpl_toolbar, callback=self.on_data_limits_changed) def add_line_plots(self): """Assuming line plots are currently disabled, enable them on the current figure The image axes must have been created first. """ if self.line_plots: return self.line_plots = True image_axes = self.ax if image_axes is None: return # Create a new GridSpec and reposition the existing image Axes gs = gridspec.GridSpec(2, 2, width_ratios=[1, 4], height_ratios=[4, 1], wspace=0.0, hspace=0.0) image_axes.set_position(gs[1].get_position(self.fig)) set_artist_property(image_axes.get_xticklabels(), visible=False) set_artist_property(image_axes.get_yticklabels(), visible=False) self.axx = self.fig.add_subplot(gs[3], sharex=image_axes) self.axx.yaxis.tick_right() self.axy = self.fig.add_subplot(gs[0], sharey=image_axes) self.axy.xaxis.tick_top() self.update_line_plot_labels() self.mpl_toolbar.update() # sync list of axes in navstack self.canvas.draw_idle() def remove_line_plots(self): """Assuming line plots are currently enabled, remove them from the current figure """ if not self.line_plots: return self.line_plots = False image_axes = self.ax if image_axes is None: return self.delete_line_plot_lines() all_axes = self.fig.axes # The order is defined by the order of the add_subplot calls so we always want to remove # the last two Axes. Do it backwards to cope with the container size change all_axes[2].remove() all_axes[1].remove() gs = gridspec.GridSpec(1, 1) image_axes.set_position(gs[0].get_position(self.fig)) image_axes.xaxis.tick_bottom() image_axes.yaxis.tick_left() self.axx, self.axy = None, None self.mpl_toolbar.update() # sync list of axes in navstack self.canvas.draw_idle() def plot_MDH_orthogonal(self, ws, **kwargs): """ clears the plot and creates a new one using a MDHistoWorkspace """ self.clear_image() self.image = self.ax.imshow(ws, origin='lower', aspect='auto', transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), **kwargs) # ensure the axes data limits are updated to match the # image. For example if the axes were zoomed and the # swap dimensions was clicked we need to restore the # appropriate extents to see the image in the correct place extent = self.image.get_extent() self.ax.set_xlim(extent[0], extent[1]) self.ax.set_ylim(extent[2], extent[3]) self.draw_plot() def plot_MDH_nonorthogonal(self, ws, **kwargs): self.clear_image() self.image = pcolormesh_nonorthogonal( self.ax, ws, self.nonortho_tr, transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), **kwargs) # swapping dimensions in nonorthogonal mode currently resets back to the # full data limits as the whole axes has been recreated so we don't have # access to the original limits # pcolormesh clears any grid that was previously visible if self.grid_on: self.ax.grid(self.grid_on) self.draw_plot() def plot_matrix(self, ws, **kwargs): """ clears the plot and creates a new one using a MatrixWorkspace keeping the axes limits that have already been set """ # ensure view is correct if zoomed in while swapping dimensions # compute required extent and just have resampling imshow deal with it old_extent = None if self.image is not None: old_extent = self.image.get_extent() if self.image.transpose != self.dimensions.transpose: e1, e2, e3, e4 = old_extent old_extent = e3, e4, e1, e2 self.clear_image() self.image = imshow_sampling(self.ax, ws, origin='lower', aspect='auto', interpolation='none', transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), extent=old_extent, **kwargs) self.draw_plot() def clear_image(self): """Removes any image from the axes""" if self.image is not None: if self.line_plots: self.delete_line_plot_lines() self.image_info_widget.updateTable(DBLMAX, DBLMAX, DBLMAX) self.image.remove() self.image = None def clear_figure(self): """Removes everything from the figure""" if self.line_plots: self.delete_line_plot_lines() self.axx, self.axy = None, None self.image = None self.canvas.disable_zoom_on_scroll() self.fig.clf() self.ax = None def draw_plot(self): self.ax.set_title('') self.colorbar.set_mappable(self.image) self.colorbar.update_clim() self.mpl_toolbar.update() # clear nav stack self.delete_line_plot_lines() self.update_line_plot_labels() self.canvas.draw_idle() def select_zoom(self): """Select the zoom control on the toolbar""" self.mpl_toolbar.zoom() def update_plot_data(self, data): """ This just updates the plot data without creating a new plot. The extents can change if the data has been rebinned """ if self.nonortho_tr: self.image.set_array(data.T.ravel()) else: self.image.set_data(data.T) self.colorbar.update_clim() def on_home_clicked(self): """Reset the view to encompass all of the data""" self.presenter.show_all_data_requested() def on_line_plots_toggle(self, state): self.presenter.line_plots(state) def on_non_orthogonal_axes_toggle(self, state): """ Switch state of the non-orthognal axes on/off """ self.presenter.nonorthogonal_axes(state) def on_data_limits_changed(self): """ React to when the data limits have changed """ self.presenter.data_limits_changed() def enable_lineplots_button(self): """ Enables line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.LINEPLOTS, True) def disable_lineplots_button(self): """ Disabled line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.LINEPLOTS, False) def enable_peaks_button(self): """ Enables line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.OVERLAYPEAKS, True) def disable_peaks_button(self): """ Disables line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.OVERLAYPEAKS, False) def enable_nonorthogonal_axes_button(self): """ Enables access to non-orthogonal axes functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.NONORTHOGONAL_AXES, True) def disable_nonorthogonal_axes_button(self): """ Disables non-orthorognal axes functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.NONORTHOGONAL_AXES, state=False) def delete_line_plot_lines(self): try: # clear old plots try: self.xfig.remove() self.yfig.remove() except ValueError: pass del self.xfig del self.yfig except AttributeError: pass def get_axes_limits(self): """ Return the limits of the image axes or None if no image yet exists """ if self.image is None: return None else: return self.ax.get_xlim(), self.ax.get_ylim() def set_axes_limits(self, xlim, ylim): """ Set the view limits on the image axes to the given extents :param xlim: 2-tuple of (xmin, xmax) :param ylim: 2-tuple of (ymin, ymax) """ self.ax.set_xlim(xlim) self.ax.set_ylim(ylim) def set_grid_on(self): """ If not visible sets the grid visibility """ if not self._grid_on: self._grid_on = True self.mpl_toolbar.set_action_checked(ToolItemText.GRID, state=self._grid_on) def set_nonorthogonal_transform(self, transform): """ Set the transform for nonorthogonal axes mode :param transform: An object with a tr method to transform from nonorthognal coordinates to display coordinates """ self.nonortho_tr = transform.tr def toggle_grid(self, state): """ Toggle the visibility of the grid on the axes """ self._grid_on = state self.ax.grid(self._grid_on) self.canvas.draw_idle() def mouse_move(self, event): if event.inaxes == self.ax: signal = self.update_image_data(event.xdata, event.ydata, self.line_plots) if self.track_cursor.checkState() == Qt.Checked: self.update_image_table_widget(event.xdata, event.ydata, signal) def mouse_outside_image(self, _): """ Indicates that the mouse have moved outside of an axes. We clear the line plots so that it is not confusing what they mean. """ if self.line_plots: self.delete_line_plot_lines() self.canvas.draw_idle() def mouse_click(self, event): if self.track_cursor.checkState() == Qt.Unchecked \ and event.inaxes == self.ax and event.button == 1: signal = self.update_image_data(event.xdata, event.ydata) self.update_image_table_widget(event.xdata, event.ydata, signal) def mouse_release(self, event): if event.button == 3 and event.inaxes == self.ax: self.on_home_clicked() def update_image_table_widget(self, xdata, ydata, signal): if signal is not None: if self.dimensions.transpose and self.ws_type == "MATRIX": self.image_info_widget.updateTable(ydata, xdata, signal) else: self.image_info_widget.updateTable(xdata, ydata, signal) def plot_x_line(self, x, y): try: self.xfig.set_data(x, y) except (AttributeError, IndexError): self.axx.clear() self.xfig = self.axx.plot(x, y, scalex=False)[0] self.update_line_plot_labels() self.update_line_plot_limits() self.canvas.draw_idle() def plot_y_line(self, x, y): try: self.yfig.set_data(y, x) except (AttributeError, IndexError): self.axy.clear() self.yfig = self.axy.plot(y, x, scaley=False)[0] self.update_line_plot_labels() self.update_line_plot_limits() self.canvas.draw_idle() def update_data_clim(self): self.image.set_clim(self.colorbar.colorbar.mappable.get_clim()) self.canvas.draw_idle() def update_line_plot_limits(self): try: # set line plot intensity axes to match colorbar limits self.axx.set_ylim(self.colorbar.cmin_value, self.colorbar.cmax_value) self.axy.set_xlim(self.colorbar.cmin_value, self.colorbar.cmax_value) except AttributeError: pass def update_line_plot_labels(self): try: # ensure plot labels are in sync with main axes self.axx.set_xlabel(self.ax.get_xlabel()) self.axy.set_ylabel(self.ax.get_ylabel()) except AttributeError: pass def update_image_data(self, x, y, update_line_plot=False): xmin, xmax, ymin, ymax = self.image.get_extent() arr = self.image.get_array() data_extent = Bbox([[ymin, xmin], [ymax, xmax]]) array_extent = Bbox([[0, 0], arr.shape[:2]]) trans = BboxTransform(boxin=data_extent, boxout=array_extent) point = trans.transform_point([y, x]) if any(np.isnan(point)): return i, j = point.astype(int) if update_line_plot: if 0 <= i < arr.shape[0]: self.plot_x_line(np.linspace(xmin, xmax, arr.shape[1]), arr[i, :]) if 0 <= j < arr.shape[1]: self.plot_y_line(np.linspace(ymin, ymax, arr.shape[0]), arr[:, j]) # Clip the coordinates at array bounds if not (0 <= i < arr.shape[0]) or not (0 <= j < arr.shape[1]): return None else: return arr[i, j] def set_normalization(self, ws, **kwargs): normalize_by_bin_width, _ = get_normalize_by_bin_width( ws, self.ax, **kwargs) is_normalized = normalize_by_bin_width or ws.isDistribution() if is_normalized: self.presenter.normalization = mantid.api.MDNormalization.VolumeNormalization self.norm_opts.setCurrentIndex(1) else: self.presenter.normalization = mantid.api.MDNormalization.NoNormalization self.norm_opts.setCurrentIndex(0)
class DGSPlannerGUI(QtWidgets.QWidget): def __init__(self, parent=None, window_flags=None, ol=None): # pylint: disable=unused-argument,super-on-old-class super(DGSPlannerGUI, self).__init__(parent) if window_flags: self.setWindowFlags(window_flags) # OrientedLattice if ValidateOL(ol): self.ol = ol else: self.ol = mantid.geometry.OrientedLattice() self.masterDict = dict() # holds info about instrument and ranges self.updatedInstrument = False self.instrumentWAND = False self.updatedOL = False self.wg = None # workspace group self.instrumentWidget = InstrumentSetupWidget.InstrumentSetupWidget( self) self.setLayout(QtWidgets.QHBoxLayout()) controlLayout = QtWidgets.QVBoxLayout() geometryBox = QtWidgets.QGroupBox("Instrument Geometry") plotBox = QtWidgets.QGroupBox("Plot Axes") geometryBoxLayout = QtWidgets.QVBoxLayout() geometryBoxLayout.addWidget(self.instrumentWidget) geometryBox.setLayout(geometryBoxLayout) controlLayout.addWidget(geometryBox) self.ublayout = QtWidgets.QHBoxLayout() self.classic = ClassicUBInputWidget.ClassicUBInputWidget(self.ol) self.ublayout.addWidget(self.classic, alignment=QtCore.Qt.AlignTop, stretch=1) self.matrix = MatrixUBInputWidget.MatrixUBInputWidget(self.ol) self.ublayout.addWidget(self.matrix, alignment=QtCore.Qt.AlignTop, stretch=1) sampleBox = QtWidgets.QGroupBox("Sample") sampleBox.setLayout(self.ublayout) controlLayout.addWidget(sampleBox) self.dimensionWidget = DimensionSelectorWidget.DimensionSelectorWidget( self) plotBoxLayout = QtWidgets.QVBoxLayout() plotBoxLayout.addWidget(self.dimensionWidget) plotControlLayout = QtWidgets.QGridLayout() self.plotButton = QtWidgets.QPushButton("Plot", self) self.oplotButton = QtWidgets.QPushButton("Overplot", self) self.helpButton = QtWidgets.QPushButton("?", self) self.colorLabel = QtWidgets.QLabel('Color by angle', self) self.colorButton = QtWidgets.QCheckBox(self) self.colorButton.toggle() self.aspectLabel = QtWidgets.QLabel('Aspect ratio 1:1', self) self.aspectButton = QtWidgets.QCheckBox(self) self.saveButton = QtWidgets.QPushButton("Save Figure", self) plotControlLayout.addWidget(self.plotButton, 0, 0) plotControlLayout.addWidget(self.oplotButton, 0, 1) plotControlLayout.addWidget(self.colorLabel, 0, 2, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.colorButton, 0, 3) plotControlLayout.addWidget(self.aspectLabel, 0, 4, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.aspectButton, 0, 5) plotControlLayout.addWidget(self.helpButton, 0, 6) plotControlLayout.addWidget(self.saveButton, 0, 7) plotBoxLayout.addLayout(plotControlLayout) plotBox = QtWidgets.QGroupBox("Plot Axes") plotBox.setLayout(plotBoxLayout) controlLayout.addWidget(plotBox) self.layout().addLayout(controlLayout) # figure self.figure = Figure() self.figure.patch.set_facecolor('white') self.canvas = FigureCanvas(self.figure) self.grid_helper = GridHelperCurveLinear((self.tr, self.inv_tr)) self.trajfig = Subplot(self.figure, 1, 1, 1, grid_helper=self.grid_helper) if matplotlib.compare_versions('2.1.0', matplotlib.__version__): self.trajfig.hold( True) # hold is deprecated since 2.1.0, true by default self.figure.add_subplot(self.trajfig) self.toolbar = MantidNavigationToolbar(self.canvas, self) figureLayout = QtWidgets.QVBoxLayout() figureLayout.addWidget(self.toolbar, 0) figureLayout.addWidget(self.canvas, 1) self.layout().addLayout(figureLayout) self.needToClear = False self.saveDir = '' # connections self.matrix.UBmodel.changed.connect(self.updateUB) self.matrix.UBmodel.changed.connect(self.classic.updateOL) self.classic.changed.connect(self.matrix.UBmodel.updateOL) self.classic.changed.connect(self.updateUB) self.instrumentWidget.changed.connect(self.updateParams) self.instrumentWidget.getInstrumentComboBox().activated[str].connect( self.instrumentUpdateEvent) self.instrumentWidget.getEditEi().textChanged.connect( self.eiWavelengthUpdateEvent) self.dimensionWidget.changed.connect(self.updateParams) self.plotButton.clicked.connect(self.updateFigure) self.oplotButton.clicked.connect(self.updateFigure) self.helpButton.clicked.connect(self.help) self.saveButton.clicked.connect(self.save) # force an update of values self.instrumentWidget.updateAll() self.dimensionWidget.updateChanges() # help self.assistant_process = QtCore.QProcess(self) # pylint: disable=protected-access self.mantidplot_name = 'DGS Planner' # control for cancel button self.iterations = 0 self.progress_canceled = False # register startup mantid.UsageService.registerFeatureUsage( mantid.kernel.FeatureType.Interface, "DGSPlanner", False) @QtCore.Slot(mantid.geometry.OrientedLattice) def updateUB(self, ol): self.ol = ol self.updatedOL = True self.trajfig.clear() def eiWavelengthUpdateEvent(self): if self.masterDict['instrument'] == 'WAND\u00B2': ei = UnitConversion.run('Wavelength', 'Energy', self.masterDict['Ei'], 0, 0, 0, Elastic, 0) offset = ei * 0.01 lowerBound = -offset upperBound = offset self.dimensionWidget.set_editMin4(lowerBound) self.dimensionWidget.set_editMax4(upperBound) def instrumentUpdateEvent(self): if self.masterDict['instrument'] == 'WAND\u00B2': self.instrumentWAND = True # change the ui accordingly self.dimensionWidget.toggleDeltaE(False) self.instrumentWidget.setLabelEi('Input Wavelength') self.instrumentWidget.setEiVal(str(1.488)) self.instrumentWidget.setGoniometerNames(['s1', 'sgl', 'sgu']) self.instrumentWidget.setGoniometerDirections( ['0,1,0', '1,0,0', '0,0,1']) self.instrumentWidget.setGoniometerRotationSense([1, -1, -1]) self.instrumentWidget.updateAll() self.eiWavelengthUpdateEvent() else: if self.instrumentWAND: self.instrumentWAND = False self.dimensionWidget.toggleDeltaE(True) self.instrumentWidget.setLabelEi('Incident Energy') self.instrumentWidget.setEiVal(str(10.0)) self.instrumentWidget.setGoniometerNames(['psi', 'gl', 'gs']) self.instrumentWidget.setGoniometerDirections( ['0,1,0', '0,0,1', '1,0,0']) self.instrumentWidget.setGoniometerRotationSense([1, 1, 1]) self.instrumentWidget.updateAll() @QtCore.Slot(dict) def updateParams(self, d): if self.sender() is self.instrumentWidget: self.updatedInstrument = True if 'dimBasis' in d and 'dimBasis' in self.masterDict and d[ 'dimBasis'] != self.masterDict['dimBasis']: self.needToClear = True if 'dimIndex' in d and 'dimIndex' in self.masterDict and d[ 'dimIndex'] != self.masterDict['dimIndex']: self.needToClear = True self.masterDict.update(copy.deepcopy(d)) def help(self): show_interface_help(self.mantidplot_name, self.assistant_process, area='direct') def closeEvent(self, event): self.assistant_process.close() self.assistant_process.waitForFinished() event.accept() def _create_goniometer_workspaces(self, gonioAxis0values, gonioAxis1values, gonioAxis2values, progressDialog): groupingStrings = [] i = 0 for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: name = "__temp_instrument" + str(i) i += 1 progressDialog.setValue(i) progressDialog.setLabelText( "Creating workspace %d of %d..." % (i, self.iterations)) QtWidgets.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled = True progressDialog.close() return None groupingStrings.append(name) mantid.simpleapi.CloneWorkspace("__temp_instrument", OutputWorkspace=name) mantid.simpleapi.SetGoniometer( Workspace=name, Axis0=str(g0) + "," + self.masterDict['gonioDirs'][0] + "," + str(self.masterDict['gonioSenses'][0]), Axis1=str(g1) + "," + self.masterDict['gonioDirs'][1] + "," + str(self.masterDict['gonioSenses'][1]), Axis2=str(g2) + "," + self.masterDict['gonioDirs'][2] + "," + str(self.masterDict['gonioSenses'][2])) return groupingStrings # pylint: disable=too-many-locals def updateFigure(self): # noqa: C901 # pylint: disable=too-many-branches if self.updatedInstrument or self.progress_canceled: self.progress_canceled = False # get goniometer settings first gonioAxis0values = numpy.arange( self.masterDict['gonioMinvals'][0], self.masterDict['gonioMaxvals'][0] + 0.1 * self.masterDict['gonioSteps'][0], self.masterDict['gonioSteps'][0]) gonioAxis1values = numpy.arange( self.masterDict['gonioMinvals'][1], self.masterDict['gonioMaxvals'][1] + 0.1 * self.masterDict['gonioSteps'][1], self.masterDict['gonioSteps'][1]) gonioAxis2values = numpy.arange( self.masterDict['gonioMinvals'][2], self.masterDict['gonioMaxvals'][2] + 0.1 * self.masterDict['gonioSteps'][2], self.masterDict['gonioSteps'][2]) self.iterations = len(gonioAxis0values) * len( gonioAxis1values) * len(gonioAxis2values) if self.iterations > 10: reply = QtWidgets.QMessageBox.warning( self, 'Goniometer', "More than 10 goniometer settings. This might be long.\n" "Are you sure you want to proceed?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.No: return if self.wg is not None: mantid.simpleapi.DeleteWorkspace(self.wg) instrumentName = self.masterDict['instrument'] if instrumentName == 'WAND\u00B2': instrumentName = 'WAND' mantid.simpleapi.LoadEmptyInstrument( mantid.api.ExperimentInfo.getInstrumentFilename( instrumentName), OutputWorkspace="__temp_instrument") if self.masterDict['instrument'] == 'HYSPEC': mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='msd', LogText='1798.5', LogType='Number Series') mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='s2', LogText=str( self.masterDict['S2']), LogType='Number Series') mantid.simpleapi.LoadInstrument(Workspace="__temp_instrument", RewriteSpectraMap=True, InstrumentName="HYSPEC") elif self.masterDict['instrument'] == 'EXED': mantid.simpleapi.RotateInstrumentComponent( Workspace="__temp_instrument", ComponentName='Tank', Y=1, Angle=str(self.masterDict['S2']), RelativeRotation=False) elif instrumentName == 'WAND': mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='HB2C:Mot:s2.RBV', LogText=str( self.masterDict['S2']), LogType='Number Series') mantid.simpleapi.AddSampleLog(Workspace="__temp_instrument", LogName='HB2C:Mot:detz.RBV', LogText=str( self.masterDict['DetZ']), LogType='Number Series') mantid.simpleapi.LoadInstrument(Workspace="__temp_instrument", RewriteSpectraMap=True, InstrumentName="WAND") # masking if 'maskFilename' in self.masterDict and len( self.masterDict['maskFilename'].strip()) > 0: try: __maskWS = mantid.simpleapi.Load( self.masterDict['maskFilename']) mantid.simpleapi.MaskDetectors( Workspace="__temp_instrument", MaskedWorkspace=__maskWS) except (ValueError, RuntimeError) as e: reply = QtWidgets.QMessageBox.critical( self, 'Error', "The following error has occurred in loading the mask:\n" + str(e) + "\nDo you want to continue without mask?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.No: return if self.masterDict['makeFast']: sp = list( range( mantid.mtd["__temp_instrument"].getNumberHistograms())) tomask = sp[1::4] + sp[2::4] + sp[3::4] mantid.simpleapi.MaskDetectors("__temp_instrument", SpectraList=tomask) progressDialog = QtWidgets.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") groupingStrings = self._create_goniometer_workspaces( gonioAxis0values, gonioAxis1values, gonioAxis2values, progressDialog) if groupingStrings is None: return progressDialog.close() mantid.simpleapi.DeleteWorkspace("__temp_instrument") self.wg = mantid.simpleapi.GroupWorkspaces( groupingStrings, OutputWorkspace="__temp_instrument") self.updatedInstrument = False # set the UB if self.updatedOL or not self.wg[0].sample().hasOrientedLattice(): mantid.simpleapi.SetUB(self.wg, UB=self.ol.getUB()) self.updatedOL = False # calculate coverage dimensions = ['Q1', 'Q2', 'Q3', 'DeltaE'] progressDialog = QtWidgets.QProgressDialog(self) progressDialog.setMinimumDuration(0) progressDialog.setCancelButtonText("&Cancel") progressDialog.setRange(0, self.iterations) progressDialog.setWindowTitle("DGSPlanner progress") if self.masterDict['instrument'] == 'WAND\u00B2': ei = UnitConversion.run('Wavelength', 'Energy', self.masterDict['Ei'], 0, 0, 0, Elastic, 0) else: ei = self.masterDict['Ei'] for i in range(self.iterations): progressDialog.setValue(i) progressDialog.setLabelText("Calculating orientation %d of %d..." % (i, self.iterations)) QtWidgets.qApp.processEvents() if progressDialog.wasCanceled(): self.progress_canceled = True progressDialog.close() return __mdws = mantid.simpleapi.CalculateCoverageDGS( self.wg[i], Q1Basis=self.masterDict['dimBasis'][0], Q2Basis=self.masterDict['dimBasis'][1], Q3Basis=self.masterDict['dimBasis'][2], IncidentEnergy=ei, Dimension1=dimensions[self.masterDict['dimIndex'][0]], Dimension1Min=float2Input(self.masterDict['dimMin'][0]), Dimension1Max=float2Input(self.masterDict['dimMax'][0]), Dimension1Step=float2Input(self.masterDict['dimStep'][0]), Dimension2=dimensions[self.masterDict['dimIndex'][1]], Dimension2Min=float2Input(self.masterDict['dimMin'][1]), Dimension2Max=float2Input(self.masterDict['dimMax'][1]), Dimension2Step=float2Input(self.masterDict['dimStep'][1]), Dimension3=dimensions[self.masterDict['dimIndex'][2]], Dimension3Min=float2Input(self.masterDict['dimMin'][2]), Dimension3Max=float2Input(self.masterDict['dimMax'][2]), Dimension4=dimensions[self.masterDict['dimIndex'][3]], Dimension4Min=float2Input(self.masterDict['dimMin'][3]), Dimension4Max=float2Input(self.masterDict['dimMax'][3])) if i == 0: intensity = __mdws.getSignalArray( )[:, :, 0, 0] * 1. # to make it writeable else: if self.colorButton.isChecked(): tempintensity = __mdws.getSignalArray()[:, :, 0, 0] intensity[numpy.where(tempintensity > 0)] = i + 1. else: tempintensity = __mdws.getSignalArray()[:, :, 0, 0] intensity[numpy.where(tempintensity > 0)] = 1. progressDialog.close() x = numpy.linspace( __mdws.getDimension(0).getMinimum(), __mdws.getDimension(0).getMaximum(), intensity.shape[0]) y = numpy.linspace( __mdws.getDimension(1).getMinimum(), __mdws.getDimension(1).getMaximum(), intensity.shape[1]) Y, X = numpy.meshgrid(y, x) xx, yy = self.tr(X, Y) Z = numpy.ma.masked_array(intensity, intensity == 0) Z = Z[:-1, :-1] # plotting if self.sender() is self.plotButton or self.needToClear: self.figure.clear() self.trajfig.clear() self.figure.add_subplot(self.trajfig) self.needToClear = False self.trajfig.pcolorfast(xx, yy, Z) if self.aspectButton.isChecked(): self.trajfig.set_aspect(1.) else: self.trajfig.set_aspect('auto') self.trajfig.set_xlabel(self.masterDict['dimNames'][0]) self.trajfig.set_ylabel(self.masterDict['dimNames'][1]) self.trajfig.grid(True) self.canvas.draw() mantid.simpleapi.DeleteWorkspace(__mdws) def save(self): fileName = QtWidgets.QFileDialog.getSaveFileName( self, 'Save Plot', self.saveDir, '*.png') if isinstance(fileName, tuple): fileName = fileName[0] if not fileName: return data = "Instrument " + self.masterDict['instrument'] + '\n' if self.masterDict['instrument'] == 'HYSPEC': data += "S2 = " + str(self.masterDict['S2']) + '\n' data += "Ei = " + str(self.masterDict['Ei']) + ' meV\n' data += "Goniometer values:\n" gonioAxis0values = numpy.arange( self.masterDict['gonioMinvals'][0], self.masterDict['gonioMaxvals'][0] + 0.1 * self.masterDict['gonioSteps'][0], self.masterDict['gonioSteps'][0]) gonioAxis1values = numpy.arange( self.masterDict['gonioMinvals'][1], self.masterDict['gonioMaxvals'][1] + 0.1 * self.masterDict['gonioSteps'][1], self.masterDict['gonioSteps'][1]) gonioAxis2values = numpy.arange( self.masterDict['gonioMinvals'][2], self.masterDict['gonioMaxvals'][2] + 0.1 * self.masterDict['gonioSteps'][2], self.masterDict['gonioSteps'][2]) for g0 in gonioAxis0values: for g1 in gonioAxis1values: for g2 in gonioAxis2values: data += " " + self.masterDict['gonioLabels'][ 0] + " = " + str(g0) data += " " + self.masterDict['gonioLabels'][ 1] + " = " + str(g1) data += " " + self.masterDict['gonioLabels'][ 2] + " = " + str(g2) + '\n' data += "Lattice parameters:\n" data += " a = " + str(self.ol.a()) + " b = " + str( self.ol.b()) + " c = " + str(self.ol.c()) + '\n' data += " alpha = " + str(self.ol.alpha()) + " beta = " + str( self.ol.beta()) + " gamma = " + str(self.ol.gamma()) + '\n' data += "Orientation vectors:\n" data += " u = " + str(self.ol.getuVector()) + '\n' data += " v = " + str(self.ol.getvVector()) + '\n' data += "Integrated " + self.masterDict['dimNames'][2] + " between " + \ str(self.masterDict['dimMin'][2]) + " and " + str(self.masterDict['dimMax'][2]) + '\n' data += "Integrated " + self.masterDict['dimNames'][3] + " between " + \ str(self.masterDict['dimMin'][3]) + " and " + str(self.masterDict['dimMax'][3]) + '\n' info = self.figure.text(0.2, 0, data, verticalalignment='top') self.figure.savefig(fileName, bbox_inches='tight', additional_artists=info) self.saveDir = os.path.dirname(fileName) def tr(self, x, y): x, y = numpy.asarray(x), numpy.asarray(y) # one of the axes is energy if self.masterDict['dimIndex'][0] == 3 or self.masterDict['dimIndex'][ 1] == 3: return x, y else: h1, k1, l1 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][0]].split(',')) h2, k2, l2 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][1]].split(',')) angle = numpy.radians(self.ol.recAngle(h1, k1, l1, h2, k2, l2)) return 1. * x + numpy.cos(angle) * y, numpy.sin(angle) * y def inv_tr(self, x, y): x, y = numpy.asarray(x), numpy.asarray(y) # one of the axes is energy if self.masterDict['dimIndex'][0] == 3 or self.masterDict['dimIndex'][ 1] == 3: return x, y else: h1, k1, l1 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][0]].split(',')) h2, k2, l2 = (float(temp) for temp in self.masterDict['dimBasis'][ self.masterDict['dimIndex'][1]].split(',')) angle = numpy.radians(self.ol.recAngle(h1, k1, l1, h2, k2, l2)) return 1. * x - y / numpy.tan(angle), y / numpy.sin(angle)
def plotSpikeCountDistributions(sessions,groups=None,sessionTypes=None,samplingRate=30000.0,save=False,windowSize=40,fname=None,figsize=(10,6)): """Plots the isi distributions for all the cells in the given sessions""" fig = plt.figure(figsize=figsize) fig.subplots_adjust(left=0.05,right=.95) spikeCounts = {} ncells = 0 nz = {} if sessionTypes != None: sessionTypes = dict(zip(sessions,sessionTypes)) for g in groups: for s in sessions: dataFile = h5py.File(os.path.expanduser('~/Documents/research/data/spikesorting/hmm/p=1e-20/%sg%.4d.hdf5' % (s,g)),'r') try: for c in dataFile['unitTimePoints'].keys(): sptrain = dataFile['unitTimePoints'][c][:]/(samplingRate/1000) nbins = sptrain.max()/windowSize bins,bs = np.linspace(0,sptrain.max(),nbins,retstep=True) sc,bins = np.histogram(sptrain,bins) cn = 'g%dc%d' % (g,int(c)) sl = s if sessionTypes != None: sl = sessionTypes[s] if cn in spikeCounts: spikeCounts[cn]['%s' %(s,)] = {'counts': sc, 'bins':bins} nz[cn]['%s' %( sl,)] = 1.0*sum(sc==0)/len(sc) else: spikeCounts[cn] = {'%s' %(s,): {'counts':sc,'bins':bins}} nz[cn] = {'%s' %(sl,): 1.0*sum(sc==0)/len(sc)} finally: dataFile.close() i = 1 ncells = len(spikeCounts.keys()) nsessions = len(sessions) colors = ['b','r','g','y','c','m'] for c in spikeCounts.keys(): ax = Subplot(fig,1,ncells,i) formatAxis(ax) fig.add_axes(ax) ax.set_title(c) j = 0 for k,v in spikeCounts[c].items(): #n,b = np.histogram(v['counts'],bins=20,normed=True) #plt.plot(b[:-1],n,label=k) if sessionTypes != None: L = sessionTypes[k] else: L = k n = np.bincount(v['counts']) b = np.unique(v['counts']) b = np.arange(b[0],len(n)) ax.bar(b+j*0.2,1.0*n/n.sum(),align='center',width=0.3,fc=colors[j],label=L) j+=1 i+=1 ax.set_xlabel('Spike counts') fig.axes[-1].legend() if save: if fname == None: fname = os.path.expanduser('~/Documents/research/figures/isi_comparison.pdf') fig.savefig(fname,bbox='tight') return nz
] x_mixing_ratios = [ x_from_Tp((Bolton.mixing_ratio_line(p_all, MRi) + C_to_K), p_all) for MRi in mixing_ratios ] mesh_T, mesh_p = np.meshgrid( np.arange(-60.0, T_levels.max() - C_to_K + 0.1, 0.1), p_all) theta_ep_mesh = Bolton.theta_ep_field(mesh_T, mesh_p) # Plotting Code! skew_grid_helper = GridHelperCurveLinear((from_thermo, to_thermo)) fig = plt.figure() ax = Subplot(fig, 1, 1, 1, grid_helper=skew_grid_helper) fig.add_subplot(ax) for yi in y_p_levels: ax.plot((x_min, x_max), (yi, yi), color=(1.0, 0.8, 0.8)) for x_T in x_T_levels: ax.plot(x_T, y_all_p, color=(1.0, 0.5, 0.5)) for x_theta in x_thetas: ax.plot(x_theta, y_all_p, color=(1.0, 0.7, 0.7)) for x_mixing_ratio in x_mixing_ratios: good = p_all >= 600 # restrict mixing ratio lines to below 600 mb ax.plot(x_mixing_ratio[good], y_all_p[good], color=(0.8, 0.8, 0.6))
def plotwitherrors(time, timeunit, means, meansunit, std): # ---> basic settings title = input('Title: ') y_name = input('Observable name: ') if title == '' or y_name == '': title = 'test' y_name = 'test' axistop_bool = False axisright_bool = False axisbottom_bool = True axisleft_bool = True # <--- basic settings # ---> analyse data # <--- analyse data # plot ---> fig = plt.figure() ax = Subplot(fig, 111) fig.add_subplot(ax) ax.errorbar(time, means, yerr=std, marker='o', markersize=2, c='black', linestyle='none', ecolor='black', elinewidth=1, capthick=1, capsize=3) ax.plot(time, means, color='black', alpha=0.5, linewidth=1.0, linestyle='--') #legend and labels ax.legend(loc='best') plt.xlabel('time in ' + timeunit, **hfont) plt.ylabel(y_name + ' in ' + meansunit, **hfont) # visible axis ax.axis["right"].set_visible(axisright_bool) ax.axis["top"].set_visible(axistop_bool) ax.axis["bottom"].set_visible(axisbottom_bool) ax.axis["left"].set_visible(axisleft_bool) # <--- plot # save plot ---> pdfdirectory = '../../figures/' + title + '.pdf' pgfdirectory = '../../figures/' + title + '.pgf' fig.savefig(pdfdirectory) plt.savefig(pgfdirectory) # <--- save plot print('test run successful') return None
def __init__(self, ol=None, parent=None): # pylint: disable=unused-argument,super-on-old-class super(DGSPlannerGUI, self).__init__(parent) #OrientedLattice if ValidateOL(ol): self.ol = ol else: self.ol = mantid.geometry.OrientedLattice() self.masterDict = dict() #holds info about instrument and ranges self.updatedInstrument = False self.updatedOL = False self.wg = None #workspace group self.instrumentWidget = InstrumentSetupWidget.InstrumentSetupWidget( self) self.setLayout(QtGui.QHBoxLayout()) controlLayout = QtGui.QVBoxLayout() controlLayout.addWidget(self.instrumentWidget) self.ublayout = QtGui.QHBoxLayout() self.classic = ClassicUBInputWidget.ClassicUBInputWidget(self.ol) self.ublayout.addWidget(self.classic, alignment=QtCore.Qt.AlignTop, stretch=1) self.matrix = MatrixUBInputWidget.MatrixUBInputWidget(self.ol) self.ublayout.addWidget(self.matrix, alignment=QtCore.Qt.AlignTop, stretch=1) controlLayout.addLayout(self.ublayout) self.dimensionWidget = DimensionSelectorWidget.DimensionSelectorWidget( self) controlLayout.addWidget(self.dimensionWidget) plotControlLayout = QtGui.QGridLayout() self.plotButton = QtGui.QPushButton("Plot", self) self.oplotButton = QtGui.QPushButton("Overplot", self) self.helpButton = QtGui.QPushButton("?", self) self.colorLabel = QtGui.QLabel('Color by angle', self) self.colorButton = QtGui.QCheckBox(self) self.colorButton.toggle() self.aspectLabel = QtGui.QLabel('Aspect ratio 1:1', self) self.aspectButton = QtGui.QCheckBox(self) self.saveButton = QtGui.QPushButton("Save Figure", self) plotControlLayout.addWidget(self.plotButton, 0, 0) plotControlLayout.addWidget(self.oplotButton, 0, 1) plotControlLayout.addWidget(self.colorLabel, 0, 2, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.colorButton, 0, 3) plotControlLayout.addWidget(self.aspectLabel, 0, 4, QtCore.Qt.AlignRight) plotControlLayout.addWidget(self.aspectButton, 0, 5) plotControlLayout.addWidget(self.helpButton, 0, 6) plotControlLayout.addWidget(self.saveButton, 0, 7) controlLayout.addLayout(plotControlLayout) self.layout().addLayout(controlLayout) #figure self.figure = Figure() self.figure.patch.set_facecolor('white') self.canvas = FigureCanvas(self.figure) self.grid_helper = GridHelperCurveLinear((self.tr, self.inv_tr)) self.trajfig = Subplot(self.figure, 1, 1, 1, grid_helper=self.grid_helper) self.trajfig.hold(True) self.figure.add_subplot(self.trajfig) self.layout().addWidget(self.canvas) self.needToClear = False self.saveDir = '' #connections self.matrix.UBmodel.changed.connect(self.updateUB) self.matrix.UBmodel.changed.connect(self.classic.updateOL) self.classic.changed.connect(self.matrix.UBmodel.updateOL) self.classic.changed.connect(self.updateUB) self.instrumentWidget.changed.connect(self.updateParams) self.dimensionWidget.changed.connect(self.updateParams) self.plotButton.clicked.connect(self.updateFigure) self.oplotButton.clicked.connect(self.updateFigure) self.helpButton.clicked.connect(self.help) self.saveButton.clicked.connect(self.save) #force an update of values self.instrumentWidget.updateAll() self.dimensionWidget.updateChanges() #help self.assistantProcess = QtCore.QProcess(self) # pylint: disable=protected-access self.collectionFile = os.path.join(mantid._bindir, '../docs/qthelp/MantidProject.qhc') version = ".".join(mantid.__version__.split(".")[:2]) self.qtUrl = 'qthelp://org.sphinx.mantidproject.' + version + '/doc/interfaces/DGSPlanner.html' self.externalUrl = 'http://docs.mantidproject.org/nightly/interfaces/DGSPlanner.html' #control for cancel button self.iterations = 0 self.progress_canceled = False
def __init__( self, figure=None, isotherm_locator=None, dry_adiabat_locator=None, anchor=None, ): """ Initialise the tephigram transformation and plot axes. Kwargs: * figure: An existing :class:`matplotlib.figure.Figure` instance for the tephigram plot. If a figure is not provided, a new figure will be created by default. * isotherm_locator: A :class:`tephi.Locator` instance or a numeric step size for the isotherm lines. * dry_adiabat_locator: A :class:`tephi.Locator` instance or a numeric step size for the dry adiabat lines. * anchor: A sequence of two pressure, temperature pairs specifying the extent of the tephigram plot in terms of the bottom left hand corner and the top right hand corner. Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. For example: .. plot:: :include-source: import matplotlib.pyplot as plt from numpy import column_stack import os.path import tephi from tephi import Tephigram dew_point = os.path.join(tephi.DATA_DIR, 'dews.txt') dry_bulb = os.path.join(tephi.DATA_DIR, 'temps.txt') dew_data, temp_data = tephi.loadtxt(dew_point, dry_bulb) dews = column_stack((dew_data.pressure, dew_data.temperature)) temps = column_stack((temp_data.pressure, temp_data.temperature)) tpg = Tephigram() tpg.plot(dews, label='Dew-point', color='blue', linewidth=2) tpg.plot(temps, label='Dry-bulb', color='red', linewidth=2) plt.show() """ if not figure: # Create a default figure. self.figure = plt.figure(0, figsize=(9, 9)) else: self.figure = figure # Configure the locators. if isotherm_locator and not isinstance(isotherm_locator, Locator): if not isinstance(isotherm_locator, numbers.Number): raise ValueError("Invalid isotherm locator") locator_isotherm = Locator(isotherm_locator) else: locator_isotherm = isotherm_locator if dry_adiabat_locator and not isinstance(dry_adiabat_locator, Locator): if not isinstance(dry_adiabat_locator, numbers.Number): raise ValueError("Invalid dry adiabat locator") locator_theta = Locator(dry_adiabat_locator) else: locator_theta = dry_adiabat_locator # Define the tephigram coordinate-system transformation. self.tephi_transform = transforms.TephiTransform() ghelper = GridHelperCurveLinear( self.tephi_transform, tick_formatter1=_FormatterIsotherm(), grid_locator1=locator_isotherm, tick_formatter2=_FormatterTheta(), grid_locator2=locator_theta, ) self.axes = Subplot(self.figure, 1, 1, 1, grid_helper=ghelper) self.transform = self.tephi_transform + self.axes.transData self.axes.axis["isotherm"] = self.axes.new_floating_axis(1, 0) self.axes.axis["theta"] = self.axes.new_floating_axis(0, 0) self.axes.axis["left"].get_helper().nth_coord_ticks = 0 self.axes.axis["left"].toggle(all=True) self.axes.axis["bottom"].get_helper().nth_coord_ticks = 1 self.axes.axis["bottom"].toggle(all=True) self.axes.axis["top"].get_helper().nth_coord_ticks = 0 self.axes.axis["top"].toggle(all=False) self.axes.axis["right"].get_helper().nth_coord_ticks = 1 self.axes.axis["right"].toggle(all=True) self.axes.gridlines.set_linestyle("solid") self.figure.add_subplot(self.axes) # Configure default axes. axis = self.axes.axis["left"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("baseline") axis.major_ticklabels.set_rotation(135) axis = self.axes.axis["right"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("baseline") axis.major_ticklabels.set_rotation(-135) self.axes.axis["top"].major_ticklabels.set_fontsize(10) axis = self.axes.axis["bottom"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_ha("left") axis.major_ticklabels.set_va("top") axis.major_ticklabels.set_rotation(-45) # Isotherms: lines of constant temperature (degC). axis = self.axes.axis["isotherm"] axis.set_axis_direction("right") axis.set_axislabel_direction("-") axis.major_ticklabels.set_rotation(90) axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("bottom") axis.major_ticklabels.set_color("grey") axis.major_ticklabels.set_visible(False) # turned-off # Dry adiabats: lines of constant potential temperature (degC). axis = self.axes.axis["theta"] axis.set_axis_direction("right") axis.set_axislabel_direction("+") axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("bottom") axis.major_ticklabels.set_color("grey") axis.major_ticklabels.set_visible(False) # turned-off axis.line.set_linewidth(3) axis.line.set_linestyle("--") # Lock down the aspect ratio. self.axes.set_aspect(1.0) self.axes.grid(True) # Initialise the text formatter for the navigation status bar. self.axes.format_coord = self._status_bar # Factor in the tephigram transform. ISOBAR_TEXT["transform"] = self.transform WET_ADIABAT_TEXT["transform"] = self.transform MIXING_RATIO_TEXT["transform"] = self.transform # Create plot collections for the tephigram isopleths. func = partial( isopleths.isobar, MIN_THETA, MAX_THETA, self.axes, self.transform, ISOBAR_LINE, ) self._isobars = _PlotCollection( self.axes, ISOBAR_SPEC, MAX_PRESSURE, func, ISOBAR_TEXT, fixed=ISOBAR_FIXED, minimum=MIN_PRESSURE, ) func = partial( isopleths.wet_adiabat, MAX_PRESSURE, MIN_TEMPERATURE, self.axes, self.transform, WET_ADIABAT_LINE, ) self._wet_adiabats = _PlotCollection( self.axes, WET_ADIABAT_SPEC, MAX_WET_ADIABAT, func, WET_ADIABAT_TEXT, fixed=WET_ADIABAT_FIXED, minimum=MIN_WET_ADIABAT, xfocus=True, ) func = partial( isopleths.mixing_ratio, MIN_PRESSURE, MAX_PRESSURE, self.axes, self.transform, MIXING_RATIO_LINE, ) self._mixing_ratios = _PlotCollection( self.axes, MIXING_RATIO_SPEC, MIXING_RATIOS, func, MIXING_RATIO_TEXT, fixed=MIXING_RATIO_FIXED, ) # Initialise for the tephigram plot event handler. plt.connect("motion_notify_event", _handler) self.axes.tephigram = True self.axes.tephigram_original_delta_xlim = DEFAULT_WIDTH self.original_delta_xlim = DEFAULT_WIDTH self.axes.tephigram_transform = self.tephi_transform self.axes.tephigram_inverse = self.tephi_transform.inverted() self.axes.tephigram_isopleths = [ self._isobars, self._wet_adiabats, self._mixing_ratios, ] # The tephigram profiles. self._profiles = [] self.axes.tephigram_profiles = self._profiles # Center the plot around the anchor extent. self._anchor = anchor if self._anchor is not None: self._anchor = np.asarray(anchor) if (self._anchor.ndim != 2 or self._anchor.shape[-1] != 2 or len(self._anchor) != 2): msg = ("Invalid anchor, expecting [(bottom-left-pressure, " "bottom-left-temperature), (top-right-pressure, " "top-right-temperature)]") raise ValueError(msg) ( (bottom_pressure, bottom_temp), (top_pressure, top_temp), ) = self._anchor if (bottom_pressure - top_pressure) < 0: raise ValueError("Invalid anchor pressure range") if (bottom_temp - top_temp) < 0: raise ValueError("Invalid anchor temperature range") self._anchor = isopleths.Profile(anchor, self.axes) self._anchor.plot(visible=False) xlim, ylim = self._calculate_extents() self.axes.set_xlim(xlim) self.axes.set_ylim(ylim)