source: trunk/GSASIIgrid.py @ 1189

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

change ring search defaults for stress/strainCalc
implement a fit all images for stress strain
image fit is on delta-d (was delta d2)

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