source: trunk/GSASIIgrid.py @ 1128

Last change on this file since 1128 was 1128, checked in by vondreele, 8 years ago

mask picking redone; all now made by key, mouse or submenu
some update of G2obj stuff

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 161.4 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIgrid - data display routines
3########### SVN repository information ###################
4# $Date: 2013-10-29 17:03:49 +0000 (Tue, 29 Oct 2013) $
5# $Author: vondreele $
6# $Revision: 1128 $
7# $URL: trunk/GSASIIgrid.py $
8# $Id: GSASIIgrid.py 1128 2013-10-29 17:03:49Z 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 numpy as np
25import numpy.ma as ma
26import os.path
27import wx.html        # could postpone this for quicker startup
28import webbrowser     # could postpone this for quicker startup
29import GSASIIpath
30GSASIIpath.SetVersionNumber("$Revision: 1128 $")
31import GSASIImath as G2mth
32import GSASIIIO as G2IO
33import GSASIIlattice as G2lat
34import GSASIIplot as G2plt
35import GSASIIpwdGUI as G2pdG
36import GSASIIimgGUI as G2imG
37import GSASIIphsGUI as G2phG
38import GSASIIspc as G2spc
39import GSASIImapvars as G2mv
40import GSASIIconstrGUI as G2cnstG
41import GSASIIrestrGUI as G2restG
42import GSASIIpy3 as G2py3
43
44# trig functions in degrees
45sind = lambda x: np.sin(x*np.pi/180.)
46tand = lambda x: np.tan(x*np.pi/180.)
47cosd = lambda x: np.cos(x*np.pi/180.)
48
49# globals we will use later
50__version__ = None # gets overridden in GSASII.py
51path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # save location of this file
52helpLocDict = {}
53htmlPanel = None
54htmlFrame = None
55helpMode = 'browser'
56#if sys.platform.lower().startswith('win'): helpMode = 'internal' # need a global control to set this
57   
58htmlFirstUse = True
59
60[ wxID_FOURCALC, wxID_FOURSEARCH, wxID_FOURCLEAR, wxID_PEAKSMOVE, wxID_PEAKSCLEAR, 
61    wxID_CHARGEFLIP, wxID_PEAKSUNIQUE, wxID_PEAKSDELETE, wxID_PEAKSDA,
62    wxID_PEAKSDISTVP, wxID_PEAKSVIEWPT, wxID_FINDEQVPEAKS,wxID_SHOWBONDS,wxID_MULTIMCSA,
63    wxID_SINGLEMCSA
64] = [wx.NewId() for item in range(15)]
65
66[ wxID_PWDRADD, wxID_HKLFADD,wxID_PWDANALYSIS,wxID_DATADELETE,
67] = [wx.NewId() for item in range(4)]
68
69[ wxID_ATOMSEDITADD, wxID_ATOMSEDITINSERT, wxID_ATOMSEDITDELETE, wxID_ATOMSREFINE, 
70    wxID_ATOMSMODIFY, wxID_ATOMSTRANSFORM, wxID_ATOMSVIEWADD, wxID_ATOMVIEWINSERT,
71    wxID_RELOADDRAWATOMS,wxID_ATOMSDISAGL,wxID_ATOMMOVE,
72    wxID_ASSIGNATMS2RB,wxID_ATOMSPDISAGL
73] = [wx.NewId() for item in range(13)]
74
75[ wxID_DRAWATOMSTYLE, wxID_DRAWATOMLABEL, wxID_DRAWATOMCOLOR, wxID_DRAWATOMRESETCOLOR, 
76    wxID_DRAWVIEWPOINT, wxID_DRAWTRANSFORM, wxID_DRAWDELETE, wxID_DRAWFILLCELL, 
77    wxID_DRAWADDEQUIV, wxID_DRAWFILLCOORD, wxID_DRAWDISAGLTOR,  wxID_DRAWPLANE,
78    wxID_DRAWDISTVP,
79] = [wx.NewId() for item in range(13)]
80
81[ wxID_DRAWRESTRBOND, wxID_DRAWRESTRANGLE, wxID_DRAWRESTRPLANE, wxID_DRAWRESTRCHIRAL,
82] = [wx.NewId() for item in range(4)]
83
84[ wxID_ADDMCSAATOM,wxID_ADDMCSARB,wxID_CLEARMCSARB,wxID_MOVEMCSA,wxID_MCSACLEARRESULTS,
85] = [wx.NewId() for item in range(5)]
86
87[ wxID_CLEARTEXTURE,wxID_REFINETEXTURE,
88] = [wx.NewId() for item in range(2)]
89
90[ wxID_PAWLEYLOAD, wxID_PAWLEYESTIMATE, wxID_PAWLEYUPDATE,
91] = [wx.NewId() for item in range(3)]
92
93[ wxID_IMCALIBRATE,wxID_IMRECALIBRATE,wxID_IMINTEGRATE, wxID_IMCLEARCALIB, 
94    wxID_IMCOPYCONTROLS, wxID_INTEGRATEALL, wxID_IMSAVECONTROLS, wxID_IMLOADCONTROLS,
95] = [wx.NewId() for item in range(8)]
96
97[ wxID_MASKCOPY, wxID_MASKSAVE, wxID_MASKLOAD,wxID_NEWMASKSPOT,wxID_NEWMASKARC,wxID_NEWMASKRING,
98  wxID_NEWMASKFRAME, wxID_NEWMASKPOLY,
99] = [wx.NewId() for item in range(8)]
100
101
102[ wxID_STRSTACOPY, wxID_STRSTAFIT, wxID_STRSTASAVE, wxID_STRSTALOAD,wxID_APPENDDZERO,
103] = [wx.NewId() for item in range(5)]
104
105[ wxID_BACKCOPY,wxID_LIMITCOPY,wxID_SAMPLECOPY, wxID_BACKFLAGCOPY, wxID_SAMPLEFLAGCOPY,
106    wxID_SAMPLESAVE, wxID_SAMPLELOAD,wxID_ADDEXCLREGION,
107] = [wx.NewId() for item in range(8)]
108
109[ wxID_INSTPRMRESET,wxID_CHANGEWAVETYPE,wxID_INSTCOPY, wxID_INSTFLAGCOPY, wxID_INSTLOAD,
110    wxID_INSTSAVE,
111] = [wx.NewId() for item in range(6)]
112
113[ wxID_UNDO,wxID_LSQPEAKFIT,wxID_LSQONECYCLE,wxID_RESETSIGGAM,wxID_CLEARPEAKS,wxID_AUTOSEARCH,
114] = [wx.NewId() for item in range(6)]
115
116[  wxID_INDXRELOAD, wxID_INDEXPEAKS, wxID_REFINECELL, wxID_COPYCELL, wxID_MAKENEWPHASE,
117] = [wx.NewId() for item in range(5)]
118
119[ wxID_CONSTRAINTADD,wxID_EQUIVADD,wxID_HOLDADD,wxID_FUNCTADD,
120] = [wx.NewId() for item in range(4)]
121
122[ wxID_RESTRAINTADD, wxID_RESTSELPHASE,wxID_RESTDELETE, wxID_RESRCHANGEVAL, 
123    wxID_RESTCHANGEESD,wxID_AARESTRAINTADD,wxID_AARESTRAINTPLOT,
124] = [wx.NewId() for item in range(7)]
125
126[ wxID_RIGIDBODYADD,wxID_DRAWDEFINERB,wxID_RIGIDBODYIMPORT,wxID_RESIDUETORSSEQ,
127    wxID_AUTOFINDRESRB,wxID_GLOBALRESREFINE,wxID_RBREMOVEALL,wxID_COPYRBPARMS,
128    wxID_GLOBALTHERM,
129] = [wx.NewId() for item in range(9)]
130
131[ wxID_SAVESEQSEL,
132] = [wx.NewId() for item in range(1)]
133
134[ wxID_SELECTPHASE,
135] = [wx.NewId() for item in range(1)]
136
137[ wxID_PDFCOPYCONTROLS, wxID_PDFSAVECONTROLS, wxID_PDFLOADCONTROLS, 
138    wxID_PDFCOMPUTE, wxID_PDFCOMPUTEALL, wxID_PDFADDELEMENT, wxID_PDFDELELEMENT,
139] = [wx.NewId() for item in range(7)]
140
141VERY_LIGHT_GREY = wx.Colour(235,235,235)
142
143################################################################################
144#### GSAS-II class definitions
145################################################################################
146       
147class ValidatedTxtCtrl(wx.TextCtrl):
148    '''Create a TextCtrl widget that uses a validator to prevent the
149    entry of inappropriate characters and changes color to highlight
150    when invalid input is supplied. As valid values are typed,
151    they are placed into the dict or list where the initial value
152    came from. The type of the initial value must be int,
153    float or str or None (see :obj:`key` and :obj:`typeHint`);
154    this type (or the one in :obj:`typeHint`) is preserved.
155
156    Float values can be entered in the TextCtrl as numbers or also
157    as algebraic expressions using operators + - / \* () and \*\*,
158    in addition pi, sind(), cosd(), tand(), and sqrt() can be used,
159    as well as appreviations s, sin, c, cos, t, tan and sq.
160
161    :param wx.Panel parent: name of panel or frame that will be
162      the parent to the TextCtrl. Can be None.
163
164    :param dict/list loc: the dict or list with the initial value to be
165      placed in the TextCtrl.
166
167    :param int/str key: the dict key or the list index for the value to be
168      edited by the TextCtrl. The ``loc[key]`` element must exist, but may
169      have value None. If None, the type for the element is taken from
170      :obj:`typeHint` and the value for the control is set initially
171      blank (and thus invalid.) This is a way to specify a field without a
172      default value: a user must set a valid value.
173      If the value is not None, it must have a base
174      type of int, float, str or unicode; the TextCrtl will be initialized
175      from this value.
176
177    :param bool notBlank: if True (default) blank values are invalid
178      for str inputs.
179     
180    :param number min: minimum allowed valid value. If None (default) the
181      lower limit is unbounded.
182
183    :param number max: maximum allowed valid value. If None (default) the
184      upper limit is unbounded
185
186    :param function OKcontrol: specifies a function or method that will be
187      called when the input is validated. The called function is supplied
188      with one argument which is False if the TextCtrl contains an invalid
189      value and True if the value is valid.
190      Note that this function should check all values
191      in the dialog when True, since other entries might be invalid.
192      The default for this is None, which indicates no function should
193      be called.
194
195    :param function OnLeave: specifies a function or method that will be
196      called when the focus for the control is lost.
197      The called function is supplied with (at present) three keyword arguments:
198
199         * invalid: (*bool*) True if the value for the TextCtrl is invalid
200         * value:   (*int/float/str*)  the value contained in the TextCtrl
201         * tc:      (*wx.TextCtrl*)  the TextCtrl name
202
203      The number of keyword arguments may be increased in the future, if needs arise,
204      so it is best to code these functions with a \*\*kwargs argument so they will
205      continue to run without errors
206
207      The default for OnLeave is None, which indicates no function should
208      be called.
209
210    :param type typeHint: the value of typeHint is overrides the initial value
211      for the dict/list element ``loc[key]``, if set to
212      int or float, which specifies the type for input to the TextCtrl.
213      Defaults as None, which is ignored.
214
215    :param bool CIFinput: for str input, indicates that only printable
216      ASCII characters may be entered into the TextCtrl. Forces output
217      to be ASCII rather than Unicode. For float and int input, allows
218      use of a single '?' or '.' character as valid input.
219
220    :param (other): other optional keyword parameters for the
221      wx.TextCtrl widget such as Size or Style may be specified.
222
223    '''
224    def __init__(self,parent,loc,key,notBlank=True,min=None,max=None,
225                 OKcontrol=None,OnLeave=None,typeHint=None,
226                 CIFinput=False, **kw):
227        # save passed values needed outside __init__
228        self.result = loc
229        self.key = key
230        self.OKcontrol=OKcontrol
231        self.OnLeave = OnLeave
232        self.CIFinput = CIFinput
233        self.type = str
234        # initialization
235        self.invalid = False   # indicates if the control has invalid contents
236        self.evaluated = False # set to True when the validator recognizes an expression
237        val = loc[key]
238        if isinstance(val,int) or typeHint is int:
239            self.type = int
240            wx.TextCtrl.__init__(
241                self,parent,wx.ID_ANY,
242                validator=NumberValidator(int,result=loc,key=key,
243                                          min=min,max=max,
244                                          OKcontrol=OKcontrol,
245                                          CIFinput=CIFinput),
246                **kw)
247            if val is not None:
248                self.SetValue(val)
249            else: # no default is invalid for a number
250                self.invalid = True
251                self._IndicateValidity()
252
253        elif isinstance(val,float) or typeHint is float:
254            self.type = float
255            wx.TextCtrl.__init__(
256                self,parent,wx.ID_ANY,
257                validator=NumberValidator(float,result=loc,key=key,
258                                          min=min,max=max,
259                                          OKcontrol=OKcontrol,
260                                          CIFinput=CIFinput),
261                **kw)
262            if val is not None:
263                self.SetValue(val)
264            else:
265                self.invalid = True
266                self._IndicateValidity()
267
268        elif isinstance(val,str) or isinstance(val,unicode):
269            if self.CIFinput:
270                wx.TextCtrl.__init__(
271                    self,parent,wx.ID_ANY,val,
272                    validator=ASCIIValidator(result=loc,key=key),
273                    **kw)
274            else:
275                wx.TextCtrl.__init__(self,parent,wx.ID_ANY,val,**kw)
276            if notBlank:
277                self.Bind(wx.EVT_CHAR,self._onStringKey)
278                self.ShowStringValidity() # test if valid input
279            else:
280                self.invalid = False
281        elif val is None:
282            raise Exception,("ValidatedTxtCtrl error: value of "+str(key)+
283                             " element is None and typeHint not defined as int or float")
284        else:
285            raise Exception,("ValidatedTxtCtrl error: Unknown element ("+str(key)+
286                             ") type: "+str(type(val)))
287        # When the mouse is moved away or the widget loses focus
288        # display the last saved value, if an expression
289        self.Bind(wx.EVT_LEAVE_WINDOW, self._onLoseFocus)
290        self.Bind(wx.EVT_KILL_FOCUS, self._onLoseFocus)
291
292    def SetValue(self,val):
293        self.invalid = False
294        if self.type is int:
295            try:
296                if int(val) != val:
297                    self.invalid = True
298                else:
299                    val = int(val)
300            except:
301                if self.CIFinput and (val == '?' or val == '.'):
302                    pass
303                else:
304                    self.invalid = True
305            wx.TextCtrl.SetValue(self,str(val))
306        elif self.type is float:
307            try:
308                float(val)
309            except:
310                if self.CIFinput and (val == '?' or val == '.'):
311                    pass
312                else:
313                    self.invalid = True
314            wx.TextCtrl.SetValue(self,str(val))
315        else:
316            wx.TextCtrl.SetValue(self,str(val))
317            self.ShowStringValidity() # test if valid input
318            return
319       
320        self._IndicateValidity()
321        if self.OKcontrol:
322            self.OKcontrol(not self.invalid)
323       
324    def _onStringKey(self,event):
325        event.Skip()
326        if self.invalid: # check for validity after processing the keystroke
327            wx.CallAfter(self.ShowStringValidity,True) # was invalid
328        else:
329            wx.CallAfter(self.ShowStringValidity,False) # was valid
330
331    def _IndicateValidity(self):
332        'Set the control colors to show invalid input'
333        if self.invalid:
334            self.SetForegroundColour("red")
335            self.SetBackgroundColour("yellow")
336            self.SetFocus()
337            self.Refresh()
338        else: # valid input
339            self.SetBackgroundColour(
340                wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
341            self.SetForegroundColour("black")
342            self.Refresh()
343
344    def ShowStringValidity(self,previousInvalid=True):
345        '''Check if input is valid. Anytime the input is
346        invalid, call self.OKcontrol (if defined) because it is fast.
347        If valid, check for any other invalid entries only when
348        changing from invalid to valid, since that is slower.
349       
350        :param bool previousInvalid: True if the TextCtrl contents were
351          invalid prior to the current change.
352         
353        '''
354        val = self.GetValue().strip()
355        self.invalid = not val
356        self._IndicateValidity()
357        if self.invalid:
358            if self.OKcontrol:
359                self.OKcontrol(False)
360        elif self.OKcontrol and previousInvalid:
361            self.OKcontrol(True)
362        # always store the result
363        if self.CIFinput: # for CIF make results ASCII
364            self.result[self.key] = val.encode('ascii','replace') 
365        else:
366            self.result[self.key] = val
367
368    def _onLoseFocus(self,event):
369        if self.evaluated: self.EvaluateExpression()
370        if self.OnLeave: self.OnLeave(invalid=self.invalid,
371                                      value=self.result[self.key],
372                                      tc=self)
373           
374    def EvaluateExpression(self):
375        '''Show the computed value when an expression is entered to the TextCtrl
376        Make sure that the number fits by truncating decimal places and switching
377        to scientific notation, as needed.
378        Called on loss of focus.
379        '''
380        if self.invalid: return # don't substitute for an invalid expression
381        if not self.evaluated: return # true when an expression is evaluated
382        if self.result is not None: # retrieve the stored result
383            val = self.result[self.key]
384            self.SetValue(G2py3.FormatValue(val))
385        self.evaluated = False # expression has been recast as value, reset flag
386       
387class NumberValidator(wx.PyValidator):
388    '''A validator to be used with a TextCtrl to prevent
389    entering characters other than digits, signs, and for float
390    input, a period and exponents.
391   
392    The value is checked for validity after every keystroke
393      If an invalid number is entered, the box is highlighted.
394      If the number is valid, it is saved in result[key]
395
396    :param type typ: the base data type. Must be int or float.
397
398    :param bool positiveonly: If True, negative integers are not allowed
399      (default False). This prevents the + or - keys from being pressed.
400      Used with typ=int; ignored for typ=float.
401
402    :param number min: Minimum allowed value. If None (default) the
403      lower limit is unbounded
404
405    :param number max: Maximum allowed value. If None (default) the
406      upper limit is unbounded
407     
408    :param dict/list result: List or dict where value should be placed when valid
409
410    :param any key: key to use for result (int for list)
411
412    :param function OKcontrol: function or class method to control
413      an OK button for a window.
414      Ignored if None (default)
415
416    :param bool CIFinput: allows use of a single '?' or '.' character
417      as valid input.
418     
419    '''
420    def __init__(self, typ, positiveonly=False, min=None, max=None,
421                 result=None, key=None, OKcontrol=None, CIFinput=False):
422        'Create the validator'
423        wx.PyValidator.__init__(self)
424        # save passed parameters
425        self.typ = typ
426        self.positiveonly = positiveonly
427        self.min = min
428        self.max = max
429        self.result = result
430        self.key = key
431        self.OKcontrol = OKcontrol
432        self.CIFinput = CIFinput
433        # set allowed keys by data type
434        self.Bind(wx.EVT_CHAR, self.OnChar)
435        if self.typ == int and self.positiveonly:
436            self.validchars = '0123456789'
437        elif self.typ == int:
438            self.validchars = '0123456789+-'
439        elif self.typ == float:
440            # allow for above and sind, cosd, sqrt, tand, pi, and abbreviations
441            # also addition, subtraction, division, multiplication, exponentiation
442            self.validchars = '0123456789.-+eE/cosindcqrtap()*'
443        else:
444            self.validchars = None
445            return
446        if self.CIFinput:
447            self.validchars += '?.'
448    def Clone(self):
449        'Create a copy of the validator, a strange, but required component'
450        return NumberValidator(typ=self.typ, 
451                               positiveonly=self.positiveonly,
452                               min=self.min, max=self.max,
453                               result=self.result, key=self.key,
454                               OKcontrol=self.OKcontrol,
455                               CIFinput=self.CIFinput)
456    def TransferToWindow(self):
457        'Needed by validator, strange, but required component'
458        return True # Prevent wxDialog from complaining.
459    def TransferFromWindow(self):
460        'Needed by validator, strange, but required component'
461        return True # Prevent wxDialog from complaining.
462    def TestValid(self,tc):
463        '''Check if the value is valid by casting the input string
464        into the current type.
465
466        Set the invalid variable in the TextCtrl object accordingly.
467
468        If the value is valid, save it in the dict/list where
469        the initial value was stored, if appropriate.
470
471        :param wx.TextCtrl tc: A reference to the TextCtrl that the validator
472          is associated with.
473        '''
474        tc.invalid = False # assume valid
475        if self.CIFinput:
476            val = tc.GetValue().strip()
477            if val == '?' or val == '.':
478                self.result[self.key] = val
479                return
480        try:
481            val = self.typ(tc.GetValue())
482        except (ValueError, SyntaxError) as e:
483            if self.typ is float: # for float values, see if an expression can be evaluated
484                val = G2py3.FormulaEval(tc.GetValue())
485                if val is None:
486                    tc.invalid = True
487                    return
488                else:
489                    tc.evaluated = True
490            else: 
491                tc.invalid = True
492                return
493        # if self.max != None and self.typ == int:
494        #     if val > self.max:
495        #         tc.invalid = True
496        # if self.min != None and self.typ == int:
497        #     if val < self.min:
498        #         tc.invalid = True  # invalid
499        if self.max != None:
500            if val > self.max:
501                tc.invalid = True
502        if self.min != None:
503            if val < self.min:
504                tc.invalid = True  # invalid
505        if self.key is not None and self.result is not None and not tc.invalid:
506            self.result[self.key] = val
507
508    def ShowValidity(self,tc):
509        '''Set the control colors to show invalid input
510
511        :param wx.TextCtrl tc: A reference to the TextCtrl that the validator
512          is associated with.
513
514        '''
515        if tc.invalid:
516            tc.SetForegroundColour("red")
517            tc.SetBackgroundColour("yellow")
518            tc.SetFocus()
519            tc.Refresh()
520            return False
521        else: # valid input
522            tc.SetBackgroundColour(
523                wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
524            tc.SetForegroundColour("black")
525            tc.Refresh()
526            return True
527
528    def CheckInput(self,previousInvalid):
529        '''called to test every change to the TextCtrl for validity and
530        to change the appearance of the TextCtrl
531
532        Anytime the input is invalid, call self.OKcontrol
533        (if defined) because it is fast.
534        If valid, check for any other invalid entries only when
535        changing from invalid to valid, since that is slower.
536
537        :param bool previousInvalid: True if the TextCtrl contents were
538          invalid prior to the current change.
539        '''
540        tc = self.GetWindow()
541        self.TestValid(tc)
542        self.ShowValidity(tc)
543        # if invalid
544        if tc.invalid and self.OKcontrol:
545            self.OKcontrol(False)
546        if not tc.invalid and self.OKcontrol and previousInvalid:
547            self.OKcontrol(True)
548
549    def OnChar(self, event):
550        '''Called each type a key is pressed
551        ignores keys that are not allowed for int and float types
552        '''
553        key = event.GetKeyCode()
554        tc = self.GetWindow()
555        if key == wx.WXK_RETURN:
556            if tc.invalid:
557                self.CheckInput(True) 
558            else:
559                self.CheckInput(False) 
560            return
561        if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255: # control characters get processed
562            event.Skip()
563            if tc.invalid:
564                wx.CallAfter(self.CheckInput,True) 
565            else:
566                wx.CallAfter(self.CheckInput,False) 
567            return
568        elif chr(key) in self.validchars: # valid char pressed?
569            event.Skip()
570            if tc.invalid:
571                wx.CallAfter(self.CheckInput,True) 
572            else:
573                wx.CallAfter(self.CheckInput,False) 
574            return
575        if not wx.Validator_IsSilent(): wx.Bell()
576        return  # Returning without calling event.Skip, which eats the keystroke
577
578################################################################################
579class ASCIIValidator(wx.PyValidator):
580    '''A validator to be used with a TextCtrl to prevent
581    entering characters other than ASCII characters.
582   
583    The value is checked for validity after every keystroke
584      If an invalid number is entered, the box is highlighted.
585      If the number is valid, it is saved in result[key]
586
587    :param dict/list result: List or dict where value should be placed when valid
588
589    :param any key: key to use for result (int for list)
590
591    '''
592    def __init__(self, result=None, key=None):
593        'Create the validator'
594        import string
595        wx.PyValidator.__init__(self)
596        # save passed parameters
597        self.result = result
598        self.key = key
599        self.validchars = string.ascii_letters + string.digits + string.punctuation + string.whitespace
600        self.Bind(wx.EVT_CHAR, self.OnChar)
601    def Clone(self):
602        'Create a copy of the validator, a strange, but required component'
603        return ASCIIValidator(result=self.result, key=self.key)
604        tc = self.GetWindow()
605        tc.invalid = False # make sure the validity flag is defined in parent
606    def TransferToWindow(self):
607        'Needed by validator, strange, but required component'
608        return True # Prevent wxDialog from complaining.
609    def TransferFromWindow(self):
610        'Needed by validator, strange, but required component'
611        return True # Prevent wxDialog from complaining.
612    def TestValid(self,tc):
613        '''Check if the value is valid by casting the input string
614        into ASCII.
615
616        Save it in the dict/list where the initial value was stored
617
618        :param wx.TextCtrl tc: A reference to the TextCtrl that the validator
619          is associated with.
620        '''
621        self.result[self.key] = tc.GetValue().encode('ascii','replace')
622
623    def OnChar(self, event):
624        '''Called each type a key is pressed
625        ignores keys that are not allowed for int and float types
626        '''
627        key = event.GetKeyCode()
628        tc = self.GetWindow()
629        if key == wx.WXK_RETURN:
630            self.TestValid(tc)
631            return
632        if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255: # control characters get processed
633            event.Skip()
634            self.TestValid(tc)
635            return
636        elif chr(key) in self.validchars: # valid char pressed?
637            event.Skip()
638            self.TestValid(tc)
639            return
640        if not wx.Validator_IsSilent():
641            wx.Bell()
642        return  # Returning without calling event.Skip, which eats the keystroke
643################################################################################
644class EnumSelector(wx.ComboBox):
645    '''A customized :class:`wxpython.ComboBox` that selects items from a list
646    of choices, but sets a dict (list) entry to the corresponding
647    entry from the input list of values.
648
649    :param wx.Panel parent: the parent to the :class:`~wxpython.ComboBox` (usually a
650      frame or panel)
651    :param dict dct: a dict (or list) to contain the value set
652      for the :class:`~wxpython.ComboBox`.
653    :param item: the dict key (or list index) where ``dct[item]`` will
654      be set to the value selected in the :class:`~wxpython.ComboBox`. Also, dct[item]
655      contains the starting value shown in the widget. If the value
656      does not match an entry in :data:`values`, the first value
657      in :data:`choices` is used as the default, but ``dct[item]`` is
658      not changed.   
659    :param list choices: a list of choices to be displayed to the
660      user such as
661      ::
662     
663      ["default","option 1","option 2",]
664
665      Note that these options will correspond to the entries in
666      :data:`values` (if specified) item by item.
667    :param list values: a list of values that correspond to
668      the options in :data:`choices`, such as
669      ::
670     
671      [0,1,2]
672     
673      The default for :data:`values` is to use the same list as
674      specified for :data:`choices`.
675    :param (other): additional keyword arguments accepted by
676      :class:`~wxpython.ComboBox` can be specified.
677    '''
678    def __init__(self,parent,dct,item,choices,values=None,**kw):
679        if values is None:
680            values = choices
681        if dct[item] in values:
682            i = values.index(dct[item])
683        else:
684            i = 0
685        startval = choices[i]
686        wx.ComboBox.__init__(self,parent,wx.ID_ANY,startval,
687                             choices = choices,
688                             style=wx.CB_DROPDOWN|wx.CB_READONLY,
689                             **kw)
690        self.choices = choices
691        self.values = values
692        self.dct = dct
693        self.item = item
694        self.Bind(wx.EVT_COMBOBOX, self.onSelection)
695    def onSelection(self,event):
696        # respond to a selection by setting the enum value in the CIF dictionary
697        if self.GetValue() in self.choices: # should always be true!
698            self.dct[self.item] = self.values[self.choices.index(self.GetValue())]
699        else:
700            self.dct[self.item] = self.values[0] # unknown
701
702################################################################################   
703def CallScrolledMultiEditor(parent,dictlst,elemlst,prelbl=[],postlbl=[],
704                 title='Edit items',header='',size=(300,250),
705                             CopyButton=False):
706    '''Shell routine to call a ScrolledMultiEditor dialog. See
707    :class:`ScrolledMultiEditor` for parameter definitions.
708
709    :returns: True if the OK button is pressed; False if the window is closed
710      with the system menu or the Cancel button.
711
712    '''
713    dlg = ScrolledMultiEditor(parent,dictlst,elemlst,prelbl,postlbl,
714                              title,header,size,
715                              CopyButton)
716    if dlg.ShowModal() == wx.ID_OK:
717        dlg.Destroy()
718        return True
719    else:
720        dlg.Destroy()
721        return False
722
723class ScrolledMultiEditor(wx.Dialog):
724    '''Define a window for editing a potentially large number of dict- or
725    list-contained values with validation for each item. Edited values are
726    automatically placed in their source location. If invalid entries
727    are provided, the TextCtrl is turned yellow and the OK button is disabled.
728
729    The type for each TextCtrl validation is determined by the
730    initial value of the entry (int, float or string).
731    Float values can be entered in the TextCtrl as numbers or also
732    as algebraic expressions using operators + - / \* () and \*\*,
733    in addition pi, sind(), cosd(), tand(), and sqrt() can be used,
734    as well as appreviations s(), sin(), c(), cos(), t(), tan() and sq().
735
736    :param wx.Frame parent: name of parent window, or may be None
737
738    :param tuple dictlst: a list of dicts or lists containing values to edit
739
740    :param tuple elemlst: a list of keys for each item in a dictlst. Must have the
741      same length as dictlst.
742
743    :param wx.Frame parent: name of parent window, or may be None
744   
745    :param tuple prelbl: a list of labels placed before the TextCtrl for each
746      item (optional)
747   
748    :param tuple postlbl: a list of labels placed after the TextCtrl for each
749      item (optional)
750
751    :param str title: a title to place in the frame of the dialog
752
753    :param str header: text to place at the top of the window. May contain
754      new line characters.
755
756    :param wx.Size size: a size parameter that dictates the
757      size for the scrolled region of the dialog. The default is
758      (300,250).
759
760    :param bool CopyButton: if True adds a small button that copies the
761      value for the current row to all fields below (default is False)
762     
763    :param list minvals: optional list of minimum values for validation
764      of float or int values. Ignored if value is None.
765    :param list maxvals: optional list of maximum values for validation
766      of float or int values. Ignored if value is None.
767    :param list sizevals: optional list of wx.Size values for each input
768      widget. Ignored if value is None.
769    :returns: the wx.Dialog created here. Use method .ShowModal() to display it.
770   
771    *Example for use of ScrolledMultiEditor:*
772
773    ::
774
775        dlg = <pkg>.ScrolledMultiEditor(frame,dictlst,elemlst,prelbl,postlbl,
776                                        header=header)
777        if dlg.ShowModal() == wx.ID_OK:
778             for d,k in zip(dictlst,elemlst):
779                 print d[k]
780
781    *Example definitions for dictlst and elemlst:*
782
783    ::
784     
785          dictlst = (dict1,list1,dict1,list1)
786          elemlst = ('a', 1, 2, 3)
787
788      This causes items dict1['a'], list1[1], dict1[2] and list1[3] to be edited.
789   
790    Note that these items must have int, float or str values assigned to
791    them. The dialog will force these types to be retained. String values
792    that are blank are marked as invalid.
793    '''
794   
795    def __init__(self,parent,dictlst,elemlst,prelbl=[],postlbl=[],
796                 title='Edit items',header='',size=(300,250),
797                 CopyButton=False,
798                 minvals=[],maxvals=[],sizevals=[]):
799        if len(dictlst) != len(elemlst):
800            raise Exception,"ScrolledMultiEditor error: len(dictlst) != len(elemlst) "+str(len(dictlst))+" != "+str(len(elemlst))
801        wx.Dialog.__init__( # create dialog & sizer
802            self,parent,wx.ID_ANY,title,
803            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
804        mainSizer = wx.BoxSizer(wx.VERTICAL)
805        self.orig = []
806        self.dictlst = dictlst
807        self.elemlst = elemlst
808        self.ButtonIndex = {}
809        for d,i in zip(dictlst,elemlst):
810            self.orig.append(d[i])
811        # add a header if supplied
812        if header:
813            subSizer = wx.BoxSizer(wx.HORIZONTAL)
814            subSizer.Add((-1,-1),1,wx.EXPAND)
815            subSizer.Add(wx.StaticText(self,wx.ID_ANY,header))
816            subSizer.Add((-1,-1),1,wx.EXPAND)
817            mainSizer.Add(subSizer,0,wx.EXPAND,0)
818        # make OK button now, because we will need it for validation
819        self.OKbtn = wx.Button(self, wx.ID_OK)
820        self.OKbtn.SetDefault()
821        # create scrolled panel and sizer
822        panel = wxscroll.ScrolledPanel(
823            self, wx.ID_ANY,
824            size=size,
825            style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
826        cols = 3
827        if CopyButton: cols += 1
828        subSizer = wx.FlexGridSizer(rows=len(dictlst),cols=cols,hgap=2,vgap=2)
829        self.ValidatedControlsList = [] # make list of TextCtrls
830        for i,(d,k) in enumerate(zip(dictlst,elemlst)):
831            if i >= len(prelbl): # label before TextCtrl, or put in a blank
832                subSizer.Add((-1,-1)) 
833            else:
834                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,str(prelbl[i])))
835            kargs = {}
836            if i < len(minvals):
837                if minvals[i] is not None: kargs['min']=minvals[i]
838            if i < len(maxvals):
839                if maxvals[i] is not None: kargs['max']=maxvals[i]
840            if i < len(sizevals):
841                if sizevals[i]: kargs['size']=sizevals[i]
842            if CopyButton:
843                import wx.lib.colourselect as wscs
844                but = wscs.ColourSelect(label='v', # would like to use u'\u2193' or u'\u25BC' but not in WinXP
845                                        # is there a way to test?
846                                        parent=panel,
847                                        colour=(255,255,200),
848                                        size=wx.Size(30,23),
849                                        style=wx.RAISED_BORDER)
850                but.Bind(wx.EVT_BUTTON, self._OnCopyButton)
851                but.SetToolTipString('Press to copy adjacent value to all rows below')
852                self.ButtonIndex[but] = i
853                subSizer.Add(but)
854            # create the validated TextCrtl, store it and add it to the sizer
855            ctrl = ValidatedTxtCtrl(panel,d,k,OKcontrol=self.ControlOKButton,
856                                    **kargs)
857            self.ValidatedControlsList.append(ctrl)
858            subSizer.Add(ctrl)
859            if i >= len(postlbl): # label after TextCtrl, or put in a blank
860                subSizer.Add((-1,-1))
861            else:
862                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,str(postlbl[i])))
863        # finish up ScrolledPanel
864        panel.SetSizer(subSizer)
865        panel.SetAutoLayout(1)
866        panel.SetupScrolling()
867        mainSizer.Add(panel,1, wx.ALL|wx.EXPAND,1)
868
869        # Sizer for OK/Close buttons. N.B. on Close changes are discarded
870        # by restoring the initial values
871        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
872        btnsizer.Add(self.OKbtn)
873        btn = wx.Button(self, wx.ID_CLOSE,"Cancel") 
874        btn.Bind(wx.EVT_BUTTON,self._onClose)
875        btnsizer.Add(btn)
876        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
877        # size out the window. Set it to be enlarged but not made smaller
878        self.SetSizer(mainSizer)
879        mainSizer.Fit(self)
880        self.SetMinSize(self.GetSize())
881
882    def _OnCopyButton(self,event):
883        'Implements the copy down functionality'
884        but = event.GetEventObject()
885        n = self.ButtonIndex.get(but)
886        if n is None: return
887        for i,(d,k,ctrl) in enumerate(zip(self.dictlst,self.elemlst,self.ValidatedControlsList)):
888            if i < n: continue
889            if i == n:
890                val = d[k]
891                continue
892            d[k] = val
893            ctrl.SetValue(val)
894
895    def _onClose(self,event):
896        'Restore original values & close the window'
897        for d,i,v in zip(self.dictlst,self.elemlst,self.orig):
898            d[i] = v
899        self.EndModal(wx.ID_CANCEL)
900       
901    def ControlOKButton(self,setvalue):
902        '''Enable or Disable the OK button for the dialog. Note that this is
903        passed into the ValidatedTxtCtrl for use by validators.
904
905        :param bool setvalue: if True, all entries in the dialog are
906          checked for validity. if False then the OK button is disabled.
907
908        '''
909        if setvalue: # turn button on, do only if all controls show as valid
910            for ctrl in self.ValidatedControlsList:
911                if ctrl.invalid:
912                    self.OKbtn.Disable()
913                    return
914            else:
915                self.OKbtn.Enable()
916        else:
917            self.OKbtn.Disable()
918
919################################################################################
920class SymOpDialog(wx.Dialog):
921    '''Class to select a symmetry operator
922    '''
923    def __init__(self,parent,SGData,New=True,ForceUnit=False):
924        wx.Dialog.__init__(self,parent,-1,'Select symmetry operator',
925            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
926        panel = wx.Panel(self)
927        self.SGData = SGData
928        self.New = New
929        self.Force = ForceUnit
930        self.OpSelected = [0,0,0,[0,0,0],False,False]
931        mainSizer = wx.BoxSizer(wx.VERTICAL)
932        if ForceUnit:
933            choice = ['No','Yes']
934            self.force = wx.RadioBox(panel,-1,'Force to unit cell?',choices=choice)
935            self.force.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
936            mainSizer.Add(self.force,0,wx.ALIGN_CENTER_VERTICAL)
937        mainSizer.Add((5,5),0)
938        if SGData['SGInv']:
939            choice = ['No','Yes']
940            self.inv = wx.RadioBox(panel,-1,'Choose inversion?',choices=choice)
941            self.inv.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
942            mainSizer.Add(self.inv,0,wx.ALIGN_CENTER_VERTICAL)
943        mainSizer.Add((5,5),0)
944        if SGData['SGLatt'] != 'P':
945            LattOp = G2spc.Latt2text(SGData['SGLatt']).split(';')
946            self.latt = wx.RadioBox(panel,-1,'Choose cell centering?',choices=LattOp)
947            self.latt.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
948            mainSizer.Add(self.latt,0,wx.ALIGN_CENTER_VERTICAL)
949        mainSizer.Add((5,5),0)
950        if SGData['SGLaue'] in ['-1','2/m','mmm','4/m','4/mmm']:
951            Ncol = 2
952        else:
953            Ncol = 3
954        OpList = []
955        for M,T in SGData['SGOps']:
956            OpList.append(G2spc.MT2text(M,T))
957        self.oprs = wx.RadioBox(panel,-1,'Choose space group operator?',choices=OpList,
958            majorDimension=Ncol)
959        self.oprs.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
960        mainSizer.Add(self.oprs,0,wx.ALIGN_CENTER_VERTICAL)
961        mainSizer.Add((5,5),0)
962        mainSizer.Add(wx.StaticText(panel,-1,"   Choose unit cell?"),0,wx.ALIGN_CENTER_VERTICAL)
963        mainSizer.Add((5,5),0)
964        cellSizer = wx.BoxSizer(wx.HORIZONTAL)
965        cellSizer.Add((5,0),0)
966        cellName = ['X','Y','Z']
967        self.cell = []
968        for i in range(3):
969            self.cell.append(wx.SpinCtrl(panel,-1,cellName[i],size=wx.Size(50,20)))
970            self.cell[-1].SetRange(-3,3)
971            self.cell[-1].SetValue(0)
972            self.cell[-1].Bind(wx.EVT_SPINCTRL, self.OnOpSelect)
973            cellSizer.Add(self.cell[-1],0,wx.ALIGN_CENTER_VERTICAL)
974        mainSizer.Add(cellSizer,0,)
975        if self.New:
976            choice = ['No','Yes']
977            self.new = wx.RadioBox(panel,-1,'Generate new positions?',choices=choice)
978            self.new.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
979            mainSizer.Add(self.new,0,wx.ALIGN_CENTER_VERTICAL)
980        mainSizer.Add((5,5),0)
981
982        OkBtn = wx.Button(panel,-1,"Ok")
983        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
984        cancelBtn = wx.Button(panel,-1,"Cancel")
985        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
986        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
987        btnSizer.Add((20,20),1)
988        btnSizer.Add(OkBtn)
989        btnSizer.Add((20,20),1)
990        btnSizer.Add(cancelBtn)
991        btnSizer.Add((20,20),1)
992
993        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
994        panel.SetSizer(mainSizer)
995        panel.Fit()
996        self.Fit()
997
998    def OnOpSelect(self,event):
999        if self.SGData['SGInv']:
1000            self.OpSelected[0] = self.inv.GetSelection()
1001        if self.SGData['SGLatt'] != 'P':
1002            self.OpSelected[1] = self.latt.GetSelection()
1003        self.OpSelected[2] = self.oprs.GetSelection()
1004        for i in range(3):
1005            self.OpSelected[3][i] = float(self.cell[i].GetValue())
1006        if self.New:
1007            self.OpSelected[4] = self.new.GetSelection()
1008        if self.Force:
1009            self.OpSelected[5] = self.force.GetSelection()
1010
1011    def GetSelection(self):
1012        return self.OpSelected
1013
1014    def OnOk(self,event):
1015        parent = self.GetParent()
1016        parent.Raise()
1017        self.EndModal(wx.ID_OK)
1018
1019    def OnCancel(self,event):
1020        parent = self.GetParent()
1021        parent.Raise()
1022        self.EndModal(wx.ID_CANCEL)
1023
1024class DisAglDialog(wx.Dialog):
1025    '''Distance/Angle Controls input dialog. After
1026    :meth:`ShowModal` returns, the results are found in
1027    dict :attr:`self.data`, which is accessed using :meth:`GetData`.
1028
1029    :param wx.Frame parent: reference to parent frame (or None)
1030    :param dict data: a dict containing the current
1031      search ranges or an empty dict, which causes default values
1032      to be used.
1033      Will be used to set element `DisAglCtls` in
1034      :ref:`Phase Tree Item <Phase_table>`
1035    :param dict default:  A dict containing the default
1036      search ranges for each element.
1037    '''
1038    def __init__(self,parent,data,default):
1039        wx.Dialog.__init__(self,parent,wx.ID_ANY,
1040                           'Distance Angle Controls', 
1041            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1042        self.default = default
1043        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
1044        self._default(data,self.default)
1045        self.Draw(self.data)
1046               
1047    def _default(self,data,default):
1048        '''Set starting values for the search values, either from
1049        the input array or from defaults, if input is null
1050        '''
1051        if data:
1052            self.data = copy.deepcopy(data) # don't mess with originals
1053        else:
1054            self.data = {}
1055            self.data['Name'] = default['Name']
1056            self.data['Factors'] = [0.85,0.85]
1057            self.data['AtomTypes'] = default['AtomTypes']
1058            self.data['BondRadii'] = default['BondRadii'][:]
1059            self.data['AngleRadii'] = default['AngleRadii'][:]
1060
1061    def Draw(self,data):
1062        '''Creates the contents of the dialog. Normally called
1063        by :meth:`__init__`.
1064        '''
1065        self.panel.Destroy()
1066        self.panel = wx.Panel(self)
1067        mainSizer = wx.BoxSizer(wx.VERTICAL)
1068        mainSizer.Add(wx.StaticText(self.panel,-1,'Controls for phase '+data['Name']),
1069            0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1070        mainSizer.Add((10,10),1)
1071       
1072        radiiSizer = wx.FlexGridSizer(2,3,5,5)
1073        radiiSizer.Add(wx.StaticText(self.panel,-1,' Type'),0,wx.ALIGN_CENTER_VERTICAL)
1074        radiiSizer.Add(wx.StaticText(self.panel,-1,'Bond radii'),0,wx.ALIGN_CENTER_VERTICAL)
1075        radiiSizer.Add(wx.StaticText(self.panel,-1,'Angle radii'),0,wx.ALIGN_CENTER_VERTICAL)
1076        self.objList = {}
1077        for id,item in enumerate(self.data['AtomTypes']):
1078            radiiSizer.Add(wx.StaticText(self.panel,-1,' '+item),0,wx.ALIGN_CENTER_VERTICAL)
1079            bRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['BondRadii'][id]),style=wx.TE_PROCESS_ENTER)
1080            self.objList[bRadii.GetId()] = ['BondRadii',id]
1081            bRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1082            bRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1083            radiiSizer.Add(bRadii,0,wx.ALIGN_CENTER_VERTICAL)
1084            aRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['AngleRadii'][id]),style=wx.TE_PROCESS_ENTER)
1085            self.objList[aRadii.GetId()] = ['AngleRadii',id]
1086            aRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1087            aRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1088            radiiSizer.Add(aRadii,0,wx.ALIGN_CENTER_VERTICAL)
1089        mainSizer.Add(radiiSizer,0,wx.EXPAND)
1090        factorSizer = wx.FlexGridSizer(2,2,5,5)
1091        Names = ['Bond','Angle']
1092        for i,name in enumerate(Names):
1093            factorSizer.Add(wx.StaticText(self.panel,-1,name+' search factor'),0,wx.ALIGN_CENTER_VERTICAL)
1094            bondFact = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['Factors'][i]),style=wx.TE_PROCESS_ENTER)
1095            self.objList[bondFact.GetId()] = ['Factors',i]
1096            bondFact.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1097            bondFact.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1098            factorSizer.Add(bondFact)
1099        mainSizer.Add(factorSizer,0,wx.EXPAND)
1100       
1101        OkBtn = wx.Button(self.panel,-1,"Ok")
1102        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1103        ResetBtn = wx.Button(self.panel,-1,'Reset')
1104        ResetBtn.Bind(wx.EVT_BUTTON, self.OnReset)
1105        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1106        btnSizer.Add((20,20),1)
1107        btnSizer.Add(OkBtn)
1108        btnSizer.Add(ResetBtn)
1109        btnSizer.Add((20,20),1)
1110        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1111        self.panel.SetSizer(mainSizer)
1112        self.panel.Fit()
1113        self.Fit()
1114   
1115    def OnRadiiVal(self,event):
1116        Obj = event.GetEventObject()
1117        item = self.objList[Obj.GetId()]
1118        try:
1119            self.data[item[0]][item[1]] = float(Obj.GetValue())
1120        except ValueError:
1121            pass
1122        Obj.SetValue("%.3f"%(self.data[item[0]][item[1]]))          #reset in case of error
1123       
1124    def GetData(self):
1125        'Returns the values from the dialog'
1126        return self.data
1127       
1128    def OnOk(self,event):
1129        'Called when the OK button is pressed'
1130        parent = self.GetParent()
1131        parent.Raise()
1132        self.EndModal(wx.ID_OK)             
1133       
1134    def OnReset(self,event):
1135        'Called when the Reset button is pressed'
1136        data = {}
1137        self._default(data,self.default)
1138        self.Draw(self.data)
1139       
1140class PickTwoDialog(wx.Dialog):
1141    '''This does not seem to be in use
1142    '''
1143    def __init__(self,parent,title,prompt,names,choices):
1144        wx.Dialog.__init__(self,parent,-1,title, 
1145            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1146        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
1147        self.prompt = prompt
1148        self.choices = choices
1149        self.names = names
1150        self.Draw()
1151
1152    def Draw(self):
1153        Indx = {}
1154       
1155        def OnSelection(event):
1156            Obj = event.GetEventObject()
1157            id = Indx[Obj.GetId()]
1158            self.choices[id] = Obj.GetValue().encode()  #to avoid Unicode versions
1159            self.Draw()
1160           
1161        self.panel.DestroyChildren()
1162        self.panel.Destroy()
1163        self.panel = wx.Panel(self)
1164        mainSizer = wx.BoxSizer(wx.VERTICAL)
1165        mainSizer.Add(wx.StaticText(self.panel,-1,self.prompt),0,wx.ALIGN_CENTER)
1166        for isel,name in enumerate(self.choices):
1167            lineSizer = wx.BoxSizer(wx.HORIZONTAL)
1168            lineSizer.Add(wx.StaticText(self.panel,-1,'Reference atom '+str(isel+1)),0,wx.ALIGN_CENTER)
1169            nameList = self.names[:]
1170            if isel:
1171                if self.choices[0] in nameList:
1172                    nameList.remove(self.choices[0])
1173            choice = wx.ComboBox(self.panel,-1,value=name,choices=nameList,
1174                style=wx.CB_READONLY|wx.CB_DROPDOWN)
1175            Indx[choice.GetId()] = isel
1176            choice.Bind(wx.EVT_COMBOBOX, OnSelection)
1177            lineSizer.Add(choice,0,wx.ALIGN_CENTER)
1178            mainSizer.Add(lineSizer)
1179        OkBtn = wx.Button(self.panel,-1,"Ok")
1180        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1181        CancelBtn = wx.Button(self.panel,-1,'Cancel')
1182        CancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1183        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1184        btnSizer.Add((20,20),1)
1185        btnSizer.Add(OkBtn)
1186        btnSizer.Add(CancelBtn)
1187        btnSizer.Add((20,20),1)
1188        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1189        self.panel.SetSizer(mainSizer)
1190        self.panel.Fit()
1191        self.Fit()
1192       
1193    def GetSelection(self):
1194        return self.choices
1195
1196    def OnOk(self,event):
1197        parent = self.GetParent()
1198        parent.Raise()
1199        self.EndModal(wx.ID_OK)             
1200       
1201    def OnCancel(self,event):
1202        parent = self.GetParent()
1203        parent.Raise()
1204        self.EndModal(wx.ID_CANCEL)
1205       
1206class SingleFloatDialog(wx.Dialog):
1207    'Dialog to obtain a single float value from user'
1208    def __init__(self,parent,title,prompt,value,limits=[0.,1.],format='%.5g'):
1209        wx.Dialog.__init__(self,parent,-1,title, 
1210            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1211        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
1212        self.limits = limits
1213        self.value = value
1214        self.prompt = prompt
1215        self.format = format
1216        self.Draw()
1217       
1218    def Draw(self):
1219       
1220        def OnValItem(event):
1221            try:
1222                val = float(valItem.GetValue())
1223                if val < self.limits[0] or val > self.limits[1]:
1224                    raise ValueError
1225            except ValueError:
1226                val = self.value
1227            self.value = val
1228            valItem.SetValue(self.format%(self.value))
1229           
1230        self.panel.Destroy()
1231        self.panel = wx.Panel(self)
1232        mainSizer = wx.BoxSizer(wx.VERTICAL)
1233        mainSizer.Add(wx.StaticText(self.panel,-1,self.prompt),0,wx.ALIGN_CENTER)
1234        valItem = wx.TextCtrl(self.panel,-1,value=self.format%(self.value),style=wx.TE_PROCESS_ENTER)
1235        mainSizer.Add(valItem,0,wx.ALIGN_CENTER)
1236        valItem.Bind(wx.EVT_TEXT_ENTER,OnValItem)
1237        valItem.Bind(wx.EVT_KILL_FOCUS,OnValItem)
1238        OkBtn = wx.Button(self.panel,-1,"Ok")
1239        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1240        CancelBtn = wx.Button(self.panel,-1,'Cancel')
1241        CancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1242        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1243        btnSizer.Add((20,20),1)
1244        btnSizer.Add(OkBtn)
1245        btnSizer.Add(CancelBtn)
1246        btnSizer.Add((20,20),1)
1247        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1248        self.panel.SetSizer(mainSizer)
1249        self.panel.Fit()
1250        self.Fit()
1251
1252    def GetValue(self):
1253        return self.value
1254       
1255    def OnOk(self,event):
1256        parent = self.GetParent()
1257        parent.Raise()
1258        self.EndModal(wx.ID_OK)             
1259       
1260    def OnCancel(self,event):
1261        parent = self.GetParent()
1262        parent.Raise()
1263        self.EndModal(wx.ID_CANCEL)
1264
1265################################################################################
1266class SingleStringDialog(wx.Dialog):
1267    '''Dialog to obtain a single string value from user
1268   
1269    :param wx.Frame parent: name of parent frame
1270    :param str title: title string for dialog
1271    :param str prompt: string to tell use what they are inputting
1272    :param str value: default input value, if any
1273    '''
1274    def __init__(self,parent,title,prompt,value='',size=(200,-1)):
1275        wx.Dialog.__init__(self,parent,wx.ID_ANY,title, 
1276                           pos=wx.DefaultPosition,
1277                           style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1278        self.value = value
1279        self.prompt = prompt
1280        self.CenterOnParent()
1281        self.panel = wx.Panel(self)
1282        mainSizer = wx.BoxSizer(wx.VERTICAL)
1283        mainSizer.Add(wx.StaticText(self.panel,-1,self.prompt),0,wx.ALIGN_CENTER)
1284        self.valItem = wx.TextCtrl(self.panel,-1,value=str(self.value),size=size)
1285        mainSizer.Add(self.valItem,0,wx.ALIGN_CENTER)
1286        btnsizer = wx.StdDialogButtonSizer()
1287        OKbtn = wx.Button(self.panel, wx.ID_OK)
1288        OKbtn.SetDefault()
1289        btnsizer.AddButton(OKbtn)
1290        btn = wx.Button(self.panel, wx.ID_CANCEL)
1291        btnsizer.AddButton(btn)
1292        btnsizer.Realize()
1293        mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER)
1294        self.panel.SetSizer(mainSizer)
1295        self.panel.Fit()
1296        self.Fit()
1297
1298    def Show(self):
1299        '''Use this method after creating the dialog to post it
1300        :returns: True if the user pressed OK; False if the User pressed Cancel
1301        '''
1302        if self.ShowModal() == wx.ID_OK:
1303            self.value = self.valItem.GetValue()
1304            return True
1305        else:
1306            return False
1307
1308    def GetValue(self):
1309        '''Use this method to get the value entered by the user
1310        :returns: string entered by user
1311        '''
1312        return self.value
1313
1314################################################################################
1315class G2MultiChoiceDialog(wx.Dialog):
1316    '''A dialog similar to MultiChoiceDialog except that buttons are
1317    added to set all choices and to toggle all choices.
1318
1319    :param wx.Frame ParentFrame: reference to parent frame
1320    :param str title: heading above list of choices
1321    :param str header: Title to place on window frame
1322    :param list ChoiceList: a list of choices where one will be selected
1323
1324    :param kw: optional keyword parameters for the wx.Dialog may
1325      be included such as Size [which defaults to `(320,310)`] and
1326      Style (which defaults to `wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE| wx.OK | wx.CANCEL`);
1327      note that `wx.OK` and `wx.CANCEL` controls
1328      the presence of the eponymous buttons in the dialog.
1329    :returns: the name of the created dialog 
1330    '''
1331    def __init__(self,parent, title, header, ChoiceList, **kw):
1332        # process keyword parameters, notably Style
1333        options = {'size':(320,310), # default Frame keywords
1334                   'style':wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE| wx.OK | wx.CANCEL,
1335                   }
1336        options.update(kw)
1337        if options['style'] & wx.OK:
1338            useOK = True
1339            options['style'] ^= wx.OK
1340        else:
1341            useOK = False
1342        if options['style'] & wx.CANCEL:
1343            useCANCEL = True
1344            options['style'] ^= wx.CANCEL
1345        else:
1346            useCANCEL = False       
1347        # create the dialog frame
1348        wx.Dialog.__init__(self,parent,wx.ID_ANY,header,**options)
1349        # fill the dialog
1350        Sizer = wx.BoxSizer(wx.VERTICAL)
1351        Sizer.Add(wx.StaticText(self,wx.ID_ANY,title),0,wx.ALL,12)
1352        self.clb = wx.CheckListBox(self, wx.ID_ANY, (30,30), wx.DefaultSize, ChoiceList)
1353        self.numchoices = len(ChoiceList)
1354        Sizer.Add(self.clb,1,wx.LEFT|wx.RIGHT|wx.EXPAND,10)
1355        Sizer.Add((-1,10))
1356        # set/toggle buttons
1357        bSizer = wx.BoxSizer(wx.VERTICAL)
1358        setBut = wx.Button(self,wx.ID_ANY,'Set All')
1359        setBut.Bind(wx.EVT_BUTTON,self._SetAll)
1360        bSizer.Add(setBut,0,wx.ALIGN_CENTER)
1361        bSizer.Add((-1,5))
1362        togBut = wx.Button(self,wx.ID_ANY,'Toggle All')
1363        togBut.Bind(wx.EVT_BUTTON,self._ToggleAll)
1364        bSizer.Add(togBut,0,wx.ALIGN_CENTER)
1365        Sizer.Add(bSizer,0,wx.LEFT,12)
1366        # OK/Cancel buttons
1367        btnsizer = wx.StdDialogButtonSizer()
1368        if useOK:
1369            OKbtn = wx.Button(self, wx.ID_OK)
1370            OKbtn.SetDefault()
1371            btnsizer.AddButton(OKbtn)
1372        if useCANCEL:
1373            btn = wx.Button(self, wx.ID_CANCEL)
1374            btnsizer.AddButton(btn)
1375        btnsizer.Realize()
1376        Sizer.Add((-1,5))
1377        Sizer.Add(btnsizer,0,wx.ALIGN_RIGHT,50)
1378        Sizer.Add((-1,20))
1379        # OK done, let's get outa here
1380        self.SetSizer(Sizer)
1381    def GetSelections(self):
1382        'Returns a list of the indices for the selected choices'
1383        return [i for i in range(self.numchoices) if self.clb.IsChecked(i)]
1384    def _SetAll(self,event):
1385        'Set all choices on'
1386        self.clb.SetChecked(range(self.numchoices))
1387    def _ToggleAll(self,event):
1388        'flip the state of all choices'
1389        for i in range(self.numchoices):
1390            self.clb.Check(i,not self.clb.IsChecked(i))
1391
1392def ItemSelector(ChoiceList, ParentFrame=None,
1393                 title='Select an item',
1394                 size=None, header='Item Selector',
1395                 useCancel=True,multiple=False):
1396        ''' Provide a wx dialog to select a single item from list of choices
1397
1398        :param list ChoiceList: a list of choices where one will be selected
1399        :param wx.Frame ParentFrame: Name of parent frame (default None)
1400        :param str title: heading above list of choices (default 'Select an item')
1401        :param wx.Size size: Size for dialog to be created (default None -- size as needed)
1402        :param str header: Title to place on window frame (default 'Item Selector')
1403        :param bool useCancel: If True (default) both the OK and Cancel buttons are offered
1404        :param bool multiple: If True then multiple items can be selected (default False)
1405
1406        :returns: the selection index or None or a selection list if multiple is true
1407        '''
1408        if multiple:
1409            if useCancel:
1410                dlg = G2MultiChoiceDialog(
1411                    ParentFrame,title, header, ChoiceList)
1412            else:
1413                dlg = G2MultiChoiceDialog(
1414                    ParentFrame,title, header, ChoiceList,
1415                    style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1416        else:
1417            if useCancel:
1418                dlg = wx.SingleChoiceDialog(
1419                    ParentFrame,title, header, ChoiceList)
1420            else:
1421                dlg = wx.SingleChoiceDialog(
1422                    ParentFrame,title, header,ChoiceList,
1423                    style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1424        if size: dlg.SetSize(size)
1425        if dlg.ShowModal() == wx.ID_OK:
1426            if multiple:
1427                dlg.Destroy()
1428                return dlg.GetSelections()
1429            else:
1430                dlg.Destroy()
1431                return dlg.GetSelection()
1432        else:
1433            dlg.Destroy()
1434            return None
1435        dlg.Destroy()
1436
1437################################################################################
1438class GridFractionEditor(wg.PyGridCellEditor):
1439    '''A grid cell editor class that allows entry of values as fractions as well
1440    as sine and cosine values [as s() and c()]
1441    '''
1442    def __init__(self,grid):
1443        wg.PyGridCellEditor.__init__(self)
1444
1445    def Create(self, parent, id, evtHandler):
1446        self._tc = wx.TextCtrl(parent, id, "")
1447        self._tc.SetInsertionPoint(0)
1448        self.SetControl(self._tc)
1449
1450        if evtHandler:
1451            self._tc.PushEventHandler(evtHandler)
1452
1453        self._tc.Bind(wx.EVT_CHAR, self.OnChar)
1454
1455    def SetSize(self, rect):
1456        self._tc.SetDimensions(rect.x, rect.y, rect.width+2, rect.height+2,
1457                               wx.SIZE_ALLOW_MINUS_ONE)
1458
1459    def BeginEdit(self, row, col, grid):
1460        self.startValue = grid.GetTable().GetValue(row, col)
1461        self._tc.SetValue(str(self.startValue))
1462        self._tc.SetInsertionPointEnd()
1463        self._tc.SetFocus()
1464        self._tc.SetSelection(0, self._tc.GetLastPosition())
1465
1466    def EndEdit(self, row, col, grid):
1467        changed = False
1468
1469        val = self._tc.GetValue().lower()
1470       
1471        if val != self.startValue:
1472            changed = True
1473            neg = False
1474            if '-' in val:
1475                neg = True
1476            if '/' in val and '.' not in val:
1477                val += '.'
1478            elif 's' in val and not 'sind(' in val:
1479                if neg:
1480                    val = '-sind('+val.strip('-s')+')'
1481                else:
1482                    val = 'sind('+val.strip('s')+')'
1483            elif 'c' in val and not 'cosd(' in val:
1484                if neg:
1485                    val = '-cosd('+val.strip('-c')+')'
1486                else:
1487                    val = 'cosd('+val.strip('c')+')'
1488            try:
1489                val = float(eval(val))
1490            except (SyntaxError,NameError):
1491                val = self.startValue
1492            grid.GetTable().SetValue(row, col, val) # update the table
1493
1494        self.startValue = ''
1495        self._tc.SetValue('')
1496        return changed
1497
1498    def Reset(self):
1499        self._tc.SetValue(self.startValue)
1500        self._tc.SetInsertionPointEnd()
1501
1502    def Clone(self):
1503        return GridFractionEditor(grid)
1504
1505    def StartingKey(self, evt):
1506        self.OnChar(evt)
1507        if evt.GetSkipped():
1508            self._tc.EmulateKeyPress(evt)
1509
1510    def OnChar(self, evt):
1511        key = evt.GetKeyCode()
1512        if key == 15:
1513            return
1514        if key > 255:
1515            evt.Skip()
1516            return
1517        char = chr(key)
1518        if char in '.+-/0123456789cosind()':
1519            self._tc.WriteText(char)
1520        else:
1521            evt.Skip()
1522
1523################################################################################
1524class downdate(wx.Dialog):
1525    '''Dialog to allow a user to select a version of GSAS-II to install
1526    '''
1527    def __init__(self,parent=None):
1528        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
1529        wx.Dialog.__init__(self, parent, wx.ID_ANY, 'Select Version', style=style)
1530        pnl = wx.Panel(self)
1531        sizer = wx.BoxSizer(wx.VERTICAL)
1532        insver = GSASIIpath.svnGetRev(local=True)
1533        curver = int(GSASIIpath.svnGetRev(local=False))
1534        label = wx.StaticText(
1535            pnl,  wx.ID_ANY,
1536            'Select a specific GSAS-II version to install'
1537            )
1538        sizer.Add(label, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
1539        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
1540        sizer1.Add(
1541            wx.StaticText(pnl,  wx.ID_ANY,
1542                          'Currently installed version: '+str(insver)),
1543            0, wx.ALIGN_CENTRE|wx.ALL, 5)
1544        sizer.Add(sizer1)
1545        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
1546        sizer1.Add(
1547            wx.StaticText(pnl,  wx.ID_ANY,
1548                          'Select GSAS-II version to install: '),
1549            0, wx.ALIGN_CENTRE|wx.ALL, 5)
1550        self.spin = wx.SpinCtrl(pnl, wx.ID_ANY,size=(150,-1))
1551        self.spin.SetRange(1, curver)
1552        self.spin.SetValue(curver)
1553        self.Bind(wx.EVT_SPINCTRL, self._onSpin, self.spin)
1554        self.Bind(wx.EVT_KILL_FOCUS, self._onSpin, self.spin)
1555        sizer1.Add(self.spin)
1556        sizer.Add(sizer1)
1557
1558        line = wx.StaticLine(pnl,-1, size=(-1,3), style=wx.LI_HORIZONTAL)
1559        sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.ALL, 10)
1560
1561        self.text = wx.StaticText(pnl,  wx.ID_ANY, "")
1562        sizer.Add(self.text, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5)
1563
1564        line = wx.StaticLine(pnl,-1, size=(-1,3), style=wx.LI_HORIZONTAL)
1565        sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.ALL, 10)
1566        sizer.Add(
1567            wx.StaticText(
1568                pnl,  wx.ID_ANY,
1569                'If "Install" is pressed, your project will be saved;\n'
1570                'GSAS-II will exit; The specified version will be loaded\n'
1571                'and GSAS-II will restart. Press "Cancel" to abort.'),
1572            0, wx.EXPAND|wx.ALL, 10)
1573        btnsizer = wx.StdDialogButtonSizer()
1574        btn = wx.Button(pnl, wx.ID_OK, "Install")
1575        btn.SetDefault()
1576        btnsizer.AddButton(btn)
1577        btn = wx.Button(pnl, wx.ID_CANCEL)
1578        btnsizer.AddButton(btn)
1579        btnsizer.Realize()
1580        sizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
1581        pnl.SetSizer(sizer)
1582        sizer.Fit(self)
1583        self.topsizer=sizer
1584        self.CenterOnParent()
1585        self._onSpin(None)
1586
1587    def _onSpin(self,event):
1588        'Called to load info about the selected version in the dialog'
1589        ver = self.spin.GetValue()
1590        d = GSASIIpath.svnGetLog(version=ver)
1591        date = d.get('date','?').split('T')[0]
1592        s = '(Version '+str(ver)+' created '+date
1593        s += ' by '+d.get('author','?')+')'
1594        msg = d.get('msg')
1595        if msg: s += '\n\nComment: '+msg
1596        self.text.SetLabel(s)
1597        self.topsizer.Fit(self)
1598
1599    def getVersion(self):
1600        'Get the version number in the dialog'
1601        return self.spin.GetValue()
1602
1603################################################################################
1604class MyHelp(wx.Menu):
1605    '''
1606    A class that creates the contents of a help menu.
1607    The menu will start with two entries:
1608
1609    * 'Help on <helpType>': where helpType is a reference to an HTML page to
1610      be opened
1611    * About: opens an About dialog using OnHelpAbout. N.B. on the Mac this
1612      gets moved to the App menu to be consistent with Apple style.
1613
1614    NOTE: for this to work properly with respect to system menus, the title
1615    for the menu must be &Help, or it will not be processed properly:
1616
1617    ::
1618
1619       menu.Append(menu=MyHelp(self,...),title="&Help")
1620
1621    '''
1622    def __init__(self,frame,helpType=None,helpLbl=None,morehelpitems=[],title=''):
1623        wx.Menu.__init__(self,title)
1624        self.HelpById = {}
1625        self.frame = frame
1626        self.Append(help='', id=wx.ID_ABOUT, kind=wx.ITEM_NORMAL,
1627            text='&About GSAS-II')
1628        frame.Bind(wx.EVT_MENU, self.OnHelpAbout, id=wx.ID_ABOUT)
1629        if GSASIIpath.whichsvn():
1630            helpobj = self.Append(
1631                help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
1632                text='&Check for updates')
1633            frame.Bind(wx.EVT_MENU, self.OnCheckUpdates, helpobj)
1634            helpobj = self.Append(
1635                help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
1636                text='&Regress to an old GSAS-II version')
1637            frame.Bind(wx.EVT_MENU, self.OnSelectVersion, helpobj)
1638        for lbl,indx in morehelpitems:
1639            helpobj = self.Append(text=lbl,
1640                id=wx.ID_ANY, kind=wx.ITEM_NORMAL)
1641            frame.Bind(wx.EVT_MENU, self.OnHelpById, helpobj)
1642            self.HelpById[helpobj.GetId()] = indx
1643        # add a help item only when helpType is specified
1644        if helpType is not None:
1645            self.AppendSeparator()
1646            if helpLbl is None: helpLbl = helpType
1647            helpobj = self.Append(text='Help on '+helpLbl,
1648                                  id=wx.ID_ANY, kind=wx.ITEM_NORMAL)
1649            frame.Bind(wx.EVT_MENU, self.OnHelpById, helpobj)
1650            self.HelpById[helpobj.GetId()] = helpType
1651       
1652    def OnHelpById(self,event):
1653        '''Called when Help on... is pressed in a menu. Brings up
1654        a web page for documentation.
1655        '''
1656        helpType = self.HelpById.get(event.GetId())
1657        if helpType is None:
1658            print 'Error: help lookup failed!',event.GetEventObject()
1659            print 'id=',event.GetId()
1660        else:
1661            ShowHelp(helpType,self.frame)
1662
1663    def OnHelpAbout(self, event):
1664        "Display an 'About GSAS-II' box"
1665        global __version__
1666        info = wx.AboutDialogInfo()
1667        info.Name = 'GSAS-II'
1668        ver = GSASIIpath.svnGetRev()
1669        if ver: 
1670            info.Version = 'Revision '+str(ver)+' (svn), version '+__version__
1671        else:
1672            info.Version = 'Revision '+str(GSASIIpath.GetVersionNumber())+' (.py files), version '+__version__
1673        #info.Developers = ['Robert B. Von Dreele','Brian H. Toby']
1674        info.Copyright = ('(c) ' + time.strftime('%Y') +
1675''' Argonne National Laboratory
1676This product includes software developed
1677by the UChicago Argonne, LLC, as
1678Operator of Argonne National Laboratory.''')
1679        info.Description = '''General Structure Analysis System-II (GSAS-II)
1680Robert B. Von Dreele and Brian H. Toby
1681
1682Please cite as:
1683B.H. Toby & R.B. Von Dreele, J. Appl. Cryst. 46, 544-549 (2013) '''
1684
1685        info.WebSite = ("https://subversion.xray.aps.anl.gov/trac/pyGSAS","GSAS-II home page")
1686        wx.AboutBox(info)
1687
1688    def OnCheckUpdates(self,event):
1689        '''Check if the GSAS-II repository has an update for the current source files
1690        and perform that update if requested.
1691        '''
1692        if not GSASIIpath.whichsvn():
1693            dlg = wx.MessageDialog(self.frame,
1694                                   'No Subversion','Cannot update GSAS-II because subversion (svn) was not found.',
1695                                   wx.OK)
1696            dlg.ShowModal()
1697            dlg.Destroy()
1698            return
1699        wx.BeginBusyCursor()
1700        local = GSASIIpath.svnGetRev()
1701        if local is None: 
1702            wx.EndBusyCursor()
1703            dlg = wx.MessageDialog(self.frame,
1704                                   'Unable to run subversion on the GSAS-II current directory. Is GSAS-II installed correctly?',
1705                                   'Subversion error',
1706                                   wx.OK)
1707            dlg.ShowModal()
1708            dlg.Destroy()
1709            return
1710        print 'Installed GSAS-II version: '+local
1711        repos = GSASIIpath.svnGetRev(local=False)
1712        wx.EndBusyCursor()
1713        if repos is None: 
1714            dlg = wx.MessageDialog(self.frame,
1715                                   'Unable to access the GSAS-II server. Is this computer on the internet?',
1716                                   'Server unavailable',
1717                                   wx.OK)
1718            dlg.ShowModal()
1719            dlg.Destroy()
1720            return
1721        print 'GSAS-II version on server: '+repos
1722        if local == repos:
1723            dlg = wx.MessageDialog(self.frame,
1724                                   'GSAS-II is up-to-date. Version '+local+' is already loaded.',
1725                                   'GSAS-II Up-to-date',
1726                                   wx.OK)
1727            dlg.ShowModal()
1728            dlg.Destroy()
1729            return
1730        mods = GSASIIpath.svnFindLocalChanges()
1731        if mods:
1732            dlg = wx.MessageDialog(self.frame,
1733                                   'You have version '+local+
1734                                   ' of GSAS-II installed, but the current version is '+repos+
1735                                   '. However, '+str(len(mods))+
1736                                   ' file(s) on your local computer have been modified.'
1737                                   ' Updating will attempt to merge your local changes with '
1738                                   'the latest GSAS-II version, but if '
1739                                   'conflicts arise, local changes will be '
1740                                   'discarded. It is also possible that the '
1741                                   'local changes my prevent GSAS-II from running. '
1742                                   'Press OK to start an update if this is acceptable:',
1743                                   'Local GSAS-II Mods',
1744                                   wx.OK|wx.CANCEL)
1745            if dlg.ShowModal() != wx.ID_OK:
1746                dlg.Destroy()
1747                return
1748            else:
1749                dlg.Destroy()
1750        else:
1751            dlg = wx.MessageDialog(self.frame,
1752                                   'You have version '+local+
1753                                   ' of GSAS-II installed, but the current version is '+repos+
1754                                   '. Press OK to start an update:',
1755                                   'GSAS-II Updates',
1756                                   wx.OK|wx.CANCEL)
1757            if dlg.ShowModal() != wx.ID_OK:
1758                dlg.Destroy()
1759                return
1760            dlg.Destroy()
1761        print 'start updates'
1762        dlg = wx.MessageDialog(self.frame,
1763                               'Your project will now be saved, GSAS-II will exit and an update '
1764                               'will be performed and GSAS-II will restart. Press Cancel to '
1765                               'abort the update',
1766                               'Start update?',
1767                               wx.OK|wx.CANCEL)
1768        if dlg.ShowModal() != wx.ID_OK:
1769            dlg.Destroy()
1770            return
1771        dlg.Destroy()
1772        self.frame.OnFileSave(event)
1773        GSASIIpath.svnUpdateProcess(projectfile=self.frame.GSASprojectfile)
1774        return
1775
1776    def OnSelectVersion(self,event):
1777        '''Allow the user to select a specific version of GSAS-II
1778        '''
1779        if not GSASIIpath.whichsvn():
1780            dlg = wx.MessageDialog(self,'No Subversion','Cannot update GSAS-II because subversion (svn) '+
1781                                   'was not found.'
1782                                   ,wx.OK)
1783            dlg.ShowModal()
1784            return
1785        local = GSASIIpath.svnGetRev()
1786        if local is None: 
1787            dlg = wx.MessageDialog(self.frame,
1788                                   'Unable to run subversion on the GSAS-II current directory. Is GSAS-II installed correctly?',
1789                                   'Subversion error',
1790                                   wx.OK)
1791            dlg.ShowModal()
1792            return
1793        mods = GSASIIpath.svnFindLocalChanges()
1794        if mods:
1795            dlg = wx.MessageDialog(self.frame,
1796                                   'You have version '+local+
1797                                   ' of GSAS-II installed'
1798                                   '. However, '+str(len(mods))+
1799                                   ' file(s) on your local computer have been modified.'
1800                                   ' Downdating will attempt to merge your local changes with '
1801                                   'the selected GSAS-II version. '
1802                                   'Downdating is not encouraged because '
1803                                   'if merging is not possible, your local changes will be '
1804                                   'discarded. It is also possible that the '
1805                                   'local changes my prevent GSAS-II from running. '
1806                                   'Press OK to continue anyway.',
1807                                   'Local GSAS-II Mods',
1808                                   wx.OK|wx.CANCEL)
1809            if dlg.ShowModal() != wx.ID_OK:
1810                dlg.Destroy()
1811                return
1812            dlg.Destroy()
1813        dlg = downdate(parent=self.frame)
1814        if dlg.ShowModal() == wx.ID_OK:
1815            ver = dlg.getVersion()
1816        else:
1817            dlg.Destroy()
1818            return
1819        dlg.Destroy()
1820        print('start regress to '+str(ver))
1821        GSASIIpath.svnUpdateProcess(
1822            projectfile=self.frame.GSASprojectfile,
1823            version=str(ver)
1824            )
1825        self.frame.OnFileSave(event)
1826        return
1827
1828################################################################################
1829class AddHelp(wx.Menu):
1830    '''For the Mac: creates an entry to the help menu of type
1831    'Help on <helpType>': where helpType is a reference to an HTML page to
1832    be opened.
1833
1834    NOTE: when appending this menu (menu.Append) be sure to set the title to
1835    '&Help' so that wx handles it correctly.
1836    '''
1837    def __init__(self,frame,helpType,helpLbl=None,title=''):
1838        wx.Menu.__init__(self,title)
1839        self.frame = frame
1840        if helpLbl is None: helpLbl = helpType
1841        # add a help item only when helpType is specified
1842        helpobj = self.Append(text='Help on '+helpLbl,
1843                              id=wx.ID_ANY, kind=wx.ITEM_NORMAL)
1844        frame.Bind(wx.EVT_MENU, self.OnHelpById, helpobj)
1845        self.HelpById = helpType
1846       
1847    def OnHelpById(self,event):
1848        '''Called when Help on... is pressed in a menu. Brings up
1849        a web page for documentation.
1850        '''
1851        ShowHelp(self.HelpById,self.frame)
1852
1853################################################################################
1854class MyHtmlPanel(wx.Panel):
1855    '''Defines a panel to display HTML help information, as an alternative to
1856    displaying help information in a web browser.
1857    '''
1858    def __init__(self, frame, id):
1859        self.frame = frame
1860        wx.Panel.__init__(self, frame, id)
1861        sizer = wx.BoxSizer(wx.VERTICAL)
1862        back = wx.Button(self, -1, "Back")
1863        back.Bind(wx.EVT_BUTTON, self.OnBack)
1864        self.htmlwin = G2HtmlWindow(self, id, size=(750,450))
1865        sizer.Add(self.htmlwin, 1,wx.EXPAND)
1866        sizer.Add(back, 0, wx.ALIGN_LEFT, 0)
1867        self.SetSizer(sizer)
1868        sizer.Fit(frame)       
1869        self.Bind(wx.EVT_SIZE,self.OnHelpSize)
1870    def OnHelpSize(self,event):         #does the job but weirdly!!
1871        anchor = self.htmlwin.GetOpenedAnchor()
1872        if anchor:           
1873            self.htmlwin.ScrollToAnchor(anchor)
1874            wx.CallAfter(self.htmlwin.ScrollToAnchor,anchor)
1875            event.Skip()
1876    def OnBack(self, event):
1877        self.htmlwin.HistoryBack()
1878    def LoadFile(self,file):
1879        pos = file.rfind('#')
1880        if pos != -1:
1881            helpfile = file[:pos]
1882            helpanchor = file[pos+1:]
1883        else:
1884            helpfile = file
1885            helpanchor = None
1886        self.htmlwin.LoadPage(helpfile)
1887        if helpanchor is not None:
1888            self.htmlwin.ScrollToAnchor(helpanchor)
1889            xs,ys = self.htmlwin.GetViewStart()
1890            self.htmlwin.Scroll(xs,ys-1)
1891
1892class G2HtmlWindow(wx.html.HtmlWindow):
1893    '''Displays help information in a primitive HTML browser type window
1894    '''
1895    def __init__(self, parent, *args, **kwargs):
1896        self.parent = parent
1897        wx.html.HtmlWindow.__init__(self, parent, *args, **kwargs)
1898    def LoadPage(self, *args, **kwargs):
1899        wx.html.HtmlWindow.LoadPage(self, *args, **kwargs)
1900        self.TitlePage()
1901    def OnLinkClicked(self, *args, **kwargs):
1902        wx.html.HtmlWindow.OnLinkClicked(self, *args, **kwargs)
1903        xs,ys = self.GetViewStart()
1904        self.Scroll(xs,ys-1)
1905        self.TitlePage()
1906    def HistoryBack(self, *args, **kwargs):
1907        wx.html.HtmlWindow.HistoryBack(self, *args, **kwargs)
1908        self.TitlePage()
1909    def TitlePage(self):
1910        self.parent.frame.SetTitle(self.GetOpenedPage() + ' -- ' + 
1911            self.GetOpenedPageTitle())
1912
1913################################################################################
1914class DataFrame(wx.Frame):
1915    '''Create the data item window and all the entries in menus used in
1916    that window. For Linux and windows, the menu entries are created for the
1917    current data item window, but in the Mac the menu is accessed from all
1918    windows. This means that a different menu is posted depending on which
1919    data item is posted. On the Mac, all the menus contain the data tree menu
1920    items, but additional menus are added specific to the data item.
1921
1922    Note that while the menus are created here,
1923    the binding for the menus is done later in various GSASII*GUI modules,
1924    where the functions to be called are defined.
1925    '''
1926    def Bind(self,*args,**kwargs):
1927        '''Override the Bind() function: on the Mac the binding is to
1928        the main window, so that menus operate with any window on top.
1929        For other platforms, call the default wx.Frame Bind()
1930        '''
1931        if sys.platform == "darwin": # mac
1932            self.G2frame.Bind(*args,**kwargs)
1933        else:
1934            wx.Frame.Bind(self,*args,**kwargs)     
1935       
1936    def PrefillDataMenu(self,menu,helpType,helpLbl=None,empty=False):
1937        '''Create the "standard" part of data frame menus. Note that on Linux and
1938        Windows nothing happens here. On Mac, this menu duplicates the
1939        tree menu, but adds an extra help command for the data item and a separator.
1940        '''
1941        self.datamenu = menu
1942        self.helpType = helpType
1943        self.helpLbl = helpLbl
1944        if sys.platform == "darwin": # mac                         
1945            self.G2frame.FillMainMenu(menu) # add the data tree menu items
1946            if not empty:
1947                menu.Append(wx.Menu(title=''),title='|') # add a separator
1948       
1949    def PostfillDataMenu(self,empty=False):
1950        '''Create the "standard" part of data frame menus. Note that on Linux and
1951        Windows, this is the standard help Menu. On Mac, this menu duplicates the
1952        tree menu, but adds an extra help command for the data item and a separator.
1953        '''
1954        menu = self.datamenu
1955        helpType = self.helpType
1956        helpLbl = self.helpLbl
1957        if sys.platform == "darwin": # mac
1958            if not empty:
1959                menu.Append(wx.Menu(title=''),title='|') # add another separator
1960            menu.Append(AddHelp(self.G2frame,helpType=helpType, helpLbl=helpLbl),
1961                        title='&Help')
1962        else: # other
1963            menu.Append(menu=MyHelp(self,helpType=helpType, helpLbl=helpLbl),
1964                        title='&Help')
1965
1966    def _init_menus(self):
1967        'define all GSAS-II data frame menus'
1968
1969        # for use where no menu or data frame help is provided
1970        self.BlankMenu = wx.MenuBar()
1971       
1972        # Controls
1973        self.ControlsMenu = wx.MenuBar()
1974        self.PrefillDataMenu(self.ControlsMenu,helpType='Controls',empty=True)
1975        self.PostfillDataMenu(empty=True)
1976       
1977        # Notebook
1978        self.DataNotebookMenu = wx.MenuBar() 
1979        self.PrefillDataMenu(self.DataNotebookMenu,helpType='Notebook',empty=True)
1980        self.PostfillDataMenu(empty=True)
1981       
1982        # Comments
1983        self.DataCommentsMenu = wx.MenuBar()
1984        self.PrefillDataMenu(self.DataCommentsMenu,helpType='Comments',empty=True)
1985        self.PostfillDataMenu(empty=True)
1986       
1987        # Constraints
1988        self.ConstraintMenu = wx.MenuBar()
1989        self.PrefillDataMenu(self.ConstraintMenu,helpType='Constraints')
1990        self.ConstraintEdit = wx.Menu(title='')
1991        self.ConstraintMenu.Append(menu=self.ConstraintEdit, title='Edit')
1992        self.ConstraintEdit.Append(id=wxID_HOLDADD, kind=wx.ITEM_NORMAL,text='Add hold',
1993            help='Add hold on a parameter value')
1994        self.ConstraintEdit.Append(id=wxID_EQUIVADD, kind=wx.ITEM_NORMAL,text='Add equivalence',
1995            help='Add equivalence between parameter values')
1996        self.ConstraintEdit.Append(id=wxID_CONSTRAINTADD, kind=wx.ITEM_NORMAL,text='Add constraint',
1997            help='Add constraint on parameter values')
1998        self.ConstraintEdit.Append(id=wxID_FUNCTADD, kind=wx.ITEM_NORMAL,text='Add New Var',
1999            help='Add variable composed of existing parameter')
2000        self.PostfillDataMenu()
2001       
2002        # Rigid bodies
2003        self.VectorRBEdit = wx.Menu(title='')
2004        self.VectorRBEdit.Append(id=wxID_RIGIDBODYADD, kind=wx.ITEM_NORMAL,text='Add rigid body',
2005            help='Add vector rigid body')
2006        self.ResidueRBMenu = wx.Menu(title='')
2007        self.ResidueRBMenu.Append(id=wxID_RIGIDBODYIMPORT, kind=wx.ITEM_NORMAL,text='Import XYZ',
2008            help='Import rigid body XYZ from file')
2009        self.ResidueRBMenu.Append(id=wxID_RESIDUETORSSEQ, kind=wx.ITEM_NORMAL,text='Define sequence',
2010            help='Define torsion sequence')
2011        self.ResidueRBMenu.Append(id=wxID_RIGIDBODYADD, kind=wx.ITEM_NORMAL,text='Import residues',
2012            help='Import residue rigid bodies from macro file')
2013           
2014        self.RigidBodyMenu = wx.MenuBar()
2015        self.PrefillDataMenu(self.RigidBodyMenu,helpType='Rigid bodies')
2016        self.RigidBodyMenu.Append(menu=self.VectorRBEdit, title='Edit')       
2017        self.PostfillDataMenu()
2018           
2019        # Restraints
2020        self.RestraintEdit = wx.Menu(title='')
2021        self.RestraintEdit.Append(id=wxID_RESTSELPHASE, kind=wx.ITEM_NORMAL,text='Select phase',
2022            help='Select phase')
2023        self.RestraintEdit.Append(id=wxID_RESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add restraints',
2024            help='Add restraints')
2025        self.RestraintEdit.Enable(wxID_RESTRAINTADD,True)    #gets disenabled if macromolecule phase
2026        self.RestraintEdit.Append(id=wxID_AARESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add residue restraints',
2027            help='Add residue based restraints for macromolecules from macro file')
2028        self.RestraintEdit.Enable(wxID_AARESTRAINTADD,False)    #gets enabled if macromolecule phase
2029        self.RestraintEdit.Append(id=wxID_AARESTRAINTPLOT, kind=wx.ITEM_NORMAL,text='Plot residue restraints',
2030            help='Plot selected residue based restraints for macromolecules from macro file')
2031        self.RestraintEdit.Enable(wxID_AARESTRAINTPLOT,False)    #gets enabled if macromolecule phase
2032        self.RestraintEdit.Append(id=wxID_RESRCHANGEVAL, kind=wx.ITEM_NORMAL,text='Change value',
2033            help='Change observed value')
2034        self.RestraintEdit.Append(id=wxID_RESTCHANGEESD, kind=wx.ITEM_NORMAL,text='Change esd',
2035            help='Change esd in observed value')
2036        self.RestraintEdit.Append(id=wxID_RESTDELETE, kind=wx.ITEM_NORMAL,text='Delete restraints',
2037            help='Delete selected restraints')
2038
2039        self.RestraintMenu = wx.MenuBar()
2040        self.PrefillDataMenu(self.RestraintMenu,helpType='Restraints')
2041        self.RestraintMenu.Append(menu=self.RestraintEdit, title='Edit')
2042        self.PostfillDataMenu()
2043           
2044        # Sequential results
2045        self.SequentialMenu = wx.MenuBar()
2046        self.PrefillDataMenu(self.SequentialMenu,helpType='Sequential',helpLbl='Sequential Refinement')
2047        self.SequentialFile = wx.Menu(title='')
2048        self.SequentialMenu.Append(menu=self.SequentialFile, title='File')
2049        self.SequentialFile.Append(id=wxID_SAVESEQSEL, kind=wx.ITEM_NORMAL,text='Save...',
2050            help='Save selected sequential refinement results')
2051        self.PostfillDataMenu()
2052           
2053        # PDR
2054        self.ErrorMenu = wx.MenuBar()
2055        self.PrefillDataMenu(self.ErrorMenu,helpType='PWD Analysis',helpLbl='Powder Fit Error Analysis')
2056        self.ErrorAnal = wx.Menu(title='')
2057        self.ErrorMenu.Append(menu=self.ErrorAnal,title='Analysis')
2058        self.ErrorAnal.Append(id=wxID_PWDANALYSIS,kind=wx.ITEM_NORMAL,text='Analyze',
2059            help='Error analysis on powder pattern')
2060        self.PostfillDataMenu()
2061           
2062        # PDR / Limits
2063        self.LimitMenu = wx.MenuBar()
2064        self.PrefillDataMenu(self.LimitMenu,helpType='Limits')
2065        self.LimitEdit = wx.Menu(title='')
2066        self.LimitMenu.Append(menu=self.LimitEdit, title='Edit')
2067        self.LimitEdit.Append(id=wxID_LIMITCOPY, kind=wx.ITEM_NORMAL,text='Copy',
2068            help='Copy limits to other histograms')
2069        self.LimitEdit.Append(id=wxID_ADDEXCLREGION, kind=wx.ITEM_NORMAL,text='Add exclude',
2070            help='Add excluded region - select a point on plot; drag to adjust')           
2071        self.PostfillDataMenu()
2072           
2073        # PDR / Background
2074        self.BackMenu = wx.MenuBar()
2075        self.PrefillDataMenu(self.BackMenu,helpType='Background')
2076        self.BackEdit = wx.Menu(title='')
2077        self.BackMenu.Append(menu=self.BackEdit, title='File')
2078        self.BackEdit.Append(id=wxID_BACKCOPY, kind=wx.ITEM_NORMAL,text='Copy',
2079            help='Copy background parameters to other histograms')
2080        self.BackEdit.Append(id=wxID_BACKFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
2081            help='Copy background refinement flags to other histograms')
2082        self.PostfillDataMenu()
2083           
2084        # PDR / Instrument Parameters
2085        self.InstMenu = wx.MenuBar()
2086        self.PrefillDataMenu(self.InstMenu,helpType='Instrument Parameters')
2087        self.InstEdit = wx.Menu(title='')
2088        self.InstMenu.Append(menu=self.InstEdit, title='Operations')
2089        self.InstEdit.Append(help='Reset instrument profile parameters to default', 
2090            id=wxID_INSTLOAD, kind=wx.ITEM_NORMAL,text='Load profile...')
2091        self.InstEdit.Append(help='Load instrument profile parameters from file', 
2092            id=wxID_INSTSAVE, kind=wx.ITEM_NORMAL,text='Save profile...')
2093        self.InstEdit.Append(help='Save instrument profile parameters to file', 
2094            id=wxID_INSTPRMRESET, kind=wx.ITEM_NORMAL,text='Reset profile')
2095        self.InstEdit.Append(help='Copy instrument profile parameters to other histograms', 
2096            id=wxID_INSTCOPY, kind=wx.ITEM_NORMAL,text='Copy')
2097        self.InstEdit.Append(id=wxID_INSTFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
2098            help='Copy instrument parameter refinement flags to other histograms')
2099#        self.InstEdit.Append(help='Change radiation type (Ka12 - synch)',
2100#            id=wxID_CHANGEWAVETYPE, kind=wx.ITEM_NORMAL,text='Change radiation')
2101        self.PostfillDataMenu()
2102       
2103        # PDR / Sample Parameters
2104        self.SampleMenu = wx.MenuBar()
2105        self.PrefillDataMenu(self.SampleMenu,helpType='Sample Parameters')
2106        self.SampleEdit = wx.Menu(title='')
2107        self.SampleMenu.Append(menu=self.SampleEdit, title='File')
2108        self.SampleEdit.Append(id=wxID_SAMPLELOAD, kind=wx.ITEM_NORMAL,text='Load',
2109            help='Load sample parameters from file')
2110        self.SampleEdit.Append(id=wxID_SAMPLESAVE, kind=wx.ITEM_NORMAL,text='Save',
2111            help='Save sample parameters to file')
2112        self.SampleEdit.Append(id=wxID_SAMPLECOPY, kind=wx.ITEM_NORMAL,text='Copy',
2113            help='Copy refinable sample parameters to other histograms')
2114        self.SampleEdit.Append(id=wxID_SAMPLEFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
2115            help='Copy sample parameter refinement flags to other histograms')
2116        self.PostfillDataMenu()
2117
2118        # PDR / Peak List
2119        self.PeakMenu = wx.MenuBar()
2120        self.PrefillDataMenu(self.PeakMenu,helpType='Peak List')
2121        self.PeakEdit = wx.Menu(title='')
2122        self.PeakMenu.Append(menu=self.PeakEdit, title='Peak Fitting')
2123        self.AutoSearch = self.PeakEdit.Append(help='Automatic peak search', 
2124            id=wxID_AUTOSEARCH, kind=wx.ITEM_NORMAL,text='Auto search')
2125        self.UnDo = self.PeakEdit.Append(help='Undo last least squares refinement', 
2126            id=wxID_UNDO, kind=wx.ITEM_NORMAL,text='UnDo')
2127        self.PeakFit = self.PeakEdit.Append(id=wxID_LSQPEAKFIT, kind=wx.ITEM_NORMAL,text='LSQ PeakFit', 
2128            help='Peak fitting via least-squares' )
2129        self.PFOneCycle = self.PeakEdit.Append(id=wxID_LSQONECYCLE, kind=wx.ITEM_NORMAL,text='LSQ one cycle', 
2130            help='One cycle of Peak fitting via least-squares' )
2131        self.PeakEdit.Append(id=wxID_RESETSIGGAM, kind=wx.ITEM_NORMAL, 
2132            text='Reset sig and gam',help='Reset sigma and gamma to global fit' )
2133        self.PeakEdit.Append(id=wxID_CLEARPEAKS, kind=wx.ITEM_NORMAL,text='Clear peaks', 
2134            help='Clear the peak list' )
2135        self.PostfillDataMenu()
2136        self.UnDo.Enable(False)
2137        self.PeakFit.Enable(False)
2138        self.PFOneCycle.Enable(False)
2139       
2140        # PDR / Index Peak List
2141        self.IndPeaksMenu = wx.MenuBar()
2142        self.PrefillDataMenu(self.IndPeaksMenu,helpType='Index Peak List')
2143        self.IndPeaksEdit = wx.Menu(title='')
2144        self.IndPeaksMenu.Append(menu=self.IndPeaksEdit,title='Operations')
2145        self.IndPeaksEdit.Append(help='Load/Reload index peaks from peak list',id=wxID_INDXRELOAD, 
2146            kind=wx.ITEM_NORMAL,text='Load/Reload')
2147        self.PostfillDataMenu()
2148       
2149        # PDR / Unit Cells List
2150        self.IndexMenu = wx.MenuBar()
2151        self.PrefillDataMenu(self.IndexMenu,helpType='Unit Cells List')
2152        self.IndexEdit = wx.Menu(title='')
2153        self.IndexMenu.Append(menu=self.IndexEdit, title='Cell Index/Refine')
2154        self.IndexPeaks = self.IndexEdit.Append(help='', id=wxID_INDEXPEAKS, kind=wx.ITEM_NORMAL,
2155            text='Index Cell')
2156        self.CopyCell = self.IndexEdit.Append( id=wxID_COPYCELL, kind=wx.ITEM_NORMAL,text='Copy Cell', 
2157            help='Copy selected unit cell from indexing to cell refinement fields')
2158        self.RefineCell = self.IndexEdit.Append( id=wxID_REFINECELL, kind=wx.ITEM_NORMAL, 
2159            text='Refine Cell',help='Refine unit cell parameters from indexed peaks')
2160        self.MakeNewPhase = self.IndexEdit.Append( id=wxID_MAKENEWPHASE, kind=wx.ITEM_NORMAL,
2161            text='Make new phase',help='Make new phase from selected unit cell')
2162        self.PostfillDataMenu()
2163        self.IndexPeaks.Enable(False)
2164        self.CopyCell.Enable(False)
2165        self.RefineCell.Enable(False)
2166        self.MakeNewPhase.Enable(False)
2167       
2168        # PDR / Reflection Lists
2169        self.ReflMenu = wx.MenuBar()
2170        self.PrefillDataMenu(self.ReflMenu,helpType='Reflection List')
2171        self.ReflEdit = wx.Menu(title='')
2172        self.ReflMenu.Append(menu=self.ReflEdit, title='Reflection List')
2173        self.SelectPhase = self.ReflEdit.Append(help='Select phase for reflection list',id=wxID_SELECTPHASE, 
2174            kind=wx.ITEM_NORMAL,text='Select phase')
2175        self.PostfillDataMenu()
2176       
2177        # IMG / Image Controls
2178        self.ImageMenu = wx.MenuBar()
2179        self.PrefillDataMenu(self.ImageMenu,helpType='Image Controls')
2180        self.ImageEdit = wx.Menu(title='')
2181        self.ImageMenu.Append(menu=self.ImageEdit, title='Operations')
2182        self.ImageEdit.Append(help='Calibrate detector by fitting to calibrant lines', 
2183            id=wxID_IMCALIBRATE, kind=wx.ITEM_NORMAL,text='Calibrate')
2184        self.ImageEdit.Append(help='Recalibrate detector by fitting to calibrant lines', 
2185            id=wxID_IMRECALIBRATE, kind=wx.ITEM_NORMAL,text='Recalibrate')
2186        self.ImageEdit.Append(help='Clear calibration data points and rings',id=wxID_IMCLEARCALIB, 
2187            kind=wx.ITEM_NORMAL,text='Clear calibration')
2188        self.ImageEdit.Append(help='Integrate selected image',id=wxID_IMINTEGRATE, 
2189            kind=wx.ITEM_NORMAL,text='Integrate')
2190        self.ImageEdit.Append(help='Integrate all images selected from list',id=wxID_INTEGRATEALL,
2191            kind=wx.ITEM_NORMAL,text='Integrate all')
2192        self.ImageEdit.Append(help='Copy image controls to other images', 
2193            id=wxID_IMCOPYCONTROLS, kind=wx.ITEM_NORMAL,text='Copy Controls')
2194        self.ImageEdit.Append(help='Save image controls to file', 
2195            id=wxID_IMSAVECONTROLS, kind=wx.ITEM_NORMAL,text='Save Controls')
2196        self.ImageEdit.Append(help='Load image controls from file', 
2197            id=wxID_IMLOADCONTROLS, kind=wx.ITEM_NORMAL,text='Load Controls')
2198        self.PostfillDataMenu()
2199           
2200        # IMG / Masks
2201        self.MaskMenu = wx.MenuBar()
2202        self.PrefillDataMenu(self.MaskMenu,helpType='Image Masks')
2203        self.MaskEdit = wx.Menu(title='')
2204        self.MaskMenu.Append(menu=self.MaskEdit, title='Operations')
2205        submenu = wx.Menu()
2206        self.MaskEdit.AppendMenu(
2207            wx.ID_ANY,'Create new', submenu,
2208            help=''
2209            )
2210        self.MaskEdit.Append(help='Copy mask to other images', 
2211            id=wxID_MASKCOPY, kind=wx.ITEM_NORMAL,text='Copy mask')
2212        self.MaskEdit.Append(help='Save mask to file', 
2213            id=wxID_MASKSAVE, kind=wx.ITEM_NORMAL,text='Save mask')
2214        self.MaskEdit.Append(help='Load mask from file', 
2215            id=wxID_MASKLOAD, kind=wx.ITEM_NORMAL,text='Load mask')
2216        submenu.Append(help='Create an arc mask with mouse input', 
2217            id=wxID_NEWMASKARC, kind=wx.ITEM_NORMAL,text='Arc mask')
2218        submenu.Append(help='Create a frame mask with mouse input', 
2219            id=wxID_NEWMASKFRAME, kind=wx.ITEM_NORMAL,text='Frame mask')
2220        submenu.Append(help='Create a polygon mask with mouse input', 
2221            id=wxID_NEWMASKPOLY, kind=wx.ITEM_NORMAL,text='Polygon mask')
2222        submenu.Append(help='Create a ring mask with mouse input', 
2223            id=wxID_NEWMASKRING, kind=wx.ITEM_NORMAL,text='Ring mask')
2224        submenu.Append(help='Create a spot mask with mouse input', 
2225            id=wxID_NEWMASKSPOT, kind=wx.ITEM_NORMAL,text='Spot mask')
2226        self.PostfillDataMenu()
2227           
2228        # IMG / Stress/Strain
2229        self.StrStaMenu = wx.MenuBar()
2230        self.PrefillDataMenu(self.StrStaMenu,helpType='Stress/Strain')
2231        self.StrStaEdit = wx.Menu(title='')
2232        self.StrStaMenu.Append(menu=self.StrStaEdit, title='Operations')
2233        self.StrStaEdit.Append(help='Append d-zero for one ring', 
2234            id=wxID_APPENDDZERO, kind=wx.ITEM_NORMAL,text='Append d-zero')
2235        self.StrStaEdit.Append(help='Fit stress/strain data', 
2236            id=wxID_STRSTAFIT, kind=wx.ITEM_NORMAL,text='Fit stress/strain')
2237        self.StrStaEdit.Append(help='Copy stress/strain data to other images', 
2238            id=wxID_STRSTACOPY, kind=wx.ITEM_NORMAL,text='Copy stress/strain')
2239        self.StrStaEdit.Append(help='Save stress/strain data to file', 
2240            id=wxID_STRSTASAVE, kind=wx.ITEM_NORMAL,text='Save stress/strain')
2241        self.StrStaEdit.Append(help='Load stress/strain data from file', 
2242            id=wxID_STRSTALOAD, kind=wx.ITEM_NORMAL,text='Load stress/strain')
2243        self.PostfillDataMenu()
2244           
2245        # PDF / PDF Controls
2246        self.PDFMenu = wx.MenuBar()
2247        self.PrefillDataMenu(self.PDFMenu,helpType='PDF Controls')
2248        self.PDFEdit = wx.Menu(title='')
2249        self.PDFMenu.Append(menu=self.PDFEdit, title='PDF Controls')
2250        self.PDFEdit.Append(help='Add element to sample composition',id=wxID_PDFADDELEMENT, kind=wx.ITEM_NORMAL,
2251            text='Add element')
2252        self.PDFEdit.Append(help='Delete element from sample composition',id=wxID_PDFDELELEMENT, kind=wx.ITEM_NORMAL,
2253            text='Delete element')
2254        self.PDFEdit.Append(help='Copy PDF controls', id=wxID_PDFCOPYCONTROLS, kind=wx.ITEM_NORMAL,
2255            text='Copy controls')
2256        #        self.PDFEdit.Append(help='Load PDF controls from file',id=wxID_PDFLOADCONTROLS, kind=wx.ITEM_NORMAL,
2257        #            text='Load Controls')
2258        #        self.PDFEdit.Append(help='Save PDF controls to file', id=wxID_PDFSAVECONTROLS, kind=wx.ITEM_NORMAL,
2259        #            text='Save controls')
2260        self.PDFEdit.Append(help='Compute PDF', id=wxID_PDFCOMPUTE, kind=wx.ITEM_NORMAL,
2261            text='Compute PDF')
2262        self.PDFEdit.Append(help='Compute all PDFs', id=wxID_PDFCOMPUTEALL, kind=wx.ITEM_NORMAL,
2263            text='Compute all PDFs')
2264        self.PostfillDataMenu()
2265       
2266        # Phase / General tab
2267        self.DataGeneral = wx.MenuBar()
2268        self.PrefillDataMenu(self.DataGeneral,helpType='General', helpLbl='Phase/General')
2269        self.DataGeneral.Append(menu=wx.Menu(title=''),title='Select tab')
2270        self.GeneralCalc = wx.Menu(title='')
2271        self.DataGeneral.Append(menu=self.GeneralCalc,title='Compute')
2272        self.GeneralCalc.Append(help='Compute Fourier map',id=wxID_FOURCALC, kind=wx.ITEM_NORMAL,
2273            text='Fourier map')
2274        self.GeneralCalc.Append(help='Search Fourier map',id=wxID_FOURSEARCH, kind=wx.ITEM_NORMAL,
2275            text='Search map')
2276        self.GeneralCalc.Append(help='Run charge flipping',id=wxID_CHARGEFLIP, kind=wx.ITEM_NORMAL,
2277            text='Charge flipping')
2278        self.GeneralCalc.Append(help='Clear map',id=wxID_FOURCLEAR, kind=wx.ITEM_NORMAL,
2279            text='Clear map')
2280        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing',id=wxID_SINGLEMCSA, kind=wx.ITEM_NORMAL,
2281            text='MC/SA')
2282        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing on multiprocessors',id=wxID_MULTIMCSA, kind=wx.ITEM_NORMAL,
2283            text='Multi MC/SA')            #currently not useful
2284        self.PostfillDataMenu()
2285       
2286        # Phase / Data tab
2287        self.DataMenu = wx.MenuBar()
2288        self.PrefillDataMenu(self.DataMenu,helpType='Data', helpLbl='Phase/Data')
2289        self.DataMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2290        self.DataEdit = wx.Menu(title='')
2291        self.DataMenu.Append(menu=self.DataEdit, title='Edit')
2292        self.DataEdit.Append(id=wxID_PWDRADD, kind=wx.ITEM_NORMAL,text='Add powder histograms',
2293            help='Select new powder histograms to be used for this phase')
2294        self.DataEdit.Append(id=wxID_HKLFADD, kind=wx.ITEM_NORMAL,text='Add single crystal histograms',
2295            help='Select new single crystal histograms to be used for this phase')
2296        self.DataEdit.Append(id=wxID_DATADELETE, kind=wx.ITEM_NORMAL,text='Delete histograms',
2297            help='Delete histograms from use for this phase')
2298        self.PostfillDataMenu()
2299           
2300        # Phase / Atoms tab
2301        self.AtomsMenu = wx.MenuBar()
2302        self.PrefillDataMenu(self.AtomsMenu,helpType='Atoms')
2303        self.AtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2304        self.AtomEdit = wx.Menu(title='')
2305        self.AtomCompute = wx.Menu(title='')
2306        self.AtomsMenu.Append(menu=self.AtomEdit, title='Edit')
2307        self.AtomsMenu.Append(menu=self.AtomCompute, title='Compute')
2308        self.AtomEdit.Append(id=wxID_ATOMSEDITADD, kind=wx.ITEM_NORMAL,text='Append atom',
2309            help='Appended as an H atom')
2310        self.AtomEdit.Append(id=wxID_ATOMSVIEWADD, kind=wx.ITEM_NORMAL,text='Append view point',
2311            help='Appended as an H atom')
2312        self.AtomEdit.Append(id=wxID_ATOMSEDITINSERT, kind=wx.ITEM_NORMAL,text='Insert atom',
2313            help='Select atom row to insert before; inserted as an H atom')
2314        self.AtomEdit.Append(id=wxID_ATOMVIEWINSERT, kind=wx.ITEM_NORMAL,text='Insert view point',
2315            help='Select atom row to insert before; inserted as an H atom')
2316        self.AtomEdit.Append(id=wxID_ATOMMOVE, kind=wx.ITEM_NORMAL,text='Move atom to view point',
2317            help='Select single atom to move')
2318        self.AtomEdit.Append(id=wxID_ATOMSEDITDELETE, kind=wx.ITEM_NORMAL,text='Delete atom',
2319            help='Select atoms to delete first')
2320        self.AtomEdit.Append(id=wxID_ATOMSREFINE, kind=wx.ITEM_NORMAL,text='Set atom refinement flags',
2321            help='Select atoms to refine first')
2322        self.AtomEdit.Append(id=wxID_ATOMSMODIFY, kind=wx.ITEM_NORMAL,text='Modify atom parameters',
2323            help='Select atoms to modify first')
2324        self.AtomEdit.Append(id=wxID_ATOMSTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
2325            help='Select atoms to transform first')
2326        self.AtomEdit.Append(id=wxID_RELOADDRAWATOMS, kind=wx.ITEM_NORMAL,text='Reload draw atoms',
2327            help='Reload atom drawing list')
2328        submenu = wx.Menu()
2329        self.AtomEdit.AppendMenu(wx.ID_ANY, 'Reimport atoms', submenu, 
2330            help='Reimport atoms from file; sequence must match')
2331        # setup a cascade menu for the formats that have been defined
2332        self.ReImportMenuId = {}  # points to readers for each menu entry
2333        for reader in self.G2frame.ImportPhaseReaderlist:
2334            item = submenu.Append(
2335                wx.ID_ANY,help=reader.longFormatName,
2336                kind=wx.ITEM_NORMAL,text='reimport coordinates from '+reader.formatName+' file')
2337            self.ReImportMenuId[item.GetId()] = reader
2338        item = submenu.Append(
2339            wx.ID_ANY,
2340            help='Reimport coordinates, try to determine format from file',
2341            kind=wx.ITEM_NORMAL,
2342            text='guess format from file')
2343        self.ReImportMenuId[item.GetId()] = None # try all readers
2344
2345        self.AtomCompute.Append(id=wxID_ATOMSDISAGL, kind=wx.ITEM_NORMAL,text='Show Distances && Angles',
2346            help='Compute distances & angles for selected atoms')
2347        self.AtomCompute.Append(id=wxID_ATOMSPDISAGL, kind=wx.ITEM_NORMAL,text='Save Distances && Angles',
2348            help='Compute distances & angles for selected atoms')
2349        self.PostfillDataMenu()
2350                 
2351        # Phase / Draw Options tab
2352        self.DataDrawOptions = wx.MenuBar()
2353        self.PrefillDataMenu(self.DataDrawOptions,helpType='Draw Options', helpLbl='Phase/Draw Options')
2354        self.DataDrawOptions.Append(menu=wx.Menu(title=''),title='Select tab')
2355        self.PostfillDataMenu()
2356       
2357        # Phase / Draw Atoms tab
2358        self.DrawAtomsMenu = wx.MenuBar()
2359        self.PrefillDataMenu(self.DrawAtomsMenu,helpType='Draw Atoms')
2360        self.DrawAtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2361        self.DrawAtomEdit = wx.Menu(title='')
2362        self.DrawAtomCompute = wx.Menu(title='')
2363        self.DrawAtomRestraint = wx.Menu(title='')
2364        self.DrawAtomRigidBody = wx.Menu(title='')
2365        self.DrawAtomsMenu.Append(menu=self.DrawAtomEdit, title='Edit')
2366        self.DrawAtomsMenu.Append(menu=self.DrawAtomCompute,title='Compute')
2367        self.DrawAtomsMenu.Append(menu=self.DrawAtomRestraint, title='Restraints')
2368        self.DrawAtomsMenu.Append(menu=self.DrawAtomRigidBody, title='Rigid body')
2369        self.DrawAtomEdit.Append(id=wxID_DRAWATOMSTYLE, kind=wx.ITEM_NORMAL,text='Atom style',
2370            help='Select atoms first')
2371        self.DrawAtomEdit.Append(id=wxID_DRAWATOMLABEL, kind=wx.ITEM_NORMAL,text='Atom label',
2372            help='Select atoms first')
2373        self.DrawAtomEdit.Append(id=wxID_DRAWATOMCOLOR, kind=wx.ITEM_NORMAL,text='Atom color',
2374            help='Select atoms first')
2375        self.DrawAtomEdit.Append(id=wxID_DRAWATOMRESETCOLOR, kind=wx.ITEM_NORMAL,text='Reset atom colors',
2376            help='Resets all atom colors to defaults')
2377        self.DrawAtomEdit.Append(id=wxID_DRAWVIEWPOINT, kind=wx.ITEM_NORMAL,text='View point',
2378            help='View point is 1st atom selected')
2379        self.DrawAtomEdit.Append(id=wxID_DRAWADDEQUIV, kind=wx.ITEM_NORMAL,text='Add atoms',
2380            help='Add symmetry & cell equivalents to drawing set from selected atoms')
2381        self.DrawAtomEdit.Append(id=wxID_DRAWTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
2382            help='Transform selected atoms by symmetry & cell translations')
2383        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCOORD, kind=wx.ITEM_NORMAL,text='Fill CN-sphere',
2384            help='Fill coordination sphere for selected atoms')           
2385        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCELL, kind=wx.ITEM_NORMAL,text='Fill unit cell',
2386            help='Fill unit cell with selected atoms')
2387        self.DrawAtomEdit.Append(id=wxID_DRAWDELETE, kind=wx.ITEM_NORMAL,text='Delete atoms',
2388            help='Delete atoms from drawing set')
2389        self.DrawAtomCompute.Append(id=wxID_DRAWDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2390            help='Compute distance of selected atoms from view point')   
2391        self.DrawAtomCompute.Append(id=wxID_DRAWDISAGLTOR, kind=wx.ITEM_NORMAL,text='Dist. Ang. Tors.',
2392            help='Compute distance, angle or torsion for 2-4 selected atoms')   
2393        self.DrawAtomCompute.Append(id=wxID_DRAWPLANE, kind=wx.ITEM_NORMAL,text='Best plane',
2394            help='Compute best plane for 4+ selected atoms')   
2395        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRBOND, kind=wx.ITEM_NORMAL,text='Add bond restraint',
2396            help='Add bond restraint for selected atoms (2)')
2397        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRANGLE, kind=wx.ITEM_NORMAL,text='Add angle restraint',
2398            help='Add angle restraint for selected atoms (3: one end 1st)')
2399        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRPLANE, kind=wx.ITEM_NORMAL,text='Add plane restraint',
2400            help='Add plane restraint for selected atoms (4+)')
2401        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRCHIRAL, kind=wx.ITEM_NORMAL,text='Add chiral restraint',
2402            help='Add chiral restraint for selected atoms (4: center atom 1st)')
2403        self.DrawAtomRigidBody.Append(id=wxID_DRAWDEFINERB, kind=wx.ITEM_NORMAL,text='Define rigid body',
2404            help='Define rigid body with selected atoms')
2405        self.PostfillDataMenu()
2406
2407        # Phase / MCSA tab
2408        self.MCSAMenu = wx.MenuBar()
2409        self.PrefillDataMenu(self.MCSAMenu,helpType='MC/SA')
2410        self.MCSAMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2411        self.MCSAEdit = wx.Menu(title='')
2412        self.MCSAMenu.Append(menu=self.MCSAEdit, title='MC/SA')
2413        self.MCSAEdit.Append(id=wxID_ADDMCSAATOM, kind=wx.ITEM_NORMAL,text='Add atom', 
2414            help='Add single atom to MC/SA model')
2415        self.MCSAEdit.Append(id=wxID_ADDMCSARB, kind=wx.ITEM_NORMAL,text='Add rigid body', 
2416            help='Add rigid body to MC/SA model' )
2417        self.MCSAEdit.Append(id=wxID_CLEARMCSARB, kind=wx.ITEM_NORMAL,text='Clear rigid bodies', 
2418            help='Clear all atoms & rigid bodies from MC/SA model' )
2419        self.MCSAEdit.Append(id=wxID_MOVEMCSA, kind=wx.ITEM_NORMAL,text='Move MC/SA solution', 
2420            help='Move MC/SA solution to atom list' )
2421        self.MCSAEdit.Append(id=wxID_MCSACLEARRESULTS, kind=wx.ITEM_NORMAL,text='Clear results', 
2422            help='Clear table of MC/SA results' )
2423        self.PostfillDataMenu()
2424           
2425        # Phase / Texture tab
2426        self.TextureMenu = wx.MenuBar()
2427        self.PrefillDataMenu(self.TextureMenu,helpType='Texture')
2428        self.TextureMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2429        self.TextureEdit = wx.Menu(title='')
2430        self.TextureMenu.Append(menu=self.TextureEdit, title='Texture')
2431        self.TextureEdit.Append(id=wxID_REFINETEXTURE, kind=wx.ITEM_NORMAL,text='Refine texture', 
2432            help='Refine the texture coefficients from sequential Pawley results')
2433        self.TextureEdit.Append(id=wxID_CLEARTEXTURE, kind=wx.ITEM_NORMAL,text='Clear texture', 
2434            help='Clear the texture coefficients' )
2435        self.PostfillDataMenu()
2436           
2437        # Phase / Pawley tab
2438        self.PawleyMenu = wx.MenuBar()
2439        self.PrefillDataMenu(self.PawleyMenu,helpType='Pawley')
2440        self.PawleyMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2441        self.PawleyEdit = wx.Menu(title='')
2442        self.PawleyMenu.Append(menu=self.PawleyEdit,title='Operations')
2443        self.PawleyEdit.Append(id=wxID_PAWLEYLOAD, kind=wx.ITEM_NORMAL,text='Pawley create',
2444            help='Initialize Pawley reflection list')
2445        self.PawleyEdit.Append(id=wxID_PAWLEYESTIMATE, kind=wx.ITEM_NORMAL,text='Pawley estimate',
2446            help='Estimate initial Pawley intensities')
2447        self.PawleyEdit.Append(id=wxID_PAWLEYUPDATE, kind=wx.ITEM_NORMAL,text='Pawley update',
2448            help='Update negative Pawley intensities with -0.5*Fobs and turn off refinemnt')
2449        self.PostfillDataMenu()
2450           
2451        # Phase / Map peaks tab
2452        self.MapPeaksMenu = wx.MenuBar()
2453        self.PrefillDataMenu(self.MapPeaksMenu,helpType='Map peaks')
2454        self.MapPeaksMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2455        self.MapPeaksEdit = wx.Menu(title='')
2456        self.MapPeaksMenu.Append(menu=self.MapPeaksEdit, title='Map peaks')
2457        self.MapPeaksEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks', 
2458            help='Move selected peaks to atom list')
2459        self.MapPeaksEdit.Append(id=wxID_PEAKSVIEWPT, kind=wx.ITEM_NORMAL,text='View point',
2460            help='View point is 1st peak selected')
2461        self.MapPeaksEdit.Append(id=wxID_PEAKSDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2462            help='Compute distance of selected peaks from view point')   
2463        self.MapPeaksEdit.Append(id=wxID_SHOWBONDS, kind=wx.ITEM_NORMAL,text='Hide bonds',
2464            help='Hide or show bonds between peak positions')   
2465        self.MapPeaksEdit.Append(id=wxID_PEAKSDA, kind=wx.ITEM_NORMAL,text='Calc dist/ang', 
2466            help='Calculate distance or angle for selection')
2467        self.MapPeaksEdit.Append(id=wxID_FINDEQVPEAKS, kind=wx.ITEM_NORMAL,text='Equivalent peaks', 
2468            help='Find equivalent peaks')
2469        self.MapPeaksEdit.Append(id=wxID_PEAKSUNIQUE, kind=wx.ITEM_NORMAL,text='Unique peaks', 
2470            help='Select unique set')
2471        self.MapPeaksEdit.Append(id=wxID_PEAKSDELETE, kind=wx.ITEM_NORMAL,text='Delete peaks', 
2472            help='Delete selected peaks')
2473        self.MapPeaksEdit.Append(id=wxID_PEAKSCLEAR, kind=wx.ITEM_NORMAL,text='Clear peaks', 
2474            help='Clear the map peak list')
2475        self.PostfillDataMenu()
2476
2477        # Phase / Rigid bodies tab
2478        self.RigidBodiesMenu = wx.MenuBar()
2479        self.PrefillDataMenu(self.RigidBodiesMenu,helpType='Rigid bodies')
2480        self.RigidBodiesMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2481        self.RigidBodiesEdit = wx.Menu(title='')
2482        self.RigidBodiesMenu.Append(menu=self.RigidBodiesEdit, title='Edit')
2483        self.RigidBodiesEdit.Append(id=wxID_ASSIGNATMS2RB, kind=wx.ITEM_NORMAL,text='Assign atoms to rigid body',
2484            help='Select & position rigid body in structure of existing atoms')
2485        self.RigidBodiesEdit.Append(id=wxID_AUTOFINDRESRB, kind=wx.ITEM_NORMAL,text='Auto find residues',
2486            help='Auto find of residue RBs in macromolecule')
2487        self.RigidBodiesEdit.Append(id=wxID_COPYRBPARMS, kind=wx.ITEM_NORMAL,text='Copy rigid body parms',
2488            help='Copy rigid body location & TLS parameters')
2489        self.RigidBodiesEdit.Append(id=wxID_GLOBALTHERM, kind=wx.ITEM_NORMAL,text='Global thermal motion',
2490            help='Global setting of residue thermal motion models')
2491        self.RigidBodiesEdit.Append(id=wxID_GLOBALRESREFINE, kind=wx.ITEM_NORMAL,text='Global residue refine',
2492            help='Global setting of residue RB refinement flags')
2493        self.RigidBodiesEdit.Append(id=wxID_RBREMOVEALL, kind=wx.ITEM_NORMAL,text='Remove all rigid bodies',
2494            help='Remove all rigid body assignment for atoms')
2495        self.PostfillDataMenu()
2496    # end of GSAS-II menu definitions
2497       
2498    def _init_ctrls(self, parent,name=None,size=None,pos=None):
2499        wx.Frame.__init__(self,parent=parent,
2500            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX | wx.FRAME_FLOAT_ON_PARENT ,
2501            size=size,pos=pos,title='GSAS-II data display')
2502        self._init_menus()
2503        if name:
2504            self.SetLabel(name)
2505        self.Show()
2506       
2507    def __init__(self,parent,frame,data=None,name=None, size=None,pos=None):
2508        self.G2frame = frame
2509        self._init_ctrls(parent,name,size,pos)
2510        self.data = data
2511        clientSize = wx.ClientDisplayRect()
2512        Size = self.GetSize()
2513        xPos = clientSize[2]-Size[0]
2514        self.SetPosition(wx.Point(xPos,clientSize[1]+250))
2515        self.AtomGrid = []
2516        self.selectedRow = 0
2517       
2518    def setSizePosLeft(self,Width):
2519        clientSize = wx.ClientDisplayRect()
2520        Width[1] = min(Width[1],clientSize[2]-300)
2521        Width[0] = max(Width[0],300)
2522        self.SetSize(Width)
2523#        self.SetPosition(wx.Point(clientSize[2]-Width[0],clientSize[1]+250))
2524       
2525    def Clear(self):
2526        self.ClearBackground()
2527        self.DestroyChildren()
2528                   
2529################################################################################
2530#####  GSNotebook
2531################################################################################           
2532       
2533class GSNoteBook(wx.aui.AuiNotebook):
2534    '''Notebook used in various locations; implemented with wx.aui extension
2535    '''
2536    def __init__(self, parent, name='',size = None):
2537        wx.aui.AuiNotebook.__init__(self, parent, -1,
2538                                    style=wx.aui.AUI_NB_TOP |
2539                                    wx.aui.AUI_NB_SCROLL_BUTTONS)
2540        if size: self.SetSize(size)
2541                                                     
2542    def Clear(self):       
2543        GSNoteBook.DeleteAllPages(self)
2544       
2545    def FindPage(self,name):
2546        numPage = self.GetPageCount()
2547        for page in range(numPage):
2548            if self.GetPageText(page) == name:
2549                return page
2550
2551    def ChangeSelection(self,page):
2552        # in wx.Notebook ChangeSelection is like SetSelection, but it
2553        # does not invoke the event related to pressing the tab button
2554        # I don't see a way to do that in aui.
2555        oldPage = self.GetSelection()
2556        self.SetSelection(page)
2557        return oldPage
2558
2559    # def __getattribute__(self,name):
2560    #     '''This method provides a way to print out a message every time
2561    #     that a method in a class is called -- to see what all the calls
2562    #     might be, or where they might be coming from.
2563    #     Cute trick for debugging!
2564    #     '''
2565    #     attr = object.__getattribute__(self, name)
2566    #     if hasattr(attr, '__call__'):
2567    #         def newfunc(*args, **kwargs):
2568    #             print('GSauiNoteBook calling %s' %attr.__name__)
2569    #             result = attr(*args, **kwargs)
2570    #             return result
2571    #         return newfunc
2572    #     else:
2573    #         return attr
2574           
2575################################################################################
2576#####  GSGrid
2577################################################################################           
2578       
2579class GSGrid(wg.Grid):
2580    '''Basic wx.Grid implementation
2581    '''
2582    def __init__(self, parent, name=''):
2583        wg.Grid.__init__(self,parent,-1,name=name)                   
2584        #self.SetSize(parent.GetClientSize())
2585        # above removed to speed drawing of initial grid
2586        # does not appear to be needed
2587           
2588    def Clear(self):
2589        wg.Grid.ClearGrid(self)
2590       
2591    def SetCellStyle(self,r,c,color="white",readonly=True):
2592        self.SetCellBackgroundColour(r,c,color)
2593        self.SetReadOnly(r,c,isReadOnly=readonly)
2594       
2595    def GetSelection(self):
2596        #this is to satisfy structure drawing stuff in G2plt when focus changes
2597        return None
2598                                               
2599################################################################################
2600#####  Table
2601################################################################################           
2602       
2603class Table(wg.PyGridTableBase):
2604    '''Basic data table for use with GSgrid
2605    '''
2606    def __init__(self, data=[], rowLabels=None, colLabels=None, types = None):
2607        wg.PyGridTableBase.__init__(self)
2608        self.colLabels = colLabels
2609        self.rowLabels = rowLabels
2610        self.dataTypes = types
2611        self.data = data
2612       
2613    def AppendRows(self, numRows=1):
2614        self.data.append([])
2615        return True
2616       
2617    def CanGetValueAs(self, row, col, typeName):
2618        if self.dataTypes:
2619            colType = self.dataTypes[col].split(':')[0]
2620            if typeName == colType:
2621                return True
2622            else:
2623                return False
2624        else:
2625            return False
2626
2627    def CanSetValueAs(self, row, col, typeName):
2628        return self.CanGetValueAs(row, col, typeName)
2629
2630    def DeleteRow(self,pos):
2631        data = self.GetData()
2632        self.SetData([])
2633        new = []
2634        for irow,row in enumerate(data):
2635            if irow <> pos:
2636                new.append(row)
2637        self.SetData(new)
2638       
2639    def GetColLabelValue(self, col):
2640        if self.colLabels:
2641            return self.colLabels[col]
2642           
2643    def GetData(self):
2644        data = []
2645        for row in range(self.GetNumberRows()):
2646            data.append(self.GetRowValues(row))
2647        return data
2648       
2649    def GetNumberCols(self):
2650        try:
2651            return len(self.colLabels)
2652        except TypeError:
2653            return None
2654       
2655    def GetNumberRows(self):
2656        return len(self.data)
2657       
2658    def GetRowLabelValue(self, row):
2659        if self.rowLabels:
2660            return self.rowLabels[row]
2661       
2662    def GetColValues(self, col):
2663        data = []
2664        for row in range(self.GetNumberRows()):
2665            data.append(self.GetValue(row, col))
2666        return data
2667       
2668    def GetRowValues(self, row):
2669        data = []
2670        for col in range(self.GetNumberCols()):
2671            data.append(self.GetValue(row, col))
2672        return data
2673       
2674    def GetTypeName(self, row, col):
2675        try:
2676            return self.dataTypes[col]
2677        except TypeError:
2678            return None
2679
2680    def GetValue(self, row, col):
2681        try:
2682            return self.data[row][col]
2683        except IndexError:
2684            return None
2685           
2686    def InsertRows(self, pos, rows):
2687        for row in range(rows):
2688            self.data.insert(pos,[])
2689            pos += 1
2690       
2691    def IsEmptyCell(self,row,col):
2692        try:
2693            return not self.data[row][col]
2694        except IndexError:
2695            return True
2696       
2697    def OnKeyPress(self, event):
2698        dellist = self.GetSelectedRows()
2699        if event.GetKeyCode() == wx.WXK_DELETE and dellist:
2700            grid = self.GetView()
2701            for i in dellist: grid.DeleteRow(i)
2702               
2703    def SetColLabelValue(self, col, label):
2704        numcols = self.GetNumberCols()
2705        if col > numcols-1:
2706            self.colLabels.append(label)
2707        else:
2708            self.colLabels[col]=label
2709       
2710    def SetData(self,data):
2711        for row in range(len(data)):
2712            self.SetRowValues(row,data[row])
2713               
2714    def SetRowLabelValue(self, row, label):
2715        self.rowLabels[row]=label
2716           
2717    def SetRowValues(self,row,data):
2718        self.data[row] = data
2719           
2720    def SetValue(self, row, col, value):
2721        def innerSetValue(row, col, value):
2722            try:
2723                self.data[row][col] = value
2724            except TypeError:
2725                return
2726            except IndexError:
2727                print row,col,value
2728                # add a new row
2729                if row > self.GetNumberRows():
2730                    self.data.append([''] * self.GetNumberCols())
2731                elif col > self.GetNumberCols():
2732                    for row in range(self.GetNumberRows):
2733                        self.data[row].append('')
2734                print self.data
2735                self.data[row][col] = value
2736        innerSetValue(row, col, value)
2737       
2738################################################################################
2739#### Help
2740################################################################################
2741
2742def ShowHelp(helpType,frame):
2743    '''Called to bring up a web page for documentation.'''
2744    global htmlFirstUse
2745    # look up a definition for help info from dict
2746    helplink = helpLocDict.get(helpType)
2747    if helplink is None:
2748        # no defined link to use, create a default based on key
2749        helplink = 'gsasII.html#'+helpType.replace(' ','_')
2750    helplink = os.path.join(path2GSAS2,'help',helplink)
2751    if helpMode == 'internal':
2752        try:
2753            htmlPanel.LoadFile(helplink)
2754            htmlFrame.Raise()
2755        except:
2756            htmlFrame = wx.Frame(frame, -1, size=(610, 510))
2757            htmlFrame.Show(True)
2758            htmlFrame.SetTitle("HTML Window") # N.B. reset later in LoadFile
2759            htmlPanel = MyHtmlPanel(htmlFrame,-1)
2760            htmlPanel.LoadFile(helplink)
2761    else:
2762        pfx = "file://"
2763        if sys.platform.lower().startswith('win'):
2764            pfx = ''
2765        if htmlFirstUse:
2766            webbrowser.open_new(pfx+helplink)
2767            htmlFirstUse = False
2768        else:
2769            webbrowser.open(pfx+helplink, new=0, autoraise=True)
2770
2771################################################################################
2772#####  Notebook
2773################################################################################           
2774       
2775def UpdateNotebook(G2frame,data):
2776    '''Called when the data tree notebook entry is selected. Allows for
2777    editing of the text in that tree entry
2778    '''
2779    def OnNoteBook(event):
2780        data = G2frame.dataDisplay.GetValue()
2781        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Notebook'),data)
2782                   
2783    if G2frame.dataDisplay:
2784        G2frame.dataDisplay.Destroy()
2785    G2frame.dataFrame.SetLabel('Notebook')
2786    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2787        style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
2788    G2frame.dataDisplay.Bind(wx.EVT_TEXT_ENTER,OnNoteBook)
2789    G2frame.dataDisplay.Bind(wx.EVT_KILL_FOCUS,OnNoteBook)
2790    for line in data:
2791        G2frame.dataDisplay.AppendText(line+"\n")
2792    G2frame.dataDisplay.AppendText('Notebook entry @ '+time.ctime()+"\n")
2793    G2frame.dataFrame.setSizePosLeft([400,250])
2794           
2795################################################################################
2796#####  Controls
2797################################################################################           
2798       
2799def UpdateControls(G2frame,data):
2800    '''Edit overall GSAS-II controls in main Controls data tree entry
2801    '''
2802    #patch
2803    if 'deriv type' not in data:
2804        data = {}
2805        data['deriv type'] = 'analytic Hessian'
2806        data['min dM/M'] = 0.0001
2807        data['shift factor'] = 1.
2808        data['max cyc'] = 3       
2809        data['F**2'] = True
2810        data['minF/sig'] = 0
2811    if 'shift factor' not in data:
2812        data['shift factor'] = 1.
2813    if 'max cyc' not in data:
2814        data['max cyc'] = 3
2815    if 'F**2' not in data:
2816        data['F**2'] = True
2817        data['minF/sig'] = 0
2818    if 'Author' not in data:
2819        data['Author'] = 'no name'
2820    #end patch
2821
2822    def SeqSizer():
2823       
2824        def OnSelectData(event):
2825            choices = ['All',]+GetPatternTreeDataNames(G2frame,['PWDR',])
2826            sel = []
2827            if 'Seq Data' in data:
2828                for item in data['Seq Data']:
2829                    sel.append(choices.index(item))
2830            names = []
2831            dlg = wx.MultiChoiceDialog(G2frame,'Select data:','Sequential refinement',choices)
2832            dlg.SetSelections(sel)
2833            if dlg.ShowModal() == wx.ID_OK:
2834                sel = dlg.GetSelections()
2835                for i in sel: names.append(choices[i])
2836                if 'All' in names:
2837                    names = choices[1:]
2838                data['Seq Data'] = names               
2839            dlg.Destroy()
2840            reverseSel.Enable(True)
2841           
2842        def OnReverse(event):
2843            data['Reverse Seq'] = reverseSel.GetValue()
2844                   
2845        seqSizer = wx.BoxSizer(wx.HORIZONTAL)
2846        seqSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Sequential Refinement Powder Data: '),0,wx.ALIGN_CENTER_VERTICAL)
2847        selSeqData = wx.Button(G2frame.dataDisplay,-1,label=' Select data')
2848        selSeqData.Bind(wx.EVT_BUTTON,OnSelectData)
2849        seqSizer.Add(selSeqData,0,wx.ALIGN_CENTER_VERTICAL)
2850        seqSizer.Add((5,0),0)
2851        reverseSel = wx.CheckBox(G2frame.dataDisplay,-1,label=' Reverse order?')
2852        reverseSel.Bind(wx.EVT_CHECKBOX,OnReverse)
2853        if 'Seq Data' not in data:
2854            reverseSel.Enable(False)
2855        if 'Reverse Seq' in data:
2856            reverseSel.SetValue(data['Reverse Seq'])
2857        seqSizer.Add(reverseSel,0,wx.ALIGN_CENTER_VERTICAL)
2858        return seqSizer
2859       
2860    def LSSizer():       
2861       
2862        def OnDerivType(event):
2863            data['deriv type'] = derivSel.GetValue()
2864            derivSel.SetValue(data['deriv type'])
2865            wx.CallAfter(UpdateControls,G2frame,data)
2866           
2867        def OnConvergence(event):
2868            try:
2869                value = max(1.e-9,min(1.0,float(Cnvrg.GetValue())))
2870            except ValueError:
2871                value = 0.0001
2872            data['min dM/M'] = value
2873            Cnvrg.SetValue('%.2g'%(value))
2874           
2875        def OnMaxCycles(event):
2876            data['max cyc'] = int(maxCyc.GetValue())
2877            maxCyc.SetValue(str(data['max cyc']))
2878                       
2879        def OnFactor(event):
2880            try:
2881                value = min(max(float(Factr.GetValue()),0.00001),100.)
2882            except ValueError:
2883                value = 1.0
2884            data['shift factor'] = value
2885            Factr.SetValue('%.5f'%(value))
2886           
2887        def OnFsqRef(event):
2888            data['F**2'] = fsqRef.GetValue()
2889       
2890        def OnMinSig(event):
2891            try:
2892                value = min(max(float(minSig.GetValue()),0.),5.)
2893            except ValueError:
2894                value = 1.0
2895            data['minF/sig'] = value
2896            minSig.SetValue('%.2f'%(value))
2897
2898        LSSizer = wx.FlexGridSizer(cols=4,vgap=5,hgap=5)
2899        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement derivatives: '),0,wx.ALIGN_CENTER_VERTICAL)
2900        Choice=['analytic Jacobian','numeric','analytic Hessian']
2901        derivSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['deriv type'],choices=Choice,
2902            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2903        derivSel.SetValue(data['deriv type'])
2904        derivSel.Bind(wx.EVT_COMBOBOX, OnDerivType)
2905           
2906        LSSizer.Add(derivSel,0,wx.ALIGN_CENTER_VERTICAL)
2907        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min delta-M/M: '),0,wx.ALIGN_CENTER_VERTICAL)
2908        Cnvrg = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2g'%(data['min dM/M']),style=wx.TE_PROCESS_ENTER)
2909        Cnvrg.Bind(wx.EVT_TEXT_ENTER,OnConvergence)
2910        Cnvrg.Bind(wx.EVT_KILL_FOCUS,OnConvergence)
2911        LSSizer.Add(Cnvrg,0,wx.ALIGN_CENTER_VERTICAL)
2912        if 'Hessian' in data['deriv type']:
2913            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Max cycles: '),0,wx.ALIGN_CENTER_VERTICAL)
2914            Choice = ['0','1','2','3','5','10','15','20']
2915            maxCyc = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['max cyc']),choices=Choice,
2916                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2917            maxCyc.SetValue(str(data['max cyc']))
2918            maxCyc.Bind(wx.EVT_COMBOBOX, OnMaxCycles)
2919            LSSizer.Add(maxCyc,0,wx.ALIGN_CENTER_VERTICAL)
2920        else:
2921            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Initial shift factor: '),0,wx.ALIGN_CENTER_VERTICAL)
2922            Factr = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.5f'%(data['shift factor']),style=wx.TE_PROCESS_ENTER)
2923            Factr.Bind(wx.EVT_TEXT_ENTER,OnFactor)
2924            Factr.Bind(wx.EVT_KILL_FOCUS,OnFactor)
2925            LSSizer.Add(Factr,0,wx.ALIGN_CENTER_VERTICAL)
2926        if G2frame.Sngl:
2927            LSSizer.Add((1,0),)
2928            LSSizer.Add((1,0),)
2929            fsqRef = wx.CheckBox(G2frame.dataDisplay,-1,label='Refine HKLF as F^2? ')
2930            fsqRef.SetValue(data['F**2'])
2931            fsqRef.Bind(wx.EVT_CHECKBOX,OnFsqRef)
2932            LSSizer.Add(fsqRef,0,wx.ALIGN_CENTER_VERTICAL)
2933            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label='Min obs/sig (0-5): '),0,wx.ALIGN_CENTER_VERTICAL)
2934            minSig = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(data['minF/sig']),style=wx.TE_PROCESS_ENTER)
2935            minSig.Bind(wx.EVT_TEXT_ENTER,OnMinSig)
2936            minSig.Bind(wx.EVT_KILL_FOCUS,OnMinSig)
2937            LSSizer.Add(minSig,0,wx.ALIGN_CENTER_VERTICAL)
2938        return LSSizer
2939       
2940    def AuthSizer():
2941
2942        def OnAuthor(event):
2943            data['Author'] = auth.GetValue()
2944
2945        Author = data['Author']
2946        authSizer = wx.BoxSizer(wx.HORIZONTAL)
2947        authSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' CIF Author (last, first):'),0,wx.ALIGN_CENTER_VERTICAL)
2948        auth = wx.TextCtrl(G2frame.dataDisplay,-1,value=Author,style=wx.TE_PROCESS_ENTER)
2949        auth.Bind(wx.EVT_TEXT_ENTER,OnAuthor)
2950        auth.Bind(wx.EVT_KILL_FOCUS,OnAuthor)
2951        authSizer.Add(auth,0,wx.ALIGN_CENTER_VERTICAL)
2952        return authSizer
2953       
2954       
2955    if G2frame.dataDisplay:
2956        G2frame.dataDisplay.Destroy()
2957    if not G2frame.dataFrame.GetStatusBar():
2958        Status = G2frame.dataFrame.CreateStatusBar()
2959        Status.SetStatusText('')
2960    G2frame.dataFrame.SetLabel('Controls')
2961    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
2962    SetDataMenuBar(G2frame,G2frame.dataFrame.ControlsMenu)
2963    mainSizer = wx.BoxSizer(wx.VERTICAL)
2964    mainSizer.Add((5,5),0)
2965    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement Controls:'),0,wx.ALIGN_CENTER_VERTICAL)   
2966    mainSizer.Add(LSSizer())
2967    mainSizer.Add((5,5),0)
2968    mainSizer.Add(SeqSizer())
2969    mainSizer.Add((5,5),0)
2970    mainSizer.Add(AuthSizer())
2971    mainSizer.Add((5,5),0)
2972       
2973    mainSizer.Layout()   
2974    G2frame.dataDisplay.SetSizer(mainSizer)
2975    G2frame.dataDisplay.SetSize(mainSizer.Fit(G2frame.dataFrame))
2976    G2frame.dataFrame.setSizePosLeft(mainSizer.Fit(G2frame.dataFrame))
2977     
2978################################################################################
2979#####  Comments
2980################################################################################           
2981       
2982def UpdateComments(G2frame,data):                   
2983
2984    if G2frame.dataDisplay:
2985        G2frame.dataDisplay.Destroy()
2986    G2frame.dataFrame.SetLabel('Comments')
2987    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2988        style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_DONTWRAP)
2989    for line in data:
2990        G2frame.dataDisplay.AppendText(line+'\n')
2991    G2frame.dataFrame.setSizePosLeft([400,250])
2992           
2993################################################################################
2994#####  Sequential Results
2995################################################################################           
2996       
2997def UpdateSeqResults(G2frame,data):
2998    """
2999    Called when the Sequential Results data tree entry is selected
3000    to show results from a sequential refinement.
3001   
3002    :param wx.Frame G2frame: main GSAS-II data tree windows
3003
3004    :param dict data: a dictionary containing the following items: 
3005
3006            * 'histNames' - list of histogram names in order as processed by Sequential Refinement
3007            * 'varyList' - list of variables - identical over all refinements in sequence
3008            * 'histName' - dictionaries for all data sets processed, which contains:
3009
3010              * 'variables'- result[0] from leastsq call
3011              * 'varyList' - list of variables; same as above
3012              * 'sig' - esds for variables
3013              * 'covMatrix' - covariance matrix from individual refinement
3014              * 'title' - histogram name; same as dict item name
3015              * 'newAtomDict' - new atom parameters after shifts applied
3016              * 'newCellDict' - new cell parameters after shifts to A0-A5 applied'
3017    """
3018    if not data:
3019        print 'No sequential refinement results'
3020        return
3021    histNames = data['histNames']
3022       
3023    def GetSampleParms():
3024        sampleParmDict = {'Temperature':[],'Pressure':[],'Humidity':[],'Voltage':[],'Force':[],}
3025        sampleParm = {}
3026        for name in histNames:
3027            Id = GetPatternTreeItemId(G2frame,G2frame.root,name)
3028            sampleData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Sample Parameters'))
3029            for item in sampleParmDict:
3030                sampleParmDict[item].append(sampleData[item])
3031        for item in sampleParmDict:
3032            frstValue = sampleParmDict[item][0]
3033            if np.any(np.array(sampleParmDict[item])-frstValue):
3034                sampleParm[item] = sampleParmDict[item]           
3035        return sampleParm
3036           
3037    def GetRwps():
3038        Rwps = []
3039        for name in histNames:
3040            Rwps.append(data[name]['Rvals']['Rwp'])
3041        return Rwps
3042           
3043    def GetSigData(parm):
3044        sigData = []
3045        for name in histNames:
3046            sigList = data[name]['sig']
3047            if colLabels[parm] in atomList:
3048                sigData.append(sigList[colLabels.index(atomList[colLabels[parm]])-1])
3049            elif colLabels[parm] in cellList:
3050                sigData.append(sigList[colLabels.index(cellList[colLabels[parm]])-1])
3051            else:
3052                sigData.append(sigList[parm-1])
3053        return sigData
3054   
3055    def Select(event):
3056        cols = G2frame.dataDisplay.GetSelectedCols()
3057        rows = G2frame.dataDisplay.GetSelectedRows()
3058        if cols:
3059            plotData = []
3060            plotSig = []
3061            plotNames = []
3062            for col in cols:
3063                plotData.append(G2frame.SeqTable.GetColValues(col))
3064                if col:     #not Rwp
3065                    plotSig.append(GetSigData(col))
3066                else:
3067                    plotSig.append(0.0)
3068                plotNames.append(G2frame.SeqTable.GetColLabelValue(col))
3069            plotData = np.array(plotData)
3070            G2plt.PlotSeq(G2frame,plotData,plotSig,plotNames,sampleParms)
3071        elif rows:
3072            name = histNames[rows[0]]       #only does 1st one selected
3073            G2plt.PlotCovariance(G2frame,data[name])
3074           
3075    def OnSaveSelSeq(event):       
3076        cols = G2frame.dataDisplay.GetSelectedCols()
3077        if cols:
3078            numRows = G2frame.SeqTable.GetNumberRows()
3079            dataNames = []
3080            saveNames = [G2frame.SeqTable.GetRowLabelValue(r) for r in range(numRows)]
3081            saveData = []
3082            for col in cols:
3083                dataNames.append(G2frame.SeqTable.GetColLabelValue(col))
3084                if col:     #not Rwp
3085                    saveData.append(zip(G2frame.SeqTable.GetColValues(col),GetSigData(col)))
3086                else:
3087                    saveData.append(zip(G2frame.SeqTable.GetColValues(col),0.0))
3088            lenName = len(saveNames[0])
3089            saveData = np.swapaxes(np.array(saveData),0,1)
3090            dlg = wx.FileDialog(G2frame, 'Choose text output file for your selection', '.', '', 
3091                'Text output file (*.txt)|*.txt',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3092            try:
3093                if dlg.ShowModal() == wx.ID_OK:
3094                    SeqTextFile = dlg.GetPath()
3095                    SeqTextFile = G2IO.FileDlgFixExt(dlg,SeqTextFile)
3096                    SeqFile = open(SeqTextFile,'w')
3097                    line = %s  '%('name'.center(lenName))
3098                    for item in dataNames:
3099                        line += ' %12s %12s '%(item.center(12),'esd'.center(12))
3100                    line += '\n'
3101                    SeqFile.write(line)
3102                    for i,item in enumerate(saveData):
3103                        line = " '%s' "%(saveNames[i])
3104                        for val,esd in item:
3105                            line += ' %12.6f %12.6f '%(val,esd)
3106                        line += '\n'
3107                        SeqFile.write(line)
3108                    SeqFile.close()
3109            finally:
3110                dlg.Destroy()
3111           
3112               
3113    if G2frame.dataDisplay:
3114        G2frame.dataDisplay.Destroy()
3115    atomList = {}
3116    newAtomDict = data[histNames[0]]['newAtomDict']
3117    for item in newAtomDict:
3118        if item in data['varyList']:
3119            atomList[newAtomDict[item][0]] = item
3120    cellList = {}
3121    newCellDict = data[histNames[0]]['newCellDict']
3122    for item in newCellDict:
3123        if item in data['varyList']:
3124            cellList[newCellDict[item][0]] = item
3125    sampleParms = GetSampleParms()
3126    Rwps = GetRwps()
3127    SetDataMenuBar(G2frame,G2frame.dataFrame.SequentialMenu)
3128    G2frame.dataFrame.SetLabel('Sequential refinement results')
3129    G2frame.dataFrame.CreateStatusBar()
3130    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeq, id=wxID_SAVESEQSEL)
3131    colLabels = ['Rwp',]+data['varyList']+atomList.keys()+cellList.keys()
3132    Types = (len(data['varyList']+atomList.keys()+cellList.keys())+1)*[wg.GRID_VALUE_FLOAT,]
3133    seqList = [[Rwps[i],]+list(data[name]['variables']) for i,name in enumerate(histNames)]
3134    for i,item in enumerate(seqList):
3135        newAtomDict = data[histNames[i]]['newAtomDict']
3136        newCellDict = data[histNames[i]]['newCellDict']
3137        item += [newAtomDict[atomList[parm]][1] for parm in atomList.keys()]
3138        item += [newCellDict[cellList[parm]][1] for parm in cellList.keys()]
3139    G2frame.SeqTable = Table(seqList,colLabels=colLabels,rowLabels=histNames,types=Types)
3140    G2frame.dataDisplay = GSGrid(parent=G2frame.dataFrame)
3141    G2frame.dataDisplay.SetTable(G2frame.SeqTable, True)
3142    G2frame.dataDisplay.EnableEditing(False)
3143    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_LEFT_DCLICK, Select)
3144    G2frame.dataDisplay.SetRowLabelSize(8*len(histNames[0]))       #pretty arbitrary 8
3145    G2frame.dataDisplay.SetMargins(0,0)
3146    G2frame.dataDisplay.AutoSizeColumns(True)
3147    G2frame.dataFrame.setSizePosLeft([700,350])
3148       
3149################################################################################
3150#####  Main PWDR panel
3151################################################################################           
3152       
3153def UpdatePWHKPlot(G2frame,kind,item):
3154    '''Called when the histogram main tree entry is called. Displays the
3155    histogram weight factor, refinement statistics for the histogram
3156    and the range of data for a simulation.
3157
3158    Also invokes a plot of the histogram.
3159    '''
3160    def onEditSimRange(event):
3161        'Edit simulation range'
3162        inp = [
3163            min(data[1][0]),
3164            max(data[1][0]),
3165            None
3166            ]
3167        inp[2] = (inp[1] - inp[0])/(len(data[1][0])-1.)
3168        names = ('start angle', 'end angle', 'step size')
3169        dictlst = [inp] * len(inp)
3170        elemlst = range(len(inp))
3171        dlg = ScrolledMultiEditor(
3172            G2frame,[inp] * len(inp), range(len(inp)), names,
3173            header='Edit simulation range',
3174            minvals=(0.001,0.001,0.0001),
3175            maxvals=(180.,180.,.1),
3176            )
3177        dlg.CenterOnParent()
3178        val = dlg.ShowModal()
3179        dlg.Destroy()
3180        if val != wx.ID_OK: return
3181        if inp[0] > inp[1]:
3182            end,start,step = inp
3183        else:               
3184            start,end,step = inp
3185        step = abs(step)
3186        N = int((end-start)/step)+1
3187        newdata = np.linspace(start,end,N,True)
3188        if len(newdata) < 2: return # too small a range - reject
3189        data[1] = [newdata,np.zeros_like(newdata),np.ones_like(newdata),
3190            np.zeros_like(newdata),np.zeros_like(newdata),np.zeros_like(newdata)]
3191        Tmin = newdata[0]
3192        Tmax = newdata[-1]
3193        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,item,'Limits'),
3194            [(Tmin,Tmax),[Tmin,Tmax]])
3195        UpdatePWHKPlot(G2frame,kind,item) # redisplay data screen
3196
3197    def OnErrorAnalysis(event):
3198        G2plt.PlotDeltSig(G2frame,kind)
3199       
3200    def OnWtFactor(event):
3201        try:
3202            val = float(wtval.GetValue())
3203        except ValueError:
3204            val = data[0]['wtFactor']
3205        data[0]['wtFactor'] = val
3206        wtval.SetValue('%.3f'%(val))
3207           
3208    data = G2frame.PatternTree.GetItemPyData(item)
3209#patches
3210    if 'wtFactor' not in data[0]:
3211        data[0] = {'wtFactor':1.0}
3212    if isinstance(data[1],list) and kind == 'HKLF':
3213        RefData = {'RefList':[],'FF':[]}
3214        for ref in data[1]:
3215            RefData['RefList'].append(ref[:11]+[ref[13],])
3216            RefData['FF'].append(ref[14])
3217        data[1] = RefData
3218        G2frame.PatternTree.SetItemPyData(item,data)
3219#end patches
3220    if G2frame.dataDisplay:
3221        G2frame.dataDisplay.Destroy()
3222    SetDataMenuBar(G2frame,G2frame.dataFrame.ErrorMenu)
3223    G2frame.dataFrame.Bind(wx.EVT_MENU,OnErrorAnalysis, id=wxID_PWDANALYSIS)
3224    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
3225   
3226    mainSizer = wx.BoxSizer(wx.VERTICAL)
3227    mainSizer.Add((5,5),)
3228    wtSizer = wx.BoxSizer(wx.HORIZONTAL)
3229    wtSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Weight factor: '),0,wx.ALIGN_CENTER_VERTICAL)
3230    wtval = wx.TextCtrl(G2frame.dataDisplay,-1,'%.3f'%(data[0]['wtFactor']),style=wx.TE_PROCESS_ENTER)
3231    wtval.Bind(wx.EVT_TEXT_ENTER,OnWtFactor)
3232    wtval.Bind(wx.EVT_KILL_FOCUS,OnWtFactor)
3233    wtSizer.Add(wtval,0,wx.ALIGN_CENTER_VERTICAL)
3234    mainSizer.Add(wtSizer)
3235    if data[0].get('Dummy'):
3236        simSizer = wx.BoxSizer(wx.HORIZONTAL)
3237        Tmin = min(data[1][0])
3238        Tmax = max(data[1][0])
3239        num = len(data[1][0])
3240        step = (Tmax - Tmin)/(num-1)
3241        t = u'2\u03b8' # 2theta
3242        lbl =  u'Simulation range: {:.2f} to {:.2f} {:s}\nwith {:.4f} steps ({:d} points)'
3243        lbl += u'\n(Edit range resets observed intensities).'
3244        lbl = lbl.format(Tmin,Tmax,t,step,num)
3245        simSizer.Add(wx.StaticText(G2frame.dataDisplay,wx.ID_ANY,lbl),
3246                    0,wx.ALIGN_CENTER_VERTICAL)
3247        but = wx.Button(G2frame.dataDisplay,wx.ID_ANY,"Edit range")
3248        but.Bind(wx.EVT_BUTTON,onEditSimRange)
3249        simSizer.Add(but,0,wx.ALIGN_CENTER_VERTICAL)
3250        mainSizer.Add(simSizer)
3251    if 'Nobs' in data[0]:
3252        mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
3253            ' Data residual wR: %.3f%% on %d observations'%(data[0]['wR'],data[0]['Nobs'])))
3254        for value in data[0]:
3255            if 'Nref' in value:
3256                mainSizer.Add((5,5),)
3257                pfx = value.split('Nref')[0]
3258                name = data[0][pfx.split(':')[0]+'::Name']
3259                mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' For phase '+name+':'))
3260                mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
3261                    u' Unweighted phase residuals RF\u00b2: %.3f%%, RF: %.3f%% on %d reflections  '% \
3262                    (data[0][pfx+'Rf^2'],data[0][pfx+'Rf'],data[0][value])))
3263    mainSizer.Add((5,5),)
3264    mainSizer.Layout()   
3265    G2frame.dataDisplay.SetSizer(mainSizer)
3266    Size = mainSizer.Fit(G2frame.dataFrame)
3267    Size[1] += 10
3268    G2frame.dataFrame.setSizePosLeft(Size)
3269    G2frame.PatternTree.SetItemPyData(item,data)
3270    if kind == 'PWDR':
3271        G2plt.PlotPatterns(G2frame,newPlot=True)
3272    elif kind == 'HKLF':
3273        G2plt.PlotSngl(G2frame,newPlot=True)
3274                 
3275################################################################################
3276#####  HKLF controls
3277################################################################################           
3278       
3279def UpdateHKLControls(G2frame,data):
3280    '''Needs a doc string
3281    '''
3282   
3283    def OnScaleSlider(event):
3284        scale = int(scaleSel.GetValue())/1000.
3285        scaleSel.SetValue(int(scale*1000.))
3286        data['Scale'] = scale*1.
3287        G2plt.PlotSngl(G2frame)
3288       
3289    def OnLayerSlider(event):
3290        layer = layerSel.GetValue()
3291        data['Layer'] = layer
3292        G2plt.PlotSngl(G2frame)
3293       
3294    def OnSelZone(event):
3295        data['Zone'] = zoneSel.GetValue()
3296        izone = zones.index(data['Zone'])
3297        layerSel.SetRange(maxValue=HKLmax[izone],minValue=HKLmin[izone])
3298        G2plt.PlotSngl(G2frame,newPlot=True)
3299       
3300    def OnSelType(event):
3301        data['Type'] = typeSel.GetValue()
3302        G2plt.PlotSngl(G2frame)
3303       
3304    def SetStatusLine():
3305        Status.SetStatusText("")
3306                                     
3307    if G2frame.dataDisplay:
3308        G2frame.dataDisplay.Destroy()
3309    if not G2frame.dataFrame.GetStatusBar():
3310        Status = G2frame.dataFrame.CreateStatusBar()
3311    SetStatusLine()
3312    zones = ['100','010','001']
3313    HKLmax = data['HKLmax']
3314    HKLmin = data['HKLmin']
3315    typeChoices = ['Fosq','Fo','|DFsq|/sig','|DFsq|>sig','|DFsq|>3sig']
3316    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
3317    SetDataMenuBar(G2frame)
3318    G2frame.dataFrame.SetTitle('HKL Plot Controls')
3319    mainSizer = wx.BoxSizer(wx.VERTICAL)
3320    mainSizer.Add((5,10),0)
3321   
3322    scaleSizer = wx.BoxSizer(wx.HORIZONTAL)
3323    scaleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Scale'),0,
3324        wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
3325    scaleSel = wx.Slider(parent=G2frame.dataDisplay,maxValue=1000,minValue=1,
3326        style=wx.SL_HORIZONTAL,value=int(data['Scale']*10))
3327    scaleSizer.Add(scaleSel,1,wx.EXPAND|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
3328    scaleSel.SetLineSize(10)
3329    scaleSel.SetPageSize(10)
3330    scaleSel.Bind(wx.EVT_SLIDER, OnScaleSlider)
3331    mainSizer.Add(scaleSizer,0,wx.EXPAND|wx.RIGHT)
3332    mainSizer.Add((0,10),0)   
3333   
3334    zoneSizer = wx.BoxSizer(wx.HORIZONTAL)
3335    zoneSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Zone  '),0,
3336        wx.ALIGN_CENTER_VERTICAL)
3337    zoneSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['Zone'],choices=['100','010','001'],
3338        style=wx.CB_READONLY|wx.CB_DROPDOWN)
3339    zoneSel.Bind(wx.EVT_COMBOBOX, OnSelZone)
3340    zoneSizer.Add(zoneSel,0,wx.ALIGN_CENTER_VERTICAL)
3341    zoneSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Plot type  '),0,
3342        wx.ALIGN_CENTER_VERTICAL)       
3343    typeSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['Type'],choices=typeChoices,
3344        style=wx.CB_READONLY|wx.CB_DROPDOWN)
3345    typeSel.Bind(wx.EVT_COMBOBOX, OnSelType)
3346    zoneSizer.Add(typeSel,0,wx.ALIGN_CENTER_VERTICAL)
3347    zoneSizer.Add((10,0),0)   
3348    mainSizer.Add(zoneSizer,0,wx.EXPAND|wx.RIGHT)
3349    mainSizer.Add((0,10),0)   
3350       
3351    izone = zones.index(data['Zone'])
3352    layerSizer = wx.BoxSizer(wx.HORIZONTAL)
3353    layerSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Layer'),0,
3354        wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
3355    layerSel = wx.Slider(parent=G2frame.dataDisplay,maxValue=HKLmax[izone],minValue=HKLmin[izone],
3356        style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS|wx.SL_LABELS,value=0)
3357    layerSel.SetLineSize(1)
3358    layerSel.SetPageSize(1)
3359    layerSel.Bind(wx.EVT_SLIDER, OnLayerSlider)   
3360    layerSizer.Add(layerSel,1,wx.EXPAND|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
3361    layerSizer.Add((10,0),0)   
3362    mainSizer.Add(layerSizer,1,wx.EXPAND|wx.RIGHT)
3363
3364       
3365    mainSizer.Layout()   
3366    G2frame.dataDisplay.SetSizer(mainSizer)
3367    G2frame.dataDisplay.SetSize(mainSizer.Fit(G2frame.dataFrame))
3368    G2frame.dataFrame.setSizePosLeft(mainSizer.Fit(G2frame.dataFrame))
3369
3370################################################################################
3371#####  Pattern tree routines
3372################################################################################           
3373       
3374def GetPatternTreeDataNames(G2frame,dataTypes):
3375    '''Needs a doc string
3376    '''
3377    names = []
3378    item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)       
3379    while item:
3380        name = G2frame.PatternTree.GetItemText(item)
3381        if name[:4] in dataTypes:
3382            names.append(name)
3383        item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
3384    return names
3385                         
3386def GetPatternTreeItemId(G2frame, parentId, itemText):
3387    '''Needs a doc string
3388    '''
3389    item, cookie = G2frame.PatternTree.GetFirstChild(parentId)
3390    while item:
3391        if G2frame.PatternTree.GetItemText(item) == itemText:
3392            return item
3393        item, cookie = G2frame.PatternTree.GetNextChild(parentId, cookie)
3394    return 0               
3395
3396def MovePatternTreeToGrid(G2frame,item):
3397    '''Needs a doc string
3398    '''
3399   
3400#    print G2frame.PatternTree.GetItemText(item)
3401   
3402    oldPage = None # will be set later if already on a Phase item
3403    if G2frame.dataFrame:
3404        SetDataMenuBar(G2frame)
3405        if G2frame.dataFrame.GetLabel() == 'Comments':
3406            try:
3407                data = [G2frame.dataDisplay.GetValue()]
3408                G2frame.dataDisplay.Clear() 
3409                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Comments')
3410                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
3411            except:     #clumsy but avoids dead window problem when opening another project
3412                pass
3413        elif G2frame.dataFrame.GetLabel() == 'Notebook':
3414            try:
3415                data = [G2frame.dataDisplay.GetValue()]
3416                G2frame.dataDisplay.Clear() 
3417                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Notebook')
3418                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
3419            except:     #clumsy but avoids dead window problem when opening another project
3420                pass
3421        elif 'Phase Data for' in G2frame.dataFrame.GetLabel():
3422            if G2frame.dataDisplay: 
3423                oldPage = G2frame.dataDisplay.GetSelection()
3424        G2frame.dataFrame.Clear()
3425        G2frame.dataFrame.SetLabel('')
3426    else:
3427        #create the frame for the data item window
3428        G2frame.dataFrame = DataFrame(parent=G2frame.mainPanel,frame=G2frame)
3429        G2frame.dataFrame.PhaseUserSize = None
3430       
3431    G2frame.dataFrame.Raise()           
3432    G2frame.PickId = 0
3433    parentID = G2frame.root
3434    for i in G2frame.ExportPattern: i.Enable(False)
3435    defWid = [250,150]
3436    if item != G2frame.root:
3437        parentID = G2frame.PatternTree.GetItemParent(item)
3438    if G2frame.PatternTree.GetItemParent(item) == G2frame.root:
3439        G2frame.PatternId = item
3440        G2frame.PickId = item
3441        if G2frame.PatternTree.GetItemText(item) == 'Notebook':
3442            SetDataMenuBar(G2frame,G2frame.dataFrame.DataNotebookMenu)
3443            G2frame.PatternId = 0
3444            for i in G2frame.ExportPattern: i.Enable(False)
3445            data = G2frame.PatternTree.GetItemPyData(item)
3446            UpdateNotebook(G2frame,data)
3447        elif G2frame.PatternTree.GetItemText(item) == 'Controls':
3448            G2frame.PatternId = 0
3449            for i in G2frame.ExportPattern: i.Enable(False)
3450            data = G2frame.PatternTree.GetItemPyData(item)
3451            if not data:           #fill in defaults
3452                data = {
3453                    #least squares controls
3454                    'deriv type':'analytic Hessian','min dM/M':0.0001,'shift factor':1.0,'max cyc':3}
3455                G2frame.PatternTree.SetItemPyData(item,data)                             
3456            for i in G2frame.Refine: i.Enable(True)
3457            for i in G2frame.SeqRefine: i.Enable(True)
3458            UpdateControls(G2frame,data)
3459        elif G2frame.PatternTree.GetItemText(item) == 'Sequential results':
3460            data = G2frame.PatternTree.GetItemPyData(item)
3461            UpdateSeqResults(G2frame,data)           
3462        elif G2frame.PatternTree.GetItemText(item) == 'Covariance':
3463            data = G2frame.PatternTree.GetItemPyData(item)
3464            G2frame.dataFrame.setSizePosLeft(defWid)
3465            text = ''
3466            if 'Rvals' in data:
3467                Nvars = len(data['varyList'])
3468                Rvals = data['Rvals']
3469                text = '\nFinal residuals: \nwR = %.3f%% \nchi**2 = %.1f \nGOF = %.2f'%(Rvals['Rwp'],Rvals['chisq'],Rvals['GOF'])
3470                text += '\nNobs = %d \nNvals = %d'%(Rvals['Nobs'],Nvars)
3471                if 'lamMax' in Rvals:
3472                    text += '\nlog10 MaxLambda = %.1f'%(np.log10(Rvals['lamMax']))
3473            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
3474                value='See plot window for covariance display'+text,style=wx.TE_MULTILINE)
3475            G2plt.PlotCovariance(G2frame,data)
3476        elif G2frame.PatternTree.GetItemText(item) == 'Constraints':
3477            data = G2frame.PatternTree.GetItemPyData(item)
3478            G2cnstG.UpdateConstraints(G2frame,data)
3479        elif G2frame.PatternTree.GetItemText(item) == 'Rigid bodies':
3480            data = G2frame.PatternTree.GetItemPyData(item)
3481            G2cnstG.UpdateRigidBodies(G2frame,data)
3482        elif G2frame.PatternTree.GetItemText(item) == 'Restraints':
3483            data = G2frame.PatternTree.GetItemPyData(item)
3484            Phases = G2frame.GetPhaseData()
3485            phase = ''
3486            phaseName = ''
3487            if Phases:
3488                phaseName = Phases.keys()[0]
3489            G2frame.dataFrame.setSizePosLeft(defWid)
3490            G2restG.UpdateRestraints(G2frame,data,Phases,phaseName)
3491        elif 'IMG' in G2frame.PatternTree.GetItemText(item):
3492            G2frame.Image = item
3493            G2plt.PlotImage(G2frame,newPlot=True)
3494        elif 'PKS' in G2frame.PatternTree.GetItemText(item):
3495            G2plt.PlotPowderLines(G2frame)
3496        elif 'PWDR' in G2frame.PatternTree.GetItemText(item):
3497            for i in G2frame.ExportPattern: i.Enable(True)
3498            UpdatePWHKPlot(G2frame,'PWDR',item)
3499        elif 'HKLF' in G2frame.PatternTree.GetItemText(item):
3500            G2frame.Sngl = item
3501            UpdatePWHKPlot(G2frame,'HKLF',item)
3502        elif 'PDF' in G2frame.PatternTree.GetItemText(item):
3503            G2frame.PatternId = item
3504            for i in G2frame.ExportPDF: i.Enable(True)
3505            G2plt.PlotISFG(G2frame,type='S(Q)')
3506        elif G2frame.PatternTree.GetItemText(item) == 'Phases':
3507            G2frame.dataFrame.setSizePosLeft(defWid)
3508            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
3509                value='Select one phase to see its parameters')           
3510    elif 'I(Q)' in G2frame.PatternTree.GetItemText(item):
3511        G2frame.PickId = item
3512        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3513        G2plt.PlotISFG(G2frame,type='I(Q)',newPlot=True)
3514    elif 'S(Q)' in G2frame.PatternTree.GetItemText(item):
3515        G2frame.PickId = item
3516        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3517        G2plt.PlotISFG(G2frame,type='S(Q)',newPlot=True)
3518    elif 'F(Q)' in G2frame.PatternTree.GetItemText(item):
3519        G2frame.PickId = item
3520        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3521        G2plt.PlotISFG(G2frame,type='F(Q)',newPlot=True)
3522    elif 'G(R)' in G2frame.PatternTree.GetItemText(item):
3523        G2frame.PickId = item
3524        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3525        G2plt.PlotISFG(G2frame,type='G(R)',newPlot=True)           
3526    elif G2frame.PatternTree.GetItemText(parentID) == 'Phases':
3527        G2frame.PickId = item
3528        data = G2frame.PatternTree.GetItemPyData(item)
3529        G2phG.UpdatePhaseData(G2frame,item,data,oldPage)
3530    elif G2frame.PatternTree.GetItemText(item) == 'Comments':
3531        SetDataMenuBar(G2frame,G2frame.dataFrame.DataCommentsMenu)
3532        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3533        G2frame.PickId = item
3534        data = G2frame.PatternTree.GetItemPyData(item)
3535        UpdateComments(G2frame,data)
3536    elif G2frame.PatternTree.GetItemText(item) == 'Image Controls':
3537        G2frame.dataFrame.SetTitle('Image Controls')
3538        G2frame.PickId = item
3539        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3540        masks = G2frame.PatternTree.GetItemPyData(
3541            GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
3542        data = G2frame.PatternTree.GetItemPyData(item)
3543        G2imG.UpdateImageControls(G2frame,data,masks)
3544        G2plt.PlotImage(G2frame)
3545    elif G2frame.PatternTree.GetItemText(item) == 'Masks':
3546        G2frame.dataFrame.SetTitle('Masks')
3547        G2frame.PickId = item
3548        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3549        data = G2frame.PatternTree.GetItemPyData(item)
3550        G2imG.UpdateMasks(G2frame,data)
3551        G2plt.PlotImage(G2frame)
3552    elif G2frame.PatternTree.GetItemText(item) == 'Stress/Strain':
3553        G2frame.dataFrame.SetTitle('Stress/Strain')
3554        G2frame.PickId = item
3555        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3556        data = G2frame.PatternTree.GetItemPyData(item)
3557        G2imG.UpdateStressStrain(G2frame,data)
3558        G2plt.PlotImage(G2frame)
3559    elif G2frame.PatternTree.GetItemText(item) == 'HKL Plot Controls':
3560        G2frame.PickId = item
3561        G2frame.Sngl = G2frame.PatternTree.GetItemParent(item)
3562        data = G2frame.PatternTree.GetItemPyData(item)
3563        UpdateHKLControls(G2frame,data)
3564        G2plt.PlotSngl(G2frame)
3565    elif G2frame.PatternTree.GetItemText(item) == 'PDF Controls':
3566        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3567        for i in G2frame.ExportPDF: i.Enable(True)
3568        G2frame.PickId = item
3569        data = G2frame.PatternTree.GetItemPyData(item)
3570        G2pdG.UpdatePDFGrid(G2frame,data)
3571        G2plt.PlotISFG(G2frame,type='I(Q)')
3572        G2plt.PlotISFG(G2frame,type='S(Q)')
3573        G2plt.PlotISFG(G2frame,type='F(Q)')
3574        G2plt.PlotISFG(G2frame,type='G(R)')
3575    elif G2frame.PatternTree.GetItemText(item) == 'Peak List':
3576        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3577        for i in G2frame.ExportPeakList: i.Enable(True)
3578        G2frame.PickId = item
3579        data = G2frame.PatternTree.GetItemPyData(item)
3580        G2pdG.UpdatePeakGrid(G2frame,data)
3581        G2plt.PlotPatterns(G2frame)
3582    elif G2frame.PatternTree.GetItemText(item) == 'Background':
3583        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3584        G2frame.PickId = item
3585        data = G2frame.PatternTree.GetItemPyData(item)
3586        G2pdG.UpdateBackground(G2frame,data)
3587        G2plt.PlotPatterns(G2frame)
3588    elif G2frame.PatternTree.GetItemText(item) == 'Limits':
3589        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3590        G2frame.PickId = item
3591        data = G2frame.PatternTree.GetItemPyData(item)
3592        G2pdG.UpdateLimitsGrid(G2frame,data)
3593        G2plt.PlotPatterns(G2frame)
3594    elif G2frame.PatternTree.GetItemText(item) == 'Instrument Parameters':
3595        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3596        G2frame.PickId = item
3597        data = G2frame.PatternTree.GetItemPyData(item)[0]
3598        G2pdG.UpdateInstrumentGrid(G2frame,data)
3599        G2plt.PlotPeakWidths(G2frame)
3600    elif G2frame.PatternTree.GetItemText(item) == 'Sample Parameters':
3601        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3602        G2frame.PickId = item
3603        data = G2frame.PatternTree.GetItemPyData(item)
3604
3605        if 'Temperature' not in data:           #temp fix for old gpx files
3606            data = {'Scale':[1.0,True],'Type':'Debye-Scherrer','Absorption':[0.0,False],'DisplaceX':[0.0,False],
3607                'DisplaceY':[0.0,False],'Diffuse':[],'Temperature':300.,'Pressure':1.0,'Humidity':0.0,'Voltage':0.0,
3608                'Force':0.0,'Gonio. radius':200.0}
3609            G2frame.PatternTree.SetItemPyData(item,data)
3610   
3611        G2pdG.UpdateSampleGrid(G2frame,data)
3612        G2plt.PlotPatterns(G2frame)
3613    elif G2frame.PatternTree.GetItemText(item) == 'Index Peak List':
3614        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3615        for i in G2frame.ExportPeakList: i.Enable(True)
3616        G2frame.PickId = item
3617        data = G2frame.PatternTree.GetItemPyData(item)
3618        G2pdG.UpdateIndexPeaksGrid(G2frame,data)
3619        if 'PKS' in G2frame.PatternTree.GetItemText(G2frame.PatternId):
3620            G2plt.PlotPowderLines(G2frame)
3621        else:
3622            G2plt.PlotPatterns(G2frame)
3623    elif G2frame.PatternTree.GetItemText(item) == 'Unit Cells List':
3624        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3625        G2frame.PickId = item
3626        data = G2frame.PatternTree.GetItemPyData(item)
3627        if not data:
3628            data.append([0,0.0,4,25.0,0,'P1',1,1,1,90,90,90]) #zero error flag, zero value, max Nc/No, start volume
3629            data.append([0,0,0,0,0,0,0,0,0,0,0,0,0,0])      #Bravais lattice flags
3630            data.append([])                                 #empty cell list
3631            data.append([])                                 #empty dmin
3632            G2frame.PatternTree.SetItemPyData(item,data)                             
3633        G2pdG.UpdateUnitCellsGrid(G2frame,data)
3634        if 'PKS' in G2frame.PatternTree.GetItemText(G2frame.PatternId):
3635            G2plt.PlotPowderLines(G2frame)
3636        else:
3637            G2plt.PlotPatterns(G2frame)
3638    elif G2frame.PatternTree.GetItemText(item) == 'Reflection Lists':   #powder reflections
3639        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3640        G2frame.PickId = item
3641        data = G2frame.PatternTree.GetItemPyData(item)
3642        G2frame.RefList = ''
3643        if len(data):
3644            G2frame.RefList = data.keys()[0]
3645        G2pdG.UpdateReflectionGrid(G2frame,data)
3646        G2plt.PlotPatterns(G2frame)
3647    elif G2frame.PatternTree.GetItemText(item) == 'Reflection List':    #HKLF reflections
3648        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3649        name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
3650        data = G2frame.PatternTree.GetItemPyData(G2frame.PatternId)
3651        G2pdG.UpdateReflectionGrid(G2frame,data,HKLF=True,Name=name)
3652
3653def SetDataMenuBar(G2frame,menu=None):
3654    '''Set the menu for the data frame. On the Mac put this
3655    menu for the data tree window instead.
3656
3657    Note that data frame items do not have menus, for these (menu=None)
3658    display a blank menu or on the Mac display the standard menu for
3659    the data tree window.
3660    '''
3661    if sys.platform == "darwin":
3662        if menu is None:
3663            G2frame.SetMenuBar(G2frame.GSASIIMenu)
3664        else:
3665            G2frame.SetMenuBar(menu)
3666    else:
3667        if menu is None:
3668            G2frame.dataFrame.SetMenuBar(G2frame.dataFrame.BlankMenu)
3669        else:
3670            G2frame.dataFrame.SetMenuBar(menu)
3671
3672def HorizontalLine(sizer,parent):
3673    '''Draws a horizontal line as wide as the window.
3674    This shows up on the Mac as a very thin line, no matter what I do
3675    '''
3676    line = wx.StaticLine(parent,-1, size=(-1,3), style=wx.LI_HORIZONTAL)
3677    sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.ALL, 10)
3678
3679if __name__ == '__main__':
3680    # test ScrolledMultiEditor
3681    app = wx.PySimpleApp()
3682    frm = wx.Frame(None) # create a frame
3683    frm.Show(True)
3684    Data1 = {
3685        'Order':1,
3686        'omega':'string',
3687        'chi':2.0,
3688        'phi':'',
3689        }
3690    elemlst = sorted(Data1.keys())
3691    postlbl = sorted(Data1.keys())
3692    dictlst = len(elemlst)*[Data1,]
3693
3694    Data2 = list(range(100))
3695    elemlst += range(2,6)
3696    postlbl += range(2,6)
3697    dictlst += len(range(2,6))*[Data2,]
3698
3699    prelbl = range(len(elemlst))
3700    postlbl[1] = "a very long label for the 2nd item to force a horiz. scrollbar"
3701    header="""This is a longer\nmultiline and perhaps silly header"""
3702    dlg = ScrolledMultiEditor(frm,dictlst,elemlst,prelbl,postlbl,
3703                              header=header,CopyButton=True)
3704    print Data1
3705    if dlg.ShowModal() == wx.ID_OK:
3706        for d,k in zip(dictlst,elemlst):
3707            print k,d[k]
3708    dlg.Destroy()
3709    if CallScrolledMultiEditor(frm,dictlst,elemlst,prelbl,postlbl,
3710                               header=header):
3711        for d,k in zip(dictlst,elemlst):
3712            print k,d[k]
3713
3714#app.MainLoop()
Note: See TracBrowser for help on using the repository browser.