source: trunk/GSASIIgrid.py @ 1667

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

fix bug in load mask ignore thresholds
inner/outer image limits now in 2-theta of q depending of binType.

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