source: trunk/GSASIIgrid.py @ 2110

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

add menu item for global setting of wave vary parameters - TBD
split StructureFactorDeriv? over twins & incommensurate
do block refl for the nontwin/powder one

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