source: trunk/GSASIIgrid.py @ 1778

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

allow Refine texture menu item to appear; use DestroyChildren? to clear the Texture tab before filling
fix azimuth calculations after image integration
put Azimuth in Sample parms.
add 3 prints to debug option in G2mapvars
add Omega, Chi, Phi & Azimuth to sequential results table if they vary

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