Source code for GSASII.GSASIIpwdplot

# -*- coding: utf-8 -*-
'''
Classes and routines defined in :mod:`GSASIIpwdplot` follow. 
'''
from __future__ import division, print_function
import time
import copy
import math
import sys
import os.path
import numpy as np
import numpy.ma as ma
from . import GSASIIpath
# Don't depend on wx/matplotlib/scipy for scriptable; or for Sphinx docs
try:
    import wx
    import wx.aui
    import wx.glcanvas
except (ImportError, ValueError):
    print('GSASIIpwdplot: wx not imported')
try:
    import matplotlib as mpl
    if not mpl.get_backend():       #could be assigned by spyder debugger
        mpl.use('wxAgg')
    import matplotlib.figure as mplfig
except (ImportError, ValueError) as err:
    print('GSASIIpwdplot: matplotlib not imported')
    if GSASIIpath.GetConfigValue('debug'): print('error msg:',err)
from . import GSASIIdataGUI as G2gd
from . import GSASIIpwdGUI as G2pdG
from . import GSASIIlattice as G2lat
from . import GSASIImath as G2mth
from . import GSASIIctrlGUI as G2G
#import GSASIIobj as G2obj
from . import GSASIIplot as G2plt
import matplotlib.colors as mpcls
try:
    from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
except ImportError:
    from matplotlib.backends.backend_wx import FigureCanvas as Canvas
try:
    from matplotlib.backends.backend_agg import FigureCanvasAgg as hcCanvas
except ImportError:
    from matplotlib.backends.backend_agg import FigureCanvas as hcCanvas # standard name
except RuntimeError:  # happens during doc builds
    pass

# useful degree trig functions
sind = lambda x: math.sin(x*math.pi/180.)
cosd = lambda x: math.cos(x*math.pi/180.)
tand = lambda x: math.tan(x*math.pi/180.)
asind = lambda x: 180.*math.asin(x)/math.pi
acosd = lambda x: 180.*math.acos(x)/math.pi
atan2d = lambda x,y: 180.*math.atan2(y,x)/math.pi
atand = lambda x: 180.*math.atan(x)/math.pi
# numpy versions
npsind = lambda x: np.sin(x*np.pi/180.)
npcosd = lambda x: np.cos(x*np.pi/180.)
nptand = lambda x: np.tan(x*np.pi/180.)
npacosd = lambda x: 180.*np.arccos(x)/np.pi
npasind = lambda x: 180.*np.arcsin(x)/np.pi
npatand = lambda x: 180.*np.arctan(x)/np.pi
npatan2d = lambda x,y: 180.*np.arctan2(x,y)/np.pi
# misc global vars
Clip_on = GSASIIpath.GetConfigValue('Clip_on',True)
Gkchisq = chr(0x03C7)+chr(0xb2)
plotDebug = False
timeDebug = GSASIIpath.GetConfigValue('Show_timing',False)
# options for publication-quality Rietveld plots
plotOpt = {}
plotOpt['obsInCaption'] = True # include the observed, calc,... items in the plot caption (PlotPatterns)
plotOpt['labelSize'] = '11'
plotOpt['dpi'] = 600
plotOpt['width'] = 8.
plotOpt['height'] = 6.
plotOpt['Show'] = {}
plotOpt['legend'] = {}
plotOpt['colors'] = {}
plotOpt['format'] = None
plotOpt['initNeeded'] = True
plotOpt['lineList']  = ('obs','calc','bkg','zero','diff')
plotOpt['phaseLabels']  = {}
plotOpt['lineWid'] = '1'
plotOpt['saveCSV'] = False
plotOpt['CSVfile'] = None
for xy in 'x','y':
    for minmax in 'min','max':
        key = f'{xy}{minmax}'
        plotOpt[key] = 0.0
        plotOpt[key+'_use'] = False
partialOpts = {}  # options for display of partials
savedX = None     # contains the X values in plot units for "master" pattern, with mask

#### PlotPatterns ################################################################################
[docs] def ReplotPattern(G2frame,newPlot,plotType,PatternName=None,PickName=None): '''This does the same as PlotPatterns except that it expects the information to be plotted (pattern name, item picked in tree + eventually the reflection list) to be passed as names rather than references to wx tree items, defined as class entries ''' if PatternName: pId = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, PatternName) if pId: G2frame.PatternId = pId else: if GSASIIpath.GetConfigValue('debug'): print('PatternName not found',PatternName) return if PickName == PatternName: G2frame.PickId = G2frame.PatternId elif PickName: pId = G2gd.GetGPXtreeItemId(G2frame, G2frame.PatternId, PickName) if pId: G2frame.PickId = pId else: if GSASIIpath.GetConfigValue('debug'): print('PickName not found',PickName) return elif GSASIIpath.GetConfigValue('debug'): print('Possible PickId problem PickId=',G2frame.PickId) # for now I am not sure how to regenerate G2frame.HKL G2frame.HKL = [] # array of generated reflections G2frame.Extinct = [] # array of extinct reflections PlotPatterns(G2frame,plotType=plotType)
[docs] def plotVline(Page,Plot,Lines,Parms,pos,color,pickrad,style='dotted'): '''shortcut to plot vertical lines for limits & Laue satellites. Was used for extrapeaks''' if Page.plotStyle['qPlot']: Lines.append(Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,pos),color=color, picker=pickrad,linestyle=style)) elif Page.plotStyle['dPlot']: Lines.append(Plot.axvline(G2lat.Pos2dsp(Parms,pos),color=color, picker=pickrad,linestyle=style)) else: Lines.append(Plot.axvline(pos,color=color, picker=pickrad,linestyle=style))
[docs] def PlotPatterns(G2frame,newPlot=False,plotType='PWDR',data=None, extraKeys=[],refineMode=False,indexFrom='',fromTree=False): '''Powder pattern plotting package - displays single or multiple powder patterns as intensity vs 2-theta, q or TOF. Can display multiple patterns as "waterfall plots" or contour plots. Log I plotting available. Note that information needed for plotting will be found in: * G2frame.PatternId: contains the tree item for the current histogram * G2frame.PickId: contains the actual selected tree item (can be child of histogram) * G2frame.HKL: used for tooltip display of hkl for a selected/generated phase's reflections when mouse is moved to a reflection location; HKL locations shown (usually as an orange line) in "Index Peak List" & "Unit Cells List" plots. N.B. reflection tick markers are generated from each phase's reflection list. * G2frame.Extinct: used for display of extinct reflections (in blue) for generated reflections when "show extinct" is selected. :param wx.Frame G2frame: main GSAS-II window :param newPlot: Set to True when a new type of plot is drawn (default False) :param plotType: Type of data entry to be plotted (SASD, REFD, PWDR) (default is 'PWDR') :param list data: Contents of histogram :param list extraKeys: list of str values with extra "command" keys to act on plot :param bool refineMode: Set to True when called from inside a refinement default is False :param str indexFrom: Status line message used to label indexing results :param bool fromTree: will be set to True when called from the data tree :returns: if refineMode is True, returns a reference to :func:`refPlotUpdate`. Otherwise, nothing is returned ''' global PlotList,IndxFrom IndxFrom = indexFrom def PublishPlot(event): msg = "" if 'PWDR' not in plottype: msg += " * only PWDR histograms can be used" if G2frame.Contour or not G2frame.SinglePlot: if msg: msg += '\n' msg += " * only when a single histogram is plotted" if Page.plotStyle['logPlot']: if msg: msg += '\n' msg += " * only when the intensity scale is linear/sqrt (not log)" if msg: msg = 'Publication export is only available under limited plot settings\n'+msg G2G.G2MessageBox(G2frame,msg,'Wrong plot settings') print(msg) elif G2frame.Weight: G2frame.Weight = False PlotPatterns(G2frame,plotType=plottype,extraKeys=extraKeys) PublishRietveldPlot(G2frame,Pattern,Plot,Page) G2frame.Weight = True PlotPatterns(G2frame,plotType=plottype,extraKeys=extraKeys) return else: PublishRietveldPlot(G2frame,Pattern,Plot,Page) def OnPlotKeyPress(event): try: #one way to check if key stroke will work on plot Parms,Parms2 = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters')) except TypeError: G2frame.G2plotNB.status.SetStatusText('Select '+plottype+' pattern first',1) return newPlot = False if event.key == 'w': G2frame.Weight = not G2frame.Weight if not G2frame.Weight and not G2frame.Contour and 'PWDR' in plottype: G2frame.SinglePlot = True elif 'PWDR' in plottype: # Turning on Weight plot clears previous limits G2frame.FixedLimits['dylims'] = ['',''] newPlot = True elif event.key in ['shift+1','!']: # save current plot settings as defaults # shift+1 assumes US keyboard print('saving plotting defaults for',G2frame.GPXtree.GetItemText(G2frame.PatternId)) data = G2frame.GPXtree.GetItemPyData(G2frame.PatternId) data[0]['PlotDefaults'] = copy.deepcopy([ Plot.get_xlim(),Plot.get_ylim(),Page.plotStyle, G2frame.SinglePlot, G2frame.Contour, G2frame.Weight, G2frame.plusPlot, G2frame.SubBack]) elif event.key == 'X' and plottype == 'PWDR': G2frame.CumeChi = not G2frame.CumeChi elif event.key == 'e' and plottype in ['SASD','REFD']: G2frame.ErrorBars = not G2frame.ErrorBars elif event.key == 'T' and 'PWDR' in plottype: Page.plotStyle['title'] = not Page.plotStyle.get('title',True) elif event.key == 'f' and 'PWDR' in plottype: # short,full length or no tick-marks if G2frame.Contour: return Page.plotStyle['flTicks'] = (Page.plotStyle.get('flTicks',0)+1)%3 elif event.key == 'x' and 'PWDR' in plottype: Page.plotStyle['exclude'] = not Page.plotStyle['exclude'] elif event.key == 'k' and G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Background': # change the fixed-background mode backPts = G2frame.dataWindow.wxID_BackPts for i,mode in enumerate(('Add','Move','Delete')): # what menu is selected? if G2frame.dataWindow.BackMenu.FindItemById(backPts[mode]).IsChecked(): break else: i = 0 next = (i+1)%3 # set next button for i,mode in enumerate(('Add','Move','Delete')): # what menu is selected? G2frame.dataWindow.BackMenu.FindItemById(backPts[mode]).Check(i==next) wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) elif event.key == '.': Page.plotStyle['WgtDiagnostic'] = not Page.plotStyle.get('WgtDiagnostic',False) newPlot = True elif event.key == 'b' and plottype not in ['SASD','REFD'] and not Page.plotStyle['logPlot'] and not Page.plotStyle['sqrtPlot']: G2frame.SubBack = not G2frame.SubBack elif event.key == 'n': if G2frame.Contour: pass else: Page.plotStyle['logPlot'] = not Page.plotStyle['logPlot'] if Page.plotStyle['logPlot']: Page.plotStyle['sqrtPlot'] = False else: Page.plotStyle['Offset'][0] = 0 newPlot = True elif event.key == 's' and 'PWDR' in plottype: Page.plotStyle['sqrtPlot'] = not Page.plotStyle['sqrtPlot'] if Page.plotStyle['sqrtPlot']: Page.plotStyle['logPlot'] = False G2frame.SubBack = False YmaxS = max(Pattern[1][1]) if Page.plotStyle['sqrtPlot']: Page.plotStyle['delOffset'] = float(.02*np.sqrt(YmaxS)) Page.plotStyle['refOffset'] = float(-0.1*np.sqrt(YmaxS)) Page.plotStyle['refDelt'] = float(.1*np.sqrt(YmaxS)) else: Page.plotStyle['delOffset'] = float(.02*YmaxS) Page.plotStyle['refOffset'] = float(-0.1*YmaxS) Page.plotStyle['refDelt'] = float(.1*YmaxS) newPlot = True elif event.key == 'S' and 'PWDR' in plottype: choice = [m for m in mpl.cm.datad.keys()]+['GSPaired','GSPaired_r',] # if not m.endswith("_r") choice.sort() dlg = wx.SingleChoiceDialog(G2frame,'Select','Color scheme',choice) if dlg.ShowModal() == wx.ID_OK: sel = dlg.GetSelection() G2frame.ContourColor = choice[sel] else: G2frame.ContourColor = GSASIIpath.GetConfigValue('Contour_color','Paired') dlg.Destroy() newPlot = True elif event.key == 'u' and (G2frame.Contour or not G2frame.SinglePlot): if G2frame.Contour: G2frame.Cmax = min(1.0,G2frame.Cmax*1.2) elif Page.plotStyle['Offset'][0] < 100.: Page.plotStyle['Offset'][0] += 1. elif event.key == 'd' and (G2frame.Contour or not G2frame.SinglePlot): if G2frame.Contour: G2frame.Cmax = max(0.0,G2frame.Cmax*0.8) elif Page.plotStyle['Offset'][0] > -100.: Page.plotStyle['Offset'][0] -= 1. elif (event.key == 'u' or event.key == 'd' ) and G2frame.GPXtree.GetItemText(G2frame.PickId) in [ 'Index Peak List','Peak List']: # select the next or previous peak and highlight it if G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Index Peak List': grid = G2frame.indxPeaks elif G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Peak List': grid = G2frame.reflGrid selected = grid.GetSelectedRows() grid.ClearSelection() if event.key == 'd': if not selected: selected = [-1,] r = selected[0]+1 if r >= grid.NumberRows: r = None else: if not selected: selected = [grid.NumberRows] r = selected[0]-1 if r < 0: r = None if r is not None: grid.SelectRow(r,True) grid.MakeCellVisible(r,0) wx.CallAfter(grid.ForceRefresh) elif event.key == 'U': if G2frame.Contour: G2frame.Cmin += (G2frame.Cmax - G2frame.Cmin)/5. elif Page.plotStyle['Offset'][0] < 100.: Page.plotStyle['Offset'][0] += 10. elif event.key == 'D': if G2frame.Contour: G2frame.Cmin -= (G2frame.Cmax - G2frame.Cmin)/5. elif Page.plotStyle['Offset'][0] > -100.: Page.plotStyle['Offset'][0] -= 10. elif event.key == 'g': mpl.rcParams['axes.grid'] = not mpl.rcParams['axes.grid'] elif event.key == 'l' and not G2frame.SinglePlot: Page.plotStyle['Offset'][1] -= 1. elif event.key == 'r' and not G2frame.SinglePlot: Page.plotStyle['Offset'][1] += 1. elif event.key in ['L','shift+l']: # controls legend if G2frame.Contour: return if not plotOpt['obsInCaption'] and len(PlotList) > 20: dlg = wx.MessageDialog(G2frame, f'You have {len(PlotList)} histograms in this plot. Are you sure you want a legend?', 'Confirm legend',wx.YES_NO | wx.ICON_QUESTION) try: result = dlg.ShowModal() finally: dlg.Destroy() if result != wx.ID_YES: return # include the observed, calc,... items in the plot caption (PlotPatterns) plotOpt['obsInCaption'] = not plotOpt['obsInCaption'] elif event.key in ['o','O','shift+o']: # resets offsets if G2frame.Contour: return if not G2frame.SinglePlot: # waterfall: reset the offsets G2frame.Cmax = 1.0 G2frame.Cmin = 0.0 Page.plotStyle['Offset'] = [0,0] elif event.key == 'C' and 'PWDR' in plottype and G2frame.Contour: #G2G.makeContourSliders(G2frame,Ymax,PlotPatterns,newPlot,plotType) G2G.makeContourSliders(G2frame,Ymax,PlotPatterns,True,plotType) # force newPlot=True, prevents blank plot on Mac elif event.key == 'c' and 'PWDR' in plottype: newPlot = True if not G2frame.Contour: G2frame.SinglePlot = False Page.plotStyle['Offset'] = [0.,0.] G2frame.FixedLimits['cylims'] = ['',''] # reset manual limits else: G2frame.SinglePlot = True G2frame.Contour = not G2frame.Contour elif (event.key == 'p' and 'PWDR' in plottype and G2frame.SinglePlot): Page.plotStyle['partials'] = not Page.plotStyle['partials'] elif (event.key == 'e' and 'PWDR' in plottype and G2frame.SinglePlot and ifLimits and not G2frame.Contour): Page.excludeMode = not Page.excludeMode if Page.excludeMode: try: # fails from key menu Page.startExclReg = event.xdata except AttributeError: G2G.G2MessageBox(G2frame,'To create an excluded region, after clicking on "OK", move to the beginning of the region and press the "e" key. Then move to the end of the region and press "e" again','How to exclude') return if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() axis.axvline(Page.startExclReg,color='b',dashes=(2,3)) Page.vLine = Plot.axvline(Page.startExclReg,color='b',dashes=(2,3)) SavePlot4animate() else: UnsavePlot4animate() wx.CallAfter(PlotPatterns,G2frame,newPlot=False, plotType=plottype,extraKeys=extraKeys) if abs(Page.startExclReg - event.xdata) < 0.1: return LimitId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Limits') limdat = G2frame.GPXtree.GetItemPyData(LimitId) mn = min(Page.startExclReg, event.xdata) mx = max(Page.startExclReg, event.xdata) limdat.append([mn,mx]) G2pdG.UpdateLimitsGrid(G2frame,limdat,plottype) return elif event.key == 'a' and 'PWDR' in plottype and G2frame.SinglePlot and not ( Page.plotStyle['logPlot'] or Page.plotStyle['sqrtPlot'] or G2frame.Contour): # add a magnification region try: xpos = event.xdata if xpos is None: return #avoid out of frame mouse position if 'Magnification' not in Pattern[0]: Pattern[0]['Magnification'] = [] try: if Page.plotStyle['qPlot']: xpos = G2lat.Dsp2pos(Parms,2.0*np.pi/xpos) elif Page.plotStyle['dPlot']: xpos = G2lat.Dsp2pos(Parms,xpos) except ValueError: return except AttributeError: # invoked when this is called from dialog rather than key press xpos = (Pattern[1][0][-1]+Pattern[1][0][0])/2 # set to middle of pattern if not Pattern[0]['Magnification']: Pattern[0]['Magnification'] = [[None,1.]] Pattern[0]['Magnification'] += [[xpos,2.]] wx.CallAfter(G2gd.UpdatePWHKPlot,G2frame,plottype,G2frame.PatternId) return elif event.key == 'q' and not ifLimits: newPlot = True if 'PWDR' in plottype: Page.plotStyle['qPlot'] = not Page.plotStyle['qPlot'] Page.plotStyle['dPlot'] = False Page.plotStyle['chanPlot'] = False elif plottype in ['SASD','REFD']: Page.plotStyle['sqPlot'] = not Page.plotStyle['sqPlot'] elif event.key == 'h' and G2frame.Contour: newPlot = True Page.plotStyle['qPlot'] = False Page.plotStyle['dPlot'] = False Page.plotStyle['chanPlot'] = not Page.plotStyle['chanPlot'] elif event.key == 'e' and G2frame.Contour: newPlot = True G2frame.TforYaxis = not G2frame.TforYaxis elif event.key == 't' and 'PWDR' in plottype and not ifLimits: newPlot = True Page.plotStyle['dPlot'] = not Page.plotStyle['dPlot'] Page.plotStyle['qPlot'] = False Page.plotStyle['chanPlot'] = False elif event.key == 'm': if not G2frame.Contour: G2frame.SinglePlot = not G2frame.SinglePlot if not G2frame.SinglePlot: plotOpt['obsInCaption'] = False # remove caption from waterfall G2frame.Contour = False newPlot = True elif event.key == 'F' and not G2frame.SinglePlot: choices = G2gd.GetGPXtreeDataNames(G2frame,plotType) dlg = G2G.G2MultiChoiceDialog(G2frame, 'Select dataset(s) to plot\n(select all or none to reset)', 'Multidata plot selection',choices) if dlg.ShowModal() == wx.ID_OK: G2frame.selections = [] select = dlg.GetSelections() if select and len(select) != len(choices): for Id in select: G2frame.selections.append(choices[Id]) else: G2frame.selections = None dlg.Destroy() newPlot = True elif event.key in ['+','=','shift+=']: # assumes US keyboard if G2frame.Contour: return if G2frame.SinglePlot: G2frame.plusPlot = (G2frame.plusPlot+1)%3 else: G2frame.plusPlot = (G2frame.plusPlot+1)%4 elif event.key == '/': Page.plotStyle['Normalize'] = not Page.plotStyle['Normalize'] newPlot = True elif event.key == 'i' and G2frame.Contour: #for smoothing contour plot choice = ['nearest','bilinear','bicubic','spline16','spline36','hanning', 'hamming','hermite','kaiser','quadric','catrom','gaussian','bessel', 'mitchell','sinc','lanczos'] dlg = wx.SingleChoiceDialog(G2frame,'Select','Interpolation',choice) if dlg.ShowModal() == wx.ID_OK: sel = dlg.GetSelection() G2frame.Interpolate = choice[sel] else: G2frame.Interpolate = 'nearest' dlg.Destroy() elif event.key in [KeyItem[0] for KeyItem in extraKeys]: for KeyItem in extraKeys: if event.key == KeyItem[0]: KeyItem[1]() break elif event.key == 'v' and 'PWDR' in plottype and G2frame.SinglePlot: plotOpt['CSVfile'] = G2G.askSaveFile(G2frame,'','.csv', 'Comma separated variable file') if plotOpt['CSVfile']: plotOpt['saveCSV'] = True else: #print('no binding for key',event.key) return if G2frame.Contour: newPlot = True # needed or plot disappears, at least on Mac wx.CallAfter(PlotPatterns,G2frame,newPlot=newPlot,plotType=plottype,extraKeys=extraKeys) def OnMotion(event): '''PlotPatterns: respond to motion of the cursor. The status line will be updated with info based on the mouse position. Also displays reflection labels as tooltips when mouse is over tickmarks ''' global PlotList G2plt.SetCursor(Page) # excluded region animation if Page.excludeMode: if event.xdata is None or G2frame.GPXtree.GetItemText( G2frame.GPXtree.GetSelection()) != 'Limits': # reset if out of bounds or not on limits UnsavePlot4animate() Page.excludeMode = False wx.CallAfter(PlotPatterns,G2frame,newPlot=False, plotType=plottype,extraKeys=extraKeys) return else: RestorePlot4animate() Page.vLine.set_xdata([event.xdata,event.xdata]) if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() axis.draw_artist(Page.vLine) BlitPlot() return if event.button and G2frame.Contour and G2frame.TforYaxis: ytics = imgAx.get_yticks() ytics = np.where(ytics<len(Temps),ytics,-1) imgAx.set_yticks(ytics) ylabs = [np.where(0<=i ,Temps[int(i)],' ') for i in ytics] imgAx.set_yticklabels(ylabs) xpos = event.xdata if xpos is None: return #avoid out of frame mouse position ypos = event.ydata try: Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters') if not Id: return Parms,Parms2 = G2frame.GPXtree.GetItemPyData(Id) limx = Plot.get_xlim() dT = tolerance = np.fabs(limx[1]-limx[0])/100. if Page.plotStyle['qPlot'] and 'PWDR' in plottype: q = xpos if q <= 0: G2frame.G2plotNB.status.SetStatusText('Q = %9.5f'%q) return try: dsp = 2.*np.pi/q xpos = G2lat.Dsp2pos(Parms,2.0*np.pi/q) except ValueError: #avoid bad value in asin beyond upper limit G2frame.G2plotNB.status.SetStatusText('Q = %9.5f'%q) return if 'T' in Parms['Type'][0]: # TOF dT = Parms['difC'][1] * 2 * np.pi * tolerance / q**2 elif 'E' in Parms['Type'][0]: # energy dispersive x-rays pass #for now else: # 'C' or 'B' in Parms['Type'][0] or 'PKS' in Parms['Type'][0]: wave = G2mth.getWave(Parms) dT = tolerance*wave*90./(np.pi**2*cosd(xpos/2)) elif Page.plotStyle['chanPlot'] and G2frame.Contour: xpos = ma.getdata(X)[min(len(X)-1,int(xpos))] try: dsp = G2lat.Pos2dsp(Parms,xpos) q = 2.*np.pi/dsp except: dsp = -1 q = -1 elif plottype in ['SASD','REFD']: q = xpos if q <= 0: G2frame.G2plotNB.status.SetStatusText('Q = %9.5f'%q) return dsp = 2.*np.pi/q elif Page.plotStyle['dPlot']: dsp = xpos if dsp <= 0: G2frame.G2plotNB.status.SetStatusText('d = %9.5f'%dsp) return try: q = 2.*np.pi/dsp xpos = G2lat.Dsp2pos(Parms,dsp) except ValueError: #avoid bad value G2frame.G2plotNB.status.SetStatusText('d = %9.5f'%dsp) return dT = tolerance*xpos/dsp else: dsp = G2lat.Pos2dsp(Parms,xpos) q = 2.*np.pi/dsp statLine = "" if G2frame.Contour: #PWDR only try: pNum = int(ypos+.5) indx = abs(PlotList[pNum][1][0] - xpos).argmin() # closest point to xpos val = 'int={:.3g}'.format(ma.getdata(PlotList[pNum][1][1])[indx]) if 'T' in Parms['Type'][0]: statLine = 'TOF=%.3f d=%.5f Q=%.5f %s pattern ID=%d, %s'%(xpos,dsp,q,val,pNum,PlotList[pNum][-1]) else: statLine = '2-theta=%.3f d=%.5f Q=%.5f %s pattern ID=%d, %s'%(xpos,dsp,q,val,pNum,PlotList[pNum][-1]) except IndexError: pass else: if 'T' in Parms['Type'][0]: if Page.plotStyle['sqrtPlot']: statLine = 'TOF = %9.3f d=%9.5f Q=%9.5f sqrt(Intensity) =%9.2f'%(xpos,dsp,q,ypos) else: statLine = 'TOF =%9.3f d=%9.5f Q=%9.5f Intensity =%9.2f'%(xpos,dsp,q,ypos) elif 'E' in Parms['Type'][0]: statLine = 'Energy =%9.3f d=%9.5f Q=%9.5f sqrt(Intensity) =%9.2f'%(xpos,dsp,q,ypos) else: if 'PWDR' in plottype: ytmp = ypos if Page.plotStyle['sqrtPlot']: ytmp = ypos**2 statLine = '2-theta=%.3f d=%.5f Q=%.4f Intensity=%.2f'%(xpos,dsp,q,ytmp) elif plottype == 'SASD': statLine = 'q =%12.5g Intensity =%12.5g d =%9.1f'%(q,ypos,dsp) elif plottype == 'REFD': statLine = 'q =%12.5g Reflectivity =%12.5g d =%9.1f'%(q,ypos,dsp) zoomstat = Page.toolbar.get_zoompan() if zoomstat: statLine = "[" + zoomstat + "] " + statLine G2frame.G2plotNB.status.SetStatusText(statLine + IndxFrom,1) s = '' if G2frame.PickId: pickIdText = G2frame.GPXtree.GetItemText(G2frame.PickId) else: pickIdText = '?' # unexpected tickMarkList = ['Index Peak List','Unit Cells List','Reflection Lists'] if pickIdText in tickMarkList and len(G2frame.HKL): found = [] indx = -1 if pickIdText in ['Index Peak List','Unit Cells List',]: indx = -2 # finds reflections within 1% of plot range in units of plot findx = np.where(np.fabs(np.array(G2frame.HKL).T[indx]-xpos) < dT/2.) found = G2frame.HKL[findx] if len(G2frame.Extinct): G2frame.Extinct = np.array(G2frame.Extinct) f2 = G2frame.Extinct[np.where(np.fabs(G2frame.Extinct.T[indx]-xpos) < dT/2.)] found = np.concatenate((found,f2)) if found.shape[0]: if len(found[0]) > 6: #SS reflections fmt = "{:.0f},{:.0f},{:.0f},{:.0f}" n = 4 else: fmt = "{:.0f},{:.0f},{:.0f}" n = 3 for i,hkl in enumerate(found): if i >= 3: s += '\n...' break if s: s += '\n' s += fmt.format(*hkl[:n]) elif G2frame.itemPicked: # not sure when this will happen s = '%9.5f'%(xpos) Page.SetToolTipString(s) except TypeError: G2frame.G2plotNB.status.SetStatusText('Select '+plottype+' pattern first',1) def OnPress(event): np.seterr(invalid='ignore') #ugh - this removes a matplotlib error for mouse clicks in log plots if G2frame.ifSetLimitsMode == 3: # start adding an excluded region savedPlot['excludedPosition'] = event.xdata Page.excludeMode = True if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() LimitId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Limits') limData = G2frame.GPXtree.GetItemPyData(LimitId) x1 = max(limData[0][0],min(event.xdata,limData[0][1])) axis.axvline(x1,color='b',dashes=(2,3)) SavePlot4animate() Page.vLine = Plot.axvline(event.xdata,color='b',dashes=(2,3)) def onMoveDiffCurve(event): '''Respond to a menu command to move the difference curve. ''' if not DifLine[0]: print('No difference curve!') return G2frame.itemPicked = DifLine[0] G2frame.G2plotNB.Parent.Raise() OnPickPwd(None) def onMoveTopTick(event): '''Respond to a menu command to move the tick locations. ''' if len(Page.phaseList) == 0: print("there are tick marks (no phases)") return G2frame.itemPicked = Page.tickDict[Page.phaseList[0]] G2frame.G2plotNB.Parent.Raise() OnPickPwd(None) def onMoveTickSpace(event): '''Respond to a menu command to move the tick spacing. ''' if len(Page.phaseList) == 0: print("there are tick marks (no phases)") return G2frame.itemPicked = Page.tickDict[Page.phaseList[-1]] G2frame.G2plotNB.Parent.Raise() OnPickPwd(None) def onMovePeak(event): reflGrid = G2frame.reflGrid selectedPeaks = list(set([row for row,col in reflGrid.GetSelectedCells()] + reflGrid.GetSelectedRows())) if len(selectedPeaks) != 1: tbl = reflGrid.GetTable().data choices = [f"{i[0]:.2f}" for i in tbl] dlg = G2G.G2SingleChoiceDialog(G2frame,'Select peak to move', 'select peak',choices) try: if dlg.ShowModal() == wx.ID_OK: selectedPeaks = [dlg.GetSelection()] finally: dlg.Destroy() if len(selectedPeaks) != 1: return G2frame.itemPicked = G2frame.Lines[selectedPeaks[0]+2] # 1st 2 lines are limits G2frame.G2plotNB.Parent.Raise() OnPickPwd(None) def OnPickPwd(event): '''Respond to an item being picked. This usually means that the item will be dragged with the mouse, or sometimes the pick object is used to create a peak or an excluded region ''' def OnDragMarker(event): '''Respond to dragging of a plot Marker ''' if event.xdata is None or event.ydata is None: return # ignore if cursor out of window if G2frame.itemPicked is None: return # not sure why this happens, if it does RestorePlot4animate() G2frame.itemPicked.set_data([event.xdata], [event.ydata]) if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() axis.draw_artist(G2frame.itemPicked) BlitPlot() def OnDragLine(event): '''Respond to dragging of a plot line ''' if event.xdata is None: return # ignore if cursor out of window if G2frame.itemPicked is None: return # not sure why this happens RestorePlot4animate() coords = G2frame.itemPicked.get_data() coords[0][0] = coords[0][1] = event.xdata coords = G2frame.itemPicked.set_data(coords) if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() axis.draw_artist(G2frame.itemPicked) BlitPlot() def OnDragLabel(event): '''Respond to dragging of a HKL label ''' if event.xdata is None: return # ignore if cursor out of window if G2frame.itemPicked is None: return # not sure why this happens try: coords = list(G2frame.itemPicked.get_position()) coords[1] = event.ydata data = G2frame.GPXtree.GetItemPyData(G2frame.PatternId) data[0]['HKLmarkers'] = data[0].get('HKLmarkers',{}) k1,k2 = G2frame.itemPicked.key if Page.plotStyle['sqrtPlot']: data[0]['HKLmarkers'][k1][k2][0] = np.sign(coords[1])*coords[1]**2 else: data[0]['HKLmarkers'][k1][k2][0] = coords[1] RestorePlot4animate() G2frame.itemPicked.set_position(coords) if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() axis.draw_artist(G2frame.itemPicked) BlitPlot() except: pass def OnDragTickmarks(event): '''Respond to dragging of the reflection tick marks ''' if event.ydata is None: return # ignore if cursor out of window if Page.tickDict is None: return # not sure why this happens, if it does RestorePlot4animate() if Page.pickTicknum: refDelt = -(event.ydata-Page.plotStyle['refOffset'])/Page.pickTicknum refOffset = Page.plotStyle['refOffset'] else: #1st row of refl ticks refOffset = event.ydata refDelt = Page.plotStyle['refDelt'] if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() for pId,phase in enumerate(Page.phaseList): pos = refOffset - pId*refDelt coords = Page.tickDict[phase].get_data() coords[1][:] = pos Page.tickDict[phase].set_data(coords) axis.draw_artist(Page.tickDict[phase]) BlitPlot() def OnDragDiffCurve(event): '''Respond to dragging of the difference curve. ''' if event.ydata is None: return # ignore if cursor out of window if G2frame.itemPicked is None: return # not sure why this happens RestorePlot4animate() coords = G2frame.itemPicked.get_data() coords[1][:] += Page.diffOffset + event.ydata Page.diffOffset = -event.ydata G2frame.itemPicked.set_data(coords) Page.figure.gca().draw_artist(G2frame.itemPicked) # Diff curve only found in 1-window plot BlitPlot() def DeleteHKLlabel(HKLmarkers,key): '''Delete an HKL label''' del HKLmarkers[key[0]][key[1]] G2frame.itemPicked = None PlotPatterns(G2frame,plotType=plottype,extraKeys=extraKeys) ####====== start of OnPickPwd plotNum = G2frame.G2plotNB.plotList.index('Powder Patterns') inXtraPeakMode = False # Ignore if peak list menubar is not yet created try: inXtraPeakMode = G2frame.dataWindow.XtraPeakMode.IsChecked() except: pass global Page try: Parms,Parms2 = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters')) except TypeError: return if event is None: # called from a menu command rather than by click on mpl artist mouse = 1 pick = G2frame.itemPicked ind = np.array([0]) elif str(event.artist).startswith('Text'): # respond to a right or left click on a HKL Label if G2frame.itemPicked is not None: # only allow one selection return pick = event.artist xpos,ypos = pick.get_position() if event.mouseevent.button == 3: # right click, delete HKL label # but only 1st of the picked items, if multiple G2frame.itemPicked = pick data = G2frame.GPXtree.GetItemPyData(G2frame.PatternId) data[0]['HKLmarkers'] = data[0].get('HKLmarkers',{}) # finish event processing before deleting the selection, so that # any other picked items are skipped wx.CallLater(100,DeleteHKLlabel,data[0]['HKLmarkers'],pick.key) return # prepare to drag HKL label (vertical position only) G2frame.itemPicked = pick pick.set_alpha(.3) # grey out text Page = G2frame.G2plotNB.nb.GetPage(plotNum) SavePlot4animate() G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragLabel) pick.set_alpha(1.0) return else: if G2frame.itemPicked is not None: return pick = event.artist mouse = event.mouseevent xpos = pick.get_xdata() ypos = pick.get_ydata() ind = event.ind xy = list(list(zip(np.take(xpos,ind),np.take(ypos,ind)))[0]) # convert from plot units xtick = xy[0] # selected tickmarck pos in 2theta/TOF or d-space (not Q) if Page.plotStyle['qPlot']: #qplot - convert back to 2-theta/TOF xy[0] = G2lat.Dsp2pos(Parms,2*np.pi/xy[0]) xtick = xy[0] elif Page.plotStyle['dPlot']: #dplot - convert back to 2-theta/TOF xy[0] = G2lat.Dsp2pos(Parms,xy[0]) # if Page.plotStyle['sqrtPlot']: # xy[1] = xy[1]**2 if G2frame.PickId and G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Peak List': # Peak List: add peaks by clicking on points, # Or, by dragging: move peaks, limits, diff curve or (XtraPeaks only) tickmarks phname = '' try: phname = str(pick).split('(',1)[1][:-1] except: pass if ind.all() != [0] and ObsLine[0].get_label() in str(pick): #picked a data point, add a new peak data = G2frame.GPXtree.GetItemPyData(G2frame.PickId) XY = G2mth.setPeakparms(Parms,Parms2,xy[0],xy[1],useFit=True) if inXtraPeakMode: data['xtraPeaks'] = data.get('xtraPeaks',[]) data['xtraPeaks'].append(XY) data['sigDict'] = {} #now invalid else: data['peaks'].append(XY) data['sigDict'] = {} #now invalid G2pdG.UpdatePeakGrid(G2frame,data) PlotPatterns(G2frame,plotType=plottype,extraKeys=extraKeys) elif inXtraPeakMode and phname in Page.phaseList: #picked a tick mark Page.pickTicknum = Page.phaseList.index(phname) resetlist = [] for pId,phase in enumerate(Page.phaseList): # set the tickmarks to a lighter color col = Page.tickDict[phase].get_color() rgb = mpcls.ColorConverter().to_rgb(col) rgb_light = [(2 + i)/3. for i in rgb] resetlist.append((Page.tickDict[phase],rgb)) Page.tickDict[phase].set_color(rgb_light) Page.tickDict[phase].set_zorder(99) # put on top SavePlot4animate() for f,v in resetlist: # reset colors back f.set_zorder(0) f.set_color(v) # reset colors back to original values G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragTickmarks) G2frame.itemPicked = pick return elif DifLine[0] is pick: # drag the difference curve G2frame.itemPicked = pick if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() SavePlot4animate() Page.canvas.draw() # save bitmap G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragDiffCurve) else: #picked a peak list line, prepare to animate move of line G2frame.itemPicked = pick pick.set_linestyle(':') # set line as dotted Page = G2frame.G2plotNB.nb.GetPage(plotNum) if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() SavePlot4animate() G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragLine) pick.set_linestyle('--') # back to dashed elif G2frame.PickId and G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Limits': if len(event.artist.get_data()[0]) != 2 and not G2frame.ifSetLimitsMode: return # don't select a tickmark to drag. But in Limits/Excluded mode # could select a point from the pattern here # Limits: add excluded region or move limits by use of menu command # and then pick a point # Or, drag line for limits/excluded region if ind.all() != [0]: #picked a data point LimitId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Limits') limData = G2frame.GPXtree.GetItemPyData(LimitId) # Q & d not currently allowed on limits plot # if Page.plotStyle['qPlot']: #qplot - convert back to 2-theta # xy[0] = G2lat.Dsp2pos(Parms,2*np.pi/xy[0]) # elif Page.plotStyle['dPlot']: #dplot - convert back to 2-theta # xy[0] = G2lat.Dsp2pos(Parms,xy[0]) # point in pattern selected as new upper or lower limit if G2frame.ifSetLimitsMode == 2: # set upper limData[1][1] = max(xy[0],limData[1][0]) elif G2frame.ifSetLimitsMode == 1: limData[1][0] = min(xy[0],limData[1][1]) # set lower G2frame.ifSetLimitsMode = 0 G2frame.CancelSetLimitsMode.Enable(False) G2frame.GPXtree.SetItemPyData(LimitId,limData) G2pdG.UpdateLimitsGrid(G2frame,limData,plottype) G2frame.GPXtree.SelectItem(LimitId) wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) else: # picked a limit line # prepare to animate move of line G2frame.itemPicked = pick if G2frame.itemPicked in G2frame.MagLines: # don't drag magnification markers here return pick.set_linestyle(':') # set line as dotted Page = G2frame.G2plotNB.nb.GetPage(plotNum) SavePlot4animate() G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragLine) pick.set_linestyle('--') # back to dashed return elif G2frame.PickId and G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Unit Cells List': # By dragging lines: move limits if ind.all() == [0]: # picked a limit line # prepare to animate move of line G2frame.itemPicked = pick pick.set_linestyle(':') # set line as dotted Page = G2frame.G2plotNB.nb.GetPage(plotNum) SavePlot4animate() G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragLine) pick.set_linestyle('--') # back to dashed elif G2frame.PickId and G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Models': if ind.all() != [0]: #picked a data point LimitId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Limits') data = G2frame.GPXtree.GetItemPyData(LimitId) if mouse.button==1: data[1][0] = min(xy[0],data[1][1]) if mouse.button==3: data[1][1] = max(xy[0],data[1][0]) G2frame.GPXtree.SetItemPyData(LimitId,data) wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) else: #picked a limit line G2frame.itemPicked = pick elif G2frame.PickId and ( G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Reflection Lists' or 'PWDR' in G2frame.GPXtree.GetItemText(G2frame.PickId) ): G2frame.itemPicked = pick Page = G2frame.G2plotNB.nb.GetPage(plotNum) if DifLine[0] is G2frame.itemPicked: # pick of difference curve SavePlot4animate() Page.diffOffset = Page.plotStyle['delOffset'] G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragDiffCurve) elif G2frame.itemPicked in G2frame.MagLines: # drag of magnification marker pick.set_dashes((1,4)) # set line as dotted sparse SavePlot4animate() G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragLine) pick.set_dashes((1,1)) # back to dotted else: # pick of plot tick mark (is anything else possible?) ################################################################# if event is not None and event.mouseevent.button == 3: data = G2frame.GPXtree.GetItemPyData(G2frame.PatternId) font = int(data[0]['HKLconfig'].get('Font','8')) angle = data[0]['HKLconfig'].get('Orientation',0) G2frame.itemPicked = None # prevent tickmark repositioning ph = event.artist.get_label() if ph not in Phases: print(f'Tickmark label ({ph}) not in Reflection Lists, very odd') return # find column for d-space or 2theta/TOF Super = 0 if Phases[ph].get('Super',False): Super = 1 if Page.plotStyle['dPlot']: indx = 4 + Super else: indx = 5 + Super limx = Plot.get_xlim() dT = tolerance = np.fabs(limx[1]-limx[0])/100. if Page.plotStyle['qPlot']: if 'T' in Parms['Type'][0]: # TOF q = 2.*np.pi/G2lat.Pos2dsp(Parms,xpos) dT = Parms['difC'][1] * 2 * np.pi * tolerance / q**2 elif 'E' in Parms['Type'][0]: # energy dispersive x-rays pass #for now else: # 'C' or 'B' in Parms['Type'][0] or 'PKS' in Parms['Type'][0]: wave = G2mth.getWave(Parms) dT = tolerance*wave*90./(np.pi**2*cosd(xpos/2)) # locate picked tick markers found_indices = np.where(np.fabs(Phases[ph]['RefList'][:,indx]-xtick) < dT/2.) if len(found_indices) == 0: return # get storage location for labeled reflections data = G2frame.GPXtree.GetItemPyData(G2frame.PatternId) data[0]['HKLmarkers'] = data[0].get('HKLmarkers',{}) n = 3 if Super: n = 4 data[0]['HKLmarkers'][ph] = data[0]['HKLmarkers'].get(ph,{}) # position for 1st tick mark ytick = ypos[0] - 3*(Plot.get_ylim()[1] - Plot.get_ylim()[0])/100 for i,refl in enumerate(Phases[ph]['RefList'][found_indices]): key = f"{refl[5+Super]:.7g}" data[0]['HKLmarkers'][ph][key] = data[0][ 'HKLmarkers'][ph].get(key,[None,[]]) if Page.plotStyle['sqrtPlot']: data[0]['HKLmarkers'][ph][key][0] = np.sign(ytick)*ytick**2 else: data[0]['HKLmarkers'][ph][key][0] = ytick # add reflection if not already present for hkl in data[0]['HKLmarkers'][ph][key][1]: if sum(np.abs(refl[:n] - hkl)) == 0: break else: data[0]['HKLmarkers'][ph][key][1].append(tuple(np.rint(refl[:n]).astype(int))) # offset next tick label position ytick -= (1+angle)*3*(font/8.)*(Plot.get_ylim()[1] - Plot.get_ylim()[0])/100 wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) return ################################################################# pick = str(G2frame.itemPicked).split('(',1)[1][:-1] if pick not in Page.phaseList: # picked something other than a tickmark return Page.pickTicknum = Page.phaseList.index(pick) resetlist = [] for pId,phase in enumerate(Page.phaseList): # set the tickmarks to a lighter color col = Page.tickDict[phase].get_color() rgb = mpcls.ColorConverter().to_rgb(col) rgb_light = [(2 + i)/3. for i in rgb] resetlist.append((Page.tickDict[phase],rgb)) Page.tickDict[phase].set_color(rgb_light) Page.tickDict[phase].set_zorder(99) # put on top SavePlot4animate() for f,v in resetlist: # reset colors back f.set_zorder(0) f.set_color(v) # reset colors back to original values G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragTickmarks) elif G2frame.PickId and G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Background': # selected a fixed background point. Can move it or delete it. backPts = G2frame.dataWindow.wxID_BackPts for mode in backPts: # what menu is selected? if G2frame.dataWindow.BackMenu.FindItemById(backPts[mode]).IsChecked(): break # mode will be 'Add' or 'Move' or 'Delete' if pick.get_marker() == 'D': # find the closest point backDict = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Background'))[1] mag2th = [0]+[x for x,m in magLineList][1:] magmult = [m for x,m in magLineList] if magmult: xy[1] /= magmult[np.searchsorted(mag2th, xy[0], side = 'right')-1] d2 = [(x-xy[0])**2+(y-xy[1])**2 for x,y in backDict['FixedPoints']] G2frame.fixPtMarker = d2.index(min(d2)) if mode == 'Move': # animate move of FixedBkg marker G2frame.itemPicked = pick pick.set_marker('|') # change the point appearance Page = G2frame.G2plotNB.nb.GetPage(plotNum) SavePlot4animate() G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragMarker) pick.set_marker('D') # put it back elif mode == 'Delete': del backDict['FixedPoints'][G2frame.fixPtMarker] wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) return elif ind.all() == [0]: # picked a line if pick not in G2frame.Lines[0:2]: # select limits only return # prepare to animate move of line G2frame.itemPicked = pick pick.set_linestyle(':') # set line as dotted Page = G2frame.G2plotNB.nb.GetPage(plotNum) SavePlot4animate() G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragLine) pick.set_linestyle('--') # back to dashed def OnRelease(event): '''This is called when the mouse button is released when a plot object is dragged due to an item pick, or when invoked via a menu item (such as in onMoveDiffCurve), or for background points, which may be added/moved/deleted here. New peaks are also added here. ''' plotNum = G2frame.G2plotNB.plotList.index('Powder Patterns') Page = G2frame.G2plotNB.nb.GetPage(plotNum) if G2frame.ifSetLimitsMode == 3: # add an excluded region G2frame.ifSetLimitsMode = 0 Page.excludeMode = False G2frame.CancelSetLimitsMode.Enable(False) if savedPlot.get('excludedPosition') is None: print('Excluded reg. set err: somehow we did not set a starting position') return x1 = savedPlot.get('excludedPosition') x2 = event.xdata LimitId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Limits') limData = G2frame.GPXtree.GetItemPyData(LimitId) x1 = max(limData[0][0],min(x1,limData[0][1])) x2 = max(limData[0][0],min(x2,limData[0][1])) excl = [min(x1,x2),max(x1,x2)] limData.append(excl) wx.CallAfter(G2pdG.UpdateLimitsGrid,G2frame,limData,plottype) wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) return if G2frame.cid is not None: # if there is a drag connection, delete it Page.canvas.mpl_disconnect(G2frame.cid) G2frame.cid = None UnsavePlot4animate() if event.xdata is None or event.ydata is None: # ignore drag if cursor is outside of plot # if GSASIIpath.GetConfigValue('debug'): print('Ignoring drag, invalid pos:',event.xdata,event.ydata) # wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) return if not G2frame.PickId: if GSASIIpath.GetConfigValue('debug'): print('Ignoring drag, G2frame.PickId is not set') return if G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Background' and event.xdata: # On Background page, deal with fixed background points or move of limits if Page.toolbar.AnyActive(): # prevent ops. if a toolbar zoom button pressed # after any mouse release event (could be a zoom), redraw magnification lines if magLineList: wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) return if G2frame.SubBack or G2frame.Weight or G2frame.Contour or not G2frame.SinglePlot: return # none of these should be True backDict = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Background'))[1] mag2th = [0]+[x for x,m in magLineList][1:] magmult = [m for x,m in magLineList] if 'FixedPoints' not in backDict: backDict['FixedPoints'] = [] try: Parms,Parms2 = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters')) except TypeError: return # unit conversions xy = [event.xdata,event.ydata] try: if Page.plotStyle['qPlot']: #qplot - convert back to 2-theta xy[0] = G2lat.Dsp2pos(Parms,2*np.pi/xy[0]) elif Page.plotStyle['dPlot']: #dplot - convert back to 2-theta xy[0] = G2lat.Dsp2pos(Parms,xy[0]) except: return if Page.plotStyle['sqrtPlot']: xy[1] = xy[1]**2 elif magmult: mult = magmult[np.searchsorted(mag2th, xy[0], side = 'right')-1] xy[1] /= mult # what is mode for Fixed bkg points? backPts = G2frame.dataWindow.wxID_BackPts for mode in backPts: # what menu is selected? if G2frame.dataWindow.BackMenu.FindItemById(backPts[mode]).IsChecked(): break if mode == 'Add': backDict['FixedPoints'].append(xy) if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() axis.plot(event.xdata,event.ydata,'rD',clip_on=Clip_on,picker=3.) Page.canvas.draw() return if G2frame.itemPicked is None: return if G2frame.itemPicked.get_marker() == 'D': # action on fixed background point # deal with dragging of fixed background points backDict['FixedPoints'][G2frame.fixPtMarker] = xy G2frame.itemPicked = None wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) return elif G2frame.itemPicked in G2frame.Lines[0:2]: # limit are 1st two lines in list # here deal with dragging of limits lineNo = G2frame.Lines.index(G2frame.itemPicked) # 0 (lower limit) or 1 (upper limit) LimitId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Limits') limits = G2frame.GPXtree.GetItemPyData(LimitId) #Id = lineNo//2+1 # 1 for limits (>1 for excluded regions) id2 = lineNo%2 # 0 (lower limit) or 1 (upper limit) limits[1][id2] = xy[0] #print(f'set limit {id2} to {xy[0]}') limits[1][0] = min(max(limits[0][0],limits[1][0]),limits[1][1]) limits[1][1] = max(min(limits[0][1],limits[1][1]),limits[1][0]) G2frame.itemPicked = None wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) return else: # unexpected oblect was picked G2frame.itemPicked = None return if G2frame.itemPicked is None: # after any mouse release event (could be a zoom) where nothing is selected, # redraw magnification lines if magLineList: wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) return if DifLine[0] is G2frame.itemPicked: # respond to dragging of the difference curve data = G2frame.GPXtree.GetItemPyData(G2frame.PickId) ypos = event.ydata Page.plotStyle['delOffset'] = float(-ypos) G2frame.itemPicked = None wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) return elif G2frame.itemPicked in G2frame.MagLines: # drag of magnification marker xpos = event.xdata try: if Page.plotStyle['qPlot']: #qplot - convert back to 2-theta xpos = G2lat.Dsp2pos(Parms,2*np.pi/xpos) elif Page.plotStyle['dPlot']: #dplot - convert back to 2-theta xpos = G2lat.Dsp2pos(Parms,xpos) except: return magIndex = G2frame.MagLines.index(G2frame.itemPicked) data = G2frame.GPXtree.GetItemPyData(G2frame.PickId) data[0]['Magnification'][magIndex][0] = xpos wx.CallAfter(G2gd.UpdatePWHKPlot,G2frame,plottype,G2frame.PatternId) return Parms,Parms2 = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters')) xpos = event.xdata phNum = None try: Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId,'Reflection Lists') pickPhase = str(G2frame.itemPicked).split('(',1)[1][:-1] phNum = Page.phaseList.index(pickPhase) except: pass if G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Peak List' and xpos and phNum is not None: # dragging tickmarks if phNum: Page.plotStyle['refDelt'] = -(event.ydata-Page.plotStyle['refOffset'])/phNum else: #1st row of refl ticks Page.plotStyle['refOffset'] = event.ydata elif G2frame.GPXtree.GetItemText(G2frame.PickId) in ['Peak List','Limits','Unit Cells List'] and xpos: lines = [] for line in G2frame.Lines: lines.append(line.get_xdata()[0]) try: lineNo = lines.index(G2frame.itemPicked.get_xdata()[0]) except ValueError: lineNo = -1 nxcl = len(exclLines) if lineNo in [0,1] or lineNo in exclLines: LimitId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Limits') limits = G2frame.GPXtree.GetItemPyData(LimitId) Id = lineNo//2+1 id2 = lineNo%2 if Page.plotStyle['qPlot'] and 'PWDR' in plottype: limits[Id][id2] = G2lat.Dsp2pos(Parms,2.*np.pi/xpos) elif Page.plotStyle['dPlot'] and 'PWDR' in plottype: limits[Id][id2] = G2lat.Dsp2pos(Parms,xpos) else: limits[Id][id2] = xpos if Id > 1 and limits[Id][0] > limits[Id][1]: limits[Id].reverse() limits[1][0] = min(max(limits[0][0],limits[1][0]),limits[1][1]) limits[1][1] = max(min(limits[0][1],limits[1][1]),limits[1][0]) if G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Limits': G2pdG.UpdateLimitsGrid(G2frame,limits,plottype) elif lineNo > 1+nxcl: PeakId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Peak List') peaks = G2frame.GPXtree.GetItemPyData(PeakId) inXtraPeakMode = False # Ignore if peak list menubar is not yet created try: inXtraPeakMode = G2frame.dataWindow.XtraPeakMode.IsChecked() except: pass if inXtraPeakMode: tbl = peaks['xtraPeaks'] else: tbl = peaks['peaks'] if event.button == 3: del tbl[lineNo-2-nxcl] else: if Page.plotStyle['qPlot']: tbl[lineNo-2-nxcl][0] = G2lat.Dsp2pos(Parms,2.*np.pi/xpos) elif Page.plotStyle['dPlot']: tbl[lineNo-2-nxcl][0] = G2lat.Dsp2pos(Parms,xpos) else: tbl[lineNo-2-nxcl][0] = xpos peaks['sigDict'] = {} #no longer valid G2pdG.UpdatePeakGrid(G2frame,peaks) elif G2frame.GPXtree.GetItemText(G2frame.PickId) in ['Models',] and xpos: lines = [] for line in G2frame.Lines: lines.append(line.get_xdata()[0]) try: lineNo = lines.index(G2frame.itemPicked.get_xdata()[0]) except ValueError: lineNo = -1 if lineNo in [0,1]: LimitId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Limits') data = G2frame.GPXtree.GetItemPyData(LimitId) data[1][lineNo] = xpos data[1][0] = min(max(data[0][0],data[1][0]),data[1][1]) data[1][1] = max(min(data[0][1],data[1][1]),data[1][0]) elif (G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Reflection Lists' or 'PWDR' in G2frame.GPXtree.GetItemText(G2frame.PickId)) and xpos: Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId,'Reflection Lists') if Id: pick = str(G2frame.itemPicked).split('(',1)[1][:-1] if 'line' not in pick: #avoid data points, etc. if pick in Page.phaseList: num = Page.phaseList.index(pick) if num: Page.plotStyle['refDelt'] = -(event.ydata-Page.plotStyle['refOffset'])/num else: #1st row of refl ticks Page.plotStyle['refOffset'] = event.ydata elif GSASIIpath.GetConfigValue('debug'): # should not happen! print('How did we get here?') GSASIIpath.IPyBreak() wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) G2frame.itemPicked = None def onSetPlotLim(event): '''Specify plot limits manually ''' def onChecked(event): try: i = cbox.index(event.EventObject) showChecked(i) except: pass def showChecked(i): checked = cbox[i].GetValue() if not checked: # fake out validation to avoid ugly yellow dbox[i].invalid = False dbox[i]._IndicateValidity() else: # reset validation dbox[i].ChangeValue(dbox[i].GetValue()) dbox[i].Enable(checked) def applyLims(event): Page.toolbar.push_current() # Page.toolbar.set_history_buttons() # this may be needed to update the zoom buttons (needs test) # Page.canvas.draw_idle() # schedule an MPL update (needs test) CurLims = {} CurLims['xlims'] = list(Plot.get_xlim()) if G2frame.Weight: CurLims['ylims'] = list(Page.figure.axes[1].get_ylim()) CurLims['dylims'] = list(Page.figure.axes[2].get_ylim()) elif G2frame.Contour: CurLims['ylims'] = list(Plot.get_ylim()) CurLims['cylims'] = list(Page.Img.get_clim()) else: CurLims['ylims'] = list(Plot.get_ylim()) CurLims['dylims'] = [0,0] for var in 'xlims','ylims','dylims','cylims': for i in range(2): if not G2frame.UseLimits[var][i]: continue try: CurLims[var][i] = float(G2frame.FixedLimits[var][i]) CurLims[var][i] = float(G2frame.FixedLimits[var][i]) except: pass Plot.set_xlim(CurLims['xlims']) if G2frame.Weight: Page.figure.axes[1].set_ylim(CurLims['ylims']) Page.figure.axes[2].set_ylim(CurLims['dylims']) elif G2frame.Contour: Plot.set_ylim(CurLims['ylims']) Page.Img.set_clim(CurLims['cylims']) else: Plot.set_ylim(CurLims['ylims']) Page.toolbar.push_current() # Page.toolbar.set_history_buttons() # this may be needed to update the zoom buttons (needs test) # Page.canvas.draw_idle() # schedule an MPL update (needs test) Plot.figure.canvas.draw() # onSetPlotLim starts here dlg = wx.Dialog(G2frame.plotFrame, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) vbox = wx.BoxSizer(wx.VERTICAL) vbox.Add(wx.StaticText(dlg,wx.ID_ANY, 'Set Plot limits' ),0,wx.ALL) gsizer = wx.FlexGridSizer(cols=5,hgap=2,vgap=2) gsizer.Add(wx.StaticText(dlg,wx.ID_ANY,' '),0,wx.ALL) gsizer.Add(wx.StaticText(dlg,wx.ID_ANY,'use'),0,wx.ALL) gsizer.Add(wx.StaticText(dlg,wx.ID_ANY,' min'),0,wx.ALL) gsizer.Add(wx.StaticText(dlg,wx.ID_ANY,'use'),0,wx.ALL) gsizer.Add(wx.StaticText(dlg,wx.ID_ANY,' max'),0,wx.ALL) cbox = [] dbox = [] lblkeys = [(' x-axis ','xlims'),(' y-axis ','ylims')] if G2frame.Weight: lblkeys += [('(obs-calc)/sig ','dylims')] elif G2frame.Contour: lblkeys += [('contour','cylims')] for lbl,key in lblkeys: gsizer.Add(wx.StaticText(dlg,wx.ID_ANY,lbl),0,wx.ALL) for i in range(2): cbox.append(G2G.G2CheckBox(dlg,'',G2frame.UseLimits[key],i, OnChange=onChecked)) dbox.append(G2G.ValidatedTxtCtrl(dlg,G2frame.FixedLimits[key],i, typeHint=float)) gsizer.Add(cbox[-1]) gsizer.Add(dbox[-1]) showChecked(-1) vbox.Add(gsizer) vbox.Add((10,10),1,wx.ALL|wx.EXPAND,1) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add((-1,-1),1,wx.ALL|wx.EXPAND,1) #btn = wx.Button(dlg, wx.ID_CLOSE) #btn.Bind(wx.EVT_BUTTON,lambda event:dlg.EndModal(wx.ID_CANCEL)) #hbox.Add(btn) btn = wx.Button(dlg, wx.ID_ANY, label='Apply') btn.Bind(wx.EVT_BUTTON,applyLims) hbox.Add(btn) OKbtn = wx.Button(dlg, wx.ID_OK) OKbtn.Bind(wx.EVT_BUTTON,lambda event:dlg.EndModal(wx.ID_OK)) hbox.Add(OKbtn) hbox.Add((-1,-1),1,wx.ALL|wx.EXPAND,1) vbox.Add(hbox,1,wx.ALL|wx.EXPAND,1) dlg.SetSizer(vbox) vbox.Fit(dlg) dlg.ShowModal() dlg.Destroy() applyLims(None) # apply limits def onPlotFormat(event): '''Change the appearance of the current plot''' changePlotSettings(G2frame,Plot) def refPlotUpdate(Histograms,cycle=None,restore=False): '''called to update an existing plot during a Rietveld fit; it only updates the curves, not the reflection marks or the legend. It should be called with restore=True to reset plotting parameters after the refinement is done. ''' if restore: (G2frame.SinglePlot,G2frame.Contour,G2frame.Weight, G2frame.plusPlot,G2frame.SubBack,Page.plotStyle['logPlot']) = savedSettings return if plottingItem not in Histograms: histoList = [i for i in Histograms.keys() if i.startswith('PWDR ')] if len(histoList) == 0: print('Skipping plot update, no PWDR items!') return plotItem = histoList[0] else: plotItem = plottingItem xye = np.array(ma.getdata(Histograms[plotItem]['Data'])) # strips mask xye0 = Histograms[plotItem]['Data'][0] limits = Histograms[plotItem]['Limits'] if Page.plotStyle['qPlot']: X = 2.*np.pi/G2lat.Pos2dsp(Parms,xye0) # might want to consider caching this Ibeg = np.searchsorted(X,2.*np.pi/G2lat.Pos2dsp(Parms,limits[1][0])) Ifin = np.searchsorted(X,2.*np.pi/G2lat.Pos2dsp(Parms,limits[1][1])) elif Page.plotStyle['dPlot']: X = G2lat.Pos2dsp(Parms,xye0) # might want to consider caching this Ibeg = np.searchsorted(X,G2lat.Pos2dsp(Parms,limits[1][1])) Ifin = np.searchsorted(X,G2lat.Pos2dsp(Parms,limits[1][0])) else: X = copy.deepcopy(xye0) Ibeg = np.searchsorted(X,limits[1][0]) Ifin = np.searchsorted(X,limits[1][1]) if Ibeg == Ifin: # if no points are within limits bad things happen Ibeg,Ifin = 0,None elif Ibeg > Ifin: Ifin, Ibeg = Ibeg, Ifin # TOF with order reversed if Page.plotStyle['sqrtPlot']: olderr = np.seterr(invalid='ignore') #get around sqrt(-ve) error Y = np.where(xye[1]>=0.,np.sqrt(xye[1]),-np.sqrt(-xye[1])) Z = np.where(xye[3]>=0.,np.sqrt(xye[3]),-np.sqrt(-xye[3])) W = np.where(xye[4]>=0.,np.sqrt(xye[4]),-np.sqrt(-xye[4])) #D = np.where(xye[5],(Y-Z),0.)-Page.plotStyle['delOffset'] np.seterr(invalid=olderr['invalid']) else: Y = copy.copy(xye[1]) Z = copy.copy(xye[3]) W = copy.copy(xye[4]) #D = xye[5]-Page.plotStyle['delOffset'] #powder background DZ = (xye[1]-xye[3])*np.sqrt(xye[2]) DifLine[0].set_xdata(X[Ibeg:Ifin]) DifLine[0].set_ydata(DZ[Ibeg:Ifin]) try: lims = [min(DZ[Ibeg:Ifin]),max(DZ[Ibeg:Ifin])] if all(np.isfinite(lims)): Plot1.set_ylim(lims) except: pass CalcLine[0].set_xdata(X) ObsLine[0].set_xdata(X) BackLine[0].set_xdata(X) CalcLine[0].set_ydata(Z) ObsLine[0].set_ydata(Y) BackLine[0].set_ydata(W) if cycle: Title = '{} cycle #{}'.format(plotItem,cycle) else: Title = plotItem if Page.plotStyle.get('title',True): if Page.plotStyle['sqrtPlot']: Plot.set_title(r'$\sqrt{I}$ for '+Title) else: Plot.set_title(Title) Page.canvas.draw() wx.GetApp().Yield() def incCptn(string): '''Adds a underscore to "hide" a MPL object from the legend if obsInCaption is False ''' if plotOpt['obsInCaption']: return string else: return '_'+string def Replot(*args): PlotPatterns(G2frame,plotType=plottype,extraKeys=extraKeys) def onHKLabelConfig(event): '''Configure all HKL markers in all phases''' def onDelAll(event): '''Delete all HKL markers in all phases''' data[0]['HKLmarkers'] = {} dlg.EndModal(wx.ID_OK) Replot() data = G2frame.GPXtree.GetItemPyData(G2frame.PatternId) data[0]['HKLconfig'] = data[0].get('HKLconfig',{}) dlg = wx.Dialog(G2frame,style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(wx.StaticText(dlg,label='Configure Reflection (hkl) Labels'), 0,wx.ALIGN_CENTER_HORIZONTAL|wx.RIGHT|wx.LEFT|wx.BOTTOM,15) hsizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2) sizer.Add(hsizer) hsizer.Add(wx.StaticText(dlg,label='Label Orientation: ')) data[0]['HKLconfig']['Orientation'] = data[0][ 'HKLconfig'].get('Orientation',0) choice = G2G.G2ChoiceButton(dlg, ['Horizontal','Vertical'], data[0]['HKLconfig'],'Orientation', onChoice=Replot) hsizer.Add(choice) # hsizer.Add(wx.StaticText(dlg,label='Font size: ')) data[0]['HKLconfig']['Font'] = data[0][ 'HKLconfig'].get('Font','8') choice = G2G.G2ChoiceButton(dlg, ['6','8','10','12','14','16'], None,None, data[0]['HKLconfig'],'Font', onChoice=Replot) hsizer.Add(choice) # hsizer.Add(wx.StaticText(dlg,label='Box transparency: ')) data[0]['HKLconfig']['alpha'] = data[0][ 'HKLconfig'].get('alpha',2) choice = G2G.G2ChoiceButton(dlg, ['0','25%','50%','75%','100%'], data[0]['HKLconfig'],'alpha', onChoice=Replot) hsizer.Add(choice) # btn = wx.Button(dlg, wx.ID_ANY,'Delete all labels') btn.Bind(wx.EVT_BUTTON, onDelAll) sizer.Add((-1,10)) sizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5) btnsizer = wx.StdDialogButtonSizer() btn = wx.Button(dlg, wx.ID_OK) btn.SetDefault() btn.Bind(wx.EVT_BUTTON, lambda x: dlg.EndModal(wx.ID_OK)) btnsizer.AddButton(btn) btnsizer.Realize() sizer.Add(btnsizer, 0, wx.EXPAND|wx.ALL, 5) dlg.SetSizer(sizer) sizer.Fit(dlg) dlg.CenterOnParent() dlg.ShowModal() return def onPartialConfig(event): '''respond to Phase partials config menu command. Computes partials if needed and then displays a window with options for each phase. Plot is refreshed each time a setting is changed. ''' Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls')) phPartialFile = Controls['PhasePartials'] if phPartialFile is None or not os.path.exists(phPartialFile): dlg = wx.MessageDialog(G2frame, 'Phase partials (intensity profiles for each individual phase) have not been computed. Do you want to compute them?', 'Confirm compute',wx.YES_NO | wx.ICON_QUESTION) try: result = dlg.ShowModal() finally: dlg.Destroy() if result != wx.ID_YES: return G2frame.OnRefinePartials(None) if not Page.plotStyle['partials']: Page.plotStyle['partials'] = True Replot() configPartialDisplay(G2frame,Page.phaseColors,Replot) def SavePlot4animate(): '''Save the region inside the axes box for animation, except on Mac (at least with wx 4.2.3), where blitting seems broken unless the entire window is used ''' Page.canvas.draw() # refresh the plot to current state if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() if sys.platform == "darwin": savedPlot['box'] = Page.figure.bbox else: savedPlot['box'] = axis.bbox # save bitmap savedPlot['map'] = Page.canvas.copy_from_bbox(savedPlot['box']) def RestorePlot4animate(): '''reset the plot to the saved version (quick) ''' if savedPlot.get('map') is None: pass else: Page.canvas.restore_region(savedPlot['map']) def BlitPlot(): '''quickly update the plot (blit) to animate movement os an artist ''' Page.canvas.blit(savedPlot['box']) def UnsavePlot4animate(): '''Done with animation, release the memory used to save a bitmap of the plot ''' savedPlot['box'] = savedPlot['map'] = None #### beginning PlotPatterns execution ##################################### global exclLines,Page global DifLine global Ymax global Pattern,mcolors,Plot,Page,imgAx,Temps global savedX global savedPlot savedPlot = {} UnsavePlot4animate() plottype = plotType inXtraPeakMode = False # Ignore if peak list menubar is not yet created try: inXtraPeakMode = G2frame.dataWindow.XtraPeakMode.IsChecked() except: pass # get powder pattern colors from config settings pwdrCol = {} for i in 'Obs_color','Calc_color','Diff_color','Bkg_color': pwdrCol[i] = '#' + GSASIIpath.GetConfigValue(i,getDefault=True) if not G2frame.PatternId: return if 'PKS' in plottype: # This is probably not used anymore; PlotPowderLines seems to be called directly G2plt.PlotPowderLines(G2frame) return if data is None: data = G2frame.GPXtree.GetItemPyData(G2frame.PatternId) if G2frame.PickId and plottype not in ['SASD','REFD'] and 'PWDR' in G2frame.GPXtree.GetItemText(G2frame.PickId): publish = PublishPlot else: publish = None new,plotNum,Page,Plot,limits = G2frame.G2plotNB.FindPlotTab('Powder Patterns','mpl',publish=publish) try: Selection = G2frame.GPXtree.GetItemText(G2frame.GPXtree.GetSelection()) except: Selection = None if G2frame.ifSetLimitsMode and Selection == 'Limits': # note mode if G2frame.ifSetLimitsMode == 1: msg = 'Click on a point to define the location of the lower limit' elif G2frame.ifSetLimitsMode == 2: msg = 'Click on a point to define the location of the upper limit' elif G2frame.ifSetLimitsMode == 3: msg = 'Click on location to start excluded region,\nthen drag and release at end of region' Page.figure.text(.02,.93, msg, fontsize=14, fontweight='bold') elif Selection == 'Background': backPts = G2frame.dataWindow.wxID_BackPts for mode in backPts: # what menu is selected? if G2frame.dataWindow.BackMenu.FindItemById(backPts[mode]).IsChecked(): break else: mode = '?' Page.figure.text(.02,.93, f'Fixed bkg mode: {mode}', fontsize=12, fontweight='bold') Page.excludeMode = False # True when defining an excluded region #patch if 'Offset' not in Page.plotStyle and plotType in ['PWDR','SASD','REFD']: #plot offset data Ymax = max(data[1][1]) Page.plotStyle.update({'Offset':[0.0,0.0],'delOffset':float(0.02*Ymax), 'refOffset':float(-0.1*Ymax),'refDelt':float(0.1*Ymax),}) #end patch if 'Normalize' not in Page.plotStyle: Page.plotStyle['Normalize'] = False # reset plot when changing between different data types try: G2frame.lastPlotType except: G2frame.lastPlotType = None if plotType == 'PWDR': try: Parms,Parms2 = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, G2frame.PatternId, 'Instrument Parameters')) if G2frame.lastPlotType != Parms['Type'][1]: if GSASIIpath.GetConfigValue('debug'): print('triggering newplot from G2frame.lastPlotType') Ymax = max(data[1][1]) if Page.plotStyle['sqrtPlot']: Page.plotStyle['delOffset'] = float(.02*np.sqrt(Ymax)) Page.plotStyle['refOffset'] = float(-0.1*np.sqrt(Ymax)) Page.plotStyle['refDelt'] = float(.1*np.sqrt(Ymax)) else: Page.plotStyle['delOffset'] = float(.02*Ymax) Page.plotStyle['refOffset'] = float(-0.1*Ymax) Page.plotStyle['refDelt'] = float(.1*Ymax) newPlot = True G2frame.lastPlotType = Parms['Type'][1] except TypeError: #bad id from GetGPXtreeItemId - skip pass try: G2frame.FixedLimits except: G2frame.FixedLimits = {i:['',''] for i in ('xlims','ylims','dylims','cylims')} try: G2frame.UseLimits except: G2frame.UseLimits = {i:[False,False] for i in ('xlims','ylims','dylims','cylims')} #===================================================================================== # code to setup for plotting Rietveld results. Turns off multiplot (contour/waterfall), # sqrtplot, turn on + and weight plot, but sqrtPlot qPlot and dPlot are not changed. # Magnification regions are ignored. # the last-plotted histogram (from G2frame.PatternId) is used for this plotting # (except in seq. fitting) # Returns a pointer to refPlotUpdate, which is used to update the plot when this # returns if refineMode: plottingItem = G2frame.GPXtree.GetItemText(G2frame.PatternId) # save settings to be restored after refinement with repPlotUpdate({},restore=True) savedSettings = (G2frame.SinglePlot,G2frame.Contour,G2frame.Weight, G2frame.plusPlot,G2frame.SubBack,Page.plotStyle['logPlot']) G2frame.SinglePlot = True G2frame.Contour = False G2frame.Weight = True G2frame.plusPlot = 1 G2frame.SubBack = False Page.plotStyle['logPlot'] = False # is the selected histogram in the refinement? if not pick the 1st to show # instead select the first powder pattern and plot it Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree() if plottingItem not in Histograms: # current plotted item is not in refinement histoList = [i for i in Histograms.keys() if i.startswith('PWDR ')] if len(histoList) != 0: plottingItem = histoList[0] G2frame.PatternId = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, plottingItem) data = G2frame.GPXtree.GetItemPyData(G2frame.PatternId) G2frame.GPXtree.SelectItem(G2frame.PatternId) PlotPatterns(G2frame,True,plotType,None,extraKeys) #===================================================================================== elif 'PlotDefaults' in data[0] and fromTree: # set style from defaults saved with '!' #print('setting plot style defaults') (xlim, ylim, styleDict, G2frame.SinglePlot, G2frame.Contour, G2frame.Weight, G2frame.plusPlot, G2frame.SubBack) = data[0]['PlotDefaults'] Page.plotStyle = copy.copy(styleDict) newPlot = True # prevent carrying limits over (may not be needed here) #===================================================================================== if not new: # plotting in previously created axes G2frame.xylim = copy.copy(limits) else: # 1st time plot is created if plottype in ['SASD','REFD']: Page.plotStyle['logPlot'] = True G2frame.ErrorBars = True newPlot = True G2frame.Cmin = 0.0 G2frame.Cmax = 1.0 Page.canvas.mpl_connect('motion_notify_event', OnMotion) Page.canvas.mpl_connect('pick_event', OnPickPwd) Page.canvas.mpl_connect('button_release_event', OnRelease) Page.canvas.mpl_connect('button_press_event',OnPress) Page.bindings = [] # redo OnPlotKeyPress binding each time the Plot is updated # since needs values that may have been changed after 1st call try: for b in Page.bindings: Page.canvas.mpl_disconnect(b) except AttributeError: pass Page.bindings = [] Page.bindings.append(Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)) if not G2frame.PickId: print('No plot, G2frame.PickId,G2frame.PatternId=',G2frame.PickId,G2frame.PatternId) return elif 'PWDR' in G2frame.GPXtree.GetItemText(G2frame.PickId): Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree() Phases = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId,'Reflection Lists')) Page.phaseList = sorted(Phases.keys()) # define an order for phases (once!) G2frame.Bind(wx.EVT_MENU, onMoveDiffCurve, id=G2frame.dataWindow.moveDiffCurve.GetId()) G2frame.Bind(wx.EVT_MENU, onMoveTopTick, id=G2frame.dataWindow.moveTickLoc.GetId()) G2frame.Bind(wx.EVT_MENU, onMoveTickSpace, id=G2frame.dataWindow.moveTickSpc.GetId()) G2frame.Bind(wx.EVT_MENU, onSetPlotLim, id=G2frame.dataWindow.setPlotLim.GetId()) G2frame.Bind(wx.EVT_MENU, onPlotFormat, id=G2frame.dataWindow.setPlotFmt.GetId()) G2frame.Bind(wx.EVT_MENU, onHKLabelConfig, id=G2G.wxID_CHHKLLBLS) if len(G2frame.Refine): # extend state for new menus to match main state = G2frame.Refine[0].IsEnabled() else: state = False G2frame.Bind(wx.EVT_MENU, onPartialConfig, id=G2G.wxID_CHPHPARTIAL) G2frame.PartialConfig.Enable(state) G2frame.Bind(wx.EVT_MENU, G2frame.OnSavePartials, id=G2G.wxID_PHPARTIALCSV) G2frame.PartialCSV.Enable(state) # disabled during sequential fits G2frame.dataWindow.moveDiffCurve.Enable(False) G2frame.dataWindow.moveTickLoc.Enable(False) G2frame.dataWindow.moveTickSpc.Enable(False) elif plottype in ['SASD','REFD']: Page.phaseList = Phases = [] elif G2frame.GPXtree.GetItemText(G2frame.PickId) in ['Reflection Lists','Limits']: # get reflection positions for other places where they are displayed Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree() Phases = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId,'Reflection Lists')) Page.phaseList = sorted(Phases.keys()) # define an order for phases (once!) elif G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Peak List': G2frame.Bind(wx.EVT_MENU, onMovePeak, id=G2frame.dataWindow.movePeak.GetId()) Page.phaseList = Phases = [] if inXtraPeakMode: Phases = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId,'Reflection Lists')) Page.phaseList = sorted(Phases.keys()) # define an order for phases (once!) else: Page.phaseList = Phases = [] # assemble a list of validated colors for tickmarks valid_colors = [] invalid_colors = [] for color in GSASIIpath.GetConfigValue('Ref_Colors',getDefault=True).split(): try: if mpl.colors.is_color_like(color): valid_colors.append(color) else: invalid_colors.append(color) except: pass if invalid_colors and not hasattr(Page,'phaseColors'): # show error once print(f'**** bad color code(s): "{", ".join(invalid_colors)}" - redo Preferences/Ref_Colors ****') if len(valid_colors) < 3: refColors=['b','r','c','g','m','k'] else: refColors = valid_colors if not hasattr(Page,'phaseColors'): Page.phaseColors = {} for i,p in enumerate(Phases): Page.phaseColors[p] = Page.phaseColors.get(p,refColors[i%len(refColors)]) # save information needed to reload from tree and redraw try: if not refineMode: kwargs={'PatternName':G2frame.GPXtree.GetItemText(G2frame.PatternId)} if G2frame.PickId: kwargs['PickName'] = G2frame.GPXtree.GetItemText(G2frame.PickId) wx.CallAfter(G2frame.G2plotNB.RegisterRedrawRoutine(G2frame.G2plotNB.lastRaisedPlotTab,ReplotPattern, (G2frame,newPlot,plotType),kwargs)) except: #skip a C++ error pass # now start plotting G2frame.G2plotNB.status.DestroyChildren() #get rid of special stuff on status bar G2frame.G2plotNB.status.SetStatusText(IndxFrom,1) # TODO: figure out why the SetHelpButton creates a second tab line (BHT, Mac, wx4.1) #G2frame.G2plotNB.SetHelpButton(G2frame.dataWindow.helpKey) Page.tickDict = {} DifLine = [''] ifLimits = False if G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Limits': ifLimits = True Page.plotStyle['qPlot'] = False Page.plotStyle['dPlot'] = False # Setup list of keypress keys in use for the current plot type # a,b,c,d,e,f,g,i,l,m,n,o,p,q,r,s,t,u,w,x, (unused: j, k, y, z) # also: +,/, C,D,S,U if G2frame.Contour: Page.Choice = (' key press','b: toggle subtract background', 'd: lower contour max','u: raise contour max', 'D: lower contour min','U: raise contour min', 'o: reset contour limits','g: toggle grid', 'i: interpolation method','S: color scheme','c: contour off', 'e: toggle temperature for y-axis','s: toggle sqrt plot', 'w: toggle w(Yo-Yc) contour plot','h: toggle channel # plot', 'q: toggle Q plot','t: toggle d-spacing plot', 'C: contour plot control window', ) else: if 'PWDR' in plottype: Page.Choice = [' key press', 'a: add magnification region','b: toggle subtract background', 'c: contour on','x: toggle excluded regions','T: toggle plot title', 'f: toggle full-length ticks','g: toggle grid', 'X: toggle cumulative chi^2', 'm: toggle multidata plot','n: toggle log(I)',] if plotOpt['obsInCaption']: addrem = 'remove' else: addrem = 'add' if G2frame.SinglePlot: what = "obs, calc,..." else: what = "histogram names" Page.Choice += [f'L: {addrem} {what} in legend',] if ifLimits: Page.Choice += ['e: create excluded region', 's: toggle sqrt plot','w: toggle (Io-Ic)/sig plot', '+: toggle obs line plot'] else: Page.Choice += [ 'q: toggle Q plot','t: toggle d-spacing plot', 's: toggle sqrt plot','w: toggle (Io-Ic)/sig plot', '+: toggle obs line plot'] if G2frame.GPXtree.GetItemText(G2frame.PickId) in ['Peak List','Index Peak List']: Page.Choice += ['d: highlight next peak in list'] Page.Choice += ['u: highlight previous peak in list'] if G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Background': Page.Choice += ['k: toggle fixed-background mode (add/move/delete)'] if Page.plotStyle['sqrtPlot'] or Page.plotStyle['logPlot']: del Page.Choice[1] del Page.Choice[1] elif not G2frame.SinglePlot: del Page.Choice[1] if not G2frame.SinglePlot: Page.Choice = Page.Choice+ \ ['u/U: offset up/10x','d/D: offset down/10x','l: offset left','r: offset right', 'o: reset offset','F: select data','/: normalize'] else: Page.Choice = Page.Choice+ ['p: toggle partials (if available)',] if G2frame.SinglePlot: Page.Choice += ['v: CSV output of plot'] Page.Choice += ['!: Save settings as default for histogram'] elif plottype in ['SASD','REFD']: Page.Choice = [' key press', 'b: toggle subtract background file','g: toggle grid', 'm: toggle multidata plot','n: toggle semilog/loglog', 'q: toggle S(q) plot','w: toggle (Io-Ic)/sig plot','+: toggle obs line plot',] if not G2frame.SinglePlot: Page.Choice = Page.Choice+ \ ['u: offset up','d: offset down','l: offset left', 'r: offset right','o: reset offset',] for KeyItem in extraKeys: Page.Choice = Page.Choice + [KeyItem[0] + ': '+KeyItem[2],] magLineList = [] # null value indicates no magnification Page.toolbar.updateActions = None # no update actions G2frame.cid = None Page.keyPress = OnPlotKeyPress # assemble a list of validated colors (not currently needed) # valid_colors = [] # invalid_colors = [] # try: # colors = GSASIIpath.GetConfigValue('Plot_Colors').split() # for color in colors: # #if color not in ['k','r','g','b','m','c']: # if mpl.colors.is_color_like(color): # valid_colors.append(color) # else: # invalid_colors.append(color) # except: # pass # if invalid_colors: # print(f'**** bad color code(s): "{", ".join(invalid_colors)}" - redo Preferences/Plot Colors ****') # if len(valid_colors) < 3: # colors = ['b','g','r','c','m','k'] # else: # colors = valid_colors Lines = [] exclLines = [] time0 = time.time() if G2frame.SinglePlot and G2frame.PatternId: try: Pattern = G2frame.GPXtree.GetItemPyData(G2frame.PatternId) Pattern.append(G2frame.GPXtree.GetItemText(G2frame.PatternId)) PlotList = [Pattern,] # PId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Background') # Pattern[0]['BackFile'] = ['',-1.0,False] # if PId: # Pattern[0]['BackFile'] = G2frame.GPXtree.GetItemPyData(PId)[1].get('background PWDR',['',-1.0,False]) Parms,Parms2 = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, G2frame.PatternId, 'Instrument Parameters')) Sample = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Sample Parameters')) Limits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Limits')) ParmList = [Parms,] SampleList = [Sample,] LimitsList = [Limits,] Title = Pattern[-1] if data[0].get('histTitle'): Title = data[0]['histTitle'] except AttributeError: pass else: #G2frame.selection Title = os.path.split(G2frame.GSASprojectfile)[1] if G2frame.selections is None: choices = G2gd.GetGPXtreeDataNames(G2frame,plotType) else: choices = G2frame.selections PlotList = [] ParmList = [] SampleList = [] LimitsList = [] Temps = [] # loop through tree looking for matching histograms to plot Id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root) while Id: name = G2frame.GPXtree.GetItemText(Id) pid = Id Id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie) if name not in choices: continue Pattern = G2frame.GPXtree.GetItemPyData(pid) if len(Pattern) < 3: # put name on end if needed Pattern.append(G2frame.GPXtree.GetItemText(pid)) if 'Offset' not in Page.plotStyle: #plot offset data Ymax = max(Pattern[1][1]) Page.plotStyle.update({'Offset':[0.0,0.0], 'delOffset':float(0.02*Ymax), 'refOffset':float(-0.1*Ymax), 'refDelt':float(0.1*Ymax),}) # PId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Background') # Pattern[0]['BackFile'] = ['',-1.0,False] # if PId: # Pattern[0]['BackFile'] = G2frame.GPXtree.GetItemPyData(PId)[1].get('background PWDR',['',-1.0,False]) PlotList.append(Pattern) ParmList.append(G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, pid,'Instrument Parameters'))[0]) SampleList.append(G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, pid, 'Sample Parameters'))) LimitsList.append(G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame, pid, 'Limits'))) Temps.append('%.1fK'%SampleList[-1]['Temperature']) if not G2frame.Contour: PlotList.reverse() ParmList.reverse() SampleList.reverse() LimitsList.reverse() if timeDebug: print('plot build time: %.3f for %dx%d patterns'%(time.time()-time0,len(PlotList[0][1][1]),len(PlotList))) lenX = 0 # length of first histogram, used for contour plots Ymax = None for ip,Pattern in enumerate(PlotList): xye = Pattern[1] try: xye = np.nan_to_num(xye) except ValueError: print('Error in diffraction pattern',ip,'skipping plot') continue if xye[1] is None: continue if Ymax is None: Ymax = max(xye[1]) Ymax = max(Ymax,max(xye[1])) if Ymax is None: return # nothing to plot offsetX = Page.plotStyle['Offset'][1] offsetY = Page.plotStyle['Offset'][0] if Page.plotStyle['logPlot']: Title = 'log('+Title+')' elif Page.plotStyle['sqrtPlot']: Title = r'$\sqrt{I}$ for '+Title Ymax = np.sqrt(Ymax) elif Page.plotStyle.get('WgtDiagnostic',False): Title = 'Scaling diagnostic for '+Title if G2frame.SubBack: Title += ' - background' if Page.plotStyle['qPlot'] or plottype in ['SASD','REFD'] and not G2frame.Contour and not ifLimits: xLabel = r'$Q, \AA^{-1}$' elif Page.plotStyle['dPlot'] and 'PWDR' in plottype and not ifLimits: xLabel = r'$d, \AA$' elif Page.plotStyle['chanPlot'] and G2frame.Contour: xLabel = 'Channel no.' else: if 'T' in ParmList[0]['Type'][0]: xLabel = r'$TOF, \mathsf{\mu}$s' elif 'E' in ParmList[0]['Type'][0]: xLabel = 'E, keV' else: xLabel = r'$\mathsf{2\theta}$' if G2frame.Weight and not G2frame.Contour: Plot.set_visible(False) #hide old plot frame, will get replaced below GS_kw = {'height_ratios':[4, 1],} # try: Plot,Plot1 = Page.figure.subplots(2,1,sharex=True,gridspec_kw=GS_kw) # except AttributeError: # figure.Figure.subplots added in MPL 2.2 # Plot,Plot1 = MPLsubplots(Page.figure, 2, 1, sharex=True, gridspec_kw=GS_kw) Plot1.set_ylabel(r'$\mathsf{\Delta(I)/\sigma(I)}$',fontsize=16) Plot1.set_xlabel(xLabel,fontsize=16) Page.figure.subplots_adjust(left=16/100.,bottom=16/150., right=.98,top=1.-16/200.,hspace=0) else: Plot.set_xlabel(xLabel,fontsize=16) if G2frame.Weight and G2frame.Contour: Title = r'$\mathsf{\Delta(I)/\sigma(I)}$ for '+Title if 'T' in ParmList[0]['Type'][0] or (Page.plotStyle['Normalize'] and not G2frame.SinglePlot): if Page.plotStyle['sqrtPlot']: Plot.set_ylabel(r'$\sqrt{Normalized\ intensity}$',fontsize=16) else: Plot.set_ylabel(r'$Normalized\ intensity$',fontsize=16) else: #neutron TOF if 'PWDR' in plottype: if Page.plotStyle['sqrtPlot']: Plot.set_ylabel(r'$\sqrt{Intensity}$',fontsize=16) elif Page.plotStyle.get('WgtDiagnostic',False): Plot.set_ylabel('Intensity * Weight') elif G2frame.CumeChi and G2frame.SinglePlot: Plot.set_ylabel(r'$Intensity, cum('+Gkchisq+')$',fontsize=16) else: Plot.set_ylabel(r'$Intensity$',fontsize=16) elif plottype == 'SASD': if Page.plotStyle['sqPlot']: Plot.set_ylabel(r'$S(Q)=I*Q^{4}$',fontsize=16) else: Plot.set_ylabel(r'$Intensity,\ cm^{-1}$',fontsize=16) elif plottype == 'REFD': if Page.plotStyle['sqPlot']: Plot.set_ylabel(r'$S(Q)=R*Q^{4}$',fontsize=16) else: Plot.set_ylabel(r'$Reflectivity$',fontsize=16) mpl.rcParams['image.cmap'] = G2frame.ContourColor mcolors = mpl.cm.ScalarMappable() #wants only default as defined in previous line!! mcolors.set_array([]) # needed for MPL <=3.0.x if G2frame.Contour: ContourZ = [] ContourY = [] ContourX = None Xlist = [] X0 = None Nseq = 0 Nmax = len(PlotList)-1 time0 = time.time() Plot.figure.subplots_adjust(right=.95) if G2frame.Contour and G2frame.TforYaxis: Plot.set_ylabel('Temperature',fontsize=14) elif G2frame.Contour: Plot.set_ylabel('Data sequence',fontsize=14) unequalArrays = False # set to True for contour plots with unequal pixels avgStep = None if G2frame.Contour: # detect unequally spaced points in a contour plot for N,Pattern in enumerate(PlotList): xye = np.array(ma.getdata(Pattern[1])) # strips mask = X,Yo,W,Yc,Yb,Yd if Page.plotStyle['qPlot'] and 'PWDR' in plottype and not ifLimits: X = 2.*np.pi/G2lat.Pos2dsp(Parms,xye[0]) elif Page.plotStyle['dPlot'] and 'PWDR' in plottype and not ifLimits: X = G2lat.Pos2dsp(Parms,xye[0]) else: X = copy.deepcopy(xye[0]) if not X0: X0 = X[0] # save 1st point in 1st pattern elif abs(X0 - X[0]) > 0.05 * X0: unequalArrays = True if Page.plotStyle['qPlot'] or Page.plotStyle['dPlot']: # not in original units unequalArrays = True elif 'T' in ParmList[0]['Type'][0] and not Page.plotStyle['chanPlot']: # assume TOF is non-linear steps unequalArrays = True # check to see if the average step size changes across the selected patterns elif avgStep is None and not unequalArrays: avgStep = (X[-1]-X[0])/(len(X)-1) elif not unequalArrays and abs(avgStep - (X[-1]-X[0])/(len(X)-1)) > 0.05 * avgStep: unequalArrays = True ExMask = [] for N,Pattern in enumerate(PlotList): Parms = ParmList[N] Sample = SampleList[N] limits = np.array(LimitsList[N]) ifpicked = False NoffY = offsetY*(Nmax-N) if Pattern[1] is None: continue # skip over uncomputed simulations xye = np.array(ma.getdata(Pattern[1])) # strips mask = X,Yo,W,Yc,Yb,Yd ExMask.append(np.full(len(xye[0]),False)) if G2frame.PickId: # when is this not true? ifpicked = Pattern[2] == G2frame.GPXtree.GetItemText(G2frame.PatternId) # recompute mask from excluded regions, in case they have changed xye0 = xye[0] # no mask in case there are no limits for excl in limits[2:]: xye0 = ma.masked_inside(xye[0],excl[0],excl[1],copy=False) #excluded region mask if unequalArrays: xye0 = ma.masked_outside(xye[0],limits[1][0],limits[1][1],copy=False) #now mask for limits Lmask = ma.getmask(xye0) # limits applied ExMask[N] = ExMask[N][~Lmask] # drop points outside limits elif not G2frame.Contour: xye0 = ma.masked_outside(xye0,limits[1][0],limits[1][1],copy=False) #now mask for limits else: xye0 = Pattern[1][0] # keeps mask Lmask = Emask = np.full(len(xye0),False) if G2frame.Contour: xye0 = xye[0] # drop mask for contouring # convert all X values and then reapply mask if xye0 is a masked array mask = None if hasattr(xye0,'mask'): mask = xye0.mask X = xye0.data else: mask = None X = xye0 if Page.plotStyle['qPlot'] and 'PWDR' in plottype and not ifLimits: X = ma.array(2.*np.pi/G2lat.Pos2dsp(Parms,X),mask=mask) elif Page.plotStyle['dPlot'] and 'PWDR' in plottype and not ifLimits: X = ma.array(G2lat.Pos2dsp(Parms,X),mask=mask) else: X = copy.deepcopy(xye0) if ifpicked and not G2frame.Contour: savedX = copy.deepcopy(X) if not lenX: lenX = len(X) # show plot magnification factors magMarkers = [] Plot.magLbls = [] multArray = np.ones_like(Pattern[1][0]) if 'PWDR' in plottype and G2frame.SinglePlot and not ( Page.plotStyle['logPlot'] or Page.plotStyle['sqrtPlot'] or G2frame.Contour): if not refineMode: magLineList = data[0].get('Magnification',[]) if ('C' in ParmList[0]['Type'][0] and Page.plotStyle['dPlot']) or ( 'B' in ParmList[0]['Type'][0] and Page.plotStyle['dPlot']) or ( 'E' in ParmList[0]['Type'][0] and Page.plotStyle['dPlot']) or ( 'T' in ParmList[0]['Type'][0] and Page.plotStyle['qPlot']): # reversed regions relative to data order tcorner = 1 tpos = 1.0 halign = 'right' else: tcorner = 0 tpos = 1.0 halign = 'left' ml0 = None for x,m in magLineList: ml = m if int(m) == m: ml = int(m) if x is None: magMarkers.append(None) multArray *= m ml0 = ml continue multArray[Pattern[1][0]>x] = m if Page.plotStyle['qPlot']: x = 2.*np.pi/G2lat.Pos2dsp(Parms,x) elif Page.plotStyle['dPlot']: x = G2lat.Pos2dsp(Parms,x) # is range in displayed range (defined after newplot)? if not newPlot: (xmin,xmax),ylim = G2frame.xylim if x < xmin: ml0 = ml continue if x > xmax: continue # magnification region marker magMarkers.append(Plot.axvline(x,color='0.5',dashes=(1,1),picker=2.,label='_magline')) lbl = Plot.annotate("x{}".format(ml), xy=(x, tpos), xycoords=("data", "axes fraction"), verticalalignment='bottom',horizontalalignment=halign,label='_maglbl') Plot.magLbls.append(lbl) if ml0: lbl = Plot.annotate("x{}".format(ml0), xy=(tcorner, tpos), xycoords="axes fraction", verticalalignment='bottom',horizontalalignment=halign,label='_maglbl') Plot.magLbls.append(lbl) Page.toolbar.updateActions = (PlotPatterns,G2frame) multArray = ma.getdata(multArray) if 'PWDR' in plottype: YI = copy.copy(xye[1]) #yo if G2frame.SubBack: YI -= xye[4] #background if Page.plotStyle['sqrtPlot']: olderr = np.seterr(invalid='ignore') #get around sqrt(-ve) error Y = np.where(YI>=0.,np.sqrt(YI),-np.sqrt(-YI))+NoffY*Ymax/100.0 np.seterr(invalid=olderr['invalid']) elif Page.plotStyle.get('WgtDiagnostic',False): Y = xye[1]*xye[2] #Y-obs*wt elif 'PWDR' in plottype and G2frame.SinglePlot and not ( Page.plotStyle['logPlot'] or Page.plotStyle['sqrtPlot'] or G2frame.Contour): Y = YI*multArray+NoffY*Ymax/100.0 else: Y = YI+NoffY*Ymax/100.0 elif plottype in ['SASD','REFD']: if plottype == 'SASD': B = xye[5] #Yo - Yc else: B = np.zeros_like(xye[5]) if Page.plotStyle['sqPlot']: Y = xye[1]*Sample['Scale'][0]*(1.05)**NoffY*X**4 else: Y = xye[1]*Sample['Scale'][0]*(1.05)**NoffY if Page.plotStyle['exclude']: Y = ma.array(Y,mask=ma.getmask(X)) if ifpicked and not G2frame.Contour: # draw limit & excluded region lines lims = limits[1:] if Page.plotStyle['qPlot'] and 'PWDR' in plottype and not ifLimits: lims = 2.*np.pi/G2lat.Pos2dsp(Parms,lims) elif Page.plotStyle['dPlot'] and 'PWDR' in plottype and not ifLimits: lims = G2lat.Pos2dsp(Parms,lims) # limit lines Lines.append(Plot.axvline(lims[0][0],color='g',dashes=(5,5),picker=3.)) Lines.append(Plot.axvline(lims[0][1],color='r',dashes=(5,5),picker=3.)) # excluded region lines for i,item in enumerate(lims[1:]): Lines.append(Plot.axvline(item[0],color='m',dashes=(6,3),picker=3.)) Lines.append(Plot.axvline(item[1],color='m',dashes=(6,3),picker=3.)) exclLines += [2*i+2,2*i+3] if G2frame.Contour: if Page.plotStyle['chanPlot']: if unequalArrays: X = np.array(range(len(X)),float) else: X = np.array(range(lenX),float) Lmask = Emask = np.full(len(X),False) if G2frame.Weight: Ytmp = (xye[1]-xye[3])*np.sqrt(xye[2]) else: Ytmp = Y # pad or truncate arrays when plotting with mpl.imshow if unequalArrays: ContourZ.append(ma.MaskedArray(Ytmp,Lmask).compressed()) elif len(Y) < lenX: Yext = np.ones(lenX)*Ytmp[-1] Yext[:len(X)] = Ytmp ContourZ.append(Yext) elif len(Y) > lenX: ContourZ.append(Ytmp[:lenX]) else: ContourZ.append(Ytmp) #if unequalArrays and G2frame.TforYaxis: # TODO: could set this to temperature and then plot # against temperature, but this only works if patterns are sorted by T ContourY.append(N) if unequalArrays: Xlist.append(ma.MaskedArray(X,Lmask).compressed()) elif ContourX is None: ContourX = X Nseq += 1 else: # not contour plot if not G2frame.plusPlot: pP = '' lW = 1.5 elif G2frame.plusPlot == 1: pP = '+' lW = 0 elif G2frame.plusPlot == 2: # same as 0 for multiplot, TODO: rethink pP = '' lW = 1.5 else: pP = '+' lW = 1.5 if plottype in ['SASD','REFD'] and Page.plotStyle['logPlot']: X *= (1.01)**(offsetX*N) else: xlim = Plot.get_xlim() DX = xlim[1]-xlim[0] X += 0.002*offsetX*DX*N if G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Limits': Xum = ma.getdata(X) # unmasked version of X, use for data limits (only) else: Xum = X[:] if ifpicked: # plotting of "master" pattern (histogram selected in data tree) ZI = copy.copy(xye[3]) #Yc if Page.plotStyle['sqrtPlot']: olderr = np.seterr(invalid='ignore') #get around sqrt(-ve) error Z = np.where(ZI>=0.,np.sqrt(ZI),-np.sqrt(-ZI)) np.seterr(invalid=olderr['invalid']) else: if 'PWDR' in plottype and G2frame.SinglePlot and not ( Page.plotStyle['logPlot'] or Page.plotStyle['sqrtPlot'] or G2frame.Contour): Z = ZI*multArray+NoffY*Ymax/100.0 #yc else: Z = ZI+NoffY*Ymax/100.0 #yc if 'PWDR' in plottype: if Page.plotStyle['sqrtPlot']: olderr = np.seterr(invalid='ignore') #get around sqrt(-ve) error W = np.where(xye[4]>=0.,np.sqrt(xye[4]),-np.sqrt(-xye[4])) #yb np.seterr(invalid=olderr['invalid']) D = np.where(xye[5],(Y-Z),0.)-Page.plotStyle['delOffset'] elif Page.plotStyle.get('WgtDiagnostic',False): Z = D = W = np.zeros_like(Y) elif G2frame.SinglePlot and not ( Page.plotStyle['logPlot'] or Page.plotStyle['sqrtPlot'] or G2frame.Contour): W = xye[4]*multArray+NoffY*Ymax/100.0 #yb D = multArray*xye[5]-Page.plotStyle['delOffset'] #Yo-Yc else: W = xye[4]+NoffY*Ymax/100.0 #yb D = xye[5]-Page.plotStyle['delOffset'] #Yo-Yc elif plottype in ['SASD','REFD']: if Page.plotStyle['sqPlot']: W = xye[4]*X**4 #Yb Z = xye[3]*X**4 #Yc B = B*X**4 #(yo-Yc)*x**4 else: W = xye[4] #Yb if G2frame.SubBack: YB = Y-B ZB = Z else: YB = Y ZB = Z+B try: Plot.set_yscale("log",nonpositive='mask') # >=3.3 except: Plot.set_yscale("log",nonpositive='mask') if np.any(W>0.): lims = [np.min(np.trim_zeros(W))/2.,np.max(Y)*2.] else: lims = [np.min(np.trim_zeros(YB))/2.,np.max(Y)*2.] if all(np.isfinite(lims)): Plot.set_ylim(bottom=lims[0],top=lims[1]) # Matplotlib artist lists used for refPlotUpdate ObsLine = None CalcLine = None BackLine = None DifLine = [None] if 'PWDR' in plottype and len(limits[2:]): # compute mask for excluded regions Emask = copy.deepcopy(ma.getmask(X)) for excl in limits[2:]: Emask += ma.getmask(ma.masked_inside(xye[0],excl[0],excl[1],copy=False)) if Page.plotStyle['exclude']: # optionally apply mask Xum = ma.array(Xum,mask=Emask) X = ma.array(X,mask=Emask) Y = ma.array(Y,mask=Emask) Z = ma.array(Z,mask=Emask) W = ma.array(W,mask=Emask) D = ma.array(D,mask=Emask) # difference plot is always masked if G2frame.Weight: Plot1.set_yscale("linear") wtFactor = Pattern[0]['wtFactor'] if plottype in ['SASD','REFD']: DZ = (Y-B-Z)*np.sqrt(wtFactor*xye[2]) else: DZ = (xye[1]-xye[3])*np.sqrt(wtFactor*xye[2]) if 'PWDR' in plottype and len(limits[2:]): DZ = ma.array(DZ,mask=Emask) # weighted difference is always masked DifLine = Plot1.plot(X,DZ,pwdrCol['Diff_color'],picker=1.,label=incCptn('diff')) #(Io-Ic)/sig(Io) Plot1.tick_params(labelsize=14) Plot1.axhline(0.,color='k') if Page.plotStyle['logPlot']: if 'PWDR' in plottype: try: Plot.set_yscale("log",nonpositive='mask') # >=3.3 except: Plot.set_yscale("log",nonpositive='mask') Plot.plot(X,Y,marker=pP,color=pwdrCol['Obs_color'],linewidth=lW,picker=3., clip_on=Clip_on,label=incCptn('obs')) if G2frame.SinglePlot or G2frame.plusPlot == 1 or G2frame.plusPlot == 2: Plot.plot(X,Z,pwdrCol['Calc_color'],label=incCptn('calc'),linewidth=1.5) if G2frame.plusPlot: Plot.plot(X,W,pwdrCol['Bkg_color'],label=incCptn('bkg'),linewidth=1.5) #background elif plottype in ['SASD','REFD']: try: Plot.set_xscale("log",nonpositive='mask') # >=3.3 Plot.set_yscale("log",nonpositive='mask') except: Plot.set_xscale("log",nonpositive='mask') Plot.set_yscale("log",nonpositive='mask') if G2frame.ErrorBars: if Page.plotStyle['sqPlot']: Plot.errorbar(X,YB,yerr=X**4*Sample['Scale'][0]*np.sqrt(1./(Pattern[0]['wtFactor']*xye[2])), ecolor=pwdrCol['Obs_color'],picker=3.,clip_on=Clip_on) else: Plot.errorbar(X,YB,yerr=Sample['Scale'][0]*np.sqrt(1./(Pattern[0]['wtFactor']*xye[2])), ecolor=pwdrCol['Obs_color'],picker=3.,clip_on=Clip_on,label=incCptn('obs')) else: Plot.plot(X,YB,marker=pP,color=pwdrCol['Obs_color'],linewidth=lW, picker=3.,clip_on=Clip_on,label=incCptn('obs')) Plot.plot(X,W,pwdrCol['Calc_color'],label=incCptn('bkg'),linewidth=1.5) #const. background Plot.plot(X,ZB,pwdrCol['Bkg_color'],label=incCptn('calc'),linewidth=1.5) else: # not logPlot ymax = 1. if Page.plotStyle['Normalize'] and Y.max() != 0 and not G2frame.SinglePlot: ymax = Y.max() if G2frame.SubBack: if 'PWDR' in plottype: ObsLine = Plot.plot(Xum,Y/ymax,color=pwdrCol['Obs_color'],marker=pP,linewidth=lW, clip_on=Clip_on,label=incCptn('obs-bkg')) #Io-Ib if np.any(Z): #only if there is a calc pattern CalcLine = Plot.plot(X,(Z-W)/ymax,pwdrCol['Calc_color'], label=incCptn('calc-bkg'),linewidth=1.5) #Ic-Ib else: Plot.plot(X,YB,color=pwdrCol['Obs_color'],marker=pP,linewidth=lW, picker=3.,clip_on=Clip_on,label=incCptn('obs')) Plot.plot(X,ZB,pwdrCol['Bkg_color'],label=incCptn('calc'),linewidth=1.5) else: if 'PWDR' in plottype: if G2frame.plusPlot != 3: ObsLine = Plot.plot(Xum,Y/ymax,color=pwdrCol['Obs_color'],marker=pP,linewidth=lW, picker=3.,clip_on=Clip_on,label=incCptn('obs')) #Io CalcLine = Plot.plot(X,Z/ymax,pwdrCol['Calc_color'], label=incCptn('calc'),linewidth=1.5) #Ic else: # waterfall mode=3: plot 1st pattern like others, name in legend? name = Pattern[2] if Pattern[0].get('histTitle'): name = Pattern[0]['histTitle'] ObsLine = Plot.plot(Xum,Y/ymax,color=pwdrCol['Obs_color'],marker='',linewidth=1.5, clip_on=Clip_on,label=incCptn(name)) #Io else: Plot.plot(X,YB,color=pwdrCol['Obs_color'],marker=pP,linewidth=lW, picker=3.,clip_on=Clip_on,label=incCptn('obs')) Plot.plot(X,ZB,pwdrCol['Bkg_color'],picker=False,label=incCptn('calc'),linewidth=1.5) if 'PWDR' in plottype and (G2frame.SinglePlot and G2frame.plusPlot): BackLine = Plot.plot(X,W/ymax,pwdrCol['Bkg_color'],picker=False,label=incCptn('bkg'),linewidth=1.5) #Ib if not G2frame.Weight and np.any(Z): DifLine = Plot.plot(X,D/ymax,pwdrCol['Diff_color'],linewidth=1.5, picker=True,pickradius=1.,label=incCptn('diff')) #Io-Ic Plot.axhline(0.,color='k',label='_zero') Plot.tick_params(labelsize=14) # write a .csv file; not fully tested, but probably works where allowed if 'PWDR' in plottype and G2frame.SinglePlot and plotOpt['saveCSV']: plotOpt['saveCSV'] = False fp = open(plotOpt['CSVfile'],'w') G2plt.Write2csv(fp,['"limits"',lims[0][0],lims[0][1]]) l = [] PeakId = G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Peak List') peaks = G2frame.GPXtree.GetItemPyData(PeakId) if inXtraPeakMode: tbl = peaks['xtraPeaks'] else: tbl = peaks['peaks'] for i,item in enumerate(tbl): if type(item) is dict: continue pos = item[0] if Page.plotStyle['qPlot']: l.append(2.*np.pi/G2lat.Pos2dsp(Parms,pos)) elif Page.plotStyle['dPlot']: l.append(G2lat.Pos2dsp(Parms,pos)) else: l.append(pos) if l: G2plt.Write2csv(fp,['"peaks"']+l) peaks['LaueFringe'] = peaks.get('LaueFringe',{}) l = [] for pos in peaks['LaueFringe'].get('satellites',[]): if Page.plotStyle['qPlot']: l.append(2.*np.pi/G2lat.Pos2dsp(Parms,pos)) elif Page.plotStyle['dPlot']: l.append(G2lat.Pos2dsp(Parms,pos)) else: l.append(pos) if l: G2plt.Write2csv(fp,['"satellites"']+l) G2plt.Write2csv(fp,['masked X','X','obs','calc','bkg','diff'],header=True) for i in range(len(X)): if hasattr(X,'mask'): G2plt.Write2csv(fp,[X[i],X.data[i],Y[i],Z[i],W[i],D[i]],header=False) else: G2plt.Write2csv(fp,[X[i],X[i],Y[i],Z[i],W[i],D[i]],header=False) fp.close() print('file',plotOpt['CSVfile'],'written') Page.SetToolTipString('') if G2frame.PickId: if G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Peak List': tip = 'On data point: Pick peak - L or R MB. On line: L-move, R-delete' Page.SetToolTipString(tip) peaks = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Peak List')) if inXtraPeakMode: peaks['xtraPeaks'] = peaks.get('xtraPeaks',[]) tbl = peaks['xtraPeaks'] color = 'r' else: tbl = peaks['peaks'] color = 'b' try: # find any peak rows that are selected selectedPeaks = list(set( [row for row,col in G2frame.reflGrid.GetSelectedCells()] + G2frame.reflGrid.GetSelectedRows())) # G2frame.dataWindow.movePeak.Enable(len(selectedPeaks) == 1) # allow peak move from table when one peak is selected for i,item in enumerate(tbl): if type(item) is dict: continue if i in selectedPeaks: #Ni = N+1 plotVline(Page,Plot,Lines,Parms,item[0],'yellow',0,'-') Lines[-1].set_lw(Lines[-1].get_lw()+1) plotVline(Page,Plot,Lines,Parms,item[0],color,2) Lines[-1].set_lw(Lines[-1].get_lw()+1) else: #Ni = N plotVline(Page,Plot,Lines,Parms,item[0],color,2) except: pass peaks['LaueFringe'] = peaks.get('LaueFringe',{}) SatLines = [] for pos in peaks['LaueFringe'].get('satellites',[]): plotVline(Page,Plot,SatLines,Parms,pos,'k',2) # for pos in peaks['xtraPeaks']: # plotVline(Page,Plot,Lines,Parms,pos[0],'r',0) if G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Limits': tip = 'On data point: Lower limit - L MB; Upper limit - R MB. On limit: MB down to move' Page.SetToolTipString(tip) limits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Limits')) # used anywhere? else: #not picked ymax = 1. if Page.plotStyle['Normalize'] and Y.max() != 0: ymax = Y.max() icolor = 256*N//len(PlotList) if Page.plotStyle['logPlot']: if 'PWDR' in plottype: try: # >=3.3 Plot.semilogy(X,Y,color=mcolors.cmap(icolor),nonpositive='mask',linewidth=1.5) except: Plot.semilogy(X,Y,color=mcolors.cmap(icolor),nonpositive='mask') elif plottype in ['SASD','REFD']: try: Plot.semilogy(X,Y,color=mcolors.cmap(icolor),nonpositive='mask',linewidth=1.5) except: Plot.semilogy(X,Y,color=mcolors.cmap(icolor),nonpositive='mask') else: if 'PWDR' in plottype: # waterfall mode=3: name in legend? name = Pattern[2] if Pattern[0].get('histTitle'): name = Pattern[0]['histTitle'] Plot.plot(X,Y/ymax,color=mcolors.cmap(icolor),picker=False,label=incCptn(name)) elif plottype in ['SASD','REFD']: try: Plot.loglog(X,Y,mcolors.cmap(icolor),nonpositive='mask',linewidth=1.5) except: Plot.loglog(X,Y,mcolors.cmap(icolor),nonpositive='mask') Plot.set_ylim(bottom=np.min(np.trim_zeros(Y))/2.,top=np.max(Y)*2.) if Page.plotStyle['logPlot'] and 'PWDR' in plottype: Plot.set_ylim(bottom=np.min(np.trim_zeros(Y))/2.,top=np.max(Y)*2.) #============================================================ # plot HKL labels data[0]['HKLconfig'] = data[0].get('HKLconfig',{}) alpha = data[0]['HKLconfig'].get('alpha',2) font = int(data[0]['HKLconfig'].get('Font','8')) angle = int(90 * data[0]['HKLconfig'].get('Orientation',0)) props = dict(boxstyle='round,pad=0.15', facecolor='#ccc', alpha=(4 - alpha)/4., ec='#ccc') markHKLs = (G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Reflection Lists' or 'PWDR' in G2frame.GPXtree.GetItemText(G2frame.PickId) or refineMode or (inXtraPeakMode and G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Peak List')) for ph,markerlist in data[0].get('HKLmarkers',{}).items(): if Page.plotStyle['logPlot'] or not markHKLs: continue if ph not in Page.phaseColors: continue color = Page.phaseColors[ph] for key,(ypos,hkls) in markerlist.items(): if len(hkls) == 0: continue comma = False # are commas needed? for hkl in hkls: if np.any([True for i in hkl if abs(i) > 9]): comma = True break lbl = '' for hkl in hkls: hkllbl = '' if lbl: lbl += '\n' for i in hkl: if hkllbl and comma: hkllbl += ',' if i < 0: lbl += r'$\overline{' + f'{-i}' + r'}$' else: lbl += f'{i}' xpos = float(key) if Page.plotStyle['qPlot']: xpos = 2.*np.pi/G2lat.Pos2dsp(Parms,xpos) elif Page.plotStyle['dPlot']: xpos = G2lat.Pos2dsp(Parms,xpos) if Page.plotStyle['sqrtPlot']: ypos = np.sqrt(abs(ypos))*np.sign(ypos) artist = Plot.text(xpos,ypos,lbl,fontsize=font,c=color,ha='center', va='top',bbox=props,picker=True,rotation=angle,label='_'+ph) artist.key = (ph,key) #============================================================ if timeDebug: print('plot fill time: %.3f'%(time.time()-time0)) if Page.plotStyle.get('title',True) and not magLineList: Plot.set_title(Title) if G2frame.PickId and not G2frame.Contour: Parms,Parms2 = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters')) orange = [255/256.,128/256.,0.] if 'PWDR' in plottype and G2frame.SinglePlot and G2frame.CumeChi: CY = np.cumsum(W**2*(Y-Z)**2) scale = np.max(CY)/np.max(Y) CY /= scale Plot.plot(X,CY,'k',label='cum('+Gkchisq+')') selectedPeaks = [] if G2frame.GPXtree.GetItemText(G2frame.PickId) in ['Index Peak List']: # find any peak rows that are selected selectedPeaks = list(set( [row for row,col in G2frame.indxPeaks.GetSelectedCells()] + G2frame.indxPeaks.GetSelectedRows())) if G2frame.GPXtree.GetItemText(G2frame.PickId) in ['Index Peak List','Unit Cells List']: peaks = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Index Peak List')) if len(peaks): # are there any peaks? for i,peak in enumerate(peaks[0]): if Page.plotStyle['qPlot']: x = 2.*np.pi/G2lat.Pos2dsp(Parms,peak[0]) elif Page.plotStyle['dPlot']: x = G2lat.Pos2dsp(Parms,peak[0]) else: x = peak[0] if i in selectedPeaks: Plot.axvline(x,color='yellow',lw=2) if peak[2]: if i in selectedPeaks: Plot.axvline(x,color='yellow',lw=2) Plot.axvline(x,color='b',lw=2,ls='dotted') else: Plot.axvline(x,color='b') for hkl in G2frame.HKL: clr = orange dash = (3,3) if len(hkl) > 6 and hkl[3]: clr = 'g' hklind = G2frame.PlotOpts.get('hklHighlight',0) if hklind != 0: # highlight selected classes of reflections if hkl[hklind-1] != 0: clr = 'b' dash = (5,2) if Page.plotStyle['qPlot']: Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,hkl[-2]),color=clr,dashes=dash,lw=1.5) elif Page.plotStyle['dPlot']: Plot.axvline(G2lat.Pos2dsp(Parms,hkl[-2]),color=clr,dashes=dash,lw=1.5) else: Plot.axvline(hkl[-2],color=clr,dashes=dash,lw=1.5) for hkl in G2frame.Extinct: # plot extinct reflections clr = 'g' if Page.plotStyle['qPlot']: Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,hkl[-2]),color=clr,dashes=(3,3),lw=2) elif Page.plotStyle['dPlot']: Plot.axvline(G2lat.Pos2dsp(Parms,hkl[-2]),color=clr,dashes=(3,3),lw=2) else: Plot.axvline(hkl[-2],color=clr,dashes=(3,3),lw=2) elif Page.plotStyle.get('WgtDiagnostic',False): pass # skip reflection markers elif (G2frame.GPXtree.GetItemText(G2frame.PickId) in ['Reflection Lists','Limits'] or 'PWDR' in G2frame.GPXtree.GetItemText(G2frame.PickId) or refineMode or (inXtraPeakMode and G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Peak List') ): l = GSASIIpath.GetConfigValue('Tick_length',8.0) w = GSASIIpath.GetConfigValue('Tick_width',1.) for pId,phase in enumerate(Page.phaseList): if 'list' in str(type(Phases[phase])): continue if phase in Page.phaseColors: plcolor = Page.phaseColors[phase] else: # how could this happen? plcolor = 'k' #continue peaks = Phases[phase].get('RefList',[]) if not len(peaks): continue if Phases[phase].get('Super',False): peak = np.array([[peak[5],peak[6]] for peak in peaks]) else: peak = np.array([[peak[4],peak[5]] for peak in peaks]) pos = Page.plotStyle['refOffset']-pId*Page.plotStyle['refDelt']*np.ones_like(peak) if Page.plotStyle['qPlot']: xtick = 2*np.pi/peak.T[0] elif Page.plotStyle['dPlot']: xtick = peak.T[0] else: xtick = peak.T[1] if Page.plotStyle.get('flTicks',0) == 0: # short tick-marks Page.tickDict[phase],_ = Plot.plot( xtick,pos,'|',mew=w,ms=l,picker=3.,label=phase,color=plcolor) # N.B. above creates two Line2D objects, 2nd is ignored. # Not sure what each does. elif Page.plotStyle.get('flTicks',0) == 1: # full length tick-marks if len(xtick) > 0: # create an ~hidden tickmark to create a legend entry Page.tickDict[phase] = Plot.plot(xtick[0],0,'|',mew=0.5,ms=l, label=phase,color=plcolor)[0] for xt in xtick: # a separate line for each reflection position Plot.axvline(xt,color=plcolor,picker=3.,label='_FLT_'+phase,lw=0.5) handles,legends = Plot.get_legend_handles_labels() if handles: labels = dict(zip(legends,handles)) # remove duplicate phase entries handles = [labels[item] for item in labels] legends = list(labels.keys()) if len(Phases) and plotOpt['obsInCaption'] and Page.plotStyle.get('flTicks',0) != 2: msg = 'Data' elif len(Phases) and plotOpt['obsInCaption']: msg = 'Phases & Data' else: msg = 'Phases' siz = None if len(legends) > 50: # make an attempt to make legend contents fit siz = 4 elif len(legends) > 25: siz = 6 elif len(legends) > 15: siz = 8 Plot.legend(handles,legends,title=msg,loc='best', fontsize=siz) if G2frame.Contour: time0 = time.time() acolor = G2plt.GetColorMap(G2frame.ContourColor) Vmin = Ymax*G2frame.Cmin Vmax = Ymax*G2frame.Cmax if unequalArrays: if G2frame.Weight: #Vmin = min([i.min() for i in ContourZ]) Vmin = min([ma.array(i,mask=m).min() for i,m in zip(ContourZ,ExMask)]) # don't count excluded points in limits #Vmax = max([i.max() for i in ContourZ]) Vmax = max([ma.array(i,mask=m).max() for i,m in zip(ContourZ,ExMask)]) if G2frame.TforYaxis: imgLbls = Temps else: imgLbls = [] uneqImgShow(Plot.figure,Plot,Xlist,ContourZ,cmap=acolor, vmin=Vmin,vmax=Vmax,Ylbls=imgLbls) Page.Img = None # don't have an overall image if G2frame.TforYaxis: Plot.yaxis.set_label_coords(-.1, .5) else: Plot.yaxis.set_label_coords(-.05, .5) Plot.xaxis.set_label_coords(0.5, -.07) else: if G2frame.Weight: Vmin = np.min(ContourZ) Vmax = np.max(ContourZ) Page.Img = Plot.imshow(ContourZ,cmap=acolor,vmin=Vmin,vmax=Vmax, interpolation=G2frame.Interpolate,extent=[ContourX[0],ContourX[-1],ContourY[0]-.5,ContourY[-1]+.5], aspect='auto',origin='lower') if G2frame.TforYaxis: imgAx = Page.Img.axes ytics = imgAx.get_yticks() # ytics = np.where(ytics<len(Temps),ytics,-1) # imgAx.set_yticks(ytics) ylabs = [Temps[int(i)] for i in ytics[:-1]] imgAx.set_yticklabels(ylabs) Page.figure.colorbar(Page.Img) if timeDebug: print('Contour display time: %.3f'%(time.time()-time0)) else: G2frame.Lines = Lines G2frame.MagLines = magMarkers if G2frame.PickId and G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Background': mag2th = [0]+[x for x,m in data[0].get('Magnification',[])][1:] magmult = [m for x,m in data[0].get('Magnification',[])] # plot fixed background points backDict = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Background'))[1] try: Parms,Parms2 = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters')) except TypeError: Parms = None for x,y in backDict.get('FixedPoints',[]): if magmult: mult = magmult[np.searchsorted(mag2th, x, side = 'right')-1] else: mult = 1. if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() # "normal" intensity modes only! if G2frame.SubBack or G2frame.Weight or G2frame.Contour or not G2frame.SinglePlot: break if y < 0 and (Page.plotStyle['sqrtPlot'] or Page.plotStyle['logPlot']): y = axis.get_ylim()[0] # put out of range point at bottom of plot elif Page.plotStyle['sqrtPlot']: y = math.sqrt(y) if Page.plotStyle['qPlot']: #Q - convert from 2-theta if Parms: x = 2*np.pi/G2lat.Pos2dsp(Parms,x) else: break elif Page.plotStyle['dPlot']: #d - convert from 2-theta if Parms: x = G2lat.Dsp2pos(Parms,x) else: break Plot.plot(x,y*mult,'rD',clip_on=Clip_on,picker=10.) # plot the partials plotOpt['lineList'] = ['obs','calc','bkg','zero','diff'] if 'PWDR' in plottype and G2frame.SinglePlot and Page.plotStyle['partials'] and 'hId' in data[0]: initPartialOpts(Page.phaseColors) x, yb, ypList = G2frame.LoadPartial(data[0]['hId']) if x is not None and len(ypList) > 1: if Page.plotStyle['qPlot']: x = 2.*np.pi/G2lat.Pos2dsp(Parms,x) elif Page.plotStyle['dPlot']: x = G2lat.Pos2dsp(Parms,x) olderr = np.seterr(invalid='ignore') #get around sqrt(-ve) error for ph in ypList: if not partialOpts[ph]['Show']: continue pcolor = partialOpts[ph]['color'] pwidth = partialOpts[ph]['width'] pLinStyl = partialOpts[ph]['MPLstyle'] if G2frame.SubBack: y = ypList[ph] else: y = ypList[ph]+yb if Page.plotStyle['sqrtPlot']: y = np.where(y>=0.,np.sqrt(y),-np.sqrt(-y)) Plot.plot(x,y,pcolor,label=ph,linewidth=pwidth,linestyle=pLinStyl) if 'PlotDefaults' in data[0] and fromTree: # set plot limists from defaults saved with '!' #print('setting plot defaults') (xlim, ylim, styleDict, G2frame.SinglePlot, G2frame.Contour, G2frame.Weight, G2frame.plusPlot, G2frame.SubBack) = data[0]['PlotDefaults'] Page.toolbar.push_current() # Page.toolbar.set_history_buttons() # this may be needed to update the zoom buttons (needs test) # Page.canvas.draw_idle() # schedule an MPL update (needs test) Plot.set_xlim((xlim[0],xlim[1])) Plot.set_ylim((ylim[0],ylim[1])) Page.toolbar.push_current() # why two? # Page.toolbar.set_history_buttons() # this may be needed to update the zoom buttons (needs test) # Page.canvas.draw_idle() # schedule an MPL update (needs test) newPlot = True # prevent carrying limits over from other histograms if not newPlot: # this restores previous plot limits (but I'm not sure why there are two .push_current calls) Page.toolbar.push_current() # Page.toolbar.set_history_buttons() # this may be needed to update the zoom buttons (needs test) # Page.canvas.draw_idle() # schedule an MPL update (needs test) if G2frame.Contour: # for contour plots expand y-axis to include all histograms G2frame.xylim = (G2frame.xylim[0], (-0.5,len(PlotList)-0.5)) if 'PWDR' in plottype: Plot.set_xlim(G2frame.xylim[0]) Plot.set_ylim(G2frame.xylim[1]) Page.toolbar.push_current() # Page.toolbar.set_history_buttons() # this may be needed to update the zoom buttons (needs test) # Page.canvas.draw_idle() # schedule an MPL update (needs test) Page.ToolBarDraw() else: G2frame.xylim = Plot.get_xlim(),Plot.get_ylim() Page.canvas.draw() if G2frame.Contour: # for contour plots remove non-integer yaxis labels as they are a distraction labels = [] vals = [] for item in Plot.get_yticklabels(): lbl = item.get_text() try: val = float(lbl.replace('\u2212', '-')) if int(val) != val: continue elif val < 0 or val > G2frame.xylim[1][1]: # out of range labels not needed continue else: vals.append(val) labels.append(str(int(val))) except: pass Plot.yaxis.set_ticks(vals,labels) Page.canvas.draw() olderr = np.seterr(invalid='ignore') #ugh - this removes a matplotlib error for mouse clicks in log plots # and sqrt(-ve) in np.where usage if 'PWDR' in G2frame.GPXtree.GetItemText(G2frame.PickId): if len(Page.tickDict.keys()) == 1: G2frame.dataWindow.moveTickLoc.Enable(True) elif len(Page.tickDict.keys()) > 1: G2frame.dataWindow.moveTickLoc.Enable(True) G2frame.dataWindow.moveTickSpc.Enable(True) if DifLine[0]: G2frame.dataWindow.moveDiffCurve.Enable(True) if refineMode: return refPlotUpdate
[docs] def PublishRietveldPlot(G2frame,Pattern,Plot,Page,reuse=None): '''Creates a window to show a customizable "Rietveld" plot. Exports that plot as a publication-quality file. Will only work only when a single pattern is displayed. :param wx.Frame G2Frame: the main GSAS-II window :param list Pattern: list of np.array items with obs, calc (etc.) diffraction pattern :param mpl.axes Plot: axes of the graph in plot window :param wx.Panel Page: tabbed panel containing the plot ''' def Init_fmts(): figure = mplfig.Figure(dpi=200,figsize=(6,8)) canvas = hcCanvas(figure) fmtDict = canvas.get_supported_filetypes() plotWidgets['fmtChoices'] = [fmtDict[j]+', '+j for j in sorted(fmtDict)] plotWidgets['fmtChoices'].append('Data file with plot elements, csv') plotWidgets['fmtChoices'].append('Grace input file, agr') plotWidgets['fmtChoices'].append('Igor Pro input file, itx') if sys.platform == "win32": plotWidgets['fmtChoices'].append('OriginPro connection') def Initialize(): '''Set up initial values in plotOpt ''' plotOpt['initNeeded'] = False # create a temporary hard-copy figure to get output options figure = mplfig.Figure(dpi=200,figsize=(6,8)) canvas = hcCanvas(figure) figure.clear() fmtDict = canvas.get_supported_filetypes() if plotOpt['format'] is None: if 'pdf' in fmtDict: plotOpt['format'] = fmtDict['pdf'] + ', pdf' else: plotOpt['format'] = plotWidgets['fmtChoices'][0] plotOpt['lineWid'] = '1' plotOpt['tickSiz'] = '6' plotOpt['tickWid'] = '1' plotOpt['markerWid'] = '1' plotOpt['markerSiz'] = '8' plotOpt['markerSym'] = '+' lims = {} lims['xmin'],lims['xmax'] = Plot.get_xlim() lims['ymin'],lims['ymax'] = Plot.get_ylim() for key in 'xmin','xmax','ymin','ymax': if not plotOpt[key+'_use']: plotOpt[key] = lims[key] #if mpl.__version__.split('.')[0] == '1': # G2G.G2MessageBox(G2frame.plotFrame, # ('You are using an older version of Matplotlib ({}). '.format(mpl.__version__) + # '\nPlot quality will be improved by updating (use conda update matplotlib)'), # 'Old matplotlib') def GetColors(): '''Set up initial values in plotOpt for colors and legend. Also catalog phases. ''' if hasattr(mpcls,'to_rgba'): MPL2rgba = mpcls.to_rgba else: MPL2rgba = mpcls.ColorConverter().to_rgba for i,l in enumerate(Plot.lines): lbl = l.get_label() if 'magline' in lbl: pass elif lbl[1:] in plotOpt['lineList']: # item not in legend if lbl[1:] in plotOpt['colors']: continue plotOpt['colors'][lbl[1:]] = MPL2rgba(l.get_color()) plotOpt['legend'][lbl[1:]] = False elif lbl in plotOpt['lineList']: if lbl in plotOpt['colors']: continue plotOpt['colors'][lbl] = MPL2rgba(l.get_color()) plotOpt['legend'][lbl] = True plotWidgets['phaseList'].clear() for ph,l in Page.tickDict.items(): plotWidgets['phaseList'].append(ph) if lbl in plotOpt['colors']: continue plotOpt['colors'][ph] = MPL2rgba(l.get_color()) plotOpt['legend'][ph] = True return def RefreshPlot(*args,**kwargs): '''Update the plot on the dialog ''' figure.clear() CopyRietveldPlot(G2frame,Pattern,Plot,Page,figure,plotWidgets['phaseList']) figure.canvas.draw() def CopyRietveld2Grace(Pattern,Plot,Page,plotOpt,filename): '''Copy the contents of the Rietveld graph from the plot window to a Grace input file (tested with QtGrace). Uses values from Pattern to also generate a delta/sigma plot below. ''' def ClosestColorNumber(color): '''Convert a RGB value to the closest default Grace color ''' import matplotlib.colors as mpcls colorlist = ('white','black','red','green','blue','yellow','brown', 'gray','purple','cyan','magenta','orange') # ordered by grace's # if not hasattr(mpcls,'to_rgba'): mpcls = mpcls.ColorConverter() return (np.sum(([np.array(mpcls.to_rgb(c)) for c in colorlist] - np.array(color[:3]))**2,axis=1)).argmin() # blocks of code used in grace .agr files linedef = '''@{0} legend "{1}" @{0} line color {2} @{0} errorbar color {2} @{0} symbol color {2} @{0} symbol {3} @{0} symbol fill color {2} @{0} linewidth {4} @{0} symbol linewidth {6} @{0} line type {7} @{0} symbol size {5} @{0} symbol char 46 @{0} symbol fill pattern 1 @{0} hidden false @{0} errorbar off\n''' linedef1 = '''@{0} legend "{1}" @{0} line color {2} @{0} errorbar color {2} @{0} symbol color {2} @{0} symbol fill color {2} @{0} symbol 11 @{0} linewidth 0 @{0} linestyle 0 @{0} symbol size {3} @{0} symbol linewidth {4} @{0} symbol char 124 @{0} symbol fill pattern 1 @{0} hidden false\n''' linedef2 = '''@{0} legend "{1}" @{0} line color {2} @{0} errorbar color {2} @{0} symbol color {2} @{0} symbol fill color {2} @{0} symbol 0 @{0} linewidth 0 @{0} linestyle 0 @{0} symbol size 1 @{0} symbol linewidth 0 @{0} symbol char 124 @{0} symbol fill pattern 1 @{0} hidden false @{0} errorbar on @{0} errorbar size 0 @{0} errorbar riser linewidth {3}\n''' linedef3 = '''@{0} legend "{1}" @{0} line color {2} @{0} errorbar color {2} @{0} symbol color {2} @{0} symbol {3} @{0} symbol fill color {2} @{0} linewidth {4} @{0} symbol linewidth {6} @{0} line type {7} @{0} symbol size {5} @{0} symbol char 46 @{0} symbol fill pattern 1 @{0} hidden false @{0} errorbar off\n''' grace_symbols = {"":0, "o":1 ,"s":2, "D":3, "^":4, "3":5, 'v':6, "4": 7, "+":8, "P":8, "x":9, "X":9, "*":10, ".":11} fp = open(filename,'w') fp.write("# Grace project file\n#\n@version 50010\n") # size of plots on page xmar = (.15,1.2) ymar = (.15,.9) top2bottom = 4. # 4 to 1 spacing for top to bottom boxes # scaling for top box fp.write('@g{0} hidden false\n@with g{0}\n@legend {1}\n'.format(0,"on")) fp.write('@legend {}, {}\n'.format(xmar[1]-.2,ymar[1]-.05)) fp.write('@world xmin {}\n@world xmax {}\n'.format(Plot.get_xlim()[0],Plot.get_xlim()[1])) fp.write('@world ymin {}\n@world ymax {}\n'.format(Plot.get_ylim()[0],Plot.get_ylim()[1])) fp.write('@view xmin {}\n@view xmax {}\n'.format(xmar[0],xmar[1])) fp.write('@view ymin {}\n@view ymax {}\n'.format((1./top2bottom)*(ymar[1]-ymar[0])+ymar[0],ymar[1])) xticks = Plot.get_xaxis().get_majorticklocs() fp.write('@{}axis tick major {}\n'.format('x',xticks[1]-xticks[0])) yticks = Plot.get_yaxis().get_majorticklocs() fp.write('@{}axis tick major {}\n'.format('y',yticks[1]-yticks[0])) fp.write('@{}axis ticklabel char size {}\n'.format('x',0)) # turns off axis labels if 'sqrt' in Plot.yaxis.get_label().get_text(): #ylbl = 'sqrt(Intensity)' # perhaps there is a way to get the symbol in xmgrace but I did not find it ylbl = r'\x\#{d6}\f{}\oIntensity\O' # from Carlo Segre else: ylbl = 'Intensity' fp.write('@{0}axis label "{1}"\n@{0}axis label char size {2}\n'.format( 'y',ylbl,float(plotOpt['labelSize'])/8.)) fp.write('@{0}axis label place spec\n@{0}axis label place {1}, {2}\n'.format('y',0.0,0.1)) # ====================================================================== # plot magnification lines and labels (first, so "under" data) for i,l in enumerate(Plot.lines): lbl = l.get_label() if 'magline' not in lbl: continue #ax0.axvline(l.get_data()[0][0],color='0.5',dashes=(1,1)) # vertical line s = '@with line\n@ line on\n@ line loctype world\n@ line g0\n' fp.write(s) s = '@ line {0}, {1}, {0}, {2}\n' fp.write(s.format( l.get_data()[0][0],Plot.get_ylim()[0],Plot.get_ylim()[1])) s = '@ line linewidth 2\n@ line linestyle 2\n@ line color 1\n@ line arrow 0\n@line def\n' fp.write(s) for l in Plot.texts: if 'magline' not in l.get_label(): continue if l.xycoords[0] == 'data': xpos = l.get_position()[0] elif l.get_position()[0] == 0: xpos = Plot.get_xlim()[0] else: xpos = Plot.get_xlim()[1]*.95 s = '@with string\n@ string on\n@ string loctype world\n' fp.write(s) s = '@ string g{0}\n@ string {1}, {2}\n@ string color 1\n' fp.write(s.format(0,xpos,Plot.get_ylim()[1])) s = '@ string rot 0\n@ string font 0\n@ string just 4\n' fp.write(s) s = '@ string char size {1}\n@ string def "{0}"\n' fp.write(s.format(l.get_text(),float(plotOpt['labelSize'])/8.)) datnum = -1 # ====================================================================== # plot data for l in Plot.lines: if l.get_label() in ('obs','calc','bkg','zero','diff'): lbl = l.get_label() elif l.get_label()[1:] in ('obs','calc','bkg','zero','diff'): lbl = l.get_label()[1:] else: continue c = plotOpt['colors'].get(lbl,mpl.colors.to_rgba(l.get_color())) gc = ClosestColorNumber(c) if sum(c) == 4.0: # white on white, skip continue marker = l.get_marker() lineWid = l.get_lw() siz = l.get_markersize() mkwid = l.get_mew() glinetyp = 1 if lbl == 'obs': gsiz = float(plotOpt['markerSiz'])/8. marker = plotOpt['markerSym'] gmw = float(plotOpt['markerWid']) gsym = grace_symbols.get(marker,5) glinetyp = 0 else: gsym = 0 gsiz = 0 gmw = 0 lineWid = float(plotOpt['lineWid']) if not plotOpt['Show'].get(lbl,True): continue if plotOpt['legend'].get(lbl): glbl = lbl else: glbl = "" datnum += 1 fp.write("@type xy\n") if lbl == 'zero': fp.write("{} {}\n".format(Plot.get_xlim()[0],0)) fp.write("{} {}\n".format(Plot.get_xlim()[1],0)) elif not ma.any(l.get_xdata().mask): for x,y in zip(l.get_xdata(),l.get_ydata()): fp.write("{} {}\n".format(x,y)) else: for x,y,m in zip(l.get_xdata(),l.get_ydata(),l.get_xdata().mask): if not m: fp.write("{} {}\n".format(x,y)) fp.write("&\n") fp.write(linedef.format("s"+str(datnum),glbl,gc,gsym,lineWid,gsiz,gmw,glinetyp)) #====================================================================== # reflection markers. Create a single hidden entry for the legend # and use error bars for the vertical lines for l in Plot.lines: glbl = lbl = l.get_label() if l not in Page.tickDict.values(): continue c = plotOpt['colors'].get(lbl,mpl.colors.to_rgba(l.get_color())) gc = ClosestColorNumber(c) siz = float(plotOpt['tickSiz'])*(Plot.get_ylim()[1] - Plot.get_ylim()[0])/(100*6) # 1% for siz=6 mkwid = float(plotOpt['tickWid']) if sum(c) == 4.0: continue # white: ignore if not plotOpt['Show'].get(lbl,True): continue if plotOpt['legend'].get(lbl): # invisible data point for datnum += 1 fp.write("@type xy\n") # fp.write("-1 -1\n".format(x,y)) fp.write("-1 -1\n") fp.write(linedef1.format( "s"+str(datnum),glbl,gc,float(plotOpt['tickSiz'])/8.,mkwid)) # plot values with error bars datnum += 1 fp.write("@type xydy\n") for x,y in zip(l.get_xdata(),l.get_ydata()): fp.write("{} {} {}\n".format(x,y,siz)) fp.write("&\n") fp.write(linedef2.format("s"+str(datnum),'',gc,mkwid)) #====================================================================== # Start (obs-cal)/sigma plot wtFactor = Pattern[0]['wtFactor'] ysig = (Pattern[1][1]-Pattern[1][3])*np.sqrt(wtFactor*Pattern[1][2]) fp.write("@type xy\n") # scaling for bottom box fp.write('@g{0} hidden false\n@with g{0}\n@legend {1}\n'.format(1,"off")) fp.write('@world xmin {}\n@world xmax {}\n'.format(Plot.get_xlim()[0],Plot.get_xlim()[1])) fp.write('@world ymin {}\n@world ymax {}\n'.format(ysig.min(),ysig.max())) fp.write('@view xmin {}\n@view xmax {}\n'.format(xmar[0],xmar[1])) fp.write('@view ymin {}\n@view ymax {}\n'.format( ymar[0],(1./top2bottom)*(ymar[1]-ymar[0])+ymar[0])) if 'theta' in Plot.get_xlabel(): xlbl = r'2\f{Symbol}q' elif 'TOF' in Plot.get_xlabel(): xlbl = r'TOF, \f{Symbol}m\f{}s' else: xlbl = Plot.get_xlabel().replace('$','') fp.write('@{0}axis label "{1}"\n@{0}axis label char size {2}\n'.format( 'x',xlbl,float(plotOpt['labelSize'])/8.)) fp.write('@{0}axis label "{1}"\n@{0}axis label char size {2}\n'.format( 'y',r'\f{Symbol}D/s',float(plotOpt['labelSize'])/8.)) xticks = Plot.get_xaxis().get_majorticklocs() # come up with a "nice" tick interval for (o-c)/sig, since I am not sure # if this can be defaulted ytick = (ysig.max()-ysig.min())/5. l10 = np.log10(ytick) if l10 < 0: yti = int(10**(1 + l10 - int(l10))) # r = -0.5 else: yti = int(10**(l10 - int(l10))) # r = 0.5 if yti == 3: yti = 2 elif yti > 5: yti = 5 ytick = yti * 10**int(np.log10(ytick/yti)+.5) fp.write('@{}axis tick major {}\n'.format('x',xticks[1]-xticks[0])) fp.write('@{}axis tick major {}\n'.format('y',ytick)) fp.write("@type xy\n") try: # new behavior: .mask can return a single np.False_ value savedX_mask = savedX.mask len(savedX_mask) except TypeError: savedX_mask = len(savedX)*[False] for x,y,m in zip(savedX,ysig,savedX_mask): if not m: fp.write("{} {}\n".format(x,y)) fp.write("&\n") fp.write(linedef3.format("s1",'',1,0,1.0,0,0,1)) fp.close() print('file',filename,'written') def CopyRietveld2Origin(Pattern,Plot,Page,plotOpt,G2frame): # Exports plot to Origin. This function was written by Conrad Gillard (conrad.gillard@gmail.com). def origin_shutdown_exception_hook(exctype, value, tracebk): '''Ensures Origin gets shut down if an uncaught exception''' try: op.exit() except: pass print('\n****OriginPro error****') import traceback traceback.print_exception(exctype, value, tracebk) G2G.G2MessageBox(G2frame, 'Failed to connect to OriginPro. Is it installed?\nSee console window for more info.') #sys.__excepthook__(exctype, value, tracebk) # Function to increase line width, for later use def increase_line_width(plot): layr = op.GLayer(plot.layer) pindex = plot.index() pname = layr.obj.GetStrProp('plot{}.name'.format(pindex+1)) layr.lt_exec('set {} -w 1000'.format(pname)) #import itertools # delay this since not commonly called or needed try: import originpro as op except: note1,note2 = '','' # get pip location pyPath = os.path.split(os.path.realpath(sys.executable))[0] import shutil if shutil.which('pip'): pip = shutil.which('pip') elif os.path.exists(os.path.join(pyPath,'pip.exe')): pip = os.path.join(pyPath,'pip.exe') elif os.path.exists(os.path.join(pyPath,'Scripts','pip.exe')): pip = os.path.join(pyPath,'Scripts','pip.exe') else: note1 = "\nNote pip not found, you may need to install that too\n" pip = 'pip' try: import win32clipboard win32clipboard.OpenClipboard() win32clipboard.EmptyClipboard() win32clipboard.SetClipboardText('{} install originpro'.format(pip)) win32clipboard.CloseClipboard() note2 = "\nNote: command copied to clipboard (use control-V to paste in cmd.exe window)" except: pass msg = """Use of the OriginPro exporter requires that OriginPro be installed on your computer as well as a communication module (originpro) via pip. {} Use command \t{} install originpro in a cmd.exe window to do this. {}""".format(note1,pip,note2) G2G.G2MessageBox(G2frame,msg) return lblList = [] valueList = [] lblList.append('Axis-limits') valueList.append(list(Plot.get_xlim())+list(Plot.get_ylim())) tickpos = {} for i,l in enumerate(Plot.lines): lbl = l.get_label() if lbl[1:] in ('obs','calc','bkg','zero','diff'): lbl = lbl[1:] if 'magline' in lbl: pass elif lbl in ('obs','calc','bkg','zero','diff'): if lbl == 'obs': lblList.append('x') valueList.append(l.get_xdata()) c = plotOpt['colors'].get(lbl,mpl.colors.to_rgba(l.get_color())) if sum(c) == 4.0: continue lblList.append(lbl) valueList.append(l.get_ydata()) elif l in Page.tickDict.values(): c = plotOpt['colors'].get(lbl,mpl.colors.to_rgba(l.get_color())) if sum(c) == 4.0: continue tickpos[lbl] = l.get_ydata()[0] lblList.append(lbl) valueList.append(l.get_xdata()) if tickpos: lblList.append('tick-pos') valueList.append([]) for i in tickpos: valueList[-1].append(i) valueList[-1].append(tickpos[i]) # add (obs-calc)/sigma [=(obs-calc)*sqrt(weight)] lblList.append('diff/sigma') valueList.append(Pattern[1][5]*np.sqrt(Pattern[1][2])) if sum(Pattern[1][0].mask): # are there are excluded points? If so, add them too lblList.append('excluded') valueList.append(1*Pattern[1][0].mask) # magnifcation values for l in Plot.texts: lbl = l.get_label() if 'magline' not in lbl: continue if l.xycoords == 'axes fraction': lbl = 'initial-mag' lblList.append(lbl) valueList.append([l.get_text()]) else: lbl = 'mag' lblList.append(lbl) valueList.append([l.get_text(),l.get_position()[0]]) # Start Origin instance if op and op.oext: sys.excepthook = origin_shutdown_exception_hook # Set Origin instance visibility if op.oext: op.set_show(True) op.new() # Create folder to hold data and graph refinementName = G2frame.Label refinementName = refinementName[17:-4] fldr = op.po.RootFolder.Folders.Add(refinementName) fldr.Activate() # Create worksheet to hold refinement data dest_wks = op.new_sheet('w', lname='Refinement Data') dest_wks.cols = 5 # Import refinement data colNamesList =["x", "Observed", 'Calculated','Background','(Obs-Calc)'] for i in range(1, 6): dest_wks.from_list(col=i-1, data=valueList[i].tolist(), lname=colNamesList[i-1]) # Create graph object, to which data will be added template = os.path.join(GSASIIpath.path2GSAS2,'inputs','OriginTemplate2.otpu') if not os.path.exists(template): # patch 3/2024 for svn dir organization template = os.path.join(GSASIIpath.path2GSAS2,'OriginTemplate2.otpu') if not os.path.exists(template): print('Error: OriginTemplate2.otpu not found') return graph = op.new_graph(template=template) graph.lname = refinementName + "_G" gl = graph[0] # Plot observed as scatter plot = gl.add_plot(dest_wks, coly=1, colx=0, type='scatter') plot.symbol_size = 10 plot.symbol_kind = 7 plot.colormap = 'Classic' plot.color = 1 # Plot calculated, background and difference as line for i in range(2, 5): plot = gl.add_plot(dest_wks, coly=i, colx=0, type='line') plot.colormap = 'Classic' plot.color = i increase_line_width(plot) # Import reflection data for each phase tickPosIdx = lblList.index("tick-pos") # Initialise counters for colour index and number of phases j = 1 k = 1 refLegendText = "" for i in range(7, tickPosIdx): # Create worksheet to hold reflections data dest_wks = op.new_sheet('w', lname=lblList[i] + " Reflections") dest_wks.cols = 2 # Generate lists of tick positions tickPosList = valueList[i].tolist() # Generate lists of tick intensities refIntens = valueList[tickPosIdx][(i - 6) * 2 - 1] refIntens = float(refIntens) refIntensList = [refIntens] * len(tickPosList) # Import tick positions and intensities to worksheet dest_wks.from_list(col=0, data=tickPosList, lname="Tick Position") dest_wks.from_list(col=1, data=refIntensList, lname="Tick Intensity") # Add reflections to plot plot = gl.add_plot(dest_wks, coly=1, colx=0, type='scatter') plot.symbol_size = 10 plot.symbol_kind = 10 plot.color = 4 + j refLegendText = refLegendText + "\\l(" + str(4 + k) + ") " + lblList[i] + " " # Increment phase counter k += 1 # increment colour index, skipping yellow because it cannot be seen if j == 2: j += 2 else: j += 1 # # Set axis limits xmin = Plot.get_xlim()[0] if Plot.dataLim.x0 > xmin: xmin = Plot.dataLim.x0 + 0.1 xmax = Plot.get_xlim()[1] if Plot.dataLim.x1 < xmax: xmax = Plot.dataLim.x1 + 0.1 gl.set_xlim(xmin, xmax) ymin = Plot.get_ylim()[0] ymax = Plot.get_ylim()[1] gl.set_ylim(ymin, ymax) # Change graph titles gl.axis(ax="x").title = "2θ (°)" gl.axis(ax="y").title = "Intensity (Arbitrary units)" # Set up legend label = gl.label('Legend') label.text = '\\l(1) %(1)\\l(2) %(2)\\l(3) %(3)\\l(4) %(4) %(CRLF)' + refLegendText def CopyRietveld2Igor(Pattern,Plot,Page,plotOpt,filename,G2frame): '''Copy the contents of the Rietveld graph from the plot window to a Igor Pro input file (tested with v7.05). Uses values from Pattern to also generate a delta/sigma plot below. Coded with lots of help from Jan Ilavsky. ''' import itertools # delay this until called, since not commonly needed InameDict = {'obs':'Intensity', 'calc':'FitIntensity', 'bkg':'Background','diff':'Difference', 'omcos':'NormResidual','zero':'Zero'} igor_symbols = {"o":19, "s":18, "D":29, "^":17, "3":46, 'v':23, "4":49, "+":1, "P":60, "x":0, "X":62, "*":2} def Write2cols(fil,dataItems): '''Write a line to a file in space-separated columns. Skips masked items. :param object fil: file object :param list dataItems: items to write as row in file ''' line = '' for item in dataItems: if ma.is_masked(item): return if line: line += ' ' item = str(item) if ' ' in item: line += '"'+item+'"' else: line += item fil.write(line+'\n') proj = os.path.splitext(G2frame.GSASprojectfile)[0] if not proj: proj = 'GSASIIproject' proj = proj.replace(' ','') valueList = [] markerSettings = [] Icolor = {} legends = [] zerovals = None fontsize = 18*float(plotOpt['labelSize'])/12. for i,l in enumerate(Plot.lines): lbl = l.get_label() if not plotOpt['Show'].get(lbl[1:],True): continue if plotOpt['legend'].get(lbl[1:]): legends.append((InameDict[lbl[1:]],lbl[1:])) if l.get_label()[1:] in ('obs','calc','bkg','zero','diff'): lbl = l.get_label()[1:] if plotOpt['legend'].get(lbl) and lbl in InameDict: legends.append((InameDict[lbl],lbl)) if lbl == 'obs': x = l.get_xdata() valueList.append(x) zerovals = (x.min(),x.max()) gsiz = 5*float(plotOpt['markerSiz'])/8. marker = plotOpt['markerSym'] gsym = igor_symbols.get(marker,12) gmw = float(plotOpt['markerWid']) markerSettings.append( 'mode({0})=3,marker({0})={1},msize({0})={2},mrkThick({0})={3}' .format('Intensity',gsym,gsiz,gmw)) elif lbl in ('calc','bkg','zero','diff'): markerSettings.append( 'mode({0})=0, lsize({0})={1}' .format(InameDict[lbl],plotOpt['lineWid'])) else: continue c = plotOpt['colors'].get(lbl,mpl.colors.to_rgba(l.get_color())) Icolor[InameDict[lbl]] = [j*65535 for j in c[0:3]] if lbl != 'zero': valueList.append(l.get_ydata()) valueList.append(Pattern[1][5]*np.sqrt(Pattern[1][2])) fp = open(filename,'w') fp.write('''IGOR X setDataFolder root: X // *** Replace GSAS2Data with name of current project in GSAS. X // *** this name will get "order" number (0,1,2...) to be unique and data will be stored there. X // *** and the graph will also be named using this base name. X NewDataFolder/O/S $(UniqueName(CleanupName("{}",0),11, 0)) X string GSAXSProjectName = GetDataFolder(0) WAVES /D/O TwoTheta, Intensity, FitIntensity, Background, Difference, NormResidual BEGIN '''.format(proj)) # invert lists into columns, use iterator for all values for row in itertools.zip_longest(*valueList,fillvalue=' '): Write2cols(fp,row) fp.write('''END X // *** static part of the code, NB reflection tickmarks later **** X SetScale d 0,0, "degree", TwoTheta X // *** this is where graph is created and data added X string G_Name=CleanupName(GSAXSProjectName,0) X Display/K=1/W=(50,40,850,640) Intensity vs twoTheta; DoWindow/C $(G_Name) X DoWindow/T $(G_Name), G_Name X AppendToGraph FitIntensity vs TwoTheta X AppendToGraph Background vs TwoTheta X AppendToGraph Difference vs TwoTheta X AppendToGraph/L=Res_left/B=Res_bot NormResidual vs TwoTheta X // *** Here we have modification of the four axes used in the graph X ModifyGraph mode=2,lsize=5, mirror=2 X SetAxis/A/E=2 Res_left X ModifyGraph freePos(Res_left)={0,kwFraction} X ModifyGraph freePos(Res_bot)={0.2,kwFraction} X ModifyGraph standoff(bottom)=0 X ModifyGraph axisEnab(left)={0.2,1} X ModifyGraph axisEnab(Res_left)={0,0.2} X ModifyGraph lblPosMode(Res_left)=1, mirror(Res_bot)=0 X ModifyGraph tickUnit(bottom)=1,manTick=0,manMinor(bottom)={0,50} X ModifyGraph tick(Res_bot)=1,noLabel(Res_bot)=2,standoff(Res_bot)=0 X ModifyGraph manMinor(Res_bot)={0,50},tick(Res_bot)=2 X ModifyGraph axThick=2 X ModifyGraph mirror(Res_bot)=0,nticks(Res_left)=3,highTrip(left)=1e+06,manTick(bottom)=0 X ModifyGraph btLen=5 ''') fp.write('X ModifyGraph gfSize={}\n'.format(int(fontsize+.5))) # line at zero if not zerovals: zerovals = (Plot.get_xlim()[0],Plot.get_xlim()[1]) fp.write('X // *** add line at y=zero\n') fp.write('WAVES /D/O ZeroX, Zero\nBEGIN\n') fp.write(' {0} 0.0\n {1} 0.0\n'.format(*zerovals)) fp.write('END\nX AppendToGraph Zero vs ZeroX\n') if 'sqrt' in Plot.yaxis.get_label().get_text(): ylabel = '\u221AIntensity' else: ylabel = 'Intensity' fp.write('''X // *** add axis labels and position them X Label left "{1}" X Label Res_left "{2}" X Label bottom "{0}" X ModifyGraph lblPosMode=0,lblPos(Res_left)=84 '''.format("2Θ",ylabel,"∆/σ")) fp.write('''X // *** set display limits. X SetAxis left {2}, {3} X SetAxis bottom {0}, {1} X SetAxis Res_bot {0}, {1} ''' .format(Plot.get_xlim()[0],Plot.get_xlim()[1], Plot.get_ylim()[0],Plot.get_ylim()[1])) fp.write('X // *** modify how data are displayed ****\n') # fp.write( # '''X // *** here starts mostly static part of the code, here we modify how data are displayed **** # X ModifyGraph mode(FitIntensity)=0,rgb(FitIntensity)=(1,39321,19939), lsize(FitIntensity)=1 # X ModifyGraph mode(Background)=0,lsize(Background)=2, rgb(Background)=(65535,0,0) # X ModifyGraph mode(Difference)=0,lsize(Difference)=2,rgb(Difference)=(0,65535,65535) # X // *** modifications for the bottom graph, here we modify how data are displayed **** # X ModifyGraph mode(NormResidual)=0,lsize(NormResidual)=1,rgb(NormResidual)=(0,0,0) # X // *** end of modifications for the main data in graph # ''') for m in markerSettings: fp.write('X ModifyGraph {}\n'.format(m)) for lbl in Icolor: fp.write('X ModifyGraph rgb({})=({:.0f},{:.0f},{:.0f})\n' .format(lbl,*Icolor[lbl])) fp.write('X ModifyGraph mode(NormResidual)=0,lsize(NormResidual)=1,rgb(NormResidual)=(0,0,0)\n') fp.write('X // *** End modify how data are displayed ****\n') # loop over reflections ticknum = 0 for i,l in enumerate(Plot.lines): lbl = l.get_label() if not plotOpt['Show'].get(lbl,True): continue if l in Page.tickDict.values(): c = plotOpt['colors'].get(lbl,mpl.colors.to_rgba(l.get_color())) if sum(c) == 4.0: continue # white is invisible ticknum += 1 phasename = 'tick{}'.format(ticknum) if plotOpt['legend'].get(lbl): legends.append((phasename,plotOpt['phaseLabels'].get(lbl,lbl))) tickpos = l.get_ydata()[0] fp.write( '''X // reflection tickmark for phase {1} WAVES /D/O {0}X, {0} BEGIN '''.format(phasename,lbl)) for x in l.get_xdata(): fp.write('{} {}\n'.format(x,tickpos)) fp.write('''END X // *** controls for {1} reflection tickmarks X AppendToGraph {0} vs {0}X X ModifyGraph mode({0})=3,mrkThick({0})=2,gaps({0})=0 X ModifyGraph marker({0})=10,rgb({0})=({2},{3},{4}) '''.format(phasename,lbl,*[j*65535 for j in c[0:3]])) # plot magnification lines and labels j = 0 for i,l in enumerate(Plot.lines): lbl = l.get_label() if 'magline' not in lbl: continue j += 1 fp.write('WAVES /D/O mag{0}X, mag{0}\nBEGIN\n'.format(j)) fp.write(' {0} {1}\n {0} {2}\n'.format( l.get_data()[0][0],Plot.get_ylim()[0],Plot.get_ylim()[1])) fp.write('END\nX AppendToGraph mag{0} vs mag{0}X\n'.format(j)) fp.write('X ModifyGraph lstyle(mag{0})=3,rgb(mag{0})=(0,0,0)\n'.format(j)) for l in Plot.texts: if 'magline' not in l.get_label(): continue if l.xycoords[0] == 'data': xpos = l.get_position()[0] elif l.get_position()[0] == 0: xpos = Plot.get_xlim()[0] else: xpos = Plot.get_xlim()[1]*.95 fp.write('X SetDrawEnv xcoord=bottom, ycoord= abs,textyjust=2,fsize={}\n'.format(fontsize)) fp.write('X DrawText {0},2,"{1}"\n'.format(xpos,l.get_text())) # legend s = "" for nam,txt in legends: if s: s += r'\r' s += r'\\s({}) {}'.format(nam,txt) fp.write('X Legend/C/N=text0/J "{}"\n'.format(s)) fp.close() print('file',filename,'written') def CopyRietveld2csv(Pattern,Plot,Page,filename): '''Copy the contents of the Rietveld graph from the plot window to .csv file ''' import itertools # delay this since not commonly called or needed lblList = [] valueList = [] tickpos = {} lblList.append('used') try: valueList.append([0 if i else 1 for i in savedX.mask]) except: pass if 'TOF' in Plot.xaxis.get_label_text(): lblList.append('x, TOF (msec)') elif 'Q,' in Plot.xaxis.get_label_text(): lblList.append('x, Q (A-1)') elif 'd,' in Plot.xaxis.get_label_text(): lblList.append('x, d-space (A)') elif 'E,' in Plot.xaxis.get_label_text(): lblList.append('x, E (keV)') elif 'theta' in Plot.xaxis.get_label_text(): lblList.append('x, 2theta (deg)') else: lblList.append('x, ?') valueList.append(savedX.data) for i,l in enumerate(Plot.lines): lbl = l.get_label() if lbl[1:] in ('obs','calc','bkg','zero','diff'): lbl = l.get_label()[1:] if 'magline' in lbl: pass elif lbl in ('obs','calc','bkg','zero','diff'): c = plotOpt['colors'].get(lbl,l.get_color()) if sum(c) == 4.0: continue #skip over any "white" entries if lbl == 'zero': continue if lbl == 'obs': # include all observed data lblList.append('obs') valueList.append(Pattern[1][1].data) continue lblList.append(lbl) valueList.append(l.get_ydata()) elif l in Page.tickDict.values(): c = plotOpt['colors'].get(lbl,mpl.colors.to_rgba(l.get_color())) if sum(c) == 4.0: continue tickpos[lbl] = l.get_ydata()[0] lblList.append(lbl) valueList.append(l.get_xdata()) if tickpos: lblList.append('tick-pos') valueList.append([]) for i in tickpos: valueList[-1].append(i) valueList[-1].append(tickpos[i]) # add (obs-calc)/sigma [=(obs-calc)*sqrt(weight)] lblList.append('diff/sigma') wtFactor = Pattern[0]['wtFactor'] DZ = (Pattern[1][1]-Pattern[1][3])*np.sqrt(wtFactor*Pattern[1][2]) valueList.append(DZ) if hasattr(Pattern[1][0],'mask') and sum(Pattern[1][0].mask): # are there are excluded points? If so, add them too lblList.append('excluded') valueList.append(1*Pattern[1][0].mask) lblList.append('Axis-limits') valueList.append(list(Plot.get_xlim())+list(Plot.get_ylim())) # magnification values for l in Plot.texts: lbl = l.get_label() if 'magline' not in lbl: continue if l.xycoords == 'axes fraction': lbl = 'initial-mag' lblList.append(lbl) valueList.append([l.get_text()]) else: lbl = 'mag' lblList.append(lbl) valueList.append([l.get_text(),l.get_position()[0]]) fp = open(filename,'w') G2plt.Write2csv(fp,lblList,header=True) # invert lists into columns, use iterator for all values for row in itertools.zip_longest(*valueList,fillvalue=' '): G2plt.Write2csv(fp,row) fp.close() def onSave(event): '''Write the current plot to a file ''' hcfigure = mplfig.Figure(dpi=plotOpt['dpi'],figsize=(plotOpt['width'],plotOpt['height'])) CopyRietveldPlot(G2frame,Pattern,Plot,Page,hcfigure,plotWidgets['phaseList']) if 'OriginPro' in plotOpt['format']: CopyRietveld2Origin(Pattern,Plot,Page,plotOpt,G2frame) dlg.EndModal(wx.ID_OK) return longFormatName,typ = plotOpt['format'].split(',') fil = G2G.askSaveFile(G2frame,'','.'+typ.strip(),longFormatName) if 'csv' in typ and fil: CopyRietveld2csv(Pattern,Plot,Page,fil) elif 'agr' in typ and fil: CopyRietveld2Grace(Pattern,Plot,Page,plotOpt,fil) elif 'itx' in typ and fil: CopyRietveld2Igor(Pattern,Plot,Page,plotOpt,fil,G2frame) elif fil: if hcfigure.canvas is None: if GSASIIpath.GetConfigValue('debug'): print('creating canvas') hcCanvas(hcfigure) hcfigure.savefig(fil,format=typ.strip()) dlg.EndModal(wx.ID_OK) def OnSelectColour(event): '''Respond to a change in color ''' # lbl = plotOpt['colorButtons'].get(list(event.GetEventObject())[:3]) if event.GetEventObject() not in plotWidgets['colorButtons']: print('Unexpected button',str(event.GetEventObject())) return lbl = plotWidgets['colorButtons'][event.GetEventObject()] c = event.GetValue() plotOpt['colors'][lbl] = (c.Red()/255.,c.Green()/255.,c.Blue()/255.,c.alpha/255.) RefreshPlot() def OnSavePlotOpt(event): 'Save current "publish" settings to a JSON file with extension .pltopts' import json filroot = os.path.splitext(G2frame.GSASprojectfile)[0]+'.pltopts' saveFile = G2G.askSaveFile(G2frame,filroot,'.pltopts', 'Saved plot options') if saveFile: json.dump(plotOpt,open(saveFile,'w')) print(f'Plot options written to {saveFile!r}') def OnLoadPlotOpt(event): 'Set current "publish" settings from saved settings in a JSON file (extension .pltopts)' import json filroot = os.path.splitext(G2frame.GSASprojectfile)[0]+'.pltopts' fdlg = wx.FileDialog(dlg, 'Choose saved plot options file', filroot, style=wx.FD_OPEN, wildcard='plot options file(*.pltopts)|*.pltopts') if fdlg.ShowModal() == wx.ID_OK: filroot = fdlg.GetPath() plotOpt.update(json.load(open(filroot,'r'))) wx.CallAfter(PublishRietveldPlot,G2frame,Pattern,Plot,Page,reuse=dlg) fdlg.Destroy() #### start of PublishRietveldPlot plotWidgets = {} plotWidgets['phaseList'] = [] Init_fmts() if reuse: dlg = reuse vbox = dlg.GetSizer() vbox.Clear() else: if plotOpt['initNeeded']: Initialize() GetColors() dlg = wx.Dialog(G2frame.plotFrame,title="Publication plot creation", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) vbox = wx.BoxSizer(wx.VERTICAL) # size choices symChoices = ('+','x','.','o','^','v','*','|') txtChoices = [str(i) for i in range (8,26)] sizChoices = [str(i) for i in range (2,21)] lwidChoices = ('0.5','0.7','1','1.5','2','2.5','3','4') sizebox = wx.BoxSizer(wx.HORIZONTAL) sizebox.Add(wx.StaticText(dlg,wx.ID_ANY,'Text size'),0,wx.ALL) w = G2G.G2ChoiceButton(dlg,txtChoices,None,None,plotOpt,'labelSize',RefreshPlot, size=(50,-1)) sizebox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) sizebox.Add((1,1),1,wx.EXPAND,1) sizebox.Add(wx.StaticText(dlg,wx.ID_ANY,' Obs type'),0,wx.ALL) w = G2G.G2ChoiceButton(dlg,symChoices,None,None,plotOpt,'markerSym',RefreshPlot, size=(40,-1)) sizebox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) sizebox.Add(wx.StaticText(dlg,wx.ID_ANY,' size'),0,wx.ALL) w = G2G.G2ChoiceButton(dlg,sizChoices,None,None,plotOpt,'markerSiz',RefreshPlot, size=(50,-1)) sizebox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) sizebox.Add(wx.StaticText(dlg,wx.ID_ANY,' width'),0,wx.ALL) w = G2G.G2ChoiceButton(dlg,lwidChoices,None,None,plotOpt,'markerWid',RefreshPlot, size=(50,-1)) sizebox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) sizebox.Add((1,1),1,wx.EXPAND,1) sizebox.Add(wx.StaticText(dlg,wx.ID_ANY,' Line widths'),0,wx.ALL) w = G2G.G2ChoiceButton(dlg,lwidChoices,None,None,plotOpt,'lineWid',RefreshPlot, size=(50,-1)) sizebox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) sizebox.Add((1,1),1,wx.EXPAND,1) sizebox.Add(wx.StaticText(dlg,wx.ID_ANY,' Tick size'),0,wx.ALL) w = G2G.G2ChoiceButton(dlg,sizChoices,None,None,plotOpt,'tickSiz',RefreshPlot, size=(50,-1)) sizebox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) sizebox.Add(wx.StaticText(dlg,wx.ID_ANY,' width'),0,wx.ALL) w = G2G.G2ChoiceButton(dlg,lwidChoices,None,None,plotOpt,'tickWid',RefreshPlot, size=(50,-1)) sizebox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) sizebox.Add((1,1),1,wx.EXPAND,1) helpinfo = '''---- Help on creating hard copy ---- Select options such as the size of text and colors for plot contents here. Tricks: * Use a color of pure white to remove an element from the plot (light gray is plotted) * LaTeX-like coding can be used for phase labels such as $\\rm FeO_2$ (for a subscript 2) or $\\gamma$-Ti for a Greek "gamma" Note that the dpi value is ignored for svg and pdf files, which are drawn with vector graphics (infinite resolution). Likewise, the agr and itx options create input files for programs Grace (QtGrace) and Igor Pro, that largely duplicate the displayed plot. Once read into Grace or Igor the graphs can then be customized. ''' if sys.platform == "win32": helpinfo += ''' Note that the OriginPro connection export requires Origin 2021 or later.''' hlp = G2G.HelpButton(dlg,helpinfo) sizebox.Add(hlp,0,wx.ALL) vbox.Add(sizebox,0,wx.ALL|wx.EXPAND) # table of colors and legend options cols = 1+len(plotOpt['lineList']) + len(plotWidgets['phaseList'] ) import wx.lib.scrolledpanel as wxscroll gpanel = wxscroll.ScrolledPanel(dlg,size=(600,105)) gsizer = wx.FlexGridSizer(cols=cols,hgap=2,vgap=2) gsizer.Add((-1,-1)) for lbl in plotOpt['lineList']: gsizer.Add(wx.StaticText(gpanel,wx.ID_ANY,lbl),0,wx.ALL) for lbl in plotWidgets['phaseList']: if lbl not in plotOpt['phaseLabels']: plotOpt['phaseLabels'][lbl] = lbl val = G2G.ValidatedTxtCtrl(gpanel,plotOpt['phaseLabels'],lbl,size=(110,-1), style=wx.TE_CENTRE,OnLeave=RefreshPlot) gsizer.Add(val,0,wx.ALL) gsizer.Add(wx.StaticText(gpanel,wx.ID_ANY,'Show'),0,wx.ALL) for lbl in list(plotOpt['lineList']) + list(plotWidgets['phaseList'] ): if lbl not in plotOpt['Show']: plotOpt['Show'][lbl] = True ch = G2G.G2CheckBox(gpanel,'',plotOpt['Show'],lbl,RefreshPlot) gsizer.Add(ch,0,wx.ALL|wx.ALIGN_CENTER) gsizer.Add(wx.StaticText(gpanel,wx.ID_ANY,'Include in legend'),0,wx.ALL) for lbl in list(plotOpt['lineList']) + list(plotWidgets['phaseList'] ): if lbl not in plotOpt['legend']: plotOpt['legend'][lbl] = False ch = G2G.G2CheckBox(gpanel,'',plotOpt['legend'],lbl,RefreshPlot) gsizer.Add(ch,0,wx.ALL|wx.ALIGN_CENTER) gsizer.Add(wx.StaticText(gpanel,wx.ID_ANY,'Color'),0,wx.ALL) plotWidgets['colorButtons'] = {} for lbl in list(plotOpt['lineList']) + list(plotWidgets['phaseList']): import wx.lib.colourselect as csel if lbl not in plotOpt['colors']: plotOpt['colors'][lbl] = (0.5, 0.5, 0.5, 1) color = wx.Colour(*[int(255*i) for i in plotOpt['colors'][lbl]]) b = csel.ColourSelect(gpanel, -1, '', color) b.Bind(csel.EVT_COLOURSELECT, OnSelectColour) plotWidgets['colorButtons'][b] = lbl gsizer.Add(b,0,wx.ALL|wx.ALIGN_CENTER) # plot limits vlbox = wx.BoxSizer(wx.VERTICAL) vlbox.Add(wx.StaticText(dlg,wx.ID_ANY,'plot limit overrides'),0,wx.TOP|wx.ALIGN_CENTER,5) limsizer = wx.FlexGridSizer(cols=3,hgap=2,vgap=2) for xy in 'x','y': for minmax in 'min','max': key = f'{xy}{minmax}' txt = wx.StaticText(dlg,wx.ID_ANY,key) limsizer.Add(txt,0,wx.ALIGN_RIGHT) ch = G2G.G2CheckBox(dlg,'',plotOpt,key+'_use',RefreshPlot) limsizer.Add(ch,0,wx.ALL|wx.ALIGN_CENTER) val = G2G.ValidatedTxtCtrl(dlg,plotOpt,key,size=(90,-1), style=wx.TE_CENTRE,OnLeave=RefreshPlot) limsizer.Add(val,0,wx.ALL) vlbox.Add(limsizer,0,wx.BOTTOM,5) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add((5,-1)) hbox.Add(vlbox,0,wx.ALL|wx.ALIGN_CENTER_VERTICAL) # assemble controls box hbox.Add((10,-1)) hbox.Add((-1,-1),1,wx.EXPAND,0) gpanel.SetSizer(gsizer) gpanel.SetAutoLayout(1) gpanel.SetupScrolling() hbox.Add(gpanel,0,wx.ALIGN_CENTER) hbox.Add((-1,-1),1,wx.EXPAND,0) vbox.Add(hbox,0,wx.ALL|wx.EXPAND) # hard copy options hbox = wx.BoxSizer(wx.HORIZONTAL) txt = wx.StaticText(dlg,wx.ID_ANY,'Hard copy ') hbox.Add(txt,0,wx.ALL|wx.ALIGN_CENTER_VERTICAL) txt = wx.StaticText(dlg,wx.ID_ANY,' Width (in):') hbox.Add(txt,0,wx.ALL|wx.ALIGN_CENTER_VERTICAL) val = G2G.ValidatedTxtCtrl(dlg,plotOpt,'width',xmin=3.,xmax=20.,nDig=(5,1),size=(45,-1)) hbox.Add(val,0,wx.ALL) txt = wx.StaticText(dlg,wx.ID_ANY,' Height (in):') hbox.Add(txt,0,wx.ALL|wx.ALIGN_CENTER_VERTICAL) val = G2G.ValidatedTxtCtrl(dlg,plotOpt,'height',xmin=3.,xmax=20.,nDig=(5,1),size=(45,-1)) hbox.Add(val,0,wx.ALL) txt = wx.StaticText(dlg,wx.ID_ANY,'File format:') hbox.Add(txt,0,wx.ALL|wx.ALIGN_CENTER_VERTICAL) fmtval = G2G.EnumSelector(dlg,plotOpt,'format',plotWidgets['fmtChoices']) hbox.Add(fmtval,0,wx.ALL) # TODO: would be nice to gray this out when format will ignore this ptxt = wx.StaticText(dlg,wx.ID_ANY,'Pixels/inch:') pval = G2G.ValidatedTxtCtrl(dlg,plotOpt,'dpi',xmin=60,xmax=1600, size=(40,-1)) hbox.Add(ptxt,0,wx.ALL|wx.ALIGN_CENTER_VERTICAL) hbox.Add(pval,0,wx.ALL) vbox.Add(hbox,0,wx.ALL|wx.ALIGN_CENTER) # screen preview figure = mplfig.Figure(figsize=(plotOpt['width'],plotOpt['height'])) canvas = Canvas(dlg,-1,figure) vbox.Add(canvas,1,wx.ALL|wx.EXPAND,1) # buttons at bottom btnsizer = wx.BoxSizer(wx.HORIZONTAL) btnsizer.Add((5,-1)) btn = wx.Button(dlg, wx.ID_ANY,'save\nsettings',size=(80,-1)) btn.Bind(wx.EVT_BUTTON,OnSavePlotOpt) btnsizer.Add(btn) btnsizer.Add((5,-1)) btn = wx.Button(dlg, wx.ID_ANY,'load\nsettings',size=(80,-1)) btn.Bind(wx.EVT_BUTTON,OnLoadPlotOpt) btnsizer.Add(btn) btnsizer.Add((-1,-1),1,wx.EXPAND) btn = wx.Button(dlg, wx.ID_CANCEL) btnsizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL) btnsizer.Add((5,-1)) btn = wx.Button(dlg, wx.ID_SAVE) btn.Bind(wx.EVT_BUTTON,onSave) btnsizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL) btnsizer.Add((-1,-1),1,wx.EXPAND) btnsizer.Add((170,-1),1,wx.EXPAND) # empty box to center buttons vbox.Add(btnsizer, 0, wx.EXPAND|wx.TOP|wx.BOTTOM,4) dlg.SetSizer(vbox) vbox.Fit(dlg) dlg.Layout() dlg.CenterOnParent() CopyRietveldPlot(G2frame,Pattern,Plot,Page,figure,plotWidgets['phaseList']) # preview plot figure.canvas.draw() if not reuse: dlg.ShowModal() dlg.Destroy() fmtval.SetFocus() # move focus off from pixel size return
[docs] def CopyRietveldPlot(G2frame,Pattern,Plot,Page,figure,phaseList): '''Copy the contents of the Rietveld graph from the plot window to another mpl figure which can be on screen or can be a file for hard copy. Uses values from Pattern to also generate a delta/sigma plot below the main figure, since the weights are not available from the plot. :param list Pattern: histogram object from data tree :param mpl.axes Plot: The axes object from the Rietveld plot :param wx.Panel Page: The tabbed panel for the Rietveld plot :param matplotlib.figure.Figure figure: The figure object from the Rietveld plot ''' # set up axes gs = mpl.gridspec.GridSpec(2, 1, height_ratios=[4, 1]) ax0 = figure.add_subplot(gs[0]) ax1 = figure.add_subplot(gs[1]) figure.subplots_adjust(left=int(plotOpt['labelSize'])/100.,bottom=int(plotOpt['labelSize'])/150., right=.98,top=1.-int(plotOpt['labelSize'])/200.,hspace=0.0) ax0.tick_params('x',direction='in',labelbottom=False) ax0.tick_params(labelsize=plotOpt['labelSize']) ax1.tick_params(labelsize=plotOpt['labelSize']) if mpl.__version__.split('.')[0] == '1': # deal with older matplotlib, which puts too many ticks ax1.yaxis.set_major_locator(mpl.ticker.MaxNLocator(nbins=2)) ax1.yaxis.set_minor_locator(mpl.ticker.MaxNLocator(nbins=4)) ax1.set_xlabel(Plot.get_xlabel(),fontsize=plotOpt['labelSize']) ax0.set_ylabel(Plot.get_ylabel(),fontsize=plotOpt['labelSize']) ax1.set_ylabel(r'$\Delta/\sigma$',fontsize=plotOpt['labelSize']) # set axes ranges, get ranges from display lims = {} lims['xmin'],lims['xmax'] = Plot.get_xlim() lims['ymin'],lims['ymax'] = Plot.get_ylim() for key in 'xmin','xmax','ymin','ymax': # apply limit overrides where use flag is set if plotOpt[key+'_use']: lims[key] = plotOpt[key] ax0.set_xlim((lims['xmin'],lims['xmax'])) ax1.set_xlim((lims['xmin'],lims['xmax'])) ax0.set_ylim((lims['ymin'],lims['ymax'])) legLbl = [] legLine = [] # get the obs/calc... & magnification lines and xfer them for i,l in enumerate(Plot.lines): lbl = l.get_label() if lbl[1:] in ('obs','calc','bkg','zero','diff'): lbl = lbl[1:] if 'magline' in lbl: # magnification lines ax0.axvline(l.get_data()[0][0],color='0.5',dashes=(1,1)) elif lbl in ('obs','calc','bkg','zero','diff'): # data segments if not plotOpt['Show'].get(lbl,True): continue marker = l.get_marker() lineWid = l.get_lw() siz = l.get_markersize() mew = l.get_mew() if lbl == 'obs': siz = float(plotOpt['markerSiz']) marker = plotOpt['markerSym'] mew = float(plotOpt['markerWid']) else: lineWid = float(plotOpt['lineWid']) c = plotOpt['colors'].get(lbl,mpl.colors.to_rgba(l.get_color())) if sum(c) == 4.0: continue if plotOpt['legend'].get(lbl): uselbl = lbl else: uselbl = '_'+lbl if lbl == 'zero': art = [ax0.axhline(0.,color=c, lw=lineWid,label=uselbl,ls=l.get_ls(), marker=marker,ms=siz,mew=mew)] else: art = ax0.plot(l.get_xdata(),l.get_ydata(),color=c, lw=lineWid,label=uselbl,ls=l.get_ls(), marker=marker,ms=siz,mew=mew, ) if plotOpt['legend'].get(lbl): legLbl.append(uselbl) legLine.append(art[0]) elif l in Page.tickDict.values(): # reflection tickmarks if not plotOpt['Show'].get(lbl,True): continue c = plotOpt['colors'].get(lbl,mpl.colors.to_rgba(l.get_color())) #siz = l.get_markersize() siz = float(plotOpt['tickSiz']) #mew = l.get_mew() mew = float(plotOpt['tickWid']) if sum(c) == 4.0: continue if not plotOpt['legend'].get(lbl): uselbl = '_'+lbl else: uselbl = plotOpt['phaseLabels'].get(lbl,lbl) art = ax0.plot(l.get_xdata(),l.get_ydata(),color=c, lw=l.get_lw(),ls=l.get_ls(),label=uselbl, marker=l.get_marker(),ms=siz,mew=mew, ) if plotOpt['legend'].get(lbl): legLbl.append(uselbl) legLine.append(art[0]) elif '_FLT_' in lbl: # full-length vertical reflection markers ph = lbl[5:] c = plotOpt['colors'][ph] ax0.axvline(l.get_xdata()[0],color=c,lw=float(plotOpt['tickWid'])) elif lbl in plotOpt['colors']: # draw phase partials if l.get_marker() != 'None': continue # ignore 2nd tickmark artist if not plotOpt['Show'].get(lbl,True): continue # remove partial w/tickmarks c = plotOpt['colors'][lbl] lineWid = float(plotOpt['lineWid']) # xfer the phase partials to the new plot, overriding the phase color and line width ax0.plot(l.get_xdata(),l.get_ydata(),color=c, linewidth=lineWid, linestyle=l.get_ls()) # elif GSASIIpath.GetConfigValue('debug'): # print('other line:',lbl) # copy text items: magnification labels and reflection markers for l in Plot.texts: lbl = l.get_label() if lbl[1:] in phaseList: # reflection markers if not plotOpt['Show'].get(lbl[1:],True): continue # remove marker w/tickmarks p = l.get_bbox_patch() props = {'facecolor':p.get_facecolor(), 'edgecolor':p.get_facecolor(), 'alpha':p.get_alpha(), 'boxstyle':p.get_boxstyle()} c = plotOpt['colors'][lbl[1:]] ax0.text(l.get_position()[0],l.get_position()[1], l.get_text(), fontsize=float(plotOpt['labelSize']), c=c, ha='center', va='top', bbox=props, rotation=l.get_rotation()) elif 'maglbl' in lbl: # label on a magnification region ax0.annotate(l.get_text(), xy=(l.get_position()), xycoords=l.xycoords, verticalalignment='bottom', horizontalalignment=l.get_horizontalalignment(), fontsize=float(plotOpt['labelSize'])) # elif GSASIIpath.GetConfigValue('debug'): # print('other text:',l.get_label()) # generate the (obs-calc)/sigma values and plot them wtFactor = Pattern[0]['wtFactor'] DZ = (Pattern[1][1]-Pattern[1][3])*np.sqrt(wtFactor*Pattern[1][2]) ax1.plot(savedX,DZ,color='k') # show the legend, if anything is in it (list legLine) if legLine: ax0.legend(legLine,legLbl,loc='best',prop={'size':plotOpt['labelSize']})
[docs] def changePlotSettings(G2frame,Plot): '''Code in development to allow changes to plot settings prior to export of plot with "floppy disk" button ''' def RefreshPlot(*args,**kwargs): '''Apply settings to the plot ''' Plot.figure.subplots_adjust(left=int(plotOpt['labelSize'])/100., bottom=int(plotOpt['labelSize'])/150., right=.98, top=1.-int(plotOpt['labelSize'])/200., hspace=0.0) for P in Plot.figure.axes: P.get_xaxis().get_label().set_fontsize(plotOpt['labelSize']) P.get_yaxis().get_label().set_fontsize(plotOpt['labelSize']) for l in P.get_xaxis().get_ticklabels(): l.set_fontsize(plotOpt['labelSize']) for l in P.get_yaxis().get_ticklabels(): l.set_fontsize(plotOpt['labelSize']) for l in P.lines: l.set_linewidth(float(plotOpt['lineWid'])) P.get_xaxis().set_tick_params(width=float(plotOpt['lineWid'])) P.get_yaxis().set_tick_params(width=float(plotOpt['lineWid'])) for l in P.spines.values(): l.set_linewidth(float(plotOpt['lineWid'])) if Page.plotStyle.get('title',True): Plot.set_title(plotOpt['title'],fontsize=plotOpt['labelSize']) for i,P in enumerate(Plot.figure.axes): if not P.get_visible(): continue if i == 0: lbl = '' else: lbl = str(i) P.get_xaxis().set_label_text(plotOpt['xtitle'+lbl]) P.get_yaxis().set_label_text(plotOpt['ytitle'+lbl]) Plot.figure.canvas.draw() txtChoices = [str(i) for i in range (8,26)] lwidChoices = ('0.5','0.7','1','1.5','2','2.5','3','4') dlg = wx.Dialog(G2frame.plotFrame,style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) vbox = wx.BoxSizer(wx.VERTICAL) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add(wx.StaticText(dlg,wx.ID_ANY,'Text size'),0,wx.ALL) w = G2G.G2ChoiceButton(dlg,txtChoices,None,None,plotOpt,'labelSize',RefreshPlot, size=(50,-1)) hbox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) vbox.Add(hbox,0,wx.ALL|wx.EXPAND) vbox.Add((1,5)) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add(wx.StaticText(dlg,wx.ID_ANY,' Line widths'),0,wx.ALL) w = G2G.G2ChoiceButton(dlg,lwidChoices,None,None,plotOpt,'lineWid',RefreshPlot, size=(50,-1)) hbox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) vbox.Add(hbox,0,wx.ALL|wx.EXPAND) vbox.Add((1,5)) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add(wx.StaticText(dlg,wx.ID_ANY,' Title'),0,wx.ALL) plotOpt['title'] = Plot.get_title() w = G2G.ValidatedTxtCtrl(dlg,plotOpt,'title',OnLeave=RefreshPlot, size=(200,-1),notBlank=False) hbox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) vbox.Add(hbox,0,wx.ALL|wx.EXPAND) for i,P in enumerate(Plot.figure.axes): if not P.get_visible(): continue if i == 0: lbl = '' else: lbl = str(i) vbox.Add((1,5)) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add(wx.StaticText(dlg,wx.ID_ANY,' x label '+lbl),0,wx.ALL) plotOpt['xtitle'+lbl] = P.get_xaxis().get_label_text() w = G2G.ValidatedTxtCtrl(dlg,plotOpt,'xtitle'+lbl,OnLeave=RefreshPlot, size=(200,-1),notBlank=False) hbox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) vbox.Add(hbox,0,wx.ALL|wx.EXPAND) vbox.Add((1,5)) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add(wx.StaticText(dlg,wx.ID_ANY,' y label '+lbl),0,wx.ALL) plotOpt['ytitle'+lbl] = P.get_yaxis().get_label_text() w = G2G.ValidatedTxtCtrl(dlg,plotOpt,'ytitle'+lbl,OnLeave=RefreshPlot, size=(200,-1),notBlank=False) hbox.Add(w,0,wx.ALL|wx.ALIGN_CENTER) vbox.Add(hbox,0,wx.ALL|wx.EXPAND) vbox.Add((1,10),1,wx.ALL|wx.EXPAND,1) hbox = wx.BoxSizer(wx.HORIZONTAL) OKbtn = wx.Button(dlg, wx.ID_OK) OKbtn.Bind(wx.EVT_BUTTON,lambda event:dlg.EndModal(wx.ID_OK)) hbox.Add((-1,-1),1,wx.ALL|wx.EXPAND,1) hbox.Add(OKbtn) hbox.Add((-1,-1),1,wx.ALL|wx.EXPAND,1) vbox.Add(hbox,1,wx.ALL|wx.EXPAND,1) dlg.SetSizer(vbox) vbox.Fit(dlg) #dlg.Show() RefreshPlot() dlg.ShowModal()
[docs] def uneqImgShow(figure,ax,Xlist,Ylist,cmap,vmin,vmax,Ylbls=[]): '''Plots a contour plot where point spacing varies within a dataset and where the X values may differ between histograms. Note that the length of Xlist and Ylist must be the same and will be the number of histograms to be plotted :param matplotlib.figure figure: The figure where the plot will be placed. :param matplotlib.axes ax: The axes where the plot will be made. :param list Xlist: A list of X values for each histogram. :param list Ylist: A list of intensities for each histogram. :param matplotlib.colormap cmap: The colormap used for shading intensities. :param float vmin: Minimum intensity. :param float vmax: float Maximum intensity. :param list Ylbls: Optional. Label to place on each histogram. The default is [] where the axes are labeled normally with the first histogram numbered starting at 0. ''' def midPoints(x): '''Return the pixel corners for a series of steps For the series [1,2,3,5] this will be [0.5,1.5,2.5,4,6] Note that n+1 points are returned for input of n points ''' return np.concatenate( [[1.5*x[0] - x[1]/2], (x[:-1]+x[1:])/2, [1.5*x[-1] - x[-2]/2]] ) lenX = len(Xlist) if lenX != len(Ylist): raise Exception("uneqImgShow error: unequal list lengths") figure.subplots_adjust(right=.85) #print('vmin,vmax',vmin,vmax) meshlist = [] for i,(X,Y) in enumerate(zip(Xlist,Ylist)): #print(i,'X',min(X),max(X),'Y',min(Y),max(Y)) meshlist.append( ax.pcolormesh(midPoints(X), [i-0.5,i+0.5], Y[np.newaxis,:], cmap=cmap,vmin=vmin,vmax=vmax)) # label y axis with provided labels if lenX == len(Ylbls): pos = np.arange(lenX) ax.set_yticks(pos,Ylbls) # add the colorbar ax1 = figure.add_axes([0.87, 0.1, 0.04, 0.8]) mpl.colorbar.ColorbarBase(ax1, cmap=cmap, norm=mpl.colors.Normalize(vmin,vmax))
# does not plot grid lines at present # if mpl.rcParams['axes.grid']
[docs] def initPartialOpts(phaseColors): '''Make sure that the options for display of partials are all defined ''' for p in phaseColors: partialOpts[p] = partialOpts.get(p,{}) partialOpts[p]['Show'] = partialOpts[p].get('Show',True) partialOpts[p]['color'] = partialOpts[p].get('color',phaseColors[p]) partialOpts[p]['width'] = partialOpts[p].get('width','1') partialOpts[p]['style'] = partialOpts[p].get('style',2) # index in ltypeChoices/ltypeMPLname (see below) partialOpts[p]['MPLstyle'] = partialOpts[p].get('MPLstyle','--')
[docs] def configPartialDisplay(G2frame,phaseColors,RefreshPlot): '''Select which phase are diplayed with phase partials and how they are displayed ''' def OnSelectColor(event): '''Respond to a change in color ''' p = event.GetEventObject().phase c = event.GetValue() # convert wx.Colour to mpl color string partialOpts[p]['color'] = mpl.colors.to_hex(np.array(c.Get())/255) phaseColors[p] = partialOpts[p]['color'] RefreshPlot() def StyleChange(*args): 'define MPL line style from line style index' for p in partialOpts: try: partialOpts[p]['MPLstyle'] = ltypeMPLname[partialOpts[p]['style']] except: partialOpts[p]['MPLstyle'] = '-' RefreshPlot() import wx.lib.colourselect as csel dlg = wx.Dialog(G2frame, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add( wx.StaticText(dlg,wx.ID_ANY,'Set options for display of Phase partials\n(profiles for each phase)', style=wx.ALIGN_CENTER), 0,wx.ALIGN_CENTER) mainSizer.Add((-1,10)) headers = ['phase name ',' Show ',' Color ','Width','Line type'] gsizer = wx.FlexGridSizer(cols=len(headers),hgap=2,vgap=2) for h in headers: txt = wx.StaticText(dlg,wx.ID_ANY,h) txt.Wrap(150) gsizer.Add(txt,0,wx.ALIGN_CENTER|wx.BOTTOM,6) initPartialOpts(phaseColors) for p in phaseColors: txt = wx.StaticText(dlg,wx.ID_ANY,p) txt.Wrap(150) gsizer.Add(txt,0,wx.ALIGN_LEFT) # ch = G2G.G2CheckBox(dlg,'',partialOpts[p],'Show',RefreshPlot) gsizer.Add(ch,0,wx.ALIGN_CENTER) # c = wx.Colour(mpl.colors.to_hex(partialOpts[p]['color'])) b = csel.ColourSelect(dlg, -1, '', c) b.phase = p b.Bind(csel.EVT_COLOURSELECT, OnSelectColor) gsizer.Add(b,0,wx.ALL|wx.ALIGN_CENTER,3) # lwidChoices = ('0.5','0.7','1','1.5','2','2.5','3','4') ch = G2G.G2ChoiceButton(dlg,lwidChoices,None,None, partialOpts[p],'width',RefreshPlot, size=(50,-1)) gsizer.Add(ch,0,wx.ALIGN_CENTER) # ltypeChoices = ('solid','dotted','dashed','dash-dot','dense dashed','dense dashdotted') ltypeMPLname = ('-', ':', '--', '-.', (0, (5, 1)), (0, (3, 1, 1, 1))) ch = G2G.G2ChoiceButton(dlg,ltypeChoices, partialOpts[p],'style', None,None,StyleChange) gsizer.Add(ch,0,wx.ALIGN_CENTER) mainSizer.Add(gsizer) # OK/Cancel buttons btnsizer = wx.StdDialogButtonSizer() OKbtn = wx.Button(dlg, wx.ID_OK) OKbtn.SetDefault() btnsizer.AddButton(OKbtn) btnsizer.Realize() mainSizer.Add(btnsizer,5,wx.TOP|wx.BOTTOM|wx.ALIGN_CENTER,0) mainSizer.Layout() dlg.SetSizer(mainSizer) mainSizer.Fit(dlg) dlg.ShowModal() StyleChange()