source: trunk/GSASIIgrid.py @ 2607

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

redo help to use DataFrame? helpKey; define .dataFrame.helpKey; rename MovePatternTreeToGrid? to SelectDataTreeItem? & OnPatternTreeSelChanged? to OnDataTreeSelChanged? to make more sense; key on initial string in data tree names, not presence of 'IMG' etc anywhere in name; cleanup Prefill of menus

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