source: trunk/GSASIIgrid.py @ 1787

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

remove user reject HKL selection from Reflection List
implement rule bases user reject for HKLF reflections in Controls
user rejection changes mul to -mul; reflection list shows this in red
implement tool tip on 3Dhkl plots showing hkl indices on each point encountered

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