source: trunk/GSASIIplot.py @ 1979

Last change on this file since 1979 was 1979, checked in by vondreele, 8 years ago

fix to SS structure factor now works
start SSderiv & block hkl processing

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 236.1 KB
Line 
1# -*- coding: utf-8 -*-
2'''
3*GSASIIplot: plotting routines*
4===============================
5
6'''
7########### SVN repository information ###################
8# $Date: 2015-09-28 13:59:29 +0000 (Mon, 28 Sep 2015) $
9# $Author: vondreele $
10# $Revision: 1979 $
11# $URL: trunk/GSASIIplot.py $
12# $Id: GSASIIplot.py 1979 2015-09-28 13:59:29Z vondreele $
13########### SVN repository information ###################
14import math
15import time
16import copy
17import sys
18import os.path
19import numpy as np
20import numpy.ma as ma
21import numpy.linalg as nl
22import wx
23import wx.aui
24import wx.glcanvas
25import matplotlib as mpl
26import mpl_toolkits.mplot3d.axes3d as mp3d
27import GSASIIpath
28GSASIIpath.SetVersionNumber("$Revision: 1979 $")
29import GSASIIgrid as G2gd
30import GSASIIimage as G2img
31import GSASIIpwd as G2pwd
32import GSASIIIO as G2IO
33import GSASIIpwdGUI as G2pdG
34import GSASIIimgGUI as G2imG
35import GSASIIphsGUI as G2phG
36import GSASIIlattice as G2lat
37import GSASIIspc as G2spc
38import GSASIImath as G2mth
39import GSASIIctrls as G2G
40import pytexture as ptx
41from  OpenGL.GL import *
42from OpenGL.GLU import *
43from OpenGL.GLE import *
44import gltext
45from matplotlib.backends.backend_wx import _load_bitmap
46from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
47from matplotlib.backends.backend_wxagg import NavigationToolbar2Wx as Toolbar
48
49# useful degree trig functions
50sind = lambda x: math.sin(x*math.pi/180.)
51cosd = lambda x: math.cos(x*math.pi/180.)
52tand = lambda x: math.tan(x*math.pi/180.)
53asind = lambda x: 180.*math.asin(x)/math.pi
54acosd = lambda x: 180.*math.acos(x)/math.pi
55atan2d = lambda x,y: 180.*math.atan2(y,x)/math.pi
56atand = lambda x: 180.*math.atan(x)/math.pi
57# numpy versions
58npsind = lambda x: np.sin(x*np.pi/180.)
59npcosd = lambda x: np.cos(x*np.pi/180.)
60nptand = lambda x: np.tan(x*np.pi/180.)
61npacosd = lambda x: 180.*np.arccos(x)/np.pi
62npasind = lambda x: 180.*np.arcsin(x)/np.pi
63npatand = lambda x: 180.*np.arctan(x)/np.pi
64npatan2d = lambda x,y: 180.*np.arctan2(x,y)/np.pi
65GkDelta = unichr(0x0394)
66   
67class G2PlotMpl(wx.Panel):   
68    'Creates a Matplotlib 2-D plot in the GSAS-II graphics window'
69    def __init__(self,parent,id=-1,dpi=None,**kwargs):
70        wx.Panel.__init__(self,parent,id=id,**kwargs)
71        mpl.rcParams['legend.fontsize'] = 10
72        self.figure = mpl.figure.Figure(dpi=dpi,figsize=(5,6))
73        self.canvas = Canvas(self,-1,self.figure)
74        self.toolbar = GSASIItoolbar(self.canvas)
75
76        self.toolbar.Realize()
77       
78        sizer=wx.BoxSizer(wx.VERTICAL)
79        sizer.Add(self.canvas,1,wx.EXPAND)
80        sizer.Add(self.toolbar,0,wx.LEFT|wx.EXPAND)
81        self.SetSizer(sizer)
82       
83class G2PlotOgl(wx.Panel):
84    'Creates an OpenGL plot in the GSAS-II graphics window'
85    def __init__(self,parent,id=-1,dpi=None,**kwargs):
86        self.figure = wx.Panel.__init__(self,parent,id=id,**kwargs)
87        if 'win' in sys.platform:           #Windows (& Mac) already double buffered
88            self.canvas = wx.glcanvas.GLCanvas(self,-1,**kwargs)
89        else:                               #fix from Jim Hester for X systems
90            attribs = (wx.glcanvas.WX_GL_DOUBLEBUFFER,)         
91            self.canvas = wx.glcanvas.GLCanvas(self,-1,attribList=attribs,**kwargs)
92        # create GL context for wx > 2.8
93        i,j= wx.__version__.split('.')[0:2]
94        if int(i)+int(j)/10. > 2.8:
95            self.context = wx.glcanvas.GLContext(self.canvas)
96            self.canvas.SetCurrent(self.context)
97        else:
98            self.context = None
99        self.camera = {}
100        sizer=wx.BoxSizer(wx.VERTICAL)
101        sizer.Add(self.canvas,1,wx.EXPAND)
102        self.SetSizer(sizer)
103       
104class G2Plot3D(wx.Panel):
105    'Creates a 3D Matplotlib plot in the GSAS-II graphics window'
106    def __init__(self,parent,id=-1,dpi=None,**kwargs):
107        wx.Panel.__init__(self,parent,id=id,**kwargs)
108        self.figure = mpl.figure.Figure(dpi=dpi,figsize=(6,6))
109        self.canvas = Canvas(self,-1,self.figure)
110        self.toolbar = GSASIItoolbar(self.canvas)
111
112        self.toolbar.Realize()
113       
114        sizer=wx.BoxSizer(wx.VERTICAL)
115        sizer.Add(self.canvas,1,wx.EXPAND)
116        sizer.Add(self.toolbar,0,wx.LEFT|wx.EXPAND)
117        self.SetSizer(sizer)
118                             
119class G2PlotNoteBook(wx.Panel):
120    'create a tabbed window for GSAS-II graphics'
121    def __init__(self,parent,id=-1):
122        wx.Panel.__init__(self,parent,id=id)
123        #so one can't delete a plot page!!
124        self.nb = wx.aui.AuiNotebook(self, \
125            style=wx.aui.AUI_NB_DEFAULT_STYLE ^ wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB)
126        sizer = wx.BoxSizer()
127        sizer.Add(self.nb,1,wx.EXPAND)
128        self.SetSizer(sizer)
129        self.status = parent.CreateStatusBar()
130        self.status.SetFieldsCount(2)
131        self.status.SetStatusWidths([150,-1])
132        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
133        self.nb.Bind(wx.EVT_KEY_UP,self.OnNotebookKey)
134       
135        self.plotList = []
136           
137    def OnNotebookKey(self,event):
138        '''Called when a keystroke event gets picked up by the notebook window
139        rather the child. This is not expected, but somehow it does sometimes
140        on the Mac and perhaps Linux.
141
142        Assume that the page associated with the currently displayed tab
143        has a child, .canvas; give that child the focus and pass it the event.
144        '''
145        try:
146            Page = self.nb.GetPage(self.nb.GetSelection())
147        except ValueError: # occurs with no plot tabs
148            return
149        try:
150            Page.canvas.SetFocus()
151            wx.PostEvent(Page.canvas,event)
152        except AttributeError:
153            pass
154
155    def addMpl(self,name=""):
156        'Add a tabbed page with a matplotlib plot'
157        page = G2PlotMpl(self.nb)
158        self.nb.AddPage(page,name)
159       
160        self.plotList.append(name)
161       
162        return page.figure
163       
164    def add3D(self,name=""):
165        'Add a tabbed page with a 3D plot'
166        page = G2Plot3D(self.nb)
167        self.nb.AddPage(page,name)
168       
169        self.plotList.append(name)
170       
171        return page.figure
172       
173    def addOgl(self,name=""):
174        'Add a tabbed page with an openGL plot'
175        page = G2PlotOgl(self.nb)
176        self.nb.AddPage(page,name)
177       
178        self.plotList.append(name)
179       
180        return page.figure
181       
182    def Delete(self,name):
183        'delete a tabbed page'
184        try:
185            item = self.plotList.index(name)
186            del self.plotList[item]
187            self.nb.DeletePage(item)
188        except ValueError:          #no plot of this name - do nothing
189            return     
190               
191    def clear(self):
192        'clear all pages from plot window'
193        while self.nb.GetPageCount():
194            self.nb.DeletePage(0)
195        self.plotList = []
196        self.status.DestroyChildren()
197       
198    def Rename(self,oldName,newName):
199        'rename a tab'
200        try:
201            item = self.plotList.index(oldName)
202            self.plotList[item] = newName
203            self.nb.SetPageText(item,newName)
204        except ValueError:          #no plot of this name - do nothing
205            return     
206       
207    def OnPageChanged(self,event):
208        'respond to someone pressing a tab on the plot window'
209        if self.plotList:
210            self.status.SetStatusText('Better to select this from GSAS-II data tree',1)
211        self.status.DestroyChildren()                           #get rid of special stuff on status bar
212       
213class GSASIItoolbar(Toolbar):
214    'Override the matplotlib toolbar so we can add more icons'
215    ON_MPL_HELP = wx.NewId()
216    ON_MPL_KEY = wx.NewId()
217    arrows = {}
218    for direc in ('left','right','up','down','Expand X',
219                  'Shrink X','Expand Y','Shrink Y'):
220        arrows[direc] = wx.NewId()
221    def __init__(self,plotCanvas):
222        '''Adds additional icons to toolbar'''
223        Toolbar.__init__(self,plotCanvas)
224        self.plotCanvas = plotCanvas
225        POSITION_OF_CONFIGURE_SUBPLOTS_BTN = 6 # remove one button, nos. start at 1!
226        self.DeleteToolByPos(POSITION_OF_CONFIGURE_SUBPLOTS_BTN)    #doesn't work in miniconda
227        self.parent = self.GetParent()
228        key = os.path.join(os.path.split(__file__)[0],'key.ico')
229        self.AddSimpleTool(self.ON_MPL_KEY,_load_bitmap(key),'Key press','Select key press')
230        wx.EVT_TOOL(self,self.ON_MPL_KEY,self.OnKey)
231        help = os.path.join(os.path.split(__file__)[0],'help.ico')
232        self.AddSimpleTool(self.ON_MPL_HELP,_load_bitmap(help),'Help on','Show help on')
233        wx.EVT_TOOL(self,self.ON_MPL_HELP,self.OnHelp)
234        # add arrow keys to control zooming
235        for direc in ('left','right','up','down'):
236            wx.EVT_TOOL(self,self.arrows[direc],self.OnArrow)
237            icon =  os.path.join(os.path.split(__file__)[0],direc[0]+'arrow.ico')
238            self.AddSimpleTool(self.arrows[direc],_load_bitmap(icon),
239                               'Shift '+direc,'Shift plot '+direc)
240        for direc in ('Expand X','Shrink X','Expand Y','Shrink Y'):
241            fil = ''.join([i[0].lower() for i in direc.split()]+['arrow.ico'])
242            wx.EVT_TOOL(self,self.arrows[direc],self.OnArrow)
243            icon =  os.path.join(os.path.split(__file__)[0],fil)
244            self.AddSimpleTool(self.arrows[direc],_load_bitmap(icon),
245                               direc,'Zoom: '+direc)
246    def OnArrow(self,event):
247        'reposition limits to scan or zoom by button press'
248        ax = self.plotCanvas.figure.get_axes()[0]
249        xmin,xmax,ymin,ymax = ax.axis()
250        #print xmin,xmax,ymin,ymax
251        if event.Id == self.arrows['right']:
252            delta = (xmax-xmin)/10.
253            xmin -= delta
254            xmax -= delta
255        elif event.Id == self.arrows['left']:
256            delta = (xmax-xmin)/10.
257            xmin += delta
258            xmax += delta
259        elif event.Id == self.arrows['up']:
260            delta = (ymax-ymin)/10.
261            ymin -= delta
262            ymax -= delta
263        elif event.Id == self.arrows['down']:
264            delta = (ymax-ymin)/10.
265            ymin += delta
266            ymax += delta
267        elif event.Id == self.arrows['Expand X']:
268            delta = (xmax-xmin)/10.
269            xmin += delta
270            xmax -= delta
271        elif event.Id == self.arrows['Expand Y']:
272            delta = (ymax-ymin)/10.
273            ymin += delta
274            ymax -= delta
275        elif event.Id == self.arrows['Shrink X']:
276            delta = (xmax-xmin)/10.
277            xmin -= delta
278            xmax += delta
279        elif event.Id == self.arrows['Shrink Y']:
280            delta = (ymax-ymin)/10.
281            ymin -= delta
282            ymax += delta
283        else:
284            # should not happen!
285            GSASIIpath.IPyBreak()
286        self.parent.toolbar.push_current()
287        ax.axis((xmin,xmax,ymin,ymax))
288        #print xmin,xmax,ymin,ymax
289        self.plotCanvas.figure.canvas.draw()
290        self.parent.toolbar.draw()
291#        self.parent.toolbar.push_current()
292       
293    def OnHelp(self,event):
294        'Respond to press of help button on plot toolbar'
295        Page = self.GetParent().GetParent()
296        pageNo = Page.GetSelection()
297        bookmark = Page.GetPageText(pageNo)
298        bookmark = bookmark.strip(')').replace('(','_')
299        G2G.ShowHelp(bookmark,self.TopLevelParent)
300    def OnKey(self,event):
301        '''Provide user with list of keystrokes defined for plot as well as an
302        alternate way to access the same functionality
303        '''
304        parent = self.GetParent()
305        if parent.Choice:
306            dlg = wx.SingleChoiceDialog(parent,'Select','Key press',list(parent.Choice))
307            if dlg.ShowModal() == wx.ID_OK:
308                sel = dlg.GetSelection()
309                event.key = parent.Choice[sel][0]
310                parent.keyPress(event)
311            dlg.Destroy()
312           
313################################################################################
314##### PlotSngl
315################################################################################
316           
317def PlotSngl(G2frame,newPlot=False,Data=None,hklRef=None,Title=''):
318    '''Structure factor plotting package - displays zone of reflections as rings proportional
319        to F, F**2, etc. as requested
320    '''
321    from matplotlib.patches import Circle,CirclePolygon
322    global HKL,HKLF,HKLref
323    HKLref = hklRef
324   
325    def OnSCKeyPress(event):
326        i = zones.index(Data['Zone'])
327        newPlot = False
328        pwdrChoice = {'f':'Fo','s':'Fosq','u':'Unit Fc'}
329        hklfChoice = {'1':'|DFsq|>sig','3':'|DFsq|>3sig','w':'|DFsq|/sig','f':'Fo','s':'Fosq','i':'Unit Fc'}
330        if event.key == 'h':
331            Data['Zone'] = '100'
332            newPlot = True
333        elif event.key == 'k':
334            Data['Zone'] = '010'
335            newPlot = True
336        elif event.key == 'l':
337            Data['Zone'] = '001'
338            newPlot = True
339        elif event.key == 'i':
340            Data['Scale'] *= 1.1
341        elif event.key == 'd':
342            Data['Scale'] /= 1.1
343        elif event.key in ['+','=']:
344            Data['Layer'] = min(Data['Layer']+1,HKLmax[i])
345        elif event.key == '-':
346            Data['Layer'] = max(Data['Layer']-1,HKLmin[i])
347        elif event.key == '0':
348            Data['Layer'] = 0
349            Data['Scale'] = 1.0
350        elif event.key in hklfChoice and 'HKLF' in Name:
351            Data['Type'] = hklfChoice[event.key]           
352            newPlot = True
353        elif event.key in pwdrChoice and 'PWDR' in Name:
354            Data['Type'] = pwdrChoice[event.key]           
355            newPlot = True       
356        PlotSngl(G2frame,newPlot,Data,HKLref,Title)
357
358    def OnSCMotion(event):
359        xpos = event.xdata
360        if xpos:
361            xpos = round(xpos)                                        #avoid out of frame mouse position
362            ypos = round(event.ydata)
363            zpos = Data['Layer']
364            if '100' in Data['Zone']:
365                HKLtxt = '(%3d,%3d,%3d)'%(zpos,xpos,ypos)
366            elif '010' in Data['Zone']:
367                HKLtxt = '(%3d,%3d,%3d)'%(xpos,zpos,ypos)
368            elif '001' in Data['Zone']:
369                HKLtxt = '(%3d,%3d,%3d)'%(xpos,ypos,zpos)
370            Page.canvas.SetToolTipString(HKLtxt)
371            G2frame.G2plotNB.status.SetStatusText('HKL = '+HKLtxt,0)
372            G2frame.G2plotNB.status.SetStatusText('Use K-box to set plot controls',1)
373               
374    def OnSCPress(event):
375        zpos = Data['Layer']
376        xpos = event.xdata
377        if xpos:
378            pos = int(round(event.xdata)),int(round(event.ydata))
379            if '100' in Data['Zone']:
380                Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(zpos,pos[0],pos[1]))
381                hkl = np.array([zpos,pos[0],pos[1]])
382            elif '010' in Data['Zone']:
383                Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(pos[0],zpos,pos[1]))
384                hkl = np.array([pos[0],zpos,pos[1]])
385            elif '001' in Data['Zone']:
386                Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(pos[0],pos[1],zpos))
387                hkl = np.array([pos[0],pos[1],zpos])
388            h,k,l = hkl
389            hklf = HKLF[np.where(np.all(HKL-hkl == [0,0,0],axis=1))]
390            if len(hklf):
391                Fosq,sig,Fcsq = hklf[0]
392                HKLtxt = '( %.2f %.3f %.2f %.2f)'%(Fosq,sig,Fcsq,(Fosq-Fcsq)/(scale*sig))
393                G2frame.G2plotNB.status.SetStatusText('Fosq, sig, Fcsq, delFsq/sig = '+HKLtxt,1)
394                                 
395    Name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
396    if not Title:
397        Title = Name
398    try:
399        plotNum = G2frame.G2plotNB.plotList.index('Structure Factors')
400        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
401        if not newPlot:
402            Plot = Page.figure.gca()          #get previous powder plot & get limits
403            xylim = Plot.get_xlim(),Plot.get_ylim()
404        Page.figure.clf()
405        Plot = Page.figure.gca()          #get a fresh plot after clf()
406    except ValueError:
407        Plot = G2frame.G2plotNB.addMpl('Structure Factors').gca()
408        plotNum = G2frame.G2plotNB.plotList.index('Structure Factors')
409        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
410        Page.canvas.mpl_connect('button_press_event', OnSCPress)
411        Page.canvas.mpl_connect('motion_notify_event', OnSCMotion)
412        Page.canvas.mpl_connect('key_press_event', OnSCKeyPress)
413        Page.keyPress = OnSCKeyPress
414        Page.Choice = (' key press','i: increase scale','d: decrease scale',
415            'h: select 100 zone','k: select 010 zone','l: select 001 zone',
416            'f: select Fo','s: select Fosq','u: select unit Fc',
417            '+: increase index','-: decrease index','0: zero layer',)
418        if 'HKLF' in Name:
419            Page.Choice += ('w: select |DFsq|/sig','1: select |DFsq|>sig','3: select |DFsq|>3sig',)
420    Page.SetFocus()
421   
422    G2frame.G2plotNB.status.SetStatusText('Use K-box to set plot controls',1)
423    Plot.set_aspect(aspect='equal')
424   
425    Type = Data['Type']           
426    scale = Data['Scale']
427    HKLmax = Data['HKLmax']
428    HKLmin = Data['HKLmin']
429    FosqMax = Data['FoMax']
430    Super = Data['Super']
431    SuperVec = []
432    if Super:
433        SuperVec = np.array(Data['SuperVec'][0])
434    FoMax = math.sqrt(FosqMax)
435    xlabel = ['k, h=','h, k=','h, l=']
436    ylabel = ['l','l','k']
437    zones = ['100','010','001']
438    pzone = [[1,2],[0,2],[0,1]]
439    izone = zones.index(Data['Zone'])
440    Plot.set_title(Data['Type']+' for '+Title)
441    HKL = []
442    HKLF = []
443    time0 = time.time()
444    for refl in HKLref:
445        H = refl[:3]
446        if 'HKLF' in Name:
447            Fosq,sig,Fcsq = refl[5+Super:8+Super]
448        else:
449            Fosq,sig,Fcsq = refl[8+Super],1.0,refl[9+Super]
450        if Super:
451            HKL.append(H+SuperVec*refl[3])
452        else:
453            HKL.append(H)
454        HKLF.append([Fosq,sig,Fcsq])
455        if H[izone] == Data['Layer']:
456            A = 0
457            B = 0
458            if Type == 'Fosq':
459                A = scale*Fosq/FosqMax
460                B = scale*Fcsq/FosqMax
461                C = abs(A-B)
462            elif Type == 'Fo':
463                A = scale*math.sqrt(max(0,Fosq))/FoMax
464                B = scale*math.sqrt(max(0,Fcsq))/FoMax
465                C = abs(A-B)
466            elif Type == 'Unit Fc':
467                A = scale/2
468                B = scale/2
469                C = 0.0
470                if Fcsq and Fosq > 0:
471                    A *= min(1.0,Fosq/Fcsq)
472                    C = abs(A-B)
473            elif Type == '|DFsq|/sig':
474                if sig > 0.:
475                    A = (Fosq-Fcsq)/(3*sig)
476                B = 0
477            elif Type == '|DFsq|>sig':
478                if sig > 0.:
479                    A = (Fosq-Fcsq)/(3*sig)
480                if abs(A) < 1.0: A = 0
481                B = 0                   
482            elif Type == '|DFsq|>3sig':
483                if sig > 0.:
484                    A = (Fosq-Fcsq)/(3*sig)
485                if abs(A) < 3.0: A = 0
486                B = 0
487            if Super:
488                h = H+SuperVec*refl[3]               
489            else:
490                h = H
491            xy = (h[pzone[izone][0]],h[pzone[izone][1]])
492            if Type in ['|DFsq|/sig','|DFsq|>sig','|DFsq|>3sig']:
493                if A > 0.0:
494                    Plot.add_artist(Circle(xy,radius=A,ec='g',fc='w'))
495                else:
496                    Plot.add_artist(Circle(xy,radius=-A,ec='r',fc='w'))
497            else:
498                if A > 0.0 and A > B:
499                    Plot.add_artist(Circle(xy,radius=A,ec='g',fc='w'))
500                if B:
501                    Plot.add_artist(Circle(xy,radius=B,ec='b',fc='w'))
502                    if A < B:
503                        Plot.add_artist(Circle(xy,radius=A,ec='g',fc='w'))
504                    radius = C
505                    if radius > 0:
506                        if A > B:
507                            Plot.add_artist(Circle(xy,radius=radius,ec='g',fc='g'))
508                        else:                   
509                            Plot.add_artist(Circle(xy,radius=radius,ec='r',fc='r'))
510#    print 'plot time: %.3f'%(time.time()-time0)
511    HKL = np.array(HKL)
512    HKLF = np.array(HKLF)
513    Plot.set_xlabel(xlabel[izone]+str(Data['Layer']),fontsize=12)
514    Plot.set_ylabel(ylabel[izone],fontsize=12)
515    if not newPlot:
516        Page.toolbar.push_current()
517        Plot.set_xlim(xylim[0])
518        Plot.set_ylim(xylim[1])
519#        xylim = []
520        Page.toolbar.push_current()
521        Page.toolbar.draw()
522    else:
523        Plot.set_xlim((HKLmin[pzone[izone][0]],HKLmax[pzone[izone][0]]))
524        Plot.set_ylim((HKLmin[pzone[izone][1]],HKLmax[pzone[izone][1]]))
525        Page.canvas.draw()
526       
527################################################################################
528##### Plot3DSngl
529################################################################################
530
531def Plot3DSngl(G2frame,newPlot=False,Data=None,hklRef=None,Title=False):
532    '''3D Structure factor plotting package - displays reflections as rings proportional
533        to F, F**2, etc. as requested as 3D array
534    '''
535
536    global ifBox
537    ifBox = False
538    def OnKeyBox(event):
539        mode = cb.GetValue()
540        if mode in ['jpeg','bmp','tiff',]:
541            try:
542                import Image as Im
543            except ImportError:
544                try:
545                    from PIL import Image as Im
546                except ImportError:
547                    print "PIL/pillow Image module not present. Cannot save images without this"
548                    raise Exception("PIL/pillow Image module not found")
549            try:
550                Fname = os.path.join(Mydir,generalData['Name']+'.'+mode)
551            except NameError:   #for when generalData doesn't exist!
552                Fname = os.path.join(Mydir,'unknown'+'.'+mode)
553            print Fname+' saved'
554            size = Page.canvas.GetSize()
555            glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
556            if mode in ['jpeg',]:
557                Pix = glReadPixels(0,0,size[0],size[1],GL_RGBA, GL_UNSIGNED_BYTE)
558                im = Im.new("RGBA", (size[0],size[1]))
559            else:
560                Pix = glReadPixels(0,0,size[0],size[1],GL_RGB, GL_UNSIGNED_BYTE)
561                im = Im.new("RGB", (size[0],size[1]))
562            im.fromstring(Pix)
563            im.save(Fname,mode)
564            cb.SetValue(' save as/key:')
565            G2frame.G2plotNB.status.SetStatusText('Drawing saved to: '+Fname,1)
566        else:
567            event.key = cb.GetValue()[0]
568            cb.SetValue(' save as/key:')
569            wx.CallAfter(OnKey,event)
570        Page.canvas.SetFocus() # redirect the Focus from the button back to the plot
571       
572    def OnKey(event):           #on key UP!!
573        global ifBox
574        Choice = {'F':'Fo','S':'Fosq','U':'Unit','D':'dFsq','W':'dFsq/sig'}
575        viewChoice = {'L':[[0,0,1],[1,0,0],[0,1,0]],'K':[[0,1,0],[0,0,1],[1,0,0]],'H':[[1,0,0],[0,0,1],[0,1,0]]}
576        try:
577            keyCode = event.GetKeyCode()
578            if keyCode > 255:
579                keyCode = 0
580            key = chr(keyCode)
581        except AttributeError:       #if from OnKeyBox above
582            key = str(event.key).upper()
583        if key in ['C','H','K','L']:
584            if key == 'C':
585                Data['Zone'] = False 
586                key = 'L'
587            Data['viewKey'] = key
588            drawingData['viewPoint'][0] = drawingData['default']
589            drawingData['viewDir'] = np.array(viewChoice[key][0])
590            drawingData['viewUp'] = np.array(viewChoice[key][1])
591            drawingData['oldxy'] = []
592            if Data['Zone']:
593                if key == 'L':
594                    Q = [-1,0,0,0]
595                else:
596                    V0 = np.array(viewChoice[key][0])
597                    V1 = np.array(viewChoice[key][1])
598                    V0 = np.inner(Amat,V0)
599                    V1 = np.inner(Amat,V1)
600                    V0 /= nl.norm(V0)
601                    V1 /= nl.norm(V1)
602                    A = np.arccos(np.sum(V1*V0))
603                    Q = G2mth.AV2Q(-A,viewChoice[key][2])
604                G2frame.G2plotNB.status.SetStatusText('zone = %s'%(str(viewChoice[key][0])),1)
605            else:
606                V0 = np.array(viewChoice[key][0])
607                V = np.inner(Bmat,V0)
608                V /= np.sqrt(np.sum(V**2))
609                V *= np.array([0,0,1])
610                A = np.arccos(np.sum(V*V0))
611                Q = G2mth.AV2Q(-A,viewChoice[key][2])
612            drawingData['Quaternion'] = Q
613        elif key in 'Z':
614            Data['Zone'] = not Data['Zone']
615        elif key in 'B':
616            ifBox = not ifBox
617        elif key in ['+','=']:
618            Data['Scale'] *= 1.25
619        elif key == '-':
620            Data['Scale'] /= 1.25
621        elif key == 'P':
622            vec = viewChoice[Data['viewKey']][0]
623            drawingData['viewPoint'][0] -= vec
624        elif key == 'N':
625            vec = viewChoice[Data['viewKey']][0]
626            drawingData['viewPoint'][0] += vec
627        elif key == '0':
628            drawingData['viewPoint'][0] = [0,0,0]
629            Data['Scale'] = 1.0
630        elif key == 'I':
631            Data['Iscale'] = not Data['Iscale']
632        elif key in Choice:
633            Data['Type'] = Choice[key]
634        Draw('key')
635           
636    Name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
637    if Title and Title in G2frame.GetPhaseData(): #NB: save image as e.g. jpeg will fail if False; MyDir is unknown
638        generalData = G2frame.GetPhaseData()[Title]['General']
639        cell = generalData['Cell'][1:7]
640        Mydir = generalData['Mydir']
641    else:
642        Title = 'Unknown'
643        cell = [10,10,10,90,90,90]
644        Mydir = G2frame.dirname
645    drawingData = Data['Drawing']
646    Super = Data['Super']
647    SuperVec = []
648    if Super:
649        SuperVec = np.array(Data['SuperVec'][0])
650    defaultViewPt = copy.copy(drawingData['viewPoint'])
651    Amat,Bmat = G2lat.cell2AB(cell)         #Amat - crystal to cartesian, Bmat - inverse
652    Gmat,gmat = G2lat.cell2Gmat(cell)
653    invcell = G2lat.Gmat2cell(Gmat)
654    A4mat = np.concatenate((np.concatenate((Amat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
655    B4mat = np.concatenate((np.concatenate((Bmat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
656    drawingData['Quaternion'] = G2mth.AV2Q(2*np.pi,np.inner(Bmat,[0,0,1]))
657    Wt = np.array([255,255,255])
658    Rd = np.array([255,0,0])
659    Gr = np.array([0,255,0])
660    wxGreen = wx.Colour(0,255,0)
661    Bl = np.array([0,0,255])
662    Or = np.array([255,128,0])
663    wxOrange = wx.Colour(255,128,0)
664    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]])
665    uEdges = np.array([
666        [uBox[0],uBox[1]],[uBox[0],uBox[3]],[uBox[0],uBox[4]],[uBox[1],uBox[2]], 
667        [uBox[2],uBox[3]],[uBox[1],uBox[5]],[uBox[2],uBox[6]],[uBox[3],uBox[7]], 
668        [uBox[4],uBox[5]],[uBox[5],uBox[6]],[uBox[6],uBox[7]],[uBox[7],uBox[4]]])
669    uColors = [Rd,Gr,Bl, Wt,Wt,Wt, Wt,Wt,Wt, Wt,Wt,Wt]
670    def FillHKLRC():
671        R = np.zeros(len(hklRef))
672        C = []
673        HKL = []
674        RC = []
675        for i,refl in enumerate(hklRef):
676            H = refl[:3]
677            if 'HKLF' in Name:
678                Fosq,sig,Fcsq = refl[5+Super:8+Super]
679                if refl[3+Super] < 0:
680                    Fosq,sig,Fcsq = [0,1,0]
681            else:
682                Fosq,sig,Fcsq = refl[8+Super],1.0,refl[9+Super]
683            if Super:
684                HKL.append(H+SuperVec*refl[3])
685            else:
686                HKL.append(H)
687            if Data['Type'] == 'Unit':
688                R[i] = 0.1
689                C.append(Gr)
690            elif Data['Type'] == 'Fosq':
691                if Fosq > 0:
692                    R[i] = Fosq
693                    C.append(Gr)
694                else:
695                    R[i] = -Fosq
696                    C.append(Rd)
697            elif Data['Type'] == 'Fo':
698                if Fosq > 0:
699                    R[i] = np.sqrt(Fosq)
700                    C.append(Gr)
701                else:
702                    R[i] = np.sqrt(-Fosq)
703                    C.append(Rd)
704            elif Data['Type'] == 'dFsq/sig':
705                dFsig = (Fosq-Fcsq)/sig
706                if dFsig > 0:
707                    R[i] = dFsig
708                    C.append(Gr)
709                else:
710                    R[i] = -dFsig
711                    C.append(Rd)
712            elif Data['Type'] == 'dFsq':
713                dF = Fosq-Fcsq
714                if dF > 0:
715                    R[i] = dF
716                    C.append(Gr)
717                else:
718                    R[i] = -dF
719                    C.append(Rd)
720        R /= np.max(R)
721        R *= Data['Scale']
722        R = np.where(R<1.e-5,1.e-5,R)
723        if Data['Iscale']:
724            R = np.where(R<=1.,R,1.)
725            C = np.array(C)
726            C = (C.T*R).T
727            R = np.ones_like(R)*0.05     
728        return HKL,zip(list(R),C)
729
730    def GetTruePosition(xy):
731        View = glGetIntegerv(GL_VIEWPORT)
732        Proj = glGetDoublev(GL_PROJECTION_MATRIX)
733        Model = glGetDoublev(GL_MODELVIEW_MATRIX)
734        Zmax = 1.
735        xy = [int(xy[0]),int(View[3]-xy[1])]
736        for i,ref in enumerate(hklRef):
737            h,k,l = ref[:3]
738            X,Y,Z = gluProject(h,k,l,Model,Proj,View)
739            XY = [int(X),int(Y)]
740            if np.allclose(xy,XY,atol=10) and Z < Zmax:
741                Zmax = Z
742                return [int(h),int(k),int(l)]
743                       
744    def SetTranslation(newxy):
745#first get translation vector in screen coords.       
746        oldxy = drawingData['oldxy']
747        if not len(oldxy): oldxy = list(newxy)
748        dxy = newxy-oldxy
749        drawingData['oldxy'] = list(newxy)
750        V = np.array([-dxy[0],dxy[1],0.])
751#then transform to rotated crystal coordinates & apply to view point       
752        Q = drawingData['Quaternion']
753        V = np.inner(Bmat,G2mth.prodQVQ(G2mth.invQ(Q),V))
754        Tx,Ty,Tz = drawingData['viewPoint'][0]
755        Tx += V[0]*0.1
756        Ty += V[1]*0.1
757        Tz += V[2]*0.1
758        drawingData['viewPoint'][0] =  Tx,Ty,Tz
759       
760    def SetRotation(newxy):
761        'Perform a rotation in x-y space due to a left-mouse drag'
762    #first get rotation vector in screen coords. & angle increment       
763        oldxy = drawingData['oldxy']
764        if not len(oldxy): oldxy = list(newxy)
765        dxy = newxy-oldxy
766        drawingData['oldxy'] = list(newxy)
767        V = np.array([dxy[1],dxy[0],0.])
768        A = 0.25*np.sqrt(dxy[0]**2+dxy[1]**2)
769        if not A: return # nothing changed, nothing to do
770    # next transform vector back to xtal coordinates via inverse quaternion
771    # & make new quaternion
772        Q = drawingData['Quaternion']
773        V = G2mth.prodQVQ(G2mth.invQ(Q),np.inner(Bmat,V))
774        DQ = G2mth.AVdeg2Q(A,V)
775        Q = G2mth.prodQQ(Q,DQ)
776        drawingData['Quaternion'] = Q
777    # finally get new view vector - last row of rotation matrix
778        VD = np.inner(Bmat,G2mth.Q2Mat(Q)[2])
779        VD /= np.sqrt(np.sum(VD**2))
780        drawingData['viewDir'] = VD
781       
782    def SetRotationZ(newxy):                       
783#first get rotation vector (= view vector) in screen coords. & angle increment       
784        View = glGetIntegerv(GL_VIEWPORT)
785        cent = [View[2]/2,View[3]/2]
786        oldxy = drawingData['oldxy']
787        if not len(oldxy): oldxy = list(newxy)
788        dxy = newxy-oldxy
789        drawingData['oldxy'] = list(newxy)
790        V = drawingData['viewDir']
791        A = [0,0]
792        A[0] = dxy[1]*.25
793        A[1] = dxy[0]*.25
794        if newxy[0] > cent[0]:
795            A[0] *= -1
796        if newxy[1] < cent[1]:
797            A[1] *= -1       
798# next transform vector back to xtal coordinates & make new quaternion
799        Q = drawingData['Quaternion']
800        V = np.inner(Amat,V)
801        Qx = G2mth.AVdeg2Q(A[0],V)
802        Qy = G2mth.AVdeg2Q(A[1],V)
803        Q = G2mth.prodQQ(Q,Qx)
804        Q = G2mth.prodQQ(Q,Qy)
805        drawingData['Quaternion'] = Q
806
807    def OnMouseDown(event):
808        xy = event.GetPosition()
809        drawingData['oldxy'] = list(xy)
810       
811    def OnMouseMove(event):
812        if event.ShiftDown():           #don't want any inadvertant moves when picking
813            return
814        newxy = event.GetPosition()
815                               
816        if event.Dragging():
817            if event.LeftIsDown():
818                SetRotation(newxy)
819                Q = drawingData['Quaternion']
820            elif event.RightIsDown():
821                SetTranslation(newxy)
822                Tx,Ty,Tz = drawingData['viewPoint'][0]
823            elif event.MiddleIsDown():
824                SetRotationZ(newxy)
825                Q = drawingData['Quaternion']
826            Draw('move')
827        else:
828            hkl = GetTruePosition(newxy)
829            if hkl:
830                h,k,l = hkl
831                Page.canvas.SetToolTipString('%d,%d,%d'%(h,k,l))
832                G2frame.G2plotNB.status.SetStatusText('hkl = %d,%d,%d'%(h,k,l),1)
833       
834    def OnMouseWheel(event):
835        if event.ShiftDown():
836            return
837        drawingData['cameraPos'] += event.GetWheelRotation()/120.
838        drawingData['cameraPos'] = max(0.1,min(20.00,drawingData['cameraPos']))
839        Draw('wheel')
840       
841    def SetBackground():
842        R,G,B,A = Page.camera['backColor']
843        glClearColor(R,G,B,A)
844        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
845       
846    def SetLights():
847        glEnable(GL_DEPTH_TEST)
848        glShadeModel(GL_SMOOTH)
849        glEnable(GL_LIGHTING)
850        glEnable(GL_LIGHT0)
851        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,0)
852        glLightfv(GL_LIGHT0,GL_AMBIENT,[1,1,1,.8])
853        glLightfv(GL_LIGHT0,GL_DIFFUSE,[1,1,1,1])
854       
855    def RenderBox(x,y,z):
856        xyz = np.array([x,y,z])
857        glEnable(GL_COLOR_MATERIAL)
858        glLineWidth(1)
859        glPushMatrix()
860        glTranslate(x,y,z)
861        glColor4ubv([0,0,0,0])
862        glBegin(GL_LINES)
863        for line,color in zip(uEdges,uColors):
864            glColor3ubv(color)
865            glVertex3fv(line[0])
866            glVertex3fv(line[1])
867        glEnd()
868        glPopMatrix()
869        glColor4ubv([0,0,0,0])
870        glDisable(GL_COLOR_MATERIAL)
871       
872    def RenderUnitVectors(x,y,z):
873        xyz = np.array([x,y,z])
874        glEnable(GL_COLOR_MATERIAL)
875        glLineWidth(1)
876        glPushMatrix()
877        glTranslate(x,y,z)
878        glBegin(GL_LINES)
879        for line,color in zip(uEdges,uColors)[:3]:
880            glColor3ubv(color)
881            glVertex3fv([0,0,0])
882#            glVertex3fv(-line[1])
883            glVertex3fv(line[1])
884        glEnd()
885        glPopMatrix()
886        glColor4ubv([0,0,0,0])
887        glDisable(GL_COLOR_MATERIAL)
888               
889    def RenderDots(XYZ,RC):
890        glEnable(GL_COLOR_MATERIAL)
891        XYZ = np.array(XYZ)
892        glPushMatrix()
893        for xyz,rc in zip(XYZ,RC):
894            x,y,z = xyz
895            r,c = rc
896            glColor3ubv(c)
897            glPointSize(r*50)
898            glBegin(GL_POINTS)
899            glVertex3fv(xyz)
900            glEnd()
901        glPopMatrix()
902        glColor4ubv([0,0,0,0])
903        glDisable(GL_COLOR_MATERIAL)
904       
905    def Draw(caller=''):
906#useful debug?       
907#        if caller:
908#            print caller
909# end of useful debug
910        G2frame.G2plotNB.status.SetStatusText('Plot type = %s for %s'%(Data['Type'],Name),1)
911        VS = np.array(Page.canvas.GetSize())
912        aspect = float(VS[0])/float(VS[1])
913        cPos = drawingData['cameraPos']
914        Zclip = drawingData['Zclip']*cPos/20.
915        if Data['Zone']:
916            Zclip = 0.1
917        Q = drawingData['Quaternion']
918        Tx,Ty,Tz = drawingData['viewPoint'][0][:3]
919        G,g = G2lat.cell2Gmat(cell)
920        GS = G
921        GS[0][1] = GS[1][0] = math.sqrt(GS[0][0]*GS[1][1])
922        GS[0][2] = GS[2][0] = math.sqrt(GS[0][0]*GS[2][2])
923        GS[1][2] = GS[2][1] = math.sqrt(GS[1][1]*GS[2][2])
924       
925        HKL,RC = FillHKLRC()
926       
927        SetBackground()
928        glInitNames()
929        glPushName(0)
930       
931        glMatrixMode(GL_PROJECTION)
932        glLoadIdentity()
933        glViewport(0,0,VS[0],VS[1])
934        gluPerspective(20.,aspect,cPos-Zclip,cPos+Zclip)
935        gluLookAt(0,0,cPos,0,0,0,0,1,0)
936        SetLights()           
937           
938        glMatrixMode(GL_MODELVIEW)
939        glLoadIdentity()
940        matRot = G2mth.Q2Mat(Q)
941        matRot = np.concatenate((np.concatenate((matRot,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
942        glMultMatrixf(matRot.T)
943        glMultMatrixf(B4mat.T)
944        glTranslate(-Tx,-Ty,-Tz)
945        x,y,z = drawingData['viewPoint'][0]
946        if ifBox:
947            RenderBox(x,y,z)
948        else:
949            RenderUnitVectors(x,y,z)
950        RenderUnitVectors(0,0,0)
951        RenderDots(HKL,RC)
952        time0 = time.time()
953        if Page.context: Page.canvas.SetCurrent(Page.context)    # wx 2.9 fix
954        Page.canvas.SwapBuffers()
955
956    # PlotStructure execution starts here (N.B. initialization above)
957    try:
958        plotNum = G2frame.G2plotNB.plotList.index('3D Structure Factors')
959        Page = G2frame.G2plotNB.nb.GetPage(plotNum)       
960    except ValueError:
961        Plot = G2frame.G2plotNB.addOgl('3D Structure Factors')
962        plotNum = G2frame.G2plotNB.plotList.index('3D Structure Factors')
963        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
964        Page.views = False
965        view = False
966        altDown = False
967    Font = Page.GetFont()
968    Page.SetFocus()
969    Page.Choice = None
970    choice = [' save as/key:','jpeg','tiff','bmp','h: view down h','k: view down k','l: view down l',
971    'z: zero zone toggle','c: reset to default','b: toggle box ','+: increase scale','-: decrease scale',
972    'f: Fobs','s: Fobs**2','u: unit','d: Fo-Fc','w: DF/sig','i: toggle intensity scaling']
973    cb = wx.ComboBox(G2frame.G2plotNB.status,style=wx.CB_DROPDOWN|wx.CB_READONLY,choices=choice)
974    cb.Bind(wx.EVT_COMBOBOX, OnKeyBox)
975    cb.SetValue(' save as/key:')
976    Page.canvas.Bind(wx.EVT_MOUSEWHEEL, OnMouseWheel)
977    Page.canvas.Bind(wx.EVT_LEFT_DOWN, OnMouseDown)
978    Page.canvas.Bind(wx.EVT_RIGHT_DOWN, OnMouseDown)
979    Page.canvas.Bind(wx.EVT_MIDDLE_DOWN, OnMouseDown)
980    Page.canvas.Bind(wx.EVT_KEY_UP, OnKey)
981    Page.canvas.Bind(wx.EVT_MOTION, OnMouseMove)
982#    Page.canvas.Bind(wx.EVT_SIZE, OnSize)
983    Page.camera['position'] = drawingData['cameraPos']
984    Page.camera['viewPoint'] = np.inner(Amat,drawingData['viewPoint'][0])
985    Page.camera['backColor'] = np.array(list(drawingData['backColor'])+[0,])/255.
986    Page.controls = Data
987    try:
988        Page.canvas.SetCurrent()
989    except:
990        pass
991    Draw('main')
992#    if firstCall: Draw('main') # draw twice the first time that graphics are displayed
993
994       
995################################################################################
996##### PlotPatterns
997################################################################################
998           
999def PlotPatterns(G2frame,newPlot=False,plotType='PWDR'):
1000    '''Powder pattern plotting package - displays single or multiple powder patterns as intensity vs
1001    2-theta, q or TOF. Can display multiple patterns as "waterfall plots" or contour plots. Log I
1002    plotting available.
1003    '''
1004    global exclLines
1005    global DifLine
1006    global Ymax
1007    plottype = plotType
1008#patch
1009    data = G2frame.PatternTree.GetItemPyData(G2frame.PatternId)
1010    if 'Offset' not in data[0] and plotType in ['PWDR','SASD']:     #plot offset data
1011        data[0].update({'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,
1012            'refDelt':0.01,})
1013        G2frame.PatternTree.SetItemPyData(G2frame.PickId,data)
1014#end patch
1015    def OnPlotKeyPress(event):
1016        try:        #one way to check if key stroke will work on plot
1017            Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters'))
1018        except TypeError:
1019            G2frame.G2plotNB.status.SetStatusText('Select '+plottype+' pattern first',1)
1020            return
1021        newPlot = False
1022        if event.key == 'w':
1023            G2frame.Weight = not G2frame.Weight
1024            if not G2frame.Weight and 'PWDR' in plottype:
1025                G2frame.SinglePlot = True
1026            newPlot = True
1027        elif event.key == 'e' and 'SASD' in plottype:
1028            G2frame.ErrorBars = not G2frame.ErrorBars
1029        elif event.key == 'b':
1030            G2frame.SubBack = not G2frame.SubBack
1031            if not G2frame.SubBack:
1032                G2frame.SinglePlot = True               
1033        elif event.key == 'n':
1034            if G2frame.Contour:
1035                pass
1036            else:
1037                G2frame.logPlot = not G2frame.logPlot
1038                if not G2frame.logPlot:
1039                    Pattern[0]['Offset'][0] = 0
1040                newPlot = True
1041        elif event.key == 's' and 'PWDR' in plottype:
1042            if G2frame.Contour:
1043                choice = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
1044                choice.sort()
1045                dlg = wx.SingleChoiceDialog(G2frame,'Select','Color scheme',choice)
1046                if dlg.ShowModal() == wx.ID_OK:
1047                    sel = dlg.GetSelection()
1048                    G2frame.ContourColor = choice[sel]
1049                else:
1050                    G2frame.ContourColor = 'Paired'
1051                dlg.Destroy()
1052            elif G2frame.SinglePlot:
1053                G2frame.plotStyle['sqrtPlot'] = not G2frame.plotStyle['sqrtPlot']
1054                if G2frame.plotStyle['sqrtPlot']:
1055                    Pattern[0]['delOffset'] = .002
1056                    Pattern[0]['refOffset'] = -1.0
1057                    Pattern[0]['refDelt'] = .001
1058                else:
1059                    Pattern[0]['delOffset'] = .02
1060                    Pattern[0]['refOffset'] = -1.0
1061                    Pattern[0]['refDelt'] = .01
1062            newPlot = True
1063        elif event.key == 'u' and (G2frame.Contour or not G2frame.SinglePlot):
1064            if G2frame.Contour:
1065                G2frame.Cmax = min(1.0,G2frame.Cmax*1.2)
1066            elif Pattern[0]['Offset'][0] < 100.:
1067                Pattern[0]['Offset'][0] += 1.
1068        elif event.key == 'd' and (G2frame.Contour or not G2frame.SinglePlot):
1069            if G2frame.Contour:
1070                G2frame.Cmax = max(0.0,G2frame.Cmax*0.8)
1071            elif Pattern[0]['Offset'][0] > 0.:
1072                Pattern[0]['Offset'][0] -= 1.
1073        elif event.key == 'l' and not G2frame.SinglePlot:
1074            Pattern[0]['Offset'][1] -= 1.
1075        elif event.key == 'r' and not G2frame.SinglePlot:
1076            Pattern[0]['Offset'][1] += 1.
1077        elif event.key == 'o' and not G2frame.SinglePlot:
1078            G2frame.Cmax = 1.0
1079            Pattern[0]['Offset'] = [0,0]
1080        elif event.key == 'c' and 'PWDR' in plottype:
1081            newPlot = True
1082            if not G2frame.Contour:
1083                G2frame.SinglePlot = False
1084                Pattern[0]['Offset'] = [0.,0.]
1085            else:
1086                G2frame.SinglePlot = True               
1087            G2frame.Contour = not G2frame.Contour
1088        elif event.key == 'q': 
1089            if 'PWDR' in plottype:
1090                newPlot = True
1091                G2frame.plotStyle['qPlot'] = not G2frame.plotStyle['qPlot']
1092                G2frame.plotStyle['dPlot'] = False
1093            elif 'SASD' in plottype:
1094                newPlot = True
1095                G2frame.plotStyle['sqPlot'] = not G2frame.plotStyle['sqPlot']
1096        elif event.key == 't' and 'PWDR' in plottype:
1097            G2frame.plotStyle['dPlot'] = not G2frame.plotStyle['dPlot']
1098            G2frame.plotStyle['qPlot'] = False
1099            newPlot = True     
1100        elif event.key == 'm':
1101            G2frame.plotStyle['sqrtPlot'] = False
1102            G2frame.SinglePlot = not G2frame.SinglePlot               
1103            newPlot = True
1104        elif event.key in ['+','=']:
1105            if G2frame.PickId:
1106                G2frame.PickId = False
1107        elif event.key == 'i' and G2frame.Contour:                  #for smoothing contour plot
1108            choice = ['nearest','bilinear','bicubic','spline16','spline36','hanning',
1109               'hamming','hermite','kaiser','quadric','catrom','gaussian','bessel',
1110               'mitchell','sinc','lanczos']
1111            dlg = wx.SingleChoiceDialog(G2frame,'Select','Interpolation',choice)
1112            if dlg.ShowModal() == wx.ID_OK:
1113                sel = dlg.GetSelection()
1114                G2frame.Interpolate = choice[sel]
1115            else:
1116                G2frame.Interpolate = 'nearest'
1117            dlg.Destroy()
1118        else:
1119#            print 'no binding for key',event.key
1120            #GSASIIpath.IPyBreak()
1121            return
1122        wx.CallAfter(PlotPatterns,G2frame,newPlot=newPlot,plotType=plottype)
1123       
1124    def OnMotion(event):
1125        xpos = event.xdata
1126        if xpos:                                        #avoid out of frame mouse position
1127            ypos = event.ydata
1128            Page.canvas.SetCursor(wx.CROSS_CURSOR)
1129            try:
1130                Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters'))
1131                if G2frame.plotStyle['qPlot'] and 'PWDR' in plottype:
1132                    q = xpos
1133                    dsp = 2.*np.pi/q
1134                    try:
1135                        xpos = G2lat.Dsp2pos(Parms,2.0*np.pi/xpos)
1136                    except ValueError:      #avoid bad value in asin beyond upper limit
1137                        pass
1138                elif 'SASD' in plottype:
1139                    q = xpos
1140                    dsp = 2.*np.pi/q
1141                elif G2frame.plotStyle['dPlot']:
1142                    dsp = xpos
1143                    q = 2.*np.pi/dsp
1144                    xpos = G2lat.Dsp2pos(Parms,xpos)
1145                elif G2frame.Contour and 'T' in Parms['Type'][0]:
1146                    xpos = X[xpos]                   
1147                    dsp = G2lat.Pos2dsp(Parms,xpos)
1148                    q = 2.*np.pi/dsp
1149                else:
1150                    dsp = G2lat.Pos2dsp(Parms,xpos)
1151                    q = 2.*np.pi/dsp
1152                if G2frame.Contour: #PWDR only
1153                    if 'C' in Parms['Type'][0]:
1154                        G2frame.G2plotNB.status.SetStatusText('2-theta =%9.3f d =%9.5f q = %9.5f pattern ID =%5d'%(xpos,dsp,q,int(ypos)),1)
1155                    else:
1156                        G2frame.G2plotNB.status.SetStatusText('TOF =%9.3f d =%9.5f q = %9.5f pattern ID =%5d'%(xpos,dsp,q,int(ypos)),1)
1157                else:
1158                    if 'C' in Parms['Type'][0]:
1159                        if 'PWDR' in plottype:
1160                            if G2frame.plotStyle['sqrtPlot']:
1161                                G2frame.G2plotNB.status.SetStatusText('2-theta =%9.3f d =%9.5f q = %9.5f sqrt(Intensity) =%9.2f'%(xpos,dsp,q,ypos),1)
1162                            else:
1163                                G2frame.G2plotNB.status.SetStatusText('2-theta =%9.3f d =%9.5f q = %9.5f Intensity =%9.2f'%(xpos,dsp,q,ypos),1)
1164                        elif 'SASD' in plottype:
1165                            G2frame.G2plotNB.status.SetStatusText('q =%12.5g Intensity =%12.5g d =%9.1f'%(q,ypos,dsp),1)
1166                    else:
1167                        if G2frame.plotStyle['sqrtPlot']:
1168                            G2frame.G2plotNB.status.SetStatusText('TOF =%9.3f d =%9.5f q =%9.5f sqrt(Intensity) =%9.2f'%(xpos,dsp,q,ypos),1)
1169                        else:
1170                            G2frame.G2plotNB.status.SetStatusText('TOF =%9.3f d =%9.5f q =%9.5f Intensity =%9.2f'%(xpos,dsp,q,ypos),1)
1171                if G2frame.itemPicked:
1172                    Page.canvas.SetToolTipString('%9.5f'%(xpos))
1173                if G2frame.PickId:
1174                    found = []
1175                    pickIdText = G2frame.PatternTree.GetItemText(G2frame.PickId)
1176                    if pickIdText in ['Index Peak List','Unit Cells List','Reflection Lists'] or \
1177                        'PWDR' in pickIdText:
1178                        indx = -1
1179                        if pickIdText in ['Index Peak List','Unit Cells List',]:
1180                            indx = -2
1181                        if len(G2frame.HKL):
1182                            view = Page.toolbar._views.forward()[0][:2]
1183                            wid = view[1]-view[0]
1184                            found = G2frame.HKL[np.where(np.fabs(G2frame.HKL.T[indx]-xpos) < 0.002*wid)]
1185                        if len(found):
1186                            if len(found[0]) > 6:   #SS reflections
1187                                h,k,l,m = found[0][:4]
1188                                Page.canvas.SetToolTipString('%d,%d,%d,%d'%(int(h),int(k),int(l),int(m)))
1189                            else:
1190                                h,k,l = found[0][:3] 
1191                                Page.canvas.SetToolTipString('%d,%d,%d'%(int(h),int(k),int(l)))
1192                        else:
1193                            Page.canvas.SetToolTipString('')
1194
1195            except TypeError:
1196                G2frame.G2plotNB.status.SetStatusText('Select '+plottype+' pattern first',1)
1197               
1198    def OnPress(event): #ugh - this removes a matplotlib error for mouse clicks in log plots                 
1199        olderr = np.seterr(invalid='ignore')
1200                                                   
1201    def OnPick(event):
1202        '''Respond to an item being picked. This usually means that the item
1203        will be dragged with the mouse.
1204        '''
1205        def OnDragMarker(event):
1206            '''Respond to dragging of a plot Marker
1207            '''
1208            Page.canvas.restore_region(savedplot)
1209            G2frame.itemPicked.set_data([event.xdata], [event.ydata])
1210            Page.figure.gca().draw_artist(G2frame.itemPicked)
1211            Page.canvas.blit(Page.figure.gca().bbox)
1212        def OnDragLine(event):
1213            '''Respond to dragging of a plot line
1214            '''
1215            Page.canvas.restore_region(savedplot)
1216            coords = G2frame.itemPicked.get_data()
1217            coords[0][0] = coords[0][1] = event.xdata
1218            coords = G2frame.itemPicked.set_data(coords)
1219            Page.figure.gca().draw_artist(G2frame.itemPicked)
1220            Page.canvas.blit(Page.figure.gca().bbox)
1221
1222        if G2frame.itemPicked is not None: return
1223        PatternId = G2frame.PatternId
1224        try:
1225            Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters'))
1226        except TypeError:
1227            return
1228        PickId = G2frame.PickId
1229        pick = event.artist
1230        mouse = event.mouseevent
1231        xpos = pick.get_xdata()
1232        ypos = pick.get_ydata()
1233        ind = event.ind
1234        xy = list(zip(np.take(xpos,ind),np.take(ypos,ind))[0])
1235        # convert from plot units
1236        if G2frame.plotStyle['qPlot']:                              #qplot - convert back to 2-theta
1237            xy[0] = G2lat.Dsp2pos(Parms,2*np.pi/xy[0])
1238        elif G2frame.plotStyle['dPlot']:                            #dplot - convert back to 2-theta
1239            xy[0] = G2lat.Dsp2pos(Parms,xy[0])
1240        if G2frame.plotStyle['sqrtPlot']:
1241            xy[1] = xy[1]**2
1242        if G2frame.PatternTree.GetItemText(PickId) == 'Peak List':
1243            if ind.all() != [0] and ObsLine[0].get_label() in str(pick):                                    #picked a data point
1244                data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
1245                XY = G2mth.setPeakparms(Parms,Parms2,xy[0],xy[1])
1246                data['peaks'].append(XY)
1247                data['sigDict'] = {}    #now invalid
1248                G2pdG.UpdatePeakGrid(G2frame,data)
1249                PlotPatterns(G2frame,plotType=plottype)
1250            else:                                                   #picked a peak list line
1251                G2frame.itemPicked = pick
1252        elif G2frame.PatternTree.GetItemText(PickId) == 'Limits':
1253            if ind.all() != [0]:                                    #picked a data point
1254                LimitId = G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Limits')
1255                data = G2frame.PatternTree.GetItemPyData(LimitId)
1256                if G2frame.plotStyle['qPlot']:                              #qplot - convert back to 2-theta
1257                    xy[0] = G2lat.Dsp2pos(Parms,2*np.pi/xy[0])
1258                elif G2frame.plotStyle['dPlot']:                            #dplot - convert back to 2-theta
1259                    xy[0] = G2lat.Dsp2pos(Parms,xy[0])
1260                if G2frame.ifGetExclude:
1261                    excl = [0,0]
1262                    excl[0] = max(data[1][0],min(xy[0],data[1][1]))
1263                    excl[1] = excl[0]+0.1
1264                    data.append(excl)
1265                    G2frame.ifGetExclude = False
1266                else:
1267                    if mouse.button==1:
1268                        data[1][0] = min(xy[0],data[1][1])
1269                    if mouse.button==3:
1270                        data[1][1] = max(xy[0],data[1][0])
1271                G2frame.PatternTree.SetItemPyData(LimitId,data)
1272                G2pdG.UpdateLimitsGrid(G2frame,data,plottype)
1273                wx.CallAfter(PlotPatterns,G2frame,plotType=plottype)
1274            else:                                                   #picked a limit line
1275                # prepare to animate move of line
1276                G2frame.itemPicked = pick
1277                pick.set_linestyle(':') # set line as dotted
1278                Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1279                Plot = Page.figure.gca()
1280                Page.canvas.draw() # refresh without dotted line & save bitmap
1281                savedplot = Page.canvas.copy_from_bbox(Page.figure.gca().bbox)
1282                G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragLine)
1283                pick.set_linestyle('--') # back to dashed
1284               
1285        elif G2frame.PatternTree.GetItemText(PickId) == 'Models':
1286            if ind.all() != [0]:                                    #picked a data point
1287                LimitId = G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Limits')
1288                data = G2frame.PatternTree.GetItemPyData(LimitId)
1289                if mouse.button==1:
1290                    data[1][0] = min(xy[0],data[1][1])
1291                if mouse.button==3:
1292                    data[1][1] = max(xy[0],data[1][0])
1293                G2frame.PatternTree.SetItemPyData(LimitId,data)
1294                wx.CallAfter(PlotPatterns,G2frame,plotType=plottype)
1295            else:                                                   #picked a limit line
1296                G2frame.itemPicked = pick
1297        elif (G2frame.PatternTree.GetItemText(PickId) == 'Reflection Lists' or
1298                'PWDR' in G2frame.PatternTree.GetItemText(PickId)
1299                ):
1300            G2frame.itemPicked = pick
1301            pick = str(pick)
1302        elif G2frame.PatternTree.GetItemText(PickId) == 'Background':
1303            # selected a fixed background point. Can move it or delete it.
1304            for mode,id in G2frame.dataFrame.wxID_BackPts.iteritems(): # what menu is selected?
1305                if G2frame.dataFrame.BackMenu.FindItemById(id).IsChecked():
1306                    break
1307            # mode will be 'Add' or 'Move' or 'Del'
1308            if pick.get_marker() == 'D':
1309                # find the closest point
1310                backDict = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Background'))[1]
1311                d2 = [(x-xy[0])**2+(y-xy[1])**2 for x,y in backDict['FixedPoints']]
1312                G2frame.fixPtMarker = d2.index(min(d2))
1313                if mode == 'Move':
1314                    # animate move of FixedBkg marker
1315                    G2frame.itemPicked = pick
1316                    pick.set_marker('|') # change the point appearance
1317                    Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1318                    Plot = Page.figure.gca()
1319                    Page.canvas.draw() # refresh with changed point & save bitmap
1320                    savedplot = Page.canvas.copy_from_bbox(Page.figure.gca().bbox)
1321                    G2frame.cid = Page.canvas.mpl_connect('motion_notify_event', OnDragMarker)
1322                    pick.set_marker('D') # put it back
1323                elif mode == 'Del':
1324                    del backDict['FixedPoints'][G2frame.fixPtMarker]
1325                    wx.CallAfter(PlotPatterns,G2frame,plotType=plottype)
1326                return
1327    def OnRelease(event): # mouse release from item pick or background pt add/move/del
1328        plotNum = G2frame.G2plotNB.plotList.index('Powder Patterns')
1329        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1330        if G2frame.cid is not None:         # if there is a drag connection, delete it
1331            Page.canvas.mpl_disconnect(G2frame.cid)
1332            G2frame.cid = None
1333        PickId = G2frame.PickId                             # points to item in tree
1334        if G2frame.PatternTree.GetItemText(PickId) == 'Background' and event.xdata:
1335            if Page.toolbar._active:    # prevent ops. if a toolbar zoom button pressed
1336                return 
1337            # Background page, deal with fixed background points
1338            if G2frame.SubBack or G2frame.Weight or G2frame.Contour or not G2frame.SinglePlot:
1339                return
1340            backDict = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Background'))[1]
1341            if 'FixedPoints' not in backDict: backDict['FixedPoints'] = []
1342            try:
1343                Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters'))
1344            except TypeError:
1345                return
1346            # unit conversions
1347            xy = [event.xdata,event.ydata]
1348            if G2frame.plotStyle['qPlot']:                              #qplot - convert back to 2-theta
1349                xy[0] = G2lat.Dsp2pos(Parms,2*np.pi/xy[0])
1350            elif G2frame.plotStyle['dPlot']:                            #dplot - convert back to 2-theta
1351                xy[0] = G2lat.Dsp2pos(Parms,xy[0])
1352            if G2frame.plotStyle['sqrtPlot']:
1353                xy[1] = xy[1]**2
1354            for mode,id in G2frame.dataFrame.wxID_BackPts.iteritems(): # what menu item is selected?
1355                if G2frame.dataFrame.BackMenu.FindItemById(id).IsChecked():
1356                    break
1357            if mode == 'Add':
1358                backDict['FixedPoints'].append(xy)
1359                Plot = Page.figure.gca()
1360                Plot.plot(event.xdata,event.ydata,'rD',clip_on=False,picker=3.)
1361                Page.canvas.draw()
1362                return
1363            elif G2frame.itemPicked is not None: # end of drag in move
1364                backDict['FixedPoints'][G2frame.fixPtMarker] = xy
1365                G2frame.itemPicked = None
1366                wx.CallAfter(PlotPatterns,G2frame,plotType=plottype)
1367                return
1368       
1369        if G2frame.itemPicked is None: return
1370        if str(DifLine[0]) == str(G2frame.itemPicked):
1371            data = G2frame.PatternTree.GetItemPyData(PickId)
1372            ypos = event.ydata
1373            Pattern[0]['delOffset'] = -ypos/Ymax
1374            G2frame.itemPicked = None
1375            wx.CallAfter(PlotPatterns,G2frame,plotType=plottype)
1376            return
1377        Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters'))
1378        xpos = event.xdata
1379        if G2frame.PatternTree.GetItemText(PickId) in ['Peak List','Limits'] and xpos:
1380            lines = []
1381            for line in G2frame.Lines: 
1382                lines.append(line.get_xdata()[0])
1383            try:
1384                lineNo = lines.index(G2frame.itemPicked.get_xdata()[0])
1385            except ValueError:
1386                lineNo = -1
1387            if  lineNo in [0,1] or lineNo in exclLines:
1388                LimitId = G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Limits')
1389                limits = G2frame.PatternTree.GetItemPyData(LimitId)
1390                id = lineNo/2+1
1391                id2 = lineNo%2
1392                if G2frame.plotStyle['qPlot'] and 'PWDR' in plottype:
1393                    limits[id][id2] = G2lat.Dsp2pos(Parms,2.*np.pi/xpos)
1394                elif G2frame.plotStyle['dPlot'] and 'PWDR' in plottype:
1395                    limits[id][id2] = G2lat.Dsp2pos(Parms,xpos)
1396                else:
1397                    limits[id][id2] = xpos
1398                if id > 1 and limits[id][0] > limits[id][1]:
1399                        limits[id].reverse()
1400                limits[1][0] = min(max(limits[0][0],limits[1][0]),limits[1][1])
1401                limits[1][1] = max(min(limits[0][1],limits[1][1]),limits[1][0])
1402                if G2frame.PatternTree.GetItemText(G2frame.PickId) == 'Limits':
1403                    G2pdG.UpdateLimitsGrid(G2frame,limits,plottype)
1404            elif lineNo > 1:
1405                PeakId = G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Peak List')
1406                peaks = G2frame.PatternTree.GetItemPyData(PeakId)
1407                if event.button == 3:
1408                    del peaks['peaks'][lineNo-2]
1409                else:
1410                    if G2frame.plotStyle['qPlot']:
1411                        peaks['peaks'][lineNo-2][0] = G2lat.Dsp2pos(Parms,2.*np.pi/xpos)
1412                    elif G2frame.plotStyle['dPlot']:
1413                        peaks['peaks'][lineNo-2][0] = G2lat.Dsp2pos(Parms,xpos)
1414                    else:
1415                        peaks['peaks'][lineNo-2][0] = xpos
1416                    peaks['sigDict'] = {}        #no longer valid
1417                G2pdG.UpdatePeakGrid(G2frame,peaks)
1418        elif G2frame.PatternTree.GetItemText(PickId) in ['Models',] and xpos:
1419            lines = []
1420            for line in G2frame.Lines: 
1421                lines.append(line.get_xdata()[0])
1422            try:
1423                lineNo = lines.index(G2frame.itemPicked.get_xdata()[0])
1424            except ValueError:
1425                lineNo = -1
1426            if  lineNo in [0,1]:
1427                LimitId = G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Limits')
1428                data = G2frame.PatternTree.GetItemPyData(LimitId)
1429                data[1][lineNo] = xpos
1430                data[1][0] = min(max(data[0][0],data[1][0]),data[1][1])
1431                data[1][1] = max(min(data[0][1],data[1][1]),data[1][0])
1432        elif (G2frame.PatternTree.GetItemText(PickId) == 'Reflection Lists' or \
1433            'PWDR' in G2frame.PatternTree.GetItemText(PickId)) and xpos:
1434            Id = G2gd.GetPatternTreeItemId(G2frame,PatternId,'Reflection Lists')
1435#            GSASIIpath.IPyBreak()
1436            if Id:     
1437                Phases = G2frame.PatternTree.GetItemPyData(Id)
1438                pick = str(G2frame.itemPicked).split('(')[1].strip(')')
1439                if 'line' not in pick:       #avoid data points, etc.
1440                    data = G2frame.PatternTree.GetItemPyData(PatternId)
1441                    num = Phases.keys().index(pick)
1442                    if num:
1443                        data[0]['refDelt'] = -(event.ydata-Pattern[0]['refOffset'])/(num*Ymax)
1444                    else:       #1st row of refl ticks
1445                        data[0]['refOffset'] = event.ydata
1446        PlotPatterns(G2frame,plotType=plottype)
1447        G2frame.itemPicked = None   
1448
1449    # beginning PlotPatterns execution
1450    try:
1451        plotNum = G2frame.G2plotNB.plotList.index('Powder Patterns')
1452        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1453        Plot = Page.figure.gca()          #get previous powder plot & get limits
1454        G2frame.xylim = Plot.get_xlim(),Plot.get_ylim()
1455        Page.figure.clf()
1456        Plot = Page.figure.gca()          #get a fresh plot after clf()
1457    except ValueError:
1458        if plottype == 'SASD':
1459            G2frame.logPlot = True
1460            G2frame.ErrorBars = True
1461        newPlot = True
1462        G2frame.Cmax = 1.0
1463        Plot = G2frame.G2plotNB.addMpl('Powder Patterns').gca()
1464        plotNum = G2frame.G2plotNB.plotList.index('Powder Patterns')
1465        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1466        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
1467        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
1468        Page.canvas.mpl_connect('pick_event', OnPick)
1469        Page.canvas.mpl_connect('button_release_event', OnRelease)
1470        Page.canvas.mpl_connect('button_press_event',OnPress)
1471    if plottype == 'PWDR':  # avoids a very nasty clash with KILL_FOCUS in SASD TextCtrl?
1472        Page.SetFocus()
1473    G2frame.G2plotNB.status.DestroyChildren()
1474    if G2frame.Contour:
1475        Page.Choice = (' key press','d: lower contour max','u: raise contour max','o: reset contour max',
1476            'i: interpolation method','s: color scheme','c: contour off')
1477    else:
1478        if G2frame.logPlot:
1479            if 'PWDR' in plottype:
1480                if G2frame.SinglePlot:
1481                    Page.Choice = (' key press','n: log(I) off',
1482                        'c: contour on','q: toggle q plot','t: toggle d-spacing plot',
1483                            'm: toggle multidata plot','w: toggle divide by sig','+: no selection')
1484                else:
1485                    Page.Choice = (' key press','n: log(I) off',
1486                        'd: offset down','l: offset left','r: offset right','u: offset up','o: reset offset',
1487                        'c: contour on','q: toggle q plot','t: toggle d-spacing plot',
1488                        'm: toggle multidata plot','w: toggle divide by sig','+: no selection')
1489            elif 'SASD' in plottype:
1490                if G2frame.SinglePlot:
1491                    Page.Choice = (' key press','b: toggle subtract background file','n: semilog on',
1492                        'q: toggle S(q) plot','m: toggle multidata plot','w: toggle (Io-Ic)/sig plot','+: no selection')
1493                else:
1494                    Page.Choice = (' key press','b: toggle subtract background file','n: semilog on',
1495                        'd: offset down','l: offset left','r: offset right','u: offset up','o: reset offset',
1496                        'q: toggle S(q) plot','m: toggle multidata plot','w: toggle (Io-Ic)/sig plot','+: no selection')
1497        else:
1498            if 'PWDR' in plottype:
1499                if G2frame.SinglePlot:
1500                    Page.Choice = (' key press',
1501                        'b: toggle subtract background','n: log(I) on','s: toggle sqrt plot','c: contour on',
1502                        'q: toggle q plot','t: toggle d-spacing plot','m: toggle multidata plot',
1503                        'w: toggle divide by sig','+: no selection')
1504                else:
1505                    Page.Choice = (' key press','l: offset left','r: offset right','d: offset down',
1506                        'u: offset up','o: reset offset','b: toggle subtract background','n: log(I) on','c: contour on',
1507                        'q: toggle q plot','t: toggle d-spacing plot','m: toggle multidata plot',
1508                        'w: toggle divide by sig','+: no selection')
1509            elif 'SASD' in plottype:
1510                if G2frame.SinglePlot:
1511                    Page.Choice = (' key press','b: toggle subtract background file','n: loglog on','e: toggle error bars',
1512                        'q: toggle S(q) plot','m: toggle multidata plot','w: toggle (Io-Ic)/sig plot','+: no selection')
1513                else:
1514                    Page.Choice = (' key press','b: toggle subtract background file','n: loglog on','e: toggle error bars',
1515                        'd: offset down','l: offset left','r: offset right','u: offset up','o: reset offset',
1516                        'q: toggle S(q) plot','m: toggle multidata plot','w: toggle (Io-Ic)/sig plot','+: no selection')
1517    G2frame.cid = None
1518    Page.keyPress = OnPlotKeyPress   
1519    PickId = G2frame.PickId
1520    PatternId = G2frame.PatternId
1521    colors=['b','g','r','c','m','k']
1522    Lines = []
1523    exclLines = []
1524    if G2frame.SinglePlot and PatternId:
1525        Pattern = G2frame.PatternTree.GetItemPyData(PatternId)
1526        Pattern.append(G2frame.PatternTree.GetItemText(PatternId))
1527        PlotList = [Pattern,]
1528        Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,
1529            G2frame.PatternId, 'Instrument Parameters'))
1530        Sample = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Sample Parameters'))
1531        ParmList = [Parms,]
1532        SampleList = [Sample,]
1533        Title = Pattern[-1]
1534    else:       
1535        Title = os.path.split(G2frame.GSASprojectfile)[1]
1536        PlotList = []
1537        ParmList = []
1538        SampleList = []
1539        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1540        while item:
1541            if plottype in G2frame.PatternTree.GetItemText(item):
1542                Pattern = G2frame.PatternTree.GetItemPyData(item)
1543                if len(Pattern) < 3:                    # put name on end if needed
1544                    Pattern.append(G2frame.PatternTree.GetItemText(item))
1545                if 'Offset' not in Pattern[0]:     #plot offset data
1546                    Pattern[0].update({'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,})
1547                PlotList.append(Pattern)
1548                ParmList.append(G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,
1549                    item,'Instrument Parameters'))[0])
1550                SampleList.append(G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,
1551                    item, 'Sample Parameters')))
1552            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)               
1553    lenX = 0
1554    Ymax = None
1555    for Pattern in PlotList:
1556        xye = Pattern[1]
1557        if xye[1] is None: continue
1558        if Ymax is None: Ymax = max(xye[1])
1559        Ymax = max(Ymax,max(xye[1]))
1560    if Ymax is None: return # nothing to plot
1561    offsetX = Pattern[0]['Offset'][1]
1562    offsetY = Pattern[0]['Offset'][0]
1563    if G2frame.logPlot:
1564        Title = 'log('+Title+')'
1565    Plot.set_title(Title)
1566    if G2frame.plotStyle['qPlot'] or 'SASD' in plottype and not G2frame.Contour:
1567        Plot.set_xlabel(r'$Q, \AA^{-1}$',fontsize=16)
1568    elif G2frame.plotStyle['dPlot'] and 'PWDR' in plottype and not G2frame.Contour:
1569        Plot.set_xlabel(r'$d, \AA$',fontsize=16)
1570    else:
1571        if 'C' in ParmList[0]['Type'][0]:       
1572            Plot.set_xlabel(r'$\mathsf{2\theta}$',fontsize=16)
1573        else:
1574            if G2frame.Contour:
1575                Plot.set_xlabel(r'Channel no.',fontsize=16)           
1576            else:
1577                Plot.set_xlabel(r'$TOF, \mathsf{\mu}$s',fontsize=16)           
1578    if G2frame.Weight:
1579        if 'PWDR' in plottype:
1580            Plot.set_ylabel(r'$\mathsf{I/\sigma(I)}$',fontsize=16)
1581        elif 'SASD' in plottype:
1582            Plot.set_ylabel(r'$\mathsf{\Delta(I)/\sigma(I)}$',fontsize=16)
1583    else:
1584        if 'C' in ParmList[0]['Type'][0]:
1585            if 'PWDR' in plottype:
1586                if G2frame.plotStyle['sqrtPlot']:
1587                    Plot.set_ylabel(r'$\sqrt{Intensity}$',fontsize=16)
1588                else:
1589                    Plot.set_ylabel(r'$Intensity$',fontsize=16)
1590            elif 'SASD' in plottype:
1591                if G2frame.sqPlot:
1592                    Plot.set_ylabel(r'$S(Q)=I*Q^{4}$',fontsize=16)
1593                else:
1594                    Plot.set_ylabel(r'$Intensity, cm^{-1}$',fontsize=16)
1595        else:
1596            if G2frame.plotStyle['sqrtPlot']:
1597                Plot.set_ylabel(r'$\sqrt{Normalized\ intensity}$',fontsize=16)
1598            else:
1599                Plot.set_ylabel(r'$Normalized\ intensity$',fontsize=16)
1600    if G2frame.Contour:
1601        ContourZ = []
1602        ContourY = []
1603        Nseq = 0
1604    for N,Pattern in enumerate(PlotList):
1605        Parms = ParmList[N]
1606        Sample = SampleList[N]
1607        if 'C' in Parms['Type'][0]:
1608            wave = G2mth.getWave(Parms)
1609        else:
1610            difC = Parms['difC'][1]
1611        ifpicked = False
1612        LimitId = 0
1613        if Pattern[1] is None: continue # skip over uncomputed simulations
1614        xye = ma.array(ma.getdata(Pattern[1]))
1615        Zero = Parms.get('Zero',[0.,0.])[1]
1616        if PickId:
1617            ifpicked = Pattern[2] == G2frame.PatternTree.GetItemText(PatternId)
1618            LimitId = G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId,'Limits')
1619            limits = G2frame.PatternTree.GetItemPyData(LimitId)
1620            excls = limits[2:]
1621            for excl in excls:
1622                xye[0] = ma.masked_inside(xye[0],excl[0],excl[1])
1623        if G2frame.plotStyle['qPlot'] and 'PWDR' in plottype:
1624            Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root, Pattern[2])
1625            X = 2.*np.pi/G2lat.Pos2dsp(Parms,xye[0])
1626        elif G2frame.plotStyle['dPlot'] and 'PWDR' in plottype:
1627            Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root, Pattern[2])
1628            X = G2lat.Pos2dsp(Parms,xye[0])
1629        else:
1630            X = xye[0]
1631        if not lenX:
1632            lenX = len(X)
1633        if 'PWDR' in plottype:
1634            if G2frame.plotStyle['sqrtPlot']:
1635                olderr = np.seterr(invalid='ignore') #get around sqrt(-ve) error
1636                Y = np.where(xye[1]>=0.,np.sqrt(xye[1]),-np.sqrt(-xye[1]))
1637                np.seterr(invalid=olderr['invalid'])
1638            else:
1639                Y = xye[1]+offsetY*N*Ymax/100.0
1640        elif 'SASD' in plottype:
1641            B = xye[5]
1642            if G2frame.sqPlot:
1643                Y = xye[1]*Sample['Scale'][0]*(1.05)**(offsetY*N)*X**4
1644            else:
1645                Y = xye[1]*Sample['Scale'][0]*(1.05)**(offsetY*N)
1646        if LimitId and ifpicked:
1647            limits = np.array(G2frame.PatternTree.GetItemPyData(LimitId))
1648            lims = limits[1]
1649            if G2frame.plotStyle['qPlot'] and 'PWDR' in plottype:
1650                lims = 2.*np.pi/G2lat.Pos2dsp(Parms,lims)
1651            elif G2frame.plotStyle['dPlot'] and 'PWDR' in plottype:
1652                lims = G2lat.Pos2dsp(Parms,lims)
1653            Lines.append(Plot.axvline(lims[0],color='g',dashes=(5,5),picker=3.))   
1654            Lines.append(Plot.axvline(lims[1],color='r',dashes=(5,5),picker=3.))
1655            for i,item in enumerate(limits[2:]):
1656                Lines.append(Plot.axvline(item[0],color='m',dashes=(5,5),picker=3.))   
1657                Lines.append(Plot.axvline(item[1],color='m',dashes=(5,5),picker=3.))
1658                exclLines += [2*i+2,2*i+3]
1659        if G2frame.Contour:           
1660            if lenX == len(X):
1661                ContourY.append(N)
1662                ContourZ.append(Y)
1663                if 'C' in ParmList[0]['Type'][0]:       
1664                    ContourX = X
1665                else: #'T'OF
1666                    ContourX = range(lenX)
1667                Nseq += 1
1668                Plot.set_ylabel('Data sequence',fontsize=12)
1669        else:
1670            if 'SASD' in plottype and G2frame.logPlot:
1671                X *= (1.01)**(offsetX*N)
1672            else:
1673                X += offsetX*.005*N
1674            Xum = ma.getdata(X)
1675            DifLine = ['']
1676            if ifpicked:
1677                if G2frame.plotStyle['sqrtPlot']:
1678                    olderr = np.seterr(invalid='ignore') #get around sqrt(-ve) error
1679                    Z = np.where(xye[3]>=0.,np.sqrt(xye[3]),-np.sqrt(-xye[3]))
1680                    np.seterr(invalid=olderr['invalid'])
1681                else:
1682                    Z = xye[3]+offsetY*N*Ymax/100.0
1683                if 'PWDR' in plottype:
1684                    if G2frame.plotStyle['sqrtPlot']:
1685                        olderr = np.seterr(invalid='ignore') #get around sqrt(-ve) error
1686                        W = np.where(xye[4]>=0.,np.sqrt(xye[4]),-np.sqrt(-xye[4]))
1687                        np.seterr(invalid=olderr['invalid'])
1688                        D = np.where(xye[5],(Y-Z),0.)-Ymax*Pattern[0]['delOffset']
1689                    else:
1690                        W = xye[4]+offsetY*N*Ymax/100.0
1691                        D = xye[5]-Ymax*Pattern[0]['delOffset']  #powder background
1692                elif 'SASD' in plottype:
1693                    if G2frame.sqPlot:
1694                        W = xye[4]*X**4
1695                        Z = xye[3]*X**4
1696                        B = B*X**4
1697                    else:
1698                        W = xye[4]
1699                    if G2frame.SubBack:
1700                        YB = Y-B
1701                        ZB = Z
1702                    else:
1703                        YB = Y
1704                        ZB = Z+B
1705                    Plot.set_yscale("log",nonposy='mask')
1706                    if np.any(W>0.):
1707                        Plot.set_ylim(bottom=np.min(np.trim_zeros(W))/2.,top=np.max(Y)*2.)
1708                    else:
1709                        Plot.set_ylim(bottom=np.min(np.trim_zeros(YB))/2.,top=np.max(Y)*2.)
1710                if G2frame.logPlot:
1711                    if 'PWDR' in plottype:
1712                        Plot.set_yscale("log",nonposy='mask')
1713                        Plot.plot(X,Y,colors[N%6]+'+',picker=3.,clip_on=False)
1714                        Plot.plot(X,Z,colors[(N+1)%6],picker=False)
1715                        Plot.plot(X,W,colors[(N+2)%6],picker=False)     #background
1716                    elif 'SASD' in plottype:
1717                        Plot.set_xscale("log",nonposx='mask')
1718                        Ibeg = np.searchsorted(X,limits[1][0])
1719                        Ifin = np.searchsorted(X,limits[1][1])
1720                        if G2frame.Weight:
1721                            Plot.set_yscale("linear")
1722                            DS = (YB-ZB)*np.sqrt(xye[2])
1723                            Plot.plot(X[Ibeg:Ifin],DS[Ibeg:Ifin],colors[(N+3)%6],picker=False)
1724                            Plot.axhline(0.,color=wx.BLACK)
1725                            Plot.set_ylim(bottom=np.min(DS[Ibeg:Ifin])*1.2,top=np.max(DS[Ibeg:Ifin])*1.2)                                                   
1726                        else:
1727                            Plot.set_yscale("log",nonposy='mask')
1728                            if G2frame.ErrorBars:
1729                                if G2frame.sqPlot:
1730                                    Plot.errorbar(X,YB,yerr=X**4*Sample['Scale'][0]*np.sqrt(1./(Pattern[0]['wtFactor']*xye[2])),
1731                                        ecolor=colors[N%6],picker=3.,clip_on=False)
1732                                else:
1733                                    Plot.errorbar(X,YB,yerr=Sample['Scale'][0]*np.sqrt(1./(Pattern[0]['wtFactor']*xye[2])),
1734                                        ecolor=colors[N%6],picker=3.,clip_on=False)
1735                            else:
1736                                Plot.plot(X,YB,colors[N%6]+'+',picker=3.,clip_on=False)
1737                            Plot.plot(X,W,colors[(N+2)%6],picker=False)     #const. background
1738                            Plot.plot(X,ZB,colors[(N+1)%6],picker=False)
1739                elif G2frame.Weight and 'PWDR' in plottype:
1740                    DY = xye[1]*np.sqrt(xye[2])
1741                    Ymax = max(DY)
1742                    DZ = xye[3]*np.sqrt(xye[2])
1743                    DS = xye[5]*np.sqrt(xye[2])-Ymax*Pattern[0]['delOffset']
1744                    ObsLine = Plot.plot(X,DY,colors[N%6]+'+',picker=3.,clip_on=False)         #Io/sig(Io)
1745                    Plot.plot(X,DZ,colors[(N+1)%6],picker=False)                    #Ic/sig(Io)
1746                    DifLine = Plot.plot(X,DS,colors[(N+3)%6],picker=1.)                    #(Io-Ic)/sig(Io)
1747                    Plot.axhline(0.,color=wx.BLACK)
1748                else:
1749                    if G2frame.SubBack:
1750                        if 'PWDR' in plottype:
1751                            Plot.plot(Xum,Y-W,colors[N%6]+'+',picker=False,clip_on=False)  #Io-Ib
1752                            Plot.plot(X,Z-W,colors[(N+1)%6],picker=False)               #Ic-Ib
1753                        else:
1754                            Plot.plot(X,YB,colors[N%6]+'+',picker=3.,clip_on=False)
1755                            Plot.plot(X,ZB,colors[(N+1)%6],picker=False)
1756                    else:
1757                        if 'PWDR' in plottype:
1758                            ObsLine = Plot.plot(Xum,Y,colors[N%6]+'+',picker=3.,clip_on=False)    #Io
1759                            Plot.plot(X,Z,colors[(N+1)%6],picker=False)                 #Ic
1760                        else:
1761                            Plot.plot(X,YB,colors[N%6]+'+',picker=3.,clip_on=False)
1762                            Plot.plot(X,ZB,colors[(N+1)%6],picker=False)
1763                    if 'PWDR' in plottype:
1764                        Plot.plot(X,W,colors[(N+2)%6],picker=False)                 #Ib
1765                        DifLine = Plot.plot(X,D,colors[(N+3)%6],picker=1.)                 #Io-Ic
1766                    Plot.axhline(0.,color=wx.BLACK)
1767                Page.canvas.SetToolTipString('')
1768                if PickId:
1769                    if G2frame.PatternTree.GetItemText(PickId) == 'Peak List':
1770                        tip = 'On data point: Pick peak - L or R MB. On line: L-move, R-delete'
1771                        Page.canvas.SetToolTipString(tip)
1772                        data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Peak List'))
1773                        for item in data['peaks']:
1774                            if G2frame.plotStyle['qPlot']:
1775                                Lines.append(Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,item[0]),color=colors[N%6],picker=2.))
1776                            elif G2frame.plotStyle['dPlot']:
1777                                Lines.append(Plot.axvline(G2lat.Pos2dsp(Parms,item[0]),color=colors[N%6],picker=2.))
1778                            else:
1779                                Lines.append(Plot.axvline(item[0],color=colors[N%6],picker=2.))
1780                    if G2frame.PatternTree.GetItemText(PickId) == 'Limits':
1781                        tip = 'On data point: Lower limit - L MB; Upper limit - R MB. On limit: MB down to move'
1782                        Page.canvas.SetToolTipString(tip)
1783                        data = G2frame.LimitsTable.GetData()
1784                       
1785            else:   #not picked
1786                if G2frame.logPlot:
1787                    if 'PWDR' in plottype:
1788                        Plot.semilogy(X,Y,colors[N%6],picker=False,nonposy='mask')
1789                    elif 'SASD' in plottype:
1790                        Plot.semilogy(X,Y,colors[N%6],picker=False,nonposy='mask')
1791                else:
1792                    if 'PWDR' in plottype:
1793                        Plot.plot(X,Y,colors[N%6],picker=False)
1794                    elif 'SASD' in plottype:
1795                        Plot.loglog(X,Y,colors[N%6],picker=False,nonposy='mask')
1796                        Plot.set_ylim(bottom=np.min(np.trim_zeros(Y))/2.,top=np.max(Y)*2.)
1797                           
1798                if G2frame.logPlot and 'PWDR' in plottype:
1799                    Plot.set_ylim(bottom=np.min(np.trim_zeros(Y))/2.,top=np.max(Y)*2.)
1800    if PickId and not G2frame.Contour:
1801        Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Instrument Parameters'))
1802        if G2frame.PatternTree.GetItemText(PickId) in ['Index Peak List','Unit Cells List']:
1803            peaks = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Index Peak List'))
1804            if not len(peaks): return # are there any peaks?
1805            for peak in peaks[0]:
1806                if peak[2]:
1807                    if G2frame.plotStyle['qPlot']:
1808                        Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,peak[0]),color='b')
1809                    if G2frame.plotStyle['dPlot']:
1810                        Plot.axvline(G2lat.Pos2dsp(Parms,peak[0]),color='b')
1811                    else:
1812                        Plot.axvline(peak[0],color='b')
1813            for hkl in G2frame.HKL:
1814                clr = 'r'
1815                if len(hkl) > 6 and hkl[3]:
1816                    clr = 'g'
1817                if G2frame.plotStyle['qPlot']:
1818                    Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,hkl[-2]),color=clr,dashes=(5,5))
1819                if G2frame.plotStyle['dPlot']:
1820                    Plot.axvline(G2lat.Pos2dsp(Parms,hkl[-2]),color=clr,dashes=(5,5))
1821                else:
1822                    Plot.axvline(hkl[-2],color=clr,dashes=(5,5))
1823        elif G2frame.PatternTree.GetItemText(PickId) in ['Reflection Lists'] or \
1824            'PWDR' in G2frame.PatternTree.GetItemText(PickId):
1825            refColors=['b','r','c','g','m','k']
1826            Phases = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId,'Reflection Lists'))
1827            for pId,phase in enumerate(Phases):
1828                peaks = Phases[phase].get('RefList',[])
1829                if not len(peaks):
1830                    continue
1831                if Phases[phase].get('Super',False):
1832                    peak = np.array([[peak[5],peak[6]] for peak in peaks])
1833                else:
1834                    peak = np.array([[peak[4],peak[5]] for peak in peaks])
1835                pos = Pattern[0]['refOffset']-pId*Ymax*Pattern[0]['refDelt']*np.ones_like(peak)
1836                if G2frame.plotStyle['qPlot']:
1837                    Plot.plot(2*np.pi/peak.T[0],pos,refColors[pId%6]+'|',mew=1,ms=8,picker=3.,label=phase)
1838                elif G2frame.plotStyle['dPlot']:
1839                    Plot.plot(peak.T[0],pos,refColors[pId%6]+'|',mew=1,ms=8,picker=3.,label=phase)
1840                else:
1841                    Plot.plot(peak.T[1],pos,refColors[pId%6]+'|',mew=1,ms=8,picker=3.,label=phase)
1842            if len(Phases):
1843                handles,legends = Plot.get_legend_handles_labels()  #got double entries in the legends for some reason
1844                if handles:
1845                    Plot.legend(handles[::2],legends[::2],title='Phases',loc='best')    #skip every other one
1846           
1847    if G2frame.Contour:
1848        acolor = mpl.cm.get_cmap(G2frame.ContourColor)
1849        Img = Plot.imshow(ContourZ,cmap=acolor,vmin=0,vmax=Ymax*G2frame.Cmax,interpolation=G2frame.Interpolate, 
1850            extent=[ContourX[0],ContourX[-1],ContourY[0],ContourY[-1]],aspect='auto',origin='lower')
1851        Page.figure.colorbar(Img)
1852    else:
1853        G2frame.Lines = Lines
1854    if G2frame.PatternTree.GetItemText(PickId) == 'Background':
1855        # plot fixed background points
1856        backDict = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Background'))[1]
1857        try:
1858            Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters'))
1859        except TypeError:
1860            Parms = None
1861        for x,y in backDict.get('FixedPoints',[]):
1862            # "normal" intensity modes only!
1863            if G2frame.SubBack or G2frame.Weight or G2frame.Contour or not G2frame.SinglePlot:
1864                break
1865            if y < 0 and (G2frame.plotStyle['sqrtPlot'] or G2frame.logPlot):
1866                y = Page.figure.gca().get_ylim()[0] # put out of range point at bottom of plot
1867            elif G2frame.plotStyle['sqrtPlot']:
1868                y = math.sqrt(y)
1869            if G2frame.plotStyle['qPlot']:     #Q - convert from 2-theta
1870                if Parms:
1871                    x = 2*np.pi/G2lat.Pos2dsp(Parms,x)
1872                else:
1873                    break
1874            elif G2frame.plotStyle['dPlot']:   #d - convert from 2-theta
1875                if Parms:
1876                    x = G2lat.Dsp2pos(Parms,x)
1877                else:
1878                    break
1879            Plot.plot(x,y,'rD',clip_on=False,picker=3.)
1880    if not newPlot:
1881        Page.toolbar.push_current()
1882        Plot.set_xlim(G2frame.xylim[0])
1883        Plot.set_ylim(G2frame.xylim[1])
1884#        xylim = []
1885        Page.toolbar.push_current()
1886        Page.toolbar.draw()
1887    else:
1888        G2frame.xylim = Plot.get_xlim(),Plot.get_ylim()
1889        Page.canvas.draw()
1890    olderr = np.seterr(invalid='ignore') #ugh - this removes a matplotlib error for mouse clicks in log plots
1891    # and sqrt(-ve) in np.where usage               
1892#    G2frame.Pwdr = True
1893   
1894################################################################################
1895##### PlotDeltSig
1896################################################################################
1897           
1898def PlotDeltSig(G2frame,kind):
1899    'needs a doc string'
1900    try:
1901        plotNum = G2frame.G2plotNB.plotList.index('Error analysis')
1902        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1903        Page.figure.clf()
1904        Plot = Page.figure.gca()          #get a fresh plot after clf()
1905    except ValueError:
1906        newPlot = True
1907        G2frame.Cmax = 1.0
1908        Plot = G2frame.G2plotNB.addMpl('Error analysis').gca()
1909        plotNum = G2frame.G2plotNB.plotList.index('Error analysis')
1910        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1911    Page.Choice = None
1912    PatternId = G2frame.PatternId
1913    Pattern = G2frame.PatternTree.GetItemPyData(PatternId)
1914    Pattern.append(G2frame.PatternTree.GetItemText(PatternId))
1915    wtFactor = Pattern[0]['wtFactor']
1916    if kind == 'PWDR':
1917        limits = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Limits'))[1]
1918        xye = np.array(Pattern[1])
1919        xmin = np.searchsorted(xye[0],limits[0])
1920        xmax = np.searchsorted(xye[0],limits[1])
1921        DS = xye[5][xmin:xmax]*np.sqrt(wtFactor*xye[2][xmin:xmax])
1922    elif kind == 'HKLF':
1923        refl = Pattern[1]['RefList']
1924        im = 0
1925        if Pattern[1]['Super']:
1926            im = 1
1927        DS = []
1928        for ref in refl:
1929            if ref[6+im] > 0.:
1930                DS.append((ref[5+im]-ref[7+im])/ref[6+im])
1931    Page.SetFocus()
1932    G2frame.G2plotNB.status.DestroyChildren()
1933    DS.sort()
1934    EDS = np.zeros_like(DS)
1935    DX = np.linspace(0.,1.,num=len(DS),endpoint=True)
1936    oldErr = np.seterr(invalid='ignore')    #avoid problem at DX==0
1937    T = np.sqrt(np.log(1.0/DX**2))
1938    top = 2.515517+0.802853*T+0.010328*T**2
1939    bot = 1.0+1.432788*T+0.189269*T**2+0.001308*T**3
1940    EDS = np.where(DX>0,-(T-top/bot),(T-top/bot))
1941    low1 = np.searchsorted(EDS,-1.)
1942    hi1 = np.searchsorted(EDS,1.)
1943    slp,intcp = np.polyfit(EDS[low1:hi1],DS[low1:hi1],deg=1)
1944    frac = 100.*(hi1-low1)/len(DS)
1945    G2frame.G2plotNB.status.SetStatusText(  \
1946        'Over range -1. to 1. :'+' slope = %.3f, intercept = %.3f for %.2f%% of the fitted data'%(slp,intcp,frac),1)
1947    Plot.set_title('Normal probability for '+Pattern[-1])
1948    Plot.set_xlabel(r'expected $\mathsf{\Delta/\sigma}$',fontsize=14)
1949    Plot.set_ylabel(r'observed $\mathsf{\Delta/\sigma}$',fontsize=14)
1950    Plot.plot(EDS,DS,'r+',label='result')
1951    Plot.plot([-2,2],[-2,2],'k',dashes=(5,5),label='ideal')
1952    Plot.legend(loc='upper left')
1953    np.seterr(invalid='warn')
1954    Page.canvas.draw()
1955       
1956################################################################################
1957##### PlotISFG
1958################################################################################
1959           
1960def PlotISFG(G2frame,newPlot=False,type=''):
1961    ''' Plotting package for PDF analysis; displays I(q), S(q), F(q) and G(r) as single
1962    or multiple plots with waterfall and contour plots as options
1963    '''
1964    if not type:
1965        type = G2frame.G2plotNB.plotList[G2frame.G2plotNB.nb.GetSelection()]
1966    if type not in ['I(Q)','S(Q)','F(Q)','G(R)']:
1967        return
1968    superMinusOne = unichr(0xaf)+unichr(0xb9)
1969   
1970    def OnPlotKeyPress(event):
1971        newPlot = False
1972        if event.key == 'u':
1973            if G2frame.Contour:
1974                G2frame.Cmax = min(1.0,G2frame.Cmax*1.2)
1975            elif Pattern[0]['Offset'][0] < 100.:
1976                Pattern[0]['Offset'][0] += 1.
1977        elif event.key == 'd':
1978            if G2frame.Contour:
1979                G2frame.Cmax = max(0.0,G2frame.Cmax*0.8)
1980            elif Pattern[0]['Offset'][0] > 0.:
1981                Pattern[0]['Offset'][0] -= 1.
1982        elif event.key == 'l':
1983            Pattern[0]['Offset'][1] -= 1.
1984        elif event.key == 'r':
1985            Pattern[0]['Offset'][1] += 1.
1986        elif event.key == 'o':
1987            Pattern[0]['Offset'] = [0,0]
1988        elif event.key == 'c':
1989            newPlot = True
1990            G2frame.Contour = not G2frame.Contour
1991            if not G2frame.Contour:
1992                G2frame.SinglePlot = False
1993                Pattern[0]['Offset'] = [0.,0.]
1994        elif event.key == 's':
1995            if G2frame.Contour:
1996                choice = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
1997                choice.sort()
1998                dlg = wx.SingleChoiceDialog(G2frame,'Select','Color scheme',choice)
1999                if dlg.ShowModal() == wx.ID_OK:
2000                    sel = dlg.GetSelection()
2001                    G2frame.ContourColor = choice[sel]
2002                else:
2003                    G2frame.ContourColor = 'Paired'
2004                dlg.Destroy()
2005            else:
2006                G2frame.SinglePlot = not G2frame.SinglePlot               
2007        elif event.key == 'i':                  #for smoothing contour plot
2008            choice = ['nearest','bilinear','bicubic','spline16','spline36','hanning',
2009               'hamming','hermite','kaiser','quadric','catrom','gaussian','bessel',
2010               'mitchell','sinc','lanczos']
2011            dlg = wx.SingleChoiceDialog(G2frame,'Select','Interpolation',choice)
2012            if dlg.ShowModal() == wx.ID_OK:
2013                sel = dlg.GetSelection()
2014                G2frame.Interpolate = choice[sel]
2015            else:
2016                G2frame.Interpolate = 'nearest'
2017            dlg.Destroy()
2018        elif event.key == 't' and not G2frame.Contour:
2019            G2frame.Legend = not G2frame.Legend
2020        PlotISFG(G2frame,newPlot=newPlot,type=type)
2021       
2022    def OnKeyBox(event):
2023        if G2frame.G2plotNB.nb.GetSelection() == G2frame.G2plotNB.plotList.index(type):
2024            event.key = cb.GetValue()[0]
2025            cb.SetValue(' key press')
2026            wx.CallAfter(OnPlotKeyPress,event)
2027        Page.canvas.SetFocus() # redirect the Focus from the button back to the plot
2028                       
2029    def OnMotion(event):
2030        xpos = event.xdata
2031        if xpos:                                        #avoid out of frame mouse position
2032            ypos = event.ydata
2033            Page.canvas.SetCursor(wx.CROSS_CURSOR)
2034            try:
2035                if G2frame.Contour:
2036                    G2frame.G2plotNB.status.SetStatusText('R =%.3fA pattern ID =%5d'%(xpos,int(ypos)),1)
2037                else:
2038                    G2frame.G2plotNB.status.SetStatusText('R =%.3fA %s =%.2f'%(xpos,type,ypos),1)                   
2039            except TypeError:
2040                G2frame.G2plotNB.status.SetStatusText('Select '+type+' pattern first',1)
2041   
2042    xylim = []
2043    try:
2044        plotNum = G2frame.G2plotNB.plotList.index(type)
2045        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2046        if not newPlot:
2047            Plot = Page.figure.gca()          #get previous plot & get limits
2048            xylim = Plot.get_xlim(),Plot.get_ylim()
2049        Page.figure.clf()
2050        Plot = Page.figure.gca()
2051    except ValueError:
2052        newPlot = True
2053        G2frame.Cmax = 1.0
2054        Plot = G2frame.G2plotNB.addMpl(type).gca()
2055        plotNum = G2frame.G2plotNB.plotList.index(type)
2056        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2057        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
2058        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2059   
2060    Page.SetFocus()
2061    G2frame.G2plotNB.status.DestroyChildren()
2062    if G2frame.Contour:
2063        Page.Choice = (' key press','d: lower contour max','u: raise contour max',
2064            'i: interpolation method','s: color scheme','c: contour off')
2065    else:
2066        Page.Choice = (' key press','l: offset left','r: offset right','d: offset down','u: offset up',
2067            'o: reset offset','t: toggle legend','c: contour on','s: toggle single plot')
2068    Page.keyPress = OnPlotKeyPress
2069    PatternId = G2frame.PatternId
2070    PickId = G2frame.PickId
2071    Plot.set_title(type)
2072    if type == 'G(R)':
2073        Plot.set_xlabel(r'$R,\AA$',fontsize=14)
2074    else:
2075        Plot.set_xlabel(r'$Q,\AA$'+superMinusOne,fontsize=14)
2076    Plot.set_ylabel(r''+type,fontsize=14)
2077    colors=['b','g','r','c','m','k']
2078    name = G2frame.PatternTree.GetItemText(PatternId)[4:]
2079    Pattern = []   
2080    if G2frame.SinglePlot:
2081        name = G2frame.PatternTree.GetItemText(PatternId)
2082        name = type+name[4:]
2083        Id = G2gd.GetPatternTreeItemId(G2frame,PatternId,name)
2084        Pattern = G2frame.PatternTree.GetItemPyData(Id)
2085        if Pattern:
2086            Pattern.append(name)
2087        PlotList = [Pattern,]
2088    else:
2089        PlotList = []
2090        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
2091        while item:
2092            if 'PDF' in G2frame.PatternTree.GetItemText(item):
2093                name = type+G2frame.PatternTree.GetItemText(item)[4:]
2094                Id = G2gd.GetPatternTreeItemId(G2frame,item,name)
2095                Pattern = G2frame.PatternTree.GetItemPyData(Id)
2096                if Pattern:
2097                    Pattern.append(name)
2098                    PlotList.append(Pattern)
2099            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
2100    PDFdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'PDF Controls'))
2101    numbDen = G2pwd.GetNumDensity(PDFdata['ElList'],PDFdata['Form Vol'])
2102    Xb = [0.,10.]
2103    Yb = [0.,-40.*np.pi*numbDen]
2104    Ymax = 0.01
2105    lenX = 0
2106    for Pattern in PlotList:
2107        try:
2108            xye = Pattern[1]
2109        except IndexError:
2110            return
2111        Ymax = max(Ymax,max(xye[1]))
2112    offset = Pattern[0]['Offset'][0]*Ymax/100.0
2113    if G2frame.Contour:
2114        ContourZ = []
2115        ContourY = []
2116        Nseq = 0
2117    for N,Pattern in enumerate(PlotList):
2118        xye = Pattern[1]
2119        if PickId:
2120            ifpicked = Pattern[2] == G2frame.PatternTree.GetItemText(PatternId)
2121        X = xye[0]
2122        if not lenX:
2123            lenX = len(X)           
2124        Y = xye[1]+offset*N
2125        if G2frame.Contour:
2126            if lenX == len(X):
2127                ContourY.append(N)
2128                ContourZ.append(Y)
2129                ContourX = X
2130                Nseq += 1
2131                Plot.set_ylabel('Data sequence',fontsize=12)
2132        else:
2133            X = xye[0]+Pattern[0]['Offset'][1]*.005*N
2134            if ifpicked:
2135                Plot.plot(X,Y,colors[N%6]+'+',picker=3.,clip_on=False)
2136                Page.canvas.SetToolTipString('')
2137            else:
2138                if G2frame.Legend:
2139                    Plot.plot(X,Y,colors[N%6],picker=False,label='Azm:'+Pattern[2].split('=')[1])
2140                else:
2141                    Plot.plot(X,Y,colors[N%6],picker=False)
2142            if type == 'G(R)':
2143                Plot.plot(Xb,Yb,color='k',dashes=(5,5))
2144            elif type == 'F(Q)':
2145                Plot.axhline(0.,color=wx.BLACK)
2146            elif type == 'S(Q)':
2147                Plot.axhline(1.,color=wx.BLACK)
2148    if G2frame.Contour:
2149        acolor = mpl.cm.get_cmap(G2frame.ContourColor)
2150        Img = Plot.imshow(ContourZ,cmap=acolor,vmin=0,vmax=Ymax*G2frame.Cmax,interpolation=G2frame.Interpolate, 
2151            extent=[ContourX[0],ContourX[-1],ContourY[0],ContourY[-1]],aspect='auto',origin='lower')
2152        Page.figure.colorbar(Img)
2153    elif G2frame.Legend:
2154        Plot.legend(loc='best')
2155    if not newPlot:
2156        Page.toolbar.push_current()
2157        Plot.set_xlim(xylim[0])
2158        Plot.set_ylim(xylim[1])
2159        xylim = []
2160        Page.toolbar.push_current()
2161        Page.toolbar.draw()
2162    else:
2163        Page.canvas.draw()
2164       
2165################################################################################
2166##### PlotCalib
2167################################################################################
2168           
2169def PlotCalib(G2frame,Inst,XY,Sigs,newPlot=False):
2170    '''plot of CW or TOF peak calibration
2171    '''
2172    def OnMotion(event):
2173        xpos = event.xdata
2174        if xpos:                                        #avoid out of frame mouse position
2175            ypos = event.ydata
2176            Page.canvas.SetCursor(wx.CROSS_CURSOR)
2177            try:
2178                G2frame.G2plotNB.status.SetStatusText('X =%9.3f %s =%9.3g'%(xpos,Title,ypos),1)                   
2179            except TypeError:
2180                G2frame.G2plotNB.status.SetStatusText('Select '+Title+' pattern first',1)
2181            found = []
2182            wid = 1
2183            view = Page.toolbar._views.forward()
2184            if view:
2185                view = view[0][:2]
2186                wid = view[1]-view[0]
2187            found = XY[np.where(np.fabs(XY.T[0]-xpos) < 0.005*wid)]
2188            if len(found):
2189                pos = found[0][1]
2190                if 'C' in Inst['Type'][0]: 
2191                    Page.canvas.SetToolTipString('position=%.4f'%(pos))
2192                else:
2193                    Page.canvas.SetToolTipString('position=%.2f'%(pos))
2194            else:
2195                Page.canvas.SetToolTipString('')
2196
2197    Title = 'Position calibration'
2198    try:
2199        plotNum = G2frame.G2plotNB.plotList.index(Title)
2200        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2201        if not newPlot:
2202            Plot = Page.figure.gca()
2203            xylim = Plot.get_xlim(),Plot.get_ylim()
2204        Page.figure.clf()
2205        Plot = Page.figure.gca()
2206    except ValueError:
2207        newPlot = True
2208        Plot = G2frame.G2plotNB.addMpl(Title).gca()
2209        plotNum = G2frame.G2plotNB.plotList.index(Title)
2210        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2211        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2212   
2213    Page.Choice = None
2214    Page.SetFocus()
2215    G2frame.G2plotNB.status.DestroyChildren()
2216    Plot.set_title(Title)
2217    Plot.set_xlabel(r'd-spacing',fontsize=14)
2218    if 'C' in Inst['Type'][0]:
2219        Plot.set_ylabel(r'$\mathsf{\Delta(2\theta)}$',fontsize=14)
2220    else:
2221        Plot.set_ylabel(r'$\mathsf{\Delta}T/T$',fontsize=14)
2222    for ixy,xyw in enumerate(XY):
2223        if len(xyw) > 2:
2224            X,Y,W = xyw
2225        else:
2226            X,Y = xyw
2227            W = 0.
2228        Yc = G2lat.Dsp2pos(Inst,X)
2229        if 'C' in Inst['Type'][0]:
2230            Y = Y-Yc
2231            E = Sigs[ixy]
2232            bin = W/2.
2233        else:
2234            Y = (Y-Yc)/Yc
2235            E = Sigs[ixy]/Yc
2236            bin = W/(2.*Yc)
2237        if E:
2238            Plot.errorbar(X,Y,ecolor='k',yerr=E)
2239        if ixy:
2240            Plot.plot(X,Y,'kx',picker=3)
2241        else:
2242            Plot.plot(X,Y,'kx',label='peak')
2243        if W:
2244            if ixy:
2245                Plot.plot(X,bin,'b+')
2246            else:
2247                Plot.plot(X,bin,'b+',label='bin width')
2248            Plot.plot(X,-bin,'b+')
2249        Plot.axhline(0.,color='r',linestyle='--')
2250    Plot.legend(loc='best')
2251    if not newPlot:
2252        Page.toolbar.push_current()
2253        Plot.set_xlim(xylim[0])
2254        Plot.set_ylim(xylim[1])
2255#        xylim = []
2256        Page.toolbar.push_current()
2257        Page.toolbar.draw()
2258    else:
2259        Page.canvas.draw()
2260
2261################################################################################
2262##### PlotXY
2263################################################################################
2264           
2265def PlotXY(G2frame,XY,XY2=None,labelX=None,labelY=None,newPlot=False,Title=''):
2266    '''simple plot of xy data, used for diagnostic purposes
2267    '''
2268    def OnMotion(event):
2269        xpos = event.xdata
2270        if xpos:                                        #avoid out of frame mouse position
2271            ypos = event.ydata
2272            Page.canvas.SetCursor(wx.CROSS_CURSOR)
2273            try:
2274                G2frame.G2plotNB.status.SetStatusText('X =%9.3f %s =%9.3f'%(xpos,Title,ypos),1)                   
2275            except TypeError:
2276                G2frame.G2plotNB.status.SetStatusText('Select '+Title+' pattern first',1)
2277
2278    try:
2279        plotNum = G2frame.G2plotNB.plotList.index(Title)
2280        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2281        if not newPlot:
2282            Plot = Page.figure.gca()
2283            xylim = Plot.get_xlim(),Plot.get_ylim()
2284        Page.figure.clf()
2285        Plot = Page.figure.gca()
2286    except ValueError:
2287        newPlot = True
2288        Plot = G2frame.G2plotNB.addMpl(Title).gca()
2289        plotNum = G2frame.G2plotNB.plotList.index(Title)
2290        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2291        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2292   
2293    Page.Choice = None
2294    Page.SetFocus()
2295    G2frame.G2plotNB.status.DestroyChildren()
2296    Plot.set_title(Title)
2297    if labelX:
2298        Plot.set_xlabel(r''+labelX,fontsize=14)
2299    else:
2300        Plot.set_xlabel(r'X',fontsize=14)
2301    if labelY:
2302        Plot.set_ylabel(r''+labelY,fontsize=14)
2303    else:
2304        Plot.set_ylabel(r'Y',fontsize=14)
2305    colors=['b','g','r','c','m','k']
2306    for ixy,xy in enumerate(XY):
2307        X,Y = xy
2308        Plot.plot(X,Y,colors[ixy%6]+'+',picker=False)
2309    if len(XY2):
2310        for ixy,xy in enumerate(XY2):
2311            X,Y = xy
2312            Plot.plot(X,Y,colors[ixy%6],picker=False)
2313    if not newPlot:
2314        Page.toolbar.push_current()
2315        Plot.set_xlim(xylim[0])
2316        Plot.set_ylim(xylim[1])
2317        xylim = []
2318        Page.toolbar.push_current()
2319        Page.toolbar.draw()
2320    else:
2321        Page.canvas.draw()
2322
2323################################################################################
2324##### PlotStrain
2325################################################################################
2326           
2327def PlotStrain(G2frame,data,newPlot=False):
2328    '''plot of strain data, used for diagnostic purposes
2329    '''
2330    def OnMotion(event):
2331        xpos = event.xdata
2332        if xpos:                                        #avoid out of frame mouse position
2333            ypos = event.ydata
2334            Page.canvas.SetCursor(wx.CROSS_CURSOR)
2335            try:
2336                G2frame.G2plotNB.status.SetStatusText('d-spacing =%9.5f Azimuth =%9.3f'%(ypos,xpos),1)                   
2337            except TypeError:
2338                G2frame.G2plotNB.status.SetStatusText('Select Strain pattern first',1)
2339
2340    try:
2341        plotNum = G2frame.G2plotNB.plotList.index('Strain')
2342        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2343        if not newPlot:
2344            Plot = Page.figure.gca()
2345            xylim = Plot.get_xlim(),Plot.get_ylim()
2346        Page.figure.clf()
2347        Plot = Page.figure.gca()
2348    except ValueError:
2349        newPlot = True
2350        Plot = G2frame.G2plotNB.addMpl('Strain').gca()
2351        plotNum = G2frame.G2plotNB.plotList.index('Strain')
2352        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2353        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2354   
2355    Page.Choice = None
2356    Page.SetFocus()
2357    G2frame.G2plotNB.status.DestroyChildren()
2358    Plot.set_title('Strain')
2359    Plot.set_ylabel(r'd-spacing',fontsize=14)
2360    Plot.set_xlabel(r'Azimuth',fontsize=14)
2361    colors=['b','g','r','c','m','k']
2362    for N,item in enumerate(data['d-zero']):
2363        Y,X = np.array(item['ImtaObs'])         #plot azimuth as X & d-spacing as Y
2364        Plot.plot(X,Y,colors[N%6]+'+',picker=False)
2365        Y,X = np.array(item['ImtaCalc'])
2366        Plot.plot(X,Y,colors[N%6],picker=False)
2367    if not newPlot:
2368        Page.toolbar.push_current()
2369        Plot.set_xlim(xylim[0])
2370        Plot.set_ylim(xylim[1])
2371        xylim = []
2372        Page.toolbar.push_current()
2373        Page.toolbar.draw()
2374    else:
2375        Page.canvas.draw()
2376       
2377################################################################################
2378##### PlotSASDSizeDist
2379################################################################################
2380           
2381def PlotSASDSizeDist(G2frame):
2382   
2383    def OnPageChanged(event):
2384        PlotText = G2frame.G2plotNB.nb.GetPageText(G2frame.G2plotNB.nb.GetSelection())
2385        if 'Powder' in PlotText:
2386            PlotPatterns(G2frame,plotType='SASD',newPlot=True)
2387        elif 'Size' in PlotText:
2388            PlotSASDSizeDist(G2frame)
2389   
2390    def OnMotion(event):
2391        xpos = event.xdata
2392        if xpos:                                        #avoid out of frame mouse position
2393            ypos = event.ydata
2394            Page.canvas.SetCursor(wx.CROSS_CURSOR)
2395            try:
2396                G2frame.G2plotNB.status.SetStatusText('diameter =%9.3f f(D) =%9.3g'%(xpos,ypos),1)                   
2397            except TypeError:
2398                G2frame.G2plotNB.status.SetStatusText('Select Strain pattern first',1)
2399
2400    try:
2401        plotNum = G2frame.G2plotNB.plotList.index('Size Distribution')
2402        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2403        Page.figure.clf()
2404        Plot = Page.figure.gca()          #get a fresh plot after clf()
2405    except ValueError:
2406        newPlot = True
2407        Plot = G2frame.G2plotNB.addMpl('Size Distribution').gca()
2408        G2frame.G2plotNB.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED,OnPageChanged)
2409        plotNum = G2frame.G2plotNB.plotList.index('Size Distribution')
2410        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2411        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2412    Page.Choice = None
2413    Page.SetFocus()
2414    PatternId = G2frame.PatternId
2415    data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Models'))
2416    Bins,Dbins,BinMag = data['Size']['Distribution']
2417    Plot.set_title('Size Distribution')
2418    Plot.set_xlabel(r'$D, \AA$',fontsize=14)
2419    Plot.set_ylabel(r'$Volume distribution f(D)$',fontsize=14)
2420    if data['Size']['logBins']:
2421        Plot.set_xscale("log",nonposy='mask')
2422        Plot.set_xlim([np.min(2.*Bins)/2.,np.max(2.*Bins)*2.])
2423    Plot.bar(2.*Bins-Dbins,BinMag,2.*Dbins,facecolor='white')       #plot diameters
2424    if 'Size Calc' in data:
2425        Rbins,Dist = data['Size Calc']
2426        for i in range(len(Rbins)):
2427            if len(Rbins[i]):
2428                Plot.plot(2.*Rbins[i],Dist[i])       #plot diameters
2429    Page.canvas.draw()
2430
2431################################################################################
2432##### PlotPowderLines
2433################################################################################
2434           
2435def PlotPowderLines(G2frame):
2436    ''' plotting of powder lines (i.e. no powder pattern) as sticks
2437    '''
2438
2439    def OnMotion(event):
2440        xpos = event.xdata
2441        if xpos:                                        #avoid out of frame mouse position
2442            Page.canvas.SetCursor(wx.CROSS_CURSOR)
2443            G2frame.G2plotNB.status.SetFields(['','2-theta =%9.3f '%(xpos,)])
2444            if G2frame.PickId and G2frame.PatternTree.GetItemText(G2frame.PickId) in ['Index Peak List','Unit Cells List']:
2445                found = []
2446                if len(G2frame.HKL):
2447                    view = Page.toolbar._views.forward()[0][:2]
2448                    wid = view[1]-view[0]
2449                    found = G2frame.HKL[np.where(np.fabs(G2frame.HKL.T[-1]-xpos) < 0.002*wid)]
2450                if len(found):
2451                    h,k,l = found[0][:3] 
2452                    Page.canvas.SetToolTipString('%d,%d,%d'%(int(h),int(k),int(l)))
2453                else:
2454                    Page.canvas.SetToolTipString('')
2455
2456    try:
2457        plotNum = G2frame.G2plotNB.plotList.index('Powder Lines')
2458        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2459        Page.figure.clf()
2460        Plot = Page.figure.gca()
2461    except ValueError:
2462        Plot = G2frame.G2plotNB.addMpl('Powder Lines').gca()
2463        plotNum = G2frame.G2plotNB.plotList.index('Powder Lines')
2464        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2465        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2466       
2467    Page.Choice = None
2468    Page.SetFocus()
2469    Plot.set_title('Powder Pattern Lines')
2470    Plot.set_xlabel(r'$\mathsf{2\theta}$',fontsize=14)
2471    PickId = G2frame.PickId
2472    PatternId = G2frame.PatternId
2473    peaks = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Index Peak List'))[0]
2474    for peak in peaks:
2475        Plot.axvline(peak[0],color='b')
2476    for hkl in G2frame.HKL:
2477        Plot.axvline(hkl[-1],color='r',dashes=(5,5))
2478    xmin = peaks[0][0]
2479    xmax = peaks[-1][0]
2480    delt = xmax-xmin
2481    xlim = [max(0,xmin-delt/20.),min(180.,xmax+delt/20.)]
2482    Plot.set_xlim(xlim)
2483    Page.canvas.draw()
2484    Page.toolbar.push_current()
2485
2486################################################################################
2487##### PlotPeakWidths
2488################################################################################
2489           
2490def PlotPeakWidths(G2frame):
2491    ''' Plotting of instrument broadening terms as function of 2-theta
2492    Seen when "Instrument Parameters" chosen from powder pattern data tree
2493    '''
2494#    sig = lambda Th,U,V,W: 1.17741*math.sqrt(U*tand(Th)**2+V*tand(Th)+W)*math.pi/18000.
2495#    gam = lambda Th,X,Y: (X/cosd(Th)+Y*tand(Th))*math.pi/18000.
2496#    gamFW = lambda s,g: np.exp(np.log(s**5+2.69269*s**4*g+2.42843*s**3*g**2+4.47163*s**2*g**3+0.07842*s*g**4+g**5)/5.)
2497#    gamFW2 = lambda s,g: math.sqrt(s**2+(0.4654996*g)**2)+.5345004*g  #Ubaldo Bafile - private communication
2498    PatternId = G2frame.PatternId
2499    limitID = G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Limits')
2500    if limitID:
2501        limits = G2frame.PatternTree.GetItemPyData(limitID)[:2]
2502    else:
2503        return
2504    Parms,Parms2 = G2frame.PatternTree.GetItemPyData( \
2505        G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Instrument Parameters'))
2506    if 'C' in Parms['Type'][0]:
2507        lam = G2mth.getWave(Parms)
2508    else:
2509        difC = Parms['difC'][0]
2510    try:  # PATCH: deal with older peak lists, before changed to dict to implement TOF
2511        peaks = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Peak List'))['peaks']
2512    except TypeError:
2513        print "Your peak list needs reformatting...",
2514        item = G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Peak List')
2515        G2frame.PatternTree.SelectItem(item) 
2516        item = G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Instrument Parameters')
2517        G2frame.PatternTree.SelectItem(item)
2518        print "done"
2519        return
2520    try:
2521        plotNum = G2frame.G2plotNB.plotList.index('Peak Widths')
2522        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2523        Page.figure.clf()
2524        Plot = Page.figure.gca()
2525    except ValueError:
2526        Plot = G2frame.G2plotNB.addMpl('Peak Widths').gca()
2527        plotNum = G2frame.G2plotNB.plotList.index('Peak Widths')
2528        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2529    Page.Choice = None
2530    Page.SetFocus()
2531   
2532    Page.canvas.SetToolTipString('')
2533    colors=['b','g','r','c','m','k']
2534    X = []
2535    Y = []
2536    Z = []
2537    W = []
2538    if 'C' in Parms['Type'][0]:
2539        Plot.set_title('Instrument and sample peak widths')
2540        Plot.set_xlabel(r'$Q, \AA^{-1}$',fontsize=14)
2541        Plot.set_ylabel(r'$\Delta Q/Q, \Delta d/d$',fontsize=14)
2542        try:
2543            Xmin,Xmax = limits[1]
2544            X = np.linspace(Xmin,Xmax,num=101,endpoint=True)
2545            Q = 4.*np.pi*npsind(X/2.)/lam
2546            Z = np.ones_like(X)
2547            data = G2mth.setPeakparms(Parms,Parms2,X,Z)
2548            s = 1.17741*np.sqrt(data[4])*np.pi/18000.
2549            g = data[6]*np.pi/18000.
2550            G = G2pwd.getgamFW(g,s)
2551            Y = s/nptand(X/2.)
2552            Z = g/nptand(X/2.)
2553            W = G/nptand(X/2.)
2554            Plot.plot(Q,Y,color='r',label='Gaussian')
2555            Plot.plot(Q,Z,color='g',label='Lorentzian')
2556            Plot.plot(Q,W,color='b',label='G+L')
2557           
2558            fit = G2mth.setPeakparms(Parms,Parms2,X,Z,useFit=True)
2559            sf = 1.17741*np.sqrt(fit[4])*np.pi/18000.
2560            gf = fit[6]*np.pi/18000.
2561            Gf = G2pwd.getgamFW(gf,sf)
2562            Yf = sf/nptand(X/2.)
2563            Zf = gf/nptand(X/2.)
2564            Wf = Gf/nptand(X/2.)
2565            Plot.plot(Q,Yf,color='r',dashes=(5,5),label='Gaussian fit')
2566            Plot.plot(Q,Zf,color='g',dashes=(5,5),label='Lorentzian fit')
2567            Plot.plot(Q,Wf,color='b',dashes=(5,5),label='G+L fit')
2568           
2569            X = []
2570            Y = []
2571            Z = []
2572            W = []
2573            V = []
2574            for peak in peaks:
2575                X.append(4.0*math.pi*sind(peak[0]/2.0)/lam)
2576                try:
2577                    s = 1.17741*math.sqrt(peak[4])*math.pi/18000.
2578                except ValueError:
2579                    s = 0.01
2580                g = peak[6]*math.pi/18000.
2581                G = G2pwd.getgamFW(g,s)
2582                Y.append(s/tand(peak[0]/2.))
2583                Z.append(g/tand(peak[0]/2.))
2584                W.append(G/tand(peak[0]/2.))
2585            if len(peaks):
2586                Plot.plot(X,Y,'+',color='r',label='G peak')
2587                Plot.plot(X,Z,'+',color='g',label='L peak')
2588                Plot.plot(X,W,'+',color='b',label='G+L peak')
2589            Plot.legend(loc='best')
2590            Page.canvas.draw()
2591        except ValueError:
2592            print '**** ERROR - default U,V,W profile coefficients yield sqrt of negative value at 2theta =', \
2593                '%.3f'%(2*theta)
2594            G2frame.G2plotNB.Delete('Peak Widths')
2595    else:   #'T'OF
2596        Plot.set_title('Instrument and sample peak coefficients')
2597        Plot.set_xlabel(r'$Q, \AA^{-1}$',fontsize=14)
2598        Plot.set_ylabel(r'$\alpha, \beta, \Delta Q/Q, \Delta d/d$',fontsize=14)
2599        Xmin,Xmax = limits[1]
2600        T = np.linspace(Xmin,Xmax,num=101,endpoint=True)
2601        Z = np.ones_like(T)
2602        data = G2mth.setPeakparms(Parms,Parms2,T,Z)
2603        ds = T/difC
2604        Q = 2.*np.pi/ds
2605        A = data[4]
2606        B = data[6]
2607        S = 1.17741*np.sqrt(data[8])/T
2608        G = data[10]/T
2609        Plot.plot(Q,A,color='r',label='Alpha')
2610        Plot.plot(Q,B,color='g',label='Beta')
2611        Plot.plot(Q,S,color='b',label='Gaussian')
2612        Plot.plot(Q,G,color='m',label='Lorentzian')
2613
2614        fit = G2mth.setPeakparms(Parms,Parms2,T,Z)
2615        ds = T/difC
2616        Q = 2.*np.pi/ds
2617        Af = fit[4]
2618        Bf = fit[6]
2619        Sf = 1.17741*np.sqrt(fit[8])/T
2620        Gf = fit[10]/T
2621        Plot.plot(Q,Af,color='r',dashes=(5,5),label='Alpha fit')
2622        Plot.plot(Q,Bf,color='g',dashes=(5,5),label='Beta fit')
2623        Plot.plot(Q,Sf,color='b',dashes=(5,5),label='Gaussian fit')
2624        Plot.plot(Q,Gf,color='m',dashes=(5,5),label='Lorentzian fit')
2625       
2626        T = []
2627        A = []
2628        B = []
2629        S = []
2630        G = []
2631        W = []
2632        Q = []
2633        V = []
2634        for peak in peaks:
2635            T.append(peak[0])
2636            A.append(peak[4])
2637            B.append(peak[6])
2638            Q.append(2.*np.pi*difC/peak[0])
2639            S.append(1.17741*np.sqrt(peak[8])/peak[0])
2640            G.append(peak[10]/peak[0])
2641           
2642       
2643        Plot.plot(Q,A,'+',color='r',label='Alpha peak')
2644        Plot.plot(Q,B,'+',color='g',label='Beta peak')
2645        Plot.plot(Q,S,'+',color='b',label='Gaussian peak')
2646        Plot.plot(Q,G,'+',color='m',label='Lorentzian peak')
2647        Plot.legend(loc='best')
2648        Page.canvas.draw()
2649
2650   
2651################################################################################
2652##### PlotSizeStrainPO
2653################################################################################
2654           
2655def PlotSizeStrainPO(G2frame,data,hist='',Start=False):
2656    '''Plot 3D mustrain/size/preferred orientation figure. In this instance data is for a phase
2657    '''
2658   
2659    PatternId = G2frame.PatternId
2660    generalData = data['General']
2661    SGData = generalData['SGData']
2662    SGLaue = SGData['SGLaue']
2663    if Start:                   #initialize the spherical harmonics qlmn arrays
2664        ptx.pyqlmninit()
2665        Start = False
2666#    MuStrKeys = G2spc.MustrainNames(SGData)
2667    cell = generalData['Cell'][1:]
2668    A,B = G2lat.cell2AB(cell[:6])
2669    Vol = cell[6]
2670    useList = data['Histograms']
2671    phase = generalData['Name']
2672    plotType = generalData['Data plot type']
2673    plotDict = {'Mustrain':'Mustrain','Size':'Size','Preferred orientation':'Pref.Ori.'}
2674    for ptype in plotDict:
2675        G2frame.G2plotNB.Delete(ptype)
2676    if plotType in ['None'] or not useList:
2677        return       
2678    if hist == '':
2679        hist = useList.keys()[0]
2680    numPlots = len(useList)
2681
2682    try:
2683        plotNum = G2frame.G2plotNB.plotList.index(plotType)
2684        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2685        Page.figure.clf()
2686        Plot = Page.figure.gca()
2687        if not Page.IsShown():
2688            Page.Show()
2689    except ValueError:
2690        if plotType in ['Mustrain','Size']:
2691            Plot = mp3d.Axes3D(G2frame.G2plotNB.add3D(plotType))
2692        else:
2693            Plot = G2frame.G2plotNB.addMpl(plotType).gca()               
2694        plotNum = G2frame.G2plotNB.plotList.index(plotType)
2695        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2696    Page.Choice = None
2697    G2frame.G2plotNB.status.SetStatusText('',1)
2698   
2699    PHI = np.linspace(0.,360.,30,True)
2700    PSI = np.linspace(0.,180.,30,True)
2701    X = np.outer(npsind(PHI),npsind(PSI))
2702    Y = np.outer(npcosd(PHI),npsind(PSI))
2703    Z = np.outer(np.ones(np.size(PHI)),npcosd(PSI))
2704    try:        #temp patch instead of 'mustrain' for old files with 'microstrain'
2705        coeff = useList[hist][plotDict[plotType]]
2706    except KeyError:
2707        return
2708    if plotType in ['Mustrain','Size']:
2709        if coeff[0] == 'isotropic':
2710            X *= coeff[1][0]
2711            Y *= coeff[1][0]
2712            Z *= coeff[1][0]                               
2713        elif coeff[0] == 'uniaxial':
2714           
2715            def uniaxCalc(xyz,iso,aniso,axes):
2716                Z = np.array(axes)
2717                cp = abs(np.dot(xyz,Z))
2718                sp = np.sqrt(1.-cp**2)
2719                R = iso*aniso/np.sqrt((iso*cp)**2+(aniso*sp)**2)
2720                return R*xyz
2721               
2722            iso,aniso = coeff[1][:2]
2723            axes = np.inner(A,np.array(coeff[3]))
2724            axes /= nl.norm(axes)
2725            Shkl = np.array(coeff[1])
2726            XYZ = np.dstack((X,Y,Z))
2727            XYZ = np.nan_to_num(np.apply_along_axis(uniaxCalc,2,XYZ,iso,aniso,axes))
2728            X,Y,Z = np.dsplit(XYZ,3)
2729            X = X[:,:,0]
2730            Y = Y[:,:,0]
2731            Z = Z[:,:,0]
2732       
2733        elif coeff[0] == 'ellipsoidal':
2734           
2735            def ellipseCalc(xyz,E,R):
2736                XYZ = xyz*E.T
2737                return np.inner(XYZ.T,R)
2738               
2739            S6 = coeff[4]
2740            Sij = G2lat.U6toUij(S6)
2741            E,R = nl.eigh(Sij)
2742            XYZ = np.dstack((X,Y,Z))
2743            XYZ = np.nan_to_num(np.apply_along_axis(ellipseCalc,2,XYZ,E,R))
2744            X,Y,Z = np.dsplit(XYZ,3)
2745            X = X[:,:,0]
2746            Y = Y[:,:,0]
2747            Z = Z[:,:,0]
2748           
2749        elif coeff[0] == 'generalized':
2750           
2751            def genMustrain(xyz,SGData,A,Shkl):
2752                uvw = np.inner(A.T,xyz)
2753                Strm = np.array(G2spc.MustrainCoeff(uvw,SGData))
2754                Sum = np.sum(np.multiply(Shkl,Strm))
2755                Sum = np.where(Sum > 0.01,Sum,0.01)
2756                Sum = np.sqrt(Sum)
2757                return Sum*xyz
2758               
2759            Shkl = np.array(coeff[4])
2760            if np.any(Shkl):
2761                XYZ = np.dstack((X,Y,Z))
2762                XYZ = np.nan_to_num(np.apply_along_axis(genMustrain,2,XYZ,SGData,A,Shkl))
2763                X,Y,Z = np.dsplit(XYZ,3)
2764                X = X[:,:,0]
2765                Y = Y[:,:,0]
2766                Z = Z[:,:,0]
2767                   
2768        if np.any(X) and np.any(Y) and np.any(Z):
2769            errFlags = np.seterr(all='ignore')
2770            Plot.plot_surface(X,Y,Z,rstride=1,cstride=1,color='g',linewidth=1)
2771            np.seterr(all='ignore')
2772            xyzlim = np.array([Plot.get_xlim3d(),Plot.get_ylim3d(),Plot.get_zlim3d()]).T
2773            XYZlim = [min(xyzlim[0]),max(xyzlim[1])]
2774            Plot.set_xlim3d(XYZlim)
2775            Plot.set_ylim3d(XYZlim)
2776            Plot.set_zlim3d(XYZlim)
2777            Plot.set_aspect('equal')
2778        if plotType == 'Size':
2779            Plot.set_title('Crystallite size for '+phase+'\n'+coeff[0]+' model')
2780            Plot.set_xlabel(r'X, $\mu$m')
2781            Plot.set_ylabel(r'Y, $\mu$m')
2782            Plot.set_zlabel(r'Z, $\mu$m')
2783        else:   
2784            Plot.set_title(r'$\mu$strain for '+phase+'\n'+coeff[0]+' model')
2785            Plot.set_xlabel(r'X, $\mu$strain')
2786            Plot.set_ylabel(r'Y, $\mu$strain')
2787            Plot.set_zlabel(r'Z, $\mu$strain')
2788    else:
2789        h,k,l = generalData['POhkl']
2790        if coeff[0] == 'MD':
2791            print 'March-Dollase preferred orientation plot'
2792       
2793        else:
2794            PH = np.array(generalData['POhkl'])
2795            phi,beta = G2lat.CrsAng(PH,cell[:6],SGData)
2796            SHCoef = {}
2797            for item in coeff[5]:
2798                L,N = eval(item.strip('C'))
2799                SHCoef['C%d,0,%d'%(L,N)] = coeff[5][item]                       
2800            ODFln = G2lat.Flnh(Start,SHCoef,phi,beta,SGData)
2801            X = np.linspace(0,90.0,26)
2802            Y = G2lat.polfcal(ODFln,'0',X,0.0)
2803            Plot.plot(X,Y,color='k',label=str(PH))
2804            Plot.legend(loc='best')
2805            Plot.set_title('Axial distribution for HKL='+str(PH)+' in '+phase+'\n'+hist)
2806            Plot.set_xlabel(r'$\psi$',fontsize=16)
2807            Plot.set_ylabel('MRD',fontsize=14)
2808    Page.canvas.draw()
2809   
2810################################################################################
2811##### PlotTexture
2812################################################################################
2813
2814def PlotTexture(G2frame,data,Start=False):
2815    '''Pole figure, inverse pole figure plotting.
2816    dict generalData contains all phase info needed which is in data
2817    '''
2818
2819    shModels = ['cylindrical','none','shear - 2/m','rolling - mmm']
2820    SamSym = dict(zip(shModels,['0','-1','2/m','mmm']))
2821    PatternId = G2frame.PatternId
2822    generalData = data['General']
2823    SGData = generalData['SGData']
2824    pName = generalData['Name']
2825    textureData = generalData['SH Texture']
2826    G2frame.G2plotNB.Delete('Texture')
2827    if not textureData['Order']:
2828        return                  #no plot!!
2829    SHData = generalData['SH Texture']
2830    SHCoef = SHData['SH Coeff'][1]
2831    cell = generalData['Cell'][1:7]
2832    Amat,Bmat = G2lat.cell2AB(cell)
2833    sq2 = 1.0/math.sqrt(2.0)
2834   
2835    def rp2xyz(r,p):
2836        z = npcosd(r)
2837        xy = np.sqrt(1.-z**2)
2838        return xy*npsind(p),xy*npcosd(p),z
2839           
2840    def OnMotion(event):
2841        SHData = data['General']['SH Texture']
2842        if event.xdata and event.ydata:                 #avoid out of frame errors
2843            xpos = event.xdata
2844            ypos = event.ydata
2845            if 'Inverse' in SHData['PlotType']:
2846                r = xpos**2+ypos**2
2847                if r <= 1.0:
2848                    if 'equal' in G2frame.Projection: 
2849                        r,p = 2.*npasind(np.sqrt(r)*sq2),npatan2d(ypos,xpos)
2850                    else:
2851                        r,p = 2.*npatand(np.sqrt(r)),npatan2d(ypos,xpos)
2852                    ipf = G2lat.invpolfcal(IODFln,SGData,np.array([r,]),np.array([p,]))
2853                    xyz = np.inner(Bmat,np.array([rp2xyz(r,p)]))
2854                    y,x,z = list(xyz/np.max(np.abs(xyz)))
2855                   
2856                    G2frame.G2plotNB.status.SetFields(['',
2857                        'psi =%9.3f, beta =%9.3f, MRD =%9.3f hkl=%5.2f,%5.2f,%5.2f'%(r,p,ipf,x,y,z)])
2858                                   
2859            elif 'Axial' in SHData['PlotType']:
2860                pass
2861               
2862            else:                       #ordinary pole figure
2863                z = xpos**2+ypos**2
2864                if z <= 1.0:
2865                    z = np.sqrt(z)
2866                    if 'equal' in G2frame.Projection: 
2867                        r,p = 2.*npasind(z*sq2),npatan2d(ypos,xpos)
2868                    else:
2869                        r,p = 2.*npatand(z),npatan2d(ypos,xpos)
2870                    pf = G2lat.polfcal(ODFln,SamSym[textureData['Model']],np.array([r,]),np.array([p,]))
2871                    G2frame.G2plotNB.status.SetFields(['','phi =%9.3f, gam =%9.3f, MRD =%9.3f'%(r,p,pf)])
2872   
2873    try:
2874        plotNum = G2frame.G2plotNB.plotList.index('Texture')
2875        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2876        Page.figure.clf()
2877        Plot = Page.figure.gca()
2878        if not Page.IsShown():
2879            Page.Show()
2880    except ValueError:
2881        if '3D' in SHData['PlotType']:
2882            Plot = mp3d.Axes3D(G2frame.G2plotNB.add3D('Texture'))
2883        else:
2884            Plot = G2frame.G2plotNB.addMpl('Texture').gca()               
2885        plotNum = G2frame.G2plotNB.plotList.index('Texture')
2886        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2887        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2888
2889    Page.Choice = None
2890    Page.SetFocus()
2891    G2frame.G2plotNB.status.SetFields(['',''])   
2892    PH = np.array(SHData['PFhkl'])
2893    phi,beta = G2lat.CrsAng(PH,cell,SGData)
2894    ODFln = G2lat.Flnh(Start,SHCoef,phi,beta,SGData)
2895    if not np.any(ODFln):
2896        return
2897    PX = np.array(SHData['PFxyz'])
2898    gam = atan2d(PX[0],PX[1])
2899    xy = math.sqrt(PX[0]**2+PX[1]**2)
2900    xyz = math.sqrt(PX[0]**2+PX[1]**2+PX[2]**2)
2901    psi = asind(xy/xyz)
2902    IODFln = G2lat.Glnh(Start,SHCoef,psi,gam,SamSym[textureData['Model']])
2903    if 'Axial' in SHData['PlotType']:
2904        X = np.linspace(0,90.0,26)
2905        Y = G2lat.polfcal(ODFln,SamSym[textureData['Model']],X,0.0)
2906        Plot.plot(X,Y,color='k',label=str(SHData['PFhkl']))
2907        Plot.legend(loc='best')
2908        h,k,l = SHData['PFhkl']
2909        Plot.set_title('%d %d %d Axial distribution for %s'%(h,k,l,pName))
2910        Plot.set_xlabel(r'$\psi$',fontsize=16)
2911        Plot.set_ylabel('MRD',fontsize=14)
2912       
2913    else:       
2914        npts = 201
2915        if 'Inverse' in SHData['PlotType']:
2916            X,Y = np.meshgrid(np.linspace(1.,-1.,npts),np.linspace(-1.,1.,npts))
2917            R,P = np.sqrt(X**2+Y**2).flatten(),npatan2d(X,Y).flatten()
2918            if 'equal' in G2frame.Projection:
2919                R = np.where(R <= 1.,2.*npasind(R*sq2),0.0)
2920            else:
2921                R = np.where(R <= 1.,2.*npatand(R),0.0)
2922            Z = np.zeros_like(R)
2923            Z = G2lat.invpolfcal(IODFln,SGData,R,P)
2924            Z = np.reshape(Z,(npts,npts))
2925            try:
2926                CS = Plot.contour(Y,X,Z,aspect='equal')
2927                Plot.clabel(CS,fontsize=9,inline=1)
2928            except ValueError:
2929                pass
2930            Img = Plot.imshow(Z.T,aspect='equal',cmap=G2frame.ContourColor,extent=[-1,1,-1,1])
2931            Page.figure.colorbar(Img)
2932            x,y,z = SHData['PFxyz']
2933            Plot.axis('off')
2934            Plot.set_title('%d %d %d Inverse pole figure for %s'%(int(x),int(y),int(z),pName))
2935            Plot.set_xlabel(G2frame.Projection.capitalize()+' projection')
2936           
2937        elif '3D' in SHData['PlotType']:
2938            PSI,GAM = np.mgrid[0:31,0:31]
2939            PSI = PSI.flatten()*6.
2940            GAM = GAM.flatten()*12.
2941            P = G2lat.polfcal(ODFln,SamSym[textureData['Model']],PSI,GAM).reshape((31,31))           
2942            GAM = np.linspace(0.,360.,31,True)
2943            PSI = np.linspace(0.,180.,31,True)
2944            X = np.outer(npsind(GAM),npsind(PSI))*P.T
2945            Y = np.outer(npcosd(GAM),npsind(PSI))*P.T
2946            Z = np.outer(np.ones(np.size(GAM)),npcosd(PSI))*P.T
2947            h,k,l = SHData['PFhkl']
2948           
2949            if np.any(X) and np.any(Y) and np.any(Z):
2950                errFlags = np.seterr(all='ignore')
2951                Plot.plot_surface(X,Y,Z,rstride=1,cstride=1,color='g',linewidth=1)
2952                np.seterr(all='ignore')
2953                xyzlim = np.array([Plot.get_xlim3d(),Plot.get_ylim3d(),Plot.get_zlim3d()]).T
2954                XYZlim = [min(xyzlim[0]),max(xyzlim[1])]
2955                Plot.set_xlim3d(XYZlim)
2956                Plot.set_ylim3d(XYZlim)
2957                Plot.set_zlim3d(XYZlim)
2958                Plot.set_aspect('equal')                       
2959                Plot.set_title('%d %d %d Pole distribution for %s'%(h,k,l,pName))
2960                Plot.set_xlabel(r'X, MRD')
2961                Plot.set_ylabel(r'Y, MRD')
2962                Plot.set_zlabel(r'Z, MRD')
2963        else:
2964            PFproj = textureData.get('PFproj','XY')
2965            PRrev = textureData.get('PFrev',False)
2966            X,Y = np.meshgrid(np.linspace(1.,-1.,npts),np.linspace(-1.,1.,npts))
2967            R,P = np.sqrt(X**2+Y**2).flatten(),npatan2d(X,Y).flatten()
2968            if 'equal' in G2frame.Projection:
2969                R = np.where(R <= 1.,2.*npasind(R*sq2),0.0)
2970            else:
2971                R = np.where(R <= 1.,2.*npatand(R),0.0)
2972            Z = np.zeros_like(R)
2973            Z = G2lat.polfcal(ODFln,SamSym[textureData['Model']],R,P)
2974            Z = np.reshape(Z,(npts,npts))
2975            try:
2976                CS = Plot.contour(Y,X,Z,aspect='equal')
2977                Plot.clabel(CS,fontsize=9,inline=1)
2978            except ValueError:
2979                pass
2980            Img = Plot.imshow(Z.T,aspect='equal',cmap=G2frame.ContourColor,extent=[-1,1,-1,1])
2981            Page.figure.colorbar(Img)
2982            h,k,l = SHData['PFhkl']
2983            Plot.axis('off')
2984            Plot.set_title('%d %d %d Pole figure for %s'%(h,k,l,pName))
2985            Plot.set_xlabel(G2frame.Projection.capitalize()+' projection')
2986    Page.canvas.draw()
2987
2988################################################################################
2989##### Plot Modulation
2990################################################################################
2991
2992def ModulationPlot(G2frame,data,atom,ax,off=0):
2993    global Off,Atom,Ax
2994    Off = off
2995    Atom = atom
2996    Ax = ax
2997    def OnMotion(event):
2998        xpos = event.xdata
2999        if xpos:                                        #avoid out of frame mouse position
3000            ypos = event.ydata
3001            Page.canvas.SetCursor(wx.CROSS_CURSOR)
3002            try:
3003                G2frame.G2plotNB.status.SetStatusText('t =%9.3f %s =%9.3f'%(xpos,GkDelta+Ax,ypos),1)                   
3004            except TypeError:
3005                G2frame.G2plotNB.status.SetStatusText('Select '+Title+' pattern first',1)
3006   
3007    def OnPlotKeyPress(event):
3008        global Off,Atom,Ax
3009        newPlot = False
3010        if event.key == '0':
3011            Off = 0
3012        elif event.key in ['+','=']:
3013            Off += 1
3014        elif event.key == '-':
3015            Off -= 1
3016        elif event.key in ['l','r',] and mapData['Flip']:
3017            roll = 1
3018            if  event.key == 'l':
3019                roll = -1
3020            rho = Map['rho']
3021            Map['rho'] = np.roll(rho,roll,axis=3)
3022        wx.CallAfter(ModulationPlot,G2frame,data,Atom,Ax,Off)
3023
3024    try:
3025        plotNum = G2frame.G2plotNB.plotList.index('Modulation')
3026        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3027        Page.figure.clf()
3028        Plot = Page.figure.gca()
3029        if not Page.IsShown():
3030            Page.Show()
3031    except ValueError:
3032        Plot = G2frame.G2plotNB.addMpl('Modulation').gca()
3033        plotNum = G2frame.G2plotNB.plotList.index('Modulation')
3034        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3035        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
3036        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
3037   
3038    Page.SetFocus()
3039    General = data['General']
3040    cx,ct,cs,cia = General['AtomPtrs']
3041    mapData = General['Map']
3042    if mapData['Flip']:
3043        Page.Choice = ['+: shift up','-: shift down','0: reset shift','l: move left','r: move right']
3044    else:
3045        Page.Choice = ['+: shift up','-: shift down','0: reset shift']
3046    Page.keyPress = OnPlotKeyPress
3047    Map = General['4DmapData']
3048    MapType = Map['MapType']
3049    rhoSize = np.array(Map['rho'].shape)
3050    atxyz = np.array(atom[cx:cx+3])
3051    waveType = atom[-1]['SS1']['waveType']
3052    Spos = atom[-1]['SS1']['Spos']
3053    tau = np.linspace(0.,2.,101)
3054    wave = np.zeros((3,101))
3055    if len(Spos):
3056        scof = []
3057        ccof = []
3058        for i,spos in enumerate(Spos):
3059            if waveType in ['Sawtooth','ZigZag'] and not i:
3060                Toff = spos[0][0]
3061                slopes = np.array(spos[0][1:])
3062                if waveType == 'Sawtooth':
3063                    wave = G2mth.posSawtooth(tau,Toff,slopes)
3064                elif waveType == 'ZigZag':
3065                    wave = G2mth.posZigZag(tau,Toff,slopes)
3066            else:
3067                scof.append(spos[0][:3])
3068                ccof.append(spos[0][3:])
3069        wave += G2mth.posFourier(tau,np.array(scof),np.array(ccof),1)  #hmm, why -1 work for Na2CO3?
3070    if mapData['Flip']:
3071        Title = 'Charge flip'
3072    else:
3073        Title = MapType
3074    Title += ' map for atom '+atom[0]+    \
3075        ' at %.4f %.4f %.4f'%(atxyz[0],atxyz[1],atxyz[2])
3076    ix = -np.array(np.rint(rhoSize[:3]*atxyz),dtype='i')
3077    ix += (rhoSize[:3]/2)
3078    ix = ix%rhoSize[:3]
3079    rho = np.roll(np.roll(np.roll(Map['rho'],ix[0],axis=0),ix[1],axis=1),ix[2],axis=2)
3080    ix = rhoSize[:3]/2
3081    ib = 4
3082    if Ax == 'x':
3083        slab = np.sum(np.sum(rho[:,ix[1]-ib:ix[1]+ib,ix[2]-ib:ix[2]+ib,:],axis=2),axis=1)
3084        Plot.plot(tau,wave[0])
3085    elif Ax == 'y':
3086        slab = np.sum(np.sum(rho[ix[0]-ib:ix[0]+ib,:,ix[2]-ib:ix[2]+ib,:],axis=2),axis=0)
3087        Plot.plot(tau,wave[1])
3088    elif Ax == 'z':
3089        slab = np.sum(np.sum(rho[ix[0]-ib:ix[0]+ib,ix[1]-ib:ix[1]+ib,:,:],axis=1),axis=0)
3090        Plot.plot(tau,wave[2])
3091    Plot.set_title(Title)
3092    Plot.set_xlabel('t')
3093    Plot.set_ylabel(r'$\mathsf{\Delta}$%s'%(Ax))
3094    Slab = np.hstack((slab,slab,slab))
3095    Plot.contour(Slab[:,:21],20,extent=(0.,2.,-.5+Off*.005,.5+Off*.005))
3096    Page.canvas.draw()
3097   
3098################################################################################
3099##### PlotCovariance
3100################################################################################
3101           
3102def PlotCovariance(G2frame,Data):
3103    'needs a doc string'
3104    if not Data:
3105        print 'No covariance matrix available'
3106        return
3107    varyList = Data['varyList']
3108    values = Data['variables']
3109    Xmax = len(varyList)
3110    covMatrix = Data['covMatrix']
3111    sig = np.sqrt(np.diag(covMatrix))
3112    xvar = np.outer(sig,np.ones_like(sig))
3113    covArray = np.divide(np.divide(covMatrix,xvar),xvar.T)
3114    title = ' for\n'+Data['title']
3115    newAtomDict = Data.get('newAtomDict',{})
3116    G2frame.G2plotNB.Delete('Covariance')
3117   
3118
3119    def OnPlotKeyPress(event):
3120        newPlot = False
3121        if event.key == 's':
3122            choice = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
3123            choice.sort()
3124            dlg = wx.SingleChoiceDialog(G2frame,'Select','Color scheme',choice)
3125            if dlg.ShowModal() == wx.ID_OK:
3126                sel = dlg.GetSelection()
3127                G2frame.VcovColor = choice[sel]
3128            else:
3129                G2frame.VcovColor = 'RdYlGn'
3130            dlg.Destroy()
3131        PlotCovariance(G2frame,Data)
3132
3133    def OnMotion(event):
3134        if event.button:
3135            ytics = imgAx.get_yticks()
3136            ytics = np.where(ytics<len(varyList),ytics,-1)
3137            ylabs = [np.where(0<=i ,varyList[int(i)],' ') for i in ytics]
3138            imgAx.set_yticklabels(ylabs)           
3139        if event.xdata and event.ydata:                 #avoid out of frame errors
3140            xpos = int(event.xdata+.5)
3141            ypos = int(event.ydata+.5)
3142            if -1 < xpos < len(varyList) and -1 < ypos < len(varyList):
3143                if xpos == ypos:
3144                    value = values[xpos]
3145                    name = varyList[xpos]
3146                    if varyList[xpos] in newAtomDict:
3147                        name,value = newAtomDict[name]                       
3148                    msg = '%s value = %.4g, esd = %.4g'%(name,value,sig[xpos])
3149                else:
3150                    msg = '%s - %s: %5.3f'%(varyList[xpos],varyList[ypos],covArray[xpos][ypos])
3151                Page.canvas.SetToolTipString(msg)
3152                G2frame.G2plotNB.status.SetFields(['',msg])
3153               
3154    try:
3155        plotNum = G2frame.G2plotNB.plotList.index('Covariance')
3156        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3157        Page.figure.clf()
3158        Plot = Page.figure.gca()
3159        if not Page.IsShown():
3160            Page.Show()
3161    except ValueError:
3162        Plot = G2frame.G2plotNB.addMpl('Covariance').gca()
3163        plotNum = G2frame.G2plotNB.plotList.index('Covariance')
3164        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3165        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
3166        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
3167    Page.Choice = ['s: to change colors']
3168    Page.keyPress = OnPlotKeyPress
3169    Page.SetFocus()
3170    G2frame.G2plotNB.status.SetFields(['',''])   
3171    acolor = mpl.cm.get_cmap(G2frame.VcovColor)
3172    Img = Plot.imshow(covArray,aspect='equal',cmap=acolor,interpolation='nearest',origin='lower',
3173        vmin=-1.,vmax=1.)
3174    imgAx = Img.get_axes()
3175    ytics = imgAx.get_yticks()
3176    ylabs = [varyList[int(i)] for i in ytics[:-1]]
3177    imgAx.set_yticklabels(ylabs)
3178    colorBar = Page.figure.colorbar(Img)
3179    Plot.set_title('V-Cov matrix'+title)
3180    Plot.set_xlabel('Variable number')
3181    Plot.set_ylabel('Variable name')
3182    Page.canvas.draw()
3183   
3184################################################################################
3185##### PlotTorsion
3186################################################################################
3187
3188def PlotTorsion(G2frame,phaseName,Torsion,TorName,Names=[],Angles=[],Coeff=[]):
3189    'needs a doc string'
3190   
3191    global names
3192    names = Names
3193    sum = np.sum(Torsion)
3194    torsion = np.log(2*Torsion+1.)/sum
3195    tMin = np.min(torsion)
3196    tMax = np.max(torsion)
3197    torsion = 3.*(torsion-tMin)/(tMax-tMin)
3198    X = np.linspace(0.,360.,num=45)
3199   
3200    def OnPick(event):
3201        ind = event.ind[0]
3202        msg = 'atoms:'+names[ind]
3203        Page.canvas.SetToolTipString(msg)
3204        try:
3205            page = G2frame.dataDisplay.GetSelection()
3206        except:
3207            return
3208        if G2frame.dataDisplay.GetPageText(page) == 'Torsion restraints':
3209            torGrid = G2frame.dataDisplay.GetPage(page).Torsions
3210            torGrid.ClearSelection()
3211            for row in range(torGrid.GetNumberRows()):
3212                if names[ind] in torGrid.GetCellValue(row,0):
3213                    torGrid.SelectRow(row)
3214            torGrid.ForceRefresh()
3215               
3216    def OnMotion(event):
3217        if event.xdata and event.ydata:                 #avoid out of frame errors
3218            xpos = event.xdata
3219            ypos = event.ydata
3220            msg = 'torsion,energy: %5.3f %5.3f'%(xpos,ypos)
3221            Page.canvas.SetToolTipString(msg)
3222
3223    try:
3224        plotNum = G2frame.G2plotNB.plotList.index('Torsion')
3225        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3226        Page.figure.clf()
3227        Plot = Page.figure.gca()
3228        if not Page.IsShown():
3229            Page.Show()
3230    except ValueError:
3231        Plot = G2frame.G2plotNB.addMpl('Torsion').gca()
3232        plotNum = G2frame.G2plotNB.plotList.index('Torsion')
3233        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3234        Page.canvas.mpl_connect('pick_event', OnPick)
3235        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
3236   
3237    Page.SetFocus()
3238    G2frame.G2plotNB.status.SetFields(['','Use mouse LB to identify torsion atoms'])
3239    Plot.plot(X,torsion,'b+')
3240    if len(Coeff):
3241        X2 = np.linspace(0.,360.,45)
3242        Y2 = np.array([-G2mth.calcTorsionEnergy(x,Coeff)[1] for x in X2])
3243        Plot.plot(X2,Y2,'r')
3244    if len(Angles):
3245        Eval = np.array([-G2mth.calcTorsionEnergy(x,Coeff)[1] for x in Angles])
3246        Plot.plot(Angles,Eval,'ro',picker=5)
3247    Plot.set_xlim((0.,360.))
3248    Plot.set_title('Torsion angles for '+TorName+' in '+phaseName)
3249    Plot.set_xlabel('angle',fontsize=16)
3250    Plot.set_ylabel('Energy',fontsize=16)
3251    Page.canvas.draw()
3252   
3253################################################################################
3254##### PlotRama
3255################################################################################
3256
3257def PlotRama(G2frame,phaseName,Rama,RamaName,Names=[],PhiPsi=[],Coeff=[]):
3258    'needs a doc string'
3259
3260    global names
3261    names = Names
3262    rama = np.log(2*Rama+1.)
3263    ramaMax = np.max(rama)
3264    rama = np.reshape(rama,(45,45))
3265    global Phi,Psi
3266    Phi = []
3267    Psi = []
3268
3269    def OnPlotKeyPress(event):
3270        newPlot = False
3271        if event.key == 's':
3272            choice = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
3273            choice.sort()
3274            dlg = wx.SingleChoiceDialog(G2frame,'Select','Color scheme',choice)
3275            if dlg.ShowModal() == wx.ID_OK:
3276                sel = dlg.GetSelection()
3277                G2frame.RamaColor = choice[sel]
3278            else:
3279                G2frame.RamaColor = 'RdYlGn'
3280            dlg.Destroy()
3281        PlotRama(G2frame,phaseName,Rama)
3282
3283    def OnPick(event):
3284        ind = event.ind[0]
3285        msg = 'atoms:'+names[ind]
3286        Page.canvas.SetToolTipString(msg)
3287        try:
3288            page = G2frame.dataDisplay.GetSelection()
3289        except:
3290            return
3291        if G2frame.dataDisplay.GetPageText(page) == 'Ramachandran restraints':
3292            ramaGrid = G2frame.dataDisplay.GetPage(page).Ramas
3293            ramaGrid.ClearSelection()
3294            for row in range(ramaGrid.GetNumberRows()):
3295                if names[ind] in ramaGrid.GetCellValue(row,0):
3296                    ramaGrid.SelectRow(row)
3297            ramaGrid.ForceRefresh()
3298
3299    def OnMotion(event):
3300        if event.xdata and event.ydata:                 #avoid out of frame errors
3301            xpos = event.xdata
3302            ypos = event.ydata
3303            msg = 'phi/psi: %5.3f %5.3f'%(xpos,ypos)
3304            Page.canvas.SetToolTipString(msg)
3305           
3306    try:
3307        plotNum = G2frame.G2plotNB.plotList.index('Ramachandran')
3308        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3309        Page.figure.clf()
3310        Plot = Page.figure.gca()
3311        if not Page.IsShown():
3312            Page.Show()
3313    except ValueError:
3314        Plot = G2frame.G2plotNB.addMpl('Ramachandran').gca()
3315        plotNum = G2frame.G2plotNB.plotList.index('Ramachandran')
3316        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3317        Page.canvas.mpl_connect('pick_event', OnPick)
3318        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
3319        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
3320
3321    Page.Choice = ['s: to change colors']
3322    Page.keyPress = OnPlotKeyPress
3323    Page.SetFocus()
3324    G2frame.G2plotNB.status.SetFields(['','Use mouse LB to identify phi/psi atoms'])
3325    acolor = mpl.cm.get_cmap(G2frame.RamaColor)
3326    if RamaName == 'All' or '-1' in RamaName:
3327        if len(Coeff): 
3328            X,Y = np.meshgrid(np.linspace(-180.,180.,45),np.linspace(-180.,180.,45))
3329            Z = np.array([-G2mth.calcRamaEnergy(x,y,Coeff)[1] for x,y in zip(X.flatten(),Y.flatten())])
3330            Plot.contour(X,Y,np.reshape(Z,(45,45)))
3331        Img = Plot.imshow(rama,aspect='equal',cmap=acolor,interpolation='nearest',
3332            extent=[-180,180,-180,180],origin='lower')
3333        if len(PhiPsi):
3334            Phi,Psi = PhiPsi.T
3335            Phi = np.where(Phi>180.,Phi-360.,Phi)
3336            Psi = np.where(Psi>180.,Psi-360.,Psi)
3337            Plot.plot(Phi,Psi,'ro',picker=5)
3338        Plot.set_xlim((-180.,180.))
3339        Plot.set_ylim((-180.,180.))
3340    else:
3341        if len(Coeff): 
3342            X,Y = np.meshgrid(np.linspace(0.,360.,45),np.linspace(0.,360.,45))
3343            Z = np.array([-G2mth.calcRamaEnergy(x,y,Coeff)[1] for x,y in zip(X.flatten(),Y.flatten())])
3344            Plot.contour(X,Y,np.reshape(Z,(45,45)))
3345        Img = Plot.imshow(rama,aspect='equal',cmap=acolor,interpolation='nearest',
3346            extent=[0,360,0,360],origin='lower')
3347        if len(PhiPsi):
3348            Phi,Psi = PhiPsi.T
3349            Plot.plot(Phi,Psi,'ro',picker=5)
3350        Plot.set_xlim((0.,360.))
3351        Plot.set_ylim((0.,360.))
3352    Plot.set_title('Ramachandran for '+RamaName+' in '+phaseName)
3353    Plot.set_xlabel(r'$\phi$',fontsize=16)
3354    Plot.set_ylabel(r'$\psi$',fontsize=16)
3355    colorBar = Page.figure.colorbar(Img)
3356    Page.canvas.draw()
3357
3358
3359################################################################################
3360##### PlotSeq
3361################################################################################
3362def PlotSelectedSequence(G2frame,ColumnList,TableGet,SelectX,fitnum=None,fitvals=None):
3363    '''Plot a result from a sequential refinement
3364
3365    :param wx.Frame G2frame: The main GSAS-II tree "window"
3366    :param list ColumnList: list of int values corresponding to columns
3367      selected as y values
3368    :param function TableGet: a function that takes a column number
3369      as argument and returns the column label, the values and there ESDs (or None)
3370    :param function SelectX: a function that returns a selected column
3371      number (or None) as the X-axis selection
3372    '''
3373    global Title,xLabel,yLabel
3374    xLabel = yLabel = Title = ''
3375    def OnMotion(event):
3376        if event.xdata and event.ydata:                 #avoid out of frame errors
3377            xpos = event.xdata
3378            ypos = event.ydata
3379            msg = '%5.3f %.6g'%(xpos,ypos)
3380            Page.canvas.SetToolTipString(msg)
3381
3382    def OnKeyPress(event):
3383        global Title,xLabel,yLabel
3384        if event.key == 's':
3385            G2frame.seqXaxis = G2frame.seqXselect()
3386            Draw()
3387        elif event.key == 't':
3388            dlg = G2G.MultiStringDialog(G2frame,'Set titles & labels',[' Title ',' x-Label ',' y-Label '],
3389                [Title,xLabel,yLabel])
3390            if dlg.Show():
3391                Title,xLabel,yLabel = dlg.GetValues()
3392            dlg.Destroy()
3393            Draw()
3394        elif event.key == 'l':
3395            G2frame.seqLines = not G2frame.seqLines
3396            wx.CallAfter(Draw)
3397           
3398    def Draw():
3399        global Title,xLabel,yLabel
3400        Page.SetFocus()
3401        G2frame.G2plotNB.status.SetStatusText(  \
3402            'press L to toggle lines, S to select X axis, T to change titles (reselect column to show?)',1)
3403        Plot.clear()
3404        if G2frame.seqXaxis is not None:   
3405            xName,X,Xsig = Page.seqTableGet(G2frame.seqXaxis)
3406        else:
3407            X = np.arange(0,G2frame.SeqTable.GetNumberRows(),1)
3408            xName = 'Data sequence number'
3409        for col in Page.seqYaxisList:
3410            name,Y,sig = Page.seqTableGet(col)
3411            # deal with missing (None) values
3412            Xnew = []
3413            Ynew = []
3414            Ysnew = []
3415            for i in range(len(X)):
3416                if X[i] is None or Y[i] is None: continue
3417                Xnew.append(X[i])
3418                Ynew.append(Y[i])
3419                if sig: Ysnew.append(sig[i])
3420            if Ysnew:
3421                if G2frame.seqReverse and not G2frame.seqXaxis:
3422                    Ynew = Ynew[::-1]
3423                    Ysnew = Ysnew[::-1]
3424                if G2frame.seqLines:
3425                    Plot.errorbar(Xnew,Ynew,yerr=Ysnew,label=name)
3426                else:
3427                    Plot.errorbar(Xnew,Ynew,yerr=Ysnew,label=name,linestyle='None',marker='x')
3428            else:
3429                if G2frame.seqReverse and not G2frame.seqXaxis:
3430                    Ynew = Ynew[::-1]
3431                Plot.plot(Xnew,Ynew)
3432                Plot.plot(Xnew,Ynew,'o',label=name)
3433        if Page.fitvals: # TODO: deal with fitting of None values
3434            if G2frame.seqReverse and not G2frame.seqXaxis:
3435                Page.fitvals = Page.fitvals[::-1]
3436            Plot.plot(X,Page.fitvals,label='Fit')
3437           
3438        Plot.legend(loc='best')
3439        if Title:
3440            Plot.set_title(Title)
3441        else:
3442            Plot.set_title('')
3443        if xLabel:
3444            Plot.set_xlabel(xLabel)
3445        else:
3446            Plot.set_xlabel(xName)
3447        if yLabel:
3448            Plot.set_ylabel(yLabel)
3449        else:
3450            Plot.set_ylabel('Parameter values')
3451        Page.canvas.draw()
3452           
3453    G2frame.seqXselect = SelectX
3454    try:
3455        G2frame.seqXaxis
3456    except:
3457        G2frame.seqXaxis = None
3458
3459    if fitnum is None:
3460        label = 'Sequential refinement'
3461    else:
3462        label = 'Parametric fit #'+str(fitnum+1)
3463    try:
3464        plotNum = G2frame.G2plotNB.plotList.index(label)
3465        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3466        Page.figure.clf()
3467        Plot = Page.figure.gca()
3468        if not Page.IsShown():
3469            Page.Show()
3470    except ValueError:
3471        Plot = G2frame.G2plotNB.addMpl(label).gca()
3472        plotNum = G2frame.G2plotNB.plotList.index(label)
3473        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3474        Page.canvas.mpl_connect('key_press_event', OnKeyPress)
3475        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
3476    Page.Choice = ['l - toggle lines','s - select x-axis','t - change titles',]
3477    Page.keyPress = OnKeyPress
3478    Page.seqYaxisList = ColumnList
3479    Page.seqTableGet = TableGet
3480    Page.fitvals = fitvals
3481       
3482    Draw()
3483    G2frame.G2plotNB.nb.SetSelection(plotNum) # raises plot tab
3484               
3485################################################################################
3486##### PlotExposedImage & PlotImage
3487################################################################################
3488           
3489def PlotExposedImage(G2frame,newPlot=False,event=None):
3490    '''General access module for 2D image plotting
3491    '''
3492    plotNo = G2frame.G2plotNB.nb.GetSelection()
3493    if G2frame.G2plotNB.nb.GetPageText(plotNo) == '2D Powder Image':
3494        PlotImage(G2frame,newPlot,event,newImage=True)
3495    elif G2frame.G2plotNB.nb.GetPageText(plotNo) == '2D Integration':
3496        PlotIntegration(G2frame,newPlot,event)
3497
3498def OnStartMask(G2frame):
3499    '''Initiate the start of a Frame or Polygon map
3500
3501    :param wx.Frame G2frame: The main GSAS-II tree "window"
3502    :param str eventkey: a single letter ('f' or 'p') that
3503      determines what type of mask is created.   
3504    '''
3505    Masks = G2frame.PatternTree.GetItemPyData(
3506        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
3507    if G2frame.MaskKey == 'f':
3508        Masks['Frames'] = []
3509    elif G2frame.MaskKey == 'p':
3510        Masks['Polygons'].append([])
3511    elif G2frame.MaskKey == 's':
3512        Masks['Points'].append([])
3513    elif G2frame.MaskKey == 'a':
3514        Masks['Arcs'].append([])
3515    elif G2frame.MaskKey == 'r':
3516        Masks['Rings'].append([])
3517    G2imG.UpdateMasks(G2frame,Masks)
3518    PlotImage(G2frame,newImage=True)
3519   
3520def OnStartNewDzero(G2frame):
3521    '''Initiate the start of adding a new d-zero to a strain data set
3522
3523    :param wx.Frame G2frame: The main GSAS-II tree "window"
3524    :param str eventkey: a single letter ('a') that
3525      triggers the addition of a d-zero.   
3526    '''
3527    G2frame.dataFrame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value',0)
3528    G2frame.PickId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Stress/Strain')
3529    data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
3530    return data
3531
3532def PlotImage(G2frame,newPlot=False,event=None,newImage=True):
3533    '''Plot of 2D detector images as contoured plot. Also plot calibration ellipses,
3534    masks, etc.
3535    '''
3536    from matplotlib.patches import Ellipse,Arc,Circle,Polygon
3537    import numpy.ma as ma
3538    Dsp = lambda tth,wave: wave/(2.*npsind(tth/2.))
3539    global Data,Masks,StrSta
3540    colors=['b','g','r','c','m','k']
3541    Data = G2frame.PatternTree.GetItemPyData(
3542        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
3543# patch
3544    if 'invert_x' not in Data:
3545        Data['invert_x'] = False
3546        Data['invert_y'] = True
3547# end patch
3548    Masks = G2frame.PatternTree.GetItemPyData(
3549        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
3550    try:    #may be absent
3551        StrSta = G2frame.PatternTree.GetItemPyData(
3552            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Stress/Strain'))
3553    except TypeError:   #is missing
3554        StrSta = {}
3555
3556    def OnImMotion(event):
3557        Page.canvas.SetToolTipString('')
3558        sizexy = Data['size']
3559        FlatBkg = Data.get('Flat Bkg',0.)
3560        if event.xdata and event.ydata and len(G2frame.ImageZ):                 #avoid out of frame errors
3561            Page.canvas.SetToolTipString('%8.2f %8.2fmm'%(event.xdata,event.ydata))
3562            Page.canvas.SetCursor(wx.CROSS_CURSOR)
3563            item = G2frame.itemPicked
3564            pixelSize = Data['pixelSize']
3565            scalex = 1000./pixelSize[0]
3566            scaley = 1000./pixelSize[1]
3567            if item and G2frame.PatternTree.GetItemText(G2frame.PickId) == 'Image Controls':
3568                if 'Text' in str(item):
3569                    Page.canvas.SetToolTipString('%8.3f %8.3fmm'%(event.xdata,event.ydata))
3570                else:
3571                    xcent,ycent = Data['center']
3572                    xpos = event.xdata-xcent
3573                    ypos = event.ydata-ycent
3574                    tth,azm = G2img.GetTthAzm(event.xdata,event.ydata,Data)
3575                    if 'line3' in  str(item) or 'line4' in str(item) and not Data['fullIntegrate']:
3576                        Page.canvas.SetToolTipString('%6d deg'%(azm))
3577                    elif 'line1' in  str(item) or 'line2' in str(item):
3578                        Page.canvas.SetToolTipString('%8.3f deg'%(tth))                           
3579            else:
3580                xpos = event.xdata
3581                ypos = event.ydata
3582                xpix = xpos*scalex
3583                ypix = ypos*scaley
3584                Int = 0
3585                if (0 <= xpix <= sizexy[0]) and (0 <= ypix <= sizexy[1]):
3586                    Int = G2frame.ImageZ[ypix][xpix]-int(FlatBkg)
3587                tth,azm,D,dsp = G2img.GetTthAzmDsp(xpos,ypos,Data)
3588                Q = 2.*math.pi/dsp
3589                fields = ['','Detector 2-th =%9.3fdeg, dsp =%9.3fA, Q = %6.5fA-1, azm = %7.2fdeg, I = %6d'%(tth,dsp,Q,azm,Int)]
3590                if G2frame.MaskKey in ['p','f']:
3591                    fields[1] = 'Polygon/frame mask pick - LB next point, RB close polygon'
3592                elif G2frame.StrainKey:
3593                    fields[0] = 'd-zero pick active'
3594                G2frame.G2plotNB.status.SetFields(fields)
3595
3596    def OnImPlotKeyPress(event):
3597        try:
3598            PickName = G2frame.PatternTree.GetItemText(G2frame.PickId)
3599        except TypeError:
3600            return
3601        if PickName == 'Masks':
3602            if event.key in ['l','p','f','s','a','r']:
3603                G2frame.MaskKey = event.key
3604                OnStartMask(G2frame)
3605                PlotImage(G2frame,newPlot=False)
3606               
3607        elif PickName == 'Stress/Strain':
3608            if event.key in ['a',]:
3609                G2frame.StrainKey = event.key
3610                StrSta = OnStartNewDzero(G2frame)
3611                PlotImage(G2frame,newPlot=False)
3612               
3613        elif PickName == 'Image Controls':
3614            if event.key in ['c',]:
3615                Xpos = event.xdata
3616                if not Xpos:            #got point out of frame
3617                    return
3618                Ypos = event.ydata
3619                dlg = wx.MessageDialog(G2frame,'Are you sure you want to change the center?',
3620                    'Center change',style=wx.OK|wx.CANCEL)
3621                try:
3622                    if dlg.ShowModal() == wx.ID_OK:
3623                        print 'move center to: ',Xpos,Ypos
3624                        Data['center'] = [Xpos,Ypos]
3625                        G2imG.UpdateImageControls(G2frame,Data,Masks)
3626                        PlotImage(G2frame,newPlot=False)
3627                finally:
3628                    dlg.Destroy()
3629                return
3630            elif event.key == 'l':
3631                G2frame.logPlot = not G2frame.logPlot
3632            elif event.key in ['x',]:
3633                Data['invert_x'] = not Data['invert_x']
3634            elif event.key in ['y',]:
3635                Data['invert_y'] = not Data['invert_y']
3636            PlotImage(G2frame,newPlot=True)
3637           
3638    def OnKeyBox(event):
3639        if G2frame.G2plotNB.nb.GetSelection() == G2frame.G2plotNB.plotList.index('2D Powder Image'):
3640            event.key = cb.GetValue()[0]
3641            cb.SetValue(' key press')
3642            if event.key in ['l','s','a','r','p','x','y']:
3643                wx.CallAfter(OnImPlotKeyPress,event)
3644        Page.canvas.SetFocus() # redirect the Focus from the button back to the plot
3645                       
3646    def OnImPick(event):
3647        if G2frame.PatternTree.GetItemText(G2frame.PickId) not in ['Image Controls','Masks']:
3648            return
3649        if G2frame.itemPicked is not None: return
3650        G2frame.itemPicked = event.artist
3651        G2frame.mousePicked = event.mouseevent
3652       
3653    def OnImRelease(event):
3654        try:
3655            PickName = G2frame.PatternTree.GetItemText(G2frame.PickId)
3656        except TypeError:
3657            return
3658        if PickName not in ['Image Controls','Masks','Stress/Strain']:
3659            return
3660        pixelSize = Data['pixelSize']
3661        FlatBkg = Data.get('Flat Bkg',0.)
3662        scalex = 1000./pixelSize[0]
3663        scaley = 1000./pixelSize[1]
3664#        pixLimit = Data['pixLimit']    #can be too tight
3665        pixLimit = 20       #this makes the search box 40x40 pixels
3666        if G2frame.itemPicked is None and PickName == 'Image Controls' and len(G2frame.ImageZ):
3667            Xpos = event.xdata
3668            if not (Xpos and G2frame.ifGetRing):                   #got point out of frame
3669                return
3670            Ypos = event.ydata
3671            if Ypos and not Page.toolbar._active:         #make sure zoom/pan not selected
3672                if event.button == 1:
3673                    Xpix = Xpos*scalex
3674                    Ypix = Ypos*scaley
3675                    xpos,ypos,I,J = G2img.ImageLocalMax(G2frame.ImageZ-FlatBkg,pixLimit,Xpix,Ypix)
3676                    if I and J:
3677                        xpos += .5                              #shift to pixel center
3678                        ypos += .5
3679                        xpos /= scalex                          #convert to mm
3680                        ypos /= scaley
3681                        Data['ring'].append([xpos,ypos])
3682                elif event.button == 3:
3683                    G2frame.dataFrame.GetStatusBar().SetStatusText('Calibrating...',0)
3684                    if G2img.ImageCalibrate(G2frame,Data):
3685                        G2frame.dataFrame.GetStatusBar().SetStatusText('Calibration successful - Show ring picks to check',0)
3686                        print 'Calibration successful'
3687                    else:
3688                        G2frame.dataFrame.GetStatusBar().SetStatusText('Calibration failed - Show ring picks to diagnose',0)
3689                        print 'Calibration failed'
3690                    G2frame.ifGetRing = False
3691                    G2imG.UpdateImageControls(G2frame,Data,Masks)
3692                    return
3693                PlotImage(G2frame,newImage=False)
3694            return
3695        elif G2frame.MaskKey and PickName == 'Masks':
3696            Xpos,Ypos = [event.xdata,event.ydata]
3697            if not Xpos or not Ypos or Page.toolbar._active:  #got point out of frame or zoom/pan selected
3698                return
3699            if G2frame.MaskKey == 's' and event.button == 1:
3700                Masks['Points'][-1] = [Xpos,Ypos,1.]
3701                G2frame.MaskKey = ''               
3702            elif G2frame.MaskKey == 'r' and event.button == 1:
3703                tth = G2img.GetTth(Xpos,Ypos,Data)
3704                Masks['Rings'][-1] = [tth,0.1]
3705                G2frame.MaskKey = ''               
3706            elif G2frame.MaskKey == 'a' and event.button == 1:
3707                tth,azm = G2img.GetTthAzm(Xpos,Ypos,Data)
3708                azm = int(azm)               
3709                Masks['Arcs'][-1] = [tth,[azm-5,azm+5],0.1]
3710                G2frame.MaskKey = ''               
3711            elif G2frame.MaskKey =='p':
3712                polygon = Masks['Polygons'][-1]
3713                if len(polygon) > 2 and event.button == 3:
3714                    x0,y0 = polygon[0]
3715                    polygon.append([x0,y0])
3716                    G2frame.MaskKey = ''
3717                    G2frame.G2plotNB.status.SetFields(['','Polygon closed'])
3718                else:
3719                    G2frame.G2plotNB.status.SetFields(['','New polygon point: %.1f,%.1f'%(Xpos,Ypos)])
3720                    polygon.append([Xpos,Ypos])
3721            elif G2frame.MaskKey =='f':
3722                frame = Masks['Frames']
3723                if len(frame) > 2 and event.button == 3:
3724                    x0,y0 = frame[0]
3725                    frame.append([x0,y0])
3726                    G2frame.MaskKey = ''
3727                    G2frame.G2plotNB.status.SetFields(['','Frame closed'])
3728                else:
3729                    G2frame.G2plotNB.status.SetFields(['','New frame point: %.1f,%.1f'%(Xpos,Ypos)])
3730                    frame.append([Xpos,Ypos])
3731            G2imG.UpdateMasks(G2frame,Masks)
3732            PlotImage(G2frame,newImage=False)
3733        elif PickName == 'Stress/Strain' and G2frame.StrainKey:
3734            Xpos,Ypos = [event.xdata,event.ydata]
3735            if not Xpos or not Ypos or Page.toolbar._active:  #got point out of frame or zoom/pan selected
3736                return
3737            dsp = G2img.GetDsp(Xpos,Ypos,Data)
3738            StrSta['d-zero'].append({'Dset':dsp,'Dcalc':0.0,'pixLimit':10,'cutoff':0.5,
3739                'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0]})
3740            R,r = G2img.MakeStrStaRing(StrSta['d-zero'][-1],G2frame.ImageZ-FlatBkg,Data)
3741            if not len(R):
3742                del StrSta['d-zero'][-1]
3743                G2frame.ErrorDialog('Strain peak selection','WARNING - No points found for this ring selection')
3744            StrSta['d-zero'] = G2mth.sortArray(StrSta['d-zero'],'Dset',reverse=True)
3745            G2frame.StrainKey = ''
3746            G2imG.UpdateStressStrain(G2frame,StrSta)
3747            PlotStrain(G2frame,StrSta)
3748            PlotImage(G2frame,newPlot=False)           
3749        else:
3750            Xpos,Ypos = [event.xdata,event.ydata]
3751            if not Xpos or not Ypos or Page.toolbar._active:  #got point out of frame or zoom/pan selected
3752                return
3753            if G2frame.ifGetRing:                          #delete a calibration ring pick
3754                xypos = [Xpos,Ypos]
3755                rings = Data['ring']
3756                for ring in rings:
3757                    if np.allclose(ring,xypos,.01,0):
3758                        rings.remove(ring)
3759            else:
3760                tth,azm,dsp = G2img.GetTthAzmDsp(Xpos,Ypos,Data)[:3]
3761                itemPicked = str(G2frame.itemPicked)
3762                if 'Line2D' in itemPicked and PickName == 'Image Controls':
3763                    if 'line1' in itemPicked:
3764                        Data['IOtth'][0] = max(tth,0.001)
3765                    elif 'line2' in itemPicked:
3766                        Data['IOtth'][1] = tth
3767                    elif 'line3' in itemPicked:
3768                        Data['LRazimuth'][0] = int(azm)
3769                    elif 'line4' in itemPicked and not Data['fullIntegrate']:
3770                        Data['LRazimuth'][1] = int(azm)
3771                   
3772                    Data['LRazimuth'][0] %= 360
3773                    Data['LRazimuth'][1] %= 360
3774                    if Data['LRazimuth'][0] > Data['LRazimuth'][1]:
3775                        Data['LRazimuth'][1] += 360                       
3776                    if Data['fullIntegrate']:
3777                        Data['LRazimuth'][1] = Data['LRazimuth'][0]+360
3778                       
3779                    if  Data['IOtth'][0] > Data['IOtth'][1]:
3780                        Data['IOtth'][0],Data['IOtth'][1] = Data['IOtth'][1],Data['IOtth'][0]
3781                       
3782                    G2frame.InnerTth.SetValue("%8.2f" % (Data['IOtth'][0]))
3783                    G2frame.OuterTth.SetValue("%8.2f" % (Data['IOtth'][1]))
3784                    G2frame.Lazim.SetValue("%6d" % (Data['LRazimuth'][0]))
3785                    G2frame.Razim.SetValue("%6d" % (Data['LRazimuth'][1]))
3786                elif 'Circle' in itemPicked and PickName == 'Masks':
3787                    spots = Masks['Points']
3788                    newPos = itemPicked.split(')')[0].split('(')[2].split(',')
3789                    newPos = np.array([float(newPos[0]),float(newPos[1])])
3790                    for spot in spots:
3791                        if spot and np.allclose(np.array([spot[:2]]),newPos):
3792                            spot[:2] = Xpos,Ypos
3793                    G2imG.UpdateMasks(G2frame,Masks)
3794                elif 'Line2D' in itemPicked and PickName == 'Masks':
3795                    Obj = G2frame.itemPicked.findobj()
3796                    rings = Masks['Rings']
3797                    arcs = Masks['Arcs']
3798                    polygons = Masks['Polygons']
3799                    frame = Masks['Frames']
3800                    for ring in G2frame.ringList:
3801                        if Obj == ring[0]:
3802                            rN = ring[1]
3803                            if ring[2] == 'o':
3804                                rings[rN][0] = G2img.GetTth(Xpos,Ypos,Data)-rings[rN][1]/2.
3805                            else:
3806                                rings[rN][0] = G2img.GetTth(Xpos,Ypos,Data)+rings[rN][1]/2.
3807                    for arc in G2frame.arcList:
3808                        if Obj == arc[0]:
3809                            aN = arc[1]
3810                            if arc[2] == 'o':
3811                                arcs[aN][0] = G2img.GetTth(Xpos,Ypos,Data)-arcs[aN][2]/2
3812                            elif arc[2] == 'i':
3813                                arcs[aN][0] = G2img.GetTth(Xpos,Ypos,Data)+arcs[aN][2]/2
3814                            elif arc[2] == 'l':
3815                                arcs[aN][1][0] = int(G2img.GetAzm(Xpos,Ypos,Data))
3816                            else:
3817                                arcs[aN][1][1] = int(G2img.GetAzm(Xpos,Ypos,Data))
3818                    for poly in G2frame.polyList:   #merging points problem here?
3819                        if Obj == poly[0]:
3820                            ind = G2frame.itemPicked.contains(G2frame.mousePicked)[1]['ind'][0]
3821                            oldPos = np.array([G2frame.mousePicked.xdata,G2frame.mousePicked.ydata])
3822                            pN = poly[1]
3823                            for i,xy in enumerate(polygons[pN]):
3824                                if np.allclose(np.array([xy]),oldPos,atol=1.0):
3825                                    if event.button == 1:
3826                                        polygons[pN][i] = Xpos,Ypos
3827                                    elif event.button == 3:
3828                                        polygons[pN].insert(i,[Xpos,Ypos])
3829                                        break
3830                    if frame:
3831                        oldPos = np.array([G2frame.mousePicked.xdata,G2frame.mousePicked.ydata])
3832                        for i,xy in enumerate(frame):
3833                            if np.allclose(np.array([xy]),oldPos,atol=1.0):
3834                                if event.button == 1:
3835                                    frame[i] = Xpos,Ypos
3836                                elif event.button == 3:
3837                                    frame.insert(i,[Xpos,Ypos])
3838                                    break
3839                    G2imG.UpdateMasks(G2frame,Masks)
3840#                else:                  #keep for future debugging
3841#                    print str(G2frame.itemPicked),event.xdata,event.ydata,event.button
3842            PlotImage(G2frame,newImage=True)
3843            G2frame.itemPicked = None
3844           
3845    try:
3846        plotNum = G2frame.G2plotNB.plotList.index('2D Powder Image')
3847        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3848        if not newPlot:
3849            Plot = Page.figure.gca()          #get previous powder plot & get limits
3850            xylim = Plot.get_xlim(),Plot.get_ylim()
3851        if newImage:
3852            Page.figure.clf()
3853            Plot = Page.figure.gca()          #get a fresh plot after clf()
3854    except ValueError:
3855        Plot = G2frame.G2plotNB.addMpl('2D Powder Image').gca()
3856        plotNum = G2frame.G2plotNB.plotList.index('2D Powder Image')
3857        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3858        Page.canvas.mpl_connect('key_press_event', OnImPlotKeyPress)
3859        Page.canvas.mpl_connect('motion_notify_event', OnImMotion)
3860        Page.canvas.mpl_connect('pick_event', OnImPick)
3861        Page.canvas.mpl_connect('button_release_event', OnImRelease)
3862        xylim = []
3863    Page.Choice = None
3864    if not event:                       #event from GUI TextCtrl - don't want focus to change to plot!!!
3865        Page.SetFocus()
3866    Title = G2frame.PatternTree.GetItemText(G2frame.Image)[4:]
3867    G2frame.G2plotNB.status.DestroyChildren()
3868    if G2frame.logPlot:
3869        Title = 'log('+Title+')'
3870    Plot.set_title(Title)
3871    try:
3872        if G2frame.PatternTree.GetItemText(G2frame.PickId) in ['Image Controls',]:
3873            Page.Choice = (' key press','l: log(I) on','x: flip x','y: flip y',)
3874            if G2frame.logPlot:
3875                Page.Choice[1] = 'l: log(I) off'
3876            Page.keyPress = OnImPlotKeyPress
3877        elif G2frame.PatternTree.GetItemText(G2frame.PickId) in ['Masks',]:
3878            Page.Choice = (' key press','l: log(I) on','s: spot mask','a: arc mask','r: ring mask',
3879                'p: polygon mask','f: frame mask',)
3880            if G2frame.logPlot:
3881                Page.Choice[1] = 'l: log(I) off'
3882            Page.keyPress = OnImPlotKeyPress
3883        elif G2frame.PatternTree.GetItemText(G2frame.PickId) in ['Stress/Strain',]:
3884            Page.Choice = (' key press','a: add new ring',)
3885            Page.keyPress = OnImPlotKeyPress
3886    except TypeError:
3887        pass
3888    size,imagefile = G2frame.PatternTree.GetItemPyData(G2frame.Image)
3889    dark = Data.get('dark image',[0,''])
3890    if dark[0]:
3891        darkfile = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, 
3892            G2frame.root,dark[0]))[1]
3893    if imagefile != G2frame.oldImagefile:
3894        imagefile = G2IO.CheckImageFile(G2frame,imagefile)
3895        if not imagefile:
3896            G2frame.G2plotNB.Delete('2D Powder Image')
3897            return
3898        G2frame.PatternTree.SetItemPyData(G2frame.Image,[size,imagefile])
3899        G2frame.ImageZ = G2IO.GetImageData(G2frame,imagefile,imageOnly=True)
3900        if dark[0]:
3901            darkImg = G2IO.GetImageData(G2frame,darkfile,imageOnly=True)
3902            G2frame.ImageZ += dark[1]*darkImg
3903        G2frame.oldImagefile = imagefile
3904
3905    imScale = 1
3906    if len(G2frame.ImageZ) > 1024:
3907        imScale = len(G2frame.ImageZ)/1024
3908    sizexy = Data['size']
3909    pixelSize = Data['pixelSize']
3910    scalex = 1000./pixelSize[0]
3911    scaley = 1000./pixelSize[1]
3912    Xmax = sizexy[0]*pixelSize[0]/1000.
3913    Ymax = sizexy[1]*pixelSize[1]/1000.
3914    xlim = (0,Xmax)
3915    ylim = (Ymax,0)
3916    Imin,Imax = Data['range'][1]
3917    acolor = mpl.cm.get_cmap(Data['color'])
3918    xcent,ycent = Data['center']
3919    Plot.set_xlabel('Image x-axis, mm',fontsize=12)
3920    Plot.set_ylabel('Image y-axis, mm',fontsize=12)
3921    #do threshold mask - "real" mask - others are just bondaries
3922    Zlim = Masks['Thresholds'][1]
3923    FlatBkg = Data.get('Flat Bkg',0.0)
3924    wx.BeginBusyCursor()
3925    try:
3926           
3927        if newImage:                   
3928            Imin,Imax = Data['range'][1]
3929            MA = ma.masked_greater(ma.masked_less(G2frame.ImageZ,Zlim[0]+FlatBkg),Zlim[1]+FlatBkg)
3930            MaskA = ma.getmaskarray(MA)
3931            A = G2img.ImageCompress(MA,imScale)-FlatBkg
3932            AM = G2img.ImageCompress(MaskA,imScale)
3933            if G2frame.logPlot:
3934                A = np.where(A>Imin,np.where(A<Imax,A,0),0)
3935                A = np.where(A>0,np.log(A),0)
3936                AM = np.where(AM>0,np.log(AM),0)
3937                Imin,Imax = [np.amin(A),np.amax(A)]
3938            ImgM = Plot.imshow(AM,aspect='equal',cmap='Reds',
3939                interpolation='nearest',vmin=0,vmax=2,extent=[0,Xmax,Ymax,0])
3940            Img = Plot.imshow(A,aspect='equal',cmap=acolor,
3941                interpolation='nearest',vmin=Imin,vmax=Imax,extent=[0,Xmax,Ymax,0])
3942   
3943        Plot.plot(xcent,ycent,'x')
3944        #G2frame.PatternTree.GetItemText(item)
3945        if Data['showLines']:
3946            LRAzim = Data['LRazimuth']                  #NB: integers
3947            Nazm = Data['outAzimuths']
3948            delAzm = float(LRAzim[1]-LRAzim[0])/Nazm
3949            AzmthOff = Data['azmthOff']
3950            IOtth = Data['IOtth']
3951            wave = Data['wavelength']
3952            dspI = wave/(2.0*sind(IOtth[0]/2.0))
3953            ellI = G2img.GetEllipse(dspI,Data)           #=False if dsp didn't yield an ellipse (ugh! a parabola or a hyperbola)
3954            dspO = wave/(2.0*sind(IOtth[1]/2.0))
3955            ellO = G2img.GetEllipse(dspO,Data)           #Ditto & more likely for outer ellipse
3956            Azm = np.array(range(LRAzim[0],LRAzim[1]+1))-AzmthOff
3957            if ellI:
3958                xyI = []
3959                for azm in Azm:
3960                    xy = G2img.GetDetectorXY(dspI,azm,Data)
3961                    if np.any(xy):
3962                        xyI.append(xy)
3963                if len(xyI):
3964                    xyI = np.array(xyI)
3965                    arcxI,arcyI = xyI.T
3966                    Plot.plot(arcxI,arcyI,picker=3)
3967            if ellO:
3968                xyO = []
3969                for azm in Azm:
3970                    xy = G2img.GetDetectorXY(dspO,azm,Data)
3971                    if np.any(xy):
3972                        xyO.append(xy)
3973                if len(xyO):
3974                    xyO = np.array(xyO)
3975                    arcxO,arcyO = xyO.T               
3976                    Plot.plot(arcxO,arcyO,picker=3)
3977            if ellO and ellI:
3978                Plot.plot([arcxI[0],arcxO[0]],[arcyI[0],arcyO[0]],picker=3)
3979                Plot.plot([arcxI[-1],arcxO[-1]],[arcyI[-1],arcyO[-1]],picker=3)
3980            for i in range(Nazm):
3981                cake = LRAzim[0]+i*delAzm-AzmthOff
3982                if Data['centerAzm']:
3983                    cake += delAzm/2.
3984                ind = np.searchsorted(Azm,cake)
3985                Plot.plot([arcxI[ind],arcxO[ind]],[arcyI[ind],arcyO[ind]],color='k',dashes=(5,5))
3986                   
3987        if G2frame.PatternTree.GetItemText(G2frame.PickId) in 'Image Controls':
3988            for xring,yring in Data['ring']:
3989                Plot.plot(xring,yring,'r+',picker=3)
3990            if Data['setRings']:
3991                N = 0
3992                for ring in Data['rings']:
3993                    xring,yring = np.array(ring).T[:2]
3994                    Plot.plot(xring,yring,'.',color=colors[N%6])
3995                    N += 1           
3996            for ellipse in Data['ellipses']:      #what about hyperbola?
3997                cent,phi,[width,height],col = ellipse
3998                if width > 0:       #ellipses
3999                    Plot.add_artist(Ellipse([cent[0],cent[1]],2*width,2*height,phi,ec=col,fc='none'))
4000                    Plot.text(cent[0],cent[1],'+',color=col,ha='center',va='center')
4001        if G2frame.PatternTree.GetItemText(G2frame.PickId) in 'Stress/Strain':
4002            for N,ring in enumerate(StrSta['d-zero']):
4003                xring,yring = ring['ImxyObs']
4004                Plot.plot(xring,yring,colors[N%6]+'.')
4005        #masks - mask lines numbered after integration limit lines
4006        spots = Masks['Points']
4007        rings = Masks['Rings']
4008        arcs = Masks['Arcs']
4009        polygons = Masks['Polygons']
4010        if 'Frames' not in Masks:
4011            Masks['Frames'] = []
4012        frame = Masks['Frames']
4013        for spot in spots:
4014            if spot:
4015                x,y,d = spot
4016                Plot.add_artist(Circle((x,y),radius=d/2,fc='none',ec='r',picker=3))
4017        G2frame.ringList = []
4018        for iring,ring in enumerate(rings):
4019            if ring:
4020                tth,thick = ring
4021                wave = Data['wavelength']
4022                xy1 = []
4023                xy2 = []
4024                Azm = np.linspace(0,362,181)
4025                for azm in Azm:
4026                    xy1.append(G2img.GetDetectorXY(Dsp(tth+thick/2.,wave),azm,Data))      #what about hyperbola
4027                    xy2.append(G2img.GetDetectorXY(Dsp(tth-thick/2.,wave),azm,Data))      #what about hyperbola
4028                x1,y1 = np.array(xy1).T
4029                x2,y2 = np.array(xy2).T
4030                G2frame.ringList.append([Plot.plot(x1,y1,'r',picker=3),iring,'o'])           
4031                G2frame.ringList.append([Plot.plot(x2,y2,'r',picker=3),iring,'i'])
4032        G2frame.arcList = []
4033        for iarc,arc in enumerate(arcs):
4034            if arc:
4035                tth,azm,thick = arc           
4036                wave = Data['wavelength']
4037                xy1 = []
4038                xy2 = []
4039                aR = azm[0],azm[1],azm[1]-azm[0]
4040                if azm[1]-azm[0] > 180:
4041                    aR[2] /= 2
4042                Azm = np.linspace(aR[0],aR[1],aR[2])
4043                for azm in Azm:
4044                    xy1.append(G2img.GetDetectorXY(Dsp(tth+thick/2.,wave),azm,Data))      #what about hyperbola
4045                    xy2.append(G2img.GetDetectorXY(Dsp(tth-thick/2.,wave),azm,Data))      #what about hyperbola
4046                x1,y1 = np.array(xy1).T
4047                x2,y2 = np.array(xy2).T
4048                G2frame.arcList.append([Plot.plot(x1,y1,'r',picker=3),iarc,'o'])           
4049                G2frame.arcList.append([Plot.plot(x2,y2,'r',picker=3),iarc,'i'])
4050                G2frame.arcList.append([Plot.plot([x1[0],x2[0]],[y1[0],y2[0]],'r',picker=3),iarc,'l'])
4051                G2frame.arcList.append([Plot.plot([x1[-1],x2[-1]],[y1[-1],y2[-1]],'r',picker=3),iarc,'u'])
4052        G2frame.polyList = []
4053        for ipoly,polygon in enumerate(polygons):
4054            if polygon:
4055                x,y = np.hsplit(np.array(polygon),2)
4056                G2frame.polyList.append([Plot.plot(x,y,'r+',picker=10),ipoly])
4057                Plot.plot(x,y,'r')           
4058        G2frame.frameList = []
4059        if frame:
4060            x,y = np.hsplit(np.array(frame),2)
4061            G2frame.frameList.append([Plot.plot(x,y,'g+',picker=10),0])
4062            Plot.plot(x,y,'g')           
4063        if newImage:
4064            colorBar = Page.figure.colorbar(Img)
4065        Plot.set_xlim(xlim)
4066        Plot.set_ylim(ylim)
4067        if Data['invert_x']:
4068            Plot.invert_xaxis()
4069        if Data['invert_y']:
4070            Plot.invert_yaxis()
4071        if not newPlot and xylim:
4072            Page.toolbar.push_current()
4073            Plot.set_xlim(xylim[0])
4074            Plot.set_ylim(xylim[1])
4075            xylim = []
4076            Page.toolbar.push_current()
4077            Page.toolbar.draw()
4078            # patch for wx 2.9 on Mac, to force a redraw
4079            i,j= wx.__version__.split('.')[0:2]
4080            if int(i)+int(j)/10. > 2.8 and 'wxOSX' in wx.PlatformInfo:
4081                Page.canvas.draw()
4082        else:
4083            Page.canvas.draw()
4084    finally:
4085        wx.EndBusyCursor()
4086       
4087################################################################################
4088##### PlotIntegration
4089################################################################################
4090           
4091def PlotIntegration(G2frame,newPlot=False,event=None):
4092    '''Plot of 2D image after image integration with 2-theta and azimuth as coordinates
4093    '''
4094           
4095    def OnMotion(event):
4096        Page.canvas.SetToolTipString('')
4097        Page.canvas.SetCursor(wx.CROSS_CURSOR)
4098        azm = event.ydata
4099        tth = event.xdata
4100        if azm and tth:
4101            G2frame.G2plotNB.status.SetFields(\
4102                ['','Detector 2-th =%9.3fdeg, azm = %7.2fdeg'%(tth,azm)])
4103                               
4104    try:
4105        plotNum = G2frame.G2plotNB.plotList.index('2D Integration')
4106        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
4107        if not newPlot:
4108            Plot = Page.figure.gca()          #get previous plot & get limits
4109            xylim = Plot.get_xlim(),Plot.get_ylim()
4110        Page.figure.clf()
4111        Plot = Page.figure.gca()          #get a fresh plot after clf()
4112       
4113    except ValueError:
4114        Plot = G2frame.G2plotNB.addMpl('2D Integration').gca()
4115        plotNum = G2frame.G2plotNB.plotList.index('2D Integration')
4116        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
4117        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
4118        Page.views = False
4119        view = False
4120    Page.Choice = None
4121    if not event:
4122        Page.SetFocus()
4123       
4124    Data = G2frame.PatternTree.GetItemPyData(
4125        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
4126    image = G2frame.Integrate[0]
4127    xsc = G2frame.Integrate[1]
4128    ysc = G2frame.Integrate[2]
4129    Imin,Imax = Data['range'][1]
4130    acolor = mpl.cm.get_cmap(Data['color'])
4131    Plot.set_title(G2frame.PatternTree.GetItemText(G2frame.Image)[4:])
4132    Plot.set_ylabel('azimuth',fontsize=12)
4133    Plot.set_xlabel('2-theta',fontsize=12)
4134    Img = Plot.imshow(image,cmap=acolor,vmin=Imin,vmax=Imax,interpolation='nearest', \
4135        extent=[ysc[0],ysc[-1],xsc[-1],xsc[0]],aspect='auto')
4136    colorBar = Page.figure.colorbar(Img)
4137#    if Data['ellipses']:           
4138#        for ellipse in Data['ellipses']:
4139#            x,y = np.array(G2img.makeIdealRing(ellipse[:3])) #skip color
4140#            tth,azm = G2img.GetTthAzm(x,y,Data)
4141##            azm = np.where(azm < 0.,azm+360,azm)
4142#            Plot.plot(tth,azm,'b,')
4143    if not newPlot:
4144        Page.toolbar.push_current()
4145        Plot.set_xlim(xylim[0])
4146        Plot.set_ylim(xylim[1])
4147        xylim = []
4148        Page.toolbar.push_current()
4149        Page.toolbar.draw()
4150    else:
4151        Page.canvas.draw()
4152               
4153################################################################################
4154##### PlotTRImage
4155################################################################################
4156           
4157def PlotTRImage(G2frame,tax,tay,taz,newPlot=False):
4158    '''a test plot routine - not normally used
4159    ''' 
4160           
4161    def OnMotion(event):
4162        Page.canvas.SetToolTipString('')
4163        Page.canvas.SetCursor(wx.CROSS_CURSOR)
4164        azm = event.xdata
4165        tth = event.ydata
4166        if azm and tth:
4167            G2frame.G2plotNB.status.SetFields(\
4168                ['','Detector 2-th =%9.3fdeg, azm = %7.2fdeg'%(tth,azm)])
4169                               
4170    try:
4171        plotNum = G2frame.G2plotNB.plotList.index('2D Transformed Powder Image')
4172        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
4173        if not newPlot:
4174            Plot = Page.figure.gca()          #get previous plot & get limits
4175            xylim = Plot.get_xlim(),Plot.get_ylim()
4176        Page.figure.clf()
4177        Plot = Page.figure.gca()          #get a fresh plot after clf()
4178       
4179    except ValueError:
4180        Plot = G2frame.G2plotNB.addMpl('2D Transformed Powder Image').gca()
4181        plotNum = G2frame.G2plotNB.plotList.index('2D Transformed Powder Image')
4182        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
4183        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
4184        Page.views = False
4185        view = False
4186    Page.Choice = None
4187    Page.SetFocus()
4188       
4189    Data = G2frame.PatternTree.GetItemPyData(
4190        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
4191    Imin,Imax = Data['range'][1]
4192    step = (Imax-Imin)/5.
4193    V = np.arange(Imin,Imax,step)
4194    acolor = mpl.cm.get_cmap(Data['color'])
4195    Plot.set_title(G2frame.PatternTree.GetItemText(G2frame.Image)[4:])
4196    Plot.set_xlabel('azimuth',fontsize=12)
4197    Plot.set_ylabel('2-theta',fontsize=12)
4198    Plot.contour(tax,tay,taz,V,cmap=acolor)
4199    if Data['showLines']:
4200        IOtth = Data['IOtth']
4201        if Data['fullIntegrate']:
4202            LRAzim = [-180,180]
4203        else:
4204            LRAzim = Data['LRazimuth']                  #NB: integers
4205        Plot.plot([LRAzim[0],LRAzim[1]],[IOtth[0],IOtth[0]],picker=True)
4206        Plot.plot([LRAzim[0],LRAzim[1]],[IOtth[1],IOtth[1]],picker=True)
4207        if not Data['fullIntegrate']:
4208            Plot.plot([LRAzim[0],LRAzim[0]],[IOtth[0],IOtth[1]],picker=True)
4209            Plot.plot([LRAzim[1],LRAzim[1]],[IOtth[0],IOtth[1]],picker=True)
4210    if Data['setRings']:
4211        rings = np.concatenate((Data['rings']),axis=0)
4212        for xring,yring,dsp in rings:
4213            x,y = G2img.GetTthAzm(xring,yring,Data)
4214            Plot.plot(y,x,'r+')           
4215    if Data['ellipses']:           
4216        for ellipse in Data['ellipses']:
4217            ring = np.array(G2img.makeIdealRing(ellipse[:3])) #skip color
4218            x,y = np.hsplit(ring,2)
4219            tth,azm = G2img.GetTthAzm(x,y,Data)
4220            Plot.plot(azm,tth,'b,')
4221    if not newPlot:
4222        Page.toolbar.push_current()
4223        Plot.set_xlim(xylim[0])
4224        Plot.set_ylim(xylim[1])
4225        xylim = []
4226        Page.toolbar.push_current()
4227        Page.toolbar.draw()
4228    else:
4229        Page.canvas.draw()
4230       
4231################################################################################
4232##### PlotStructure
4233################################################################################
4234           
4235def PlotStructure(G2frame,data,firstCall=False):
4236    '''Crystal structure plotting package. Can show structures as balls, sticks, lines,
4237    thermal motion ellipsoids and polyhedra
4238    '''
4239
4240    def FindPeaksBonds(XYZ):
4241        rFact = data['Drawing'].get('radiusFactor',0.85)    #data['Drawing'] could be empty!
4242        Bonds = [[] for x in XYZ]
4243        for i,xyz in enumerate(XYZ):
4244            Dx = XYZ-xyz
4245            dist = np.sqrt(np.sum(np.inner(Dx,Amat)**2,axis=1))
4246            IndB = ma.nonzero(ma.masked_greater(dist,rFact*2.2))
4247            for j in IndB[0]:
4248                Bonds[i].append(Dx[j]/2.)
4249                Bonds[j].append(-Dx[j]/2.)
4250        return Bonds
4251
4252    # PlotStructure initialization here
4253    ForthirdPI = 4.0*math.pi/3.0
4254    generalData = data['General']
4255    cell = generalData['Cell'][1:7]
4256    Vol = generalData['Cell'][7:8][0]
4257    Amat,Bmat = G2lat.cell2AB(cell)         #Amat - crystal to cartesian, Bmat - inverse
4258    Gmat,gmat = G2lat.cell2Gmat(cell)
4259    A4mat = np.concatenate((np.concatenate((Amat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
4260    B4mat = np.concatenate((np.concatenate((Bmat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
4261    SGData = generalData['SGData']
4262    if generalData['Type'] in ['modulated','magnetic']:
4263        SSGData = generalData['SSGData']
4264    Mydir = generalData['Mydir']
4265    Super = generalData.get('Super',0)
4266    atomData = data['Atoms']
4267    mapPeaks = []
4268    drawingData = data['Drawing']
4269    if not drawingData:
4270        return          #nothing setup, nothing to draw   
4271    if 'Map Peaks' in data:
4272        mapPeaks = np.array(data['Map Peaks'])
4273        peakMax = 100.
4274        if len(mapPeaks):
4275            peakMax = np.max(mapPeaks.T[0])
4276    resRBData = data['RBModels'].get('Residue',[])
4277    vecRBData = data['RBModels'].get('Vector',[])
4278    rbAtmDict = {}
4279    for rbObj in resRBData+vecRBData:
4280        exclList = ['X' for i in range(len(rbObj['Ids']))]
4281        rbAtmDict.update(dict(zip(rbObj['Ids'],exclList)))
4282    testRBObj = data.get('testRBObj',{})
4283    rbObj = testRBObj.get('rbObj',{})
4284    MCSA = data.get('MCSA',{})
4285    mcsaModels = MCSA.get('Models',[])
4286    if len(mcsaModels) > 1:
4287        XYZs,Types = G2mth.UpdateMCSAxyz(Bmat,MCSA)
4288        mcsaXYZ = []
4289        mcsaTypes = []
4290        neqv = 0
4291        for xyz,atyp in zip(XYZs,Types):
4292            equiv = G2spc.GenAtom(xyz,SGData,All=True,Move=False)
4293            neqv = max(neqv,len(equiv))
4294            for item in equiv:
4295                mcsaXYZ.append(item[0]) 
4296                mcsaTypes.append(atyp)
4297        mcsaXYZ = np.array(mcsaXYZ)
4298        mcsaTypes = np.array(mcsaTypes)
4299        nuniq = mcsaXYZ.shape[0]/neqv
4300