source: trunk/GSASIIplot.py @ 1976

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

fix bug in plotting 3D HKLs
SS str. Fctr. mods - add site fraction & Uij modulation math

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