source: trunk/GSASIIgrid.py @ 936

Last change on this file since 936 was 936, checked in by vondreele, 10 years ago

more MC/SA stuff
further drawing optimization

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