source: trunk/GSASIIgrid.py @ 1205

Last change on this file since 1205 was 1205, checked in by vondreele, 10 years ago

Add models to SASD data tree
Add error bar plotting to SASD data
Add I*Q4 option to log SASD data
got rid of the if, else blocks for all key driven toggles in G2plot

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