source: trunk/GSASIIgrid.py @ 1930

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

allow exclude atoms from H-atom position calcs.
hydrogen add complete(?) - needs testing
new hydrogen update implemented

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