source: trunk/GSASIIgrid.py @ 978

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

make MC/SA fortran stuff all REAL*8
add glFreeType.py - doesn't work correctly yet
implement glFreeType font in OpenGl? labels
add new menu items for RBs
MCSA anneal in a test mode no random start for fixed T0

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