source: trunk/GSASIIgrid.py @ 2480

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

add make magnetic phase to General/Transform? option
trap missing rigid bodies to define torsion seq option
fix lighting issues for polygons
fix xye importer to stop on trailing blank lines rather than crashing
fix error in powder structure factor calc
add magnetic structure factor calc. (some error still & no derivatives yet)

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