source: trunk/GSASIIgrid.py @ 968

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

disable multiprocess stuff comment one line

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