source: trunk/GSASIIgrid.py @ 1817

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

Add plotting of all HKLF data sets together in one 3D plot

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