source: trunk/GSASIIgrid.py @ 1770

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

moved MultipleChoicesDialog? and SingleChoiceDialog? from G2grid to G2ctrls

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