source: trunk/GSASIIgrid.py @ 1699

Last change on this file since 1699 was 1699, checked in by vondreele, 10 years ago

rework the DData window - now shows only one histogram at a time. Eliminate the Show button, Show/Hide? menu item, etc. Show a ListBox? & SpinButton? for histogram selection
some more work on ss special positions

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 195.8 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIgrid - data display routines
3########### SVN repository information ###################
4# $Date: 2015-03-13 20:46:05 +0000 (Fri, 13 Mar 2015) $
5# $Author: vondreele $
6# $Revision: 1699 $
7# $URL: trunk/GSASIIgrid.py $
8# $Id: GSASIIgrid.py 1699 2015-03-13 20:46:05Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIgrid: Basic GUI routines*
12--------------------------------
13
14Note that a number of routines here should be moved to GSASIIctrls eventually, such as
15G2LoggedButton, EnumSelector, G2ChoiceButton, SingleFloatDialog, SingleStringDialog,
16MultiStringDialog, G2MultiChoiceDialog, G2SingleChoiceDialog, G2ColumnIDDialog, ItemSelector, GridFractionEditor
17
18Probably SGMessageBox, SymOpDialog, DisAglDialog, too.
19
20'''
21import wx
22import wx.grid as wg
23import wx.wizard as wz
24import wx.aui
25import wx.lib.scrolledpanel as wxscroll
26import time
27import copy
28import cPickle
29import sys
30import os
31import numpy as np
32import numpy.ma as ma
33import scipy.optimize as so
34import GSASIIpath
35GSASIIpath.SetVersionNumber("$Revision: 1699 $")
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,
70] = [wx.NewId() for item in range(6)]
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_DATADELETE, kind=wx.ITEM_NORMAL,text='Remove histograms',
1964            help='Remove histograms from use for this phase')
1965        self.PostfillDataMenu()
1966           
1967        # Phase / Atoms tab
1968        self.AtomsMenu = wx.MenuBar()
1969        self.PrefillDataMenu(self.AtomsMenu,helpType='Atoms')
1970        self.AtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1971        self.AtomEdit = wx.Menu(title='')
1972        self.AtomCompute = wx.Menu(title='')
1973        self.AtomsMenu.Append(menu=self.AtomEdit, title='Edit')
1974        self.AtomsMenu.Append(menu=self.AtomCompute, title='Compute')
1975        self.AtomEdit.Append(id=wxID_ATOMSEDITADD, kind=wx.ITEM_NORMAL,text='Append atom',
1976            help='Appended as an H atom')
1977        self.AtomEdit.Append(id=wxID_ATOMSVIEWADD, kind=wx.ITEM_NORMAL,text='Append view point',
1978            help='Appended as an H atom')
1979        self.AtomEdit.Append(id=wxID_ATOMSEDITINSERT, kind=wx.ITEM_NORMAL,text='Insert atom',
1980            help='Select atom row to insert before; inserted as an H atom')
1981        self.AtomEdit.Append(id=wxID_ATOMVIEWINSERT, kind=wx.ITEM_NORMAL,text='Insert view point',
1982            help='Select atom row to insert before; inserted as an H atom')
1983        self.AtomEdit.Append(id=wxID_ATOMMOVE, kind=wx.ITEM_NORMAL,text='Move atom to view point',
1984            help='Select single atom to move')
1985        self.AtomEdit.Append(id=wxID_ATOMSEDITDELETE, kind=wx.ITEM_NORMAL,text='Delete atom',
1986            help='Select atoms to delete first')
1987        self.AtomEdit.Append(id=wxID_ATOMSREFINE, kind=wx.ITEM_NORMAL,text='Set atom refinement flags',
1988            help='Select atoms to refine first')
1989        self.AtomEdit.Append(id=wxID_ATOMSMODIFY, kind=wx.ITEM_NORMAL,text='Modify atom parameters',
1990            help='Select atoms to modify first')
1991        self.AtomEdit.Append(id=wxID_ATOMSTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
1992            help='Select atoms to transform first')
1993        self.AtomEdit.Append(id=wxID_MAKEMOLECULE, kind=wx.ITEM_NORMAL,text='Assemble molecule',
1994            help='Assemble molecule from scatterd atom positions')
1995        self.AtomEdit.Append(id=wxID_RELOADDRAWATOMS, kind=wx.ITEM_NORMAL,text='Reload draw atoms',
1996            help='Reload atom drawing list')
1997        submenu = wx.Menu()
1998        self.AtomEdit.AppendMenu(wx.ID_ANY, 'Reimport atoms', submenu, 
1999            help='Reimport atoms from file; sequence must match')
2000        # setup a cascade menu for the formats that have been defined
2001        self.ReImportMenuId = {}  # points to readers for each menu entry
2002        for reader in self.G2frame.ImportPhaseReaderlist:
2003            item = submenu.Append(
2004                wx.ID_ANY,help=reader.longFormatName,
2005                kind=wx.ITEM_NORMAL,text='reimport coordinates from '+reader.formatName+' file')
2006            self.ReImportMenuId[item.GetId()] = reader
2007        item = submenu.Append(
2008            wx.ID_ANY,
2009            help='Reimport coordinates, try to determine format from file',
2010            kind=wx.ITEM_NORMAL,
2011            text='guess format from file')
2012        self.ReImportMenuId[item.GetId()] = None # try all readers
2013
2014        self.AtomCompute.Append(id=wxID_ATOMSDISAGL, kind=wx.ITEM_NORMAL,text='Show Distances && Angles',
2015            help='Compute distances & angles for selected atoms')
2016        self.AtomCompute.Append(id=wxID_ATOMSPDISAGL, kind=wx.ITEM_NORMAL,text='Save Distances && Angles',
2017            help='Compute distances & angles for selected atoms')
2018        self.AtomCompute.ISOcalc = self.AtomCompute.Append(
2019            id=wxID_ISODISP, kind=wx.ITEM_NORMAL,
2020            text='Compute ISODISTORT mode values',
2021            help='Compute values of ISODISTORT modes from atom parameters')
2022        self.PostfillDataMenu()
2023       
2024        # Phase / Imcommensurate "waves" tab
2025        self.WavesData = wx.MenuBar()
2026        self.PrefillDataMenu(self.WavesData,helpType='Wave Data', helpLbl='Imcommensurate wave data')
2027        self.WavesData.Append(menu=wx.Menu(title=''),title='Select tab')
2028        self.WavesDataCompute = wx.Menu(title='')
2029        self.WavesData.Append(menu=self.WavesDataCompute,title='Compute')
2030        self.WavesDataCompute.Append(id=wxID_4DMAPCOMPUTE, kind=wx.ITEM_NORMAL,text='Compute 4D map',
2031            help='Compute 4-dimensional map')
2032        self.PostfillDataMenu()
2033                 
2034        # Phase / Draw Options tab
2035        self.DataDrawOptions = wx.MenuBar()
2036        self.PrefillDataMenu(self.DataDrawOptions,helpType='Draw Options', helpLbl='Phase/Draw Options')
2037        self.DataDrawOptions.Append(menu=wx.Menu(title=''),title='Select tab')
2038        self.PostfillDataMenu()
2039       
2040        # Phase / Draw Atoms tab
2041        self.DrawAtomsMenu = wx.MenuBar()
2042        self.PrefillDataMenu(self.DrawAtomsMenu,helpType='Draw Atoms')
2043        self.DrawAtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2044        self.DrawAtomEdit = wx.Menu(title='')
2045        self.DrawAtomCompute = wx.Menu(title='')
2046        self.DrawAtomRestraint = wx.Menu(title='')
2047        self.DrawAtomRigidBody = wx.Menu(title='')
2048        self.DrawAtomsMenu.Append(menu=self.DrawAtomEdit, title='Edit')
2049        self.DrawAtomsMenu.Append(menu=self.DrawAtomCompute,title='Compute')
2050        self.DrawAtomsMenu.Append(menu=self.DrawAtomRestraint, title='Restraints')
2051        self.DrawAtomsMenu.Append(menu=self.DrawAtomRigidBody, title='Rigid body')
2052        self.DrawAtomEdit.Append(id=wxID_DRAWATOMSTYLE, kind=wx.ITEM_NORMAL,text='Atom style',
2053            help='Select atoms first')
2054        self.DrawAtomEdit.Append(id=wxID_DRAWATOMLABEL, kind=wx.ITEM_NORMAL,text='Atom label',
2055            help='Select atoms first')
2056        self.DrawAtomEdit.Append(id=wxID_DRAWATOMCOLOR, kind=wx.ITEM_NORMAL,text='Atom color',
2057            help='Select atoms first')
2058        self.DrawAtomEdit.Append(id=wxID_DRAWATOMRESETCOLOR, kind=wx.ITEM_NORMAL,text='Reset atom colors',
2059            help='Resets all atom colors to defaults')
2060        self.DrawAtomEdit.Append(id=wxID_DRAWVIEWPOINT, kind=wx.ITEM_NORMAL,text='View point',
2061            help='View point is 1st atom selected')
2062        self.DrawAtomEdit.Append(id=wxID_DRAWADDEQUIV, kind=wx.ITEM_NORMAL,text='Add atoms',
2063            help='Add symmetry & cell equivalents to drawing set from selected atoms')
2064        self.DrawAtomEdit.Append(id=wxID_DRAWTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform draw atoms',
2065            help='Transform selected atoms by symmetry & cell translations')
2066        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCOORD, kind=wx.ITEM_NORMAL,text='Fill CN-sphere',
2067            help='Fill coordination sphere for selected atoms')           
2068        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCELL, kind=wx.ITEM_NORMAL,text='Fill unit cell',
2069            help='Fill unit cell with selected atoms')
2070        self.DrawAtomEdit.Append(id=wxID_DRAWDELETE, kind=wx.ITEM_NORMAL,text='Delete atoms',
2071            help='Delete atoms from drawing set')
2072        self.DrawAtomCompute.Append(id=wxID_DRAWDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2073            help='Compute distance of selected atoms from view point')   
2074        self.DrawAtomCompute.Append(id=wxID_DRAWDISAGLTOR, kind=wx.ITEM_NORMAL,text='Dist. Ang. Tors.',
2075            help='Compute distance, angle or torsion for 2-4 selected atoms')   
2076        self.DrawAtomCompute.Append(id=wxID_DRAWPLANE, kind=wx.ITEM_NORMAL,text='Best plane',
2077            help='Compute best plane for 4+ selected atoms')   
2078        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRBOND, kind=wx.ITEM_NORMAL,text='Add bond restraint',
2079            help='Add bond restraint for selected atoms (2)')
2080        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRANGLE, kind=wx.ITEM_NORMAL,text='Add angle restraint',
2081            help='Add angle restraint for selected atoms (3: one end 1st)')
2082        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRPLANE, kind=wx.ITEM_NORMAL,text='Add plane restraint',
2083            help='Add plane restraint for selected atoms (4+)')
2084        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRCHIRAL, kind=wx.ITEM_NORMAL,text='Add chiral restraint',
2085            help='Add chiral restraint for selected atoms (4: center atom 1st)')
2086        self.DrawAtomRigidBody.Append(id=wxID_DRAWDEFINERB, kind=wx.ITEM_NORMAL,text='Define rigid body',
2087            help='Define rigid body with selected atoms')
2088        self.PostfillDataMenu()
2089
2090        # Phase / MCSA tab
2091        self.MCSAMenu = wx.MenuBar()
2092        self.PrefillDataMenu(self.MCSAMenu,helpType='MC/SA')
2093        self.MCSAMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2094        self.MCSAEdit = wx.Menu(title='')
2095        self.MCSAMenu.Append(menu=self.MCSAEdit, title='MC/SA')
2096        self.MCSAEdit.Append(id=wxID_ADDMCSAATOM, kind=wx.ITEM_NORMAL,text='Add atom', 
2097            help='Add single atom to MC/SA model')
2098        self.MCSAEdit.Append(id=wxID_ADDMCSARB, kind=wx.ITEM_NORMAL,text='Add rigid body', 
2099            help='Add rigid body to MC/SA model' )
2100        self.MCSAEdit.Append(id=wxID_CLEARMCSARB, kind=wx.ITEM_NORMAL,text='Clear rigid bodies', 
2101            help='Clear all atoms & rigid bodies from MC/SA model' )
2102        self.MCSAEdit.Append(id=wxID_MOVEMCSA, kind=wx.ITEM_NORMAL,text='Move MC/SA solution', 
2103            help='Move MC/SA solution to atom list' )
2104        self.MCSAEdit.Append(id=wxID_MCSACLEARRESULTS, kind=wx.ITEM_NORMAL,text='Clear results', 
2105            help='Clear table of MC/SA results' )
2106        self.PostfillDataMenu()
2107           
2108        # Phase / Texture tab
2109        self.TextureMenu = wx.MenuBar()
2110        self.PrefillDataMenu(self.TextureMenu,helpType='Texture')
2111        self.TextureMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2112        self.TextureEdit = wx.Menu(title='')
2113        self.TextureMenu.Append(menu=self.TextureEdit, title='Texture')
2114#        self.TextureEdit.Append(id=wxID_REFINETEXTURE, kind=wx.ITEM_NORMAL,text='Refine texture',
2115#            help='Refine the texture coefficients from sequential Pawley results')
2116# N.B. Binding is now commented out
2117        self.TextureEdit.Append(id=wxID_CLEARTEXTURE, kind=wx.ITEM_NORMAL,text='Clear texture', 
2118            help='Clear the texture coefficients' )
2119        self.PostfillDataMenu()
2120           
2121        # Phase / Pawley tab
2122        self.PawleyMenu = wx.MenuBar()
2123        self.PrefillDataMenu(self.PawleyMenu,helpType='Pawley')
2124        self.PawleyMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2125        self.PawleyEdit = wx.Menu(title='')
2126        self.PawleyMenu.Append(menu=self.PawleyEdit,title='Operations')
2127        self.PawleyEdit.Append(id=wxID_PAWLEYLOAD, kind=wx.ITEM_NORMAL,text='Pawley create',
2128            help='Initialize Pawley reflection list')
2129        self.PawleyEdit.Append(id=wxID_PAWLEYESTIMATE, kind=wx.ITEM_NORMAL,text='Pawley estimate',
2130            help='Estimate initial Pawley intensities')
2131        self.PawleyEdit.Append(id=wxID_PAWLEYUPDATE, kind=wx.ITEM_NORMAL,text='Pawley update',
2132            help='Update negative Pawley intensities with -0.5*Fobs and turn off refinemnt')
2133        self.PostfillDataMenu()
2134           
2135        # Phase / Map peaks tab
2136        self.MapPeaksMenu = wx.MenuBar()
2137        self.PrefillDataMenu(self.MapPeaksMenu,helpType='Map peaks')
2138        self.MapPeaksMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2139        self.MapPeaksEdit = wx.Menu(title='')
2140        self.MapPeaksMenu.Append(menu=self.MapPeaksEdit, title='Map peaks')
2141        self.MapPeaksEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks', 
2142            help='Move selected peaks to atom list')
2143        self.MapPeaksEdit.Append(id=wxID_PEAKSVIEWPT, kind=wx.ITEM_NORMAL,text='View point',
2144            help='View point is 1st peak selected')
2145        self.MapPeaksEdit.Append(id=wxID_PEAKSDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2146            help='Compute distance of selected peaks from view point')   
2147        self.MapPeaksEdit.Append(id=wxID_SHOWBONDS, kind=wx.ITEM_NORMAL,text='Hide bonds',
2148            help='Hide or show bonds between peak positions')   
2149        self.MapPeaksEdit.Append(id=wxID_PEAKSDA, kind=wx.ITEM_NORMAL,text='Calc dist/ang', 
2150            help='Calculate distance or angle for selection')
2151        self.MapPeaksEdit.Append(id=wxID_FINDEQVPEAKS, kind=wx.ITEM_NORMAL,text='Equivalent peaks', 
2152            help='Find equivalent peaks')
2153        self.MapPeaksEdit.Append(id=wxID_PEAKSUNIQUE, kind=wx.ITEM_NORMAL,text='Unique peaks', 
2154            help='Select unique set')
2155        self.MapPeaksEdit.Append(id=wxID_PEAKSDELETE, kind=wx.ITEM_NORMAL,text='Delete peaks', 
2156            help='Delete selected peaks')
2157        self.MapPeaksEdit.Append(id=wxID_PEAKSCLEAR, kind=wx.ITEM_NORMAL,text='Clear peaks', 
2158            help='Clear the map peak list')
2159        self.PostfillDataMenu()
2160
2161        # Phase / Rigid bodies tab
2162        self.RigidBodiesMenu = wx.MenuBar()
2163        self.PrefillDataMenu(self.RigidBodiesMenu,helpType='Rigid bodies')
2164        self.RigidBodiesMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2165        self.RigidBodiesEdit = wx.Menu(title='')
2166        self.RigidBodiesMenu.Append(menu=self.RigidBodiesEdit, title='Edit')
2167        self.RigidBodiesEdit.Append(id=wxID_ASSIGNATMS2RB, kind=wx.ITEM_NORMAL,text='Assign atoms to rigid body',
2168            help='Select & position rigid body in structure of existing atoms')
2169        self.RigidBodiesEdit.Append(id=wxID_AUTOFINDRESRB, kind=wx.ITEM_NORMAL,text='Auto find residues',
2170            help='Auto find of residue RBs in macromolecule')
2171        self.RigidBodiesEdit.Append(id=wxID_COPYRBPARMS, kind=wx.ITEM_NORMAL,text='Copy rigid body parms',
2172            help='Copy rigid body location & TLS parameters')
2173        self.RigidBodiesEdit.Append(id=wxID_GLOBALTHERM, kind=wx.ITEM_NORMAL,text='Global thermal motion',
2174            help='Global setting of residue thermal motion models')
2175        self.RigidBodiesEdit.Append(id=wxID_GLOBALRESREFINE, kind=wx.ITEM_NORMAL,text='Global residue refine',
2176            help='Global setting of residue RB refinement flags')
2177        self.RigidBodiesEdit.Append(id=wxID_RBREMOVEALL, kind=wx.ITEM_NORMAL,text='Remove all rigid bodies',
2178            help='Remove all rigid body assignment for atoms')
2179        self.PostfillDataMenu()
2180    # end of GSAS-II menu definitions
2181       
2182    def _init_ctrls(self, parent,name=None,size=None,pos=None):
2183        wx.Frame.__init__(
2184            self,parent=parent,
2185            #style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX | wx.FRAME_FLOAT_ON_PARENT ,
2186            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX,
2187            size=size,pos=pos,title='GSAS-II data display')
2188        self._init_menus()
2189        if name:
2190            self.SetLabel(name)
2191        self.Show()
2192       
2193    def __init__(self,parent,frame,data=None,name=None, size=None,pos=None):
2194        self.G2frame = frame
2195        self._init_ctrls(parent,name,size,pos)
2196        self.data = data
2197        clientSize = wx.ClientDisplayRect()
2198        Size = self.GetSize()
2199        xPos = clientSize[2]-Size[0]
2200        self.SetPosition(wx.Point(xPos,clientSize[1]+250))
2201        self.AtomGrid = []
2202        self.selectedRow = 0
2203       
2204    def setSizePosLeft(self,Width):
2205        clientSize = wx.ClientDisplayRect()
2206        Width[1] = min(Width[1],clientSize[2]-300)
2207        Width[0] = max(Width[0],300)
2208        self.SetSize(Width)
2209#        self.SetPosition(wx.Point(clientSize[2]-Width[0],clientSize[1]+250))
2210       
2211    def Clear(self):
2212        self.ClearBackground()
2213        self.DestroyChildren()
2214                   
2215################################################################################
2216#####  GSNotebook
2217################################################################################           
2218       
2219class GSNoteBook(wx.aui.AuiNotebook):
2220    '''Notebook used in various locations; implemented with wx.aui extension
2221    '''
2222    def __init__(self, parent, name='',size = None):
2223        wx.aui.AuiNotebook.__init__(self, parent, -1,
2224                                    style=wx.aui.AUI_NB_TOP |
2225                                    wx.aui.AUI_NB_SCROLL_BUTTONS)
2226        if size: self.SetSize(size)
2227        self.parent = parent
2228        self.PageChangeHandler = None
2229       
2230    def PageChangeEvent(self,event):
2231        G2frame = self.parent.G2frame
2232        page = event.GetSelection()
2233        if self.PageChangeHandler:
2234            if log.LogInfo['Logging']:
2235                log.MakeTabLog(
2236                    G2frame.dataFrame.GetTitle(),
2237                    G2frame.dataDisplay.GetPageText(page)
2238                    )
2239            self.PageChangeHandler(event)
2240           
2241    def Bind(self,eventtype,handler,*args,**kwargs):
2242        '''Override the Bind() function so that page change events can be trapped
2243        '''
2244        if eventtype == wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED:
2245            self.PageChangeHandler = handler
2246            wx.aui.AuiNotebook.Bind(self,eventtype,self.PageChangeEvent)
2247            return
2248        wx.aui.AuiNotebook.Bind(self,eventtype,handler,*args,**kwargs)
2249                                                     
2250    def Clear(self):       
2251        GSNoteBook.DeleteAllPages(self)
2252       
2253    def FindPage(self,name):
2254        numPage = self.GetPageCount()
2255        for page in range(numPage):
2256            if self.GetPageText(page) == name:
2257                return page
2258
2259    def ChangeSelection(self,page):
2260        # in wx.Notebook ChangeSelection is like SetSelection, but it
2261        # does not invoke the event related to pressing the tab button
2262        # I don't see a way to do that in aui.
2263        oldPage = self.GetSelection()
2264        self.SetSelection(page)
2265        return oldPage
2266
2267    # def __getattribute__(self,name):
2268    #     '''This method provides a way to print out a message every time
2269    #     that a method in a class is called -- to see what all the calls
2270    #     might be, or where they might be coming from.
2271    #     Cute trick for debugging!
2272    #     '''
2273    #     attr = object.__getattribute__(self, name)
2274    #     if hasattr(attr, '__call__'):
2275    #         def newfunc(*args, **kwargs):
2276    #             print('GSauiNoteBook calling %s' %attr.__name__)
2277    #             result = attr(*args, **kwargs)
2278    #             return result
2279    #         return newfunc
2280    #     else:
2281    #         return attr
2282           
2283################################################################################
2284#####  GSGrid
2285################################################################################           
2286       
2287class GSGrid(wg.Grid):
2288    '''Basic wx.Grid implementation
2289    '''
2290    def __init__(self, parent, name=''):
2291        wg.Grid.__init__(self,parent,-1,name=name)                   
2292        #self.SetSize(parent.GetClientSize())
2293        # above removed to speed drawing of initial grid
2294        # does not appear to be needed
2295           
2296    def Clear(self):
2297        wg.Grid.ClearGrid(self)
2298       
2299    def SetCellReadOnly(self,r,c,readonly=True):
2300        self.SetReadOnly(r,c,isReadOnly=readonly)
2301       
2302    def SetCellStyle(self,r,c,color="white",readonly=True):
2303        self.SetCellBackgroundColour(r,c,color)
2304        self.SetReadOnly(r,c,isReadOnly=readonly)
2305       
2306    def GetSelection(self):
2307        #this is to satisfy structure drawing stuff in G2plt when focus changes
2308        return None
2309
2310    def InstallGridToolTip(self, rowcolhintcallback,
2311                           colLblCallback=None,rowLblCallback=None):
2312        '''code to display a tooltip for each item on a grid
2313        from http://wiki.wxpython.org/wxGrid%20ToolTips (buggy!), expanded to
2314        column and row labels using hints from
2315        https://groups.google.com/forum/#!topic/wxPython-users/bm8OARRVDCs
2316
2317        :param function rowcolhintcallback: a routine that returns a text
2318          string depending on the selected row and column, to be used in
2319          explaining grid entries.
2320        :param function colLblCallback: a routine that returns a text
2321          string depending on the selected column, to be used in
2322          explaining grid columns (if None, the default), column labels
2323          do not get a tooltip.
2324        :param function rowLblCallback: a routine that returns a text
2325          string depending on the selected row, to be used in
2326          explaining grid rows (if None, the default), row labels
2327          do not get a tooltip.
2328        '''
2329        prev_rowcol = [None,None,None]
2330        def OnMouseMotion(event):
2331            # event.GetRow() and event.GetCol() would be nice to have here,
2332            # but as this is a mouse event, not a grid event, they are not
2333            # available and we need to compute them by hand.
2334            x, y = self.CalcUnscrolledPosition(event.GetPosition())
2335            row = self.YToRow(y)
2336            col = self.XToCol(x)
2337            hinttext = ''
2338            win = event.GetEventObject()
2339            if [row,col,win] == prev_rowcol: # no change from last position
2340                event.Skip()
2341                return
2342            if win == self.GetGridWindow() and row >= 0 and col >= 0:
2343                hinttext = rowcolhintcallback(row, col)
2344            elif win == self.GetGridColLabelWindow() and col >= 0:
2345                if colLblCallback: hinttext = colLblCallback(col)
2346            elif win == self.GetGridRowLabelWindow() and row >= 0:
2347                if rowLblCallback: hinttext = rowLblCallback(row)
2348            else: # this should be the upper left corner, which is empty
2349                event.Skip()
2350                return
2351            if hinttext is None: hinttext = ''
2352            win.SetToolTipString(hinttext)
2353            prev_rowcol[:] = [row,col,win]
2354            event.Skip()
2355
2356        wx.EVT_MOTION(self.GetGridWindow(), OnMouseMotion)
2357        if colLblCallback: wx.EVT_MOTION(self.GetGridColLabelWindow(), OnMouseMotion)
2358        if rowLblCallback: wx.EVT_MOTION(self.GetGridRowLabelWindow(), OnMouseMotion)
2359                                                   
2360################################################################################
2361#####  Table
2362################################################################################           
2363       
2364class Table(wg.PyGridTableBase):
2365    '''Basic data table for use with GSgrid
2366    '''
2367    def __init__(self, data=[], rowLabels=None, colLabels=None, types = None):
2368        wg.PyGridTableBase.__init__(self)
2369        self.colLabels = colLabels
2370        self.rowLabels = rowLabels
2371        self.dataTypes = types
2372        self.data = data
2373       
2374    def AppendRows(self, numRows=1):
2375        self.data.append([])
2376        return True
2377       
2378    def CanGetValueAs(self, row, col, typeName):
2379        if self.dataTypes:
2380            colType = self.dataTypes[col].split(':')[0]
2381            if typeName == colType:
2382                return True
2383            else:
2384                return False
2385        else:
2386            return False
2387
2388    def CanSetValueAs(self, row, col, typeName):
2389        return self.CanGetValueAs(row, col, typeName)
2390
2391    def DeleteRow(self,pos):
2392        data = self.GetData()
2393        self.SetData([])
2394        new = []
2395        for irow,row in enumerate(data):
2396            if irow <> pos:
2397                new.append(row)
2398        self.SetData(new)
2399       
2400    def GetColLabelValue(self, col):
2401        if self.colLabels:
2402            return self.colLabels[col]
2403           
2404    def GetData(self):
2405        data = []
2406        for row in range(self.GetNumberRows()):
2407            data.append(self.GetRowValues(row))
2408        return data
2409       
2410    def GetNumberCols(self):
2411        try:
2412            return len(self.colLabels)
2413        except TypeError:
2414            return None
2415       
2416    def GetNumberRows(self):
2417        return len(self.data)
2418       
2419    def GetRowLabelValue(self, row):
2420        if self.rowLabels:
2421            return self.rowLabels[row]
2422       
2423    def GetColValues(self, col):
2424        data = []
2425        for row in range(self.GetNumberRows()):
2426            data.append(self.GetValue(row, col))
2427        return data
2428       
2429    def GetRowValues(self, row):
2430        data = []
2431        for col in range(self.GetNumberCols()):
2432            data.append(self.GetValue(row, col))
2433        return data
2434       
2435    def GetTypeName(self, row, col):
2436        try:
2437            if self.data[row][col] is None: return None
2438            return self.dataTypes[col]
2439        except (TypeError,IndexError):
2440            return None
2441
2442    def GetValue(self, row, col):
2443        try:
2444            if self.data[row][col] is None: return ""
2445            return self.data[row][col]
2446        except IndexError:
2447            return None
2448           
2449    def InsertRows(self, pos, rows):
2450        for row in range(rows):
2451            self.data.insert(pos,[])
2452            pos += 1
2453       
2454    def IsEmptyCell(self,row,col):
2455        try:
2456            return not self.data[row][col]
2457        except IndexError:
2458            return True
2459       
2460    def OnKeyPress(self, event):
2461        dellist = self.GetSelectedRows()
2462        if event.GetKeyCode() == wx.WXK_DELETE and dellist:
2463            grid = self.GetView()
2464            for i in dellist: grid.DeleteRow(i)
2465               
2466    def SetColLabelValue(self, col, label):
2467        numcols = self.GetNumberCols()
2468        if col > numcols-1:
2469            self.colLabels.append(label)
2470        else:
2471            self.colLabels[col]=label
2472       
2473    def SetData(self,data):
2474        for row in range(len(data)):
2475            self.SetRowValues(row,data[row])
2476               
2477    def SetRowLabelValue(self, row, label):
2478        self.rowLabels[row]=label
2479           
2480    def SetRowValues(self,row,data):
2481        self.data[row] = data
2482           
2483    def SetValue(self, row, col, value):
2484        def innerSetValue(row, col, value):
2485            try:
2486                self.data[row][col] = value
2487            except TypeError:
2488                return
2489            except IndexError:
2490                print row,col,value
2491                # add a new row
2492                if row > self.GetNumberRows():
2493                    self.data.append([''] * self.GetNumberCols())
2494                elif col > self.GetNumberCols():
2495                    for row in range(self.GetNumberRows):
2496                        self.data[row].append('')
2497                print self.data
2498                self.data[row][col] = value
2499        innerSetValue(row, col, value)
2500
2501################################################################################
2502#####  Notebook
2503################################################################################           
2504       
2505def UpdateNotebook(G2frame,data):
2506    '''Called when the data tree notebook entry is selected. Allows for
2507    editing of the text in that tree entry
2508    '''
2509    def OnNoteBook(event):
2510        data = G2frame.dataDisplay.GetValue().split('\n')
2511        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Notebook'),data)
2512        if 'nt' not in os.name:
2513            G2frame.dataDisplay.AppendText('\n')
2514                   
2515    if G2frame.dataDisplay:
2516        G2frame.dataDisplay.Destroy()
2517    G2frame.dataFrame.SetLabel('Notebook')
2518    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2519        style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
2520    G2frame.dataDisplay.Bind(wx.EVT_TEXT_ENTER,OnNoteBook)
2521    G2frame.dataDisplay.Bind(wx.EVT_KILL_FOCUS,OnNoteBook)
2522    for line in data:
2523        G2frame.dataDisplay.AppendText(line+"\n")
2524    G2frame.dataDisplay.AppendText('Notebook entry @ '+time.ctime()+"\n")
2525    G2frame.dataFrame.setSizePosLeft([400,250])
2526           
2527################################################################################
2528#####  Controls
2529################################################################################           
2530       
2531def UpdateControls(G2frame,data):
2532    '''Edit overall GSAS-II controls in main Controls data tree entry
2533    '''
2534    #patch
2535    if 'deriv type' not in data:
2536        data = {}
2537        data['deriv type'] = 'analytic Hessian'
2538        data['min dM/M'] = 0.0001
2539        data['shift factor'] = 1.
2540        data['max cyc'] = 3       
2541        data['F**2'] = True
2542        data['minF/sig'] = 0
2543    if 'shift factor' not in data:
2544        data['shift factor'] = 1.
2545    if 'max cyc' not in data:
2546        data['max cyc'] = 3
2547    if 'F**2' not in data:
2548        data['F**2'] = True
2549        data['minF/sig'] = 0
2550    if 'Author' not in data:
2551        data['Author'] = 'no name'
2552    if 'FreePrm1' not in data:
2553        data['FreePrm1'] = 'Sample humidity (%)'
2554    if 'FreePrm2' not in data:
2555        data['FreePrm2'] = 'Sample voltage (V)'
2556    if 'FreePrm3' not in data:
2557        data['FreePrm3'] = 'Applied load (MN)'
2558    if 'Copy2Next' not in data:
2559        data['Copy2Next'] = False
2560    if 'Reverse Seq' not in data:
2561        data['Reverse Seq'] = False   
2562     
2563   
2564    #end patch
2565
2566    def SeqSizer():
2567       
2568        def OnSelectData(event):
2569            choices = GetPatternTreeDataNames(G2frame,['PWDR',])
2570            sel = []
2571            if 'Seq Data' in data:
2572                for item in data['Seq Data']:
2573                    sel.append(choices.index(item))
2574                sel = [choices.index(item) for item in data['Seq Data']]
2575            dlg = G2MultiChoiceDialog(G2frame.dataFrame, 'Sequential refinement',
2576                                      'Select dataset to include',
2577                                      choices)
2578            dlg.SetSelections(sel)
2579            names = []
2580            if dlg.ShowModal() == wx.ID_OK:
2581                for sel in dlg.GetSelections():
2582                    names.append(choices[sel])
2583                data['Seq Data'] = names               
2584                G2frame.EnableSeqRefineMenu()
2585            dlg.Destroy()
2586            wx.CallAfter(UpdateControls,G2frame,data)
2587           
2588        def OnReverse(event):
2589            data['Reverse Seq'] = reverseSel.GetValue()
2590           
2591        def OnCopySel(event):
2592            data['Copy2Next'] = copySel.GetValue() 
2593                   
2594        seqSizer = wx.BoxSizer(wx.VERTICAL)
2595        dataSizer = wx.BoxSizer(wx.HORIZONTAL)
2596        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Sequential Refinement: '),0,WACV)
2597        selSeqData = wx.Button(G2frame.dataDisplay,-1,label=' Select data')
2598        selSeqData.Bind(wx.EVT_BUTTON,OnSelectData)
2599        dataSizer.Add(selSeqData,0,WACV)
2600        SeqData = data.get('Seq Data',[])
2601        if not SeqData:
2602            lbl = ' (no powder data selected)'
2603        else:
2604            lbl = ' ('+str(len(SeqData))+' dataset(s) selected)'
2605
2606        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=lbl),0,WACV)
2607        seqSizer.Add(dataSizer,0)
2608        if SeqData:
2609            selSizer = wx.BoxSizer(wx.HORIZONTAL)
2610            reverseSel = wx.CheckBox(G2frame.dataDisplay,-1,label=' Reverse order?')
2611            reverseSel.Bind(wx.EVT_CHECKBOX,OnReverse)
2612            reverseSel.SetValue(data['Reverse Seq'])
2613            selSizer.Add(reverseSel,0,WACV)
2614            copySel =  wx.CheckBox(G2frame.dataDisplay,-1,label=' Copy results to next histogram?')
2615            copySel.Bind(wx.EVT_CHECKBOX,OnCopySel)
2616            copySel.SetValue(data['Copy2Next'])
2617            selSizer.Add(copySel,0,WACV)
2618            seqSizer.Add(selSizer,0)
2619        return seqSizer
2620       
2621    def LSSizer():       
2622       
2623        def OnDerivType(event):
2624            data['deriv type'] = derivSel.GetValue()
2625            derivSel.SetValue(data['deriv type'])
2626            wx.CallAfter(UpdateControls,G2frame,data)
2627           
2628        def OnConvergence(event):
2629            try:
2630                value = max(1.e-9,min(1.0,float(Cnvrg.GetValue())))
2631            except ValueError:
2632                value = 0.0001
2633            data['min dM/M'] = value
2634            Cnvrg.SetValue('%.2g'%(value))
2635           
2636        def OnMaxCycles(event):
2637            data['max cyc'] = int(maxCyc.GetValue())
2638            maxCyc.SetValue(str(data['max cyc']))
2639                       
2640        def OnFactor(event):
2641            try:
2642                value = min(max(float(Factr.GetValue()),0.00001),100.)
2643            except ValueError:
2644                value = 1.0
2645            data['shift factor'] = value
2646            Factr.SetValue('%.5f'%(value))
2647           
2648        def OnFsqRef(event):
2649            data['F**2'] = fsqRef.GetValue()
2650       
2651        def OnMinSig(event):
2652            try:
2653                value = min(max(float(minSig.GetValue()),0.),5.)
2654            except ValueError:
2655                value = 1.0
2656            data['minF/sig'] = value
2657            minSig.SetValue('%.2f'%(value))
2658
2659        LSSizer = wx.FlexGridSizer(cols=4,vgap=5,hgap=5)
2660        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement derivatives: '),0,WACV)
2661        Choice=['analytic Jacobian','numeric','analytic Hessian']
2662        derivSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['deriv type'],choices=Choice,
2663            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2664        derivSel.SetValue(data['deriv type'])
2665        derivSel.Bind(wx.EVT_COMBOBOX, OnDerivType)
2666           
2667        LSSizer.Add(derivSel,0,WACV)
2668        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min delta-M/M: '),0,WACV)
2669        Cnvrg = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2g'%(data['min dM/M']),style=wx.TE_PROCESS_ENTER)
2670        Cnvrg.Bind(wx.EVT_TEXT_ENTER,OnConvergence)
2671        Cnvrg.Bind(wx.EVT_KILL_FOCUS,OnConvergence)
2672        LSSizer.Add(Cnvrg,0,WACV)
2673        if 'Hessian' in data['deriv type']:
2674            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Max cycles: '),0,WACV)
2675            Choice = ['0','1','2','3','5','10','15','20']
2676            maxCyc = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['max cyc']),choices=Choice,
2677                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2678            maxCyc.SetValue(str(data['max cyc']))
2679            maxCyc.Bind(wx.EVT_COMBOBOX, OnMaxCycles)
2680            LSSizer.Add(maxCyc,0,WACV)
2681        else:
2682            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Initial shift factor: '),0,WACV)
2683            Factr = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.5f'%(data['shift factor']),style=wx.TE_PROCESS_ENTER)
2684            Factr.Bind(wx.EVT_TEXT_ENTER,OnFactor)
2685            Factr.Bind(wx.EVT_KILL_FOCUS,OnFactor)
2686            LSSizer.Add(Factr,0,WACV)
2687        if G2frame.Sngl:
2688            LSSizer.Add((1,0),)
2689            LSSizer.Add((1,0),)
2690            fsqRef = wx.CheckBox(G2frame.dataDisplay,-1,label='Refine HKLF as F^2? ')
2691            fsqRef.SetValue(data['F**2'])
2692            fsqRef.Bind(wx.EVT_CHECKBOX,OnFsqRef)
2693            LSSizer.Add(fsqRef,0,WACV)
2694            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label='Min obs/sig (0-5): '),0,WACV)
2695            minSig = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(data['minF/sig']),style=wx.TE_PROCESS_ENTER)
2696            minSig.Bind(wx.EVT_TEXT_ENTER,OnMinSig)
2697            minSig.Bind(wx.EVT_KILL_FOCUS,OnMinSig)
2698            LSSizer.Add(minSig,0,WACV)
2699        return LSSizer
2700       
2701    def AuthSizer():
2702
2703        def OnAuthor(event):
2704            data['Author'] = auth.GetValue()
2705
2706        Author = data['Author']
2707        authSizer = wx.BoxSizer(wx.HORIZONTAL)
2708        authSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' CIF Author (last, first):'),0,WACV)
2709        auth = wx.TextCtrl(G2frame.dataDisplay,-1,value=Author,style=wx.TE_PROCESS_ENTER)
2710        auth.Bind(wx.EVT_TEXT_ENTER,OnAuthor)
2711        auth.Bind(wx.EVT_KILL_FOCUS,OnAuthor)
2712        authSizer.Add(auth,0,WACV)
2713        return authSizer
2714       
2715       
2716    if G2frame.dataDisplay:
2717        G2frame.dataDisplay.Destroy()
2718    if not G2frame.dataFrame.GetStatusBar():
2719        Status = G2frame.dataFrame.CreateStatusBar()
2720        Status.SetStatusText('')
2721    G2frame.dataFrame.SetLabel('Controls')
2722    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
2723    SetDataMenuBar(G2frame,G2frame.dataFrame.ControlsMenu)
2724    mainSizer = wx.BoxSizer(wx.VERTICAL)
2725    mainSizer.Add((5,5),0)
2726    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement Controls:'),0,WACV)   
2727    mainSizer.Add(LSSizer())
2728    mainSizer.Add((5,5),0)
2729    mainSizer.Add(SeqSizer())
2730    mainSizer.Add((5,5),0)
2731    mainSizer.Add(AuthSizer())
2732    mainSizer.Add((5,5),0)
2733       
2734    mainSizer.Layout()   
2735    G2frame.dataDisplay.SetSizer(mainSizer)
2736    G2frame.dataDisplay.SetSize(mainSizer.Fit(G2frame.dataFrame))
2737    G2frame.dataFrame.setSizePosLeft(mainSizer.Fit(G2frame.dataFrame))
2738     
2739################################################################################
2740#####  Comments
2741################################################################################           
2742       
2743def UpdateComments(G2frame,data):                   
2744
2745    if G2frame.dataDisplay:
2746        G2frame.dataDisplay.Destroy()
2747    G2frame.dataFrame.SetLabel('Comments')
2748    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2749        style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_DONTWRAP)
2750    for line in data:
2751        G2frame.dataDisplay.AppendText(line+'\n')
2752    G2frame.dataFrame.setSizePosLeft([400,250])
2753           
2754################################################################################
2755#####  Display of Sequential Results
2756################################################################################           
2757       
2758def UpdateSeqResults(G2frame,data,prevSize=None):
2759    """
2760    Called when the Sequential Results data tree entry is selected
2761    to show results from a sequential refinement.
2762   
2763    :param wx.Frame G2frame: main GSAS-II data tree windows
2764
2765    :param dict data: a dictionary containing the following items: 
2766
2767            * 'histNames' - list of histogram names in order as processed by Sequential Refinement
2768            * 'varyList' - list of variables - identical over all refinements in sequence
2769              note that this is the original list of variables, prior to processing
2770              constraints.
2771            * 'variableLabels' -- a dict of labels to be applied to each parameter
2772              (this is created as an empty dict if not present in data).
2773            * keyed by histName - dictionaries for all data sets processed, which contains:
2774
2775              * 'variables'- result[0] from leastsq call
2776              * 'varyList' - list of variables passed to leastsq call (not same as above)
2777              * 'sig' - esds for variables
2778              * 'covMatrix' - covariance matrix from individual refinement
2779              * 'title' - histogram name; same as dict item name
2780              * 'newAtomDict' - new atom parameters after shifts applied
2781              * 'newCellDict' - refined cell parameters after shifts to A0-A5 from Dij terms applied'
2782    """
2783
2784    def GetSampleParms():
2785        '''Make a dictionary of the sample parameters are not the same over the
2786        refinement series.
2787        '''
2788        if 'IMG' in histNames[0]:
2789            sampleParmDict = {'Sample load':[],}
2790        else:
2791            sampleParmDict = {'Temperature':[],'Pressure':[],'Time':[],
2792                              'FreePrm1':[],'FreePrm2':[],'FreePrm3':[],}
2793        Controls = G2frame.PatternTree.GetItemPyData(
2794            GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
2795        sampleParm = {}
2796        for name in histNames:
2797            if 'IMG' in name:
2798                for item in sampleParmDict:
2799                    sampleParmDict[item].append(data[name]['parmDict'].get(item,0))
2800            else:
2801                Id = GetPatternTreeItemId(G2frame,G2frame.root,name)
2802                sampleData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Sample Parameters'))
2803                for item in sampleParmDict:
2804                    sampleParmDict[item].append(sampleData.get(item,0))
2805        for item in sampleParmDict:
2806            frstValue = sampleParmDict[item][0]
2807            if np.any(np.array(sampleParmDict[item])-frstValue):
2808                if item.startswith('FreePrm'):
2809                    sampleParm[Controls[item]] = sampleParmDict[item]
2810                else:
2811                    sampleParm[item] = sampleParmDict[item]
2812        return sampleParm
2813
2814    def GetColumnInfo(col):
2815        '''returns column label, lists of values and errors (or None) for each column in the table
2816        for plotting. The column label is reformatted from Unicode to MatPlotLib encoding
2817        '''
2818        colName = G2frame.SeqTable.GetColLabelValue(col)
2819        plotName = variableLabels.get(colName,colName)
2820        plotName = plotSpCharFix(plotName)
2821        return plotName,colList[col],colSigs[col]
2822           
2823    def PlotSelect(event):
2824        'Plots a row (covariance) or column on double-click'
2825        cols = G2frame.dataDisplay.GetSelectedCols()
2826        rows = G2frame.dataDisplay.GetSelectedRows()
2827        if cols:
2828            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2829        elif rows:
2830            name = histNames[rows[0]]       #only does 1st one selected
2831            G2plt.PlotCovariance(G2frame,data[name])
2832        else:
2833            G2frame.ErrorDialog(
2834                'Select row or columns',
2835                'Nothing selected in table. Click on column or row label(s) to plot. N.B. Grid selection can be a bit funky.'
2836                )
2837           
2838    def OnPlotSelSeq(event):
2839        'plot the selected columns or row from menu command'
2840        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2841        rows = G2frame.dataDisplay.GetSelectedRows()
2842        if cols:
2843            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2844        elif rows:
2845            name = histNames[rows[0]]       #only does 1st one selected
2846            G2plt.PlotCovariance(G2frame,data[name])
2847        else:
2848            G2frame.ErrorDialog(
2849                'Select columns',
2850                'No columns or rows selected in table. Click on row or column labels to select fields for plotting.'
2851                )
2852               
2853    def OnRenameSelSeq(event):
2854        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2855        colNames = [G2frame.SeqTable.GetColLabelValue(c) for c in cols]
2856        newNames = colNames[:]
2857        for i,name in enumerate(colNames):
2858            if name in variableLabels:
2859                newNames[i] = variableLabels[name]
2860        if not cols:
2861            G2frame.ErrorDialog('Select columns',
2862                'No columns selected in table. Click on column labels to select fields for rename.')
2863            return
2864        dlg = MultiStringDialog(G2frame.dataDisplay,'Set column names',colNames,newNames)
2865        if dlg.Show():
2866            newNames = dlg.GetValues()           
2867            variableLabels.update(dict(zip(colNames,newNames)))
2868        data['variableLabels'] = variableLabels
2869        dlg.Destroy()
2870        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2871        G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2872           
2873    def OnReOrgSelSeq(event):
2874        'Reorder the columns'
2875        G2G.GetItemOrder(G2frame,VaryListChanges,vallookup,posdict)   
2876        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2877
2878    def OnSaveSelSeqCSV(event):
2879        'export the selected columns to a .csv file from menu command'
2880        OnSaveSelSeq(event,csv=True)
2881       
2882    def OnSaveSeqCSV(event):
2883        'export all columns to a .csv file from menu command'
2884        OnSaveSelSeq(event,csv=True,allcols=True)
2885       
2886    def OnSaveSelSeq(event,csv=False,allcols=False):
2887        'export the selected columns to a .txt or .csv file from menu command'
2888        def WriteCSV():
2889            def WriteList(headerItems):
2890                line = ''
2891                for lbl in headerItems:
2892                    if line: line += ','
2893                    line += '"'+lbl+'"'
2894                return line
2895            head = ['name']
2896            for col in cols:
2897                item = G2frame.SeqTable.GetColLabelValue(col)
2898                # get rid of labels that have Unicode characters
2899                if not all([ord(c) < 128 and ord(c) != 0 for c in item]): item = '?'
2900                if col in havesig:
2901                    head += [item,'esd-'+item]
2902                else:
2903                    head += [item]
2904            SeqFile.write(WriteList(head)+'\n')
2905            for row,name in enumerate(saveNames):
2906                line = '"'+saveNames[row]+'"'
2907                for col in cols:
2908                    if col in havesig:
2909                        line += ','+str(saveData[col][row])+','+str(saveSigs[col][row])
2910                    else:
2911                        line += ','+str(saveData[col][row])
2912                SeqFile.write(line+'\n')
2913        def WriteSeq():
2914            lenName = len(saveNames[0])
2915            line = %s  '%('name'.center(lenName))
2916            for col in cols:
2917                item = G2frame.SeqTable.GetColLabelValue(col)
2918                if col in havesig:
2919                    line += ' %12s %12s '%(item.center(12),'esd'.center(12))
2920                else:
2921                    line += ' %12s '%(item.center(12))
2922            SeqFile.write(line+'\n')
2923            for row,name in enumerate(saveNames):
2924                line = " '%s' "%(saveNames[row])
2925                for col in cols:
2926                    if col in havesig:
2927                        line += ' %12.6f %12.6f '%(saveData[col][row],saveSigs[col][row])
2928                    else:
2929                        line += ' %12.6f '%saveData[col][row]
2930                SeqFile.write(line+'\n')
2931
2932        # start of OnSaveSelSeq code
2933        if allcols:
2934            cols = range(G2frame.SeqTable.GetNumberCols())
2935        else:
2936            cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2937        nrows = G2frame.SeqTable.GetNumberRows()
2938        if not cols:
2939            G2frame.ErrorDialog('Select columns',
2940                             'No columns selected in table. Click on column labels to select fields for output.')
2941            return
2942        saveNames = [G2frame.SeqTable.GetRowLabelValue(r) for r in range(nrows)]
2943        saveData = {}
2944        saveSigs = {}
2945        havesig = []
2946        for col in cols:
2947            name,vals,sigs = GetColumnInfo(col)
2948            saveData[col] = vals
2949            if sigs:
2950                havesig.append(col)
2951                saveSigs[col] = sigs
2952        if csv:
2953            wild = 'CSV output file (*.csv)|*.csv'
2954        else:
2955            wild = 'Text output file (*.txt)|*.txt'
2956        dlg = wx.FileDialog(
2957            G2frame,
2958            'Choose text output file for your selection', '.', '', 
2959            wild,wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2960        try:
2961            if dlg.ShowModal() == wx.ID_OK:
2962                SeqTextFile = dlg.GetPath()
2963                SeqTextFile = G2IO.FileDlgFixExt(dlg,SeqTextFile) 
2964                SeqFile = open(SeqTextFile,'w')
2965                if csv:
2966                    WriteCSV()
2967                else:
2968                    WriteSeq()
2969                SeqFile.close()
2970        finally:
2971            dlg.Destroy()
2972               
2973    def striphist(var,insChar=''):
2974        'strip a histogram number from a var name'
2975        sv = var.split(':')
2976        if len(sv) <= 1: return var
2977        if sv[1]:
2978            sv[1] = insChar
2979        return ':'.join(sv)
2980       
2981    def plotSpCharFix(lbl):
2982        'Change selected unicode characters to their matplotlib equivalent'
2983        for u,p in [
2984            (u'\u03B1',r'$\alpha$'),
2985            (u'\u03B2',r'$\beta$'),
2986            (u'\u03B3',r'$\gamma$'),
2987            (u'\u0394\u03C7',r'$\Delta\chi$'),
2988            ]:
2989            lbl = lbl.replace(u,p)
2990        return lbl
2991   
2992    def SelectXaxis():
2993        'returns a selected column number (or None) as the X-axis selection'
2994        ncols = G2frame.SeqTable.GetNumberCols()
2995        colNames = [G2frame.SeqTable.GetColLabelValue(r) for r in range(ncols)]
2996        dlg = G2SingleChoiceDialog(
2997            G2frame.dataDisplay,
2998            'Select x-axis parameter for plot or Cancel for sequence number',
2999            'Select X-axis',
3000            colNames)
3001        try:
3002            if dlg.ShowModal() == wx.ID_OK:
3003                col = dlg.GetSelection()
3004            else:
3005                col = None
3006        finally:
3007            dlg.Destroy()
3008        return col
3009   
3010    def EnablePseudoVarMenus():
3011        'Enables or disables the PseudoVar menu items that require existing defs'
3012        if Controls['SeqPseudoVars']:
3013            val = True
3014        else:
3015            val = False
3016        G2frame.dataFrame.SequentialPvars.Enable(wxDELSEQVAR,val)
3017        G2frame.dataFrame.SequentialPvars.Enable(wxEDITSEQVAR,val)
3018
3019    def DelPseudoVar(event):
3020        'Ask the user to select a pseudo var expression to delete'
3021        choices = Controls['SeqPseudoVars'].keys()
3022        selected = ItemSelector(
3023            choices,G2frame.dataFrame,
3024            multiple=True,
3025            title='Select expressions to remove',
3026            header='Delete expression')
3027        if selected is None: return
3028        for item in selected:
3029            del Controls['SeqPseudoVars'][choices[item]]
3030        if selected:
3031            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3032
3033    def EditPseudoVar(event):
3034        'Edit an existing pseudo var expression'
3035        choices = Controls['SeqPseudoVars'].keys()
3036        if len(choices) == 1:
3037            selected = 0
3038        else:
3039            selected = ItemSelector(
3040                choices,G2frame.dataFrame,
3041                multiple=False,
3042                title='Select an expression to edit',
3043                header='Edit expression')
3044        if selected is not None:
3045            dlg = G2exG.ExpressionDialog(
3046                G2frame.dataDisplay,PSvarDict,
3047                Controls['SeqPseudoVars'][choices[selected]],
3048                header="Edit the PseudoVar expression",
3049                VarLabel="PseudoVar #"+str(selected+1),
3050                fit=False)
3051            newobj = dlg.Show(True)
3052            if newobj:
3053                calcobj = G2obj.ExpressionCalcObj(newobj)
3054                del Controls['SeqPseudoVars'][choices[selected]]
3055                Controls['SeqPseudoVars'][calcobj.eObj.expression] = newobj
3056                UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3057       
3058    def AddNewPseudoVar(event):
3059        'Create a new pseudo var expression'
3060        dlg = G2exG.ExpressionDialog(
3061            G2frame.dataDisplay,PSvarDict,
3062            header='Enter an expression for a PseudoVar here',
3063            VarLabel = "New PseudoVar",
3064            fit=False)
3065        obj = dlg.Show(True)
3066        dlg.Destroy()
3067        if obj:
3068            calcobj = G2obj.ExpressionCalcObj(obj)
3069            Controls['SeqPseudoVars'][calcobj.eObj.expression] = obj
3070            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
3071
3072    def UpdateParmDict(parmDict):
3073        '''generate the atom positions and the direct & reciprocal cell values,
3074        because they might be needed to evaluate the pseudovar
3075        '''
3076        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
3077                         ['A'+str(i) for i in range(6)])
3078                     )
3079        delList = []
3080        phaselist = []
3081        for item in parmDict: 
3082            if ':' not in item: continue
3083            key = item.split(':')
3084            if len(key) < 3: continue
3085            # remove the dA[xyz] terms, they would only bring confusion
3086            if key[2].startswith('dA'):
3087                delList.append(item)
3088            # compute and update the corrected reciprocal cell terms using the Dij values
3089            elif key[2] in Ddict:
3090                if key[0] not in phaselist: phaselist.append(key[0])
3091                akey = key[0]+'::'+Ddict[key[2]]
3092                parmDict[akey] -= parmDict[item]
3093                delList.append(item)
3094        for item in delList:
3095            del parmDict[item]               
3096        for i in phaselist:
3097            pId = int(i)
3098            # apply cell symmetry
3099            A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],parmDict,zeroDict[pId])
3100            # convert to direct cell & add the unique terms to the dictionary
3101            for i,val in enumerate(G2lat.A2cell(A)):
3102                if i in uniqCellIndx[pId]:
3103                    lbl = str(pId)+'::'+cellUlbl[i]
3104                    parmDict[lbl] = val
3105            lbl = str(pId)+'::'+'vol'
3106            parmDict[lbl] = G2lat.calc_V(A)
3107        return parmDict
3108
3109    def EvalPSvarDeriv(calcobj,parmDict,sampleDict,var,ESD):
3110        '''Evaluate an expression derivative with respect to a
3111        GSAS-II variable name.
3112
3113        Note this likely could be faster if the loop over calcobjs were done
3114        inside after the Dict was created.
3115        '''
3116        step = ESD/10
3117        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
3118                         ['A'+str(i) for i in range(6)])
3119                     )
3120        results = []
3121        phaselist = []
3122        VparmDict = sampleDict.copy()
3123        for incr in step,-step:
3124            VparmDict.update(parmDict.copy())           
3125            # as saved, the parmDict has updated 'A[xyz]' values, but 'dA[xyz]'
3126            # values are not zeroed: fix that!
3127            VparmDict.update({item:0.0 for item in parmDict if 'dA' in item})
3128            VparmDict[var] += incr
3129            G2mv.Dict2Map(VparmDict,[]) # apply constraints
3130            # generate the atom positions and the direct & reciprocal cell values now, because they might
3131            # needed to evaluate the pseudovar
3132            for item in VparmDict:
3133                if item in sampleDict:
3134                    continue 
3135                if ':' not in item: continue
3136                key = item.split(':')
3137                if len(key) < 3: continue
3138                # apply any new shifts to atom positions
3139                if key[2].startswith('dA'):
3140                    VparmDict[''.join(item.split('d'))] += VparmDict[item]
3141                    VparmDict[item] = 0.0
3142                # compute and update the corrected reciprocal cell terms using the Dij values
3143                if key[2] in Ddict:
3144                    if key[0] not in phaselist: phaselist.append(key[0])
3145                    akey = key[0]+'::'+Ddict[key[2]]
3146                    VparmDict[akey] -= VparmDict[item]
3147            for i in phaselist:
3148                pId = int(i)
3149                # apply cell symmetry
3150                A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],VparmDict,zeroDict[pId])
3151                # convert to direct cell & add the unique terms to the dictionary
3152                for i,val in enumerate(G2lat.A2cell(A)):
3153                    if i in uniqCellIndx[pId]:
3154                        lbl = str(pId)+'::'+cellUlbl[i]
3155                        VparmDict[lbl] = val
3156                lbl = str(pId)+'::'+'vol'
3157                VparmDict[lbl] = G2lat.calc_V(A)
3158            # dict should be fully updated, use it & calculate
3159            calcobj.SetupCalc(VparmDict)
3160            results.append(calcobj.EvalExpression())
3161        return (results[0] - results[1]) / (2.*step)
3162       
3163    def EnableParFitEqMenus():
3164        'Enables or disables the Parametric Fit menu items that require existing defs'
3165        if Controls['SeqParFitEqList']:
3166            val = True
3167        else:
3168            val = False
3169        G2frame.dataFrame.SequentialPfit.Enable(wxDELPARFIT,val)
3170        G2frame.dataFrame.SequentialPfit.Enable(wxEDITPARFIT,val)
3171        G2frame.dataFrame.SequentialPfit.Enable(wxDOPARFIT,val)
3172
3173    def ParEqEval(Values,calcObjList,varyList):
3174        '''Evaluate the parametric expression(s)
3175        :param list Values: a list of values for each variable parameter
3176        :param list calcObjList: a list of :class:`GSASIIobj.ExpressionCalcObj`
3177          expression objects to evaluate
3178        :param list varyList: a list of variable names for each value in Values
3179        '''
3180        result = []
3181        for calcobj in calcObjList:
3182            calcobj.UpdateVars(varyList,Values)
3183            result.append((calcobj.depVal-calcobj.EvalExpression())/calcobj.depSig)
3184        return result
3185
3186    def DoParEqFit(event,eqObj=None):
3187        'Parametric fit minimizer'
3188        varyValueDict = {} # dict of variables and their initial values
3189        calcObjList = [] # expression objects, ready to go for each data point
3190        if eqObj is not None:
3191            eqObjList = [eqObj,]
3192        else:
3193            eqObjList = Controls['SeqParFitEqList']
3194        UseFlags = G2frame.SeqTable.GetColValues(0)         
3195        for obj in eqObjList:
3196            expr = obj.expression
3197            # assemble refined vars for this equation
3198            varyValueDict.update({var:val for var,val in obj.GetVariedVarVal()})
3199            # lookup dependent var position
3200            depVar = obj.GetDepVar()
3201            if depVar in colLabels:
3202                indx = colLabels.index(depVar)
3203            else:
3204                raise Exception('Dependent variable '+depVar+' not found')
3205            # assemble a list of the independent variables
3206            indepVars = obj.GetIndependentVars()
3207            # loop over each datapoint
3208            for j,row in enumerate(zip(*colList)):
3209                if not UseFlags[j]: continue
3210                # assemble equations to fit
3211                calcobj = G2obj.ExpressionCalcObj(obj)
3212                # prepare a dict of needed independent vars for this expression
3213                indepVarDict = {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
3214                calcobj.SetupCalc(indepVarDict)               
3215                # values and sigs for current value of dependent var
3216                calcobj.depVal = row[indx]
3217                calcobj.depSig = colSigs[indx][j]
3218                calcObjList.append(calcobj)
3219        # varied parameters
3220        varyList = varyValueDict.keys()
3221        values = varyValues = [varyValueDict[key] for key in varyList]
3222        if not varyList:
3223            print 'no variables to refine!'
3224            return
3225        try:
3226            result = so.leastsq(ParEqEval,varyValues,full_output=True,   #ftol=Ftol,
3227                                args=(calcObjList,varyList)
3228                                )
3229            values = result[0]
3230            covar = result[1]
3231            if covar is None:
3232                raise Exception
3233            esdDict = {}
3234            for i,avar in enumerate(varyList):
3235                esdDict[avar] = np.sqrt(covar[i,i])
3236        except:
3237            print('====> Fit failed')
3238            return
3239        print('==== Fit Results ====')
3240        for obj in eqObjList:
3241            obj.UpdateVariedVars(varyList,values)
3242            ind = '      '
3243            print('  '+obj.GetDepVar()+' = '+obj.expression)
3244            for var in obj.assgnVars:
3245                print(ind+var+' = '+obj.assgnVars[var])
3246            for var in obj.freeVars:
3247                avar = "::"+obj.freeVars[var][0]
3248                val = obj.freeVars[var][1]
3249                if obj.freeVars[var][2]:
3250                    print(ind+var+' = '+avar + " = " + G2mth.ValEsd(val,esdDict[avar]))
3251                else:
3252                    print(ind+var+' = '+avar + " =" + G2mth.ValEsd(val,0))
3253        # create a plot for each parametric variable
3254        for fitnum,obj in enumerate(eqObjList):
3255            calcobj = G2obj.ExpressionCalcObj(obj)
3256            # lookup dependent var position
3257            indx = colLabels.index(obj.GetDepVar())
3258            # assemble a list of the independent variables
3259            indepVars = obj.GetIndependentVars()           
3260            # loop over each datapoint
3261            fitvals = []
3262            for j,row in enumerate(zip(*colList)):
3263                calcobj.SetupCalc(
3264                    {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
3265                    )
3266                fitvals.append(calcobj.EvalExpression())
3267            G2plt.PlotSelectedSequence(
3268                G2frame,[indx],GetColumnInfo,SelectXaxis,
3269                fitnum,fitvals)
3270
3271    def SingleParEqFit(eqObj):
3272        DoParEqFit(None,eqObj)
3273
3274    def DelParFitEq(event):
3275        'Ask the user to select function to delete'
3276        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3277        selected = ItemSelector(
3278            txtlst,G2frame.dataFrame,
3279            multiple=True,
3280            title='Select a parametric equation(s) to remove',
3281            header='Delete equation')
3282        if selected is None: return
3283        Controls['SeqParFitEqList'] = [obj for i,obj in enumerate(Controls['SeqParFitEqList']) if i not in selected]
3284        EnableParFitEqMenus()
3285        if Controls['SeqParFitEqList']: DoParEqFit(event)
3286       
3287    def EditParFitEq(event):
3288        'Edit an existing parametric equation'
3289        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3290        if len(txtlst) == 1:
3291            selected = 0
3292        else:
3293            selected = ItemSelector(
3294                txtlst,G2frame.dataFrame,
3295                multiple=False,
3296                title='Select a parametric equation to edit',
3297                header='Edit equation')
3298        if selected is not None:
3299            dlg = G2exG.ExpressionDialog(
3300                G2frame.dataDisplay,indepVarDict,
3301                Controls['SeqParFitEqList'][selected],
3302                depVarDict=depVarDict,
3303                header="Edit the formula for this minimization function",
3304                ExtraButton=['Fit',SingleParEqFit])
3305            newobj = dlg.Show(True)
3306            if newobj:
3307                calcobj = G2obj.ExpressionCalcObj(newobj)
3308                Controls['SeqParFitEqList'][selected] = newobj
3309                EnableParFitEqMenus()
3310            if Controls['SeqParFitEqList']: DoParEqFit(event)
3311
3312    def AddNewParFitEq(event):
3313        'Create a new parametric equation to be fit to sequential results'
3314
3315        # compile the variable names used in previous freevars to avoid accidental name collisions
3316        usedvarlist = []
3317        for obj in Controls['SeqParFitEqList']:
3318            for var in obj.freeVars:
3319                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3320
3321        dlg = G2exG.ExpressionDialog(
3322            G2frame.dataDisplay,indepVarDict,
3323            depVarDict=depVarDict,
3324            header='Define an equation to minimize in the parametric fit',
3325            ExtraButton=['Fit',SingleParEqFit],
3326            usedVars=usedvarlist)
3327        obj = dlg.Show(True)
3328        dlg.Destroy()
3329        if obj:
3330            Controls['SeqParFitEqList'].append(obj)
3331            EnableParFitEqMenus()
3332            if Controls['SeqParFitEqList']: DoParEqFit(event)
3333               
3334    def CopyParFitEq(event):
3335        'Copy an existing parametric equation to be fit to sequential results'
3336        # compile the variable names used in previous freevars to avoid accidental name collisions
3337        usedvarlist = []
3338        for obj in Controls['SeqParFitEqList']:
3339            for var in obj.freeVars:
3340                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3341        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3342        if len(txtlst) == 1:
3343            selected = 0
3344        else:
3345            selected = ItemSelector(
3346                txtlst,G2frame.dataFrame,
3347                multiple=False,
3348                title='Select a parametric equation to copy',
3349                header='Copy equation')
3350        if selected is not None:
3351            newEqn = copy.deepcopy(Controls['SeqParFitEqList'][selected])
3352            for var in newEqn.freeVars:
3353                newEqn.freeVars[var][0] = G2obj.MakeUniqueLabel(newEqn.freeVars[var][0],usedvarlist)
3354            dlg = G2exG.ExpressionDialog(
3355                G2frame.dataDisplay,indepVarDict,
3356                newEqn,
3357                depVarDict=depVarDict,
3358                header="Edit the formula for this minimization function",
3359                ExtraButton=['Fit',SingleParEqFit])
3360            newobj = dlg.Show(True)
3361            if newobj:
3362                calcobj = G2obj.ExpressionCalcObj(newobj)
3363                Controls['SeqParFitEqList'].append(newobj)
3364                EnableParFitEqMenus()
3365            if Controls['SeqParFitEqList']: DoParEqFit(event)
3366                                           
3367    def GridSetToolTip(row,col):
3368        '''Routine to show standard uncertainties for each element in table
3369        as a tooltip
3370        '''
3371        if colSigs[col]:
3372            return u'\u03c3 = '+str(colSigs[col][row])
3373        return ''
3374       
3375    def GridColLblToolTip(col):
3376        '''Define a tooltip for a column. This will be the user-entered value
3377        (from data['variableLabels']) or the default name
3378        '''
3379        if col < 0 or col > len(colLabels):
3380            print 'Illegal column #',col
3381            return
3382        var = colLabels[col]
3383        return variableLabels.get(var,G2obj.fmtVarDescr(var))
3384       
3385    def SetLabelString(event):
3386        '''Define or edit the label for a column in the table, to be used
3387        as a tooltip and for plotting
3388        '''
3389        col = event.GetCol()
3390        if col < 0 or col > len(colLabels):
3391            return
3392        var = colLabels[col]
3393        lbl = variableLabels.get(var,G2obj.fmtVarDescr(var))
3394        dlg = SingleStringDialog(G2frame.dataFrame,'Set variable label',
3395                                 'Set a new name for variable '+var,lbl,size=(400,-1))
3396        if dlg.Show():
3397            variableLabels[var] = dlg.GetValue()
3398        dlg.Destroy()
3399       
3400    #def GridRowLblToolTip(row): return 'Row ='+str(row)
3401   
3402    # lookup table for unique cell parameters by symmetry
3403    cellGUIlist = [
3404        [['m3','m3m'],(0,)],
3405        [['3R','3mR'],(0,3)],
3406        [['3','3m1','31m','6/m','6/mmm','4/m','4/mmm'],(0,2)],
3407        [['mmm'],(0,1,2)],
3408        [['2/m'+'a'],(0,1,2,3)],
3409        [['2/m'+'b'],(0,1,2,4)],
3410        [['2/m'+'c'],(0,1,2,5)],
3411        [['-1'],(0,1,2,3,4,5)],
3412        ]
3413    # cell labels
3414    cellUlbl = ('a','b','c',u'\u03B1',u'\u03B2',u'\u03B3') # unicode a,b,c,alpha,beta,gamma
3415
3416    #======================================================================
3417    # start processing sequential results here (UpdateSeqResults)
3418    #======================================================================
3419    if not data:
3420        print 'No sequential refinement results'
3421        return
3422    variableLabels = data.get('variableLabels',{})
3423    data['variableLabels'] = variableLabels
3424    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
3425    Controls = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Controls'))
3426    # create a place to store Pseudo Vars & Parametric Fit functions, if not present
3427    if 'SeqPseudoVars' not in Controls: Controls['SeqPseudoVars'] = {}
3428    if 'SeqParFitEqList' not in Controls: Controls['SeqParFitEqList'] = []
3429    histNames = data['histNames']
3430    if G2frame.dataDisplay:
3431        G2frame.dataDisplay.Destroy()
3432    if not G2frame.dataFrame.GetStatusBar():
3433        Status = G2frame.dataFrame.CreateStatusBar()
3434        Status.SetStatusText("Select column to export; Double click on column to plot data; on row for Covariance")
3435    sampleParms = GetSampleParms()
3436
3437    # make dict of varied atom coords keyed by absolute position
3438    newAtomDict = data[histNames[0]].get('newAtomDict',{}) # dict with atom positions; relative & absolute
3439    # Possible error: the next might need to be data[histNames[0]]['varyList']
3440    # error will arise if there constraints on coordinates?
3441    atomLookup = {newAtomDict[item][0]:item for item in newAtomDict if item in data['varyList']}
3442   
3443    # make dict of varied cell parameters equivalents
3444    ESDlookup = {} # provides the Dij term for each Ak term (where terms are refined)
3445    Dlookup = {} # provides the Ak term for each Dij term (where terms are refined)
3446    # N.B. These Dij vars are missing a histogram #
3447    newCellDict = data[histNames[0]].get('newCellDict',{})
3448    for item in newCellDict:
3449        if item in data['varyList']:
3450            ESDlookup[newCellDict[item][0]] = item
3451            Dlookup[item] = newCellDict[item][0]
3452    # add coordinate equivalents to lookup table
3453    for parm in atomLookup:
3454        Dlookup[atomLookup[parm]] = parm
3455        ESDlookup[parm] = atomLookup[parm]
3456
3457    # get unit cell & symmetry for all phases & initial stuff for later use
3458    RecpCellTerms = {}
3459    SGdata = {}
3460    uniqCellIndx = {}
3461    initialCell = {}
3462    RcellLbls = {}
3463    zeroDict = {}
3464    Rcelldict = {}
3465    for phase in Phases:
3466        phasedict = Phases[phase]
3467        pId = phasedict['pId']
3468        pfx = str(pId)+'::' # prefix for A values from phase
3469        RcellLbls[pId] = [pfx+'A'+str(i) for i in range(6)]
3470        RecpCellTerms[pId] = G2lat.cell2A(phasedict['General']['Cell'][1:7])
3471        zeroDict[pId] = dict(zip(RcellLbls[pId],6*[0.,]))
3472        SGdata[pId] = phasedict['General']['SGData']
3473        Rcelldict.update({lbl:val for lbl,val in zip(RcellLbls[pId],RecpCellTerms[pId])})
3474        laue = SGdata[pId]['SGLaue']
3475        if laue == '2/m':
3476            laue += SGdata[pId]['SGUniq']
3477        for symlist,celllist in cellGUIlist:
3478            if laue in symlist:
3479                uniqCellIndx[pId] = celllist
3480                break
3481        else: # should not happen
3482            uniqCellIndx[pId] = range(6)
3483        for i in uniqCellIndx[pId]:
3484            initialCell[str(pId)+'::A'+str(i)] =  RecpCellTerms[pId][i]
3485
3486    SetDataMenuBar(G2frame,G2frame.dataFrame.SequentialMenu)
3487    G2frame.dataFrame.SetLabel('Sequential refinement results')
3488    if not G2frame.dataFrame.GetStatusBar():
3489        Status = G2frame.dataFrame.CreateStatusBar()
3490        Status.SetStatusText('')
3491    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRenameSelSeq, id=wxID_RENAMESEQSEL)
3492    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeq, id=wxID_SAVESEQSEL)
3493    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeqCSV, id=wxID_SAVESEQSELCSV)
3494    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSeqCSV, id=wxID_SAVESEQCSV)
3495    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotSelSeq, id=wxID_PLOTSEQSEL)
3496    G2frame.dataFrame.Bind(wx.EVT_MENU, OnReOrgSelSeq, id=wxID_ORGSEQSEL)
3497    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewPseudoVar, id=wxADDSEQVAR)
3498    G2frame.dataFrame.Bind(wx.EVT_MENU, DelPseudoVar, id=wxDELSEQVAR)
3499    G2frame.dataFrame.Bind(wx.EVT_MENU, EditPseudoVar, id=wxEDITSEQVAR)
3500    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewParFitEq, id=wxADDPARFIT)
3501    G2frame.dataFrame.Bind(wx.EVT_MENU, CopyParFitEq, id=wxCOPYPARFIT)
3502    G2frame.dataFrame.Bind(wx.EVT_MENU, DelParFitEq, id=wxDELPARFIT)
3503    G2frame.dataFrame.Bind(wx.EVT_MENU, EditParFitEq, id=wxEDITPARFIT)
3504    G2frame.dataFrame.Bind(wx.EVT_MENU, DoParEqFit, id=wxDOPARFIT)
3505    EnablePseudoVarMenus()
3506    EnableParFitEqMenus()
3507
3508    # scan for locations where the variables change
3509    VaryListChanges = [] # histograms where there is a change
3510    combinedVaryList = []
3511    firstValueDict = {}
3512    vallookup = {}
3513    posdict = {}
3514    prevVaryList = []
3515    for i,name in enumerate(histNames):
3516        for var,val,sig in zip(data[name]['varyList'],data[name]['variables'],data[name]['sig']):
3517            svar = striphist(var,'*') # wild-carded
3518            if svar not in combinedVaryList:
3519                # add variables to list as they appear
3520                combinedVaryList.append(svar)
3521                firstValueDict[svar] = (val,sig)
3522        if prevVaryList != data[name]['varyList']: # this refinement has a different refinement list from previous
3523            prevVaryList = data[name]['varyList']
3524            vallookup[name] = dict(zip(data[name]['varyList'],data[name]['variables']))
3525            posdict[name] = {}
3526            for var in data[name]['varyList']:
3527                svar = striphist(var,'*')
3528                posdict[name][combinedVaryList.index(svar)] = svar
3529            VaryListChanges.append(name)
3530    if len(VaryListChanges) > 1:
3531        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,True)
3532    else:
3533        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,False)
3534    #-----------------------------------------------------------------------------------
3535    # build up the data table by columns -----------------------------------------------
3536    nRows = len(histNames)
3537    colList = [nRows*[True]]
3538    colSigs = [None]
3539    colLabels = ['Use']
3540    Types = [wg.GRID_VALUE_BOOL]
3541    # start with Rwp values
3542    if 'IMG ' not in histNames[0][:4]:
3543        colList += [[data[name]['Rvals']['Rwp'] for name in histNames]]
3544        colSigs += [None]
3545        colLabels += ['Rwp']
3546        Types += [wg.GRID_VALUE_FLOAT+':10,3',]
3547    # add % change in Chi^2 in last cycle
3548    if histNames[0][:4] not in ['SASD','IMG '] and Controls.get('ShowCell'):
3549        colList += [[100.*data[name]['Rvals'].get('DelChi2',-1) for name in histNames]]
3550        colSigs += [None]
3551        colLabels += [u'\u0394\u03C7\u00B2 (%)']
3552        Types += [wg.GRID_VALUE_FLOAT,]
3553    deltaChiCol = len(colLabels)-1
3554    # add changing sample parameters to table
3555    for key in sampleParms:
3556        colList += [sampleParms[key]]
3557        colSigs += [None]
3558        colLabels += [key]
3559        Types += [wg.GRID_VALUE_FLOAT,]
3560    sampleDict = {}
3561    for i,name in enumerate(histNames):
3562        sampleDict[name] = dict(zip(sampleParms.keys(),[sampleParms[key][i] for key in sampleParms.keys()])) 
3563    # add unique cell parameters TODO: review this where the cell symmetry changes (when possible)
3564    if Controls.get('ShowCell',False):
3565        for pId in sorted(RecpCellTerms):
3566            pfx = str(pId)+'::' # prefix for A values from phase
3567            cells = []
3568            cellESDs = []
3569            colLabels += [pfx+cellUlbl[i] for i in uniqCellIndx[pId]]
3570            colLabels += [pfx+'Vol']
3571            Types += (1+len(uniqCellIndx[pId]))*[wg.GRID_VALUE_FLOAT,]
3572            for name in histNames:
3573                covData = {
3574                    'varyList': [Dlookup.get(striphist(v),v) for v in data[name]['varyList']],
3575                    'covMatrix': data[name]['covMatrix']
3576                    }
3577                A = RecpCellTerms[pId][:] # make copy of starting A values
3578                # update with refined values
3579                for i in range(6):
3580                    var = str(pId)+'::A'+str(i)
3581                    if var in ESDlookup:
3582                        val = data[name]['newCellDict'][ESDlookup[var]][1] # get refined value
3583                        A[i] = val # override with updated value
3584                # apply symmetry
3585                Albls = [pfx+'A'+str(i) for i in range(6)]
3586                cellDict = dict(zip(Albls,A))
3587                A,zeros = G2stIO.cellFill(pfx,SGdata[pId],cellDict,zeroDict[pId])
3588                # convert to direct cell & add only unique values to table
3589                c = G2lat.A2cell(A)
3590                vol = G2lat.calc_V(A)
3591                cE = G2stIO.getCellEsd(pfx,SGdata[pId],A,covData)
3592                cells += [[c[i] for i in uniqCellIndx[pId]]+[vol]]
3593                cellESDs += [[cE[i] for i in uniqCellIndx[pId]]+[cE[-1]]]
3594            colList += zip(*cells)
3595            colSigs += zip(*cellESDs)
3596    # sort out the variables in their selected order
3597    varcols = 0
3598    for d in posdict.itervalues():
3599        varcols = max(varcols,max(d.keys())+1)
3600    # get labels for each column
3601    for i in range(varcols):
3602        lbl = ''
3603        for h in VaryListChanges:
3604            if posdict[h].get(i):
3605                if posdict[h].get(i) in lbl: continue
3606                if lbl != "": lbl += '/'
3607                lbl += posdict[h].get(i)
3608        colLabels.append(lbl)
3609    Types += varcols*[wg.GRID_VALUE_FLOAT]
3610    vals = []
3611    esds = []
3612    varsellist = None        # will be a list of variable names in the order they are selected to appear
3613    # tabulate values for each hist, leaving None for blank columns
3614    for name in histNames:
3615        if name in posdict:
3616            varsellist = [posdict[name].get(i) for i in range(varcols)]
3617            # translate variable names to how they will be used in the headings
3618            vs = [striphist(v,'*') for v in data[name]['varyList']]
3619            # determine the index for each column (or None) in the data[]['variables'] and ['sig'] lists
3620            sellist = [vs.index(v) if v is not None else None for v in varsellist]
3621            #sellist = [i if striphist(v,'*') in varsellist else None for i,v in enumerate(data[name]['varyList'])]
3622        if not varsellist: raise Exception()
3623        vals.append([data[name]['variables'][s] if s is not None else None for s in sellist])
3624        esds.append([data[name]['sig'][s] if s is not None else None for s in sellist])
3625        #GSASIIpath.IPyBreak()
3626    colList += zip(*vals)
3627    colSigs += zip(*esds)
3628               
3629    # tabulate constrained variables, removing histogram numbers if needed
3630    # from parameter label
3631    depValDict = {}
3632    depSigDict = {}
3633    for name in histNames:
3634        for var in data[name].get('depParmDict',{}):
3635            val,sig = data[name]['depParmDict'][var]
3636            svar = striphist(var,'*')
3637            if svar not in depValDict:
3638               depValDict[svar] = [val]
3639               depSigDict[svar] = [sig]
3640            else:
3641               depValDict[svar].append(val)
3642               depSigDict[svar].append(sig)
3643    # add the dependent constrained variables to the table
3644    for var in sorted(depValDict):
3645        if len(depValDict[var]) != len(histNames): continue
3646        colLabels.append(var)
3647        Types += [wg.GRID_VALUE_FLOAT,]
3648        colSigs += [depSigDict[var]]
3649        colList += [depValDict[var]]
3650
3651    # add atom parameters to table
3652    colLabels += atomLookup.keys()
3653    Types += len(atomLookup)*[wg.GRID_VALUE_FLOAT]
3654    for parm in sorted(atomLookup):
3655        colList += [[data[name]['newAtomDict'][atomLookup[parm]][1] for name in histNames]]
3656        if atomLookup[parm] in data[histNames[0]]['varyList']:
3657            col = data[histNames[0]]['varyList'].index(atomLookup[parm])
3658            colSigs += [[data[name]['sig'][col] for name in histNames]]
3659        else:
3660            colSigs += [None] # should not happen
3661    # evaluate Pseudovars, their ESDs and add them to grid
3662    for expr in Controls['SeqPseudoVars']:
3663        obj = Controls['SeqPseudoVars'][expr]
3664        calcobj = G2obj.ExpressionCalcObj(obj)
3665        valList = []
3666        esdList = []
3667        for seqnum,name in enumerate(histNames):
3668            sigs = data[name]['sig']
3669            G2mv.InitVars()
3670            parmDict = data[name].get('parmDict')
3671            badVary = data[name].get('badVary',[])
3672            constraintInfo = data[name].get('constraintInfo',[[],[],{},[],seqnum])
3673            groups,parmlist,constrDict,fixedList,ihst = constraintInfo
3674            varyList = data[name]['varyList']
3675            parmDict = data[name]['parmDict']
3676            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict,SeqHist=ihst)
3677            derivs = np.array(
3678                [EvalPSvarDeriv(calcobj,parmDict.copy(),sampleDict[name],var,ESD)
3679                 for var,ESD in zip(varyList,sigs)]
3680                )
3681            esdList.append(np.sqrt(
3682                np.inner(derivs,np.inner(data[name]['covMatrix'],derivs.T))
3683                ))
3684            PSvarDict = parmDict.copy()
3685            PSvarDict.update(sampleDict[name])
3686            UpdateParmDict(PSvarDict)
3687            calcobj.UpdateDict(PSvarDict)
3688            valList.append(calcobj.EvalExpression())
3689        if not esdList:
3690            esdList = None
3691        colList += [valList]
3692        colSigs += [esdList]
3693        colLabels += [expr]
3694        Types += [wg.GRID_VALUE_FLOAT,]
3695    #---- table build done -------------------------------------------------------------
3696
3697    # Make dict needed for creating & editing pseudovars (PSvarDict).
3698    name = histNames[0]
3699    parmDict = data[name].get('parmDict')
3700    PSvarDict = parmDict.copy()
3701    PSvarDict.update(sampleParms)
3702    UpdateParmDict(PSvarDict)
3703    # Also dicts of dependent (depVarDict) & independent vars (indepVarDict)
3704    # for Parametric fitting from the data table
3705    parmDict = dict(zip(colLabels,zip(*colList)[0])) # scratch dict w/all values in table
3706    parmDict.update(
3707        {var:val for var,val in data[name].get('newCellDict',{}).values()} #  add varied reciprocal cell terms
3708    )
3709    name = histNames[0]
3710
3711    #******************************************************************************
3712    # create a set of values for example evaluation of pseudovars and
3713    # this does not work for refinements that have differing numbers of variables.
3714    #raise Exception
3715    indepVarDict = {}     #  values in table w/o ESDs
3716    depVarDict = {}
3717    for i,var in enumerate(colLabels):
3718        if var == 'Use': continue
3719        if colList[i][0] is None:
3720            val,sig = firstValueDict.get(var,[None,None])
3721        elif colSigs[i]:
3722            val,sig = colList[i][0],colSigs[i][0]
3723        else:
3724            val,sig = colList[i][0],None
3725        if val is None:
3726            continue
3727        elif sig is None:
3728            indepVarDict[var] = val
3729        elif striphist(var) not in Dlookup:
3730            depVarDict[var] = val
3731    # add recip cell coeff. values
3732    depVarDict.update({var:val for var,val in data[name].get('newCellDict',{}).values()})
3733
3734    G2frame.dataDisplay = GSGrid(parent=G2frame.dataFrame)
3735    G2frame.SeqTable = Table(
3736        [list(c) for c in zip(*colList)],     # convert from columns to rows
3737        colLabels=colLabels,rowLabels=histNames,types=Types)
3738    G2frame.dataDisplay.SetTable(G2frame.SeqTable, True)
3739    #G2frame.dataDisplay.EnableEditing(False)
3740    # make all but first column read-only
3741    for c in range(1,len(colLabels)):
3742        for r in range(nRows):
3743            G2frame.dataDisplay.SetCellReadOnly(r,c)
3744    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_LEFT_DCLICK, PlotSelect)
3745    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_RIGHT_CLICK, SetLabelString)
3746    G2frame.dataDisplay.SetRowLabelSize(8*len(histNames[0]))       #pretty arbitrary 8
3747    G2frame.dataDisplay.SetMargins(0,0)
3748    G2frame.dataDisplay.AutoSizeColumns(True)
3749    if prevSize:
3750        G2frame.dataDisplay.SetSize(prevSize)
3751    else:
3752        G2frame.dataFrame.setSizePosLeft([700,350])
3753    # highlight unconverged shifts
3754    if histNames[0][:4] not in ['SASD','IMG ']:
3755        for row,name in enumerate(histNames):
3756            deltaChi = G2frame.SeqTable.GetValue(row,deltaChiCol)
3757            if deltaChi > 10.:
3758                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,0,0))
3759            elif deltaChi > 1.0:
3760                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,255,0))
3761    G2frame.dataDisplay.InstallGridToolTip(GridSetToolTip,GridColLblToolTip)
3762    G2frame.dataDisplay.SendSizeEvent() # resize needed on mac
3763    G2frame.dataDisplay.Refresh() # shows colored text on mac
3764   
3765################################################################################
3766#####  Main PWDR panel
3767################################################################################           
3768       
3769def UpdatePWHKPlot(G2frame,kind,item):
3770    '''Called when the histogram main tree entry is called. Displays the
3771    histogram weight factor, refinement statistics for the histogram
3772    and the range of data for a simulation.
3773
3774    Also invokes a plot of the histogram.
3775    '''
3776    def onEditSimRange(event):
3777        'Edit simulation range'
3778        inp = [
3779            min(data[1][0]),
3780            max(data[1][0]),
3781            None
3782            ]
3783        inp[2] = (inp[1] - inp[0])/(len(data[1][0])-1.)
3784        names = ('start angle', 'end angle', 'step size')
3785        dictlst = [inp] * len(inp)
3786        elemlst = range(len(inp))
3787        dlg = G2G.ScrolledMultiEditor(
3788            G2frame,[inp] * len(inp), range(len(inp)), names,
3789            header='Edit simulation range',
3790            minvals=(0.001,0.001,0.0001),
3791            maxvals=(180.,180.,.1),
3792            )
3793        dlg.CenterOnParent()
3794        val = dlg.ShowModal()
3795        dlg.Destroy()
3796        if val != wx.ID_OK: return
3797        if inp[0] > inp[1]:
3798            end,start,step = inp
3799        else:               
3800            start,end,step = inp
3801        step = abs(step)
3802        N = int((end-start)/step)+1
3803        newdata = np.linspace(start,end,N,True)
3804        if len(newdata) < 2: return # too small a range - reject
3805        data[1] = [newdata,np.zeros_like(newdata),np.ones_like(newdata),
3806            np.zeros_like(newdata),np.zeros_like(newdata),np.zeros_like(newdata)]
3807        Tmin = newdata[0]
3808        Tmax = newdata[-1]
3809        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,item,'Limits'),
3810            [(Tmin,Tmax),[Tmin,Tmax]])
3811        UpdatePWHKPlot(G2frame,kind,item) # redisplay data screen
3812
3813    def OnPlot3DHKL(event):
3814        refList = data[1]['RefList']
3815        FoMax = np.max(refList.T[8+Super])
3816        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
3817        Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
3818        Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
3819        controls = {'Type' : 'Fosq','Iscale' : False,'HKLmax' : Hmax,'HKLmin' : Hmin,
3820            'FoMax' : FoMax,'Scale' : 1.0,'Drawing':{'viewPoint':[Vpoint,[]],'default':Vpoint[:],
3821            'backColor':[0,0,0],'depthFog':False,'Zclip':10.0,'cameraPos':10.,'Zstep':0.05,
3822            'Scale':1.0,'oldxy':[],'viewDir':[1,0,0]},'Super':Super,'SuperVec':SuperVec}
3823        G2plt.Plot3DSngl(G2frame,newPlot=True,Data=controls,hklRef=refList,Title=phaseName)
3824       
3825    def OnErrorAnalysis(event):
3826        G2plt.PlotDeltSig(G2frame,kind)
3827       
3828    def OnWtFactor(event):
3829        try:
3830            val = float(wtval.GetValue())
3831        except ValueError:
3832            val = data[0]['wtFactor']
3833        data[0]['wtFactor'] = val
3834        wtval.SetValue('%.3f'%(val))
3835       
3836    def onCopyPlotCtrls(event):
3837        '''Respond to menu item to copy multiple sections from a histogram.
3838        Need this here to pass on the G2frame object.
3839        '''
3840        G2pdG.CopyPlotCtrls(G2frame)
3841
3842    def onCopySelectedItems(event):
3843        '''Respond to menu item to copy multiple sections from a histogram.
3844        Need this here to pass on the G2frame object.
3845        '''
3846        G2pdG.CopySelectedHistItems(G2frame)
3847           
3848    data = G2frame.PatternTree.GetItemPyData(item)
3849#patches
3850    if 'wtFactor' not in data[0]:
3851        data[0] = {'wtFactor':1.0}
3852    #if isinstance(data[1],list) and kind == 'HKLF':
3853    if 'list' in str(type(data[1])) and kind == 'HKLF':
3854        RefData = {'RefList':[],'FF':[]}
3855        for ref in data[1]:
3856            RefData['RefList'].append(ref[:11]+[ref[13],])
3857            RefData['FF'].append(ref[14])
3858        data[1] = RefData
3859        G2frame.PatternTree.SetItemPyData(item,data)
3860#end patches
3861    if G2frame.dataDisplay:
3862        G2frame.dataDisplay.Destroy()
3863    if kind in ['PWDR','SASD']:
3864        SetDataMenuBar(G2frame,G2frame.dataFrame.PWDRMenu)
3865        G2frame.dataFrame.Bind(wx.EVT_MENU, OnErrorAnalysis, id=wxID_PWDANALYSIS)
3866        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopySelectedItems, id=wxID_PWDCOPY)
3867        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopyPlotCtrls, id=wxID_PLOTCTRLCOPY)
3868    elif kind in ['HKLF',]:
3869        SetDataMenuBar(G2frame,G2frame.dataFrame.HKLFMenu)
3870#        G2frame.dataFrame.Bind(wx.EVT_MENU, OnErrorAnalysis, id=wxID_PWDANALYSIS)
3871        G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlot3DHKL, id=wxID_PWD3DHKLPLOT)
3872#        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopySelectedItems, id=wxID_PWDCOPY)
3873    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
3874   
3875    mainSizer = wx.BoxSizer(wx.VERTICAL)
3876    mainSizer.Add((5,5),)
3877    wtSizer = wx.BoxSizer(wx.HORIZONTAL)
3878    wtSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Weight factor: '),0,WACV)
3879    wtval = wx.TextCtrl(G2frame.dataDisplay,-1,'%.3f'%(data[0]['wtFactor']),style=wx.TE_PROCESS_ENTER)
3880    wtval.Bind(wx.EVT_TEXT_ENTER,OnWtFactor)
3881    wtval.Bind(wx.EVT_KILL_FOCUS,OnWtFactor)
3882    wtSizer.Add(wtval,0,WACV)
3883    mainSizer.Add(wtSizer)
3884    if data[0].get('Dummy'):
3885        simSizer = wx.BoxSizer(wx.HORIZONTAL)
3886        Tmin = min(data[1][0])
3887        Tmax = max(data[1][0])
3888        num = len(data[1][0])
3889        step = (Tmax - Tmin)/(num-1)
3890        t = u'2\u03b8' # 2theta
3891        lbl =  u'Simulation range: {:.2f} to {:.2f} {:s}\nwith {:.4f} steps ({:d} points)'
3892        lbl += u'\n(Edit range resets observed intensities).'
3893        lbl = lbl.format(Tmin,Tmax,t,step,num)
3894        simSizer.Add(wx.StaticText(G2frame.dataDisplay,wx.ID_ANY,lbl),
3895                    0,WACV)
3896        but = wx.Button(G2frame.dataDisplay,wx.ID_ANY,"Edit range")
3897        but.Bind(wx.EVT_BUTTON,onEditSimRange)
3898        simSizer.Add(but,0,WACV)
3899        mainSizer.Add(simSizer)
3900    if 'Nobs' in data[0]:
3901        mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
3902            ' Data residual wR: %.3f%% on %d observations'%(data[0]['wR'],data[0]['Nobs'])))
3903        for value in data[0]:
3904            if 'Nref' in value:
3905                mainSizer.Add((5,5),)
3906                pfx = value.split('Nref')[0]
3907                name = data[0].get(pfx.split(':')[0]+'::Name','?')
3908                mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' For phase '+name+':'))
3909                mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
3910                    u' Unweighted phase residuals RF\u00b2: %.3f%%, RF: %.3f%% on %d reflections  '% \
3911                    (data[0][pfx+'Rf^2'],data[0][pfx+'Rf'],data[0][value])))
3912    mainSizer.Add((5,5),)
3913    mainSizer.Layout()   
3914    G2frame.dataDisplay.SetSizer(mainSizer)
3915    Size = mainSizer.Fit(G2frame.dataFrame)
3916    Size[1] += 10
3917    G2frame.dataFrame.setSizePosLeft(Size)
3918    G2frame.PatternTree.SetItemPyData(item,data)
3919    if kind in ['PWDR','SASD']:
3920        G2plt.PlotPatterns(G2frame,plotType=kind,newPlot=True)
3921    elif kind == 'HKLF':
3922        Name = G2frame.PatternTree.GetItemText(item)
3923        phaseName = G2pdG.IsHistogramInAnyPhase(G2frame,Name)
3924        if phaseName:
3925            pId = GetPatternTreeItemId(G2frame,G2frame.root,'Phases')
3926            phaseId =  GetPatternTreeItemId(G2frame,pId,phaseName)
3927            General = G2frame.PatternTree.GetItemPyData(phaseId)['General']
3928            Super = General.get('Super',0)
3929            SuperVec = General.get('SuperVec',[])
3930        else:
3931            Super = 0
3932            SuperVec = []       
3933        refList = data[1]['RefList']
3934        FoMax = np.max(refList.T[5+data[1].get('Super',0)])
3935        page = G2frame.G2plotNB.nb.GetSelection()
3936        tab = ''
3937        if page >= 0:
3938            tab = G2frame.G2plotNB.nb.GetPageText(page)
3939        if '3D' in tab:
3940            Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
3941            Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
3942            Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
3943            Page = G2frame.G2plotNB.nb.GetPage(page)
3944            controls = Page.controls
3945            G2plt.Plot3DSngl(G2frame,newPlot=False,Data=controls,hklRef=refList,Title=phaseName)
3946        else:
3947            controls = {'Type' : 'Fo','ifFc' : True,     
3948                'HKLmax' : [int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))],
3949                'HKLmin' : [int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))],
3950                'FoMax' : FoMax,'Zone' : '001','Layer' : 0,'Scale' : 1.0,'Super':Super,'SuperVec':SuperVec}
3951            G2plt.PlotSngl(G2frame,newPlot=True,Data=controls,hklRef=refList)
3952                 
3953################################################################################
3954#####  Pattern tree routines
3955################################################################################           
3956       
3957def GetPatternTreeDataNames(G2frame,dataTypes):
3958    '''Needs a doc string
3959    '''
3960    names = []
3961    item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)       
3962    while item:
3963        name = G2frame.PatternTree.GetItemText(item)
3964        if name[:4] in dataTypes:
3965            names.append(name)
3966        item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
3967    return names
3968                         
3969def GetPatternTreeItemId(G2frame, parentId, itemText):
3970    '''Needs a doc string
3971    '''
3972    item, cookie = G2frame.PatternTree.GetFirstChild(parentId)
3973    while item:
3974        if G2frame.PatternTree.GetItemText(item) == itemText:
3975            return item
3976        item, cookie = G2frame.PatternTree.GetNextChild(parentId, cookie)
3977    return 0               
3978
3979def MovePatternTreeToGrid(G2frame,item):
3980    '''Called from GSASII.OnPatternTreeSelChanged when a item is selected on the tree
3981    '''
3982   
3983#    print G2frame.PatternTree.GetItemText(item)
3984   
3985    oldPage = None # will be set later if already on a Phase item
3986    if G2frame.dataFrame:
3987        SetDataMenuBar(G2frame)
3988        if G2frame.dataFrame.GetLabel() == 'Comments':
3989            try:
3990                data = [G2frame.dataDisplay.GetValue()]
3991                G2frame.dataDisplay.Clear() 
3992                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Comments')
3993                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
3994            except:     #clumsy but avoids dead window problem when opening another project
3995                pass
3996        elif G2frame.dataFrame.GetLabel() == 'Notebook':
3997            try:
3998                data = [G2frame.dataDisplay.GetValue()]
3999                G2frame.dataDisplay.Clear() 
4000                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Notebook')
4001                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
4002            except:     #clumsy but avoids dead window problem when opening another project
4003                pass
4004        elif 'Phase Data for' in G2frame.dataFrame.GetLabel():
4005            if G2frame.dataDisplay: 
4006                oldPage = G2frame.dataDisplay.GetSelection()
4007        G2frame.dataFrame.Clear()
4008        G2frame.dataFrame.SetLabel('')
4009    else:
4010        #create the frame for the data item window
4011        G2frame.dataFrame = DataFrame(parent=G2frame.mainPanel,frame=G2frame)
4012        G2frame.dataFrame.PhaseUserSize = None
4013       
4014    G2frame.dataFrame.Raise()           
4015    G2frame.PickId = 0
4016    parentID = G2frame.root
4017    #for i in G2frame.ExportPattern: i.Enable(False)
4018    defWid = [250,150]
4019    if item != G2frame.root:
4020        parentID = G2frame.PatternTree.GetItemParent(item)
4021    if G2frame.PatternTree.GetItemParent(item) == G2frame.root:
4022        G2frame.PatternId = item
4023        G2frame.PickId = item
4024        if G2frame.PatternTree.GetItemText(item) == 'Notebook':
4025            SetDataMenuBar(G2frame,G2frame.dataFrame.DataNotebookMenu)
4026            G2frame.PatternId = 0
4027            #for i in G2frame.ExportPattern: i.Enable(False)
4028            data = G2frame.PatternTree.GetItemPyData(item)
4029            UpdateNotebook(G2frame,data)
4030        elif G2frame.PatternTree.GetItemText(item) == 'Controls':
4031            G2frame.PatternId = 0
4032            #for i in G2frame.ExportPattern: i.Enable(False)
4033            data = G2frame.PatternTree.GetItemPyData(item)
4034            if not data:           #fill in defaults
4035                data = copy.copy(G2obj.DefaultControls)    #least squares controls
4036                G2frame.PatternTree.SetItemPyData(item,data)                             
4037            for i in G2frame.Refine: i.Enable(True)
4038            G2frame.EnableSeqRefineMenu()
4039            UpdateControls(G2frame,data)
4040        elif G2frame.PatternTree.GetItemText(item) == 'Sequential results':
4041            data = G2frame.PatternTree.GetItemPyData(item)
4042            UpdateSeqResults(G2frame,data)
4043        elif G2frame.PatternTree.GetItemText(item) == 'Covariance':
4044            data = G2frame.PatternTree.GetItemPyData(item)
4045            G2frame.dataFrame.setSizePosLeft(defWid)
4046            text = ''
4047            if 'Rvals' in data:
4048                Nvars = len(data['varyList'])
4049                Rvals = data['Rvals']
4050                text = '\nFinal residuals: \nwR = %.3f%% \nchi**2 = %.1f \nGOF = %.2f'%(Rvals['Rwp'],Rvals['chisq'],Rvals['GOF'])
4051                text += '\nNobs = %d \nNvals = %d'%(Rvals['Nobs'],Nvars)
4052                if 'lamMax' in Rvals:
4053                    text += '\nlog10 MaxLambda = %.1f'%(np.log10(Rvals['lamMax']))
4054            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
4055                value='See plot window for covariance display'+text,style=wx.TE_MULTILINE)
4056            G2plt.PlotCovariance(G2frame,data)
4057        elif G2frame.PatternTree.GetItemText(item) == 'Constraints':
4058            data = G2frame.PatternTree.GetItemPyData(item)
4059            G2cnstG.UpdateConstraints(G2frame,data)
4060        elif G2frame.PatternTree.GetItemText(item) == 'Rigid bodies':
4061            data = G2frame.PatternTree.GetItemPyData(item)
4062            G2cnstG.UpdateRigidBodies(G2frame,data)
4063        elif G2frame.PatternTree.GetItemText(item) == 'Restraints':
4064            data = G2frame.PatternTree.GetItemPyData(item)
4065            Phases = G2frame.GetPhaseData()
4066            phase = ''
4067            phaseName = ''
4068            if Phases:
4069                phaseName = Phases.keys()[0]
4070            G2frame.dataFrame.setSizePosLeft(defWid)
4071            G2restG.UpdateRestraints(G2frame,data,Phases,phaseName)
4072        elif 'IMG' in G2frame.PatternTree.GetItemText(item):
4073            G2frame.Image = item
4074            G2frame.dataFrame.SetTitle('Image Data')
4075            data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId( \
4076                G2frame,item,'Image Controls'))
4077            G2imG.UpdateImageData(G2frame,data)
4078            G2plt.PlotImage(G2frame,newPlot=True)
4079        elif 'PKS' in G2frame.PatternTree.GetItemText(item):
4080            G2plt.PlotPowderLines(G2frame)
4081        elif 'PWDR' in G2frame.PatternTree.GetItemText(item):
4082            #for i in G2frame.ExportPattern: i.Enable(True)
4083            if G2frame.EnablePlot:
4084                UpdatePWHKPlot(G2frame,'PWDR',item)
4085        elif 'SASD' in G2frame.PatternTree.GetItemText(item):
4086            #for i in G2frame.ExportPattern: i.Enable(True)
4087            if G2frame.EnablePlot:
4088                UpdatePWHKPlot(G2frame,'SASD',item)
4089        elif 'HKLF' in G2frame.PatternTree.GetItemText(item):
4090            G2frame.Sngl = True
4091            UpdatePWHKPlot(G2frame,'HKLF',item)
4092        elif 'PDF' in G2frame.PatternTree.GetItemText(item):
4093            G2frame.PatternId = item
4094            for i in G2frame.ExportPDF: i.Enable(True)
4095            G2plt.PlotISFG(G2frame,type='S(Q)')
4096        elif G2frame.PatternTree.GetItemText(item) == 'Phases':
4097            G2frame.dataFrame.setSizePosLeft(defWid)
4098            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
4099                value='Select one phase to see its parameters')           
4100    elif 'I(Q)' in G2frame.PatternTree.GetItemText(item):
4101        G2frame.PickId = item
4102        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4103        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
4104        G2pdG.UpdatePDFGrid(G2frame,data)
4105        G2plt.PlotISFG(G2frame,type='I(Q)',newPlot=True)
4106    elif 'S(Q)' in G2frame.PatternTree.GetItemText(item):
4107        G2frame.PickId = item
4108        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4109        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
4110        G2pdG.UpdatePDFGrid(G2frame,data)
4111        G2plt.PlotISFG(G2frame,type='S(Q)',newPlot=True)
4112    elif 'F(Q)' in G2frame.PatternTree.GetItemText(item):
4113        G2frame.PickId = item
4114        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4115        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
4116        G2pdG.UpdatePDFGrid(G2frame,data)
4117        G2plt.PlotISFG(G2frame,type='F(Q)',newPlot=True)
4118    elif 'G(R)' in G2frame.PatternTree.GetItemText(item):
4119        G2frame.PickId = item
4120        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4121        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
4122        G2pdG.UpdatePDFGrid(G2frame,data)
4123        G2plt.PlotISFG(G2frame,type='G(R)',newPlot=True)           
4124    elif G2frame.PatternTree.GetItemText(parentID) == 'Phases':
4125        G2frame.PickId = item
4126        data = G2frame.PatternTree.GetItemPyData(item)
4127        G2phG.UpdatePhaseData(G2frame,item,data,oldPage)
4128    elif G2frame.PatternTree.GetItemText(item) == 'Comments':
4129        SetDataMenuBar(G2frame,G2frame.dataFrame.DataCommentsMenu)
4130        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4131        G2frame.PickId = item
4132        data = G2frame.PatternTree.GetItemPyData(item)
4133        UpdateComments(G2frame,data)
4134    elif G2frame.PatternTree.GetItemText(item) == 'Image Controls':
4135        G2frame.dataFrame.SetTitle('Image Controls')
4136        G2frame.PickId = item
4137        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
4138        masks = G2frame.PatternTree.GetItemPyData(
4139            GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
4140        data = G2frame.PatternTree.GetItemPyData(item)
4141        G2imG.UpdateImageControls(G2frame,data,masks)
4142        G2plt.PlotImage(G2frame)
4143    elif G2frame.PatternTree.GetItemText(item) == 'Masks':
4144        G2frame.dataFrame.SetTitle('Masks')
4145        G2frame.PickId = item
4146        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
4147        data = G2frame.PatternTree.GetItemPyData(item)
4148        G2imG.UpdateMasks(G2frame,data)
4149        G2plt.PlotImage(G2frame)
4150    elif G2frame.PatternTree.GetItemText(item) == 'Stress/Strain':
4151        G2frame.dataFrame.SetTitle('Stress/Strain')
4152        G2frame.PickId = item
4153        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
4154        data = G2frame.PatternTree.GetItemPyData(item)
4155        G2plt.PlotImage(G2frame)
4156        G2plt.PlotStrain(G2frame,data,newPlot=True)
4157        G2imG.UpdateStressStrain(G2frame,data)
4158    elif G2frame.PatternTree.GetItemText(item) == 'PDF Controls':
4159        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4160        for i in G2frame.ExportPDF: i.Enable(True)
4161        G2frame.PickId = item
4162        data = G2frame.PatternTree.GetItemPyData(item)
4163        G2pdG.UpdatePDFGrid(G2frame,data)
4164        G2plt.PlotISFG(G2frame,type='I(Q)')
4165        G2plt.PlotISFG(G2frame,type='S(Q)')
4166        G2plt.PlotISFG(G2frame,type='F(Q)')
4167        G2plt.PlotISFG(G2frame,type='G(R)')
4168    elif G2frame.PatternTree.GetItemText(item) == 'Peak List':
4169        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4170        for i in G2frame.ExportPeakList: i.Enable(True)
4171        G2frame.PickId = item
4172        data = G2frame.PatternTree.GetItemPyData(item)
4173#patch
4174        if 'list' in str(type(data)):
4175            data = {'peaks':data,'sigDict':{}}
4176            G2frame.PatternTree.SetItemPyData(item,data)
4177#end patch
4178        G2pdG.UpdatePeakGrid(G2frame,data)
4179        G2plt.PlotPatterns(G2frame)
4180    elif G2frame.PatternTree.GetItemText(item) == 'Background':
4181        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4182        G2frame.PickId = item
4183        data = G2frame.PatternTree.GetItemPyData(item)
4184        G2pdG.UpdateBackground(G2frame,data)
4185        G2plt.PlotPatterns(G2frame)
4186    elif G2frame.PatternTree.GetItemText(item) == 'Limits':
4187        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4188        datatype = G2frame.PatternTree.GetItemText(G2frame.PatternId)[:4]
4189        G2frame.PickId = item
4190        data = G2frame.PatternTree.GetItemPyData(item)
4191        G2pdG.UpdateLimitsGrid(G2frame,data,datatype)
4192        G2plt.PlotPatterns(G2frame,plotType=datatype)
4193    elif G2frame.PatternTree.GetItemText(item) == 'Instrument Parameters':
4194        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4195        G2frame.PickId = item
4196        data = G2frame.PatternTree.GetItemPyData(item)[0]
4197        G2pdG.UpdateInstrumentGrid(G2frame,data)
4198        if 'P' in data['Type'][0]:          #powder data only
4199            G2plt.PlotPeakWidths(G2frame)
4200    elif G2frame.PatternTree.GetItemText(item) == 'Models':
4201        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4202        G2frame.PickId = item
4203        data = G2frame.PatternTree.GetItemPyData(item)
4204        G2pdG.UpdateModelsGrid(G2frame,data)
4205        G2plt.PlotPatterns(G2frame,plotType='SASD')
4206        if len(data['Size']['Distribution']):
4207            G2plt.PlotSASDSizeDist(G2frame)
4208    elif G2frame.PatternTree.GetItemText(item) == 'Substances':
4209        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4210        G2frame.PickId = item
4211        data = G2frame.PatternTree.GetItemPyData(item)
4212        G2pdG.UpdateSubstanceGrid(G2frame,data)
4213    elif G2frame.PatternTree.GetItemText(item) == 'Sample Parameters':
4214        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4215        G2frame.PickId = item
4216        data = G2frame.PatternTree.GetItemPyData(item)
4217        datatype = G2frame.PatternTree.GetItemPyData(G2frame.PatternId)[2][:4]
4218
4219        if 'Temperature' not in data:           #temp fix for old gpx files
4220            data = {'Scale':[1.0,True],'Type':'Debye-Scherrer','Absorption':[0.0,False],'DisplaceX':[0.0,False],
4221                'DisplaceY':[0.0,False],'Diffuse':[],'Temperature':300.,'Pressure':1.0,
4222                    'FreePrm1':0.,'FreePrm2':0.,'FreePrm3':0.,
4223                    'Gonio. radius':200.0}
4224            G2frame.PatternTree.SetItemPyData(item,data)
4225   
4226        G2pdG.UpdateSampleGrid(G2frame,data)
4227        G2plt.PlotPatterns(G2frame,plotType=datatype)
4228    elif G2frame.PatternTree.GetItemText(item) == 'Index Peak List':
4229        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4230        for i in G2frame.ExportPeakList: i.Enable(True)
4231        G2frame.PickId = item
4232        data = G2frame.PatternTree.GetItemPyData(item)
4233#patch
4234        if len(data) != 2:
4235            data = [data,[]]
4236            G2frame.PatternTree.SetItemPyData(item,data)
4237#end patch
4238        G2pdG.UpdateIndexPeaksGrid(G2frame,data)
4239        if 'PKS' in G2frame.PatternTree.GetItemText(G2frame.PatternId):
4240            G2plt.PlotPowderLines(G2frame)
4241        else:
4242            G2plt.PlotPatterns(G2frame)
4243    elif G2frame.PatternTree.GetItemText(item) == 'Unit Cells List':
4244        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4245        G2frame.PickId = item
4246        data = G2frame.PatternTree.GetItemPyData(item)
4247        if not data:
4248            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
4249            data.append([0,0,0,0,0,0,0,0,0,0,0,0,0,0])      #Bravais lattice flags
4250            data.append([])                                 #empty cell list
4251            data.append([])                                 #empty dmin
4252            data.append({})                                 #empty superlattice stuff
4253            G2frame.PatternTree.SetItemPyData(item,data)                             
4254#patch
4255        if len(data) < 5:
4256            data.append({'Use':False,'ModVec':[0,0,0.1],'maxH':1,'ssSymb':''})                                 #empty superlattice stuff
4257            G2frame.PatternTree.SetItemPyData(item,data) 
4258#end patch
4259        G2pdG.UpdateUnitCellsGrid(G2frame,data)
4260        if 'PKS' in G2frame.PatternTree.GetItemText(G2frame.PatternId):
4261            G2plt.PlotPowderLines(G2frame)
4262        else:
4263            G2plt.PlotPatterns(G2frame)
4264    elif G2frame.PatternTree.GetItemText(item) == 'Reflection Lists':   #powder reflections
4265        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4266        G2frame.PickId = item
4267        data = G2frame.PatternTree.GetItemPyData(item)
4268        G2frame.RefList = ''
4269        if len(data):
4270            G2frame.RefList = data.keys()[0]
4271        G2pdG.UpdateReflectionGrid(G2frame,data)
4272        G2plt.PlotPatterns(G2frame)
4273    elif G2frame.PatternTree.GetItemText(item) == 'Reflection List':    #HKLF reflections
4274        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4275        name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
4276        data = G2frame.PatternTree.GetItemPyData(G2frame.PatternId)
4277        G2pdG.UpdateReflectionGrid(G2frame,data,HKLF=True,Name=name)
4278    G2frame.dataFrame.Raise()
4279
4280def SetDataMenuBar(G2frame,menu=None):
4281    '''Set the menu for the data frame. On the Mac put this
4282    menu for the data tree window instead.
4283
4284    Note that data frame items do not have menus, for these (menu=None)
4285    display a blank menu or on the Mac display the standard menu for
4286    the data tree window.
4287    '''
4288    if sys.platform == "darwin":
4289        if menu is None:
4290            G2frame.SetMenuBar(G2frame.GSASIIMenu)
4291        else:
4292            G2frame.SetMenuBar(menu)
4293    else:
4294        if menu is None:
4295            G2frame.dataFrame.SetMenuBar(G2frame.dataFrame.BlankMenu)
4296        else:
4297            G2frame.dataFrame.SetMenuBar(menu)
4298
4299def HorizontalLine(sizer,parent):
4300    '''Draws a horizontal line as wide as the window.
4301    This shows up on the Mac as a very thin line, no matter what I do
4302    '''
4303    line = wx.StaticLine(parent,-1, size=(-1,3), style=wx.LI_HORIZONTAL)
4304    sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.ALL, 10)
4305
4306def HowDidIgetHere():
4307    '''Show a traceback with calls that brought us to the current location.
4308    Used for debugging.
4309    '''
4310    import traceback
4311    print 70*'*'   
4312    for i in traceback.format_list(traceback.extract_stack()[:-1]): print(i.strip.rstrip())
4313    print 70*'*'   
4314       
Note: See TracBrowser for help on using the repository browser.