source: trunk/GSASIIgrid.py @ 1056

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

restore multiprocessing MCSA

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