source: trunk/GSASIIgrid.py @ 1925

Last change on this file since 1925 was 1925, checked in by vondreele, 8 years ago

create AddHatomDialog? & work on OnHydAtomAdd?
allow reading of .cor images made at APS 1ID

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 159.3 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIgrid - data display routines
3########### SVN repository information ###################
4# $Date: 2015-07-10 20:50:10 +0000 (Fri, 10 Jul 2015) $
5# $Author: vondreele $
6# $Revision: 1925 $
7# $URL: trunk/GSASIIgrid.py $
8# $Id: GSASIIgrid.py 1925 2015-07-10 20:50:10Z 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 numpy as np
26import numpy.ma as ma
27import scipy.optimize as so
28import GSASIIpath
29GSASIIpath.SetVersionNumber("$Revision: 1925 $")
30import GSASIImath as G2mth
31import GSASIIIO as G2IO
32import GSASIIstrIO as G2stIO
33import GSASIIlattice as G2lat
34import GSASIIplot as G2plt
35import GSASIIpwdGUI as G2pdG
36import GSASIIimgGUI as G2imG
37import GSASIIphsGUI as G2phG
38import GSASIIspc as G2spc
39import GSASIImapvars as G2mv
40import GSASIIconstrGUI as G2cnstG
41import GSASIIrestrGUI as G2restG
42import GSASIIpy3 as G2py3
43import GSASIIobj as G2obj
44import GSASIIexprGUI as G2exG
45import GSASIIlog as log
46import GSASIIctrls as G2G
47
48# trig functions in degrees
49sind = lambda x: np.sin(x*np.pi/180.)
50tand = lambda x: np.tan(x*np.pi/180.)
51cosd = lambda x: np.cos(x*np.pi/180.)
52
53# Define a short name for convenience
54WACV = wx.ALIGN_CENTER_VERTICAL
55
56[ wxID_FOURCALC, wxID_FOURSEARCH, wxID_FOURCLEAR, wxID_PEAKSMOVE, wxID_PEAKSCLEAR, 
57    wxID_CHARGEFLIP, wxID_PEAKSUNIQUE, wxID_PEAKSDELETE, wxID_PEAKSDA,
58    wxID_PEAKSDISTVP, wxID_PEAKSVIEWPT, wxID_FINDEQVPEAKS,wxID_SHOWBONDS,wxID_MULTIMCSA,
59    wxID_SINGLEMCSA, wxID_4DMAPCOMPUTE,wxID_4DCHARGEFLIP,
60] = [wx.NewId() for item in range(17)]
61
62[ wxID_PWDRADD, wxID_HKLFADD, wxID_PWDANALYSIS, wxID_PWDCOPY, wxID_PLOTCTRLCOPY, 
63    wxID_DATADELETE,wxID_DATACOPY,wxID_DATACOPYFLAGS,wxID_DATASELCOPY,
64] = [wx.NewId() for item in range(9)]
65
66[ wxID_ATOMSEDITADD, wxID_ATOMSEDITINSERT, wxID_ATOMSEDITDELETE, wxID_ATOMSREFINE, 
67    wxID_ATOMSMODIFY, wxID_ATOMSTRANSFORM, wxID_ATOMSVIEWADD, wxID_ATOMVIEWINSERT,
68    wxID_RELOADDRAWATOMS,wxID_ATOMSDISAGL,wxID_ATOMMOVE,wxID_MAKEMOLECULE,
69    wxID_ASSIGNATMS2RB,wxID_ATOMSPDISAGL, wxID_ISODISP,wxID_ADDHATOM,
70] = [wx.NewId() for item in range(16)]
71
72[ wxID_DRAWATOMSTYLE, wxID_DRAWATOMLABEL, wxID_DRAWATOMCOLOR, wxID_DRAWATOMRESETCOLOR, 
73    wxID_DRAWVIEWPOINT, wxID_DRAWTRANSFORM, wxID_DRAWDELETE, wxID_DRAWFILLCELL, 
74    wxID_DRAWADDEQUIV, wxID_DRAWFILLCOORD, wxID_DRAWDISAGLTOR,  wxID_DRAWPLANE,
75    wxID_DRAWDISTVP,
76] = [wx.NewId() for item in range(13)]
77
78[ wxID_DRAWRESTRBOND, wxID_DRAWRESTRANGLE, wxID_DRAWRESTRPLANE, wxID_DRAWRESTRCHIRAL,
79] = [wx.NewId() for item in range(4)]
80
81[ wxID_ADDMCSAATOM,wxID_ADDMCSARB,wxID_CLEARMCSARB,wxID_MOVEMCSA,wxID_MCSACLEARRESULTS,
82] = [wx.NewId() for item in range(5)]
83
84[ wxID_CLEARTEXTURE,wxID_REFINETEXTURE,
85] = [wx.NewId() for item in range(2)]
86
87[ wxID_PAWLEYLOAD, wxID_PAWLEYESTIMATE, wxID_PAWLEYUPDATE,
88] = [wx.NewId() for item in range(3)]
89
90[ wxID_IMCALIBRATE,wxID_IMRECALIBRATE,wxID_IMINTEGRATE, wxID_IMCLEARCALIB, 
91    wxID_IMCOPYCONTROLS, wxID_INTEGRATEALL, wxID_IMSAVECONTROLS, wxID_IMLOADCONTROLS,
92] = [wx.NewId() for item in range(8)]
93
94[ wxID_MASKCOPY, wxID_MASKSAVE, wxID_MASKLOAD, wxID_NEWMASKSPOT,wxID_NEWMASKARC,wxID_NEWMASKRING,
95    wxID_NEWMASKFRAME, wxID_NEWMASKPOLY,  wxID_MASKLOADNOT,
96] = [wx.NewId() for item in range(9)]
97
98[ wxID_STRSTACOPY, wxID_STRSTAFIT, wxID_STRSTASAVE, wxID_STRSTALOAD,wxID_STRSTSAMPLE,
99    wxID_APPENDDZERO,wxID_STRSTAALLFIT,wxID_UPDATEDZERO,
100] = [wx.NewId() for item in range(8)]
101
102[ wxID_BACKCOPY,wxID_LIMITCOPY, wxID_SAMPLECOPY, wxID_SAMPLECOPYSOME, wxID_BACKFLAGCOPY, wxID_SAMPLEFLAGCOPY,
103    wxID_SAMPLESAVE, wxID_SAMPLELOAD,wxID_ADDEXCLREGION,wxID_SETSCALE,wxID_SAMPLE1VAL,wxID_ALLSAMPLELOAD,
104] = [wx.NewId() for item in range(12)]
105
106[ wxID_INSTPRMRESET,wxID_CHANGEWAVETYPE,wxID_INSTCOPY, wxID_INSTFLAGCOPY, wxID_INSTLOAD,
107    wxID_INSTSAVE, wxID_INST1VAL, wxID_INSTCALIB,
108] = [wx.NewId() for item in range(8)]
109
110[ wxID_UNDO,wxID_LSQPEAKFIT,wxID_LSQONECYCLE,wxID_RESETSIGGAM,wxID_CLEARPEAKS,wxID_AUTOSEARCH,
111    wxID_PEAKSCOPY, wxID_SEQPEAKFIT,
112] = [wx.NewId() for item in range(8)]
113
114[  wxID_INDXRELOAD, wxID_INDEXPEAKS, wxID_REFINECELL, wxID_COPYCELL, wxID_MAKENEWPHASE,
115    wxID_EXPORTCELLS,
116] = [wx.NewId() for item in range(6)]
117
118[ wxID_CONSTRAINTADD,wxID_EQUIVADD,wxID_HOLDADD,wxID_FUNCTADD,wxID_ADDRIDING,
119  wxID_CONSPHASE, wxID_CONSHIST, wxID_CONSHAP, wxID_CONSGLOBAL,wxID_EQUIVALANCEATOMS,
120] = [wx.NewId() for item in range(10)]
121
122[ wxID_RESTRAINTADD, wxID_RESTSELPHASE,wxID_RESTDELETE, wxID_RESRCHANGEVAL, 
123    wxID_RESTCHANGEESD,wxID_AARESTRAINTADD,wxID_AARESTRAINTPLOT,
124] = [wx.NewId() for item in range(7)]
125
126[ wxID_RIGIDBODYADD,wxID_DRAWDEFINERB,wxID_RIGIDBODYIMPORT,wxID_RESIDUETORSSEQ,
127    wxID_AUTOFINDRESRB,wxID_GLOBALRESREFINE,wxID_RBREMOVEALL,wxID_COPYRBPARMS,
128    wxID_GLOBALTHERM,wxID_VECTORBODYADD
129] = [wx.NewId() for item in range(10)]
130
131[ wxID_RENAMESEQSEL,wxID_SAVESEQSEL,wxID_SAVESEQSELCSV,wxID_SAVESEQCSV,wxID_PLOTSEQSEL,
132  wxID_ORGSEQSEL,wxADDSEQVAR,wxDELSEQVAR,wxEDITSEQVAR,wxCOPYPARFIT,wxID_AVESEQSEL,
133  wxADDPARFIT,wxDELPARFIT,wxEDITPARFIT,wxDOPARFIT,
134] = [wx.NewId() for item in range(15)]
135
136[ wxID_MODELCOPY,wxID_MODELFIT,wxID_MODELADD,wxID_ELEMENTADD,wxID_ELEMENTDELETE,
137    wxID_ADDSUBSTANCE,wxID_LOADSUBSTANCE,wxID_DELETESUBSTANCE,wxID_COPYSUBSTANCE,
138    wxID_MODELUNDO,wxID_MODELFITALL,wxID_MODELCOPYFLAGS,
139] = [wx.NewId() for item in range(12)]
140
141[ wxID_SELECTPHASE,wxID_PWDHKLPLOT,wxID_PWD3DHKLPLOT,wxID_3DALLHKLPLOT,
142] = [wx.NewId() for item in range(4)]
143
144[ wxID_PDFCOPYCONTROLS, wxID_PDFSAVECONTROLS, wxID_PDFLOADCONTROLS, 
145    wxID_PDFCOMPUTE, wxID_PDFCOMPUTEALL, wxID_PDFADDELEMENT, wxID_PDFDELELEMENT,
146] = [wx.NewId() for item in range(7)]
147
148[ wxID_MCRON,wxID_MCRLIST,wxID_MCRSAVE,wxID_MCRPLAY,
149] = [wx.NewId() for item in range(4)]
150
151VERY_LIGHT_GREY = wx.Colour(235,235,235)
152
153# Aliases for Classes/Functions moved to GSASIIctrls, all should be tracked down but leaving as a reminder
154#SingleFloatDialog = G2G.SingleFloatDialog
155#SingleStringDialog = G2G.SingleStringDialog
156#MultiStringDialog = G2G.MultiStringDialog
157#G2ColumnIDDialog = G2G.G2ColumnIDDialog
158#ItemSelector = G2G.ItemSelector
159#HorizontalLine = G2G.HorizontalLine
160#G2LoggedButton = G2G.G2LoggedButton
161#EnumSelector = G2G.EnumSelector
162#G2ChoiceButton = G2G.G2ChoiceButton
163#GSGrid = G2G.GSGrid
164#Table = G2G.Table
165#GridFractionEditor = G2G.GridFractionEditor
166#GSNoteBook = G2G.GSNoteBook
167
168# Should SGMessageBox, SymOpDialog, DisAglDialog be moved?
169
170################################################################################
171#### GSAS-II class definitions
172################################################################################
173
174class SGMessageBox(wx.Dialog):
175    ''' Special version of MessageBox that displays space group & super space group text
176    in two blocks
177    '''
178    def __init__(self,parent,title,text,table,):
179        wx.Dialog.__init__(self,parent,wx.ID_ANY,title,pos=wx.DefaultPosition,
180            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
181        self.text=text
182        self.table = table
183        self.panel = wx.Panel(self)
184        mainSizer = wx.BoxSizer(wx.VERTICAL)
185        mainSizer.Add((0,10))
186        for line in text:
187            mainSizer.Add(wx.StaticText(self.panel,label='     %s     '%(line)),0,WACV)
188        ncol = self.table[0].count(',')+1
189        tableSizer = wx.FlexGridSizer(0,2*ncol+3,0,0)
190        for j,item in enumerate(self.table):
191            num,flds = item.split(')')
192            tableSizer.Add(wx.StaticText(self.panel,label='     %s  '%(num+')')),0,WACV|wx.ALIGN_LEFT)           
193            flds = flds.replace(' ','').split(',')
194            for i,fld in enumerate(flds):
195                if i < ncol-1:
196                    tableSizer.Add(wx.StaticText(self.panel,label='%s, '%(fld)),0,WACV|wx.ALIGN_RIGHT)
197                else:
198                    tableSizer.Add(wx.StaticText(self.panel,label='%s'%(fld)),0,WACV|wx.ALIGN_RIGHT)
199            if not j%2:
200                tableSizer.Add((20,0))
201        mainSizer.Add(tableSizer,0,wx.ALIGN_LEFT)
202        btnsizer = wx.StdDialogButtonSizer()
203        OKbtn = wx.Button(self.panel, wx.ID_OK)
204        OKbtn.SetDefault()
205        btnsizer.AddButton(OKbtn)
206        btnsizer.Realize()
207        mainSizer.Add((0,10))
208        mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER)
209        self.panel.SetSizer(mainSizer)
210        self.panel.Fit()
211        self.Fit()
212        size = self.GetSize()
213        self.SetSize([size[0]+20,size[1]])
214
215    def Show(self):
216        '''Use this method after creating the dialog to post it
217        '''
218        self.ShowModal()
219        return
220
221################################################################################
222class SymOpDialog(wx.Dialog):
223    '''Class to select a symmetry operator
224    '''
225    def __init__(self,parent,SGData,New=True,ForceUnit=False):
226        wx.Dialog.__init__(self,parent,-1,'Select symmetry operator',
227            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
228        panel = wx.Panel(self)
229        self.SGData = SGData
230        self.New = New
231        self.Force = ForceUnit
232        self.OpSelected = [0,0,0,[0,0,0],False,False]
233        mainSizer = wx.BoxSizer(wx.VERTICAL)
234        if ForceUnit:
235            choice = ['No','Yes']
236            self.force = wx.RadioBox(panel,-1,'Force to unit cell?',choices=choice)
237            self.force.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
238            mainSizer.Add(self.force,0,WACV|wx.TOP,5)
239#        if SGData['SGInv']:
240        choice = ['No','Yes']
241        self.inv = wx.RadioBox(panel,-1,'Choose inversion?',choices=choice)
242        self.inv.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
243        mainSizer.Add(self.inv,0,WACV)
244        if SGData['SGLatt'] != 'P':
245            LattOp = G2spc.Latt2text(SGData['SGLatt']).split(';')
246            self.latt = wx.RadioBox(panel,-1,'Choose cell centering?',choices=LattOp)
247            self.latt.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
248            mainSizer.Add(self.latt,0,WACV)
249        if SGData['SGLaue'] in ['-1','2/m','mmm','4/m','4/mmm']:
250            Ncol = 2
251        else:
252            Ncol = 3
253        OpList = []
254        for Opr in SGData['SGOps']:
255            OpList.append(G2spc.MT2text(Opr))
256        self.oprs = wx.RadioBox(panel,-1,'Choose space group operator?',choices=OpList,
257            majorDimension=Ncol)
258        self.oprs.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
259        mainSizer.Add(self.oprs,0,WACV|wx.BOTTOM,5)
260        mainSizer.Add(wx.StaticText(panel,-1,"   Choose unit cell?"),0,WACV)
261        cellSizer = wx.BoxSizer(wx.HORIZONTAL)
262        cellName = ['X','Y','Z']
263        self.cell = []
264        for i in range(3):
265            self.cell.append(wx.SpinCtrl(panel,-1,cellName[i],size=wx.Size(50,20)))
266            self.cell[-1].SetRange(-3,3)
267            self.cell[-1].SetValue(0)
268            self.cell[-1].Bind(wx.EVT_SPINCTRL, self.OnOpSelect)
269            cellSizer.Add(self.cell[-1],0,WACV)
270        mainSizer.Add(cellSizer,0,WACV|wx.BOTTOM,5)
271        if self.New:
272            choice = ['No','Yes']
273            self.new = wx.RadioBox(panel,-1,'Generate new positions?',choices=choice)
274            self.new.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
275            mainSizer.Add(self.new,0,WACV)
276
277        OkBtn = wx.Button(panel,-1,"Ok")
278        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
279        cancelBtn = wx.Button(panel,-1,"Cancel")
280        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
281        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
282        btnSizer.Add((20,20),1)
283        btnSizer.Add(OkBtn)
284        btnSizer.Add((20,20),1)
285        btnSizer.Add(cancelBtn)
286        btnSizer.Add((20,20),1)
287
288        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
289        panel.SetSizer(mainSizer)
290        panel.Fit()
291        self.Fit()
292
293    def OnOpSelect(self,event):
294#        if self.SGData['SGInv']:
295        self.OpSelected[0] = self.inv.GetSelection()
296        if self.SGData['SGLatt'] != 'P':
297            self.OpSelected[1] = self.latt.GetSelection()
298        self.OpSelected[2] = self.oprs.GetSelection()
299        for i in range(3):
300            self.OpSelected[3][i] = float(self.cell[i].GetValue())
301        if self.New:
302            self.OpSelected[4] = self.new.GetSelection()
303        if self.Force:
304            self.OpSelected[5] = self.force.GetSelection()
305
306    def GetSelection(self):
307        return self.OpSelected
308
309    def OnOk(self,event):
310        parent = self.GetParent()
311        parent.Raise()
312        self.EndModal(wx.ID_OK)
313
314    def OnCancel(self,event):
315        parent = self.GetParent()
316        parent.Raise()
317        self.EndModal(wx.ID_CANCEL)
318       
319class AddHatomDialog(wx.Dialog):
320    '''H atom addition dialog. After :meth:`ShowModal` returns, the results
321    are found in dict :attr:`self.data`, which is accessed using :meth:`GetData`.
322    :param wx.Frame parent: reference to parent frame (or None)
323    :param dict Neigh: a dict of atom names with list of atom name, dist pairs for neighboring atoms
324    :param dict phase: a dict containing the phase as defined by
325      :ref:`Phase Tree Item <Phase_table>`   
326    '''
327    def __init__(self,parent,Neigh,phase):
328        wx.Dialog.__init__(self,parent,wx.ID_ANY,'H atom add', 
329            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
330        self.panel = wxscroll.ScrolledPanel(self)         #just a dummy - gets destroyed in Draw!
331        self.Neigh = Neigh
332        self.phase = phase
333        self.Hatoms = []
334        self.Draw(self.Neigh,self.phase)
335           
336    def Draw(self,Neigh,phase):
337        '''Creates the contents of the dialog. Normally called
338        by :meth:`__init__`.
339        '''
340        def OnHSelect(event):
341            Obj = event.GetEventObject()
342            item,i = Indx[Obj.GetId()]
343            for obj in Indx[item]:
344                obj.SetValue(False)
345            Obj.SetValue(True)
346            self.Neigh[item][2] = i
347           
348        self.panel.Destroy()
349        self.panel = wxscroll.ScrolledPanel(self,style = wx.DEFAULT_DIALOG_STYLE)
350        mainSizer = wx.BoxSizer(wx.VERTICAL)
351        mainSizer.Add(wx.StaticText(self.panel,-1,'H atom add controls for phase %s:'%(phase['General']['Name'])),
352            0,wx.LEFT|wx.TOP,10)
353        mainSizer.Add(wx.StaticText(self.panel,-1," Atom:  Add # H's          Neighbors, dist"),0,wx.TOP|wx.LEFT,5)
354        nHatms = ['0','1','2','3']
355        dataSizer = wx.FlexGridSizer(0,3,0,0)
356        Indx = {}
357        for inei,neigh in enumerate(Neigh):
358            dataSizer.Add(wx.StaticText(self.panel,-1,' %s:  '%(neigh[0])),0,WACV)
359            nH = 1      #for O atom
360            if 'C' in neigh[0] or 'N' in neigh[0]:
361                nH = 4-len(neigh[1])
362            neigh[2] = nH
363            checks = wx.BoxSizer(wx.HORIZONTAL)
364            Ids = []
365            for i in range(nH+1):
366                nHs = wx.CheckBox(self.panel,-1,label=nHatms[i])
367                if i == neigh[2]:
368                    nHs.SetValue(True)
369                Indx[nHs.GetId()] = [inei,i]
370                Ids.append(nHs)
371                nHs.Bind(wx.EVT_CHECKBOX, OnHSelect)
372                checks.Add(nHs,0,WACV)
373            Indx[inei] = Ids
374            dataSizer.Add(checks,0,WACV)
375            lineSizer = wx.BoxSizer(wx.HORIZONTAL)
376            for bond in neigh[1]:
377                lineSizer.Add(wx.StaticText(self.panel,-1,' %s, %.3f'%(bond[0],bond[1])),0,WACV)
378            dataSizer.Add(lineSizer,0,WACV)
379        mainSizer.Add(dataSizer,0,wx.LEFT,5)
380
381        CancelBtn = wx.Button(self.panel,-1,'Cancel')
382        CancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
383        OkBtn = wx.Button(self.panel,-1,'Ok')
384        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
385        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
386        btnSizer.Add((20,20),1)
387        btnSizer.Add(OkBtn)
388        btnSizer.Add((20,20),1)
389        btnSizer.Add(CancelBtn)
390        btnSizer.Add((20,20),1)
391        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
392        self.panel.SetSizer(mainSizer)
393        self.panel.SetupScrolling()
394       
395    def GetData(self):
396        'Returns the values from the dialog'
397        return self.Neigh       #has #Hs to add for each entry
398       
399    def OnOk(self,event):
400        'Called when the OK button is pressed'
401        parent = self.GetParent()
402        parent.Raise()
403        self.EndModal(wx.ID_OK)             
404
405    def OnCancel(self,event):
406        parent = self.GetParent()
407        parent.Raise()
408        self.EndModal(wx.ID_CANCEL)
409
410class DisAglDialog(wx.Dialog):
411    '''Distance/Angle Controls input dialog. After
412    :meth:`ShowModal` returns, the results are found in
413    dict :attr:`self.data`, which is accessed using :meth:`GetData`.
414
415    :param wx.Frame parent: reference to parent frame (or None)
416    :param dict data: a dict containing the current
417      search ranges or an empty dict, which causes default values
418      to be used.
419      Will be used to set element `DisAglCtls` in
420      :ref:`Phase Tree Item <Phase_table>`
421    :param dict default:  A dict containing the default
422      search ranges for each element.
423    '''
424    def __init__(self,parent,data,default,Reset=True):
425        wx.Dialog.__init__(self,parent,wx.ID_ANY,
426                           'Distance Angle Controls', 
427            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
428        self.default = default
429        self.Reset = Reset
430        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
431        self._default(data,self.default)
432        self.Draw(self.data)
433               
434    def _default(self,data,default):
435        '''Set starting values for the search values, either from
436        the input array or from defaults, if input is null
437        '''
438        if data:
439            self.data = copy.deepcopy(data) # don't mess with originals
440        else:
441            self.data = {}
442            self.data['Name'] = default['Name']
443            self.data['Factors'] = [0.85,0.85]
444            self.data['AtomTypes'] = default['AtomTypes']
445            self.data['BondRadii'] = default['BondRadii'][:]
446            self.data['AngleRadii'] = default['AngleRadii'][:]
447
448    def Draw(self,data):
449        '''Creates the contents of the dialog. Normally called
450        by :meth:`__init__`.
451        '''
452        self.panel.Destroy()
453        self.panel = wx.Panel(self)
454        mainSizer = wx.BoxSizer(wx.VERTICAL)
455        mainSizer.Add(wx.StaticText(self.panel,-1,'Controls for phase '+data['Name']),
456            0,WACV|wx.LEFT,10)
457        mainSizer.Add((10,10),1)
458       
459        radiiSizer = wx.FlexGridSizer(0,3,5,5)
460        radiiSizer.Add(wx.StaticText(self.panel,-1,' Type'),0,WACV)
461        radiiSizer.Add(wx.StaticText(self.panel,-1,'Bond radii'),0,WACV)
462        radiiSizer.Add(wx.StaticText(self.panel,-1,'Angle radii'),0,WACV)
463        self.objList = {}
464        for id,item in enumerate(self.data['AtomTypes']):
465            radiiSizer.Add(wx.StaticText(self.panel,-1,' '+item),0,WACV)
466            bRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['BondRadii'][id]),style=wx.TE_PROCESS_ENTER)
467            self.objList[bRadii.GetId()] = ['BondRadii',id]
468            bRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
469            bRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
470            radiiSizer.Add(bRadii,0,WACV)
471            aRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['AngleRadii'][id]),style=wx.TE_PROCESS_ENTER)
472            self.objList[aRadii.GetId()] = ['AngleRadii',id]
473            aRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
474            aRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
475            radiiSizer.Add(aRadii,0,WACV)
476        mainSizer.Add(radiiSizer,0,wx.EXPAND)
477        factorSizer = wx.FlexGridSizer(0,2,5,5)
478        Names = ['Bond','Angle']
479        for i,name in enumerate(Names):
480            factorSizer.Add(wx.StaticText(self.panel,-1,name+' search factor'),0,WACV)
481            bondFact = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['Factors'][i]),style=wx.TE_PROCESS_ENTER)
482            self.objList[bondFact.GetId()] = ['Factors',i]
483            bondFact.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
484            bondFact.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
485            factorSizer.Add(bondFact)
486        mainSizer.Add(factorSizer,0,wx.EXPAND)
487       
488        OkBtn = wx.Button(self.panel,-1,"Ok")
489        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
490        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
491        btnSizer.Add((20,20),1)
492        btnSizer.Add(OkBtn)
493        if self.Reset:
494            ResetBtn = wx.Button(self.panel,-1,'Reset')
495            ResetBtn.Bind(wx.EVT_BUTTON, self.OnReset)
496            btnSizer.Add(ResetBtn)
497        btnSizer.Add((20,20),1)
498        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
499        self.panel.SetSizer(mainSizer)
500        self.panel.Fit()
501        self.Fit()
502   
503    def OnRadiiVal(self,event):
504        Obj = event.GetEventObject()
505        item = self.objList[Obj.GetId()]
506        try:
507            self.data[item[0]][item[1]] = float(Obj.GetValue())
508        except ValueError:
509            pass
510        Obj.SetValue("%.3f"%(self.data[item[0]][item[1]]))          #reset in case of error
511       
512    def GetData(self):
513        'Returns the values from the dialog'
514        return self.data
515       
516    def OnOk(self,event):
517        'Called when the OK button is pressed'
518        parent = self.GetParent()
519        parent.Raise()
520        self.EndModal(wx.ID_OK)             
521       
522    def OnReset(self,event):
523        'Called when the Reset button is pressed'
524        data = {}
525        self._default(data,self.default)
526        self.Draw(self.data)
527               
528################################################################################
529class ShowLSParms(wx.Dialog):
530    '''Create frame to show least-squares parameters
531    '''
532    def __init__(self,parent,title,parmDict,varyList,fullVaryList,
533                 size=(300,430)):
534        wx.Dialog.__init__(self,parent,wx.ID_ANY,title,size=size,
535                           style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
536        mainSizer = wx.BoxSizer(wx.VERTICAL)
537
538        panel = wxscroll.ScrolledPanel(
539            self, wx.ID_ANY,
540            #size=size,
541            style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
542        num = len(varyList)
543        mainSizer.Add(wx.StaticText(self,wx.ID_ANY,'Number of refined variables: '+str(num)))
544        if len(varyList) != len(fullVaryList):
545            num = len(fullVaryList) - len(varyList)
546            mainSizer.Add(wx.StaticText(self,wx.ID_ANY,' + '+str(num)+' parameters are varied via constraints'))
547        subSizer = wx.FlexGridSizer(cols=4,hgap=2,vgap=2)
548        parmNames = parmDict.keys()
549        parmNames.sort()
550        subSizer.Add((-1,-1))
551        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'Parameter name  '))
552        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'refine?'))
553        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'value'),0,wx.ALIGN_RIGHT)
554        explainRefine = False
555        for name in parmNames:
556            # skip entries without numerical values
557            if isinstance(parmDict[name],basestring): continue
558            try:
559                value = G2py3.FormatSigFigs(parmDict[name])
560            except TypeError:
561                value = str(parmDict[name])+' -?' # unexpected
562                #continue
563            v = G2obj.getVarDescr(name)
564            if v is None or v[-1] is None:
565                subSizer.Add((-1,-1))
566            else:               
567                ch = G2G.HelpButton(panel,G2obj.fmtVarDescr(name))
568                subSizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)
569            subSizer.Add(wx.StaticText(panel,wx.ID_ANY,str(name)))
570            if name in varyList:
571                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'R'))
572            elif name in fullVaryList:
573                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'C'))
574                explainRefine = True
575            else:
576                subSizer.Add((-1,-1))
577            subSizer.Add(wx.StaticText(panel,wx.ID_ANY,value),0,wx.ALIGN_RIGHT)
578
579        # finish up ScrolledPanel
580        panel.SetSizer(subSizer)
581        panel.SetAutoLayout(1)
582        panel.SetupScrolling()
583        mainSizer.Add(panel,1, wx.ALL|wx.EXPAND,1)
584
585        if explainRefine:
586            mainSizer.Add(
587                wx.StaticText(self,wx.ID_ANY,
588                          '"R" indicates a refined variable\n'+
589                          '"C" indicates generated from a constraint'
590                          ),
591                0, wx.ALL,0)
592        # make OK button
593        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
594        btn = wx.Button(self, wx.ID_CLOSE,"Close") 
595        btn.Bind(wx.EVT_BUTTON,self._onClose)
596        btnsizer.Add(btn)
597        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
598        # Allow window to be enlarged but not made smaller
599        self.SetSizer(mainSizer)
600        self.SetMinSize(self.GetSize())
601
602    def _onClose(self,event):
603        self.EndModal(wx.ID_CANCEL)
604 
605################################################################################
606class DataFrame(wx.Frame):
607    '''Create the data item window and all the entries in menus used in
608    that window. For Linux and windows, the menu entries are created for the
609    current data item window, but in the Mac the menu is accessed from all
610    windows. This means that a different menu is posted depending on which
611    data item is posted. On the Mac, all the menus contain the data tree menu
612    items, but additional menus are added specific to the data item.
613
614    Note that while the menus are created here,
615    the binding for the menus is done later in various GSASII*GUI modules,
616    where the functions to be called are defined.
617    '''
618    def Bind(self,eventtype,handler,*args,**kwargs):
619        '''Override the Bind() function: on the Mac the binding is to
620        the main window, so that menus operate with any window on top.
621        For other platforms, either wrap calls that will be logged
622        or call the default wx.Frame Bind() to bind to the menu item directly.
623
624        Note that bindings can be made to objects by Id or by direct reference to the
625        object. As a convention, when bindings are to objects, they are not logged
626        but when bindings are by Id, they are logged.
627        '''
628        if sys.platform == "darwin": # mac
629            self.G2frame.Bind(eventtype,handler,*args,**kwargs)
630            return
631        if eventtype == wx.EVT_MENU and 'id' in kwargs:
632            menulabels = log.SaveMenuCommand(kwargs['id'],self.G2frame,handler)
633            if menulabels:
634                #print 'intercepting bind for',handler,menulabels,kwargs['id']
635                wx.Frame.Bind(self,eventtype,self.G2frame.MenuBinding,*args,**kwargs)
636                return
637            wx.Frame.Bind(self,eventtype,handler,*args,**kwargs)     
638       
639    def PrefillDataMenu(self,menu,helpType,helpLbl=None,empty=False):
640        '''Create the "standard" part of data frame menus. Note that on Linux and
641        Windows nothing happens here. On Mac, this menu duplicates the
642        tree menu, but adds an extra help command for the data item and a separator.
643        '''
644        self.datamenu = menu
645        self.G2frame.dataMenuBars.append(menu)
646        self.helpType = helpType
647        self.helpLbl = helpLbl
648        if sys.platform == "darwin": # mac                         
649            self.G2frame.FillMainMenu(menu) # add the data tree menu items
650            if not empty:
651                menu.Append(wx.Menu(title=''),title='|') # add a separator
652       
653    def PostfillDataMenu(self,empty=False):
654        '''Create the "standard" part of data frame menus. Note that on Linux and
655        Windows, this is the standard help Menu. On Mac, this menu duplicates the
656        tree menu, but adds an extra help command for the data item and a separator.
657        '''
658        menu = self.datamenu
659        helpType = self.helpType
660        helpLbl = self.helpLbl
661        if sys.platform == "darwin": # mac
662            if not empty:
663                menu.Append(wx.Menu(title=''),title='|') # add another separator
664            menu.Append(G2G.AddHelp(self.G2frame,helpType=helpType, helpLbl=helpLbl),
665                        title='&Help')
666        else: # other
667            menu.Append(menu=G2G.MyHelp(self,helpType=helpType, helpLbl=helpLbl),
668                        title='&Help')
669
670    def _init_menus(self):
671        'define all GSAS-II data frame menus'
672
673        # for use where no menu or data frame help is provided
674        self.BlankMenu = wx.MenuBar()
675       
676        # Controls
677        self.ControlsMenu = wx.MenuBar()
678        self.PrefillDataMenu(self.ControlsMenu,helpType='Controls',empty=True)
679        self.PostfillDataMenu(empty=True)
680       
681        # Notebook
682        self.DataNotebookMenu = wx.MenuBar() 
683        self.PrefillDataMenu(self.DataNotebookMenu,helpType='Notebook',empty=True)
684        self.PostfillDataMenu(empty=True)
685       
686        # Comments
687        self.DataCommentsMenu = wx.MenuBar()
688        self.PrefillDataMenu(self.DataCommentsMenu,helpType='Comments',empty=True)
689        self.PostfillDataMenu(empty=True)
690       
691        # Constraints - something amiss here - get weird wx C++ error after refine!
692        self.ConstraintMenu = wx.MenuBar()
693        self.PrefillDataMenu(self.ConstraintMenu,helpType='Constraints')
694        self.ConstraintTab = wx.Menu(title='')
695        self.ConstraintMenu.Append(menu=self.ConstraintTab, title='Select tab')
696        for id,txt in (
697            (wxID_CONSPHASE,'Phase'),
698            (wxID_CONSHAP,'Histogram/Phase'),
699            (wxID_CONSHIST,'Histogram'),
700            (wxID_CONSGLOBAL,'Global')):
701            self.ConstraintTab.Append(
702                id=id, kind=wx.ITEM_NORMAL,text=txt,
703                help='Select '+txt+' constraint editing tab')
704        self.ConstraintEdit = wx.Menu(title='')
705        self.ConstraintMenu.Append(menu=self.ConstraintEdit, title='Edit')
706        self.ConstraintEdit.Append(id=wxID_HOLDADD, kind=wx.ITEM_NORMAL,text='Add hold',
707            help='Add hold on a parameter value')
708        self.ConstraintEdit.Append(id=wxID_EQUIVADD, kind=wx.ITEM_NORMAL,text='Add equivalence',
709            help='Add equivalence between parameter values')
710        self.ConstraintEdit.Append(id=wxID_CONSTRAINTADD, kind=wx.ITEM_NORMAL,text='Add constraint',
711            help='Add constraint on parameter values')
712        self.ConstraintEdit.Append(id=wxID_FUNCTADD, kind=wx.ITEM_NORMAL,text='Add New Var',
713            help='Add variable composed of existing parameter')
714        self.ConstraintEdit.Append(id=wxID_EQUIVALANCEATOMS, kind=wx.ITEM_NORMAL,text='Add atom equivalence',
715            help='Add equivalences between atom parameter values')
716        self.ConstraintEdit.Enable(wxID_EQUIVALANCEATOMS,False)
717        self.ConstraintEdit.Append(id=wxID_ADDRIDING, kind=wx.ITEM_NORMAL,text='Add H riding constraints',
718            help='Add H atom riding constraints between atom parameter values')
719        self.ConstraintEdit.Enable(wxID_ADDRIDING,False)
720        wxID_ADDRIDING
721        self.PostfillDataMenu()
722
723        # item = self.ConstraintEdit.Append(id=wx.ID_ANY,kind=wx.ITEM_NORMAL,text='Update GUI')
724        # def UpdateGSASIIconstrGUI(event):
725        #     import GSASIIconstrGUI
726        #     reload(GSASIIconstrGUI)
727        #     import GSASIIobj
728        #     reload(GSASIIobj)
729        # self.Bind(wx.EVT_MENU,UpdateGSASIIconstrGUI,id=item.GetId())
730
731        # Rigid bodies
732        self.RigidBodyMenu = wx.MenuBar()
733        self.PrefillDataMenu(self.RigidBodyMenu,helpType='Rigid bodies')
734        self.ResidueRBMenu = wx.Menu(title='')
735        self.ResidueRBMenu.Append(id=wxID_RIGIDBODYIMPORT, kind=wx.ITEM_NORMAL,text='Import XYZ',
736            help='Import rigid body XYZ from file')
737        self.ResidueRBMenu.Append(id=wxID_RESIDUETORSSEQ, kind=wx.ITEM_NORMAL,text='Define sequence',
738            help='Define torsion sequence')
739        self.ResidueRBMenu.Append(id=wxID_RIGIDBODYADD, kind=wx.ITEM_NORMAL,text='Import residues',
740            help='Import residue rigid bodies from macro file')
741        self.RigidBodyMenu.Append(menu=self.ResidueRBMenu, title='Edit Body')
742        self.PostfillDataMenu()
743
744        self.VectorBodyMenu = wx.MenuBar()
745        self.PrefillDataMenu(self.VectorBodyMenu,helpType='Vector rigid bodies')
746        self.VectorRBEdit = wx.Menu(title='')
747        self.VectorRBEdit.Append(id=wxID_VECTORBODYADD, kind=wx.ITEM_NORMAL,text='Add rigid body',
748            help='Add vector rigid body')
749        self.VectorBodyMenu.Append(menu=self.VectorRBEdit, title='Edit Vector Body')
750        self.PostfillDataMenu()
751
752                   
753        # Restraints
754        self.RestraintTab = wx.Menu(title='')
755        self.RestraintEdit = wx.Menu(title='')
756        self.RestraintEdit.Append(id=wxID_RESTSELPHASE, kind=wx.ITEM_NORMAL,text='Select phase',
757            help='Select phase')
758        self.RestraintEdit.Append(id=wxID_RESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add restraints',
759            help='Add restraints')
760        self.RestraintEdit.Enable(wxID_RESTRAINTADD,True)    #gets disabled if macromolecule phase
761        self.RestraintEdit.Append(id=wxID_AARESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add residue restraints',
762            help='Add residue based restraints for macromolecules from macro file')
763        self.RestraintEdit.Enable(wxID_AARESTRAINTADD,False)    #gets enabled if macromolecule phase
764        self.RestraintEdit.Append(id=wxID_AARESTRAINTPLOT, kind=wx.ITEM_NORMAL,text='Plot residue restraints',
765            help='Plot selected residue based restraints for macromolecules from macro file')
766        self.RestraintEdit.Enable(wxID_AARESTRAINTPLOT,False)    #gets enabled if macromolecule phase
767        self.RestraintEdit.Append(id=wxID_RESRCHANGEVAL, kind=wx.ITEM_NORMAL,text='Change value',
768            help='Change observed value')
769        self.RestraintEdit.Append(id=wxID_RESTCHANGEESD, kind=wx.ITEM_NORMAL,text='Change esd',
770            help='Change esd in observed value')
771        self.RestraintEdit.Append(id=wxID_RESTDELETE, kind=wx.ITEM_NORMAL,text='Delete restraints',
772            help='Delete selected restraints')
773
774        self.RestraintMenu = wx.MenuBar()
775        self.PrefillDataMenu(self.RestraintMenu,helpType='Restraints')
776        self.RestraintMenu.Append(menu=self.RestraintTab, title='Select tab')
777        self.RestraintMenu.Append(menu=self.RestraintEdit, title='Edit')
778        self.PostfillDataMenu()
779           
780        # Sequential results
781        self.SequentialMenu = wx.MenuBar()
782        self.PrefillDataMenu(self.SequentialMenu,helpType='Sequential',helpLbl='Sequential Refinement')
783        self.SequentialFile = wx.Menu(title='')
784        self.SequentialMenu.Append(menu=self.SequentialFile, title='Columns')
785        self.SequentialFile.Append(id=wxID_RENAMESEQSEL, kind=wx.ITEM_NORMAL,text='Rename selected',
786            help='Rename selected sequential refinement columns')
787        self.SequentialFile.Append(id=wxID_SAVESEQSEL, kind=wx.ITEM_NORMAL,text='Save selected as text',
788            help='Save selected sequential refinement results as a text file')
789        self.SequentialFile.Append(id=wxID_SAVESEQCSV, kind=wx.ITEM_NORMAL,text='Save all as CSV',
790            help='Save all sequential refinement results as a CSV spreadsheet file')
791        self.SequentialFile.Append(id=wxID_SAVESEQSELCSV, kind=wx.ITEM_NORMAL,text='Save selected as CSV',
792            help='Save selected sequential refinement results as a CSV spreadsheet file')
793        self.SequentialFile.Append(id=wxID_PLOTSEQSEL, kind=wx.ITEM_NORMAL,text='Plot selected',
794            help='Plot selected sequential refinement results')
795        self.SequentialFile.Append(id=wxID_AVESEQSEL, kind=wx.ITEM_NORMAL,text='Compute average',
796            help='Compute average for selected parameter')           
797        self.SequentialFile.Append(id=wxID_ORGSEQSEL, kind=wx.ITEM_NORMAL,text='Reorganize',
798            help='Reorganize variables where variables change')
799        self.SequentialPvars = wx.Menu(title='')
800        self.SequentialMenu.Append(menu=self.SequentialPvars, title='Pseudo Vars')
801        self.SequentialPvars.Append(
802            id=wxADDSEQVAR, kind=wx.ITEM_NORMAL,text='Add',
803            help='Add a new pseudo-variable')
804        self.SequentialPvars.Append(
805            id=wxDELSEQVAR, kind=wx.ITEM_NORMAL,text='Delete',
806            help='Delete an existing pseudo-variable')
807        self.SequentialPvars.Append(
808            id=wxEDITSEQVAR, kind=wx.ITEM_NORMAL,text='Edit',
809            help='Edit an existing pseudo-variable')
810
811        self.SequentialPfit = wx.Menu(title='')
812        self.SequentialMenu.Append(menu=self.SequentialPfit, title='Parametric Fit')
813        self.SequentialPfit.Append(
814            id=wxADDPARFIT, kind=wx.ITEM_NORMAL,text='Add equation',
815            help='Add a new equation to minimize')
816        self.SequentialPfit.Append(
817            id=wxCOPYPARFIT, kind=wx.ITEM_NORMAL,text='Copy equation',
818            help='Copy an equation to minimize - edit it next')
819        self.SequentialPfit.Append(
820            id=wxDELPARFIT, kind=wx.ITEM_NORMAL,text='Delete equation',
821            help='Delete an equation for parametric minimization')
822        self.SequentialPfit.Append(
823            id=wxEDITPARFIT, kind=wx.ITEM_NORMAL,text='Edit equation',
824            help='Edit an existing parametric minimization equation')
825        self.SequentialPfit.Append(
826            id=wxDOPARFIT, kind=wx.ITEM_NORMAL,text='Fit to equation(s)',
827            help='Perform a parametric minimization')
828        self.PostfillDataMenu()
829           
830        # PWDR & SASD
831        self.PWDRMenu = wx.MenuBar()
832        self.PrefillDataMenu(self.PWDRMenu,helpType='PWDR Analysis',helpLbl='Powder Fit Error Analysis')
833        self.ErrorAnal = wx.Menu(title='')
834        self.PWDRMenu.Append(menu=self.ErrorAnal,title='Commands')
835        self.ErrorAnal.Append(id=wxID_PWDANALYSIS,kind=wx.ITEM_NORMAL,text='Error Analysis',
836            help='Error analysis on powder pattern')
837        self.ErrorAnal.Append(id=wxID_PWDCOPY,kind=wx.ITEM_NORMAL,text='Copy params',
838            help='Copy of PWDR parameters')
839        self.ErrorAnal.Append(id=wxID_PLOTCTRLCOPY,kind=wx.ITEM_NORMAL,text='Copy plot controls',
840            help='Copy of PWDR plot controls')
841           
842        self.PostfillDataMenu()
843           
844        # HKLF
845        self.HKLFMenu = wx.MenuBar()
846        self.PrefillDataMenu(self.HKLFMenu,helpType='HKLF Analysis',helpLbl='HKLF Fit Error Analysis')
847        self.ErrorAnal = wx.Menu(title='')
848        self.HKLFMenu.Append(menu=self.ErrorAnal,title='Commands')
849        self.ErrorAnal.Append(id=wxID_PWDANALYSIS,kind=wx.ITEM_NORMAL,text='Error Analysis',
850            help='Error analysis on single crystal data')
851        self.ErrorAnal.Append(id=wxID_PWD3DHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot 3D HKLs',
852            help='Plot HKLs from single crystal data in 3D')
853        self.ErrorAnal.Append(id=wxID_3DALLHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot all 3D HKLs',
854            help='Plot HKLs from all single crystal data in 3D')
855        self.ErrorAnal.Append(id=wxID_PWDCOPY,kind=wx.ITEM_NORMAL,text='Copy params',
856            help='Copy of HKLF parameters')
857        self.PostfillDataMenu()
858           
859        # PDR / Limits
860        self.LimitMenu = wx.MenuBar()
861        self.PrefillDataMenu(self.LimitMenu,helpType='Limits')
862        self.LimitEdit = wx.Menu(title='')
863        self.LimitMenu.Append(menu=self.LimitEdit, title='Edit')
864        self.LimitEdit.Append(id=wxID_LIMITCOPY, kind=wx.ITEM_NORMAL,text='Copy',
865            help='Copy limits to other histograms')
866        self.LimitEdit.Append(id=wxID_ADDEXCLREGION, kind=wx.ITEM_NORMAL,text='Add exclude',
867            help='Add excluded region - select a point on plot; drag to adjust')           
868        self.PostfillDataMenu()
869           
870        # PDR / Background
871        self.BackMenu = wx.MenuBar()
872        self.PrefillDataMenu(self.BackMenu,helpType='Background')
873        self.BackEdit = wx.Menu(title='')
874        self.BackMenu.Append(menu=self.BackEdit, title='File')
875        self.BackEdit.Append(id=wxID_BACKCOPY, kind=wx.ITEM_NORMAL,text='Copy',
876            help='Copy background parameters to other histograms')
877        self.BackEdit.Append(id=wxID_BACKFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
878            help='Copy background refinement flags to other histograms')
879        self.BackEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks',
880            help='Move background peaks to Peak List')
881        self.BackFixed = wx.Menu(title='') # fixed background point menu
882        self.BackMenu.Append(menu=self.BackFixed, title='Fixed Points')
883        self.wxID_BackPts = {}
884        self.wxID_BackPts['Add'] = wx.NewId() # N.B. not using wxID_ global as for other menu items
885        self.BackFixed.Append(id=self.wxID_BackPts['Add'], kind=wx.ITEM_RADIO,text='Add',
886            help='Add fixed background points with mouse clicks')
887        self.wxID_BackPts['Move'] = wx.NewId() 
888        item = self.BackFixed.Append(id=self.wxID_BackPts['Move'], kind=wx.ITEM_RADIO,text='Move',
889            help='Move selected fixed background points with mouse drags')
890        item.Check(True)
891        self.wxID_BackPts['Del'] = wx.NewId()
892        self.BackFixed.Append(id=self.wxID_BackPts['Del'], kind=wx.ITEM_RADIO,text='Delete',
893            help='Delete fixed background points with mouse clicks')
894        self.wxID_BackPts['Clear'] = wx.NewId() 
895        self.BackFixed.Append(id=self.wxID_BackPts['Clear'], kind=wx.ITEM_NORMAL,text='Clear',
896            help='Clear fixed background points')
897        self.wxID_BackPts['Fit'] = wx.NewId() 
898        self.BackFixed.Append(id=self.wxID_BackPts['Fit'], kind=wx.ITEM_NORMAL,text='Fit background',
899            help='Fit background function to fixed background points')
900        self.PostfillDataMenu()
901           
902        # PDR / Instrument Parameters
903        self.InstMenu = wx.MenuBar()
904        self.PrefillDataMenu(self.InstMenu,helpType='Instrument Parameters')
905        self.InstEdit = wx.Menu(title='')
906        self.InstMenu.Append(menu=self.InstEdit, title='Operations')
907        self.InstEdit.Append(help='Calibrate from indexed peaks', 
908            id=wxID_INSTCALIB, kind=wx.ITEM_NORMAL,text='Calibrate')           
909        self.InstEdit.Append(help='Reset instrument profile parameters to default', 
910            id=wxID_INSTPRMRESET, kind=wx.ITEM_NORMAL,text='Reset profile')           
911        self.InstEdit.Append(help='Load instrument profile parameters from file', 
912            id=wxID_INSTLOAD, kind=wx.ITEM_NORMAL,text='Load profile...')           
913        self.InstEdit.Append(help='Save instrument profile parameters to file', 
914            id=wxID_INSTSAVE, kind=wx.ITEM_NORMAL,text='Save profile...')           
915        self.InstEdit.Append(help='Copy instrument profile parameters to other histograms', 
916            id=wxID_INSTCOPY, kind=wx.ITEM_NORMAL,text='Copy')
917        self.InstEdit.Append(id=wxID_INSTFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
918            help='Copy instrument parameter refinement flags to other histograms')
919#        self.InstEdit.Append(help='Change radiation type (Ka12 - synch)',
920#            id=wxID_CHANGEWAVETYPE, kind=wx.ITEM_NORMAL,text='Change radiation')
921        self.InstEdit.Append(id=wxID_INST1VAL, kind=wx.ITEM_NORMAL,text='Set one value',
922            help='Set one instrument parameter value across multiple histograms')
923
924        self.PostfillDataMenu()
925       
926        # PDR / Sample Parameters
927        self.SampleMenu = wx.MenuBar()
928        self.PrefillDataMenu(self.SampleMenu,helpType='Sample Parameters')
929        self.SampleEdit = wx.Menu(title='')
930        self.SampleMenu.Append(menu=self.SampleEdit, title='Command')
931        self.SetScale = self.SampleEdit.Append(id=wxID_SETSCALE, kind=wx.ITEM_NORMAL,text='Set scale',
932            help='Set scale by matching to another histogram')
933        self.SampleEdit.Append(id=wxID_SAMPLELOAD, kind=wx.ITEM_NORMAL,text='Load',
934            help='Load sample parameters from file')
935        self.SampleEdit.Append(id=wxID_SAMPLESAVE, kind=wx.ITEM_NORMAL,text='Save',
936            help='Save sample parameters to file')
937        self.SampleEdit.Append(id=wxID_SAMPLECOPY, kind=wx.ITEM_NORMAL,text='Copy',
938            help='Copy refinable and most other sample parameters to other histograms')
939        self.SampleEdit.Append(id=wxID_SAMPLECOPYSOME, kind=wx.ITEM_NORMAL,text='Copy selected...',
940            help='Copy selected sample parameters to other histograms')
941        self.SampleEdit.Append(id=wxID_SAMPLEFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
942            help='Copy sample parameter refinement flags to other histograms')
943        self.SampleEdit.Append(id=wxID_SAMPLE1VAL, kind=wx.ITEM_NORMAL,text='Set one value',
944            help='Set one sample parameter value across multiple histograms')
945        self.SampleEdit.Append(id=wxID_ALLSAMPLELOAD, kind=wx.ITEM_NORMAL,text='Load all',
946            help='Load sample parmameters over multiple histograms')
947
948        self.PostfillDataMenu()
949        self.SetScale.Enable(False)
950
951        # PDR / Peak List
952        self.PeakMenu = wx.MenuBar()
953        self.PrefillDataMenu(self.PeakMenu,helpType='Peak List')
954        self.PeakEdit = wx.Menu(title='')
955        self.PeakMenu.Append(menu=self.PeakEdit, title='Peak Fitting')
956        self.AutoSearch = self.PeakEdit.Append(help='Automatic peak search', 
957            id=wxID_AUTOSEARCH, kind=wx.ITEM_NORMAL,text='Auto search')
958        self.UnDo = self.PeakEdit.Append(help='Undo last least squares refinement', 
959            id=wxID_UNDO, kind=wx.ITEM_NORMAL,text='UnDo')
960        self.PeakFit = self.PeakEdit.Append(id=wxID_LSQPEAKFIT, kind=wx.ITEM_NORMAL,text='Peakfit', 
961            help='Peak fitting' )
962        self.PFOneCycle = self.PeakEdit.Append(id=wxID_LSQONECYCLE, kind=wx.ITEM_NORMAL,text='Peakfit one cycle', 
963            help='One cycle of Peak fitting' )
964        self.PeakEdit.Append(id=wxID_RESETSIGGAM, kind=wx.ITEM_NORMAL, 
965            text='Reset sig and gam',help='Reset sigma and gamma to global fit' )
966        self.PeakCopy = self.PeakEdit.Append(help='Copy peaks to other histograms', 
967            id=wxID_PEAKSCOPY, kind=wx.ITEM_NORMAL,text='Peak copy')
968        self.SeqPeakFit = self.PeakEdit.Append(id=wxID_SEQPEAKFIT, kind=wx.ITEM_NORMAL,text='Seq PeakFit', 
969            help='Sequential Peak fitting for all histograms' )
970        self.PeakEdit.Append(id=wxID_CLEARPEAKS, kind=wx.ITEM_NORMAL,text='Clear peaks', 
971            help='Clear the peak list' )
972        self.PostfillDataMenu()
973        self.UnDo.Enable(False)
974        self.PeakFit.Enable(False)
975        self.PFOneCycle.Enable(False)
976        self.AutoSearch.Enable(True)
977       
978        # PDR / Index Peak List
979        self.IndPeaksMenu = wx.MenuBar()
980        self.PrefillDataMenu(self.IndPeaksMenu,helpType='Index Peak List')
981        self.IndPeaksEdit = wx.Menu(title='')
982        self.IndPeaksMenu.Append(menu=self.IndPeaksEdit,title='Operations')
983        self.IndPeaksEdit.Append(help='Load/Reload index peaks from peak list',id=wxID_INDXRELOAD, 
984            kind=wx.ITEM_NORMAL,text='Load/Reload')
985        self.PostfillDataMenu()
986       
987        # PDR / Unit Cells List
988        self.IndexMenu = wx.MenuBar()
989        self.PrefillDataMenu(self.IndexMenu,helpType='Unit Cells List')
990        self.IndexEdit = wx.Menu(title='')
991        self.IndexMenu.Append(menu=self.IndexEdit, title='Cell Index/Refine')
992        self.IndexPeaks = self.IndexEdit.Append(help='', id=wxID_INDEXPEAKS, kind=wx.ITEM_NORMAL,
993            text='Index Cell')
994        self.CopyCell = self.IndexEdit.Append( id=wxID_COPYCELL, kind=wx.ITEM_NORMAL,text='Copy Cell', 
995            help='Copy selected unit cell from indexing to cell refinement fields')
996        self.RefineCell = self.IndexEdit.Append( id=wxID_REFINECELL, kind=wx.ITEM_NORMAL, 
997            text='Refine Cell',help='Refine unit cell parameters from indexed peaks')
998        self.MakeNewPhase = self.IndexEdit.Append( id=wxID_MAKENEWPHASE, kind=wx.ITEM_NORMAL,
999            text='Make new phase',help='Make new phase from selected unit cell')
1000        self.ExportCells = self.IndexEdit.Append( id=wxID_EXPORTCELLS, kind=wx.ITEM_NORMAL,
1001            text='Export cell list',help='Export cell list to csv file')
1002        self.PostfillDataMenu()
1003        self.IndexPeaks.Enable(False)
1004        self.CopyCell.Enable(False)
1005        self.RefineCell.Enable(False)
1006        self.MakeNewPhase.Enable(False)
1007       
1008        # PDR / Reflection Lists
1009        self.ReflMenu = wx.MenuBar()
1010        self.PrefillDataMenu(self.ReflMenu,helpType='Reflection List')
1011        self.ReflEdit = wx.Menu(title='')
1012        self.ReflMenu.Append(menu=self.ReflEdit, title='Reflection List')
1013        self.SelectPhase = self.ReflEdit.Append(help='Select phase for reflection list',id=wxID_SELECTPHASE, 
1014            kind=wx.ITEM_NORMAL,text='Select phase')
1015        self.ReflEdit.Append(id=wxID_PWDHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot HKLs',
1016            help='Plot HKLs from powder pattern')
1017        self.ReflEdit.Append(id=wxID_PWD3DHKLPLOT,kind=wx.ITEM_NORMAL,text='Plot 3D HKLs',
1018            help='Plot HKLs from powder pattern in 3D')
1019        self.PostfillDataMenu()
1020       
1021        # SASD / Instrument Parameters
1022        self.SASDInstMenu = wx.MenuBar()
1023        self.PrefillDataMenu(self.SASDInstMenu,helpType='Instrument Parameters')
1024        self.SASDInstEdit = wx.Menu(title='')
1025        self.SASDInstMenu.Append(menu=self.SASDInstEdit, title='Operations')
1026        self.InstEdit.Append(help='Reset instrument profile parameters to default', 
1027            id=wxID_INSTPRMRESET, kind=wx.ITEM_NORMAL,text='Reset profile')
1028        self.SASDInstEdit.Append(help='Copy instrument profile parameters to other histograms', 
1029            id=wxID_INSTCOPY, kind=wx.ITEM_NORMAL,text='Copy')
1030        self.PostfillDataMenu()
1031       
1032        #SASD & REFL/ Substance editor
1033        self.SubstanceMenu = wx.MenuBar()
1034        self.PrefillDataMenu(self.SubstanceMenu,helpType='Substances')
1035        self.SubstanceEdit = wx.Menu(title='')
1036        self.SubstanceMenu.Append(menu=self.SubstanceEdit, title='Edit')
1037        self.SubstanceEdit.Append(id=wxID_LOADSUBSTANCE, kind=wx.ITEM_NORMAL,text='Load substance',
1038            help='Load substance from file')
1039        self.SubstanceEdit.Append(id=wxID_ADDSUBSTANCE, kind=wx.ITEM_NORMAL,text='Add substance',
1040            help='Add new substance to list')
1041        self.SubstanceEdit.Append(id=wxID_COPYSUBSTANCE, kind=wx.ITEM_NORMAL,text='Copy substances',
1042            help='Copy substances')
1043        self.SubstanceEdit.Append(id=wxID_DELETESUBSTANCE, kind=wx.ITEM_NORMAL,text='Delete substance',
1044            help='Delete substance from list')           
1045        self.SubstanceEdit.Append(id=wxID_ELEMENTADD, kind=wx.ITEM_NORMAL,text='Add elements',
1046            help='Add elements to substance')
1047        self.SubstanceEdit.Append(id=wxID_ELEMENTDELETE, kind=wx.ITEM_NORMAL,text='Delete elements',
1048            help='Delete elements from substance')
1049        self.PostfillDataMenu()
1050       
1051        # SASD/ Models
1052        self.ModelMenu = wx.MenuBar()
1053        self.PrefillDataMenu(self.ModelMenu,helpType='Models')
1054        self.ModelEdit = wx.Menu(title='')
1055        self.ModelMenu.Append(menu=self.ModelEdit, title='Models')
1056        self.ModelEdit.Append(id=wxID_MODELADD,kind=wx.ITEM_NORMAL,text='Add',
1057            help='Add new term to model')
1058        self.ModelEdit.Append(id=wxID_MODELFIT, kind=wx.ITEM_NORMAL,text='Fit',
1059            help='Fit model parameters to data')
1060        self.SasdUndo = self.ModelEdit.Append(id=wxID_MODELUNDO, kind=wx.ITEM_NORMAL,text='Undo',
1061            help='Undo model fit')
1062        self.SasdUndo.Enable(False)           
1063        self.ModelEdit.Append(id=wxID_MODELFITALL, kind=wx.ITEM_NORMAL,text='Sequential fit',
1064            help='Sequential fit of model parameters to all SASD data')
1065        self.ModelEdit.Append(id=wxID_MODELCOPY, kind=wx.ITEM_NORMAL,text='Copy',
1066            help='Copy model parameters to other histograms')
1067        self.ModelEdit.Append(id=wxID_MODELCOPYFLAGS, kind=wx.ITEM_NORMAL,text='Copy flags',
1068            help='Copy model refinement flags to other histograms')
1069        self.PostfillDataMenu()
1070       
1071        # IMG / Image Controls
1072        self.ImageMenu = wx.MenuBar()
1073        self.PrefillDataMenu(self.ImageMenu,helpType='Image Controls')
1074        self.ImageEdit = wx.Menu(title='')
1075        self.ImageMenu.Append(menu=self.ImageEdit, title='Operations')
1076        self.ImageEdit.Append(help='Calibrate detector by fitting to calibrant lines', 
1077            id=wxID_IMCALIBRATE, kind=wx.ITEM_NORMAL,text='Calibrate')
1078        self.ImageEdit.Append(help='Recalibrate detector by fitting to calibrant lines', 
1079            id=wxID_IMRECALIBRATE, kind=wx.ITEM_NORMAL,text='Recalibrate')
1080        self.ImageEdit.Append(help='Clear calibration data points and rings',id=wxID_IMCLEARCALIB, 
1081            kind=wx.ITEM_NORMAL,text='Clear calibration')
1082        self.ImageEdit.Append(help='Integrate selected image',id=wxID_IMINTEGRATE, 
1083            kind=wx.ITEM_NORMAL,text='Integrate')
1084        self.ImageEdit.Append(help='Integrate all images selected from list',id=wxID_INTEGRATEALL,
1085            kind=wx.ITEM_NORMAL,text='Integrate all')
1086        self.ImageEdit.Append(help='Copy image controls to other images', 
1087            id=wxID_IMCOPYCONTROLS, kind=wx.ITEM_NORMAL,text='Copy Controls')
1088        self.ImageEdit.Append(help='Save image controls to file', 
1089            id=wxID_IMSAVECONTROLS, kind=wx.ITEM_NORMAL,text='Save Controls')
1090        self.ImageEdit.Append(help='Load image controls from file', 
1091            id=wxID_IMLOADCONTROLS, kind=wx.ITEM_NORMAL,text='Load Controls')
1092        self.PostfillDataMenu()
1093           
1094        # IMG / Masks
1095        self.MaskMenu = wx.MenuBar()
1096        self.PrefillDataMenu(self.MaskMenu,helpType='Image Masks')
1097        self.MaskEdit = wx.Menu(title='')
1098        self.MaskMenu.Append(menu=self.MaskEdit, title='Operations')
1099        submenu = wx.Menu()
1100        self.MaskEdit.AppendMenu(
1101            wx.ID_ANY,'Create new', submenu,
1102            help=''
1103            )
1104        self.MaskEdit.Append(help='Copy mask to other images', 
1105            id=wxID_MASKCOPY, kind=wx.ITEM_NORMAL,text='Copy mask')
1106        self.MaskEdit.Append(help='Save mask to file', 
1107            id=wxID_MASKSAVE, kind=wx.ITEM_NORMAL,text='Save mask')
1108        self.MaskEdit.Append(help='Load mask from file', 
1109            id=wxID_MASKLOAD, kind=wx.ITEM_NORMAL,text='Load mask')
1110        self.MaskEdit.Append(help='Load mask from file; ignore threshold', 
1111            id=wxID_MASKLOADNOT, kind=wx.ITEM_NORMAL,text='Load mask w/o threshold')
1112        submenu.Append(help='Create an arc mask with mouse input', 
1113            id=wxID_NEWMASKARC, kind=wx.ITEM_NORMAL,text='Arc mask')
1114        submenu.Append(help='Create a frame mask with mouse input', 
1115            id=wxID_NEWMASKFRAME, kind=wx.ITEM_NORMAL,text='Frame mask')
1116        submenu.Append(help='Create a polygon mask with mouse input', 
1117            id=wxID_NEWMASKPOLY, kind=wx.ITEM_NORMAL,text='Polygon mask')
1118        submenu.Append(help='Create a ring mask with mouse input', 
1119            id=wxID_NEWMASKRING, kind=wx.ITEM_NORMAL,text='Ring mask')
1120        submenu.Append(help='Create a spot mask with mouse input', 
1121            id=wxID_NEWMASKSPOT, kind=wx.ITEM_NORMAL,text='Spot mask')
1122        self.PostfillDataMenu()
1123           
1124        # IMG / Stress/Strain
1125        self.StrStaMenu = wx.MenuBar()
1126        self.PrefillDataMenu(self.StrStaMenu,helpType='Stress/Strain')
1127        self.StrStaEdit = wx.Menu(title='')
1128        self.StrStaMenu.Append(menu=self.StrStaEdit, title='Operations')
1129        self.StrStaEdit.Append(help='Append d-zero for one ring', 
1130            id=wxID_APPENDDZERO, kind=wx.ITEM_NORMAL,text='Append d-zero')
1131        self.StrStaEdit.Append(help='Fit stress/strain data', 
1132            id=wxID_STRSTAFIT, kind=wx.ITEM_NORMAL,text='Fit stress/strain')
1133        self.StrStaEdit.Append(help='Update d-zero from ave d-zero',
1134            id=wxID_UPDATEDZERO, kind=wx.ITEM_NORMAL,text='Update d-zero')       
1135        self.StrStaEdit.Append(help='Fit stress/strain data for all images', 
1136            id=wxID_STRSTAALLFIT, kind=wx.ITEM_NORMAL,text='All image fit')
1137        self.StrStaEdit.Append(help='Copy stress/strain data to other images', 
1138            id=wxID_STRSTACOPY, kind=wx.ITEM_NORMAL,text='Copy stress/strain')
1139        self.StrStaEdit.Append(help='Save stress/strain data to file', 
1140            id=wxID_STRSTASAVE, kind=wx.ITEM_NORMAL,text='Save stress/strain')
1141        self.StrStaEdit.Append(help='Load stress/strain data from file', 
1142            id=wxID_STRSTALOAD, kind=wx.ITEM_NORMAL,text='Load stress/strain')
1143        self.StrStaEdit.Append(help='Load sample data from file', 
1144            id=wxID_STRSTSAMPLE, kind=wx.ITEM_NORMAL,text='Load sample data')
1145        self.PostfillDataMenu()
1146           
1147        # PDF / PDF Controls
1148        self.PDFMenu = wx.MenuBar()
1149        self.PrefillDataMenu(self.PDFMenu,helpType='PDF Controls')
1150        self.PDFEdit = wx.Menu(title='')
1151        self.PDFMenu.Append(menu=self.PDFEdit, title='PDF Controls')
1152        self.PDFEdit.Append(help='Add element to sample composition',id=wxID_PDFADDELEMENT, kind=wx.ITEM_NORMAL,
1153            text='Add element')
1154        self.PDFEdit.Append(help='Delete element from sample composition',id=wxID_PDFDELELEMENT, kind=wx.ITEM_NORMAL,
1155            text='Delete element')
1156        self.PDFEdit.Append(help='Copy PDF controls', id=wxID_PDFCOPYCONTROLS, kind=wx.ITEM_NORMAL,
1157            text='Copy controls')
1158        self.PDFEdit.Append(help='Load PDF controls from file',id=wxID_PDFLOADCONTROLS, kind=wx.ITEM_NORMAL,
1159            text='Load Controls')
1160        self.PDFEdit.Append(help='Save PDF controls to file', id=wxID_PDFSAVECONTROLS, kind=wx.ITEM_NORMAL,
1161            text='Save controls')
1162        self.PDFEdit.Append(help='Compute PDF', id=wxID_PDFCOMPUTE, kind=wx.ITEM_NORMAL,
1163            text='Compute PDF')
1164        self.PDFEdit.Append(help='Compute all PDFs', id=wxID_PDFCOMPUTEALL, kind=wx.ITEM_NORMAL,
1165            text='Compute all PDFs')
1166        self.PostfillDataMenu()
1167       
1168        # Phase / General tab
1169        self.DataGeneral = wx.MenuBar()
1170        self.PrefillDataMenu(self.DataGeneral,helpType='General', helpLbl='Phase/General')
1171        self.DataGeneral.Append(menu=wx.Menu(title=''),title='Select tab')
1172        self.GeneralCalc = wx.Menu(title='')
1173        self.DataGeneral.Append(menu=self.GeneralCalc,title='Compute')
1174        self.GeneralCalc.Append(help='Compute Fourier map',id=wxID_FOURCALC, kind=wx.ITEM_NORMAL,
1175            text='Fourier map')
1176        self.GeneralCalc.Append(help='Search Fourier map',id=wxID_FOURSEARCH, kind=wx.ITEM_NORMAL,
1177            text='Search map')
1178        self.GeneralCalc.Append(help='Run charge flipping',id=wxID_CHARGEFLIP, kind=wx.ITEM_NORMAL,
1179            text='Charge flipping')
1180        self.GeneralCalc.Append(help='Run 4D charge flipping',id=wxID_4DCHARGEFLIP, kind=wx.ITEM_NORMAL,
1181            text='4D Charge flipping')
1182        self.GeneralCalc.Enable(wxID_4DCHARGEFLIP,False)   
1183        self.GeneralCalc.Append(help='Clear map',id=wxID_FOURCLEAR, kind=wx.ITEM_NORMAL,
1184            text='Clear map')
1185        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing',id=wxID_SINGLEMCSA, kind=wx.ITEM_NORMAL,
1186            text='MC/SA')
1187        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing on multiprocessors',id=wxID_MULTIMCSA, kind=wx.ITEM_NORMAL,
1188            text='Multi MC/SA')            #currently not useful
1189        self.PostfillDataMenu()
1190       
1191        # Phase / Data tab
1192        self.DataMenu = wx.MenuBar()
1193        self.PrefillDataMenu(self.DataMenu,helpType='Data', helpLbl='Phase/Data')
1194        self.DataMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1195        self.DataEdit = wx.Menu(title='')
1196        self.DataMenu.Append(menu=self.DataEdit, title='Edit')
1197        self.DataEdit.Append(id=wxID_DATACOPY, kind=wx.ITEM_NORMAL,text='Copy data',
1198            help='Copy phase data to other histograms')
1199        self.DataEdit.Append(id=wxID_DATACOPYFLAGS, kind=wx.ITEM_NORMAL,text='Copy flags',
1200            help='Copy phase data flags to other histograms')
1201        self.DataEdit.Append(id=wxID_DATASELCOPY, kind=wx.ITEM_NORMAL,text='Copy selected data',
1202            help='Copy selected phase data to other histograms')
1203        self.DataEdit.Append(id=wxID_PWDRADD, kind=wx.ITEM_NORMAL,text='Add powder histograms',
1204            help='Select new powder histograms to be used for this phase')
1205        self.DataEdit.Append(id=wxID_HKLFADD, kind=wx.ITEM_NORMAL,text='Add single crystal histograms',
1206            help='Select new single crystal histograms to be used for this phase')
1207        self.DataEdit.Append(id=wxID_DATADELETE, kind=wx.ITEM_NORMAL,text='Remove histograms',
1208            help='Remove histograms from use for this phase')
1209        self.PostfillDataMenu()
1210           
1211        # Phase / Atoms tab
1212        self.AtomsMenu = wx.MenuBar()
1213        self.PrefillDataMenu(self.AtomsMenu,helpType='Atoms')
1214        self.AtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1215        self.AtomEdit = wx.Menu(title='')
1216        self.AtomCompute = wx.Menu(title='')
1217        self.AtomsMenu.Append(menu=self.AtomEdit, title='Edit')
1218        self.AtomsMenu.Append(menu=self.AtomCompute, title='Compute')
1219        self.AtomEdit.Append(id=wxID_ATOMSEDITADD, kind=wx.ITEM_NORMAL,text='Append atom',
1220            help='Appended as an H atom')
1221        self.AtomEdit.Append(id=wxID_ATOMSVIEWADD, kind=wx.ITEM_NORMAL,text='Append view point',
1222            help='Appended as an H atom')
1223        self.AtomEdit.Append(id=wxID_ATOMSEDITINSERT, kind=wx.ITEM_NORMAL,text='Insert atom',
1224            help='Select atom row to insert before; inserted as an H atom')
1225        self.AtomEdit.Append(id=wxID_ATOMVIEWINSERT, kind=wx.ITEM_NORMAL,text='Insert view point',
1226            help='Select atom row to insert before; inserted as an H atom')
1227        self.AtomEdit.Append(id=wxID_ADDHATOM, kind=wx.ITEM_NORMAL,text='Insert H atoms',
1228            help='Insert H atoms in standard positions bonded to selected atoms')
1229        self.AtomEdit.Append(id=wxID_ATOMMOVE, kind=wx.ITEM_NORMAL,text='Move atom to view point',
1230            help='Select single atom to move')
1231        self.AtomEdit.Append(id=wxID_ATOMSEDITDELETE, kind=wx.ITEM_NORMAL,text='Delete atom',
1232            help='Select atoms to delete first')
1233        self.AtomEdit.Append(id=wxID_ATOMSREFINE, kind=wx.ITEM_NORMAL,text='Set atom refinement flags',
1234            help='Select atoms to refine first')
1235        self.AtomEdit.Append(id=wxID_ATOMSMODIFY, kind=wx.ITEM_NORMAL,text='Modify atom parameters',
1236            help='Select atoms to modify first')
1237        self.AtomEdit.Append(id=wxID_ATOMSTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
1238            help='Select atoms to transform first')
1239        self.AtomEdit.Append(id=wxID_MAKEMOLECULE, kind=wx.ITEM_NORMAL,text='Assemble molecule',
1240            help='Assemble molecule from scatterd atom positions')
1241        self.AtomEdit.Append(id=wxID_RELOADDRAWATOMS, kind=wx.ITEM_NORMAL,text='Reload draw atoms',
1242            help='Reload atom drawing list')
1243        submenu = wx.Menu()
1244        self.AtomEdit.AppendMenu(wx.ID_ANY, 'Reimport atoms', submenu, 
1245            help='Reimport atoms from file; sequence must match')
1246        # setup a cascade menu for the formats that have been defined
1247        self.ReImportMenuId = {}  # points to readers for each menu entry
1248        for reader in self.G2frame.ImportPhaseReaderlist:
1249            item = submenu.Append(
1250                wx.ID_ANY,help=reader.longFormatName,
1251                kind=wx.ITEM_NORMAL,text='reimport coordinates from '+reader.formatName+' file')
1252            self.ReImportMenuId[item.GetId()] = reader
1253        item = submenu.Append(
1254            wx.ID_ANY,
1255            help='Reimport coordinates, try to determine format from file',
1256            kind=wx.ITEM_NORMAL,
1257            text='guess format from file')
1258        self.ReImportMenuId[item.GetId()] = None # try all readers
1259
1260        self.AtomCompute.Append(id=wxID_ATOMSDISAGL, kind=wx.ITEM_NORMAL,text='Show Distances && Angles',
1261            help='Compute distances & angles for selected atoms')
1262        self.AtomCompute.Append(id=wxID_ATOMSPDISAGL, kind=wx.ITEM_NORMAL,text='Save Distances && Angles',
1263            help='Compute distances & angles for selected atoms')
1264        self.AtomCompute.ISOcalc = self.AtomCompute.Append(
1265            id=wxID_ISODISP, kind=wx.ITEM_NORMAL,
1266            text='Compute ISODISTORT mode values',
1267            help='Compute values of ISODISTORT modes from atom parameters')
1268        self.PostfillDataMenu()
1269       
1270        # Phase / Imcommensurate "waves" tab
1271        self.WavesData = wx.MenuBar()
1272        self.PrefillDataMenu(self.WavesData,helpType='Wave Data', helpLbl='Imcommensurate wave data')
1273        self.WavesData.Append(menu=wx.Menu(title=''),title='Select tab')
1274        self.WavesDataCompute = wx.Menu(title='')
1275        self.WavesData.Append(menu=self.WavesDataCompute,title='Compute')
1276        self.WavesDataCompute.Append(id=wxID_4DMAPCOMPUTE, kind=wx.ITEM_NORMAL,text='Compute 4D map',
1277            help='Compute 4-dimensional map')
1278        self.PostfillDataMenu()
1279                 
1280        # Phase / Draw Options tab
1281        self.DataDrawOptions = wx.MenuBar()
1282        self.PrefillDataMenu(self.DataDrawOptions,helpType='Draw Options', helpLbl='Phase/Draw Options')
1283        self.DataDrawOptions.Append(menu=wx.Menu(title=''),title='Select tab')
1284        self.PostfillDataMenu()
1285       
1286        # Phase / Draw Atoms tab
1287        self.DrawAtomsMenu = wx.MenuBar()
1288        self.PrefillDataMenu(self.DrawAtomsMenu,helpType='Draw Atoms')
1289        self.DrawAtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1290        self.DrawAtomEdit = wx.Menu(title='')
1291        self.DrawAtomCompute = wx.Menu(title='')
1292        self.DrawAtomRestraint = wx.Menu(title='')
1293        self.DrawAtomRigidBody = wx.Menu(title='')
1294        self.DrawAtomsMenu.Append(menu=self.DrawAtomEdit, title='Edit')
1295        self.DrawAtomsMenu.Append(menu=self.DrawAtomCompute,title='Compute')
1296        self.DrawAtomsMenu.Append(menu=self.DrawAtomRestraint, title='Restraints')
1297        self.DrawAtomsMenu.Append(menu=self.DrawAtomRigidBody, title='Rigid body')
1298        self.DrawAtomEdit.Append(id=wxID_DRAWATOMSTYLE, kind=wx.ITEM_NORMAL,text='Atom style',
1299            help='Select atoms first')
1300        self.DrawAtomEdit.Append(id=wxID_DRAWATOMLABEL, kind=wx.ITEM_NORMAL,text='Atom label',
1301            help='Select atoms first')
1302        self.DrawAtomEdit.Append(id=wxID_DRAWATOMCOLOR, kind=wx.ITEM_NORMAL,text='Atom color',
1303            help='Select atoms first')
1304        self.DrawAtomEdit.Append(id=wxID_DRAWATOMRESETCOLOR, kind=wx.ITEM_NORMAL,text='Reset atom colors',
1305            help='Resets all atom colors to defaults')
1306        self.DrawAtomEdit.Append(id=wxID_DRAWVIEWPOINT, kind=wx.ITEM_NORMAL,text='View point',
1307            help='View point is 1st atom selected')
1308        self.DrawAtomEdit.Append(id=wxID_DRAWADDEQUIV, kind=wx.ITEM_NORMAL,text='Add atoms',
1309            help='Add symmetry & cell equivalents to drawing set from selected atoms')
1310        self.DrawAtomEdit.Append(id=wxID_DRAWTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform draw atoms',
1311            help='Transform selected atoms by symmetry & cell translations')
1312        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCOORD, kind=wx.ITEM_NORMAL,text='Fill CN-sphere',
1313            help='Fill coordination sphere for selected atoms')           
1314        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCELL, kind=wx.ITEM_NORMAL,text='Fill unit cell',
1315            help='Fill unit cell with selected atoms')
1316        self.DrawAtomEdit.Append(id=wxID_DRAWDELETE, kind=wx.ITEM_NORMAL,text='Delete atoms',
1317            help='Delete atoms from drawing set')
1318        self.DrawAtomCompute.Append(id=wxID_DRAWDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
1319            help='Compute distance of selected atoms from view point')   
1320        self.DrawAtomCompute.Append(id=wxID_DRAWDISAGLTOR, kind=wx.ITEM_NORMAL,text='Dist. Ang. Tors.',
1321            help='Compute distance, angle or torsion for 2-4 selected atoms')   
1322        self.DrawAtomCompute.Append(id=wxID_DRAWPLANE, kind=wx.ITEM_NORMAL,text='Best plane',
1323            help='Compute best plane for 4+ selected atoms')   
1324        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRBOND, kind=wx.ITEM_NORMAL,text='Add bond restraint',
1325            help='Add bond restraint for selected atoms (2)')
1326        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRANGLE, kind=wx.ITEM_NORMAL,text='Add angle restraint',
1327            help='Add angle restraint for selected atoms (3: one end 1st)')
1328        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRPLANE, kind=wx.ITEM_NORMAL,text='Add plane restraint',
1329            help='Add plane restraint for selected atoms (4+)')
1330        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRCHIRAL, kind=wx.ITEM_NORMAL,text='Add chiral restraint',
1331            help='Add chiral restraint for selected atoms (4: center atom 1st)')
1332        self.DrawAtomRigidBody.Append(id=wxID_DRAWDEFINERB, kind=wx.ITEM_NORMAL,text='Define rigid body',
1333            help='Define rigid body with selected atoms')
1334        self.PostfillDataMenu()
1335
1336        # Phase / MCSA tab
1337        self.MCSAMenu = wx.MenuBar()
1338        self.PrefillDataMenu(self.MCSAMenu,helpType='MC/SA')
1339        self.MCSAMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1340        self.MCSAEdit = wx.Menu(title='')
1341        self.MCSAMenu.Append(menu=self.MCSAEdit, title='MC/SA')
1342        self.MCSAEdit.Append(id=wxID_ADDMCSAATOM, kind=wx.ITEM_NORMAL,text='Add atom', 
1343            help='Add single atom to MC/SA model')
1344        self.MCSAEdit.Append(id=wxID_ADDMCSARB, kind=wx.ITEM_NORMAL,text='Add rigid body', 
1345            help='Add rigid body to MC/SA model' )
1346        self.MCSAEdit.Append(id=wxID_CLEARMCSARB, kind=wx.ITEM_NORMAL,text='Clear rigid bodies', 
1347            help='Clear all atoms & rigid bodies from MC/SA model' )
1348        self.MCSAEdit.Append(id=wxID_MOVEMCSA, kind=wx.ITEM_NORMAL,text='Move MC/SA solution', 
1349            help='Move MC/SA solution to atom list' )
1350        self.MCSAEdit.Append(id=wxID_MCSACLEARRESULTS, kind=wx.ITEM_NORMAL,text='Clear results', 
1351            help='Clear table of MC/SA results' )
1352        self.PostfillDataMenu()
1353           
1354        # Phase / Texture tab
1355        self.TextureMenu = wx.MenuBar()
1356        self.PrefillDataMenu(self.TextureMenu,helpType='Texture')
1357        self.TextureMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1358        self.TextureEdit = wx.Menu(title='')
1359        self.TextureMenu.Append(menu=self.TextureEdit, title='Texture')
1360        self.TextureEdit.Append(id=wxID_REFINETEXTURE, kind=wx.ITEM_NORMAL,text='Refine texture', 
1361            help='Refine the texture coefficients from sequential results')
1362#        self.TextureEdit.Append(id=wxID_CLEARTEXTURE, kind=wx.ITEM_NORMAL,text='Clear texture',
1363#            help='Clear the texture coefficients' )
1364        self.PostfillDataMenu()
1365           
1366        # Phase / Pawley tab
1367        self.PawleyMenu = wx.MenuBar()
1368        self.PrefillDataMenu(self.PawleyMenu,helpType='Pawley')
1369        self.PawleyMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1370        self.PawleyEdit = wx.Menu(title='')
1371        self.PawleyMenu.Append(menu=self.PawleyEdit,title='Operations')
1372        self.PawleyEdit.Append(id=wxID_PAWLEYLOAD, kind=wx.ITEM_NORMAL,text='Pawley create',
1373            help='Initialize Pawley reflection list')
1374        self.PawleyEdit.Append(id=wxID_PAWLEYESTIMATE, kind=wx.ITEM_NORMAL,text='Pawley estimate',
1375            help='Estimate initial Pawley intensities')
1376        self.PawleyEdit.Append(id=wxID_PAWLEYUPDATE, kind=wx.ITEM_NORMAL,text='Pawley update',
1377            help='Update negative Pawley intensities with -0.5*Fobs and turn off refinemnt')
1378        self.PostfillDataMenu()
1379           
1380        # Phase / Map peaks tab
1381        self.MapPeaksMenu = wx.MenuBar()
1382        self.PrefillDataMenu(self.MapPeaksMenu,helpType='Map peaks')
1383        self.MapPeaksMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1384        self.MapPeaksEdit = wx.Menu(title='')
1385        self.MapPeaksMenu.Append(menu=self.MapPeaksEdit, title='Map peaks')
1386        self.MapPeaksEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks', 
1387            help='Move selected peaks to atom list')
1388        self.MapPeaksEdit.Append(id=wxID_PEAKSVIEWPT, kind=wx.ITEM_NORMAL,text='View point',
1389            help='View point is 1st peak selected')
1390        self.MapPeaksEdit.Append(id=wxID_PEAKSDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
1391            help='Compute distance of selected peaks from view point')   
1392        self.MapPeaksEdit.Append(id=wxID_SHOWBONDS, kind=wx.ITEM_NORMAL,text='Hide bonds',
1393            help='Hide or show bonds between peak positions')   
1394        self.MapPeaksEdit.Append(id=wxID_PEAKSDA, kind=wx.ITEM_NORMAL,text='Calc dist/ang', 
1395            help='Calculate distance or angle for selection')
1396        self.MapPeaksEdit.Append(id=wxID_FINDEQVPEAKS, kind=wx.ITEM_NORMAL,text='Equivalent peaks', 
1397            help='Find equivalent peaks')
1398        self.MapPeaksEdit.Append(id=wxID_PEAKSUNIQUE, kind=wx.ITEM_NORMAL,text='Unique peaks', 
1399            help='Select unique set')
1400        self.MapPeaksEdit.Append(id=wxID_PEAKSDELETE, kind=wx.ITEM_NORMAL,text='Delete peaks', 
1401            help='Delete selected peaks')
1402        self.MapPeaksEdit.Append(id=wxID_PEAKSCLEAR, kind=wx.ITEM_NORMAL,text='Clear peaks', 
1403            help='Clear the map peak list')
1404        self.PostfillDataMenu()
1405
1406        # Phase / Rigid bodies tab
1407        self.RigidBodiesMenu = wx.MenuBar()
1408        self.PrefillDataMenu(self.RigidBodiesMenu,helpType='Rigid bodies')
1409        self.RigidBodiesMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1410        self.RigidBodiesEdit = wx.Menu(title='')
1411        self.RigidBodiesMenu.Append(menu=self.RigidBodiesEdit, title='Edit')
1412        self.RigidBodiesEdit.Append(id=wxID_ASSIGNATMS2RB, kind=wx.ITEM_NORMAL,text='Assign atoms to rigid body',
1413            help='Select & position rigid body in structure of existing atoms')
1414        self.RigidBodiesEdit.Append(id=wxID_AUTOFINDRESRB, kind=wx.ITEM_NORMAL,text='Auto find residues',
1415            help='Auto find of residue RBs in macromolecule')
1416        self.RigidBodiesEdit.Append(id=wxID_COPYRBPARMS, kind=wx.ITEM_NORMAL,text='Copy rigid body parms',
1417            help='Copy rigid body location & TLS parameters')
1418        self.RigidBodiesEdit.Append(id=wxID_GLOBALTHERM, kind=wx.ITEM_NORMAL,text='Global thermal motion',
1419            help='Global setting of residue thermal motion models')
1420        self.RigidBodiesEdit.Append(id=wxID_GLOBALRESREFINE, kind=wx.ITEM_NORMAL,text='Global residue refine',
1421            help='Global setting of residue RB refinement flags')
1422        self.RigidBodiesEdit.Append(id=wxID_RBREMOVEALL, kind=wx.ITEM_NORMAL,text='Remove all rigid bodies',
1423            help='Remove all rigid body assignment for atoms')
1424        self.PostfillDataMenu()
1425    # end of GSAS-II menu definitions
1426       
1427    def _init_ctrls(self, parent,name=None,size=None,pos=None):
1428        wx.Frame.__init__(
1429            self,parent=parent,
1430            #style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX | wx.FRAME_FLOAT_ON_PARENT ,
1431            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX,
1432            size=size,pos=pos,title='GSAS-II data display')
1433        self._init_menus()
1434        if name:
1435            self.SetLabel(name)
1436        self.Show()
1437       
1438    def __init__(self,parent,frame,data=None,name=None, size=None,pos=None):
1439        self.G2frame = frame
1440        self._init_ctrls(parent,name,size,pos)
1441        self.data = data
1442        clientSize = wx.ClientDisplayRect()
1443        Size = self.GetSize()
1444        xPos = clientSize[2]-Size[0]
1445        self.SetPosition(wx.Point(xPos,clientSize[1]+250))
1446        self.AtomGrid = []
1447        self.selectedRow = 0
1448       
1449    def setSizePosLeft(self,Width):
1450        clientSize = wx.ClientDisplayRect()
1451        Width[1] = min(Width[1],clientSize[2]-300)
1452        Width[0] = max(Width[0],300)
1453        self.SetSize(Width)
1454#        self.SetPosition(wx.Point(clientSize[2]-Width[0],clientSize[1]+250))
1455       
1456    def Clear(self):
1457        self.ClearBackground()
1458        self.DestroyChildren()
1459                   
1460
1461################################################################################
1462#####  Notebook Tree Item editor
1463################################################################################                 
1464def UpdateNotebook(G2frame,data):
1465    '''Called when the data tree notebook entry is selected. Allows for
1466    editing of the text in that tree entry
1467    '''
1468    def OnNoteBook(event):
1469        data = G2frame.dataDisplay.GetValue().split('\n')
1470        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Notebook'),data)
1471        if 'nt' not in os.name:
1472            G2frame.dataDisplay.AppendText('\n')
1473                   
1474    if G2frame.dataDisplay:
1475        G2frame.dataDisplay.Destroy()
1476    G2frame.dataFrame.SetLabel('Notebook')
1477    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
1478        style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
1479    G2frame.dataDisplay.Bind(wx.EVT_TEXT_ENTER,OnNoteBook)
1480    G2frame.dataDisplay.Bind(wx.EVT_KILL_FOCUS,OnNoteBook)
1481    for line in data:
1482        G2frame.dataDisplay.AppendText(line+"\n")
1483    G2frame.dataDisplay.AppendText('Notebook entry @ '+time.ctime()+"\n")
1484    G2frame.dataFrame.setSizePosLeft([400,250])
1485           
1486################################################################################
1487#####  Controls Tree Item editor
1488################################################################################           
1489def UpdateControls(G2frame,data):
1490    '''Edit overall GSAS-II controls in main Controls data tree entry
1491    '''
1492    #patch
1493    if 'deriv type' not in data:
1494        data = {}
1495        data['deriv type'] = 'analytic Hessian'
1496        data['min dM/M'] = 0.0001
1497        data['shift factor'] = 1.
1498        data['max cyc'] = 3       
1499        data['F**2'] = False
1500    if 'shift factor' not in data:
1501        data['shift factor'] = 1.
1502    if 'max cyc' not in data:
1503        data['max cyc'] = 3
1504    if 'F**2' not in data:
1505        data['F**2'] = False
1506    if 'Author' not in data:
1507        data['Author'] = 'no name'
1508    if 'FreePrm1' not in data:
1509        data['FreePrm1'] = 'Sample humidity (%)'
1510    if 'FreePrm2' not in data:
1511        data['FreePrm2'] = 'Sample voltage (V)'
1512    if 'FreePrm3' not in data:
1513        data['FreePrm3'] = 'Applied load (MN)'
1514    if 'Copy2Next' not in data:
1515        data['Copy2Next'] = False
1516    if 'Reverse Seq' not in data:
1517        data['Reverse Seq'] = False
1518    if 'UsrReject' not in data:
1519        data['UsrReject'] = {'minF/sig':0,'MinExt':0.01,'MaxDF/F':20.,'MaxD':500.,'MinD':0.05}
1520    if 'HatomFix' not in data:
1521        data['HatomFix'] = False
1522   
1523    #end patch
1524
1525    def SeqSizer():
1526       
1527        def OnSelectData(event):
1528            choices = GetPatternTreeDataNames(G2frame,['PWDR','HKLF',])
1529            sel = []
1530            try:
1531                if 'Seq Data' in data:
1532                    for item in data['Seq Data']:
1533                        sel.append(choices.index(item))
1534                    sel = [choices.index(item) for item in data['Seq Data']]
1535            except ValueError:  #data changed somehow - start fresh
1536                sel = []
1537            dlg = G2G.G2MultiChoiceDialog(G2frame.dataFrame, 'Sequential refinement',
1538                'Select dataset to include',choices)
1539            dlg.SetSelections(sel)
1540            names = []
1541            if dlg.ShowModal() == wx.ID_OK:
1542                for sel in dlg.GetSelections():
1543                    names.append(choices[sel])
1544                data['Seq Data'] = names               
1545                G2frame.EnableSeqRefineMenu()
1546            dlg.Destroy()
1547            wx.CallAfter(UpdateControls,G2frame,data)
1548           
1549        def OnReverse(event):
1550            data['Reverse Seq'] = reverseSel.GetValue()
1551           
1552        def OnCopySel(event):
1553            data['Copy2Next'] = copySel.GetValue() 
1554                   
1555        seqSizer = wx.BoxSizer(wx.VERTICAL)
1556        dataSizer = wx.BoxSizer(wx.HORIZONTAL)
1557        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Sequential Refinement: '),0,WACV)
1558        selSeqData = wx.Button(G2frame.dataDisplay,-1,label=' Select data')
1559        selSeqData.Bind(wx.EVT_BUTTON,OnSelectData)
1560        dataSizer.Add(selSeqData,0,WACV)
1561        SeqData = data.get('Seq Data',[])
1562        if not SeqData:
1563            lbl = ' (no data selected)'
1564        else:
1565            lbl = ' ('+str(len(SeqData))+' dataset(s) selected)'
1566
1567        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=lbl),0,WACV)
1568        seqSizer.Add(dataSizer,0)
1569        if SeqData:
1570            selSizer = wx.BoxSizer(wx.HORIZONTAL)
1571            reverseSel = wx.CheckBox(G2frame.dataDisplay,-1,label=' Reverse order?')
1572            reverseSel.Bind(wx.EVT_CHECKBOX,OnReverse)
1573            reverseSel.SetValue(data['Reverse Seq'])
1574            selSizer.Add(reverseSel,0,WACV)
1575            copySel =  wx.CheckBox(G2frame.dataDisplay,-1,label=' Copy results to next histogram?')
1576            copySel.Bind(wx.EVT_CHECKBOX,OnCopySel)
1577            copySel.SetValue(data['Copy2Next'])
1578            selSizer.Add(copySel,0,WACV)
1579            seqSizer.Add(selSizer,0)
1580        return seqSizer
1581       
1582    def LSSizer():       
1583       
1584        def OnDerivType(event):
1585            data['deriv type'] = derivSel.GetValue()
1586            derivSel.SetValue(data['deriv type'])
1587            wx.CallAfter(UpdateControls,G2frame,data)
1588           
1589        def OnConvergence(event):
1590            try:
1591                value = max(1.e-9,min(1.0,float(Cnvrg.GetValue())))
1592            except ValueError:
1593                value = 0.0001
1594            data['min dM/M'] = value
1595            Cnvrg.SetValue('%.2g'%(value))
1596           
1597        def OnMaxCycles(event):
1598            data['max cyc'] = int(maxCyc.GetValue())
1599            maxCyc.SetValue(str(data['max cyc']))
1600                       
1601        def OnFactor(event):
1602            try:
1603                value = min(max(float(Factr.GetValue()),0.00001),100.)
1604            except ValueError:
1605                value = 1.0
1606            data['shift factor'] = value
1607            Factr.SetValue('%.5f'%(value))
1608           
1609        def OnFsqRef(event):
1610            data['F**2'] = fsqRef.GetValue()
1611           
1612        def OnHatomFix(event):
1613            data['HatomFix'] = Hfix.GetValue()
1614       
1615        def OnUsrRej(event):
1616            Obj = event.GetEventObject()
1617            item,limits = Indx[Obj]
1618            try:
1619                value = min(max(float(Obj.GetValue()),limits[0]),limits[1])
1620            except ValueError:
1621                value = data['UsrReject'][item]
1622            data['UsrReject'][item] = value
1623            Obj.SetValue('%.2f'%(value))
1624
1625        LSSizer = wx.FlexGridSizer(cols=4,vgap=5,hgap=5)
1626        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement derivatives: '),0,WACV)
1627        Choice=['analytic Jacobian','numeric','analytic Hessian']
1628        derivSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['deriv type'],choices=Choice,
1629            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1630        derivSel.SetValue(data['deriv type'])
1631        derivSel.Bind(wx.EVT_COMBOBOX, OnDerivType)
1632           
1633        LSSizer.Add(derivSel,0,WACV)
1634        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min delta-M/M: '),0,WACV)
1635        Cnvrg = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2g'%(data['min dM/M']),style=wx.TE_PROCESS_ENTER)
1636        Cnvrg.Bind(wx.EVT_TEXT_ENTER,OnConvergence)
1637        Cnvrg.Bind(wx.EVT_KILL_FOCUS,OnConvergence)
1638        LSSizer.Add(Cnvrg,0,WACV)
1639        Indx = {}
1640        if 'Hessian' in data['deriv type']:
1641            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Max cycles: '),0,WACV)
1642            Choice = ['0','1','2','3','5','10','15','20']
1643            maxCyc = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['max cyc']),choices=Choice,
1644                style=wx.CB_READONLY|wx.CB_DROPDOWN)
1645            maxCyc.SetValue(str(data['max cyc']))
1646            maxCyc.Bind(wx.EVT_COMBOBOX, OnMaxCycles)
1647            LSSizer.Add(maxCyc,0,WACV)
1648        else:
1649            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Initial shift factor: '),0,WACV)
1650            Factr = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.5f'%(data['shift factor']),style=wx.TE_PROCESS_ENTER)
1651            Factr.Bind(wx.EVT_TEXT_ENTER,OnFactor)
1652            Factr.Bind(wx.EVT_KILL_FOCUS,OnFactor)
1653            LSSizer.Add(Factr,0,WACV)
1654        if G2frame.Sngl:
1655            userReject = data['UsrReject']
1656            usrRej = {'minF/sig':[' Min obs/sig (0-5): ',[0,5], ],'MinExt':[' Min extinct. (0-.9): ',[0,.9],],
1657                'MaxDF/F':[' Max delt-F/sig (3-1000): ',[3.,1000.],],'MaxD':[' Max d-spacing (3-500): ',[3,500],],
1658                'MinD':[' Min d-spacing (0.1-1.0): ',[0.1,1.0],]}
1659
1660            fsqRef = wx.CheckBox(G2frame.dataDisplay,-1,label='Refine HKLF as F^2? ')
1661            fsqRef.SetValue(data['F**2'])
1662            fsqRef.Bind(wx.EVT_CHECKBOX,OnFsqRef)
1663            LSSizer.Add(fsqRef,0,WACV)
1664            LSSizer.Add((1,0),)
1665            for item in usrRej:
1666                LSSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=usrRej[item][0]),0,WACV)
1667                usrrej = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(userReject[item]),style=wx.TE_PROCESS_ENTER)
1668                Indx[usrrej] = [item,usrRej[item][1]]
1669                usrrej.Bind(wx.EVT_TEXT_ENTER,OnUsrRej)
1670                usrrej.Bind(wx.EVT_KILL_FOCUS,OnUsrRej)
1671                LSSizer.Add(usrrej,0,WACV)
1672#        Hfix = wx.CheckBox(G2frame.dataDisplay,-1,label='Regularize H atoms? ')
1673#        Hfix.SetValue(data['HatomFix'])
1674#        Hfix.Bind(wx.EVT_CHECKBOX,OnHatomFix)
1675#        LSSizer.Add(Hfix,0,WACV)   #for now
1676        return LSSizer
1677       
1678    def AuthSizer():
1679
1680        def OnAuthor(event):
1681            data['Author'] = auth.GetValue()
1682
1683        Author = data['Author']
1684        authSizer = wx.BoxSizer(wx.HORIZONTAL)
1685        authSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' CIF Author (last, first):'),0,WACV)
1686        auth = wx.TextCtrl(G2frame.dataDisplay,-1,value=Author,style=wx.TE_PROCESS_ENTER)
1687        auth.Bind(wx.EVT_TEXT_ENTER,OnAuthor)
1688        auth.Bind(wx.EVT_KILL_FOCUS,OnAuthor)
1689        authSizer.Add(auth,0,WACV)
1690        return authSizer
1691       
1692       
1693    if G2frame.dataDisplay:
1694        G2frame.dataDisplay.Destroy()
1695    if not G2frame.dataFrame.GetStatusBar():
1696        Status = G2frame.dataFrame.CreateStatusBar()
1697        Status.SetStatusText('')
1698    G2frame.dataFrame.SetLabel('Controls')
1699    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
1700    SetDataMenuBar(G2frame,G2frame.dataFrame.ControlsMenu)
1701    mainSizer = wx.BoxSizer(wx.VERTICAL)
1702    mainSizer.Add((5,5),0)
1703    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement Controls:'),0,WACV)   
1704    mainSizer.Add(LSSizer())
1705    mainSizer.Add((5,5),0)
1706    mainSizer.Add(SeqSizer())
1707    mainSizer.Add((5,5),0)
1708    mainSizer.Add(AuthSizer())
1709    mainSizer.Add((5,5),0)
1710       
1711    mainSizer.Layout()   
1712    G2frame.dataDisplay.SetSizer(mainSizer)
1713    G2frame.dataDisplay.SetSize(mainSizer.Fit(G2frame.dataFrame))
1714    G2frame.dataFrame.setSizePosLeft(mainSizer.Fit(G2frame.dataFrame))
1715     
1716################################################################################
1717#####  Comments
1718################################################################################           
1719       
1720def UpdateComments(G2frame,data):                   
1721
1722    if G2frame.dataDisplay:
1723        G2frame.dataDisplay.Destroy()
1724    G2frame.dataFrame.SetLabel('Comments')
1725    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
1726        style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_DONTWRAP)
1727    for line in data:
1728        G2frame.dataDisplay.AppendText(line+'\n')
1729    G2frame.dataFrame.setSizePosLeft([400,250])
1730           
1731################################################################################
1732#####  Display of Sequential Results
1733################################################################################           
1734       
1735def UpdateSeqResults(G2frame,data,prevSize=None):
1736    """
1737    Called when the Sequential Results data tree entry is selected
1738    to show results from a sequential refinement.
1739   
1740    :param wx.Frame G2frame: main GSAS-II data tree windows
1741
1742    :param dict data: a dictionary containing the following items: 
1743
1744            * 'histNames' - list of histogram names in order as processed by Sequential Refinement
1745            * 'varyList' - list of variables - identical over all refinements in sequence
1746              note that this is the original list of variables, prior to processing
1747              constraints.
1748            * 'variableLabels' -- a dict of labels to be applied to each parameter
1749              (this is created as an empty dict if not present in data).
1750            * keyed by histName - dictionaries for all data sets processed, which contains:
1751
1752              * 'variables'- result[0] from leastsq call
1753              * 'varyList' - list of variables passed to leastsq call (not same as above)
1754              * 'sig' - esds for variables
1755              * 'covMatrix' - covariance matrix from individual refinement
1756              * 'title' - histogram name; same as dict item name
1757              * 'newAtomDict' - new atom parameters after shifts applied
1758              * 'newCellDict' - refined cell parameters after shifts to A0-A5 from Dij terms applied'
1759    """
1760
1761    def GetSampleParms():
1762        '''Make a dictionary of the sample parameters are not the same over the
1763        refinement series.
1764        '''
1765        if 'IMG' in histNames[0]:
1766            sampleParmDict = {'Sample load':[],}
1767        else:
1768            sampleParmDict = {'Temperature':[],'Pressure':[],'Time':[],
1769                'FreePrm1':[],'FreePrm2':[],'FreePrm3':[],'Omega':[],
1770                'Chi':[],'Phi':[],'Azimuth':[],}
1771        Controls = G2frame.PatternTree.GetItemPyData(
1772            GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
1773        sampleParm = {}
1774        for name in histNames:
1775            if 'IMG' in name:
1776                for item in sampleParmDict:
1777                    sampleParmDict[item].append(data[name]['parmDict'].get(item,0))
1778            else:
1779                Id = GetPatternTreeItemId(G2frame,G2frame.root,name)
1780                sampleData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Sample Parameters'))
1781                for item in sampleParmDict:
1782                    sampleParmDict[item].append(sampleData.get(item,0))
1783        for item in sampleParmDict:
1784            frstValue = sampleParmDict[item][0]
1785            if np.any(np.array(sampleParmDict[item])-frstValue):
1786                if item.startswith('FreePrm'):
1787                    sampleParm[Controls[item]] = sampleParmDict[item]
1788                else:
1789                    sampleParm[item] = sampleParmDict[item]
1790        return sampleParm
1791
1792    def GetColumnInfo(col):
1793        '''returns column label, lists of values and errors (or None) for each column in the table
1794        for plotting. The column label is reformatted from Unicode to MatPlotLib encoding
1795        '''
1796        colName = G2frame.SeqTable.GetColLabelValue(col)
1797        plotName = variableLabels.get(colName,colName)
1798        plotName = plotSpCharFix(plotName)
1799        return plotName,colList[col],colSigs[col]
1800           
1801    def PlotSelect(event):
1802        'Plots a row (covariance) or column on double-click'
1803        cols = G2frame.dataDisplay.GetSelectedCols()
1804        rows = G2frame.dataDisplay.GetSelectedRows()
1805        if cols:
1806            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
1807        elif rows:
1808            name = histNames[rows[0]]       #only does 1st one selected
1809            G2plt.PlotCovariance(G2frame,data[name])
1810        else:
1811            G2frame.ErrorDialog(
1812                'Select row or columns',
1813                'Nothing selected in table. Click on column or row label(s) to plot. N.B. Grid selection can be a bit funky.'
1814                )
1815           
1816    def OnPlotSelSeq(event):
1817        'plot the selected columns or row from menu command'
1818        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
1819        rows = G2frame.dataDisplay.GetSelectedRows()
1820        if cols:
1821            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
1822        elif rows:
1823            name = histNames[rows[0]]       #only does 1st one selected
1824            G2plt.PlotCovariance(G2frame,data[name])
1825        else:
1826            G2frame.ErrorDialog(
1827                'Select columns',
1828                'No columns or rows selected in table. Click on row or column labels to select fields for plotting.'
1829                )
1830               
1831    def OnAveSelSeq(event):
1832        'average the selected columns from menu command'
1833        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
1834        if cols:
1835            for col in cols:
1836                ave = np.mean(GetColumnInfo(col)[1])
1837                sig = np.std(GetColumnInfo(col)[1])
1838                print ' Average for '+G2frame.SeqTable.GetColLabelValue(col)+': '+'%.6g'%(ave)+' +/- '+'%.6g'%(sig)
1839        else:
1840            G2frame.ErrorDialog(
1841                'Select columns',
1842                'No columns selected in table. Click on column labels to select fields for averaging.'
1843                )
1844               
1845    def OnRenameSelSeq(event):
1846        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
1847        colNames = [G2frame.SeqTable.GetColLabelValue(c) for c in cols]
1848        newNames = colNames[:]
1849        for i,name in enumerate(colNames):
1850            if name in variableLabels:
1851                newNames[i] = variableLabels[name]
1852        if not cols:
1853            G2frame.ErrorDialog('Select columns',
1854                'No columns selected in table. Click on column labels to select fields for rename.')
1855            return
1856        dlg = G2G.MultiStringDialog(G2frame.dataDisplay,'Set column names',colNames,newNames)
1857        if dlg.Show():
1858            newNames = dlg.GetValues()           
1859            variableLabels.update(dict(zip(colNames,newNames)))
1860        data['variableLabels'] = variableLabels
1861        dlg.Destroy()
1862        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
1863        G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
1864           
1865    def OnReOrgSelSeq(event):
1866        'Reorder the columns'
1867        G2G.GetItemOrder(G2frame,VaryListChanges,vallookup,posdict)   
1868        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
1869
1870    def OnSaveSelSeqCSV(event):
1871        'export the selected columns to a .csv file from menu command'
1872        OnSaveSelSeq(event,csv=True)
1873       
1874    def OnSaveSeqCSV(event):
1875        'export all columns to a .csv file from menu command'
1876        OnSaveSelSeq(event,csv=True,allcols=True)
1877       
1878    def OnSaveSelSeq(event,csv=False,allcols=False):
1879        'export the selected columns to a .txt or .csv file from menu command'
1880        def WriteCSV():
1881            def WriteList(headerItems):
1882                line = ''
1883                for lbl in headerItems:
1884                    if line: line += ','
1885                    line += '"'+lbl+'"'
1886                return line
1887            head = ['name']
1888            for col in cols:
1889                item = G2frame.SeqTable.GetColLabelValue(col)
1890                # get rid of labels that have Unicode characters
1891                if not all([ord(c) < 128 and ord(c) != 0 for c in item]): item = '?'
1892                if col in havesig:
1893                    head += [item,'esd-'+item]
1894                else:
1895                    head += [item]
1896            SeqFile.write(WriteList(head)+'\n')
1897            for row,name in enumerate(saveNames):
1898                line = '"'+saveNames[row]+'"'
1899                for col in cols:
1900                    if col in havesig:
1901                        line += ','+str(saveData[col][row])+','+str(saveSigs[col][row])
1902                    else:
1903                        line += ','+str(saveData[col][row])
1904                SeqFile.write(line+'\n')
1905        def WriteSeq():
1906            lenName = len(saveNames[0])
1907            line = %s  '%('name'.center(lenName))
1908            for col in cols:
1909                item = G2frame.SeqTable.GetColLabelValue(col)
1910                if col in havesig:
1911                    line += ' %12s %12s '%(item.center(12),'esd'.center(12))
1912                else:
1913                    line += ' %12s '%(item.center(12))
1914            SeqFile.write(line+'\n')
1915            for row,name in enumerate(saveNames):
1916                line = " '%s' "%(saveNames[row])
1917                for col in cols:
1918                    if col in havesig:
1919                        line += ' %12.6f %12.6f '%(saveData[col][row],saveSigs[col][row])
1920                    else:
1921                        line += ' %12.6f '%saveData[col][row]
1922                SeqFile.write(line+'\n')
1923
1924        # start of OnSaveSelSeq code
1925        if allcols:
1926            cols = range(G2frame.SeqTable.GetNumberCols())
1927        else:
1928            cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
1929        nrows = G2frame.SeqTable.GetNumberRows()
1930        if not cols:
1931            G2frame.ErrorDialog('Select columns',
1932                             'No columns selected in table. Click on column labels to select fields for output.')
1933            return
1934        saveNames = [G2frame.SeqTable.GetRowLabelValue(r) for r in range(nrows)]
1935        saveData = {}
1936        saveSigs = {}
1937        havesig = []
1938        for col in cols:
1939            name,vals,sigs = GetColumnInfo(col)
1940            saveData[col] = vals
1941            if sigs:
1942                havesig.append(col)
1943                saveSigs[col] = sigs
1944        if csv:
1945            wild = 'CSV output file (*.csv)|*.csv'
1946        else:
1947            wild = 'Text output file (*.txt)|*.txt'
1948        dlg = wx.FileDialog(
1949            G2frame,
1950            'Choose text output file for your selection', '.', '', 
1951            wild,wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
1952        try:
1953            if dlg.ShowModal() == wx.ID_OK:
1954                SeqTextFile = dlg.GetPath()
1955                SeqTextFile = G2IO.FileDlgFixExt(dlg,SeqTextFile) 
1956                SeqFile = open(SeqTextFile,'w')
1957                if csv:
1958                    WriteCSV()
1959                else:
1960                    WriteSeq()
1961                SeqFile.close()
1962        finally:
1963            dlg.Destroy()
1964               
1965    def striphist(var,insChar=''):
1966        'strip a histogram number from a var name'
1967        sv = var.split(':')
1968        if len(sv) <= 1: return var
1969        if sv[1]:
1970            sv[1] = insChar
1971        return ':'.join(sv)
1972       
1973    def plotSpCharFix(lbl):
1974        'Change selected unicode characters to their matplotlib equivalent'
1975        for u,p in [
1976            (u'\u03B1',r'$\alpha$'),
1977            (u'\u03B2',r'$\beta$'),
1978            (u'\u03B3',r'$\gamma$'),
1979            (u'\u0394\u03C7',r'$\Delta\chi$'),
1980            ]:
1981            lbl = lbl.replace(u,p)
1982        return lbl
1983   
1984    def SelectXaxis():
1985        'returns a selected column number (or None) as the X-axis selection'
1986        ncols = G2frame.SeqTable.GetNumberCols()
1987        colNames = [G2frame.SeqTable.GetColLabelValue(r) for r in range(ncols)]
1988        dlg = G2G.G2SingleChoiceDialog(
1989            G2frame.dataDisplay,
1990            'Select x-axis parameter for plot or Cancel for sequence number',
1991            'Select X-axis',
1992            colNames)
1993        try:
1994            if dlg.ShowModal() == wx.ID_OK:
1995                col = dlg.GetSelection()
1996            else:
1997                col = None
1998        finally:
1999            dlg.Destroy()
2000        return col
2001   
2002    def EnablePseudoVarMenus():
2003        'Enables or disables the PseudoVar menu items that require existing defs'
2004        if Controls['SeqPseudoVars']:
2005            val = True
2006        else:
2007            val = False
2008        G2frame.dataFrame.SequentialPvars.Enable(wxDELSEQVAR,val)
2009        G2frame.dataFrame.SequentialPvars.Enable(wxEDITSEQVAR,val)
2010
2011    def DelPseudoVar(event):
2012        'Ask the user to select a pseudo var expression to delete'
2013        choices = Controls['SeqPseudoVars'].keys()
2014        selected = G2G.ItemSelector(
2015            choices,G2frame.dataFrame,
2016            multiple=True,
2017            title='Select expressions to remove',
2018            header='Delete expression')
2019        if selected is None: return
2020        for item in selected:
2021            del Controls['SeqPseudoVars'][choices[item]]
2022        if selected:
2023            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2024
2025    def EditPseudoVar(event):
2026        'Edit an existing pseudo var expression'
2027        choices = Controls['SeqPseudoVars'].keys()
2028        if len(choices) == 1:
2029            selected = 0
2030        else:
2031            selected = G2G.ItemSelector(
2032                choices,G2frame.dataFrame,
2033                multiple=False,
2034                title='Select an expression to edit',
2035                header='Edit expression')
2036        if selected is not None:
2037            dlg = G2exG.ExpressionDialog(
2038                G2frame.dataDisplay,PSvarDict,
2039                Controls['SeqPseudoVars'][choices[selected]],
2040                header="Edit the PseudoVar expression",
2041                VarLabel="PseudoVar #"+str(selected+1),
2042                fit=False)
2043            newobj = dlg.Show(True)
2044            if newobj:
2045                calcobj = G2obj.ExpressionCalcObj(newobj)
2046                del Controls['SeqPseudoVars'][choices[selected]]
2047                Controls['SeqPseudoVars'][calcobj.eObj.expression] = newobj
2048                UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2049       
2050    def AddNewPseudoVar(event):
2051        'Create a new pseudo var expression'
2052        dlg = G2exG.ExpressionDialog(
2053            G2frame.dataDisplay,PSvarDict,
2054            header='Enter an expression for a PseudoVar here',
2055            VarLabel = "New PseudoVar",
2056            fit=False)
2057        obj = dlg.Show(True)
2058        dlg.Destroy()
2059        if obj:
2060            calcobj = G2obj.ExpressionCalcObj(obj)
2061            Controls['SeqPseudoVars'][calcobj.eObj.expression] = obj
2062            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2063
2064    def UpdateParmDict(parmDict):
2065        '''generate the atom positions and the direct & reciprocal cell values,
2066        because they might be needed to evaluate the pseudovar
2067        '''
2068        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
2069                         ['A'+str(i) for i in range(6)])
2070                     )
2071        delList = []
2072        phaselist = []
2073        for item in parmDict: 
2074            if ':' not in item: continue
2075            key = item.split(':')
2076            if len(key) < 3: continue
2077            # remove the dA[xyz] terms, they would only bring confusion
2078            if key[2].startswith('dA'):
2079                delList.append(item)
2080            # compute and update the corrected reciprocal cell terms using the Dij values
2081            elif key[2] in Ddict:
2082                if key[0] not in phaselist: phaselist.append(key[0])
2083                akey = key[0]+'::'+Ddict[key[2]]
2084                parmDict[akey] -= parmDict[item]
2085                delList.append(item)
2086        for item in delList:
2087            del parmDict[item]               
2088        for i in phaselist:
2089            pId = int(i)
2090            # apply cell symmetry
2091            A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],parmDict,zeroDict[pId])
2092            # convert to direct cell & add the unique terms to the dictionary
2093            for i,val in enumerate(G2lat.A2cell(A)):
2094                if i in uniqCellIndx[pId]:
2095                    lbl = str(pId)+'::'+cellUlbl[i]
2096                    parmDict[lbl] = val
2097            lbl = str(pId)+'::'+'vol'
2098            parmDict[lbl] = G2lat.calc_V(A)
2099        return parmDict
2100
2101    def EvalPSvarDeriv(calcobj,parmDict,sampleDict,var,ESD):
2102        '''Evaluate an expression derivative with respect to a
2103        GSAS-II variable name.
2104
2105        Note this likely could be faster if the loop over calcobjs were done
2106        inside after the Dict was created.
2107        '''
2108        step = ESD/10
2109        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
2110                         ['A'+str(i) for i in range(6)])
2111                     )
2112        results = []
2113        phaselist = []
2114        VparmDict = sampleDict.copy()
2115        for incr in step,-step:
2116            VparmDict.update(parmDict.copy())           
2117            # as saved, the parmDict has updated 'A[xyz]' values, but 'dA[xyz]'
2118            # values are not zeroed: fix that!
2119            VparmDict.update({item:0.0 for item in parmDict if 'dA' in item})
2120            VparmDict[var] += incr
2121            G2mv.Dict2Map(VparmDict,[]) # apply constraints
2122            # generate the atom positions and the direct & reciprocal cell values now, because they might
2123            # needed to evaluate the pseudovar
2124            for item in VparmDict:
2125                if item in sampleDict:
2126                    continue 
2127                if ':' not in item: continue
2128                key = item.split(':')
2129                if len(key) < 3: continue
2130                # apply any new shifts to atom positions
2131                if key[2].startswith('dA'):
2132                    VparmDict[''.join(item.split('d'))] += VparmDict[item]
2133                    VparmDict[item] = 0.0
2134                # compute and update the corrected reciprocal cell terms using the Dij values
2135                if key[2] in Ddict:
2136                    if key[0] not in phaselist: phaselist.append(key[0])
2137                    akey = key[0]+'::'+Ddict[key[2]]
2138                    VparmDict[akey] -= VparmDict[item]
2139            for i in phaselist:
2140                pId = int(i)
2141                # apply cell symmetry
2142                A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],VparmDict,zeroDict[pId])
2143                # convert to direct cell & add the unique terms to the dictionary
2144                for i,val in enumerate(G2lat.A2cell(A)):
2145                    if i in uniqCellIndx[pId]:
2146                        lbl = str(pId)+'::'+cellUlbl[i]
2147                        VparmDict[lbl] = val
2148                lbl = str(pId)+'::'+'vol'
2149                VparmDict[lbl] = G2lat.calc_V(A)
2150            # dict should be fully updated, use it & calculate
2151            calcobj.SetupCalc(VparmDict)
2152            results.append(calcobj.EvalExpression())
2153        return (results[0] - results[1]) / (2.*step)
2154       
2155    def EnableParFitEqMenus():
2156        'Enables or disables the Parametric Fit menu items that require existing defs'
2157        if Controls['SeqParFitEqList']:
2158            val = True
2159        else:
2160            val = False
2161        G2frame.dataFrame.SequentialPfit.Enable(wxDELPARFIT,val)
2162        G2frame.dataFrame.SequentialPfit.Enable(wxEDITPARFIT,val)
2163        G2frame.dataFrame.SequentialPfit.Enable(wxDOPARFIT,val)
2164
2165    def ParEqEval(Values,calcObjList,varyList):
2166        '''Evaluate the parametric expression(s)
2167        :param list Values: a list of values for each variable parameter
2168        :param list calcObjList: a list of :class:`GSASIIobj.ExpressionCalcObj`
2169          expression objects to evaluate
2170        :param list varyList: a list of variable names for each value in Values
2171        '''
2172        result = []
2173        for calcobj in calcObjList:
2174            calcobj.UpdateVars(varyList,Values)
2175            result.append((calcobj.depVal-calcobj.EvalExpression())/calcobj.depSig)
2176        return result
2177
2178    def DoParEqFit(event,eqObj=None):
2179        'Parametric fit minimizer'
2180        varyValueDict = {} # dict of variables and their initial values
2181        calcObjList = [] # expression objects, ready to go for each data point
2182        if eqObj is not None:
2183            eqObjList = [eqObj,]
2184        else:
2185            eqObjList = Controls['SeqParFitEqList']
2186        UseFlags = G2frame.SeqTable.GetColValues(0)         
2187        for obj in eqObjList:
2188            expr = obj.expression
2189            # assemble refined vars for this equation
2190            varyValueDict.update({var:val for var,val in obj.GetVariedVarVal()})
2191            # lookup dependent var position
2192            depVar = obj.GetDepVar()
2193            if depVar in colLabels:
2194                indx = colLabels.index(depVar)
2195            else:
2196                raise Exception('Dependent variable '+depVar+' not found')
2197            # assemble a list of the independent variables
2198            indepVars = obj.GetIndependentVars()
2199            # loop over each datapoint
2200            for j,row in enumerate(zip(*colList)):
2201                if not UseFlags[j]: continue
2202                # assemble equations to fit
2203                calcobj = G2obj.ExpressionCalcObj(obj)
2204                # prepare a dict of needed independent vars for this expression
2205                indepVarDict = {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
2206                calcobj.SetupCalc(indepVarDict)               
2207                # values and sigs for current value of dependent var
2208                calcobj.depVal = row[indx]
2209                calcobj.depSig = colSigs[indx][j]
2210                calcObjList.append(calcobj)
2211        # varied parameters
2212        varyList = varyValueDict.keys()
2213        values = varyValues = [varyValueDict[key] for key in varyList]
2214        if not varyList:
2215            print 'no variables to refine!'
2216            return
2217        try:
2218            result = so.leastsq(ParEqEval,varyValues,full_output=True,   #ftol=Ftol,
2219                                args=(calcObjList,varyList)
2220                                )
2221            values = result[0]
2222            covar = result[1]
2223            if covar is None:
2224                raise Exception
2225            esdDict = {}
2226            for i,avar in enumerate(varyList):
2227                esdDict[avar] = np.sqrt(covar[i,i])
2228        except:
2229            print('====> Fit failed')
2230            return
2231        print('==== Fit Results ====')
2232        for obj in eqObjList:
2233            obj.UpdateVariedVars(varyList,values)
2234            ind = '      '
2235            print('  '+obj.GetDepVar()+' = '+obj.expression)
2236            for var in obj.assgnVars:
2237                print(ind+var+' = '+obj.assgnVars[var])
2238            for var in obj.freeVars:
2239                avar = "::"+obj.freeVars[var][0]
2240                val = obj.freeVars[var][1]
2241                if obj.freeVars[var][2]:
2242                    print(ind+var+' = '+avar + " = " + G2mth.ValEsd(val,esdDict[avar]))
2243                else:
2244                    print(ind+var+' = '+avar + " =" + G2mth.ValEsd(val,0))
2245        # create a plot for each parametric variable
2246        for fitnum,obj in enumerate(eqObjList):
2247            calcobj = G2obj.ExpressionCalcObj(obj)
2248            # lookup dependent var position
2249            indx = colLabels.index(obj.GetDepVar())
2250            # assemble a list of the independent variables
2251            indepVars = obj.GetIndependentVars()           
2252            # loop over each datapoint
2253            fitvals = []
2254            for j,row in enumerate(zip(*colList)):
2255                calcobj.SetupCalc(
2256                    {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
2257                    )
2258                fitvals.append(calcobj.EvalExpression())
2259            G2plt.PlotSelectedSequence(
2260                G2frame,[indx],GetColumnInfo,SelectXaxis,
2261                fitnum,fitvals)
2262
2263    def SingleParEqFit(eqObj):
2264        DoParEqFit(None,eqObj)
2265
2266    def DelParFitEq(event):
2267        'Ask the user to select function to delete'
2268        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
2269        selected = G2G.ItemSelector(
2270            txtlst,G2frame.dataFrame,
2271            multiple=True,
2272            title='Select a parametric equation(s) to remove',
2273            header='Delete equation')
2274        if selected is None: return
2275        Controls['SeqParFitEqList'] = [obj for i,obj in enumerate(Controls['SeqParFitEqList']) if i not in selected]
2276        EnableParFitEqMenus()
2277        if Controls['SeqParFitEqList']: DoParEqFit(event)
2278       
2279    def EditParFitEq(event):
2280        'Edit an existing parametric equation'
2281        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
2282        if len(txtlst) == 1:
2283            selected = 0
2284        else:
2285            selected = G2G.ItemSelector(
2286                txtlst,G2frame.dataFrame,
2287                multiple=False,
2288                title='Select a parametric equation to edit',
2289                header='Edit equation')
2290        if selected is not None:
2291            dlg = G2exG.ExpressionDialog(
2292                G2frame.dataDisplay,indepVarDict,
2293                Controls['SeqParFitEqList'][selected],
2294                depVarDict=depVarDict,
2295                header="Edit the formula for this minimization function",
2296                ExtraButton=['Fit',SingleParEqFit])
2297            newobj = dlg.Show(True)
2298            if newobj:
2299                calcobj = G2obj.ExpressionCalcObj(newobj)
2300                Controls['SeqParFitEqList'][selected] = newobj
2301                EnableParFitEqMenus()
2302            if Controls['SeqParFitEqList']: DoParEqFit(event)
2303
2304    def AddNewParFitEq(event):
2305        'Create a new parametric equation to be fit to sequential results'
2306
2307        # compile the variable names used in previous freevars to avoid accidental name collisions
2308        usedvarlist = []
2309        for obj in Controls['SeqParFitEqList']:
2310            for var in obj.freeVars:
2311                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
2312
2313        dlg = G2exG.ExpressionDialog(
2314            G2frame.dataDisplay,indepVarDict,
2315            depVarDict=depVarDict,
2316            header='Define an equation to minimize in the parametric fit',
2317            ExtraButton=['Fit',SingleParEqFit],
2318            usedVars=usedvarlist)
2319        obj = dlg.Show(True)
2320        dlg.Destroy()
2321        if obj:
2322            Controls['SeqParFitEqList'].append(obj)
2323            EnableParFitEqMenus()
2324            if Controls['SeqParFitEqList']: DoParEqFit(event)
2325               
2326    def CopyParFitEq(event):
2327        'Copy an existing parametric equation to be fit to sequential results'
2328        # compile the variable names used in previous freevars to avoid accidental name collisions
2329        usedvarlist = []
2330        for obj in Controls['SeqParFitEqList']:
2331            for var in obj.freeVars:
2332                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
2333        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
2334        if len(txtlst) == 1:
2335            selected = 0
2336        else:
2337            selected = G2G.ItemSelector(
2338                txtlst,G2frame.dataFrame,
2339                multiple=False,
2340                title='Select a parametric equation to copy',
2341                header='Copy equation')
2342        if selected is not None:
2343            newEqn = copy.deepcopy(Controls['SeqParFitEqList'][selected])
2344            for var in newEqn.freeVars:
2345                newEqn.freeVars[var][0] = G2obj.MakeUniqueLabel(newEqn.freeVars[var][0],usedvarlist)
2346            dlg = G2exG.ExpressionDialog(
2347                G2frame.dataDisplay,indepVarDict,
2348                newEqn,
2349                depVarDict=depVarDict,
2350                header="Edit the formula for this minimization function",
2351                ExtraButton=['Fit',SingleParEqFit])
2352            newobj = dlg.Show(True)
2353            if newobj:
2354                calcobj = G2obj.ExpressionCalcObj(newobj)
2355                Controls['SeqParFitEqList'].append(newobj)
2356                EnableParFitEqMenus()
2357            if Controls['SeqParFitEqList']: DoParEqFit(event)
2358                                           
2359    def GridSetToolTip(row,col):
2360        '''Routine to show standard uncertainties for each element in table
2361        as a tooltip
2362        '''
2363        if colSigs[col]:
2364            return u'\u03c3 = '+str(colSigs[col][row])
2365        return ''
2366       
2367    def GridColLblToolTip(col):
2368        '''Define a tooltip for a column. This will be the user-entered value
2369        (from data['variableLabels']) or the default name
2370        '''
2371        if col < 0 or col > len(colLabels):
2372            print 'Illegal column #',col
2373            return
2374        var = colLabels[col]
2375        return variableLabels.get(var,G2obj.fmtVarDescr(var))
2376       
2377    def SetLabelString(event):
2378        '''Define or edit the label for a column in the table, to be used
2379        as a tooltip and for plotting
2380        '''
2381        col = event.GetCol()
2382        if col < 0 or col > len(colLabels):
2383            return
2384        var = colLabels[col]
2385        lbl = variableLabels.get(var,G2obj.fmtVarDescr(var))
2386        dlg = G2G.SingleStringDialog(G2frame.dataFrame,'Set variable label',
2387                                 'Set a new name for variable '+var,lbl,size=(400,-1))
2388        if dlg.Show():
2389            variableLabels[var] = dlg.GetValue()
2390        dlg.Destroy()
2391       
2392    #def GridRowLblToolTip(row): return 'Row ='+str(row)
2393   
2394    # lookup table for unique cell parameters by symmetry
2395    cellGUIlist = [
2396        [['m3','m3m'],(0,)],
2397        [['3R','3mR'],(0,3)],
2398        [['3','3m1','31m','6/m','6/mmm','4/m','4/mmm'],(0,2)],
2399        [['mmm'],(0,1,2)],
2400        [['2/m'+'a'],(0,1,2,3)],
2401        [['2/m'+'b'],(0,1,2,4)],
2402        [['2/m'+'c'],(0,1,2,5)],
2403        [['-1'],(0,1,2,3,4,5)],
2404        ]
2405    # cell labels
2406    cellUlbl = ('a','b','c',u'\u03B1',u'\u03B2',u'\u03B3') # unicode a,b,c,alpha,beta,gamma
2407
2408    #======================================================================
2409    # start processing sequential results here (UpdateSeqResults)
2410    #======================================================================
2411    if not data:
2412        print 'No sequential refinement results'
2413        return
2414    variableLabels = data.get('variableLabels',{})
2415    data['variableLabels'] = variableLabels
2416    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
2417    Controls = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Controls'))
2418    # create a place to store Pseudo Vars & Parametric Fit functions, if not present
2419    if 'SeqPseudoVars' not in Controls: Controls['SeqPseudoVars'] = {}
2420    if 'SeqParFitEqList' not in Controls: Controls['SeqParFitEqList'] = []
2421    histNames = data['histNames']
2422    if G2frame.dataDisplay:
2423        G2frame.dataDisplay.Destroy()
2424    if not G2frame.dataFrame.GetStatusBar():
2425        Status = G2frame.dataFrame.CreateStatusBar()
2426        Status.SetStatusText("Select column to export; Double click on column to plot data; on row for Covariance")
2427    sampleParms = GetSampleParms()
2428
2429    # make dict of varied atom coords keyed by absolute position
2430    newAtomDict = data[histNames[0]].get('newAtomDict',{}) # dict with atom positions; relative & absolute
2431    # Possible error: the next might need to be data[histNames[0]]['varyList']
2432    # error will arise if there constraints on coordinates?
2433    atomLookup = {newAtomDict[item][0]:item for item in newAtomDict if item in data['varyList']}
2434   
2435    # make dict of varied cell parameters equivalents
2436    ESDlookup = {} # provides the Dij term for each Ak term (where terms are refined)
2437    Dlookup = {} # provides the Ak term for each Dij term (where terms are refined)
2438    # N.B. These Dij vars are missing a histogram #
2439    newCellDict = data[histNames[0]].get('newCellDict',{})
2440    for item in newCellDict:
2441        if item in data['varyList']:
2442            ESDlookup[newCellDict[item][0]] = item
2443            Dlookup[item] = newCellDict[item][0]
2444    # add coordinate equivalents to lookup table
2445    for parm in atomLookup:
2446        Dlookup[atomLookup[parm]] = parm
2447        ESDlookup[parm] = atomLookup[parm]
2448
2449    # get unit cell & symmetry for all phases & initial stuff for later use
2450    RecpCellTerms = {}
2451    SGdata = {}
2452    uniqCellIndx = {}
2453    initialCell = {}
2454    RcellLbls = {}
2455    zeroDict = {}
2456    Rcelldict = {}
2457    for phase in Phases:
2458        phasedict = Phases[phase]
2459        pId = phasedict['pId']
2460        pfx = str(pId)+'::' # prefix for A values from phase
2461        RcellLbls[pId] = [pfx+'A'+str(i) for i in range(6)]
2462        RecpCellTerms[pId] = G2lat.cell2A(phasedict['General']['Cell'][1:7])
2463        zeroDict[pId] = dict(zip(RcellLbls[pId],6*[0.,]))
2464        SGdata[pId] = phasedict['General']['SGData']
2465        Rcelldict.update({lbl:val for lbl,val in zip(RcellLbls[pId],RecpCellTerms[pId])})
2466        laue = SGdata[pId]['SGLaue']
2467        if laue == '2/m':
2468            laue += SGdata[pId]['SGUniq']
2469        for symlist,celllist in cellGUIlist:
2470            if laue in symlist:
2471                uniqCellIndx[pId] = celllist
2472                break
2473        else: # should not happen
2474            uniqCellIndx[pId] = range(6)
2475        for i in uniqCellIndx[pId]:
2476            initialCell[str(pId)+'::A'+str(i)] =  RecpCellTerms[pId][i]
2477
2478    SetDataMenuBar(G2frame,G2frame.dataFrame.SequentialMenu)
2479    G2frame.dataFrame.SetLabel('Sequential refinement results')
2480    if not G2frame.dataFrame.GetStatusBar():
2481        Status = G2frame.dataFrame.CreateStatusBar()
2482        Status.SetStatusText('')
2483    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRenameSelSeq, id=wxID_RENAMESEQSEL)
2484    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeq, id=wxID_SAVESEQSEL)
2485    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeqCSV, id=wxID_SAVESEQSELCSV)
2486    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSeqCSV, id=wxID_SAVESEQCSV)
2487    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotSelSeq, id=wxID_PLOTSEQSEL)
2488    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAveSelSeq, id=wxID_AVESEQSEL)
2489    G2frame.dataFrame.Bind(wx.EVT_MENU, OnReOrgSelSeq, id=wxID_ORGSEQSEL)
2490    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewPseudoVar, id=wxADDSEQVAR)
2491    G2frame.dataFrame.Bind(wx.EVT_MENU, DelPseudoVar, id=wxDELSEQVAR)
2492    G2frame.dataFrame.Bind(wx.EVT_MENU, EditPseudoVar, id=wxEDITSEQVAR)
2493    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewParFitEq, id=wxADDPARFIT)
2494    G2frame.dataFrame.Bind(wx.EVT_MENU, CopyParFitEq, id=wxCOPYPARFIT)
2495    G2frame.dataFrame.Bind(wx.EVT_MENU, DelParFitEq, id=wxDELPARFIT)
2496    G2frame.dataFrame.Bind(wx.EVT_MENU, EditParFitEq, id=wxEDITPARFIT)
2497    G2frame.dataFrame.Bind(wx.EVT_MENU, DoParEqFit, id=wxDOPARFIT)
2498    EnablePseudoVarMenus()
2499    EnableParFitEqMenus()
2500
2501    # scan for locations where the variables change
2502    VaryListChanges = [] # histograms where there is a change
2503    combinedVaryList = []
2504    firstValueDict = {}
2505    vallookup = {}
2506    posdict = {}
2507    prevVaryList = []
2508    for i,name in enumerate(histNames):
2509        for var,val,sig in zip(data[name]['varyList'],data[name]['variables'],data[name]['sig']):
2510            svar = striphist(var,'*') # wild-carded
2511            if svar not in combinedVaryList:
2512                # add variables to list as they appear
2513                combinedVaryList.append(svar)
2514                firstValueDict[svar] = (val,sig)
2515        if prevVaryList != data[name]['varyList']: # this refinement has a different refinement list from previous
2516            prevVaryList = data[name]['varyList']
2517            vallookup[name] = dict(zip(data[name]['varyList'],data[name]['variables']))
2518            posdict[name] = {}
2519            for var in data[name]['varyList']:
2520                svar = striphist(var,'*')
2521                posdict[name][combinedVaryList.index(svar)] = svar
2522            VaryListChanges.append(name)
2523    if len(VaryListChanges) > 1:
2524        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,True)
2525    else:
2526        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,False)
2527    #-----------------------------------------------------------------------------------
2528    # build up the data table by columns -----------------------------------------------
2529    nRows = len(histNames)
2530    colList = [nRows*[True]]
2531    colSigs = [None]
2532    colLabels = ['Use']
2533    Types = [wg.GRID_VALUE_BOOL]
2534    # start with Rwp values
2535    if 'IMG ' not in histNames[0][:4]:
2536        colList += [[data[name]['Rvals']['Rwp'] for name in histNames]]
2537        colSigs += [None]
2538        colLabels += ['Rwp']
2539        Types += [wg.GRID_VALUE_FLOAT+':10,3',]
2540    # add % change in Chi^2 in last cycle
2541    if histNames[0][:4] not in ['SASD','IMG '] and Controls.get('ShowCell'):
2542        colList += [[100.*data[name]['Rvals'].get('DelChi2',-1) for name in histNames]]
2543        colSigs += [None]
2544        colLabels += [u'\u0394\u03C7\u00B2 (%)']
2545        Types += [wg.GRID_VALUE_FLOAT,]
2546    deltaChiCol = len(colLabels)-1
2547    # add changing sample parameters to table
2548    for key in sampleParms:
2549        colList += [sampleParms[key]]
2550        colSigs += [None]
2551        colLabels += [key]
2552        Types += [wg.GRID_VALUE_FLOAT,]
2553    sampleDict = {}
2554    for i,name in enumerate(histNames):
2555        sampleDict[name] = dict(zip(sampleParms.keys(),[sampleParms[key][i] for key in sampleParms.keys()])) 
2556    # add unique cell parameters TODO: review this where the cell symmetry changes (when possible)
2557    if Controls.get('ShowCell',False):
2558        for pId in sorted(RecpCellTerms):
2559            pfx = str(pId)+'::' # prefix for A values from phase
2560            cells = []
2561            cellESDs = []
2562            colLabels += [pfx+cellUlbl[i] for i in uniqCellIndx[pId]]
2563            colLabels += [pfx+'Vol']
2564            Types += (1+len(uniqCellIndx[pId]))*[wg.GRID_VALUE_FLOAT,]
2565            for name in histNames:
2566                covData = {
2567                    'varyList': [Dlookup.get(striphist(v),v) for v in data[name]['varyList']],
2568                    'covMatrix': data[name]['covMatrix']
2569                    }
2570                A = RecpCellTerms[pId][:] # make copy of starting A values
2571                # update with refined values
2572                for i in range(6):
2573                    var = str(pId)+'::A'+str(i)
2574                    if var in ESDlookup:
2575                        val = data[name]['newCellDict'][ESDlookup[var]][1] # get refined value
2576                        A[i] = val # override with updated value
2577                # apply symmetry
2578                Albls = [pfx+'A'+str(i) for i in range(6)]
2579                cellDict = dict(zip(Albls,A))
2580                A,zeros = G2stIO.cellFill(pfx,SGdata[pId],cellDict,zeroDict[pId])
2581                # convert to direct cell & add only unique values to table
2582                c = G2lat.A2cell(A)
2583                vol = G2lat.calc_V(A)
2584                cE = G2stIO.getCellEsd(pfx,SGdata[pId],A,covData)
2585                cells += [[c[i] for i in uniqCellIndx[pId]]+[vol]]
2586                cellESDs += [[cE[i] for i in uniqCellIndx[pId]]+[cE[-1]]]
2587            colList += zip(*cells)
2588            colSigs += zip(*cellESDs)
2589    # sort out the variables in their selected order
2590    varcols = 0
2591    for d in posdict.itervalues():
2592        varcols = max(varcols,max(d.keys())+1)
2593    # get labels for each column
2594    for i in range(varcols):
2595        lbl = ''
2596        for h in VaryListChanges:
2597            if posdict[h].get(i):
2598                if posdict[h].get(i) in lbl: continue
2599                if lbl != "": lbl += '/'
2600                lbl += posdict[h].get(i)
2601        colLabels.append(lbl)
2602    Types += varcols*[wg.GRID_VALUE_FLOAT]
2603    vals = []
2604    esds = []
2605    varsellist = None        # will be a list of variable names in the order they are selected to appear
2606    # tabulate values for each hist, leaving None for blank columns
2607    for name in histNames:
2608        if name in posdict:
2609            varsellist = [posdict[name].get(i) for i in range(varcols)]
2610            # translate variable names to how they will be used in the headings
2611            vs = [striphist(v,'*') for v in data[name]['varyList']]
2612            # determine the index for each column (or None) in the data[]['variables'] and ['sig'] lists
2613            sellist = [vs.index(v) if v is not None else None for v in varsellist]
2614            #sellist = [i if striphist(v,'*') in varsellist else None for i,v in enumerate(data[name]['varyList'])]
2615        if not varsellist: raise Exception()
2616        vals.append([data[name]['variables'][s] if s is not None else None for s in sellist])
2617        esds.append([data[name]['sig'][s] if s is not None else None for s in sellist])
2618        #GSASIIpath.IPyBreak()
2619    colList += zip(*vals)
2620    colSigs += zip(*esds)
2621               
2622    # tabulate constrained variables, removing histogram numbers if needed
2623    # from parameter label
2624    depValDict = {}
2625    depSigDict = {}
2626    for name in histNames:
2627        for var in data[name].get('depParmDict',{}):
2628            val,sig = data[name]['depParmDict'][var]
2629            svar = striphist(var,'*')
2630            if svar not in depValDict:
2631               depValDict[svar] = [val]
2632               depSigDict[svar] = [sig]
2633            else:
2634               depValDict[svar].append(val)
2635               depSigDict[svar].append(sig)
2636    # add the dependent constrained variables to the table
2637    for var in sorted(depValDict):
2638        if len(depValDict[var]) != len(histNames): continue
2639        colLabels.append(var)
2640        Types += [wg.GRID_VALUE_FLOAT,]
2641        colSigs += [depSigDict[var]]
2642        colList += [depValDict[var]]
2643
2644    # add atom parameters to table
2645    colLabels += atomLookup.keys()
2646    Types += len(atomLookup)*[wg.GRID_VALUE_FLOAT]
2647    for parm in sorted(atomLookup):
2648        colList += [[data[name]['newAtomDict'][atomLookup[parm]][1] for name in histNames]]
2649        if atomLookup[parm] in data[histNames[0]]['varyList']:
2650            col = data[histNames[0]]['varyList'].index(atomLookup[parm])
2651            colSigs += [[data[name]['sig'][col] for name in histNames]]
2652        else:
2653            colSigs += [None] # should not happen
2654    # evaluate Pseudovars, their ESDs and add them to grid
2655    for expr in Controls['SeqPseudoVars']:
2656        obj = Controls['SeqPseudoVars'][expr]
2657        calcobj = G2obj.ExpressionCalcObj(obj)
2658        valList = []
2659        esdList = []
2660        for seqnum,name in enumerate(histNames):
2661            sigs = data[name]['sig']
2662            G2mv.InitVars()
2663            parmDict = data[name].get('parmDict')
2664            badVary = data[name].get('badVary',[])
2665            constraintInfo = data[name].get('constraintInfo',[[],[],{},[],seqnum])
2666            groups,parmlist,constrDict,fixedList,ihst = constraintInfo
2667            varyList = data[name]['varyList']
2668            parmDict = data[name]['parmDict']
2669            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict,SeqHist=ihst)
2670            derivs = np.array(
2671                [EvalPSvarDeriv(calcobj,parmDict.copy(),sampleDict[name],var,ESD)
2672                 for var,ESD in zip(varyList,sigs)]
2673                )
2674            esdList.append(np.sqrt(
2675                np.inner(derivs,np.inner(data[name]['covMatrix'],derivs.T))
2676                ))
2677            PSvarDict = parmDict.copy()
2678            PSvarDict.update(sampleDict[name])
2679            UpdateParmDict(PSvarDict)
2680            calcobj.UpdateDict(PSvarDict)
2681            valList.append(calcobj.EvalExpression())
2682        if not esdList:
2683            esdList = None
2684        colList += [valList]
2685        colSigs += [esdList]
2686        colLabels += [expr]
2687        Types += [wg.GRID_VALUE_FLOAT,]
2688    #---- table build done -------------------------------------------------------------
2689
2690    # Make dict needed for creating & editing pseudovars (PSvarDict).
2691    name = histNames[0]
2692    parmDict = data[name].get('parmDict')
2693    PSvarDict = parmDict.copy()
2694    PSvarDict.update(sampleParms)
2695    UpdateParmDict(PSvarDict)
2696    # Also dicts of dependent (depVarDict) & independent vars (indepVarDict)
2697    # for Parametric fitting from the data table
2698    parmDict = dict(zip(colLabels,zip(*colList)[0])) # scratch dict w/all values in table
2699    parmDict.update(
2700        {var:val for var,val in data[name].get('newCellDict',{}).values()} #  add varied reciprocal cell terms
2701    )
2702    name = histNames[0]
2703
2704    #******************************************************************************
2705    # create a set of values for example evaluation of pseudovars and
2706    # this does not work for refinements that have differing numbers of variables.
2707    #raise Exception
2708    indepVarDict = {}     #  values in table w/o ESDs
2709    depVarDict = {}
2710    for i,var in enumerate(colLabels):
2711        if var == 'Use': continue
2712        if colList[i][0] is None:
2713            val,sig = firstValueDict.get(var,[None,None])
2714        elif colSigs[i]:
2715            val,sig = colList[i][0],colSigs[i][0]
2716        else:
2717            val,sig = colList[i][0],None
2718        if val is None:
2719            continue
2720        elif sig is None:
2721            indepVarDict[var] = val
2722        elif striphist(var) not in Dlookup:
2723            depVarDict[var] = val
2724    # add recip cell coeff. values
2725    depVarDict.update({var:val for var,val in data[name].get('newCellDict',{}).values()})
2726
2727    G2frame.dataDisplay = G2G.GSGrid(parent=G2frame.dataFrame)
2728    G2frame.SeqTable = G2G.Table(
2729        [list(c) for c in zip(*colList)],     # convert from columns to rows
2730        colLabels=colLabels,rowLabels=histNames,types=Types)
2731    G2frame.dataDisplay.SetTable(G2frame.SeqTable, True)
2732    #G2frame.dataDisplay.EnableEditing(False)
2733    # make all but first column read-only
2734    for c in range(1,len(colLabels)):
2735        for r in range(nRows):
2736            G2frame.dataDisplay.SetCellReadOnly(r,c)
2737    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_LEFT_DCLICK, PlotSelect)
2738    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_RIGHT_CLICK, SetLabelString)
2739    G2frame.dataDisplay.SetRowLabelSize(8*len(histNames[0]))       #pretty arbitrary 8
2740    G2frame.dataDisplay.SetMargins(0,0)
2741    G2frame.dataDisplay.AutoSizeColumns(True)
2742    if prevSize:
2743        G2frame.dataDisplay.SetSize(prevSize)
2744    else:
2745        G2frame.dataFrame.setSizePosLeft([700,350])
2746    # highlight unconverged shifts
2747    if histNames[0][:4] not in ['SASD','IMG ']:
2748        for row,name in enumerate(histNames):
2749            deltaChi = G2frame.SeqTable.GetValue(row,deltaChiCol)
2750            if deltaChi > 10.:
2751                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,0,0))
2752            elif deltaChi > 1.0:
2753                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,255,0))
2754    G2frame.dataDisplay.InstallGridToolTip(GridSetToolTip,GridColLblToolTip)
2755    G2frame.dataDisplay.SendSizeEvent() # resize needed on mac
2756    G2frame.dataDisplay.Refresh() # shows colored text on mac
2757   
2758################################################################################
2759#####  Main PWDR panel
2760################################################################################           
2761       
2762def UpdatePWHKPlot(G2frame,kind,item):
2763    '''Called when the histogram main tree entry is called. Displays the
2764    histogram weight factor, refinement statistics for the histogram
2765    and the range of data for a simulation.
2766
2767    Also invokes a plot of the histogram.
2768    '''
2769    def onEditSimRange(event):
2770        'Edit simulation range'
2771        inp = [
2772            min(data[1][0]),
2773            max(data[1][0]),
2774            None
2775            ]
2776        inp[2] = (inp[1] - inp[0])/(len(data[1][0])-1.)
2777        names = ('start angle', 'end angle', 'step size')
2778        dictlst = [inp] * len(inp)
2779        elemlst = range(len(inp))
2780        dlg = G2G.ScrolledMultiEditor(
2781            G2frame,[inp] * len(inp), range(len(inp)), names,
2782            header='Edit simulation range',
2783            minvals=(0.001,0.001,0.0001),
2784            maxvals=(180.,180.,.1),
2785            )
2786        dlg.CenterOnParent()
2787        val = dlg.ShowModal()
2788        dlg.Destroy()
2789        if val != wx.ID_OK: return
2790        if inp[0] > inp[1]:
2791            end,start,step = inp
2792        else:               
2793            start,end,step = inp
2794        step = abs(step)
2795        N = int((end-start)/step)+1
2796        newdata = np.linspace(start,end,N,True)
2797        if len(newdata) < 2: return # too small a range - reject
2798        data[1] = [newdata,np.zeros_like(newdata),np.ones_like(newdata),
2799            np.zeros_like(newdata),np.zeros_like(newdata),np.zeros_like(newdata)]
2800        Tmin = newdata[0]
2801        Tmax = newdata[-1]
2802        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,item,'Limits'),
2803            [(Tmin,Tmax),[Tmin,Tmax]])
2804        UpdatePWHKPlot(G2frame,kind,item) # redisplay data screen
2805
2806    def OnPlot3DHKL(event):
2807        refList = data[1]['RefList']
2808        FoMax = np.max(refList.T[8+Super])
2809        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
2810        Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
2811        Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
2812        controls = {'Type' : 'Fosq','Iscale' : False,'HKLmax' : Hmax,'HKLmin' : Hmin,
2813            'FoMax' : FoMax,'Scale' : 1.0,'Drawing':{'viewPoint':[Vpoint,[]],'default':Vpoint[:],
2814            'backColor':[0,0,0],'depthFog':False,'Zclip':10.0,'cameraPos':10.,'Zstep':0.05,
2815            'Scale':1.0,'oldxy':[],'viewDir':[1,0,0]},'Super':Super,'SuperVec':SuperVec}
2816        G2plt.Plot3DSngl(G2frame,newPlot=True,Data=controls,hklRef=refList,Title=phaseName)
2817       
2818    def OnPlotAll3DHKL(event):
2819        choices = GetPatternTreeDataNames(G2frame,['HKLF',])
2820        dlg = G2G.G2MultiChoiceDialog(G2frame, 'Select reflection sets to plot',
2821            'Use data',choices)
2822        try:
2823            if dlg.ShowModal() == wx.ID_OK:
2824                refNames = [choices[i] for i in dlg.GetSelections()]
2825            else:
2826                return
2827        finally:
2828            dlg.Destroy()
2829        refList = np.zeros(0)
2830        for name in refNames:
2831            Id = GetPatternTreeItemId(G2frame,G2frame.root, name)
2832            reflData = G2frame.PatternTree.GetItemPyData(Id)[1]
2833            if len(refList):
2834                refList = np.concatenate((refList,reflData['RefList']))
2835            else:
2836                refList = reflData['RefList']
2837           
2838        FoMax = np.max(refList.T[8+Super])
2839        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
2840        Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
2841        Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
2842        controls = {'Type' : 'Fosq','Iscale' : False,'HKLmax' : Hmax,'HKLmin' : Hmin,
2843            'FoMax' : FoMax,'Scale' : 1.0,'Drawing':{'viewPoint':[Vpoint,[]],'default':Vpoint[:],
2844            'backColor':[0,0,0],'depthFog':False,'Zclip':10.0,'cameraPos':10.,'Zstep':0.05,
2845            'Scale':1.0,'oldxy':[],'viewDir':[1,0,0]},'Super':Super,'SuperVec':SuperVec}
2846        G2plt.Plot3DSngl(G2frame,newPlot=True,Data=controls,hklRef=refList,Title=phaseName)
2847       
2848       
2849    def OnErrorAnalysis(event):
2850        G2plt.PlotDeltSig(G2frame,kind)
2851       
2852    def OnWtFactor(event):
2853        try:
2854            val = float(wtval.GetValue())
2855        except ValueError:
2856            val = data[0]['wtFactor']
2857        data[0]['wtFactor'] = val
2858        wtval.SetValue('%.3f'%(val))
2859       
2860    def OnCompression(event):
2861        data[0] = int(comp.GetValue())
2862       
2863    def onCopyPlotCtrls(event):
2864        '''Respond to menu item to copy multiple sections from a histogram.
2865        Need this here to pass on the G2frame object.
2866        '''
2867        G2pdG.CopyPlotCtrls(G2frame)
2868
2869    def onCopySelectedItems(event):
2870        '''Respond to menu item to copy multiple sections from a histogram.
2871        Need this here to pass on the G2frame object.
2872        '''
2873        G2pdG.CopySelectedHistItems(G2frame)
2874           
2875    data = G2frame.PatternTree.GetItemPyData(item)
2876#patches
2877    if 'wtFactor' not in data[0]:
2878        data[0] = {'wtFactor':1.0}
2879#    if kind == 'PWDR' and 'Compression' not in data[0]:
2880#        data[0]['Compression'] = 1
2881    #if isinstance(data[1],list) and kind == 'HKLF':
2882    if 'list' in str(type(data[1])) and kind == 'HKLF':
2883        RefData = {'RefList':[],'FF':[]}
2884        for ref in data[1]:
2885            RefData['RefList'].append(ref[:11]+[ref[13],])
2886            RefData['FF'].append(ref[14])
2887        data[1] = RefData
2888        G2frame.PatternTree.SetItemPyData(item,data)
2889#end patches
2890    if G2frame.dataDisplay:
2891        G2frame.dataDisplay.Destroy()
2892    if kind in ['PWDR','SASD']:
2893        SetDataMenuBar(G2frame,G2frame.dataFrame.PWDRMenu)
2894        G2frame.dataFrame.Bind(wx.EVT_MENU, OnErrorAnalysis, id=wxID_PWDANALYSIS)
2895        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopySelectedItems, id=wxID_PWDCOPY)
2896        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopyPlotCtrls, id=wxID_PLOTCTRLCOPY)
2897    elif kind in ['HKLF',]:
2898        SetDataMenuBar(G2frame,G2frame.dataFrame.HKLFMenu)
2899        G2frame.dataFrame.Bind(wx.EVT_MENU, OnErrorAnalysis, id=wxID_PWDANALYSIS)
2900        G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlot3DHKL, id=wxID_PWD3DHKLPLOT)
2901        G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotAll3DHKL, id=wxID_3DALLHKLPLOT)
2902#        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopySelectedItems, id=wxID_PWDCOPY)
2903    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
2904   
2905    mainSizer = wx.BoxSizer(wx.VERTICAL)
2906    mainSizer.Add((5,5),)
2907    wtSizer = wx.BoxSizer(wx.HORIZONTAL)
2908    wtSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Weight factor: '),0,WACV)
2909    wtval = wx.TextCtrl(G2frame.dataDisplay,-1,'%.3f'%(data[0]['wtFactor']),style=wx.TE_PROCESS_ENTER)
2910    wtval.Bind(wx.EVT_TEXT_ENTER,OnWtFactor)
2911    wtval.Bind(wx.EVT_KILL_FOCUS,OnWtFactor)
2912    wtSizer.Add(wtval,0,WACV)
2913#    if kind == 'PWDR':         #possible future compression feature; NB above patch as well
2914#        wtSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Compression factor: '),0,WACV)
2915#        choice = ['1','2','3','4','5','6']
2916#        comp = wx.ComboBox(parent=G2frame.dataDisplay,choices=choice,
2917#            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2918#        comp.SetValue(str(data[0]['Compression']))
2919#        comp.Bind(wx.EVT_COMBOBOX, OnCompression)
2920#        wtSizer.Add(comp,0,WACV)
2921    mainSizer.Add(wtSizer)
2922    if data[0].get('Dummy'):
2923        simSizer = wx.BoxSizer(wx.HORIZONTAL)
2924        Tmin = min(data[1][0])
2925        Tmax = max(data[1][0])
2926        num = len(data[1][0])
2927        step = (Tmax - Tmin)/(num-1)
2928        t = u'2\u03b8' # 2theta
2929        lbl =  u'Simulation range: {:.2f} to {:.2f} {:s}\nwith {:.4f} steps ({:d} points)'
2930        lbl += u'\n(Edit range resets observed intensities).'
2931        lbl = lbl.format(Tmin,Tmax,t,step,num)
2932        simSizer.Add(wx.StaticText(G2frame.dataDisplay,wx.ID_ANY,lbl),
2933                    0,WACV)
2934        but = wx.Button(G2frame.dataDisplay,wx.ID_ANY,"Edit range")
2935        but.Bind(wx.EVT_BUTTON,onEditSimRange)
2936        simSizer.Add(but,0,WACV)
2937        mainSizer.Add(simSizer)
2938    if 'Nobs' in data[0]:
2939        mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
2940            ' Data residual wR: %.3f%% on %d observations'%(data[0]['wR'],data[0]['Nobs'])))
2941        for value in data[0]:
2942            if 'Nref' in value:
2943                mainSizer.Add((5,5),)
2944                pfx = value.split('Nref')[0]
2945                name = data[0].get(pfx.split(':')[0]+'::Name','?')
2946                mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' For phase '+name+':'))
2947                mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
2948                    u' Unweighted phase residuals RF\u00b2: %.3f%%, RF: %.3f%% on %d reflections  '% \
2949                    (data[0][pfx+'Rf^2'],data[0][pfx+'Rf'],data[0][value])))
2950    mainSizer.Add((5,5),)
2951    mainSizer.Layout()   
2952    G2frame.dataDisplay.SetSizer(mainSizer)
2953    Size = mainSizer.Fit(G2frame.dataFrame)
2954    Size[1] += 10
2955    G2frame.dataFrame.setSizePosLeft(Size)
2956    G2frame.PatternTree.SetItemPyData(item,data)
2957    if kind in ['PWDR','SASD']:
2958        if 'xylim' in dir(G2frame):
2959            NewPlot = False
2960        else:
2961
2962            NewPlot = True
2963        G2plt.PlotPatterns(G2frame,plotType=kind,newPlot=NewPlot)
2964    elif kind == 'HKLF':
2965        Name = G2frame.PatternTree.GetItemText(item)
2966        phaseName = G2pdG.IsHistogramInAnyPhase(G2frame,Name)
2967        if phaseName:
2968            pId = GetPatternTreeItemId(G2frame,G2frame.root,'Phases')
2969            phaseId =  GetPatternTreeItemId(G2frame,pId,phaseName)
2970            General = G2frame.PatternTree.GetItemPyData(phaseId)['General']
2971            Super = General.get('Super',0)
2972            SuperVec = General.get('SuperVec',[])
2973        else:
2974            Super = 0
2975            SuperVec = []       
2976        refList = data[1]['RefList']
2977        FoMax = np.max(refList.T[5+data[1].get('Super',0)])
2978        page = G2frame.G2plotNB.nb.GetSelection()
2979        tab = ''
2980        if page >= 0:
2981            tab = G2frame.G2plotNB.nb.GetPageText(page)
2982        if '3D' in tab:
2983            Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
2984            Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
2985            Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
2986            Page = G2frame.G2plotNB.nb.GetPage(page)
2987            controls = Page.controls
2988            G2plt.Plot3DSngl(G2frame,newPlot=False,Data=controls,hklRef=refList,Title=phaseName)
2989        else:
2990            controls = {'Type' : 'Fo','ifFc' : True,     
2991                'HKLmax' : [int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))],
2992                'HKLmin' : [int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))],
2993                'FoMax' : FoMax,'Zone' : '001','Layer' : 0,'Scale' : 1.0,'Super':Super,'SuperVec':SuperVec}
2994            G2plt.PlotSngl(G2frame,newPlot=True,Data=controls,hklRef=refList)
2995                 
2996################################################################################
2997#####  Pattern tree routines
2998################################################################################           
2999       
3000def GetPatternTreeDataNames(G2frame,dataTypes):
3001    '''Needs a doc string
3002    '''
3003    names = []
3004    item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)       
3005    while item:
3006        name = G2frame.PatternTree.GetItemText(item)
3007        if name[:4] in dataTypes:
3008            names.append(name)
3009        item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
3010    return names
3011                         
3012def GetPatternTreeItemId(G2frame, parentId, itemText):
3013    '''Needs a doc string
3014    '''
3015    item, cookie = G2frame.PatternTree.GetFirstChild(parentId)
3016    while item:
3017        if G2frame.PatternTree.GetItemText(item) == itemText:
3018            return item
3019        item, cookie = G2frame.PatternTree.GetNextChild(parentId, cookie)
3020    return 0               
3021
3022def MovePatternTreeToGrid(G2frame,item):
3023    '''Called from GSASII.OnPatternTreeSelChanged when a item is selected on the tree
3024    '''
3025    pickName = G2frame.PatternTree.GetItemText(item)
3026    if G2frame.PickIdText == pickName:
3027        return
3028   
3029    oldPage = None # will be set later if already on a Phase item
3030    if G2frame.dataFrame:
3031        SetDataMenuBar(G2frame)
3032        if G2frame.dataFrame.GetLabel() == 'Comments':
3033            try:
3034                data = [G2frame.dataDisplay.GetValue()]
3035                G2frame.dataDisplay.Clear() 
3036                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Comments')
3037                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
3038            except:     #clumsy but avoids dead window problem when opening another project
3039                pass
3040        elif G2frame.dataFrame.GetLabel() == 'Notebook':
3041            try:
3042                data = [G2frame.dataDisplay.GetValue()]
3043                G2frame.dataDisplay.Clear() 
3044                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Notebook')
3045                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
3046            except:     #clumsy but avoids dead window problem when opening another project
3047                pass
3048        elif 'Phase Data for' in G2frame.dataFrame.GetLabel():
3049            if G2frame.dataDisplay: 
3050                oldPage = G2frame.dataDisplay.GetSelection()
3051        G2frame.dataFrame.Clear()
3052        G2frame.dataFrame.SetLabel('')
3053    else:
3054        #create the frame for the data item window
3055        G2frame.dataFrame = DataFrame(parent=G2frame.mainPanel,frame=G2frame)
3056        G2frame.dataFrame.PhaseUserSize = None
3057       
3058    G2frame.dataFrame.Raise()           
3059    G2frame.PickId = item
3060    G2frame.PickIdText = None
3061    parentID = G2frame.root
3062    #for i in G2frame.ExportPattern: i.Enable(False)
3063    defWid = [250,150]
3064    if item != G2frame.root:
3065        parentID = G2frame.PatternTree.GetItemParent(item)
3066    if G2frame.PatternTree.GetItemParent(item) == G2frame.root:
3067        G2frame.PatternId = item
3068        if G2frame.PatternTree.GetItemText(item) == 'Notebook':
3069            SetDataMenuBar(G2frame,G2frame.dataFrame.DataNotebookMenu)
3070            G2frame.PatternId = 0
3071            #for i in G2frame.ExportPattern: i.Enable(False)
3072            data = G2frame.PatternTree.GetItemPyData(item)
3073            UpdateNotebook(G2frame,data)
3074        elif G2frame.PatternTree.GetItemText(item) == 'Controls':
3075            G2frame.PatternId = 0
3076            #for i in G2frame.ExportPattern: i.Enable(False)
3077            data = G2frame.PatternTree.GetItemPyData(item)
3078            if not data:           #fill in defaults
3079                data = copy.copy(G2obj.DefaultControls)    #least squares controls
3080                G2frame.PatternTree.SetItemPyData(item,data)                             
3081            for i in G2frame.Refine: i.Enable(True)
3082            G2frame.EnableSeqRefineMenu()
3083            UpdateControls(G2frame,data)
3084        elif G2frame.PatternTree.GetItemText(item) == 'Sequential results':
3085            data = G2frame.PatternTree.GetItemPyData(item)
3086            UpdateSeqResults(G2frame,data)
3087        elif G2frame.PatternTree.GetItemText(item) == 'Covariance':
3088            data = G2frame.PatternTree.GetItemPyData(item)
3089            G2frame.dataFrame.setSizePosLeft(defWid)
3090            text = ''
3091            if 'Rvals' in data:
3092                Nvars = len(data['varyList'])
3093                Rvals = data['Rvals']
3094                text = '\nFinal residuals: \nwR = %.3f%% \nchi**2 = %.1f \nGOF = %.2f'%(Rvals['Rwp'],Rvals['chisq'],Rvals['GOF'])
3095                text += '\nNobs = %d \nNvals = %d'%(Rvals['Nobs'],Nvars)
3096                if 'lamMax' in Rvals:
3097                    text += '\nlog10 MaxLambda = %.1f'%(np.log10(Rvals['lamMax']))
3098            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
3099                value='See plot window for covariance display'+text,style=wx.TE_MULTILINE)
3100            G2plt.PlotCovariance(G2frame,data)
3101        elif G2frame.PatternTree.GetItemText(item) == 'Constraints':
3102            data = G2frame.PatternTree.GetItemPyData(item)
3103            G2cnstG.UpdateConstraints(G2frame,data)
3104        elif G2frame.PatternTree.GetItemText(item) == 'Rigid bodies':
3105            data = G2frame.PatternTree.GetItemPyData(item)
3106            G2cnstG.UpdateRigidBodies(G2frame,data)
3107        elif G2frame.PatternTree.GetItemText(item) == 'Restraints':
3108            data = G2frame.PatternTree.GetItemPyData(item)
3109            Phases = G2frame.GetPhaseData()
3110            phase = ''
3111            phaseName = ''
3112            if Phases:
3113                phaseName = Phases.keys()[0]
3114            G2frame.dataFrame.setSizePosLeft(defWid)
3115            G2restG.UpdateRestraints(G2frame,data,Phases,phaseName)
3116        elif 'IMG' in G2frame.PatternTree.GetItemText(item):
3117            G2frame.Image = item
3118            G2frame.dataFrame.SetTitle('Image Data')
3119            data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId( \
3120                G2frame,item,'Image Controls'))
3121            G2imG.UpdateImageData(G2frame,data)
3122            G2plt.PlotImage(G2frame,newPlot=True)
3123        elif 'PKS' in G2frame.PatternTree.GetItemText(item):
3124            G2plt.PlotPowderLines(G2frame)
3125        elif 'PWDR' in G2frame.PatternTree.GetItemText(item):
3126            #for i in G2frame.ExportPattern: i.Enable(True)
3127            if G2frame.EnablePlot:
3128                UpdatePWHKPlot(G2frame,'PWDR',item)
3129        elif 'SASD' in G2frame.PatternTree.GetItemText(item):
3130            #for i in G2frame.ExportPattern: i.Enable(True)
3131            if G2frame.EnablePlot:
3132                UpdatePWHKPlot(G2frame,'SASD',item)
3133        elif 'HKLF' in G2frame.PatternTree.GetItemText(item):
3134            G2frame.Sngl = True
3135            UpdatePWHKPlot(G2frame,'HKLF',item)
3136        elif 'PDF' in G2frame.PatternTree.GetItemText(item):
3137            G2frame.PatternId = item
3138            for i in G2frame.ExportPDF: i.Enable(True)
3139            G2plt.PlotISFG(G2frame,type='S(Q)')
3140        elif G2frame.PatternTree.GetItemText(item) == 'Phases':
3141            G2frame.dataFrame.setSizePosLeft(defWid)
3142            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
3143                value='Select one phase to see its parameters')           
3144    elif 'I(Q)' in G2frame.PatternTree.GetItemText(item):
3145        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3146        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
3147        G2pdG.UpdatePDFGrid(G2frame,data)
3148        G2plt.PlotISFG(G2frame,type='I(Q)',newPlot=True)
3149    elif 'S(Q)' in G2frame.PatternTree.GetItemText(item):
3150        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3151        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
3152        G2pdG.UpdatePDFGrid(G2frame,data)
3153        G2plt.PlotISFG(G2frame,type='S(Q)',newPlot=True)
3154    elif 'F(Q)' in G2frame.PatternTree.GetItemText(item):
3155        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3156        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
3157        G2pdG.UpdatePDFGrid(G2frame,data)
3158        G2plt.PlotISFG(G2frame,type='F(Q)',newPlot=True)
3159    elif 'G(R)' in G2frame.PatternTree.GetItemText(item):
3160        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3161        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
3162        G2pdG.UpdatePDFGrid(G2frame,data)
3163        G2plt.PlotISFG(G2frame,type='G(R)',newPlot=True)           
3164    elif G2frame.PatternTree.GetItemText(parentID) == 'Phases':
3165        data = G2frame.PatternTree.GetItemPyData(item)
3166        G2phG.UpdatePhaseData(G2frame,item,data,oldPage)
3167    elif G2frame.PatternTree.GetItemText(item) == 'Comments':
3168        SetDataMenuBar(G2frame,G2frame.dataFrame.DataCommentsMenu)
3169        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3170        data = G2frame.PatternTree.GetItemPyData(item)
3171        UpdateComments(G2frame,data)
3172    elif G2frame.PatternTree.GetItemText(item) == 'Image Controls':
3173        G2frame.dataFrame.SetTitle('Image Controls')
3174        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3175        masks = G2frame.PatternTree.GetItemPyData(
3176            GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
3177        data = G2frame.PatternTree.GetItemPyData(item)
3178        G2imG.UpdateImageControls(G2frame,data,masks)
3179        G2plt.PlotImage(G2frame)
3180    elif G2frame.PatternTree.GetItemText(item) == 'Masks':
3181        G2frame.dataFrame.SetTitle('Masks')
3182        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3183        data = G2frame.PatternTree.GetItemPyData(item)
3184        G2imG.UpdateMasks(G2frame,data)
3185        G2plt.PlotImage(G2frame)
3186    elif G2frame.PatternTree.GetItemText(item) == 'Stress/Strain':
3187        G2frame.dataFrame.SetTitle('Stress/Strain')
3188        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3189        data = G2frame.PatternTree.GetItemPyData(item)
3190        G2plt.PlotImage(G2frame)
3191        G2plt.PlotStrain(G2frame,data,newPlot=True)
3192        G2imG.UpdateStressStrain(G2frame,data)
3193    elif G2frame.PatternTree.GetItemText(item) == 'PDF Controls':
3194        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3195        for i in G2frame.ExportPDF: i.Enable(True)
3196        data = G2frame.PatternTree.GetItemPyData(item)
3197        G2pdG.UpdatePDFGrid(G2frame,data)
3198        G2plt.PlotISFG(G2frame,type='I(Q)')
3199        G2plt.PlotISFG(G2frame,type='S(Q)')
3200        G2plt.PlotISFG(G2frame,type='F(Q)')
3201        G2plt.PlotISFG(G2frame,type='G(R)')
3202    elif G2frame.PatternTree.GetItemText(item) == 'Peak List':
3203        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3204        for i in G2frame.ExportPeakList: i.Enable(True)
3205        data = G2frame.PatternTree.GetItemPyData(item)
3206#patch
3207        if 'list' in str(type(data)):
3208            data = {'peaks':data,'sigDict':{}}
3209            G2frame.PatternTree.SetItemPyData(item,data)
3210#end patch
3211        G2pdG.UpdatePeakGrid(G2frame,data)
3212        G2plt.PlotPatterns(G2frame)
3213    elif G2frame.PatternTree.GetItemText(item) == 'Background':
3214        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3215        data = G2frame.PatternTree.GetItemPyData(item)
3216        G2pdG.UpdateBackground(G2frame,data)
3217        G2plt.PlotPatterns(G2frame)
3218    elif G2frame.PatternTree.GetItemText(item) == 'Limits':
3219        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3220        datatype = G2frame.PatternTree.GetItemText(G2frame.PatternId)[:4]
3221        data = G2frame.PatternTree.GetItemPyData(item)
3222        G2pdG.UpdateLimitsGrid(G2frame,data,datatype)
3223        G2plt.PlotPatterns(G2frame,plotType=datatype)
3224    elif G2frame.PatternTree.GetItemText(item) == 'Instrument Parameters':
3225        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3226        data = G2frame.PatternTree.GetItemPyData(item)[0]
3227        G2pdG.UpdateInstrumentGrid(G2frame,data)
3228        if 'P' in data['Type'][0]:          #powder data only
3229            G2plt.PlotPeakWidths(G2frame)
3230    elif G2frame.PatternTree.GetItemText(item) == 'Models':
3231        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3232        data = G2frame.PatternTree.GetItemPyData(item)
3233        G2pdG.UpdateModelsGrid(G2frame,data)
3234        G2plt.PlotPatterns(G2frame,plotType='SASD')
3235        if len(data['Size']['Distribution']):
3236            G2plt.PlotSASDSizeDist(G2frame)
3237    elif G2frame.PatternTree.GetItemText(item) == 'Substances':
3238        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3239        data = G2frame.PatternTree.GetItemPyData(item)
3240        G2pdG.UpdateSubstanceGrid(G2frame,data)
3241    elif G2frame.PatternTree.GetItemText(item) == 'Sample Parameters':
3242        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3243        data = G2frame.PatternTree.GetItemPyData(item)
3244        datatype = G2frame.PatternTree.GetItemPyData(G2frame.PatternId)[2][:4]
3245
3246        if 'Temperature' not in data:           #temp fix for old gpx files
3247            data = {'Scale':[1.0,True],'Type':'Debye-Scherrer','Absorption':[0.0,False],'DisplaceX':[0.0,False],
3248                'DisplaceY':[0.0,False],'Diffuse':[],'Temperature':300.,'Pressure':1.0,
3249                    'FreePrm1':0.,'FreePrm2':0.,'FreePrm3':0.,
3250                    'Gonio. radius':200.0}
3251            G2frame.PatternTree.SetItemPyData(item,data)
3252   
3253        G2pdG.UpdateSampleGrid(G2frame,data)
3254        G2plt.PlotPatterns(G2frame,plotType=datatype)
3255    elif G2frame.PatternTree.GetItemText(item) == 'Index Peak List':
3256        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3257        for i in G2frame.ExportPeakList: i.Enable(True)
3258        data = G2frame.PatternTree.GetItemPyData(item)
3259#patch
3260        if len(data) != 2:
3261            data = [data,[]]
3262            G2frame.PatternTree.SetItemPyData(item,data)
3263#end patch
3264        G2pdG.UpdateIndexPeaksGrid(G2frame,data)
3265        if 'PKS' in G2frame.PatternTree.GetItemText(G2frame.PatternId):
3266            G2plt.PlotPowderLines(G2frame)
3267        else:
3268            G2plt.PlotPatterns(G2frame)
3269    elif G2frame.PatternTree.GetItemText(item) == 'Unit Cells List':
3270        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3271        data = G2frame.PatternTree.GetItemPyData(item)
3272        if not data:
3273            data.append([0,0.0,4,25.0,0,'P1',1,1,1,90,90,90]) #zero error flag, zero value, max Nc/No, start volume
3274            data.append([0,0,0,0,0,0,0,0,0,0,0,0,0,0])      #Bravais lattice flags
3275            data.append([])                                 #empty cell list
3276            data.append([])                                 #empty dmin
3277            data.append({})                                 #empty superlattice stuff
3278            G2frame.PatternTree.SetItemPyData(item,data)                             
3279#patch
3280        if len(data) < 5:
3281            data.append({'Use':False,'ModVec':[0,0,0.1],'maxH':1,'ssSymb':''})                                 #empty superlattice stuff
3282            G2frame.PatternTree.SetItemPyData(item,data) 
3283#end patch
3284        G2pdG.UpdateUnitCellsGrid(G2frame,data)
3285        if 'PKS' in G2frame.PatternTree.GetItemText(G2frame.PatternId):
3286            G2plt.PlotPowderLines(G2frame)
3287        else:
3288            G2plt.PlotPatterns(G2frame)
3289    elif G2frame.PatternTree.GetItemText(item) == 'Reflection Lists':   #powder reflections
3290        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3291        data = G2frame.PatternTree.GetItemPyData(item)
3292        G2frame.RefList = ''
3293        if len(data):
3294            G2frame.RefList = data.keys()[0]
3295        G2pdG.UpdateReflectionGrid(G2frame,data)
3296        G2plt.PlotPatterns(G2frame)
3297    elif G2frame.PatternTree.GetItemText(item) == 'Reflection List':    #HKLF reflections
3298        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3299        name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
3300        data = G2frame.PatternTree.GetItemPyData(G2frame.PatternId)
3301        G2pdG.UpdateReflectionGrid(G2frame,data,HKLF=True,Name=name)
3302
3303    if G2frame.PickId:
3304        G2frame.PickIdText = G2frame.GetTreeItemsList(G2frame.PickId)
3305    G2frame.dataFrame.Raise()
3306
3307def SetDataMenuBar(G2frame,menu=None):
3308    '''Set the menu for the data frame. On the Mac put this
3309    menu for the data tree window instead.
3310
3311    Note that data frame items do not have menus, for these (menu=None)
3312    display a blank menu or on the Mac display the standard menu for
3313    the data tree window.
3314    '''
3315    if sys.platform == "darwin":
3316        if menu is None:
3317            G2frame.SetMenuBar(G2frame.GSASIIMenu)
3318        else:
3319            G2frame.SetMenuBar(menu)
3320    else:
3321        if menu is None:
3322            G2frame.dataFrame.SetMenuBar(G2frame.dataFrame.BlankMenu)
3323        else:
3324            G2frame.dataFrame.SetMenuBar(menu)
3325
3326def HowDidIgetHere():
3327    '''Show a traceback with calls that brought us to the current location.
3328    Used for debugging.
3329    '''
3330    import traceback
3331    print 70*'*'   
3332    for i in traceback.format_list(traceback.extract_stack()[:-1]): print(i.strip.rstrip())
3333    print 70*'*'   
3334       
Note: See TracBrowser for help on using the repository browser.