source: trunk/GSASIIplot.py @ 1996

Last change on this file since 1996 was 1996, checked in by vondreele, 6 years ago

fix Fade problem
ModulationDeriv? work
twin sf error fixed

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