source: trunk/GSASIIgrid.py @ 1815

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

revise MovePatternTreeToGrid? to have PickId? set near top & not in if/elif blocks
fix plot problem - not able to move difference curve.
put test on negative diag terms in G-matrix in "right" place

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