source: trunk/GSASIIgrid.py @ 1779

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

Add FWHM to exported peak lists
change default F2 refinement to False
Add histogram name to axial dist plots from DData

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