source: trunk/GSASIIgrid.py @ 1660

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

fix crash in viewing PWDR data Limits
add new item to PWDR data ('Super':True/False?)
fix plotting problem with reflection tic mark positions
add recovery after bas LS fails making new copy of gpx file

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