source: trunk/GSASIIplot.py @ 1981

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

fixes to modulation plots & 3D & 4D Fourier map processing

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