source: trunk/GSASIIgrid.py @ 1688

Last change on this file since 1688 was 1688, checked in by toby, 7 years ago

allow non-identical sequential refinement when copynext is not used (still bugs on table display); implement tutorial downloads

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