source: trunk/GSASIIgrid.py @ 933

Last change on this file since 933 was 933, checked in by toby, 10 years ago

Add megawidgets needed for CIF development

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