source: trunk/GSASIIgrid.py @ 937

Last change on this file since 937 was 937, checked in by toby, 11 years ago

More improvements to ValidatedTxtCtrl?

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