source: trunk/GSASIIgrid.py @ 1195

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

Add an update d-zero command
add new tutorial for strain fitting

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