source: trunk/GSASIIplot.py @ 1889

Last change on this file since 1889 was 1889, checked in by toby, 7 years ago

Add fixed background points; animate dragging points and lines; add fixed bkg fit routine (bkg peaks buggy\!)

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