source: trunk/GSASIIgrid.py @ 2711

Last change on this file since 2711 was 2711, checked in by vondreele, 5 years ago

fix sequential problem with unrefined parms in a block of histograms

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