source: trunk/GSASIIgrid.py @ 2698

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

work over sequentia fit stuff - allow all parameters shown in table to be used in pseudoVariables & in fit equations.
Allow for gaps in columns
Sequential results can be accumulated
Allow for not used results & gaps in column averages

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