source: trunk/GSASIIgrid.py @ 1007

Last change on this file since 1007 was 1007, checked in by vondreele, 9 years ago

"improved" Pawley extraction by flipping negative intensitied to 1/2 or 1/3 value on Pawley Update.

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