source: trunk/GSASIIgrid.py @ 1822

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

selections on tree now update properly
Refine now returns to whatever tree item was selected & which phase tab was selected

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