source: trunk/GSASIIgrid.py @ 1784

Last change on this file since 1784 was 1784, checked in by vondreele, 7 years ago

Make F refinement the default
Add a clearreject option for HKLF DataFrame?
fix problems with chi2, Rw, etc. for single xtal data - now agrees with old GSAS

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