source: trunk/GSASIIgrid.py @ 2879

Last change on this file since 2879 was 2879, checked in by toby, 6 years ago

Seq. Ref.: fix tickmarks on plot; single click on hist in table plots it

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