source: trunk/GSASIIgrid.py @ 2509

Last change on this file since 2509 was 2509, checked in by toby, 5 years ago

fixes to help

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 214.5 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIgrid - data display routines
3########### SVN repository information ###################
4# $Date: 2016-10-31 23:18:14 +0000 (Mon, 31 Oct 2016) $
5# $Author: toby $
6# $Revision: 2509 $
7# $URL: trunk/GSASIIgrid.py $
8# $Id: GSASIIgrid.py 2509 2016-10-31 23:18:14Z toby $
9########### SVN repository information ###################
10'''
11*GSASIIgrid: Basic GUI routines*
12--------------------------------
13
14'''
15import wx
16import wx.grid as wg
17#import wx.wizard as wz
18#import wx.aui
19import wx.lib.scrolledpanel as wxscroll
20import time
21import copy
22import cPickle
23import sys
24import os
25import random as ran
26import numpy as np
27import numpy.ma as ma
28import scipy.optimize as so
29import GSASIIpath
30GSASIIpath.SetVersionNumber("$Revision: 2509 $")
31import GSASIImath as G2mth
32import GSASIIIO as G2IO
33import GSASIIstrIO as G2stIO
34import GSASIIlattice as G2lat
35import GSASIIplot as G2plt
36import GSASIIpwdGUI as G2pdG
37import GSASIIimgGUI as G2imG
38import GSASIIphsGUI as G2phG
39import GSASIIspc as G2spc
40import GSASIImapvars as G2mv
41import GSASIIconstrGUI as G2cnstG
42import GSASIIrestrGUI as G2restG
43import GSASIIpy3 as G2py3
44import GSASIIobj as G2obj
45import GSASIIexprGUI as G2exG
46import GSASIIlog as log
47import GSASIIctrls as G2G
48
49# trig functions in degrees
50sind = lambda x: np.sin(x*np.pi/180.)
51tand = lambda x: np.tan(x*np.pi/180.)
52cosd = lambda x: np.cos(x*np.pi/180.)
53
54# Define a short name for convenience
55WACV = wx.ALIGN_CENTER_VERTICAL
56
57[ wxID_FOURCALC, wxID_FOURSEARCH, wxID_FOURCLEAR, wxID_PEAKSMOVE, wxID_PEAKSCLEAR, 
58    wxID_CHARGEFLIP, wxID_PEAKSUNIQUE, wxID_PEAKSDELETE, wxID_PEAKSDA,
59    wxID_PEAKSDISTVP, wxID_PEAKSVIEWPT, wxID_FINDEQVPEAKS,wxID_SHOWBONDS,wxID_MULTIMCSA,
60    wxID_SINGLEMCSA,wxID_4DCHARGEFLIP,wxID_TRANSFORMSTRUCTURE,
61] = [wx.NewId() for item in range(17)]
62
63[ wxID_PWDRADD, wxID_HKLFADD, wxID_PWDANALYSIS, wxID_PWDCOPY, wxID_PLOTCTRLCOPY, 
64    wxID_DATADELETE,wxID_DATACOPY,wxID_DATACOPYFLAGS,wxID_DATASELCOPY,wxID_DATAUSE,
65] = [wx.NewId() for item in range(10)]
66
67[ wxID_ATOMSEDITADD, wxID_ATOMSEDITINSERT, wxID_ATOMSEDITDELETE, wxID_ATOMSREFINE, 
68    wxID_ATOMSMODIFY, wxID_ATOMSTRANSFORM, wxID_ATOMSVIEWADD, wxID_ATOMVIEWINSERT,
69    wxID_RELOADDRAWATOMS,wxID_ATOMSDISAGL,wxID_ATOMMOVE,wxID_MAKEMOLECULE,
70    wxID_ASSIGNATMS2RB,wxID_ATOMSPDISAGL, wxID_ISODISP,wxID_ADDHATOM,wxID_UPDATEHATOM,
71    wxID_WAVEVARY,wxID_ATOMSROTATE, wxID_ATOMSDENSITY,
72] = [wx.NewId() for item in range(20)]
73
74[ wxID_DRAWATOMSTYLE, wxID_DRAWATOMLABEL, wxID_DRAWATOMCOLOR, wxID_DRAWATOMRESETCOLOR, 
75    wxID_DRAWVIEWPOINT, wxID_DRAWTRANSFORM, wxID_DRAWDELETE, wxID_DRAWFILLCELL, 
76    wxID_DRAWADDEQUIV, wxID_DRAWFILLCOORD, wxID_DRAWDISAGLTOR,  wxID_DRAWPLANE,
77    wxID_DRAWDISTVP, wxID_DRAWADDSPHERE,
78] = [wx.NewId() for item in range(14)]
79
80[ wxID_DRAWRESTRBOND, wxID_DRAWRESTRANGLE, wxID_DRAWRESTRPLANE, wxID_DRAWRESTRCHIRAL,
81] = [wx.NewId() for item in range(4)]
82
83[ wxID_ADDMCSAATOM,wxID_ADDMCSARB,wxID_CLEARMCSARB,wxID_MOVEMCSA,wxID_MCSACLEARRESULTS,
84] = [wx.NewId() for item in range(5)]
85
86[ wxID_CLEARTEXTURE,wxID_REFINETEXTURE,
87] = [wx.NewId() for item in range(2)]
88
89[ wxID_LOADDIFFAX,wxID_LAYERSIMULATE,wxID_SEQUENCESIMULATE, wxID_COPYPHASE,
90] = [wx.NewId() for item in range(4)]
91
92[ wxID_PAWLEYLOAD, wxID_PAWLEYESTIMATE, wxID_PAWLEYUPDATE,
93] = [wx.NewId() for item in range(3)]
94
95[ wxID_IMCALIBRATE,wxID_IMRECALIBRATE,wxID_IMINTEGRATE, wxID_IMCLEARCALIB,wxID_IMRECALIBALL, 
96    wxID_IMCOPYCONTROLS, wxID_INTEGRATEALL, wxID_IMSAVECONTROLS, wxID_IMLOADCONTROLS, wxID_IMAUTOINTEG,
97    wxID_IMCOPYSELECTED, wxID_SAVESELECTEDCONTROLS,
98] = [wx.NewId() for item in range(12)]
99
100[ wxID_MASKCOPY, wxID_MASKSAVE, wxID_MASKLOAD, wxID_NEWMASKSPOT,wxID_NEWMASKARC,wxID_NEWMASKRING,
101    wxID_NEWMASKFRAME, wxID_NEWMASKPOLY,  wxID_MASKLOADNOT,
102] = [wx.NewId() for item in range(9)]
103
104[ wxID_STRSTACOPY, wxID_STRSTAFIT, wxID_STRSTASAVE, wxID_STRSTALOAD,wxID_STRSTSAMPLE,
105    wxID_APPENDDZERO,wxID_STRSTAALLFIT,wxID_UPDATEDZERO,wxID_STRSTAPLOT,
106] = [wx.NewId() for item in range(9)]
107
108[ wxID_BACKCOPY,wxID_LIMITCOPY, wxID_SAMPLECOPY, wxID_SAMPLECOPYSOME, wxID_BACKFLAGCOPY, wxID_SAMPLEFLAGCOPY,
109    wxID_SAMPLESAVE, wxID_SAMPLELOAD,wxID_ADDEXCLREGION,wxID_SETSCALE,wxID_SAMPLE1VAL,wxID_ALLSAMPLELOAD,
110    wxID_MAKEBACKRDF,
111] = [wx.NewId() for item in range(13)]
112
113[ wxID_INSTPRMRESET,wxID_CHANGEWAVETYPE,wxID_INSTCOPY, wxID_INSTFLAGCOPY, wxID_INSTLOAD,
114    wxID_INSTSAVE, wxID_INST1VAL, wxID_INSTCALIB,wxID_INSTSAVEALL,
115] = [wx.NewId() for item in range(9)]
116
117[ wxID_UNDO,wxID_LSQPEAKFIT,wxID_LSQONECYCLE,wxID_RESETSIGGAM,wxID_CLEARPEAKS,wxID_AUTOSEARCH,
118    wxID_PEAKSCOPY, wxID_SEQPEAKFIT,
119] = [wx.NewId() for item in range(8)]
120
121[  wxID_INDXRELOAD, wxID_INDEXPEAKS, wxID_REFINECELL, wxID_COPYCELL, wxID_MAKENEWPHASE,
122    wxID_EXPORTCELLS,
123] = [wx.NewId() for item in range(6)]
124
125[ wxID_CONSTRAINTADD,wxID_EQUIVADD,wxID_HOLDADD,wxID_FUNCTADD,wxID_ADDRIDING,
126  wxID_CONSPHASE, wxID_CONSHIST, wxID_CONSHAP, wxID_CONSGLOBAL,wxID_EQUIVALANCEATOMS,
127] = [wx.NewId() for item in range(10)]
128
129[ wxID_RESTRAINTADD, wxID_RESTSELPHASE,wxID_RESTDELETE, wxID_RESRCHANGEVAL, 
130    wxID_RESTCHANGEESD,wxID_AARESTRAINTADD,wxID_AARESTRAINTPLOT,
131] = [wx.NewId() for item in range(7)]
132
133[ wxID_RIGIDBODYADD,wxID_DRAWDEFINERB,wxID_RIGIDBODYIMPORT,wxID_RESIDUETORSSEQ,
134    wxID_AUTOFINDRESRB,wxID_GLOBALRESREFINE,wxID_RBREMOVEALL,wxID_COPYRBPARMS,
135    wxID_GLOBALTHERM,wxID_VECTORBODYADD
136] = [wx.NewId() for item in range(10)]
137
138[ wxID_RENAMESEQSEL,wxID_SAVESEQSEL,wxID_SAVESEQSELCSV,wxID_SAVESEQCSV,wxID_PLOTSEQSEL,
139  wxID_ORGSEQSEL,wxADDSEQVAR,wxDELSEQVAR,wxEDITSEQVAR,wxCOPYPARFIT,wxID_AVESEQSEL,
140  wxADDPARFIT,wxDELPARFIT,wxEDITPARFIT,wxDOPARFIT,wxADDSEQDIST,wxADDSEQANGLE
141] = [wx.NewId() for item in range(17)]
142
143[ wxID_MODELCOPY,wxID_MODELFIT,wxID_MODELADD,wxID_ELEMENTADD,wxID_ELEMENTDELETE,
144    wxID_ADDSUBSTANCE,wxID_LOADSUBSTANCE,wxID_DELETESUBSTANCE,wxID_COPYSUBSTANCE,
145    wxID_MODELUNDO,wxID_MODELFITALL,wxID_MODELCOPYFLAGS,
146] = [wx.NewId() for item in range(12)]
147
148[ wxID_SELECTPHASE,wxID_PWDHKLPLOT,wxID_PWD3DHKLPLOT,wxID_3DALLHKLPLOT,wxID_MERGEHKL,
149] = [wx.NewId() for item in range(5)]
150
151[ wxID_PDFCOPYCONTROLS, wxID_PDFSAVECONTROLS, wxID_PDFLOADCONTROLS, 
152    wxID_PDFCOMPUTE, wxID_PDFCOMPUTEALL, wxID_PDFADDELEMENT, wxID_PDFDELELEMENT,
153] = [wx.NewId() for item in range(7)]
154
155[ wxID_MCRON,wxID_MCRLIST,wxID_MCRSAVE,wxID_MCRPLAY,
156] = [wx.NewId() for item in range(4)]
157
158VERY_LIGHT_GREY = wx.Colour(235,235,235)
159
160commonTrans = {'abc':np.eye(3),'a-cb':np.array([[1,0,0],[0,0,-1],[0,1,0]]),
161    'ba-c':np.array([[0,1,0],[1,0,0],[0,0,-1]]),'-cba':np.array([[0,0,-1],[0,1,0],[1,0,0]]),
162    'bca':np.array([[0,1,0],[0,0,1],[1,0,0]]),'cab':np.array([[0,0,1],[1,0,0],[0,1,0]]),
163    'P->R':np.array([[1,-1,0],[0,1,-1],[1,1,1]]),'R->P':np.array([[2./3,1./3,1./3],[-1./3,1./3,1./3],[-1./3,-2./3,1./3]]),
164    'P->A':np.array([[-1,0,0],[0,-1,1],[0,1,1]]),'R->O':np.array([[-1,0,0],[0,-1,0],[0,0,1]]),
165    'P->B':np.array([[-1,0,1],[0,-1,0],[1,0,1]]),'B->P':np.array([[-.5,0,.5],[0,-1,0],[.5,0,.5]]),
166    'P->C':np.array([[1,1,0],[1,-1,0],[0,0,-1]]),'C->P':np.array([[.5,.5,0],[.5,-.5,0],[0,0,-1]]),
167    'P->F':np.array([[-1,1,1],[1,-1,1],[1,1,-1]]),'F->P':np.array([[0,.5,.5],[.5,0,.5],[.5,.5,0]]),   
168    'P->I':np.array([[0,1,1],[1,0,1],[1,1,0]]),'I->P':np.array([[-.5,.5,.5],[.5,-.5,.5],[.5,.5,-.5]]),   
169    'A->P':np.array([[-1,0,0],[0,-.5,.5],[0,.5,.5]]),'O->R':np.array([[-1,0,0],[0,-1,0],[0,0,1]]), 
170    'abc*':np.eye(3), }
171commonNames = ['abc','bca','cab','a-cb','ba-c','-cba','P->A','A->P','P->B','B->P','P->C','C->P',
172    'P->I','I->P','P->F','F->P','P->R','R->P','R->O','O->R','abc*',]
173
174# Should SGMessageBox, SymOpDialog, DisAglDialog be moved?
175
176################################################################################
177#### GSAS-II class definitions
178################################################################################
179
180class SGMessageBox(wx.Dialog):
181    ''' Special version of MessageBox that displays space group & super space group text
182    in two blocks
183    '''
184    def __init__(self,parent,title,text,table,):
185        wx.Dialog.__init__(self,parent,wx.ID_ANY,title,pos=wx.DefaultPosition,
186            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
187        self.text = text
188        self.table = table
189        self.panel = wx.Panel(self)
190        mainSizer = wx.BoxSizer(wx.VERTICAL)
191        mainSizer.Add((0,10))
192        for line in text:
193            mainSizer.Add(wx.StaticText(self.panel,label='     %s     '%(line)),0,WACV)
194        ncol = self.table[0].count(',')+1
195        tableSizer = wx.FlexGridSizer(0,2*ncol+3,0,0)
196        for j,item in enumerate(self.table):
197            num,flds = item.split(')')
198            tableSizer.Add(wx.StaticText(self.panel,label='     %s  '%(num+')')),0,WACV|wx.ALIGN_LEFT)           
199            flds = flds.replace(' ','').split(',')
200            for i,fld in enumerate(flds):
201                if i < ncol-1:
202                    tableSizer.Add(wx.StaticText(self.panel,label='%s, '%(fld)),0,WACV|wx.ALIGN_RIGHT)
203                else:
204                    tableSizer.Add(wx.StaticText(self.panel,label='%s'%(fld)),0,WACV|wx.ALIGN_RIGHT)
205            if not j%2:
206                tableSizer.Add((20,0))
207        mainSizer.Add(tableSizer,0,wx.ALIGN_LEFT)
208        btnsizer = wx.StdDialogButtonSizer()
209        OKbtn = wx.Button(self.panel, wx.ID_OK)
210        OKbtn.SetDefault()
211        btnsizer.AddButton(OKbtn)
212        btnsizer.Realize()
213        mainSizer.Add((0,10))
214        mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER)
215        self.panel.SetSizer(mainSizer)
216        self.panel.Fit()
217        self.Fit()
218        size = self.GetSize()
219        self.SetSize([size[0]+20,size[1]])
220
221    def Show(self):
222        '''Use this method after creating the dialog to post it
223        '''
224        self.ShowModal()
225        return
226
227class SGMagSpinBox(wx.Dialog):
228    ''' Special version of MessageBox that displays magnetic spin text
229    '''
230    def __init__(self,parent,title,text,table,names,spins,):
231        wx.Dialog.__init__(self,parent,wx.ID_ANY,title,pos=wx.DefaultPosition,
232            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER,size=wx.Size(420,350))
233        self.text = text
234        self.table = table
235        self.names = names
236        self.spins = spins
237        self.panel = wxscroll.ScrolledPanel(self)
238        mainSizer = wx.BoxSizer(wx.VERTICAL)
239        mainSizer.Add((0,10))
240        first = text[0].split(':')[-1].strip()
241        cents = [0,]
242        if 'P' != first[0]:
243            cents = text[-1].split(';')
244        for line in text:
245            mainSizer.Add(wx.StaticText(self.panel,label='     %s     '%(line)),0,WACV)
246        try:
247            nops = self.names.index(' 1bar ')
248        except ValueError:
249            nops = len(self.names)
250        ncol = self.table[0].count(',')+2
251        for ic,cent in enumerate(cents):
252            if cent:
253                cent = cent.strip(' (').strip(')+\n') 
254                mainSizer.Add(wx.StaticText(self.panel,label=' for (%s)+'%(cent)),0,WACV)
255            tableSizer = wx.FlexGridSizer(0,2*ncol+3,0,0)
256            for j,item in enumerate(self.table):
257                flds = item.split(')')[1]
258                tableSizer.Add(wx.StaticText(self.panel,label='  (%2d)  '%(j+1)),0,WACV|wx.ALIGN_LEFT)           
259                flds = flds.replace(' ','').split(',')
260                for i,fld in enumerate(flds):
261                    if i < ncol-1:
262                        text = wx.StaticText(self.panel,label='%s, '%(fld))
263                        tableSizer.Add(text,0,WACV|wx.ALIGN_RIGHT)
264                    else:
265                        text = wx.StaticText(self.panel,label='%s '%(fld))
266                        tableSizer.Add(text,0,WACV|wx.ALIGN_RIGHT)
267                text = wx.StaticText(self.panel,label=' (%s) '%(self.names[j]))
268                if self.spins[j+ic*len(self.table)] < 0:
269                    text.SetForegroundColour('Red')
270                tableSizer.Add(text,0,WACV|wx.ALIGN_RIGHT)
271                if not j%2:
272                    tableSizer.Add((20,0))
273            mainSizer.Add(tableSizer,0,wx.ALIGN_CENTER)
274           
275        btnsizer = wx.StdDialogButtonSizer()
276        OKbtn = wx.Button(self.panel, wx.ID_OK)
277        OKbtn.SetDefault()
278        btnsizer.AddButton(OKbtn)
279        btnsizer.Realize()
280        mainSizer.Add((0,10))
281        mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER)
282        self.panel.SetSizer(mainSizer)
283        size = np.array(self.GetSize())
284        self.panel.SetupScrolling()
285        size = [size[0]-5,size[1]-20]       #this fiddling is needed for older wx!
286        self.panel.SetSize(size)
287        self.panel.SetAutoLayout(1)
288
289    def Show(self):
290        '''Use this method after creating the dialog to post it
291        '''
292        self.ShowModal()
293        return   
294
295################################################################################
296class SymOpDialog(wx.Dialog):
297    '''Class to select a symmetry operator
298    '''
299    def __init__(self,parent,SGData,New=True,ForceUnit=False):
300        wx.Dialog.__init__(self,parent,-1,'Select symmetry operator',
301            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
302        panel = wx.Panel(self)
303        self.SGData = SGData
304        self.New = New
305        self.Force = ForceUnit
306        self.OpSelected = [0,0,0,[0,0,0],False,False]
307        mainSizer = wx.BoxSizer(wx.VERTICAL)
308        if ForceUnit:
309            choice = ['No','Yes']
310            self.force = wx.RadioBox(panel,-1,'Force to unit cell?',choices=choice)
311            self.force.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
312            mainSizer.Add(self.force,0,WACV|wx.TOP,5)
313#        if SGData['SGInv']:
314        choice = ['No','Yes']
315        self.inv = wx.RadioBox(panel,-1,'Choose inversion?',choices=choice)
316        self.inv.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
317        mainSizer.Add(self.inv,0,WACV)
318        if SGData['SGLatt'] != 'P':
319            LattOp = G2spc.Latt2text(SGData['SGLatt']).split(';')
320            self.latt = wx.RadioBox(panel,-1,'Choose cell centering?',choices=LattOp)
321            self.latt.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
322            mainSizer.Add(self.latt,0,WACV)
323        if SGData['SGLaue'] in ['-1','2/m','mmm','4/m','4/mmm']:
324            Ncol = 2
325        else:
326            Ncol = 3
327        OpList = []
328        for Opr in SGData['SGOps']:
329            OpList.append(G2spc.MT2text(Opr))
330        self.oprs = wx.RadioBox(panel,-1,'Choose space group operator?',choices=OpList,
331            majorDimension=Ncol)
332        self.oprs.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
333        mainSizer.Add(self.oprs,0,WACV|wx.BOTTOM,5)
334        mainSizer.Add(wx.StaticText(panel,-1,"   Choose unit cell?"),0,WACV)
335        cellSizer = wx.BoxSizer(wx.HORIZONTAL)
336        cellName = ['X','Y','Z']
337        self.cell = []
338        for i in range(3):
339            self.cell.append(wx.SpinCtrl(panel,-1,cellName[i],size=wx.Size(50,20)))
340            self.cell[-1].SetRange(-3,3)
341            self.cell[-1].SetValue(0)
342            self.cell[-1].Bind(wx.EVT_SPINCTRL, self.OnOpSelect)
343            cellSizer.Add(self.cell[-1],0,WACV)
344        mainSizer.Add(cellSizer,0,WACV|wx.BOTTOM,5)
345        if self.New:
346            choice = ['No','Yes']
347            self.new = wx.RadioBox(panel,-1,'Generate new positions?',choices=choice)
348            self.new.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
349            mainSizer.Add(self.new,0,WACV)
350
351        OkBtn = wx.Button(panel,-1,"Ok")
352        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
353        cancelBtn = wx.Button(panel,-1,"Cancel")
354        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
355        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
356        btnSizer.Add((20,20),1)
357        btnSizer.Add(OkBtn)
358        btnSizer.Add((20,20),1)
359        btnSizer.Add(cancelBtn)
360        btnSizer.Add((20,20),1)
361
362        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
363        panel.SetSizer(mainSizer)
364        panel.Fit()
365        self.Fit()
366
367    def OnOpSelect(self,event):
368#        if self.SGData['SGInv']:
369        self.OpSelected[0] = self.inv.GetSelection()
370        if self.SGData['SGLatt'] != 'P':
371            self.OpSelected[1] = self.latt.GetSelection()
372        self.OpSelected[2] = self.oprs.GetSelection()
373        for i in range(3):
374            self.OpSelected[3][i] = float(self.cell[i].GetValue())
375        if self.New:
376            self.OpSelected[4] = self.new.GetSelection()
377        if self.Force:
378            self.OpSelected[5] = self.force.GetSelection()
379
380    def GetSelection(self):
381        return self.OpSelected
382
383    def OnOk(self,event):
384        parent = self.GetParent()
385        parent.Raise()
386        self.EndModal(wx.ID_OK)
387
388    def OnCancel(self,event):
389        parent = self.GetParent()
390        parent.Raise()
391        self.EndModal(wx.ID_CANCEL)
392       
393################################################################################
394class SphereEnclosure(wx.Dialog):
395    ''' Add atoms within sphere of enclosure to drawing
396   
397    :param wx.Frame parent: reference to parent frame (or None)
398    :param general: general data (includes drawing data)
399    :param atoms: drawing atoms data
400    :param indx: list of selected atoms (may be empty)
401   
402    '''
403    def __init__(self,parent,general,drawing,indx):
404        wx.Dialog.__init__(self,parent,wx.ID_ANY,'Setup phase transformation', 
405            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
406        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
407        self.General = general
408        self.Drawing = drawing
409        self.indx = indx
410        self.Sphere = 1.0
411        self.centers = []
412        self.atomTypes = [[item,True] for item in self.General['AtomTypes']]
413       
414        self.Draw()
415       
416    def Draw(self):
417       
418        def OnRadius(event):
419            event.Skip()
420            try:
421                val = float(radius.GetValue())
422                if val < 0.5:
423                    raise ValueError
424                self.Sphere = val
425            except ValueError:
426                pass
427            radius.SetValue('%.3f'%(self.Sphere))
428           
429        def OnAtomType(event):
430            Obj = event.GetEventObject()
431            id = Ind[Obj.GetId()]
432            self.atomTypes[id][1] = Obj.GetValue()
433       
434        self.panel.Destroy()
435        self.panel = wx.Panel(self)
436        mainSizer = wx.BoxSizer(wx.VERTICAL)
437        mainSizer.Add(wx.StaticText(self.panel,label=' Sphere of enclosure controls:'),0,WACV)
438        topSizer = wx.BoxSizer(wx.HORIZONTAL)
439        atoms = []
440        if len(self.indx):
441            topSizer.Add(wx.StaticText(self.panel,label=' Sphere centered at atoms: '),0,WACV)
442            cx,ct,cs = self.Drawing['atomPtrs'][:3]
443#            print self.Drawing.keys()
444            for id in self.indx:
445                atom = self.Drawing['Atoms'][id]
446                self.centers.append(atom[cx:cx+3])
447                atoms.append('%s(%s)'%(atom[ct-1],atom[cs-1]))
448            topSizer.Add(wx.ComboBox(self.panel,choices=atoms,value=atoms[0],
449                style=wx.CB_READONLY|wx.CB_DROPDOWN),0,WACV)
450        else:
451            topSizer.Add(wx.StaticText(self.panel,label=' Sphere centered at drawing view point'),0,WACV)
452            self.centers.append(self.Drawing['viewPoint'][0])
453        mainSizer.Add(topSizer,0,WACV)
454        sphereSizer = wx.BoxSizer(wx.HORIZONTAL)
455        sphereSizer.Add(wx.StaticText(self.panel,label=' Sphere radius: '),0,WACV)
456        radius = wx.TextCtrl(self.panel,value='%.3f'%(self.Sphere),style=wx.TE_PROCESS_ENTER)
457        radius.Bind(wx.EVT_TEXT_ENTER,OnRadius)
458        radius.Bind(wx.EVT_KILL_FOCUS,OnRadius)
459        sphereSizer.Add(radius,0,WACV)
460        mainSizer.Add(sphereSizer,0,WACV)
461        mainSizer.Add(wx.StaticText(self.panel,label=' Target selected atoms:'),0,WACV)
462        atSizer = wx.BoxSizer(wx.HORIZONTAL)
463        Ind = {}
464        for i,item in enumerate(self.atomTypes):
465            atm = wx.CheckBox(self.panel,label=item[0])
466            atm.SetValue(item[1])
467            atm.Bind(wx.EVT_CHECKBOX, OnAtomType)
468            Ind[atm.GetId()] = i
469            atSizer.Add(atm,0,WACV)
470        mainSizer.Add(atSizer,0,WACV)
471       
472        OkBtn = wx.Button(self.panel,-1,"Ok")
473        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
474        cancelBtn = wx.Button(self.panel,-1,"Cancel")
475        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
476        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
477        btnSizer.Add((20,20),1)
478        btnSizer.Add(OkBtn)
479        btnSizer.Add((20,20),1)
480        btnSizer.Add(cancelBtn)
481        btnSizer.Add((20,20),1)
482       
483        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
484        self.panel.SetSizer(mainSizer)
485        self.panel.Fit()
486        self.Fit()
487       
488    def GetSelection(self):
489        used = []
490        for atm in self.atomTypes:
491            if atm[1]:
492                used.append(str(atm[0]))
493        return self.centers,self.Sphere,used
494
495    def OnOk(self,event):
496        parent = self.GetParent()
497        parent.Raise()
498        self.EndModal(wx.ID_OK)
499
500    def OnCancel(self,event):
501        parent = self.GetParent()
502        parent.Raise()
503        self.EndModal(wx.ID_CANCEL)
504       
505################################################################################
506class TransformDialog(wx.Dialog):
507    ''' Phase transformation
508   
509    :param wx.Frame parent: reference to parent frame (or None)
510    :param phase: phase data
511   
512    #NB: commonNames & commonTrans defined at top of this file
513    '''
514    def __init__(self,parent,phase):
515        wx.Dialog.__init__(self,parent,wx.ID_ANY,'Setup phase transformation', 
516            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
517        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
518        self.Phase = copy.deepcopy(phase)   #will be a new phase!
519#        self.Super = phase['General']['Super']
520#        if self.Super:
521#            self.Trans = np.eye(4)
522#            self.Vec = np.zeros(4)
523#        else:
524        self.Trans = np.eye(3)
525        self.Vec = np.zeros(3)
526        self.oldSpGrp = phase['General']['SGData']['SpGrp']
527        self.oldSGdata = phase['General']['SGData']
528        self.newSpGrp = self.Phase['General']['SGData']['SpGrp']
529        self.oldCell = phase['General']['Cell'][1:8]
530        self.newCell = self.Phase['General']['Cell'][1:8]
531        self.Common = 'abc'
532        self.ifMag = False
533        self.ifConstr = True
534        self.Draw()
535
536    def Draw(self):
537               
538        def OnMatValue(event):
539            event.Skip()
540            Obj = event.GetEventObject()
541            ix,iy = Ind[Obj.GetId()]
542            val = Obj.GetValue()
543            try:
544                if '/' in val:
545                    vals = val.split('/')
546                    self.Trans[iy,ix] = float(vals[0])/float(vals[1])
547                else:   
548                    self.Trans[iy,ix] = float(Obj.GetValue())
549            except ValueError:
550                pass
551            Obj.SetValue('%5.3f'%(self.Trans[iy,ix]))
552           
553        def OnVecValue(event):
554            event.Skip()
555            Obj = event.GetEventObject()
556            iy = Ind[Obj.GetId()]
557            val = Obj.GetValue()
558            try:
559                if '/' in val:
560                    vals = val.split('/')
561                    self.Vec[iy] = float(vals[0])/float(vals[1])
562                else:   
563                    self.Vec[iy] = float(Obj.GetValue())
564            except ValueError:
565                pass
566            Obj.SetValue('%5.3f'%(self.Vec[iy]))
567               
568        def OnCommon(event):
569            Obj = event.GetEventObject()
570            self.Common = Obj.GetValue()
571            if '*' in self.Common:
572                A,B = G2lat.cell2AB(self.oldCell[:6])
573                self.newCell[2:5] = [A[2,2],90.,90.]
574                a,b = G2lat.cell2AB(self.newCell[:6])
575                self.Trans = np.inner(a.T,B)    #correct!
576                self.newSpGrp = 'P 1'
577                SGErr,SGData = G2spc.SpcGroup(self.newSpGrp)
578                self.Phase['General']['SGData'] = SGData
579            else:
580                self.Trans = commonTrans[self.Common]
581            OnTest(event)
582       
583        def OnSpaceGroup(event):
584            event.Skip()
585            Flds = SGTxt.GetValue().split()
586            #get rid of extra spaces between fields first
587            for fld in Flds: fld = fld.strip()
588            SpcGp = ' '.join(Flds)
589            if SpcGp == self.newSpGrp: #didn't change it!
590                return
591            # try a lookup on the user-supplied name
592            SpGrpNorm = G2spc.StandardizeSpcName(SpcGp)
593            if SpGrpNorm:
594                SGErr,SGData = G2spc.SpcGroup(SpGrpNorm)
595            else:
596                SGErr,SGData = G2spc.SpcGroup(SpcGp)
597            if SGErr:
598                text = [G2spc.SGErrors(SGErr)+'\nSpace Group set to previous']
599                SGTxt.SetValue(self.newSpGrp)
600                msg = 'Space Group Error'
601                Style = wx.ICON_EXCLAMATION
602                Text = '\n'.join(text)
603                wx.MessageBox(Text,caption=msg,style=Style)
604            else:
605                text,table = G2spc.SGPrint(SGData)
606                self.Phase['General']['SGData'] = SGData
607                self.newSpGrp = SpcGp
608                SGTxt.SetValue(self.Phase['General']['SGData']['SpGrp'])
609                msg = 'Space Group Information'
610                SGMessageBox(self.panel,msg,text,table).Show()
611            if self.Phase['General']['Type'] == 'magnetic':
612                Nops = len(SGData['SGOps'])*len(SGData['SGCen'])
613                if SGData['SGInv']:
614                    Nops *= 2
615                SGData['SpnFlp'] = Nops*[1,]
616#            if self.Phase['General']['Type'] in ['modulated',]:
617#                self.Phase['General']['SuperSg'] = SetDefaultSSsymbol()
618#                self.Phase['General']['SSGData'] = G2spc.SSpcGroup(generalData['SGData'],generalData['SuperSg'])[1]
619
620        def OnTest(event):
621            self.newCell = G2lat.TransformCell(self.oldCell[:6],self.Trans)
622            wx.CallAfter(self.Draw)
623           
624        def OnMag(event):
625            self.ifMag = mag.GetValue()
626           
627        def OnConstr(event):
628            self.ifConstr = constr.GetValue()
629
630        self.panel.Destroy()
631        self.panel = wx.Panel(self)
632        Ind = {}
633        mainSizer = wx.BoxSizer(wx.VERTICAL)
634        MatSizer = wx.BoxSizer(wx.HORIZONTAL)
635        transSizer = wx.BoxSizer(wx.VERTICAL)
636        transSizer.Add(wx.StaticText(self.panel,label=" XYZ Transformation matrix & vector: M*X+V = X'"))
637#        if self.Super:
638#            Trmat = wx.FlexGridSizer(4,4,0,0)
639#        else:
640        commonSizer = wx.BoxSizer(wx.HORIZONTAL)
641        commonSizer.Add(wx.StaticText(self.panel,label=' Common transformations: '),0,WACV)
642        common = wx.ComboBox(self.panel,value=self.Common,choices=commonNames,
643            style=wx.CB_READONLY|wx.CB_DROPDOWN)
644        common.Bind(wx.EVT_COMBOBOX,OnCommon)
645        commonSizer.Add(common,0,WACV)
646        transSizer.Add(commonSizer)
647        Trmat = wx.FlexGridSizer(3,5,0,0)
648        for iy,line in enumerate(self.Trans):
649            for ix,val in enumerate(line):
650                item = wx.TextCtrl(self.panel,value='%5.3f'%(val),
651                    size=(50,25),style=wx.TE_PROCESS_ENTER)
652                Ind[item.GetId()] = [ix,iy]
653                item.Bind(wx.EVT_TEXT_ENTER,OnMatValue)
654                item.Bind(wx.EVT_KILL_FOCUS,OnMatValue)
655                Trmat.Add(item)
656            Trmat.Add((25,0),0)
657            vec = wx.TextCtrl(self.panel,value='%5.3f'%(self.Vec[iy]),
658                    size=(50,25),style=wx.TE_PROCESS_ENTER)
659            Ind[vec.GetId()] = [iy]       
660            vec.Bind(wx.EVT_TEXT_ENTER,OnVecValue)
661            vec.Bind(wx.EVT_KILL_FOCUS,OnVecValue)
662            Trmat.Add(vec)
663        transSizer.Add(Trmat)
664        MatSizer.Add((10,0),0)
665        MatSizer.Add(transSizer)
666        mainSizer.Add(MatSizer)
667        mainSizer.Add(wx.StaticText(self.panel,label=' Old lattice parameters:'),0,WACV)
668        mainSizer.Add(wx.StaticText(self.panel,label=
669            ' a = %.5f       b = %.5f      c = %.5f'%(self.oldCell[0],self.oldCell[1],self.oldCell[2])),0,WACV)
670        mainSizer.Add(wx.StaticText(self.panel,label=' alpha = %.3f beta = %.3f gamma = %.3f'%
671            (self.oldCell[3],self.oldCell[4],self.oldCell[5])),0,WACV)
672        mainSizer.Add(wx.StaticText(self.panel,label=' volume = %.3f'%(self.oldCell[6])),0,WACV)
673        mainSizer.Add(wx.StaticText(self.panel,label=' New lattice parameters:'),0,WACV)
674        mainSizer.Add(wx.StaticText(self.panel,label=
675            ' a = %.5f       b = %.5f      c = %.5f'%(self.newCell[0],self.newCell[1],self.newCell[2])),0,WACV)
676        mainSizer.Add(wx.StaticText(self.panel,label=' alpha = %.3f beta = %.3f gamma = %.3f'%
677            (self.newCell[3],self.newCell[4],self.newCell[5])),0,WACV)
678        mainSizer.Add(wx.StaticText(self.panel,label=' volume = %.3f'%(self.newCell[6])),0,WACV)
679        sgSizer = wx.BoxSizer(wx.HORIZONTAL)
680        sgSizer.Add(wx.StaticText(self.panel,label='  Space group: '),0,WACV)
681        SGTxt = wx.TextCtrl(self.panel,value=self.newSpGrp,style=wx.TE_PROCESS_ENTER)
682        SGTxt.Bind(wx.EVT_TEXT_ENTER,OnSpaceGroup)
683        SGTxt.Bind(wx.EVT_KILL_FOCUS,OnSpaceGroup)
684        sgSizer.Add(SGTxt,0,WACV)
685        mainSizer.Add(sgSizer,0,WACV)
686        if 'magnetic' not in self.Phase['General']['Type']:
687            mag = wx.CheckBox(self.panel,label=' Make new phase magnetic?')
688            mag.Bind(wx.EVT_CHECKBOX,OnMag)
689            mainSizer.Add(mag,0,WACV)
690            mainSizer.Add(wx.StaticText(self.panel, \
691                label=' NB: Nonmagnetic atoms will be deleted from new phase'),0,WACV)
692            constr = wx.CheckBox(self.panel,label=' Make constraints between phases?')
693            constr.SetValue(self.ifConstr)
694            constr.Bind(wx.EVT_CHECKBOX,OnConstr)
695            mainSizer.Add(constr,0,WACV)
696
697        TestBtn = wx.Button(self.panel,-1,"Test")
698        TestBtn.Bind(wx.EVT_BUTTON, OnTest)
699        OkBtn = wx.Button(self.panel,-1,"Ok")
700        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
701        cancelBtn = wx.Button(self.panel,-1,"Cancel")
702        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
703        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
704        btnSizer.Add((20,20),1)
705        btnSizer.Add(TestBtn)
706        btnSizer.Add((20,20),1)
707        btnSizer.Add(OkBtn)
708        btnSizer.Add((20,20),1)
709        btnSizer.Add(cancelBtn)
710        btnSizer.Add((20,20),1)
711       
712        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
713        self.panel.SetSizer(mainSizer)
714        self.panel.Fit()
715        self.Fit()
716       
717    def GetSelection(self):
718        self.Phase['General']['Name'] += ' %s'%(self.Common)
719        self.Phase['General']['Cell'][1:] = G2lat.TransformCell(self.oldCell[:6],self.Trans)           
720        return self.Phase,self.Trans,self.Vec,self.ifMag,self.ifConstr
721
722    def OnOk(self,event):
723        parent = self.GetParent()
724        parent.Raise()
725        self.EndModal(wx.ID_OK)
726
727    def OnCancel(self,event):
728        parent = self.GetParent()
729        parent.Raise()
730        self.EndModal(wx.ID_CANCEL)
731       
732################################################################################
733class RotationDialog(wx.Dialog):
734    ''' Get Rotate & translate matrix & vector - currently not used
735    needs rethinking - possible use to rotate a group of atoms about some
736    vector/origin + translation
737   
738    '''
739    def __init__(self,parent):
740        wx.Dialog.__init__(self,parent,wx.ID_ANY,'Atom group rotation/translation', 
741            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
742        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
743        self.Trans = np.eye(3)
744        self.Vec = np.zeros(3)
745        self.rotAngle = 0.
746        self.rotVec = np.array([0.,0.,1.])
747        self.Expand = ''
748        self.Draw()
749
750    def Draw(self):
751
752        def OnMatValue(event):
753            event.Skip()
754            Obj = event.GetEventObject()
755            ix,iy = Ind[Obj.GetId()]
756            val = Obj.GetValue()
757            if '/' in val:
758                vals = val.split('/')
759                self.Trans[iy,ix] = float(vals[0])/float(vals[1])
760            else:   
761                self.Trans[iy,ix] = float(Obj.GetValue())
762            Obj.SetValue('%5.3f'%(self.Trans[iy,ix]))
763           
764           
765        def OnVecValue(event):
766            event.Skip()
767            Obj = event.GetEventObject()
768            iy = Ind[Obj.GetId()]
769            val = Obj.GetValue()
770            if '/' in val:
771                vals = val.split('/')
772                self.Vec[iy] = float(vals[0])/float(vals[1])
773            else:   
774                self.Vec[iy] = float(Obj.GetValue())
775            Obj.SetValue('%5.3f'%(self.Vec[iy]))
776           
777        def OnExpand(event):
778            self.Expand = expand.GetValue()
779           
780        def OnRotAngle(event):
781            event.Skip()
782            self.rotAngle = float(rotangle.GetValue())
783            rotangle.SetValue('%5.3f'%(self.rotAngle))
784            Q = G2mth.AVdeg2Q(self.rotAngle,self.rotVec)
785            self.Trans = G2mth.Q2Mat(Q)
786            self.Draw()
787           
788        def OnRotVec(event):
789            event.Skip()
790            vals = rotvec.GetValue()
791            vals = vals.split()
792            self.rotVec = np.array([float(val) for val in vals])
793            rotvec.SetValue('%5.3f %5.3f %5.3f'%(self.rotVec[0],self.rotVec[1],self.rotVec[2]))
794            Q = G2mth.AVdeg2Q(self.rotAngle,self.rotVec)
795            self.Trans = G2mth.Q2Mat(Q)
796            self.Draw()
797           
798        self.panel.Destroy()
799        self.panel = wx.Panel(self)
800        Ind = {}
801        mainSizer = wx.BoxSizer(wx.VERTICAL)
802        MatSizer = wx.BoxSizer(wx.HORIZONTAL)
803        transSizer = wx.BoxSizer(wx.VERTICAL)
804        transSizer.Add(wx.StaticText(self.panel,label=" XYZ Transformation matrix && vector: "+ \
805            "\n B*M*A*(X-V)+V = X'\n A,B: Cartesian transformation matrices"))
806        Trmat = wx.FlexGridSizer(3,5,0,0)
807        for iy,line in enumerate(self.Trans):
808            for ix,val in enumerate(line):
809                item = wx.TextCtrl(self.panel,value='%5.3f'%(val),
810                    size=(50,25),style=wx.TE_PROCESS_ENTER)
811                Ind[item.GetId()] = [ix,iy]
812                item.Bind(wx.EVT_TEXT_ENTER,OnMatValue)
813                item.Bind(wx.EVT_KILL_FOCUS,OnMatValue)
814                Trmat.Add(item)
815            Trmat.Add((25,0),0)
816            vec = wx.TextCtrl(self.panel,value='%5.3f'%(self.Vec[iy]),
817                    size=(50,25),style=wx.TE_PROCESS_ENTER)
818            Ind[vec.GetId()] = [iy]       
819            vec.Bind(wx.EVT_TEXT_ENTER,OnVecValue)
820            vec.Bind(wx.EVT_KILL_FOCUS,OnVecValue)
821            Trmat.Add(vec)
822        transSizer.Add(Trmat)
823        MatSizer.Add((10,0),0)
824        MatSizer.Add(transSizer)
825        mainSizer.Add(MatSizer)
826        rotationBox = wx.BoxSizer(wx.HORIZONTAL)
827        rotationBox.Add(wx.StaticText(self.panel,label=' Rotation angle: '),0,WACV)
828        rotangle = wx.TextCtrl(self.panel,value='%5.3f'%(self.rotAngle),
829            size=(50,25),style=wx.TE_PROCESS_ENTER)
830        rotangle.Bind(wx.EVT_TEXT_ENTER,OnRotAngle)
831        rotangle.Bind(wx.EVT_KILL_FOCUS,OnRotAngle)
832        rotationBox.Add(rotangle,0,WACV)
833        rotationBox.Add(wx.StaticText(self.panel,label=' about vector: '),0,WACV)
834        rotvec = wx.TextCtrl(self.panel,value='%5.3f %5.3f %5.3f'%(self.rotVec[0],self.rotVec[1],self.rotVec[2]),
835            size=(100,25),style=wx.TE_PROCESS_ENTER)
836        rotvec.Bind(wx.EVT_TEXT_ENTER,OnRotVec)
837        rotvec.Bind(wx.EVT_KILL_FOCUS,OnRotVec)
838        rotationBox.Add(rotvec,0,WACV)
839        mainSizer.Add(rotationBox,0,WACV)
840        expandChoice = ['','xy','xz','yz','xyz']
841        expandBox = wx.BoxSizer(wx.HORIZONTAL)
842        expandBox.Add(wx.StaticText(self.panel,label=' Expand -1 to +1 on: '),0,WACV)
843        expand = wx.ComboBox(self.panel,value=self.Expand,choices=expandChoice,
844            style=wx.CB_READONLY|wx.CB_DROPDOWN)
845        expand.Bind(wx.EVT_COMBOBOX,OnExpand)
846        expandBox.Add(expand,0,WACV)
847        expandBox.Add(wx.StaticText(self.panel,label=' and find unique atoms '),0,WACV)       
848        mainSizer.Add(expandBox)
849               
850        OkBtn = wx.Button(self.panel,-1,"Ok")
851        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
852        cancelBtn = wx.Button(self.panel,-1,"Cancel")
853        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
854        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
855        btnSizer.Add((20,20),1)
856        btnSizer.Add(OkBtn)
857        btnSizer.Add((20,20),1)
858        btnSizer.Add(cancelBtn)
859        btnSizer.Add((20,20),1)
860       
861        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
862        self.panel.SetSizer(mainSizer)
863        self.panel.Fit()
864        self.Fit()
865
866    def GetSelection(self):
867        return self.Trans,self.Vec,self.Expand
868
869    def OnOk(self,event):
870        parent = self.GetParent()
871        parent.Raise()
872        self.EndModal(wx.ID_OK)
873
874    def OnCancel(self,event):
875        parent = self.GetParent()
876        parent.Raise()
877        self.EndModal(wx.ID_CANCEL)   
878       
879################################################################################
880class DIFFaXcontrols(wx.Dialog):
881    ''' Solicit items needed to prepare DIFFaX control.dif file
882    '''
883    def __init__(self,parent,ctrls,parms=None):
884        wx.Dialog.__init__(self,parent,wx.ID_ANY,'DIFFaX controls', 
885            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
886        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
887        self.ctrls = ctrls
888        self.calcType = 'powder pattern'
889        self.plane = 'h0l'
890        self.planeChoice = ['h0l','0kl','hhl','h-hl',]
891        self.lmax = '2'
892        self.lmaxChoice = [str(i+1) for i in range(6)]
893        self.Parms = parms
894        self.Parm = None
895        if self.Parms != None:
896            self.Parm = self.Parms[0]
897        self.parmRange = [0.,1.]
898        self.parmStep = 2
899        self.Inst = 'Gaussian'
900        self.Draw()
901       
902    def Draw(self):
903       
904        def OnCalcType(event):
905            self.calcType = calcType.GetValue()
906            wx.CallAfter(self.Draw)
907           
908        def OnPlane(event):
909            self.plane = plane.GetValue()
910           
911        def OnMaxL(event):
912            self.lmax = lmax.GetValue()
913           
914        def OnParmSel(event):
915            self.Parm = parmsel.GetValue()
916           
917        def OnNumStep(event):
918            self.parmStep = int(numStep.GetValue())
919           
920        def OnParmRange(event):
921            event.Skip()
922            vals = parmrange.GetValue().split()
923            try:
924                vals = [float(vals[0]),float(vals[1])]
925            except ValueError:
926                vals = self.parmRange
927            parmrange.SetValue('%.3f %.3f'%(vals[0],vals[1]))
928            self.parmRange = vals
929           
930        def OnInstSel(event):
931            self.Inst = instsel.GetValue()
932       
933        self.panel.Destroy()
934        self.panel = wx.Panel(self)
935        mainSizer = wx.BoxSizer(wx.VERTICAL)
936        mainSizer.Add(wx.StaticText(self.panel,label=' Controls for DIFFaX'),0,WACV)
937        if self.Parms:
938            mainSizer.Add(wx.StaticText(self.panel,label=' Sequential powder pattern simulation'),0,WACV)
939        else:
940            calcChoice = ['powder pattern','selected area']
941            calcSizer = wx.BoxSizer(wx.HORIZONTAL)
942            calcSizer.Add(wx.StaticText(self.panel,label=' Select calculation type: '),0,WACV)
943            calcType = wx.ComboBox(self.panel,value=self.calcType,choices=calcChoice,
944                style=wx.CB_READONLY|wx.CB_DROPDOWN)
945            calcType.Bind(wx.EVT_COMBOBOX,OnCalcType)
946            calcSizer.Add(calcType,0,WACV)
947            mainSizer.Add(calcSizer)
948        if self.Parms:
949            parmSel = wx.BoxSizer(wx.HORIZONTAL)
950            parmSel.Add(wx.StaticText(self.panel,label=' Select parameter to vary: '),0,WACV)
951            parmsel = wx.ComboBox(self.panel,value=self.Parm,choices=self.Parms,
952                style=wx.CB_READONLY|wx.CB_DROPDOWN)
953            parmsel.Bind(wx.EVT_COMBOBOX,OnParmSel)
954            parmSel.Add(parmsel,0,WACV)
955            mainSizer.Add(parmSel)
956            mainSizer.Add(wx.StaticText(self.panel,label=' Enter parameter range & no. steps: '),0,WACV)
957            parmRange =  wx.BoxSizer(wx.HORIZONTAL)
958            numChoice = [str(i+1) for i in range(10)]
959            parmrange = wx.TextCtrl(self.panel,value='%.3f %.3f'%(self.parmRange[0],self.parmRange[1]),
960                style=wx.TE_PROCESS_ENTER)
961            parmrange.Bind(wx.EVT_TEXT_ENTER,OnParmRange)
962            parmrange.Bind(wx.EVT_KILL_FOCUS,OnParmRange)
963            parmRange.Add(parmrange,0,WACV)
964            numStep = wx.ComboBox(self.panel,value=str(self.parmStep),choices=numChoice,
965                style=wx.CB_READONLY|wx.CB_DROPDOWN)
966            numStep.Bind(wx.EVT_COMBOBOX,OnNumStep)
967            parmRange.Add(numStep,0,WACV)
968            mainSizer.Add(parmRange)           
969        if 'selected' in self.calcType:
970            planeSizer = wx.BoxSizer(wx.HORIZONTAL)
971            planeSizer.Add(wx.StaticText(self.panel,label=' Select plane: '),0,WACV)
972            plane = wx.ComboBox(self.panel,value=self.plane,choices=self.planeChoice,
973                style=wx.CB_READONLY|wx.CB_DROPDOWN)
974            plane.Bind(wx.EVT_COMBOBOX,OnPlane)
975            planeSizer.Add(plane,0,WACV)
976            planeSizer.Add(wx.StaticText(self.panel,label=' Max. l index: '),0,WACV)
977            lmax = wx.ComboBox(self.panel,value=self.lmax,choices=self.lmaxChoice,
978                style=wx.CB_READONLY|wx.CB_DROPDOWN)
979            lmax.Bind(wx.EVT_COMBOBOX,OnMaxL)
980            planeSizer.Add(lmax,0,WACV)           
981            mainSizer.Add(planeSizer)
982        else:
983            instChoice = ['None','Mean Gaussian','Gaussian',]
984            instSizer = wx.BoxSizer(wx.HORIZONTAL)
985            instSizer.Add(wx.StaticText(self.panel,label=' Select instrument broadening: '),0,WACV)
986            instsel = wx.ComboBox(self.panel,value=self.Inst,choices=instChoice,
987                style=wx.CB_READONLY|wx.CB_DROPDOWN)
988            instsel.Bind(wx.EVT_COMBOBOX,OnInstSel)
989            instSizer.Add(instsel,0,WACV)
990            mainSizer.Add(instSizer)
991        OkBtn = wx.Button(self.panel,-1,"Ok")
992        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
993        cancelBtn = wx.Button(self.panel,-1,"Cancel")
994        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
995        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
996        btnSizer.Add((20,20),1)
997        btnSizer.Add(OkBtn)
998        btnSizer.Add((20,20),1)
999        btnSizer.Add(cancelBtn)
1000        btnSizer.Add((20,20),1)
1001       
1002        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1003        self.panel.SetSizer(mainSizer)
1004        self.panel.Fit()
1005        self.Fit()
1006       
1007    def GetSelection(self):
1008        if 'powder' in self.calcType:
1009            return 'PWDR',self.Inst,self.Parm,self.parmRange,self.parmStep
1010        elif 'selected' in self.calcType:
1011            return 'SADP',self.plane,self.lmax
1012
1013    def OnOk(self,event):
1014        parent = self.GetParent()
1015        parent.Raise()
1016        self.EndModal(wx.ID_OK)
1017
1018    def OnCancel(self,event):
1019        parent = self.GetParent()
1020        parent.Raise()
1021        self.EndModal(wx.ID_CANCEL)
1022           
1023       
1024################################################################################
1025class MergeDialog(wx.Dialog):
1026    ''' HKL transformation & merge dialog
1027   
1028    :param wx.Frame parent: reference to parent frame (or None)
1029    :param data: HKLF data
1030   
1031    #NB: commonNames & commonTrans defined at top of this file     
1032    '''       
1033    def __init__(self,parent,data):
1034        wx.Dialog.__init__(self,parent,wx.ID_ANY,'Setup HKLF merge', 
1035            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1036        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
1037        self.data = data
1038        self.Super = data[1]['Super']
1039        if self.Super:
1040            self.Trans = np.eye(4)
1041        else:
1042            self.Trans = np.eye(3)
1043        self.Cent = 'noncentrosymmetric'
1044        self.Laue = '1'
1045        self.Class = 'triclinic'
1046        self.Common = 'abc'
1047        self.Draw()
1048       
1049    def Draw(self):
1050               
1051        def OnMatValue(event):
1052            event.Skip()
1053            Obj = event.GetEventObject()
1054            ix,iy = Ind[Obj.GetId()]
1055            self.Trans[ix,iy] = float(Obj.GetValue())
1056               
1057        def OnCent(event):
1058            Obj = event.GetEventObject()
1059            self.Cent = Obj.GetValue()
1060            self.Laue = ''
1061            wx.CallAfter(self.Draw)
1062           
1063        def OnLaue(event):
1064            Obj = event.GetEventObject()
1065            self.Laue = Obj.GetValue()
1066            wx.CallAfter(self.Draw)
1067           
1068        def OnClass(event):
1069            Obj = event.GetEventObject()
1070            self.Class = Obj.GetValue()
1071            self.Laue = ''
1072            wx.CallAfter(self.Draw)
1073           
1074        def OnCommon(event):
1075            Obj = event.GetEventObject()
1076            self.Common = Obj.GetValue()
1077            self.Trans = commonTrans[self.Common]
1078            wx.CallAfter(self.Draw)
1079       
1080        self.panel.Destroy()
1081        self.panel = wx.Panel(self)
1082        Ind = {}
1083        mainSizer = wx.BoxSizer(wx.VERTICAL)
1084        MatSizer = wx.BoxSizer(wx.HORIZONTAL)
1085        transSizer = wx.BoxSizer(wx.VERTICAL)
1086        transSizer.Add(wx.StaticText(self.panel,label=" HKL Transformation matrix: M*H = H'"))
1087        if self.Super:
1088            Trmat = wx.FlexGridSizer(4,4,0,0)
1089        else:
1090            commonSizer = wx.BoxSizer(wx.HORIZONTAL)
1091            commonSizer.Add(wx.StaticText(self.panel,label=' Common transformations: '),0,WACV)
1092            common = wx.ComboBox(self.panel,value=self.Common,choices=commonNames[:-1], #not the last one!
1093                style=wx.CB_READONLY|wx.CB_DROPDOWN)
1094            common.Bind(wx.EVT_COMBOBOX,OnCommon)
1095            commonSizer.Add(common,0,WACV)
1096            transSizer.Add(commonSizer)
1097            Trmat = wx.FlexGridSizer(3,3,0,0)
1098        for iy,line in enumerate(self.Trans):
1099            for ix,val in enumerate(line):
1100                item = wx.TextCtrl(self.panel,value='%5.3f'%(val),
1101                    size=(50,25),style=wx.TE_PROCESS_ENTER)
1102                Ind[item.GetId()] = [ix,iy]
1103                item.Bind(wx.EVT_TEXT_ENTER,OnMatValue)
1104                item.Bind(wx.EVT_KILL_FOCUS,OnMatValue)
1105                Trmat.Add(item)
1106        transSizer.Add(Trmat)
1107        MatSizer.Add((10,0),0)
1108        MatSizer.Add(transSizer)
1109        mainSizer.Add(MatSizer)
1110        laueClass = ['triclinic','monoclinic','orthorhombic','trigonal(H)','tetragonal','hexagonal','cubic']
1111        centroLaue = {'triclinic':['-1',],'monoclinic':['2/m','1 1 2/m','2/m 1 1',],
1112            'orthorhombic':['m m m',],'trigonal(H)':['-3','-3 m 1','-3 1 m',],    \
1113            'tetragonal':['4/m','4/m m m',],'hexagonal':['6/m','6/m m m',],'cubic':['m 3','m 3 m']}
1114        noncentroLaue = {'triclinic':['1',],'monoclinic':['2','2 1 1','1 1 2','m','m 1 1','1 1 m',],
1115            'orthorhombic':['2 2 2','m m 2','m 2 m','2 m m',],
1116            'trigonal(H)':['3','3 1 2','3 2 1','3 m 1','3 1 m',],
1117            'tetragonal':['4','-4','4 2 2','4 m m','-4 2 m','-4 m 2',], \
1118            'hexagonal':['6','-6','6 2 2','6 m m','-6 m 2','-6 2 m',],'cubic':['2 3','4 3 2','-4 3 m']}
1119        centChoice = ['noncentrosymmetric','centrosymmetric']
1120        mainSizer.Add(wx.StaticText(self.panel,label=' Select Laue class for new lattice:'),0,WACV)
1121        Class = wx.ComboBox(self.panel,value=self.Class,choices=laueClass,
1122            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1123        Class.Bind(wx.EVT_COMBOBOX,OnClass)
1124        mainSizer.Add(Class,0,WACV)
1125        mainSizer.Add(wx.StaticText(self.panel,label=' Target Laue symmetry:'),0,WACV)
1126        Cent = wx.ComboBox(self.panel,value=self.Cent,choices=centChoice,
1127            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1128        Cent.Bind(wx.EVT_COMBOBOX,OnCent)
1129        mergeSizer = wx.BoxSizer(wx.HORIZONTAL)
1130        mergeSizer.Add(Cent,0,WACV)
1131        mergeSizer.Add((10,0),0)
1132        Choice = centroLaue[self.Class]
1133        if 'non' in self.Cent:
1134            Choice = noncentroLaue[self.Class]
1135        Laue = wx.ComboBox(self.panel,value=self.Laue,choices=Choice,
1136            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1137        Laue.Bind(wx.EVT_COMBOBOX,OnLaue)
1138        mergeSizer.Add(Laue,0,WACV)
1139        mainSizer.Add(mergeSizer)
1140
1141        OkBtn = wx.Button(self.panel,-1,"Ok")
1142        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1143        cancelBtn = wx.Button(self.panel,-1,"Cancel")
1144        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1145        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1146        btnSizer.Add((20,20),1)
1147        if self.Laue:
1148            btnSizer.Add(OkBtn)
1149            btnSizer.Add((20,20),1)
1150        btnSizer.Add(cancelBtn)
1151        btnSizer.Add((20,20),1)
1152       
1153        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1154        self.panel.SetSizer(mainSizer)
1155        self.panel.Fit()
1156        self.Fit()
1157       
1158    def GetSelection(self):
1159        return self.Trans,self.Cent,self.Laue
1160
1161    def OnOk(self,event):
1162        parent = self.GetParent()
1163        parent.Raise()
1164        self.EndModal(wx.ID_OK)
1165
1166    def OnCancel(self,event):
1167        parent = self.GetParent()
1168        parent.Raise()
1169        self.EndModal(wx.ID_CANCEL)
1170
1171       
1172################################################################################
1173class AddHatomDialog(wx.Dialog):
1174    '''H atom addition dialog. After :meth:`ShowModal` returns, the results
1175    are found in dict :attr:`self.data`, which is accessed using :meth:`GetData`.
1176   
1177    :param wx.Frame parent: reference to parent frame (or None)
1178    :param dict Neigh: a dict of atom names with list of atom name, dist pairs for neighboring atoms
1179    :param dict phase: a dict containing the phase as defined by
1180      :ref:`Phase Tree Item <Phase_table>`   
1181    '''
1182    def __init__(self,parent,Neigh,phase):
1183        wx.Dialog.__init__(self,parent,wx.ID_ANY,'H atom add', 
1184            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1185        self.panel = wxscroll.ScrolledPanel(self)         #just a dummy - gets destroyed in Draw!
1186        self.Neigh = Neigh
1187        self.phase = phase
1188        self.Hatoms = []
1189        self.Draw(self.Neigh,self.phase)
1190           
1191    def Draw(self,Neigh,phase):
1192        '''Creates the contents of the dialog. Normally called
1193        by :meth:`__init__`.
1194        '''
1195        def OnHSelect(event):
1196            Obj = event.GetEventObject()
1197            item,i = Indx[Obj.GetId()]
1198            for obj in Indx[item]:
1199                obj.SetValue(False)
1200            Obj.SetValue(True)
1201            self.Neigh[item][2] = i
1202           
1203        def OnBond(event):
1204            Obj = event.GetEventObject()
1205            inei,ibond = Indx[Obj.GetId()]
1206            self.Neigh[inei][1][0][ibond][2] = Obj.GetValue()
1207           
1208        self.panel.Destroy()
1209        self.panel = wxscroll.ScrolledPanel(self,style = wx.DEFAULT_DIALOG_STYLE)
1210        mainSizer = wx.BoxSizer(wx.VERTICAL)
1211        mainSizer.Add(wx.StaticText(self.panel,-1,'H atom add controls for phase %s:'%(phase['General']['Name'])),
1212            0,wx.LEFT|wx.TOP,10)
1213        mainSizer.Add(wx.StaticText(self.panel,-1,'NB: Check selections as they may not be correct'),0,WACV|wx.LEFT,10)
1214        mainSizer.Add(wx.StaticText(self.panel,-1," Atom:  Add # H's          Use: Neighbors, dist"),0,wx.TOP|wx.LEFT,5)
1215        nHatms = ['0','1','2','3']
1216        dataSizer = wx.FlexGridSizer(0,3,0,0)
1217        Indx = {}
1218        for inei,neigh in enumerate(Neigh):
1219            dataSizer.Add(wx.StaticText(self.panel,-1,' %s:  '%(neigh[0])),0,WACV)
1220            nH = 1      #for O atom
1221            if 'C' in neigh[0] or 'N' in neigh[0]:
1222                nH = 4-len(neigh[1][0])
1223            checks = wx.BoxSizer(wx.HORIZONTAL)
1224            Ids = []
1225            for i in range(nH+1):
1226                nHs = wx.CheckBox(self.panel,-1,label=nHatms[i])
1227                if i == neigh[2]:
1228                    nHs.SetValue(True)
1229                Indx[nHs.GetId()] = [inei,i]
1230                Ids.append(nHs)
1231                nHs.Bind(wx.EVT_CHECKBOX, OnHSelect)
1232                checks.Add(nHs,0,WACV)
1233            Indx[inei] = Ids
1234            dataSizer.Add(checks,0,WACV)
1235            lineSizer = wx.BoxSizer(wx.HORIZONTAL)
1236            for ib,bond in enumerate(neigh[1][0]):
1237                Bond = wx.CheckBox(self.panel,-1,label=': %s, %.3f'%(bond[0],bond[1]))
1238                Bond.SetValue(bond[2])
1239                Indx[Bond.GetId()] = [inei,ib]
1240                Bond.Bind(wx.EVT_CHECKBOX,OnBond)               
1241                lineSizer.Add(Bond,0,WACV)               
1242            dataSizer.Add(lineSizer,0,WACV|wx.RIGHT,10)
1243        mainSizer.Add(dataSizer,0,wx.LEFT,5)
1244
1245        CancelBtn = wx.Button(self.panel,-1,'Cancel')
1246        CancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1247        OkBtn = wx.Button(self.panel,-1,'Ok')
1248        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1249        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1250        btnSizer.Add((20,20),1)
1251        btnSizer.Add(OkBtn)
1252        btnSizer.Add((20,20),1)
1253        btnSizer.Add(CancelBtn)
1254        btnSizer.Add((20,20),1)
1255        mainSizer.Add(btnSizer,0,wx.BOTTOM|wx.TOP, 10)
1256        self.panel.SetSizer(mainSizer)
1257        size = np.array(self.GetSize())
1258        self.panel.SetupScrolling()
1259        self.panel.SetAutoLayout(1)
1260        size = [size[0]-5,size[1]-20]       #this fiddling is needed for older wx!
1261        self.panel.SetSize(size)
1262       
1263    def GetData(self):
1264        'Returns the values from the dialog'
1265        for neigh in self.Neigh:
1266            for ibond,bond in enumerate(neigh[1][0]):
1267                if not bond[2]:
1268                    neigh[1][1][1][ibond] = 0   #deselected bond
1269            neigh[1][1][1] = [a for a in  neigh[1][1][1] if a]
1270        return self.Neigh       #has #Hs to add for each entry
1271       
1272    def OnOk(self,event):
1273        'Called when the OK button is pressed'
1274        parent = self.GetParent()
1275        parent.Raise()
1276        self.EndModal(wx.ID_OK)             
1277
1278    def OnCancel(self,event):
1279        parent = self.GetParent()
1280        parent.Raise()
1281        self.EndModal(wx.ID_CANCEL)
1282
1283################################################################################
1284class DisAglDialog(wx.Dialog):
1285    '''Distance/Angle Controls input dialog. After
1286    :meth:`ShowModal` returns, the results are found in
1287    dict :attr:`self.data`, which is accessed using :meth:`GetData`.
1288
1289    :param wx.Frame parent: reference to parent frame (or None)
1290    :param dict data: a dict containing the current
1291      search ranges or an empty dict, which causes default values
1292      to be used.
1293      Will be used to set element `DisAglCtls` in
1294      :ref:`Phase Tree Item <Phase_table>`
1295    :param dict default:  A dict containing the default
1296      search ranges for each element.
1297    '''
1298    def __init__(self,parent,data,default,Reset=True):
1299        wx.Dialog.__init__(self,parent,wx.ID_ANY,'Distance Angle Controls', 
1300            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1301        self.default = default
1302        self.Reset = Reset
1303        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
1304        self._default(data,self.default)
1305        self.Draw(self.data)
1306               
1307    def _default(self,data,default):
1308        '''Set starting values for the search values, either from
1309        the input array or from defaults, if input is null
1310        '''
1311        if data:
1312            self.data = copy.deepcopy(data) # don't mess with originals
1313        else:
1314            self.data = {}
1315            self.data['Name'] = default['Name']
1316            self.data['Factors'] = [0.85,0.85]
1317            self.data['AtomTypes'] = default['AtomTypes']
1318            self.data['BondRadii'] = default['BondRadii'][:]
1319            self.data['AngleRadii'] = default['AngleRadii'][:]
1320
1321    def Draw(self,data):
1322        '''Creates the contents of the dialog. Normally called
1323        by :meth:`__init__`.
1324        '''
1325        self.panel.Destroy()
1326        self.panel = wx.Panel(self)
1327        mainSizer = wx.BoxSizer(wx.VERTICAL)
1328        mainSizer.Add(wx.StaticText(self.panel,-1,'Controls for phase '+data['Name']),
1329            0,WACV|wx.LEFT,10)
1330        mainSizer.Add((10,10),1)
1331       
1332        radiiSizer = wx.FlexGridSizer(0,3,5,5)
1333        radiiSizer.Add(wx.StaticText(self.panel,-1,' Type'),0,WACV)
1334        radiiSizer.Add(wx.StaticText(self.panel,-1,'Bond radii'),0,WACV)
1335        radiiSizer.Add(wx.StaticText(self.panel,-1,'Angle radii'),0,WACV)
1336        self.objList = {}
1337        for id,item in enumerate(self.data['AtomTypes']):
1338            radiiSizer.Add(wx.StaticText(self.panel,-1,' '+item),0,WACV)
1339            bRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['BondRadii'][id]),style=wx.TE_PROCESS_ENTER)
1340            self.objList[bRadii.GetId()] = ['BondRadii',id]
1341            bRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1342            bRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1343            radiiSizer.Add(bRadii,0,WACV)
1344            aRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['AngleRadii'][id]),style=wx.TE_PROCESS_ENTER)
1345            self.objList[aRadii.GetId()] = ['AngleRadii',id]
1346            aRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1347            aRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1348            radiiSizer.Add(aRadii,0,WACV)
1349        mainSizer.Add(radiiSizer,0,wx.EXPAND)
1350        factorSizer = wx.FlexGridSizer(0,2,5,5)
1351        Names = ['Bond','Angle']
1352        for i,name in enumerate(Names):
1353            factorSizer.Add(wx.StaticText(self.panel,-1,name+' search factor'),0,WACV)
1354            bondFact = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['Factors'][i]),style=wx.TE_PROCESS_ENTER)
1355            self.objList[bondFact.GetId()] = ['Factors',i]
1356            bondFact.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1357            bondFact.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1358            factorSizer.Add(bondFact)
1359        mainSizer.Add(factorSizer,0,wx.EXPAND)
1360       
1361        OkBtn = wx.Button(self.panel,-1,"Ok")
1362        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1363        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1364        btnSizer.Add((20,20),1)
1365        btnSizer.Add(OkBtn)
1366        if self.Reset:
1367            ResetBtn = wx.Button(self.panel,-1,'Reset')
1368            ResetBtn.Bind(wx.EVT_BUTTON, self.OnReset)
1369            btnSizer.Add(ResetBtn)
1370        btnSizer.Add((20,20),1)
1371        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1372        self.panel.SetSizer(mainSizer)
1373        self.panel.Fit()
1374        self.Fit()
1375   
1376    def OnRadiiVal(self,event):
1377        event.Skip()
1378        Obj = event.GetEventObject()
1379        item = self.objList[Obj.GetId()]
1380        try:
1381            self.data[item[0]][item[1]] = float(Obj.GetValue())
1382        except ValueError:
1383            pass
1384        Obj.SetValue("%.3f"%(self.data[item[0]][item[1]]))          #reset in case of error
1385       
1386    def GetData(self):
1387        'Returns the values from the dialog'
1388        return self.data
1389       
1390    def OnOk(self,event):
1391        'Called when the OK button is pressed'
1392        parent = self.GetParent()
1393        parent.Raise()
1394        self.EndModal(wx.ID_OK)             
1395       
1396    def OnReset(self,event):
1397        'Called when the Reset button is pressed'
1398        data = {}
1399        self._default(data,self.default)
1400        self.Draw(self.data)
1401               
1402################################################################################
1403class ShowLSParms(wx.Dialog):
1404    '''Create frame to show least-squares parameters
1405    '''
1406    def __init__(self,parent,title,parmDict,varyList,fullVaryList,
1407                 size=(300,430)):
1408        wx.Dialog.__init__(self,parent,wx.ID_ANY,title,size=size,
1409                           style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1410        mainSizer = wx.BoxSizer(wx.VERTICAL)
1411
1412        panel = wxscroll.ScrolledPanel(
1413            self, wx.ID_ANY,
1414            #size=size,
1415            style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
1416        num = len(varyList)
1417        mainSizer.Add(wx.StaticText(self,wx.ID_ANY,'Number of refined variables: '+str(num)))
1418        if len(varyList) != len(fullVaryList):
1419            num = len(fullVaryList) - len(varyList)
1420            mainSizer.Add(wx.StaticText(self,wx.ID_ANY,' + '+str(num)+' parameters are varied via constraints'))
1421        subSizer = wx.FlexGridSizer(cols=4,hgap=2,vgap=2)
1422        parmNames = parmDict.keys()
1423        parmNames.sort()
1424        subSizer.Add((-1,-1))
1425        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'Parameter name  '))
1426        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'refine?'))
1427        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'value'),0,wx.ALIGN_RIGHT)
1428        explainRefine = False
1429        for name in parmNames:
1430            # skip entries without numerical values
1431            if isinstance(parmDict[name],basestring): continue
1432            try:
1433                value = G2py3.FormatSigFigs(parmDict[name])
1434            except TypeError:
1435                value = str(parmDict[name])+' -?' # unexpected
1436                #continue
1437            v = G2obj.getVarDescr(name)
1438            if v is None or v[-1] is None:
1439                subSizer.Add((-1,-1))
1440            else:               
1441                ch = G2G.HelpButton(panel,G2obj.fmtVarDescr(name))
1442                subSizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)
1443            subSizer.Add(wx.StaticText(panel,wx.ID_ANY,str(name)))
1444            if name in varyList:
1445                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'R'))
1446            elif name in fullVaryList:
1447                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'C'))
1448                explainRefine = True
1449            else:
1450                subSizer.Add((-1,-1))
1451            subSizer.Add(wx.StaticText(panel,wx.ID_ANY,value),0,wx.ALIGN_RIGHT)
1452
1453        # finish up ScrolledPanel
1454        panel.SetSizer(subSizer)
1455        panel.SetAutoLayout(1)
1456        panel.SetupScrolling()
1457        mainSizer.Add(panel,1, wx.ALL|wx.EXPAND,1)
1458
1459        if explainRefine:
1460            mainSizer.Add(
1461                wx.StaticText(self,wx.ID_ANY,
1462                          '"R" indicates a refined variable\n'+
1463                          '"C" indicates generated from a constraint'
1464                          ),
1465                0, wx.ALL,0)
1466        # make OK button
1467        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
1468        btn = wx.Button(self, wx.ID_CLOSE,"Close") 
1469        btn.Bind(wx.EVT_BUTTON,self._onClose)
1470        btnsizer.Add(btn)
1471        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
1472        # Allow window to be enlarged but not made smaller
1473        self.SetSizer(mainSizer)
1474        self.SetMinSize(self.GetSize())
1475
1476    def _onClose(self,event):
1477        self.EndModal(wx.ID_CANCEL)
1478 
1479################################################################################
1480class DataFrame(wx.Frame):
1481    '''Create the data item window and all the entries in menus used in
1482    that window. For Linux and windows, the menu entries are created for the
1483    current data item window, but in the Mac the menu is accessed from all
1484    windows. This means that a different menu is posted depending on which
1485    data item is posted. On the Mac, all the menus contain the data tree menu
1486    items, but additional menus are added specific to the data item.
1487
1488    Note that while the menus are created here,
1489    the binding for the menus is done later in various GSASII*GUI modules,
1490    where the functions to be called are defined.
1491    '''
1492    def Bind(self,eventtype,handler,*args,**kwargs):
1493        '''Override the Bind() function: on the Mac the binding is to
1494        the main window, so that menus operate with any window on top.
1495        For other platforms, either wrap calls that will be logged
1496        or call the default wx.Frame Bind() to bind to the menu item directly.
1497
1498        Note that bindings can be made to objects by Id or by direct reference to the
1499        object. As a convention, when bindings are to objects, they are not logged
1500        but when bindings are by Id, they are logged.
1501        '''
1502        if sys.platform == "darwin": # mac
1503            self.G2frame.Bind(eventtype,handler,*args,**kwargs)
1504            return
1505        if eventtype == wx.EVT_MENU and 'id' in kwargs:
1506            menulabels = log.SaveMenuCommand(kwargs['id'],self.G2frame,handler)
1507            if menulabels:
1508                #print 'intercepting bind for',handler,menulabels,kwargs['id']
1509                wx.Frame.Bind(self,eventtype,self.G2frame.MenuBinding,*args,**kwargs)
1510                return
1511            wx.Frame.Bind(self,eventtype,handler,*args,**kwargs)     
1512       
1513    def PrefillDataMenu(self,menu,helpType,helpLbl=None,empty=False):
1514        '''Create the "standard" part of data frame menus. Note that on Linux and
1515        Windows nothing happens here. On Mac, this menu duplicates the
1516        tree menu, but adds an extra help command for the data item and a separator.
1517        '''
1518        self.datamenu = menu
1519        self.G2frame.dataMenuBars.append(menu)
1520        self.helpType = helpType
1521        self.helpLbl = helpLbl
1522        if sys.platform == "darwin": # mac                         
1523            self.G2frame.FillMainMenu(menu,addhelp=False) # add the data tree menu items
1524            if not empty:
1525                menu.Append(wx.Menu(title=''),title='|') # add a separator
1526       
1527    def PostfillDataMenu(self,empty=False):
1528        '''Add the help menu to the data frame menus. Note that on Linux and
1529        Windows, this is the standard help Menu but without the update commands but adds an extra help
1530        command for the data item.
1531        On Mac, this is the entire help menu including the update commands, a separator and the
1532        extra help command for the data item.
1533        '''
1534        menu = self.datamenu
1535        helpType = self.helpType
1536        helpLbl = self.helpLbl
1537        if sys.platform == "darwin": # mac
1538            if not empty:
1539                menu.Append(wx.Menu(title=''),title='|') # add another separator
1540            HelpMenu=G2G.MyHelp(self,helpType=helpType,includeTree=True,
1541                morehelpitems=[('&Tutorials','Tutorials'),])
1542            menu.Append(menu=HelpMenu,title='&Help')
1543        else: # other
1544            menu.Append(menu=G2G.MyHelp(self,helpType=helpType, helpLbl=helpLbl),
1545                        title='&Help')
1546
1547    def _init_menus(self):
1548        'define all GSAS-II data frame menus'
1549
1550        # for use where no menu or data frame help is provided
1551        self.BlankMenu = wx.MenuBar()
1552       
1553        # Controls
1554        self.ControlsMenu = wx.MenuBar()
1555        self.PrefillDataMenu(self.ControlsMenu,helpType='Controls',empty=True)
1556        self.PostfillDataMenu(empty=True)
1557       
1558        # Notebook
1559        self.DataNotebookMenu = wx.MenuBar() 
1560        self.PrefillDataMenu(self.DataNotebookMenu,helpType='Notebook',empty=True)
1561        self.PostfillDataMenu(empty=True)
1562       
1563        # Comments
1564        self.DataCommentsMenu = wx.MenuBar()
1565        self.PrefillDataMenu(self.DataCommentsMenu,helpType='Comments',empty=True)
1566        self.PostfillDataMenu(empty=True)
1567       
1568        # Constraints
1569        self.ConstraintMenu = wx.MenuBar()
1570        self.PrefillDataMenu(self.ConstraintMenu,helpType='Constraints')
1571        self.ConstraintTab = wx.Menu(title='')
1572        self.ConstraintMenu.Append(menu=self.ConstraintTab, title='Select tab')
1573        for id,txt in (
1574            (wxID_CONSPHASE,'Phase'),
1575            (wxID_CONSHAP,'Histogram/Phase'),
1576            (wxID_CONSHIST,'Histogram'),
1577            (wxID_CONSGLOBAL,'Global')):
1578            self.ConstraintTab.Append(
1579                id=id, kind=wx.ITEM_NORMAL,text=txt,
1580                help='Select '+txt+' constraint editing tab')
1581        self.ConstraintEdit = wx.Menu(title='')
1582        self.ConstraintMenu.Append(menu=self.ConstraintEdit, title='Edit Constr.') # renamed from Edit due to Mac adding extra items to menu
1583        self.ConstraintEdit.Append(id=wxID_HOLDADD, kind=wx.ITEM_NORMAL,text='Add hold',
1584            help='Prevent refinement of parameter values')
1585        self.ConstraintEdit.Append(id=wxID_EQUIVADD, kind=wx.ITEM_NORMAL,text='Add equivalence',
1586            help='Force parameter values to be equivalent')
1587        self.ConstraintEdit.Append(id=wxID_CONSTRAINTADD, kind=wx.ITEM_NORMAL,text='Add constraint equation',
1588            help='Add a constraint equation to apply to parameter values')
1589        self.ConstraintEdit.Append(id=wxID_FUNCTADD, kind=wx.ITEM_NORMAL,text='Add New Var',
1590            help='Create a variable composed of existing parameters')
1591        self.ConstraintEdit.Append(id=wxID_EQUIVALANCEATOMS, kind=wx.ITEM_NORMAL,text='Make atoms equivalent',
1592            help='Force atom parameter values to be equivalent')
1593        self.ConstraintEdit.Enable(wxID_EQUIVALANCEATOMS,False)
1594#        self.ConstraintEdit.Append(id=wxID_ADDRIDING, kind=wx.ITEM_NORMAL,text='Add H riding constraints',
1595#            help='Add H atom riding constraints between atom parameter values')
1596#        self.ConstraintEdit.Enable(wxID_ADDRIDING,False)
1597        self.PostfillDataMenu()
1598
1599        # item = self.ConstraintEdit.Append(id=wx.ID_ANY,kind=wx.ITEM_NORMAL,text='Update GUI')
1600        # def UpdateGSASIIconstrGUI(event):
1601        #     import GSASIIconstrGUI
1602        #     reload(GSASIIconstrGUI)
1603        #     import GSASIIobj
1604        #     reload(GSASIIobj)
1605        # self.Bind(wx.EVT_MENU,UpdateGSASIIconstrGUI,id=item.GetId())
1606
1607        # Rigid bodies
1608        self.RigidBodyMenu = wx.MenuBar()
1609        self.PrefillDataMenu(self.RigidBodyMenu,helpType='Rigid bodies')
1610        self.ResidueRBMenu = wx.Menu(title='')
1611        self.ResidueRBMenu.Append(id=wxID_RIGIDBODYIMPORT, kind=wx.ITEM_NORMAL,text='Import XYZ',
1612            help='Import rigid body XYZ from file')
1613        self.ResidueRBMenu.Append(id=wxID_RESIDUETORSSEQ, kind=wx.ITEM_NORMAL,text='Define sequence',
1614            help='Define torsion sequence')
1615        self.ResidueRBMenu.Append(id=wxID_RIGIDBODYADD, kind=wx.ITEM_NORMAL,text='Import residues',
1616            help='Import residue rigid bodies from macro file')
1617        self.RigidBodyMenu.Append(menu=self.ResidueRBMenu, title='Edit Body')
1618        self.PostfillDataMenu()
1619
1620        self.VectorBodyMenu = wx.MenuBar()
1621        self.PrefillDataMenu(self.VectorBodyMenu,helpType='Vector rigid bodies')
1622        self.VectorRBEdit = wx.Menu(title='')
1623        self.VectorRBEdit.Append(id=wxID_VECTORBODYADD, kind=wx.ITEM_NORMAL,text='Add rigid body',
1624            help='Add vector rigid body')
1625        self.VectorBodyMenu.Append(menu=self.VectorRBEdit, title='Edit Vector Body')
1626        self.PostfillDataMenu()
1627
1628                   
1629        # Restraints
1630        self.RestraintTab = wx.Menu(title='')
1631        self.RestraintEdit = wx.Menu(title='')
1632        self.RestraintEdit.Append(id=wxID_RESTSELPHASE, kind=wx.ITEM_NORMAL,text='Select phase',
1633            help='Select phase')
1634        self.RestraintEdit.Append(id=wxID_RESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add restraints',
1635            help='Add restraints')
1636        self.RestraintEdit.Enable(wxID_RESTRAINTADD,True)    #gets disabled if macromolecule phase
1637        self.RestraintEdit.Append(id=wxID_AARESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add residue restraints',
1638            help='Add residue based restraints for macromolecules from macro file')
1639        self.RestraintEdit.Enable(wxID_AARESTRAINTADD,False)    #gets enabled if macromolecule phase
1640        self.RestraintEdit.Append(id=wxID_AARESTRAINTPLOT, kind=wx.ITEM_NORMAL,text='Plot residue restraints',
1641            help='Plot selected residue based restraints for macromolecules from macro file')
1642        self.RestraintEdit.Enable(wxID_AARESTRAINTPLOT,False)    #gets enabled if macromolecule phase
1643        self.RestraintEdit.Append(id=wxID_RESRCHANGEVAL, kind=wx.ITEM_NORMAL,text='Change value',
1644            help='Change observed value')
1645        self.RestraintEdit.Append(id=wxID_RESTCHANGEESD, kind=wx.ITEM_NORMAL,text='Change esd',
1646            help='Change esd in observed value')
1647        self.RestraintEdit.Append(id=wxID_RESTDELETE, kind=wx.ITEM_NORMAL,text='Delete restraints',
1648            help='Delete selected restraints')
1649
1650        self.RestraintMenu = wx.MenuBar()
1651        self.PrefillDataMenu(self.RestraintMenu,helpType='Restraints')
1652        self.RestraintMenu.Append(menu=self.RestraintTab, title='Select tab')
1653        self.RestraintMenu.Append(menu=self.RestraintEdit, title='Edit Restr.')
1654        self.PostfillDataMenu()
1655           
1656        # Sequential results
1657        self.SequentialMenu = wx.MenuBar()
1658        self.PrefillDataMenu(self.SequentialMenu,helpType='Sequential',helpLbl='Sequential Refinement')
1659        self.SequentialFile = wx.Menu(title='')
1660        self.SequentialMenu.Append(menu=self.SequentialFile, title='Columns')
1661        self.SequentialFile.Append(id=wxID_RENAMESEQSEL, kind=wx.ITEM_NORMAL,text='Rename selected',
1662            help='Rename selected sequential refinement columns')
1663        self.SequentialFile.Append(id=wxID_SAVESEQSEL, kind=wx.ITEM_NORMAL,text='Save selected as text',
1664            help='Save selected sequential refinement results as a text file')
1665        self.SequentialFile.Append(id=wxID_SAVESEQCSV, kind=wx.ITEM_NORMAL,text='Save all as CSV',
1666            help='Save all sequential refinement results as a CSV spreadsheet file')
1667        self.SequentialFile.Append(id=wxID_SAVESEQSELCSV, kind=wx.ITEM_NORMAL,text='Save selected as CSV',
1668            help='Save selected sequential refinement results as a CSV spreadsheet file')
1669        self.SequentialFile.Append(id=wxID_PLOTSEQSEL, kind=wx.ITEM_NORMAL,text='Plot selected',
1670            help='Plot selected sequential refinement results')
1671        self.SequentialFile.Append(id=wxID_AVESEQSEL, kind=wx.ITEM_NORMAL,text='Compute average',
1672            help='Compute average for selected parameter')           
1673        self.SequentialFile.Append(id=wxID_ORGSEQSEL, kind=wx.ITEM_NORMAL,text='Reorganize',
1674            help='Reorganize variables where variables change')
1675        self.SequentialPvars = wx.Menu(title='')
1676        self.SequentialMenu.Append(menu=self.SequentialPvars, title='Pseudo Vars')
1677        self.SequentialPvars.Append(
1678            id=wxADDSEQVAR, kind=wx.ITEM_NORMAL,text='Add Formula',
1679            help='Add a new custom pseudo-variable')
1680        self.SequentialPvars.Append(
1681            id=wxADDSEQDIST, kind=wx.ITEM_NORMAL,text='Add Distance',
1682            help='Add a new bond distance pseudo-variable')
1683        self.SequentialPvars.Append(
1684            id=wxADDSEQANGLE, kind=wx.ITEM_NORMAL,text='Add Angle',
1685            help='Add a new bond angle pseudo-variable')
1686        self.SequentialPvars.Append(
1687            id=wxDELSEQVAR, kind=wx.ITEM_NORMAL,text='Delete',
1688            help='Delete an existing pseudo-variable')
1689        self.SequentialPvars.Append(
1690            id=wxEDITSEQVAR, kind=wx.ITEM_NORMAL,text='Edit',
1691            help='Edit an existing pseudo-variable')
1692
1693        self.SequentialPfit = wx.Menu(title='')
1694        self.SequentialMenu.Append(menu=self.SequentialPfit, title='Parametric Fit')
1695        self.SequentialPfit.Append(
1696            id=wxADDPARFIT, kind=wx.ITEM_NORMAL,text='Add equation',
1697            help='Add a new equation to minimize')
1698        self.SequentialPfit.Append(
1699            id=wxCOPYPARFIT, kind=wx.ITEM_NORMAL,text='Copy equation',
1700            help='Copy an equation to minimize - edit it next')
1701        self.SequentialPfit.Append(
1702            id=wxDELPARFIT, kind=wx.ITEM_NORMAL,text='Delete equation',
1703            help='Delete an equation for parametric minimization')
1704        self.SequentialPfit.Append(
1705            id=wxEDITPARFIT, kind=wx.ITEM_NORMAL,text='Edit equation',
1706            help='Edit an existing parametric minimization equation')
1707        self.SequentialPfit.Append(
1708            id=wxDOPARFIT, kind=wx.ITEM_NORMAL,text='Fit to equation(s)',
1709            help='Perform a parametric minimization')
1710        # fill sequential Export menu
1711        self.SeqExportLookup = {}
1712        self.SequentialEx = wx.Menu(title='')
1713        self.SequentialMenu.Append(menu=self.SequentialEx, title='Seq Export')
1714        for lbl,txt in (('Phase','Export selected phase(s)'),
1715                        ('Project','Export entire sequential fit')):
1716            objlist = []
1717            for obj in self.G2frame.exporterlist:
1718                if lbl.lower() in obj.exporttype:
1719                    try:
1720                        obj.Writer
1721                    except AttributeError:
1722                        continue
1723                    objlist.append(obj)
1724            if objlist:
1725                submenu = wx.Menu()
1726                item = self.SequentialEx.AppendMenu(
1727                    wx.ID_ANY, lbl+' as',
1728                    submenu, help=txt)
1729                for obj in objlist:
1730                    item = submenu.Append(
1731                        wx.ID_ANY,
1732                        help=obj.longFormatName,
1733                        kind=wx.ITEM_NORMAL,
1734                        text=obj.formatName)
1735                    self.SeqExportLookup[item.GetId()] = (obj,lbl) # lookup table for submenu item
1736       
1737        self.PostfillDataMenu()
1738           
1739        # PWDR & SASD
1740        self.PWDRMenu = wx.MenuBar()
1741        self.PrefillDataMenu(self.PWDRMenu,helpType='PWDR Analysis',helpLbl='Powder Fit Error Analysis')
1742        self.ErrorAnal = wx.Menu(title='')
1743        self.PWDRMenu.Append(menu=self.ErrorAnal,title='Commands')
1744        self.ErrorAnal.Append(id=wxID_PWDANALYSIS,kind=wx.ITEM_NORMAL,text='Error Analysis',
1745            help='Error analysis on powder pattern')
1746        self.ErrorAnal.Append(id=wxID_PWDCOPY,kind=wx.ITEM_NORMAL,text='Copy params',
1747            help='Copy of PWDR parameters')
1748        self.ErrorAnal.Append(id=wxID_PLOTCTRLCOPY,kind=wx.ITEM_NORMAL,text='Copy plot controls',
1749            help='Copy of PWDR plot controls')
1750           
1751        self.PostfillDataMenu()
1752           
1753        # HKLF
1754        self.HKLFMenu = wx.MenuBar()
1755        self.PrefillDataMenu(self.HKLFMenu,helpType='HKLF Analysis',helpLbl='HKLF Fit Error Analysis')
1756        self.ErrorAnal = wx.Menu(title='')
1757        self.HKLFMenu.Append(menu=self.ErrorAnal,title='Commands')
1758        self.ErrorAnal.Append(id=wxID_PWDANALYSIS,kind=wx.ITEM_NORMAL,text='Error Analysis',
1759            help='Error analysis on single crystal data')
1760        self.ErrorAnal.Append(id=wxID_MERGEHKL,kind=wx.ITEM_NORMAL,text='Merge HKLs',
1761            help='Transform & merge HKLF data to new histogram')
1762        self.ErrorAnal.Append(id=wxID_PWD3DHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot 3D HKLs',
1763            help='Plot HKLs from single crystal data in 3D')
1764        self.ErrorAnal.Append(id=wxID_3DALLHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot all 3D HKLs',
1765            help='Plot HKLs from all single crystal data in 3D')
1766        self.ErrorAnal.Append(id=wxID_PWDCOPY,kind=wx.ITEM_NORMAL,text='Copy params',
1767            help='Copy of HKLF parameters')
1768        self.PostfillDataMenu()
1769           
1770        # PWDR / Limits
1771        self.LimitMenu = wx.MenuBar()
1772        self.PrefillDataMenu(self.LimitMenu,helpType='Limits')
1773        self.LimitEdit = wx.Menu(title='')
1774        self.LimitMenu.Append(menu=self.LimitEdit, title='Edit Limits')
1775        self.LimitEdit.Append(id=wxID_LIMITCOPY, kind=wx.ITEM_NORMAL,text='Copy',
1776            help='Copy limits to other histograms')
1777        self.LimitEdit.Append(id=wxID_ADDEXCLREGION, kind=wx.ITEM_NORMAL,text='Add exclude',
1778            help='Add excluded region - select a point on plot; drag to adjust')           
1779        self.PostfillDataMenu()
1780           
1781        # PDR / Background
1782        self.BackMenu = wx.MenuBar()
1783        self.PrefillDataMenu(self.BackMenu,helpType='Background')
1784        self.BackEdit = wx.Menu(title='')
1785        self.BackMenu.Append(menu=self.BackEdit, title='File')
1786        self.BackEdit.Append(id=wxID_BACKCOPY, kind=wx.ITEM_NORMAL,text='Copy',
1787            help='Copy background parameters to other histograms')
1788        self.BackEdit.Append(id=wxID_BACKFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
1789            help='Copy background refinement flags to other histograms')
1790        self.BackEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks',
1791            help='Move background peaks to Peak List')
1792        self.BackEdit.Append(id=wxID_MAKEBACKRDF, kind=wx.ITEM_NORMAL,text='Plot RDF',
1793            help='Plot radial distribution from differences')
1794        self.BackFixed = wx.Menu(title='') # fixed background point menu
1795        self.BackMenu.Append(menu=self.BackFixed, title='Fixed Points')
1796        self.wxID_BackPts = {}
1797        self.wxID_BackPts['Add'] = wx.NewId() # N.B. not using wxID_ global as for other menu items
1798        self.BackFixed.Append(id=self.wxID_BackPts['Add'], kind=wx.ITEM_RADIO,text='Add',
1799            help='Add fixed background points with mouse clicks')
1800        self.wxID_BackPts['Move'] = wx.NewId() 
1801        item = self.BackFixed.Append(id=self.wxID_BackPts['Move'], kind=wx.ITEM_RADIO,text='Move',
1802            help='Move selected fixed background points with mouse drags')
1803        item.Check(True)
1804        self.wxID_BackPts['Del'] = wx.NewId()
1805        self.BackFixed.Append(id=self.wxID_BackPts['Del'], kind=wx.ITEM_RADIO,text='Delete',
1806            help='Delete fixed background points with mouse clicks')
1807        self.wxID_BackPts['Clear'] = wx.NewId() 
1808        self.BackFixed.Append(id=self.wxID_BackPts['Clear'], kind=wx.ITEM_NORMAL,text='Clear',
1809            help='Clear fixed background points')
1810        self.wxID_BackPts['Fit'] = wx.NewId() 
1811        self.BackFixed.Append(id=self.wxID_BackPts['Fit'], kind=wx.ITEM_NORMAL,text='Fit background',
1812            help='Fit background function to fixed background points')
1813        self.PostfillDataMenu()
1814           
1815        # PDR / Instrument Parameters
1816        self.InstMenu = wx.MenuBar()
1817        self.PrefillDataMenu(self.InstMenu,helpType='Instrument Parameters')
1818        self.InstEdit = wx.Menu(title='')
1819        self.InstMenu.Append(menu=self.InstEdit, title='Operations')
1820        self.InstEdit.Append(help='Calibrate from indexed peaks', 
1821            id=wxID_INSTCALIB, kind=wx.ITEM_NORMAL,text='Calibrate')           
1822        self.InstEdit.Append(help='Reset instrument profile parameters to default', 
1823            id=wxID_INSTPRMRESET, kind=wx.ITEM_NORMAL,text='Reset profile')           
1824        self.InstEdit.Append(help='Load instrument profile parameters from file', 
1825            id=wxID_INSTLOAD, kind=wx.ITEM_NORMAL,text='Load profile...')           
1826        self.InstEdit.Append(help='Save instrument profile parameters to file', 
1827            id=wxID_INSTSAVE, kind=wx.ITEM_NORMAL,text='Save profile...')
1828        self.InstEdit.Append(help='Save all instrument profile parameters to one file', 
1829            id=wxID_INSTSAVEALL, kind=wx.ITEM_NORMAL,text='Save all profile...')           
1830        self.InstEdit.Append(help='Copy instrument profile parameters to other histograms', 
1831            id=wxID_INSTCOPY, kind=wx.ITEM_NORMAL,text='Copy')
1832        self.InstEdit.Append(id=wxID_INSTFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
1833            help='Copy instrument parameter refinement flags to other histograms')
1834#        self.InstEdit.Append(help='Change radiation type (Ka12 - synch)',
1835#            id=wxID_CHANGEWAVETYPE, kind=wx.ITEM_NORMAL,text='Change radiation')
1836        self.InstEdit.Append(id=wxID_INST1VAL, kind=wx.ITEM_NORMAL,text='Set one value',
1837            help='Set one instrument parameter value across multiple histograms')
1838
1839        self.PostfillDataMenu()
1840       
1841        # PDR / Sample Parameters
1842        self.SampleMenu = wx.MenuBar()
1843        self.PrefillDataMenu(self.SampleMenu,helpType='Sample Parameters')
1844        self.SampleEdit = wx.Menu(title='')
1845        self.SampleMenu.Append(menu=self.SampleEdit, title='Command')
1846        self.SetScale = self.SampleEdit.Append(id=wxID_SETSCALE, kind=wx.ITEM_NORMAL,text='Set scale',
1847            help='Set scale by matching to another histogram')
1848        self.SampleEdit.Append(id=wxID_SAMPLELOAD, kind=wx.ITEM_NORMAL,text='Load',
1849            help='Load sample parameters from file')
1850        self.SampleEdit.Append(id=wxID_SAMPLESAVE, kind=wx.ITEM_NORMAL,text='Save',
1851            help='Save sample parameters to file')
1852        self.SampleEdit.Append(id=wxID_SAMPLECOPY, kind=wx.ITEM_NORMAL,text='Copy',
1853            help='Copy refinable and most other sample parameters to other histograms')
1854        self.SampleEdit.Append(id=wxID_SAMPLECOPYSOME, kind=wx.ITEM_NORMAL,text='Copy selected...',
1855            help='Copy selected sample parameters to other histograms')
1856        self.SampleEdit.Append(id=wxID_SAMPLEFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
1857            help='Copy sample parameter refinement flags to other histograms')
1858        self.SampleEdit.Append(id=wxID_SAMPLE1VAL, kind=wx.ITEM_NORMAL,text='Set one value',
1859            help='Set one sample parameter value across multiple histograms')
1860        self.SampleEdit.Append(id=wxID_ALLSAMPLELOAD, kind=wx.ITEM_NORMAL,text='Load all',
1861            help='Load sample parmameters over multiple histograms')
1862
1863        self.PostfillDataMenu()
1864        self.SetScale.Enable(False)
1865
1866        # PDR / Peak List
1867        self.PeakMenu = wx.MenuBar()
1868        self.PrefillDataMenu(self.PeakMenu,helpType='Peak List')
1869        self.PeakEdit = wx.Menu(title='')
1870        self.PeakMenu.Append(menu=self.PeakEdit, title='Peak Fitting')
1871        self.AutoSearch = self.PeakEdit.Append(help='Automatic peak search', 
1872            id=wxID_AUTOSEARCH, kind=wx.ITEM_NORMAL,text='Auto search')
1873        self.UnDo = self.PeakEdit.Append(help='Undo last least squares refinement', 
1874            id=wxID_UNDO, kind=wx.ITEM_NORMAL,text='UnDo')
1875        self.PeakFit = self.PeakEdit.Append(id=wxID_LSQPEAKFIT, kind=wx.ITEM_NORMAL,text='Peakfit', 
1876            help='Peak fitting' )
1877        self.PFOneCycle = self.PeakEdit.Append(id=wxID_LSQONECYCLE, kind=wx.ITEM_NORMAL,text='Peakfit one cycle', 
1878            help='One cycle of Peak fitting' )
1879        self.PeakEdit.Append(id=wxID_RESETSIGGAM, kind=wx.ITEM_NORMAL, 
1880            text='Reset sig and gam',help='Reset sigma and gamma to global fit' )
1881        self.PeakCopy = self.PeakEdit.Append(help='Copy peaks to other histograms', 
1882            id=wxID_PEAKSCOPY, kind=wx.ITEM_NORMAL,text='Peak copy')
1883        self.SeqPeakFit = self.PeakEdit.Append(id=wxID_SEQPEAKFIT, kind=wx.ITEM_NORMAL,text='Seq PeakFit', 
1884            help='Sequential Peak fitting for all histograms' )
1885        self.PeakEdit.Append(id=wxID_CLEARPEAKS, kind=wx.ITEM_NORMAL,text='Clear peaks', 
1886            help='Clear the peak list' )
1887        self.PostfillDataMenu()
1888        self.UnDo.Enable(False)
1889        self.PeakFit.Enable(False)
1890        self.PFOneCycle.Enable(False)
1891        self.AutoSearch.Enable(True)
1892       
1893        # PDR / Index Peak List
1894        self.IndPeaksMenu = wx.MenuBar()
1895        self.PrefillDataMenu(self.IndPeaksMenu,helpType='Index Peak List')
1896        self.IndPeaksEdit = wx.Menu(title='')
1897        self.IndPeaksMenu.Append(menu=self.IndPeaksEdit,title='Operations')
1898        self.IndPeaksEdit.Append(help='Load/Reload index peaks from peak list',id=wxID_INDXRELOAD, 
1899            kind=wx.ITEM_NORMAL,text='Load/Reload')
1900        self.PostfillDataMenu()
1901       
1902        # PDR / Unit Cells List
1903        self.IndexMenu = wx.MenuBar()
1904        self.PrefillDataMenu(self.IndexMenu,helpType='Unit Cells List')
1905        self.IndexEdit = wx.Menu(title='')
1906        self.IndexMenu.Append(menu=self.IndexEdit, title='Cell Index/Refine')
1907        self.IndexPeaks = self.IndexEdit.Append(help='', id=wxID_INDEXPEAKS, kind=wx.ITEM_NORMAL,
1908            text='Index Cell')
1909        self.CopyCell = self.IndexEdit.Append( id=wxID_COPYCELL, kind=wx.ITEM_NORMAL,text='Copy Cell', 
1910            help='Copy selected unit cell from indexing to cell refinement fields')
1911        self.RefineCell = self.IndexEdit.Append( id=wxID_REFINECELL, kind=wx.ITEM_NORMAL, 
1912            text='Refine Cell',help='Refine unit cell parameters from indexed peaks')
1913        self.MakeNewPhase = self.IndexEdit.Append( id=wxID_MAKENEWPHASE, kind=wx.ITEM_NORMAL,
1914            text='Make new phase',help='Make new phase from selected unit cell')
1915        self.ExportCells = self.IndexEdit.Append( id=wxID_EXPORTCELLS, kind=wx.ITEM_NORMAL,
1916            text='Export cell list',help='Export cell list to csv file')
1917        self.PostfillDataMenu()
1918        self.IndexPeaks.Enable(False)
1919        self.CopyCell.Enable(False)
1920        self.RefineCell.Enable(False)
1921        self.MakeNewPhase.Enable(False)
1922       
1923        # PDR / Reflection Lists
1924        self.ReflMenu = wx.MenuBar()
1925        self.PrefillDataMenu(self.ReflMenu,helpType='Reflection List')
1926        self.ReflEdit = wx.Menu(title='')
1927        self.ReflMenu.Append(menu=self.ReflEdit, title='Reflection List')
1928        self.SelectPhase = self.ReflEdit.Append(help='Select phase for reflection list',id=wxID_SELECTPHASE, 
1929            kind=wx.ITEM_NORMAL,text='Select phase')
1930        self.ReflEdit.Append(id=wxID_PWDHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot HKLs',
1931            help='Plot HKLs from powder pattern')
1932        self.ReflEdit.Append(id=wxID_PWD3DHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot 3D HKLs',
1933            help='Plot HKLs from powder pattern in 3D')
1934        self.PostfillDataMenu()
1935       
1936        # SASD / Instrument Parameters
1937        self.SASDInstMenu = wx.MenuBar()
1938        self.PrefillDataMenu(self.SASDInstMenu,helpType='Instrument Parameters')
1939        self.SASDInstEdit = wx.Menu(title='')
1940        self.SASDInstMenu.Append(menu=self.SASDInstEdit, title='Operations')
1941        self.InstEdit.Append(help='Reset instrument profile parameters to default', 
1942            id=wxID_INSTPRMRESET, kind=wx.ITEM_NORMAL,text='Reset profile')
1943        self.SASDInstEdit.Append(help='Copy instrument profile parameters to other histograms', 
1944            id=wxID_INSTCOPY, kind=wx.ITEM_NORMAL,text='Copy')
1945        self.PostfillDataMenu()
1946       
1947        #SASD & REFL/ Substance editor
1948        self.SubstanceMenu = wx.MenuBar()
1949        self.PrefillDataMenu(self.SubstanceMenu,helpType='Substances')
1950        self.SubstanceEdit = wx.Menu(title='')
1951        self.SubstanceMenu.Append(menu=self.SubstanceEdit, title='Edit substance')
1952        self.SubstanceEdit.Append(id=wxID_LOADSUBSTANCE, kind=wx.ITEM_NORMAL,text='Load substance',
1953            help='Load substance from file')
1954        self.SubstanceEdit.Append(id=wxID_ADDSUBSTANCE, kind=wx.ITEM_NORMAL,text='Add substance',
1955            help='Add new substance to list')
1956        self.SubstanceEdit.Append(id=wxID_COPYSUBSTANCE, kind=wx.ITEM_NORMAL,text='Copy substances',
1957            help='Copy substances')
1958        self.SubstanceEdit.Append(id=wxID_DELETESUBSTANCE, kind=wx.ITEM_NORMAL,text='Delete substance',
1959            help='Delete substance from list')           
1960        self.SubstanceEdit.Append(id=wxID_ELEMENTADD, kind=wx.ITEM_NORMAL,text='Add elements',
1961            help='Add elements to substance')
1962        self.SubstanceEdit.Append(id=wxID_ELEMENTDELETE, kind=wx.ITEM_NORMAL,text='Delete elements',
1963            help='Delete elements from substance')
1964        self.PostfillDataMenu()
1965       
1966        # SASD/ Models
1967        self.ModelMenu = wx.MenuBar()
1968        self.PrefillDataMenu(self.ModelMenu,helpType='Models')
1969        self.ModelEdit = wx.Menu(title='')
1970        self.ModelMenu.Append(menu=self.ModelEdit, title='Models')
1971        self.ModelEdit.Append(id=wxID_MODELADD,kind=wx.ITEM_NORMAL,text='Add',
1972            help='Add new term to model')
1973        self.ModelEdit.Append(id=wxID_MODELFIT, kind=wx.ITEM_NORMAL,text='Fit',
1974            help='Fit model parameters to data')
1975        self.SasdUndo = self.ModelEdit.Append(id=wxID_MODELUNDO, kind=wx.ITEM_NORMAL,text='Undo',
1976            help='Undo model fit')
1977        self.SasdUndo.Enable(False)           
1978        self.ModelEdit.Append(id=wxID_MODELFITALL, kind=wx.ITEM_NORMAL,text='Sequential fit',
1979            help='Sequential fit of model parameters to all SASD data')
1980        self.ModelEdit.Append(id=wxID_MODELCOPY, kind=wx.ITEM_NORMAL,text='Copy',
1981            help='Copy model parameters to other histograms')
1982        self.ModelEdit.Append(id=wxID_MODELCOPYFLAGS, kind=wx.ITEM_NORMAL,text='Copy flags',
1983            help='Copy model refinement flags to other histograms')
1984        self.PostfillDataMenu()
1985       
1986        # IMG / Image Controls
1987        self.ImageMenu = wx.MenuBar()
1988        self.PrefillDataMenu(self.ImageMenu,helpType='Image Controls')
1989        self.ImageEdit = wx.Menu(title='')
1990        self.ImageMenu.Append(menu=self.ImageEdit, title='Operations')
1991        self.ImageEdit.Append(help='Calibrate detector by fitting to calibrant lines', 
1992            id=wxID_IMCALIBRATE, kind=wx.ITEM_NORMAL,text='Calibrate')
1993        self.ImageEdit.Append(help='Recalibrate detector by fitting to calibrant lines', 
1994            id=wxID_IMRECALIBRATE, kind=wx.ITEM_NORMAL,text='Recalibrate')
1995        self.ImageEdit.Append(help='Recalibrate all images by fitting to calibrant lines', 
1996            id=wxID_IMRECALIBALL, kind=wx.ITEM_NORMAL,text='Recalibrate all')           
1997        self.ImageEdit.Append(help='Clear calibration data points and rings',id=wxID_IMCLEARCALIB, 
1998            kind=wx.ITEM_NORMAL,text='Clear calibration')
1999        self.ImageEdit.Append(help='Integrate selected image',id=wxID_IMINTEGRATE, 
2000            kind=wx.ITEM_NORMAL,text='Integrate')
2001        self.ImageEdit.Append(help='Integrate all images selected from list',id=wxID_INTEGRATEALL,
2002            kind=wx.ITEM_NORMAL,text='Integrate all')
2003        self.ImageEdit.Append(help='Copy image controls to other images', 
2004            id=wxID_IMCOPYCONTROLS, kind=wx.ITEM_NORMAL,text='Copy Controls')
2005        self.ImageEdit.Append(help='Copy selected image controls to other images', 
2006            id=wxID_IMCOPYSELECTED, kind=wx.ITEM_NORMAL,text='Copy Selected')
2007        self.ImageEdit.Append(help='Save image controls to file', 
2008            id=wxID_IMSAVECONTROLS, kind=wx.ITEM_NORMAL,text='Save Controls')
2009        self.ImageEdit.Append(help='Save controls from selected images to file', 
2010            id=wxID_SAVESELECTEDCONTROLS, kind=wx.ITEM_NORMAL,text='Save Multiple Controls')
2011        self.ImageEdit.Append(help='Load image controls from file', 
2012            id=wxID_IMLOADCONTROLS, kind=wx.ITEM_NORMAL,text='Load Controls')
2013        self.ImageEdit.Append(help='Open Auto-integration window to integrate a series of images', 
2014            id=wxID_IMAUTOINTEG, kind=wx.ITEM_NORMAL,text='Auto Integrate')
2015        self.PostfillDataMenu()
2016           
2017        # IMG / Masks
2018        self.MaskMenu = wx.MenuBar()
2019        self.PrefillDataMenu(self.MaskMenu,helpType='Image Masks')
2020        self.MaskEdit = wx.Menu(title='')
2021        self.MaskMenu.Append(menu=self.MaskEdit, title='Operations')
2022        submenu = wx.Menu()
2023        self.MaskEdit.AppendMenu(
2024            wx.ID_ANY,'Create new', submenu,
2025            help=''
2026            )
2027        self.MaskEdit.Append(help='Copy mask to other images', 
2028            id=wxID_MASKCOPY, kind=wx.ITEM_NORMAL,text='Copy mask')
2029        self.MaskEdit.Append(help='Save mask to file', 
2030            id=wxID_MASKSAVE, kind=wx.ITEM_NORMAL,text='Save mask')
2031        self.MaskEdit.Append(help='Load mask from file', 
2032            id=wxID_MASKLOAD, kind=wx.ITEM_NORMAL,text='Load mask')
2033        self.MaskEdit.Append(help='Load mask from file; ignore threshold', 
2034            id=wxID_MASKLOADNOT, kind=wx.ITEM_NORMAL,text='Load mask w/o threshold')
2035        submenu.Append(help='Create an arc mask with mouse input', 
2036            id=wxID_NEWMASKARC, kind=wx.ITEM_NORMAL,text='Arc mask')
2037        submenu.Append(help='Create a frame mask with mouse input', 
2038            id=wxID_NEWMASKFRAME, kind=wx.ITEM_NORMAL,text='Frame mask')
2039        submenu.Append(help='Create a polygon mask with mouse input', 
2040            id=wxID_NEWMASKPOLY, kind=wx.ITEM_NORMAL,text='Polygon mask')
2041        submenu.Append(help='Create a ring mask with mouse input', 
2042            id=wxID_NEWMASKRING, kind=wx.ITEM_NORMAL,text='Ring mask')
2043        submenu.Append(help='Create a spot mask with mouse input', 
2044            id=wxID_NEWMASKSPOT, kind=wx.ITEM_NORMAL,text='Spot mask')
2045        self.PostfillDataMenu()
2046           
2047        # IMG / Stress/Strain
2048        self.StrStaMenu = wx.MenuBar()
2049        self.PrefillDataMenu(self.StrStaMenu,helpType='Stress/Strain')
2050        self.StrStaEdit = wx.Menu(title='')
2051        self.StrStaMenu.Append(menu=self.StrStaEdit, title='Operations')
2052        self.StrStaEdit.Append(help='Append d-zero for one ring', 
2053            id=wxID_APPENDDZERO, kind=wx.ITEM_NORMAL,text='Append d-zero')
2054        self.StrStaEdit.Append(help='Fit stress/strain data', 
2055            id=wxID_STRSTAFIT, kind=wx.ITEM_NORMAL,text='Fit stress/strain')
2056        self.StrStaEdit.Append(help='Plot intensity distribution', 
2057            id=wxID_STRSTAPLOT, kind=wx.ITEM_NORMAL,text='Plot intensity distribution')
2058        self.StrStaEdit.Append(help='Update d-zero from ave d-zero',
2059            id=wxID_UPDATEDZERO, kind=wx.ITEM_NORMAL,text='Update d-zero')       
2060        self.StrStaEdit.Append(help='Fit stress/strain data for all images', 
2061            id=wxID_STRSTAALLFIT, kind=wx.ITEM_NORMAL,text='All image fit')
2062        self.StrStaEdit.Append(help='Copy stress/strain data to other images', 
2063            id=wxID_STRSTACOPY, kind=wx.ITEM_NORMAL,text='Copy stress/strain')
2064        self.StrStaEdit.Append(help='Save stress/strain data to file', 
2065            id=wxID_STRSTASAVE, kind=wx.ITEM_NORMAL,text='Save stress/strain')
2066        self.StrStaEdit.Append(help='Load stress/strain data from file', 
2067            id=wxID_STRSTALOAD, kind=wx.ITEM_NORMAL,text='Load stress/strain')
2068        self.StrStaEdit.Append(help='Load sample data from file', 
2069            id=wxID_STRSTSAMPLE, kind=wx.ITEM_NORMAL,text='Load sample data')
2070        self.PostfillDataMenu()
2071           
2072        # PDF / PDF Controls
2073        self.PDFMenu = wx.MenuBar()
2074        self.PrefillDataMenu(self.PDFMenu,helpType='PDF Controls')
2075        self.PDFEdit = wx.Menu(title='')
2076        self.PDFMenu.Append(menu=self.PDFEdit, title='PDF Controls')
2077        self.PDFEdit.Append(help='Add element to sample composition',id=wxID_PDFADDELEMENT, kind=wx.ITEM_NORMAL,
2078            text='Add element')
2079        self.PDFEdit.Append(help='Delete element from sample composition',id=wxID_PDFDELELEMENT, kind=wx.ITEM_NORMAL,
2080            text='Delete element')
2081        self.PDFEdit.Append(help='Copy PDF controls', id=wxID_PDFCOPYCONTROLS, kind=wx.ITEM_NORMAL,
2082            text='Copy controls')
2083        self.PDFEdit.Append(help='Load PDF controls from file',id=wxID_PDFLOADCONTROLS, kind=wx.ITEM_NORMAL,
2084            text='Load Controls')
2085        self.PDFEdit.Append(help='Save PDF controls to file', id=wxID_PDFSAVECONTROLS, kind=wx.ITEM_NORMAL,
2086            text='Save controls')
2087        self.PDFEdit.Append(help='Compute PDF', id=wxID_PDFCOMPUTE, kind=wx.ITEM_NORMAL,
2088            text='Compute PDF')
2089        self.PDFEdit.Append(help='Compute all PDFs', id=wxID_PDFCOMPUTEALL, kind=wx.ITEM_NORMAL,
2090            text='Compute all PDFs')
2091        self.PostfillDataMenu()
2092       
2093        # Phase / General tab
2094        self.DataGeneral = wx.MenuBar()
2095        self.PrefillDataMenu(self.DataGeneral,helpType='General', helpLbl='Phase/General')
2096        self.DataGeneral.Append(menu=wx.Menu(title=''),title='Select tab')
2097        self.GeneralCalc = wx.Menu(title='')
2098        self.DataGeneral.Append(menu=self.GeneralCalc,title='Compute')
2099        self.GeneralCalc.Append(help='Compute Fourier map',id=wxID_FOURCALC, kind=wx.ITEM_NORMAL,
2100            text='Fourier map')
2101        self.GeneralCalc.Append(help='Search Fourier map',id=wxID_FOURSEARCH, kind=wx.ITEM_NORMAL,
2102            text='Search map')
2103        self.GeneralCalc.Append(help='Run charge flipping',id=wxID_CHARGEFLIP, kind=wx.ITEM_NORMAL,
2104            text='Charge flipping')
2105        self.GeneralCalc.Append(help='Run 4D charge flipping',id=wxID_4DCHARGEFLIP, kind=wx.ITEM_NORMAL,
2106            text='4D Charge flipping')
2107        self.GeneralCalc.Enable(wxID_4DCHARGEFLIP,False)   
2108        self.GeneralCalc.Append(help='Clear map',id=wxID_FOURCLEAR, kind=wx.ITEM_NORMAL,
2109            text='Clear map')
2110        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing',id=wxID_SINGLEMCSA, kind=wx.ITEM_NORMAL,
2111            text='MC/SA')
2112        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing on multiprocessors',id=wxID_MULTIMCSA, kind=wx.ITEM_NORMAL,
2113            text='Multi MC/SA')            #currently not useful
2114        self.GeneralCalc.Append(help='Transform crystal structure',id=wxID_TRANSFORMSTRUCTURE, kind=wx.ITEM_NORMAL,
2115            text='Transform')
2116        self.PostfillDataMenu()
2117       
2118        # Phase / Data tab
2119        self.DataMenu = wx.MenuBar()
2120        self.PrefillDataMenu(self.DataMenu,helpType='Data', helpLbl='Phase/Data')
2121        self.DataMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2122        self.DataEdit = wx.Menu(title='')
2123        self.DataMenu.Append(menu=self.DataEdit, title='Edit Phase')
2124        self.DataEdit.Append(id=wxID_DATACOPY, kind=wx.ITEM_NORMAL,text='Copy data',
2125            help='Copy phase data to other histograms')
2126        self.DataEdit.Append(id=wxID_DATACOPYFLAGS, kind=wx.ITEM_NORMAL,text='Copy flags',
2127            help='Copy phase data flags to other histograms')
2128        self.DataEdit.Append(id=wxID_DATASELCOPY, kind=wx.ITEM_NORMAL,text='Copy selected data',
2129            help='Copy selected phase data to other histograms')
2130        self.DataEdit.Append(id=wxID_DATAUSE, kind=wx.ITEM_NORMAL,text='Select used data',
2131            help='Select all histograms to use')
2132        self.DataEdit.Append(id=wxID_PWDRADD, kind=wx.ITEM_NORMAL,text='Add powder histograms',
2133            help='Select new powder histograms to be used for this phase')
2134        self.DataEdit.Append(id=wxID_HKLFADD, kind=wx.ITEM_NORMAL,text='Add single crystal histograms',
2135            help='Select new single crystal histograms to be used for this phase')
2136        self.DataEdit.Append(id=wxID_DATADELETE, kind=wx.ITEM_NORMAL,text='Remove histograms',
2137            help='Remove histograms from use for this phase')
2138        self.PostfillDataMenu()
2139           
2140        # Phase / Atoms tab
2141        self.AtomsMenu = wx.MenuBar()
2142        self.PrefillDataMenu(self.AtomsMenu,helpType='Atoms')
2143        self.AtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2144        self.AtomEdit = wx.Menu(title='')
2145        self.AtomCompute = wx.Menu(title='')
2146        self.AtomsMenu.Append(menu=self.AtomEdit, title='Edit Atoms')
2147        self.AtomsMenu.Append(menu=self.AtomCompute, title='Compute')
2148        self.AtomEdit.Append(id=wxID_ATOMSEDITADD, kind=wx.ITEM_NORMAL,text='Append atom',
2149            help='Appended as an H atom')
2150        self.AtomEdit.Append(id=wxID_ATOMSVIEWADD, kind=wx.ITEM_NORMAL,text='Append view point',
2151            help='Appended as an H atom')
2152        self.AtomEdit.Append(id=wxID_ATOMSEDITINSERT, kind=wx.ITEM_NORMAL,text='Insert atom',
2153            help='Select atom row to insert before; inserted as an H atom')
2154        self.AtomEdit.Append(id=wxID_ATOMVIEWINSERT, kind=wx.ITEM_NORMAL,text='Insert view point',
2155            help='Select atom row to insert before; inserted as an H atom')
2156        self.AtomEdit.Append(id=wxID_ADDHATOM, kind=wx.ITEM_NORMAL,text='Insert H atoms',
2157            help='Insert H atoms in standard positions bonded to selected atoms')
2158        self.AtomEdit.Append(id=wxID_UPDATEHATOM, kind=wx.ITEM_NORMAL,text='Update H atoms',
2159            help='Update H atoms in standard positions')
2160        self.AtomEdit.Append(id=wxID_ATOMMOVE, kind=wx.ITEM_NORMAL,text='Move atom to view point',
2161            help='Select single atom to move')
2162        self.AtomEdit.Append(id=wxID_ATOMSEDITDELETE, kind=wx.ITEM_NORMAL,text='Delete atom',
2163            help='Select atoms to delete first')
2164        self.AtomEdit.Append(id=wxID_ATOMSREFINE, kind=wx.ITEM_NORMAL,text='Set atom refinement flags',
2165            help='Select atoms to refine first')
2166        self.AtomEdit.Append(id=wxID_ATOMSMODIFY, kind=wx.ITEM_NORMAL,text='Modify atom parameters',
2167            help='Select atoms to modify first')
2168        self.AtomEdit.Append(id=wxID_ATOMSTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
2169            help='Select atoms to transform first')
2170#        self.AtomEdit.Append(id=wxID_ATOMSROTATE, kind=wx.ITEM_NORMAL,text='Rotate atoms',
2171#            help='Select atoms to rotate first')
2172        self.AtomEdit.Append(id=wxID_MAKEMOLECULE, kind=wx.ITEM_NORMAL,text='Assemble molecule',
2173            help='Assemble molecule from scatterd atom positions')
2174        self.AtomEdit.Append(id=wxID_RELOADDRAWATOMS, kind=wx.ITEM_NORMAL,text='Reload draw atoms',
2175            help='Reload atom drawing list')
2176        submenu = wx.Menu()
2177        self.AtomEdit.AppendMenu(wx.ID_ANY, 'Reimport atoms', submenu, 
2178            help='Reimport atoms from file; sequence must match')
2179        # setup a cascade menu for the formats that have been defined
2180        self.ReImportMenuId = {}  # points to readers for each menu entry
2181        for reader in self.G2frame.ImportPhaseReaderlist:
2182            item = submenu.Append(
2183                wx.ID_ANY,help=reader.longFormatName,
2184                kind=wx.ITEM_NORMAL,text='reimport coordinates from '+reader.formatName+' file')
2185            self.ReImportMenuId[item.GetId()] = reader
2186        item = submenu.Append(
2187            wx.ID_ANY,
2188            help='Reimport coordinates, try to determine format from file',
2189            kind=wx.ITEM_NORMAL,
2190            text='guess format from file')
2191        self.ReImportMenuId[item.GetId()] = None # try all readers
2192
2193        self.AtomCompute.Append(id=wxID_ATOMSDISAGL, kind=wx.ITEM_NORMAL,text='Show Distances && Angles',
2194            help='Compute distances & angles for selected atoms')
2195        self.AtomCompute.Append(id=wxID_ATOMSPDISAGL, kind=wx.ITEM_NORMAL,text='Save Distances && Angles',
2196            help='Compute distances & angles for selected atoms')
2197        self.AtomCompute.ISOcalc = self.AtomCompute.Append(
2198            id=wxID_ISODISP, kind=wx.ITEM_NORMAL,
2199            text='ISODISTORT mode values',
2200            help='Compute values of ISODISTORT modes from atom parameters')
2201        self.AtomCompute.Append(id=wxID_ATOMSDENSITY, kind=wx.ITEM_NORMAL,
2202            text='Density',
2203            help='Compute density for current phase')
2204        self.PostfillDataMenu()
2205       
2206        # Phase / Imcommensurate "waves" tab
2207        self.WavesData = wx.MenuBar()
2208        self.PrefillDataMenu(self.WavesData,helpType='Wave Data', helpLbl='Imcommensurate wave data')
2209        self.WavesData.Append(menu=wx.Menu(title=''),title='Select tab')
2210        self.WavesDataEdit = wx.Menu(title='')
2211        self.WavesData.Append(menu=self.WavesDataEdit, title='Edit Wave')
2212        self.WavesDataEdit.Append(id=wxID_WAVEVARY, kind=wx.ITEM_NORMAL,text='Global wave vary',
2213            help='Global setting of wave vary flags')
2214        self.PostfillDataMenu()
2215       
2216        # Phase / Layer tab
2217        self.LayerData = wx.MenuBar()
2218        self.PrefillDataMenu(self.LayerData,helpType='Layer Data', helpLbl='Stacking fault layers')
2219        self.LayerData.Append(menu=wx.Menu(title=''),title='Select tab')
2220        self.LayerDataEdit = wx.Menu(title='')
2221        self.LayerData.Append(menu=self.LayerDataEdit, title='Operations')
2222        self.LayerDataEdit.Append(id=wxID_LOADDIFFAX, kind=wx.ITEM_NORMAL,text='Load from DIFFaX file',
2223            help='Load layer info from DIFFaX file')
2224        self.LayerDataEdit.Append(id=wxID_COPYPHASE, kind=wx.ITEM_NORMAL,text='Copy phase cell',
2225            help='Copy phase cell from another project')
2226        self.LayerDataEdit.Append(id=wxID_LAYERSIMULATE, kind=wx.ITEM_NORMAL,text='Simulate pattern',
2227            help='Simulate diffraction pattern from layer stacking')
2228        self.LayerDataEdit.Append(id=wxID_SEQUENCESIMULATE, kind=wx.ITEM_NORMAL,text='Sequence simulations',
2229            help='Sequence simulation changing one parameter')
2230        self.PostfillDataMenu()
2231                 
2232        # Phase / Draw Options tab
2233        self.DataDrawOptions = wx.MenuBar()
2234        self.PrefillDataMenu(self.DataDrawOptions,helpType='Draw Options', helpLbl='Phase/Draw Options')
2235        self.DataDrawOptions.Append(menu=wx.Menu(title=''),title='Select tab')
2236        self.PostfillDataMenu()
2237       
2238        # Phase / Draw Atoms tab
2239        self.DrawAtomsMenu = wx.MenuBar()
2240        self.PrefillDataMenu(self.DrawAtomsMenu,helpType='Draw Atoms')
2241        self.DrawAtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2242        self.DrawAtomEdit = wx.Menu(title='')
2243        self.DrawAtomCompute = wx.Menu(title='')
2244        self.DrawAtomRestraint = wx.Menu(title='')
2245        self.DrawAtomRigidBody = wx.Menu(title='')
2246        self.DrawAtomsMenu.Append(menu=self.DrawAtomEdit, title='Edit Figure')
2247        self.DrawAtomsMenu.Append(menu=self.DrawAtomCompute,title='Compute')
2248        self.DrawAtomsMenu.Append(menu=self.DrawAtomRestraint, title='Restraints')
2249        self.DrawAtomsMenu.Append(menu=self.DrawAtomRigidBody, title='Rigid body')
2250        self.DrawAtomEdit.Append(id=wxID_DRAWATOMSTYLE, kind=wx.ITEM_NORMAL,text='Atom style',
2251            help='Select atoms first')
2252        self.DrawAtomEdit.Append(id=wxID_DRAWATOMLABEL, kind=wx.ITEM_NORMAL,text='Atom label',
2253            help='Select atoms first')
2254        self.DrawAtomEdit.Append(id=wxID_DRAWATOMCOLOR, kind=wx.ITEM_NORMAL,text='Atom color',
2255            help='Select atoms first')
2256        self.DrawAtomEdit.Append(id=wxID_DRAWATOMRESETCOLOR, kind=wx.ITEM_NORMAL,text='Reset atom colors',
2257            help='Resets all atom colors to defaults')
2258        self.DrawAtomEdit.Append(id=wxID_DRAWVIEWPOINT, kind=wx.ITEM_NORMAL,text='View point',
2259            help='View point is 1st atom selected')
2260        self.DrawAtomEdit.Append(id=wxID_DRAWADDEQUIV, kind=wx.ITEM_NORMAL,text='Add atoms',
2261            help='Add symmetry & cell equivalents to drawing set from selected atoms')
2262        self.DrawAtomEdit.Append(id=wxID_DRAWADDSPHERE, kind=wx.ITEM_NORMAL,text='Add sphere of atoms',
2263            help='Add atoms within sphere of enclosure')
2264        self.DrawAtomEdit.Append(id=wxID_DRAWTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform draw atoms',
2265            help='Transform selected atoms by symmetry & cell translations')
2266        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCOORD, kind=wx.ITEM_NORMAL,text='Fill CN-sphere',
2267            help='Fill coordination sphere for selected atoms')           
2268        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCELL, kind=wx.ITEM_NORMAL,text='Fill unit cell',
2269            help='Fill unit cell with selected atoms')
2270        self.DrawAtomEdit.Append(id=wxID_DRAWDELETE, kind=wx.ITEM_NORMAL,text='Delete atoms',
2271            help='Delete atoms from drawing set')
2272        self.DrawAtomCompute.Append(id=wxID_DRAWDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2273            help='Compute distance of selected atoms from view point')   
2274        self.DrawAtomCompute.Append(id=wxID_DRAWDISAGLTOR, kind=wx.ITEM_NORMAL,text='Dist. Ang. Tors.',
2275            help='Compute distance, angle or torsion for 2-4 selected atoms')   
2276        self.DrawAtomCompute.Append(id=wxID_DRAWPLANE, kind=wx.ITEM_NORMAL,text='Best plane',
2277            help='Compute best plane for 4+ selected atoms')   
2278        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRBOND, kind=wx.ITEM_NORMAL,text='Add bond restraint',
2279            help='Add bond restraint for selected atoms (2)')
2280        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRANGLE, kind=wx.ITEM_NORMAL,text='Add angle restraint',
2281            help='Add angle restraint for selected atoms (3: one end 1st)')
2282        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRPLANE, kind=wx.ITEM_NORMAL,text='Add plane restraint',
2283            help='Add plane restraint for selected atoms (4+)')
2284        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRCHIRAL, kind=wx.ITEM_NORMAL,text='Add chiral restraint',
2285            help='Add chiral restraint for selected atoms (4: center atom 1st)')
2286        self.DrawAtomRigidBody.Append(id=wxID_DRAWDEFINERB, kind=wx.ITEM_NORMAL,text='Define rigid body',
2287            help='Define rigid body with selected atoms')
2288        self.PostfillDataMenu()
2289
2290        # Phase / MCSA tab
2291        self.MCSAMenu = wx.MenuBar()
2292        self.PrefillDataMenu(self.MCSAMenu,helpType='MC/SA')
2293        self.MCSAMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2294        self.MCSAEdit = wx.Menu(title='')
2295        self.MCSAMenu.Append(menu=self.MCSAEdit, title='MC/SA')
2296        self.MCSAEdit.Append(id=wxID_ADDMCSAATOM, kind=wx.ITEM_NORMAL,text='Add atom', 
2297            help='Add single atom to MC/SA model')
2298        self.MCSAEdit.Append(id=wxID_ADDMCSARB, kind=wx.ITEM_NORMAL,text='Add rigid body', 
2299            help='Add rigid body to MC/SA model' )
2300        self.MCSAEdit.Append(id=wxID_CLEARMCSARB, kind=wx.ITEM_NORMAL,text='Clear rigid bodies', 
2301            help='Clear all atoms & rigid bodies from MC/SA model' )
2302        self.MCSAEdit.Append(id=wxID_MOVEMCSA, kind=wx.ITEM_NORMAL,text='Move MC/SA solution', 
2303            help='Move MC/SA solution to atom list' )
2304        self.MCSAEdit.Append(id=wxID_MCSACLEARRESULTS, kind=wx.ITEM_NORMAL,text='Clear results', 
2305            help='Clear table of MC/SA results' )
2306        self.PostfillDataMenu()
2307           
2308        # Phase / Texture tab
2309        self.TextureMenu = wx.MenuBar()
2310        self.PrefillDataMenu(self.TextureMenu,helpType='Texture')
2311        self.TextureMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2312        self.TextureEdit = wx.Menu(title='')
2313        self.TextureMenu.Append(menu=self.TextureEdit, title='Texture')
2314        self.TextureEdit.Append(id=wxID_REFINETEXTURE, kind=wx.ITEM_NORMAL,text='Refine texture', 
2315            help='Refine the texture coefficients from sequential results')
2316#        self.TextureEdit.Append(id=wxID_CLEARTEXTURE, kind=wx.ITEM_NORMAL,text='Clear texture',
2317#            help='Clear the texture coefficients' )
2318        self.PostfillDataMenu()
2319           
2320        # Phase / Pawley tab
2321        self.PawleyMenu = wx.MenuBar()
2322        self.PrefillDataMenu(self.PawleyMenu,helpType='Pawley')
2323        self.PawleyMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2324        self.PawleyEdit = wx.Menu(title='')
2325        self.PawleyMenu.Append(menu=self.PawleyEdit,title='Operations')
2326        self.PawleyEdit.Append(id=wxID_PAWLEYLOAD, kind=wx.ITEM_NORMAL,text='Pawley create',
2327            help='Initialize Pawley reflection list')
2328        self.PawleyEdit.Append(id=wxID_PAWLEYESTIMATE, kind=wx.ITEM_NORMAL,text='Pawley estimate',
2329            help='Estimate initial Pawley intensities')
2330        self.PawleyEdit.Append(id=wxID_PAWLEYUPDATE, kind=wx.ITEM_NORMAL,text='Pawley update',
2331            help='Update negative Pawley intensities with -0.5*Fobs and turn off refinemnt')
2332        self.PostfillDataMenu()
2333           
2334        # Phase / Map peaks tab
2335        self.MapPeaksMenu = wx.MenuBar()
2336        self.PrefillDataMenu(self.MapPeaksMenu,helpType='Map peaks')
2337        self.MapPeaksMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2338        self.MapPeaksEdit = wx.Menu(title='')
2339        self.MapPeaksMenu.Append(menu=self.MapPeaksEdit, title='Map peaks')
2340        self.MapPeaksEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks', 
2341            help='Move selected peaks to atom list')
2342        self.MapPeaksEdit.Append(id=wxID_PEAKSVIEWPT, kind=wx.ITEM_NORMAL,text='View point',
2343            help='View point is 1st peak selected')
2344        self.MapPeaksEdit.Append(id=wxID_PEAKSDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2345            help='Compute distance of selected peaks from view point')   
2346        self.MapPeaksEdit.Append(id=wxID_SHOWBONDS, kind=wx.ITEM_NORMAL,text='Hide bonds',
2347            help='Hide or show bonds between peak positions')   
2348        self.MapPeaksEdit.Append(id=wxID_PEAKSDA, kind=wx.ITEM_NORMAL,text='Calc dist/ang', 
2349            help='Calculate distance or angle for selection')
2350        self.MapPeaksEdit.Append(id=wxID_FINDEQVPEAKS, kind=wx.ITEM_NORMAL,text='Equivalent peaks', 
2351            help='Find equivalent peaks')
2352        self.MapPeaksEdit.Append(id=wxID_PEAKSUNIQUE, kind=wx.ITEM_NORMAL,text='Unique peaks', 
2353            help='Select unique set')
2354        self.MapPeaksEdit.Append(id=wxID_PEAKSDELETE, kind=wx.ITEM_NORMAL,text='Delete peaks', 
2355            help='Delete selected peaks')
2356        self.MapPeaksEdit.Append(id=wxID_PEAKSCLEAR, kind=wx.ITEM_NORMAL,text='Clear peaks', 
2357            help='Clear the map peak list')
2358        self.PostfillDataMenu()
2359
2360        # Phase / Rigid bodies tab
2361        self.RigidBodiesMenu = wx.MenuBar()
2362        self.PrefillDataMenu(self.RigidBodiesMenu,helpType='Rigid bodies')
2363        self.RigidBodiesMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2364        self.RigidBodiesEdit = wx.Menu(title='')
2365        self.RigidBodiesMenu.Append(menu=self.RigidBodiesEdit, title='Edit Body')
2366        self.RigidBodiesEdit.Append(id=wxID_ASSIGNATMS2RB, kind=wx.ITEM_NORMAL,text='Assign atoms to rigid body',
2367            help='Select & position rigid body in structure of existing atoms')
2368        self.RigidBodiesEdit.Append(id=wxID_AUTOFINDRESRB, kind=wx.ITEM_NORMAL,text='Auto find residues',
2369            help='Auto find of residue RBs in macromolecule')
2370        self.RigidBodiesEdit.Append(id=wxID_COPYRBPARMS, kind=wx.ITEM_NORMAL,text='Copy rigid body parms',
2371            help='Copy rigid body location & TLS parameters')
2372        self.RigidBodiesEdit.Append(id=wxID_GLOBALTHERM, kind=wx.ITEM_NORMAL,text='Global thermal motion',
2373            help='Global setting of residue thermal motion models')
2374        self.RigidBodiesEdit.Append(id=wxID_GLOBALRESREFINE, kind=wx.ITEM_NORMAL,text='Global residue refine',
2375            help='Global setting of residue RB refinement flags')
2376        self.RigidBodiesEdit.Append(id=wxID_RBREMOVEALL, kind=wx.ITEM_NORMAL,text='Remove all rigid bodies',
2377            help='Remove all rigid body assignment for atoms')
2378        self.PostfillDataMenu()
2379    # end of GSAS-II menu definitions
2380       
2381    def _init_ctrls(self, parent,name=None,size=None,pos=None):
2382        wx.Frame.__init__(
2383            self,parent=parent,
2384            #style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX | wx.FRAME_FLOAT_ON_PARENT ,
2385            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX,
2386            size=size,pos=pos,title='GSAS-II data display')
2387        self._init_menus()
2388        if name:
2389            self.SetLabel(name)
2390        self.Show()
2391       
2392    def __init__(self,parent,frame,data=None,name=None, size=None,pos=None):
2393        self.G2frame = frame
2394        self._init_ctrls(parent,name,size,pos)
2395        self.data = data
2396        clientSize = wx.ClientDisplayRect()
2397        Size = self.GetSize()
2398        xPos = clientSize[2]-Size[0]
2399        self.SetPosition(wx.Point(xPos,clientSize[1]+250))
2400        self.AtomGrid = []
2401        self.selectedRow = 0
2402       
2403    def setSizePosLeft(self,Width):
2404        clientSize = wx.ClientDisplayRect()
2405        Width[1] = min(Width[1],clientSize[2]-300)
2406        Width[0] = max(Width[0],300)
2407        self.SetSize(Width)
2408#        self.SetPosition(wx.Point(clientSize[2]-Width[0],clientSize[1]+250))
2409       
2410    def Clear(self):
2411        self.ClearBackground()
2412        self.DestroyChildren()
2413                   
2414
2415################################################################################
2416#####  Notebook Tree Item editor
2417################################################################################                 
2418def UpdateNotebook(G2frame,data):
2419    '''Called when the data tree notebook entry is selected. Allows for
2420    editing of the text in that tree entry
2421    '''
2422    def OnNoteBook(event):
2423        event.Skip()
2424        data = G2frame.dataDisplay.GetValue().split('\n')
2425        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Notebook'),data)
2426        if 'nt' not in os.name:
2427            G2frame.dataDisplay.AppendText('\n')
2428                   
2429    if G2frame.dataDisplay:
2430        G2frame.dataDisplay.Destroy()
2431    G2frame.dataFrame.SetLabel('Notebook')
2432    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2433        style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
2434    G2frame.dataDisplay.Bind(wx.EVT_TEXT_ENTER,OnNoteBook)
2435    G2frame.dataDisplay.Bind(wx.EVT_KILL_FOCUS,OnNoteBook)
2436    for line in data:
2437        G2frame.dataDisplay.AppendText(line+"\n")
2438    G2frame.dataDisplay.AppendText('Notebook entry @ '+time.ctime()+"\n")
2439    G2frame.dataFrame.setSizePosLeft([400,250])
2440           
2441################################################################################
2442#####  Controls Tree Item editor
2443################################################################################           
2444def UpdateControls(G2frame,data):
2445    '''Edit overall GSAS-II controls in main Controls data tree entry
2446    '''
2447    #patch
2448    if 'deriv type' not in data:
2449        data = {}
2450        data['deriv type'] = 'analytic Hessian'
2451        data['min dM/M'] = 0.0001
2452        data['shift factor'] = 1.
2453        data['max cyc'] = 3       
2454        data['F**2'] = False
2455    if 'shift factor' not in data:
2456        data['shift factor'] = 1.
2457    if 'max cyc' not in data:
2458        data['max cyc'] = 3
2459    if 'F**2' not in data:
2460        data['F**2'] = False
2461    if 'Author' not in data:
2462        data['Author'] = 'no name'
2463    if 'FreePrm1' not in data:
2464        data['FreePrm1'] = 'Sample humidity (%)'
2465    if 'FreePrm2' not in data:
2466        data['FreePrm2'] = 'Sample voltage (V)'
2467    if 'FreePrm3' not in data:
2468        data['FreePrm3'] = 'Applied load (MN)'
2469    if 'Copy2Next' not in data:
2470        data['Copy2Next'] = False
2471    if 'Reverse Seq' not in data:
2472        data['Reverse Seq'] = False
2473    if 'UsrReject' not in data:
2474        data['UsrReject'] = {'minF/sig':0,'MinExt':0.01,'MaxDF/F':20.,'MaxD':500.,'MinD':0.05}
2475    if 'HatomFix' not in data:
2476        data['HatomFix'] = False
2477    if 'Marquardt' not in data:
2478        data['Marquardt'] = -3
2479   
2480    #end patch
2481
2482    def SeqSizer():
2483       
2484        def OnSelectData(event):
2485            choices = GetPatternTreeDataNames(G2frame,['PWDR','HKLF',])
2486            sel = []
2487            try:
2488                if 'Seq Data' in data:
2489                    for item in data['Seq Data']:
2490                        sel.append(choices.index(item))
2491                    sel = [choices.index(item) for item in data['Seq Data']]
2492            except ValueError:  #data changed somehow - start fresh
2493                sel = []
2494            dlg = G2G.G2MultiChoiceDialog(G2frame.dataFrame, 'Sequential refinement',
2495                'Select dataset to include',choices)
2496            dlg.SetSelections(sel)
2497            names = []
2498            if dlg.ShowModal() == wx.ID_OK:
2499                for sel in dlg.GetSelections():
2500                    names.append(choices[sel])
2501                data['Seq Data'] = names               
2502                G2frame.EnableSeqRefineMenu()
2503            dlg.Destroy()
2504            wx.CallAfter(UpdateControls,G2frame,data)
2505           
2506        def OnReverse(event):
2507            data['Reverse Seq'] = reverseSel.GetValue()
2508           
2509        def OnCopySel(event):
2510            data['Copy2Next'] = copySel.GetValue() 
2511                   
2512        seqSizer = wx.BoxSizer(wx.VERTICAL)
2513        dataSizer = wx.BoxSizer(wx.HORIZONTAL)
2514        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Sequential Refinement: '),0,WACV)
2515        selSeqData = wx.Button(G2frame.dataDisplay,-1,label=' Select data')
2516        selSeqData.Bind(wx.EVT_BUTTON,OnSelectData)
2517        dataSizer.Add(selSeqData,0,WACV)
2518        SeqData = data.get('Seq Data',[])
2519        if not SeqData:
2520            lbl = ' (no data selected)'
2521        else:
2522            lbl = ' ('+str(len(SeqData))+' dataset(s) selected)'
2523
2524        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=lbl),0,WACV)
2525        seqSizer.Add(dataSizer,0)
2526        if SeqData:
2527            selSizer = wx.BoxSizer(wx.HORIZONTAL)
2528            reverseSel = wx.CheckBox(G2frame.dataDisplay,-1,label=' Reverse order?')
2529            reverseSel.Bind(wx.EVT_CHECKBOX,OnReverse)
2530            reverseSel.SetValue(data['Reverse Seq'])
2531            selSizer.Add(reverseSel,0,WACV)
2532            copySel =  wx.CheckBox(G2frame.dataDisplay,-1,label=' Copy results to next histogram?')
2533            copySel.Bind(wx.EVT_CHECKBOX,OnCopySel)
2534            copySel.SetValue(data['Copy2Next'])
2535            selSizer.Add(copySel,0,WACV)
2536            seqSizer.Add(selSizer,0)
2537        return seqSizer
2538       
2539    def LSSizer():       
2540       
2541        def OnDerivType(event):
2542            data['deriv type'] = derivSel.GetValue()
2543            derivSel.SetValue(data['deriv type'])
2544            wx.CallAfter(UpdateControls,G2frame,data)
2545           
2546        def OnConvergence(event):
2547            event.Skip()
2548            try:
2549                value = max(1.e-9,min(1.0,float(Cnvrg.GetValue())))
2550            except ValueError:
2551                value = 0.0001
2552            data['min dM/M'] = value
2553            Cnvrg.SetValue('%.2g'%(value))
2554           
2555        def OnMaxCycles(event):
2556            data['max cyc'] = int(maxCyc.GetValue())
2557            maxCyc.SetValue(str(data['max cyc']))
2558           
2559        def OnMarqLam(event):
2560            data['Marquardt'] = int(marqLam.GetValue())
2561            marqLam.SetValue(str(data['Marquardt']))
2562                       
2563        def OnFactor(event):
2564            event.Skip()
2565            try:
2566                value = min(max(float(Factr.GetValue()),0.00001),100.)
2567            except ValueError:
2568                value = 1.0
2569            data['shift factor'] = value
2570            Factr.SetValue('%.5f'%(value))
2571           
2572        def OnFsqRef(event):
2573            data['F**2'] = fsqRef.GetValue()
2574           
2575        def OnHatomFix(event):
2576            data['HatomFix'] = Hfix.GetValue()
2577       
2578        def OnUsrRej(event):
2579            event.Skip()
2580            Obj = event.GetEventObject()
2581            item,limits = Indx[Obj]
2582            try:
2583                value = min(max(float(Obj.GetValue()),limits[0]),limits[1])
2584            except ValueError:
2585                value = data['UsrReject'][item]
2586            data['UsrReject'][item] = value
2587            Obj.SetValue('%.2f'%(value))
2588
2589        LSSizer = wx.FlexGridSizer(cols=4,vgap=5,hgap=5)
2590        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement derivatives: '),0,WACV)
2591        Choice=['analytic Jacobian','numeric','analytic Hessian']
2592        derivSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['deriv type'],choices=Choice,
2593            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2594        derivSel.SetValue(data['deriv type'])
2595        derivSel.Bind(wx.EVT_COMBOBOX, OnDerivType)
2596           
2597        LSSizer.Add(derivSel,0,WACV)
2598        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min delta-M/M: '),0,WACV)
2599        Cnvrg = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2g'%(data['min dM/M']),style=wx.TE_PROCESS_ENTER)
2600        Cnvrg.Bind(wx.EVT_TEXT_ENTER,OnConvergence)
2601        Cnvrg.Bind(wx.EVT_KILL_FOCUS,OnConvergence)
2602        LSSizer.Add(Cnvrg,0,WACV)
2603        Indx = {}
2604        if 'Hessian' in data['deriv type']:
2605            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Max cycles: '),0,WACV)
2606            Choice = ['0','1','2','3','5','10','15','20']
2607            maxCyc = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['max cyc']),choices=Choice,
2608                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2609#            maxCyc.SetValue(str(data['max cyc']))
2610            maxCyc.Bind(wx.EVT_COMBOBOX, OnMaxCycles)
2611            LSSizer.Add(maxCyc,0,WACV)
2612            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Initial lambda = 10**'),0,WACV)
2613            MarqChoice = ['-3','-2','-1','0','1','2','3','4']
2614            marqLam = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['Marquardt']),choices=MarqChoice,
2615                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2616            marqLam.Bind(wx.EVT_COMBOBOX,OnMarqLam)
2617            LSSizer.Add(marqLam,0,WACV)
2618        else:
2619            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Initial shift factor: '),0,WACV)
2620            Factr = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.5f'%(data['shift factor']),style=wx.TE_PROCESS_ENTER)
2621            Factr.Bind(wx.EVT_TEXT_ENTER,OnFactor)
2622            Factr.Bind(wx.EVT_KILL_FOCUS,OnFactor)
2623            LSSizer.Add(Factr,0,WACV)
2624        if G2frame.Sngl:
2625            userReject = data['UsrReject']
2626            usrRej = {'minF/sig':[' Min obs/sig (0-5): ',[0,5], ],'MinExt':[' Min extinct. (0-.9): ',[0,.9],],
2627                'MaxDF/F':[' Max delt-F/sig (3-1000): ',[3.,1000.],],'MaxD':[' Max d-spacing (3-500): ',[3,500],],
2628                'MinD':[' Min d-spacing (0.1-2.0): ',[0.1,2.0],]}
2629
2630            fsqRef = wx.CheckBox(G2frame.dataDisplay,-1,label='Refine HKLF as F^2? ')
2631            fsqRef.SetValue(data['F**2'])
2632            fsqRef.Bind(wx.EVT_CHECKBOX,OnFsqRef)
2633            LSSizer.Add(fsqRef,0,WACV)
2634            LSSizer.Add((1,0),)
2635            for item in usrRej:
2636                LSSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=usrRej[item][0]),0,WACV)
2637                usrrej = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(userReject[item]),style=wx.TE_PROCESS_ENTER)
2638                Indx[usrrej] = [item,usrRej[item][1]]
2639                usrrej.Bind(wx.EVT_TEXT_ENTER,OnUsrRej)
2640                usrrej.Bind(wx.EVT_KILL_FOCUS,OnUsrRej)
2641                LSSizer.Add(usrrej,0,WACV)
2642#        Hfix = wx.CheckBox(G2frame.dataDisplay,-1,label='Regularize H atoms? ')
2643#        Hfix.SetValue(data['HatomFix'])
2644#        Hfix.Bind(wx.EVT_CHECKBOX,OnHatomFix)
2645#        LSSizer.Add(Hfix,0,WACV)   #for now
2646        return LSSizer
2647       
2648    def AuthSizer():
2649
2650        def OnAuthor(event):
2651            event.Skip()
2652            data['Author'] = auth.GetValue()
2653
2654        Author = data['Author']
2655        authSizer = wx.BoxSizer(wx.HORIZONTAL)
2656        authSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' CIF Author (last, first):'),0,WACV)
2657        auth = wx.TextCtrl(G2frame.dataDisplay,-1,value=Author,style=wx.TE_PROCESS_ENTER)
2658        auth.Bind(wx.EVT_TEXT_ENTER,OnAuthor)
2659        auth.Bind(wx.EVT_KILL_FOCUS,OnAuthor)
2660        authSizer.Add(auth,0,WACV)
2661        return authSizer
2662       
2663       
2664    if G2frame.dataDisplay:
2665        G2frame.dataDisplay.Destroy()
2666    if not G2frame.dataFrame.GetStatusBar():
2667        Status = G2frame.dataFrame.CreateStatusBar()
2668        Status.SetStatusText('')
2669    G2frame.dataFrame.SetLabel('Controls')
2670    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
2671    SetDataMenuBar(G2frame,G2frame.dataFrame.ControlsMenu)
2672    mainSizer = wx.BoxSizer(wx.VERTICAL)
2673    mainSizer.Add((5,5),0)
2674    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement Controls:'),0,WACV)   
2675    mainSizer.Add(LSSizer())
2676    mainSizer.Add((5,5),0)
2677    mainSizer.Add(SeqSizer())
2678    mainSizer.Add((5,5),0)
2679    mainSizer.Add(AuthSizer())
2680    mainSizer.Add((5,5),0)
2681       
2682    mainSizer.Layout()   
2683    G2frame.dataDisplay.SetSizer(mainSizer)
2684    G2frame.dataDisplay.SetSize(mainSizer.Fit(G2frame.dataFrame))
2685    G2frame.dataFrame.setSizePosLeft(mainSizer.Fit(G2frame.dataFrame))
2686     
2687################################################################################
2688#####  Comments
2689################################################################################           
2690       
2691def UpdateComments(G2frame,data):                   
2692
2693    if G2frame.dataDisplay:
2694        G2frame.dataDisplay.Destroy()
2695    G2frame.dataFrame.SetLabel('Comments')
2696    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2697        style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_DONTWRAP)
2698    for line in data:
2699        if '\n' not in line:
2700            G2frame.dataDisplay.AppendText(line+'\n')
2701        else:
2702            G2frame.dataDisplay.AppendText(line)
2703    G2frame.dataFrame.setSizePosLeft([400,250])
2704           
2705################################################################################
2706#####  Display of Sequential Results
2707################################################################################           
2708       
2709def UpdateSeqResults(G2frame,data,prevSize=None):
2710    """
2711    Called when the Sequential Results data tree entry is selected
2712    to show results from a sequential refinement.
2713   
2714    :param wx.Frame G2frame: main GSAS-II data tree windows
2715
2716    :param dict data: a dictionary containing the following items: 
2717
2718            * 'histNames' - list of histogram names in order as processed by Sequential Refinement
2719            * 'varyList' - list of variables - identical over all refinements in sequence
2720              note that this is the original list of variables, prior to processing
2721              constraints.
2722            * 'variableLabels' -- a dict of labels to be applied to each parameter
2723              (this is created as an empty dict if not present in data).
2724            * keyed by histName - dictionaries for all data sets processed, which contains:
2725
2726              * 'variables'- result[0] from leastsq call
2727              * 'varyList' - list of variables passed to leastsq call (not same as above)
2728              * 'sig' - esds for variables
2729              * 'covMatrix' - covariance matrix from individual refinement
2730              * 'title' - histogram name; same as dict item name
2731              * 'newAtomDict' - new atom parameters after shifts applied
2732              * 'newCellDict' - refined cell parameters after shifts to A0-A5 from Dij terms applied'
2733    """
2734
2735    def GetSampleParms():
2736        '''Make a dictionary of the sample parameters are not the same over the
2737        refinement series.
2738        '''
2739        if 'IMG' in histNames[0]:
2740            sampleParmDict = {'Sample load':[],}
2741        else:
2742            sampleParmDict = {'Temperature':[],'Pressure':[],'Time':[],
2743                'FreePrm1':[],'FreePrm2':[],'FreePrm3':[],'Omega':[],
2744                'Chi':[],'Phi':[],'Azimuth':[],}
2745        Controls = G2frame.PatternTree.GetItemPyData(
2746            GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
2747        sampleParm = {}
2748        for name in histNames:
2749            if 'IMG' in name:
2750                for item in sampleParmDict:
2751                    sampleParmDict[item].append(data[name]['parmDict'].get(item,0))
2752            else:
2753                Id = GetPatternTreeItemId(G2frame,G2frame.root,name)
2754                sampleData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Sample Parameters'))
2755                for item in sampleParmDict:
2756                    sampleParmDict[item].append(sampleData.get(item,0))
2757        for item in sampleParmDict:
2758            frstValue = sampleParmDict[item][0]
2759            if np.any(np.array(sampleParmDict[item])-frstValue):
2760                if item.startswith('FreePrm'):
2761                    sampleParm[Controls[item]] = sampleParmDict[item]
2762                else:
2763                    sampleParm[item] = sampleParmDict[item]
2764        return sampleParm
2765
2766    def GetColumnInfo(col):
2767        '''returns column label, lists of values and errors (or None) for each column in the table
2768        for plotting. The column label is reformatted from Unicode to MatPlotLib encoding
2769        '''
2770        colName = G2frame.SeqTable.GetColLabelValue(col)
2771        plotName = variableLabels.get(colName,colName)
2772        plotName = plotSpCharFix(plotName)
2773        return plotName,colList[col],colSigs[col]
2774           
2775    def PlotSelect(event):
2776        'Plots a row (covariance) or column on double-click'
2777        cols = G2frame.dataDisplay.GetSelectedCols()
2778        rows = G2frame.dataDisplay.GetSelectedRows()
2779        if cols:
2780            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2781        elif rows:
2782            name = histNames[rows[0]]       #only does 1st one selected
2783            G2plt.PlotCovariance(G2frame,data[name])
2784        else:
2785            G2frame.ErrorDialog(
2786                'Select row or columns',
2787                'Nothing selected in table. Click on column or row label(s) to plot. N.B. Grid selection can be a bit funky.'
2788                )
2789           
2790    def OnPlotSelSeq(event):
2791        'plot the selected columns or row from menu command'
2792        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2793        rows = G2frame.dataDisplay.GetSelectedRows()
2794        if cols:
2795            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2796        elif rows:
2797            name = histNames[rows[0]]       #only does 1st one selected
2798            G2plt.PlotCovariance(G2frame,data[name])
2799        else:
2800            G2frame.ErrorDialog(
2801                'Select columns',
2802                'No columns or rows selected in table. Click on row or column labels to select fields for plotting.'
2803                )
2804               
2805    def OnAveSelSeq(event):
2806        'average the selected columns from menu command'
2807        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2808        if cols:
2809            for col in cols:
2810                ave = np.mean(GetColumnInfo(col)[1])
2811                sig = np.std(GetColumnInfo(col)[1])
2812                print ' Average for '+G2frame.SeqTable.GetColLabelValue(col)+': '+'%.6g'%(ave)+' +/- '+'%.6g'%(sig)
2813        else:
2814            G2frame.ErrorDialog(
2815                'Select columns',
2816                'No columns selected in table. Click on column labels to select fields for averaging.'
2817                )
2818               
2819    def OnRenameSelSeq(event):
2820        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2821        colNames = [G2frame.SeqTable.GetColLabelValue(c) for c in cols]
2822        newNames = colNames[:]
2823        for i,name in enumerate(colNames):
2824            if name in variableLabels:
2825                newNames[i] = variableLabels[name]
2826        if not cols:
2827            G2frame.ErrorDialog('Select columns',
2828                'No columns selected in table. Click on column labels to select fields for rename.')
2829            return
2830        dlg = G2G.MultiStringDialog(G2frame.dataDisplay,'Set column names',colNames,newNames)
2831        if dlg.Show():
2832            newNames = dlg.GetValues()           
2833            variableLabels.update(dict(zip(colNames,newNames)))
2834        data['variableLabels'] = variableLabels
2835        dlg.Destroy()
2836        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2837        G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2838           
2839    def OnReOrgSelSeq(event):
2840        'Reorder the columns'
2841        G2G.GetItemOrder(G2frame,VaryListChanges,vallookup,posdict)   
2842        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2843
2844    def OnSaveSelSeqCSV(event):
2845        'export the selected columns to a .csv file from menu command'
2846        OnSaveSelSeq(event,csv=True)
2847       
2848    def OnSaveSeqCSV(event):
2849        'export all columns to a .csv file from menu command'
2850        OnSaveSelSeq(event,csv=True,allcols=True)
2851       
2852    def OnSaveSelSeq(event,csv=False,allcols=False):
2853        'export the selected columns to a .txt or .csv file from menu command'
2854        def WriteCSV():
2855            def WriteList(headerItems):
2856                line = ''
2857                for lbl in headerItems:
2858                    if line: line += ','
2859                    line += '"'+lbl+'"'
2860                return line
2861            head = ['name']
2862            for col in cols:
2863                item = G2frame.SeqTable.GetColLabelValue(col)
2864                # get rid of labels that have Unicode characters
2865                if not all([ord(c) < 128 and ord(c) != 0 for c in item]): item = '?'
2866                if col in havesig:
2867                    head += [item,'esd-'+item]
2868                else:
2869                    head += [item]
2870            SeqFile.write(WriteList(head)+'\n')
2871            for row,name in enumerate(saveNames):
2872                line = '"'+saveNames[row]+'"'
2873                for col in cols:
2874                    if col in havesig:
2875                        line += ','+str(saveData[col][row])+','+str(saveSigs[col][row])
2876                    else:
2877                        line += ','+str(saveData[col][row])
2878                SeqFile.write(line+'\n')
2879        def WriteSeq():
2880            lenName = len(saveNames[0])
2881            line = %s  '%('name'.center(lenName))
2882            for col in cols:
2883                item = G2frame.SeqTable.GetColLabelValue(col)
2884                if col in havesig:
2885                    line += ' %12s %12s '%(item.center(12),'esd'.center(12))
2886                else:
2887                    line += ' %12s '%(item.center(12))
2888            SeqFile.write(line+'\n')
2889            for row,name in enumerate(saveNames):
2890                line = " '%s' "%(saveNames[row])
2891                for col in cols:
2892                    if col in havesig:
2893                        line += ' %12.6f %12.6f '%(saveData[col][row],saveSigs[col][row])
2894                    else:
2895                        line += ' %12.6f '%saveData[col][row]
2896                SeqFile.write(line+'\n')
2897
2898        # start of OnSaveSelSeq code
2899        if allcols:
2900            cols = range(G2frame.SeqTable.GetNumberCols())
2901        else:
2902            cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2903        nrows = G2frame.SeqTable.GetNumberRows()
2904        if not cols:
2905            G2frame.ErrorDialog('Select columns',
2906                             'No columns selected in table. Click on column labels to select fields for output.')
2907            return
2908        saveNames = [G2frame.SeqTable.GetRowLabelValue(r) for r in range(nrows)]
2909        saveData = {}
2910        saveSigs = {}
2911        havesig = []
2912        for col in cols:
2913            name,vals,sigs = GetColumnInfo(col)
2914            saveData[col] = vals
2915            if sigs:
2916                havesig.append(col)
2917                saveSigs[col] = sigs
2918        if csv:
2919            wild = 'CSV output file (*.csv)|*.csv'
2920        else:
2921            wild = 'Text output file (*.txt)|*.txt'
2922        pth = G2G.GetExportPath(G2frame)
2923        dlg = wx.FileDialog(
2924            G2frame,
2925            'Choose text output file for your selection', pth, '', 
2926            wild,wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2927        try:
2928            if dlg.ShowModal() == wx.ID_OK:
2929                SeqTextFile = dlg.GetPath()
2930                SeqTextFile = G2IO.FileDlgFixExt(dlg,SeqTextFile) 
2931                SeqFile = open(SeqTextFile,'w')
2932                if csv:
2933                    WriteCSV()
2934                else:
2935                    WriteSeq()
2936                SeqFile.close()
2937        finally:
2938            dlg.Destroy()
2939               
2940    def striphist(var,insChar=''):
2941        'strip a histogram number from a var name'
2942        sv = var.split(':')
2943        if len(sv) <= 1: return var
2944        if sv[1]:
2945            sv[1] = insChar
2946        return ':'.join(sv)
2947       
2948    def plotSpCharFix(lbl):
2949        'Change selected unicode characters to their matplotlib equivalent'
2950        for u,p in [
2951            (u'\u03B1',r'$\alpha$'),
2952            (u'\u03B2',r'$\beta$'),
2953            (u'\u03B3',r'$\gamma$'),
2954            (u'\u0394\u03C7',r'$\Delta\chi$'),
2955            ]:
2956            lbl = lbl.replace(u,p)
2957        return lbl
2958   
2959    def SelectXaxis():
2960        'returns a selected column number (or None) as the X-axis selection'
2961        ncols = G2frame.SeqTable.GetNumberCols()
2962        colNames = [G2frame.SeqTable.GetColLabelValue(r) for r in range(ncols)]
2963        dlg = G2G.G2SingleChoiceDialog(
2964            G2frame.dataDisplay,
2965            'Select x-axis parameter for plot or Cancel for sequence number',
2966            'Select X-axis',
2967            colNames)
2968        try:
2969            if dlg.ShowModal() == wx.ID_OK:
2970                col = dlg.GetSelection()
2971            else:
2972                col = None
2973        finally:
2974            dlg.Destroy()
2975        return col
2976   
2977    def EnablePseudoVarMenus():
2978        'Enables or disables the PseudoVar menu items that require existing defs'
2979        if Controls['SeqPseudoVars']:
2980            val = True
2981        else:
2982            val = False
2983        G2frame.dataFrame.SequentialPvars.Enable(wxDELSEQVAR,val)
2984        G2frame.dataFrame.SequentialPvars.Enable(wxEDITSEQVAR,val)
2985
2986    def DelPseudoVar(event):
2987        'Ask the user to select a pseudo var expression to delete'
2988        choices = Controls['SeqPseudoVars'].keys()
2989        selected = G2G.ItemSelector(
2990            choices,G2frame.dataFrame,
2991            multiple=True,
2992            title='Select expressions to remove',
2993            header='Delete expression')
2994        if selected is None: return
2995        for item in selected:
2996            del Controls['SeqPseudoVars'][choices[item]]
2997        if selected:
2998            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2999
3000    def EditPseudoVar(event):
3001        'Edit an existing pseudo var expression'
3002        choices = Controls['SeqPseudoVars'].keys()
3003        if len(choices) == 1:
3004            selected = 0
3005        else:
3006            selected = G2G.ItemSelector(
3007                choices,G2frame.dataFrame,
3008                multiple=False,
3009                title='Select an expression to edit',
3010                header='Edit expression')
3011        if selected is not None:
3012            dlg = G2exG.ExpressionDialog(
3013                G2frame.dataDisplay,PSvarDict,
3014                Controls['SeqPseudoVars'][choices[selected]],
3015                header="Edit the PseudoVar expression",
3016                VarLabel="PseudoVar #"+str(selected+1),
3017                fit=False)
3018            newobj = dlg.Show(True)
3019            if newobj:
3020                calcobj = G2obj.ExpressionCalcObj(newobj)
3021                del Controls['SeqPseudoVars'][choices[selected]]
3022                Controls['SeqPseudoVars'][calcobj.eObj.expression] = newobj
3023                UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3024       
3025    def AddNewPseudoVar(event):
3026        'Create a new pseudo var expression'
3027        dlg = G2exG.ExpressionDialog(
3028            G2frame.dataDisplay,PSvarDict,
3029            header='Enter an expression for a PseudoVar here',
3030            VarLabel = "New PseudoVar",
3031            fit=False)
3032        obj = dlg.Show(True)
3033        dlg.Destroy()
3034        if obj:
3035            calcobj = G2obj.ExpressionCalcObj(obj)
3036            Controls['SeqPseudoVars'][calcobj.eObj.expression] = obj
3037            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3038           
3039    def AddNewDistPseudoVar(event):
3040        obj = None
3041        dlg = G2exG.BondDialog(
3042            G2frame.dataDisplay,Phases,PSvarDict,
3043            header='Select a Bond here',
3044            VarLabel = "New Bond")
3045        if dlg.ShowModal() == wx.ID_OK:
3046            pName,Oatom,Tatom = dlg.GetSelection()
3047            if Tatom:
3048                Phase = Phases[pName]
3049                General = Phase['General']
3050                cx,ct = General['AtomPtrs'][:2]
3051                pId = Phase['pId']
3052                SGData = General['SGData']
3053                sB = Tatom.find('(')+1
3054                symNo = 0
3055                if sB:
3056                    sF = Tatom.find(')')
3057                    symNo = int(Tatom[sB:sF])
3058                cellNo = [0,0,0]
3059                cB = Tatom.find('[')
3060                if cB>0:
3061                    cF = Tatom.find(']')+1
3062                    cellNo = eval(Tatom[cB:cF])
3063                Atoms = Phase['Atoms']
3064                aNames = [atom[ct-1] for atom in Atoms]
3065                oId = aNames.index(Oatom)
3066                tId = aNames.index(Tatom.split(' +')[0])
3067                # create an expression object
3068                obj = G2obj.ExpressionObj()
3069                obj.expression = 'Dist(%s,\n%s)'%(Oatom,Tatom.split(' d=')[0].replace(' ',''))
3070                obj.distance_dict = {'pId':pId,'SGData':SGData,'symNo':symNo,'cellNo':cellNo}
3071                obj.distance_atoms = [oId,tId]
3072        else: 
3073            dlg.Destroy()
3074            return
3075        dlg.Destroy()
3076        if obj:
3077            Controls['SeqPseudoVars'][obj.expression] = obj
3078            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3079
3080    def AddNewAnglePseudoVar(event):
3081        obj = None
3082        dlg = G2exG.AngleDialog(
3083            G2frame.dataDisplay,Phases,PSvarDict,
3084            header='Enter an Angle here',
3085            VarLabel = "New Angle")
3086        if dlg.ShowModal() == wx.ID_OK:
3087            pName,Oatom,Tatoms = dlg.GetSelection()
3088            if Tatoms:
3089                Phase = Phases[pName]
3090                General = Phase['General']
3091                cx,ct = General['AtomPtrs'][:2]
3092                pId = Phase['pId']
3093                SGData = General['SGData']
3094                Atoms = Phase['Atoms']
3095                aNames = [atom[ct-1] for atom in Atoms]
3096                tIds = []
3097                symNos = []
3098                cellNos = []
3099                oId = aNames.index(Oatom)
3100                Tatoms = Tatoms.split(';')
3101                for Tatom in Tatoms:
3102                    sB = Tatom.find('(')+1
3103                    symNo = 0
3104                    if sB:
3105                        sF = Tatom.find(')')
3106                        symNo = int(Tatom[sB:sF])
3107                    symNos.append(symNo)
3108                    cellNo = [0,0,0]
3109                    cB = Tatom.find('[')
3110                    if cB>0:
3111                        cF = Tatom.find(']')+1
3112                        cellNo = eval(Tatom[cB:cF])
3113                    cellNos.append(cellNo)
3114                    tIds.append(aNames.index(Tatom.split('+')[0]))
3115                # create an expression object
3116                obj = G2obj.ExpressionObj()
3117                obj.expression = 'Angle(%s,%s,\n%s)'%(Tatoms[0],Oatom,Tatoms[1])
3118                obj.angle_dict = {'pId':pId,'SGData':SGData,'symNo':symNos,'cellNo':cellNos}
3119                obj.angle_atoms = [oId,tIds]
3120        else: 
3121            dlg.Destroy()
3122            return
3123        dlg.Destroy()
3124        if obj:
3125            Controls['SeqPseudoVars'][obj.expression] = obj
3126            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3127           
3128    def UpdateParmDict(parmDict):
3129        '''generate the atom positions and the direct & reciprocal cell values,
3130        because they might be needed to evaluate the pseudovar
3131        '''
3132        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
3133                         ['A'+str(i) for i in range(6)])
3134                     )
3135        delList = []
3136        phaselist = []
3137        for item in parmDict: 
3138            if ':' not in item: continue
3139            key = item.split(':')
3140            if len(key) < 3: continue
3141            # remove the dA[xyz] terms, they would only bring confusion
3142            if key[2].startswith('dA'):
3143                delList.append(item)
3144            # compute and update the corrected reciprocal cell terms using the Dij values
3145            elif key[2] in Ddict:
3146                if key[0] not in phaselist: phaselist.append(key[0])
3147                akey = key[0]+'::'+Ddict[key[2]]
3148                parmDict[akey] -= parmDict[item]
3149                delList.append(item)
3150        for item in delList:
3151            del parmDict[item]               
3152        for i in phaselist:
3153            pId = int(i)
3154            # apply cell symmetry
3155            A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],parmDict,zeroDict[pId])
3156            # convert to direct cell & add the unique terms to the dictionary
3157            for i,val in enumerate(G2lat.A2cell(A)):
3158                if i in uniqCellIndx[pId]:
3159                    lbl = str(pId)+'::'+cellUlbl[i]
3160                    parmDict[lbl] = val
3161            lbl = str(pId)+'::'+'Vol'
3162            parmDict[lbl] = G2lat.calc_V(A)
3163        return parmDict
3164
3165    def EvalPSvarDeriv(calcobj,parmDict,sampleDict,var,ESD):
3166        '''Evaluate an expression derivative with respect to a
3167        GSAS-II variable name.
3168
3169        Note this likely could be faster if the loop over calcobjs were done
3170        inside after the Dict was created.
3171        '''
3172        step = ESD/10
3173        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
3174                         ['A'+str(i) for i in range(6)])
3175                     )
3176        results = []
3177        phaselist = []
3178        VparmDict = sampleDict.copy()
3179        for incr in step,-step:
3180            VparmDict.update(parmDict.copy())           
3181            # as saved, the parmDict has updated 'A[xyz]' values, but 'dA[xyz]'
3182            # values are not zeroed: fix that!
3183            VparmDict.update({item:0.0 for item in parmDict if 'dA' in item})
3184            VparmDict[var] += incr
3185            G2mv.Dict2Map(VparmDict,[]) # apply constraints
3186            # generate the atom positions and the direct & reciprocal cell values now, because they might
3187            # needed to evaluate the pseudovar
3188            for item in VparmDict:
3189                if item in sampleDict:
3190                    continue 
3191                if ':' not in item: continue
3192                key = item.split(':')
3193                if len(key) < 3: continue
3194                # apply any new shifts to atom positions
3195                if key[2].startswith('dA'):
3196                    VparmDict[''.join(item.split('d'))] += VparmDict[item]
3197                    VparmDict[item] = 0.0
3198                # compute and update the corrected reciprocal cell terms using the Dij values
3199                if key[2] in Ddict:
3200                    if key[0] not in phaselist: phaselist.append(key[0])
3201                    akey = key[0]+'::'+Ddict[key[2]]
3202                    VparmDict[akey] -= VparmDict[item]
3203            for i in phaselist:
3204                pId = int(i)
3205                # apply cell symmetry
3206                A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],VparmDict,zeroDict[pId])
3207                # convert to direct cell & add the unique terms to the dictionary
3208                for i,val in enumerate(G2lat.A2cell(A)):
3209                    if i in uniqCellIndx[pId]:
3210                        lbl = str(pId)+'::'+cellUlbl[i]
3211                        VparmDict[lbl] = val
3212                lbl = str(pId)+'::'+'Vol'
3213                VparmDict[lbl] = G2lat.calc_V(A)
3214            # dict should be fully updated, use it & calculate
3215            calcobj.SetupCalc(VparmDict)
3216            results.append(calcobj.EvalExpression())
3217        return (results[0] - results[1]) / (2.*step)
3218       
3219    def EnableParFitEqMenus():
3220        'Enables or disables the Parametric Fit menu items that require existing defs'
3221        if Controls['SeqParFitEqList']:
3222            val = True
3223        else:
3224            val = False
3225        G2frame.dataFrame.SequentialPfit.Enable(wxDELPARFIT,val)
3226        G2frame.dataFrame.SequentialPfit.Enable(wxEDITPARFIT,val)
3227        G2frame.dataFrame.SequentialPfit.Enable(wxDOPARFIT,val)
3228
3229    def ParEqEval(Values,calcObjList,varyList):
3230        '''Evaluate the parametric expression(s)
3231        :param list Values: a list of values for each variable parameter
3232        :param list calcObjList: a list of :class:`GSASIIobj.ExpressionCalcObj`
3233          expression objects to evaluate
3234        :param list varyList: a list of variable names for each value in Values
3235        '''
3236        result = []
3237        for calcobj in calcObjList:
3238            calcobj.UpdateVars(varyList,Values)
3239            result.append((calcobj.depVal-calcobj.EvalExpression())/calcobj.depSig)
3240        return result
3241
3242    def DoParEqFit(event,eqObj=None):
3243        'Parametric fit minimizer'
3244        varyValueDict = {} # dict of variables and their initial values
3245        calcObjList = [] # expression objects, ready to go for each data point
3246        if eqObj is not None:
3247            eqObjList = [eqObj,]
3248        else:
3249            eqObjList = Controls['SeqParFitEqList']
3250        UseFlags = G2frame.SeqTable.GetColValues(0)         
3251        for obj in eqObjList:
3252            expr = obj.expression
3253            # assemble refined vars for this equation
3254            varyValueDict.update({var:val for var,val in obj.GetVariedVarVal()})
3255            # lookup dependent var position
3256            depVar = obj.GetDepVar()
3257            if depVar in colLabels:
3258                indx = colLabels.index(depVar)
3259            else:
3260                raise Exception('Dependent variable '+depVar+' not found')
3261            # assemble a list of the independent variables
3262            indepVars = obj.GetIndependentVars()
3263            # loop over each datapoint
3264            for j,row in enumerate(zip(*colList)):
3265                if not UseFlags[j]: continue
3266                # assemble equations to fit
3267                calcobj = G2obj.ExpressionCalcObj(obj)
3268                # prepare a dict of needed independent vars for this expression
3269                indepVarDict = {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
3270                calcobj.SetupCalc(indepVarDict)               
3271                # values and sigs for current value of dependent var
3272                calcobj.depVal = row[indx]
3273                calcobj.depSig = colSigs[indx][j]
3274                calcObjList.append(calcobj)
3275        # varied parameters
3276        varyList = varyValueDict.keys()
3277        values = varyValues = [varyValueDict[key] for key in varyList]
3278        if not varyList:
3279            print 'no variables to refine!'
3280            return
3281        try:
3282            result = so.leastsq(ParEqEval,varyValues,full_output=True,   #ftol=Ftol,
3283                                args=(calcObjList,varyList)
3284                                )
3285            values = result[0]
3286            covar = result[1]
3287            if covar is None:
3288                raise Exception
3289            esdDict = {}
3290            for i,avar in enumerate(varyList):
3291                esdDict[avar] = np.sqrt(covar[i,i])
3292        except:
3293            print('====> Fit failed')
3294            return
3295        print('==== Fit Results ====')
3296        for obj in eqObjList:
3297            obj.UpdateVariedVars(varyList,values)
3298            ind = '      '
3299            print('  '+obj.GetDepVar()+' = '+obj.expression)
3300            for var in obj.assgnVars:
3301                print(ind+var+' = '+obj.assgnVars[var])
3302            for var in obj.freeVars:
3303                avar = "::"+obj.freeVars[var][0]
3304                val = obj.freeVars[var][1]
3305                if obj.freeVars[var][2]:
3306                    print(ind+var+' = '+avar + " = " + G2mth.ValEsd(val,esdDict[avar]))
3307                else:
3308                    print(ind+var+' = '+avar + " =" + G2mth.ValEsd(val,0))
3309        # create a plot for each parametric variable
3310        for fitnum,obj in enumerate(eqObjList):
3311            calcobj = G2obj.ExpressionCalcObj(obj)
3312            # lookup dependent var position
3313            indx = colLabels.index(obj.GetDepVar())
3314            # assemble a list of the independent variables
3315            indepVars = obj.GetIndependentVars()           
3316            # loop over each datapoint
3317            fitvals = []
3318            for j,row in enumerate(zip(*colList)):
3319                calcobj.SetupCalc(
3320                    {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
3321                    )
3322                fitvals.append(calcobj.EvalExpression())
3323            G2plt.PlotSelectedSequence(
3324                G2frame,[indx],GetColumnInfo,SelectXaxis,
3325                fitnum,fitvals)
3326
3327    def SingleParEqFit(eqObj):
3328        DoParEqFit(None,eqObj)
3329
3330    def DelParFitEq(event):
3331        'Ask the user to select function to delete'
3332        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3333        selected = G2G.ItemSelector(
3334            txtlst,G2frame.dataFrame,
3335            multiple=True,
3336            title='Select a parametric equation(s) to remove',
3337            header='Delete equation')
3338        if selected is None: return
3339        Controls['SeqParFitEqList'] = [obj for i,obj in enumerate(Controls['SeqParFitEqList']) if i not in selected]
3340        EnableParFitEqMenus()
3341        if Controls['SeqParFitEqList']: DoParEqFit(event)
3342       
3343    def EditParFitEq(event):
3344        'Edit an existing parametric equation'
3345        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3346        if len(txtlst) == 1:
3347            selected = 0
3348        else:
3349            selected = G2G.ItemSelector(
3350                txtlst,G2frame.dataFrame,
3351                multiple=False,
3352                title='Select a parametric equation to edit',
3353                header='Edit equation')
3354        if selected is not None:
3355            dlg = G2exG.ExpressionDialog(
3356                G2frame.dataDisplay,indepVarDict,
3357                Controls['SeqParFitEqList'][selected],
3358                depVarDict=depVarDict,
3359                header="Edit the formula for this minimization function",
3360                ExtraButton=['Fit',SingleParEqFit])
3361            newobj = dlg.Show(True)
3362            if newobj:
3363                calcobj = G2obj.ExpressionCalcObj(newobj)
3364                Controls['SeqParFitEqList'][selected] = newobj
3365                EnableParFitEqMenus()
3366            if Controls['SeqParFitEqList']: DoParEqFit(event)
3367
3368    def AddNewParFitEq(event):
3369        'Create a new parametric equation to be fit to sequential results'
3370
3371        # compile the variable names used in previous freevars to avoid accidental name collisions
3372        usedvarlist = []
3373        for obj in Controls['SeqParFitEqList']:
3374            for var in obj.freeVars:
3375                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3376
3377        dlg = G2exG.ExpressionDialog(
3378            G2frame.dataDisplay,indepVarDict,
3379            depVarDict=depVarDict,
3380            header='Define an equation to minimize in the parametric fit',
3381            ExtraButton=['Fit',SingleParEqFit],
3382            usedVars=usedvarlist)
3383        obj = dlg.Show(True)
3384        dlg.Destroy()
3385        if obj:
3386            Controls['SeqParFitEqList'].append(obj)
3387            EnableParFitEqMenus()
3388            if Controls['SeqParFitEqList']: DoParEqFit(event)
3389               
3390    def CopyParFitEq(event):
3391        'Copy an existing parametric equation to be fit to sequential results'
3392        # compile the variable names used in previous freevars to avoid accidental name collisions
3393        usedvarlist = []
3394        for obj in Controls['SeqParFitEqList']:
3395            for var in obj.freeVars:
3396                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3397        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3398        if len(txtlst) == 1:
3399            selected = 0
3400        else:
3401            selected = G2G.ItemSelector(
3402                txtlst,G2frame.dataFrame,
3403                multiple=False,
3404                title='Select a parametric equation to copy',
3405                header='Copy equation')
3406        if selected is not None:
3407            newEqn = copy.deepcopy(Controls['SeqParFitEqList'][selected])
3408            for var in newEqn.freeVars:
3409                newEqn.freeVars[var][0] = G2obj.MakeUniqueLabel(newEqn.freeVars[var][0],usedvarlist)
3410            dlg = G2exG.ExpressionDialog(
3411                G2frame.dataDisplay,indepVarDict,
3412                newEqn,
3413                depVarDict=depVarDict,
3414                header="Edit the formula for this minimization function",
3415                ExtraButton=['Fit',SingleParEqFit])
3416            newobj = dlg.Show(True)
3417            if newobj:
3418                calcobj = G2obj.ExpressionCalcObj(newobj)
3419                Controls['SeqParFitEqList'].append(newobj)
3420                EnableParFitEqMenus()
3421            if Controls['SeqParFitEqList']: DoParEqFit(event)
3422                                           
3423    def GridSetToolTip(row,col):
3424        '''Routine to show standard uncertainties for each element in table
3425        as a tooltip
3426        '''
3427        if colSigs[col]:
3428            return u'\u03c3 = '+str(colSigs[col][row])
3429        return ''
3430       
3431    def GridColLblToolTip(col):
3432        '''Define a tooltip for a column. This will be the user-entered value
3433        (from data['variableLabels']) or the default name
3434        '''
3435        if col < 0 or col > len(colLabels):
3436            print 'Illegal column #',col
3437            return
3438        var = colLabels[col]
3439        return variableLabels.get(var,G2obj.fmtVarDescr(var))
3440       
3441    def SetLabelString(event):
3442        '''Define or edit the label for a column in the table, to be used
3443        as a tooltip and for plotting
3444        '''
3445        col = event.GetCol()
3446        if col < 0 or col > len(colLabels):
3447            return
3448        var = colLabels[col]
3449        lbl = variableLabels.get(var,G2obj.fmtVarDescr(var))
3450        dlg = G2G.SingleStringDialog(G2frame.dataFrame,'Set variable label',
3451                                 'Set a new name for variable '+var,lbl,size=(400,-1))
3452        if dlg.Show():
3453            variableLabels[var] = dlg.GetValue()
3454        dlg.Destroy()
3455
3456    def DoSequentialExport(event):
3457        '''Event handler for all Sequential Export menu items
3458        '''
3459        vals = G2frame.dataFrame.SeqExportLookup.get(event.GetId())
3460        if vals is None:
3461            print('Error: Id not found. This should not happen!')
3462        G2IO.ExportSequential(G2frame,data,*vals)
3463   
3464    #def GridRowLblToolTip(row): return 'Row ='+str(row)
3465   
3466    # lookup table for unique cell parameters by symmetry
3467    cellGUIlist = [
3468        [['m3','m3m'],(0,)],
3469        [['3R','3mR'],(0,3)],
3470        [['3','3m1','31m','6/m','6/mmm','4/m','4/mmm'],(0,2)],
3471        [['mmm'],(0,1,2)],
3472        [['2/m'+'a'],(0,1,2,3)],
3473        [['2/m'+'b'],(0,1,2,4)],
3474        [['2/m'+'c'],(0,1,2,5)],
3475        [['-1'],(0,1,2,3,4,5)],
3476        ]
3477    # cell labels
3478    cellUlbl = ('a','b','c',u'\u03B1',u'\u03B2',u'\u03B3') # unicode a,b,c,alpha,beta,gamma
3479
3480    #======================================================================
3481    # start processing sequential results here (UpdateSeqResults)
3482    #======================================================================
3483    if not data:
3484        print 'No sequential refinement results'
3485        return
3486    variableLabels = data.get('variableLabels',{})
3487    data['variableLabels'] = variableLabels
3488    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
3489    Controls = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Controls'))
3490    # create a place to store Pseudo Vars & Parametric Fit functions, if not present
3491    if 'SeqPseudoVars' not in Controls: Controls['SeqPseudoVars'] = {}
3492    if 'SeqParFitEqList' not in Controls: Controls['SeqParFitEqList'] = []
3493    histNames = data['histNames']
3494    if G2frame.dataDisplay:
3495        G2frame.dataDisplay.Destroy()
3496    if not G2frame.dataFrame.GetStatusBar():
3497        Status = G2frame.dataFrame.CreateStatusBar()
3498        Status.SetStatusText("Select column to export; Double click on column to plot data; on row for Covariance")
3499    sampleParms = GetSampleParms()
3500
3501    # make dict of varied atom coords keyed by absolute position
3502    newAtomDict = data[histNames[0]].get('newAtomDict',{}) # dict with atom positions; relative & absolute
3503    # Possible error: the next might need to be data[histNames[0]]['varyList']
3504    # error will arise if there constraints on coordinates?
3505    atomLookup = {newAtomDict[item][0]:item for item in newAtomDict if item in data['varyList']}
3506   
3507    # make dict of varied cell parameters equivalents
3508    ESDlookup = {} # provides the Dij term for each Ak term (where terms are refined)
3509    Dlookup = {} # provides the Ak term for each Dij term (where terms are refined)
3510    # N.B. These Dij vars are missing a histogram #
3511    newCellDict = data[histNames[0]].get('newCellDict',{})
3512    for item in newCellDict:
3513        if item in data['varyList']:
3514            ESDlookup[newCellDict[item][0]] = item
3515            Dlookup[item] = newCellDict[item][0]
3516    # add coordinate equivalents to lookup table
3517    for parm in atomLookup:
3518        Dlookup[atomLookup[parm]] = parm
3519        ESDlookup[parm] = atomLookup[parm]
3520
3521    # get unit cell & symmetry for all phases & initial stuff for later use
3522    RecpCellTerms = {}
3523    SGdata = {}
3524    uniqCellIndx = {}
3525    initialCell = {}
3526    RcellLbls = {}
3527    zeroDict = {}
3528    for phase in Phases:
3529        phasedict = Phases[phase]
3530        pId = phasedict['pId']
3531        pfx = str(pId)+'::' # prefix for A values from phase
3532        RcellLbls[pId] = [pfx+'A'+str(i) for i in range(6)]
3533        RecpCellTerms[pId] = G2lat.cell2A(phasedict['General']['Cell'][1:7])
3534        zeroDict[pId] = dict(zip(RcellLbls[pId],6*[0.,]))
3535        SGdata[pId] = phasedict['General']['SGData']
3536        laue = SGdata[pId]['SGLaue']
3537        if laue == '2/m':
3538            laue += SGdata[pId]['SGUniq']
3539        for symlist,celllist in cellGUIlist:
3540            if laue in symlist:
3541                uniqCellIndx[pId] = celllist
3542                break
3543        else: # should not happen
3544            uniqCellIndx[pId] = range(6)
3545        for i in uniqCellIndx[pId]:
3546            initialCell[str(pId)+'::A'+str(i)] =  RecpCellTerms[pId][i]
3547
3548    SetDataMenuBar(G2frame,G2frame.dataFrame.SequentialMenu)
3549    G2frame.dataFrame.SetLabel('Sequential refinement results')
3550    if not G2frame.dataFrame.GetStatusBar():
3551        Status = G2frame.dataFrame.CreateStatusBar()
3552        Status.SetStatusText('')
3553    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRenameSelSeq, id=wxID_RENAMESEQSEL)
3554    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeq, id=wxID_SAVESEQSEL)
3555    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeqCSV, id=wxID_SAVESEQSELCSV)
3556    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSeqCSV, id=wxID_SAVESEQCSV)
3557    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotSelSeq, id=wxID_PLOTSEQSEL)
3558    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAveSelSeq, id=wxID_AVESEQSEL)
3559    G2frame.dataFrame.Bind(wx.EVT_MENU, OnReOrgSelSeq, id=wxID_ORGSEQSEL)
3560    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewPseudoVar, id=wxADDSEQVAR)
3561    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewDistPseudoVar, id=wxADDSEQDIST)
3562    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewAnglePseudoVar, id=wxADDSEQANGLE)
3563    G2frame.dataFrame.Bind(wx.EVT_MENU, DelPseudoVar, id=wxDELSEQVAR)
3564    G2frame.dataFrame.Bind(wx.EVT_MENU, EditPseudoVar, id=wxEDITSEQVAR)
3565    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewParFitEq, id=wxADDPARFIT)
3566    G2frame.dataFrame.Bind(wx.EVT_MENU, CopyParFitEq, id=wxCOPYPARFIT)
3567    G2frame.dataFrame.Bind(wx.EVT_MENU, DelParFitEq, id=wxDELPARFIT)
3568    G2frame.dataFrame.Bind(wx.EVT_MENU, EditParFitEq, id=wxEDITPARFIT)
3569    G2frame.dataFrame.Bind(wx.EVT_MENU, DoParEqFit, id=wxDOPARFIT)
3570
3571    for id in G2frame.dataFrame.SeqExportLookup:       
3572        G2frame.dataFrame.Bind(wx.EVT_MENU, DoSequentialExport, id=id)
3573
3574    EnablePseudoVarMenus()
3575    EnableParFitEqMenus()
3576
3577    # scan for locations where the variables change
3578    VaryListChanges = [] # histograms where there is a change
3579    combinedVaryList = []
3580    firstValueDict = {}
3581    vallookup = {}
3582    posdict = {}
3583    prevVaryList = []
3584    for i,name in enumerate(histNames):
3585        for var,val,sig in zip(data[name]['varyList'],data[name]['variables'],data[name]['sig']):
3586            svar = striphist(var,'*') # wild-carded
3587            if svar not in combinedVaryList:
3588                # add variables to list as they appear
3589                combinedVaryList.append(svar)
3590                firstValueDict[svar] = (val,sig)
3591        if prevVaryList != data[name]['varyList']: # this refinement has a different refinement list from previous
3592            prevVaryList = data[name]['varyList']
3593            vallookup[name] = dict(zip(data[name]['varyList'],data[name]['variables']))
3594            posdict[name] = {}
3595            for var in data[name]['varyList']:
3596                svar = striphist(var,'*')
3597                posdict[name][combinedVaryList.index(svar)] = svar
3598            VaryListChanges.append(name)
3599    if len(VaryListChanges) > 1:
3600        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,True)
3601    else:
3602        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,False)
3603    #-----------------------------------------------------------------------------------
3604    # build up the data table by columns -----------------------------------------------
3605    nRows = len(histNames)
3606    colList = [nRows*[True]]
3607    colSigs = [None]
3608    colLabels = ['Use']
3609    Types = [wg.GRID_VALUE_BOOL]
3610    # start with Rwp values
3611    if 'IMG ' not in histNames[0][:4]:
3612        colList += [[data[name]['Rvals']['Rwp'] for name in histNames]]
3613        colSigs += [None]
3614        colLabels += ['Rwp']
3615        Types += [wg.GRID_VALUE_FLOAT+':10,3',]
3616    # add % change in Chi^2 in last cycle
3617    if histNames[0][:4] not in ['SASD','IMG '] and Controls.get('ShowCell'):
3618        colList += [[100.*data[name]['Rvals'].get('DelChi2',-1) for name in histNames]]
3619        colSigs += [None]
3620        colLabels += [u'\u0394\u03C7\u00B2 (%)']
3621        Types += [wg.GRID_VALUE_FLOAT,]
3622    deltaChiCol = len(colLabels)-1
3623    # add changing sample parameters to table
3624    for key in sampleParms:
3625        colList += [sampleParms[key]]
3626        colSigs += [None]
3627        colLabels += [key]
3628        Types += [wg.GRID_VALUE_FLOAT,]
3629    sampleDict = {}
3630    for i,name in enumerate(histNames):
3631        sampleDict[name] = dict(zip(sampleParms.keys(),[sampleParms[key][i] for key in sampleParms.keys()])) 
3632    # add unique cell parameters TODO: review this where the cell symmetry changes (when possible)
3633    if Controls.get('ShowCell',False):
3634        for pId in sorted(RecpCellTerms):
3635            pfx = str(pId)+'::' # prefix for A values from phase
3636            cells = []
3637            cellESDs = []
3638            colLabels += [pfx+cellUlbl[i] for i in uniqCellIndx[pId]]
3639            colLabels += [pfx+'Vol']
3640            Types += (1+len(uniqCellIndx[pId]))*[wg.GRID_VALUE_FLOAT,]
3641            for name in histNames:
3642                covData = {
3643                    'varyList': [Dlookup.get(striphist(v),v) for v in data[name]['varyList']],
3644                    'covMatrix': data[name]['covMatrix']
3645                    }
3646                A = RecpCellTerms[pId][:] # make copy of starting A values
3647                # update with refined values
3648                for i in range(6):
3649                    var = str(pId)+'::A'+str(i)
3650                    if var in ESDlookup:
3651                        val = data[name]['newCellDict'][ESDlookup[var]][1] # get refined value
3652                        A[i] = val # override with updated value
3653                # apply symmetry
3654                Albls = [pfx+'A'+str(i) for i in range(6)]
3655                cellDict = dict(zip(Albls,A))
3656                A,zeros = G2stIO.cellFill(pfx,SGdata[pId],cellDict,zeroDict[pId])
3657                # convert to direct cell & add only unique values to table
3658                c = G2lat.A2cell(A)
3659                vol = G2lat.calc_V(A)
3660                cE = G2stIO.getCellEsd(pfx,SGdata[pId],A,covData)
3661                cells += [[c[i] for i in uniqCellIndx[pId]]+[vol]]
3662                cellESDs += [[cE[i] for i in uniqCellIndx[pId]]+[cE[-1]]]
3663            colList += zip(*cells)
3664            colSigs += zip(*cellESDs)
3665    # sort out the variables in their selected order
3666    varcols = 0
3667    for d in posdict.itervalues():
3668        varcols = max(varcols,max(d.keys())+1)
3669    # get labels for each column
3670    for i in range(varcols):
3671        lbl = ''
3672        for h in VaryListChanges:
3673            if posdict[h].get(i):
3674                if posdict[h].get(i) in lbl: continue
3675                if lbl != "": lbl += '/'
3676                lbl += posdict[h].get(i)
3677        colLabels.append(lbl)
3678    Types += varcols*[wg.GRID_VALUE_FLOAT]
3679    vals = []
3680    esds = []
3681    varsellist = None        # will be a list of variable names in the order they are selected to appear
3682    # tabulate values for each hist, leaving None for blank columns
3683    for name in histNames:
3684        if name in posdict:
3685            varsellist = [posdict[name].get(i) for i in range(varcols)]
3686            # translate variable names to how they will be used in the headings
3687            vs = [striphist(v,'*') for v in data[name]['varyList']]
3688            # determine the index for each column (or None) in the data[]['variables'] and ['sig'] lists
3689            sellist = [vs.index(v) if v is not None else None for v in varsellist]
3690            #sellist = [i if striphist(v,'*') in varsellist else None for i,v in enumerate(data[name]['varyList'])]
3691        if not varsellist: raise Exception()
3692        vals.append([data[name]['variables'][s] if s is not None else None for s in sellist])
3693        esds.append([data[name]['sig'][s] if s is not None else None for s in sellist])
3694        #GSASIIpath.IPyBreak()
3695    colList += zip(*vals)
3696    colSigs += zip(*esds)
3697    # compute and add weight fractions to table if varied
3698    for phase in Phases:
3699        var = str(Phases[phase]['pId'])+':*:Scale'
3700        if var not in combinedVaryList: continue
3701        wtFrList = []
3702        sigwtFrList = []
3703        for i,name in enumerate(histNames):
3704            wtFrSum = 0.
3705            for phase1 in Phases:
3706                wtFrSum += Phases[phase1]['Histograms'][name]['Scale'][0]*Phases[phase1]['General']['Mass']
3707            var = str(Phases[phase]['pId'])+':'+str(i)+':Scale'
3708            wtFr = Phases[phase]['Histograms'][name]['Scale'][0]*Phases[phase]['General']['Mass']/wtFrSum
3709            wtFrList.append(wtFr)
3710            if var in data[name]['varyList']:
3711                sig = data[name]['sig'][data[name]['varyList'].index(var)]*wtFr/Phases[phase]['Histograms'][name]['Scale'][0]
3712            else:
3713                sig = 0.0
3714            sigwtFrList.append(sig)
3715        colLabels.append(str(Phases[phase]['pId'])+':*:WgtFrac')
3716        colList += [wtFrList]
3717        colSigs += [sigwtFrList]
3718               
3719    # tabulate constrained variables, removing histogram numbers if needed
3720    # from parameter label
3721    depValDict = {}
3722    depSigDict = {}
3723    for name in histNames:
3724        for var in data[name].get('depParmDict',{}):
3725            val,sig = data[name]['depParmDict'][var]
3726            svar = striphist(var,'*')
3727            if svar not in depValDict:
3728               depValDict[svar] = [val]
3729               depSigDict[svar] = [sig]
3730            else:
3731               depValDict[svar].append(val)
3732               depSigDict[svar].append(sig)
3733    # add the dependent constrained variables to the table
3734    for var in sorted(depValDict):
3735        if len(depValDict[var]) != len(histNames): continue
3736        colLabels.append(var)
3737        Types += [wg.GRID_VALUE_FLOAT,]
3738        colSigs += [depSigDict[var]]
3739        colList += [depValDict[var]]
3740
3741    # add atom parameters to table
3742    colLabels += atomLookup.keys()
3743    Types += len(atomLookup)*[wg.GRID_VALUE_FLOAT]
3744    for parm in sorted(atomLookup):
3745        colList += [[data[name]['newAtomDict'][atomLookup[parm]][1] for name in histNames]]
3746        if atomLookup[parm] in data[histNames[0]]['varyList']:
3747            col = data[histNames[0]]['varyList'].index(atomLookup[parm])
3748            colSigs += [[data[name]['sig'][col] for name in histNames]]
3749        else:
3750            colSigs += [None] # should not happen
3751    # evaluate Pseudovars, their ESDs and add them to grid
3752    for expr in Controls['SeqPseudoVars']:
3753        obj = Controls['SeqPseudoVars'][expr]
3754        calcobj = G2obj.ExpressionCalcObj(obj)
3755        valList = []
3756        esdList = []
3757        for seqnum,name in enumerate(histNames):
3758            sigs = data[name]['sig']
3759            G2mv.InitVars()
3760            parmDict = data[name].get('parmDict')
3761            badVary = data[name].get('badVary',[])
3762            constraintInfo = data[name].get('constraintInfo',[[],[],{},[],seqnum])
3763            groups,parmlist,constrDict,fixedList,ihst = constraintInfo
3764            varyList = data[name]['varyList']
3765            parmDict = data[name]['parmDict']
3766            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict,SeqHist=ihst)
3767            if 'Dist' in expr:
3768                derivs = G2mth.CalcDistDeriv(obj.distance_dict,obj.distance_atoms, parmDict)
3769                pId = obj.distance_dict['pId']
3770                aId,bId = obj.distance_atoms
3771                varyNames = ['%d::dA%s:%d'%(pId,ip,aId) for ip in ['x','y','z']]
3772                varyNames += ['%d::dA%s:%d'%(pId,ip,bId) for ip in ['x','y','z']]
3773                VCoV = G2mth.getVCov(varyNames,varyList,data[name]['covMatrix'])
3774                esdList.append(np.sqrt(np.inner(derivs,np.inner(VCoV,derivs.T)) ))
3775#                GSASIIpath.IPyBreak()
3776            elif 'Angle' in expr:
3777                derivs = G2mth.CalcAngleDeriv(obj.angle_dict,obj.angle_atoms, parmDict)
3778                pId = obj.angle_dict['pId']
3779                aId,bId = obj.angle_atoms
3780                varyNames = ['%d::dA%s:%d'%(pId,ip,aId) for ip in ['x','y','z']]
3781                varyNames += ['%d::dA%s:%d'%(pId,ip,bId[0]) for ip in ['x','y','z']]
3782                varyNames += ['%d::dA%s:%d'%(pId,ip,bId[1]) for ip in ['x','y','z']]
3783                VCoV = G2mth.getVCov(varyNames,varyList,data[name]['covMatrix'])
3784                esdList.append(np.sqrt(np.inner(derivs,np.inner(VCoV,derivs.T)) ))
3785            else:
3786                derivs = np.array(
3787                    [EvalPSvarDeriv(calcobj,parmDict.copy(),sampleDict[name],var,ESD)
3788                     for var,ESD in zip(varyList,sigs)])
3789                esdList.append(np.sqrt(
3790                    np.inner(derivs,np.inner(data[name]['covMatrix'],derivs.T)) ))
3791            PSvarDict = parmDict.copy()
3792            PSvarDict.update(sampleDict[name])
3793            UpdateParmDict(PSvarDict)
3794            calcobj.UpdateDict(PSvarDict)
3795            valList.append(calcobj.EvalExpression())
3796#            if calcobj.su is not None: esdList[-1] = calcobj.su
3797        if not esdList:
3798            esdList = None
3799        colList += [valList]
3800        colSigs += [esdList]
3801        colLabels += [expr]
3802        Types += [wg.GRID_VALUE_FLOAT,]
3803    #---- table build done -------------------------------------------------------------
3804
3805    # Make dict needed for creating & editing pseudovars (PSvarDict).
3806    name = histNames[0]
3807    parmDict = data[name].get('parmDict',{})
3808    PSvarDict = parmDict.copy()
3809    PSvarDict.update(sampleParms)
3810    UpdateParmDict(PSvarDict)
3811    # Also dicts of dependent (depVarDict) & independent vars (indepVarDict)
3812    # for Parametric fitting from the data table
3813    parmDict = dict(zip(colLabels,zip(*colList)[0])) # scratch dict w/all values in table
3814    parmDict.update(
3815        {var:val for var,val in data[name].get('newCellDict',{}).values()} #  add varied reciprocal cell terms
3816    )
3817    name = histNames[0]
3818
3819    #******************************************************************************
3820    # create a set of values for example evaluation of pseudovars and
3821    # this does not work for refinements that have differing numbers of variables.
3822    #raise Exception
3823    indepVarDict = {}     #  values in table w/o ESDs
3824    depVarDict = {}
3825    for i,var in enumerate(colLabels):
3826        if var == 'Use': continue
3827        if colList[i][0] is None:
3828            val,sig = firstValueDict.get(var,[None,None])
3829        elif colSigs[i]:
3830            val,sig = colList[i][0],colSigs[i][0]
3831        else:
3832            val,sig = colList[i][0],None
3833        if val is None:
3834            continue
3835        elif sig is None:
3836            indepVarDict[var] = val
3837        elif striphist(var) not in Dlookup:
3838            depVarDict[var] = val
3839    # add recip cell coeff. values
3840    depVarDict.update({var:val for var,val in data[name].get('newCellDict',{}).values()})
3841
3842    G2frame.dataDisplay = G2G.GSGrid(parent=G2frame.dataFrame)
3843    G2frame.SeqTable = G2G.Table(
3844        [list(c) for c in zip(*colList)],     # convert from columns to rows
3845        colLabels=colLabels,rowLabels=histNames,types=Types)
3846    G2frame.dataDisplay.SetTable(G2frame.SeqTable, True)
3847    #G2frame.dataDisplay.EnableEditing(False)
3848    # make all but first column read-only
3849    for c in range(1,len(colLabels)):
3850        for r in range(nRows):
3851            G2frame.dataDisplay.SetCellReadOnly(r,c)
3852    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_LEFT_DCLICK, PlotSelect)
3853    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_RIGHT_CLICK, SetLabelString)
3854    G2frame.dataDisplay.SetRowLabelSize(8*len(histNames[0]))       #pretty arbitrary 8
3855    G2frame.dataDisplay.SetMargins(0,0)
3856    G2frame.dataDisplay.AutoSizeColumns(True)
3857    if prevSize:
3858        G2frame.dataDisplay.SetSize(prevSize)
3859    else:
3860        G2frame.dataFrame.setSizePosLeft([700,350])
3861    # highlight unconverged shifts
3862    if histNames[0][:4] not in ['SASD','IMG ']:
3863        for row,name in enumerate(histNames):
3864            deltaChi = G2frame.SeqTable.GetValue(row,deltaChiCol)
3865            if deltaChi > 10.:
3866                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,0,0))
3867            elif deltaChi > 1.0:
3868                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,255,0))
3869    G2frame.dataDisplay.InstallGridToolTip(GridSetToolTip,GridColLblToolTip)
3870    G2frame.dataDisplay.SendSizeEvent() # resize needed on mac
3871    G2frame.dataDisplay.Refresh() # shows colored text on mac
3872   
3873################################################################################
3874#####  Main PWDR panel
3875################################################################################           
3876       
3877def UpdatePWHKPlot(G2frame,kind,item):
3878    '''Called when the histogram main tree entry is called. Displays the
3879    histogram weight factor, refinement statistics for the histogram
3880    and the range of data for a simulation.
3881
3882    Also invokes a plot of the histogram.
3883    '''
3884    def onEditSimRange(event):
3885        'Edit simulation range'
3886        inp = [
3887            min(data[1][0]),
3888            max(data[1][0]),
3889            None
3890            ]
3891        inp[2] = (inp[1] - inp[0])/(len(data[1][0])-1.)
3892        names = ('start angle', 'end angle', 'step size')
3893        dictlst = [inp] * len(inp)
3894        elemlst = range(len(inp))
3895        dlg = G2G.ScrolledMultiEditor(
3896            G2frame,[inp] * len(inp), range(len(inp)), names,
3897            header='Edit simulation range',
3898            minvals=(0.001,0.001,0.0001),
3899            maxvals=(180.,180.,.1),
3900            )
3901        dlg.CenterOnParent()
3902        val = dlg.ShowModal()
3903        dlg.Destroy()
3904        if val != wx.ID_OK: return
3905        if inp[0] > inp[1]:
3906            end,start,step = inp
3907        else:               
3908            start,end,step = inp
3909        step = abs(step)
3910        N = int((end-start)/step)+1
3911        newdata = np.linspace(start,end,N,True)
3912        if len(newdata) < 2: return # too small a range - reject
3913        data[1] = [newdata,np.zeros_like(newdata),np.ones_like(newdata),
3914            np.zeros_like(newdata),np.zeros_like(newdata),np.zeros_like(newdata)]
3915        Tmin = newdata[0]
3916        Tmax = newdata[-1]
3917        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,item,'Limits'),
3918            [(Tmin,Tmax),[Tmin,Tmax]])
3919        UpdatePWHKPlot(G2frame,kind,item) # redisplay data screen
3920
3921    def OnPlot3DHKL(event):
3922        refList = data[1]['RefList']
3923        FoMax = np.max(refList.T[8+Super])
3924        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
3925        Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
3926        Vpoint = np.array([int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))])
3927        controls = {'Type' : 'Fosq','Iscale' : False,'HKLmax' : Hmax,'HKLmin' : Hmin,'Zone':False,'viewKey':'L',
3928            'FoMax' : FoMax,'Scale' : 1.0,'Drawing':{'viewPoint':[Vpoint,[]],'default':Vpoint[:],
3929            'backColor':[0,0,0],'depthFog':False,'Zclip':10.0,'cameraPos':10.,'Zstep':0.05,'viewUp':[0,1,0],
3930            'Scale':1.0,'oldxy':[],'viewDir':[0,0,1]},'Super':Super,'SuperVec':SuperVec}
3931        G2plt.Plot3DSngl(G2frame,newPlot=True,Data=controls,hklRef=refList,Title=phaseName)
3932       
3933    def OnPlotAll3DHKL(event):
3934        choices = GetPatternTreeDataNames(G2frame,['HKLF',])
3935        dlg = G2G.G2MultiChoiceDialog(G2frame, 'Select reflection sets to plot',
3936            'Use data',choices)
3937        try:
3938            if dlg.ShowModal() == wx.ID_OK:
3939                refNames = [choices[i] for i in dlg.GetSelections()]
3940            else:
3941                return
3942        finally:
3943            dlg.Destroy()
3944        refList = np.zeros(0)
3945        for name in refNames:
3946            Id = GetPatternTreeItemId(G2frame,G2frame.root, name)
3947            reflData = G2frame.PatternTree.GetItemPyData(Id)[1]
3948            if len(refList):
3949                refList = np.concatenate((refList,reflData['RefList']))
3950            else:
3951                refList = reflData['RefList']
3952           
3953        FoMax = np.max(refList.T[8+Super])
3954        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
3955        Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
3956        Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
3957        controls = {'Type' : 'Fosq','Iscale' : False,'HKLmax' : Hmax,'HKLmin' : Hmin,'Zone':False,'viewKey':'L',
3958            'FoMax' : FoMax,'Scale' : 1.0,'Drawing':{'viewPoint':[Vpoint,[]],'default':Vpoint[:],
3959            'backColor':[0,0,0],'depthFog':False,'Zclip':10.0,'cameraPos':10.,'Zstep':0.05,'viewUp':[0,1,0],
3960            'Scale':1.0,'oldxy':[],'viewDir':[1,0,0]},'Super':Super,'SuperVec':SuperVec}
3961        G2plt.Plot3DSngl(G2frame,newPlot=True,Data=controls,hklRef=refList,Title=phaseName)
3962                 
3963    def OnMergeHKL(event):
3964        Name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
3965        Inst = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,
3966            G2frame.PatternId,'Instrument Parameters'))
3967        CId = GetPatternTreeItemId(G2frame,G2frame.PatternId,'Comments')
3968        if CId:
3969            Comments = G2frame.PatternTree.GetItemPyData(CId)
3970        else:
3971            Comments = []
3972        refList = np.copy(data[1]['RefList'])
3973        Comments.append(' Merging %d reflections from %s'%(len(refList),Name))
3974        dlg = MergeDialog(G2frame,data)
3975        try:
3976            if dlg.ShowModal() == wx.ID_OK:
3977                Trans,Cent,Laue = dlg.GetSelection()
3978            else:
3979                return
3980        finally:
3981            dlg.Destroy()
3982        Super = data[1]['Super']
3983        refList,badRefs = G2lat.transposeHKLF(Trans,Super,refList)
3984        if len(badRefs):    #do I want to list badRefs?
3985            G2frame.ErrorDialog('Failed transformation','Matrix yields fractional hkl indices')
3986            return
3987        Comments.append(" Transformation M*H = H' applied; M=")
3988        Comments.append(str(Trans))
3989        SG = 'P '+Laue
3990        SGData = G2spc.SpcGroup(SG)[1]
3991        refList = G2lat.LaueUnique(Laue,refList)
3992        dlg = wx.ProgressDialog('Build HKL dictonary','',len(refList)+1, 
3993            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE)
3994        HKLdict = {}
3995        for ih,hkl in enumerate(refList):
3996            if str(hkl[:3+Super]) not in HKLdict:
3997                HKLdict[str(hkl[:3+Super])] = [hkl[:3+Super],[hkl[3+Super:],]]
3998            else:
3999                HKLdict[str(hkl[:3+Super])][1].append(hkl[3+Super:])
4000            dlg.Update(ih)
4001        dlg.Destroy()
4002        mergeRef = []
4003        dlg = wx.ProgressDialog('Processing merge','',len(HKLdict)+1, 
4004            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE)
4005        sumDf = 0.
4006        sumFo = 0.
4007        for ih,hkl in enumerate(HKLdict):
4008            HKL = HKLdict[hkl]
4009            newHKL = list(HKL[0])+list(HKL[1][0])
4010            if len(HKL[1]) > 1:
4011                fos = np.array(HKL[1])
4012                wFo = 1/fos[:,3]**2
4013                Fo = np.average(fos[:,2],weights=wFo)
4014                std = np.std(fos[:,2])
4015                sig = np.sqrt(np.mean(fos[:,3])**2+std**2)
4016                sumFo += np.sum(fos[:,2])
4017                sumDf += np.sum(np.abs(fos[:,2]-Fo))
4018                dlg.Update(ih)
4019                newHKL[5+Super] = Fo
4020                newHKL[6+Super] = sig
4021                newHKL[8+Super] = Fo
4022            if newHKL[5+Super] > 0.:
4023                mergeRef.append(list(newHKL)) 
4024        dlg.Destroy()
4025        if Super:
4026            mergeRef = G2mth.sortArray(G2mth.sortArray(G2mth.sortArray(G2mth.sortArray(mergeRef,3),2),1),0)
4027        else:
4028            mergeRef = G2mth.sortArray(G2mth.sortArray(G2mth.sortArray(mergeRef,2),1),0)
4029        mergeRef = np.array(mergeRef)
4030        if sumFo:
4031            mtext = ' merge R = %6.2f%s for %d reflections in %s'%(100.*sumDf/sumFo,'%',mergeRef.shape[0],Laue)
4032            print mtext
4033            Comments.append(mtext)
4034        else:
4035            print 'nothing to merge for %s reflections'%(mergeRef.shape[0])
4036        HKLFlist = []
4037        newName = Name+' '+Laue
4038        if G2frame.PatternTree.GetCount():
4039            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
4040            while item:
4041                name = G2frame.PatternTree.GetItemText(item)
4042                if name.startswith('HKLF ') and name not in HKLFlist:
4043                    HKLFlist.append(name)
4044                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
4045        newName = G2obj.MakeUniqueLabel(newName,HKLFlist)
4046        newData = copy.deepcopy(data)
4047        newData[0]['ranId'] = ran.randint(0,sys.maxint)
4048        newData[1]['RefList'] = mergeRef
4049        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=newName)
4050        G2frame.PatternTree.SetItemPyData(
4051            G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)
4052        G2frame.PatternTree.SetItemPyData(Id,newData)
4053        G2frame.PatternTree.SetItemPyData(
4054            G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
4055        G2frame.PatternTree.SetItemPyData(
4056            G2frame.PatternTree.AppendItem(Id,text='Reflection List'),{})  #dummy entry for GUI use
4057                   
4058    def OnErrorAnalysis(event):
4059        G2plt.PlotDeltSig(G2frame,kind)
4060       
4061    def OnWtFactor(event):
4062        event.Skip()
4063        try:
4064            val = float(wtval.GetValue())
4065        except ValueError:
4066            val = data[0]['wtFactor']
4067        data[0]['wtFactor'] = val
4068        wtval.SetValue('%.3f'%(val))
4069       
4070    def OnCompression(event):
4071        data[0] = int(comp.GetValue())
4072       
4073    def onCopyPlotCtrls(event):
4074        '''Respond to menu item to copy multiple sections from a histogram.
4075        Need this here to pass on the G2frame object.
4076        '''
4077        G2pdG.CopyPlotCtrls(G2frame)
4078
4079    def onCopySelectedItems(event):
4080        '''Respond to menu item to copy multiple sections from a histogram.
4081        Need this here to pass on the G2frame object.
4082        '''
4083        G2pdG.CopySelectedHistItems(G2frame)
4084           
4085    data = G2frame.PatternTree.GetItemPyData(item)
4086#patches
4087    if not data:
4088        return
4089    if 'wtFactor' not in data[0]:
4090        data[0] = {'wtFactor':1.0}
4091#    if kind == 'PWDR' and 'Compression' not in data[0]:
4092#        data[0]['Compression'] = 1
4093    #if isinstance(data[1],list) and kind == 'HKLF':
4094    if 'list' in str(type(data[1])) and kind == 'HKLF':
4095        RefData = {'RefList':[],'FF':[]}
4096        for ref in data[1]:
4097            RefData['RefList'].append(ref[:11]+[ref[13],])
4098            RefData['FF'].append(ref[14])
4099        data[1] = RefData
4100        G2frame.PatternTree.SetItemPyData(item,data)
4101#end patches
4102    if G2frame.dataDisplay:
4103        G2frame.dataDisplay.Destroy()
4104    if kind in ['PWDR','SASD']:
4105        SetDataMenuBar(G2frame,G2frame.dataFrame.PWDRMenu)
4106        G2frame.dataFrame.Bind(wx.EVT_MENU, OnErrorAnalysis, id=wxID_PWDANALYSIS)
4107        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopySelectedItems, id=wxID_PWDCOPY)
4108        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopyPlotCtrls, id=wxID_PLOTCTRLCOPY)
4109    elif kind in ['HKLF',]:
4110        SetDataMenuBar(G2frame,G2frame.dataFrame.HKLFMenu)
4111        G2frame.dataFrame.Bind(wx.EVT_MENU, OnErrorAnalysis, id=wxID_PWDANALYSIS)
4112        G2frame.dataFrame.Bind(wx.EVT_MENU, OnMergeHKL, id=wxID_MERGEHKL)
4113        G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlot3DHKL, id=wxID_PWD3DHKLPLOT)
4114        G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotAll3DHKL, id=wxID_3DALLHKLPLOT)
4115#        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopySelectedItems, id=wxID_PWDCOPY)
4116    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
4117   
4118    mainSizer = wx.BoxSizer(wx.VERTICAL)
4119    mainSizer.Add((5,5),)
4120    wtSizer = wx.BoxSizer(wx.HORIZONTAL)
4121    wtSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Weight factor: '),0,WACV)
4122    wtval = wx.TextCtrl(G2frame.dataDisplay,-1,'%.3f'%(data[0]['wtFactor']),style=wx.TE_PROCESS_ENTER)
4123    wtval.Bind(wx.EVT_TEXT_ENTER,OnWtFactor)
4124    wtval.Bind(wx.EVT_KILL_FOCUS,OnWtFactor)
4125    wtSizer.Add(wtval,0,WACV)
4126#    if kind == 'PWDR':         #possible future compression feature; NB above patch as well
4127#        wtSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Compression factor: '),0,WACV)
4128#        choice = ['1','2','3','4','5','6']
4129#        comp = wx.ComboBox(parent=G2frame.dataDisplay,choices=choice,
4130#            style=wx.CB_READONLY|wx.CB_DROPDOWN)
4131#        comp.SetValue(str(data[0]['Compression']))
4132#        comp.Bind(wx.EVT_COMBOBOX, OnCompression)
4133#        wtSizer.Add(comp,0,WACV)
4134    mainSizer.Add(wtSizer)
4135    if data[0].get('Dummy'):
4136        simSizer = wx.BoxSizer(wx.HORIZONTAL)
4137        Tmin = min(data[1][0])
4138        Tmax = max(data[1][0])
4139        num = len(data[1][0])
4140        step = (Tmax - Tmin)/(num-1)
4141        t = u'2\u03b8' # 2theta
4142        lbl =  u'Simulation range: {:.2f} to {:.2f} {:s}\nwith {:.4f} steps ({:d} points)'
4143        lbl += u'\n(Edit range resets observed intensities).'
4144        lbl = lbl.format(Tmin,Tmax,t,step,num)
4145        simSizer.Add(wx.StaticText(G2frame.dataDisplay,wx.ID_ANY,lbl),
4146                    0,WACV)
4147        but = wx.Button(G2frame.dataDisplay,wx.ID_ANY,"Edit range")
4148        but.Bind(wx.EVT_BUTTON,onEditSimRange)
4149        simSizer.Add(but,0,WACV)
4150        mainSizer.Add(simSizer)
4151    if 'Nobs' in data[0]:
4152        mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
4153            ' Data residual wR: %.3f%% on %d observations'%(data[0]['wR'],data[0]['Nobs'])))
4154        for value in data[0]:
4155            if 'Nref' in value:
4156                pfx = value.split('Nref')[0]
4157                name = data[0].get(pfx.split(':')[0]+'::Name','?')
4158                if 'SS' in value:
4159                    mainSizer.Add((5,5),)
4160                    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' For incommensurate phase '+name+':'))
4161                    for m,(Rf2,Rf,Nobs) in enumerate(zip(data[0][pfx+'Rf^2'],data[0][pfx+'Rf'],data[0][value])):
4162                        mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
4163                            u' m = +/- %d: RF\u00b2: %.3f%%, RF: %.3f%% on %d reflections  '% \
4164                            (m,Rf2,Rf,Nobs)))
4165                else:
4166                    mainSizer.Add((5,5),)
4167                    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' For phase '+name+':'))
4168                    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
4169                        u' Unweighted phase residuals RF\u00b2: %.3f%%, RF: %.3f%% on %d reflections  '% \
4170                        (data[0][pfx+'Rf^2'],data[0][pfx+'Rf'],data[0][value])))
4171                   
4172    mainSizer.Add((5,5),)
4173    mainSizer.Layout()   
4174    G2frame.dataDisplay.SetSizer(mainSizer)
4175    Size = mainSizer.Fit(G2frame.dataFrame)
4176    Size[1] += 10
4177    G2frame.dataFrame.setSizePosLeft(Size)
4178    G2frame.PatternTree.SetItemPyData(item,data)
4179    if kind in ['PWDR','SASD']:
4180        NewPlot = True
4181        if 'xylim' in dir(G2frame):
4182            NewPlot = False
4183        G2plt.PlotPatterns(G2frame,plotType=kind,newPlot=NewPlot)
4184    elif kind == 'HKLF':
4185        Name = G2frame.PatternTree.GetItemText(item)
4186        phaseName = G2pdG.IsHistogramInAnyPhase(G2frame,Name)
4187        if phaseName:
4188            pId = GetPatternTreeItemId(G2frame,G2frame.root,'Phases')
4189            phaseId =  GetPatternTreeItemId(G2frame,pId,phaseName)
4190            General = G2frame.PatternTree.GetItemPyData(phaseId)['General']
4191            Super = General.get('Super',0)
4192            SuperVec = General.get('SuperVec',[])
4193        else:
4194            Super = 0
4195            SuperVec = []       
4196        refList = data[1]['RefList']
4197#        GSASIIpath.IPyBreak()
4198        FoMax = np.max(refList.T[5+data[1].get('Super',0)])
4199        page = G2frame.G2plotNB.nb.GetSelection()
4200        tab = ''
4201        if page >= 0:
4202            tab = G2frame.G2plotNB.nb.GetPageText(page)
4203        if '3D' in tab:
4204            Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
4205            Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
4206            Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
4207            Page = G2frame.G2plotNB.nb.GetPage(page)
4208            controls = Page.controls
4209            G2plt.Plot3DSngl(G2frame,newPlot=False,Data=controls,hklRef=refList,Title=phaseName)
4210        else:
4211            controls = {'Type' : 'Fo','ifFc' : True,     
4212                'HKLmax' : [int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))],
4213                'HKLmin' : [int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))],
4214                'FoMax' : FoMax,'Zone' : '001','Layer' : 0,'Scale' : 1.0,'Super':Super,'SuperVec':SuperVec}
4215            G2plt.PlotSngl(G2frame,newPlot=True,Data=controls,hklRef=refList)
4216                 
4217################################################################################
4218#####  Pattern tree routines
4219################################################################################           
4220       
4221def GetPatternTreeDataNames(G2frame,dataTypes):
4222    '''Finds all items in tree that match a 4 character prefix
4223   
4224    :param wx.Frame G2frame: Data tree frame object
4225    :param list dataTypes: Contains one or more data tree item types to be matched
4226      such as ['IMG '] or ['PWDR','HKLF']
4227    :returns: a list of tree item names for the matching items 
4228    '''
4229    names = []
4230    item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)       
4231    while item: