source: trunk/GSASIIgrid.py @ 2536

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

better tickmark movement animation; menu commands

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