source: trunk/GSASIIgrid.py @ 1655

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

add a Show/Hide? command in the phase/data menu
add importer for ISIS SXD data

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