source: trunk/GSASIIgrid.py @ 2797

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

fix simulation problem when calc intensities too small

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