source: trunk/GSASIIgrid.py @ 2759

Last change on this file since 2759 was 2759, checked in by vondreele, 6 years ago

fix problem viewing LS parameters with constraints
fix problem accessing auto spot masks (NB: working on auto spot masks)

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