source: trunk/GSASIIgrid.py @ 1210

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

constrast changed to substance
add absorption results to substances
begin models stuff

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 179.5 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIgrid - data display routines
3########### SVN repository information ###################
4# $Date: 2014-01-31 18:18:59 +0000 (Fri, 31 Jan 2014) $
5# $Author: vondreele $
6# $Revision: 1210 $
7# $URL: trunk/GSASIIgrid.py $
8# $Id: GSASIIgrid.py 1210 2014-01-31 18:18:59Z 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 copy
22import cPickle
23import sys
24import numpy as np
25import numpy.ma as ma
26import os
27import wx.html        # could postpone this for quicker startup
28import webbrowser     # could postpone this for quicker startup
29import GSASIIpath
30GSASIIpath.SetVersionNumber("$Revision: 1210 $")
31import GSASIImath as G2mth
32import GSASIIIO as G2IO
33import GSASIIlattice as G2lat
34import GSASIIplot as G2plt
35import GSASIIpwdGUI as G2pdG
36import GSASIIimgGUI as G2imG
37import GSASIIphsGUI as G2phG
38import GSASIIspc as G2spc
39import GSASIImapvars as G2mv
40import GSASIIconstrGUI as G2cnstG
41import GSASIIrestrGUI as G2restG
42import GSASIIpy3 as G2py3
43import GSASIIobj as G2obj
44
45# trig functions in degrees
46sind = lambda x: np.sin(x*np.pi/180.)
47tand = lambda x: np.tan(x*np.pi/180.)
48cosd = lambda x: np.cos(x*np.pi/180.)
49
50# globals we will use later
51__version__ = None # gets overridden in GSASII.py
52path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # save location of this file
53helpLocDict = {}
54htmlPanel = None
55htmlFrame = None
56helpMode = 'browser'
57#if sys.platform.lower().startswith('win'): helpMode = 'internal' # need a global control to set this
58   
59htmlFirstUse = True
60
61[ wxID_FOURCALC, wxID_FOURSEARCH, wxID_FOURCLEAR, wxID_PEAKSMOVE, wxID_PEAKSCLEAR, 
62    wxID_CHARGEFLIP, wxID_PEAKSUNIQUE, wxID_PEAKSDELETE, wxID_PEAKSDA,
63    wxID_PEAKSDISTVP, wxID_PEAKSVIEWPT, wxID_FINDEQVPEAKS,wxID_SHOWBONDS,wxID_MULTIMCSA,
64    wxID_SINGLEMCSA
65] = [wx.NewId() for item in range(15)]
66
67[ wxID_PWDRADD, wxID_HKLFADD,wxID_PWDANALYSIS,wxID_DATADELETE,
68] = [wx.NewId() for item in range(4)]
69
70[ wxID_ATOMSEDITADD, wxID_ATOMSEDITINSERT, wxID_ATOMSEDITDELETE, wxID_ATOMSREFINE, 
71    wxID_ATOMSMODIFY, wxID_ATOMSTRANSFORM, wxID_ATOMSVIEWADD, wxID_ATOMVIEWINSERT,
72    wxID_RELOADDRAWATOMS,wxID_ATOMSDISAGL,wxID_ATOMMOVE,
73    wxID_ASSIGNATMS2RB,wxID_ATOMSPDISAGL, wxID_ISODISP,
74] = [wx.NewId() for item in range(14)]
75
76[ wxID_DRAWATOMSTYLE, wxID_DRAWATOMLABEL, wxID_DRAWATOMCOLOR, wxID_DRAWATOMRESETCOLOR, 
77    wxID_DRAWVIEWPOINT, wxID_DRAWTRANSFORM, wxID_DRAWDELETE, wxID_DRAWFILLCELL, 
78    wxID_DRAWADDEQUIV, wxID_DRAWFILLCOORD, wxID_DRAWDISAGLTOR,  wxID_DRAWPLANE,
79    wxID_DRAWDISTVP,
80] = [wx.NewId() for item in range(13)]
81
82[ wxID_DRAWRESTRBOND, wxID_DRAWRESTRANGLE, wxID_DRAWRESTRPLANE, wxID_DRAWRESTRCHIRAL,
83] = [wx.NewId() for item in range(4)]
84
85[ wxID_ADDMCSAATOM,wxID_ADDMCSARB,wxID_CLEARMCSARB,wxID_MOVEMCSA,wxID_MCSACLEARRESULTS,
86] = [wx.NewId() for item in range(5)]
87
88[ wxID_CLEARTEXTURE,wxID_REFINETEXTURE,
89] = [wx.NewId() for item in range(2)]
90
91[ wxID_PAWLEYLOAD, wxID_PAWLEYESTIMATE, wxID_PAWLEYUPDATE,
92] = [wx.NewId() for item in range(3)]
93
94[ wxID_IMCALIBRATE,wxID_IMRECALIBRATE,wxID_IMINTEGRATE, wxID_IMCLEARCALIB, 
95    wxID_IMCOPYCONTROLS, wxID_INTEGRATEALL, wxID_IMSAVECONTROLS, wxID_IMLOADCONTROLS,
96] = [wx.NewId() for item in range(8)]
97
98[ wxID_MASKCOPY, wxID_MASKSAVE, wxID_MASKLOAD,wxID_NEWMASKSPOT,wxID_NEWMASKARC,wxID_NEWMASKRING,
99    wxID_NEWMASKFRAME, wxID_NEWMASKPOLY,
100] = [wx.NewId() for item in range(8)]
101
102[ wxID_STRSTACOPY, wxID_STRSTAFIT, wxID_STRSTASAVE, wxID_STRSTALOAD,wxID_APPENDDZERO,
103    wxID_STRSTAALLFIT,wxID_UPDATEDZERO,
104] = [wx.NewId() for item in range(7)]
105
106[ wxID_BACKCOPY,wxID_LIMITCOPY,wxID_SAMPLECOPY, wxID_BACKFLAGCOPY, wxID_SAMPLEFLAGCOPY,
107    wxID_SAMPLESAVE, wxID_SAMPLELOAD,wxID_ADDEXCLREGION,
108] = [wx.NewId() for item in range(8)]
109
110[ wxID_INSTPRMRESET,wxID_CHANGEWAVETYPE,wxID_INSTCOPY, wxID_INSTFLAGCOPY, wxID_INSTLOAD,
111    wxID_INSTSAVE,
112] = [wx.NewId() for item in range(6)]
113
114[ wxID_UNDO,wxID_LSQPEAKFIT,wxID_LSQONECYCLE,wxID_RESETSIGGAM,wxID_CLEARPEAKS,wxID_AUTOSEARCH,
115] = [wx.NewId() for item in range(6)]
116
117[  wxID_INDXRELOAD, wxID_INDEXPEAKS, wxID_REFINECELL, wxID_COPYCELL, wxID_MAKENEWPHASE,
118] = [wx.NewId() for item in range(5)]
119
120[ wxID_CONSTRAINTADD,wxID_EQUIVADD,wxID_HOLDADD,wxID_FUNCTADD,
121  wxID_CONSPHASE, wxID_CONSHIST, wxID_CONSHAP, wxID_CONSGLOBAL,
122] = [wx.NewId() for item in range(8)]
123
124[ wxID_RESTRAINTADD, wxID_RESTSELPHASE,wxID_RESTDELETE, wxID_RESRCHANGEVAL, 
125    wxID_RESTCHANGEESD,wxID_AARESTRAINTADD,wxID_AARESTRAINTPLOT,
126] = [wx.NewId() for item in range(7)]
127
128[ wxID_RIGIDBODYADD,wxID_DRAWDEFINERB,wxID_RIGIDBODYIMPORT,wxID_RESIDUETORSSEQ,
129    wxID_AUTOFINDRESRB,wxID_GLOBALRESREFINE,wxID_RBREMOVEALL,wxID_COPYRBPARMS,
130    wxID_GLOBALTHERM,
131] = [wx.NewId() for item in range(9)]
132
133[ wxID_SAVESEQSEL,
134] = [wx.NewId() for item in range(1)]
135
136[ wxID_MODELCOPY,wxID_MODELFIT,wxID_ELEMENTADD,wxID_ELEMENTDELETE,wxID_ADDSUBSTANCE,
137    wxID_LOADSUBSTANCE,wxID_DELETESUBSTANCE,
138] = [wx.NewId() for item in range(7)]
139
140[ wxID_SELECTPHASE,
141] = [wx.NewId() for item in range(1)]
142
143[ wxID_PDFCOPYCONTROLS, wxID_PDFSAVECONTROLS, wxID_PDFLOADCONTROLS, 
144    wxID_PDFCOMPUTE, wxID_PDFCOMPUTEALL, wxID_PDFADDELEMENT, wxID_PDFDELELEMENT,
145] = [wx.NewId() for item in range(7)]
146
147VERY_LIGHT_GREY = wx.Colour(235,235,235)
148################################################################################
149#### GSAS-II class definitions
150################################################################################
151       
152class ValidatedTxtCtrl(wx.TextCtrl):
153    '''Create a TextCtrl widget that uses a validator to prevent the
154    entry of inappropriate characters and changes color to highlight
155    when invalid input is supplied. As valid values are typed,
156    they are placed into the dict or list where the initial value
157    came from. The type of the initial value must be int,
158    float or str or None (see :obj:`key` and :obj:`typeHint`);
159    this type (or the one in :obj:`typeHint`) is preserved.
160
161    Float values can be entered in the TextCtrl as numbers or also
162    as algebraic expressions using operators + - / \* () and \*\*,
163    in addition pi, sind(), cosd(), tand(), and sqrt() can be used,
164    as well as appreviations s, sin, c, cos, t, tan and sq.
165
166    :param wx.Panel parent: name of panel or frame that will be
167      the parent to the TextCtrl. Can be None.
168
169    :param dict/list loc: the dict or list with the initial value to be
170      placed in the TextCtrl.
171
172    :param int/str key: the dict key or the list index for the value to be
173      edited by the TextCtrl. The ``loc[key]`` element must exist, but may
174      have value None. If None, the type for the element is taken from
175      :obj:`typeHint` and the value for the control is set initially
176      blank (and thus invalid.) This is a way to specify a field without a
177      default value: a user must set a valid value.
178      If the value is not None, it must have a base
179      type of int, float, str or unicode; the TextCrtl will be initialized
180      from this value.
181     
182    :param list [nDig,nPlc]: number of digits & places after decimal to use
183      for display of float. Alternately, None can be specified which causes
184      numbers to be displayed with approximately 5 significant figures
185      (Default=None).
186
187    :param bool notBlank: if True (default) blank values are invalid
188      for str inputs.
189     
190    :param number min: minimum allowed valid value. If None (default) the
191      lower limit is unbounded.
192
193    :param number max: maximum allowed valid value. If None (default) the
194      upper limit is unbounded
195
196    :param function OKcontrol: specifies a function or method that will be
197      called when the input is validated. The called function is supplied
198      with one argument which is False if the TextCtrl contains an invalid
199      value and True if the value is valid.
200      Note that this function should check all values
201      in the dialog when True, since other entries might be invalid.
202      The default for this is None, which indicates no function should
203      be called.
204
205    :param function OnLeave: specifies a function or method that will be
206      called when the focus for the control is lost.
207      The called function is supplied with (at present) three keyword arguments:
208
209         * invalid: (*bool*) True if the value for the TextCtrl is invalid
210         * value:   (*int/float/str*)  the value contained in the TextCtrl
211         * tc:      (*wx.TextCtrl*)  the TextCtrl name
212
213      The number of keyword arguments may be increased in the future should needs arise,
214      so it is best to code these functions with a \*\*kwargs argument so they will
215      continue to run without errors
216
217      The default for OnLeave is None, which indicates no function should
218      be called.
219
220    :param type typeHint: the value of typeHint is overrides the initial value
221      for the dict/list element ``loc[key]``, if set to
222      int or float, which specifies the type for input to the TextCtrl.
223      Defaults as None, which is ignored.
224
225    :param bool CIFinput: for str input, indicates that only printable
226      ASCII characters may be entered into the TextCtrl. Forces output
227      to be ASCII rather than Unicode. For float and int input, allows
228      use of a single '?' or '.' character as valid input.
229
230    :param dict OnLeaveArgs: a dict with keyword args that are passed to
231      the :attr:`OnLeave` function. Defaults to ``{}``
232
233    :param (other): other optional keyword parameters for the
234      wx.TextCtrl widget such as Size or Style may be specified.
235
236    '''
237    def __init__(self,parent,loc,key,nDig=None,notBlank=True,min=None,max=None,
238                 OKcontrol=None,OnLeave=None,typeHint=None,
239                 CIFinput=False, OnLeaveArgs={}, **kw):
240        # save passed values needed outside __init__
241        self.result = loc
242        self.key = key
243        self.nDig = nDig
244        self.OKcontrol=OKcontrol
245        self.OnLeave = OnLeave
246        self.OnLeaveArgs = OnLeaveArgs
247        self.CIFinput = CIFinput
248        self.type = str
249        # initialization
250        self.invalid = False   # indicates if the control has invalid contents
251        self.evaluated = False # set to True when the validator recognizes an expression
252        val = loc[key]
253        if isinstance(val,int) or typeHint is int:
254            self.type = int
255            wx.TextCtrl.__init__(
256                self,parent,wx.ID_ANY,
257                validator=NumberValidator(int,result=loc,key=key,
258                                          min=min,max=max,
259                                          OKcontrol=OKcontrol,
260                                          CIFinput=CIFinput),
261                **kw)
262            if val is not None:
263                self.SetValue(val)
264            else: # no default is invalid for a number
265                self.invalid = True
266                self._IndicateValidity()
267
268        elif isinstance(val,float) or typeHint is float:
269            self.type = float
270            wx.TextCtrl.__init__(
271                self,parent,wx.ID_ANY,
272                validator=NumberValidator(float,result=loc,key=key,
273                                          min=min,max=max,
274                                          OKcontrol=OKcontrol,
275                                          CIFinput=CIFinput),
276                **kw)
277            if val is not None:
278                self.SetValue(val)
279            else:
280                self.invalid = True
281                self._IndicateValidity()
282
283        elif isinstance(val,str) or isinstance(val,unicode):
284            if self.CIFinput:
285                wx.TextCtrl.__init__(
286                    self,parent,wx.ID_ANY,val,
287                    validator=ASCIIValidator(result=loc,key=key),
288                    **kw)
289            else:
290                wx.TextCtrl.__init__(self,parent,wx.ID_ANY,val,**kw)
291            if notBlank:
292                self.Bind(wx.EVT_CHAR,self._onStringKey)
293                self.ShowStringValidity() # test if valid input
294            else:
295                self.invalid = False
296                self.Bind(wx.EVT_CHAR,self._GetStringValue)
297        elif val is None:
298            raise Exception,("ValidatedTxtCtrl error: value of "+str(key)+
299                             " element is None and typeHint not defined as int or float")
300        else:
301            raise Exception,("ValidatedTxtCtrl error: Unknown element ("+str(key)+
302                             ") type: "+str(type(val)))
303        # When the mouse is moved away or the widget loses focus,
304        # display the last saved value, if an expression
305        self.Bind(wx.EVT_LEAVE_WINDOW, self._onLeaveWindow)
306        self.Bind(wx.EVT_TEXT_ENTER, self._onLoseFocus)
307        self.Bind(wx.EVT_KILL_FOCUS, self._onLoseFocus)
308
309    def SetValue(self,val):
310        self.invalid = False
311        if self.type is int:
312            try:
313                if int(val) != val:
314                    self.invalid = True
315                else:
316                    val = int(val)
317            except:
318                if self.CIFinput and (val == '?' or val == '.'):
319                    pass
320                else:
321                    self.invalid = True
322            wx.TextCtrl.SetValue(self,str(val))
323        elif self.type is float:
324            try:
325                float(val)
326            except:
327                if self.CIFinput and (val == '?' or val == '.'):
328                    pass
329                else:
330                    self.invalid = True
331            if self.nDig:
332                wx.TextCtrl.SetValue(self,str(G2py3.FormatValue(val,self.nDig)))
333            else:
334                wx.TextCtrl.SetValue(self,str(G2py3.FormatSigFigs(val)).rstrip('0'))
335        else:
336            wx.TextCtrl.SetValue(self,str(val))
337            self.ShowStringValidity() # test if valid input
338            return
339       
340        self._IndicateValidity()
341        if self.OKcontrol:
342            self.OKcontrol(not self.invalid)
343       
344    def _onStringKey(self,event):
345        event.Skip()
346        if self.invalid: # check for validity after processing the keystroke
347            wx.CallAfter(self.ShowStringValidity,True) # was invalid
348        else:
349            wx.CallAfter(self.ShowStringValidity,False) # was valid
350
351    def _IndicateValidity(self):
352        'Set the control colors to show invalid input'
353        if self.invalid:
354            self.SetForegroundColour("red")
355            self.SetBackgroundColour("yellow")
356            self.SetFocus()
357            self.Refresh()
358        else: # valid input
359            self.SetBackgroundColour(
360                wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
361            self.SetForegroundColour("black")
362            self.Refresh()
363
364    def ShowStringValidity(self,previousInvalid=True):
365        '''Check if input is valid. Anytime the input is
366        invalid, call self.OKcontrol (if defined) because it is fast.
367        If valid, check for any other invalid entries only when
368        changing from invalid to valid, since that is slower.
369       
370        :param bool previousInvalid: True if the TextCtrl contents were
371          invalid prior to the current change.
372         
373        '''
374        val = self.GetValue().strip()
375        self.invalid = not val
376        self._IndicateValidity()
377        if self.invalid:
378            if self.OKcontrol:
379                self.OKcontrol(False)
380        elif self.OKcontrol and previousInvalid:
381            self.OKcontrol(True)
382        # always store the result
383        if self.CIFinput: # for CIF make results ASCII
384            self.result[self.key] = val.encode('ascii','replace') 
385        else:
386            self.result[self.key] = val
387
388    def _GetStringValue(self,event):
389        '''Get string input and store.
390        '''
391        event.Skip() # process keystroke
392        wx.CallAfter(self._SaveStringValue)
393       
394    def _SaveStringValue(self):
395        val = self.GetValue().strip()
396        # always store the result
397        if self.CIFinput: # for CIF make results ASCII
398            self.result[self.key] = val.encode('ascii','replace') 
399        else:
400            self.result[self.key] = val
401
402    def _onLoseFocus(self,event):
403        if self.evaluated: self.EvaluateExpression()
404        if self.OnLeave: self.OnLeave(invalid=self.invalid,
405                                      value=self.result[self.key],
406                                      tc=self,
407                                      **self.OnLeaveArgs)
408
409    def _onLeaveWindow(self,event):
410        if self.evaluated: self.EvaluateExpression()
411
412    def EvaluateExpression(self):
413        '''Show the computed value when an expression is entered to the TextCtrl
414        Make sure that the number fits by truncating decimal places and switching
415        to scientific notation, as needed.
416        Called on loss of focus.
417        '''
418        if self.invalid: return # don't substitute for an invalid expression
419        if not self.evaluated: return # true when an expression is evaluated
420        if self.result is not None: # retrieve the stored result
421            self.SetValue(self.result[self.key])
422        self.evaluated = False # expression has been recast as value, reset flag
423       
424class NumberValidator(wx.PyValidator):
425    '''A validator to be used with a TextCtrl to prevent
426    entering characters other than digits, signs, and for float
427    input, a period and exponents.
428   
429    The value is checked for validity after every keystroke
430      If an invalid number is entered, the box is highlighted.
431      If the number is valid, it is saved in result[key]
432
433    :param type typ: the base data type. Must be int or float.
434
435    :param bool positiveonly: If True, negative integers are not allowed
436      (default False). This prevents the + or - keys from being pressed.
437      Used with typ=int; ignored for typ=float.
438
439    :param number min: Minimum allowed value. If None (default) the
440      lower limit is unbounded
441
442    :param number max: Maximum allowed value. If None (default) the
443      upper limit is unbounded
444     
445    :param dict/list result: List or dict where value should be placed when valid
446
447    :param any key: key to use for result (int for list)
448
449    :param function OKcontrol: function or class method to control
450      an OK button for a window.
451      Ignored if None (default)
452
453    :param bool CIFinput: allows use of a single '?' or '.' character
454      as valid input.
455     
456    '''
457    def __init__(self, typ, positiveonly=False, min=None, max=None,
458                 result=None, key=None, OKcontrol=None, CIFinput=False):
459        'Create the validator'
460        wx.PyValidator.__init__(self)
461        # save passed parameters
462        self.typ = typ
463        self.positiveonly = positiveonly
464        self.min = min
465        self.max = max
466        self.result = result
467        self.key = key
468        self.OKcontrol = OKcontrol
469        self.CIFinput = CIFinput
470        # set allowed keys by data type
471        self.Bind(wx.EVT_CHAR, self.OnChar)
472        if self.typ == int and self.positiveonly:
473            self.validchars = '0123456789'
474        elif self.typ == int:
475            self.validchars = '0123456789+-'
476        elif self.typ == float:
477            # allow for above and sind, cosd, sqrt, tand, pi, and abbreviations
478            # also addition, subtraction, division, multiplication, exponentiation
479            self.validchars = '0123456789.-+eE/cosindcqrtap()*'
480        else:
481            self.validchars = None
482            return
483        if self.CIFinput:
484            self.validchars += '?.'
485    def Clone(self):
486        'Create a copy of the validator, a strange, but required component'
487        return NumberValidator(typ=self.typ, 
488                               positiveonly=self.positiveonly,
489                               min=self.min, max=self.max,
490                               result=self.result, key=self.key,
491                               OKcontrol=self.OKcontrol,
492                               CIFinput=self.CIFinput)
493    def TransferToWindow(self):
494        'Needed by validator, strange, but required component'
495        return True # Prevent wxDialog from complaining.
496    def TransferFromWindow(self):
497        'Needed by validator, strange, but required component'
498        return True # Prevent wxDialog from complaining.
499    def TestValid(self,tc):
500        '''Check if the value is valid by casting the input string
501        into the current type.
502
503        Set the invalid variable in the TextCtrl object accordingly.
504
505        If the value is valid, save it in the dict/list where
506        the initial value was stored, if appropriate.
507
508        :param wx.TextCtrl tc: A reference to the TextCtrl that the validator
509          is associated with.
510        '''
511        tc.invalid = False # assume valid
512        if self.CIFinput:
513            val = tc.GetValue().strip()
514            if val == '?' or val == '.':
515                self.result[self.key] = val
516                return
517        try:
518            val = self.typ(tc.GetValue())
519        except (ValueError, SyntaxError) as e:
520            if self.typ is float: # for float values, see if an expression can be evaluated
521                val = G2py3.FormulaEval(tc.GetValue())
522                if val is None:
523                    tc.invalid = True
524                    return
525                else:
526                    tc.evaluated = True
527            else: 
528                tc.invalid = True
529                return
530        # if self.max != None and self.typ == int:
531        #     if val > self.max:
532        #         tc.invalid = True
533        # if self.min != None and self.typ == int:
534        #     if val < self.min:
535        #         tc.invalid = True  # invalid
536        if self.max != None:
537            if val > self.max:
538                tc.invalid = True
539        if self.min != None:
540            if val < self.min:
541                tc.invalid = True  # invalid
542        if self.key is not None and self.result is not None and not tc.invalid:
543            self.result[self.key] = val
544
545    def ShowValidity(self,tc):
546        '''Set the control colors to show invalid input
547
548        :param wx.TextCtrl tc: A reference to the TextCtrl that the validator
549          is associated with.
550
551        '''
552        if tc.invalid:
553            tc.SetForegroundColour("red")
554            tc.SetBackgroundColour("yellow")
555            tc.SetFocus()
556            tc.Refresh()
557            return False
558        else: # valid input
559            tc.SetBackgroundColour(
560                wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
561            tc.SetForegroundColour("black")
562            tc.Refresh()
563            return True
564
565    def CheckInput(self,previousInvalid):
566        '''called to test every change to the TextCtrl for validity and
567        to change the appearance of the TextCtrl
568
569        Anytime the input is invalid, call self.OKcontrol
570        (if defined) because it is fast.
571        If valid, check for any other invalid entries only when
572        changing from invalid to valid, since that is slower.
573
574        :param bool previousInvalid: True if the TextCtrl contents were
575          invalid prior to the current change.
576        '''
577        tc = self.GetWindow()
578        self.TestValid(tc)
579        self.ShowValidity(tc)
580        # if invalid
581        if tc.invalid and self.OKcontrol:
582            self.OKcontrol(False)
583        if not tc.invalid and self.OKcontrol and previousInvalid:
584            self.OKcontrol(True)
585
586    def OnChar(self, event):
587        '''Called each type a key is pressed
588        ignores keys that are not allowed for int and float types
589        '''
590        key = event.GetKeyCode()
591        tc = self.GetWindow()
592        if key == wx.WXK_RETURN:
593            if tc.invalid:
594                self.CheckInput(True) 
595            else:
596                self.CheckInput(False) 
597            return
598        if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255: # control characters get processed
599            event.Skip()
600            if tc.invalid:
601                wx.CallAfter(self.CheckInput,True) 
602            else:
603                wx.CallAfter(self.CheckInput,False) 
604            return
605        elif chr(key) in self.validchars: # valid char pressed?
606            event.Skip()
607            if tc.invalid:
608                wx.CallAfter(self.CheckInput,True) 
609            else:
610                wx.CallAfter(self.CheckInput,False) 
611            return
612        if not wx.Validator_IsSilent(): wx.Bell()
613        return  # Returning without calling event.Skip, which eats the keystroke
614
615################################################################################
616class ASCIIValidator(wx.PyValidator):
617    '''A validator to be used with a TextCtrl to prevent
618    entering characters other than ASCII characters.
619   
620    The value is checked for validity after every keystroke
621      If an invalid number is entered, the box is highlighted.
622      If the number is valid, it is saved in result[key]
623
624    :param dict/list result: List or dict where value should be placed when valid
625
626    :param any key: key to use for result (int for list)
627
628    '''
629    def __init__(self, result=None, key=None):
630        'Create the validator'
631        import string
632        wx.PyValidator.__init__(self)
633        # save passed parameters
634        self.result = result
635        self.key = key
636        self.validchars = string.ascii_letters + string.digits + string.punctuation + string.whitespace
637        self.Bind(wx.EVT_CHAR, self.OnChar)
638    def Clone(self):
639        'Create a copy of the validator, a strange, but required component'
640        return ASCIIValidator(result=self.result, key=self.key)
641        tc = self.GetWindow()
642        tc.invalid = False # make sure the validity flag is defined in parent
643    def TransferToWindow(self):
644        'Needed by validator, strange, but required component'
645        return True # Prevent wxDialog from complaining.
646    def TransferFromWindow(self):
647        'Needed by validator, strange, but required component'
648        return True # Prevent wxDialog from complaining.
649    def TestValid(self,tc):
650        '''Check if the value is valid by casting the input string
651        into ASCII.
652
653        Save it in the dict/list where the initial value was stored
654
655        :param wx.TextCtrl tc: A reference to the TextCtrl that the validator
656          is associated with.
657        '''
658        self.result[self.key] = tc.GetValue().encode('ascii','replace')
659
660    def OnChar(self, event):
661        '''Called each type a key is pressed
662        ignores keys that are not allowed for int and float types
663        '''
664        key = event.GetKeyCode()
665        tc = self.GetWindow()
666        if key == wx.WXK_RETURN:
667            self.TestValid(tc)
668            return
669        if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255: # control characters get processed
670            event.Skip()
671            self.TestValid(tc)
672            return
673        elif chr(key) in self.validchars: # valid char pressed?
674            event.Skip()
675            self.TestValid(tc)
676            return
677        if not wx.Validator_IsSilent():
678            wx.Bell()
679        return  # Returning without calling event.Skip, which eats the keystroke
680################################################################################
681class EnumSelector(wx.ComboBox):
682    '''A customized :class:`wxpython.ComboBox` that selects items from a list
683    of choices, but sets a dict (list) entry to the corresponding
684    entry from the input list of values.
685
686    :param wx.Panel parent: the parent to the :class:`~wxpython.ComboBox` (usually a
687      frame or panel)
688    :param dict dct: a dict (or list) to contain the value set
689      for the :class:`~wxpython.ComboBox`.
690    :param item: the dict key (or list index) where ``dct[item]`` will
691      be set to the value selected in the :class:`~wxpython.ComboBox`. Also, dct[item]
692      contains the starting value shown in the widget. If the value
693      does not match an entry in :data:`values`, the first value
694      in :data:`choices` is used as the default, but ``dct[item]`` is
695      not changed.   
696    :param list choices: a list of choices to be displayed to the
697      user such as
698      ::
699     
700      ["default","option 1","option 2",]
701
702      Note that these options will correspond to the entries in
703      :data:`values` (if specified) item by item.
704    :param list values: a list of values that correspond to
705      the options in :data:`choices`, such as
706      ::
707     
708      [0,1,2]
709     
710      The default for :data:`values` is to use the same list as
711      specified for :data:`choices`.
712    :param (other): additional keyword arguments accepted by
713      :class:`~wxpython.ComboBox` can be specified.
714    '''
715    def __init__(self,parent,dct,item,choices,values=None,**kw):
716        if values is None:
717            values = choices
718        if dct[item] in values:
719            i = values.index(dct[item])
720        else:
721            i = 0
722        startval = choices[i]
723        wx.ComboBox.__init__(self,parent,wx.ID_ANY,startval,
724                             choices = choices,
725                             style=wx.CB_DROPDOWN|wx.CB_READONLY,
726                             **kw)
727        self.choices = choices
728        self.values = values
729        self.dct = dct
730        self.item = item
731        self.Bind(wx.EVT_COMBOBOX, self.onSelection)
732    def onSelection(self,event):
733        # respond to a selection by setting the enum value in the CIF dictionary
734        if self.GetValue() in self.choices: # should always be true!
735            self.dct[self.item] = self.values[self.choices.index(self.GetValue())]
736        else:
737            self.dct[self.item] = self.values[0] # unknown
738
739################################################################################
740class G2CheckBox(wx.CheckBox):
741    '''A customized version of a CheckBox that automatically initializes
742    the control to match a supplied
743
744    :param wx.Panel parent: name of panel or frame that will be
745      the parent to the TextCtrl. Can be None.
746    :param str label: text to put on check button
747    :param dict/list loc: the dict or list with the initial value to be
748      placed in the CheckBox.
749    :param int/str key: the dict key or the list index for the value to be
750      edited by the CheckBox. The ``loc[key]`` element must exist.
751      The CheckBox will be initialized from this value.
752      If the value is anything other that True (or 1), it will be taken as
753      False.
754    '''
755    def __init__(self,parent,label,loc,key):
756        wx.CheckBox.__init__(self,parent,id=wx.ID_ANY,label=label)
757        self.loc = loc
758        self.key = key
759        self.SetValue(self.loc[self.key]==True)
760        self.Bind(wx.EVT_CHECKBOX, self._OnCheckBox)
761    def _OnCheckBox(self,event):
762        self.loc[self.key] = self.GetValue()
763################################################################################   
764def CallScrolledMultiEditor(parent,dictlst,elemlst,prelbl=[],postlbl=[],
765                 title='Edit items',header='',size=(300,250),
766                             CopyButton=False):
767    '''Shell routine to call a ScrolledMultiEditor dialog. See
768    :class:`ScrolledMultiEditor` for parameter definitions.
769
770    :returns: True if the OK button is pressed; False if the window is closed
771      with the system menu or the Cancel button.
772
773    '''
774    dlg = ScrolledMultiEditor(parent,dictlst,elemlst,prelbl,postlbl,
775                              title,header,size,
776                              CopyButton)
777    if dlg.ShowModal() == wx.ID_OK:
778        dlg.Destroy()
779        return True
780    else:
781        dlg.Destroy()
782        return False
783
784class ScrolledMultiEditor(wx.Dialog):
785    '''Define a window for editing a potentially large number of dict- or
786    list-contained values with validation for each item. Edited values are
787    automatically placed in their source location. If invalid entries
788    are provided, the TextCtrl is turned yellow and the OK button is disabled.
789
790    The type for each TextCtrl validation is determined by the
791    initial value of the entry (int, float or string).
792    Float values can be entered in the TextCtrl as numbers or also
793    as algebraic expressions using operators + - / \* () and \*\*,
794    in addition pi, sind(), cosd(), tand(), and sqrt() can be used,
795    as well as appreviations s(), sin(), c(), cos(), t(), tan() and sq().
796
797    :param wx.Frame parent: name of parent window, or may be None
798
799    :param tuple dictlst: a list of dicts or lists containing values to edit
800
801    :param tuple elemlst: a list of keys for each item in a dictlst. Must have the
802      same length as dictlst.
803
804    :param wx.Frame parent: name of parent window, or may be None
805   
806    :param tuple prelbl: a list of labels placed before the TextCtrl for each
807      item (optional)
808   
809    :param tuple postlbl: a list of labels placed after the TextCtrl for each
810      item (optional)
811
812    :param str title: a title to place in the frame of the dialog
813
814    :param str header: text to place at the top of the window. May contain
815      new line characters.
816
817    :param wx.Size size: a size parameter that dictates the
818      size for the scrolled region of the dialog. The default is
819      (300,250).
820
821    :param bool CopyButton: if True adds a small button that copies the
822      value for the current row to all fields below (default is False)
823     
824    :param list minvals: optional list of minimum values for validation
825      of float or int values. Ignored if value is None.
826    :param list maxvals: optional list of maximum values for validation
827      of float or int values. Ignored if value is None.
828    :param list sizevals: optional list of wx.Size values for each input
829      widget. Ignored if value is None.
830    :returns: the wx.Dialog created here. Use method .ShowModal() to display it.
831   
832    *Example for use of ScrolledMultiEditor:*
833
834    ::
835
836        dlg = <pkg>.ScrolledMultiEditor(frame,dictlst,elemlst,prelbl,postlbl,
837                                        header=header)
838        if dlg.ShowModal() == wx.ID_OK:
839             for d,k in zip(dictlst,elemlst):
840                 print d[k]
841
842    *Example definitions for dictlst and elemlst:*
843
844    ::
845     
846          dictlst = (dict1,list1,dict1,list1)
847          elemlst = ('a', 1, 2, 3)
848
849      This causes items dict1['a'], list1[1], dict1[2] and list1[3] to be edited.
850   
851    Note that these items must have int, float or str values assigned to
852    them. The dialog will force these types to be retained. String values
853    that are blank are marked as invalid.
854    '''
855   
856    def __init__(self,parent,dictlst,elemlst,prelbl=[],postlbl=[],
857                 title='Edit items',header='',size=(300,250),
858                 CopyButton=False,
859                 minvals=[],maxvals=[],sizevals=[]):
860        if len(dictlst) != len(elemlst):
861            raise Exception,"ScrolledMultiEditor error: len(dictlst) != len(elemlst) "+str(len(dictlst))+" != "+str(len(elemlst))
862        wx.Dialog.__init__( # create dialog & sizer
863            self,parent,wx.ID_ANY,title,
864            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
865        mainSizer = wx.BoxSizer(wx.VERTICAL)
866        self.orig = []
867        self.dictlst = dictlst
868        self.elemlst = elemlst
869        self.ButtonIndex = {}
870        for d,i in zip(dictlst,elemlst):
871            self.orig.append(d[i])
872        # add a header if supplied
873        if header:
874            subSizer = wx.BoxSizer(wx.HORIZONTAL)
875            subSizer.Add((-1,-1),1,wx.EXPAND)
876            subSizer.Add(wx.StaticText(self,wx.ID_ANY,header))
877            subSizer.Add((-1,-1),1,wx.EXPAND)
878            mainSizer.Add(subSizer,0,wx.EXPAND,0)
879        # make OK button now, because we will need it for validation
880        self.OKbtn = wx.Button(self, wx.ID_OK)
881        self.OKbtn.SetDefault()
882        # create scrolled panel and sizer
883        panel = wxscroll.ScrolledPanel(
884            self, wx.ID_ANY,
885            size=size,
886            style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
887        cols = 3
888        if CopyButton: cols += 1
889        subSizer = wx.FlexGridSizer(rows=len(dictlst),cols=cols,hgap=2,vgap=2)
890        self.ValidatedControlsList = [] # make list of TextCtrls
891        for i,(d,k) in enumerate(zip(dictlst,elemlst)):
892            if i >= len(prelbl): # label before TextCtrl, or put in a blank
893                subSizer.Add((-1,-1)) 
894            else:
895                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,str(prelbl[i])))
896            kargs = {}
897            if i < len(minvals):
898                if minvals[i] is not None: kargs['min']=minvals[i]
899            if i < len(maxvals):
900                if maxvals[i] is not None: kargs['max']=maxvals[i]
901            if i < len(sizevals):
902                if sizevals[i]: kargs['size']=sizevals[i]
903            if CopyButton:
904                import wx.lib.colourselect as wscs
905                but = wscs.ColourSelect(label='v', # would like to use u'\u2193' or u'\u25BC' but not in WinXP
906                                        # is there a way to test?
907                                        parent=panel,
908                                        colour=(255,255,200),
909                                        size=wx.Size(30,23),
910                                        style=wx.RAISED_BORDER)
911                but.Bind(wx.EVT_BUTTON, self._OnCopyButton)
912                but.SetToolTipString('Press to copy adjacent value to all rows below')
913                self.ButtonIndex[but] = i
914                subSizer.Add(but)
915            # create the validated TextCrtl, store it and add it to the sizer
916            ctrl = ValidatedTxtCtrl(panel,d,k,OKcontrol=self.ControlOKButton,
917                                    **kargs)
918            self.ValidatedControlsList.append(ctrl)
919            subSizer.Add(ctrl)
920            if i >= len(postlbl): # label after TextCtrl, or put in a blank
921                subSizer.Add((-1,-1))
922            else:
923                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,str(postlbl[i])))
924        # finish up ScrolledPanel
925        panel.SetSizer(subSizer)
926        panel.SetAutoLayout(1)
927        panel.SetupScrolling()
928        mainSizer.Add(panel,1, wx.ALL|wx.EXPAND,1)
929
930        # Sizer for OK/Close buttons. N.B. on Close changes are discarded
931        # by restoring the initial values
932        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
933        btnsizer.Add(self.OKbtn)
934        btn = wx.Button(self, wx.ID_CLOSE,"Cancel") 
935        btn.Bind(wx.EVT_BUTTON,self._onClose)
936        btnsizer.Add(btn)
937        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
938        # size out the window. Set it to be enlarged but not made smaller
939        self.SetSizer(mainSizer)
940        mainSizer.Fit(self)
941        self.SetMinSize(self.GetSize())
942
943    def _OnCopyButton(self,event):
944        'Implements the copy down functionality'
945        but = event.GetEventObject()
946        n = self.ButtonIndex.get(but)
947        if n is None: return
948        for i,(d,k,ctrl) in enumerate(zip(self.dictlst,self.elemlst,self.ValidatedControlsList)):
949            if i < n: continue
950            if i == n:
951                val = d[k]
952                continue
953            d[k] = val
954            ctrl.SetValue(val)
955
956    def _onClose(self,event):
957        'Restore original values & close the window'
958        for d,i,v in zip(self.dictlst,self.elemlst,self.orig):
959            d[i] = v
960        self.EndModal(wx.ID_CANCEL)
961       
962    def ControlOKButton(self,setvalue):
963        '''Enable or Disable the OK button for the dialog. Note that this is
964        passed into the ValidatedTxtCtrl for use by validators.
965
966        :param bool setvalue: if True, all entries in the dialog are
967          checked for validity. if False then the OK button is disabled.
968
969        '''
970        if setvalue: # turn button on, do only if all controls show as valid
971            for ctrl in self.ValidatedControlsList:
972                if ctrl.invalid:
973                    self.OKbtn.Disable()
974                    return
975            else:
976                self.OKbtn.Enable()
977        else:
978            self.OKbtn.Disable()
979
980################################################################################
981class SymOpDialog(wx.Dialog):
982    '''Class to select a symmetry operator
983    '''
984    def __init__(self,parent,SGData,New=True,ForceUnit=False):
985        wx.Dialog.__init__(self,parent,-1,'Select symmetry operator',
986            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
987        panel = wx.Panel(self)
988        self.SGData = SGData
989        self.New = New
990        self.Force = ForceUnit
991        self.OpSelected = [0,0,0,[0,0,0],False,False]
992        mainSizer = wx.BoxSizer(wx.VERTICAL)
993        if ForceUnit:
994            choice = ['No','Yes']
995            self.force = wx.RadioBox(panel,-1,'Force to unit cell?',choices=choice)
996            self.force.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
997            mainSizer.Add(self.force,0,wx.ALIGN_CENTER_VERTICAL)
998        mainSizer.Add((5,5),0)
999        if SGData['SGInv']:
1000            choice = ['No','Yes']
1001            self.inv = wx.RadioBox(panel,-1,'Choose inversion?',choices=choice)
1002            self.inv.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
1003            mainSizer.Add(self.inv,0,wx.ALIGN_CENTER_VERTICAL)
1004        mainSizer.Add((5,5),0)
1005        if SGData['SGLatt'] != 'P':
1006            LattOp = G2spc.Latt2text(SGData['SGLatt']).split(';')
1007            self.latt = wx.RadioBox(panel,-1,'Choose cell centering?',choices=LattOp)
1008            self.latt.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
1009            mainSizer.Add(self.latt,0,wx.ALIGN_CENTER_VERTICAL)
1010        mainSizer.Add((5,5),0)
1011        if SGData['SGLaue'] in ['-1','2/m','mmm','4/m','4/mmm']:
1012            Ncol = 2
1013        else:
1014            Ncol = 3
1015        OpList = []
1016        for M,T in SGData['SGOps']:
1017            OpList.append(G2spc.MT2text(M,T))
1018        self.oprs = wx.RadioBox(panel,-1,'Choose space group operator?',choices=OpList,
1019            majorDimension=Ncol)
1020        self.oprs.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
1021        mainSizer.Add(self.oprs,0,wx.ALIGN_CENTER_VERTICAL)
1022        mainSizer.Add((5,5),0)
1023        mainSizer.Add(wx.StaticText(panel,-1,"   Choose unit cell?"),0,wx.ALIGN_CENTER_VERTICAL)
1024        mainSizer.Add((5,5),0)
1025        cellSizer = wx.BoxSizer(wx.HORIZONTAL)
1026        cellSizer.Add((5,0),0)
1027        cellName = ['X','Y','Z']
1028        self.cell = []
1029        for i in range(3):
1030            self.cell.append(wx.SpinCtrl(panel,-1,cellName[i],size=wx.Size(50,20)))
1031            self.cell[-1].SetRange(-3,3)
1032            self.cell[-1].SetValue(0)
1033            self.cell[-1].Bind(wx.EVT_SPINCTRL, self.OnOpSelect)
1034            cellSizer.Add(self.cell[-1],0,wx.ALIGN_CENTER_VERTICAL)
1035        mainSizer.Add(cellSizer,0,)
1036        if self.New:
1037            choice = ['No','Yes']
1038            self.new = wx.RadioBox(panel,-1,'Generate new positions?',choices=choice)
1039            self.new.Bind(wx.EVT_RADIOBOX, self.OnOpSelect)
1040            mainSizer.Add(self.new,0,wx.ALIGN_CENTER_VERTICAL)
1041        mainSizer.Add((5,5),0)
1042
1043        OkBtn = wx.Button(panel,-1,"Ok")
1044        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1045        cancelBtn = wx.Button(panel,-1,"Cancel")
1046        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1047        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1048        btnSizer.Add((20,20),1)
1049        btnSizer.Add(OkBtn)
1050        btnSizer.Add((20,20),1)
1051        btnSizer.Add(cancelBtn)
1052        btnSizer.Add((20,20),1)
1053
1054        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1055        panel.SetSizer(mainSizer)
1056        panel.Fit()
1057        self.Fit()
1058
1059    def OnOpSelect(self,event):
1060        if self.SGData['SGInv']:
1061            self.OpSelected[0] = self.inv.GetSelection()
1062        if self.SGData['SGLatt'] != 'P':
1063            self.OpSelected[1] = self.latt.GetSelection()
1064        self.OpSelected[2] = self.oprs.GetSelection()
1065        for i in range(3):
1066            self.OpSelected[3][i] = float(self.cell[i].GetValue())
1067        if self.New:
1068            self.OpSelected[4] = self.new.GetSelection()
1069        if self.Force:
1070            self.OpSelected[5] = self.force.GetSelection()
1071
1072    def GetSelection(self):
1073        return self.OpSelected
1074
1075    def OnOk(self,event):
1076        parent = self.GetParent()
1077        parent.Raise()
1078        self.EndModal(wx.ID_OK)
1079
1080    def OnCancel(self,event):
1081        parent = self.GetParent()
1082        parent.Raise()
1083        self.EndModal(wx.ID_CANCEL)
1084
1085class DisAglDialog(wx.Dialog):
1086    '''Distance/Angle Controls input dialog. After
1087    :meth:`ShowModal` returns, the results are found in
1088    dict :attr:`self.data`, which is accessed using :meth:`GetData`.
1089
1090    :param wx.Frame parent: reference to parent frame (or None)
1091    :param dict data: a dict containing the current
1092      search ranges or an empty dict, which causes default values
1093      to be used.
1094      Will be used to set element `DisAglCtls` in
1095      :ref:`Phase Tree Item <Phase_table>`
1096    :param dict default:  A dict containing the default
1097      search ranges for each element.
1098    '''
1099    def __init__(self,parent,data,default):
1100        wx.Dialog.__init__(self,parent,wx.ID_ANY,
1101                           'Distance Angle Controls', 
1102            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1103        self.default = default
1104        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
1105        self._default(data,self.default)
1106        self.Draw(self.data)
1107               
1108    def _default(self,data,default):
1109        '''Set starting values for the search values, either from
1110        the input array or from defaults, if input is null
1111        '''
1112        if data:
1113            self.data = copy.deepcopy(data) # don't mess with originals
1114        else:
1115            self.data = {}
1116            self.data['Name'] = default['Name']
1117            self.data['Factors'] = [0.85,0.85]
1118            self.data['AtomTypes'] = default['AtomTypes']
1119            self.data['BondRadii'] = default['BondRadii'][:]
1120            self.data['AngleRadii'] = default['AngleRadii'][:]
1121
1122    def Draw(self,data):
1123        '''Creates the contents of the dialog. Normally called
1124        by :meth:`__init__`.
1125        '''
1126        self.panel.Destroy()
1127        self.panel = wx.Panel(self)
1128        mainSizer = wx.BoxSizer(wx.VERTICAL)
1129        mainSizer.Add(wx.StaticText(self.panel,-1,'Controls for phase '+data['Name']),
1130            0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1131        mainSizer.Add((10,10),1)
1132       
1133        radiiSizer = wx.FlexGridSizer(2,3,5,5)
1134        radiiSizer.Add(wx.StaticText(self.panel,-1,' Type'),0,wx.ALIGN_CENTER_VERTICAL)
1135        radiiSizer.Add(wx.StaticText(self.panel,-1,'Bond radii'),0,wx.ALIGN_CENTER_VERTICAL)
1136        radiiSizer.Add(wx.StaticText(self.panel,-1,'Angle radii'),0,wx.ALIGN_CENTER_VERTICAL)
1137        self.objList = {}
1138        for id,item in enumerate(self.data['AtomTypes']):
1139            radiiSizer.Add(wx.StaticText(self.panel,-1,' '+item),0,wx.ALIGN_CENTER_VERTICAL)
1140            bRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['BondRadii'][id]),style=wx.TE_PROCESS_ENTER)
1141            self.objList[bRadii.GetId()] = ['BondRadii',id]
1142            bRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1143            bRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1144            radiiSizer.Add(bRadii,0,wx.ALIGN_CENTER_VERTICAL)
1145            aRadii = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['AngleRadii'][id]),style=wx.TE_PROCESS_ENTER)
1146            self.objList[aRadii.GetId()] = ['AngleRadii',id]
1147            aRadii.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1148            aRadii.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1149            radiiSizer.Add(aRadii,0,wx.ALIGN_CENTER_VERTICAL)
1150        mainSizer.Add(radiiSizer,0,wx.EXPAND)
1151        factorSizer = wx.FlexGridSizer(2,2,5,5)
1152        Names = ['Bond','Angle']
1153        for i,name in enumerate(Names):
1154            factorSizer.Add(wx.StaticText(self.panel,-1,name+' search factor'),0,wx.ALIGN_CENTER_VERTICAL)
1155            bondFact = wx.TextCtrl(self.panel,-1,value='%.3f'%(data['Factors'][i]),style=wx.TE_PROCESS_ENTER)
1156            self.objList[bondFact.GetId()] = ['Factors',i]
1157            bondFact.Bind(wx.EVT_TEXT_ENTER,self.OnRadiiVal)
1158            bondFact.Bind(wx.EVT_KILL_FOCUS,self.OnRadiiVal)
1159            factorSizer.Add(bondFact)
1160        mainSizer.Add(factorSizer,0,wx.EXPAND)
1161       
1162        OkBtn = wx.Button(self.panel,-1,"Ok")
1163        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1164        ResetBtn = wx.Button(self.panel,-1,'Reset')
1165        ResetBtn.Bind(wx.EVT_BUTTON, self.OnReset)
1166        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1167        btnSizer.Add((20,20),1)
1168        btnSizer.Add(OkBtn)
1169        btnSizer.Add(ResetBtn)
1170        btnSizer.Add((20,20),1)
1171        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1172        self.panel.SetSizer(mainSizer)
1173        self.panel.Fit()
1174        self.Fit()
1175   
1176    def OnRadiiVal(self,event):
1177        Obj = event.GetEventObject()
1178        item = self.objList[Obj.GetId()]
1179        try:
1180            self.data[item[0]][item[1]] = float(Obj.GetValue())
1181        except ValueError:
1182            pass
1183        Obj.SetValue("%.3f"%(self.data[item[0]][item[1]]))          #reset in case of error
1184       
1185    def GetData(self):
1186        'Returns the values from the dialog'
1187        return self.data
1188       
1189    def OnOk(self,event):
1190        'Called when the OK button is pressed'
1191        parent = self.GetParent()
1192        parent.Raise()
1193        self.EndModal(wx.ID_OK)             
1194       
1195    def OnReset(self,event):
1196        'Called when the Reset button is pressed'
1197        data = {}
1198        self._default(data,self.default)
1199        self.Draw(self.data)
1200       
1201class PickTwoDialog(wx.Dialog):
1202    '''This does not seem to be in use
1203    '''
1204    def __init__(self,parent,title,prompt,names,choices):
1205        wx.Dialog.__init__(self,parent,-1,title, 
1206            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1207        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
1208        self.prompt = prompt
1209        self.choices = choices
1210        self.names = names
1211        self.Draw()
1212
1213    def Draw(self):
1214        Indx = {}
1215       
1216        def OnSelection(event):
1217            Obj = event.GetEventObject()
1218            id = Indx[Obj.GetId()]
1219            self.choices[id] = Obj.GetValue().encode()  #to avoid Unicode versions
1220            self.Draw()
1221           
1222        self.panel.DestroyChildren()
1223        self.panel.Destroy()
1224        self.panel = wx.Panel(self)
1225        mainSizer = wx.BoxSizer(wx.VERTICAL)
1226        mainSizer.Add(wx.StaticText(self.panel,-1,self.prompt),0,wx.ALIGN_CENTER)
1227        for isel,name in enumerate(self.choices):
1228            lineSizer = wx.BoxSizer(wx.HORIZONTAL)
1229            lineSizer.Add(wx.StaticText(self.panel,-1,'Reference atom '+str(isel+1)),0,wx.ALIGN_CENTER)
1230            nameList = self.names[:]
1231            if isel:
1232                if self.choices[0] in nameList:
1233                    nameList.remove(self.choices[0])
1234            choice = wx.ComboBox(self.panel,-1,value=name,choices=nameList,
1235                style=wx.CB_READONLY|wx.CB_DROPDOWN)
1236            Indx[choice.GetId()] = isel
1237            choice.Bind(wx.EVT_COMBOBOX, OnSelection)
1238            lineSizer.Add(choice,0,wx.ALIGN_CENTER)
1239            mainSizer.Add(lineSizer)
1240        OkBtn = wx.Button(self.panel,-1,"Ok")
1241        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1242        CancelBtn = wx.Button(self.panel,-1,'Cancel')
1243        CancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1244        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1245        btnSizer.Add((20,20),1)
1246        btnSizer.Add(OkBtn)
1247        btnSizer.Add(CancelBtn)
1248        btnSizer.Add((20,20),1)
1249        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1250        self.panel.SetSizer(mainSizer)
1251        self.panel.Fit()
1252        self.Fit()
1253       
1254    def GetSelection(self):
1255        return self.choices
1256
1257    def OnOk(self,event):
1258        parent = self.GetParent()
1259        parent.Raise()
1260        self.EndModal(wx.ID_OK)             
1261       
1262    def OnCancel(self,event):
1263        parent = self.GetParent()
1264        parent.Raise()
1265        self.EndModal(wx.ID_CANCEL)
1266       
1267class SingleFloatDialog(wx.Dialog):
1268    'Dialog to obtain a single float value from user'
1269    def __init__(self,parent,title,prompt,value,limits=[0.,1.],format='%.5g'):
1270        wx.Dialog.__init__(self,parent,-1,title, 
1271            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1272        self.panel = wx.Panel(self)         #just a dummy - gets destroyed in Draw!
1273        self.limits = limits
1274        self.value = value
1275        self.prompt = prompt
1276        self.format = format
1277        self.Draw()
1278       
1279    def Draw(self):
1280       
1281        def OnValItem(event):
1282            try:
1283                val = float(valItem.GetValue())
1284                if val < self.limits[0] or val > self.limits[1]:
1285                    raise ValueError
1286            except ValueError:
1287                val = self.value
1288            self.value = val
1289            valItem.SetValue(self.format%(self.value))
1290           
1291        self.panel.Destroy()
1292        self.panel = wx.Panel(self)
1293        mainSizer = wx.BoxSizer(wx.VERTICAL)
1294        mainSizer.Add(wx.StaticText(self.panel,-1,self.prompt),0,wx.ALIGN_CENTER)
1295        valItem = wx.TextCtrl(self.panel,-1,value=self.format%(self.value),style=wx.TE_PROCESS_ENTER)
1296        mainSizer.Add(valItem,0,wx.ALIGN_CENTER)
1297        valItem.Bind(wx.EVT_TEXT_ENTER,OnValItem)
1298        valItem.Bind(wx.EVT_KILL_FOCUS,OnValItem)
1299        OkBtn = wx.Button(self.panel,-1,"Ok")
1300        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1301        CancelBtn = wx.Button(self.panel,-1,'Cancel')
1302        CancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1303        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1304        btnSizer.Add((20,20),1)
1305        btnSizer.Add(OkBtn)
1306        btnSizer.Add(CancelBtn)
1307        btnSizer.Add((20,20),1)
1308        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1309        self.panel.SetSizer(mainSizer)
1310        self.panel.Fit()
1311        self.Fit()
1312
1313    def GetValue(self):
1314        return self.value
1315       
1316    def OnOk(self,event):
1317        parent = self.GetParent()
1318        parent.Raise()
1319        self.EndModal(wx.ID_OK)             
1320       
1321    def OnCancel(self,event):
1322        parent = self.GetParent()
1323        parent.Raise()
1324        self.EndModal(wx.ID_CANCEL)
1325
1326################################################################################
1327class SingleStringDialog(wx.Dialog):
1328    '''Dialog to obtain a single string value from user
1329   
1330    :param wx.Frame parent: name of parent frame
1331    :param str title: title string for dialog
1332    :param str prompt: string to tell use what they are inputting
1333    :param str value: default input value, if any
1334    '''
1335    def __init__(self,parent,title,prompt,value='',size=(200,-1)):
1336        wx.Dialog.__init__(self,parent,wx.ID_ANY,title, 
1337                           pos=wx.DefaultPosition,
1338                           style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1339        self.value = value
1340        self.prompt = prompt
1341        self.CenterOnParent()
1342        self.panel = wx.Panel(self)
1343        mainSizer = wx.BoxSizer(wx.VERTICAL)
1344        mainSizer.Add(wx.StaticText(self.panel,-1,self.prompt),0,wx.ALIGN_CENTER)
1345        self.valItem = wx.TextCtrl(self.panel,-1,value=str(self.value),size=size)
1346        mainSizer.Add(self.valItem,0,wx.ALIGN_CENTER)
1347        btnsizer = wx.StdDialogButtonSizer()
1348        OKbtn = wx.Button(self.panel, wx.ID_OK)
1349        OKbtn.SetDefault()
1350        btnsizer.AddButton(OKbtn)
1351        btn = wx.Button(self.panel, wx.ID_CANCEL)
1352        btnsizer.AddButton(btn)
1353        btnsizer.Realize()
1354        mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER)
1355        self.panel.SetSizer(mainSizer)
1356        self.panel.Fit()
1357        self.Fit()
1358
1359    def Show(self):
1360        '''Use this method after creating the dialog to post it
1361        :returns: True if the user pressed OK; False if the User pressed Cancel
1362        '''
1363        if self.ShowModal() == wx.ID_OK:
1364            self.value = self.valItem.GetValue()
1365            return True
1366        else:
1367            return False
1368
1369    def GetValue(self):
1370        '''Use this method to get the value entered by the user
1371        :returns: string entered by user
1372        '''
1373        return self.value
1374
1375################################################################################
1376
1377class G2MultiChoiceDialog(wx.Dialog):
1378    '''A dialog similar to MultiChoiceDialog except that buttons are
1379    added to set all choices and to toggle all choices.
1380
1381    :param wx.Frame ParentFrame: reference to parent frame
1382    :param str title: heading above list of choices
1383    :param str header: Title to place on window frame
1384    :param list ChoiceList: a list of choices where one will be selected
1385    :param bool toggle: If True (default) the toggle and select all buttons
1386      are displayed
1387    :param bool monoFont: If False (default), use a variable-spaced font;
1388      if True use a equally-spaced font.
1389    :param bool filterBox: If True (default) an input widget is placed on
1390      the window and only entries matching the entered text are shown.
1391    :param kw: optional keyword parameters for the wx.Dialog may
1392      be included such as size [which defaults to `(320,310)`] and
1393      style (which defaults to `wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE| wx.OK | wx.CANCEL`);
1394      note that `wx.OK` and `wx.CANCEL` controls
1395      the presence of the eponymous buttons in the dialog.
1396    :returns: the name of the created dialog 
1397    '''
1398    def __init__(self,parent, title, header, ChoiceList, toggle=True,
1399                 monoFont=False, filterBox=True, **kw):
1400        # process keyword parameters, notably style
1401        options = {'size':(320,310), # default Frame keywords
1402                   'style':wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE| wx.OK | wx.CANCEL,
1403                   }
1404        options.update(kw)
1405        self.ChoiceList = ChoiceList
1406        self.filterlist = range(len(self.ChoiceList))
1407        if options['style'] & wx.OK:
1408            useOK = True
1409            options['style'] ^= wx.OK
1410        else:
1411            useOK = False
1412        if options['style'] & wx.CANCEL:
1413            useCANCEL = True
1414            options['style'] ^= wx.CANCEL
1415        else:
1416            useCANCEL = False       
1417        # create the dialog frame
1418        wx.Dialog.__init__(self,parent,wx.ID_ANY,header,**options)
1419        # fill the dialog
1420        Sizer = wx.BoxSizer(wx.VERTICAL)
1421        topSizer = wx.BoxSizer(wx.HORIZONTAL)
1422        topSizer.Add(wx.StaticText(self,wx.ID_ANY,title),1,wx.ALL|wx.EXPAND,0)
1423        if filterBox:
1424            self.timer = wx.Timer()
1425            self.timer.Bind(wx.EVT_TIMER,self.Filter)
1426            topSizer.Add(wx.StaticText(self,wx.ID_ANY,'Filter: '),0,wx.ALL,1)
1427            self.txt = wx.TextCtrl(self, wx.ID_ANY, size=(80,-1))
1428            self.txt.Bind(wx.EVT_CHAR,self.onChar)
1429        topSizer.Add(self.txt,0,wx.ALL,0)
1430        Sizer.Add(topSizer,0,wx.ALL|wx.EXPAND,8)
1431        self.clb = wx.CheckListBox(self, wx.ID_ANY, (30,30), wx.DefaultSize, ChoiceList)
1432        if monoFont:
1433            font1 = wx.Font(self.clb.GetFont().GetPointSize()-1,
1434                            wx.MODERN, wx.NORMAL, wx.NORMAL, False)
1435            self.clb.SetFont(font1)
1436        self.numchoices = len(ChoiceList)
1437        Sizer.Add(self.clb,1,wx.LEFT|wx.RIGHT|wx.EXPAND,10)
1438        Sizer.Add((-1,10))
1439        # set/toggle buttons
1440        if toggle:
1441            bSizer = wx.BoxSizer(wx.VERTICAL)
1442            setBut = wx.Button(self,wx.ID_ANY,'Set All')
1443            setBut.Bind(wx.EVT_BUTTON,self._SetAll)
1444            bSizer.Add(setBut,0,wx.ALIGN_CENTER)
1445            bSizer.Add((-1,5))
1446            togBut = wx.Button(self,wx.ID_ANY,'Toggle All')
1447            togBut.Bind(wx.EVT_BUTTON,self._ToggleAll)
1448            bSizer.Add(togBut,0,wx.ALIGN_CENTER)
1449            Sizer.Add(bSizer,0,wx.LEFT,12)
1450        # OK/Cancel buttons
1451        btnsizer = wx.StdDialogButtonSizer()
1452        if useOK:
1453            OKbtn = wx.Button(self, wx.ID_OK)
1454            OKbtn.SetDefault()
1455            btnsizer.AddButton(OKbtn)
1456        if useCANCEL:
1457            btn = wx.Button(self, wx.ID_CANCEL)
1458            btnsizer.AddButton(btn)
1459        btnsizer.Realize()
1460        Sizer.Add((-1,5))
1461        Sizer.Add(btnsizer,0,wx.ALIGN_RIGHT,50)
1462        Sizer.Add((-1,20))
1463        # OK done, let's get outa here
1464        self.SetSizer(Sizer)
1465    def GetSelections(self):
1466        'Returns a list of the indices for the selected choices'
1467        return [self.filterlist[i] for i in range(self.numchoices) if self.clb.IsChecked(i)]
1468    def _SetAll(self,event):
1469        'Set all choices on'
1470        self.clb.SetChecked(range(self.numchoices))
1471    def _ToggleAll(self,event):
1472        'flip the state of all choices'
1473        for i in range(self.numchoices):
1474            self.clb.Check(i,not self.clb.IsChecked(i))
1475    def onChar(self,event):
1476        if self.timer.IsRunning():
1477            self.timer.Stop()
1478        self.timer.Start(1000,oneShot=True)
1479        event.Skip()
1480    def Filter(self,event):
1481        txt = self.txt.GetValue()
1482        self.clb.Clear()
1483        self.Update()
1484        self.filterlist = []
1485        if txt:
1486            txt = txt.lower()
1487            ChoiceList = []
1488            for i,item in enumerate(self.ChoiceList):
1489                if item.lower().find(txt) != -1:
1490                    ChoiceList.append(item)
1491                    self.filterlist.append(i)
1492        else:
1493            self.filterlist = range(len(self.ChoiceList))
1494            ChoiceList = self.ChoiceList
1495        self.clb.AppendItems(ChoiceList)
1496
1497################################################################################
1498
1499class G2SingleChoiceDialog(wx.Dialog):
1500    '''A dialog similar to wx.SingleChoiceDialog except that a filter can be
1501    added.
1502
1503    :param wx.Frame ParentFrame: reference to parent frame
1504    :param str title: heading above list of choices
1505    :param str header: Title to place on window frame
1506    :param list ChoiceList: a list of choices where one will be selected
1507    :param bool monoFont: If False (default), use a variable-spaced font;
1508      if True use a equally-spaced font.
1509    :param bool filterBox: If True (default) an input widget is placed on
1510      the window and only entries matching the entered text are shown.
1511    :param kw: optional keyword parameters for the wx.Dialog may
1512      be included such as size [which defaults to `(320,310)`] and
1513      style (which defaults to ``wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.CENTRE | wx.OK | wx.CANCEL``);
1514      note that ``wx.OK`` and ``wx.CANCEL`` controls
1515      the presence of the eponymous buttons in the dialog.
1516    :returns: the name of the created dialog
1517    '''
1518    def __init__(self,parent, title, header, ChoiceList, 
1519                 monoFont=False, filterBox=True, **kw):
1520        # process keyword parameters, notably style
1521        options = {'size':(320,310), # default Frame keywords
1522                   'style':wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE| wx.OK | wx.CANCEL,
1523                   }
1524        options.update(kw)
1525        self.ChoiceList = ChoiceList
1526        self.filterlist = range(len(self.ChoiceList))
1527        if options['style'] & wx.OK:
1528            useOK = True
1529            options['style'] ^= wx.OK
1530        else:
1531            useOK = False
1532        if options['style'] & wx.CANCEL:
1533            useCANCEL = True
1534            options['style'] ^= wx.CANCEL
1535        else:
1536            useCANCEL = False       
1537        # create the dialog frame
1538        wx.Dialog.__init__(self,parent,wx.ID_ANY,header,**options)
1539        # fill the dialog
1540        Sizer = wx.BoxSizer(wx.VERTICAL)
1541        topSizer = wx.BoxSizer(wx.HORIZONTAL)
1542        topSizer.Add(wx.StaticText(self,wx.ID_ANY,title),1,wx.ALL|wx.EXPAND,0)
1543        if filterBox:
1544            self.timer = wx.Timer()
1545            self.timer.Bind(wx.EVT_TIMER,self.Filter)
1546            topSizer.Add(wx.StaticText(self,wx.ID_ANY,'Filter: '),0,wx.ALL,1)
1547            self.txt = wx.TextCtrl(self, wx.ID_ANY, size=(80,-1))
1548            self.txt.Bind(wx.EVT_CHAR,self.onChar)
1549        topSizer.Add(self.txt,0,wx.ALL,0)
1550        Sizer.Add(topSizer,0,wx.ALL|wx.EXPAND,8)
1551        self.clb = wx.ListBox(self, wx.ID_ANY, (30,30), wx.DefaultSize, ChoiceList)
1552        self.clb.Bind(wx.EVT_LEFT_DCLICK,self.onDoubleClick)
1553        if monoFont:
1554            font1 = wx.Font(self.clb.GetFont().GetPointSize()-1,
1555                            wx.MODERN, wx.NORMAL, wx.NORMAL, False)
1556            self.clb.SetFont(font1)
1557        self.numchoices = len(ChoiceList)
1558        Sizer.Add(self.clb,1,wx.LEFT|wx.RIGHT|wx.EXPAND,10)
1559        Sizer.Add((-1,10))
1560        # OK/Cancel buttons
1561        btnsizer = wx.StdDialogButtonSizer()
1562        if useOK:
1563            OKbtn = wx.Button(self, wx.ID_OK)
1564            OKbtn.SetDefault()
1565            btnsizer.AddButton(OKbtn)
1566        if useCANCEL:
1567            btn = wx.Button(self, wx.ID_CANCEL)
1568            btnsizer.AddButton(btn)
1569        btnsizer.Realize()
1570        Sizer.Add((-1,5))
1571        Sizer.Add(btnsizer,0,wx.ALIGN_RIGHT,50)
1572        Sizer.Add((-1,20))
1573        # OK done, let's get outa here
1574        self.SetSizer(Sizer)
1575    def GetSelection(self):
1576        'Returns the index of the selected choice'
1577        i = self.clb.GetSelection()
1578        if i < 0 or i >= len(self.filterlist):
1579            return wx.NOT_FOUND
1580        return self.filterlist[i]
1581    def onChar(self,event):
1582        if self.timer.IsRunning():
1583            self.timer.Stop()
1584        self.timer.Start(1000,oneShot=True)
1585        event.Skip()
1586    def Filter(self,event):
1587        txt = self.txt.GetValue()
1588        self.clb.Clear()
1589        self.Update()
1590        self.filterlist = []
1591        if txt:
1592            txt = txt.lower()
1593            ChoiceList = []
1594            for i,item in enumerate(self.ChoiceList):
1595                if item.lower().find(txt) != -1:
1596                    ChoiceList.append(item)
1597                    self.filterlist.append(i)
1598        else:
1599            self.filterlist = range(len(self.ChoiceList))
1600            ChoiceList = self.ChoiceList
1601        self.clb.AppendItems(ChoiceList)
1602    def onDoubleClick(self,event):
1603        self.EndModal(wx.ID_OK)
1604
1605################################################################################
1606
1607def ItemSelector(ChoiceList, ParentFrame=None,
1608                 title='Select an item',
1609                 size=None, header='Item Selector',
1610                 useCancel=True,multiple=False):
1611    ''' Provide a wx dialog to select a single item or multiple items from list of choices
1612
1613    :param list ChoiceList: a list of choices where one will be selected
1614    :param wx.Frame ParentFrame: Name of parent frame (default None)
1615    :param str title: heading above list of choices (default 'Select an item')
1616    :param wx.Size size: Size for dialog to be created (default None -- size as needed)
1617    :param str header: Title to place on window frame (default 'Item Selector')
1618    :param bool useCancel: If True (default) both the OK and Cancel buttons are offered
1619    :param bool multiple: If True then multiple items can be selected (default False)
1620   
1621    :returns: the selection index or None or a selection list if multiple is true
1622    '''
1623    if multiple:
1624        if useCancel:
1625            dlg = G2MultiChoiceDialog(
1626                ParentFrame,title, header, ChoiceList)
1627        else:
1628            dlg = G2MultiChoiceDialog(
1629                ParentFrame,title, header, ChoiceList,
1630                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1631    else:
1632        if useCancel:
1633            dlg = wx.SingleChoiceDialog(
1634                ParentFrame,title, header, ChoiceList)
1635        else:
1636            dlg = wx.SingleChoiceDialog(
1637                ParentFrame,title, header,ChoiceList,
1638                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1639    if size: dlg.SetSize(size)
1640    if dlg.ShowModal() == wx.ID_OK:
1641        if multiple:
1642            dlg.Destroy()
1643            return dlg.GetSelections()
1644        else:
1645            dlg.Destroy()
1646            return dlg.GetSelection()
1647    else:
1648        dlg.Destroy()
1649        return None
1650    dlg.Destroy()
1651
1652################################################################################
1653class GridFractionEditor(wg.PyGridCellEditor):
1654    '''A grid cell editor class that allows entry of values as fractions as well
1655    as sine and cosine values [as s() and c()]
1656    '''
1657    def __init__(self,grid):
1658        wg.PyGridCellEditor.__init__(self)
1659
1660    def Create(self, parent, id, evtHandler):
1661        self._tc = wx.TextCtrl(parent, id, "")
1662        self._tc.SetInsertionPoint(0)
1663        self.SetControl(self._tc)
1664
1665        if evtHandler:
1666            self._tc.PushEventHandler(evtHandler)
1667
1668        self._tc.Bind(wx.EVT_CHAR, self.OnChar)
1669
1670    def SetSize(self, rect):
1671        self._tc.SetDimensions(rect.x, rect.y, rect.width+2, rect.height+2,
1672                               wx.SIZE_ALLOW_MINUS_ONE)
1673
1674    def BeginEdit(self, row, col, grid):
1675        self.startValue = grid.GetTable().GetValue(row, col)
1676        self._tc.SetValue(str(self.startValue))
1677        self._tc.SetInsertionPointEnd()
1678        self._tc.SetFocus()
1679        self._tc.SetSelection(0, self._tc.GetLastPosition())
1680
1681    def EndEdit(self, row, col, grid):
1682        changed = False
1683
1684        val = self._tc.GetValue().lower()
1685       
1686        if val != self.startValue:
1687            changed = True
1688            neg = False
1689            if '-' in val:
1690                neg = True
1691            if '/' in val and '.' not in val:
1692                val += '.'
1693            elif 's' in val and not 'sind(' in val:
1694                if neg:
1695                    val = '-sind('+val.strip('-s')+')'
1696                else:
1697                    val = 'sind('+val.strip('s')+')'
1698            elif 'c' in val and not 'cosd(' in val:
1699                if neg:
1700                    val = '-cosd('+val.strip('-c')+')'
1701                else:
1702                    val = 'cosd('+val.strip('c')+')'
1703            try:
1704                val = float(eval(val))
1705            except (SyntaxError,NameError):
1706                val = self.startValue
1707            grid.GetTable().SetValue(row, col, val) # update the table
1708
1709        self.startValue = ''
1710        self._tc.SetValue('')
1711        return changed
1712
1713    def Reset(self):
1714        self._tc.SetValue(self.startValue)
1715        self._tc.SetInsertionPointEnd()
1716
1717    def Clone(self):
1718        return GridFractionEditor(grid)
1719
1720    def StartingKey(self, evt):
1721        self.OnChar(evt)
1722        if evt.GetSkipped():
1723            self._tc.EmulateKeyPress(evt)
1724
1725    def OnChar(self, evt):
1726        key = evt.GetKeyCode()
1727        if key == 15:
1728            return
1729        if key > 255:
1730            evt.Skip()
1731            return
1732        char = chr(key)
1733        if char in '.+-/0123456789cosind()':
1734            self._tc.WriteText(char)
1735        else:
1736            evt.Skip()
1737
1738################################################################################
1739class ShowLSParms(wx.Dialog):
1740    '''Create frame to show least-squares parameters
1741    '''
1742    def __init__(self,parent,title,parmDict,varyList,fullVaryList,
1743                 size=(300,430)):
1744        wx.Dialog.__init__(self,parent,wx.ID_ANY,title,size=size,
1745                           style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1746        mainSizer = wx.BoxSizer(wx.VERTICAL)
1747
1748        panel = wxscroll.ScrolledPanel(
1749            self, wx.ID_ANY,
1750            #size=size,
1751            style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
1752        num = len(varyList)
1753        mainSizer.Add(wx.StaticText(self,wx.ID_ANY,'Number of refined variables: '+str(num)))
1754        if len(varyList) != len(fullVaryList):
1755            num = len(fullVaryList) - len(varyList)
1756            mainSizer.Add(wx.StaticText(self,wx.ID_ANY,' + '+str(num)+' parameters are varied via constraints'))
1757        subSizer = wx.FlexGridSizer(rows=len(parmDict)+1,cols=4,hgap=2,vgap=2)
1758        parmNames = parmDict.keys()
1759        parmNames.sort()
1760        subSizer.Add((-1,-1))
1761        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'Parameter name  '))
1762        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'refine?'))
1763        subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'value'),0,wx.ALIGN_RIGHT)
1764        explainRefine = False
1765        for name in parmNames:
1766            # skip entries without numerical values
1767            if isinstance(parmDict[name],basestring): continue
1768            try:
1769                value = G2py3.FormatSigFigs(parmDict[name])
1770            except TypeError:
1771                value = str(parmDict[name])+' -?' # unexpected
1772                #continue
1773            v = G2obj.getVarDescr(name)
1774            if v is None or v[-1] is None:
1775                subSizer.Add((-1,-1))
1776            else:               
1777                ch = HelpButton(panel,G2obj.fmtVarDescr(name))
1778                subSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
1779            subSizer.Add(wx.StaticText(panel,wx.ID_ANY,str(name)))
1780            if name in varyList:
1781                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'R'))
1782            elif name in fullVaryList:
1783                subSizer.Add(wx.StaticText(panel,wx.ID_ANY,'C'))
1784                explainRefine = True
1785            else:
1786                subSizer.Add((-1,-1))
1787            subSizer.Add(wx.StaticText(panel,wx.ID_ANY,value),0,wx.ALIGN_RIGHT)
1788
1789        # finish up ScrolledPanel
1790        panel.SetSizer(subSizer)
1791        panel.SetAutoLayout(1)
1792        panel.SetupScrolling()
1793        mainSizer.Add(panel,1, wx.ALL|wx.EXPAND,1)
1794
1795        if explainRefine:
1796            mainSizer.Add(
1797                wx.StaticText(self,wx.ID_ANY,
1798                          '"R" indicates a refined variable\n'+
1799                          '"C" indicates generated from a constraint'
1800                          ),
1801                0, wx.ALL,0)
1802        # make OK button
1803        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
1804        btn = wx.Button(self, wx.ID_CLOSE,"Close") 
1805        btn.Bind(wx.EVT_BUTTON,self._onClose)
1806        btnsizer.Add(btn)
1807        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
1808        # Allow window to be enlarged but not made smaller
1809        self.SetSizer(mainSizer)
1810        self.SetMinSize(self.GetSize())
1811
1812    def _onClose(self,event):
1813        self.EndModal(wx.ID_CANCEL)
1814 
1815################################################################################
1816class downdate(wx.Dialog):
1817    '''Dialog to allow a user to select a version of GSAS-II to install
1818    '''
1819    def __init__(self,parent=None):
1820        style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
1821        wx.Dialog.__init__(self, parent, wx.ID_ANY, 'Select Version', style=style)
1822        pnl = wx.Panel(self)
1823        sizer = wx.BoxSizer(wx.VERTICAL)
1824        insver = GSASIIpath.svnGetRev(local=True)
1825        curver = int(GSASIIpath.svnGetRev(local=False))
1826        label = wx.StaticText(
1827            pnl,  wx.ID_ANY,
1828            'Select a specific GSAS-II version to install'
1829            )
1830        sizer.Add(label, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
1831        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
1832        sizer1.Add(
1833            wx.StaticText(pnl,  wx.ID_ANY,
1834                          'Currently installed version: '+str(insver)),
1835            0, wx.ALIGN_CENTRE|wx.ALL, 5)
1836        sizer.Add(sizer1)
1837        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
1838        sizer1.Add(
1839            wx.StaticText(pnl,  wx.ID_ANY,
1840                          'Select GSAS-II version to install: '),
1841            0, wx.ALIGN_CENTRE|wx.ALL, 5)
1842        self.spin = wx.SpinCtrl(pnl, wx.ID_ANY,size=(150,-1))
1843        self.spin.SetRange(1, curver)
1844        self.spin.SetValue(curver)
1845        self.Bind(wx.EVT_SPINCTRL, self._onSpin, self.spin)
1846        self.Bind(wx.EVT_KILL_FOCUS, self._onSpin, self.spin)
1847        sizer1.Add(self.spin)
1848        sizer.Add(sizer1)
1849
1850        line = wx.StaticLine(pnl,-1, size=(-1,3), style=wx.LI_HORIZONTAL)
1851        sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.ALL, 10)
1852
1853        self.text = wx.StaticText(pnl,  wx.ID_ANY, "")
1854        sizer.Add(self.text, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5)
1855
1856        line = wx.StaticLine(pnl,-1, size=(-1,3), style=wx.LI_HORIZONTAL)
1857        sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.ALL, 10)
1858        sizer.Add(
1859            wx.StaticText(
1860                pnl,  wx.ID_ANY,
1861                'If "Install" is pressed, your project will be saved;\n'
1862                'GSAS-II will exit; The specified version will be loaded\n'
1863                'and GSAS-II will restart. Press "Cancel" to abort.'),
1864            0, wx.EXPAND|wx.ALL, 10)
1865        btnsizer = wx.StdDialogButtonSizer()
1866        btn = wx.Button(pnl, wx.ID_OK, "Install")
1867        btn.SetDefault()
1868        btnsizer.AddButton(btn)
1869        btn = wx.Button(pnl, wx.ID_CANCEL)
1870        btnsizer.AddButton(btn)
1871        btnsizer.Realize()
1872        sizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
1873        pnl.SetSizer(sizer)
1874        sizer.Fit(self)
1875        self.topsizer=sizer
1876        self.CenterOnParent()
1877        self._onSpin(None)
1878
1879    def _onSpin(self,event):
1880        'Called to load info about the selected version in the dialog'
1881        ver = self.spin.GetValue()
1882        d = GSASIIpath.svnGetLog(version=ver)
1883        date = d.get('date','?').split('T')[0]
1884        s = '(Version '+str(ver)+' created '+date
1885        s += ' by '+d.get('author','?')+')'
1886        msg = d.get('msg')
1887        if msg: s += '\n\nComment: '+msg
1888        self.text.SetLabel(s)
1889        self.topsizer.Fit(self)
1890
1891    def getVersion(self):
1892        'Get the version number in the dialog'
1893        return self.spin.GetValue()
1894
1895################################################################################
1896class MyHelp(wx.Menu):
1897    '''
1898    A class that creates the contents of a help menu.
1899    The menu will start with two entries:
1900
1901    * 'Help on <helpType>': where helpType is a reference to an HTML page to
1902      be opened
1903    * About: opens an About dialog using OnHelpAbout. N.B. on the Mac this
1904      gets moved to the App menu to be consistent with Apple style.
1905
1906    NOTE: for this to work properly with respect to system menus, the title
1907    for the menu must be &Help, or it will not be processed properly:
1908
1909    ::
1910
1911       menu.Append(menu=MyHelp(self,...),title="&Help")
1912
1913    '''
1914    def __init__(self,frame,helpType=None,helpLbl=None,morehelpitems=[],title=''):
1915        wx.Menu.__init__(self,title)
1916        self.HelpById = {}
1917        self.frame = frame
1918        self.Append(help='', id=wx.ID_ABOUT, kind=wx.ITEM_NORMAL,
1919            text='&About GSAS-II')
1920        frame.Bind(wx.EVT_MENU, self.OnHelpAbout, id=wx.ID_ABOUT)
1921        if GSASIIpath.whichsvn():
1922            helpobj = self.Append(
1923                help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
1924                text='&Check for updates')
1925            frame.Bind(wx.EVT_MENU, self.OnCheckUpdates, helpobj)
1926            helpobj = self.Append(
1927                help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
1928                text='&Regress to an old GSAS-II version')
1929            frame.Bind(wx.EVT_MENU, self.OnSelectVersion, helpobj)
1930        for lbl,indx in morehelpitems:
1931            helpobj = self.Append(text=lbl,
1932                id=wx.ID_ANY, kind=wx.ITEM_NORMAL)
1933            frame.Bind(wx.EVT_MENU, self.OnHelpById, helpobj)
1934            self.HelpById[helpobj.GetId()] = indx
1935        # add a help item only when helpType is specified
1936        if helpType is not None:
1937            self.AppendSeparator()
1938            if helpLbl is None: helpLbl = helpType
1939            helpobj = self.Append(text='Help on '+helpLbl,
1940                                  id=wx.ID_ANY, kind=wx.ITEM_NORMAL)
1941            frame.Bind(wx.EVT_MENU, self.OnHelpById, helpobj)
1942            self.HelpById[helpobj.GetId()] = helpType
1943       
1944    def OnHelpById(self,event):
1945        '''Called when Help on... is pressed in a menu. Brings up
1946        a web page for documentation.
1947        '''
1948        helpType = self.HelpById.get(event.GetId())
1949        if helpType is None:
1950            print 'Error: help lookup failed!',event.GetEventObject()
1951            print 'id=',event.GetId()
1952        else:
1953            ShowHelp(helpType,self.frame)
1954
1955    def OnHelpAbout(self, event):
1956        "Display an 'About GSAS-II' box"
1957        global __version__
1958        info = wx.AboutDialogInfo()
1959        info.Name = 'GSAS-II'
1960        ver = GSASIIpath.svnGetRev()
1961        if ver: 
1962            info.Version = 'Revision '+str(ver)+' (svn), version '+__version__
1963        else:
1964            info.Version = 'Revision '+str(GSASIIpath.GetVersionNumber())+' (.py files), version '+__version__
1965        #info.Developers = ['Robert B. Von Dreele','Brian H. Toby']
1966        info.Copyright = ('(c) ' + time.strftime('%Y') +
1967''' Argonne National Laboratory
1968This product includes software developed
1969by the UChicago Argonne, LLC, as
1970Operator of Argonne National Laboratory.''')
1971        info.Description = '''General Structure Analysis System-II (GSAS-II)
1972Robert B. Von Dreele and Brian H. Toby
1973
1974Please cite as:
1975B.H. Toby & R.B. Von Dreele, J. Appl. Cryst. 46, 544-549 (2013) '''
1976
1977        info.WebSite = ("https://subversion.xray.aps.anl.gov/trac/pyGSAS","GSAS-II home page")
1978        wx.AboutBox(info)
1979
1980    def OnCheckUpdates(self,event):
1981        '''Check if the GSAS-II repository has an update for the current source files
1982        and perform that update if requested.
1983        '''
1984        if not GSASIIpath.whichsvn():
1985            dlg = wx.MessageDialog(self.frame,
1986                                   'No Subversion','Cannot update GSAS-II because subversion (svn) was not found.',
1987                                   wx.OK)
1988            dlg.ShowModal()
1989            dlg.Destroy()
1990            return
1991        wx.BeginBusyCursor()
1992        local = GSASIIpath.svnGetRev()
1993        if local is None: 
1994            wx.EndBusyCursor()
1995            dlg = wx.MessageDialog(self.frame,
1996                                   'Unable to run subversion on the GSAS-II current directory. Is GSAS-II installed correctly?',
1997                                   'Subversion error',
1998                                   wx.OK)
1999            dlg.ShowModal()
2000            dlg.Destroy()
2001            return
2002        print 'Installed GSAS-II version: '+local
2003        repos = GSASIIpath.svnGetRev(local=False)
2004        wx.EndBusyCursor()
2005        if repos is None: 
2006            dlg = wx.MessageDialog(self.frame,
2007                                   'Unable to access the GSAS-II server. Is this computer on the internet?',
2008                                   'Server unavailable',
2009                                   wx.OK)
2010            dlg.ShowModal()
2011            dlg.Destroy()
2012            return
2013        print 'GSAS-II version on server: '+repos
2014        if local == repos:
2015            dlg = wx.MessageDialog(self.frame,
2016                                   'GSAS-II is up-to-date. Version '+local+' is already loaded.',
2017                                   'GSAS-II Up-to-date',
2018                                   wx.OK)
2019            dlg.ShowModal()
2020            dlg.Destroy()
2021            return
2022        mods = GSASIIpath.svnFindLocalChanges()
2023        if mods:
2024            dlg = wx.MessageDialog(self.frame,
2025                                   'You have version '+local+
2026                                   ' of GSAS-II installed, but the current version is '+repos+
2027                                   '. However, '+str(len(mods))+
2028                                   ' file(s) on your local computer have been modified.'
2029                                   ' Updating will attempt to merge your local changes with '
2030                                   'the latest GSAS-II version, but if '
2031                                   'conflicts arise, local changes will be '
2032                                   'discarded. It is also possible that the '
2033                                   'local changes my prevent GSAS-II from running. '
2034                                   'Press OK to start an update if this is acceptable:',
2035                                   'Local GSAS-II Mods',
2036                                   wx.OK|wx.CANCEL)
2037            if dlg.ShowModal() != wx.ID_OK:
2038                dlg.Destroy()
2039                return
2040            else:
2041                dlg.Destroy()
2042        else:
2043            dlg = wx.MessageDialog(self.frame,
2044                                   'You have version '+local+
2045                                   ' of GSAS-II installed, but the current version is '+repos+
2046                                   '. Press OK to start an update:',
2047                                   'GSAS-II Updates',
2048                                   wx.OK|wx.CANCEL)
2049            if dlg.ShowModal() != wx.ID_OK:
2050                dlg.Destroy()
2051                return
2052            dlg.Destroy()
2053        print 'start updates'
2054        dlg = wx.MessageDialog(self.frame,
2055                               'Your project will now be saved, GSAS-II will exit and an update '
2056                               'will be performed and GSAS-II will restart. Press Cancel to '
2057                               'abort the update',
2058                               'Start update?',
2059                               wx.OK|wx.CANCEL)
2060        if dlg.ShowModal() != wx.ID_OK:
2061            dlg.Destroy()
2062            return
2063        dlg.Destroy()
2064        self.frame.OnFileSave(event)
2065        GSASIIpath.svnUpdateProcess(projectfile=self.frame.GSASprojectfile)
2066        return
2067
2068    def OnSelectVersion(self,event):
2069        '''Allow the user to select a specific version of GSAS-II
2070        '''
2071        if not GSASIIpath.whichsvn():
2072            dlg = wx.MessageDialog(self,'No Subversion','Cannot update GSAS-II because subversion (svn) '+
2073                                   'was not found.'
2074                                   ,wx.OK)
2075            dlg.ShowModal()
2076            return
2077        local = GSASIIpath.svnGetRev()
2078        if local is None: 
2079            dlg = wx.MessageDialog(self.frame,
2080                                   'Unable to run subversion on the GSAS-II current directory. Is GSAS-II installed correctly?',
2081                                   'Subversion error',
2082                                   wx.OK)
2083            dlg.ShowModal()
2084            return
2085        mods = GSASIIpath.svnFindLocalChanges()
2086        if mods:
2087            dlg = wx.MessageDialog(self.frame,
2088                                   'You have version '+local+
2089                                   ' of GSAS-II installed'
2090                                   '. However, '+str(len(mods))+
2091                                   ' file(s) on your local computer have been modified.'
2092                                   ' Downdating will attempt to merge your local changes with '
2093                                   'the selected GSAS-II version. '
2094                                   'Downdating is not encouraged because '
2095                                   'if merging is not possible, your local changes will be '
2096                                   'discarded. It is also possible that the '
2097                                   'local changes my prevent GSAS-II from running. '
2098                                   'Press OK to continue anyway.',
2099                                   'Local GSAS-II Mods',
2100                                   wx.OK|wx.CANCEL)
2101            if dlg.ShowModal() != wx.ID_OK:
2102                dlg.Destroy()
2103                return
2104            dlg.Destroy()
2105        dlg = downdate(parent=self.frame)
2106        if dlg.ShowModal() == wx.ID_OK:
2107            ver = dlg.getVersion()
2108        else:
2109            dlg.Destroy()
2110            return
2111        dlg.Destroy()
2112        print('start regress to '+str(ver))
2113        GSASIIpath.svnUpdateProcess(
2114            projectfile=self.frame.GSASprojectfile,
2115            version=str(ver)
2116            )
2117        self.frame.OnFileSave(event)
2118        return
2119
2120################################################################################
2121class AddHelp(wx.Menu):
2122    '''For the Mac: creates an entry to the help menu of type
2123    'Help on <helpType>': where helpType is a reference to an HTML page to
2124    be opened.
2125
2126    NOTE: when appending this menu (menu.Append) be sure to set the title to
2127    '&Help' so that wx handles it correctly.
2128    '''
2129    def __init__(self,frame,helpType,helpLbl=None,title=''):
2130        wx.Menu.__init__(self,title)
2131        self.frame = frame
2132        if helpLbl is None: helpLbl = helpType
2133        # add a help item only when helpType is specified
2134        helpobj = self.Append(text='Help on '+helpLbl,
2135                              id=wx.ID_ANY, kind=wx.ITEM_NORMAL)
2136        frame.Bind(wx.EVT_MENU, self.OnHelpById, helpobj)
2137        self.HelpById = helpType
2138       
2139    def OnHelpById(self,event):
2140        '''Called when Help on... is pressed in a menu. Brings up
2141        a web page for documentation.
2142        '''
2143        ShowHelp(self.HelpById,self.frame)
2144
2145################################################################################
2146class HelpButton(wx.Button):
2147    '''Create a help button that displays help information.
2148    The text is displayed in a modal message window.
2149
2150    TODO: it might be nice if it were non-modal: e.g. it stays around until
2151    the parent is deleted or the user closes it, but this did not work for
2152    me.
2153
2154    :param parent: the panel which will be the parent of the button
2155    :param str msg: the help text to be displayed
2156    '''
2157    def __init__(self,parent,msg):
2158        if sys.platform == "darwin": 
2159            wx.Button.__init__(self,parent,wx.ID_HELP)
2160        else:
2161            wx.Button.__init__(self,parent,wx.ID_ANY,'?',style=wx.BU_EXACTFIT)
2162        self.Bind(wx.EVT_BUTTON,self._onPress)
2163        self.msg=msg
2164        self.parent = parent
2165    def _onClose(self,event):
2166        self.dlg.EndModal(wx.ID_CANCEL)
2167    def _onPress(self,event):
2168        'Respond to a button press by displaying the requested text'
2169        #dlg = wx.MessageDialog(self.parent,self.msg,'Help info',wx.OK)
2170        self.dlg = wx.Dialog(self.parent,wx.ID_ANY,'Help information', 
2171                        style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
2172        #self.dlg.SetBackgroundColour(wx.WHITE)
2173        mainSizer = wx.BoxSizer(wx.VERTICAL)
2174        txt = wx.StaticText(self.dlg,wx.ID_ANY,self.msg)
2175        mainSizer.Add(txt,1,wx.ALL|wx.EXPAND,10)
2176        txt.SetBackgroundColour(wx.WHITE)
2177
2178        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
2179        btn = wx.Button(self.dlg, wx.ID_CLOSE) 
2180        btn.Bind(wx.EVT_BUTTON,self._onClose)
2181        btnsizer.Add(btn)
2182        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
2183        self.dlg.SetSizer(mainSizer)
2184        mainSizer.Fit(self.dlg)
2185        self.dlg.ShowModal()
2186        self.dlg.Destroy()
2187################################################################################
2188class MyHtmlPanel(wx.Panel):
2189    '''Defines a panel to display HTML help information, as an alternative to
2190    displaying help information in a web browser.
2191    '''
2192    def __init__(self, frame, id):
2193        self.frame = frame
2194        wx.Panel.__init__(self, frame, id)
2195        sizer = wx.BoxSizer(wx.VERTICAL)
2196        back = wx.Button(self, -1, "Back")
2197        back.Bind(wx.EVT_BUTTON, self.OnBack)
2198        self.htmlwin = G2HtmlWindow(self, id, size=(750,450))
2199        sizer.Add(self.htmlwin, 1,wx.EXPAND)
2200        sizer.Add(back, 0, wx.ALIGN_LEFT, 0)
2201        self.SetSizer(sizer)
2202        sizer.Fit(frame)       
2203        self.Bind(wx.EVT_SIZE,self.OnHelpSize)
2204    def OnHelpSize(self,event):         #does the job but weirdly!!
2205        anchor = self.htmlwin.GetOpenedAnchor()
2206        if anchor:           
2207            self.htmlwin.ScrollToAnchor(anchor)
2208            wx.CallAfter(self.htmlwin.ScrollToAnchor,anchor)
2209            event.Skip()
2210    def OnBack(self, event):
2211        self.htmlwin.HistoryBack()
2212    def LoadFile(self,file):
2213        pos = file.rfind('#')
2214        if pos != -1:
2215            helpfile = file[:pos]
2216            helpanchor = file[pos+1:]
2217        else:
2218            helpfile = file
2219            helpanchor = None
2220        self.htmlwin.LoadPage(helpfile)
2221        if helpanchor is not None:
2222            self.htmlwin.ScrollToAnchor(helpanchor)
2223            xs,ys = self.htmlwin.GetViewStart()
2224            self.htmlwin.Scroll(xs,ys-1)
2225
2226class G2HtmlWindow(wx.html.HtmlWindow):
2227    '''Displays help information in a primitive HTML browser type window
2228    '''
2229    def __init__(self, parent, *args, **kwargs):
2230        self.parent = parent
2231        wx.html.HtmlWindow.__init__(self, parent, *args, **kwargs)
2232    def LoadPage(self, *args, **kwargs):
2233        wx.html.HtmlWindow.LoadPage(self, *args, **kwargs)
2234        self.TitlePage()
2235    def OnLinkClicked(self, *args, **kwargs):
2236        wx.html.HtmlWindow.OnLinkClicked(self, *args, **kwargs)
2237        xs,ys = self.GetViewStart()
2238        self.Scroll(xs,ys-1)
2239        self.TitlePage()
2240    def HistoryBack(self, *args, **kwargs):
2241        wx.html.HtmlWindow.HistoryBack(self, *args, **kwargs)
2242        self.TitlePage()
2243    def TitlePage(self):
2244        self.parent.frame.SetTitle(self.GetOpenedPage() + ' -- ' + 
2245            self.GetOpenedPageTitle())
2246
2247################################################################################
2248class DataFrame(wx.Frame):
2249    '''Create the data item window and all the entries in menus used in
2250    that window. For Linux and windows, the menu entries are created for the
2251    current data item window, but in the Mac the menu is accessed from all
2252    windows. This means that a different menu is posted depending on which
2253    data item is posted. On the Mac, all the menus contain the data tree menu
2254    items, but additional menus are added specific to the data item.
2255
2256    Note that while the menus are created here,
2257    the binding for the menus is done later in various GSASII*GUI modules,
2258    where the functions to be called are defined.
2259    '''
2260    def Bind(self,*args,**kwargs):
2261        '''Override the Bind() function: on the Mac the binding is to
2262        the main window, so that menus operate with any window on top.
2263        For other platforms, call the default wx.Frame Bind()
2264        '''
2265        if sys.platform == "darwin": # mac
2266            self.G2frame.Bind(*args,**kwargs)
2267        else:
2268            wx.Frame.Bind(self,*args,**kwargs)     
2269       
2270    def PrefillDataMenu(self,menu,helpType,helpLbl=None,empty=False):
2271        '''Create the "standard" part of data frame menus. Note that on Linux and
2272        Windows nothing happens here. On Mac, this menu duplicates the
2273        tree menu, but adds an extra help command for the data item and a separator.
2274        '''
2275        self.datamenu = menu
2276        self.helpType = helpType
2277        self.helpLbl = helpLbl
2278        if sys.platform == "darwin": # mac                         
2279            self.G2frame.FillMainMenu(menu) # add the data tree menu items
2280            if not empty:
2281                menu.Append(wx.Menu(title=''),title='|') # add a separator
2282       
2283    def PostfillDataMenu(self,empty=False):
2284        '''Create the "standard" part of data frame menus. Note that on Linux and
2285        Windows, this is the standard help Menu. On Mac, this menu duplicates the
2286        tree menu, but adds an extra help command for the data item and a separator.
2287        '''
2288        menu = self.datamenu
2289        helpType = self.helpType
2290        helpLbl = self.helpLbl
2291        if sys.platform == "darwin": # mac
2292            if not empty:
2293                menu.Append(wx.Menu(title=''),title='|') # add another separator
2294            menu.Append(AddHelp(self.G2frame,helpType=helpType, helpLbl=helpLbl),
2295                        title='&Help')
2296        else: # other
2297            menu.Append(menu=MyHelp(self,helpType=helpType, helpLbl=helpLbl),
2298                        title='&Help')
2299
2300    def _init_menus(self):
2301        'define all GSAS-II data frame menus'
2302
2303        # for use where no menu or data frame help is provided
2304        self.BlankMenu = wx.MenuBar()
2305       
2306        # Controls
2307        self.ControlsMenu = wx.MenuBar()
2308        self.PrefillDataMenu(self.ControlsMenu,helpType='Controls',empty=True)
2309        self.PostfillDataMenu(empty=True)
2310       
2311        # Notebook
2312        self.DataNotebookMenu = wx.MenuBar() 
2313        self.PrefillDataMenu(self.DataNotebookMenu,helpType='Notebook',empty=True)
2314        self.PostfillDataMenu(empty=True)
2315       
2316        # Comments
2317        self.DataCommentsMenu = wx.MenuBar()
2318        self.PrefillDataMenu(self.DataCommentsMenu,helpType='Comments',empty=True)
2319        self.PostfillDataMenu(empty=True)
2320       
2321        # Constraints
2322        self.ConstraintMenu = wx.MenuBar()
2323        self.PrefillDataMenu(self.ConstraintMenu,helpType='Constraints')
2324        self.ConstraintTab = wx.Menu(title='')
2325        self.ConstraintMenu.Append(menu=self.ConstraintTab, title='Select tab')
2326        for id,txt in (
2327            (wxID_CONSPHASE,'Phase'),
2328            (wxID_CONSHAP,'Histogram/Phase'),
2329            (wxID_CONSHIST,'Histogram'),
2330            (wxID_CONSGLOBAL,'Global')):
2331            self.ConstraintTab.Append(
2332                id=id, kind=wx.ITEM_NORMAL,text=txt,
2333                help='Select '+txt+' constraint editing tab')
2334        self.ConstraintEdit = wx.Menu(title='')
2335        self.ConstraintMenu.Append(menu=self.ConstraintEdit, title='Edit')
2336        self.ConstraintEdit.Append(id=wxID_HOLDADD, kind=wx.ITEM_NORMAL,text='Add hold',
2337            help='Add hold on a parameter value')
2338        self.ConstraintEdit.Append(id=wxID_EQUIVADD, kind=wx.ITEM_NORMAL,text='Add equivalence',
2339            help='Add equivalence between parameter values')
2340        self.ConstraintEdit.Append(id=wxID_CONSTRAINTADD, kind=wx.ITEM_NORMAL,text='Add constraint',
2341            help='Add constraint on parameter values')
2342        self.ConstraintEdit.Append(id=wxID_FUNCTADD, kind=wx.ITEM_NORMAL,text='Add New Var',
2343            help='Add variable composed of existing parameter')
2344
2345        # item = self.ConstraintEdit.Append(id=wx.ID_ANY,kind=wx.ITEM_NORMAL,text='Update GUI')
2346        # def UpdateGSASIIconstrGUI(event):
2347        #     import GSASIIconstrGUI
2348        #     reload(GSASIIconstrGUI)
2349        #     import GSASIIobj
2350        #     reload(GSASIIobj)
2351        # self.Bind(wx.EVT_MENU,UpdateGSASIIconstrGUI,id=item.GetId())
2352
2353        self.PostfillDataMenu()
2354       
2355        # Rigid bodies
2356        self.VectorRBEdit = wx.Menu(title='')
2357        self.VectorRBEdit.Append(id=wxID_RIGIDBODYADD, kind=wx.ITEM_NORMAL,text='Add rigid body',
2358            help='Add vector rigid body')
2359        self.ResidueRBMenu = wx.Menu(title='')
2360        self.ResidueRBMenu.Append(id=wxID_RIGIDBODYIMPORT, kind=wx.ITEM_NORMAL,text='Import XYZ',
2361            help='Import rigid body XYZ from file')
2362        self.ResidueRBMenu.Append(id=wxID_RESIDUETORSSEQ, kind=wx.ITEM_NORMAL,text='Define sequence',
2363            help='Define torsion sequence')
2364        self.ResidueRBMenu.Append(id=wxID_RIGIDBODYADD, kind=wx.ITEM_NORMAL,text='Import residues',
2365            help='Import residue rigid bodies from macro file')
2366           
2367        self.RigidBodyMenu = wx.MenuBar()
2368        self.PrefillDataMenu(self.RigidBodyMenu,helpType='Rigid bodies')
2369        self.RigidBodyMenu.Append(menu=self.VectorRBEdit, title='Edit')       
2370        self.PostfillDataMenu()
2371           
2372        # Restraints
2373        self.RestraintEdit = wx.Menu(title='')
2374        self.RestraintEdit.Append(id=wxID_RESTSELPHASE, kind=wx.ITEM_NORMAL,text='Select phase',
2375            help='Select phase')
2376        self.RestraintEdit.Append(id=wxID_RESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add restraints',
2377            help='Add restraints')
2378        self.RestraintEdit.Enable(wxID_RESTRAINTADD,True)    #gets disenabled if macromolecule phase
2379        self.RestraintEdit.Append(id=wxID_AARESTRAINTADD, kind=wx.ITEM_NORMAL,text='Add residue restraints',
2380            help='Add residue based restraints for macromolecules from macro file')
2381        self.RestraintEdit.Enable(wxID_AARESTRAINTADD,False)    #gets enabled if macromolecule phase
2382        self.RestraintEdit.Append(id=wxID_AARESTRAINTPLOT, kind=wx.ITEM_NORMAL,text='Plot residue restraints',
2383            help='Plot selected residue based restraints for macromolecules from macro file')
2384        self.RestraintEdit.Enable(wxID_AARESTRAINTPLOT,False)    #gets enabled if macromolecule phase
2385        self.RestraintEdit.Append(id=wxID_RESRCHANGEVAL, kind=wx.ITEM_NORMAL,text='Change value',
2386            help='Change observed value')
2387        self.RestraintEdit.Append(id=wxID_RESTCHANGEESD, kind=wx.ITEM_NORMAL,text='Change esd',
2388            help='Change esd in observed value')
2389        self.RestraintEdit.Append(id=wxID_RESTDELETE, kind=wx.ITEM_NORMAL,text='Delete restraints',
2390            help='Delete selected restraints')
2391
2392        self.RestraintMenu = wx.MenuBar()
2393        self.PrefillDataMenu(self.RestraintMenu,helpType='Restraints')
2394        self.RestraintMenu.Append(menu=self.RestraintEdit, title='Edit')
2395        self.PostfillDataMenu()
2396           
2397        # Sequential results
2398        self.SequentialMenu = wx.MenuBar()
2399        self.PrefillDataMenu(self.SequentialMenu,helpType='Sequential',helpLbl='Sequential Refinement')
2400        self.SequentialFile = wx.Menu(title='')
2401        self.SequentialMenu.Append(menu=self.SequentialFile, title='File')
2402        self.SequentialFile.Append(id=wxID_SAVESEQSEL, kind=wx.ITEM_NORMAL,text='Save...',
2403            help='Save selected sequential refinement results')
2404        self.PostfillDataMenu()
2405           
2406        # PDR
2407        self.ErrorMenu = wx.MenuBar()
2408        self.PrefillDataMenu(self.ErrorMenu,helpType='PWD Analysis',helpLbl='Powder Fit Error Analysis')
2409        self.ErrorAnal = wx.Menu(title='')
2410        self.ErrorMenu.Append(menu=self.ErrorAnal,title='Analysis')
2411        self.ErrorAnal.Append(id=wxID_PWDANALYSIS,kind=wx.ITEM_NORMAL,text='Analyze',
2412            help='Error analysis on powder pattern')
2413        self.PostfillDataMenu()
2414           
2415        # PDR / Limits
2416        self.LimitMenu = wx.MenuBar()
2417        self.PrefillDataMenu(self.LimitMenu,helpType='Limits')
2418        self.LimitEdit = wx.Menu(title='')
2419        self.LimitMenu.Append(menu=self.LimitEdit, title='Edit')
2420        self.LimitEdit.Append(id=wxID_LIMITCOPY, kind=wx.ITEM_NORMAL,text='Copy',
2421            help='Copy limits to other histograms')
2422        self.LimitEdit.Append(id=wxID_ADDEXCLREGION, kind=wx.ITEM_NORMAL,text='Add exclude',
2423            help='Add excluded region - select a point on plot; drag to adjust')           
2424        self.PostfillDataMenu()
2425           
2426        # PDR / Background
2427        self.BackMenu = wx.MenuBar()
2428        self.PrefillDataMenu(self.BackMenu,helpType='Background')
2429        self.BackEdit = wx.Menu(title='')
2430        self.BackMenu.Append(menu=self.BackEdit, title='File')
2431        self.BackEdit.Append(id=wxID_BACKCOPY, kind=wx.ITEM_NORMAL,text='Copy',
2432            help='Copy background parameters to other histograms')
2433        self.BackEdit.Append(id=wxID_BACKFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
2434            help='Copy background refinement flags to other histograms')
2435        self.PostfillDataMenu()
2436           
2437        # PDR / Instrument Parameters
2438        self.InstMenu = wx.MenuBar()
2439        self.PrefillDataMenu(self.InstMenu,helpType='Instrument Parameters')
2440        self.InstEdit = wx.Menu(title='')
2441        self.InstMenu.Append(menu=self.InstEdit, title='Operations')
2442        self.InstEdit.Append(help='Reset instrument profile parameters to default', 
2443            id=wxID_INSTLOAD, kind=wx.ITEM_NORMAL,text='Load profile...')
2444        self.InstEdit.Append(help='Load instrument profile parameters from file', 
2445            id=wxID_INSTSAVE, kind=wx.ITEM_NORMAL,text='Save profile...')
2446        self.InstEdit.Append(help='Save instrument profile parameters to file', 
2447            id=wxID_INSTPRMRESET, kind=wx.ITEM_NORMAL,text='Reset profile')
2448        self.InstEdit.Append(help='Copy instrument profile parameters to other histograms', 
2449            id=wxID_INSTCOPY, kind=wx.ITEM_NORMAL,text='Copy')
2450        self.InstEdit.Append(id=wxID_INSTFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
2451            help='Copy instrument parameter refinement flags to other histograms')
2452#        self.InstEdit.Append(help='Change radiation type (Ka12 - synch)',
2453#            id=wxID_CHANGEWAVETYPE, kind=wx.ITEM_NORMAL,text='Change radiation')
2454        self.PostfillDataMenu()
2455       
2456        # PDR / Sample Parameters
2457        self.SampleMenu = wx.MenuBar()
2458        self.PrefillDataMenu(self.SampleMenu,helpType='Sample Parameters')
2459        self.SampleEdit = wx.Menu(title='')
2460        self.SampleMenu.Append(menu=self.SampleEdit, title='Command')
2461        self.SampleEdit.Append(id=wxID_SAMPLELOAD, kind=wx.ITEM_NORMAL,text='Load',
2462            help='Load sample parameters from file')
2463        self.SampleEdit.Append(id=wxID_SAMPLESAVE, kind=wx.ITEM_NORMAL,text='Save',
2464            help='Save sample parameters to file')
2465        self.SampleEdit.Append(id=wxID_SAMPLECOPY, kind=wx.ITEM_NORMAL,text='Copy',
2466            help='Copy refinable sample parameters to other histograms')
2467        self.SampleEdit.Append(id=wxID_SAMPLEFLAGCOPY, kind=wx.ITEM_NORMAL,text='Copy flags',
2468            help='Copy sample parameter refinement flags to other histograms')
2469        self.PostfillDataMenu()
2470
2471        # PDR / Peak List
2472        self.PeakMenu = wx.MenuBar()
2473        self.PrefillDataMenu(self.PeakMenu,helpType='Peak List')
2474        self.PeakEdit = wx.Menu(title='')
2475        self.PeakMenu.Append(menu=self.PeakEdit, title='Peak Fitting')
2476        self.AutoSearch = self.PeakEdit.Append(help='Automatic peak search', 
2477            id=wxID_AUTOSEARCH, kind=wx.ITEM_NORMAL,text='Auto search')
2478        self.UnDo = self.PeakEdit.Append(help='Undo last least squares refinement', 
2479            id=wxID_UNDO, kind=wx.ITEM_NORMAL,text='UnDo')
2480        self.PeakFit = self.PeakEdit.Append(id=wxID_LSQPEAKFIT, kind=wx.ITEM_NORMAL,text='LSQ PeakFit', 
2481            help='Peak fitting via least-squares' )
2482        self.PFOneCycle = self.PeakEdit.Append(id=wxID_LSQONECYCLE, kind=wx.ITEM_NORMAL,text='LSQ one cycle', 
2483            help='One cycle of Peak fitting via least-squares' )
2484        self.PeakEdit.Append(id=wxID_RESETSIGGAM, kind=wx.ITEM_NORMAL, 
2485            text='Reset sig and gam',help='Reset sigma and gamma to global fit' )
2486        self.PeakEdit.Append(id=wxID_CLEARPEAKS, kind=wx.ITEM_NORMAL,text='Clear peaks', 
2487            help='Clear the peak list' )
2488        self.PostfillDataMenu()
2489        self.UnDo.Enable(False)
2490        self.PeakFit.Enable(False)
2491        self.PFOneCycle.Enable(False)
2492       
2493        # PDR / Index Peak List
2494        self.IndPeaksMenu = wx.MenuBar()
2495        self.PrefillDataMenu(self.IndPeaksMenu,helpType='Index Peak List')
2496        self.IndPeaksEdit = wx.Menu(title='')
2497        self.IndPeaksMenu.Append(menu=self.IndPeaksEdit,title='Operations')
2498        self.IndPeaksEdit.Append(help='Load/Reload index peaks from peak list',id=wxID_INDXRELOAD, 
2499            kind=wx.ITEM_NORMAL,text='Load/Reload')
2500        self.PostfillDataMenu()
2501       
2502        # PDR / Unit Cells List
2503        self.IndexMenu = wx.MenuBar()
2504        self.PrefillDataMenu(self.IndexMenu,helpType='Unit Cells List')
2505        self.IndexEdit = wx.Menu(title='')
2506        self.IndexMenu.Append(menu=self.IndexEdit, title='Cell Index/Refine')
2507        self.IndexPeaks = self.IndexEdit.Append(help='', id=wxID_INDEXPEAKS, kind=wx.ITEM_NORMAL,
2508            text='Index Cell')
2509        self.CopyCell = self.IndexEdit.Append( id=wxID_COPYCELL, kind=wx.ITEM_NORMAL,text='Copy Cell', 
2510            help='Copy selected unit cell from indexing to cell refinement fields')
2511        self.RefineCell = self.IndexEdit.Append( id=wxID_REFINECELL, kind=wx.ITEM_NORMAL, 
2512            text='Refine Cell',help='Refine unit cell parameters from indexed peaks')
2513        self.MakeNewPhase = self.IndexEdit.Append( id=wxID_MAKENEWPHASE, kind=wx.ITEM_NORMAL,
2514            text='Make new phase',help='Make new phase from selected unit cell')
2515        self.PostfillDataMenu()
2516        self.IndexPeaks.Enable(False)
2517        self.CopyCell.Enable(False)
2518        self.RefineCell.Enable(False)
2519        self.MakeNewPhase.Enable(False)
2520       
2521        # PDR / Reflection Lists
2522        self.ReflMenu = wx.MenuBar()
2523        self.PrefillDataMenu(self.ReflMenu,helpType='Reflection List')
2524        self.ReflEdit = wx.Menu(title='')
2525        self.ReflMenu.Append(menu=self.ReflEdit, title='Reflection List')
2526        self.SelectPhase = self.ReflEdit.Append(help='Select phase for reflection list',id=wxID_SELECTPHASE, 
2527            kind=wx.ITEM_NORMAL,text='Select phase')
2528        self.PostfillDataMenu()
2529       
2530        #SASD & REFL/ Substance editor
2531        self.SubstanceMenu = wx.MenuBar()
2532        self.PrefillDataMenu(self.SubstanceMenu,helpType='Substances')
2533        self.SubstanceEdit = wx.Menu(title='')
2534        self.SubstanceMenu.Append(menu=self.SubstanceEdit, title='Edit')
2535        self.SubstanceEdit.Append(id=wxID_LOADSUBSTANCE, kind=wx.ITEM_NORMAL,text='Load substance',
2536            help='Load substance from file')
2537        self.SubstanceEdit.Append(id=wxID_ADDSUBSTANCE, kind=wx.ITEM_NORMAL,text='Add substance',
2538            help='Add new substance to list')
2539        self.SubstanceEdit.Append(id=wxID_DELETESUBSTANCE, kind=wx.ITEM_NORMAL,text='Delete substance',
2540            help='Delete substance from list')           
2541        self.SubstanceEdit.Append(id=wxID_ELEMENTADD, kind=wx.ITEM_NORMAL,text='Add elements',
2542            help='Add elements to substance')
2543        self.SubstanceEdit.Append(id=wxID_ELEMENTDELETE, kind=wx.ITEM_NORMAL,text='Delete elements',
2544            help='Delete elements from substance')
2545        self.PostfillDataMenu()
2546       
2547        # SASD/ Models
2548        self.ModelMenu = wx.MenuBar()
2549        self.PrefillDataMenu(self.ModelMenu,helpType='Models')
2550        self.ModelEdit = wx.Menu(title='')
2551        self.ModelMenu.Append(menu=self.ModelEdit, title='Models')
2552        self.ModelEdit.Append(id=wxID_MODELFIT, kind=wx.ITEM_NORMAL,text='Fit',
2553            help='Fit model parameters to data')
2554        self.ModelEdit.Append(id=wxID_MODELCOPY, kind=wx.ITEM_NORMAL,text='Copy',
2555            help='Copy model parameters to other histograms')
2556        self.PostfillDataMenu()
2557       
2558        # IMG / Image Controls
2559        self.ImageMenu = wx.MenuBar()
2560        self.PrefillDataMenu(self.ImageMenu,helpType='Image Controls')
2561        self.ImageEdit = wx.Menu(title='')
2562        self.ImageMenu.Append(menu=self.ImageEdit, title='Operations')
2563        self.ImageEdit.Append(help='Calibrate detector by fitting to calibrant lines', 
2564            id=wxID_IMCALIBRATE, kind=wx.ITEM_NORMAL,text='Calibrate')
2565        self.ImageEdit.Append(help='Recalibrate detector by fitting to calibrant lines', 
2566            id=wxID_IMRECALIBRATE, kind=wx.ITEM_NORMAL,text='Recalibrate')
2567        self.ImageEdit.Append(help='Clear calibration data points and rings',id=wxID_IMCLEARCALIB, 
2568            kind=wx.ITEM_NORMAL,text='Clear calibration')
2569        self.ImageEdit.Append(help='Integrate selected image',id=wxID_IMINTEGRATE, 
2570            kind=wx.ITEM_NORMAL,text='Integrate')
2571        self.ImageEdit.Append(help='Integrate all images selected from list',id=wxID_INTEGRATEALL,
2572            kind=wx.ITEM_NORMAL,text='Integrate all')
2573        self.ImageEdit.Append(help='Copy image controls to other images', 
2574            id=wxID_IMCOPYCONTROLS, kind=wx.ITEM_NORMAL,text='Copy Controls')
2575        self.ImageEdit.Append(help='Save image controls to file', 
2576            id=wxID_IMSAVECONTROLS, kind=wx.ITEM_NORMAL,text='Save Controls')
2577        self.ImageEdit.Append(help='Load image controls from file', 
2578            id=wxID_IMLOADCONTROLS, kind=wx.ITEM_NORMAL,text='Load Controls')
2579        self.PostfillDataMenu()
2580           
2581        # IMG / Masks
2582        self.MaskMenu = wx.MenuBar()
2583        self.PrefillDataMenu(self.MaskMenu,helpType='Image Masks')
2584        self.MaskEdit = wx.Menu(title='')
2585        self.MaskMenu.Append(menu=self.MaskEdit, title='Operations')
2586        submenu = wx.Menu()
2587        self.MaskEdit.AppendMenu(
2588            wx.ID_ANY,'Create new', submenu,
2589            help=''
2590            )
2591        self.MaskEdit.Append(help='Copy mask to other images', 
2592            id=wxID_MASKCOPY, kind=wx.ITEM_NORMAL,text='Copy mask')
2593        self.MaskEdit.Append(help='Save mask to file', 
2594            id=wxID_MASKSAVE, kind=wx.ITEM_NORMAL,text='Save mask')
2595        self.MaskEdit.Append(help='Load mask from file', 
2596            id=wxID_MASKLOAD, kind=wx.ITEM_NORMAL,text='Load mask')
2597        submenu.Append(help='Create an arc mask with mouse input', 
2598            id=wxID_NEWMASKARC, kind=wx.ITEM_NORMAL,text='Arc mask')
2599        submenu.Append(help='Create a frame mask with mouse input', 
2600            id=wxID_NEWMASKFRAME, kind=wx.ITEM_NORMAL,text='Frame mask')
2601        submenu.Append(help='Create a polygon mask with mouse input', 
2602            id=wxID_NEWMASKPOLY, kind=wx.ITEM_NORMAL,text='Polygon mask')
2603        submenu.Append(help='Create a ring mask with mouse input', 
2604            id=wxID_NEWMASKRING, kind=wx.ITEM_NORMAL,text='Ring mask')
2605        submenu.Append(help='Create a spot mask with mouse input', 
2606            id=wxID_NEWMASKSPOT, kind=wx.ITEM_NORMAL,text='Spot mask')
2607        self.PostfillDataMenu()
2608           
2609        # IMG / Stress/Strain
2610        self.StrStaMenu = wx.MenuBar()
2611        self.PrefillDataMenu(self.StrStaMenu,helpType='Stress/Strain')
2612        self.StrStaEdit = wx.Menu(title='')
2613        self.StrStaMenu.Append(menu=self.StrStaEdit, title='Operations')
2614        self.StrStaEdit.Append(help='Append d-zero for one ring', 
2615            id=wxID_APPENDDZERO, kind=wx.ITEM_NORMAL,text='Append d-zero')
2616        self.StrStaEdit.Append(help='Fit stress/strain data', 
2617            id=wxID_STRSTAFIT, kind=wx.ITEM_NORMAL,text='Fit stress/strain')
2618        self.StrStaEdit.Append(help='Update d-zero from ave d-zero',
2619            id=wxID_UPDATEDZERO, kind=wx.ITEM_NORMAL,text='Update d-zero')       
2620        self.StrStaEdit.Append(help='Fit stress/strain data for all images', 
2621            id=wxID_STRSTAALLFIT, kind=wx.ITEM_NORMAL,text='All image fit')
2622        self.StrStaEdit.Append(help='Copy stress/strain data to other images', 
2623            id=wxID_STRSTACOPY, kind=wx.ITEM_NORMAL,text='Copy stress/strain')
2624        self.StrStaEdit.Append(help='Save stress/strain data to file', 
2625            id=wxID_STRSTASAVE, kind=wx.ITEM_NORMAL,text='Save stress/strain')
2626        self.StrStaEdit.Append(help='Load stress/strain data from file', 
2627            id=wxID_STRSTALOAD, kind=wx.ITEM_NORMAL,text='Load stress/strain')
2628        self.PostfillDataMenu()
2629           
2630        # PDF / PDF Controls
2631        self.PDFMenu = wx.MenuBar()
2632        self.PrefillDataMenu(self.PDFMenu,helpType='PDF Controls')
2633        self.PDFEdit = wx.Menu(title='')
2634        self.PDFMenu.Append(menu=self.PDFEdit, title='PDF Controls')
2635        self.PDFEdit.Append(help='Add element to sample composition',id=wxID_PDFADDELEMENT, kind=wx.ITEM_NORMAL,
2636            text='Add element')
2637        self.PDFEdit.Append(help='Delete element from sample composition',id=wxID_PDFDELELEMENT, kind=wx.ITEM_NORMAL,
2638            text='Delete element')
2639        self.PDFEdit.Append(help='Copy PDF controls', id=wxID_PDFCOPYCONTROLS, kind=wx.ITEM_NORMAL,
2640            text='Copy controls')
2641        #        self.PDFEdit.Append(help='Load PDF controls from file',id=wxID_PDFLOADCONTROLS, kind=wx.ITEM_NORMAL,
2642        #            text='Load Controls')
2643        #        self.PDFEdit.Append(help='Save PDF controls to file', id=wxID_PDFSAVECONTROLS, kind=wx.ITEM_NORMAL,
2644        #            text='Save controls')
2645        self.PDFEdit.Append(help='Compute PDF', id=wxID_PDFCOMPUTE, kind=wx.ITEM_NORMAL,
2646            text='Compute PDF')
2647        self.PDFEdit.Append(help='Compute all PDFs', id=wxID_PDFCOMPUTEALL, kind=wx.ITEM_NORMAL,
2648            text='Compute all PDFs')
2649        self.PostfillDataMenu()
2650       
2651        # Phase / General tab
2652        self.DataGeneral = wx.MenuBar()
2653        self.PrefillDataMenu(self.DataGeneral,helpType='General', helpLbl='Phase/General')
2654        self.DataGeneral.Append(menu=wx.Menu(title=''),title='Select tab')
2655        self.GeneralCalc = wx.Menu(title='')
2656        self.DataGeneral.Append(menu=self.GeneralCalc,title='Compute')
2657        self.GeneralCalc.Append(help='Compute Fourier map',id=wxID_FOURCALC, kind=wx.ITEM_NORMAL,
2658            text='Fourier map')
2659        self.GeneralCalc.Append(help='Search Fourier map',id=wxID_FOURSEARCH, kind=wx.ITEM_NORMAL,
2660            text='Search map')
2661        self.GeneralCalc.Append(help='Run charge flipping',id=wxID_CHARGEFLIP, kind=wx.ITEM_NORMAL,
2662            text='Charge flipping')
2663        self.GeneralCalc.Append(help='Clear map',id=wxID_FOURCLEAR, kind=wx.ITEM_NORMAL,
2664            text='Clear map')
2665        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing',id=wxID_SINGLEMCSA, kind=wx.ITEM_NORMAL,
2666            text='MC/SA')
2667        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing on multiprocessors',id=wxID_MULTIMCSA, kind=wx.ITEM_NORMAL,
2668            text='Multi MC/SA')            #currently not useful
2669        self.PostfillDataMenu()
2670       
2671        # Phase / Data tab
2672        self.DataMenu = wx.MenuBar()
2673        self.PrefillDataMenu(self.DataMenu,helpType='Data', helpLbl='Phase/Data')
2674        self.DataMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2675        self.DataEdit = wx.Menu(title='')
2676        self.DataMenu.Append(menu=self.DataEdit, title='Edit')
2677        self.DataEdit.Append(id=wxID_PWDRADD, kind=wx.ITEM_NORMAL,text='Add powder histograms',
2678            help='Select new powder histograms to be used for this phase')
2679        self.DataEdit.Append(id=wxID_HKLFADD, kind=wx.ITEM_NORMAL,text='Add single crystal histograms',
2680            help='Select new single crystal histograms to be used for this phase')
2681        self.DataEdit.Append(id=wxID_DATADELETE, kind=wx.ITEM_NORMAL,text='Remove histograms',
2682            help='Remove histograms from use for this phase')
2683        self.PostfillDataMenu()
2684           
2685        # Phase / Atoms tab
2686        self.AtomsMenu = wx.MenuBar()
2687        self.PrefillDataMenu(self.AtomsMenu,helpType='Atoms')
2688        self.AtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2689        self.AtomEdit = wx.Menu(title='')
2690        self.AtomCompute = wx.Menu(title='')
2691        self.AtomsMenu.Append(menu=self.AtomEdit, title='Edit')
2692        self.AtomsMenu.Append(menu=self.AtomCompute, title='Compute')
2693        self.AtomEdit.Append(id=wxID_ATOMSEDITADD, kind=wx.ITEM_NORMAL,text='Append atom',
2694            help='Appended as an H atom')
2695        self.AtomEdit.Append(id=wxID_ATOMSVIEWADD, kind=wx.ITEM_NORMAL,text='Append view point',
2696            help='Appended as an H atom')
2697        self.AtomEdit.Append(id=wxID_ATOMSEDITINSERT, kind=wx.ITEM_NORMAL,text='Insert atom',
2698            help='Select atom row to insert before; inserted as an H atom')
2699        self.AtomEdit.Append(id=wxID_ATOMVIEWINSERT, kind=wx.ITEM_NORMAL,text='Insert view point',
2700            help='Select atom row to insert before; inserted as an H atom')
2701        self.AtomEdit.Append(id=wxID_ATOMMOVE, kind=wx.ITEM_NORMAL,text='Move atom to view point',
2702            help='Select single atom to move')
2703        self.AtomEdit.Append(id=wxID_ATOMSEDITDELETE, kind=wx.ITEM_NORMAL,text='Delete atom',
2704            help='Select atoms to delete first')
2705        self.AtomEdit.Append(id=wxID_ATOMSREFINE, kind=wx.ITEM_NORMAL,text='Set atom refinement flags',
2706            help='Select atoms to refine first')
2707        self.AtomEdit.Append(id=wxID_ATOMSMODIFY, kind=wx.ITEM_NORMAL,text='Modify atom parameters',
2708            help='Select atoms to modify first')
2709        self.AtomEdit.Append(id=wxID_ATOMSTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
2710            help='Select atoms to transform first')
2711        self.AtomEdit.Append(id=wxID_RELOADDRAWATOMS, kind=wx.ITEM_NORMAL,text='Reload draw atoms',
2712            help='Reload atom drawing list')
2713        submenu = wx.Menu()
2714        self.AtomEdit.AppendMenu(wx.ID_ANY, 'Reimport atoms', submenu, 
2715            help='Reimport atoms from file; sequence must match')
2716        # setup a cascade menu for the formats that have been defined
2717        self.ReImportMenuId = {}  # points to readers for each menu entry
2718        for reader in self.G2frame.ImportPhaseReaderlist:
2719            item = submenu.Append(
2720                wx.ID_ANY,help=reader.longFormatName,
2721                kind=wx.ITEM_NORMAL,text='reimport coordinates from '+reader.formatName+' file')
2722            self.ReImportMenuId[item.GetId()] = reader
2723        item = submenu.Append(
2724            wx.ID_ANY,
2725            help='Reimport coordinates, try to determine format from file',
2726            kind=wx.ITEM_NORMAL,
2727            text='guess format from file')
2728        self.ReImportMenuId[item.GetId()] = None # try all readers
2729
2730        self.AtomCompute.Append(id=wxID_ATOMSDISAGL, kind=wx.ITEM_NORMAL,text='Show Distances && Angles',
2731            help='Compute distances & angles for selected atoms')
2732        self.AtomCompute.Append(id=wxID_ATOMSPDISAGL, kind=wx.ITEM_NORMAL,text='Save Distances && Angles',
2733            help='Compute distances & angles for selected atoms')
2734        self.AtomCompute.ISOcalc = self.AtomCompute.Append(
2735            id=wxID_ISODISP, kind=wx.ITEM_NORMAL,
2736            text='Compute ISODISTORT mode values',
2737            help='Compute values of ISODISTORT modes from atom parameters')
2738        self.PostfillDataMenu()
2739                 
2740        # Phase / Draw Options tab
2741        self.DataDrawOptions = wx.MenuBar()
2742        self.PrefillDataMenu(self.DataDrawOptions,helpType='Draw Options', helpLbl='Phase/Draw Options')
2743        self.DataDrawOptions.Append(menu=wx.Menu(title=''),title='Select tab')
2744        self.PostfillDataMenu()
2745       
2746        # Phase / Draw Atoms tab
2747        self.DrawAtomsMenu = wx.MenuBar()
2748        self.PrefillDataMenu(self.DrawAtomsMenu,helpType='Draw Atoms')
2749        self.DrawAtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2750        self.DrawAtomEdit = wx.Menu(title='')
2751        self.DrawAtomCompute = wx.Menu(title='')
2752        self.DrawAtomRestraint = wx.Menu(title='')
2753        self.DrawAtomRigidBody = wx.Menu(title='')
2754        self.DrawAtomsMenu.Append(menu=self.DrawAtomEdit, title='Edit')
2755        self.DrawAtomsMenu.Append(menu=self.DrawAtomCompute,title='Compute')
2756        self.DrawAtomsMenu.Append(menu=self.DrawAtomRestraint, title='Restraints')
2757        self.DrawAtomsMenu.Append(menu=self.DrawAtomRigidBody, title='Rigid body')
2758        self.DrawAtomEdit.Append(id=wxID_DRAWATOMSTYLE, kind=wx.ITEM_NORMAL,text='Atom style',
2759            help='Select atoms first')
2760        self.DrawAtomEdit.Append(id=wxID_DRAWATOMLABEL, kind=wx.ITEM_NORMAL,text='Atom label',
2761            help='Select atoms first')
2762        self.DrawAtomEdit.Append(id=wxID_DRAWATOMCOLOR, kind=wx.ITEM_NORMAL,text='Atom color',
2763            help='Select atoms first')
2764        self.DrawAtomEdit.Append(id=wxID_DRAWATOMRESETCOLOR, kind=wx.ITEM_NORMAL,text='Reset atom colors',
2765            help='Resets all atom colors to defaults')
2766        self.DrawAtomEdit.Append(id=wxID_DRAWVIEWPOINT, kind=wx.ITEM_NORMAL,text='View point',
2767            help='View point is 1st atom selected')
2768        self.DrawAtomEdit.Append(id=wxID_DRAWADDEQUIV, kind=wx.ITEM_NORMAL,text='Add atoms',
2769            help='Add symmetry & cell equivalents to drawing set from selected atoms')
2770        self.DrawAtomEdit.Append(id=wxID_DRAWTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
2771            help='Transform selected atoms by symmetry & cell translations')
2772        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCOORD, kind=wx.ITEM_NORMAL,text='Fill CN-sphere',
2773            help='Fill coordination sphere for selected atoms')           
2774        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCELL, kind=wx.ITEM_NORMAL,text='Fill unit cell',
2775            help='Fill unit cell with selected atoms')
2776        self.DrawAtomEdit.Append(id=wxID_DRAWDELETE, kind=wx.ITEM_NORMAL,text='Delete atoms',
2777            help='Delete atoms from drawing set')
2778        self.DrawAtomCompute.Append(id=wxID_DRAWDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2779            help='Compute distance of selected atoms from view point')   
2780        self.DrawAtomCompute.Append(id=wxID_DRAWDISAGLTOR, kind=wx.ITEM_NORMAL,text='Dist. Ang. Tors.',
2781            help='Compute distance, angle or torsion for 2-4 selected atoms')   
2782        self.DrawAtomCompute.Append(id=wxID_DRAWPLANE, kind=wx.ITEM_NORMAL,text='Best plane',
2783            help='Compute best plane for 4+ selected atoms')   
2784        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRBOND, kind=wx.ITEM_NORMAL,text='Add bond restraint',
2785            help='Add bond restraint for selected atoms (2)')
2786        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRANGLE, kind=wx.ITEM_NORMAL,text='Add angle restraint',
2787            help='Add angle restraint for selected atoms (3: one end 1st)')
2788        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRPLANE, kind=wx.ITEM_NORMAL,text='Add plane restraint',
2789            help='Add plane restraint for selected atoms (4+)')
2790        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRCHIRAL, kind=wx.ITEM_NORMAL,text='Add chiral restraint',
2791            help='Add chiral restraint for selected atoms (4: center atom 1st)')
2792        self.DrawAtomRigidBody.Append(id=wxID_DRAWDEFINERB, kind=wx.ITEM_NORMAL,text='Define rigid body',
2793            help='Define rigid body with selected atoms')
2794        self.PostfillDataMenu()
2795
2796        # Phase / MCSA tab
2797        self.MCSAMenu = wx.MenuBar()
2798        self.PrefillDataMenu(self.MCSAMenu,helpType='MC/SA')
2799        self.MCSAMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2800        self.MCSAEdit = wx.Menu(title='')
2801        self.MCSAMenu.Append(menu=self.MCSAEdit, title='MC/SA')
2802        self.MCSAEdit.Append(id=wxID_ADDMCSAATOM, kind=wx.ITEM_NORMAL,text='Add atom', 
2803            help='Add single atom to MC/SA model')
2804        self.MCSAEdit.Append(id=wxID_ADDMCSARB, kind=wx.ITEM_NORMAL,text='Add rigid body', 
2805            help='Add rigid body to MC/SA model' )
2806        self.MCSAEdit.Append(id=wxID_CLEARMCSARB, kind=wx.ITEM_NORMAL,text='Clear rigid bodies', 
2807            help='Clear all atoms & rigid bodies from MC/SA model' )
2808        self.MCSAEdit.Append(id=wxID_MOVEMCSA, kind=wx.ITEM_NORMAL,text='Move MC/SA solution', 
2809            help='Move MC/SA solution to atom list' )
2810        self.MCSAEdit.Append(id=wxID_MCSACLEARRESULTS, kind=wx.ITEM_NORMAL,text='Clear results', 
2811            help='Clear table of MC/SA results' )
2812        self.PostfillDataMenu()
2813           
2814        # Phase / Texture tab
2815        self.TextureMenu = wx.MenuBar()
2816        self.PrefillDataMenu(self.TextureMenu,helpType='Texture')
2817        self.TextureMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2818        self.TextureEdit = wx.Menu(title='')
2819        self.TextureMenu.Append(menu=self.TextureEdit, title='Texture')
2820        self.TextureEdit.Append(id=wxID_REFINETEXTURE, kind=wx.ITEM_NORMAL,text='Refine texture', 
2821            help='Refine the texture coefficients from sequential Pawley results')
2822        self.TextureEdit.Append(id=wxID_CLEARTEXTURE, kind=wx.ITEM_NORMAL,text='Clear texture', 
2823            help='Clear the texture coefficients' )
2824        self.PostfillDataMenu()
2825           
2826        # Phase / Pawley tab
2827        self.PawleyMenu = wx.MenuBar()
2828        self.PrefillDataMenu(self.PawleyMenu,helpType='Pawley')
2829        self.PawleyMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2830        self.PawleyEdit = wx.Menu(title='')
2831        self.PawleyMenu.Append(menu=self.PawleyEdit,title='Operations')
2832        self.PawleyEdit.Append(id=wxID_PAWLEYLOAD, kind=wx.ITEM_NORMAL,text='Pawley create',
2833            help='Initialize Pawley reflection list')
2834        self.PawleyEdit.Append(id=wxID_PAWLEYESTIMATE, kind=wx.ITEM_NORMAL,text='Pawley estimate',
2835            help='Estimate initial Pawley intensities')
2836        self.PawleyEdit.Append(id=wxID_PAWLEYUPDATE, kind=wx.ITEM_NORMAL,text='Pawley update',
2837            help='Update negative Pawley intensities with -0.5*Fobs and turn off refinemnt')
2838        self.PostfillDataMenu()
2839           
2840        # Phase / Map peaks tab
2841        self.MapPeaksMenu = wx.MenuBar()
2842        self.PrefillDataMenu(self.MapPeaksMenu,helpType='Map peaks')
2843        self.MapPeaksMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2844        self.MapPeaksEdit = wx.Menu(title='')
2845        self.MapPeaksMenu.Append(menu=self.MapPeaksEdit, title='Map peaks')
2846        self.MapPeaksEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks', 
2847            help='Move selected peaks to atom list')
2848        self.MapPeaksEdit.Append(id=wxID_PEAKSVIEWPT, kind=wx.ITEM_NORMAL,text='View point',
2849            help='View point is 1st peak selected')
2850        self.MapPeaksEdit.Append(id=wxID_PEAKSDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
2851            help='Compute distance of selected peaks from view point')   
2852        self.MapPeaksEdit.Append(id=wxID_SHOWBONDS, kind=wx.ITEM_NORMAL,text='Hide bonds',
2853            help='Hide or show bonds between peak positions')   
2854        self.MapPeaksEdit.Append(id=wxID_PEAKSDA, kind=wx.ITEM_NORMAL,text='Calc dist/ang', 
2855            help='Calculate distance or angle for selection')
2856        self.MapPeaksEdit.Append(id=wxID_FINDEQVPEAKS, kind=wx.ITEM_NORMAL,text='Equivalent peaks', 
2857            help='Find equivalent peaks')
2858        self.MapPeaksEdit.Append(id=wxID_PEAKSUNIQUE, kind=wx.ITEM_NORMAL,text='Unique peaks', 
2859            help='Select unique set')
2860        self.MapPeaksEdit.Append(id=wxID_PEAKSDELETE, kind=wx.ITEM_NORMAL,text='Delete peaks', 
2861            help='Delete selected peaks')
2862        self.MapPeaksEdit.Append(id=wxID_PEAKSCLEAR, kind=wx.ITEM_NORMAL,text='Clear peaks', 
2863            help='Clear the map peak list')
2864        self.PostfillDataMenu()
2865
2866        # Phase / Rigid bodies tab
2867        self.RigidBodiesMenu = wx.MenuBar()
2868        self.PrefillDataMenu(self.RigidBodiesMenu,helpType='Rigid bodies')
2869        self.RigidBodiesMenu.Append(menu=wx.Menu(title=''),title='Select tab')
2870        self.RigidBodiesEdit = wx.Menu(title='')
2871        self.RigidBodiesMenu.Append(menu=self.RigidBodiesEdit, title='Edit')
2872        self.RigidBodiesEdit.Append(id=wxID_ASSIGNATMS2RB, kind=wx.ITEM_NORMAL,text='Assign atoms to rigid body',
2873            help='Select & position rigid body in structure of existing atoms')
2874        self.RigidBodiesEdit.Append(id=wxID_AUTOFINDRESRB, kind=wx.ITEM_NORMAL,text='Auto find residues',
2875            help='Auto find of residue RBs in macromolecule')
2876        self.RigidBodiesEdit.Append(id=wxID_COPYRBPARMS, kind=wx.ITEM_NORMAL,text='Copy rigid body parms',
2877            help='Copy rigid body location & TLS parameters')
2878        self.RigidBodiesEdit.Append(id=wxID_GLOBALTHERM, kind=wx.ITEM_NORMAL,text='Global thermal motion',
2879            help='Global setting of residue thermal motion models')
2880        self.RigidBodiesEdit.Append(id=wxID_GLOBALRESREFINE, kind=wx.ITEM_NORMAL,text='Global residue refine',
2881            help='Global setting of residue RB refinement flags')
2882        self.RigidBodiesEdit.Append(id=wxID_RBREMOVEALL, kind=wx.ITEM_NORMAL,text='Remove all rigid bodies',
2883            help='Remove all rigid body assignment for atoms')
2884        self.PostfillDataMenu()
2885    # end of GSAS-II menu definitions
2886       
2887    def _init_ctrls(self, parent,name=None,size=None,pos=None):
2888        wx.Frame.__init__(self,parent=parent,
2889            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX | wx.FRAME_FLOAT_ON_PARENT ,
2890            size=size,pos=pos,title='GSAS-II data display')
2891        self._init_menus()
2892        if name:
2893            self.SetLabel(name)
2894        self.Show()
2895       
2896    def __init__(self,parent,frame,data=None,name=None, size=None,pos=None):
2897        self.G2frame = frame
2898        self._init_ctrls(parent,name,size,pos)
2899        self.data = data
2900        clientSize = wx.ClientDisplayRect()
2901        Size = self.GetSize()
2902        xPos = clientSize[2]-Size[0]
2903        self.SetPosition(wx.Point(xPos,clientSize[1]+250))
2904        self.AtomGrid = []
2905        self.selectedRow = 0
2906       
2907    def setSizePosLeft(self,Width):
2908        clientSize = wx.ClientDisplayRect()
2909        Width[1] = min(Width[1],clientSize[2]-300)
2910        Width[0] = max(Width[0],300)
2911        self.SetSize(Width)
2912#        self.SetPosition(wx.Point(clientSize[2]-Width[0],clientSize[1]+250))
2913       
2914    def Clear(self):
2915        self.ClearBackground()
2916        self.DestroyChildren()
2917                   
2918################################################################################
2919#####  GSNotebook
2920################################################################################           
2921       
2922class GSNoteBook(wx.aui.AuiNotebook):
2923    '''Notebook used in various locations; implemented with wx.aui extension
2924    '''
2925    def __init__(self, parent, name='',size = None):
2926        wx.aui.AuiNotebook.__init__(self, parent, -1,
2927                                    style=wx.aui.AUI_NB_TOP |
2928                                    wx.aui.AUI_NB_SCROLL_BUTTONS)
2929        if size: self.SetSize(size)
2930                                                     
2931    def Clear(self):       
2932        GSNoteBook.DeleteAllPages(self)
2933       
2934    def FindPage(self,name):
2935        numPage = self.GetPageCount()
2936        for page in range(numPage):
2937            if self.GetPageText(page) == name:
2938                return page
2939
2940    def ChangeSelection(self,page):
2941        # in wx.Notebook ChangeSelection is like SetSelection, but it
2942        # does not invoke the event related to pressing the tab button
2943        # I don't see a way to do that in aui.
2944        oldPage = self.GetSelection()
2945        self.SetSelection(page)
2946        return oldPage
2947
2948    # def __getattribute__(self,name):
2949    #     '''This method provides a way to print out a message every time
2950    #     that a method in a class is called -- to see what all the calls
2951    #     might be, or where they might be coming from.
2952    #     Cute trick for debugging!
2953    #     '''
2954    #     attr = object.__getattribute__(self, name)
2955    #     if hasattr(attr, '__call__'):
2956    #         def newfunc(*args, **kwargs):
2957    #             print('GSauiNoteBook calling %s' %attr.__name__)
2958    #             result = attr(*args, **kwargs)
2959    #             return result
2960    #         return newfunc
2961    #     else:
2962    #         return attr
2963           
2964################################################################################
2965#####  GSGrid
2966################################################################################           
2967       
2968class GSGrid(wg.Grid):
2969    '''Basic wx.Grid implementation
2970    '''
2971    def __init__(self, parent, name=''):
2972        wg.Grid.__init__(self,parent,-1,name=name)                   
2973        #self.SetSize(parent.GetClientSize())
2974        # above removed to speed drawing of initial grid
2975        # does not appear to be needed
2976           
2977    def Clear(self):
2978        wg.Grid.ClearGrid(self)
2979       
2980    def SetCellStyle(self,r,c,color="white",readonly=True):
2981        self.SetCellBackgroundColour(r,c,color)
2982        self.SetReadOnly(r,c,isReadOnly=readonly)
2983       
2984    def GetSelection(self):
2985        #this is to satisfy structure drawing stuff in G2plt when focus changes
2986        return None
2987                                               
2988################################################################################
2989#####  Table
2990################################################################################           
2991       
2992class Table(wg.PyGridTableBase):
2993    '''Basic data table for use with GSgrid
2994    '''
2995    def __init__(self, data=[], rowLabels=None, colLabels=None, types = None):
2996        wg.PyGridTableBase.__init__(self)
2997        self.colLabels = colLabels
2998        self.rowLabels = rowLabels
2999        self.dataTypes = types
3000        self.data = data
3001       
3002    def AppendRows(self, numRows=1):
3003        self.data.append([])
3004        return True
3005       
3006    def CanGetValueAs(self, row, col, typeName):
3007        if self.dataTypes:
3008            colType = self.dataTypes[col].split(':')[0]
3009            if typeName == colType:
3010                return True
3011            else:
3012                return False
3013        else:
3014            return False
3015
3016    def CanSetValueAs(self, row, col, typeName):
3017        return self.CanGetValueAs(row, col, typeName)
3018
3019    def DeleteRow(self,pos):
3020        data = self.GetData()
3021        self.SetData([])
3022        new = []
3023        for irow,row in enumerate(data):
3024            if irow <> pos:
3025                new.append(row)
3026        self.SetData(new)
3027       
3028    def GetColLabelValue(self, col):
3029        if self.colLabels:
3030            return self.colLabels[col]
3031           
3032    def GetData(self):
3033        data = []
3034        for row in range(self.GetNumberRows()):
3035            data.append(self.GetRowValues(row))
3036        return data
3037       
3038    def GetNumberCols(self):
3039        try:
3040            return len(self.colLabels)
3041        except TypeError:
3042            return None
3043       
3044    def GetNumberRows(self):
3045        return len(self.data)
3046       
3047    def GetRowLabelValue(self, row):
3048        if self.rowLabels:
3049            return self.rowLabels[row]
3050       
3051    def GetColValues(self, col):
3052        data = []
3053        for row in range(self.GetNumberRows()):
3054            data.append(self.GetValue(row, col))
3055        return data
3056       
3057    def GetRowValues(self, row):
3058        data = []
3059        for col in range(self.GetNumberCols()):
3060            data.append(self.GetValue(row, col))
3061        return data
3062       
3063    def GetTypeName(self, row, col):
3064        try:
3065            return self.dataTypes[col]
3066        except TypeError:
3067            return None
3068
3069    def GetValue(self, row, col):
3070        try:
3071            return self.data[row][col]
3072        except IndexError:
3073            return None
3074           
3075    def InsertRows(self, pos, rows):
3076        for row in range(rows):
3077            self.data.insert(pos,[])
3078            pos += 1
3079       
3080    def IsEmptyCell(self,row,col):
3081        try:
3082            return not self.data[row][col]
3083        except IndexError:
3084            return True
3085       
3086    def OnKeyPress(self, event):
3087        dellist = self.GetSelectedRows()
3088        if event.GetKeyCode() == wx.WXK_DELETE and dellist:
3089            grid = self.GetView()
3090            for i in dellist: grid.DeleteRow(i)
3091               
3092    def SetColLabelValue(self, col, label):
3093        numcols = self.GetNumberCols()
3094        if col > numcols-1:
3095            self.colLabels.append(label)
3096        else:
3097            self.colLabels[col]=label
3098       
3099    def SetData(self,data):
3100        for row in range(len(data)):
3101            self.SetRowValues(row,data[row])
3102               
3103    def SetRowLabelValue(self, row, label):
3104        self.rowLabels[row]=label
3105           
3106    def SetRowValues(self,row,data):
3107        self.data[row] = data
3108           
3109    def SetValue(self, row, col, value):
3110        def innerSetValue(row, col, value):
3111            try:
3112                self.data[row][col] = value
3113            except TypeError:
3114                return
3115            except IndexError:
3116                print row,col,value
3117                # add a new row
3118                if row > self.GetNumberRows():
3119                    self.data.append([''] * self.GetNumberCols())
3120                elif col > self.GetNumberCols():
3121                    for row in range(self.GetNumberRows):
3122                        self.data[row].append('')
3123                print self.data
3124                self.data[row][col] = value
3125        innerSetValue(row, col, value)
3126       
3127################################################################################
3128#### Help
3129################################################################################
3130
3131def ShowHelp(helpType,frame):
3132    '''Called to bring up a web page for documentation.'''
3133    global htmlFirstUse
3134    # look up a definition for help info from dict
3135    helplink = helpLocDict.get(helpType)
3136    if helplink is None:
3137        # no defined link to use, create a default based on key
3138        helplink = 'gsasII.html#'+helpType.replace(' ','_')
3139    helplink = os.path.join(path2GSAS2,'help',helplink)
3140    if helpMode == 'internal':
3141        try:
3142            htmlPanel.LoadFile(helplink)
3143            htmlFrame.Raise()
3144        except:
3145            htmlFrame = wx.Frame(frame, -1, size=(610, 510))
3146            htmlFrame.Show(True)
3147            htmlFrame.SetTitle("HTML Window") # N.B. reset later in LoadFile
3148            htmlPanel = MyHtmlPanel(htmlFrame,-1)
3149            htmlPanel.LoadFile(helplink)
3150    else:
3151        pfx = "file://"
3152        if sys.platform.lower().startswith('win'):
3153            pfx = ''
3154        if htmlFirstUse:
3155            webbrowser.open_new(pfx+helplink)
3156            htmlFirstUse = False
3157        else:
3158            webbrowser.open(pfx+helplink, new=0, autoraise=True)
3159
3160################################################################################
3161#####  Notebook
3162################################################################################           
3163       
3164def UpdateNotebook(G2frame,data):
3165    '''Called when the data tree notebook entry is selected. Allows for
3166    editing of the text in that tree entry
3167    '''
3168    def OnNoteBook(event):
3169        data = G2frame.dataDisplay.GetValue().split('\n')
3170        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Notebook'),data)
3171        if 'nt' not in os.name:
3172            G2frame.dataDisplay.AppendText('\n')
3173                   
3174    if G2frame.dataDisplay:
3175        G2frame.dataDisplay.Destroy()
3176    G2frame.dataFrame.SetLabel('Notebook')
3177    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
3178        style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
3179    G2frame.dataDisplay.Bind(wx.EVT_TEXT_ENTER,OnNoteBook)
3180    G2frame.dataDisplay.Bind(wx.EVT_KILL_FOCUS,OnNoteBook)
3181    for line in data:
3182        G2frame.dataDisplay.AppendText(line+"\n")
3183    G2frame.dataDisplay.AppendText('Notebook entry @ '+time.ctime()+"\n")
3184    G2frame.dataFrame.setSizePosLeft([400,250])
3185           
3186################################################################################
3187#####  Controls
3188################################################################################           
3189       
3190def UpdateControls(G2frame,data):
3191    '''Edit overall GSAS-II controls in main Controls data tree entry
3192    '''
3193    #patch
3194    if 'deriv type' not in data:
3195        data = {}
3196        data['deriv type'] = 'analytic Hessian'
3197        data['min dM/M'] = 0.0001
3198        data['shift factor'] = 1.
3199        data['max cyc'] = 3       
3200        data['F**2'] = True
3201        data['minF/sig'] = 0
3202    if 'shift factor' not in data:
3203        data['shift factor'] = 1.
3204    if 'max cyc' not in data:
3205        data['max cyc'] = 3
3206    if 'F**2' not in data:
3207        data['F**2'] = True
3208        data['minF/sig'] = 0
3209    if 'Author' not in data:
3210        data['Author'] = 'no name'
3211    if 'FreePrm1' not in data:
3212        data['FreePrm1'] = 'Sample humidity (%)'
3213    if 'FreePrm2' not in data:
3214        data['FreePrm2'] = 'Sample voltage (V)'
3215    if 'FreePrm3' not in data:
3216        data['FreePrm3'] = 'Applied load (MN)'
3217    #end patch
3218
3219    def SeqSizer():
3220       
3221        def OnSelectData(event):
3222            choices = ['All',]+GetPatternTreeDataNames(G2frame,['PWDR',])
3223            sel = []
3224            if 'Seq Data' in data:
3225                for item in data['Seq Data']:
3226                    sel.append(choices.index(item))
3227            names = []
3228            dlg = wx.MultiChoiceDialog(G2frame,'Select data:','Sequential refinement',choices)
3229            dlg.SetSelections(sel)
3230            if dlg.ShowModal() == wx.ID_OK:
3231                sel = dlg.GetSelections()
3232                for i in sel: names.append(choices[i])
3233                if 'All' in names:
3234                    names = choices[1:]
3235                data['Seq Data'] = names               
3236            dlg.Destroy()
3237            reverseSel.Enable(True)
3238           
3239        def OnReverse(event):
3240            data['Reverse Seq'] = reverseSel.GetValue()
3241                   
3242        seqSizer = wx.BoxSizer(wx.HORIZONTAL)
3243        seqSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Sequential Refinement Powder Data: '),0,wx.ALIGN_CENTER_VERTICAL)
3244        selSeqData = wx.Button(G2frame.dataDisplay,-1,label=' Select data')
3245        selSeqData.Bind(wx.EVT_BUTTON,OnSelectData)
3246        seqSizer.Add(selSeqData,0,wx.ALIGN_CENTER_VERTICAL)
3247        seqSizer.Add((5,0),0)
3248        reverseSel = wx.CheckBox(G2frame.dataDisplay,-1,label=' Reverse order?')
3249        reverseSel.Bind(wx.EVT_CHECKBOX,OnReverse)
3250        if 'Seq Data' not in data:
3251            reverseSel.Enable(False)
3252        if 'Reverse Seq' in data:
3253            reverseSel.SetValue(data['Reverse Seq'])
3254        seqSizer.Add(reverseSel,0,wx.ALIGN_CENTER_VERTICAL)
3255        return seqSizer
3256       
3257    def LSSizer():       
3258       
3259        def OnDerivType(event):
3260            data['deriv type'] = derivSel.GetValue()
3261            derivSel.SetValue(data['deriv type'])
3262            wx.CallAfter(UpdateControls,G2frame,data)
3263           
3264        def OnConvergence(event):
3265            try:
3266                value = max(1.e-9,min(1.0,float(Cnvrg.GetValue())))
3267            except ValueError:
3268                value = 0.0001
3269            data['min dM/M'] = value
3270            Cnvrg.SetValue('%.2g'%(value))
3271           
3272        def OnMaxCycles(event):
3273            data['max cyc'] = int(maxCyc.GetValue())
3274            maxCyc.SetValue(str(data['max cyc']))
3275                       
3276        def OnFactor(event):
3277            try:
3278                value = min(max(float(Factr.GetValue()),0.00001),100.)
3279            except ValueError:
3280                value = 1.0
3281            data['shift factor'] = value
3282            Factr.SetValue('%.5f'%(value))
3283           
3284        def OnFsqRef(event):
3285            data['F**2'] = fsqRef.GetValue()
3286       
3287        def OnMinSig(event):
3288            try:
3289                value = min(max(float(minSig.GetValue()),0.),5.)
3290            except ValueError:
3291                value = 1.0
3292            data['minF/sig'] = value
3293            minSig.SetValue('%.2f'%(value))
3294
3295        LSSizer = wx.FlexGridSizer(cols=4,vgap=5,hgap=5)
3296        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement derivatives: '),0,wx.ALIGN_CENTER_VERTICAL)
3297        Choice=['analytic Jacobian','numeric','analytic Hessian']
3298        derivSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['deriv type'],choices=Choice,
3299            style=wx.CB_READONLY|wx.CB_DROPDOWN)
3300        derivSel.SetValue(data['deriv type'])
3301        derivSel.Bind(wx.EVT_COMBOBOX, OnDerivType)
3302           
3303        LSSizer.Add(derivSel,0,wx.ALIGN_CENTER_VERTICAL)
3304        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min delta-M/M: '),0,wx.ALIGN_CENTER_VERTICAL)
3305        Cnvrg = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2g'%(data['min dM/M']),style=wx.TE_PROCESS_ENTER)
3306        Cnvrg.Bind(wx.EVT_TEXT_ENTER,OnConvergence)
3307        Cnvrg.Bind(wx.EVT_KILL_FOCUS,OnConvergence)
3308        LSSizer.Add(Cnvrg,0,wx.ALIGN_CENTER_VERTICAL)
3309        if 'Hessian' in data['deriv type']:
3310            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Max cycles: '),0,wx.ALIGN_CENTER_VERTICAL)
3311            Choice = ['0','1','2','3','5','10','15','20']
3312            maxCyc = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['max cyc']),choices=Choice,
3313                style=wx.CB_READONLY|wx.CB_DROPDOWN)
3314            maxCyc.SetValue(str(data['max cyc']))
3315            maxCyc.Bind(wx.EVT_COMBOBOX, OnMaxCycles)
3316            LSSizer.Add(maxCyc,0,wx.ALIGN_CENTER_VERTICAL)
3317        else:
3318            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Initial shift factor: '),0,wx.ALIGN_CENTER_VERTICAL)
3319            Factr = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.5f'%(data['shift factor']),style=wx.TE_PROCESS_ENTER)
3320            Factr.Bind(wx.EVT_TEXT_ENTER,OnFactor)
3321            Factr.Bind(wx.EVT_KILL_FOCUS,OnFactor)
3322            LSSizer.Add(Factr,0,wx.ALIGN_CENTER_VERTICAL)
3323        if G2frame.Sngl:
3324            LSSizer.Add((1,0),)
3325            LSSizer.Add((1,0),)
3326            fsqRef = wx.CheckBox(G2frame.dataDisplay,-1,label='Refine HKLF as F^2? ')
3327            fsqRef.SetValue(data['F**2'])
3328            fsqRef.Bind(wx.EVT_CHECKBOX,OnFsqRef)
3329            LSSizer.Add(fsqRef,0,wx.ALIGN_CENTER_VERTICAL)
3330            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label='Min obs/sig (0-5): '),0,wx.ALIGN_CENTER_VERTICAL)
3331            minSig = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(data['minF/sig']),style=wx.TE_PROCESS_ENTER)
3332            minSig.Bind(wx.EVT_TEXT_ENTER,OnMinSig)
3333            minSig.Bind(wx.EVT_KILL_FOCUS,OnMinSig)
3334            LSSizer.Add(minSig,0,wx.ALIGN_CENTER_VERTICAL)
3335        return LSSizer
3336       
3337    def AuthSizer():
3338
3339        def OnAuthor(event):
3340            data['Author'] = auth.GetValue()
3341
3342        Author = data['Author']
3343        authSizer = wx.BoxSizer(wx.HORIZONTAL)
3344        authSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' CIF Author (last, first):'),0,wx.ALIGN_CENTER_VERTICAL)
3345        auth = wx.TextCtrl(G2frame.dataDisplay,-1,value=Author,style=wx.TE_PROCESS_ENTER)
3346        auth.Bind(wx.EVT_TEXT_ENTER,OnAuthor)
3347        auth.Bind(wx.EVT_KILL_FOCUS,OnAuthor)
3348        authSizer.Add(auth,0,wx.ALIGN_CENTER_VERTICAL)
3349        return authSizer
3350       
3351       
3352    if G2frame.dataDisplay:
3353        G2frame.dataDisplay.Destroy()
3354    if not G2frame.dataFrame.GetStatusBar():
3355        Status = G2frame.dataFrame.CreateStatusBar()
3356        Status.SetStatusText('')
3357    G2frame.dataFrame.SetLabel('Controls')
3358    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
3359    SetDataMenuBar(G2frame,G2frame.dataFrame.ControlsMenu)
3360    mainSizer = wx.BoxSizer(wx.VERTICAL)
3361    mainSizer.Add((5,5),0)
3362    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement Controls:'),0,wx.ALIGN_CENTER_VERTICAL)   
3363    mainSizer.Add(LSSizer())
3364    mainSizer.Add((5,5),0)
3365    mainSizer.Add(SeqSizer())
3366    mainSizer.Add((5,5),0)
3367    mainSizer.Add(AuthSizer())
3368    mainSizer.Add((5,5),0)
3369       
3370    mainSizer.Layout()   
3371    G2frame.dataDisplay.SetSizer(mainSizer)
3372    G2frame.dataDisplay.SetSize(mainSizer.Fit(G2frame.dataFrame))
3373    G2frame.dataFrame.setSizePosLeft(mainSizer.Fit(G2frame.dataFrame))
3374     
3375################################################################################
3376#####  Comments
3377################################################################################           
3378       
3379def UpdateComments(G2frame,data):                   
3380
3381    if G2frame.dataDisplay:
3382        G2frame.dataDisplay.Destroy()
3383    G2frame.dataFrame.SetLabel('Comments')
3384    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
3385        style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_DONTWRAP)
3386    for line in data:
3387        G2frame.dataDisplay.AppendText(line+'\n')
3388    G2frame.dataFrame.setSizePosLeft([400,250])
3389           
3390################################################################################
3391#####  Sequential Results
3392################################################################################           
3393       
3394def UpdateSeqResults(G2frame,data):
3395    """
3396    Called when the Sequential Results data tree entry is selected
3397    to show results from a sequential refinement.
3398   
3399    :param wx.Frame G2frame: main GSAS-II data tree windows
3400
3401    :param dict data: a dictionary containing the following items: 
3402
3403            * 'histNames' - list of histogram names in order as processed by Sequential Refinement
3404            * 'varyList' - list of variables - identical over all refinements in sequence
3405            * 'histName' - dictionaries for all data sets processed, which contains:
3406
3407              * 'variables'- result[0] from leastsq call
3408              * 'varyList' - list of variables; same as above
3409              * 'sig' - esds for variables
3410              * 'covMatrix' - covariance matrix from individual refinement
3411              * 'title' - histogram name; same as dict item name
3412              * 'newAtomDict' - new atom parameters after shifts applied
3413              * 'newCellDict' - new cell parameters after shifts to A0-A5 applied'
3414    """
3415    if not data:
3416        print 'No sequential refinement results'
3417        return
3418    histNames = data['histNames']
3419       
3420    def GetSampleParms():
3421        sampleParmDict = {'Temperature':300.,'Pressure':1.,
3422                          'FreePrm1':0.,'FreePrm2':0.,'FreePrm3':0.,}
3423        sampleParm = {}
3424        for name in histNames:
3425            Id = GetPatternTreeItemId(G2frame,G2frame.root,name)
3426            sampleData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Sample Parameters'))
3427            for item in sampleParmDict:
3428                sampleParmDict[item].append(sampleData[item])
3429        for item in sampleParmDict:
3430            frstValue = sampleParmDict[item][0]
3431            if np.any(np.array(sampleParmDict[item])-frstValue):
3432                sampleParm[item] = sampleParmDict[item]           
3433        return sampleParm
3434           
3435    def GetRwps():
3436        Rwps = []
3437        for name in histNames:
3438            Rwps.append(data[name]['Rvals']['Rwp'])
3439        return Rwps
3440           
3441    def GetSigData(parm):
3442        sigData = []
3443        for name in histNames:
3444            sigList = data[name]['sig']
3445            if colLabels[parm] in atomList:
3446                sigData.append(sigList[colLabels.index(atomList[colLabels[parm]])-1])
3447            elif colLabels[parm] in cellList:
3448                sigData.append(sigList[colLabels.index(cellList[colLabels[parm]])-1])
3449            else:
3450                sigData.append(sigList[parm-1])
3451        return sigData
3452   
3453    def Select(event):
3454        cols = G2frame.dataDisplay.GetSelectedCols()
3455        rows = G2frame.dataDisplay.GetSelectedRows()
3456        if cols:
3457            plotData = []
3458            plotSig = []
3459            plotNames = []
3460            for col in cols:
3461                plotData.append(G2frame.SeqTable.GetColValues(col))
3462                if col:     #not Rwp
3463                    plotSig.append(GetSigData(col))
3464                else:
3465                    plotSig.append(0.0)
3466                plotNames.append(G2frame.SeqTable.GetColLabelValue(col))
3467            plotData = np.array(plotData)
3468            G2plt.PlotSeq(G2frame,plotData,plotSig,plotNames,sampleParms)
3469        elif rows:
3470            name = histNames[rows[0]]       #only does 1st one selected
3471            G2plt.PlotCovariance(G2frame,data[name])
3472           
3473    def OnSaveSelSeq(event):       
3474        cols = G2frame.dataDisplay.GetSelectedCols()
3475        if cols:
3476            numRows = G2frame.SeqTable.GetNumberRows()
3477            dataNames = []
3478            saveNames = [G2frame.SeqTable.GetRowLabelValue(r) for r in range(numRows)]
3479            saveData = []
3480            for col in cols:
3481                dataNames.append(G2frame.SeqTable.GetColLabelValue(col))
3482                if col:     #not Rwp
3483                    saveData.append(zip(G2frame.SeqTable.GetColValues(col),GetSigData(col)))
3484                else:
3485                    saveData.append(zip(G2frame.SeqTable.GetColValues(col),0.0))
3486            lenName = len(saveNames[0])
3487            saveData = np.swapaxes(np.array(saveData),0,1)
3488            dlg = wx.FileDialog(G2frame, 'Choose text output file for your selection', '.', '', 
3489                'Text output file (*.txt)|*.txt',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
3490            try:
3491                if dlg.ShowModal() == wx.ID_OK:
3492                    SeqTextFile = dlg.GetPath()
3493                    SeqTextFile = G2IO.FileDlgFixExt(dlg,SeqTextFile)
3494                    SeqFile = open(SeqTextFile,'w')
3495                    line = %s  '%('name'.center(lenName))
3496                    for item in dataNames:
3497                        line += ' %12s %12s '%(item.center(12),'esd'.center(12))
3498                    line += '\n'
3499                    SeqFile.write(line)
3500                    for i,item in enumerate(saveData):
3501                        line = " '%s' "%(saveNames[i])
3502                        for val,esd in item:
3503                            line += ' %12.6f %12.6f '%(val,esd)
3504                        line += '\n'
3505                        SeqFile.write(line)
3506                    SeqFile.close()
3507            finally:
3508                dlg.Destroy()
3509           
3510               
3511    if G2frame.dataDisplay:
3512        G2frame.dataDisplay.Destroy()
3513    atomList = {}
3514    newAtomDict = data[histNames[0]]['newAtomDict']
3515    for item in newAtomDict:
3516        if item in data['varyList']:
3517            atomList[newAtomDict[item][0]] = item
3518    cellList = {}
3519    newCellDict = data[histNames[0]]['newCellDict']
3520    for item in newCellDict:
3521        if item in data['varyList']:
3522            cellList[newCellDict[item][0]] = item
3523    sampleParms = GetSampleParms()
3524    Rwps = GetRwps()
3525    SetDataMenuBar(G2frame,G2frame.dataFrame.SequentialMenu)
3526    G2frame.dataFrame.SetLabel('Sequential refinement results')
3527    G2frame.dataFrame.CreateStatusBar()
3528    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeq, id=wxID_SAVESEQSEL)
3529    colLabels = ['Rwp',]+data['varyList']+atomList.keys()+cellList.keys()
3530    Types = (len(data['varyList']+atomList.keys()+cellList.keys())+1)*[wg.GRID_VALUE_FLOAT,]
3531    seqList = [[Rwps[i],]+list(data[name]['variables']) for i,name in enumerate(histNames)]
3532    for i,item in enumerate(seqList):
3533        newAtomDict = data[histNames[i]]['newAtomDict']
3534        newCellDict = data[histNames[i]]['newCellDict']
3535        item += [newAtomDict[atomList[parm]][1] for parm in atomList.keys()]
3536        item += [newCellDict[cellList[parm]][1] for parm in cellList.keys()]
3537    G2frame.SeqTable = Table(seqList,colLabels=colLabels,rowLabels=histNames,types=Types)
3538    G2frame.dataDisplay = GSGrid(parent=G2frame.dataFrame)
3539    G2frame.dataDisplay.SetTable(G2frame.SeqTable, True)
3540    G2frame.dataDisplay.EnableEditing(False)
3541    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_LEFT_DCLICK, Select)
3542    G2frame.dataDisplay.SetRowLabelSize(8*len(histNames[0]))       #pretty arbitrary 8
3543    G2frame.dataDisplay.SetMargins(0,0)
3544    G2frame.dataDisplay.AutoSizeColumns(True)
3545    G2frame.dataFrame.setSizePosLeft([700,350])
3546       
3547################################################################################
3548#####  Main PWDR panel
3549################################################################################           
3550       
3551def UpdatePWHKPlot(G2frame,kind,item):
3552    '''Called when the histogram main tree entry is called. Displays the
3553    histogram weight factor, refinement statistics for the histogram
3554    and the range of data for a simulation.
3555
3556    Also invokes a plot of the histogram.
3557    '''
3558    def onEditSimRange(event):
3559        'Edit simulation range'
3560        inp = [
3561            min(data[1][0]),
3562            max(data[1][0]),
3563            None
3564            ]
3565        inp[2] = (inp[1] - inp[0])/(len(data[1][0])-1.)
3566        names = ('start angle', 'end angle', 'step size')
3567        dictlst = [inp] * len(inp)
3568        elemlst = range(len(inp))
3569        dlg = ScrolledMultiEditor(
3570            G2frame,[inp] * len(inp), range(len(inp)), names,
3571            header='Edit simulation range',
3572            minvals=(0.001,0.001,0.0001),
3573            maxvals=(180.,180.,.1),
3574            )
3575        dlg.CenterOnParent()
3576        val = dlg.ShowModal()
3577        dlg.Destroy()
3578        if val != wx.ID_OK: return
3579        if inp[0] > inp[1]:
3580            end,start,step = inp
3581        else:               
3582            start,end,step = inp
3583        step = abs(step)
3584        N = int((end-start)/step)+1
3585        newdata = np.linspace(start,end,N,True)
3586        if len(newdata) < 2: return # too small a range - reject
3587        data[1] = [newdata,np.zeros_like(newdata),np.ones_like(newdata),
3588            np.zeros_like(newdata),np.zeros_like(newdata),np.zeros_like(newdata)]
3589        Tmin = newdata[0]
3590        Tmax = newdata[-1]
3591        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,item,'Limits'),
3592            [(Tmin,Tmax),[Tmin,Tmax]])
3593        UpdatePWHKPlot(G2frame,kind,item) # redisplay data screen
3594
3595    def OnErrorAnalysis(event):
3596        G2plt.PlotDeltSig(G2frame,kind)
3597       
3598    def OnWtFactor(event):
3599        try:
3600            val = float(wtval.GetValue())
3601        except ValueError:
3602            val = data[0]['wtFactor']
3603        data[0]['wtFactor'] = val
3604        wtval.SetValue('%.3f'%(val))
3605           
3606    data = G2frame.PatternTree.GetItemPyData(item)
3607#patches
3608    if 'wtFactor' not in data[0]:
3609        data[0] = {'wtFactor':1.0}
3610    if isinstance(data[1],list) and kind == 'HKLF':
3611        RefData = {'RefList':[],'FF':[]}
3612        for ref in data[1]:
3613            RefData['RefList'].append(ref[:11]+[ref[13],])
3614            RefData['FF'].append(ref[14])
3615        data[1] = RefData
3616        G2frame.PatternTree.SetItemPyData(item,data)
3617#end patches
3618    if G2frame.dataDisplay:
3619        G2frame.dataDisplay.Destroy()
3620    SetDataMenuBar(G2frame,G2frame.dataFrame.ErrorMenu)
3621    G2frame.dataFrame.Bind(wx.EVT_MENU,OnErrorAnalysis, id=wxID_PWDANALYSIS)
3622    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
3623   
3624    mainSizer = wx.BoxSizer(wx.VERTICAL)
3625    mainSizer.Add((5,5),)
3626    wtSizer = wx.BoxSizer(wx.HORIZONTAL)
3627    wtSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Weight factor: '),0,wx.ALIGN_CENTER_VERTICAL)
3628    wtval = wx.TextCtrl(G2frame.dataDisplay,-1,'%.3f'%(data[0]['wtFactor']),style=wx.TE_PROCESS_ENTER)
3629    wtval.Bind(wx.EVT_TEXT_ENTER,OnWtFactor)
3630    wtval.Bind(wx.EVT_KILL_FOCUS,OnWtFactor)
3631    wtSizer.Add(wtval,0,wx.ALIGN_CENTER_VERTICAL)
3632    mainSizer.Add(wtSizer)
3633    if data[0].get('Dummy'):
3634        simSizer = wx.BoxSizer(wx.HORIZONTAL)
3635        Tmin = min(data[1][0])
3636        Tmax = max(data[1][0])
3637        num = len(data[1][0])
3638        step = (Tmax - Tmin)/(num-1)
3639        t = u'2\u03b8' # 2theta
3640        lbl =  u'Simulation range: {:.2f} to {:.2f} {:s}\nwith {:.4f} steps ({:d} points)'
3641        lbl += u'\n(Edit range resets observed intensities).'
3642        lbl = lbl.format(Tmin,Tmax,t,step,num)
3643        simSizer.Add(wx.StaticText(G2frame.dataDisplay,wx.ID_ANY,lbl),
3644                    0,wx.ALIGN_CENTER_VERTICAL)
3645        but = wx.Button(G2frame.dataDisplay,wx.ID_ANY,"Edit range")
3646        but.Bind(wx.EVT_BUTTON,onEditSimRange)
3647        simSizer.Add(but,0,wx.ALIGN_CENTER_VERTICAL)
3648        mainSizer.Add(simSizer)
3649    if 'Nobs' in data[0]:
3650        mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
3651            ' Data residual wR: %.3f%% on %d observations'%(data[0]['wR'],data[0]['Nobs'])))
3652        for value in data[0]:
3653            if 'Nref' in value:
3654                mainSizer.Add((5,5),)
3655                pfx = value.split('Nref')[0]
3656                name = data[0][pfx.split(':')[0]+'::Name']
3657                mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' For phase '+name+':'))
3658                mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
3659                    u' Unweighted phase residuals RF\u00b2: %.3f%%, RF: %.3f%% on %d reflections  '% \
3660                    (data[0][pfx+'Rf^2'],data[0][pfx+'Rf'],data[0][value])))
3661    mainSizer.Add((5,5),)
3662    mainSizer.Layout()   
3663    G2frame.dataDisplay.SetSizer(mainSizer)
3664    Size = mainSizer.Fit(G2frame.dataFrame)
3665    Size[1] += 10
3666    G2frame.dataFrame.setSizePosLeft(Size)
3667    G2frame.PatternTree.SetItemPyData(item,data)
3668    if kind == 'PWDR':
3669        G2plt.PlotPatterns(G2frame,plotType=kind,newPlot=True)
3670    elif kind == 'SASD':
3671        G2plt.PlotPatterns(G2frame,plotType=kind,newPlot=True)
3672    elif kind == 'HKLF':
3673        G2plt.PlotSngl(G2frame,newPlot=True)
3674                 
3675################################################################################
3676#####  HKLF controls
3677################################################################################           
3678       
3679def UpdateHKLControls(G2frame,data):
3680    '''Needs a doc string
3681    '''
3682   
3683    def OnScaleSlider(event):
3684        scale = int(scaleSel.GetValue())/1000.
3685        scaleSel.SetValue(int(scale*1000.))
3686        data['Scale'] = scale*1.
3687        G2plt.PlotSngl(G2frame)
3688       
3689    def OnLayerSlider(event):
3690        layer = layerSel.GetValue()
3691        data['Layer'] = layer
3692        G2plt.PlotSngl(G2frame)
3693       
3694    def OnSelZone(event):
3695        data['Zone'] = zoneSel.GetValue()
3696        izone = zones.index(data['Zone'])
3697        layerSel.SetRange(maxValue=HKLmax[izone],minValue=HKLmin[izone])
3698        G2plt.PlotSngl(G2frame,newPlot=True)
3699       
3700    def OnSelType(event):
3701        data['Type'] = typeSel.GetValue()
3702        G2plt.PlotSngl(G2frame)
3703       
3704    def SetStatusLine():
3705        Status.SetStatusText("")
3706                                     
3707    if G2frame.dataDisplay:
3708        G2frame.dataDisplay.Destroy()
3709    if not G2frame.dataFrame.GetStatusBar():
3710        Status = G2frame.dataFrame.CreateStatusBar()
3711    SetStatusLine()
3712    zones = ['100','010','001']
3713    HKLmax = data['HKLmax']
3714    HKLmin = data['HKLmin']
3715    typeChoices = ['Fosq','Fo','|DFsq|/sig','|DFsq|>sig','|DFsq|>3sig']
3716    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
3717    SetDataMenuBar(G2frame)
3718    G2frame.dataFrame.SetTitle('HKL Plot Controls')
3719    mainSizer = wx.BoxSizer(wx.VERTICAL)
3720    mainSizer.Add((5,10),0)
3721   
3722    scaleSizer = wx.BoxSizer(wx.HORIZONTAL)
3723    scaleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Scale'),0,
3724        wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
3725    scaleSel = wx.Slider(parent=G2frame.dataDisplay,maxValue=1000,minValue=1,
3726        style=wx.SL_HORIZONTAL,value=int(data['Scale']*10))
3727    scaleSizer.Add(scaleSel,1,wx.EXPAND|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
3728    scaleSel.SetLineSize(10)
3729    scaleSel.SetPageSize(10)
3730    scaleSel.Bind(wx.EVT_SLIDER, OnScaleSlider)
3731    mainSizer.Add(scaleSizer,0,wx.EXPAND|wx.RIGHT)
3732    mainSizer.Add((0,10),0)   
3733   
3734    zoneSizer = wx.BoxSizer(wx.HORIZONTAL)
3735    zoneSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Zone  '),0,
3736        wx.ALIGN_CENTER_VERTICAL)
3737    zoneSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['Zone'],choices=['100','010','001'],
3738        style=wx.CB_READONLY|wx.CB_DROPDOWN)
3739    zoneSel.Bind(wx.EVT_COMBOBOX, OnSelZone)
3740    zoneSizer.Add(zoneSel,0,wx.ALIGN_CENTER_VERTICAL)
3741    zoneSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Plot type  '),0,
3742        wx.ALIGN_CENTER_VERTICAL)       
3743    typeSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['Type'],choices=typeChoices,
3744        style=wx.CB_READONLY|wx.CB_DROPDOWN)
3745    typeSel.Bind(wx.EVT_COMBOBOX, OnSelType)
3746    zoneSizer.Add(typeSel,0,wx.ALIGN_CENTER_VERTICAL)
3747    zoneSizer.Add((10,0),0)   
3748    mainSizer.Add(zoneSizer,0,wx.EXPAND|wx.RIGHT)
3749    mainSizer.Add((0,10),0)   
3750       
3751    izone = zones.index(data['Zone'])
3752    layerSizer = wx.BoxSizer(wx.HORIZONTAL)
3753    layerSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Layer'),0,
3754        wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
3755    layerSel = wx.Slider(parent=G2frame.dataDisplay,maxValue=HKLmax[izone],minValue=HKLmin[izone],
3756        style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS|wx.SL_LABELS,value=0)
3757    layerSel.SetLineSize(1)
3758    layerSel.SetPageSize(1)
3759    layerSel.Bind(wx.EVT_SLIDER, OnLayerSlider)   
3760    layerSizer.Add(layerSel,1,wx.EXPAND|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
3761    layerSizer.Add((10,0),0)   
3762    mainSizer.Add(layerSizer,1,wx.EXPAND|wx.RIGHT)
3763
3764       
3765    mainSizer.Layout()   
3766    G2frame.dataDisplay.SetSizer(mainSizer)
3767    G2frame.dataDisplay.SetSize(mainSizer.Fit(G2frame.dataFrame))
3768    G2frame.dataFrame.setSizePosLeft(mainSizer.Fit(G2frame.dataFrame))
3769
3770################################################################################
3771#####  Pattern tree routines
3772################################################################################           
3773       
3774def GetPatternTreeDataNames(G2frame,dataTypes):
3775    '''Needs a doc string
3776    '''
3777    names = []
3778    item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)       
3779    while item:
3780        name = G2frame.PatternTree.GetItemText(item)
3781        if name[:4] in dataTypes:
3782            names.append(name)
3783        item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
3784    return names
3785                         
3786def GetPatternTreeItemId(G2frame, parentId, itemText):
3787    '''Needs a doc string
3788    '''
3789    item, cookie = G2frame.PatternTree.GetFirstChild(parentId)
3790    while item:
3791        if G2frame.PatternTree.GetItemText(item) == itemText:
3792            return item
3793        item, cookie = G2frame.PatternTree.GetNextChild(parentId, cookie)
3794    return 0               
3795
3796def MovePatternTreeToGrid(G2frame,item):
3797    '''Needs a doc string
3798    '''
3799   
3800#    print G2frame.PatternTree.GetItemText(item)
3801   
3802    oldPage = None # will be set later if already on a Phase item
3803    if G2frame.dataFrame:
3804        SetDataMenuBar(G2frame)
3805        if G2frame.dataFrame.GetLabel() == 'Comments':
3806            try:
3807                data = [G2frame.dataDisplay.GetValue()]
3808                G2frame.dataDisplay.Clear() 
3809                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Comments')
3810                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
3811            except:     #clumsy but avoids dead window problem when opening another project
3812                pass
3813        elif G2frame.dataFrame.GetLabel() == 'Notebook':
3814            try:
3815                data = [G2frame.dataDisplay.GetValue()]
3816                G2frame.dataDisplay.Clear() 
3817                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Notebook')
3818                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
3819            except:     #clumsy but avoids dead window problem when opening another project
3820                pass
3821        elif 'Phase Data for' in G2frame.dataFrame.GetLabel():
3822            if G2frame.dataDisplay: 
3823                oldPage = G2frame.dataDisplay.GetSelection()
3824        G2frame.dataFrame.Clear()
3825        G2frame.dataFrame.SetLabel('')
3826    else:
3827        #create the frame for the data item window
3828        G2frame.dataFrame = DataFrame(parent=G2frame.mainPanel,frame=G2frame)
3829        G2frame.dataFrame.PhaseUserSize = None
3830       
3831    G2frame.dataFrame.Raise()           
3832    G2frame.PickId = 0
3833    parentID = G2frame.root
3834    for i in G2frame.ExportPattern: i.Enable(False)
3835    defWid = [250,150]
3836    if item != G2frame.root:
3837        parentID = G2frame.PatternTree.GetItemParent(item)
3838    if G2frame.PatternTree.GetItemParent(item) == G2frame.root:
3839        G2frame.PatternId = item
3840        G2frame.PickId = item
3841        if G2frame.PatternTree.GetItemText(item) == 'Notebook':
3842            SetDataMenuBar(G2frame,G2frame.dataFrame.DataNotebookMenu)
3843            G2frame.PatternId = 0
3844            for i in G2frame.ExportPattern: i.Enable(False)
3845            data = G2frame.PatternTree.GetItemPyData(item)
3846            UpdateNotebook(G2frame,data)
3847        elif G2frame.PatternTree.GetItemText(item) == 'Controls':
3848            G2frame.PatternId = 0
3849            for i in G2frame.ExportPattern: i.Enable(False)
3850            data = G2frame.PatternTree.GetItemPyData(item)
3851            if not data:           #fill in defaults
3852                data = copy.copy(G2obj.DefaultControls)    #least squares controls
3853                G2frame.PatternTree.SetItemPyData(item,data)                             
3854            for i in G2frame.Refine: i.Enable(True)
3855            for i in G2frame.SeqRefine: i.Enable(True)
3856            UpdateControls(G2frame,data)
3857        elif G2frame.PatternTree.GetItemText(item) == 'Sequential results':
3858            data = G2frame.PatternTree.GetItemPyData(item)
3859            UpdateSeqResults(G2frame,data)           
3860        elif G2frame.PatternTree.GetItemText(item) == 'Covariance':
3861            data = G2frame.PatternTree.GetItemPyData(item)
3862            G2frame.dataFrame.setSizePosLeft(defWid)
3863            text = ''
3864            if 'Rvals' in data:
3865                Nvars = len(data['varyList'])
3866                Rvals = data['Rvals']
3867                text = '\nFinal residuals: \nwR = %.3f%% \nchi**2 = %.1f \nGOF = %.2f'%(Rvals['Rwp'],Rvals['chisq'],Rvals['GOF'])
3868                text += '\nNobs = %d \nNvals = %d'%(Rvals['Nobs'],Nvars)
3869                if 'lamMax' in Rvals:
3870                    text += '\nlog10 MaxLambda = %.1f'%(np.log10(Rvals['lamMax']))
3871            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
3872                value='See plot window for covariance display'+text,style=wx.TE_MULTILINE)
3873            G2plt.PlotCovariance(G2frame,data)
3874        elif G2frame.PatternTree.GetItemText(item) == 'Constraints':
3875            data = G2frame.PatternTree.GetItemPyData(item)
3876            G2cnstG.UpdateConstraints(G2frame,data)
3877        elif G2frame.PatternTree.GetItemText(item) == 'Rigid bodies':
3878            data = G2frame.PatternTree.GetItemPyData(item)
3879            G2cnstG.UpdateRigidBodies(G2frame,data)
3880        elif G2frame.PatternTree.GetItemText(item) == 'Restraints':
3881            data = G2frame.PatternTree.GetItemPyData(item)
3882            Phases = G2frame.GetPhaseData()
3883            phase = ''
3884            phaseName = ''
3885            if Phases:
3886                phaseName = Phases.keys()[0]
3887            G2frame.dataFrame.setSizePosLeft(defWid)
3888            G2restG.UpdateRestraints(G2frame,data,Phases,phaseName)
3889        elif 'IMG' in G2frame.PatternTree.GetItemText(item):
3890            G2frame.Image = item
3891            G2plt.PlotImage(G2frame,newPlot=True)
3892        elif 'PKS' in G2frame.PatternTree.GetItemText(item):
3893            G2plt.PlotPowderLines(G2frame)
3894        elif 'PWDR' in G2frame.PatternTree.GetItemText(item):
3895            for i in G2frame.ExportPattern: i.Enable(True)
3896            UpdatePWHKPlot(G2frame,'PWDR',item)
3897        elif 'SASD' in G2frame.PatternTree.GetItemText(item):
3898            for i in G2frame.ExportPattern: i.Enable(True)
3899            UpdatePWHKPlot(G2frame,'SASD',item)
3900        elif 'HKLF' in G2frame.PatternTree.GetItemText(item):
3901            G2frame.Sngl = item
3902            UpdatePWHKPlot(G2frame,'HKLF',item)
3903        elif 'PDF' in G2frame.PatternTree.GetItemText(item):
3904            G2frame.PatternId = item
3905            for i in G2frame.ExportPDF: i.Enable(True)
3906            G2plt.PlotISFG(G2frame,type='S(Q)')
3907        elif G2frame.PatternTree.GetItemText(item) == 'Phases':
3908            G2frame.dataFrame.setSizePosLeft(defWid)
3909            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
3910                value='Select one phase to see its parameters')           
3911    elif 'I(Q)' in G2frame.PatternTree.GetItemText(item):
3912        G2frame.PickId = item
3913        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3914        G2plt.PlotISFG(G2frame,type='I(Q)',newPlot=True)
3915    elif 'S(Q)' in G2frame.PatternTree.GetItemText(item):
3916        G2frame.PickId = item
3917        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3918        G2plt.PlotISFG(G2frame,type='S(Q)',newPlot=True)
3919    elif 'F(Q)' in G2frame.PatternTree.GetItemText(item):
3920        G2frame.PickId = item
3921        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3922        G2plt.PlotISFG(G2frame,type='F(Q)',newPlot=True)
3923    elif 'G(R)' in G2frame.PatternTree.GetItemText(item):
3924        G2frame.PickId = item
3925        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3926        G2plt.PlotISFG(G2frame,type='G(R)',newPlot=True)           
3927    elif G2frame.PatternTree.GetItemText(parentID) == 'Phases':
3928        G2frame.PickId = item
3929        data = G2frame.PatternTree.GetItemPyData(item)
3930        G2phG.UpdatePhaseData(G2frame,item,data,oldPage)
3931    elif G2frame.PatternTree.GetItemText(item) == 'Comments':
3932        SetDataMenuBar(G2frame,G2frame.dataFrame.DataCommentsMenu)
3933        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3934        G2frame.PickId = item
3935        data = G2frame.PatternTree.GetItemPyData(item)
3936        UpdateComments(G2frame,data)
3937    elif G2frame.PatternTree.GetItemText(item) == 'Image Controls':
3938        G2frame.dataFrame.SetTitle('Image Controls')
3939        G2frame.PickId = item
3940        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3941        masks = G2frame.PatternTree.GetItemPyData(
3942            GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
3943        data = G2frame.PatternTree.GetItemPyData(item)
3944        G2imG.UpdateImageControls(G2frame,data,masks)
3945        G2plt.PlotImage(G2frame)
3946    elif G2frame.PatternTree.GetItemText(item) == 'Masks':
3947        G2frame.dataFrame.SetTitle('Masks')
3948        G2frame.PickId = item
3949        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3950        data = G2frame.PatternTree.GetItemPyData(item)
3951        G2imG.UpdateMasks(G2frame,data)
3952        G2plt.PlotImage(G2frame)
3953    elif G2frame.PatternTree.GetItemText(item) == 'Stress/Strain':
3954        G2frame.dataFrame.SetTitle('Stress/Strain')
3955        G2frame.PickId = item
3956        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3957        data = G2frame.PatternTree.GetItemPyData(item)
3958        G2imG.UpdateStressStrain(G2frame,data)
3959        G2plt.PlotImage(G2frame)
3960        G2plt.PlotStrain(G2frame,data,newPlot=True)
3961    elif G2frame.PatternTree.GetItemText(item) == 'HKL Plot Controls':
3962        G2frame.PickId = item
3963        G2frame.Sngl = G2frame.PatternTree.GetItemParent(item)
3964        data = G2frame.PatternTree.GetItemPyData(item)
3965        UpdateHKLControls(G2frame,data)
3966        G2plt.PlotSngl(G2frame)
3967    elif G2frame.PatternTree.GetItemText(item) == 'PDF Controls':
3968        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3969        for i in G2frame.ExportPDF: i.Enable(True)
3970        G2frame.PickId = item
3971        data = G2frame.PatternTree.GetItemPyData(item)
3972        G2pdG.UpdatePDFGrid(G2frame,data)
3973        G2plt.PlotISFG(G2frame,type='I(Q)')
3974        G2plt.PlotISFG(G2frame,type='S(Q)')
3975        G2plt.PlotISFG(G2frame,type='F(Q)')
3976        G2plt.PlotISFG(G2frame,type='G(R)')
3977    elif G2frame.PatternTree.GetItemText(item) == 'Peak List':
3978        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3979        for i in G2frame.ExportPeakList: i.Enable(True)
3980        G2frame.PickId = item
3981        data = G2frame.PatternTree.GetItemPyData(item)
3982        G2pdG.UpdatePeakGrid(G2frame,data)
3983        G2plt.PlotPatterns(G2frame)
3984    elif G2frame.PatternTree.GetItemText(item) == 'Background':
3985        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3986        G2frame.PickId = item
3987        data = G2frame.PatternTree.GetItemPyData(item)
3988        G2pdG.UpdateBackground(G2frame,data)
3989        G2plt.PlotPatterns(G2frame)
3990    elif G2frame.PatternTree.GetItemText(item) == 'Limits':
3991        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3992        G2frame.PickId = item
3993        data = G2frame.PatternTree.GetItemPyData(item)
3994        G2pdG.UpdateLimitsGrid(G2frame,data)
3995        G2plt.PlotPatterns(G2frame)
3996    elif G2frame.PatternTree.GetItemText(item) == 'Instrument Parameters':
3997        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3998        G2frame.PickId = item
3999        data = G2frame.PatternTree.GetItemPyData(item)[0]
4000        G2pdG.UpdateInstrumentGrid(G2frame,data)
4001        if 'P' in data['Type'][0]:          #powder data only
4002            G2plt.PlotPeakWidths(G2frame)
4003    elif G2frame.PatternTree.GetItemText(item) == 'Models':
4004        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4005        G2frame.PickId = item
4006        data = G2frame.PatternTree.GetItemPyData(item)
4007        G2pdG.UpdateModelsGrid(G2frame,data)
4008    elif G2frame.PatternTree.GetItemText(item) == 'Substances':
4009        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4010        G2frame.PickId = item
4011        data = G2frame.PatternTree.GetItemPyData(item)
4012        G2pdG.UpdateSubstanceGrid(G2frame,data)
4013    elif G2frame.PatternTree.GetItemText(item) == 'Sample Parameters':
4014        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4015        G2frame.PickId = item
4016        data = G2frame.PatternTree.GetItemPyData(item)
4017
4018        if 'Temperature' not in data:           #temp fix for old gpx files
4019            data = {'Scale':[1.0,True],'Type':'Debye-Scherrer','Absorption':[0.0,False],'DisplaceX':[0.0,False],
4020                'DisplaceY':[0.0,False],'Diffuse':[],'Temperature':300.,'Pressure':1.0,
4021                    'FreePrm1':0.,'FreePrm2':0.,'FreePrm3':0.,
4022                    'Gonio. radius':200.0}
4023            G2frame.PatternTree.SetItemPyData(item,data)
4024   
4025        G2pdG.UpdateSampleGrid(G2frame,data)
4026        G2plt.PlotPatterns(G2frame)
4027    elif G2frame.PatternTree.GetItemText(item) == 'Index Peak List':
4028        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4029        for i in G2frame.ExportPeakList: i.Enable(True)
4030        G2frame.PickId = item
4031        data = G2frame.PatternTree.GetItemPyData(item)
4032        G2pdG.UpdateIndexPeaksGrid(G2frame,data)
4033        if 'PKS' in G2frame.PatternTree.GetItemText(G2frame.PatternId):
4034            G2plt.PlotPowderLines(G2frame)
4035        else:
4036            G2plt.PlotPatterns(G2frame)
4037    elif G2frame.PatternTree.GetItemText(item) == 'Unit Cells List':
4038        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4039        G2frame.PickId = item
4040        data = G2frame.PatternTree.GetItemPyData(item)
4041        if not data:
4042            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
4043            data.append([0,0,0,0,0,0,0,0,0,0,0,0,0,0])      #Bravais lattice flags
4044            data.append([])                                 #empty cell list
4045            data.append([])                                 #empty dmin
4046            G2frame.PatternTree.SetItemPyData(item,data)                             
4047        G2pdG.UpdateUnitCellsGrid(G2frame,data)
4048        if 'PKS' in G2frame.PatternTree.GetItemText(G2frame.PatternId):
4049            G2plt.PlotPowderLines(G2frame)
4050        else:
4051            G2plt.PlotPatterns(G2frame)
4052    elif G2frame.PatternTree.GetItemText(item) == 'Reflection Lists':   #powder reflections
4053        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4054        G2frame.PickId = item
4055        data = G2frame.PatternTree.GetItemPyData(item)
4056        G2frame.RefList = ''
4057        if len(data):
4058            G2frame.RefList = data.keys()[0]
4059        G2pdG.UpdateReflectionGrid(G2frame,data)
4060        G2plt.PlotPatterns(G2frame)
4061    elif G2frame.PatternTree.GetItemText(item) == 'Reflection List':    #HKLF reflections
4062        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4063        name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
4064        data = G2frame.PatternTree.GetItemPyData(G2frame.PatternId)
4065        G2pdG.UpdateReflectionGrid(G2frame,data,HKLF=True,Name=name)
4066
4067def SetDataMenuBar(G2frame,menu=None):
4068    '''Set the menu for the data frame. On the Mac put this
4069    menu for the data tree window instead.
4070
4071    Note that data frame items do not have menus, for these (menu=None)
4072    display a blank menu or on the Mac display the standard menu for
4073    the data tree window.
4074    '''
4075    if sys.platform == "darwin":
4076        if menu is None:
4077            G2frame.SetMenuBar(G2frame.GSASIIMenu)
4078        else:
4079            G2frame.SetMenuBar(menu)
4080    else:
4081        if menu is None:
4082            G2frame.dataFrame.SetMenuBar(G2frame.dataFrame.BlankMenu)
4083        else:
4084            G2frame.dataFrame.SetMenuBar(menu)
4085
4086def HorizontalLine(sizer,parent):
4087    '''Draws a horizontal line as wide as the window.
4088    This shows up on the Mac as a very thin line, no matter what I do
4089    '''
4090    line = wx.StaticLine(parent,-1, size=(-1,3), style=wx.LI_HORIZONTAL)
4091    sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.ALL, 10)
4092
4093if __name__ == '__main__':
4094    # test ScrolledMultiEditor
4095    app = wx.PySimpleApp()
4096    frm = wx.Frame(None) # create a frame
4097    frm.Show(True)
4098    Data1 = {
4099        'Order':1,
4100        'omega':'string',
4101        'chi':2.0,
4102        'phi':'',
4103        }
4104    elemlst = sorted(Data1.keys())
4105    postlbl = sorted(Data1.keys())
4106    dictlst = len(elemlst)*[Data1,]
4107
4108    Data2 = list(range(100))
4109    elemlst += range(2,6)
4110    postlbl += range(2,6)
4111    dictlst += len(range(2,6))*[Data2,]
4112
4113    prelbl = range(len(elemlst))
4114    postlbl[1] = "a very long label for the 2nd item to force a horiz. scrollbar"
4115    header="""This is a longer\nmultiline and perhaps silly header"""
4116    dlg = ScrolledMultiEditor(frm,dictlst,elemlst,prelbl,postlbl,
4117                              header=header,CopyButton=True)
4118    print Data1
4119    if dlg.ShowModal() == wx.ID_OK:
4120        for d,k in zip(dictlst,elemlst):
4121            print k,d[k]
4122    dlg.Destroy()
4123    if CallScrolledMultiEditor(frm,dictlst,elemlst,prelbl,postlbl,
4124                               header=header):
4125        for d,k in zip(dictlst,elemlst):
4126            print k,d[k]
4127
4128#app.MainLoop()
Note: See TracBrowser for help on using the repository browser.