source: trunk/GSASIIplot.py @ 1968

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

3D axes now right way around

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