source: trunk/GSASIIgrid.py @ 1201

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

remove bind to leave window in ValidatedTxtCtrl?; interfered with plotting
implement plot of intensity*scale for small angle data
remove some dead code in G2pwdGUI

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