Source code for GSASIIplot

# -*- coding: utf-8 -*-
########### SVN repository information ###################
# $Date: 2021-08-29 14:13:06 +0000 (Sun, 29 Aug 2021) $
# $Author: vondreele $
# $Revision: 5018 $
# $URL: https://subversion.xray.aps.anl.gov/pyGSAS/trunk/GSASIIplot.py $
# $Id: GSASIIplot.py 5018 2021-08-29 14:13:06Z vondreele $
########### SVN repository information ###################
'''
*GSASIIplot: plotting routines*
===============================

This module performs all visualization using matplotlib and OpenGL graphics. The following plotting
routines are defined: 

============================  ===========================================================================
plotting routine               action        
============================  ===========================================================================
:func:`PlotPatterns`          Powder pattern plotting
:func:`PublishRietveldPlot`   Create publication-quality Rietveld plots from :func:`PlotPatterns` plot
:func:`PlotImage`             Plots of 2D detector images
:func:`PlotPeakWidths`        Plot instrument broadening terms as function of 2-theta/TOF
:func:`PlotCovariance`        Show covariance terms in 2D 
:func:`PlotStructure`         Crystal structure plotting with balls, sticks, lines,
                              ellipsoids, polyhedra and magnetic moments
:func:`PlotBeadModel`         Plots representation of protein shape from small angle scattering
:func:`Plot1DSngl`            1D stick plots of structure factors                              
:func:`PlotSngl`              Structure factor plotting
:func:`Plot3DSngl`            3D Structure factor plotting
:func:`PlotDeltSig`           Normal probability plot (powder or single crystal)
:func:`PlotISFG`              PDF analysis: displays I(Q), S(Q), F(Q) and G(r)
:func:`PlotCalib`             CW or TOF peak calibration
:func:`PlotXY`                Simple plot of xy data
:func:`PlotXYZ`               Simple contour plot of xyz data
:func:`PlotXYZvect`           Quiver Plot for 3D cartesian vectors
:func:`Plot3Dxyz`             Surface Plot for 3D vectors
:func:`PlotAAProb`            Protein "quality" plot 
:func:`PlotStrain`            Plot of strain data, used for diagnostic purposes
:func:`PlotSASDSizeDist`      Small angle scattering size distribution plot
:func:`PlotPowderLines`       Plot powder pattern as a stick plot (vertical lines)
:func:`PlotSizeStrainPO`      Plot 3D mustrain/size/preferred orientation figure
:func:`PlotTexture`           Pole figure, inverse pole figure plotting
:func:`ModulationPlot`        Plots modulation information
:func:`PlotTorsion`           Plots MC torsion angles
:func:`PlotRama`              Ramachandran of energetically allowed regions for dihedral
                              angles in protein
:func:`PlotSelectedSequence`  Plot one or more sets of values selected from the sequential
                              refinement table
:func:`PlotIntegration`       Rectified plot of 2D image after image integration with 2-theta and
                              azimuth as coordinates
:func:`PlotTRImage`           test plot routine
:func:`PlotRigidBody`         show rigid body structures as balls & sticks
:func:`PlotLayers`            show layer structures as balls & sticks
:func:`PlotFPAconvolutors`    plots the convolutors from Fundamental Parameters
============================  ===========================================================================

These plotting routines place their graphics in the GSAS-II Plot Window, which contains a
:class:`G2PlotNoteBook` tabbed panel allowing multiple plots to be viewed. Methods 
:meth:`G2PlotNoteBook.addMpl` (2-D matplotlib), 
:meth:`G2PlotNoteBook.add3D` (3-D matplotlib), and 
:meth:`G2PlotNoteBook.addOgl` (OpenGL) are used to
create tabbed plot objects to hold plots of the following classes:
:class:`G2PlotMpl` (2-D matplotlib), 
:class:`G2Plot3D` (3-D matplotlib), and 
:class:`G2PlotOgl` (OpenGL). Note that two :class:`G2PlotNoteBook` methods are
potentially used to determine how plot updates after a refinement are handled: 

============================================     ========================================================
class method                                      description
============================================     ========================================================
:meth:`G2PlotNoteBook.RegisterRedrawRoutine`      This specifies a function 
                                                  to redraw the plot after the data tree has been
                                                  reloaded. Be sure this updates data
                                                  objects with new values from the tree, when needed.
                                                  
:meth:`G2PlotNoteBook.SetNoDelete`                Use this to indicate that a plot does not need to be
                                                  updated after a refinement and should not be closed.
============================================     ========================================================

These two methods define the following attributes (variables) in the plot tab classes: 

======================    ===============     ============================================================
variable                   default             use
======================    ===============     ============================================================
replotFunction              None               Defines a routine to be called to update the plot 
                                               after a refinement (unless None). Use
                                               :meth:`G2PlotNoteBook.RegisterRedrawRoutine`
                                               to define this (and replotArgs & replotKwArgs). 
                                               Plotting functions that take significant time
                                               to complete should probably not use this.)
replotArgs                  []                 Defines the positional arguments to be supplied to
                                               the replotFunction function or method.
replotKwArgs                {}                 Defines the keyword arguments to be supplied to
                                               the replotFunction function or method. 
plotRequiresRedraw         True                If set to True, after a refinement, the plot will be
                                               closed (in :func:`GSASIIdataGUI.GSASII.CleanupOldPlots`)
                                               if it was not updated after the refinement. Set this to
                                               False using :meth:`G2PlotNoteBook.SetNoDelete`
                                               for plots that should not be deleted or do
                                               not change based on refinement results.
plotInvalid                 False              Used to track if a plot has been updated. Set to False
                                               in :meth:`G2PlotNoteBook.FindPlotTab` when a plot is
                                               drawn. After a refinement is completed, method
                                               :func:`GSASIIdataGUI.GSASII.ResetPlots` sets
                                               plotInvalid to False for all plots before any routines
                                               are called. 
======================    ===============     ============================================================

Note that the plot toolbar is customized with :class:`GSASIItoolbar` 
                                        
'''
from __future__ import division, print_function
import platform
import time
import copy
import math
import sys
import os.path
import numpy as np
import numpy.ma as ma
import numpy.linalg as nl
# Don't depend on wx/matplotlib/scipy for scriptable; or for Sphinx docs
try:
    import wx
    import wx.aui
    import wx.glcanvas
    import matplotlib as mpl
    if not mpl.get_backend():       #could be assigned by spyder debugger
        mpl.use('wxAgg')
    import matplotlib.collections as mplC
    import mpl_toolkits.mplot3d.axes3d as mp3d
    from scipy.ndimage.interpolation import map_coordinates
except (ImportError, ValueError):
    pass
import GSASIIpath
Clip_on = GSASIIpath.GetConfigValue('Clip_on',True)
GSASIIpath.SetVersionNumber("$Revision: 5018 $")
import GSASIIdataGUI as G2gd
import GSASIIimage as G2img
import GSASIIpwd as G2pwd
import GSASIIIO as G2IO
import GSASIIpwdGUI as G2pdG
import GSASIIimgGUI as G2imG
import GSASIIphsGUI as G2phG
import GSASIIlattice as G2lat
import GSASIIspc as G2spc
import GSASIImath as G2mth
import GSASIIctrlGUI as G2G
import GSASIIobj as G2obj
try:
    import pytexture as ptx
except ImportError:
    print('binary load error: pytexture not found')
#from  OpenGL.GL import *
import OpenGL.GL as GL
import OpenGL.GLU as GLU
import gltext
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_wxagg import NavigationToolbar2WxAgg as Toolbar
except ImportError:
    from matplotlib.backends.backend_wxagg import Toolbar as Toolbar # name changes in wx4.0.1
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
try:  # fails on doc build
    sq8ln2 = np.sqrt(8.0*np.log(2.0))
except TypeError:
    pass
if '2' in platform.python_version_tuple()[0]:
    GkDelta = unichr(0x0394)
    Gkrho = unichr(0x03C1)
    super2 = unichr(0xb2)
    Angstr = unichr(0x00c5)
    Pwrm1 = unichr(0x207b)+unichr(0x0b9)
else:
    GkDelta = chr(0x0394)
    Gkrho = chr(0x03C1)
    super2 = chr(0xb2)
    Angstr = chr(0x00c5)
    Pwrm1 = chr(0x207b)+chr(0x0b9)
# misc global vars
nxs = np.newaxis
plotDebug = False
timeDebug = GSASIIpath.GetConfigValue('Show_timing',False)
obsInCaption = True # include the observed, calc,... items in the plot caption (PlotPatterns)

#matplotlib 2.0.x dumbed down Paired to 16 colors - 
#   this restores the pre 2.0 Paired color map found in matplotlib._cm.py
try:
    _Old_Paired_data = {'blue': [(0.0, 0.89019608497619629,
        0.89019608497619629), (0.090909090909090912, 0.70588237047195435,
        0.70588237047195435), (0.18181818181818182, 0.54117649793624878,
        0.54117649793624878), (0.27272727272727271, 0.17254902422428131,
        0.17254902422428131), (0.36363636363636365, 0.60000002384185791,
        0.60000002384185791), (0.45454545454545453, 0.10980392247438431,
        0.10980392247438431), (0.54545454545454541, 0.43529412150382996,
        0.43529412150382996), (0.63636363636363635, 0.0, 0.0),
        (0.72727272727272729, 0.83921569585800171, 0.83921569585800171),
        (0.81818181818181823, 0.60392159223556519, 0.60392159223556519),
        (0.90909090909090906, 0.60000002384185791, 0.60000002384185791), (1.0,
        0.15686275064945221, 0.15686275064945221)],

        'green': [(0.0, 0.80784314870834351, 0.80784314870834351),
        (0.090909090909090912, 0.47058823704719543, 0.47058823704719543),
        (0.18181818181818182, 0.87450981140136719, 0.87450981140136719),
        (0.27272727272727271, 0.62745100259780884, 0.62745100259780884),
        (0.36363636363636365, 0.60392159223556519, 0.60392159223556519),
        (0.45454545454545453, 0.10196078568696976, 0.10196078568696976),
        (0.54545454545454541, 0.74901962280273438, 0.74901962280273438),
        (0.63636363636363635, 0.49803921580314636, 0.49803921580314636),
        (0.72727272727272729, 0.69803923368453979, 0.69803923368453979),
        (0.81818181818181823, 0.23921568691730499, 0.23921568691730499),
        (0.90909090909090906, 1.0, 1.0), (1.0, 0.3490196168422699,
        0.3490196168422699)],

        'red': [(0.0, 0.65098041296005249, 0.65098041296005249),
        (0.090909090909090912, 0.12156862765550613, 0.12156862765550613),
        (0.18181818181818182, 0.69803923368453979, 0.69803923368453979),
        (0.27272727272727271, 0.20000000298023224, 0.20000000298023224),
        (0.36363636363636365, 0.9843137264251709, 0.9843137264251709),
        (0.45454545454545453, 0.89019608497619629, 0.89019608497619629),
        (0.54545454545454541, 0.99215686321258545, 0.99215686321258545),
        (0.63636363636363635, 1.0, 1.0), (0.72727272727272729,
        0.7921568751335144, 0.7921568751335144), (0.81818181818181823,
        0.41568627953529358, 0.41568627953529358), (0.90909090909090906,
        1.0, 1.0), (1.0, 0.69411766529083252, 0.69411766529083252)]}
    #This can be done on request for other colors
    oldpaired = mpl.colors.LinearSegmentedColormap('Paired',_Old_Paired_data,N=256)
    mpl.cm.register_cmap(cmap=oldpaired,lut=256)   
    blue = [tuple(1.-np.array(item)) for item in _Old_Paired_data['blue']]
    blue.reverse()
    green = [tuple(1.-np.array(item)) for item in _Old_Paired_data['green']]
    green.reverse()
    red = [tuple(1.-np.array(item)) for item in _Old_Paired_data['red']]
    red.reverse()
    Old_Paired_data_r = {'blue':blue,'green':green,'red':red}
    oldpaired_r = mpl.colors.LinearSegmentedColormap('Paired_r',Old_Paired_data_r,N=256)
    mpl.cm.register_cmap(cmap=oldpaired_r,lut=256)   

except:  # causes error in Sphinx 
    pass
# options for publication-quality Rietveld plots
plotOpt = {}
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['phaseList']  = []
plotOpt['phaseLabels']  = {}
plotOpt['fmtChoices']  = {}
plotOpt['lineWid'] = '1'

[docs]def Write2csv(fil,dataItems,header=False): '''Write a line to a CSV file :param object fil: file object :param list dataItems: items to write as row in file :param bool header: True if all items should be written with quotes (default is False) ''' line = '' for item in dataItems: if line: line += ',' item = str(item) if header or ' ' in item: line += '"'+item+'"' else: line += item fil.write(line+'\n')
class _tabPlotWin(wx.Panel): 'Creates a basic tabbed plot window for GSAS-II graphics' def __init__(self,parent,id=-1,dpi=None,**kwargs): self.replotFunction = None self.replotArgs = [] self.replotKwArgs = {} self.plotInvalid = False # valid self.plotRequiresRedraw = True # delete plot if not updated wx.Panel.__init__(self,parent,id=id,**kwargs)
[docs]class G2PlotMpl(_tabPlotWin): 'Creates a Matplotlib 2-D plot in the GSAS-II graphics window' def __init__(self,parent,id=-1,dpi=None,publish=None,**kwargs): _tabPlotWin.__init__(self,parent,id=id,**kwargs) mpl.rcParams['legend.fontsize'] = 10 mpl.rcParams['axes.grid'] = False self.figure = mpl.figure.Figure(dpi=dpi,figsize=(5,6)) self.canvas = Canvas(self,-1,self.figure) self.toolbar = GSASIItoolbar(self.canvas,publish=publish) self.toolbar.Realize() self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False,'sqPlot':False,'logPlot':False,'exclude':False} sizer=wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas,1,wx.EXPAND) sizer.Add(self.toolbar,0,) self.SetSizer(sizer) def SetToolTipString(self,text): if 'phoenix' in wx.version(): return self.canvas.SetToolTip(text) else: return self.canvas.SetToolTipString(text) def ToolBarDraw(self): mplv = eval(mpl.__version__.replace('.',',')) if mplv[0] >= 3 and mplv[1] >= 3: self.toolbar.canvas.draw_idle() else: self.toolbar.draw()
[docs]class G2PlotOgl(_tabPlotWin): 'Creates an OpenGL plot in the GSAS-II graphics window' def __init__(self,parent,id=-1,dpi=None,**kwargs): self.figure = _tabPlotWin.__init__(self,parent,id=id,**kwargs) if 'win' in sys.platform: #Windows (& Mac) already double buffered self.canvas = wx.glcanvas.GLCanvas(self,-1,**kwargs) else: #fix from Jim Hester for X systems attribs = (wx.glcanvas.WX_GL_DOUBLEBUFFER,wx.glcanvas.WX_GL_DEPTH_SIZE,24) self.canvas = wx.glcanvas.GLCanvas(self,-1,attribList=attribs,**kwargs) GL.glEnable(GL.GL_NORMALIZE) # create GL context i,j= wx.__version__.split('.')[0:2] if int(i)+int(j)/10. > 2.8: self.context = wx.glcanvas.GLContext(self.canvas) self.canvas.SetCurrent(self.context) else: self.context = None self.camera = {} sizer=wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas,1,wx.EXPAND) self.SetSizer(sizer) def SetToolTipString(self,text): if 'phoenix' in wx.version(): self.canvas.SetToolTip(wx.ToolTip(text)) else: self.canvas.SetToolTipString(text)
[docs]class G2Plot3D(_tabPlotWin): 'Creates a 3D Matplotlib plot in the GSAS-II graphics window' def __init__(self,parent,id=-1,dpi=None,**kwargs): _tabPlotWin.__init__(self,parent,id=id,**kwargs) self.figure = mpl.figure.Figure(dpi=dpi,figsize=(6,6)) self.canvas = Canvas(self,-1,self.figure) self.toolbar = GSASIItoolbar(self.canvas,Arrows=False) self.toolbar.Realize() sizer=wx.BoxSizer(wx.VERTICAL) sizer.Add(self.canvas,1,wx.EXPAND) sizer.Add(self.toolbar,) self.SetSizer(sizer) def SetToolTipString(self,text): if 'phoenix' in wx.version(): self.canvas.SetToolTip(wx.ToolTip(text)) else: self.canvas.SetToolTipString(text) def ToolBarDraw(self): mplv = eval(mpl.__version__.replace('.',',')) if mplv[0] >= 3 and mplv[1] >= 3: self.toolbar.canvas.draw_idle() else: self.toolbar.draw()
# mplv = eval(mpl.__version__.replace('.',',')) # if mplv[0] >= 3 and mplv[1] >= 3: # self.toolbar.draw_idle() # else: # self.toolbar.draw()
[docs]class G2PlotNoteBook(wx.Panel): 'create a tabbed panel to hold a GSAS-II graphics window' def __init__(self,parent,id=-1,G2frame=None): wx.Panel.__init__(self,parent,id=id) #so one can't delete a plot page from tab!! self.nb = wx.aui.AuiNotebook(self, \ style=wx.aui.AUI_NB_DEFAULT_STYLE ^ wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB) sizer = wx.BoxSizer() sizer.Add(self.nb,1,wx.EXPAND) self.SetSizer(sizer) self.status = parent.CreateStatusBar() self.status.SetFieldsCount(2) self.status.firstLen = 150 self.status.SetStatusWidths([self.status.firstLen,-1]) self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.OnPageChanged) self.nb.Bind(wx.EVT_KEY_UP,self.OnNotebookKey) self.G2frame = G2frame self.MPLwarn = False self.plotList = [] # contains the tab label for each plot self.panelList = [] # contains the panel object for each plot #self.skipPageChange = False # set to True when no plot update is needed self.allowZoomReset = True # this indicates plot should be updated not initialized # (BHT: should this be in tabbed panel rather than here?) self.lastRaisedPlotTab = None
[docs] def OnNotebookKey(self,event): '''Called when a keystroke event gets picked up by the notebook window rather the child. This is not expected, but somehow it does sometimes on the Mac and perhaps Linux. Assume that the page associated with the currently displayed tab has a child, .canvas; give that child the focus and pass it the event. ''' try: Page = self.nb.GetPage(self.nb.GetSelection()) except: # occurs with no plot tabs event.Skip() return try: Page.canvas.SetFocus() wx.PostEvent(Page.canvas,event) except AttributeError: pass
[docs] def SetNoDelete(self,name): '''Indicate that a plot does not need to be redrawn ''' if name not in self.plotList: print('Error, in SetNoDelete plot not found: '+name) return page = self.panelList[self.plotList.index(name)] page.plotRequiresRedraw = False # plot should not be deleted even if not redrawn
[docs] def RegisterRedrawRoutine(self,name,routine=None,args=(),kwargs={}): '''Save information to determine how to redraw a plot :param str name: label on tab of plot :param Object routine: a function to be called :param args: a list of positional parameters for the function :param kwargs: a dict with keyword parameters for the function ''' if name not in self.plotList: print('Error, plot not found: '+name) return page = self.panelList[self.plotList.index(name)] page.replotFunction = routine page.replotArgs = args page.replotKWargs = kwargs
[docs] def GetTabIndex(self,label): '''Look up a tab label and return the index in the notebook (this appears to be independent to the order it is dragged to -- at least in Windows) as well as the associated wx.Panel An exception is raised if the label is not found ''' for i in range(self.nb.GetPageCount()): if label == self.nb.GetPageText(i): return i,self.nb.GetPage(i) else: raise ValueError('Plot not found')
# def RaiseLastPage(self,lastRaisedPlotTab,treeItemPlot): # '''Raises either the Last tab clicked on or what is drawn by the selected tree item # This is called after a refinement is completed by :meth:`GSASIIdataGUI.GSASII.ResetPlots` # ''' # plotNum = None # if lastRaisedPlotTab in self.plotList: # plotNum = self.plotList.index(lastRaisedPlotTab) # elif treeItemPlot in self.plotList: # plotNum = self.plotList.index(treeItemPlot) # if plotNum is not None: # wx.CallAfter(self.SetSelectionNoRefresh,plotNum)
[docs] def FindPlotTab(self,label,Type,newImage=True,publish=None): '''Open a plot tab for initial plotting, or raise the tab if it already exists Set a flag (Page.plotInvalid) that it has been redrawn Record the name of the this plot in self.lastRaisedPlotTab ''' limits = None Plot = None try: new = False plotNum,Page = self.GetTabIndex(label) if Type == 'mpl' or Type == '3d': Axes = Page.figure.get_axes() Plot = Page.figure.gca() #get previous plot limits = [Plot.get_xlim(),Plot.get_ylim()] # save previous limits if len(Axes)>1 and Plot.get_aspect() == 'auto': # aspect will be equal for image plotting if Axes[1].get_aspect() != 'auto': #not colorbars! limits[1] = Axes[0].get_ylim() else: limits[1] = Axes[1].get_ylim() if newImage: Page.figure.clf() Plot = Page.figure.gca() #get a fresh plot after clf() self.SetSelectionNoRefresh(plotNum) # raises plot tab except (ValueError,AttributeError): new = True if Type == 'mpl': Plot = self.addMpl(label,publish=publish).gca() elif Type == 'ogl': Plot = self.addOgl(label) elif Type == '3d': Plot = mp3d.Axes3D(self.add3D(label)) plotNum = self.plotList.index(label) Page = self.nb.GetPage(plotNum) self.SetSelectionNoRefresh(plotNum) # raises plot tab Page.plotInvalid = False # plot has just been drawn self.lastRaisedPlotTab = label self.RaisePageNoRefresh(Page) # Save the help name from the DataItem that has created the plot in Tabbed page object # so we can use it in self.OnHelp(). # Are there any cases where plot tabs are created that are not tied to Data Tree entries? # One example is GSASII.SumDialog, where a test plot is created. Are there others? try: Page.helpKey = self.G2frame.dataWindow.helpKey except AttributeError: Page.helpKey = 'Data tree' return new,plotNum,Page,Plot,limits
def _addPage(self,name,page): '''Add the newly created page to the notebook and associated lists. :param name: the label placed on the tab, which should be unique :param page: the wx.Frame for the matplotlib, openGL, etc. window ''' #self.skipPageChange = True if name in self.plotList: print('Warning: duplicate plot name! Name='+name) self.nb.AddPage(page,name) self.plotList.append(name) # used to lookup plot in self.panelList # Note that order in lists make not agree with actual tab order; use self.nb.GetPageText(i) # where (self=G2plotNB) for latter self.panelList.append(page) # panel object for plot self.lastRaisedPlotTab = name #page.plotInvalid = False # plot has just been drawn #page.plotRequiresRedraw = True # set to False if plot should be retained even if not refreshed #page.replotFunction = None # used to specify a routine to redraw the routine #page.replotArgs = [] #page.replotKWargs = {} #self.skipPageChange = False
[docs] def addMpl(self,name="",publish=None): 'Add a tabbed page with a matplotlib plot' page = G2PlotMpl(self.nb,publish=publish) self._addPage(name,page) return page.figure
[docs] def add3D(self,name=""): 'Add a tabbed page with a 3D plot' page = G2Plot3D(self.nb) self._addPage(name,page) mplv = eval(mpl.__version__.replace('.',',')) if mplv[0] == 3: if mplv == [3,0,3] or mplv[1] >= 3: pass elif not self.MPLwarn: # patch for bad MPL 3D self.MPLwarn = True G2G.G2MessageBox(self,'3D plots with Matplotlib 3.1.x and 3.2.x are distorted, use MPL 3.0.3 or 3.3. You have '+mpl.__version__, 'Avoid Matplotlib 3.1 & 3.2') return page.figure
[docs] def addOgl(self,name=""): 'Add a tabbed page with an openGL plot' page = G2PlotOgl(self.nb) self._addPage(name,page) self.RaisePageNoRefresh(page) # need to give window focus before GL use return page.figure
[docs] def Delete(self,name): 'delete a tabbed page' try: item = self.plotList.index(name) del self.plotList[item] del self.panelList[item] self.nb.DeletePage(item) except ValueError: #no plot of this name - do nothing return
[docs] def clear(self): 'clear all pages from plot window' for i in range(self.nb.GetPageCount()-1,-1,-1): self.nb.DeletePage(i) self.plotList = [] self.panelList = [] self.status.DestroyChildren() #get rid of special stuff on status bar
[docs] def Rename(self,oldName,newName): 'rename a tab' try: item = self.plotList.index(oldName) self.plotList[item] = newName self.nb.SetPageText(item,newName) except ValueError: #no plot of this name - do nothing return
[docs] def RaisePageNoRefresh(self,Page): 'Raises a plot tab without triggering a refresh via OnPageChanged' if plotDebug: print ('Raise'+str(self).split('0x')[1]) #self.skipPageChange = True Page.SetFocus()
#self.skipPageChange = False
[docs] def SetSelectionNoRefresh(self,plotNum): 'Raises a plot tab without triggering a refresh via OnPageChanged' if plotDebug: print ('Select'+str(self).split('0x')[1]) #self.skipPageChange = True self.nb.SetSelection(plotNum) # raises plot tab Page = self.G2frame.G2plotNB.nb.GetPage(plotNum) Page.SetFocus()
#self.skipPageChange = False
[docs] def OnPageChanged(self,event): '''respond to someone pressing a tab on the plot window. Called when a plot tab is clicked. on some platforms (Mac for sure) this is also called when a plot is created or selected with .SetSelection() or .SetFocus(). (removed) The self.skipPageChange is used variable is set to suppress repeated replotting. ''' tabLabel = event.GetEventObject().GetPageText(event.GetSelection()) self.lastRaisedPlotTab = tabLabel if plotDebug: print ('PageChanged, self='+str(self).split('0x')[1]+tabLabel) print ('event type=',event.GetEventType()) self.status.DestroyChildren() #get rid of special stuff on status bar self.status.SetStatusText('') # clear old status message self.status.SetStatusWidths([self.status.firstLen,-1])
[docs] def SetHelpButton(self,help): '''Adds a Help button to the status bar on plots. TODO: This has a problem with PlotPatterns where creation of the HelpButton causes the notebook tabs to be duplicated. A manual resize fixes that, but the SendSizeEvent has not worked. ''' hlp = G2G.HelpButton(self.status,helpIndex=help) rect = self.status.GetFieldRect(1) rect.x += rect.width - 20 rect.width = 20 rect.y += 1 hlp.SetRect(rect)
#wx.CallLater(100,self.TopLevelParent.SendSizeEvent)
[docs] def InvokeTreeItem(self,pid): '''This is called to select an item from the tree using the self.allowZoomReset flag to prevent a reset to the zoom of the plot (where implemented) ''' self.allowZoomReset = False if pid: self.G2frame.GPXtree.SelectItem(pid) self.allowZoomReset = True if plotDebug: print ('invoke'+str(self).split('0x')[1]+str(pid))
[docs]class GSASIItoolbar(Toolbar): 'Override the matplotlib toolbar so we can add more icons' def __init__(self,plotCanvas,publish=None,Arrows=True): '''Adds additional icons to toolbar''' self.arrows = {} # try to remove a button from the bar POS_CONFIG_SPLTS_BTN = 6 # position of button to remove self.plotCanvas = plotCanvas Toolbar.__init__(self,plotCanvas) self.updateActions = None # defines a call to be made as part of plot updates self.DeleteToolByPos(POS_CONFIG_SPLTS_BTN) self.parent = self.GetParent() self.AddToolBarTool('Key press','Select key press','key.ico',self.OnKey) self.AddToolBarTool('Help on','Show help on this plot','help.ico',self.OnHelp) # add arrow keys to control zooming if Arrows: for direc in ('left','right','up','down', 'Expand X','Shrink X','Expand Y','Shrink Y'): if ' ' in direc: sprfx = '' prfx = 'Zoom: ' else: sprfx = 'Shift ' prfx = 'Shift plot ' fil = ''.join([i[0].lower() for i in direc.split()]+['arrow.ico']) self.arrows[direc] = self.AddToolBarTool(sprfx+direc,prfx+direc,fil,self.OnArrow) if publish: self.AddToolBarTool('Publish plot','Create publishable version of plot','publish.ico',publish) def AddToolBarTool(self,label,title,filename,callback): G2path = os.path.split(os.path.abspath(__file__))[0] bmpFilename = os.path.normpath(os.path.join(G2path, filename)) if not os.path.exists(bmpFilename): print('Could not find bitmap file "%s"; skipping' % bmpFilename) bmp = wx.EmptyBitmap(32,32) else: bmp = wx.Bitmap(bmpFilename) if 'phoenix' in wx.version(): button = self.AddTool(wx.ID_ANY, label, bmp, title) else: button = self.AddSimpleTool(wx.ID_ANY, bmp, label, title) wx.EVT_TOOL.Bind(self, button.GetId(), button.GetId(), callback) return button.GetId() def _update_view(self): '''Overrides the post-buttonbar update action to invoke a redraw; needed for plot magnification ''' if self.updateActions: wx.CallAfter(*self.updateActions) Toolbar._update_view(self) def AnyActive(self): for Itool in range(self.GetToolsCount()): if self.GetToolState(self.GetToolByPos(Itool).GetId()): return True return False def GetActive(self): for Itool in range(self.GetToolsCount()): tool = self.GetToolByPos(Itool) if self.GetToolState(tool.GetId()): return tool.GetLabel() return None
[docs] def OnArrow(self,event): 'reposition limits to scan or zoom by button press' ax = self.plotCanvas.figure.get_axes()[0] xmin,xmax,ymin,ymax = ax.axis() #print xmin,xmax,ymin,ymax if event.Id == self.arrows['right']: delta = (xmax-xmin)/10. xmin -= delta xmax -= delta elif event.Id == self.arrows['left']: delta = (xmax-xmin)/10. xmin += delta xmax += delta elif event.Id == self.arrows['up']: delta = (ymax-ymin)/10. ymin -= delta ymax -= delta elif event.Id == self.arrows['down']: delta = (ymax-ymin)/10. ymin += delta ymax += delta elif event.Id == self.arrows['Expand X']: delta = (xmax-xmin)/10. xmin += delta xmax -= delta elif event.Id == self.arrows['Expand Y']: delta = (ymax-ymin)/10. ymin += delta ymax -= delta elif event.Id == self.arrows['Shrink X']: delta = (xmax-xmin)/10. xmin -= delta xmax += delta elif event.Id == self.arrows['Shrink Y']: delta = (ymax-ymin)/10. ymin -= delta ymax += delta else: # should not happen! if GSASIIpath.GetConfigValue('debug'): GSASIIpath.IPyBreak() self.parent.toolbar.push_current() #NB: self.parent.toolbar = self ax.axis((xmin,xmax,ymin,ymax)) #print xmin,xmax,ymin,ymax self.plotCanvas.figure.canvas.draw() self.parent.ToolBarDraw() # self.parent.toolbar.push_current() if self.updateActions: wx.CallAfter(*self.updateActions)
[docs] def OnHelp(self,event): 'Respond to press of help button on plot toolbar' bookmark = self.Parent.helpKey # get help category used to create plot #if GSASIIpath.GetConfigValue('debug'): print 'plot help: key=',bookmark G2G.ShowHelp(bookmark,self.TopLevelParent)
[docs] def OnKey(self,event): '''Provide user with list of keystrokes defined for plot as well as an alternate way to access the same functionality ''' parent = self.GetParent() if parent.Choice: # remove the 1st entry in list if key press if 'key press' in parent.Choice[0].lower(): choices = list(parent.Choice[1:]) else: choices = list(parent.Choice) dlg = wx.SingleChoiceDialog(parent,'Select a keyboard command', 'Key press list',choices) if dlg.ShowModal() == wx.ID_OK: sel = dlg.GetSelection() dlg.Destroy() event.key = choices[sel][0] if event.key != ' ': parent.keyPress(event) else: G2G.G2MessageBox(self.TopLevelParent, 'Use this command only from the keyboard', 'Key not in menu') return else: dlg.Destroy()
# these routines are not currently in use, but there are probably good # places in the graphics to disable the zoom/pan to release the mouse bind # # use as Page.canvas.toolbar.reset_zoompan() #
[docs] def get_zoompan(self): """Return "ZOOM" if Zoom is active, , "PAN" if Pan is active, or None if neither """ return self.GetActive()
[docs] def reset_zoompan(self): '''Turns off Zoom or Pan mode, if on. Ignored if neither is set ''' if self._active == 'ZOOM': self._active = None if self._idPress is not None: self._idPress = self.canvas.mpl_disconnect(self._idPress) self.mode = '' if self._idRelease is not None: self._idRelease = self.canvas.mpl_disconnect(self._idRelease) self.mode = '' self.canvas.widgetlock.release(self) if hasattr(self,'_NTB2_ZOOM'): self.ToggleTool(self._NTB2_ZOOM, False) elif hasattr(self,'wx_ids'): self.ToggleTool(self.wx_ids['Zoom'], False) else: print('Unable to reset Zoom button, please report this with matplotlib version') elif self._active == 'PAN': self._active = None if self._idPress is not None: self._idPress = self.canvas.mpl_disconnect(self._idPress) self.mode = '' if self._idRelease is not None: self._idRelease = self.canvas.mpl_disconnect(self._idRelease) self.mode = '' self.canvas.widgetlock.release(self) if hasattr(self,'_NTB2_PAN'): self.ToggleTool(self._NTB2_PAN, False) elif hasattr(self,'wx_ids'): self.ToggleTool(self.wx_ids['Pan'], False) else: print('Unable to reset Pan button, please report this with matplotlib version')
def SetCursor(page): mode = page.toolbar.GetActive() if mode == 'Pan': if 'phoenix' in wx.version(): page.canvas.Cursor = wx.Cursor(wx.CURSOR_SIZING) else: page.canvas.SetCursor(wx.StockCursor(wx.CURSOR_SIZING)) elif mode == 'Zoom': if 'phoenix' in wx.version(): page.canvas.Cursor = wx.Cursor(wx.CURSOR_MAGNIFIER) else: page.canvas.SetCursor(wx.StockCursor(wx.CURSOR_MAGNIFIER)) else: if 'phoenix' in wx.version(): page.canvas.Cursor = wx.Cursor(wx.CURSOR_CROSS) else: page.canvas.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
[docs]def PlotFPAconvolutors(G2frame,NISTpk): '''Plot the convolutions used for the current peak computed with :func:`GSASIIfpaGUI.doFPAcalc` ''' import NIST_profile as FP new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('FPA convolutors','mpl') Page.SetToolTipString('') cntr = NISTpk.twotheta_window_center_deg Plot.set_title('Peak convolution functions @ 2theta={:.3f}'.format(cntr)) Plot.set_xlabel(r'$\Delta 2\theta, deg$',fontsize=14) Plot.set_ylabel(r'Intensity (arbitrary)',fontsize=14) # refColors=['b','r','c','g','m','k'] refColors = ['xkcd:blue','xkcd:red','xkcd:green','xkcd:cyan','xkcd:magenta','xkcd:black', 'xkcd:pink','xkcd:brown','xkcd:teal','xkcd:orange','xkcd:grey','xkcd:violet',] ttmin = ttmax = 0 #GSASIIpath.IPyBreak() i = -1 for conv in NISTpk.convolvers: if 'smoother' in conv: continue if 'crystallite_size' in conv: continue f = NISTpk.convolver_funcs[conv]() if f is None: continue i += 1 FFT = FP.best_irfft(f) if f[1].real > 0: FFT = np.roll(FFT,int(len(FFT)/2.)) FFT /= FFT.max() ttArr = np.linspace(-NISTpk.twotheta_window_fullwidth_deg/2, NISTpk.twotheta_window_fullwidth_deg/2,len(FFT)) ttmin = min(ttmin,ttArr[np.argmax(FFT>.005)]) ttmax = max(ttmax,ttArr[::-1][np.argmax(FFT[::-1]>.005)]) color = refColors[i%len(refColors)] Plot.plot(ttArr,FFT,color,label=conv[5:]) legend = Plot.legend(loc='best') SetupLegendPick(legend,new) Page.toolbar.push_current() Plot.set_xlim((ttmin,ttmax)) Page.toolbar.push_current() Page.ToolBarDraw() Page.canvas.draw()
def SetupLegendPick(legend,new,delay=5): mplv = eval(mpl.__version__.replace('.',',')) legend.delay = delay*1000 # Hold time in ms for clear; 0 == forever for line in legend.get_lines(): if mplv[0] >= 3 and mplv[1] >= 3: line.set_pickradius(4) else: line.set_picker(4) # bug: legend items with single markers don't seem to respond to a "pick" #GSASIIpath.IPyBreak() for txt in legend.get_texts(): try: # as of MPL 3.3.2 this has not changed txt.set_picker(4) except AttributeError: txt.set_pickradius(4) if new: legend.figure.canvas.mpl_connect('pick_event',onLegendPick)
[docs]def onLegendPick(event): '''When a line in the legend is selected, find the matching line in the plot and then highlight it by adding/enlarging markers. Set up a timer to make a reset after delay selected in SetupLegendPick ''' def clearHighlight(event): if not canvas.timer: return l,lm,lms,lmw = canvas.timer.lineinfo l.set_marker(lm) l.set_markersize(lms) l.set_markeredgewidth(lmw) canvas.draw() canvas.timer = None canvas = event.artist.get_figure().canvas if not hasattr(canvas,'timer'): canvas.timer = None plot = event.artist.get_figure().get_axes()[0] if hasattr(plot.get_legend(),'delay'): delay = plot.get_legend().delay if canvas.timer: # clear previous highlight if delay > 0: canvas.timer.Stop() clearHighlight(None) #if delay <= 0: return # use this in place of return # so that the next selected item is automatically highlighted (except when delay is 0) return if event.artist in plot.get_legend().get_lines(): # is this an artist item in the legend? lbl = event.artist.get_label() elif event.artist in plot.get_legend().get_texts(): # is this a text item in the legend? lbl = event.artist.get_text() else: #GSASIIpath.IPyBreak() return for l in plot.get_lines(): if lbl == l.get_label(): canvas.timer = wx.Timer() canvas.timer.Bind(wx.EVT_TIMER, clearHighlight) #GSASIIpath.IPyBreak() canvas.timer.lineinfo = (l,l.get_marker(),l.get_markersize(),l.get_markeredgewidth()) # highlight the selected item if l.get_marker() == 'None': l.set_marker('o') else: l.set_markersize(2*l.get_markersize()) l.set_markeredgewidth(2*l.get_markeredgewidth()) canvas.draw() if delay > 0: canvas.timer.Start(delay,oneShot=True) break else: print('Warning: artist matching ',lbl,' not found')
[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(plotOpt['lineWid']) P.get_xaxis().set_tick_params(width=plotOpt['lineWid']) P.get_yaxis().set_tick_params(width=plotOpt['lineWid']) for l in P.spines.values(): l.set_linewidth(plotOpt['lineWid']) 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()
##### PlotSngl ################################################################
[docs]def PlotSngl(G2frame,newPlot=False,Data=None,hklRef=None,Title=''): '''Structure factor plotting package - displays zone of reflections as rings proportional to F, F**2, etc. as requested via matpltlib; plots are not geometrically correct ''' from matplotlib.patches import Circle global HKL,HKLF,HKLref HKLref = hklRef def OnSCKeyPress(event): i = zones.index(Data['Zone']) newPlot = False pwdrChoice = {'f':'Fo','s':'Fosq','u':'Unit Fc'} hklfChoice = {'1':'|DFsq|>sig','3':'|DFsq|>3sig','w':'|DFsq|/sig','f':'Fo','s':'Fosq','i':'Unit Fc'} if event.key == 'h': Data['Zone'] = '100' newPlot = True elif event.key == 'k': Data['Zone'] = '010' newPlot = True elif event.key == 'l': Data['Zone'] = '001' newPlot = True elif event.key == 'i': Data['Scale'] *= 1.1 elif event.key == 'd': Data['Scale'] /= 1.1 elif event.key in ['+','=']: Data['Layer'] = min(Data['Layer']+1,HKLmax[i]) elif event.key == '-': Data['Layer'] = max(Data['Layer']-1,HKLmin[i]) elif event.key == '0': Data['Layer'] = 0 Data['Scale'] = 1.0 elif event.key in hklfChoice and 'HKLF' in Name: Data['Type'] = hklfChoice[event.key] newPlot = True elif event.key in pwdrChoice and 'PWDR' in Name: Data['Type'] = pwdrChoice[event.key] newPlot = True PlotSngl(G2frame,newPlot,Data,HKLref,Title) def OnSCMotion(event): xpos = event.xdata if xpos: xpos = round(xpos) #avoid out of frame mouse position ypos = round(event.ydata) zpos = Data['Layer'] if '100' in Data['Zone']: HKLtxt = '(%d,%d,%d)'%(zpos,xpos,ypos) elif '010' in Data['Zone']: HKLtxt = '(%d,%d,%d)'%(xpos,zpos,ypos) elif '001' in Data['Zone']: HKLtxt = '(%d,%d,%d)'%(xpos,ypos,zpos) Page.SetToolTipString(HKLtxt) G2frame.G2plotNB.status.SetStatusText('HKL = '+HKLtxt,0) def OnSCPress(event): zpos = Data['Layer'] xpos = event.xdata if xpos: pos = int(round(event.xdata)),int(round(event.ydata)) if '100' in Data['Zone']: hkl = np.array([zpos,pos[0],pos[1]]) elif '010' in Data['Zone']: hkl = np.array([pos[0],zpos,pos[1]]) elif '001' in Data['Zone']: hkl = np.array([pos[0],pos[1],zpos]) h,k,l = hkl hklf = HKLF[np.where(np.all(HKL-hkl == [0,0,0],axis=1))] if len(hklf): Fosq,sig,Fcsq = hklf[0] HKLtxt = '( %.2f %.3f %.2f %.2f)'%(Fosq,sig,Fcsq,(Fosq-Fcsq)/(scale*sig)) G2frame.G2plotNB.status.SetStatusText('Fosq, sig, Fcsq, delFsq/sig = '+HKLtxt,1) def OnPick(event): pick = event.artist HKLtext = pick.get_gid() Page.SetToolTipString(HKLtext) G2frame.G2plotNB.status.SetStatusText('H = '+HKLtext,0) if not G2frame.PatternId: return Name = G2frame.GPXtree.GetItemText(G2frame.PatternId) if not Title: Title = Name new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('Structure Factors','mpl') if not new: if not newPlot: xylim = copy.copy(lim) else: Page.canvas.mpl_connect('button_press_event', OnSCPress) Page.canvas.mpl_connect('motion_notify_event', OnSCMotion) Page.canvas.mpl_connect('pick_event', OnPick) Page.canvas.mpl_connect('key_press_event', OnSCKeyPress) Page.keyPress = OnSCKeyPress Page.Choice = (' key press','i: increase scale','d: decrease scale', 'h: select 100 zone','k: select 010 zone','l: select 001 zone', 'f: select Fo','s: select Fosq','u: select unit Fc', '+: increase index','-: decrease index','0: zero layer',) if 'HKLF' in Name: Page.Choice += ('w: select |DFsq|/sig','1: select |DFsq|>sig','3: select |DFsq|>3sig',) try: Plot.set_aspect(aspect='equal') except: #broken in mpl 3.1.1; worked in mpl 3.0.3 pass Type = Data['Type'] scale = Data['Scale'] HKLmax = Data['HKLmax'] HKLmin = Data['HKLmin'] FosqMax = Data['FoMax'] Super = Data['Super'] SuperVec = [] if Super: SuperVec = np.array(Data['SuperVec'][0]) FoMax = math.sqrt(FosqMax) xlabel = ['k, h=','h, k=','h, l='] ylabel = ['l','l','k'] zones = ['100','010','001'] pzone = [[1,2],[0,2],[0,1]] izone = zones.index(Data['Zone']) Plot.set_title(Data['Type']+' for '+Title) HKL = [] HKLF = [] sumFo = 0. sumDF = 0. for refl in HKLref: H = refl[:3] if 'HKLF' in Name: Fosq,sig,Fcsq = refl[5+Super:8+Super] else: Fosq,sig,Fcsq = refl[8+Super],1.0,refl[9+Super] if Super: HKL.append(H+SuperVec*refl[3]) else: HKL.append(H) HKLF.append([Fosq,sig,Fcsq]) if H[izone] == Data['Layer']: A = 0 B = 0 if Type == 'Fosq': A = scale*Fosq/FosqMax sumFo += A B = scale*Fcsq/FosqMax C = abs(A-B) sumDF += C elif Type == 'Fo': A = scale*math.sqrt(max(0,Fosq))/FoMax sumFo += A B = scale*math.sqrt(max(0,Fcsq))/FoMax C = abs(A-B) sumDF += C elif Type == 'Unit Fc': A = scale/2 B = scale/2 C = 0.0 if Fcsq and Fosq > 0: A *= min(1.0,Fosq/Fcsq) C = abs(A-B) elif Type == '|DFsq|/sig': if sig > 0.: A = scale*(Fosq-Fcsq)/(3*sig) B = 0 elif Type == '|DFsq|>sig': if sig > 0.: A = scale*(Fosq-Fcsq)/(3*sig) if abs(A) < 1.0: A = 0 B = 0 elif Type == '|DFsq|>3sig': if sig > 0.: A = scale*(Fosq-Fcsq)/(3*sig) if abs(A) < 3.0: A = 0 B = 0 if Super: h = H+SuperVec*refl[3] if refl[3]: hid = '(%d,%d,%d,%d)'%(refl[0],refl[1],refl[2],refl[3]) else: hid = '(%d,%d,%d)'%(refl[0],refl[1],refl[2]) else: h = H hid = '(%d,%d,%d)'%(refl[0],refl[1],refl[2]) xy = (h[pzone[izone][0]],h[pzone[izone][1]]) if Type in ['|DFsq|/sig','|DFsq|>sig','|DFsq|>3sig']: if A > 0.0: Plot.add_artist(Circle(xy,radius=A,ec='g',fc='w', picker=True,gid=hid)) else: Plot.add_artist(Circle(xy,radius=-A,ec='r',fc='w', picker=True,gid=hid)) else: if A > 0.0 and A > B: Plot.add_artist(Circle(xy,radius=A,ec='g',fc='w')) if B: Plot.add_artist(Circle(xy,radius=B,ec='b',fc='w', picker=True,gid=hid)) if A < B: Plot.add_artist(Circle(xy,radius=A,ec='g',fc='w')) radius = C if radius > 0: if A > B: if refl[3+Super] < 0: Plot.add_artist(Circle(xy,radius=radius,ec=(0.,1,.0,.1),fc='g')) else: Plot.add_artist(Circle(xy,radius=radius,fc='g',ec='g')) else: if refl[3+Super] < 0: Plot.add_artist(Circle(xy,radius=radius,fc=(1.,0.,0.,.1),ec='r')) else: Plot.add_artist(Circle(xy,radius=radius,ec='r',fc='r')) # print 'plot time: %.3f'%(time.time()-time0) HKL = np.array(HKL) HKLF = np.array(HKLF) Plot.set_xlabel(xlabel[izone]+str(Data['Layer']),fontsize=12) Plot.set_ylabel(ylabel[izone],fontsize=12) if sumFo and sumDF: G2frame.G2plotNB.status.SetStatusText(xlabel[izone].split(',')[1]+str(Data['Layer'])+ \ ' layer R = %6.2f%s'%(100.*sumDF/sumFo,'%'),1) else: G2frame.G2plotNB.status.SetStatusText('Use K-box to set plot controls',1) if not newPlot: Page.toolbar.push_current() Plot.set_xlim(xylim[0]) Plot.set_ylim(xylim[1]) # xylim = [] Page.toolbar.push_current() Page.ToolBarDraw() else: Plot.set_xlim((HKLmin[pzone[izone][0]],HKLmax[pzone[izone][0]])) Plot.set_ylim((HKLmin[pzone[izone][1]],HKLmax[pzone[izone][1]])) Page.canvas.draw()
##### Plot1DSngl ################################################################################
[docs]def Plot1DSngl(G2frame,newPlot=False,hklRef=None,Super=0,Title=False): '''1D Structure factor plotting package - displays reflections as sticks proportional to F, F**2, etc. as requested ''' global xylim,X Name = G2frame.GPXtree.GetItemText(G2frame.PatternId) def OnKeyPress(event): if event.key == 'q': Page.qaxis = not Page.qaxis elif event.key == 'f': Page.faxis = not Page.faxis Draw() def OnMotion(event): global X xpos = event.xdata limx = Plot.get_xlim() if xpos: #avoid out of frame mouse position Xpos = xpos if Page.qaxis: dT = np.fabs(2.*np.pi/limx[0]-2.*np.pi/limx[1])/100. Xpos = 2.*np.pi/xpos found = hklRef[np.where(np.fabs(hklRef.T[4+Super]-Xpos) < dT/2.)] else: dT = np.fabs(limx[1]-limx[0])/100. found = hklRef[np.where(np.fabs(hklRef.T[4+Super]-xpos) < dT/2.)] s = '' if len(found): if Super: #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]) ypos = event.ydata SetCursor(Page) try: G2frame.G2plotNB.status.SetStatusText('d =%9.3f F^2 =%9.3f'%(Xpos,ypos),1) except TypeError: G2frame.G2plotNB.status.SetStatusText('Select '+Title+Name+' pattern first',1) Page.SetToolTipString(s) def Draw(): global xylim Plot.clear() Plot.set_title(Title) Plot.set_xlabel(r'd, '+Angstr,fontsize=14) Plot.set_ylabel(r'F'+super2,fontsize=14) colors=['b','r','g','c','m','k'] Page.keyPress = OnKeyPress if Page.qaxis: Plot.set_xlabel(r'q, '+Angstr+Pwrm1,fontsize=14) X = 2.*np.pi/hklRef.T[4+Super] else: X = hklRef.T[4+Super] if Page.faxis: Plot.set_ylabel(r'F',fontsize=14) Y = np.nan_to_num(np.sqrt(hklRef.T[8+Super])) Z = np.sqrt(hklRef.T[9+Super]) else: Y = hklRef.T[8+Super] Z = hklRef.T[9+Super] Ymax = np.max(Y) XY = np.vstack((X,X,np.zeros_like(X),Y)).reshape((2,2,-1)).T XZ = np.vstack((X,X,np.zeros_like(X),Z)).reshape((2,2,-1)).T XD = np.vstack((X,X,np.zeros_like(X)-Ymax/10.,Y-Z-Ymax/10.)).reshape((2,2,-1)).T lines = mplC.LineCollection(XY,color=colors[0]) Plot.add_collection(lines) lines = mplC.LineCollection(XZ,color=colors[1]) Plot.add_collection(lines) lines = mplC.LineCollection(XD,color=colors[2]) Plot.add_collection(lines) xylim = np.array([[np.min(X),np.max(X)],[np.min(Y-Z-Ymax/10.),np.max(np.concatenate((Y,Z)))]]) dxylim = np.array([xylim[0][1]-xylim[0][0],xylim[1][1]-xylim[1][0]])/20. xylim[0,0] -= dxylim[0] xylim[0,1] += dxylim[0] xylim[1,0] -= dxylim[1] xylim[1,1] += dxylim[1] Plot.set_xlim(xylim[0]) Plot.set_ylim(xylim[1]) if not newPlot: print('not newPlot') Page.toolbar.push_current() Plot.set_xlim(xylim[0]) Plot.set_ylim(xylim[1]) xylim = [] Page.toolbar.push_current() Page.ToolBarDraw() Page.canvas.draw() else: Page.canvas.draw() new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab(Title+Name,'mpl') Page.Offset = [0,0] if not new: if not newPlot: xylim = copy.copy(lim) else: newPlot = True Page.qaxis = False Page.faxis = False Page.canvas.mpl_connect('key_press_event', OnKeyPress) Page.canvas.mpl_connect('motion_notify_event', OnMotion) Page.Offset = [0,0] Page.Choice = (' key press','g: toggle grid','f: toggle Fhkl/F^2hkl plot','q: toggle q/d plot') Draw()
##### Plot3DSngl ################################################################################
[docs]def Plot3DSngl(G2frame,newPlot=False,Data=None,hklRef=None,Title=False): '''3D Structure factor plotting package - displays reflections as spots proportional to F, F**2, etc. as requested as 3D array via pyOpenGl ''' global ifBox ifBox = False def OnKeyBox(event): mode = cb.GetValue() if mode in ['jpeg','bmp','tiff',]: try: import Image as Im except ImportError: try: from PIL import Image as Im except ImportError: print ("PIL/pillow Image module not present. Cannot save images without this") raise Exception("PIL/pillow Image module not found") try: Fname = os.path.join(Mydir,generalData['Name']+'.'+mode) except NameError: #for when generalData doesn't exist! Fname = (os.path.join(Mydir,'unknown'+'.'+mode)).replace('*','+') print (Fname+' saved') size = Page.canvas.GetSize() GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1) Pix = GL.glReadPixels(0,0,size[0],size[1],GL.GL_RGB,GL.GL_UNSIGNED_BYTE) im = Im.new("RGB", (size[0],size[1])) try: im.frombytes(Pix) except AttributeError: im.fromstring(Pix) im = im.transpose(Im.FLIP_TOP_BOTTOM) im.save(Fname,mode) cb.SetValue(' save as/key:') G2frame.G2plotNB.status.SetStatusText('Drawing saved to: '+Fname,1) else: event.key = cb.GetValue()[0] cb.SetValue(' save as/key:') wx.CallAfter(OnKey,event) Page.canvas.SetFocus() # redirect the Focus from the button back to the plot def OnKey(event): #on key UP!! global ifBox Choice = {'F':'Fo','S':'Fosq','U':'Unit','D':'dFsq','W':'dFsq/sig'} viewChoice = {'L':np.array([[0,0,1],[1,0,0],[0,1,0]]),'K':np.array([[0,1,0],[0,0,1],[1,0,0]]),'H':np.array([[1,0,0],[0,0,1],[0,1,0]])} try: keyCode = event.GetKeyCode() if keyCode > 255: keyCode = 0 key = chr(keyCode) except AttributeError: #if from OnKeyBox above key = str(event.key).upper() if key in ['C','H','K','L']: if key == 'C': Data['Zone'] = False key = 'L' Data['viewKey'] = key drawingData['viewPoint'][0] = np.array(drawingData['default']) drawingData['viewDir'] = viewChoice[key][0] drawingData['viewUp'] = viewChoice[key][1] drawingData['oldxy'] = [] if Data['Zone']: if key == 'L': Q = [-1,0,0,0] else: V0 = viewChoice[key][0] V1 = viewChoice[key][1] V0 = np.inner(Amat,V0) V1 = np.inner(Amat,V1) V0 /= nl.norm(V0) V1 /= nl.norm(V1) A = np.arccos(np.sum(V1*V0)) Q = G2mth.AV2Q(-A,viewChoice[key][2]) G2frame.G2plotNB.status.SetStatusText('zone = %s'%(str(list(viewChoice[key][0]))),1) else: V0 = viewChoice[key][0] V = np.inner(Bmat,V0) V /= np.sqrt(np.sum(V**2)) V *= np.array([0,0,1]) A = np.arccos(np.sum(V*V0)) Q = G2mth.AV2Q(-A,viewChoice[key][2]) drawingData['Quaternion'] = Q elif key in 'O': drawingData['viewPoint'][0] = [0,0,0] elif key in 'Z': Data['Zone'] = not Data['Zone'] elif key in 'B': ifBox = not ifBox elif key in ['+','=']: Data['Scale'] *= 1.25 elif key == '-': Data['Scale'] /= 1.25 elif key == 'P': vec = viewChoice[Data['viewKey']][0] drawingData['viewPoint'][0] += vec elif key == 'M': vec = viewChoice[Data['viewKey']][0] drawingData['viewPoint'][0] -= vec elif key == '0': drawingData['viewPoint'][0] = np.array([0,0,0]) Data['Scale'] = 1.0 elif key == 'I': Data['Iscale'] = not Data['Iscale'] elif key in Choice: Data['Type'] = Choice[key] Draw('key') Name = G2frame.GPXtree.GetItemText(G2frame.PatternId) if Title and Title in G2frame.GetPhaseData(): #NB: save image as e.g. jpeg will fail if False; MyDir is unknown generalData = G2frame.GetPhaseData()[Title]['General'] cell = generalData['Cell'][1:7] Mydir = generalData['Mydir'] else: Title = 'Unknown' cell = [10,10,10,90,90,90] Mydir = G2frame.dirname drawingData = Data['Drawing'] Super = Data['Super'] SuperVec = [] if Super: SuperVec = np.array(Data['SuperVec'][0]) Amat,Bmat = G2lat.cell2AB(cell) #Amat - crystal to cartesian, Bmat - inverse Gmat,gmat = G2lat.cell2Gmat(cell) B4mat = np.concatenate((np.concatenate((Bmat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0) drawingData['Quaternion'] = G2mth.AV2Q(2*np.pi,np.inner(Bmat,[0,0,1])) Wt = np.array([255,255,255]) Rd = np.array([255,0,0]) Gr = np.array([0,255,0]) Bl = np.array([0,0,255]) uBox = np.array([[0,0,0],[1,0,0],[1,1,0],[0,1,0],[0,0,1],[1,0,1],[1,1,1],[0,1,1]]) uEdges = np.array([ [uBox[0],uBox[1]],[uBox[0],uBox[3]],[uBox[0],uBox[4]],[uBox[1],uBox[2]], [uBox[2],uBox[3]],[uBox[1],uBox[5]],[uBox[2],uBox[6]],[uBox[3],uBox[7]], [uBox[4],uBox[5]],[uBox[5],uBox[6]],[uBox[6],uBox[7]],[uBox[7],uBox[4]]]) uColors = [Rd,Gr,Bl, Wt,Wt,Wt, Wt,Wt,Wt, Wt,Wt,Wt] def FillHKLRC(): sumFo2 = 0. sumDF2 = 0. sumFo = 0. sumDF = 0. R = np.zeros(len(hklRef)) C = [] HKL = [] for i,refl in enumerate(hklRef): H = refl[:3] if 'HKLF' in Name: Fosq,sig,Fcsq = refl[5+Super:8+Super] if refl[3+Super] < 0: Fosq,sig,Fcsq = [0,1,0] else: Fosq,sig,Fcsq = refl[8+Super],1.0,refl[9+Super] sumFo2 += Fosq sumDF2 += abs(Fosq-Fcsq) if Fosq > 0.: sumFo += np.sqrt(Fosq) sumDF += abs(np.sqrt(Fosq)-np.sqrt(Fcsq)) if Super: HKL.append(H+SuperVec*refl[3]) else: HKL.append(H) if Data['Type'] == 'Unit': R[i] = 0.1 C.append(Gr) elif Data['Type'] == 'Fosq': if Fosq > 0: R[i] = Fosq C.append(Gr) else: R[i] = -Fosq C.append(Rd) elif Data['Type'] == 'Fo': if Fosq > 0: R[i] = np.sqrt(Fosq) C.append(Gr) else: R[i] = np.sqrt(-Fosq) C.append(Rd) elif Data['Type'] == 'dFsq/sig': dFsig = (Fosq-Fcsq)/sig if dFsig > 0: R[i] = dFsig C.append(Gr) else: R[i] = -dFsig C.append(Rd) elif Data['Type'] == 'dFsq': dF = Fosq-Fcsq if dF > 0: R[i] = dF C.append(Gr) else: R[i] = -dF C.append(Rd) R /= np.max(R) R *= Data['Scale'] R = np.where(R<1.e-5,1.e-5,R) if Data['Iscale']: R = np.where(R<=1.,R,1.) C = np.array(C) C = (C.T*R).T R = np.ones_like(R)*0.05 RF = 100. RF2 = 100. if sumFo and sumDF: RF = 100.*sumDF/sumFo RF2 = 100.*sumDF2/sumFo2 return HKL,zip(list(R),C),RF,RF2 def GetTruePosition(xy): View = GL.glGetIntegerv(GL.GL_VIEWPORT) Proj = GL.glGetDoublev(GL.GL_PROJECTION_MATRIX) Model = GL.glGetDoublev(GL.GL_MODELVIEW_MATRIX) Zmax = 1. xy = [int(xy[0]),int(View[3]-xy[1])] for i,ref in enumerate(hklRef): h,k,l = ref[:3] try: X,Y,Z = GLU.gluProject(h,k,l,Model,Proj,View) XY = [int(X),int(Y)] if np.allclose(xy,XY,atol=10) and Z < Zmax: Zmax = Z return [int(h),int(k),int(l)] except ValueError: return [int(h),int(k),int(l)] def SetTranslation(newxy): #first get translation vector in screen coords. oldxy = drawingData['oldxy'] if not len(oldxy): oldxy = list(newxy) dxy = newxy-oldxy drawingData['oldxy'] = list(newxy) V = np.array([-dxy[0],dxy[1],0.]) #then transform to rotated crystal coordinates & apply to view point Q = drawingData['Quaternion'] V = np.inner(Bmat,G2mth.prodQVQ(G2mth.invQ(Q),V)) Tx,Ty,Tz = drawingData['viewPoint'][0] Tx += V[0]*0.1 Ty += V[1]*0.1 Tz += V[2]*0.1 drawingData['viewPoint'][0] = np.array([Tx,Ty,Tz]) def SetRotation(newxy): 'Perform a rotation in x-y space due to a left-mouse drag' #first get rotation vector in screen coords. & angle increment oldxy = drawingData['oldxy'] if not len(oldxy): oldxy = list(newxy) dxy = newxy-oldxy if dxy[0] == dxy[1] == 0: return # on Mac motion can be less than a full pixel! drawingData['oldxy'] = list(newxy) V = np.array([dxy[1],dxy[0],0.]) A = 0.25*np.sqrt(dxy[0]**2+dxy[1]**2) if not A: return # nothing changed, nothing to do # next transform vector back to xtal coordinates via inverse quaternion # & make new quaternion Q = drawingData['Quaternion'] V = G2mth.prodQVQ(G2mth.invQ(Q),np.inner(Bmat,V)) DQ = G2mth.AVdeg2Q(A,V) Q = G2mth.prodQQ(Q,DQ) drawingData['Quaternion'] = Q # finally get new view vector - last row of rotation matrix VD = np.inner(Bmat,G2mth.Q2Mat(Q)[2]) VD /= np.sqrt(np.sum(VD**2)) drawingData['viewDir'] = VD def SetRotationZ(newxy): #first get rotation vector (= view vector) in screen coords. & angle increment View = GL.glGetIntegerv(GL.GL_VIEWPORT) cent = [View[2]/2,View[3]/2] oldxy = drawingData['oldxy'] if not len(oldxy): oldxy = list(newxy) dxy = newxy-oldxy if dxy[0] == dxy[1] == 0: return # on Mac motion can be less than a full pixel! drawingData['oldxy'] = list(newxy) V = drawingData['viewDir'] A = [0,0] A[0] = dxy[1]*.25 A[1] = dxy[0]*.25 if newxy[0] > cent[0]: A[0] *= -1 if newxy[1] < cent[1]: A[1] *= -1 # next transform vector back to xtal coordinates & make new quaternion Q = drawingData['Quaternion'] V = np.inner(Amat,V) Qx = G2mth.AVdeg2Q(A[0],V) Qy = G2mth.AVdeg2Q(A[1],V) Q = G2mth.prodQQ(Q,Qx) Q = G2mth.prodQQ(Q,Qy) drawingData['Quaternion'] = Q def OnMouseDown(event): xy = event.GetPosition() drawingData['oldxy'] = list(xy) def OnMouseMove(event): if event.ShiftDown(): #don't want any inadvertant moves when picking return newxy = event.GetPosition() if event.Dragging(): if event.LeftIsDown(): SetRotation(newxy) elif event.RightIsDown(): SetTranslation(newxy) Tx,Ty,Tz = drawingData['viewPoint'][0] elif event.MiddleIsDown(): SetRotationZ(newxy) Draw('move') else: hkl = GetTruePosition(newxy) if hkl: h,k,l = hkl Page.SetToolTipString('%d,%d,%d'%(h,k,l)) G2frame.G2plotNB.status.SetStatusText('hkl = %d,%d,%d'%(h,k,l),1) def OnMouseWheel(event): if event.ShiftDown(): return drawingData['cameraPos'] += event.GetWheelRotation()/120. drawingData['cameraPos'] = max(0.1,min(20.00,drawingData['cameraPos'])) Draw('wheel') def SetBackground(): R,G,B,A = Page.camera['backColor'] GL.glClearColor(R,G,B,A) GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) def SetLights(): GL.glEnable(GL.GL_DEPTH_TEST) # GL.glShadeModel(GL.GL_SMOOTH) GL.glEnable(GL.GL_LIGHTING) GL.glEnable(GL.GL_LIGHT0) GL.glLightModeli(GL.GL_LIGHT_MODEL_TWO_SIDE,0) GL.glLightfv(GL.GL_LIGHT0,GL.GL_AMBIENT,[1,1,1,1]) GL.glLightfv(GL.GL_LIGHT0,GL.GL_DIFFUSE,[1,1,1,1]) def RenderBox(x,y,z): GL.glEnable(GL.GL_COLOR_MATERIAL) GL.glLineWidth(1) GL.glPushMatrix() GL.glTranslate(x,y,z) GL.glColor4ubv([0,0,0,0]) GL.glBegin(GL.GL_LINES) for line,color in zip(uEdges,uColors): GL.glColor3ubv(color) GL.glVertex3fv(line[0]) GL.glVertex3fv(line[1]) GL.glEnd() GL.glPopMatrix() GL.glColor4ubv([0,0,0,0]) GL.glDisable(GL.GL_COLOR_MATERIAL) def RenderUnitVectors(x,y,z,labxyz=['','','']): GL.glEnable(GL.GL_COLOR_MATERIAL) GL.glLineWidth(1) GL.glPushMatrix() GL.glTranslate(x,y,z) GL.glBegin(GL.GL_LINES) for line,color in list(zip(uEdges,uColors))[:3]: GL.glColor3ubv(color) GL.glVertex3fv([0,0,0]) # GL.glVertex3fv(-line[1]) GL.glVertex3fv(line[1]) GL.glEnd() GL.glRotate(180,1,0,0) #fix to flip about x-axis for ix,txt in enumerate(labxyz): if txt: pos = uEdges[ix][1] GL.glTranslate(pos[0],-1.5*pos[1],-pos[2]) text = gltext.TextElement(text=txt,font=Font) text.draw_text(scale=0.05) GL.glTranslate(-pos[0],1.5*pos[1],pos[2]) GL.glPopMatrix() GL.glColor4ubv([0,0,0,0]) GL.glDisable(GL.GL_COLOR_MATERIAL) def RenderDots(XYZ,RC): GL.glEnable(GL.GL_COLOR_MATERIAL) XYZ = np.array(XYZ) GL.glPushMatrix() for xyz,rc in zip(XYZ,RC): x,y,z = xyz r,c = rc GL.glColor3ubv(c) GL.glPointSize(r*50) GL.glBegin(GL.GL_POINTS) GL.glVertex3fv(xyz) GL.glEnd() GL.glPopMatrix() GL.glColor4ubv([0,0,0,0]) GL.glDisable(GL.GL_COLOR_MATERIAL) def Draw(caller=''): #useful debug? # if caller: # print caller # end of useful debug VS = np.array(Page.canvas.GetSize()) aspect = float(VS[0])/float(VS[1]) cPos = drawingData['cameraPos'] Zclip = drawingData['Zclip']*cPos/20. if Data['Zone']: Zclip = 0.002 Q = drawingData['Quaternion'] Tx,Ty,Tz = drawingData['viewPoint'][0][:3] G,g = G2lat.cell2Gmat(cell) GS = G GS[0][1] = GS[1][0] = math.sqrt(GS[0][0]*GS[1][1]) GS[0][2] = GS[2][0] = math.sqrt(GS[0][0]*GS[2][2]) GS[1][2] = GS[2][1] = math.sqrt(GS[1][1]*GS[2][2]) HKL,RC,RF,RF2 = FillHKLRC() if Data['Zone']: G2frame.G2plotNB.status.SetStatusText \ ('Plot type = %s for %s; RF = %6.2f%%, RF%s = %6.2f%% layer %s'% \ (Data['Type'],Name,RF,super2,RF2,str(list(drawingData['viewPoint'][0]))),1) else: G2frame.G2plotNB.status.SetStatusText \ ('Plot type = %s for %s; RF = %6.2f%%, RF%s = %6.2f%%'%(Data['Type'],Name,RF,super2,RF2),1) SetBackground() GL.glInitNames() GL.glPushName(0) GL.glMatrixMode(GL.GL_PROJECTION) GL.glLoadIdentity() GL.glViewport(0,0,VS[0],VS[1]) GLU.gluPerspective(20.,aspect,cPos-Zclip,cPos+Zclip) GLU.gluLookAt(0,0,cPos,0,0,0,0,1,0) SetLights() GL.glMatrixMode(GL.GL_MODELVIEW) GL.glLoadIdentity() matRot = G2mth.Q2Mat(Q) matRot = np.concatenate((np.concatenate((matRot,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0) GL.glMultMatrixf(matRot.T) GL.glMultMatrixf(B4mat) GL.glTranslate(-Tx,-Ty,-Tz) x,y,z = drawingData['viewPoint'][0] if ifBox: RenderBox(x,y,z) else: RenderUnitVectors(x,y,z) RenderUnitVectors(0,0,0,labxyz=['h','k','l']) RenderDots(HKL,RC) try: if Page.context: Page.canvas.SetCurrent(Page.context) except: pass Page.canvas.SwapBuffers() # Plot3DSngl execution starts here (N.B. initialization above) new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('3D Structure Factors','ogl') if new: Page.views = False Font = Page.GetFont() Page.Choice = None choice = [' save as/key:','jpeg','tiff','bmp','h: view down h','k: view down k','l: view down l', 'z: zero zone toggle','p: increment layer','m: decrement layer','c: reset to default','o: set view point = 0,0,0','b: toggle box ','+: increase scale','-: decrease scale', 'f: Fobs','s: Fobs**2','u: unit','d: Fo-Fc','w: DF/sig','i: toggle intensity scaling'] cb = wx.ComboBox(G2frame.G2plotNB.status,style=wx.CB_DROPDOWN|wx.CB_READONLY,choices=choice, size=(G2frame.G2plotNB.status.firstLen,-1)) cb.Bind(wx.EVT_COMBOBOX, OnKeyBox) cb.SetValue(' save as/key:') Page.canvas.Bind(wx.EVT_MOUSEWHEEL, OnMouseWheel) Page.canvas.Bind(wx.EVT_LEFT_DOWN, OnMouseDown) Page.canvas.Bind(wx.EVT_RIGHT_DOWN, OnMouseDown) Page.canvas.Bind(wx.EVT_MIDDLE_DOWN, OnMouseDown) Page.canvas.Bind(wx.EVT_KEY_UP, OnKey) Page.canvas.Bind(wx.EVT_MOTION, OnMouseMove) # Page.canvas.Bind(wx.EVT_SIZE, OnSize) Page.camera['position'] = drawingData['cameraPos'] Page.camera['viewPoint'] = np.inner(Amat,drawingData['viewPoint'][0]) Page.camera['backColor'] = np.array(list(drawingData['backColor'])+[0,])/255. Page.controls = Data try: Page.canvas.SetCurrent() except: pass Draw('main')
# if firstCall: Draw('main') # draw twice the first time that graphics are displayed ##### 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 = [] PlotPatterns(G2frame,newPlot,plotType)
[docs]def PlotPatterns(G2frame,newPlot=False,plotType='PWDR',data=None, extraKeys=[],refineMode=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 plotting information 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 tool tip display of hkl for selected phase reflection list) ''' 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,newPlot=newPlot,plotType=plottype,extraKeys=extraKeys) PublishRietveldPlot(G2frame,Pattern,Plot,Page) G2frame.Weight = True PlotPatterns(G2frame,newPlot=newPlot,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': # and not Page.plotStyle['qPlot'] and not Page.plotStyle['dPlot']: #can't do weight plots when x-axis is different G2frame.Weight = not G2frame.Weight if not G2frame.Weight 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 == 'e' and plottype in ['SASD','REFD']: G2frame.ErrorBars = not G2frame.ErrorBars elif event.key == 'x'and 'PWDR' in plottype: Page.plotStyle['exclude'] = not Page.plotStyle['exclude'] 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 Ymax = max(Pattern[1][1]) if Page.plotStyle['sqrtPlot']: Page.plotStyle['delOffset'] = .02*np.sqrt(Ymax) Page.plotStyle['refOffset'] = -0.1*np.sqrt(Ymax) Page.plotStyle['refDelt'] = .1*np.sqrt(Ymax) else: Page.plotStyle['delOffset'] = .02*Ymax Page.plotStyle['refOffset'] = -0.1*Ymax Page.plotStyle['refDelt'] = .1*Ymax newPlot = True elif event.key == 'S' and 'PWDR' in plottype: choice = [m for m in mpl.cm.datad.keys()] # 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': 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 == 'o': if G2frame.SinglePlot and not G2frame.Contour: global obsInCaption # include the observed, calc,... items in the plot caption (PlotPatterns) obsInCaption = not obsInCaption elif not G2frame.SinglePlot: G2frame.Cmax = 1.0 G2frame.Cmin = 0.0 Page.plotStyle['Offset'] = [0,0] 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 if G2frame.Contour: Page.plotStyle['qPlot'] = False Page.plotStyle['dPlot'] = False 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 Plot.axvline(Page.startExclReg,color='b',dashes=(2,3)) Page.canvas.draw() Page.savedplot = Page.canvas.copy_from_bbox(Page.figure.gca().bbox) y1, y2= Page.figure.axes[0].get_ylim() Page.vLine = Plot.axvline(Page.startExclReg,color='b',dashes=(2,3)) Page.canvas.draw() else: Page.savedplot = None wx.CallAfter(PlotPatterns,G2frame,newPlot=False, plotType=plottype,extraKeys=extraKeys) if abs(Page.startExclReg - event.xdata) < 0.1: return LimitId = G2gd.GetGPXtreeItemId(G2frame,PatternId, 'Limits') data = G2frame.GPXtree.GetItemPyData(LimitId) mn = min(Page.startExclReg, event.xdata) mx = max(Page.startExclReg, event.xdata) data.append([mn,mx]) G2pdG.UpdateLimitsGrid(G2frame,data,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'] if Page.plotStyle['qPlot']: G2frame.Contour = False Page.plotStyle['dPlot'] = False elif plottype in ['SASD','REFD']: Page.plotStyle['sqPlot'] = not Page.plotStyle['sqPlot'] elif event.key == 't' and 'PWDR' in plottype and not ifLimits: if G2frame.Contour: G2frame.TforYaxis = not G2frame.TforYaxis else: Page.plotStyle['dPlot'] = not Page.plotStyle['dPlot'] if Page.plotStyle['dPlot']: G2frame.Contour = False Page.plotStyle['qPlot'] = False newPlot = True elif event.key == 'm': if not G2frame.Contour: G2frame.SinglePlot = not G2frame.SinglePlot 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 ['+','=']: G2frame.plusPlot = not G2frame.plusPlot 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 else: #print('no binding for key',event.key) return wx.CallAfter(PlotPatterns,G2frame,newPlot=newPlot,plotType=plottype,extraKeys=extraKeys) def OnMotion(event): 'Update the status line with info based on the mouse position' SetCursor(Page) # excluded region animation if Page.excludeMode and Page.savedplot: if event.xdata is None or G2frame.GPXtree.GetItemText( G2frame.GPXtree.GetSelection()) != 'Limits': # reset if out of bounds or not on limits Page.savedplot = None Page.excludeMode = False wx.CallAfter(PlotPatterns,G2frame,newPlot=False, plotType=plottype,extraKeys=extraKeys) return else: Page.canvas.restore_region(Page.savedplot) Page.vLine.set_xdata([event.xdata,event.xdata]) Page.figure.gca().draw_artist(Page.vLine) Page.canvas.blit(Page.figure.gca().bbox) return elif Page.excludeMode or Page.savedplot: # reset if out of mode somehow Page.savedplot = None Page.excludeMode = False wx.CallAfter(PlotPatterns,G2frame,newPlot=False, plotType=plottype,extraKeys=extraKeys) return if event.button and G2frame.Contour and G2frame.TforYaxis: ytics = imgAx.get_yticks() ytics = np.where(ytics<len(Temps),ytics,-1) 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 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 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 elif G2frame.Contour and 'T' in Parms['Type'][0]: xpos = X[int(xpos)] dsp = G2lat.Pos2dsp(Parms,xpos) q = 2.*np.pi/dsp else: dsp = G2lat.Pos2dsp(Parms,xpos) q = 2.*np.pi/dsp if G2frame.Contour: #PWDR only if 'T' in Parms['Type'][0]: G2frame.G2plotNB.status.SetStatusText('TOF =%9.3f d=%9.5f Q=%9.5f pattern ID =%5d'%(xpos,dsp,q,int(ypos)),1) else: G2frame.G2plotNB.status.SetStatusText('2-theta =%9.3f d=%9.5f Q= %9.5f pattern ID =%5d'%(xpos,dsp,q,int(ypos)),1) else: if 'T' in Parms['Type'][0]: if Page.plotStyle['sqrtPlot']: G2frame.G2plotNB.status.SetStatusText('TOF = %9.3f d=%9.5f Q=%9.5f sqrt(Intensity) =%9.2f'%(xpos,dsp,q,ypos),1) else: G2frame.G2plotNB.status.SetStatusText('TOF =%9.3f d=%9.5f Q=%9.5f Intensity =%9.2f'%(xpos,dsp,q,ypos),1) else: if 'PWDR' in plottype: if Page.plotStyle['sqrtPlot']: G2frame.G2plotNB.status.SetStatusText('2-theta =%9.3f d=%9.5f Q=%9.5f sqrt(Intensity) =%9.2f'%(xpos,dsp,q,ypos),1) else: G2frame.G2plotNB.status.SetStatusText('2-theta =%9.3f d=%9.5f Q=%9.5f Intensity =%9.2f'%(xpos,dsp,q,ypos),1) elif plottype == 'SASD': G2frame.G2plotNB.status.SetStatusText('q =%12.5g Intensity =%12.5g d =%9.1f'%(q,ypos,dsp),1) elif plottype == 'REFD': G2frame.G2plotNB.status.SetStatusText('q =%12.5g Reflectivity =%12.5g d =%9.1f'%(q,ypos,dsp),1) s = '' if G2frame.PickId: pickIdText = G2frame.GPXtree.GetItemText(G2frame.PickId) else: pickIdText = '?' # unexpected if pickIdText in ['Index Peak List','Unit Cells List','Reflection Lists'] 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 found = G2frame.HKL[np.where(np.fabs(G2frame.HKL.T[indx]-xpos) < dT/2.)] if len(found): 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): #ugh - this removes a matplotlib error for mouse clicks in log plots np.seterr(invalid='ignore') 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() OnPick(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() OnPick(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() OnPick(None) def onMovePeak(event): selectedPeaks = list(set([row for row,col in G2frame.phaseDisplay.GetSelectedCells()] + G2frame.phaseDisplay.GetSelectedRows())) if len(selectedPeaks) != 1: G2G.G2MessageBox(G2frame,'You must select one peak in the table first. # selected ='+ str(len(selectedPeaks)),'Select one peak') return G2frame.itemPicked = G2frame.Lines[selectedPeaks[0]+2] # 1st 2 lines are limits G2frame.G2plotNB.Parent.Raise() OnPick(None) def OnPick(event): '''Respond to an item being picked. This usually means that the item will be dragged with the mouse. ''' 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 Page.canvas.restore_region(savedplot) G2frame.itemPicked.set_data([event.xdata], [event.ydata]) Page.figure.gca().draw_artist(G2frame.itemPicked) Page.canvas.blit(Page.figure.gca().bbox) 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 Page.canvas.restore_region(savedplot) coords = G2frame.itemPicked.get_data() coords[0][0] = coords[0][1] = event.xdata coords = G2frame.itemPicked.set_data(coords) Page.figure.gca().draw_artist(G2frame.itemPicked) Page.canvas.blit(Page.figure.gca().bbox) 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 Page.canvas.restore_region(savedplot) 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]) Page.canvas.blit(axis.bbox) 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 Page.canvas.restore_region(savedplot) 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) Page.canvas.blit(Page.figure.gca().bbox) 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]) 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 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]) # if Page.plotStyle['sqrtPlot']: # xy[1] = xy[1]**2 PatternId = G2frame.PatternId PickId = G2frame.PickId if PickId and G2frame.GPXtree.GetItemText(PickId) == 'Peak List': 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) data['peaks'].append(XY) data['sigDict'] = {} #now invalid G2pdG.UpdatePeakGrid(G2frame,data) PlotPatterns(G2frame,plotType=plottype,extraKeys=extraKeys) 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) Page.figure.gca() Page.canvas.draw() # refresh without dotted line & save bitmap savedplot = Page.canvas.copy_from_bbox(Page.figure.gca().bbox) G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragLine) pick.set_linestyle('--') # back to dashed elif PickId and G2frame.GPXtree.GetItemText(PickId) in ['Limits','Unit Cells List']: if ind.all() != [0]: #picked a data point LimitId = G2gd.GetGPXtreeItemId(G2frame,PatternId, 'Limits') data = G2frame.GPXtree.GetItemPyData(LimitId) # likely that these conversions are not needed, Q & d not allowed on limits (BHT) 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]) if G2frame.ifGetExclude: excl = [0,0] excl[0] = max(data[1][0],min(xy[0],data[1][1])) excl[1] = excl[0]+0.1 data.append(excl) G2frame.ifGetExclude = False else: 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) G2pdG.UpdateLimitsGrid(G2frame,data,plottype) wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) else: #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) Page.figure.gca() Page.canvas.draw() # refresh without dotted line & save bitmap savedplot = Page.canvas.copy_from_bbox(Page.figure.gca().bbox) G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragLine) pick.set_linestyle('--') # back to dashed elif PickId and G2frame.GPXtree.GetItemText(PickId) == 'Models': if ind.all() != [0]: #picked a data point LimitId = G2gd.GetGPXtreeItemId(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 PickId and (G2frame.GPXtree.GetItemText(PickId) == 'Reflection Lists' or 'PWDR' in G2frame.GPXtree.GetItemText(PickId) ): G2frame.itemPicked = pick Page = G2frame.G2plotNB.nb.GetPage(plotNum) Page.figure.gca() if DifLine[0] is G2frame.itemPicked: # pick of difference curve Page.canvas.draw() # save bitmap savedplot = Page.canvas.copy_from_bbox(Page.figure.gca().bbox) 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 Page.canvas.draw() # refresh without dotted line & save bitmap savedplot = Page.canvas.copy_from_bbox(Page.figure.gca().bbox) 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?) 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 Page.canvas.draw() # refresh with dimmed tickmarks if G2frame.Weight: axis = Page.figure.axes[1] else: axis = Page.figure.gca() savedplot = Page.canvas.copy_from_bbox(axis.bbox) 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 PickId and G2frame.GPXtree.GetItemText(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 'Del' if pick.get_marker() == 'D': # find the closest point backDict = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Background'))[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) Page.figure.gca() Page.canvas.draw() # refresh with changed point & save bitmap savedplot = Page.canvas.copy_from_bbox(Page.figure.gca().bbox) G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragMarker) pick.set_marker('D') # put it back elif mode == 'Del': del backDict['FixedPoints'][G2frame.fixPtMarker] wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) return 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.cid is not None: # if there is a drag connection, delete it Page.canvas.mpl_disconnect(G2frame.cid) G2frame.cid = None 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 PickId = G2frame.PickId # points to item in tree if G2frame.GPXtree.GetItemText(PickId) == 'Background' and event.xdata: 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 # Background page, deal with fixed background points if G2frame.SubBack or G2frame.Weight or G2frame.Contour or not G2frame.SinglePlot: return backDict = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.PatternId, 'Background'))[1] 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 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) Plot = Page.figure.gca() Plot.plot(event.xdata,event.ydata,'rD',clip_on=Clip_on, picker=True,pickradius=3.) Page.canvas.draw() return elif G2frame.itemPicked is not None: # end of drag in move backDict['FixedPoints'][G2frame.fixPtMarker] = xy G2frame.itemPicked = None wx.CallAfter(PlotPatterns,G2frame,plotType=plottype,extraKeys=extraKeys) 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(PickId) ypos = event.ydata Page.plotStyle['delOffset'] = -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(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 if G2frame.GPXtree.GetItemText(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) if event.button == 3: del peaks['peaks'][lineNo-2-nxcl] else: if Page.plotStyle['qPlot']: peaks['peaks'][lineNo-2-nxcl][0] = G2lat.Dsp2pos(Parms,2.*np.pi/xpos) elif Page.plotStyle['dPlot']: peaks['peaks'][lineNo-2-nxcl][0] = G2lat.Dsp2pos(Parms,xpos) else: peaks['peaks'][lineNo-2-nxcl][0] = xpos peaks['sigDict'] = {} #no longer valid G2pdG.UpdatePeakGrid(G2frame,peaks) elif G2frame.GPXtree.GetItemText(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(PickId) == 'Reflection Lists' or 'PWDR' in G2frame.GPXtree.GetItemText(PickId)) and xpos: Id = G2gd.GetGPXtreeItemId(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() 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].SetValue(dbox[i].GetValue()) dbox[i].Enable(checked) def applyLims(event): Page.toolbar.push_current() 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() 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 #GSASIIpath.IPyBreak() 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 ''' 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, no PWDR item found!') return plotItem = histoList[0] else: plotItem = plottingItem if Page.plotStyle['dPlot'] or Page.plotStyle['qPlot']: print("Skipping plot, can't do this for d-plots or q-plots!") return 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) # 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) # 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 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]) lims = [min(DZ[Ibeg:Ifin]),max(DZ[Ibeg:Ifin])] if all(np.isfinite(lims)): Plot1.set_ylim(lims) 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['sqrtPlot']: Plot.set_title(r'$\sqrt{I}$ for '+Title) else: Plot.set_title(Title) Page.canvas.draw() def incCptn(string): '''Adds a underscore to "hide" a MPL object from the legend if obsInCaption is False ''' if obsInCaption: return string else: return '_'+string #### beginning PlotPatterns execution global exclLines,Page global DifLine # BHT: probably does not need to be global global Ymax global Pattern,mcolors,Plot,Page,imgAx,Temps plottype = plotType if not G2frame.PatternId: return if 'PKS' in plottype: 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) Page.excludeMode = False # True when defining an excluded region Page.savedplot = None #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':0.02*Ymax,'refOffset':-0.1*Ymax, 'refDelt':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'] = .02*np.sqrt(Ymax) Page.plotStyle['refOffset'] = -0.1*np.sqrt(Ymax) Page.plotStyle['refDelt'] = .1*np.sqrt(Ymax) else: Page.plotStyle['delOffset'] = .02*Ymax Page.plotStyle['refOffset'] = -0.1*Ymax Page.plotStyle['refDelt'] = .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, # 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 = True G2frame.SubBack = False Page.plotStyle['logPlot'] = False # is the selected histogram in the refinement? if not pick the 1st to show Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree() if plottingItem not in Histograms: histoList = [i for i in Histograms.keys() if i.startswith('PWDR ')] if len(histoList) != 0: plottingItem = histoList[0] Id = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, plottingItem) G2frame.GPXtree.SelectItem(Id) wx.CallAfter(PlotPatterns,G2frame,newPlot,plotType,None, extraKeys,refineMode) return #===================================================================================== if not new: G2frame.xylim = copy.copy(limits) else: 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', OnPick) 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 for b in Page.bindings: Page.canvas.mpl_disconnect(b) 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() refColors=['b','r','c','g','m','k'] Page.phaseColors = {p:refColors[i%len(refColors)] for i,p in enumerate(Phases)} 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.dataWindow.moveDiffCurve.Enable(False) G2frame.dataWindow.moveTickLoc.Enable(False) G2frame.dataWindow.moveTickSpc.Enable(False) elif G2frame.GPXtree.GetItemText(G2frame.PickId) == 'Peak List': G2frame.Bind(wx.EVT_MENU, onMovePeak, id=G2frame.dataWindow.movePeak.GetId()) # 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) 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 # TODO: figure out why the SetHelpButton creates a second tab line (BHT, Mac, wx4.1) #G2frame.G2plotNB.SetHelpButton(G2frame.dataWindow.helpKey) Page.tickDict = {} DifLine = [''] PickId = G2frame.PickId PatternId = G2frame.PatternId ifLimits = False if G2frame.GPXtree.GetItemText(PickId) == 'Limits': ifLimits = True Page.plotStyle['qPlot'] = False Page.plotStyle['dPlot'] = False 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','t: temperature for y-axis','s: toggle sqrt plot') else: if 'PWDR' in plottype: Page.Choice = [' key press', 'a: add magnification region','b: toggle subtract background', 'c: contour on','x: toggle excluded regions','g: toggle grid', 'm: toggle multidata plot','n: toggle log(I)',] if obsInCaption: Page.Choice += ['o: remove obs, calc,... from legend',] else: Page.Choice += ['o: add obs, calc,... to legend',] if ifLimits: Page.Choice += ['e: create excluded region', 's: toggle sqrt plot','w: toggle (Io-Ic)/sig plot', '+: no selection'] else: Page.Choice += ['q: toggle q plot','s: toggle sqrt plot', 't: toggle d-spacing plot','w: toggle (Io-Ic)/sig plot', '+: no selection'] 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'] 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 selection',] 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 try: colors = GSASIIpath.GetConfigValue('Plot_Colors').split() for color in colors: if color not in ['k','r','g','b','m','c']: print('**** bad color code: '+str(color)+' - redo Preferences/Plot Colors ****') raise Exception except: colors = ['b','g','r','c','m','k'] Lines = [] exclLines = [] time0 = time.time() if G2frame.SinglePlot and PatternId: try: Pattern = G2frame.GPXtree.GetItemPyData(PatternId) Pattern.append(G2frame.GPXtree.GetItemText(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 = data[0].get('histTitle') if not Title: Title = Pattern[-1] 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':0.02*Ymax,'refOffset':-0.1*Ymax,'refDelt':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 Ymax = None for ip,Pattern in enumerate(PlotList): xye = Pattern[1] xye = np.nan_to_num(xye) 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 G2frame.Contour and not ifLimits: xLabel = r'$d, \AA$' else: if 'T' in ParmList[0]['Type'][0]: if G2frame.Contour: xLabel = r'Channel no.' else: xLabel = r'$TOF, \mathsf{\mu}$s' else: xLabel = r'$\mathsf{2\theta}$' if G2frame.Weight: 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 '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') 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 = [] Nseq = 0 Nmax = len(PlotList)-1 time0 = time.time() 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 xye0 = Pattern[1][0] # keeps mask if PickId: ifpicked = Pattern[2] == G2frame.GPXtree.GetItemText(PatternId) # recompute mask from excluded regions, in case they have changed excls = limits[2:] for excl in excls: xye0 = ma.masked_inside(xye[0],excl[0],excl[1],copy=False) #excluded region mask if not G2frame.Contour: xye0 = ma.masked_outside(xye[0],limits[1][0],limits[1][1],copy=False) #now mask for limits if Page.plotStyle['qPlot'] and 'PWDR' in plottype and not ifLimits: X = 2.*np.pi/G2lat.Pos2dsp(Parms,xye0) elif Page.plotStyle['dPlot'] and 'PWDR' in plottype and not ifLimits: X = G2lat.Pos2dsp(Parms,xye0) else: X = copy.deepcopy(xye0) 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 \ ('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 magMarkers.append(Plot.axvline(x,color='0.5',dashes=(1,1), picker=True,pickradius=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: 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) Lines.append(Plot.axvline(lims[0][0],color='g',dashes=(5,5), picker=True,pickradius=3.)) Lines.append(Plot.axvline(lims[0][1],color='r',dashes=(5,5), picker=True,pickradius=3.)) for i,item in enumerate(lims[1:]): Lines.append(Plot.axvline(item[0],color='m',dashes=(5,5), picker=True,pickradius=3.)) Lines.append(Plot.axvline(item[1],color='m',dashes=(5,5), picker=True,pickradius=3.)) exclLines += [2*i+2,2*i+3] if G2frame.Contour: if lenX == len(X): ContourY.append(N) if G2frame.SubBack: ContourZ.append(Y) else: ContourZ.append(Y) if 'C' in ParmList[0]['Type'][0]: ContourX = X else: #'T'OF ContourX = range(lenX) Nseq += 1 if G2frame.TforYaxis: Plot.set_ylabel('Temperature',fontsize=14) else: Plot.set_ylabel('Data sequence',fontsize=14) else: if G2frame.plusPlot: pP = '+' else: pP = '' 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(PickId) == 'Limits': Xum = ma.getdata(X) # unmasked version of X, use for data limits (only) else: Xum = X[:] if ifpicked: 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",nonposy='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 Page.plotStyle['exclude'] and len(limits[2:]): Emask = ma.getmask(X) excls = limits[2:] for excl in excls: Emask += ma.getmask(ma.masked_inside(xye[0],excl[0],excl[1],copy=False)) 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) 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 Page.plotStyle['exclude']: DZ = ma.array(DZ,mask=Emask) DifLine = Plot1.plot(X,DZ,colors[3], picker=True,pickradius=1.,label=incCptn('diff')) #(Io-Ic)/sig(Io) 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",nonposy='mask') Plot.plot(X,Y,marker=pP,color=colors[0], picker=True,pickradius=3.,clip_on=Clip_on,label=incCptn('obs')) if G2frame.SinglePlot or G2frame.plusPlot: Plot.plot(X,Z,colors[1],picker=False,label=incCptn('calc')) if G2frame.plusPlot: Plot.plot(X,W,colors[2],picker=False,label=incCptn('bkg')) #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",nonposx='mask') Plot.set_yscale("log",nonposy='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=colors[0], picker=True,pickradius=3.,clip_on=Clip_on) else: Plot.errorbar(X,YB,yerr=Sample['Scale'][0]*np.sqrt(1./(Pattern[0]['wtFactor']*xye[2])), ecolor=colors[0], picker=True,pickradius=3.,clip_on=Clip_on,label=incCptn('obs')) else: Plot.plot(X,YB,marker=pP,color=colors[0], picker=True,pickradius=3.,clip_on=Clip_on,label=incCptn('obs')) Plot.plot(X,W,colors[1],picker=False,label=incCptn('bkg')) #const. background Plot.plot(X,ZB,colors[2],picker=False,label=incCptn('calc')) 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=colors[0],marker=pP, picker=False,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,colors[1],picker=False,label=incCptn('calc-bkg')) #Ic-Ib else: Plot.plot(X,YB,color=colors[0],marker=pP, picker=True,pickradius=3.,clip_on=Clip_on,label=incCptn('obs')) Plot.plot(X,ZB,colors[2],picker=False,label=incCptn('calc')) else: if 'PWDR' in plottype: ObsLine = Plot.plot(Xum,Y/ymax,color=colors[0],marker=pP, picker=True,pickradius=3.,clip_on=Clip_on,label=incCptn('obs')) #Io CalcLine = Plot.plot(X,Z/ymax,colors[1],picker=False,label=incCptn('calc')) #Ic else: Plot.plot(X,YB,color=colors[0],marker=pP, picker=True,pickradius=3.,clip_on=Clip_on,label=incCptn('obs')) Plot.plot(X,ZB,colors[2],picker=False,label=incCptn('calc')) if 'PWDR' in plottype and (G2frame.SinglePlot and G2frame.plusPlot): BackLine = Plot.plot(X,W/ymax,colors[2],picker=False,label=incCptn('bkg')) #Ib if not G2frame.Weight and np.any(Z): DifLine = Plot.plot(X,D/ymax,colors[3], picker=True,pickradius=1.,label=incCptn('diff')) #Io-Ic Plot.axhline(0.,color='k',label='_zero') Page.SetToolTipString('') if PickId: if G2frame.GPXtree.GetItemText(PickId) == 'Peak List': tip = 'On data point: Pick peak - L or R MB. On line: L-move, R-delete' Page.SetToolTipString(tip) data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,PatternId, 'Peak List')) 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(data['peaks']): if i in selectedPeaks: Ni = N+1 else: Ni = N if Page.plotStyle['qPlot']: Lines.append(Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,item[0]),color='b', picker=True,pickradius=2.)) elif Page.plotStyle['dPlot']: Lines.append(Plot.axvline(G2lat.Pos2dsp(Parms,item[0]),color='b', picker=True,pickradius=2.)) else: Lines.append(Plot.axvline(item[0],color='b', picker=True,pickradius=2.)) if Ni == N+1: Lines[-1].set_lw(Lines[-1].get_lw()+1) if G2frame.GPXtree.GetItemText(PickId) == 'Limits': tip = 'On data point: Lower limit - L MB; Upper limit - R MB. On limit: MB down to move' Page.SetToolTipString(tip) data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,PatternId, 'Limits')) 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: Plot.semilogy(X,Y,color=mcolors.cmap(icolor), # >=3.3 picker=False,nonpositive='mask') except: Plot.semilogy(X,Y,color=mcolors.cmap(icolor), picker=False,nonposy='mask') elif plottype in ['SASD','REFD']: try: Plot.semilogy(X,Y,color=mcolors.cmap(icolor), picker=False,nonpositive='mask') except: Plot.semilogy(X,Y,color=mcolors.cmap(icolor), picker=False,nonposy='mask') else: if 'PWDR' in plottype: Plot.plot(X,Y/ymax,color=mcolors.cmap(icolor),picker=False) elif plottype in ['SASD','REFD']: try: Plot.loglog(X,Y,mcolors.cmap(icolor), picker=False,nonpositive='mask') except: Plot.loglog(X,Y,mcolors.cmap(icolor), picker=False,nonposy='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.) if timeDebug: print('plot fill time: %.3f'%(time.time()-time0)) if not magLineList: Plot.set_title(Title) if PickId and not G2frame.Contour: Parms,Parms2 = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,PatternId, 'Instrument Parameters')) orange = [255/256.,128/256.,0.] if G2frame.GPXtree.GetItemText(PickId) in ['Index Peak List','Unit Cells List']: peaks = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,PatternId, 'Index Peak List')) if not len(peaks): return # are there any peaks? for peak in peaks[0]: if peak[2]: if Page.plotStyle['qPlot']: Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,peak[0]),color='b') if Page.plotStyle['dPlot']: Plot.axvline(G2lat.Pos2dsp(Parms,peak[0]),color='b') else: Plot.axvline(peak[0],color='b') for hkl in G2frame.HKL: clr = orange if len(hkl) > 6 and hkl[3]: clr = 'g' if Page.plotStyle['qPlot']: Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,hkl[-2]),color=clr,dashes=(3,3),lw=1.5) if Page.plotStyle['dPlot']: Plot.axvline(G2lat.Pos2dsp(Parms,hkl[-2]),color=clr,dashes=(3,3),lw=1.5) else: Plot.axvline(hkl[-2],color=clr,dashes=(3,3),lw=1.5) elif Page.plotStyle.get('WgtDiagnostic',False): pass # skip reflection markers elif (G2frame.GPXtree.GetItemText(PickId) in ['Reflection Lists'] or 'PWDR' in G2frame.GPXtree.GetItemText(PickId) or refineMode): Phases = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,PatternId,'Reflection Lists')) l = GSASIIpath.GetConfigValue('Tick_length',8.0) w = GSASIIpath.GetConfigValue('Tick_width',1.) refColors=['b','r','c','g','m','k'] Page.phaseList = sorted(Phases.keys()) # define an order for phases (once!) Page.phaseColors = {p:refColors[i%len(refColors)] for i,p in enumerate(Phases)} for pId,phase in enumerate(Page.phaseList): if 'list' in str(type(Phases[phase])): continue if phase not in Page.phaseColors: 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) plsym = Page.phaseColors[phase]+'|' # yellow should never happen! if Page.plotStyle['qPlot']: Page.tickDict[phase],j = Plot.plot(2*np.pi/peak.T[0],pos,plsym,mew=w,ms=l, picker=True,pickradius=3.,label=phase) elif Page.plotStyle['dPlot']: Page.tickDict[phase],j = Plot.plot(peak.T[0],pos,plsym,mew=w,ms=l, picker=True,pickradius=3.,label=phase) else: Page.tickDict[phase],j = Plot.plot(peak.T[1],pos,plsym,mew=w,ms=l, picker=True,pickradius=3.,label=phase) handles,legends = Plot.get_legend_handles_labels() #got double entries in the phase legends for some reason if handles: labels = dict(zip(legends,handles)) #this removes duplicate phase entries handles = [labels[item] for item in labels] legends = list(labels.keys()) if len(Phases) and obsInCaption: Plot.legend(handles,legends,title='Phases & Data',loc='best') else: Plot.legend(handles,legends,title='Data',loc='best') if G2frame.Contour: time0 = time.time() acolor = mpl.cm.get_cmap(G2frame.ContourColor) Page.Img = Plot.imshow(ContourZ,cmap=acolor, vmin=Ymax*G2frame.Cmin,vmax=Ymax*G2frame.Cmax, interpolation=G2frame.Interpolate, extent=[ContourX[0],ContourX[-1],ContourY[0],ContourY[-1]],aspect='auto',origin='lower') if G2frame.TforYaxis: imgAx = Page.Img.axes ytics = imgAx.get_yticks() 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 PickId and G2frame.GPXtree.GetItemText(PickId) == 'Background': # 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',[]): # "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 = Page.figure.gca().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,'rD',clip_on=Clip_on,picker=True,pickradius=10.) if not newPlot: # this restores previous plot limits (but I'm not sure why there are two .push_current calls) Page.toolbar.push_current() if G2frame.Contour: # for contour plots expand y-axis to include all histograms G2frame.xylim = (G2frame.xylim[0], (0.,len(PlotList)-1.)) Plot.set_xlim(G2frame.xylim[0]) Plot.set_ylim(G2frame.xylim[1]) Page.toolbar.push_current() Page.ToolBarDraw() else: G2frame.xylim = Plot.get_xlim(),Plot.get_ylim() 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): '''Show a customizable "Rietveld" plot and export as a publication-quality file. Will only work 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 Initialize(): '''Set up initial values in plotOpt ''' plotOpt['initNeeded'] = False # create a temporary hard-copy figure to get output options figure = mpl.figure.Figure(dpi=200,figsize=(6,8)) canvas = hcCanvas(figure) fmtDict = canvas.get_supported_filetypes() figure.clear() plotOpt['fmtChoices'] = [fmtDict[j]+', '+j for j in sorted(fmtDict)] plotOpt['fmtChoices'].append('Data file with plot elements, csv') plotOpt['fmtChoices'].append('Grace input file, agr') plotOpt['fmtChoices'].append('Igor Pro input file, itx') if plotOpt['format'] is None: if 'pdf' in fmtDict: plotOpt['format'] = fmtDict['pdf'] + ', pdf' else: plotOpt['format'] = plotOpt['fmtChoices'][0] plotOpt['lineWid'] = '1' plotOpt['tickSiz'] = '6' plotOpt['tickWid'] = '1' plotOpt['markerWid'] = '1' plotOpt['markerSiz'] = '8' plotOpt['markerSym'] = '+' #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 ''' if hasattr(mpcls,'to_rgba'): MPL2rgba = mpcls.to_rgba else: MPL2rgba = mpcls.ColorConverter().to_rgba plotOpt['phaseList'] = [] 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 elif l in Page.tickDict.values(): plotOpt['phaseList'] .append(lbl) if lbl in plotOpt['colors']: continue plotOpt['colors'][lbl] = MPL2rgba(l.get_color()) plotOpt['legend'][lbl] = True def RefreshPlot(*args,**kwargs): '''Update the plot on the dialog ''' figure.clear() CopyRietveldPlot(G2frame,Pattern,Plot,Page,figure) figure.canvas.draw() # 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''' 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() 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,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': obsartist = l 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,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 rsig = np.sqrt(Pattern[1][2]) rsig[rsig>1] = 1 fp.write("@type xy\n") l = obsartist ysig = Pattern[1][5]*rsig # 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)) rsig = np.sqrt(Pattern[1][2]) rsig[rsig>1] = 1 fp.write("@type xy\n") l = obsartist if ma.any(l.get_xdata().mask): for x,y,m in zip(l.get_xdata(),Pattern[1][5]*rsig,l.get_xdata().mask): if not m: fp.write("{} {}\n".format(x,y)) else: for x,y in zip(l.get_xdata(),Pattern[1][5]*rsig): 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 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,l.get_color()) #if sum(c) == 4.0: continue 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])) # invert lists into columns, use iterator for all values if hasattr(itertools,'zip_longest'): #Python 3+ invertIter = itertools.zip_longest(*valueList,fillvalue=' ') else: invertIter = itertools.izip_longest(*valueList,fillvalue=' ') 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)) for row in invertIter: 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,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() 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 = [] lblList.append('Axis-limits') valueList.append(list(Plot.get_xlim())+list(Plot.get_ylim())) tickpos = {} for i,l in enumerate(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: lbl = l.get_label() 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,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,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]]) # invert lists into columns, use iterator for all values if hasattr(itertools,'zip_longest'): #Python 3+ invertIter = itertools.zip_longest(*valueList,fillvalue=' ') else: invertIter = itertools.izip_longest(*valueList,fillvalue=' ') fp = open(filename,'w') Write2csv(fp,lblList,header=True) for row in invertIter: Write2csv(fp,row) fp.close() def onSave(event): '''Write the current plot to a file ''' hcfigure = mpl.figure.Figure(dpi=plotOpt['dpi'],figsize=(plotOpt['width'],plotOpt['height'])) CopyRietveldPlot(G2frame,Pattern,Plot,Page,hcfigure) longFormatName,typ = plotOpt['format'].split(',') fil = G2G.askSaveFile(G2frame,'','.'+typ.strip(),longFormatName) if 'csv' in typ and fil: CopyRietveld2csv(Pattern,Plot,Page,fil) dlg.EndModal(wx.ID_OK) elif 'agr' in typ and fil: CopyRietveld2Grace(Pattern,Plot,Page,plotOpt,fil) dlg.EndModal(wx.ID_OK) elif 'itx' in typ and fil: CopyRietveld2Igor(Pattern,Plot,Page,plotOpt,fil,G2frame) dlg.EndModal(wx.ID_OK) 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 plotOpt['colorButtons']: print('Unexpected button',str(event.GetEventObject())) return lbl = plotOpt['colorButtons'][event.GetEventObject()] c = event.GetValue() plotOpt['colors'][lbl] = (c.Red()/255.,c.Green()/255.,c.Blue()/255.,c.alpha/255.) RefreshPlot() # start of PublishRietveldPlot 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,