source: trunk/GSASIIplot.py @ 250

Last change on this file since 250 was 250, checked in by vondreele, 12 years ago

GSASIIgrid.py - improve Comments listing
GSASII.py - add qPlot option
GSASIIpwdGUI.py - remove a couple of prints
GSASIIplot.py - add q & log(Y) plotting

  • Property svn:keywords set to Date Author Revision URL Id
File size: 84.0 KB
Line 
1#GSASII plotting routines
2########### SVN repository information ###################
3# $Date: 2011-02-22 19:36:58 +0000 (Tue, 22 Feb 2011) $
4# $Author: vondreele $
5# $Revision: 250 $
6# $URL: trunk/GSASIIplot.py $
7# $Id: GSASIIplot.py 250 2011-02-22 19:36:58Z vondreele $
8########### SVN repository information ###################
9import math
10import time
11import copy
12import os.path
13import numpy as np
14import numpy.linalg as nl
15import wx
16import wx.aui
17import wx.glcanvas
18import matplotlib as mpl
19import mpl_toolkits.mplot3d.axes3d as mp3d
20import GSASIIpath
21import GSASIIgrid as G2gd
22import GSASIIimage as G2img
23import GSASIIIO as G2IO
24import GSASIIpwdGUI as G2pdG
25import GSASIIimgGUI as G2imG
26import GSASIIphsGUI as G2phG
27import GSASIIlattice as G2lat
28import GSASIIspc as G2spc
29from  OpenGL.GL import *
30from OpenGL.GLU import *
31from OpenGL.GLUT import *
32from OpenGL.GLE import *
33from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
34from matplotlib.backends.backend_wxagg import NavigationToolbar2Wx as Toolbar
35
36# useful degree trig functions
37sind = lambda x: math.sin(x*math.pi/180.)
38cosd = lambda x: math.cos(x*math.pi/180.)
39tand = lambda x: math.tan(x*math.pi/180.)
40asind = lambda x: 180.*math.asin(x)/math.pi
41acosd = lambda x: 180.*math.acos(x)/math.pi
42atan2d = lambda x,y: 180.*math.atan2(y,x)/math.pi
43atand = lambda x: 180.*math.atan(x)/math.pi
44# numpy versions
45npsind = lambda x: np.sin(x*np.pi/180.)
46npcosd = lambda x: np.cos(x*np.pi/180.)
47npacosd = lambda x: 180.*np.arccos(x)/np.pi
48   
49class G2PlotMpl(wx.Panel):   
50    def __init__(self,parent,id=-1,dpi=None,**kwargs):
51        wx.Panel.__init__(self,parent,id=id,**kwargs)
52        self.figure = mpl.figure.Figure(dpi=dpi,figsize=(5,7))
53        self.canvas = Canvas(self,-1,self.figure)
54        self.toolbar = Toolbar(self.canvas)
55
56        self.toolbar.Realize()
57       
58        sizer=wx.BoxSizer(wx.VERTICAL)
59        sizer.Add(self.canvas,1,wx.EXPAND)
60        sizer.Add(self.toolbar,0,wx.LEFT|wx.EXPAND)
61        self.SetSizer(sizer)
62       
63class G2PlotOgl(wx.Panel):
64    def __init__(self,parent,id=-1,dpi=None,**kwargs):
65        self.figure = wx.Panel.__init__(self,parent,id=id,**kwargs)
66        self.canvas = wx.glcanvas.GLCanvas(self,-1,**kwargs)
67        self.camera = {}
68        sizer=wx.BoxSizer(wx.VERTICAL)
69        sizer.Add(self.canvas,1,wx.EXPAND)
70        self.SetSizer(sizer)
71       
72class G2Plot3D(wx.Panel):
73    def __init__(self,parent,id=-1,dpi=None,**kwargs):
74        wx.Panel.__init__(self,parent,id=id,**kwargs)
75        self.figure = mpl.figure.Figure(dpi=dpi,figsize=(6,6))
76        self.canvas = Canvas(self,-1,self.figure)
77        self.toolbar = Toolbar(self.canvas)
78
79        self.toolbar.Realize()
80       
81        sizer=wx.BoxSizer(wx.VERTICAL)
82        sizer.Add(self.canvas,1,wx.EXPAND)
83        sizer.Add(self.toolbar,0,wx.LEFT|wx.EXPAND)
84        self.SetSizer(sizer)
85                             
86class G2PlotNoteBook(wx.Panel):
87    def __init__(self,parent,id=-1):
88        wx.Panel.__init__(self,parent,id=id)
89        #so one can't delete a plot page!!
90        self.nb = wx.aui.AuiNotebook(self, \
91            style=wx.aui.AUI_NB_DEFAULT_STYLE ^ wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB)
92        sizer = wx.BoxSizer()
93        sizer.Add(self.nb,1,wx.EXPAND)
94        self.SetSizer(sizer)
95        self.status = parent.CreateStatusBar()
96        self.status.SetFieldsCount(2)
97        self.status.SetStatusWidths([125,-1])
98        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
99       
100        self.plotList = []
101           
102    def addMpl(self,name=""):
103        page = G2PlotMpl(self.nb)
104        self.nb.AddPage(page,name)
105       
106        self.plotList.append(name)
107       
108        return page.figure
109       
110    def add3D(self,name=""):
111        page = G2Plot3D(self.nb)
112        self.nb.AddPage(page,name)
113       
114        self.plotList.append(name)
115       
116        return page.figure
117       
118    def addOgl(self,name=""):
119        page = G2PlotOgl(self.nb)
120        self.nb.AddPage(page,name)
121       
122        self.plotList.append(name)
123       
124        return page.figure
125       
126    def Delete(self,name):
127        try:
128            item = self.plotList.index(name)
129            del self.plotList[item]
130            self.nb.DeletePage(item)
131        except ValueError:          #no plot of this name - do nothing
132            return     
133               
134    def clear(self):
135        while self.nb.GetPageCount():
136            self.nb.DeletePage(0)
137        self.plotList = []
138        self.status.DestroyChildren()
139       
140    def Rename(self,oldName,newName):
141        try:
142            item = self.plotList.index(oldName)
143            self.plotList[item] = newName
144            self.nb.SetPageText(item,newName)
145        except ValueError:          #no plot of this name - do nothing
146            return     
147       
148    def OnPageChanged(self,event):       
149        if self.plotList:
150            self.status.SetStatusText('Better to select this from GSAS-II data tree',1)
151        self.status.DestroyChildren()                           #get rid of special stuff on status bar
152       
153def PlotSngl(self,newPlot=False):
154    from matplotlib.patches import Circle
155    global HKL,HKLF
156
157    def OnSCMotion(event):
158        xpos = event.xdata
159        if xpos:
160            xpos = round(xpos)                                        #avoid out of frame mouse position
161            ypos = round(event.ydata)
162            zpos = Data['Layer']
163            if '100' in Data['Zone']:
164                HKLtxt = '(%3d,%3d,%3d)'%(zpos,xpos,ypos)
165            elif '010' in Data['Zone']:
166                HKLtxt = '(%3d,%3d,%3d)'%(xpos,zpos,ypos)
167            elif '001' in Data['Zone']:
168                HKLtxt = '(%3d,%3d,%3d)'%(xpos,ypos,zpos)
169            Page.canvas.SetToolTipString(HKLtxt)
170            self.G2plotNB.status.SetFields(['HKL = '+HKLtxt,''])
171               
172    def OnSCPick(event):
173        zpos = Data['Layer']
174        pos = event.artist.center
175        if '100' in Data['Zone']:
176            Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(zpos,pos[0],pos[1]))
177            hkl = np.array([zpos,pos[0],pos[1]])
178        elif '010' in Data['Zone']:
179            Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(pos[0],zpos,pos[1]))
180            hkl = np.array([pos[0],zpos,pos[1]])
181        elif '001' in Data['Zone']:
182            Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(pos[0],pos[1],zpos))
183            hkl = np.array([pos[0],pos[1],zpos])
184        h,k,l = hkl
185        hklf = HKLF[np.where(np.all(HKL-hkl == [0,0,0],axis=1))]
186        if len(hklf):
187            Fosq,sig,Fcsq = hklf[0]
188            HKLtxt = '(%3d,%3d,%3d %.2f %.3f %.2f %.2f)'%(h,k,l,Fosq,sig,Fcsq,(Fosq-Fcsq)/(scale*sig))
189            self.G2plotNB.status.SetFields(['','HKL, Fosq, sig, Fcsq, delFsq/sig = '+HKLtxt])
190                                 
191    def OnSCKeyPress(event):
192        print event.key
193
194    try:
195        plotNum = self.G2plotNB.plotList.index('Structure Factors')
196        Page = self.G2plotNB.nb.GetPage(plotNum)
197        if not newPlot:
198            Plot = Page.figure.gca()          #get previous powder plot & get limits
199            xylim = Plot.get_xlim(),Plot.get_ylim()
200        Page.figure.clf()
201        Plot = Page.figure.gca()          #get a fresh plot after clf()
202    except ValueError,error:
203        Plot = self.G2plotNB.addMpl('Structure Factors').gca()
204        plotNum = self.G2plotNB.plotList.index('Structure Factors')
205        Page = self.G2plotNB.nb.GetPage(plotNum)
206#        Page.canvas.mpl_connect('key_press_event', OnSCKeyPress)
207        Page.canvas.mpl_connect('pick_event', OnSCPick)
208        Page.canvas.mpl_connect('motion_notify_event', OnSCMotion)
209    Page.SetFocus()
210   
211    Plot.set_aspect(aspect='equal')
212    HKLref = self.PatternTree.GetItemPyData(self.Sngl)
213    Data = self.PatternTree.GetItemPyData( \
214        G2gd.GetPatternTreeItemId(self,self.Sngl, 'HKL Plot Controls'))
215    Type = Data['Type']           
216    scale = Data['Scale']
217    HKLmax = Data['HKLmax']
218    HKLmin = Data['HKLmin']
219    FosqMax = Data['FoMax']
220    FoMax = math.sqrt(FosqMax)
221    ifFc = Data['ifFc']
222    xlabel = ['k, h=','h, k=','h, l=']
223    ylabel = ['l','l','k']
224    zones = ['100','010','001']
225    pzone = [[1,2],[0,2],[0,1]]
226    izone = zones.index(Data['Zone'])
227    Plot.set_title(self.PatternTree.GetItemText(self.Sngl)[5:])
228    HKL = []
229    HKLF = []
230    for H,Fosq,sig,Fcsq,x,x,x in HKLref:
231        HKL.append(H)
232        HKLF.append([Fosq,sig,Fcsq])
233        if H[izone] == Data['Layer']:
234            B = 0
235            if Type == 'Fosq':
236                A = scale*Fosq/FosqMax
237                B = scale*Fcsq/FosqMax
238                C = abs(A-B)
239            elif Type == 'Fo':
240                A = scale*math.sqrt(max(0,Fosq))/FoMax
241                B = scale*math.sqrt(max(0,Fcsq))/FoMax
242                C = abs(A-B)
243            elif Type == '|DFsq|/sig':
244                A = abs(Fosq-Fcsq)/(scale*sig)
245            elif Type == '|DFsq|>sig':
246                A = abs(Fosq-Fcsq)/(scale*sig)
247                if A < 1.0: A = 0                   
248            elif Type == '|DFsq|>3sig':
249                A = abs(Fosq-Fcsq)/(scale*sig)
250                if A < 3.0: A = 0                   
251            xy = (H[pzone[izone][0]],H[pzone[izone][1]])
252            if A > 0.0:
253                Plot.add_artist(Circle(xy,radius=A,ec='g',fc='w',picker=3))
254            if B:
255                Plot.add_artist(Circle(xy,radius=B,ec='b',fc='w'))
256                radius = C
257                if radius > 0:
258                    if A > B:
259                        Plot.add_artist(Circle(xy,radius=radius,ec='g',fc='g'))
260                    else:                   
261                        Plot.add_artist(Circle(xy,radius=radius,ec='r',fc='r'))
262    HKL = np.array(HKL,dtype=np.int)
263    HKLF = np.array(HKLF)
264    Plot.set_xlabel(xlabel[izone]+str(Data['Layer']),fontsize=12)
265    Plot.set_ylabel(ylabel[izone],fontsize=12)
266    Plot.set_xlim((HKLmin[pzone[izone][0]],HKLmax[pzone[izone][0]]))
267    Plot.set_ylim((HKLmin[pzone[izone][1]],HKLmax[pzone[izone][1]]))
268    if not newPlot:
269        Page.toolbar.push_current()
270        Plot.set_xlim(xylim[0])
271        Plot.set_ylim(xylim[1])
272        xylim = []
273        Page.toolbar.push_current()
274        Page.toolbar.draw()
275    else:
276        Page.canvas.draw()
277       
278def PlotPatterns(self,newPlot=False):
279    global HKL
280   
281    def OnPick(event):
282        if self.itemPicked is not None: return
283        PatternId = self.PatternId
284        try:
285            Values,Names = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.PatternId, 'Instrument Parameters'))[1::2]
286        except TypeError:
287            return
288        Parms = dict(zip(Names,Values))
289        try:
290            wave = Parms['Lam']
291        except KeyError:
292            wave = Parms['Lam1']
293        PickId = self.PickId
294        pick = event.artist
295        mouse = event.mouseevent       
296        xpos = pick.get_xdata()
297        ypos = pick.get_ydata()
298        ind = event.ind
299        xy = list(zip(np.take(xpos,ind),np.take(ypos,ind))[0])
300        if self.PatternTree.GetItemText(PickId) == 'Peak List':
301            if ind.all() != [0]:                                    #picked a data point
302                ins = [Parms[x] for x in ['U','V','W','X','Y','SH/L']]
303                if self.qPlot:                              #qplot - convert back to 2-theta
304                    xy[0] = 2.0*asind(xy[0]*wave/(4*math.pi))
305                sig = ins[0]*tand(xy[0]/2.0)**2+ins[1]*tand(xy[0]/2.0)+ins[2]
306                gam = ins[3]/cosd(xy[0]/2.0)+ins[4]*tand(xy[0]/2.0)           
307                data = self.PatternTree.GetItemPyData(self.PickId)
308                XY = [xy[0],0, xy[1],1, sig,0, gam,0]       #default refine intensity 1st
309                data.append(XY)
310                G2pdG.UpdatePeakGrid(self,data)
311                PlotPatterns(self)
312            else:                                                   #picked a peak list line
313                self.itemPicked = pick
314        elif self.PatternTree.GetItemText(PickId) == 'Limits':
315            if ind.all() != [0]:                                    #picked a data point
316                LimitId = G2gd.GetPatternTreeItemId(self,PatternId, 'Limits')
317                data = self.PatternTree.GetItemPyData(LimitId)
318                if self.qPlot:                              #qplot - convert back to 2-theta
319                    xy[0] = 2.0*asind(xy[0]*wave/(4*math.pi))
320                if mouse.button==1:
321                    data[1][0] = min(xy[0],data[1][1])
322                if mouse.button==3:
323                    data[1][1] = max(xy[0],data[1][0])
324                self.PatternTree.SetItemPyData(LimitId,data)
325                G2pdG.UpdateLimitsGrid(self,data)
326                PlotPatterns(self)
327            else:                                                   #picked a limit line
328                self.itemPicked = pick
329       
330    def OnPlotKeyPress(event):
331        newPlot = False
332        if event.key == 'w':
333            if self.Weight:
334                self.Weight = False
335            else:
336                self.Weight = True
337            print 'plot weighting:',self.Weight
338        elif event.key == 'l':
339            if self.Contour:
340                pass
341            else:
342                if self.logPlot:
343                    self.logPlot = False
344                else:
345                    self.Offset = 0
346                    self.logPlot = True
347        elif event.key == 'u':
348            if self.Contour:
349                self.Cmax = min(1.0,self.Cmax*1.2)
350            elif self.logPlot:
351                pass
352            elif self.Offset < 100.:
353                self.Offset += 1.
354        elif event.key == 'd':
355            if self.Contour:
356                self.Cmax = max(0.0,self.Cmax*0.8)
357            elif self.logPlot:
358                pass
359            elif self.Offset > 0.:
360                self.Offset -= 1.
361        elif event.key == 'c':
362            newPlot = True
363            if self.Contour:
364                self.Contour = False
365            else:
366                self.Contour = True
367                self.SinglePlot = False
368                self.Offset = 0
369        elif event.key == 'q':
370            newPlot = True
371            if self.qPlot:
372                self.qPlot = False
373            else:
374                self.qPlot = True
375        elif event.key == 's':
376            if self.Contour:
377                choice = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
378                dlg = wx.SingleChoiceDialog(self,'Select','Color scheme',choice)
379                if dlg.ShowModal() == wx.ID_OK:
380                    sel = dlg.GetSelection()
381                    self.ContourColor = choice[sel]
382                else:
383                    self.ContourColor = 'Paired'
384                dlg.Destroy()
385            else:               
386                if self.SinglePlot:
387                    self.SinglePlot = False
388                else:
389                    self.SinglePlot = True
390        elif event.key == '+':
391            if self.PickId:
392                self.PickId = False
393        elif event.key == 'i':                  #for smoothing contour plot
394            choice = ['nearest','bilinear','bicubic','spline16','spline36','hanning',
395               'hamming','hermite','kaiser','quadric','catrom','gaussian','bessel',
396               'mitchell','sinc','lanczos']
397            dlg = wx.SingleChoiceDialog(self,'Select','Interpolation',choice)
398            if dlg.ShowModal() == wx.ID_OK:
399                sel = dlg.GetSelection()
400                self.Interpolate = choice[sel]
401            else:
402                self.Interpolate = 'nearest'
403            dlg.Destroy()
404           
405        PlotPatterns(self,newPlot=newPlot)
406       
407    def OnKeyBox(event):
408        if self.G2plotNB.nb.GetSelection() == self.G2plotNB.plotList.index('Powder Patterns'):
409            event.key = cb.GetValue()[0]
410            cb.SetValue(' key press')
411            OnPlotKeyPress(event)
412                       
413    def OnMotion(event):
414        xpos = event.xdata
415        if xpos:                                        #avoid out of frame mouse position
416            ypos = event.ydata
417            Page.canvas.SetCursor(wx.CROSS_CURSOR)
418            try:
419                Values,Names = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.PatternId, 'Instrument Parameters'))[1::2]
420                Parms = dict(zip(Names,Values))
421                try:
422                    wave = Parms['Lam']
423                except KeyError:
424                    wave = Parms['Lam1']
425                if self.qPlot:
426                    xpos = 2.0*asind(xpos*wave/(4*math.pi))
427                dsp = 0.0
428                if abs(xpos) > 0.:                  #avoid possible singularity at beam center
429                    dsp = wave/(2.*sind(abs(xpos)/2.0))
430                if self.Contour:
431                    self.G2plotNB.status.SetStatusText('2-theta =%9.3f d =%9.5f pattern ID =%5d'%(xpos,dsp,int(ypos)),1)
432                else:
433                    self.G2plotNB.status.SetStatusText('2-theta =%9.3f d =%9.5f Intensity =%9.1f'%(xpos,dsp,ypos),1)
434                if self.itemPicked:
435                    Page.canvas.SetToolTipString('%9.3f'%(xpos))
436                if self.PickId and self.PatternTree.GetItemText(self.PickId) in ['Index Peak List','Unit Cells List']:
437                    found = []
438                    if len(HKL):
439                        view = Page.toolbar._views.forward()[0][:2]
440                        wid = view[1]-view[0]
441                        found = HKL[np.where(np.fabs(HKL.T[5]-xpos) < 0.002*wid)]
442                    if len(found):
443                        h,k,l = found[0][:3] 
444                        Page.canvas.SetToolTipString('%d,%d,%d'%(int(h),int(k),int(l)))
445                    else:
446                        Page.canvas.SetToolTipString('')
447
448            except TypeError:
449                self.G2plotNB.status.SetStatusText('Select PWDR powder pattern first',1)
450                                                   
451    def OnRelease(event):
452        if self.itemPicked is None: return
453        Values,Names = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.PatternId, 'Instrument Parameters'))[1::2]
454        Parms = dict(zip(Names,Values))
455        try:
456            wave = Parms['Lam']
457        except KeyError:
458            wave = Parms['Lam1']
459        xpos = event.xdata
460        if xpos:                                        #avoid out of frame mouse position
461            lines = []
462            for line in self.Lines: lines.append(line.get_xdata()[0])
463            lineNo = lines.index(self.itemPicked.get_xdata()[0])
464            if  lineNo in [0,1]:
465                LimitId = G2gd.GetPatternTreeItemId(self,self.PatternId, 'Limits')
466                data = self.PatternTree.GetItemPyData(LimitId)
467#                print 'limits',xpos
468                if self.qPlot:
469                    data[1][lineNo] = 2.0*asind(wave*xpos/(4*math.pi))
470                else:
471                    data[1][lineNo] = xpos
472                self.PatternTree.SetItemPyData(LimitId,data)
473                if self.PatternTree.GetItemText(self.PickId) == 'Limits':
474                    G2pdG.UpdateLimitsGrid(self,data)
475            else:
476                PeakId = G2gd.GetPatternTreeItemId(self,self.PatternId, 'Peak List')
477                data = self.PatternTree.GetItemPyData(PeakId)
478#                print 'peaks',xpos
479                if self.qPlot:
480                    data[lineNo-2][0] = 2.0*asind(wave*xpos/(4*math.pi))
481                else:
482                    data[lineNo-2][0] = xpos
483                self.PatternTree.SetItemPyData(PeakId,data)
484                G2pdG.UpdatePeakGrid(self,data)
485        PlotPatterns(self)
486        self.itemPicked = None   
487
488    xylim = []
489    try:
490        plotNum = self.G2plotNB.plotList.index('Powder Patterns')
491        Page = self.G2plotNB.nb.GetPage(plotNum)
492        if not newPlot:
493            Plot = Page.figure.gca()          #get previous powder plot & get limits
494            xylim = Plot.get_xlim(),Plot.get_ylim()
495        Page.figure.clf()
496        Plot = Page.figure.gca()          #get a fresh plot after clf()
497    except ValueError,error:
498        newPlot = True
499        self.Cmax = 1.0
500        Plot = self.G2plotNB.addMpl('Powder Patterns').gca()
501        plotNum = self.G2plotNB.plotList.index('Powder Patterns')
502        Page = self.G2plotNB.nb.GetPage(plotNum)
503        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
504        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
505        Page.canvas.mpl_connect('pick_event', OnPick)
506        Page.canvas.mpl_connect('button_release_event', OnRelease)
507    Page.SetFocus()
508    self.G2plotNB.status.DestroyChildren()
509    if self.Contour:
510        Choice = (' key press','d: lower contour max','u: raise contour max',
511            'i: interpolation method','s: color scheme','c: contour off')
512    else:
513        if self.logPlot:
514            Choice = (' key press','l: log(I) off',
515                'c: contour on','q: toggle q plot','s: toggle single plot','+: no selection')
516        else:
517            Choice = (' key press','d: offset down','u: offset up','l: log(I) on',
518                'c: contour on','q: toggle q plot','s: toggle single plot','+: no selection')
519    cb = wx.ComboBox(self.G2plotNB.status,style=wx.CB_DROPDOWN|wx.CB_READONLY,
520        choices=Choice)
521    cb.Bind(wx.EVT_COMBOBOX, OnKeyBox)
522    cb.SetValue(' key press')
523   
524    PickId = self.PickId
525    PatternId = self.PatternId
526    colors=['b','g','r','c','m','k']
527    Lines = []
528    if self.SinglePlot:
529        Pattern = self.PatternTree.GetItemPyData(PatternId)
530        Pattern.append(self.PatternTree.GetItemText(PatternId))
531        PlotList = [Pattern,]
532        ParmList = [self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,
533            self.PatternId, 'Instrument Parameters'))[1],]
534    else:       
535        PlotList = []
536        ParmList = []
537        item, cookie = self.PatternTree.GetFirstChild(self.root)
538        while item:
539            if 'PWDR' in self.PatternTree.GetItemText(item):
540                Pattern = self.PatternTree.GetItemPyData(item)
541                if len(Pattern) < 3:                    # put name on end if needed
542                    Pattern.append(self.PatternTree.GetItemText(item))
543                PlotList.append(Pattern)
544                ParmList.append(self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,
545                    item,'Instrument Parameters'))[1])
546            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
547    Ymax = 1.0
548    lenX = 0
549    HKL = np.array(self.HKL)
550    for Pattern in PlotList:
551        xye = Pattern[1]
552        Ymax = max(Ymax,max(xye[1]))
553    offset = self.Offset*Ymax/100.0
554    Title = 'Powder Patterns: '+os.path.split(self.GSASprojectfile)[1]
555    if self.logPlot:
556        Title = 'log('+Title+')'
557    Plot.set_title(Title)
558    if self.qPlot:
559        Plot.set_xlabel(r'$q, \AA^{-1}$',fontsize=14)
560    else:       
561        Plot.set_xlabel(r'$\mathsf{2\theta}$',fontsize=14)
562    Plot.set_ylabel('Intensity',fontsize=12)
563    if self.Contour:
564        ContourZ = []
565        ContourY = []
566        Nseq = 0
567    for N,Pattern in enumerate(PlotList):
568        Parms = ParmList[N]
569        ifpicked = False
570        LimitId = 0
571        xye = np.array(Pattern[1])
572        if PickId:
573            ifpicked = Pattern[2] == self.PatternTree.GetItemText(PatternId)
574            LimitId = G2gd.GetPatternTreeItemId(self,PatternId, 'Limits')
575        if self.qPlot:
576            Id = G2gd.GetPatternTreeItemId(self,self.root, Pattern[2])
577            X = 4*np.pi*npsind(xye[0]/2.0)/Parms[1]
578        else:
579            X = xye[0]
580        if not lenX:
581            lenX = len(X)           
582        Y = xye[1]+offset*N
583        if LimitId:
584            limits = np.array(self.PatternTree.GetItemPyData(LimitId))
585            if self.qPlot:
586                limits = 4*np.pi*npsind(limits/2.0)/Parms[1]
587            Lines.append(Plot.axvline(limits[1][0],color='g',dashes=(5,5),picker=3.))   
588            Lines.append(Plot.axvline(limits[1][1],color='r',dashes=(5,5),picker=3.))                   
589        if self.Contour:
590           
591            if lenX == len(X):
592                ContourY.append(N)
593                ContourZ.append(Y)
594                ContourX = X
595                Nseq += 1
596                Plot.set_ylabel('Data sequence',fontsize=12)
597        else:
598            if ifpicked:
599                Z = xye[3]+offset*N
600                W = xye[4]+offset*N
601                D = xye[5]+offset*N-Ymax*.02
602                if self.Weight:
603                    W2 = np.sqrt(xye[2])
604                    D *= W2-Ymax*.02
605                if self.logPlot:
606                    Plot.semilogy(X,Y,colors[N%6]+'+',picker=3.,clip_on=False,nonposy='mask')
607                    Plot.semilogy(X,Z,colors[(N+1)%6],picker=False,nonposy='mask')
608                    Plot.semilogy(X,W,colors[(N+2)%6],picker=False,nonposy='mask')
609                else:
610                    Plot.plot(X,Y,colors[N%6]+'+',picker=3.,clip_on=False)
611                    Plot.plot(X,Z,colors[(N+1)%6],picker=False)
612                    Plot.plot(X,W,colors[(N+2)%6],picker=False)
613                    Plot.plot(X,D,colors[(N+3)%6],picker=False)
614                    Plot.axhline(0.,color=wx.BLACK)
615                Page.canvas.SetToolTipString('')
616                if self.PatternTree.GetItemText(PickId) == 'Peak List':
617                    tip = 'On data point: Pick peak - L or R MB.On line: MB down to move'
618                    Page.canvas.SetToolTipString(tip)
619                    data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PatternId, 'Peak List'))
620                    for item in data:
621                        if self.qPlot:
622                            Lines.append(Plot.axvline(4*math.pi*sind(item[0]/2.)/Parms[1],color=colors[N%6],picker=2.))
623                        else:
624                            Lines.append(Plot.axvline(item[0],color=colors[N%6],picker=2.))
625                if self.PatternTree.GetItemText(PickId) == 'Limits':
626                    tip = 'On data point: Lower limit - L MB; Upper limit - R MB. On limit: MB down to move'
627                    Page.canvas.SetToolTipString(tip)
628                    data = self.LimitsTable.GetData()
629            else:
630                if self.logPlot:
631                    Plot.semilogy(X,Y,colors[N%6],picker=False,nonposy='clip')
632                else:
633                    Plot.plot(X,Y,colors[N%6],picker=False)
634    if PickId and self.PatternTree.GetItemText(PickId) in ['Index Peak List','Unit Cells List']:
635        Values,Names = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PatternId, 'Instrument Parameters'))[1::2]
636        Parms = dict(zip(Names,Values))
637        try:
638            wave = Parms['Lam']
639        except KeyError:
640            wave = Parms['Lam1']
641        peaks = np.array((self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PatternId, 'Index Peak List'))))
642        for peak in peaks:
643            if self.qPlot:
644                Plot.axvline(4*np.pi*sind(peak[0]/2.0)/wave,color='b')
645            else:
646                Plot.axvline(peak[0],color='b')
647        for hkl in self.HKL:
648            if self.qPlot:
649                Plot.axvline(4*np.pi*sind(hkl[5]/2.0)/wave,color='r',dashes=(5,5))
650            else:
651                Plot.axvline(hkl[5],color='r',dashes=(5,5))
652    if self.Contour:
653        acolor = mpl.cm.get_cmap(self.ContourColor)
654        Img = Plot.imshow(ContourZ,cmap=acolor,vmin=0,vmax=Ymax*self.Cmax,interpolation=self.Interpolate, 
655            extent=[ContourX[0],ContourX[-1],ContourY[0],ContourY[-1]],aspect='auto',origin='lower')
656        Page.figure.colorbar(Img)
657    else:
658        self.Lines = Lines
659    if not newPlot:
660        Page.toolbar.push_current()
661        Plot.set_xlim(xylim[0])
662        Plot.set_ylim(xylim[1])
663        xylim = []
664        Page.toolbar.push_current()
665        Page.toolbar.draw()
666    else:
667        Page.canvas.draw()
668    self.Pwdr = True
669
670def PlotPowderLines(self):
671    global HKL
672
673    def OnMotion(event):
674        xpos = event.xdata
675        if xpos:                                        #avoid out of frame mouse position
676            Page.canvas.SetCursor(wx.CROSS_CURSOR)
677            self.G2plotNB.status.SetFields(['','2-theta =%9.3f '%(xpos,)])
678            if self.PickId and self.PatternTree.GetItemText(self.PickId) in ['Index Peak List','Unit Cells List']:
679                found = []
680                if len(HKL):
681                    view = Page.toolbar._views.forward()[0][:2]
682                    wid = view[1]-view[0]
683                    found = HKL[np.where(np.fabs(HKL.T[5]-xpos) < 0.002*wid)]
684                if len(found):
685                    h,k,l = found[0][:3] 
686                    Page.canvas.SetToolTipString('%d,%d,%d'%(int(h),int(k),int(l)))
687                else:
688                    Page.canvas.SetToolTipString('')
689
690    try:
691        plotNum = self.G2plotNB.plotList.index('Powder Lines')
692        Page = self.G2plotNB.nb.GetPage(plotNum)
693        Page.figure.clf()
694        Plot = Page.figure.gca()
695    except ValueError,error:
696        Plot = self.G2plotNB.addMpl('Powder Lines').gca()
697        plotNum = self.G2plotNB.plotList.index('Powder Lines')
698        Page = self.G2plotNB.nb.GetPage(plotNum)
699        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
700       
701    Page.SetFocus()
702    Plot.set_title('Powder Pattern Lines')
703    Plot.set_xlabel(r'$\mathsf{2\theta}$',fontsize=14)
704    PickId = self.PickId
705    PatternId = self.PatternId
706    peaks = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PatternId, 'Index Peak List'))
707    for peak in peaks:
708        Plot.axvline(peak[0],color='b')
709    HKL = np.array(self.HKL)
710    for hkl in self.HKL:
711        Plot.axvline(hkl[5],color='r',dashes=(5,5))
712    xmin = peaks[0][0]
713    xmax = peaks[-1][0]
714    delt = xmax-xmin
715    xlim = [max(0,xmin-delt/20.),min(180.,xmax+delt/20.)]
716    Plot.set_xlim(xlim)
717    Page.canvas.draw()
718
719def PlotPeakWidths(self):
720    PatternId = self.PatternId
721    limitID = G2gd.GetPatternTreeItemId(self,PatternId, 'Limits')
722    if limitID:
723        limits = self.PatternTree.GetItemPyData(limitID)
724    else:
725        return
726    instParms = self.PatternTree.GetItemPyData( \
727        G2gd.GetPatternTreeItemId(self,PatternId, 'Instrument Parameters'))
728    if instParms[0][0] == 'PXC':
729        lam = instParms[1][1]
730        if len(instParms[1]) == 13:
731            GU,GV,GW,LX,LY = instParms[0][6:11]
732        else:
733            GU,GV,GW,LX,LY = instParms[0][4:9]
734    peakID = G2gd.GetPatternTreeItemId(self,PatternId, 'Peak List')
735    if peakID:
736        peaks = self.PatternTree.GetItemPyData(peakID)
737    else:
738        peaks = []
739   
740    try:
741        plotNum = self.G2plotNB.plotList.index('Peak Widths')
742        Page = self.G2plotNB.nb.GetPage(plotNum)
743        Page.figure.clf()
744        Plot = Page.figure.gca()
745    except ValueError,error:
746        Plot = self.G2plotNB.addMpl('Peak Widths').gca()
747        plotNum = self.G2plotNB.plotList.index('Peak Widths')
748        Page = self.G2plotNB.nb.GetPage(plotNum)
749    Page.SetFocus()
750   
751    Page.canvas.SetToolTipString('')
752    colors=['b','g','r','c','m','k']
753    Xmin,Xmax = limits[1]
754    Xmin = min(0.5,max(Xmin,1))
755    Xmin /= 2
756    Xmax /= 2
757    nPts = 100
758    delt = (Xmax-Xmin)/nPts
759    thetas = []
760    for i in range(nPts):
761        thetas.append(Xmin+i*delt)
762    X = []
763    Y = []
764    Z = []
765    W = []
766    V = []
767    sig = lambda Th,U,V,W: 1.17741*math.sqrt(U*tand(Th)**2+V*tand(Th)+W)*math.pi/18000.
768    gam = lambda Th,X,Y: (X/cosd(Th)+Y*tand(Th))*math.pi/18000.
769    gamFW = lambda s,g: math.exp(math.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.)
770    gamFW2 = lambda s,g: math.sqrt(s**2+(0.4654996*g)**2)+.5345004*#Ubaldo Bafile - private communication
771    for theta in thetas:
772        X.append(4.0*math.pi*sind(theta)/lam)              #q
773        s = sig(theta,GU,GV,GW)
774        g = gam(theta,LX,LY)
775        G = gamFW(g,s)
776        H = gamFW2(g,s)
777        Y.append(s/tand(theta))
778        Z.append(g/tand(theta))
779        W.append(G/tand(theta))
780        V.append(H/tand(theta))
781    Plot.set_title('Instrument and sample peak widths')
782    Plot.set_ylabel(r'$\Delta q/q, \Delta d/d$',fontsize=14)
783    Plot.set_xlabel(r'$q, \AA^{-1}$',fontsize=14)
784    Plot.plot(X,Y,color='r',label='Gaussian')
785    Plot.plot(X,Z,color='g',label='Lorentzian')
786    Plot.plot(X,W,color='b',label='G+L')
787    Plot.plot(X,V,color='k',label='G+L2')
788    X = []
789    Y = []
790    Z = []
791    W = []
792    V = []
793    for peak in peaks:
794        X.append(4.0*math.pi*sind(peak[0]/2.0)/lam)
795        s = 1.17741*math.sqrt(peak[4])*math.pi/18000.
796        g = peak[6]*math.pi/18000.
797        G = gamFW(g,s)
798        H = gamFW2(g,s)
799        Y.append(s/tand(peak[0]/2.))
800        Z.append(g/tand(peak[0]/2.))
801        W.append(G/tand(peak[0]/2.))
802        V.append(H/tand(peak[0]/2.))
803    Plot.plot(X,Y,'+',color='r',label='G peak')
804    Plot.plot(X,Z,'+',color='g',label='L peak')
805    Plot.plot(X,W,'+',color='b',label='G+L peak')
806    Plot.plot(X,V,'+',color='k',label='G+L2 peak')
807    Plot.legend(loc='best')
808    Page.canvas.draw()
809   
810def PlotStrain(self,data):
811    # in this instance data is for a phase
812    PatternId = self.PatternId
813    generalData = data['General']
814    SGData = generalData['SGData']
815    MuStrKeys = G2spc.MustrainNames(SGData)
816    cell = generalData['Cell'][1:]
817    A,B = G2lat.cell2AB(cell[:6])
818    Vol = cell[6]
819    useList = data['Histograms']
820    numPlots = len(useList)
821   
822    try:
823        plotNum = self.G2plotNB.plotList.index('Microstrain')
824        Page = self.G2plotNB.nb.GetPage(plotNum)
825        Page.figure.clf()
826        Plot = mp3d.Axes3D(Page.figure)
827    except ValueError,error:
828        Plot = mp3d.Axes3D(self.G2plotNB.add3D('Microstrain'))
829        plotNum = self.G2plotNB.plotList.index('Microstrain')
830        Page = self.G2plotNB.nb.GetPage(plotNum)
831    Page.SetFocus()
832    self.G2plotNB.status.SetStatusText('Adjust frame size to get desired aspect ratio',1)
833   
834    for item in useList:
835        if useList[item]['Show']:
836            muStrain = useList[item]['Mustrain']
837            PHI = np.linspace(0.,360.,30,True)
838            PSI = np.linspace(0.,180.,30,True)
839            X = np.outer(npsind(PHI),npsind(PSI))
840            Y = np.outer(npcosd(PHI),npsind(PSI))
841            Z = np.outer(np.ones(np.size(PHI)),npcosd(PSI))
842            if muStrain[0] == 'isotropic':
843                muiso = muStrain[1][0]*math.pi/0.018
844                X *= muiso
845                Y *= muiso
846                Z *= muiso               
847               
848            elif muStrain[0] == 'uniaxial':
849                def uniaxMustrain(xyz,muiso,muaniso,axes):
850                    cp = abs(np.dot(xyz,axes))
851                    S = muiso+muaniso*cp
852                    return S*xyz*math.pi/0.018
853                muiso,muaniso = muStrain[1][:2]
854                axes = np.inner(A,np.array(muStrain[3]))
855                axes /= nl.norm(axes)
856                Shkl = np.array(muStrain[1])
857                Shape = X.shape[0]
858                XYZ = np.dstack((X,Y,Z))
859                XYZ = np.nan_to_num(np.apply_along_axis(uniaxMustrain,2,XYZ,muiso,muaniso,axes))
860                X,Y,Z = np.dsplit(XYZ,3)
861                X = X[:,:,0]
862                Y = Y[:,:,0]
863                Z = Z[:,:,0]
864               
865            elif muStrain[0] == 'generalized':
866                def genMustrain(xyz,SGData,A,Shkl):
867                    uvw = np.inner(A.T,xyz)
868                    Strm = np.array(G2spc.MustrainCoeff(uvw,SGData))
869                    sum = np.sqrt(np.sum(np.multiply(Shkl,Strm)))*math.pi/0.018
870                    return sum*xyz
871                Shkl = np.array(muStrain[4])
872                if np.any(Shkl):
873                    Shape = X.shape[0]
874                    XYZ = np.dstack((X,Y,Z))
875                    XYZ = np.nan_to_num(np.apply_along_axis(genMustrain,2,XYZ,SGData,A,Shkl))
876                    X,Y,Z = np.dsplit(XYZ,3)
877                    X = X[:,:,0]
878                    Y = Y[:,:,0]
879                    Z = Z[:,:,0]
880                   
881            if np.any(X) and np.any(Y) and np.any(Z):
882                Plot.plot_surface(X,Y,Z,rstride=1,cstride=1,color='g')
883               
884            Plot.set_xlabel('X')
885            Plot.set_ylabel('Y')
886            Plot.set_zlabel('Z')
887    Page.canvas.draw()
888           
889def PlotExposedImage(self,newPlot=False,event=None):
890    plotNo = self.G2plotNB.nb.GetSelection()
891    if self.G2plotNB.nb.GetPageText(plotNo) == '2D Powder Image':
892        PlotImage(self,newPlot,event)
893    elif self.G2plotNB.nb.GetPageText(plotNo) == '2D Integration':
894        PlotIntegration(self,newPlot,event)
895
896def PlotImage(self,newPlot=False,event=None):
897    from matplotlib.patches import Ellipse,Arc,Circle,Polygon
898    import numpy.ma as ma
899    Dsp = lambda tth,wave: wave/(2.*sind(tth/2.))
900    global Data,Masks
901    Data = self.PatternTree.GetItemPyData(
902        G2gd.GetPatternTreeItemId(self,self.Image, 'Image Controls'))
903    Masks = self.PatternTree.GetItemPyData(
904        G2gd.GetPatternTreeItemId(self,self.Image, 'Masks'))
905
906    def OnImMotion(event):
907        Page.canvas.SetToolTipString('')
908        sizexy = Data['size']
909        if event.xdata and event.ydata:                 #avoid out of frame errors
910            Page.canvas.SetCursor(wx.CROSS_CURSOR)
911            item = self.itemPicked
912            pixelSize = Data['pixelSize']
913            scalex = 1000./pixelSize[0]
914            scaley = 1000./pixelSize[1]
915            if item and self.PatternTree.GetItemText(self.PickId) == 'Image Controls':
916                if 'Text' in str(item):
917                    Page.canvas.SetToolTipString('%8.3f %8.3fmm'%(event.xdata,event.ydata))
918                else:
919                    xcent,ycent = Data['center']
920                    xpos = event.xdata-xcent
921                    ypos = event.ydata-ycent
922                    tth,azm = G2img.GetTthAzm(event.xdata,event.ydata,Data)
923                    if 'line3' in  str(item) or 'line4' in str(item) and not Data['fullIntegrate']:
924                        Page.canvas.SetToolTipString('%6d deg'%(azm))
925                    elif 'line1' in  str(item) or 'line2' in str(item):
926                        Page.canvas.SetToolTipString('%8.3fdeg'%(tth))                           
927            else:
928                xpos = event.xdata
929                ypos = event.ydata
930                xpix = xpos*scalex
931                ypix = ypos*scaley
932                Int = 0
933                if (0 <= xpix <= sizexy[0]) and (0 <= ypix <= sizexy[1]):
934                    Int = self.ImageZ[ypix][xpix]
935                tth,azm,dsp = G2img.GetTthAzmDsp(xpos,ypos,Data)
936                Q = 2.*math.pi/dsp
937                if self.setPoly:
938                    self.G2plotNB.status.SetFields(['','Polygon mask pick - LB next point, RB close polygon'])
939                else:
940                    self.G2plotNB.status.SetFields(\
941                        ['','Detector 2-th =%9.3fdeg, dsp =%9.3fA, Q = %6.5fA-1, azm = %7.2fdeg, I = %6d'%(tth,dsp,Q,azm,Int)])
942
943    def OnImPlotKeyPress(event):
944        try:
945            PickName = self.PatternTree.GetItemText(self.PickId)
946        except TypeError:
947            return
948        if PickName == 'Masks':
949            Xpos = event.xdata
950            if not Xpos:            #got point out of frame
951                return
952            Ypos = event.ydata
953            if event.key == 's':
954                print 'spot mask @ ',Xpos,Ypos
955                Masks['Points'].append([Xpos,Ypos,1.])
956            elif event.key == 'r':
957                tth = G2img.GetTth(Xpos,Ypos,Data)
958                print 'ring mask @ ',Xpos,Ypos,tth
959                Masks['Rings'].append([tth,0.1])
960            elif event.key == 'a':
961                tth,azm = G2img.GetTthAzm(Xpos,Ypos,Data)
962                azm = int(azm)               
963                print 'arc mask @ ', Xpos,Ypos
964                Masks['Arcs'].append([tth,[azm-5,azm+5],0.1])
965            elif event.key == 'p':
966                self.setPoly = True
967                Masks['Polygons'].append([])
968                self.G2plotNB.status.SetFields(['','Polygon mask active - LB pick next point, RB close polygon'])
969            G2imG.UpdateMasks(self,Masks)
970        elif PickName == 'Image Controls':
971            if event.key == 'c':
972                Xpos = event.xdata
973                if not Xpos:            #got point out of frame
974                    return
975                Ypos = event.ydata
976                dlg = wx.MessageDialog(self,'Are you sure you want to change the center?',
977                    'Center change',style=wx.OK|wx.CANCEL)
978                try:
979                    if dlg.ShowModal() == wx.ID_OK:
980                        print 'move center to: ',Xpos,Ypos
981                        Data['center'] = [Xpos,Ypos]
982                        G2imG.UpdateImageControls(self,Data,Masks)
983                finally:
984                    dlg.Destroy()
985            elif event.key == 'l':
986                if self.logPlot:
987                    self.logPlot = False
988                else:
989                    self.logPlot = True
990        PlotImage(self)
991           
992    def OnKeyBox(event):
993        if self.G2plotNB.nb.GetSelection() == self.G2plotNB.plotList.index('2D Powder Image'):
994            event.key = cb.GetValue()[0]
995            cb.SetValue(' key press')
996            if event.key in 'l':
997                OnImPlotKeyPress(event)
998                       
999    def OnImPick(event):
1000        if self.PatternTree.GetItemText(self.PickId) not in ['Image Controls','Masks']:
1001            return
1002        if self.setPoly:
1003            polygon = Masks['Polygons'][-1]
1004            xpos,ypos = event.mouseevent.xdata,event.mouseevent.ydata
1005            if xpos and ypos:                       #point inside image
1006                if len(polygon) > 2 and event.mouseevent.button == 3:
1007                    x0,y0 = polygon[0]
1008                    polygon.append([x0,y0])
1009                    self.setPoly = False
1010                    self.G2plotNB.status.SetFields(['','Polygon closed - RB drag a vertex to change shape'])
1011                else:
1012                    self.G2plotNB.status.SetFields(['','New polygon point: %.1f,%.1f'%(xpos,ypos)])
1013                    polygon.append([xpos,ypos])
1014                G2imG.UpdateMasks(self,Masks)
1015        else:
1016            if self.itemPicked is not None: return
1017            self.itemPicked = event.artist
1018            self.mousePicked = event.mouseevent
1019       
1020    def OnImRelease(event):
1021        try:
1022            PickName = self.PatternTree.GetItemText(self.PickId)
1023        except TypeError:
1024            return
1025        if PickName not in ['Image Controls','Masks']:
1026            return
1027        pixelSize = Data['pixelSize']
1028        scalex = 1000./pixelSize[0]
1029        scaley = 1000./pixelSize[1]
1030        pixLimit = Data['pixLimit']
1031        if self.itemPicked is None and PickName == 'Image Controls':
1032#            sizexy = Data['size']
1033            Xpos = event.xdata
1034            if not (Xpos and self.ifGetRing):                   #got point out of frame
1035                return
1036            Ypos = event.ydata
1037            if Ypos and not Page.toolbar._active:         #make sure zoom/pan not selected
1038                if event.button == 1:
1039                    Xpix = Xpos*scalex
1040                    Ypix = Ypos*scaley
1041                    xpos,ypos,I,J = G2img.ImageLocalMax(self.ImageZ,pixLimit,Xpix,Ypix)
1042                    if I and J:
1043                        xpos += .5                              #shift to pixel center
1044                        ypos += .5
1045                        xpos /= scalex                          #convert to mm
1046                        ypos /= scaley
1047                        Data['ring'].append([xpos,ypos])
1048                PlotImage(self)
1049            return
1050        else:
1051            xpos = event.xdata
1052            if xpos:                                        #avoid out of frame mouse position
1053                ypos = event.ydata
1054                if self.ifGetRing:
1055                    xypos = [xpos,ypos]
1056                    rings = Data['ring']
1057                    for ring in rings:
1058                        if np.allclose(ring,xypos,.01,0):
1059                            rings.remove(ring)                                                                       
1060                else:
1061                    tth,azm,dsp = G2img.GetTthAzmDsp(xpos,ypos,Data)
1062                    itemPicked = str(self.itemPicked)
1063                    if 'Line2D' in itemPicked and PickName == 'Image Controls':
1064                        if 'line1' in itemPicked:
1065                            Data['IOtth'][0] = max(tth,0.001)
1066                        elif 'line2' in itemPicked:
1067                            Data['IOtth'][1] = tth
1068                        elif 'line3' in itemPicked and not Data['fullIntegrate']:
1069                            Data['LRazimuth'][0] = int(azm)
1070                        elif 'line4' in itemPicked and not Data['fullIntegrate']:
1071                            Data['LRazimuth'][1] = int(azm)
1072                           
1073                        if Data['LRazimuth'][1] < Data['LRazimuth'][0]:
1074                            Data['LRazimuth'][1] += 360
1075                        if  Data['IOtth'][0] > Data['IOtth'][1]:
1076                            Data['IOtth'][0],Data['IOtth'][1] = Data['IOtth'][1],Data['IOtth'][0]
1077                           
1078                        self.InnerTth.SetValue("%8.2f" % (Data['IOtth'][0]))
1079                        self.OuterTth.SetValue("%8.2f" % (Data['IOtth'][1]))
1080                        self.Lazim.SetValue("%6d" % (Data['LRazimuth'][0]))
1081                        self.Razim.SetValue("%6d" % (Data['LRazimuth'][1]))
1082                    elif 'Circle' in itemPicked and PickName == 'Masks':
1083                        spots = Masks['Points']
1084                        newPos = itemPicked.split(')')[0].split('(')[2].split(',')
1085                        newPos = np.array([float(newPos[0]),float(newPos[1])])
1086                        for spot in spots:
1087                            if np.allclose(np.array([spot[:2]]),newPos):
1088                                spot[:2] = xpos,ypos
1089                        G2imG.UpdateMasks(self,Masks)
1090                    elif 'Line2D' in itemPicked and PickName == 'Masks':
1091                        Obj = self.itemPicked.findobj()
1092                        rings = Masks['Rings']
1093                        arcs = Masks['Arcs']
1094                        polygons = Masks['Polygons']
1095                        for ring in self.ringList:
1096                            if Obj == ring[0]:
1097                                rN = ring[1]
1098                                if ring[2] == 'o':
1099                                    rings[rN][0] = G2img.GetTth(xpos,ypos,Data)-rings[rN][1]/2.
1100                                else:
1101                                    rings[rN][0] = G2img.GetTth(xpos,ypos,Data)+rings[rN][1]/2.
1102                        for arc in self.arcList:
1103                            if Obj == arc[0]:
1104                                aN = arc[1]
1105                                if arc[2] == 'o':
1106                                    arcs[aN][0] = G2img.GetTth(xpos,ypos,Data)-arcs[aN][2]/2
1107                                elif arc[2] == 'i':
1108                                    arcs[aN][0] = G2img.GetTth(xpos,ypos,Data)+arcs[aN][2]/2
1109                                elif arc[2] == 'l':
1110                                    arcs[aN][1][0] = int(G2img.GetAzm(xpos,ypos,Data))
1111                                else:
1112                                    arcs[aN][1][1] = int(G2img.GetAzm(xpos,ypos,Data))
1113                        for poly in self.polyList:
1114                            if Obj == poly[0]:
1115                                ind = self.itemPicked.contains(self.mousePicked)[1]['ind'][0]
1116                                oldPos = np.array([self.mousePicked.xdata,self.mousePicked.ydata])
1117                                pN = poly[1]
1118                                for i,xy in enumerate(polygons[pN]):
1119                                    if np.allclose(np.array([xy]),oldPos,atol=1.0):
1120                                        polygons[pN][i] = xpos,ypos
1121                        G2imG.UpdateMasks(self,Masks)
1122#                    else:                  #keep for future debugging
1123#                        print str(self.itemPicked),event.xdata,event.ydata,event.button
1124                PlotImage(self)
1125            self.itemPicked = None
1126           
1127    try:
1128        plotNum = self.G2plotNB.plotList.index('2D Powder Image')
1129        Page = self.G2plotNB.nb.GetPage(plotNum)
1130        if not newPlot:
1131            Plot = Page.figure.gca()          #get previous powder plot & get limits
1132            xylim = Plot.get_xlim(),Plot.get_ylim()
1133        Page.figure.clf()
1134        Plot = Page.figure.gca()          #get a fresh plot after clf()
1135       
1136    except ValueError,error:
1137        Plot = self.G2plotNB.addMpl('2D Powder Image').gca()
1138        plotNum = self.G2plotNB.plotList.index('2D Powder Image')
1139        Page = self.G2plotNB.nb.GetPage(plotNum)
1140        Page.canvas.mpl_connect('key_press_event', OnImPlotKeyPress)
1141        Page.canvas.mpl_connect('motion_notify_event', OnImMotion)
1142        Page.canvas.mpl_connect('pick_event', OnImPick)
1143        Page.canvas.mpl_connect('button_release_event', OnImRelease)
1144        xylim = []
1145    if not event:                       #event from GUI TextCtrl - don't want focus to change to plot!!!
1146        Page.SetFocus()
1147    Title = self.PatternTree.GetItemText(self.Image)[4:]
1148    self.G2plotNB.status.DestroyChildren()
1149    if self.logPlot:
1150        Title = 'log('+Title+')'
1151    Plot.set_title(Title)
1152    try:
1153        if self.PatternTree.GetItemText(self.PickId) in ['Image Controls',]:
1154            if self.logPlot:
1155                Choice = (' key press','l: log(I) off')
1156            else:
1157                Choice = (' key press','l: log(I) on')
1158            cb = wx.ComboBox(self.G2plotNB.status,style=wx.CB_DROPDOWN|wx.CB_READONLY,
1159                choices=Choice)
1160            cb.Bind(wx.EVT_COMBOBOX, OnKeyBox)
1161            cb.SetValue(' key press')
1162    except TypeError:
1163        pass
1164    size,imagefile = self.PatternTree.GetItemPyData(self.Image)
1165    if imagefile != self.oldImagefile:
1166        imagefile = G2IO.CheckImageFile(self,imagefile)
1167        if not imagefile:
1168            self.G2plotNB.Delete('2D Powder Image')
1169            return
1170        self.PatternTree.SetItemPyData(self.Image,[size,imagefile])
1171        self.ImageZ = G2IO.GetImageData(self,imagefile,imageOnly=True)
1172#        print self.ImageZ.shape,self.ImageZ.size,Data['size'] #might be useful debugging line
1173        self.oldImagefile = imagefile
1174
1175    imScale = 1
1176    if len(self.ImageZ) > 1024:
1177        imScale = len(self.ImageZ)/1024
1178    sizexy = Data['size']
1179    pixelSize = Data['pixelSize']
1180    scalex = 1000./pixelSize[0]
1181    scaley = 1000./pixelSize[1]
1182    Xmax = sizexy[0]*pixelSize[0]/1000.
1183    Ymax = sizexy[1]*pixelSize[1]/1000.
1184    xlim = (0,Xmax)
1185    ylim = (Ymax,0)
1186    Imin,Imax = Data['range'][1]
1187    acolor = mpl.cm.get_cmap(Data['color'])
1188    xcent,ycent = Data['center']
1189    Plot.set_xlabel('Image x-axis, mm',fontsize=12)
1190    Plot.set_ylabel('Image y-axis, mm',fontsize=12)
1191    #do threshold mask - "real" mask - others are just bondaries
1192    Zlim = Masks['Thresholds'][1]
1193    wx.BeginBusyCursor()
1194    try:
1195        MA = ma.masked_greater(ma.masked_less(self.ImageZ,Zlim[0]),Zlim[1])
1196        MaskA = ma.getmaskarray(MA)
1197        A = G2img.ImageCompress(MA,imScale)
1198        AM = G2img.ImageCompress(MaskA,imScale)
1199        if self.logPlot:
1200            A = np.log(A)
1201            AM = np.log(AM)
1202            Imin,Imax = [np.amin(A),np.amax(A)]
1203                   
1204        ImgM = Plot.imshow(AM,aspect='equal',cmap='Reds',
1205            interpolation='nearest',vmin=0,vmax=2,extent=[0,Xmax,Xmax,0])
1206        Img = Plot.imshow(A,aspect='equal',cmap=acolor,
1207            interpolation='nearest',vmin=Imin,vmax=Imax,extent=[0,Xmax,Ymax,0])
1208        if self.setPoly:
1209            Img.set_picker(True)
1210   
1211        Plot.plot(xcent,ycent,'x')
1212        if Data['showLines']:
1213            LRAzim = Data['LRazimuth']                  #NB: integers
1214            AzmthOff = Data['azmthOff']
1215            IOtth = Data['IOtth']
1216            wave = Data['wavelength']
1217            dspI = wave/(2.0*sind(IOtth[0]/2.0))
1218            ellI = G2img.GetEllipse(dspI,Data)           #=False if dsp didn't yield an ellipse (ugh! a parabola or a hyperbola)
1219            dspO = wave/(2.0*sind(IOtth[1]/2.0))
1220            ellO = G2img.GetEllipse(dspO,Data)           #Ditto & more likely for outer ellipse
1221            if Data['fullIntegrate']:
1222                Azm = np.array(range(0,361))
1223            else:
1224                Azm = np.array(range(LRAzim[0],LRAzim[1]+1))-AzmthOff
1225            if ellI:
1226                xyI = []
1227                for azm in Azm:
1228                    xyI.append(G2img.GetDetectorXY(dspI,azm,Data))
1229                xyI = np.array(xyI)
1230                arcxI,arcyI = xyI.T
1231                Plot.plot(arcxI,arcyI,picker=3)
1232            if ellO:
1233                xyO = []
1234                for azm in Azm:
1235                    xyO.append(G2img.GetDetectorXY(dspO,azm,Data))
1236                xyO = np.array(xyO)
1237                arcxO,arcyO = xyO.T
1238                Plot.plot(arcxO,arcyO,picker=3)
1239            if ellO and ellI and not Data['fullIntegrate']:
1240                Plot.plot([arcxI[0],arcxO[0]],[arcyI[0],arcyO[0]],picker=3)
1241                Plot.plot([arcxI[-1],arcxO[-1]],[arcyI[-1],arcyO[-1]],picker=3)
1242        for xring,yring in Data['ring']:
1243            Plot.plot(xring,yring,'r+',picker=3)
1244        if Data['setRings']:
1245            rings = np.concatenate((Data['rings']),axis=0)
1246            for xring,yring,dsp in rings:
1247                Plot.plot(xring,yring,'r+')           
1248        for ellipse in Data['ellipses']:
1249            cent,phi,[width,height],col = ellipse
1250            Plot.add_artist(Ellipse([cent[0],cent[1]],2*width,2*height,phi,ec=col,fc='none'))
1251            Plot.text(cent[0],cent[1],'+',color=col,ha='center',va='center')
1252        #masks - mask lines numbered after integration limit lines
1253        spots = Masks['Points']
1254        rings = Masks['Rings']
1255        arcs = Masks['Arcs']
1256        polygons = Masks['Polygons']
1257        for x,y,d in spots:
1258            Plot.add_artist(Circle((x,y),radius=d/2,fc='none',ec='r',picker=3))
1259        self.ringList = []
1260        for iring,(tth,thick) in enumerate(rings):
1261            wave = Data['wavelength']
1262            x1,y1 = np.hsplit(np.array(G2img.makeIdealRing(G2img.GetEllipse(Dsp(tth+thick/2.,wave),Data))),2)
1263            x2,y2 = np.hsplit(np.array(G2img.makeIdealRing(G2img.GetEllipse(Dsp(tth-thick/2.,wave),Data))),2)
1264            self.ringList.append([Plot.plot(x1,y1,'r',picker=3),iring,'o'])           
1265            self.ringList.append([Plot.plot(x2,y2,'r',picker=3),iring,'i'])
1266        self.arcList = []
1267        for iarc,(tth,azm,thick) in enumerate(arcs):           
1268            wave = Data['wavelength']
1269            x1,y1 = np.hsplit(np.array(G2img.makeIdealRing(G2img.GetEllipse(Dsp(tth+thick/2.,wave),Data),azm)),2)
1270            x2,y2 = np.hsplit(np.array(G2img.makeIdealRing(G2img.GetEllipse(Dsp(max(0.01,tth-thick/2.),wave),Data),azm)),2)
1271            self.arcList.append([Plot.plot(x1,y1,'r',picker=3),iarc,'o'])           
1272            self.arcList.append([Plot.plot(x2,y2,'r',picker=3),iarc,'i'])
1273            self.arcList.append([Plot.plot([x1[0],x2[0]],[y1[0],y2[0]],'r',picker=3),iarc,'l'])
1274            self.arcList.append([Plot.plot([x1[-1],x2[-1]],[y1[-1],y2[-1]],'r',picker=3),iarc,'u'])
1275        self.polyList = []
1276        for ipoly,polygon in enumerate(polygons):
1277            x,y = np.hsplit(np.array(polygon),2)
1278            self.polyList.append([Plot.plot(x,y,'r+',picker=10),ipoly])
1279            Plot.plot(x,y,'r')           
1280        colorBar = Page.figure.colorbar(Img)
1281        Plot.set_xlim(xlim)
1282        Plot.set_ylim(ylim)
1283        if not newPlot and xylim:
1284            Page.toolbar.push_current()
1285            Plot.set_xlim(xylim[0])
1286            Plot.set_ylim(xylim[1])
1287            xylim = []
1288            Page.toolbar.push_current()
1289            Page.toolbar.draw()
1290        else:
1291            Page.canvas.draw()
1292    finally:
1293        wx.EndBusyCursor()
1294       
1295def PlotIntegration(self,newPlot=False,event=None):
1296           
1297    def OnMotion(event):
1298        Page.canvas.SetToolTipString('')
1299        Page.canvas.SetCursor(wx.CROSS_CURSOR)
1300        azm = event.ydata
1301        tth = event.xdata
1302        if azm and tth:
1303            self.G2plotNB.status.SetFields(\
1304                ['','Detector 2-th =%9.3fdeg, azm = %7.2fdeg'%(tth,azm)])
1305                               
1306    try:
1307        plotNum = self.G2plotNB.plotList.index('2D Integration')
1308        Page = self.G2plotNB.nb.GetPage(plotNum)
1309        if not newPlot:
1310            Plot = Page.figure.gca()          #get previous plot & get limits
1311            xylim = Plot.get_xlim(),Plot.get_ylim()
1312        Page.figure.clf()
1313        Plot = Page.figure.gca()          #get a fresh plot after clf()
1314       
1315    except ValueError,error:
1316        Plot = self.G2plotNB.addMpl('2D Integration').gca()
1317        plotNum = self.G2plotNB.plotList.index('2D Integration')
1318        Page = self.G2plotNB.nb.GetPage(plotNum)
1319        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
1320        Page.views = False
1321        view = False
1322    if not event:
1323        Page.SetFocus()
1324       
1325    Data = self.PatternTree.GetItemPyData(
1326        G2gd.GetPatternTreeItemId(self,self.Image, 'Image Controls'))
1327    image = self.Integrate[0]
1328    xsc = self.Integrate[1]
1329    ysc = self.Integrate[2]
1330    Imin,Imax = Data['range'][1]
1331    acolor = mpl.cm.get_cmap(Data['color'])
1332    Plot.set_title(self.PatternTree.GetItemText(self.Image)[4:])
1333    Plot.set_ylabel('azimuth',fontsize=12)
1334    Plot.set_xlabel('2-theta',fontsize=12)
1335    Img = Plot.imshow(image,cmap=acolor,vmin=Imin,vmax=Imax,interpolation='nearest', \
1336        extent=[ysc[0],ysc[-1],xsc[-1],xsc[0]],aspect='auto')
1337    colorBar = Page.figure.colorbar(Img)
1338    if Data['setRings']:
1339        rings = np.concatenate((Data['rings']),axis=0)
1340        for xring,yring,dsp in rings:
1341            x,y = G2img.GetTthAzm(xring,yring,Data)
1342            Plot.plot(x,y,'r+')
1343    if Data['ellipses']:           
1344        for ellipse in Data['ellipses']:
1345            ring = np.array(G2img.makeIdealRing(ellipse[:3])) #skip color
1346            x,y = np.hsplit(ring,2)
1347            tth,azm = G2img.GetTthAzm(x,y,Data)
1348            Plot.plot(tth,azm,'b,')
1349    if not newPlot:
1350        Page.toolbar.push_current()
1351        Plot.set_xlim(xylim[0])
1352        Plot.set_ylim(xylim[1])
1353        xylim = []
1354        Page.toolbar.push_current()
1355        Page.toolbar.draw()
1356    else:
1357        Page.canvas.draw()
1358               
1359def PlotTRImage(self,tax,tay,taz,newPlot=False):
1360    #a test plot routine - not normally used
1361           
1362    def OnMotion(event):
1363        Page.canvas.SetToolTipString('')
1364        Page.canvas.SetCursor(wx.CROSS_CURSOR)
1365        azm = event.xdata
1366        tth = event.ydata
1367        if azm and tth:
1368            self.G2plotNB.status.SetFields(\
1369                ['','Detector 2-th =%9.3fdeg, azm = %7.2fdeg'%(tth,azm)])
1370                               
1371    try:
1372        plotNum = self.G2plotNB.plotList.index('2D Transformed Powder Image')
1373        Page = self.G2plotNB.nb.GetPage(plotNum)
1374        if not newPlot:
1375            Plot = Page.figure.gca()          #get previous plot & get limits
1376            xylim = Plot.get_xlim(),Plot.get_ylim()
1377        Page.figure.clf()
1378        Plot = Page.figure.gca()          #get a fresh plot after clf()
1379       
1380    except ValueError,error:
1381        Plot = self.G2plotNB.addMpl('2D Transformed Powder Image').gca()
1382        plotNum = self.G2plotNB.plotList.index('2D Transformed Powder Image')
1383        Page = self.G2plotNB.nb.GetPage(plotNum)
1384        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
1385        Page.views = False
1386        view = False
1387    Page.SetFocus()
1388       
1389    Data = self.PatternTree.GetItemPyData(
1390        G2gd.GetPatternTreeItemId(self,self.Image, 'Image Controls'))
1391    Imin,Imax = Data['range'][1]
1392    step = (Imax-Imin)/5.
1393    V = np.arange(Imin,Imax,step)
1394    acolor = mpl.cm.get_cmap(Data['color'])
1395    Plot.set_title(self.PatternTree.GetItemText(self.Image)[4:])
1396    Plot.set_xlabel('azimuth',fontsize=12)
1397    Plot.set_ylabel('2-theta',fontsize=12)
1398    Plot.contour(tax,tay,taz,V,cmap=acolor)
1399    if Data['showLines']:
1400        IOtth = Data['IOtth']
1401        if Data['fullIntegrate']:
1402            LRAzim = [-180,180]
1403        else:
1404            LRAzim = Data['LRazimuth']                  #NB: integers
1405        Plot.plot([LRAzim[0],LRAzim[1]],[IOtth[0],IOtth[0]],picker=True)
1406        Plot.plot([LRAzim[0],LRAzim[1]],[IOtth[1],IOtth[1]],picker=True)
1407        if not Data['fullIntegrate']:
1408            Plot.plot([LRAzim[0],LRAzim[0]],[IOtth[0],IOtth[1]],picker=True)
1409            Plot.plot([LRAzim[1],LRAzim[1]],[IOtth[0],IOtth[1]],picker=True)
1410    if Data['setRings']:
1411        rings = np.concatenate((Data['rings']),axis=0)
1412        for xring,yring,dsp in rings:
1413            x,y = G2img.GetTthAzm(xring,yring,Data)
1414            Plot.plot(y,x,'r+')           
1415    if Data['ellipses']:           
1416        for ellipse in Data['ellipses']:
1417            ring = np.array(G2img.makeIdealRing(ellipse[:3])) #skip color
1418            x,y = np.hsplit(ring,2)
1419            tth,azm = G2img.GetTthAzm(x,y,Data)
1420            Plot.plot(azm,tth,'b,')
1421    if not newPlot:
1422        Page.toolbar.push_current()
1423        Plot.set_xlim(xylim[0])
1424        Plot.set_ylim(xylim[1])
1425        xylim = []
1426        Page.toolbar.push_current()
1427        Page.toolbar.draw()
1428    else:
1429        Page.canvas.draw()
1430       
1431def PlotStructure(self,data):
1432    generalData = data['General']
1433    cell = generalData['Cell'][1:7]
1434    Amat,Bmat = G2lat.cell2AB(cell)         #Amat - crystal to cartesian, Bmat - inverse
1435    A4mat = np.concatenate((np.concatenate((Amat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
1436    B4mat = np.concatenate((np.concatenate((Bmat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
1437    Mydir = generalData['Mydir']
1438    atomData = data['Atoms']
1439    drawingData = data['Drawing']
1440    drawAtoms = drawingData['Atoms']
1441    cx,ct,cs = drawingData['atomPtrs']
1442    Wt = [255,255,255]
1443    Rd = [255,0,0]
1444    Gr = [0,255,0]
1445    Bl = [0,0,255]
1446    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]])
1447    uEdges = np.array([
1448        [uBox[0],uBox[1]],[uBox[0],uBox[3]],[uBox[0],uBox[4]],[uBox[1],uBox[2]], 
1449        [uBox[2],uBox[3]],[uBox[1],uBox[5]],[uBox[2],uBox[6]],[uBox[3],uBox[7]], 
1450        [uBox[4],uBox[5]],[uBox[5],uBox[6]],[uBox[6],uBox[7]],[uBox[7],uBox[4]]])
1451    uColors = [Rd,Gr,Bl,Wt, Wt,Wt,Wt,Wt, Wt,Wt,Wt,Wt]
1452    altDown = False
1453    shiftDown = False
1454    ctrlDown = False
1455   
1456    def OnKeyBox(event):
1457        import Image
1458        Draw()                          #make sure plot is fresh!!
1459        mode = cb.GetValue()
1460        Fname = Mydir+'\\'+generalData['Name']+'.'+mode
1461        size = Page.canvas.GetSize()
1462        glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
1463        if mode in ['jpeg',]:
1464            Pix = glReadPixels(0,0,size[0],size[1],GL_RGBA, GL_UNSIGNED_BYTE)
1465            im = Image.new("RGBA", (size[0],size[1]))
1466        else:
1467            Pix = glReadPixels(0,0,size[0],size[1],GL_RGB, GL_UNSIGNED_BYTE)
1468            im = Image.new("RGB", (size[0],size[1]))
1469        im.fromstring(Pix)
1470        im.save(Fname,mode)
1471        cb.SetValue(' Save as:')
1472        self.G2plotNB.status.SetStatusText('Drawing saved to: '+Fname,1)
1473   
1474    def GetTruePosition(xy):
1475        View = glGetIntegerv(GL_VIEWPORT)
1476        Proj = glGetDoublev(GL_PROJECTION_MATRIX)
1477        Model = glGetDoublev(GL_MODELVIEW_MATRIX)
1478        Zmax = 1.
1479        for i,atom in enumerate(drawAtoms):
1480            x,y,z = atom[cx:cx+3]
1481            X,Y,Z = gluProject(x,y,z,Model,Proj,View)
1482            XY = [int(X),int(View[3]-Y)]
1483            if np.allclose(xy,XY,atol=10) and Z < Zmax:
1484                Zmax = Z
1485                SetSelectedAtoms(i)
1486                   
1487    def OnMouseDown(event):
1488        xy = event.GetPosition()
1489        if event.ShiftDown():
1490            GetTruePosition(xy)
1491        else:
1492            drawingData['Rotation'][3] = xy
1493        Draw()
1494       
1495    def OnMouseMove(event):
1496        newxy = event.GetPosition()
1497        if event.ControlDown() and drawingData['showABC']:
1498            if event.LeftIsDown():
1499                ctrlDown = True
1500                SetTestRot(newxy)
1501            elif event.RightIsDown():
1502                SetTestPos(newxy)
1503            elif event.MiddleIsDown():
1504                SetTestRotZ(newxy)
1505               
1506               
1507        if event.Dragging() and not event.ControlDown():
1508            if event.LeftIsDown():
1509                SetRotation(newxy)
1510            elif event.RightIsDown():
1511                SetTranslation(newxy)
1512            elif event.MiddleIsDown():
1513                SetRotationZ(newxy)
1514        Draw()
1515       
1516    def OnMouseWheel(event):
1517        drawingData['cameraPos'] += event.GetWheelRotation()/24
1518        drawingData['cameraPos'] = max(10,min(500,drawingData['cameraPos']))
1519        page = self.dataDisplay.GetSelection()
1520        if page:
1521            if self.dataDisplay.GetPageText(page) == 'Draw Options':
1522                panel = self.dataDisplay.GetPage(page).GetChildren()[0].GetChildren()
1523                names = [child.GetName() for child in panel]
1524                panel[names.index('cameraPos')].SetLabel('Camera Position: '+'%.2f'%(drawingData['cameraPos']))
1525                panel[names.index('cameraSlider')].SetValue(drawingData['cameraPos'])
1526        Draw()
1527           
1528    def SetViewPointText(VP):
1529        page = self.dataDisplay.GetSelection()
1530        if page:
1531            if self.dataDisplay.GetPageText(page) == 'Draw Options':
1532                panel = self.dataDisplay.GetPage(page).GetChildren()[0].GetChildren()
1533                names = [child.GetName() for child in panel]
1534                panel[names.index('viewPoint')].SetValue('%.3f, %.3f, %.3f'%(VP[0],VP[1],VP[2]))
1535           
1536    def ClearSelectedAtoms():
1537        page = self.dataDisplay.GetSelection()
1538        if page:
1539            if self.dataDisplay.GetPageText(page) == 'Draw Atoms':
1540                self.dataDisplay.GetPage(page).ClearSelection()      #this is the Atoms grid in Draw Atoms
1541            elif self.dataDisplay.GetPageText(page) == 'Atoms':
1542                self.dataDisplay.GetPage(page).ClearSelection()      #this is the Atoms grid in Atoms
1543                   
1544    def SetSelectedAtoms(ind):
1545        page = self.dataDisplay.GetSelection()
1546        if page:
1547            if self.dataDisplay.GetPageText(page) == 'Draw Atoms':
1548                self.dataDisplay.GetPage(page).SelectRow(ind)      #this is the Atoms grid in Draw Atoms
1549            elif self.dataDisplay.GetPageText(page) == 'Atoms':
1550                Id = drawAtoms[ind][-2]
1551                for i,atom in enumerate(atomData):
1552                    if atom[-1] == Id:
1553                        self.dataDisplay.GetPage(page).SelectRow(i)      #this is the Atoms grid in Atoms
1554                 
1555    def GetSelectedAtoms():
1556        page = self.dataDisplay.GetSelection()
1557        Ind = []
1558        if page:
1559            if self.dataDisplay.GetPageText(page) == 'Draw Atoms':
1560                Ind = self.dataDisplay.GetPage(page).GetSelectedRows()      #this is the Atoms grid in Draw Atoms
1561            elif self.dataDisplay.GetPageText(page) == 'Atoms':
1562                Ind = self.dataDisplay.GetPage(page).GetSelectedRows()      #this is the Atoms grid in Atoms
1563        return Ind
1564                                       
1565    def OnKey(event):           #on key UP!!
1566        keyCode = event.GetKeyCode()
1567        if keyCode > 255:
1568            keyCode = 0
1569        key,xyz = chr(keyCode),event.GetPosition()
1570        indx = drawingData['selectedAtoms']
1571        if key in ['c','C']:
1572            drawingData['viewPoint'] = [[.5,.5,.5],[0,0]]
1573            drawingData['testPos'] = [[-.1,-.1,-.1],[0.0,0.0,0.0],[0,0]]
1574            drawingData['Rotation'] = [0.0,0.0,0.0,[]]
1575            SetViewPointText(drawingData['viewPoint'][0])
1576        elif key in ['n','N']:
1577            drawAtoms = drawingData['Atoms']
1578            pI = drawingData['viewPoint'][1]
1579            if indx:
1580                pI[0] = indx[pI[1]]
1581                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]
1582                pI[1] += 1
1583                if pI[1] >= len(indx):
1584                    pI[1] = 0
1585            else:
1586                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]               
1587                pI[0] += 1
1588                if pI[0] >= len(drawAtoms):
1589                    pI[0] = 0
1590            drawingData['viewPoint'] = [[Tx,Ty,Tz],pI]
1591            SetViewPointText(drawingData['viewPoint'][0])
1592               
1593        elif key in ['p','P']:
1594            drawAtoms = drawingData['Atoms']
1595            pI = drawingData['viewPoint'][1]
1596            if indx:
1597                pI[0] = indx[pI[1]]
1598                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]
1599                pI[1] -= 1
1600                if pI[1] < 0:
1601                    pI[1] = len(indx)-1
1602            else:
1603                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]               
1604                pI[0] -= 1
1605                if pI[0] < 0:
1606                    pI[0] = len(drawAtoms)-1
1607            drawingData['viewPoint'] = [[Tx,Ty,Tz],pI]
1608            SetViewPointText(drawingData['viewPoint'][0])           
1609        Draw()
1610           
1611    def SetBackground():
1612        R,G,B,A = Page.camera['backColor']
1613        glClearColor(R,G,B,A)
1614        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
1615       
1616    def SetLights():
1617        glEnable(GL_DEPTH_TEST)
1618        glShadeModel(GL_SMOOTH)
1619        glEnable(GL_LIGHTING)
1620        glEnable(GL_LIGHT0)
1621        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,0)
1622        glLightfv(GL_LIGHT0,GL_AMBIENT,[1,1,1,.8])
1623        glLightfv(GL_LIGHT0,GL_DIFFUSE,[1,1,1,1])
1624       
1625    def SetTranslation(newxy):
1626        Tx,Ty,Tz = drawingData['viewPoint'][0]
1627        anglex,angley,anglez,oldxy = drawingData['Rotation']
1628        Rx = G2lat.rotdMat(anglex,0)
1629        Ry = G2lat.rotdMat(angley,1)
1630        Rz = G2lat.rotdMat(anglez,2)
1631        dxy = list(newxy-oldxy)+[0,]
1632        dxy = np.inner(Bmat,np.inner(Rz,np.inner(Ry,np.inner(Rx,dxy))))
1633        Tx -= dxy[0]*0.01
1634        Ty += dxy[1]*0.01
1635        Tz -= dxy[2]*0.01
1636        drawingData['Rotation'][3] = newxy
1637        drawingData['viewPoint'][0] =  Tx,Ty,Tz
1638        SetViewPointText([Tx,Ty,Tz])
1639       
1640    def SetTestPos(newxy):
1641        Tx,Ty,Tz = drawingData['testPos'][0]
1642        anglex,angley,anglez,oldxy = drawingData['Rotation']
1643        Rx = G2lat.rotdMat(anglex,0)
1644        Ry = G2lat.rotdMat(angley,1)
1645        Rz = G2lat.rotdMat(anglez,2)
1646        dxy = list(newxy-oldxy)+[0,]
1647        dxy = np.inner(Rz,np.inner(Ry,np.inner(Rx,dxy)))
1648        Tx += dxy[0]*0.001
1649        Ty -= dxy[1]*0.001
1650        Tz += dxy[2]*0.001
1651        drawingData['Rotation'][3] = newxy
1652        drawingData['testPos'][0] =  Tx,Ty,Tz
1653       
1654    def SetTestRot(newxy):
1655        Txyz = np.array(drawingData['testPos'][0])
1656        oldxy = drawingData['testPos'][2]
1657        Ax,Ay,Az = drawingData['testPos'][1]
1658        Vxyz = np.array(drawingData['viewPoint'][0])
1659        Dxyz = np.inner(Amat,Txyz-Vxyz)
1660        dxy = list(newxy-oldxy)+[0,]
1661        Ax += dxy[1]*0.01
1662        Ay += dxy[0]*0.01
1663        Rx = G2lat.rotdMat(Ax,0)
1664        Ry = G2lat.rotdMat(Ay,1)
1665        Dxyz = np.inner(Ry,np.inner(Rx,Dxyz))       
1666        Dxyz = np.inner(Bmat,Dxyz)+Vxyz
1667        drawingData['testPos'][1] = [Ax,Ay,Az]
1668        drawingData['testPos'][2] = newxy
1669        drawingData['testPos'][0] = Dxyz
1670       
1671    def SetTestRotZ(newxy):
1672        Txyz = np.array(drawingData['testPos'][0])
1673        oldxy = drawingData['testPos'][2]
1674        Ax,Ay,Az = drawingData['testPos'][1]
1675        Vxyz = np.array(drawingData['viewPoint'][0])
1676        Dxyz = np.inner(Amat,Txyz-Vxyz)       
1677        dxy = list(newxy-oldxy)+[0,]
1678        Az += (dxy[0]+dxy[1])*.01
1679        Rz = G2lat.rotdMat(Az,2)
1680        Dxyz = np.inner(Rz,Dxyz)       
1681        Dxyz = np.inner(Bmat,Dxyz)+Vxyz
1682        drawingData['testPos'][1] = [Ax,Ay,Az]
1683        drawingData['testPos'][2] = newxy
1684        drawingData['testPos'][0] = Dxyz
1685                             
1686    def SetRotation(newxy):       
1687        anglex,angley,anglez,oldxy = drawingData['Rotation']
1688        dxy = newxy-oldxy
1689        anglex += dxy[1]*.25
1690        angley += dxy[0]*.25
1691        oldxy = newxy
1692        drawingData['Rotation'] = [anglex,angley,anglez,oldxy]
1693       
1694    def SetRotationZ(newxy):                       
1695        anglex,angley,anglez,oldxy = drawingData['Rotation']
1696        dxy = newxy-oldxy
1697        anglez += (dxy[0]+dxy[1])*.25
1698        oldxy = newxy
1699        drawingData['Rotation'] = [anglex,angley,anglez,oldxy]
1700       
1701    def RenderBox():
1702        glEnable(GL_COLOR_MATERIAL)
1703        glLineWidth(2)
1704        glEnable(GL_BLEND)
1705        glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)
1706        glEnable(GL_LINE_SMOOTH)
1707        glBegin(GL_LINES)
1708        for line,color in zip(uEdges,uColors):
1709            glColor3ubv(color)
1710            glVertex3fv(line[0])
1711            glVertex3fv(line[1])
1712        glEnd()
1713        glColor4ubv([0,0,0,0])
1714        glDisable(GL_LINE_SMOOTH)
1715        glDisable(GL_BLEND)
1716        glDisable(GL_COLOR_MATERIAL)
1717       
1718    def RenderUnitVectors(x,y,z):
1719        xyz = np.array([x,y,z])
1720        glEnable(GL_COLOR_MATERIAL)
1721        glLineWidth(1)
1722        glPushMatrix()
1723        glTranslate(x,y,z)
1724        glScalef(1/cell[0],1/cell[1],1/cell[2])
1725        glBegin(GL_LINES)
1726        for line,color in zip(uEdges,uColors)[:3]:
1727            glColor3ubv(color)
1728            glVertex3fv(line[0])
1729            glVertex3fv(line[1])
1730        glEnd()
1731        glPopMatrix()
1732        glColor4ubv([0,0,0,0])
1733        glDisable(GL_COLOR_MATERIAL)
1734               
1735    def RenderSphere(x,y,z,radius,color):
1736        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color)
1737        glPushMatrix()
1738        glTranslate(x,y,z)
1739        glMultMatrixf(B4mat.T)
1740        q = gluNewQuadric()
1741        gluSphere(q,radius,20,10)
1742        glPopMatrix()
1743       
1744    def RenderEllipsoid(x,y,z,ellipseProb,E,R4,color):
1745        s1,s2,s3 = E
1746        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color)
1747        glPushMatrix()
1748        glTranslate(x,y,z)
1749        glMultMatrixf(B4mat.T)
1750        glMultMatrixf(R4.T)
1751        glEnable(GL_NORMALIZE)
1752        glScale(s1,s2,s3)
1753        q = gluNewQuadric()
1754        gluSphere(q,ellipseProb,20,10)
1755        glDisable(GL_NORMALIZE)
1756        glPopMatrix()
1757       
1758    def RenderBonds(x,y,z,Bonds,radius,color,slice=20):
1759        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color)
1760        glPushMatrix()
1761        glTranslate(x,y,z)
1762        glMultMatrixf(B4mat.T)
1763        for bond in Bonds:
1764            glPushMatrix()
1765            Dx = np.inner(Amat,bond)
1766            Z = np.sqrt(np.sum(Dx**2))
1767            azm = atan2d(-Dx[1],-Dx[0])
1768            phi = acosd(Dx[2]/Z)
1769            glRotate(-azm,0,0,1)
1770            glRotate(phi,1,0,0)
1771            q = gluNewQuadric()
1772            gluCylinder(q,radius,radius,Z,slice,2)
1773            glPopMatrix()           
1774        glPopMatrix()
1775               
1776    def RenderLines(x,y,z,Bonds,color):
1777        xyz = np.array([x,y,z])
1778        glEnable(GL_COLOR_MATERIAL)
1779        glLineWidth(1)
1780        glColor3fv(color)
1781        glPushMatrix()
1782        glBegin(GL_LINES)
1783        for bond in Bonds:
1784            glVertex3fv(xyz)
1785            glVertex3fv(xyz+bond)
1786        glEnd()
1787        glColor4ubv([0,0,0,0])
1788        glPopMatrix()
1789        glDisable(GL_COLOR_MATERIAL)
1790       
1791    def RenderPolyhedra(x,y,z,Faces,color):
1792        glPushMatrix()
1793        glTranslate(x,y,z)
1794        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color)
1795        glShadeModel(GL_SMOOTH)
1796        glMultMatrixf(B4mat.T)
1797        for face,norm in Faces:
1798            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL)
1799            glFrontFace(GL_CW)
1800            glNormal3fv(norm)
1801            glBegin(GL_TRIANGLES)
1802            for vert in face:
1803                glVertex3fv(vert)
1804            glEnd()
1805        glPopMatrix()
1806       
1807    def RenderBackbone(Backbone,BackboneColor,radius):
1808        glPushMatrix()
1809        glMultMatrixf(B4mat.T)
1810        glEnable(GL_COLOR_MATERIAL)
1811        glShadeModel(GL_SMOOTH)
1812        gleSetJoinStyle(TUBE_NORM_EDGE | TUBE_JN_ANGLE | TUBE_JN_CAP)
1813        glePolyCylinder(Backbone,BackboneColor,radius)
1814        glPopMatrix()       
1815        glDisable(GL_COLOR_MATERIAL)
1816       
1817    def RenderLabel(x,y,z,label,r):       
1818        glPushMatrix()
1819        glTranslate(x,y,z)
1820        glMultMatrixf(B4mat.T)
1821        glDisable(GL_LIGHTING)
1822        glColor3f(0,1.,0)
1823        glRasterPos3f(r,r,r)
1824        for c in list(label):
1825            glutBitmapCharacter(GLUT_BITMAP_8_BY_13,ord(c))
1826        glEnable(GL_LIGHTING)
1827        glPopMatrix()
1828                           
1829    def Draw():
1830        import numpy.linalg as nl
1831        Ind = GetSelectedAtoms()
1832        VS = np.array(Page.canvas.GetSize())
1833        aspect = float(VS[0])/float(VS[1])
1834        cPos = drawingData['cameraPos']
1835        Zclip = drawingData['Zclip']*cPos/200.
1836        anglex,angley,anglez = drawingData['Rotation'][:3]
1837        Tx,Ty,Tz = drawingData['viewPoint'][0]
1838        cx,ct,cs = drawingData['atomPtrs']
1839        bondR = drawingData['bondRadius']
1840        G,g = G2lat.cell2Gmat(cell)
1841        GS = G
1842        GS[0][1] = GS[1][0] = math.sqrt(GS[0][0]*GS[1][1])
1843        GS[0][2] = GS[2][0] = math.sqrt(GS[0][0]*GS[2][2])
1844        GS[1][2] = GS[2][1] = math.sqrt(GS[1][1]*GS[2][2])
1845        ellipseProb = G2lat.criticalEllipse(drawingData['ellipseProb']/100.)
1846       
1847        SetBackground()
1848        glInitNames()
1849        glPushName(0)
1850       
1851        glMatrixMode(GL_PROJECTION)
1852        glLoadIdentity()
1853        glViewport(0,0,VS[0],VS[1])
1854        gluPerspective(20.,aspect,cPos-Zclip,cPos+Zclip)
1855        gluLookAt(0,0,cPos,0,0,0,0,1,0)
1856        SetLights()           
1857           
1858        glMatrixMode(GL_MODELVIEW)
1859        glLoadIdentity()
1860        glRotate(anglez,0,0,1)
1861        glRotate(anglex,cosd(anglez),-sind(anglez),0)
1862        glRotate(angley,sind(anglez),cosd(anglez),0)
1863        glMultMatrixf(A4mat.T)
1864        glTranslate(-Tx,-Ty,-Tz)
1865        if drawingData['unitCellBox']:
1866            RenderBox()
1867        if drawingData['showABC']:
1868#            try:            #temporary fix - not needed further?
1869#                x,y,z = drawingData['testPos'][0]
1870#            except TypeError:
1871#                x,y,z = drawingData['testPos']
1872            x,y,z = drawingData['testPos'][0]
1873            if altDown:
1874                self.G2plotNB.status.SetStatusText('moving test point %.4f,%.4f,%.4f'%(x,y,z),1)
1875            else:
1876                self.G2plotNB.status.SetStatusText('test point %.4f,%.4f,%.4f'%(x,y,z),1)           
1877            RenderUnitVectors(x,y,z)
1878        Backbone = []
1879        BackboneColor = []
1880        time0 = time.time()
1881        for iat,atom in enumerate(drawingData['Atoms']):
1882            x,y,z = atom[cx:cx+3]
1883            Bonds = atom[-2]
1884            Faces = atom[-1]
1885            try:
1886                atNum = generalData['AtomTypes'].index(atom[ct])
1887            except ValueError:
1888                atNum = -1
1889            CL = atom[cs+2]
1890            color = np.array(CL)/255.
1891            if iat in Ind:
1892                color = np.array(Gr)/255.
1893            radius = 0.5
1894            if atom[cs] != '':
1895                glLoadName(atom[-3])                   
1896            if 'balls' in atom[cs]:
1897                vdwScale = drawingData['vdwScale']
1898                ballScale = drawingData['ballScale']
1899                if atNum < 0:
1900                    radius = 0.2
1901                elif 'H' == atom[ct]:
1902                    if drawingData['showHydrogen']:
1903                        if 'vdW' in atom[cs] and atNum >= 0:
1904                            radius = vdwScale*generalData['vdWRadii'][atNum]
1905                        else:
1906                            radius = ballScale*drawingData['sizeH']
1907                    else:
1908                        radius = 0.0
1909                else:
1910                    if 'vdW' in atom[cs]:
1911                        radius = vdwScale*generalData['vdWRadii'][atNum]
1912                    else:
1913                        radius = ballScale*generalData['BondRadii'][atNum]
1914                RenderSphere(x,y,z,radius,color)
1915                if 'sticks' in atom[cs]:
1916                    RenderBonds(x,y,z,Bonds,bondR,color)
1917            elif 'ellipsoids' in atom[cs]:
1918                RenderBonds(x,y,z,Bonds,bondR,color)
1919                if atom[cs+3] == 'A':                   
1920                    Uij = atom[cs+5:cs+11]
1921                    U = np.multiply(G2spc.Uij2U(Uij),GS)
1922                    U = np.inner(Amat,np.inner(U,Amat).T)
1923                    E,R = nl.eigh(U)
1924                    R4 = np.concatenate((np.concatenate((R,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
1925                    E = np.sqrt(E)
1926                    if atom[ct] == 'H' and not drawingData['showHydrogen']:
1927                        pass
1928                    else:
1929                        RenderEllipsoid(x,y,z,ellipseProb,E,R4,color)                   
1930                else:
1931                    if atom[ct] == 'H' and not drawingData['showHydrogen']:
1932                        pass
1933                    else:
1934                        radius = ellipseProb*math.sqrt(abs(atom[cs+4]))
1935                        RenderSphere(x,y,z,radius,color)
1936            elif 'lines' in atom[cs]:
1937                radius = 0.1
1938                RenderLines(x,y,z,Bonds,color)
1939#                RenderBonds(x,y,z,Bonds,0.05,color,6)
1940            elif atom[cs] == 'sticks':
1941                radius = 0.1
1942                RenderBonds(x,y,z,Bonds,bondR,color)
1943            elif atom[cs] == 'polyhedra':
1944                RenderPolyhedra(x,y,z,Faces,color)
1945            elif atom[cs] == 'backbone':
1946                if atom[ct-1].split()[0] in ['C','N']:
1947                    Backbone.append(list(np.inner(Amat,np.array([x,y,z]))))
1948                    BackboneColor.append(list(color))
1949                   
1950            if atom[cs+1] == 'type':
1951                RenderLabel(x,y,z,atom[ct],radius)
1952            elif atom[cs+1] == 'name':
1953                RenderLabel(x,y,z,atom[ct-1],radius)
1954            elif atom[cs+1] == 'number':
1955                RenderLabel(x,y,z,str(iat+1),radius)
1956            elif atom[cs+1] == 'residue' and atom[ct-1] == 'CA':
1957                RenderLabel(x,y,z,atom[ct-4],radius)
1958            elif atom[cs+1] == '1-letter' and atom[ct-1] == 'CA':
1959                RenderLabel(x,y,z,atom[ct-3],radius)
1960            elif atom[cs+1] == 'chain' and atom[ct-1] == 'CA':
1961                RenderLabel(x,y,z,atom[ct-2],radius)
1962        if Backbone:
1963            RenderBackbone(Backbone,BackboneColor,bondR)
1964#        print time.time()-time0
1965        Page.canvas.SwapBuffers()
1966       
1967    def OnSize(event):
1968        Draw()
1969       
1970    def OnFocus(event):
1971        Draw()
1972       
1973    try:
1974        plotNum = self.G2plotNB.plotList.index(generalData['Name'])
1975        Page = self.G2plotNB.nb.GetPage(plotNum)       
1976    except (ValueError,error):
1977        Plot = self.G2plotNB.addOgl(generalData['Name'])
1978        plotNum = self.G2plotNB.plotList.index(generalData['Name'])
1979        Page = self.G2plotNB.nb.GetPage(plotNum)
1980        Page.views = False
1981        view = False
1982        altDown = False
1983    Page.SetFocus()
1984    cb = wx.ComboBox(self.G2plotNB.status,style=wx.CB_DROPDOWN|wx.CB_READONLY,
1985        choices=(' save as:','jpeg','tiff','bmp'))
1986    cb.Bind(wx.EVT_COMBOBOX, OnKeyBox)
1987    cb.SetValue(' save as:')
1988    Page.canvas.Bind(wx.EVT_MOUSEWHEEL, OnMouseWheel)
1989    Page.canvas.Bind(wx.EVT_LEFT_DOWN, OnMouseDown)
1990    Page.canvas.Bind(wx.EVT_RIGHT_DOWN, OnMouseDown)
1991    Page.canvas.Bind(wx.EVT_MIDDLE_DOWN, OnMouseDown)
1992    Page.canvas.Bind(wx.EVT_KEY_UP, OnKey)
1993    Page.canvas.Bind(wx.EVT_MOTION, OnMouseMove)
1994    Page.canvas.Bind(wx.EVT_SIZE, OnSize)
1995    Page.canvas.Bind(wx.EVT_SET_FOCUS, OnFocus)
1996    Page.camera['position'] = drawingData['cameraPos']
1997    Page.camera['viewPoint'] = np.inner(Amat,drawingData['viewPoint'][0])
1998    Page.camera['backColor'] = np.array(list(drawingData['backColor'])+[0,])/255.
1999    Page.canvas.SetCurrent()
2000    Draw()
2001       
Note: See TracBrowser for help on using the repository browser.