source: trunk/GSASIIgrid.py @ 1080

Last change on this file since 1080 was 1080, checked in by toby, 8 years ago

revise export menu; add multiple selection to G2gd.ItemSelector?

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