source: trunk/GSASIIgrid.py @ 1781

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

fix bug from old Pref.Ori. not having penalty items
Implement colors for bad items in Reflection Lists; Prfo < 0 for PWDR
red Fc for abs(Fo-Fc)/sig > 10 & yellow if > 3.
Implement user selected rejection scheme for HKLF; set mul < 0. Get calculated but not used in least squares

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 185.0 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIgrid - data display routines
3########### SVN repository information ###################
4# $Date: 2015-04-08 15:13:30 +0000 (Wed, 08 Apr 2015) $
5# $Author: vondreele $
6# $Revision: 1781 $
7# $URL: trunk/GSASIIgrid.py $
8# $Id: GSASIIgrid.py 1781 2015-04-08 15:13:30Z 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: 1781 $")
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,
147] = [wx.NewId() for item in range(4)]
148
149[ wxID_PDFCOPYCONTROLS, wxID_PDFSAVECONTROLS, wxID_PDFLOADCONTROLS, 
150    wxID_PDFCOMPUTE, wxID_PDFCOMPUTEALL, wxID_PDFADDELEMENT, wxID_PDFDELELEMENT,
151] = [wx.NewId() for item in range(7)]
152
153[ wxID_MCRON,wxID_MCRLIST,wxID_MCRSAVE,wxID_MCRPLAY,
154] = [wx.NewId() for item in range(4)]
155
156VERY_LIGHT_GREY = wx.Colour(235,235,235)
157################################################################################
158#### GSAS-II class definitions
159################################################################################
160
161class SGMessageBox(wx.Dialog):
162    ''' Special version of MessageBox that displays space group & super space group text
163    in two blocks
164    '''
165    def __init__(self,parent,title,text,table,):
166        wx.Dialog.__init__(self,parent,wx.ID_ANY,title,pos=wx.DefaultPosition,
167            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
168        self.text=text
169        self.table = table
170        self.panel = wx.Panel(self)
171        mainSizer = wx.BoxSizer(wx.VERTICAL)
172        mainSizer.Add((0,10))
173        for line in text:
174            mainSizer.Add(wx.StaticText(self.panel,label='     %s     '%(line)),0,WACV)
175        ncol = self.table[0].count(',')+1
176        tableSizer = wx.FlexGridSizer(0,2*ncol+3,0,0)
177        for j,item in enumerate(self.table):
178            num,flds = item.split(')')
179            tableSizer.Add(wx.StaticText(self.panel,label='     %s  '%(num+')')),0,WACV|wx.ALIGN_LEFT)           
180            flds = flds.replace(' ','').split(',')
181            for i,fld in enumerate(flds):
182                if i < ncol-1:
183                    tableSizer.Add(wx.StaticText(self.panel,label='%s, '%(fld)),0,WACV|wx.ALIGN_RIGHT)
184                else:
185                    tableSizer.Add(wx.StaticText(self.panel,label='%s'%(fld)),0,WACV|wx.ALIGN_RIGHT)
186            if not j%2:
187                tableSizer.Add((20,0))
188        mainSizer.Add(tableSizer,0,wx.ALIGN_LEFT)
189        btnsizer = wx.StdDialogButtonSizer()
190        OKbtn = wx.Button(self.panel, wx.ID_OK)
191        OKbtn.SetDefault()
192        btnsizer.AddButton(OKbtn)
193        btnsizer.Realize()
194        mainSizer.Add((0,10))
195        mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER)
196        self.panel.SetSizer(mainSizer)
197        self.panel.Fit()
198        self.Fit()
199        size = self.GetSize()
200        self.SetSize([size[0]+20,size[1]])
201
202    def Show(self):
203        '''Use this method after creating the dialog to post it
204        '''
205        self.ShowModal()
206        return
207       
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 selected HKL',
1488            help='Reject selected HKL; make mul < 0')
1489        self.PostfillDataMenu()
1490       
1491        # SASD / Instrument Parameters
1492        self.SASDInstMenu = wx.MenuBar()
1493        self.PrefillDataMenu(self.SASDInstMenu,helpType='Instrument Parameters')
1494        self.SASDInstEdit = wx.Menu(title='')
1495        self.SASDInstMenu.Append(menu=self.SASDInstEdit, title='Operations')
1496        self.InstEdit.Append(help='Reset instrument profile parameters to default', 
1497            id=wxID_INSTPRMRESET, kind=wx.ITEM_NORMAL,text='Reset profile')
1498        self.SASDInstEdit.Append(help='Copy instrument profile parameters to other histograms', 
1499            id=wxID_INSTCOPY, kind=wx.ITEM_NORMAL,text='Copy')
1500        self.PostfillDataMenu()
1501       
1502        #SASD & REFL/ Substance editor
1503        self.SubstanceMenu = wx.MenuBar()
1504        self.PrefillDataMenu(self.SubstanceMenu,helpType='Substances')
1505        self.SubstanceEdit = wx.Menu(title='')
1506        self.SubstanceMenu.Append(menu=self.SubstanceEdit, title='Edit')
1507        self.SubstanceEdit.Append(id=wxID_LOADSUBSTANCE, kind=wx.ITEM_NORMAL,text='Load substance',
1508            help='Load substance from file')
1509        self.SubstanceEdit.Append(id=wxID_ADDSUBSTANCE, kind=wx.ITEM_NORMAL,text='Add substance',
1510            help='Add new substance to list')
1511        self.SubstanceEdit.Append(id=wxID_COPYSUBSTANCE, kind=wx.ITEM_NORMAL,text='Copy substances',
1512            help='Copy substances')
1513        self.SubstanceEdit.Append(id=wxID_DELETESUBSTANCE, kind=wx.ITEM_NORMAL,text='Delete substance',
1514            help='Delete substance from list')           
1515        self.SubstanceEdit.Append(id=wxID_ELEMENTADD, kind=wx.ITEM_NORMAL,text='Add elements',
1516            help='Add elements to substance')
1517        self.SubstanceEdit.Append(id=wxID_ELEMENTDELETE, kind=wx.ITEM_NORMAL,text='Delete elements',
1518            help='Delete elements from substance')
1519        self.PostfillDataMenu()
1520       
1521        # SASD/ Models
1522        self.ModelMenu = wx.MenuBar()
1523        self.PrefillDataMenu(self.ModelMenu,helpType='Models')
1524        self.ModelEdit = wx.Menu(title='')
1525        self.ModelMenu.Append(menu=self.ModelEdit, title='Models')
1526        self.ModelEdit.Append(id=wxID_MODELADD,kind=wx.ITEM_NORMAL,text='Add',
1527            help='Add new term to model')
1528        self.ModelEdit.Append(id=wxID_MODELFIT, kind=wx.ITEM_NORMAL,text='Fit',
1529            help='Fit model parameters to data')
1530        self.SasdUndo = self.ModelEdit.Append(id=wxID_MODELUNDO, kind=wx.ITEM_NORMAL,text='Undo',
1531            help='Undo model fit')
1532        self.SasdUndo.Enable(False)           
1533        self.ModelEdit.Append(id=wxID_MODELFITALL, kind=wx.ITEM_NORMAL,text='Sequential fit',
1534            help='Sequential fit of model parameters to all SASD data')
1535        self.ModelEdit.Append(id=wxID_MODELCOPY, kind=wx.ITEM_NORMAL,text='Copy',
1536            help='Copy model parameters to other histograms')
1537        self.ModelEdit.Append(id=wxID_MODELCOPYFLAGS, kind=wx.ITEM_NORMAL,text='Copy flags',
1538            help='Copy model refinement flags to other histograms')
1539        self.PostfillDataMenu()
1540       
1541        # IMG / Image Controls
1542        self.ImageMenu = wx.MenuBar()
1543        self.PrefillDataMenu(self.ImageMenu,helpType='Image Controls')
1544        self.ImageEdit = wx.Menu(title='')
1545        self.ImageMenu.Append(menu=self.ImageEdit, title='Operations')
1546        self.ImageEdit.Append(help='Calibrate detector by fitting to calibrant lines', 
1547            id=wxID_IMCALIBRATE, kind=wx.ITEM_NORMAL,text='Calibrate')
1548        self.ImageEdit.Append(help='Recalibrate detector by fitting to calibrant lines', 
1549            id=wxID_IMRECALIBRATE, kind=wx.ITEM_NORMAL,text='Recalibrate')
1550        self.ImageEdit.Append(help='Clear calibration data points and rings',id=wxID_IMCLEARCALIB, 
1551            kind=wx.ITEM_NORMAL,text='Clear calibration')
1552        self.ImageEdit.Append(help='Integrate selected image',id=wxID_IMINTEGRATE, 
1553            kind=wx.ITEM_NORMAL,text='Integrate')
1554        self.ImageEdit.Append(help='Integrate all images selected from list',id=wxID_INTEGRATEALL,
1555            kind=wx.ITEM_NORMAL,text='Integrate all')
1556        self.ImageEdit.Append(help='Copy image controls to other images', 
1557            id=wxID_IMCOPYCONTROLS, kind=wx.ITEM_NORMAL,text='Copy Controls')
1558        self.ImageEdit.Append(help='Save image controls to file', 
1559            id=wxID_IMSAVECONTROLS, kind=wx.ITEM_NORMAL,text='Save Controls')
1560        self.ImageEdit.Append(help='Load image controls from file', 
1561            id=wxID_IMLOADCONTROLS, kind=wx.ITEM_NORMAL,text='Load Controls')
1562        self.PostfillDataMenu()
1563           
1564        # IMG / Masks
1565        self.MaskMenu = wx.MenuBar()
1566        self.PrefillDataMenu(self.MaskMenu,helpType='Image Masks')
1567        self.MaskEdit = wx.Menu(title='')
1568        self.MaskMenu.Append(menu=self.MaskEdit, title='Operations')
1569        submenu = wx.Menu()
1570        self.MaskEdit.AppendMenu(
1571            wx.ID_ANY,'Create new', submenu,
1572            help=''
1573            )
1574        self.MaskEdit.Append(help='Copy mask to other images', 
1575            id=wxID_MASKCOPY, kind=wx.ITEM_NORMAL,text='Copy mask')
1576        self.MaskEdit.Append(help='Save mask to file', 
1577            id=wxID_MASKSAVE, kind=wx.ITEM_NORMAL,text='Save mask')
1578        self.MaskEdit.Append(help='Load mask from file', 
1579            id=wxID_MASKLOAD, kind=wx.ITEM_NORMAL,text='Load mask')
1580        self.MaskEdit.Append(help='Load mask from file; ignore threshold', 
1581            id=wxID_MASKLOADNOT, kind=wx.ITEM_NORMAL,text='Load mask w/o threshold')
1582        submenu.Append(help='Create an arc mask with mouse input', 
1583            id=wxID_NEWMASKARC, kind=wx.ITEM_NORMAL,text='Arc mask')
1584        submenu.Append(help='Create a frame mask with mouse input', 
1585            id=wxID_NEWMASKFRAME, kind=wx.ITEM_NORMAL,text='Frame mask')
1586        submenu.Append(help='Create a polygon mask with mouse input', 
1587            id=wxID_NEWMASKPOLY, kind=wx.ITEM_NORMAL,text='Polygon mask')
1588        submenu.Append(help='Create a ring mask with mouse input', 
1589            id=wxID_NEWMASKRING, kind=wx.ITEM_NORMAL,text='Ring mask')
1590        submenu.Append(help='Create a spot mask with mouse input', 
1591            id=wxID_NEWMASKSPOT, kind=wx.ITEM_NORMAL,text='Spot mask')
1592        self.PostfillDataMenu()
1593           
1594        # IMG / Stress/Strain
1595        self.StrStaMenu = wx.MenuBar()
1596        self.PrefillDataMenu(self.StrStaMenu,helpType='Stress/Strain')
1597        self.StrStaEdit = wx.Menu(title='')
1598        self.StrStaMenu.Append(menu=self.StrStaEdit, title='Operations')
1599        self.StrStaEdit.Append(help='Append d-zero for one ring', 
1600            id=wxID_APPENDDZERO, kind=wx.ITEM_NORMAL,text='Append d-zero')
1601        self.StrStaEdit.Append(help='Fit stress/strain data', 
1602            id=wxID_STRSTAFIT, kind=wx.ITEM_NORMAL,text='Fit stress/strain')
1603        self.StrStaEdit.Append(help='Update d-zero from ave d-zero',
1604            id=wxID_UPDATEDZERO, kind=wx.ITEM_NORMAL,text='Update d-zero')       
1605        self.StrStaEdit.Append(help='Fit stress/strain data for all images', 
1606            id=wxID_STRSTAALLFIT, kind=wx.ITEM_NORMAL,text='All image fit')
1607        self.StrStaEdit.Append(help='Copy stress/strain data to other images', 
1608            id=wxID_STRSTACOPY, kind=wx.ITEM_NORMAL,text='Copy stress/strain')
1609        self.StrStaEdit.Append(help='Save stress/strain data to file', 
1610            id=wxID_STRSTASAVE, kind=wx.ITEM_NORMAL,text='Save stress/strain')
1611        self.StrStaEdit.Append(help='Load stress/strain data from file', 
1612            id=wxID_STRSTALOAD, kind=wx.ITEM_NORMAL,text='Load stress/strain')
1613        self.StrStaEdit.Append(help='Load sample data from file', 
1614            id=wxID_STRSTSAMPLE, kind=wx.ITEM_NORMAL,text='Load sample data')
1615        self.PostfillDataMenu()
1616           
1617        # PDF / PDF Controls
1618        self.PDFMenu = wx.MenuBar()
1619        self.PrefillDataMenu(self.PDFMenu,helpType='PDF Controls')
1620        self.PDFEdit = wx.Menu(title='')
1621        self.PDFMenu.Append(menu=self.PDFEdit, title='PDF Controls')
1622        self.PDFEdit.Append(help='Add element to sample composition',id=wxID_PDFADDELEMENT, kind=wx.ITEM_NORMAL,
1623            text='Add element')
1624        self.PDFEdit.Append(help='Delete element from sample composition',id=wxID_PDFDELELEMENT, kind=wx.ITEM_NORMAL,
1625            text='Delete element')
1626        self.PDFEdit.Append(help='Copy PDF controls', id=wxID_PDFCOPYCONTROLS, kind=wx.ITEM_NORMAL,
1627            text='Copy controls')
1628        self.PDFEdit.Append(help='Load PDF controls from file',id=wxID_PDFLOADCONTROLS, kind=wx.ITEM_NORMAL,
1629            text='Load Controls')
1630        self.PDFEdit.Append(help='Save PDF controls to file', id=wxID_PDFSAVECONTROLS, kind=wx.ITEM_NORMAL,
1631            text='Save controls')
1632        self.PDFEdit.Append(help='Compute PDF', id=wxID_PDFCOMPUTE, kind=wx.ITEM_NORMAL,
1633            text='Compute PDF')
1634        self.PDFEdit.Append(help='Compute all PDFs', id=wxID_PDFCOMPUTEALL, kind=wx.ITEM_NORMAL,
1635            text='Compute all PDFs')
1636        self.PostfillDataMenu()
1637       
1638        # Phase / General tab
1639        self.DataGeneral = wx.MenuBar()
1640        self.PrefillDataMenu(self.DataGeneral,helpType='General', helpLbl='Phase/General')
1641        self.DataGeneral.Append(menu=wx.Menu(title=''),title='Select tab')
1642        self.GeneralCalc = wx.Menu(title='')
1643        self.DataGeneral.Append(menu=self.GeneralCalc,title='Compute')
1644        self.GeneralCalc.Append(help='Compute Fourier map',id=wxID_FOURCALC, kind=wx.ITEM_NORMAL,
1645            text='Fourier map')
1646        self.GeneralCalc.Append(help='Search Fourier map',id=wxID_FOURSEARCH, kind=wx.ITEM_NORMAL,
1647            text='Search map')
1648        self.GeneralCalc.Append(help='Run charge flipping',id=wxID_CHARGEFLIP, kind=wx.ITEM_NORMAL,
1649            text='Charge flipping')
1650        self.GeneralCalc.Append(help='Run 4D charge flipping',id=wxID_4DCHARGEFLIP, kind=wx.ITEM_NORMAL,
1651            text='4D Charge flipping')
1652        self.GeneralCalc.Enable(wxID_4DCHARGEFLIP,False)   
1653        self.GeneralCalc.Append(help='Clear map',id=wxID_FOURCLEAR, kind=wx.ITEM_NORMAL,
1654            text='Clear map')
1655        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing',id=wxID_SINGLEMCSA, kind=wx.ITEM_NORMAL,
1656            text='MC/SA')
1657        self.GeneralCalc.Append(help='Run Monte Carlo - Simulated Annealing on multiprocessors',id=wxID_MULTIMCSA, kind=wx.ITEM_NORMAL,
1658            text='Multi MC/SA')            #currently not useful
1659        self.PostfillDataMenu()
1660       
1661        # Phase / Data tab
1662        self.DataMenu = wx.MenuBar()
1663        self.PrefillDataMenu(self.DataMenu,helpType='Data', helpLbl='Phase/Data')
1664        self.DataMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1665        self.DataEdit = wx.Menu(title='')
1666        self.DataMenu.Append(menu=self.DataEdit, title='Edit')
1667        self.DataEdit.Append(id=wxID_DATACOPY, kind=wx.ITEM_NORMAL,text='Copy data',
1668            help='Copy phase data to other histograms')
1669        self.DataEdit.Append(id=wxID_DATACOPYFLAGS, kind=wx.ITEM_NORMAL,text='Copy flags',
1670            help='Copy phase data flags to other histograms')
1671        self.DataEdit.Append(id=wxID_DATASELCOPY, kind=wx.ITEM_NORMAL,text='Copy selected data',
1672            help='Copy selected phase data to other histograms')
1673        self.DataEdit.Append(id=wxID_PWDRADD, kind=wx.ITEM_NORMAL,text='Add powder histograms',
1674            help='Select new powder histograms to be used for this phase')
1675        self.DataEdit.Append(id=wxID_HKLFADD, kind=wx.ITEM_NORMAL,text='Add single crystal histograms',
1676            help='Select new single crystal histograms to be used for this phase')
1677        self.DataEdit.Append(id=wxID_DATADELETE, kind=wx.ITEM_NORMAL,text='Remove histograms',
1678            help='Remove histograms from use for this phase')
1679        self.PostfillDataMenu()
1680           
1681        # Phase / Atoms tab
1682        self.AtomsMenu = wx.MenuBar()
1683        self.PrefillDataMenu(self.AtomsMenu,helpType='Atoms')
1684        self.AtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1685        self.AtomEdit = wx.Menu(title='')
1686        self.AtomCompute = wx.Menu(title='')
1687        self.AtomsMenu.Append(menu=self.AtomEdit, title='Edit')
1688        self.AtomsMenu.Append(menu=self.AtomCompute, title='Compute')
1689        self.AtomEdit.Append(id=wxID_ATOMSEDITADD, kind=wx.ITEM_NORMAL,text='Append atom',
1690            help='Appended as an H atom')
1691        self.AtomEdit.Append(id=wxID_ATOMSVIEWADD, kind=wx.ITEM_NORMAL,text='Append view point',
1692            help='Appended as an H atom')
1693        self.AtomEdit.Append(id=wxID_ATOMSEDITINSERT, kind=wx.ITEM_NORMAL,text='Insert atom',
1694            help='Select atom row to insert before; inserted as an H atom')
1695        self.AtomEdit.Append(id=wxID_ATOMVIEWINSERT, kind=wx.ITEM_NORMAL,text='Insert view point',
1696            help='Select atom row to insert before; inserted as an H atom')
1697        self.AtomEdit.Append(id=wxID_ATOMMOVE, kind=wx.ITEM_NORMAL,text='Move atom to view point',
1698            help='Select single atom to move')
1699        self.AtomEdit.Append(id=wxID_ATOMSEDITDELETE, kind=wx.ITEM_NORMAL,text='Delete atom',
1700            help='Select atoms to delete first')
1701        self.AtomEdit.Append(id=wxID_ATOMSREFINE, kind=wx.ITEM_NORMAL,text='Set atom refinement flags',
1702            help='Select atoms to refine first')
1703        self.AtomEdit.Append(id=wxID_ATOMSMODIFY, kind=wx.ITEM_NORMAL,text='Modify atom parameters',
1704            help='Select atoms to modify first')
1705        self.AtomEdit.Append(id=wxID_ATOMSTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform atoms',
1706            help='Select atoms to transform first')
1707        self.AtomEdit.Append(id=wxID_MAKEMOLECULE, kind=wx.ITEM_NORMAL,text='Assemble molecule',
1708            help='Assemble molecule from scatterd atom positions')
1709        self.AtomEdit.Append(id=wxID_RELOADDRAWATOMS, kind=wx.ITEM_NORMAL,text='Reload draw atoms',
1710            help='Reload atom drawing list')
1711        submenu = wx.Menu()
1712        self.AtomEdit.AppendMenu(wx.ID_ANY, 'Reimport atoms', submenu, 
1713            help='Reimport atoms from file; sequence must match')
1714        # setup a cascade menu for the formats that have been defined
1715        self.ReImportMenuId = {}  # points to readers for each menu entry
1716        for reader in self.G2frame.ImportPhaseReaderlist:
1717            item = submenu.Append(
1718                wx.ID_ANY,help=reader.longFormatName,
1719                kind=wx.ITEM_NORMAL,text='reimport coordinates from '+reader.formatName+' file')
1720            self.ReImportMenuId[item.GetId()] = reader
1721        item = submenu.Append(
1722            wx.ID_ANY,
1723            help='Reimport coordinates, try to determine format from file',
1724            kind=wx.ITEM_NORMAL,
1725            text='guess format from file')
1726        self.ReImportMenuId[item.GetId()] = None # try all readers
1727
1728        self.AtomCompute.Append(id=wxID_ATOMSDISAGL, kind=wx.ITEM_NORMAL,text='Show Distances && Angles',
1729            help='Compute distances & angles for selected atoms')
1730        self.AtomCompute.Append(id=wxID_ATOMSPDISAGL, kind=wx.ITEM_NORMAL,text='Save Distances && Angles',
1731            help='Compute distances & angles for selected atoms')
1732        self.AtomCompute.ISOcalc = self.AtomCompute.Append(
1733            id=wxID_ISODISP, kind=wx.ITEM_NORMAL,
1734            text='Compute ISODISTORT mode values',
1735            help='Compute values of ISODISTORT modes from atom parameters')
1736        self.PostfillDataMenu()
1737       
1738        # Phase / Imcommensurate "waves" tab
1739        self.WavesData = wx.MenuBar()
1740        self.PrefillDataMenu(self.WavesData,helpType='Wave Data', helpLbl='Imcommensurate wave data')
1741        self.WavesData.Append(menu=wx.Menu(title=''),title='Select tab')
1742        self.WavesDataCompute = wx.Menu(title='')
1743        self.WavesData.Append(menu=self.WavesDataCompute,title='Compute')
1744        self.WavesDataCompute.Append(id=wxID_4DMAPCOMPUTE, kind=wx.ITEM_NORMAL,text='Compute 4D map',
1745            help='Compute 4-dimensional map')
1746        self.PostfillDataMenu()
1747                 
1748        # Phase / Draw Options tab
1749        self.DataDrawOptions = wx.MenuBar()
1750        self.PrefillDataMenu(self.DataDrawOptions,helpType='Draw Options', helpLbl='Phase/Draw Options')
1751        self.DataDrawOptions.Append(menu=wx.Menu(title=''),title='Select tab')
1752        self.PostfillDataMenu()
1753       
1754        # Phase / Draw Atoms tab
1755        self.DrawAtomsMenu = wx.MenuBar()
1756        self.PrefillDataMenu(self.DrawAtomsMenu,helpType='Draw Atoms')
1757        self.DrawAtomsMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1758        self.DrawAtomEdit = wx.Menu(title='')
1759        self.DrawAtomCompute = wx.Menu(title='')
1760        self.DrawAtomRestraint = wx.Menu(title='')
1761        self.DrawAtomRigidBody = wx.Menu(title='')
1762        self.DrawAtomsMenu.Append(menu=self.DrawAtomEdit, title='Edit')
1763        self.DrawAtomsMenu.Append(menu=self.DrawAtomCompute,title='Compute')
1764        self.DrawAtomsMenu.Append(menu=self.DrawAtomRestraint, title='Restraints')
1765        self.DrawAtomsMenu.Append(menu=self.DrawAtomRigidBody, title='Rigid body')
1766        self.DrawAtomEdit.Append(id=wxID_DRAWATOMSTYLE, kind=wx.ITEM_NORMAL,text='Atom style',
1767            help='Select atoms first')
1768        self.DrawAtomEdit.Append(id=wxID_DRAWATOMLABEL, kind=wx.ITEM_NORMAL,text='Atom label',
1769            help='Select atoms first')
1770        self.DrawAtomEdit.Append(id=wxID_DRAWATOMCOLOR, kind=wx.ITEM_NORMAL,text='Atom color',
1771            help='Select atoms first')
1772        self.DrawAtomEdit.Append(id=wxID_DRAWATOMRESETCOLOR, kind=wx.ITEM_NORMAL,text='Reset atom colors',
1773            help='Resets all atom colors to defaults')
1774        self.DrawAtomEdit.Append(id=wxID_DRAWVIEWPOINT, kind=wx.ITEM_NORMAL,text='View point',
1775            help='View point is 1st atom selected')
1776        self.DrawAtomEdit.Append(id=wxID_DRAWADDEQUIV, kind=wx.ITEM_NORMAL,text='Add atoms',
1777            help='Add symmetry & cell equivalents to drawing set from selected atoms')
1778        self.DrawAtomEdit.Append(id=wxID_DRAWTRANSFORM, kind=wx.ITEM_NORMAL,text='Transform draw atoms',
1779            help='Transform selected atoms by symmetry & cell translations')
1780        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCOORD, kind=wx.ITEM_NORMAL,text='Fill CN-sphere',
1781            help='Fill coordination sphere for selected atoms')           
1782        self.DrawAtomEdit.Append(id=wxID_DRAWFILLCELL, kind=wx.ITEM_NORMAL,text='Fill unit cell',
1783            help='Fill unit cell with selected atoms')
1784        self.DrawAtomEdit.Append(id=wxID_DRAWDELETE, kind=wx.ITEM_NORMAL,text='Delete atoms',
1785            help='Delete atoms from drawing set')
1786        self.DrawAtomCompute.Append(id=wxID_DRAWDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
1787            help='Compute distance of selected atoms from view point')   
1788        self.DrawAtomCompute.Append(id=wxID_DRAWDISAGLTOR, kind=wx.ITEM_NORMAL,text='Dist. Ang. Tors.',
1789            help='Compute distance, angle or torsion for 2-4 selected atoms')   
1790        self.DrawAtomCompute.Append(id=wxID_DRAWPLANE, kind=wx.ITEM_NORMAL,text='Best plane',
1791            help='Compute best plane for 4+ selected atoms')   
1792        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRBOND, kind=wx.ITEM_NORMAL,text='Add bond restraint',
1793            help='Add bond restraint for selected atoms (2)')
1794        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRANGLE, kind=wx.ITEM_NORMAL,text='Add angle restraint',
1795            help='Add angle restraint for selected atoms (3: one end 1st)')
1796        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRPLANE, kind=wx.ITEM_NORMAL,text='Add plane restraint',
1797            help='Add plane restraint for selected atoms (4+)')
1798        self.DrawAtomRestraint.Append(id=wxID_DRAWRESTRCHIRAL, kind=wx.ITEM_NORMAL,text='Add chiral restraint',
1799            help='Add chiral restraint for selected atoms (4: center atom 1st)')
1800        self.DrawAtomRigidBody.Append(id=wxID_DRAWDEFINERB, kind=wx.ITEM_NORMAL,text='Define rigid body',
1801            help='Define rigid body with selected atoms')
1802        self.PostfillDataMenu()
1803
1804        # Phase / MCSA tab
1805        self.MCSAMenu = wx.MenuBar()
1806        self.PrefillDataMenu(self.MCSAMenu,helpType='MC/SA')
1807        self.MCSAMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1808        self.MCSAEdit = wx.Menu(title='')
1809        self.MCSAMenu.Append(menu=self.MCSAEdit, title='MC/SA')
1810        self.MCSAEdit.Append(id=wxID_ADDMCSAATOM, kind=wx.ITEM_NORMAL,text='Add atom', 
1811            help='Add single atom to MC/SA model')
1812        self.MCSAEdit.Append(id=wxID_ADDMCSARB, kind=wx.ITEM_NORMAL,text='Add rigid body', 
1813            help='Add rigid body to MC/SA model' )
1814        self.MCSAEdit.Append(id=wxID_CLEARMCSARB, kind=wx.ITEM_NORMAL,text='Clear rigid bodies', 
1815            help='Clear all atoms & rigid bodies from MC/SA model' )
1816        self.MCSAEdit.Append(id=wxID_MOVEMCSA, kind=wx.ITEM_NORMAL,text='Move MC/SA solution', 
1817            help='Move MC/SA solution to atom list' )
1818        self.MCSAEdit.Append(id=wxID_MCSACLEARRESULTS, kind=wx.ITEM_NORMAL,text='Clear results', 
1819            help='Clear table of MC/SA results' )
1820        self.PostfillDataMenu()
1821           
1822        # Phase / Texture tab
1823        self.TextureMenu = wx.MenuBar()
1824        self.PrefillDataMenu(self.TextureMenu,helpType='Texture')
1825        self.TextureMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1826        self.TextureEdit = wx.Menu(title='')
1827        self.TextureMenu.Append(menu=self.TextureEdit, title='Texture')
1828        self.TextureEdit.Append(id=wxID_REFINETEXTURE, kind=wx.ITEM_NORMAL,text='Refine texture', 
1829            help='Refine the texture coefficients from sequential results')
1830        self.TextureEdit.Append(id=wxID_CLEARTEXTURE, kind=wx.ITEM_NORMAL,text='Clear texture', 
1831            help='Clear the texture coefficients' )
1832        self.PostfillDataMenu()
1833           
1834        # Phase / Pawley tab
1835        self.PawleyMenu = wx.MenuBar()
1836        self.PrefillDataMenu(self.PawleyMenu,helpType='Pawley')
1837        self.PawleyMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1838        self.PawleyEdit = wx.Menu(title='')
1839        self.PawleyMenu.Append(menu=self.PawleyEdit,title='Operations')
1840        self.PawleyEdit.Append(id=wxID_PAWLEYLOAD, kind=wx.ITEM_NORMAL,text='Pawley create',
1841            help='Initialize Pawley reflection list')
1842        self.PawleyEdit.Append(id=wxID_PAWLEYESTIMATE, kind=wx.ITEM_NORMAL,text='Pawley estimate',
1843            help='Estimate initial Pawley intensities')
1844        self.PawleyEdit.Append(id=wxID_PAWLEYUPDATE, kind=wx.ITEM_NORMAL,text='Pawley update',
1845            help='Update negative Pawley intensities with -0.5*Fobs and turn off refinemnt')
1846        self.PostfillDataMenu()
1847           
1848        # Phase / Map peaks tab
1849        self.MapPeaksMenu = wx.MenuBar()
1850        self.PrefillDataMenu(self.MapPeaksMenu,helpType='Map peaks')
1851        self.MapPeaksMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1852        self.MapPeaksEdit = wx.Menu(title='')
1853        self.MapPeaksMenu.Append(menu=self.MapPeaksEdit, title='Map peaks')
1854        self.MapPeaksEdit.Append(id=wxID_PEAKSMOVE, kind=wx.ITEM_NORMAL,text='Move peaks', 
1855            help='Move selected peaks to atom list')
1856        self.MapPeaksEdit.Append(id=wxID_PEAKSVIEWPT, kind=wx.ITEM_NORMAL,text='View point',
1857            help='View point is 1st peak selected')
1858        self.MapPeaksEdit.Append(id=wxID_PEAKSDISTVP, kind=wx.ITEM_NORMAL,text='View pt. dist.',
1859            help='Compute distance of selected peaks from view point')   
1860        self.MapPeaksEdit.Append(id=wxID_SHOWBONDS, kind=wx.ITEM_NORMAL,text='Hide bonds',
1861            help='Hide or show bonds between peak positions')   
1862        self.MapPeaksEdit.Append(id=wxID_PEAKSDA, kind=wx.ITEM_NORMAL,text='Calc dist/ang', 
1863            help='Calculate distance or angle for selection')
1864        self.MapPeaksEdit.Append(id=wxID_FINDEQVPEAKS, kind=wx.ITEM_NORMAL,text='Equivalent peaks', 
1865            help='Find equivalent peaks')
1866        self.MapPeaksEdit.Append(id=wxID_PEAKSUNIQUE, kind=wx.ITEM_NORMAL,text='Unique peaks', 
1867            help='Select unique set')
1868        self.MapPeaksEdit.Append(id=wxID_PEAKSDELETE, kind=wx.ITEM_NORMAL,text='Delete peaks', 
1869            help='Delete selected peaks')
1870        self.MapPeaksEdit.Append(id=wxID_PEAKSCLEAR, kind=wx.ITEM_NORMAL,text='Clear peaks', 
1871            help='Clear the map peak list')
1872        self.PostfillDataMenu()
1873
1874        # Phase / Rigid bodies tab
1875        self.RigidBodiesMenu = wx.MenuBar()
1876        self.PrefillDataMenu(self.RigidBodiesMenu,helpType='Rigid bodies')
1877        self.RigidBodiesMenu.Append(menu=wx.Menu(title=''),title='Select tab')
1878        self.RigidBodiesEdit = wx.Menu(title='')
1879        self.RigidBodiesMenu.Append(menu=self.RigidBodiesEdit, title='Edit')
1880        self.RigidBodiesEdit.Append(id=wxID_ASSIGNATMS2RB, kind=wx.ITEM_NORMAL,text='Assign atoms to rigid body',
1881            help='Select & position rigid body in structure of existing atoms')
1882        self.RigidBodiesEdit.Append(id=wxID_AUTOFINDRESRB, kind=wx.ITEM_NORMAL,text='Auto find residues',
1883            help='Auto find of residue RBs in macromolecule')
1884        self.RigidBodiesEdit.Append(id=wxID_COPYRBPARMS, kind=wx.ITEM_NORMAL,text='Copy rigid body parms',
1885            help='Copy rigid body location & TLS parameters')
1886        self.RigidBodiesEdit.Append(id=wxID_GLOBALTHERM, kind=wx.ITEM_NORMAL,text='Global thermal motion',
1887            help='Global setting of residue thermal motion models')
1888        self.RigidBodiesEdit.Append(id=wxID_GLOBALRESREFINE, kind=wx.ITEM_NORMAL,text='Global residue refine',
1889            help='Global setting of residue RB refinement flags')
1890        self.RigidBodiesEdit.Append(id=wxID_RBREMOVEALL, kind=wx.ITEM_NORMAL,text='Remove all rigid bodies',
1891            help='Remove all rigid body assignment for atoms')
1892        self.PostfillDataMenu()
1893    # end of GSAS-II menu definitions
1894       
1895    def _init_ctrls(self, parent,name=None,size=None,pos=None):
1896        wx.Frame.__init__(
1897            self,parent=parent,
1898            #style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX | wx.FRAME_FLOAT_ON_PARENT ,
1899            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX,
1900            size=size,pos=pos,title='GSAS-II data display')
1901        self._init_menus()
1902        if name:
1903            self.SetLabel(name)
1904        self.Show()
1905       
1906    def __init__(self,parent,frame,data=None,name=None, size=None,pos=None):
1907        self.G2frame = frame
1908        self._init_ctrls(parent,name,size,pos)
1909        self.data = data
1910        clientSize = wx.ClientDisplayRect()
1911        Size = self.GetSize()
1912        xPos = clientSize[2]-Size[0]
1913        self.SetPosition(wx.Point(xPos,clientSize[1]+250))
1914        self.AtomGrid = []
1915        self.selectedRow = 0
1916       
1917    def setSizePosLeft(self,Width):
1918        clientSize = wx.ClientDisplayRect()
1919        Width[1] = min(Width[1],clientSize[2]-300)
1920        Width[0] = max(Width[0],300)
1921        self.SetSize(Width)
1922#        self.SetPosition(wx.Point(clientSize[2]-Width[0],clientSize[1]+250))
1923       
1924    def Clear(self):
1925        self.ClearBackground()
1926        self.DestroyChildren()
1927                   
1928################################################################################
1929#####  GSNotebook
1930################################################################################           
1931       
1932class GSNoteBook(wx.aui.AuiNotebook):
1933    '''Notebook used in various locations; implemented with wx.aui extension
1934    '''
1935    def __init__(self, parent, name='',size = None):
1936        wx.aui.AuiNotebook.__init__(self, parent, -1,
1937                                    style=wx.aui.AUI_NB_TOP |
1938                                    wx.aui.AUI_NB_SCROLL_BUTTONS)
1939        if size: self.SetSize(size)
1940        self.parent = parent
1941        self.PageChangeHandler = None
1942       
1943    def PageChangeEvent(self,event):
1944        G2frame = self.parent.G2frame
1945        page = event.GetSelection()
1946        if self.PageChangeHandler:
1947            if log.LogInfo['Logging']:
1948                log.MakeTabLog(
1949                    G2frame.dataFrame.GetTitle(),
1950                    G2frame.dataDisplay.GetPageText(page)
1951                    )
1952            self.PageChangeHandler(event)
1953           
1954    def Bind(self,eventtype,handler,*args,**kwargs):
1955        '''Override the Bind() function so that page change events can be trapped
1956        '''
1957        if eventtype == wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED:
1958            self.PageChangeHandler = handler
1959            wx.aui.AuiNotebook.Bind(self,eventtype,self.PageChangeEvent)
1960            return
1961        wx.aui.AuiNotebook.Bind(self,eventtype,handler,*args,**kwargs)
1962                                                     
1963    def Clear(self):       
1964        GSNoteBook.DeleteAllPages(self)
1965       
1966    def FindPage(self,name):
1967        numPage = self.GetPageCount()
1968        for page in range(numPage):
1969            if self.GetPageText(page) == name:
1970                return page
1971
1972    def ChangeSelection(self,page):
1973        # in wx.Notebook ChangeSelection is like SetSelection, but it
1974        # does not invoke the event related to pressing the tab button
1975        # I don't see a way to do that in aui.
1976        oldPage = self.GetSelection()
1977        self.SetSelection(page)
1978        return oldPage
1979
1980    # def __getattribute__(self,name):
1981    #     '''This method provides a way to print out a message every time
1982    #     that a method in a class is called -- to see what all the calls
1983    #     might be, or where they might be coming from.
1984    #     Cute trick for debugging!
1985    #     '''
1986    #     attr = object.__getattribute__(self, name)
1987    #     if hasattr(attr, '__call__'):
1988    #         def newfunc(*args, **kwargs):
1989    #             print('GSauiNoteBook calling %s' %attr.__name__)
1990    #             result = attr(*args, **kwargs)
1991    #             return result
1992    #         return newfunc
1993    #     else:
1994    #         return attr
1995           
1996################################################################################
1997#####  GSGrid
1998################################################################################           
1999       
2000class GSGrid(wg.Grid):
2001    '''Basic wx.Grid implementation
2002    '''
2003    def __init__(self, parent, name=''):
2004        wg.Grid.__init__(self,parent,-1,name=name)                   
2005        #self.SetSize(parent.GetClientSize())
2006        # above removed to speed drawing of initial grid
2007        # does not appear to be needed
2008           
2009    def Clear(self):
2010        wg.Grid.ClearGrid(self)
2011       
2012    def SetCellReadOnly(self,r,c,readonly=True):
2013        self.SetReadOnly(r,c,isReadOnly=readonly)
2014       
2015    def SetCellStyle(self,r,c,color="white",readonly=True):
2016        self.SetCellBackgroundColour(r,c,color)
2017        self.SetReadOnly(r,c,isReadOnly=readonly)
2018       
2019    def GetSelection(self):
2020        #this is to satisfy structure drawing stuff in G2plt when focus changes
2021        return None
2022
2023    def InstallGridToolTip(self, rowcolhintcallback,
2024                           colLblCallback=None,rowLblCallback=None):
2025        '''code to display a tooltip for each item on a grid
2026        from http://wiki.wxpython.org/wxGrid%20ToolTips (buggy!), expanded to
2027        column and row labels using hints from
2028        https://groups.google.com/forum/#!topic/wxPython-users/bm8OARRVDCs
2029
2030        :param function rowcolhintcallback: a routine that returns a text
2031          string depending on the selected row and column, to be used in
2032          explaining grid entries.
2033        :param function colLblCallback: a routine that returns a text
2034          string depending on the selected column, to be used in
2035          explaining grid columns (if None, the default), column labels
2036          do not get a tooltip.
2037        :param function rowLblCallback: a routine that returns a text
2038          string depending on the selected row, to be used in
2039          explaining grid rows (if None, the default), row labels
2040          do not get a tooltip.
2041        '''
2042        prev_rowcol = [None,None,None]
2043        def OnMouseMotion(event):
2044            # event.GetRow() and event.GetCol() would be nice to have here,
2045            # but as this is a mouse event, not a grid event, they are not
2046            # available and we need to compute them by hand.
2047            x, y = self.CalcUnscrolledPosition(event.GetPosition())
2048            row = self.YToRow(y)
2049            col = self.XToCol(x)
2050            hinttext = ''
2051            win = event.GetEventObject()
2052            if [row,col,win] == prev_rowcol: # no change from last position
2053                event.Skip()
2054                return
2055            if win == self.GetGridWindow() and row >= 0 and col >= 0:
2056                hinttext = rowcolhintcallback(row, col)
2057            elif win == self.GetGridColLabelWindow() and col >= 0:
2058                if colLblCallback: hinttext = colLblCallback(col)
2059            elif win == self.GetGridRowLabelWindow() and row >= 0:
2060                if rowLblCallback: hinttext = rowLblCallback(row)
2061            else: # this should be the upper left corner, which is empty
2062                event.Skip()
2063                return
2064            if hinttext is None: hinttext = ''
2065            win.SetToolTipString(hinttext)
2066            prev_rowcol[:] = [row,col,win]
2067            event.Skip()
2068
2069        wx.EVT_MOTION(self.GetGridWindow(), OnMouseMotion)
2070        if colLblCallback: wx.EVT_MOTION(self.GetGridColLabelWindow(), OnMouseMotion)
2071        if rowLblCallback: wx.EVT_MOTION(self.GetGridRowLabelWindow(), OnMouseMotion)
2072                                                   
2073################################################################################
2074#####  Table
2075################################################################################           
2076       
2077class Table(wg.PyGridTableBase):
2078    '''Basic data table for use with GSgrid
2079    '''
2080    def __init__(self, data=[], rowLabels=None, colLabels=None, types = None):
2081        wg.PyGridTableBase.__init__(self)
2082        self.colLabels = colLabels
2083        self.rowLabels = rowLabels
2084        self.dataTypes = types
2085        self.data = data
2086       
2087    def AppendRows(self, numRows=1):
2088        self.data.append([])
2089        return True
2090       
2091    def CanGetValueAs(self, row, col, typeName):
2092        if self.dataTypes:
2093            colType = self.dataTypes[col].split(':')[0]
2094            if typeName == colType:
2095                return True
2096            else:
2097                return False
2098        else:
2099            return False
2100
2101    def CanSetValueAs(self, row, col, typeName):
2102        return self.CanGetValueAs(row, col, typeName)
2103
2104    def DeleteRow(self,pos):
2105        data = self.GetData()
2106        self.SetData([])
2107        new = []
2108        for irow,row in enumerate(data):
2109            if irow <> pos:
2110                new.append(row)
2111        self.SetData(new)
2112       
2113    def GetColLabelValue(self, col):
2114        if self.colLabels:
2115            return self.colLabels[col]
2116           
2117    def GetData(self):
2118        data = []
2119        for row in range(self.GetNumberRows()):
2120            data.append(self.GetRowValues(row))
2121        return data
2122       
2123    def GetNumberCols(self):
2124        try:
2125            return len(self.colLabels)
2126        except TypeError:
2127            return None
2128       
2129    def GetNumberRows(self):
2130        return len(self.data)
2131       
2132    def GetRowLabelValue(self, row):
2133        if self.rowLabels:
2134            return self.rowLabels[row]
2135       
2136    def GetColValues(self, col):
2137        data = []
2138        for row in range(self.GetNumberRows()):
2139            data.append(self.GetValue(row, col))
2140        return data
2141       
2142    def GetRowValues(self, row):
2143        data = []
2144        for col in range(self.GetNumberCols()):
2145            data.append(self.GetValue(row, col))
2146        return data
2147       
2148    def GetTypeName(self, row, col):
2149        try:
2150            if self.data[row][col] is None: return None
2151            return self.dataTypes[col]
2152        except (TypeError,IndexError):
2153            return None
2154
2155    def GetValue(self, row, col):
2156        try:
2157            if self.data[row][col] is None: return ""
2158            return self.data[row][col]
2159        except IndexError:
2160            return None
2161           
2162    def InsertRows(self, pos, rows):
2163        for row in range(rows):
2164            self.data.insert(pos,[])
2165            pos += 1
2166       
2167    def IsEmptyCell(self,row,col):
2168        try:
2169            return not self.data[row][col]
2170        except IndexError:
2171            return True
2172       
2173    def OnKeyPress(self, event):
2174        dellist = self.GetSelectedRows()
2175        if event.GetKeyCode() == wx.WXK_DELETE and dellist:
2176            grid = self.GetView()
2177            for i in dellist: grid.DeleteRow(i)
2178               
2179    def SetColLabelValue(self, col, label):
2180        numcols = self.GetNumberCols()
2181        if col > numcols-1:
2182            self.colLabels.append(label)
2183        else:
2184            self.colLabels[col]=label
2185       
2186    def SetData(self,data):
2187        for row in range(len(data)):
2188            self.SetRowValues(row,data[row])
2189               
2190    def SetRowLabelValue(self, row, label):
2191        self.rowLabels[row]=label
2192           
2193    def SetRowValues(self,row,data):
2194        self.data[row] = data
2195           
2196    def SetValue(self, row, col, value):
2197        def innerSetValue(row, col, value):
2198            try:
2199                self.data[row][col] = value
2200            except TypeError:
2201                return
2202            except IndexError:
2203                print row,col,value
2204                # add a new row
2205                if row > self.GetNumberRows():
2206                    self.data.append([''] * self.GetNumberCols())
2207                elif col > self.GetNumberCols():
2208                    for row in range(self.GetNumberRows):
2209                        self.data[row].append('')
2210                print self.data
2211                self.data[row][col] = value
2212        innerSetValue(row, col, value)
2213
2214################################################################################
2215#####  Notebook
2216################################################################################           
2217       
2218def UpdateNotebook(G2frame,data):
2219    '''Called when the data tree notebook entry is selected. Allows for
2220    editing of the text in that tree entry
2221    '''
2222    def OnNoteBook(event):
2223        data = G2frame.dataDisplay.GetValue().split('\n')
2224        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Notebook'),data)
2225        if 'nt' not in os.name:
2226            G2frame.dataDisplay.AppendText('\n')
2227                   
2228    if G2frame.dataDisplay:
2229        G2frame.dataDisplay.Destroy()
2230    G2frame.dataFrame.SetLabel('Notebook')
2231    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2232        style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER | wx.TE_DONTWRAP)
2233    G2frame.dataDisplay.Bind(wx.EVT_TEXT_ENTER,OnNoteBook)
2234    G2frame.dataDisplay.Bind(wx.EVT_KILL_FOCUS,OnNoteBook)
2235    for line in data:
2236        G2frame.dataDisplay.AppendText(line+"\n")
2237    G2frame.dataDisplay.AppendText('Notebook entry @ '+time.ctime()+"\n")
2238    G2frame.dataFrame.setSizePosLeft([400,250])
2239           
2240################################################################################
2241#####  Controls
2242################################################################################           
2243       
2244def UpdateControls(G2frame,data):
2245    '''Edit overall GSAS-II controls in main Controls data tree entry
2246    '''
2247    #patch
2248    if 'deriv type' not in data:
2249        data = {}
2250        data['deriv type'] = 'analytic Hessian'
2251        data['min dM/M'] = 0.0001
2252        data['shift factor'] = 1.
2253        data['max cyc'] = 3       
2254        data['F**2'] = False
2255        data['minF/sig'] = 0
2256    if 'shift factor' not in data:
2257        data['shift factor'] = 1.
2258    if 'max cyc' not in data:
2259        data['max cyc'] = 3
2260    if 'F**2' not in data:
2261        data['F**2'] = True
2262        data['minF/sig'] = 0
2263    if 'Author' not in data:
2264        data['Author'] = 'no name'
2265    if 'FreePrm1' not in data:
2266        data['FreePrm1'] = 'Sample humidity (%)'
2267    if 'FreePrm2' not in data:
2268        data['FreePrm2'] = 'Sample voltage (V)'
2269    if 'FreePrm3' not in data:
2270        data['FreePrm3'] = 'Applied load (MN)'
2271    if 'Copy2Next' not in data:
2272        data['Copy2Next'] = False
2273    if 'Reverse Seq' not in data:
2274        data['Reverse Seq'] = False   
2275     
2276   
2277    #end patch
2278
2279    def SeqSizer():
2280       
2281        def OnSelectData(event):
2282            choices = GetPatternTreeDataNames(G2frame,['PWDR',])
2283            sel = []
2284            try:
2285                if 'Seq Data' in data:
2286                    for item in data['Seq Data']:
2287                        sel.append(choices.index(item))
2288                    sel = [choices.index(item) for item in data['Seq Data']]
2289            except ValueError:  #data changed somehow - start fresh
2290                sel = []
2291            dlg = G2G.G2MultiChoiceDialog(G2frame.dataFrame, 'Sequential refinement',
2292                'Select dataset to include',choices)
2293            dlg.SetSelections(sel)
2294            names = []
2295            if dlg.ShowModal() == wx.ID_OK:
2296                for sel in dlg.GetSelections():
2297                    names.append(choices[sel])
2298                data['Seq Data'] = names               
2299                G2frame.EnableSeqRefineMenu()
2300            dlg.Destroy()
2301            wx.CallAfter(UpdateControls,G2frame,data)
2302           
2303        def OnReverse(event):
2304            data['Reverse Seq'] = reverseSel.GetValue()
2305           
2306        def OnCopySel(event):
2307            data['Copy2Next'] = copySel.GetValue() 
2308                   
2309        seqSizer = wx.BoxSizer(wx.VERTICAL)
2310        dataSizer = wx.BoxSizer(wx.HORIZONTAL)
2311        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Sequential Refinement: '),0,WACV)
2312        selSeqData = wx.Button(G2frame.dataDisplay,-1,label=' Select data')
2313        selSeqData.Bind(wx.EVT_BUTTON,OnSelectData)
2314        dataSizer.Add(selSeqData,0,WACV)
2315        SeqData = data.get('Seq Data',[])
2316        if not SeqData:
2317            lbl = ' (no powder data selected)'
2318        else:
2319            lbl = ' ('+str(len(SeqData))+' dataset(s) selected)'
2320
2321        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label=lbl),0,WACV)
2322        seqSizer.Add(dataSizer,0)
2323        if SeqData:
2324            selSizer = wx.BoxSizer(wx.HORIZONTAL)
2325            reverseSel = wx.CheckBox(G2frame.dataDisplay,-1,label=' Reverse order?')
2326            reverseSel.Bind(wx.EVT_CHECKBOX,OnReverse)
2327            reverseSel.SetValue(data['Reverse Seq'])
2328            selSizer.Add(reverseSel,0,WACV)
2329            copySel =  wx.CheckBox(G2frame.dataDisplay,-1,label=' Copy results to next histogram?')
2330            copySel.Bind(wx.EVT_CHECKBOX,OnCopySel)
2331            copySel.SetValue(data['Copy2Next'])
2332            selSizer.Add(copySel,0,WACV)
2333            seqSizer.Add(selSizer,0)
2334        return seqSizer
2335       
2336    def LSSizer():       
2337       
2338        def OnDerivType(event):
2339            data['deriv type'] = derivSel.GetValue()
2340            derivSel.SetValue(data['deriv type'])
2341            wx.CallAfter(UpdateControls,G2frame,data)
2342           
2343        def OnConvergence(event):
2344            try:
2345                value = max(1.e-9,min(1.0,float(Cnvrg.GetValue())))
2346            except ValueError:
2347                value = 0.0001
2348            data['min dM/M'] = value
2349            Cnvrg.SetValue('%.2g'%(value))
2350           
2351        def OnMaxCycles(event):
2352            data['max cyc'] = int(maxCyc.GetValue())
2353            maxCyc.SetValue(str(data['max cyc']))
2354                       
2355        def OnFactor(event):
2356            try:
2357                value = min(max(float(Factr.GetValue()),0.00001),100.)
2358            except ValueError:
2359                value = 1.0
2360            data['shift factor'] = value
2361            Factr.SetValue('%.5f'%(value))
2362           
2363        def OnFsqRef(event):
2364            data['F**2'] = fsqRef.GetValue()
2365       
2366        def OnMinSig(event):
2367            try:
2368                value = min(max(float(minSig.GetValue()),0.),5.)
2369            except ValueError:
2370                value = 1.0
2371            data['minF/sig'] = value
2372            minSig.SetValue('%.2f'%(value))
2373
2374        LSSizer = wx.FlexGridSizer(cols=4,vgap=5,hgap=5)
2375        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement derivatives: '),0,WACV)
2376        Choice=['analytic Jacobian','numeric','analytic Hessian']
2377        derivSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['deriv type'],choices=Choice,
2378            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2379        derivSel.SetValue(data['deriv type'])
2380        derivSel.Bind(wx.EVT_COMBOBOX, OnDerivType)
2381           
2382        LSSizer.Add(derivSel,0,WACV)
2383        LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min delta-M/M: '),0,WACV)
2384        Cnvrg = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2g'%(data['min dM/M']),style=wx.TE_PROCESS_ENTER)
2385        Cnvrg.Bind(wx.EVT_TEXT_ENTER,OnConvergence)
2386        Cnvrg.Bind(wx.EVT_KILL_FOCUS,OnConvergence)
2387        LSSizer.Add(Cnvrg,0,WACV)
2388        if 'Hessian' in data['deriv type']:
2389            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Max cycles: '),0,WACV)
2390            Choice = ['0','1','2','3','5','10','15','20']
2391            maxCyc = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['max cyc']),choices=Choice,
2392                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2393            maxCyc.SetValue(str(data['max cyc']))
2394            maxCyc.Bind(wx.EVT_COMBOBOX, OnMaxCycles)
2395            LSSizer.Add(maxCyc,0,WACV)
2396        else:
2397            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Initial shift factor: '),0,WACV)
2398            Factr = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.5f'%(data['shift factor']),style=wx.TE_PROCESS_ENTER)
2399            Factr.Bind(wx.EVT_TEXT_ENTER,OnFactor)
2400            Factr.Bind(wx.EVT_KILL_FOCUS,OnFactor)
2401            LSSizer.Add(Factr,0,WACV)
2402        if G2frame.Sngl:
2403            LSSizer.Add((1,0),)
2404            LSSizer.Add((1,0),)
2405            fsqRef = wx.CheckBox(G2frame.dataDisplay,-1,label='Refine HKLF as F^2? ')
2406            fsqRef.SetValue(data['F**2'])
2407            fsqRef.Bind(wx.EVT_CHECKBOX,OnFsqRef)
2408            LSSizer.Add(fsqRef,0,WACV)
2409            LSSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label='Min obs/sig (0-5): '),0,WACV)
2410            minSig = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(data['minF/sig']),style=wx.TE_PROCESS_ENTER)
2411            minSig.Bind(wx.EVT_TEXT_ENTER,OnMinSig)
2412            minSig.Bind(wx.EVT_KILL_FOCUS,OnMinSig)
2413            LSSizer.Add(minSig,0,WACV)
2414        return LSSizer
2415       
2416    def AuthSizer():
2417
2418        def OnAuthor(event):
2419            data['Author'] = auth.GetValue()
2420
2421        Author = data['Author']
2422        authSizer = wx.BoxSizer(wx.HORIZONTAL)
2423        authSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' CIF Author (last, first):'),0,WACV)
2424        auth = wx.TextCtrl(G2frame.dataDisplay,-1,value=Author,style=wx.TE_PROCESS_ENTER)
2425        auth.Bind(wx.EVT_TEXT_ENTER,OnAuthor)
2426        auth.Bind(wx.EVT_KILL_FOCUS,OnAuthor)
2427        authSizer.Add(auth,0,WACV)
2428        return authSizer
2429       
2430       
2431    if G2frame.dataDisplay:
2432        G2frame.dataDisplay.Destroy()
2433    if not G2frame.dataFrame.GetStatusBar():
2434        Status = G2frame.dataFrame.CreateStatusBar()
2435        Status.SetStatusText('')
2436    G2frame.dataFrame.SetLabel('Controls')
2437    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
2438    SetDataMenuBar(G2frame,G2frame.dataFrame.ControlsMenu)
2439    mainSizer = wx.BoxSizer(wx.VERTICAL)
2440    mainSizer.Add((5,5),0)
2441    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Refinement Controls:'),0,WACV)   
2442    mainSizer.Add(LSSizer())
2443    mainSizer.Add((5,5),0)
2444    mainSizer.Add(SeqSizer())
2445    mainSizer.Add((5,5),0)
2446    mainSizer.Add(AuthSizer())
2447    mainSizer.Add((5,5),0)
2448       
2449    mainSizer.Layout()   
2450    G2frame.dataDisplay.SetSizer(mainSizer)
2451    G2frame.dataDisplay.SetSize(mainSizer.Fit(G2frame.dataFrame))
2452    G2frame.dataFrame.setSizePosLeft(mainSizer.Fit(G2frame.dataFrame))
2453     
2454################################################################################
2455#####  Comments
2456################################################################################           
2457       
2458def UpdateComments(G2frame,data):                   
2459
2460    if G2frame.dataDisplay:
2461        G2frame.dataDisplay.Destroy()
2462    G2frame.dataFrame.SetLabel('Comments')
2463    G2frame.dataDisplay = wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
2464        style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_DONTWRAP)
2465    for line in data:
2466        G2frame.dataDisplay.AppendText(line+'\n')
2467    G2frame.dataFrame.setSizePosLeft([400,250])
2468           
2469################################################################################
2470#####  Display of Sequential Results
2471################################################################################           
2472       
2473def UpdateSeqResults(G2frame,data,prevSize=None):
2474    """
2475    Called when the Sequential Results data tree entry is selected
2476    to show results from a sequential refinement.
2477   
2478    :param wx.Frame G2frame: main GSAS-II data tree windows
2479
2480    :param dict data: a dictionary containing the following items: 
2481
2482            * 'histNames' - list of histogram names in order as processed by Sequential Refinement
2483            * 'varyList' - list of variables - identical over all refinements in sequence
2484              note that this is the original list of variables, prior to processing
2485              constraints.
2486            * 'variableLabels' -- a dict of labels to be applied to each parameter
2487              (this is created as an empty dict if not present in data).
2488            * keyed by histName - dictionaries for all data sets processed, which contains:
2489
2490              * 'variables'- result[0] from leastsq call
2491              * 'varyList' - list of variables passed to leastsq call (not same as above)
2492              * 'sig' - esds for variables
2493              * 'covMatrix' - covariance matrix from individual refinement
2494              * 'title' - histogram name; same as dict item name
2495              * 'newAtomDict' - new atom parameters after shifts applied
2496              * 'newCellDict' - refined cell parameters after shifts to A0-A5 from Dij terms applied'
2497    """
2498
2499    def GetSampleParms():
2500        '''Make a dictionary of the sample parameters are not the same over the
2501        refinement series.
2502        '''
2503        if 'IMG' in histNames[0]:
2504            sampleParmDict = {'Sample load':[],}
2505        else:
2506            sampleParmDict = {'Temperature':[],'Pressure':[],'Time':[],
2507                'FreePrm1':[],'FreePrm2':[],'FreePrm3':[],'Omega':[],
2508                'Chi':[],'Phi':[],'Azimuth':[],}
2509        Controls = G2frame.PatternTree.GetItemPyData(
2510            GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
2511        sampleParm = {}
2512        for name in histNames:
2513            if 'IMG' in name:
2514                for item in sampleParmDict:
2515                    sampleParmDict[item].append(data[name]['parmDict'].get(item,0))
2516            else:
2517                Id = GetPatternTreeItemId(G2frame,G2frame.root,name)
2518                sampleData = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,Id,'Sample Parameters'))
2519                for item in sampleParmDict:
2520                    sampleParmDict[item].append(sampleData.get(item,0))
2521        for item in sampleParmDict:
2522            frstValue = sampleParmDict[item][0]
2523            if np.any(np.array(sampleParmDict[item])-frstValue):
2524                if item.startswith('FreePrm'):
2525                    sampleParm[Controls[item]] = sampleParmDict[item]
2526                else:
2527                    sampleParm[item] = sampleParmDict[item]
2528        return sampleParm
2529
2530    def GetColumnInfo(col):
2531        '''returns column label, lists of values and errors (or None) for each column in the table
2532        for plotting. The column label is reformatted from Unicode to MatPlotLib encoding
2533        '''
2534        colName = G2frame.SeqTable.GetColLabelValue(col)
2535        plotName = variableLabels.get(colName,colName)
2536        plotName = plotSpCharFix(plotName)
2537        return plotName,colList[col],colSigs[col]
2538           
2539    def PlotSelect(event):
2540        'Plots a row (covariance) or column on double-click'
2541        cols = G2frame.dataDisplay.GetSelectedCols()
2542        rows = G2frame.dataDisplay.GetSelectedRows()
2543        if cols:
2544            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2545        elif rows:
2546            name = histNames[rows[0]]       #only does 1st one selected
2547            G2plt.PlotCovariance(G2frame,data[name])
2548        else:
2549            G2frame.ErrorDialog(
2550                'Select row or columns',
2551                'Nothing selected in table. Click on column or row label(s) to plot. N.B. Grid selection can be a bit funky.'
2552                )
2553           
2554    def OnPlotSelSeq(event):
2555        'plot the selected columns or row from menu command'
2556        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2557        rows = G2frame.dataDisplay.GetSelectedRows()
2558        if cols:
2559            G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2560        elif rows:
2561            name = histNames[rows[0]]       #only does 1st one selected
2562            G2plt.PlotCovariance(G2frame,data[name])
2563        else:
2564            G2frame.ErrorDialog(
2565                'Select columns',
2566                'No columns or rows selected in table. Click on row or column labels to select fields for plotting.'
2567                )
2568               
2569    def OnAveSelSeq(event):
2570        'average the selected columns from menu command'
2571        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2572        if cols:
2573            for col in cols:
2574                ave = np.mean(GetColumnInfo(col)[1])
2575                sig = np.std(GetColumnInfo(col)[1])
2576                print ' Average for '+G2frame.SeqTable.GetColLabelValue(col)+': '+'%.6g'%(ave)+' +/- '+'%.6g'%(sig)
2577        else:
2578            G2frame.ErrorDialog(
2579                'Select columns',
2580                'No columns selected in table. Click on column labels to select fields for averaging.'
2581                )
2582               
2583    def OnRenameSelSeq(event):
2584        cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2585        colNames = [G2frame.SeqTable.GetColLabelValue(c) for c in cols]
2586        newNames = colNames[:]
2587        for i,name in enumerate(colNames):
2588            if name in variableLabels:
2589                newNames[i] = variableLabels[name]
2590        if not cols:
2591            G2frame.ErrorDialog('Select columns',
2592                'No columns selected in table. Click on column labels to select fields for rename.')
2593            return
2594        dlg = MultiStringDialog(G2frame.dataDisplay,'Set column names',colNames,newNames)
2595        if dlg.Show():
2596            newNames = dlg.GetValues()           
2597            variableLabels.update(dict(zip(colNames,newNames)))
2598        data['variableLabels'] = variableLabels
2599        dlg.Destroy()
2600        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2601        G2plt.PlotSelectedSequence(G2frame,cols,GetColumnInfo,SelectXaxis)
2602           
2603    def OnReOrgSelSeq(event):
2604        'Reorder the columns'
2605        G2G.GetItemOrder(G2frame,VaryListChanges,vallookup,posdict)   
2606        UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2607
2608    def OnSaveSelSeqCSV(event):
2609        'export the selected columns to a .csv file from menu command'
2610        OnSaveSelSeq(event,csv=True)
2611       
2612    def OnSaveSeqCSV(event):
2613        'export all columns to a .csv file from menu command'
2614        OnSaveSelSeq(event,csv=True,allcols=True)
2615       
2616    def OnSaveSelSeq(event,csv=False,allcols=False):
2617        'export the selected columns to a .txt or .csv file from menu command'
2618        def WriteCSV():
2619            def WriteList(headerItems):
2620                line = ''
2621                for lbl in headerItems:
2622                    if line: line += ','
2623                    line += '"'+lbl+'"'
2624                return line
2625            head = ['name']
2626            for col in cols:
2627                item = G2frame.SeqTable.GetColLabelValue(col)
2628                # get rid of labels that have Unicode characters
2629                if not all([ord(c) < 128 and ord(c) != 0 for c in item]): item = '?'
2630                if col in havesig:
2631                    head += [item,'esd-'+item]
2632                else:
2633                    head += [item]
2634            SeqFile.write(WriteList(head)+'\n')
2635            for row,name in enumerate(saveNames):
2636                line = '"'+saveNames[row]+'"'
2637                for col in cols:
2638                    if col in havesig:
2639                        line += ','+str(saveData[col][row])+','+str(saveSigs[col][row])
2640                    else:
2641                        line += ','+str(saveData[col][row])
2642                SeqFile.write(line+'\n')
2643        def WriteSeq():
2644            lenName = len(saveNames[0])
2645            line = %s  '%('name'.center(lenName))
2646            for col in cols:
2647                item = G2frame.SeqTable.GetColLabelValue(col)
2648                if col in havesig:
2649                    line += ' %12s %12s '%(item.center(12),'esd'.center(12))
2650                else:
2651                    line += ' %12s '%(item.center(12))
2652            SeqFile.write(line+'\n')
2653            for row,name in enumerate(saveNames):
2654                line = " '%s' "%(saveNames[row])
2655                for col in cols:
2656                    if col in havesig:
2657                        line += ' %12.6f %12.6f '%(saveData[col][row],saveSigs[col][row])
2658                    else:
2659                        line += ' %12.6f '%saveData[col][row]
2660                SeqFile.write(line+'\n')
2661
2662        # start of OnSaveSelSeq code
2663        if allcols:
2664            cols = range(G2frame.SeqTable.GetNumberCols())
2665        else:
2666            cols = sorted(G2frame.dataDisplay.GetSelectedCols()) # ignore selection order
2667        nrows = G2frame.SeqTable.GetNumberRows()
2668        if not cols:
2669            G2frame.ErrorDialog('Select columns',
2670                             'No columns selected in table. Click on column labels to select fields for output.')
2671            return
2672        saveNames = [G2frame.SeqTable.GetRowLabelValue(r) for r in range(nrows)]
2673        saveData = {}
2674        saveSigs = {}
2675        havesig = []
2676        for col in cols:
2677            name,vals,sigs = GetColumnInfo(col)
2678            saveData[col] = vals
2679            if sigs:
2680                havesig.append(col)
2681                saveSigs[col] = sigs
2682        if csv:
2683            wild = 'CSV output file (*.csv)|*.csv'
2684        else:
2685            wild = 'Text output file (*.txt)|*.txt'
2686        dlg = wx.FileDialog(
2687            G2frame,
2688            'Choose text output file for your selection', '.', '', 
2689            wild,wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2690        try:
2691            if dlg.ShowModal() == wx.ID_OK:
2692                SeqTextFile = dlg.GetPath()
2693                SeqTextFile = G2IO.FileDlgFixExt(dlg,SeqTextFile) 
2694                SeqFile = open(SeqTextFile,'w')
2695                if csv:
2696                    WriteCSV()
2697                else:
2698                    WriteSeq()
2699                SeqFile.close()
2700        finally:
2701            dlg.Destroy()
2702               
2703    def striphist(var,insChar=''):
2704        'strip a histogram number from a var name'
2705        sv = var.split(':')
2706        if len(sv) <= 1: return var
2707        if sv[1]:
2708            sv[1] = insChar
2709        return ':'.join(sv)
2710       
2711    def plotSpCharFix(lbl):
2712        'Change selected unicode characters to their matplotlib equivalent'
2713        for u,p in [
2714            (u'\u03B1',r'$\alpha$'),
2715            (u'\u03B2',r'$\beta$'),
2716            (u'\u03B3',r'$\gamma$'),
2717            (u'\u0394\u03C7',r'$\Delta\chi$'),
2718            ]:
2719            lbl = lbl.replace(u,p)
2720        return lbl
2721   
2722    def SelectXaxis():
2723        'returns a selected column number (or None) as the X-axis selection'
2724        ncols = G2frame.SeqTable.GetNumberCols()
2725        colNames = [G2frame.SeqTable.GetColLabelValue(r) for r in range(ncols)]
2726        dlg = G2G.G2SingleChoiceDialog(
2727            G2frame.dataDisplay,
2728            'Select x-axis parameter for plot or Cancel for sequence number',
2729            'Select X-axis',
2730            colNames)
2731        try:
2732            if dlg.ShowModal() == wx.ID_OK:
2733                col = dlg.GetSelection()
2734            else:
2735                col = None
2736        finally:
2737            dlg.Destroy()
2738        return col
2739   
2740    def EnablePseudoVarMenus():
2741        'Enables or disables the PseudoVar menu items that require existing defs'
2742        if Controls['SeqPseudoVars']:
2743            val = True
2744        else:
2745            val = False
2746        G2frame.dataFrame.SequentialPvars.Enable(wxDELSEQVAR,val)
2747        G2frame.dataFrame.SequentialPvars.Enable(wxEDITSEQVAR,val)
2748
2749    def DelPseudoVar(event):
2750        'Ask the user to select a pseudo var expression to delete'
2751        choices = Controls['SeqPseudoVars'].keys()
2752        selected = ItemSelector(
2753            choices,G2frame.dataFrame,
2754            multiple=True,
2755            title='Select expressions to remove',
2756            header='Delete expression')
2757        if selected is None: return
2758        for item in selected:
2759            del Controls['SeqPseudoVars'][choices[item]]
2760        if selected:
2761            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2762
2763    def EditPseudoVar(event):
2764        'Edit an existing pseudo var expression'
2765        choices = Controls['SeqPseudoVars'].keys()
2766        if len(choices) == 1:
2767            selected = 0
2768        else:
2769            selected = ItemSelector(
2770                choices,G2frame.dataFrame,
2771                multiple=False,
2772                title='Select an expression to edit',
2773                header='Edit expression')
2774        if selected is not None:
2775            dlg = G2exG.ExpressionDialog(
2776                G2frame.dataDisplay,PSvarDict,
2777                Controls['SeqPseudoVars'][choices[selected]],
2778                header="Edit the PseudoVar expression",
2779                VarLabel="PseudoVar #"+str(selected+1),
2780                fit=False)
2781            newobj = dlg.Show(True)
2782            if newobj:
2783                calcobj = G2obj.ExpressionCalcObj(newobj)
2784                del Controls['SeqPseudoVars'][choices[selected]]
2785                Controls['SeqPseudoVars'][calcobj.eObj.expression] = newobj
2786                UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2787       
2788    def AddNewPseudoVar(event):
2789        'Create a new pseudo var expression'
2790        dlg = G2exG.ExpressionDialog(
2791            G2frame.dataDisplay,PSvarDict,
2792            header='Enter an expression for a PseudoVar here',
2793            VarLabel = "New PseudoVar",
2794            fit=False)
2795        obj = dlg.Show(True)
2796        dlg.Destroy()
2797        if obj:
2798            calcobj = G2obj.ExpressionCalcObj(obj)
2799            Controls['SeqPseudoVars'][calcobj.eObj.expression] = obj
2800            UpdateSeqResults(G2frame,data,G2frame.dataDisplay.GetSize()) # redisplay variables
2801
2802    def UpdateParmDict(parmDict):
2803        '''generate the atom positions and the direct & reciprocal cell values,
2804        because they might be needed to evaluate the pseudovar
2805        '''
2806        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
2807                         ['A'+str(i) for i in range(6)])
2808                     )
2809        delList = []
2810        phaselist = []
2811        for item in parmDict: 
2812            if ':' not in item: continue
2813            key = item.split(':')
2814            if len(key) < 3: continue
2815            # remove the dA[xyz] terms, they would only bring confusion
2816            if key[2].startswith('dA'):
2817                delList.append(item)
2818            # compute and update the corrected reciprocal cell terms using the Dij values
2819            elif key[2] in Ddict:
2820                if key[0] not in phaselist: phaselist.append(key[0])
2821                akey = key[0]+'::'+Ddict[key[2]]
2822                parmDict[akey] -= parmDict[item]
2823                delList.append(item)
2824        for item in delList:
2825            del parmDict[item]               
2826        for i in phaselist:
2827            pId = int(i)
2828            # apply cell symmetry
2829            A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],parmDict,zeroDict[pId])
2830            # convert to direct cell & add the unique terms to the dictionary
2831            for i,val in enumerate(G2lat.A2cell(A)):
2832                if i in uniqCellIndx[pId]:
2833                    lbl = str(pId)+'::'+cellUlbl[i]
2834                    parmDict[lbl] = val
2835            lbl = str(pId)+'::'+'vol'
2836            parmDict[lbl] = G2lat.calc_V(A)
2837        return parmDict
2838
2839    def EvalPSvarDeriv(calcobj,parmDict,sampleDict,var,ESD):
2840        '''Evaluate an expression derivative with respect to a
2841        GSAS-II variable name.
2842
2843        Note this likely could be faster if the loop over calcobjs were done
2844        inside after the Dict was created.
2845        '''
2846        step = ESD/10
2847        Ddict = dict(zip(['D11','D22','D33','D12','D13','D23'],
2848                         ['A'+str(i) for i in range(6)])
2849                     )
2850        results = []
2851        phaselist = []
2852        VparmDict = sampleDict.copy()
2853        for incr in step,-step:
2854            VparmDict.update(parmDict.copy())           
2855            # as saved, the parmDict has updated 'A[xyz]' values, but 'dA[xyz]'
2856            # values are not zeroed: fix that!
2857            VparmDict.update({item:0.0 for item in parmDict if 'dA' in item})
2858            VparmDict[var] += incr
2859            G2mv.Dict2Map(VparmDict,[]) # apply constraints
2860            # generate the atom positions and the direct & reciprocal cell values now, because they might
2861            # needed to evaluate the pseudovar
2862            for item in VparmDict:
2863                if item in sampleDict:
2864                    continue 
2865                if ':' not in item: continue
2866                key = item.split(':')
2867                if len(key) < 3: continue
2868                # apply any new shifts to atom positions
2869                if key[2].startswith('dA'):
2870                    VparmDict[''.join(item.split('d'))] += VparmDict[item]
2871                    VparmDict[item] = 0.0
2872                # compute and update the corrected reciprocal cell terms using the Dij values
2873                if key[2] in Ddict:
2874                    if key[0] not in phaselist: phaselist.append(key[0])
2875                    akey = key[0]+'::'+Ddict[key[2]]
2876                    VparmDict[akey] -= VparmDict[item]
2877            for i in phaselist:
2878                pId = int(i)
2879                # apply cell symmetry
2880                A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata[pId],VparmDict,zeroDict[pId])
2881                # convert to direct cell & add the unique terms to the dictionary
2882                for i,val in enumerate(G2lat.A2cell(A)):
2883                    if i in uniqCellIndx[pId]:
2884                        lbl = str(pId)+'::'+cellUlbl[i]
2885                        VparmDict[lbl] = val
2886                lbl = str(pId)+'::'+'vol'
2887                VparmDict[lbl] = G2lat.calc_V(A)
2888            # dict should be fully updated, use it & calculate
2889            calcobj.SetupCalc(VparmDict)
2890            results.append(calcobj.EvalExpression())
2891        return (results[0] - results[1]) / (2.*step)
2892       
2893    def EnableParFitEqMenus():
2894        'Enables or disables the Parametric Fit menu items that require existing defs'
2895        if Controls['SeqParFitEqList']:
2896            val = True
2897        else:
2898            val = False
2899        G2frame.dataFrame.SequentialPfit.Enable(wxDELPARFIT,val)
2900        G2frame.dataFrame.SequentialPfit.Enable(wxEDITPARFIT,val)
2901        G2frame.dataFrame.SequentialPfit.Enable(wxDOPARFIT,val)
2902
2903    def ParEqEval(Values,calcObjList,varyList):
2904        '''Evaluate the parametric expression(s)
2905        :param list Values: a list of values for each variable parameter
2906        :param list calcObjList: a list of :class:`GSASIIobj.ExpressionCalcObj`
2907          expression objects to evaluate
2908        :param list varyList: a list of variable names for each value in Values
2909        '''
2910        result = []
2911        for calcobj in calcObjList:
2912            calcobj.UpdateVars(varyList,Values)
2913            result.append((calcobj.depVal-calcobj.EvalExpression())/calcobj.depSig)
2914        return result
2915
2916    def DoParEqFit(event,eqObj=None):
2917        'Parametric fit minimizer'
2918        varyValueDict = {} # dict of variables and their initial values
2919        calcObjList = [] # expression objects, ready to go for each data point
2920        if eqObj is not None:
2921            eqObjList = [eqObj,]
2922        else:
2923            eqObjList = Controls['SeqParFitEqList']
2924        UseFlags = G2frame.SeqTable.GetColValues(0)         
2925        for obj in eqObjList:
2926            expr = obj.expression
2927            # assemble refined vars for this equation
2928            varyValueDict.update({var:val for var,val in obj.GetVariedVarVal()})
2929            # lookup dependent var position
2930            depVar = obj.GetDepVar()
2931            if depVar in colLabels:
2932                indx = colLabels.index(depVar)
2933            else:
2934                raise Exception('Dependent variable '+depVar+' not found')
2935            # assemble a list of the independent variables
2936            indepVars = obj.GetIndependentVars()
2937            # loop over each datapoint
2938            for j,row in enumerate(zip(*colList)):
2939                if not UseFlags[j]: continue
2940                # assemble equations to fit
2941                calcobj = G2obj.ExpressionCalcObj(obj)
2942                # prepare a dict of needed independent vars for this expression
2943                indepVarDict = {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
2944                calcobj.SetupCalc(indepVarDict)               
2945                # values and sigs for current value of dependent var
2946                calcobj.depVal = row[indx]
2947                calcobj.depSig = colSigs[indx][j]
2948                calcObjList.append(calcobj)
2949        # varied parameters
2950        varyList = varyValueDict.keys()
2951        values = varyValues = [varyValueDict[key] for key in varyList]
2952        if not varyList:
2953            print 'no variables to refine!'
2954            return
2955        try:
2956            result = so.leastsq(ParEqEval,varyValues,full_output=True,   #ftol=Ftol,
2957                                args=(calcObjList,varyList)
2958                                )
2959            values = result[0]
2960            covar = result[1]
2961            if covar is None:
2962                raise Exception
2963            esdDict = {}
2964            for i,avar in enumerate(varyList):
2965                esdDict[avar] = np.sqrt(covar[i,i])
2966        except:
2967            print('====> Fit failed')
2968            return
2969        print('==== Fit Results ====')
2970        for obj in eqObjList:
2971            obj.UpdateVariedVars(varyList,values)
2972            ind = '      '
2973            print('  '+obj.GetDepVar()+' = '+obj.expression)
2974            for var in obj.assgnVars:
2975                print(ind+var+' = '+obj.assgnVars[var])
2976            for var in obj.freeVars:
2977                avar = "::"+obj.freeVars[var][0]
2978                val = obj.freeVars[var][1]
2979                if obj.freeVars[var][2]:
2980                    print(ind+var+' = '+avar + " = " + G2mth.ValEsd(val,esdDict[avar]))
2981                else:
2982                    print(ind+var+' = '+avar + " =" + G2mth.ValEsd(val,0))
2983        # create a plot for each parametric variable
2984        for fitnum,obj in enumerate(eqObjList):
2985            calcobj = G2obj.ExpressionCalcObj(obj)
2986            # lookup dependent var position
2987            indx = colLabels.index(obj.GetDepVar())
2988            # assemble a list of the independent variables
2989            indepVars = obj.GetIndependentVars()           
2990            # loop over each datapoint
2991            fitvals = []
2992            for j,row in enumerate(zip(*colList)):
2993                calcobj.SetupCalc(
2994                    {var:row[i] for i,var in enumerate(colLabels) if var in indepVars}
2995                    )
2996                fitvals.append(calcobj.EvalExpression())
2997            G2plt.PlotSelectedSequence(
2998                G2frame,[indx],GetColumnInfo,SelectXaxis,
2999                fitnum,fitvals)
3000
3001    def SingleParEqFit(eqObj):
3002        DoParEqFit(None,eqObj)
3003
3004    def DelParFitEq(event):
3005        'Ask the user to select function to delete'
3006        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3007        selected = ItemSelector(
3008            txtlst,G2frame.dataFrame,
3009            multiple=True,
3010            title='Select a parametric equation(s) to remove',
3011            header='Delete equation')
3012        if selected is None: return
3013        Controls['SeqParFitEqList'] = [obj for i,obj in enumerate(Controls['SeqParFitEqList']) if i not in selected]
3014        EnableParFitEqMenus()
3015        if Controls['SeqParFitEqList']: DoParEqFit(event)
3016       
3017    def EditParFitEq(event):
3018        'Edit an existing parametric equation'
3019        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3020        if len(txtlst) == 1:
3021            selected = 0
3022        else:
3023            selected = ItemSelector(
3024                txtlst,G2frame.dataFrame,
3025                multiple=False,
3026                title='Select a parametric equation to edit',
3027                header='Edit equation')
3028        if selected is not None:
3029            dlg = G2exG.ExpressionDialog(
3030                G2frame.dataDisplay,indepVarDict,
3031                Controls['SeqParFitEqList'][selected],
3032                depVarDict=depVarDict,
3033                header="Edit the formula for this minimization function",
3034                ExtraButton=['Fit',SingleParEqFit])
3035            newobj = dlg.Show(True)
3036            if newobj:
3037                calcobj = G2obj.ExpressionCalcObj(newobj)
3038                Controls['SeqParFitEqList'][selected] = newobj
3039                EnableParFitEqMenus()
3040            if Controls['SeqParFitEqList']: DoParEqFit(event)
3041
3042    def AddNewParFitEq(event):
3043        'Create a new parametric equation to be fit to sequential results'
3044
3045        # compile the variable names used in previous freevars to avoid accidental name collisions
3046        usedvarlist = []
3047        for obj in Controls['SeqParFitEqList']:
3048            for var in obj.freeVars:
3049                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3050
3051        dlg = G2exG.ExpressionDialog(
3052            G2frame.dataDisplay,indepVarDict,
3053            depVarDict=depVarDict,
3054            header='Define an equation to minimize in the parametric fit',
3055            ExtraButton=['Fit',SingleParEqFit],
3056            usedVars=usedvarlist)
3057        obj = dlg.Show(True)
3058        dlg.Destroy()
3059        if obj:
3060            Controls['SeqParFitEqList'].append(obj)
3061            EnableParFitEqMenus()
3062            if Controls['SeqParFitEqList']: DoParEqFit(event)
3063               
3064    def CopyParFitEq(event):
3065        'Copy an existing parametric equation to be fit to sequential results'
3066        # compile the variable names used in previous freevars to avoid accidental name collisions
3067        usedvarlist = []
3068        for obj in Controls['SeqParFitEqList']:
3069            for var in obj.freeVars:
3070                if obj.freeVars[var][0] not in usedvarlist: usedvarlist.append(obj.freeVars[var][0])
3071        txtlst = [obj.GetDepVar()+' = '+obj.expression for obj in Controls['SeqParFitEqList']]
3072        if len(txtlst) == 1:
3073            selected = 0
3074        else:
3075            selected = ItemSelector(
3076                txtlst,G2frame.dataFrame,
3077                multiple=False,
3078                title='Select a parametric equation to copy',
3079                header='Copy equation')
3080        if selected is not None:
3081            newEqn = copy.deepcopy(Controls['SeqParFitEqList'][selected])
3082            for var in newEqn.freeVars:
3083                newEqn.freeVars[var][0] = G2obj.MakeUniqueLabel(newEqn.freeVars[var][0],usedvarlist)
3084            dlg = G2exG.ExpressionDialog(
3085                G2frame.dataDisplay,indepVarDict,
3086                newEqn,
3087                depVarDict=depVarDict,
3088                header="Edit the formula for this minimization function",
3089                ExtraButton=['Fit',SingleParEqFit])
3090            newobj = dlg.Show(True)
3091            if newobj:
3092                calcobj = G2obj.ExpressionCalcObj(newobj)
3093                Controls['SeqParFitEqList'].append(newobj)
3094                EnableParFitEqMenus()
3095            if Controls['SeqParFitEqList']: DoParEqFit(event)
3096                                           
3097    def GridSetToolTip(row,col):
3098        '''Routine to show standard uncertainties for each element in table
3099        as a tooltip
3100        '''
3101        if colSigs[col]:
3102            return u'\u03c3 = '+str(colSigs[col][row])
3103        return ''
3104       
3105    def GridColLblToolTip(col):
3106        '''Define a tooltip for a column. This will be the user-entered value
3107        (from data['variableLabels']) or the default name
3108        '''
3109        if col < 0 or col > len(colLabels):
3110            print 'Illegal column #',col
3111            return
3112        var = colLabels[col]
3113        return variableLabels.get(var,G2obj.fmtVarDescr(var))
3114       
3115    def SetLabelString(event):
3116        '''Define or edit the label for a column in the table, to be used
3117        as a tooltip and for plotting
3118        '''
3119        col = event.GetCol()
3120        if col < 0 or col > len(colLabels):
3121            return
3122        var = colLabels[col]
3123        lbl = variableLabels.get(var,G2obj.fmtVarDescr(var))
3124        dlg = SingleStringDialog(G2frame.dataFrame,'Set variable label',
3125                                 'Set a new name for variable '+var,lbl,size=(400,-1))
3126        if dlg.Show():
3127            variableLabels[var] = dlg.GetValue()
3128        dlg.Destroy()
3129       
3130    #def GridRowLblToolTip(row): return 'Row ='+str(row)
3131   
3132    # lookup table for unique cell parameters by symmetry
3133    cellGUIlist = [
3134        [['m3','m3m'],(0,)],
3135        [['3R','3mR'],(0,3)],
3136        [['3','3m1','31m','6/m','6/mmm','4/m','4/mmm'],(0,2)],
3137        [['mmm'],(0,1,2)],
3138        [['2/m'+'a'],(0,1,2,3)],
3139        [['2/m'+'b'],(0,1,2,4)],
3140        [['2/m'+'c'],(0,1,2,5)],
3141        [['-1'],(0,1,2,3,4,5)],
3142        ]
3143    # cell labels
3144    cellUlbl = ('a','b','c',u'\u03B1',u'\u03B2',u'\u03B3') # unicode a,b,c,alpha,beta,gamma
3145
3146    #======================================================================
3147    # start processing sequential results here (UpdateSeqResults)
3148    #======================================================================
3149    if not data:
3150        print 'No sequential refinement results'
3151        return
3152    variableLabels = data.get('variableLabels',{})
3153    data['variableLabels'] = variableLabels
3154    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
3155    Controls = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.root,'Controls'))
3156    # create a place to store Pseudo Vars & Parametric Fit functions, if not present
3157    if 'SeqPseudoVars' not in Controls: Controls['SeqPseudoVars'] = {}
3158    if 'SeqParFitEqList' not in Controls: Controls['SeqParFitEqList'] = []
3159    histNames = data['histNames']
3160    if G2frame.dataDisplay:
3161        G2frame.dataDisplay.Destroy()
3162    if not G2frame.dataFrame.GetStatusBar():
3163        Status = G2frame.dataFrame.CreateStatusBar()
3164        Status.SetStatusText("Select column to export; Double click on column to plot data; on row for Covariance")
3165    sampleParms = GetSampleParms()
3166
3167    # make dict of varied atom coords keyed by absolute position
3168    newAtomDict = data[histNames[0]].get('newAtomDict',{}) # dict with atom positions; relative & absolute
3169    # Possible error: the next might need to be data[histNames[0]]['varyList']
3170    # error will arise if there constraints on coordinates?
3171    atomLookup = {newAtomDict[item][0]:item for item in newAtomDict if item in data['varyList']}
3172   
3173    # make dict of varied cell parameters equivalents
3174    ESDlookup = {} # provides the Dij term for each Ak term (where terms are refined)
3175    Dlookup = {} # provides the Ak term for each Dij term (where terms are refined)
3176    # N.B. These Dij vars are missing a histogram #
3177    newCellDict = data[histNames[0]].get('newCellDict',{})
3178    for item in newCellDict:
3179        if item in data['varyList']:
3180            ESDlookup[newCellDict[item][0]] = item
3181            Dlookup[item] = newCellDict[item][0]
3182    # add coordinate equivalents to lookup table
3183    for parm in atomLookup:
3184        Dlookup[atomLookup[parm]] = parm
3185        ESDlookup[parm] = atomLookup[parm]
3186
3187    # get unit cell & symmetry for all phases & initial stuff for later use
3188    RecpCellTerms = {}
3189    SGdata = {}
3190    uniqCellIndx = {}
3191    initialCell = {}
3192    RcellLbls = {}
3193    zeroDict = {}
3194    Rcelldict = {}
3195    for phase in Phases:
3196        phasedict = Phases[phase]
3197        pId = phasedict['pId']
3198        pfx = str(pId)+'::' # prefix for A values from phase
3199        RcellLbls[pId] = [pfx+'A'+str(i) for i in range(6)]
3200        RecpCellTerms[pId] = G2lat.cell2A(phasedict['General']['Cell'][1:7])
3201        zeroDict[pId] = dict(zip(RcellLbls[pId],6*[0.,]))
3202        SGdata[pId] = phasedict['General']['SGData']
3203        Rcelldict.update({lbl:val for lbl,val in zip(RcellLbls[pId],RecpCellTerms[pId])})
3204        laue = SGdata[pId]['SGLaue']
3205        if laue == '2/m':
3206            laue += SGdata[pId]['SGUniq']
3207        for symlist,celllist in cellGUIlist:
3208            if laue in symlist:
3209                uniqCellIndx[pId] = celllist
3210                break
3211        else: # should not happen
3212            uniqCellIndx[pId] = range(6)
3213        for i in uniqCellIndx[pId]:
3214            initialCell[str(pId)+'::A'+str(i)] =  RecpCellTerms[pId][i]
3215
3216    SetDataMenuBar(G2frame,G2frame.dataFrame.SequentialMenu)
3217    G2frame.dataFrame.SetLabel('Sequential refinement results')
3218    if not G2frame.dataFrame.GetStatusBar():
3219        Status = G2frame.dataFrame.CreateStatusBar()
3220        Status.SetStatusText('')
3221    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRenameSelSeq, id=wxID_RENAMESEQSEL)
3222    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeq, id=wxID_SAVESEQSEL)
3223    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSelSeqCSV, id=wxID_SAVESEQSELCSV)
3224    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveSeqCSV, id=wxID_SAVESEQCSV)
3225    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotSelSeq, id=wxID_PLOTSEQSEL)
3226    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAveSelSeq, id=wxID_AVESEQSEL)
3227    G2frame.dataFrame.Bind(wx.EVT_MENU, OnReOrgSelSeq, id=wxID_ORGSEQSEL)
3228    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewPseudoVar, id=wxADDSEQVAR)
3229    G2frame.dataFrame.Bind(wx.EVT_MENU, DelPseudoVar, id=wxDELSEQVAR)
3230    G2frame.dataFrame.Bind(wx.EVT_MENU, EditPseudoVar, id=wxEDITSEQVAR)
3231    G2frame.dataFrame.Bind(wx.EVT_MENU, AddNewParFitEq, id=wxADDPARFIT)
3232    G2frame.dataFrame.Bind(wx.EVT_MENU, CopyParFitEq, id=wxCOPYPARFIT)
3233    G2frame.dataFrame.Bind(wx.EVT_MENU, DelParFitEq, id=wxDELPARFIT)
3234    G2frame.dataFrame.Bind(wx.EVT_MENU, EditParFitEq, id=wxEDITPARFIT)
3235    G2frame.dataFrame.Bind(wx.EVT_MENU, DoParEqFit, id=wxDOPARFIT)
3236    EnablePseudoVarMenus()
3237    EnableParFitEqMenus()
3238
3239    # scan for locations where the variables change
3240    VaryListChanges = [] # histograms where there is a change
3241    combinedVaryList = []
3242    firstValueDict = {}
3243    vallookup = {}
3244    posdict = {}
3245    prevVaryList = []
3246    for i,name in enumerate(histNames):
3247        for var,val,sig in zip(data[name]['varyList'],data[name]['variables'],data[name]['sig']):
3248            svar = striphist(var,'*') # wild-carded
3249            if svar not in combinedVaryList:
3250                # add variables to list as they appear
3251                combinedVaryList.append(svar)
3252                firstValueDict[svar] = (val,sig)
3253        if prevVaryList != data[name]['varyList']: # this refinement has a different refinement list from previous
3254            prevVaryList = data[name]['varyList']
3255            vallookup[name] = dict(zip(data[name]['varyList'],data[name]['variables']))
3256            posdict[name] = {}
3257            for var in data[name]['varyList']:
3258                svar = striphist(var,'*')
3259                posdict[name][combinedVaryList.index(svar)] = svar
3260            VaryListChanges.append(name)
3261    if len(VaryListChanges) > 1:
3262        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,True)
3263    else:
3264        G2frame.dataFrame.SequentialFile.Enable(wxID_ORGSEQSEL,False)
3265    #-----------------------------------------------------------------------------------
3266    # build up the data table by columns -----------------------------------------------
3267    nRows = len(histNames)
3268    colList = [nRows*[True]]
3269    colSigs = [None]
3270    colLabels = ['Use']
3271    Types = [wg.GRID_VALUE_BOOL]
3272    # start with Rwp values
3273    if 'IMG ' not in histNames[0][:4]:
3274        colList += [[data[name]['Rvals']['Rwp'] for name in histNames]]
3275        colSigs += [None]
3276        colLabels += ['Rwp']
3277        Types += [wg.GRID_VALUE_FLOAT+':10,3',]
3278    # add % change in Chi^2 in last cycle
3279    if histNames[0][:4] not in ['SASD','IMG '] and Controls.get('ShowCell'):
3280        colList += [[100.*data[name]['Rvals'].get('DelChi2',-1) for name in histNames]]
3281        colSigs += [None]
3282        colLabels += [u'\u0394\u03C7\u00B2 (%)']
3283        Types += [wg.GRID_VALUE_FLOAT,]
3284    deltaChiCol = len(colLabels)-1
3285    # add changing sample parameters to table
3286    for key in sampleParms:
3287        colList += [sampleParms[key]]
3288        colSigs += [None]
3289        colLabels += [key]
3290        Types += [wg.GRID_VALUE_FLOAT,]
3291    sampleDict = {}
3292    for i,name in enumerate(histNames):
3293        sampleDict[name] = dict(zip(sampleParms.keys(),[sampleParms[key][i] for key in sampleParms.keys()])) 
3294    # add unique cell parameters TODO: review this where the cell symmetry changes (when possible)
3295    if Controls.get('ShowCell',False):
3296        for pId in sorted(RecpCellTerms):
3297            pfx = str(pId)+'::' # prefix for A values from phase
3298            cells = []
3299            cellESDs = []
3300            colLabels += [pfx+cellUlbl[i] for i in uniqCellIndx[pId]]
3301            colLabels += [pfx+'Vol']
3302            Types += (1+len(uniqCellIndx[pId]))*[wg.GRID_VALUE_FLOAT,]
3303            for name in histNames:
3304                covData = {
3305                    'varyList': [Dlookup.get(striphist(v),v) for v in data[name]['varyList']],
3306                    'covMatrix': data[name]['covMatrix']
3307                    }
3308                A = RecpCellTerms[pId][:] # make copy of starting A values
3309                # update with refined values
3310                for i in range(6):
3311                    var = str(pId)+'::A'+str(i)
3312                    if var in ESDlookup:
3313                        val = data[name]['newCellDict'][ESDlookup[var]][1] # get refined value
3314                        A[i] = val # override with updated value
3315                # apply symmetry
3316                Albls = [pfx+'A'+str(i) for i in range(6)]
3317                cellDict = dict(zip(Albls,A))
3318                A,zeros = G2stIO.cellFill(pfx,SGdata[pId],cellDict,zeroDict[pId])
3319                # convert to direct cell & add only unique values to table
3320                c = G2lat.A2cell(A)
3321                vol = G2lat.calc_V(A)
3322                cE = G2stIO.getCellEsd(pfx,SGdata[pId],A,covData)
3323                cells += [[c[i] for i in uniqCellIndx[pId]]+[vol]]
3324                cellESDs += [[cE[i] for i in uniqCellIndx[pId]]+[cE[-1]]]
3325            colList += zip(*cells)
3326            colSigs += zip(*cellESDs)
3327    # sort out the variables in their selected order
3328    varcols = 0
3329    for d in posdict.itervalues():
3330        varcols = max(varcols,max(d.keys())+1)
3331    # get labels for each column
3332    for i in range(varcols):
3333        lbl = ''
3334        for h in VaryListChanges:
3335            if posdict[h].get(i):
3336                if posdict[h].get(i) in lbl: continue
3337                if lbl != "": lbl += '/'
3338                lbl += posdict[h].get(i)
3339        colLabels.append(lbl)
3340    Types += varcols*[wg.GRID_VALUE_FLOAT]
3341    vals = []
3342    esds = []
3343    varsellist = None        # will be a list of variable names in the order they are selected to appear
3344    # tabulate values for each hist, leaving None for blank columns
3345    for name in histNames:
3346        if name in posdict:
3347            varsellist = [posdict[name].get(i) for i in range(varcols)]
3348            # translate variable names to how they will be used in the headings
3349            vs = [striphist(v,'*') for v in data[name]['varyList']]
3350            # determine the index for each column (or None) in the data[]['variables'] and ['sig'] lists
3351            sellist = [vs.index(v) if v is not None else None for v in varsellist]
3352            #sellist = [i if striphist(v,'*') in varsellist else None for i,v in enumerate(data[name]['varyList'])]
3353        if not varsellist: raise Exception()
3354        vals.append([data[name]['variables'][s] if s is not None else None for s in sellist])
3355        esds.append([data[name]['sig'][s] if s is not None else None for s in sellist])
3356        #GSASIIpath.IPyBreak()
3357    colList += zip(*vals)
3358    colSigs += zip(*esds)
3359               
3360    # tabulate constrained variables, removing histogram numbers if needed
3361    # from parameter label
3362    depValDict = {}
3363    depSigDict = {}
3364    for name in histNames:
3365        for var in data[name].get('depParmDict',{}):
3366            val,sig = data[name]['depParmDict'][var]
3367            svar = striphist(var,'*')
3368            if svar not in depValDict:
3369               depValDict[svar] = [val]
3370               depSigDict[svar] = [sig]
3371            else:
3372               depValDict[svar].append(val)
3373               depSigDict[svar].append(sig)
3374    # add the dependent constrained variables to the table
3375    for var in sorted(depValDict):
3376        if len(depValDict[var]) != len(histNames): continue
3377        colLabels.append(var)
3378        Types += [wg.GRID_VALUE_FLOAT,]
3379        colSigs += [depSigDict[var]]
3380        colList += [depValDict[var]]
3381
3382    # add atom parameters to table
3383    colLabels += atomLookup.keys()
3384    Types += len(atomLookup)*[wg.GRID_VALUE_FLOAT]
3385    for parm in sorted(atomLookup):
3386        colList += [[data[name]['newAtomDict'][atomLookup[parm]][1] for name in histNames]]
3387        if atomLookup[parm] in data[histNames[0]]['varyList']:
3388            col = data[histNames[0]]['varyList'].index(atomLookup[parm])
3389            colSigs += [[data[name]['sig'][col] for name in histNames]]
3390        else:
3391            colSigs += [None] # should not happen
3392    # evaluate Pseudovars, their ESDs and add them to grid
3393    for expr in Controls['SeqPseudoVars']:
3394        obj = Controls['SeqPseudoVars'][expr]
3395        calcobj = G2obj.ExpressionCalcObj(obj)
3396        valList = []
3397        esdList = []
3398        for seqnum,name in enumerate(histNames):
3399            sigs = data[name]['sig']
3400            G2mv.InitVars()
3401            parmDict = data[name].get('parmDict')
3402            badVary = data[name].get('badVary',[])
3403            constraintInfo = data[name].get('constraintInfo',[[],[],{},[],seqnum])
3404            groups,parmlist,constrDict,fixedList,ihst = constraintInfo
3405            varyList = data[name]['varyList']
3406            parmDict = data[name]['parmDict']
3407            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict,SeqHist=ihst)
3408            derivs = np.array(
3409                [EvalPSvarDeriv(calcobj,parmDict.copy(),sampleDict[name],var,ESD)
3410                 for var,ESD in zip(varyList,sigs)]
3411                )
3412            esdList.append(np.sqrt(
3413                np.inner(derivs,np.inner(data[name]['covMatrix'],derivs.T))
3414                ))
3415            PSvarDict = parmDict.copy()
3416            PSvarDict.update(sampleDict[name])
3417            UpdateParmDict(PSvarDict)
3418            calcobj.UpdateDict(PSvarDict)
3419            valList.append(calcobj.EvalExpression())
3420        if not esdList:
3421            esdList = None
3422        colList += [valList]
3423        colSigs += [esdList]
3424        colLabels += [expr]
3425        Types += [wg.GRID_VALUE_FLOAT,]
3426    #---- table build done -------------------------------------------------------------
3427
3428    # Make dict needed for creating & editing pseudovars (PSvarDict).
3429    name = histNames[0]
3430    parmDict = data[name].get('parmDict')
3431    PSvarDict = parmDict.copy()
3432    PSvarDict.update(sampleParms)
3433    UpdateParmDict(PSvarDict)
3434    # Also dicts of dependent (depVarDict) & independent vars (indepVarDict)
3435    # for Parametric fitting from the data table
3436    parmDict = dict(zip(colLabels,zip(*colList)[0])) # scratch dict w/all values in table
3437    parmDict.update(
3438        {var:val for var,val in data[name].get('newCellDict',{}).values()} #  add varied reciprocal cell terms
3439    )
3440    name = histNames[0]
3441
3442    #******************************************************************************
3443    # create a set of values for example evaluation of pseudovars and
3444    # this does not work for refinements that have differing numbers of variables.
3445    #raise Exception
3446    indepVarDict = {}     #  values in table w/o ESDs
3447    depVarDict = {}
3448    for i,var in enumerate(colLabels):
3449        if var == 'Use': continue
3450        if colList[i][0] is None:
3451            val,sig = firstValueDict.get(var,[None,None])
3452        elif colSigs[i]:
3453            val,sig = colList[i][0],colSigs[i][0]
3454        else:
3455            val,sig = colList[i][0],None
3456        if val is None:
3457            continue
3458        elif sig is None:
3459            indepVarDict[var] = val
3460        elif striphist(var) not in Dlookup:
3461            depVarDict[var] = val
3462    # add recip cell coeff. values
3463    depVarDict.update({var:val for var,val in data[name].get('newCellDict',{}).values()})
3464
3465    G2frame.dataDisplay = GSGrid(parent=G2frame.dataFrame)
3466    G2frame.SeqTable = Table(
3467        [list(c) for c in zip(*colList)],     # convert from columns to rows
3468        colLabels=colLabels,rowLabels=histNames,types=Types)
3469    G2frame.dataDisplay.SetTable(G2frame.SeqTable, True)
3470    #G2frame.dataDisplay.EnableEditing(False)
3471    # make all but first column read-only
3472    for c in range(1,len(colLabels)):
3473        for r in range(nRows):
3474            G2frame.dataDisplay.SetCellReadOnly(r,c)
3475    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_LEFT_DCLICK, PlotSelect)
3476    G2frame.dataDisplay.Bind(wg.EVT_GRID_LABEL_RIGHT_CLICK, SetLabelString)
3477    G2frame.dataDisplay.SetRowLabelSize(8*len(histNames[0]))       #pretty arbitrary 8
3478    G2frame.dataDisplay.SetMargins(0,0)
3479    G2frame.dataDisplay.AutoSizeColumns(True)
3480    if prevSize:
3481        G2frame.dataDisplay.SetSize(prevSize)
3482    else:
3483        G2frame.dataFrame.setSizePosLeft([700,350])
3484    # highlight unconverged shifts
3485    if histNames[0][:4] not in ['SASD','IMG ']:
3486        for row,name in enumerate(histNames):
3487            deltaChi = G2frame.SeqTable.GetValue(row,deltaChiCol)
3488            if deltaChi > 10.:
3489                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,0,0))
3490            elif deltaChi > 1.0:
3491                G2frame.dataDisplay.SetCellStyle(row,deltaChiCol,color=wx.Colour(255,255,0))
3492    G2frame.dataDisplay.InstallGridToolTip(GridSetToolTip,GridColLblToolTip)
3493    G2frame.dataDisplay.SendSizeEvent() # resize needed on mac
3494    G2frame.dataDisplay.Refresh() # shows colored text on mac
3495   
3496################################################################################
3497#####  Main PWDR panel
3498################################################################################           
3499       
3500def UpdatePWHKPlot(G2frame,kind,item):
3501    '''Called when the histogram main tree entry is called. Displays the
3502    histogram weight factor, refinement statistics for the histogram
3503    and the range of data for a simulation.
3504
3505    Also invokes a plot of the histogram.
3506    '''
3507    def onEditSimRange(event):
3508        'Edit simulation range'
3509        inp = [
3510            min(data[1][0]),
3511            max(data[1][0]),
3512            None
3513            ]
3514        inp[2] = (inp[1] - inp[0])/(len(data[1][0])-1.)
3515        names = ('start angle', 'end angle', 'step size')
3516        dictlst = [inp] * len(inp)
3517        elemlst = range(len(inp))
3518        dlg = G2G.ScrolledMultiEditor(
3519            G2frame,[inp] * len(inp), range(len(inp)), names,
3520            header='Edit simulation range',
3521            minvals=(0.001,0.001,0.0001),
3522            maxvals=(180.,180.,.1),
3523            )
3524        dlg.CenterOnParent()
3525        val = dlg.ShowModal()
3526        dlg.Destroy()
3527        if val != wx.ID_OK: return
3528        if inp[0] > inp[1]:
3529            end,start,step = inp
3530        else:               
3531            start,end,step = inp
3532        step = abs(step)
3533        N = int((end-start)/step)+1
3534        newdata = np.linspace(start,end,N,True)
3535        if len(newdata) < 2: return # too small a range - reject
3536        data[1] = [newdata,np.zeros_like(newdata),np.ones_like(newdata),
3537            np.zeros_like(newdata),np.zeros_like(newdata),np.zeros_like(newdata)]
3538        Tmin = newdata[0]
3539        Tmax = newdata[-1]
3540        G2frame.PatternTree.SetItemPyData(GetPatternTreeItemId(G2frame,item,'Limits'),
3541            [(Tmin,Tmax),[Tmin,Tmax]])
3542        UpdatePWHKPlot(G2frame,kind,item) # redisplay data screen
3543
3544    def OnPlot3DHKL(event):
3545        refList = data[1]['RefList']
3546        FoMax = np.max(refList.T[8+Super])
3547        Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
3548        Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
3549        Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
3550        controls = {'Type' : 'Fosq','Iscale' : False,'HKLmax' : Hmax,'HKLmin' : Hmin,
3551            'FoMax' : FoMax,'Scale' : 1.0,'Drawing':{'viewPoint':[Vpoint,[]],'default':Vpoint[:],
3552            'backColor':[0,0,0],'depthFog':False,'Zclip':10.0,'cameraPos':10.,'Zstep':0.05,
3553            'Scale':1.0,'oldxy':[],'viewDir':[1,0,0]},'Super':Super,'SuperVec':SuperVec}
3554        G2plt.Plot3DSngl(G2frame,newPlot=True,Data=controls,hklRef=refList,Title=phaseName)
3555       
3556    def OnErrorAnalysis(event):
3557        G2plt.PlotDeltSig(G2frame,kind)
3558       
3559    def OnWtFactor(event):
3560        try:
3561            val = float(wtval.GetValue())
3562        except ValueError:
3563            val = data[0]['wtFactor']
3564        data[0]['wtFactor'] = val
3565        wtval.SetValue('%.3f'%(val))
3566       
3567    def onCopyPlotCtrls(event):
3568        '''Respond to menu item to copy multiple sections from a histogram.
3569        Need this here to pass on the G2frame object.
3570        '''
3571        G2pdG.CopyPlotCtrls(G2frame)
3572
3573    def onCopySelectedItems(event):
3574        '''Respond to menu item to copy multiple sections from a histogram.
3575        Need this here to pass on the G2frame object.
3576        '''
3577        G2pdG.CopySelectedHistItems(G2frame)
3578           
3579    data = G2frame.PatternTree.GetItemPyData(item)
3580#patches
3581    if 'wtFactor' not in data[0]:
3582        data[0] = {'wtFactor':1.0}
3583    #if isinstance(data[1],list) and kind == 'HKLF':
3584    if 'list' in str(type(data[1])) and kind == 'HKLF':
3585        RefData = {'RefList':[],'FF':[]}
3586        for ref in data[1]:
3587            RefData['RefList'].append(ref[:11]+[ref[13],])
3588            RefData['FF'].append(ref[14])
3589        data[1] = RefData
3590        G2frame.PatternTree.SetItemPyData(item,data)
3591#end patches
3592    if G2frame.dataDisplay:
3593        G2frame.dataDisplay.Destroy()
3594    if kind in ['PWDR','SASD']:
3595        SetDataMenuBar(G2frame,G2frame.dataFrame.PWDRMenu)
3596        G2frame.dataFrame.Bind(wx.EVT_MENU, OnErrorAnalysis, id=wxID_PWDANALYSIS)
3597        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopySelectedItems, id=wxID_PWDCOPY)
3598        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopyPlotCtrls, id=wxID_PLOTCTRLCOPY)
3599    elif kind in ['HKLF',]:
3600        SetDataMenuBar(G2frame,G2frame.dataFrame.HKLFMenu)
3601#        G2frame.dataFrame.Bind(wx.EVT_MENU, OnErrorAnalysis, id=wxID_PWDANALYSIS)
3602        G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlot3DHKL, id=wxID_PWD3DHKLPLOT)
3603#        G2frame.dataFrame.Bind(wx.EVT_MENU, onCopySelectedItems, id=wxID_PWDCOPY)
3604    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
3605   
3606    mainSizer = wx.BoxSizer(wx.VERTICAL)
3607    mainSizer.Add((5,5),)
3608    wtSizer = wx.BoxSizer(wx.HORIZONTAL)
3609    wtSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Weight factor: '),0,WACV)
3610    wtval = wx.TextCtrl(G2frame.dataDisplay,-1,'%.3f'%(data[0]['wtFactor']),style=wx.TE_PROCESS_ENTER)
3611    wtval.Bind(wx.EVT_TEXT_ENTER,OnWtFactor)
3612    wtval.Bind(wx.EVT_KILL_FOCUS,OnWtFactor)
3613    wtSizer.Add(wtval,0,WACV)
3614    mainSizer.Add(wtSizer)
3615    if data[0].get('Dummy'):
3616        simSizer = wx.BoxSizer(wx.HORIZONTAL)
3617        Tmin = min(data[1][0])
3618        Tmax = max(data[1][0])
3619        num = len(data[1][0])
3620        step = (Tmax - Tmin)/(num-1)
3621        t = u'2\u03b8' # 2theta
3622        lbl =  u'Simulation range: {:.2f} to {:.2f} {:s}\nwith {:.4f} steps ({:d} points)'
3623        lbl += u'\n(Edit range resets observed intensities).'
3624        lbl = lbl.format(Tmin,Tmax,t,step,num)
3625        simSizer.Add(wx.StaticText(G2frame.dataDisplay,wx.ID_ANY,lbl),
3626                    0,WACV)
3627        but = wx.Button(G2frame.dataDisplay,wx.ID_ANY,"Edit range")
3628        but.Bind(wx.EVT_BUTTON,onEditSimRange)
3629        simSizer.Add(but,0,WACV)
3630        mainSizer.Add(simSizer)
3631    if 'Nobs' in data[0]:
3632        mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
3633            ' Data residual wR: %.3f%% on %d observations'%(data[0]['wR'],data[0]['Nobs'])))
3634        for value in data[0]:
3635            if 'Nref' in value:
3636                mainSizer.Add((5,5),)
3637                pfx = value.split('Nref')[0]
3638                name = data[0].get(pfx.split(':')[0]+'::Name','?')
3639                mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' For phase '+name+':'))
3640                mainSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,
3641                    u' Unweighted phase residuals RF\u00b2: %.3f%%, RF: %.3f%% on %d reflections  '% \
3642                    (data[0][pfx+'Rf^2'],data[0][pfx+'Rf'],data[0][value])))
3643    mainSizer.Add((5,5),)
3644    mainSizer.Layout()   
3645    G2frame.dataDisplay.SetSizer(mainSizer)
3646    Size = mainSizer.Fit(G2frame.dataFrame)
3647    Size[1] += 10
3648    G2frame.dataFrame.setSizePosLeft(Size)
3649    G2frame.PatternTree.SetItemPyData(item,data)
3650    if kind in ['PWDR','SASD']:
3651        G2plt.PlotPatterns(G2frame,plotType=kind,newPlot=True)
3652    elif kind == 'HKLF':
3653        Name = G2frame.PatternTree.GetItemText(item)
3654        phaseName = G2pdG.IsHistogramInAnyPhase(G2frame,Name)
3655        if phaseName:
3656            pId = GetPatternTreeItemId(G2frame,G2frame.root,'Phases')
3657            phaseId =  GetPatternTreeItemId(G2frame,pId,phaseName)
3658            General = G2frame.PatternTree.GetItemPyData(phaseId)['General']
3659            Super = General.get('Super',0)
3660            SuperVec = General.get('SuperVec',[])
3661        else:
3662            Super = 0
3663            SuperVec = []       
3664        refList = data[1]['RefList']
3665        FoMax = np.max(refList.T[5+data[1].get('Super',0)])
3666        page = G2frame.G2plotNB.nb.GetSelection()
3667        tab = ''
3668        if page >= 0:
3669            tab = G2frame.G2plotNB.nb.GetPageText(page)
3670        if '3D' in tab:
3671            Hmin = np.array([int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))])
3672            Hmax = np.array([int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))])
3673            Vpoint = [int(np.mean(refList.T[0])),int(np.mean(refList.T[1])),int(np.mean(refList.T[2]))]
3674            Page = G2frame.G2plotNB.nb.GetPage(page)
3675            controls = Page.controls
3676            G2plt.Plot3DSngl(G2frame,newPlot=False,Data=controls,hklRef=refList,Title=phaseName)
3677        else:
3678            controls = {'Type' : 'Fo','ifFc' : True,     
3679                'HKLmax' : [int(np.max(refList.T[0])),int(np.max(refList.T[1])),int(np.max(refList.T[2]))],
3680                'HKLmin' : [int(np.min(refList.T[0])),int(np.min(refList.T[1])),int(np.min(refList.T[2]))],
3681                'FoMax' : FoMax,'Zone' : '001','Layer' : 0,'Scale' : 1.0,'Super':Super,'SuperVec':SuperVec}
3682            G2plt.PlotSngl(G2frame,newPlot=True,Data=controls,hklRef=refList)
3683                 
3684################################################################################
3685#####  Pattern tree routines
3686################################################################################           
3687       
3688def GetPatternTreeDataNames(G2frame,dataTypes):
3689    '''Needs a doc string
3690    '''
3691    names = []
3692    item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)       
3693    while item:
3694        name = G2frame.PatternTree.GetItemText(item)
3695        if name[:4] in dataTypes:
3696            names.append(name)
3697        item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
3698    return names
3699                         
3700def GetPatternTreeItemId(G2frame, parentId, itemText):
3701    '''Needs a doc string
3702    '''
3703    item, cookie = G2frame.PatternTree.GetFirstChild(parentId)
3704    while item:
3705        if G2frame.PatternTree.GetItemText(item) == itemText:
3706            return item
3707        item, cookie = G2frame.PatternTree.GetNextChild(parentId, cookie)
3708    return 0               
3709
3710def MovePatternTreeToGrid(G2frame,item):
3711    '''Called from GSASII.OnPatternTreeSelChanged when a item is selected on the tree
3712    '''
3713   
3714#    print G2frame.PatternTree.GetItemText(item)
3715   
3716    oldPage = None # will be set later if already on a Phase item
3717    if G2frame.dataFrame:
3718        SetDataMenuBar(G2frame)
3719        if G2frame.dataFrame.GetLabel() == 'Comments':
3720            try:
3721                data = [G2frame.dataDisplay.GetValue()]
3722                G2frame.dataDisplay.Clear() 
3723                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Comments')
3724                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
3725            except:     #clumsy but avoids dead window problem when opening another project
3726                pass
3727        elif G2frame.dataFrame.GetLabel() == 'Notebook':
3728            try:
3729                data = [G2frame.dataDisplay.GetValue()]
3730                G2frame.dataDisplay.Clear() 
3731                Id = GetPatternTreeItemId(G2frame,G2frame.root, 'Notebook')
3732                if Id: G2frame.PatternTree.SetItemPyData(Id,data)
3733            except:     #clumsy but avoids dead window problem when opening another project
3734                pass
3735        elif 'Phase Data for' in G2frame.dataFrame.GetLabel():
3736            if G2frame.dataDisplay: 
3737                oldPage = G2frame.dataDisplay.GetSelection()
3738        G2frame.dataFrame.Clear()
3739        G2frame.dataFrame.SetLabel('')
3740    else:
3741        #create the frame for the data item window
3742        G2frame.dataFrame = DataFrame(parent=G2frame.mainPanel,frame=G2frame)
3743        G2frame.dataFrame.PhaseUserSize = None
3744       
3745    G2frame.dataFrame.Raise()           
3746    G2frame.PickId = 0
3747    parentID = G2frame.root
3748    #for i in G2frame.ExportPattern: i.Enable(False)
3749    defWid = [250,150]
3750    if item != G2frame.root:
3751        parentID = G2frame.PatternTree.GetItemParent(item)
3752    if G2frame.PatternTree.GetItemParent(item) == G2frame.root:
3753        G2frame.PatternId = item
3754        G2frame.PickId = item
3755        if G2frame.PatternTree.GetItemText(item) == 'Notebook':
3756            SetDataMenuBar(G2frame,G2frame.dataFrame.DataNotebookMenu)
3757            G2frame.PatternId = 0
3758            #for i in G2frame.ExportPattern: i.Enable(False)
3759            data = G2frame.PatternTree.GetItemPyData(item)
3760            UpdateNotebook(G2frame,data)
3761        elif G2frame.PatternTree.GetItemText(item) == 'Controls':
3762            G2frame.PatternId = 0
3763            #for i in G2frame.ExportPattern: i.Enable(False)
3764            data = G2frame.PatternTree.GetItemPyData(item)
3765            if not data:           #fill in defaults
3766                data = copy.copy(G2obj.DefaultControls)    #least squares controls
3767                G2frame.PatternTree.SetItemPyData(item,data)                             
3768            for i in G2frame.Refine: i.Enable(True)
3769            G2frame.EnableSeqRefineMenu()
3770            UpdateControls(G2frame,data)
3771        elif G2frame.PatternTree.GetItemText(item) == 'Sequential results':
3772            data = G2frame.PatternTree.GetItemPyData(item)
3773            UpdateSeqResults(G2frame,data)
3774        elif G2frame.PatternTree.GetItemText(item) == 'Covariance':
3775            data = G2frame.PatternTree.GetItemPyData(item)
3776            G2frame.dataFrame.setSizePosLeft(defWid)
3777            text = ''
3778            if 'Rvals' in data:
3779                Nvars = len(data['varyList'])
3780                Rvals = data['Rvals']
3781                text = '\nFinal residuals: \nwR = %.3f%% \nchi**2 = %.1f \nGOF = %.2f'%(Rvals['Rwp'],Rvals['chisq'],Rvals['GOF'])
3782                text += '\nNobs = %d \nNvals = %d'%(Rvals['Nobs'],Nvars)
3783                if 'lamMax' in Rvals:
3784                    text += '\nlog10 MaxLambda = %.1f'%(np.log10(Rvals['lamMax']))
3785            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
3786                value='See plot window for covariance display'+text,style=wx.TE_MULTILINE)
3787            G2plt.PlotCovariance(G2frame,data)
3788        elif G2frame.PatternTree.GetItemText(item) == 'Constraints':
3789            data = G2frame.PatternTree.GetItemPyData(item)
3790            G2cnstG.UpdateConstraints(G2frame,data)
3791        elif G2frame.PatternTree.GetItemText(item) == 'Rigid bodies':
3792            data = G2frame.PatternTree.GetItemPyData(item)
3793            G2cnstG.UpdateRigidBodies(G2frame,data)
3794        elif G2frame.PatternTree.GetItemText(item) == 'Restraints':
3795            data = G2frame.PatternTree.GetItemPyData(item)
3796            Phases = G2frame.GetPhaseData()
3797            phase = ''
3798            phaseName = ''
3799            if Phases:
3800                phaseName = Phases.keys()[0]
3801            G2frame.dataFrame.setSizePosLeft(defWid)
3802            G2restG.UpdateRestraints(G2frame,data,Phases,phaseName)
3803        elif 'IMG' in G2frame.PatternTree.GetItemText(item):
3804            G2frame.Image = item
3805            G2frame.dataFrame.SetTitle('Image Data')
3806            data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId( \
3807                G2frame,item,'Image Controls'))
3808            G2imG.UpdateImageData(G2frame,data)
3809            G2plt.PlotImage(G2frame,newPlot=True)
3810        elif 'PKS' in G2frame.PatternTree.GetItemText(item):
3811            G2plt.PlotPowderLines(G2frame)
3812        elif 'PWDR' in G2frame.PatternTree.GetItemText(item):
3813            #for i in G2frame.ExportPattern: i.Enable(True)
3814            if G2frame.EnablePlot:
3815                UpdatePWHKPlot(G2frame,'PWDR',item)
3816        elif 'SASD' in G2frame.PatternTree.GetItemText(item):
3817            #for i in G2frame.ExportPattern: i.Enable(True)
3818            if G2frame.EnablePlot:
3819                UpdatePWHKPlot(G2frame,'SASD',item)
3820        elif 'HKLF' in G2frame.PatternTree.GetItemText(item):
3821            G2frame.Sngl = True
3822            UpdatePWHKPlot(G2frame,'HKLF',item)
3823        elif 'PDF' in G2frame.PatternTree.GetItemText(item):
3824            G2frame.PatternId = item
3825            for i in G2frame.ExportPDF: i.Enable(True)
3826            G2plt.PlotISFG(G2frame,type='S(Q)')
3827        elif G2frame.PatternTree.GetItemText(item) == 'Phases':
3828            G2frame.dataFrame.setSizePosLeft(defWid)
3829            wx.TextCtrl(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize(),
3830                value='Select one phase to see its parameters')           
3831    elif 'I(Q)' in G2frame.PatternTree.GetItemText(item):
3832        G2frame.PickId = item
3833        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3834        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
3835        G2pdG.UpdatePDFGrid(G2frame,data)
3836        G2plt.PlotISFG(G2frame,type='I(Q)',newPlot=True)
3837    elif 'S(Q)' in G2frame.PatternTree.GetItemText(item):
3838        G2frame.PickId = item
3839        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3840        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
3841        G2pdG.UpdatePDFGrid(G2frame,data)
3842        G2plt.PlotISFG(G2frame,type='S(Q)',newPlot=True)
3843    elif 'F(Q)' in G2frame.PatternTree.GetItemText(item):
3844        G2frame.PickId = item
3845        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3846        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
3847        G2pdG.UpdatePDFGrid(G2frame,data)
3848        G2plt.PlotISFG(G2frame,type='F(Q)',newPlot=True)
3849    elif 'G(R)' in G2frame.PatternTree.GetItemText(item):
3850        G2frame.PickId = item
3851        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3852        data = G2frame.PatternTree.GetItemPyData(GetPatternTreeItemId(G2frame,G2frame.PatternId,'PDF Controls'))
3853        G2pdG.UpdatePDFGrid(G2frame,data)
3854        G2plt.PlotISFG(G2frame,type='G(R)',newPlot=True)           
3855    elif G2frame.PatternTree.GetItemText(parentID) == 'Phases':
3856        G2frame.PickId = item
3857        data = G2frame.PatternTree.GetItemPyData(item)
3858        G2phG.UpdatePhaseData(G2frame,item,data,oldPage)
3859    elif G2frame.PatternTree.GetItemText(item) == 'Comments':
3860        SetDataMenuBar(G2frame,G2frame.dataFrame.DataCommentsMenu)
3861        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3862        G2frame.PickId = item
3863        data = G2frame.PatternTree.GetItemPyData(item)
3864        UpdateComments(G2frame,data)
3865    elif G2frame.PatternTree.GetItemText(item) == 'Image Controls':
3866        G2frame.dataFrame.SetTitle('Image Controls')
3867        G2frame.PickId = item
3868        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3869        masks = G2frame.PatternTree.GetItemPyData(
3870            GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
3871        data = G2frame.PatternTree.GetItemPyData(item)
3872        G2imG.UpdateImageControls(G2frame,data,masks)
3873        G2plt.PlotImage(G2frame)
3874    elif G2frame.PatternTree.GetItemText(item) == 'Masks':
3875        G2frame.dataFrame.SetTitle('Masks')
3876        G2frame.PickId = item
3877        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3878        data = G2frame.PatternTree.GetItemPyData(item)
3879        G2imG.UpdateMasks(G2frame,data)
3880        G2plt.PlotImage(G2frame)
3881    elif G2frame.PatternTree.GetItemText(item) == 'Stress/Strain':
3882        G2frame.dataFrame.SetTitle('Stress/Strain')
3883        G2frame.PickId = item
3884        G2frame.Image = G2frame.PatternTree.GetItemParent(item)
3885        data = G2frame.PatternTree.GetItemPyData(item)
3886        G2plt.PlotImage(G2frame)
3887        G2plt.PlotStrain(G2frame,data,newPlot=True)
3888        G2imG.UpdateStressStrain(G2frame,data)
3889    elif G2frame.PatternTree.GetItemText(item) == 'PDF Controls':
3890        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3891        for i in G2frame.ExportPDF: i.Enable(True)
3892        G2frame.PickId = item
3893        data = G2frame.PatternTree.GetItemPyData(item)
3894        G2pdG.UpdatePDFGrid(G2frame,data)
3895        G2plt.PlotISFG(G2frame,type='I(Q)')
3896        G2plt.PlotISFG(G2frame,type='S(Q)')
3897        G2plt.PlotISFG(G2frame,type='F(Q)')
3898        G2plt.PlotISFG(G2frame,type='G(R)')
3899    elif G2frame.PatternTree.GetItemText(item) == 'Peak List':
3900        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3901        for i in G2frame.ExportPeakList: i.Enable(True)
3902        G2frame.PickId = item
3903        data = G2frame.PatternTree.GetItemPyData(item)
3904#patch
3905        if 'list' in str(type(data)):
3906            data = {'peaks':data,'sigDict':{}}
3907            G2frame.PatternTree.SetItemPyData(item,data)
3908#end patch
3909        G2pdG.UpdatePeakGrid(G2frame,data)
3910        G2plt.PlotPatterns(G2frame)
3911    elif G2frame.PatternTree.GetItemText(item) == 'Background':
3912        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3913        G2frame.PickId = item
3914        data = G2frame.PatternTree.GetItemPyData(item)
3915        G2pdG.UpdateBackground(G2frame,data)
3916        G2plt.PlotPatterns(G2frame)
3917    elif G2frame.PatternTree.GetItemText(item) == 'Limits':
3918        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3919        datatype = G2frame.PatternTree.GetItemText(G2frame.PatternId)[:4]
3920        G2frame.PickId = item
3921        data = G2frame.PatternTree.GetItemPyData(item)
3922        G2pdG.UpdateLimitsGrid(G2frame,data,datatype)
3923        G2plt.PlotPatterns(G2frame,plotType=datatype)
3924    elif G2frame.PatternTree.GetItemText(item) == 'Instrument Parameters':
3925        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3926        G2frame.PickId = item
3927        data = G2frame.PatternTree.GetItemPyData(item)[0]
3928        G2pdG.UpdateInstrumentGrid(G2frame,data)
3929        if 'P' in data['Type'][0]:          #powder data only
3930            G2plt.PlotPeakWidths(G2frame)
3931    elif G2frame.PatternTree.GetItemText(item) == 'Models':
3932        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3933        G2frame.PickId = item
3934        data = G2frame.PatternTree.GetItemPyData(item)
3935        G2pdG.UpdateModelsGrid(G2frame,data)
3936        G2plt.PlotPatterns(G2frame,plotType='SASD')
3937        if len(data['Size']['Distribution']):
3938            G2plt.PlotSASDSizeDist(G2frame)
3939    elif G2frame.PatternTree.GetItemText(item) == 'Substances':
3940        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3941        G2frame.PickId = item
3942        data = G2frame.PatternTree.GetItemPyData(item)
3943        G2pdG.UpdateSubstanceGrid(G2frame,data)
3944    elif G2frame.PatternTree.GetItemText(item) == 'Sample Parameters':
3945        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3946        G2frame.PickId = item
3947        data = G2frame.PatternTree.GetItemPyData(item)
3948        datatype = G2frame.PatternTree.GetItemPyData(G2frame.PatternId)[2][:4]
3949
3950        if 'Temperature' not in data:           #temp fix for old gpx files
3951            data = {'Scale':[1.0,True],'Type':'Debye-Scherrer','Absorption':[0.0,False],'DisplaceX':[0.0,False],
3952                'DisplaceY':[0.0,False],'Diffuse':[],'Temperature':300.,'Pressure':1.0,
3953                    'FreePrm1':0.,'FreePrm2':0.,'FreePrm3':0.,
3954                    'Gonio. radius':200.0}
3955            G2frame.PatternTree.SetItemPyData(item,data)
3956   
3957        G2pdG.UpdateSampleGrid(G2frame,data)
3958        G2plt.PlotPatterns(G2frame,plotType=datatype)
3959    elif G2frame.PatternTree.GetItemText(item) == 'Index Peak List':
3960        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3961        for i in G2frame.ExportPeakList: i.Enable(True)
3962        G2frame.PickId = item
3963        data = G2frame.PatternTree.GetItemPyData(item)
3964#patch
3965        if len(data) != 2:
3966            data = [data,[]]
3967            G2frame.PatternTree.SetItemPyData(item,data)
3968#end patch
3969        G2pdG.UpdateIndexPeaksGrid(G2frame,data)
3970        if 'PKS' in G2frame.PatternTree.GetItemText(G2frame.PatternId):
3971            G2plt.PlotPowderLines(G2frame)
3972        else:
3973            G2plt.PlotPatterns(G2frame)
3974    elif G2frame.PatternTree.GetItemText(item) == 'Unit Cells List':
3975        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3976        G2frame.PickId = item
3977        data = G2frame.PatternTree.GetItemPyData(item)
3978        if not data:
3979            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
3980            data.append([0,0,0,0,0,0,0,0,0,0,0,0,0,0])      #Bravais lattice flags
3981            data.append([])                                 #empty cell list
3982            data.append([])                                 #empty dmin
3983            data.append({})                                 #empty superlattice stuff
3984            G2frame.PatternTree.SetItemPyData(item,data)                             
3985#patch
3986        if len(data) < 5:
3987            data.append({'Use':False,'ModVec':[0,0,0.1],'maxH':1,'ssSymb':''})                                 #empty superlattice stuff
3988            G2frame.PatternTree.SetItemPyData(item,data) 
3989#end patch
3990        G2pdG.UpdateUnitCellsGrid(G2frame,data)
3991        if 'PKS' in G2frame.PatternTree.GetItemText(G2frame.PatternId):
3992            G2plt.PlotPowderLines(G2frame)
3993        else:
3994            G2plt.PlotPatterns(G2frame)
3995    elif G2frame.PatternTree.GetItemText(item) == 'Reflection Lists':   #powder reflections
3996        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
3997        G2frame.PickId = item
3998        data = G2frame.PatternTree.GetItemPyData(item)
3999        G2frame.RefList = ''
4000        if len(data):
4001            G2frame.RefList = data.keys()[0]
4002        G2pdG.UpdateReflectionGrid(G2frame,data)
4003        G2plt.PlotPatterns(G2frame)
4004    elif G2frame.PatternTree.GetItemText(item) == 'Reflection List':    #HKLF reflections
4005        G2frame.PatternId = G2frame.PatternTree.GetItemParent(item)
4006        name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
4007        data = G2frame.PatternTree.GetItemPyData(G2frame.PatternId)
4008        G2pdG.UpdateReflectionGrid(G2frame,data,HKLF=True,Name=name)
4009    G2frame.dataFrame.Raise()
4010
4011def SetDataMenuBar(G2frame,menu=None):
4012    '''Set the menu for the data frame. On the Mac put this
4013    menu for the data tree window instead.
4014
4015    Note that data frame items do not have menus, for these (menu=None)
4016    display a blank menu or on the Mac display the standard menu for
4017    the data tree window.
4018    '''
4019    if sys.platform == "darwin":
4020        if menu is None:
4021            G2frame.SetMenuBar(G2frame.GSASIIMenu)
4022        else:
4023            G2frame.SetMenuBar(menu)
4024    else:
4025        if menu is None:
4026            G2frame.dataFrame.SetMenuBar(G2frame.dataFrame.BlankMenu)
4027        else:
4028            G2frame.dataFrame.SetMenuBar(menu)
4029
4030def HorizontalLine(sizer,parent):
4031    '''Draws a horizontal line as wide as the window.
4032    This shows up on the Mac as a very thin line, no matter what I do
4033    '''
4034    line = wx.StaticLine(parent,-1, size=(-1,3), style=wx.LI_HORIZONTAL)
4035    sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.ALL, 10)
4036
4037def HowDidIgetHere():
4038    '''Show a traceback with calls that brought us to the current location.
4039    Used for debugging.
4040    '''
4041    import traceback
4042    print 70*'*'   
4043    for i in traceback.format_list(traceback.extract_stack()[:-1]): print(i.strip.rstrip())
4044    print 70*'*'   
4045       
Note: See TracBrowser for help on using the repository browser.