source: trunk/GSASIIplot.py @ 2022

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

fixes to TOF powder indexing & incommensurate structures

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