source: trunk/GSASIIgrid.py @ 2504

Last change on this file since 2504 was 2504, checked in by vondreele, 7 years ago

fix typo in G2VarObj repr 'str' not 'self'
set correct Scale for transformed magnetic phase

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