source: trunk/GSASIIgrid.py @ 2118

Last change on this file since 2118 was 2118, checked in by vondreele, 6 years ago

Fix plotting error with mixed powder data types
Add refinement of modulation vector for TOF

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