source: trunk/GSASIIplot.py @ 1969

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

3Dhkl plots fixed - zone plots & full allow 'P' & 'N' steps on origin

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