source: trunk/GSASIIplot.py @ 1909

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

fix import names for HKLF files - now explicit Shelx names
fix importing problems with run-on numbers in Shelx HKLF 4 & 5 files
fix issues with importing structure factors before importing a phase

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