source: trunk/GSASIIimgGUI.py @ 3136

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

make GSAS-II python 3.6 compliant & preserve python 2.7 use;changes:
do from future import division, print_function for all GSAS-II py sources
all menu items revised to be py 2.7/3.6 compliant
all wx.OPEN --> wx.FD_OPEN in file dialogs
all integer divides (typically for image pixel math) made explicit with ; ambiguous ones made floats as appropriate
all print "stuff" --> print (stuff)
all print >> pFile,'stuff' --> pFile.writeCIFtemplate('stuff')
all read file opens made explicit 'r' or 'rb'
all cPickle imports made for py2.7 or 3.6 as cPickle or _pickle; test for '2' platform.version_tuple[0] for py 2.7
define cPickleload to select load(fp) or load(fp,encoding='latin-1') for loading gpx files; provides cross compatibility between py 2.7/3.6 gpx files
make dict.keys() as explicit list(dict.keys()) as needed (NB: possible source of remaining py3.6 bugs)
make zip(a,b) as explicit list(zip(a,b)) as needed (NB: possible source of remaining py3.6 bugs)
select unichr/chr according test for '2' platform.version_tuple[0] for py 2.7 (G2pwdGUI * G2plot) for special characters
select wg.EVT_GRID_CELL_CHANGE (classic) or wg.EVT_GRID_CELL_CHANGED (phoenix) in grid Bind
maxint --> maxsize; used in random number stuff
raise Exception,"stuff" --> raise Exception("stuff")
wx 'classic' sizer.DeleteWindows?() or 'phoenix' sizer.Clear(True)
wx 'classic' SetToolTipString?(text) or 'phoenix' SetToolTip?(wx.ToolTip?(text)); define SetToolTipString?(self,text) to handle the choice in plots
status.SetFields? --> status.SetStatusText?
'classic' AddSimpleTool? or 'phoenix' self.AddTool? for plot toolbar; Bind different as well
define GetItemPydata? as it doesn't exist in wx 'phoenix'
allow python versions 2.7 & 3.6 to run GSAS-II
Bind override commented out - no logging capability (NB: remove all logging code?)
all import ContentsValidator? open filename & test if valid then close; filepointer removed from Reader
binary importers (mostly images) test for 'byte' type & convert as needed to satisfy py 3.6 str/byte rules

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 157.6 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2017-10-23 16:39:16 +0000 (Mon, 23 Oct 2017) $
5# $Author: vondreele $
6# $Revision: 3136 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 3136 2017-10-23 16:39:16Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIimgGUI: Image GUI*
12-------------------------
13
14Control image display and processing
15
16'''
17from __future__ import division, print_function
18import os
19import copy
20import glob
21import re
22import math
23import sys
24import wx
25import wx.lib.mixins.listctrl  as  listmix
26import wx.grid as wg
27import matplotlib as mpl
28import numpy as np
29import GSASIIpath
30GSASIIpath.SetVersionNumber("$Revision: 3136 $")
31import GSASIIimage as G2img
32import GSASIImath as G2mth
33import GSASIIElem as G2elem
34import GSASIIpwdGUI as G2pdG
35import GSASIIplot as G2plt
36import GSASIIIO as G2IO
37import GSASIIdataGUI as G2gd
38import GSASIIctrlGUI as G2G
39import GSASIIobj as G2obj
40import GSASIIpy3 as G2py3
41
42VERY_LIGHT_GREY = wx.Colour(235,235,235)
43WACV = wx.ALIGN_CENTER_VERTICAL
44
45# trig functions in degrees
46sind = lambda x: math.sin(x*math.pi/180.)
47tand = lambda x: math.tan(x*math.pi/180.)
48cosd = lambda x: math.cos(x*math.pi/180.)
49asind = lambda x: 180.*math.asin(x)/math.pi
50tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
51tof2q = lambda t,C:2.0*math.pi*C/t
52atand = lambda x: 180.*math.atan(x)/math.pi
53atan2d = lambda y,x: 180.*math.atan2(y,x)/math.pi
54
55################################################################################
56##### Image Data
57################################################################################
58
59def GetImageZ(G2frame,data,newRange=False):
60    '''Gets image & applies dark, background & flat background corrections
61    :param wx.Frame G2frame: main GSAS-II frame
62    param: dict data: Image Controls dictionary
63    return: array sumImg: corrected image for background/dark/flat back
64    '''
65   
66    Npix,imagefile,imagetag = G2IO.GetCheckImageFile(G2frame,G2frame.Image)
67    formatName = data.get('formatName','')
68    sumImg = G2IO.GetImageData(G2frame,imagefile,True,ImageTag=imagetag,FormatName=formatName)
69    if sumImg is None:
70        return []
71    darkImg = False
72    if 'dark image' in data:
73        darkImg,darkScale = data['dark image']
74        if darkImg:
75            Did = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, darkImg)
76            if Did:
77                Ddata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Did,'Image Controls'))
78                dformatName = Ddata.get('formatName','')
79                Npix,darkfile,imagetag = G2IO.GetCheckImageFile(G2frame,Did)
80                darkImage = G2IO.GetImageData(G2frame,darkfile,True,ImageTag=imagetag,FormatName=dformatName)
81                if darkImg is not None:               
82                    sumImg += np.array(darkImage*darkScale,dtype='int32')
83    if 'background image' in data:
84        backImg,backScale = data['background image']           
85        if backImg:     #ignores any transmission effect in the background image
86            Bid = G2gd.GetGPXtreeItemId(G2frame, G2frame.root, backImg)
87            if Bid:
88                Npix,backfile,imagetag = G2IO.GetCheckImageFile(G2frame,Bid)
89                Bdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Bid,'Image Controls'))
90                bformatName = Bdata.get('formatName','')
91                backImage = G2IO.GetImageData(G2frame,backfile,True,ImageTag=imagetag,FormatName=bformatName)
92                if darkImg and backImage is not None:
93                    backImage += np.array(darkImage*darkScale/backScale,dtype='int32')
94                if backImage is not None:
95                    sumImg += np.array(backImage*backScale,dtype='int32')
96#    if darkImg: del darkImg         #force cleanup
97#    if backImg: del backImg
98    sumImg -= int(data.get('Flat Bkg',0))
99    Imax = np.max(sumImg)
100    if 'range' not in data or newRange:
101        data['range'] = [(0,Imax),[0,Imax]]
102    return np.asarray(sumImg,dtype='int32')
103
104def UpdateImageData(G2frame,data):
105   
106    def OnPixVal(invalid,value,tc):
107        G2plt.PlotExposedImage(G2frame,newPlot=True,event=tc.event)
108       
109    #G2frame.dataWindow.ClearData()
110    G2frame.ImageZ = GetImageZ(G2frame,data)
111    mainSizer = G2frame.dataWindow.GetSizer()
112    mainSizer.Add(wx.StaticText(G2frame.dataWindow,
113        label='Do not change anything here unless you are absolutely sure!'),0,WACV)
114    mainSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Image size: %d by %d'%(data['size'][0],data['size'][1])),0,WACV)
115    pixSize = wx.FlexGridSizer(0,4,5,5)
116    pixLabels = [u' Pixel X-dimension (\xb5m)',u' Pixel Y-dimension (\xb5m)']
117    for i,[pixLabel,pix] in enumerate(zip(pixLabels,data['pixelSize'])):
118        pixSize.Add(wx.StaticText(G2frame.dataWindow,label=pixLabel),0,WACV)
119        pixVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data['pixelSize'],i,nDig=(10,3),
120            typeHint=float,OnLeave=OnPixVal)
121        pixSize.Add(pixVal,0,WACV)
122    mainSizer.Add(pixSize,0)
123    distSizer = wx.BoxSizer(wx.HORIZONTAL)
124    distSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Set detector distance: '),0,WACV)
125    if 'setdist' not in data:
126        data['setdist'] = data['distance']
127    distSizer.Add(G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'setdist',nDig=(10,4),
128            typeHint=float),0,WACV)
129    distSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Polarization: '),0,WACV)
130    if 'PolaVal' not in data:       #patch
131        data['PolaVal'] = [0.99,False]
132    distSizer.Add(G2G.ValidatedTxtCtrl(G2frame.dataWindow,data['PolaVal'],0,nDig=(10,4),
133        min=0.,max=1.,typeHint=float),0,WACV)
134    mainSizer.Add(distSizer,0)
135
136################################################################################
137##### Image Controls
138################################################################################                   
139def UpdateImageControls(G2frame,data,masks,IntegrateOnly=False):
140    '''Shows and handles the controls on the "Image Controls"
141    data tree entry
142    '''
143    import ImageCalibrants as calFile
144#patch
145    if 'Flat Bkg' not in data:
146        data['Flat Bkg'] = 0.0
147    if 'GonioAngles' not in data:
148        data['GonioAngles'] = [0.,0.,0.]
149    if 'DetDepth' not in data:
150        data['DetDepth'] = 0.
151    if 'SampleAbs' not in data:
152        data['SampleShape'] = 'Cylinder'
153        data['SampleAbs'] = [0.0,False]
154    if 'binType' not in data:
155        if 'PWDR' in data['type']:
156            data['binType'] = '2-theta'
157        elif 'SASD' in data['type']:
158            data['binType'] = 'log(q)'
159    if 'varyList' not in data:
160        data['varyList'] = {'dist':True,'det-X':True,'det-Y':True,'tilt':True,'phi':True,'dep':False,'wave':False}
161    if data['DetDepth'] > 0.5:
162        data['DetDepth'] /= data['distance']
163#end patch
164
165# Menu items
166
167    def OnCalibrate(event):
168        G2frame.GetStatusBar().SetStatusText('Select > 4 points on 1st used ring; LB to pick (shift key to force pick), RB on point to delete else RB to finish',1)
169        G2frame.ifGetRing = True
170               
171    def OnRecalibrate(event):
172        G2img.ImageRecalibrate(G2frame,data,masks)
173        wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
174       
175    def OnRecalibAll(event):
176        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
177        dlg = G2G.G2MultiChoiceDialog(G2frame,'Image calibration controls','Select images to recalibrate:',Names)
178        try:
179            if dlg.ShowModal() == wx.ID_OK:
180                Id =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Sequential image calibration results')
181                if Id:
182                    SeqResult = G2frame.GPXtree.GetItemPyData(Id)
183                else:
184                    Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text='Sequential image calibration results')
185                SeqResult = {'SeqPseudoVars':{},'SeqParFitEqList':[]}
186                items = dlg.GetSelections()
187                G2frame.EnablePlot = False
188                for item in items:
189                    name = Names[item]
190                    print ('calibrating'+name)
191                    G2frame.Image = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
192                    Data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls'))
193                    G2frame.ImageZ = GetImageZ(G2frame,Data)
194                    Data['setRings'] = True
195                    Mid = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Masks')
196                    Masks = G2frame.GPXtree.GetItemPyData(Mid)
197                    vals,varyList,sigList,parmDict = G2img.ImageRecalibrate(G2frame,Data,Masks)
198                    sigList = list(sigList)
199                    if 'dist' not in varyList:
200                        vals.append(parmDict['dist'])
201                        varyList.append('dist')
202                        sigList.append(None)
203                    vals.append(Data.get('setdist',Data['distance']))
204                    varyList.append('setdist')
205                    sigList.append(None)
206                    SeqResult[name] = {'variables':vals,'varyList':varyList,'sig':sigList,'Rvals':[],
207                        'covMatrix':np.eye(len(varyList)),'title':name,'parmDict':parmDict}
208                SeqResult['histNames'] = Names               
209                G2frame.GPXtree.SetItemPyData(Id,SeqResult)
210        finally:
211            dlg.Destroy()
212        print ('All selected images recalibrated - results in Sequential image calibration results')
213        G2frame.G2plotNB.Delete('Sequential refinement')    #clear away probably invalid plot
214        G2plt.PlotExposedImage(G2frame,event=None)
215        G2frame.GPXtree.SelectItem(Id)
216       
217    def OnClearCalib(event):
218        data['ring'] = []
219        data['rings'] = []
220        data['ellipses'] = []
221        G2plt.PlotExposedImage(G2frame,event=event)
222           
223    def ResetThresholds():
224        Imin = max(0.,np.min(G2frame.ImageZ))
225        Imax = np.max(G2frame.ImageZ)
226        data['range'] = [(0,Imax),[Imin,Imax]]
227        masks['Thresholds'] = [(0,Imax),[Imin,Imax]]
228        MaxSizer.GetChildren()[2].Window.SetValue(Imax)   #tricky
229        MaxSizer.GetChildren()[5].Window.SetValue(Imin)   #tricky
230         
231    def OnIntegrate(event):
232        '''Integrate image in response to a menu event or from the AutoIntegrate
233        dialog. In the latter case, event=None.
234        '''
235        CleanupMasks(masks)
236        blkSize = 128   #this seems to be optimal; will break in polymask if >1024
237        sumImg = GetImageZ(G2frame,data)
238        wx.BeginBusyCursor()
239        try:
240            G2frame.Integrate = G2img.ImageIntegrate(sumImg,data,masks,blkSize)           
241        finally:
242            wx.EndBusyCursor()   
243        G2frame.PauseIntegration = G2frame.Integrate[-1]
244        del sumImg  #force cleanup
245        Id = G2IO.SaveIntegration(G2frame,G2frame.PickId,data,(event is None))
246        G2frame.PatternId = Id
247        G2frame.GPXtree.SelectItem(Id)
248        G2frame.GPXtree.Expand(Id)
249        for item in G2frame.MakePDF: item.Enable(True)
250       
251    def OnIntegrateAll(event):
252        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
253        dlg = G2G.G2MultiChoiceDialog(G2frame,'Image integration controls','Select images to integrate:',Names)
254        try:
255            if dlg.ShowModal() == wx.ID_OK:
256                items = dlg.GetSelections()
257                G2frame.EnablePlot = False
258                dlgp = wx.ProgressDialog("Elapsed time","2D image integrations",len(items)+1,
259                    style = wx.PD_ELAPSED_TIME|wx.PD_CAN_ABORT)
260                try:
261                    for icnt,item in enumerate(items):
262                        GoOn = dlgp.Update(icnt)
263                        if not GoOn[0]:
264                            break
265                        name = Names[item]
266                        G2frame.Image = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
267                        CId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')
268                        Data = G2frame.GPXtree.GetItemPyData(CId)
269                        Masks = G2frame.GPXtree.GetItemPyData(
270                            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Masks'))
271                        image = GetImageZ(G2frame,Data)
272                        blkSize = 128   #this seems to be optimal; will break in polymask if >1024
273                        G2frame.Integrate = G2img.ImageIntegrate(image,Data,Masks,blkSize)
274                        del image   #force cleanup
275                        pId = G2IO.SaveIntegration(G2frame,CId,Data)
276                finally:   
277                    dlgp.Destroy()
278                    G2frame.EnablePlot = True
279                    G2frame.GPXtree.SelectItem(pId)
280                    G2frame.GPXtree.Expand(pId)
281                    G2frame.PatternId = pId
282        finally:
283            dlg.Destroy()
284       
285    def OnCopyControls(event):
286        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
287        if len(Names) == 1:
288            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
289            return
290        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
291        Names.pop(Names.index(Source))
292# select targets & do copy
293        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy image controls','Copy controls from '+Source+' to:',Names)
294        try:
295            if dlg.ShowModal() == wx.ID_OK:
296                items = dlg.GetSelections()
297                G2frame.EnablePlot = False
298                for item in items:
299                    name = Names[item]
300                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
301                    CId = G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls')
302                    oldData = copy.deepcopy(G2frame.GPXtree.GetItemPyData(CId))
303                    Data = copy.deepcopy(data)
304                    Data['range'][0] = oldData['range'][0]
305                    Data['size'] = oldData['size']
306                    Data['GonioAngles'] = oldData.get('GonioAngles', [0.,0.,0.])
307                    Data['ring'] = []
308                    Data['rings'] = []
309                    Data['ellipses'] = []
310                    if name == Data['dark image'][0]:
311                        Data['dark image'] = ['',-1.]
312                    if name == Data['background image'][0]:
313                        Data['background image'] = ['',-1.]
314                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Image Controls'),Data)
315        finally:
316            dlg.Destroy()
317            G2frame.GPXtree.SelectItem(G2frame.PickId)
318           
319    def OnCopySelected(event):
320        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
321        if len(Names) == 1:
322            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
323            return
324        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
325        # Assemble a list of item labels
326        keyList = ['type','wavelength','calibrant','distance','center','Oblique',
327                    'tilt','rotation','azmthOff','fullIntegrate','LRazimuth','setdist',
328                    'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
329                    'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList',
330                    'binType','SampleShape','PolaVal','SampleAbs','dark image','background image']
331        keyList.sort(key=lambda s: s.lower())
332        keyText = [i+' = '+str(data[i]) for i in keyList]
333        # sort both lists together, ordered by keyText
334        selectedKeys = []
335        dlg = G2G.G2MultiChoiceDialog(G2frame,'Select which image controls\nto copy',
336            'Select image controls', keyText)
337        try:
338            if dlg.ShowModal() == wx.ID_OK:
339                selectedKeys = [keyList[i] for i in dlg.GetSelections()]
340        finally:
341            dlg.Destroy()
342        if not selectedKeys: return # nothing to copy
343        copyDict = {}
344        for parm in selectedKeys:
345            copyDict[parm] = data[parm]
346        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy image controls from\n'+Source+' to...',
347            'Copy image controls', Names)
348        try:
349            if dlg.ShowModal() == wx.ID_OK:
350                result = dlg.GetSelections()
351                for i in result: 
352                    item = Names[i]
353                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,item)
354                    Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
355                    Controls.update(copy.deepcopy(copyDict))
356        finally:
357            dlg.Destroy()           
358               
359    def OnSaveControls(event):
360        pth = G2G.GetExportPath(G2frame)
361        dlg = wx.FileDialog(G2frame, 'Choose image controls file', pth, '', 
362            'image control files (*.imctrl)|*.imctrl',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
363        try:
364            if dlg.ShowModal() == wx.ID_OK:
365                filename = dlg.GetPath()
366                # make sure extension is .imctrl
367                filename = os.path.splitext(filename)[0]+'.imctrl'
368                WriteControls(filename,data)
369        finally:
370            dlg.Destroy()
371
372    def WriteControls(filename,data):
373        File = open(filename,'w')
374        keys = ['type','wavelength','calibrant','distance','center','Oblique',
375            'tilt','rotation','azmthOff','fullIntegrate','LRazimuth','setdist',
376            'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
377            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList',
378            'binType','SampleShape','PolaVal','SampleAbs','dark image','background image',
379            ]
380        for key in keys:
381            if key not in data:     #uncalibrated!
382                continue
383            File.write(key+':'+str(data[key])+'\n')
384        File.close()
385       
386    def OnSaveMultiControls(event):
387        '''Save controls from multiple images
388        '''
389        imglist = []
390        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
391        while item:
392            name = G2frame.GPXtree.GetItemText(item)
393            if name.startswith('IMG '): 
394                imglist.append(name)               
395            item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
396        if not imglist:
397            print('No images!')
398            return
399        dlg = G2G.G2MultiChoiceDialog(G2frame, 'Which images to select?',
400            'Select images', imglist, wx.CHOICEDLG_STYLE)
401        try:
402            if dlg.ShowModal() == wx.ID_OK:
403                treeEntries = [imglist[i] for i in dlg.GetSelections()]
404        finally:
405            dlg.Destroy()
406        if not treeEntries:
407            print('No images selected!')
408            return
409        pth = G2G.GetExportPath(G2frame)
410        dlg = wx.DirDialog(
411            G2frame, 'Select directory for output files',pth,wx.DD_DEFAULT_STYLE)
412        dlg.CenterOnParent()
413        outdir = None
414        try:
415            if dlg.ShowModal() == wx.ID_OK:
416                outdir = dlg.GetPath()
417        finally:
418            dlg.Destroy()
419        if not outdir:
420            print('No directory')
421            return
422        for img in treeEntries:
423            item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
424            data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
425                G2frame,item,'Image Controls'))
426            Npix,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(item)
427            filename = os.path.join(outdir,
428                                    os.path.splitext(os.path.split(imagefile)[1])[0]
429                                    + '.imctrl')
430            print('writing '+filename)
431            WriteControls(filename,data)
432           
433    def OnLoadControls(event):
434        cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type','Oblique',
435            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
436            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList','setdist',
437            'PolaVal','SampleAbs','dark image','background image']
438        pth = G2G.GetImportPath(G2frame)
439        if not pth: pth = '.'
440        dlg = wx.FileDialog(G2frame, 'Choose image controls file', pth, '', 
441            'image control files (*.imctrl)|*.imctrl',wx.FD_OPEN)
442        try:
443            if dlg.ShowModal() == wx.ID_OK:
444                filename = dlg.GetPath()
445                File = open(filename,'r')
446                save = {}
447                S = File.readline()
448                while S:
449                    if S[0] == '#':
450                        S = File.readline()
451                        continue
452                    [key,val] = S.strip().split(':',1)
453                    if key in ['type','calibrant','binType','SampleShape',]:    #strings
454                        save[key] = val
455                    elif key in ['varyList',]:
456                        save[key] = eval(val)   #dictionary
457                    elif key in ['rotation']:
458                        save[key] = float(val)
459                    elif key in ['center',]:
460                        if ',' in val:
461                            save[key] = eval(val)
462                        else:
463                            vals = val.strip('[] ').split()
464                            save[key] = [float(vals[0]),float(vals[1])] 
465                    elif key in cntlList:
466                        save[key] = eval(val)
467                    S = File.readline()
468                data.update(save)
469                # next line removed. Previous updates tree contents. The next
470                # makes a copy of data, puts it into tree and "disconnects" data
471                # from tree contents (later changes to data are lost!)
472                #G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'),copy.deepcopy(data))
473                File.close()
474        finally:
475            dlg.Destroy()
476        G2frame.ImageZ = GetImageZ(G2frame,data)
477        ResetThresholds()
478        G2plt.PlotExposedImage(G2frame,event=event)
479        wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
480       
481    def OnTransferAngles(event):
482        '''Sets the integration range for the selected Images based on the difference in detector distance
483        '''
484        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
485        if len(Names) == 1:
486            G2frame.ErrorDialog('No images to transfer integration angles to','Need more "IMG"s')
487            return
488        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
489        Names.pop(Names.index(Source))
490        # select targets & do copy
491        extraopts = {"label_1":"Xfer scaled calib d-min", "value_1":False,
492                     "label_2":"Xfer scaled 2-theta min", "value_2":False,
493                     "label_3":"Xfer scaled 2-theta max", "value_3":True,
494                     }
495        dlg = G2G.G2MultiChoiceDialog(G2frame,'Xfer angles','Transfer integration range from '+Source+' to:',
496            Names,extraOpts=extraopts)
497        try:
498            if dlg.ShowModal() == wx.ID_OK:
499                for i in '_1','_2','_3':
500                    if extraopts['value'+i]: break
501                else:
502                    G2G.G2MessageBox(G2frame,'Nothing to do!')
503                    return
504                xferAng = lambda tth,dist1,dist2: atand(dist1 * tand(tth) / dist2)
505                items = dlg.GetSelections()
506                G2frame.EnablePlot = False
507                Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Source)
508                data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
509                ttmin0,ttmax0 = data['IOtth']
510                dist0 = data['distance']
511                wave0 = data['wavelength']
512                dsp0 = data['calibdmin']
513                print('distance = {:.2f} integration range: [{:.4f}, {:.4f}], calib dmin {:.3f}'
514                            .format(dist0,ttmin0,ttmax0,dsp0))
515                for item in items:
516                    name = Names[item]
517                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
518                    data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
519                    dist1 = data['distance']
520                    if extraopts["value_2"]:
521                        data['IOtth'][0] = xferAng(ttmin0,dist0,dist1)
522                    if extraopts["value_3"]:
523                        data['IOtth'][1] = xferAng(ttmax0,dist0,dist1)
524                    if extraopts["value_1"]:
525                        ang1 = xferAng(2.0*asind(wave0/(2.*dsp0)),dist0,dist1)
526                        data['calibdmin'] = data['wavelength']/(2.*sind(ang1/2.))
527                        print('distance = {:.2f} integration range: [{:.4f}, {:.4f}], calib dmin {:.3f}'
528                            .format(dist1,data['IOtth'][0],data['IOtth'][1],data['calibdmin']))
529                    else:
530                        print('distance = {:.2f} integration range: [{:.4f}, {:.4f}]'
531                            .format(dist1,data['IOtth'][0],data['IOtth'][1]))
532        finally:
533            dlg.Destroy()
534            G2frame.GPXtree.SelectItem(G2frame.PickId)       
535           
536    def OnResetDist(event):
537        dlg = wx.MessageDialog(G2frame,'Are you sure you want to do this?',caption='Reset dist to set dist',style=wx.YES_NO|wx.ICON_EXCLAMATION)
538        if dlg.ShowModal() != wx.ID_YES:
539            dlg.Destroy()
540            return
541        dlg.Destroy()
542        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
543        dlg = G2G.G2MultiChoiceDialog(G2frame,'Reset dist','Reset dist to set dist for:',Names)
544        try:
545            if dlg.ShowModal() == wx.ID_OK:
546                items = dlg.GetSelections()
547                for item in items:
548                    name = Names[item]
549                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
550                    Data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
551                    Data['distance'] = Data['setdist']
552        finally:
553            dlg.Destroy()
554        wx.CallAfter(UpdateImageControls,G2frame,data,masks)
555           
556# Sizers
557    Indx = {}                                   
558    def ComboSizer():
559
560        def OnDataType(event):
561            data['type'] = typeSel.GetValue()[:4]
562            if 'SASD' in data['type']:
563                data['SampleAbs'][0] = np.exp(-data['SampleAbs'][0]) #switch from muT to trans!
564                if data['binType'] == '2-theta': data['binType'] = 'log(q)'  #switch default bin type
565            elif 'PWDR' in data['type']:
566                data['SampleAbs'][0] = -np.log(data['SampleAbs'][0])  #switch from trans to muT!
567                if data['binType'] == 'log(q)': data['binType'] = '2-theta'  #switch default bin type                 
568            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
569   
570        def OnNewColorBar(event):
571            data['color'] = colSel.GetValue()
572            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
573       
574        def OnAzmthOff(invalid,value,tc):
575            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
576       
577        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
578        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Type of image data: '),0,WACV)
579        typeSel = wx.ComboBox(parent=G2frame.dataWindow,value=typeDict[data['type']],choices=typeList,
580            style=wx.CB_READONLY|wx.CB_DROPDOWN)
581        typeSel.SetValue(data['type'])
582        typeSel.Bind(wx.EVT_COMBOBOX, OnDataType)
583        comboSizer.Add(typeSel,0,WACV)
584        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Color bar '),0,WACV)
585        colSel = wx.ComboBox(parent=G2frame.dataWindow,value=data['color'],choices=colorList,
586            style=wx.CB_READONLY|wx.CB_DROPDOWN)
587        colSel.Bind(wx.EVT_COMBOBOX, OnNewColorBar)
588        comboSizer.Add(colSel,0,WACV)
589        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Azimuth offset '),0,WACV)
590        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'azmthOff',nDig=(10,2),
591            typeHint=float,OnLeave=OnAzmthOff)
592        comboSizer.Add(azmthOff,0,WACV)
593        return comboSizer
594       
595    def MaxSizer():
596        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
597        for the image, as well as callback routines.
598        '''
599        def OnNewVal(invalid,value,tc):
600            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
601            the value into the data['range'] nested list).
602            This adjusts the slider positions to match the current values
603            '''
604            scaleSel.SetSelection(len(scaleChoices)-1)
605            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
606            if r11 != Range[1][1]:
607                Range[1][1] = r11
608                maxVal.SetValue(int(Range[1][1]))
609            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
610            if r10 != Range[1][0]:
611                Range[1][0] = r10
612                minVal.SetValue(int(Range[1][0]))
613            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
614            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
615            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
616            maxSel.SetValue(sv1)
617            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
618            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
619            minSel.SetValue(sv0)
620            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
621            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
622            if mplOld:
623                Page.canvas.draw()
624            else:
625                Page.canvas.draw_idle()
626           
627        G2frame.prevMaxValue = None   
628        def OnMaxSlider(event):
629            val = maxSel.GetValue()
630            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
631            scaleSel.SetSelection(len(scaleChoices)-1)
632            G2frame.prevMaxValue = val
633            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
634            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
635            maxVal.SetValue(int(0.5+Range[1][1]))
636            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
637            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
638            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
639            minSel.SetValue(sv0)
640            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
641            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
642            if mplOld:
643                Page.canvas.draw()
644            else:
645                Page.canvas.draw_idle()
646           
647        G2frame.prevMinValue = None   
648        def OnMinSlider(event):
649            val = minSel.GetValue()
650            scaleSel.SetSelection(len(scaleChoices)-1)
651            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
652            G2frame.prevMinValue = val
653            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
654            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
655            minVal.SetValue(int(Range[1][0]))
656            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
657            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
658            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
659            maxSel.SetValue(sv1)
660            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
661            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
662            if mplOld:
663                Page.canvas.draw()
664            else:
665                Page.canvas.draw_idle()
666           
667        def OnAutoSet(event):
668            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
669            for the image so that 95% (etc.) of pixels are inside the color map limits.
670            An equal number of pixels are dropped at the minimum and maximum levels.
671            '''
672            try:
673                val = int(event.GetEventObject().GetStringSelection()[:-1])
674                margin = (100-val)/2.
675            except:
676                margin = 0
677                event.GetEventObject().SetSelection(0)
678            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
679            if margin == 0:
680                Range[1] = list(Range[0])
681            else:
682                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
683                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
684            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
685            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
686            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
687            maxSel.SetValue(sv1)
688            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
689            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
690            minSel.SetValue(sv0)
691            minVal.SetValue(int(Range[1][0]))
692            maxVal.SetValue(int(Range[1][1]))
693            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
694            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
695            if mplOld:
696                Page.canvas.draw()
697            else:
698                Page.canvas.draw_idle()
699
700        mplv = mpl.__version__.split('.')
701        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
702        # Plot color scaling uses limits as below:
703        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
704        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
705        maxSizer = wx.GridBagSizer(0,0)
706        r = c = 0
707        maxSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Max intensity'),(r,c))
708        c += 1
709        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
710        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
711        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
712        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
713        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
714        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
715        maxSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv1,size=(300,-1))
716        maxSizer.Add(maxSel,(r,c),flag=wx.EXPAND)
717        maxSizer.AddGrowableCol(c)
718        c += 1
719        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
720        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],1,min=Range[0][0]+1,
721            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
722        maxSizer.Add(maxVal,(r,c))
723        c += 1
724        scaleChoices = ("100%","99%","95%","90%","80%","?")
725        scaleSel = wx.Choice(G2frame.dataWindow,choices=scaleChoices,size=(-1,-1))
726        if (Range[1][0] == Range[0][0] and
727            Range[1][1] == Range[0][1]):
728            scaleSel.SetSelection(0)
729        else:
730            scaleSel.SetSelection(len(scaleChoices)-1)
731        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
732        maxSizer.Add(scaleSel,(r,c),(2,1),flag=wx.ALIGN_CENTER)
733        c = 0
734        r = 1
735        maxSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Min intensity'),(r,c))
736        c += 1
737        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
738        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
739        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
740        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
741        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
742        minSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv0,size=(300,-1))
743        maxSizer.Add(minSel,(r,c),flag=wx.EXPAND|wx.ALL)
744        c += 1
745        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
746        minVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],0,
747            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
748        maxSizer.Add(minVal,(r,c))
749        return maxSizer
750       
751    def CalibCoeffSizer():
752       
753        def OnCalRef(event):
754            Obj = event.GetEventObject()
755            name = Indx[Obj]
756            data['varyList'][name] = Obj.GetValue()
757           
758        calibSizer = wx.FlexGridSizer(0,2,5,5)
759        calibSizer.SetFlexibleDirection(wx.HORIZONTAL)
760        calibSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Calibration coefficients'),0,WACV)   
761        calibSizer.Add((5,0),0)
762        Names = ['det-X','det-Y','wave','dist','tilt','phi']
763        if 'PWDR' in data['type']:
764            Names.append('dep') 
765        Parms = {'dist':['Distance',(10,3),data,'distance'],'det-X':['Beam center X',(10,3),data['center'],0],
766            'det-Y':['Beam center Y',(10,3),data['center'],1],'tilt':['Tilt angle',(10,3),data,'tilt'],
767            'phi':['Tilt rotation',(10,2),data,'rotation'],'dep':['Penetration',(10,4),data,'DetDepth'],
768            'wave':['Wavelength',(10,6),data,'wavelength']}
769        for name in Names:
770            calSel = wx.CheckBox(parent=G2frame.dataWindow,label=Parms[name][0])
771            calibSizer.Add(calSel,0,WACV)
772            calSel.Bind(wx.EVT_CHECKBOX, OnCalRef)
773            calSel.SetValue(data['varyList'][name])
774            Indx[calSel] = name
775            if name == 'wave':
776                calVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Parms[name][2],
777                    Parms[name][3],min=0.01,max=10.,nDig=Parms[name][1],typeHint=float)
778            elif name == 'dep':
779                calVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Parms[name][2],
780                    Parms[name][3],min=0.0,max=0.2,nDig=Parms[name][1],typeHint=float)
781            else:
782                calVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Parms[name][2],
783                    Parms[name][3],nDig=Parms[name][1],typeHint=float)
784            calibSizer.Add(calVal,0,WACV)
785        return calibSizer
786   
787    def IntegrateSizer():
788       
789        def OnNewBinType(event):
790            data['binType'] = binSel.GetValue()
791            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
792       
793        def OnIOtth(invalid,value,tc):
794            Ltth = float(G2frame.InnerTth.GetValue())
795            Utth = float(G2frame.OuterTth.GetValue())
796            if Ltth > Utth:
797                Ltth,Utth = Utth,Ltth
798            if 'q' in data['binType'].lower():
799                data['IOtth'] = [2.*asind(Ltth*wave/(4.*math.pi)),2.*asind(Utth*wave/(4.*math.pi))]
800            else:
801                data['IOtth'] = [Ltth,Utth]
802            G2frame.InnerTth.SetValue(Ltth)
803            G2frame.OuterTth.SetValue(Utth)
804            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
805       
806        def OnLRazim(invalid,value,tc):
807            Lazm = float(G2frame.Lazim.GetValue())%360.
808            Razm = float(G2frame.Razim.GetValue())%360.
809            if Lazm > Razm:
810                Razm += 360.
811            if data['fullIntegrate']:
812                Razm = Lazm+360.
813            G2frame.Lazim.SetValue(Lazm)
814            G2frame.Razim.SetValue(Razm)
815            data['LRazimuth'] = [Lazm,Razm]
816            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
817               
818        def OnNumOutAzms(invalid,value,tc):
819            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
820       
821        def OnOblique(event):
822            data['Oblique'][1] = not data['Oblique'][1]
823               
824        def OnSampleShape(event):
825            data['SampleShape'] = samShape.GetValue()
826            if 'Cylind' in data['SampleShape']:
827                data['SampleAbs'][0] = 0.0
828            elif 'Fixed' in data['SampleShape']:
829                data['SampleAbs'][0] = 1.0
830            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
831                           
832        def OnSamAbs(event):
833            data['SampleAbs'][1] = not data['SampleAbs'][1]
834                           
835        def OnShowLines(event):
836            data['showLines'] = not data['showLines']
837            G2plt.PlotExposedImage(G2frame,event=event)
838           
839        def OnFullIntegrate(event):
840            Lazm = float(G2frame.Lazim.GetValue())
841            if data['fullIntegrate']:
842                data['fullIntegrate'] = False
843                data['LRazimuth'] = [Lazm,Lazm+20.]
844            else:
845                data['fullIntegrate'] = True
846                data['LRazimuth'] = [Lazm,Lazm+360.]
847            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
848            G2plt.PlotExposedImage(G2frame,event=event)
849           
850        def OnSetDefault(event):
851            if data['setDefault']:
852                G2frame.imageDefault = {}
853                data['setDefault'] = False
854            else:
855                G2frame.imageDefault = copy.copy(data)
856                data['setDefault'] = True
857               
858        def OnCenterAzm(event):
859            data['centerAzm'] = not data['centerAzm']
860            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
861               
862        def OnApplyPola(event):
863            data['PolaVal'][1] = not data['PolaVal'][1]
864               
865        dataSizer = wx.FlexGridSizer(0,2,5,3)
866        dataSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Integration coefficients'),0,WACV)   
867        dataSizer.Add((5,0),0)
868        if 'PWDR' in data['type']:
869            binChoice = ['2-theta','Q']
870        elif 'SASD' in data['type']:
871            binChoice = ['2-theta','Q','log(q)']
872        dataSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Bin style: Constant step bins in'),0,WACV)           
873        binSel = wx.ComboBox(parent=G2frame.dataWindow,value=data['binType'],choices=binChoice,
874            style=wx.CB_READONLY|wx.CB_DROPDOWN)
875        binSel.Bind(wx.EVT_COMBOBOX, OnNewBinType)
876        dataSizer.Add(binSel,0,WACV)
877        binType = '2-theta'
878        if 'q' in data['binType'].lower():
879            binType = 'Q'
880        dataSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Inner/Outer '+binType),0,WACV)           
881        IOtth = data['IOtth'][:]
882        if 'q' in data['binType'].lower():
883            wave = data['wavelength']
884            IOtth = [4.*math.pi*sind(IOtth[0]/2.)/wave,4.*math.pi*sind(IOtth[1]/2.)/wave]
885        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
886        G2frame.InnerTth = G2G.ValidatedTxtCtrl(G2frame.dataWindow,IOtth,0,nDig=(8,3,'f'),min=0.001,typeHint=float,OnLeave=OnIOtth)
887        littleSizer.Add(G2frame.InnerTth,0,WACV)
888        G2frame.OuterTth = G2G.ValidatedTxtCtrl(G2frame.dataWindow,IOtth,1,nDig=(8,3,'f'),min=0.001,typeHint=float,OnLeave=OnIOtth)
889        littleSizer.Add(G2frame.OuterTth,0,WACV)
890        dataSizer.Add(littleSizer,0,)
891        dataSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Start/End azimuth'),0,WACV)
892        LRazim = data['LRazimuth']
893        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
894        G2frame.Lazim = G2G.ValidatedTxtCtrl(G2frame.dataWindow,LRazim,0,nDig=(6,1,'f'),typeHint=float,OnLeave=OnLRazim)
895        littleSizer.Add(G2frame.Lazim,0,WACV)
896        G2frame.Razim = G2G.ValidatedTxtCtrl(G2frame.dataWindow,LRazim,1,nDig=(6,1,'f'),typeHint=float,OnLeave=OnLRazim)
897        if data['fullIntegrate']:
898            G2frame.Razim.Enable(False)
899            G2frame.Razim.SetBackgroundColour(VERY_LIGHT_GREY)
900            G2frame.Razim.SetValue(LRazim[0]+360.)
901        littleSizer.Add(G2frame.Razim,0,WACV)
902        dataSizer.Add(littleSizer,0,)
903        dataSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' No. 2-theta/azimuth bins'),0,WACV)
904        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
905        outChan = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'outChannels',typeHint=int,min=10)
906        littleSizer.Add(outChan,0,WACV)
907        outAzim = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'outAzimuths',nDig=(10,4),min=1,typeHint=int,OnLeave=OnNumOutAzms)
908        littleSizer.Add(outAzim,0,WACV)
909        dataSizer.Add(littleSizer,0,)
910        samplechoice = ['Cylinder','Fixed flat plate',]
911        dataSizer.Add(wx.StaticText(G2frame.dataWindow,label='Select sample shape'),0,WACV)
912        samShape = wx.ComboBox(G2frame.dataWindow,value=data['SampleShape'],choices=samplechoice,
913            style=wx.CB_READONLY|wx.CB_DROPDOWN)
914        samShape.Bind(wx.EVT_COMBOBOX,OnSampleShape)
915        dataSizer.Add(samShape,0,WACV)
916        #SampleShape - cylinder or flat plate choice?
917        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
918        samabs = wx.CheckBox(parent=G2frame.dataWindow,label='Apply sample absorption?')
919        dataSizer.Add(samabs,0,WACV)
920        samabs.Bind(wx.EVT_CHECKBOX, OnSamAbs)
921        samabs.SetValue(data['SampleAbs'][1])
922        minmax = [0.,2.]
923        if 'Cylind' in data['SampleShape']: #cylinder mu*R; flat plate transmission
924            littleSizer.Add(wx.StaticText(G2frame.dataWindow,label='mu*R (0.00-2.0) '),0,WACV)
925        elif 'Fixed' in data['SampleShape']:
926            littleSizer.Add(wx.StaticText(G2frame.dataWindow,label='transmission '),0,WACV) #for flat plate
927            minmax = [.05,1.0]
928        samabsVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data['SampleAbs'],0,nDig=(10,3),
929            typeHint=float,min=minmax[0],max=minmax[1])
930        littleSizer.Add(samabsVal,0,WACV)
931        dataSizer.Add(littleSizer,0,)       
932        if 'PWDR' in data['type']:
933            littleSizer = wx.BoxSizer(wx.HORIZONTAL)
934            oblique = wx.CheckBox(parent=G2frame.dataWindow,label='Apply detector absorption?')
935            dataSizer.Add(oblique,0,WACV)
936            oblique.Bind(wx.EVT_CHECKBOX, OnOblique)
937            oblique.SetValue(data['Oblique'][1])
938            littleSizer.Add(wx.StaticText(G2frame.dataWindow,label='Value (0.01-0.99)  '),0,WACV)
939            obliqVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data['Oblique'],0,nDig=(10,3),typeHint=float,min=0.01,max=0.99)
940            littleSizer.Add(obliqVal,0,WACV)
941            dataSizer.Add(littleSizer,0,)
942        if 'SASD' in data['type']:
943            littleSizer = wx.BoxSizer(wx.HORIZONTAL)
944            setPolariz = wx.CheckBox(parent=G2frame.dataWindow,label='Apply polarization?')
945            dataSizer.Add(setPolariz,0,WACV)
946            setPolariz.Bind(wx.EVT_CHECKBOX, OnApplyPola)
947            setPolariz.SetValue(data['PolaVal'][1])
948            littleSizer.Add(wx.StaticText(G2frame.dataWindow,label='Value (0.001-0.999)  '),0,WACV)
949            polaVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data['PolaVal'],0,nDig=(10,3),typeHint=float,min=0.001,max=0.999)
950            littleSizer.Add(polaVal,0,WACV)
951            dataSizer.Add(littleSizer,0,)
952       
953        showLines = wx.CheckBox(parent=G2frame.dataWindow,label='Show integration limits?')
954        dataSizer.Add(showLines,0,WACV)
955        showLines.Bind(wx.EVT_CHECKBOX, OnShowLines)
956        showLines.SetValue(data['showLines'])
957        fullIntegrate = wx.CheckBox(parent=G2frame.dataWindow,label='Do full integration?')
958        dataSizer.Add(fullIntegrate,0,WACV)
959        fullIntegrate.Bind(wx.EVT_CHECKBOX, OnFullIntegrate)
960        fullIntegrate.SetValue(data['fullIntegrate'])
961        setDefault = wx.CheckBox(parent=G2frame.dataWindow,label='Use for all new images?')
962        dataSizer.Add(setDefault,0,WACV)
963        setDefault.Bind(wx.EVT_CHECKBOX, OnSetDefault)
964        setDefault.SetValue(data['setDefault'])
965        centerAzm = wx.CheckBox(parent=G2frame.dataWindow,label='Azimuth at bin center?')
966        dataSizer.Add(centerAzm,0,WACV)
967        centerAzm.Bind(wx.EVT_CHECKBOX, OnCenterAzm)
968        centerAzm.SetValue(data['centerAzm'])
969        return dataSizer
970       
971    def BackSizer():
972       
973        global oldFlat
974        def OnBackImage(event):
975            data['background image'][0] = backImage.GetValue()
976            G2frame.ImageZ = GetImageZ(G2frame,data,newRange=True)
977            ResetThresholds()
978            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
979           
980        def OnDarkImage(event):
981            data['dark image'][0] = darkImage.GetValue()
982            G2frame.ImageZ = GetImageZ(G2frame,data,newRange=True)
983            ResetThresholds()
984            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
985           
986        def OnFlatBkg(invalid,value,tc):
987            global oldFlat
988            G2frame.ImageZ += int(oldFlat-data['Flat Bkg'])
989            oldFlat = data['Flat Bkg']
990            ResetThresholds()
991            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
992
993        def OnMult(invalid,value,tc):
994            G2frame.ImageZ = GetImageZ(G2frame,data,newRange=True)
995            ResetThresholds()
996            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
997       
998        backSizer = wx.FlexGridSizer(0,6,5,5)
999        oldFlat = data.get('Flat Bkg',0.)
1000
1001        backSizer.Add(wx.StaticText(G2frame.dataWindow,-1,' Dark image'),0,WACV)
1002        Choices = ['',]+G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1003        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
1004        Choices.pop(Choices.index(Source))
1005        darkImage = wx.ComboBox(parent=G2frame.dataWindow,value=data['dark image'][0],choices=Choices,
1006            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1007        darkImage.Bind(wx.EVT_COMBOBOX,OnDarkImage)
1008        backSizer.Add(darkImage)
1009        backSizer.Add(wx.StaticText(G2frame.dataWindow,-1,' multiplier'),0,WACV)
1010        darkMult = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data['dark image'],1,nDig=(10,3),
1011            typeHint=float,OnLeave=OnMult)
1012        backSizer.Add(darkMult,0,WACV)
1013        backSizer.Add(wx.StaticText(G2frame.dataWindow,-1,' Flat Bkg: '),0,WACV)
1014        flatbkg = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Flat Bkg',nDig=(10,0),
1015            typeHint=float,OnLeave=OnFlatBkg)
1016        backSizer.Add(flatbkg,0,WACV)
1017
1018        backSizer.Add(wx.StaticText(G2frame.dataWindow,-1,' Background image'),0,WACV)
1019        backImage = wx.ComboBox(parent=G2frame.dataWindow,value=data['background image'][0],choices=Choices,
1020            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1021        backImage.Bind(wx.EVT_COMBOBOX,OnBackImage)
1022        backSizer.Add(backImage)
1023        backSizer.Add(wx.StaticText(G2frame.dataWindow,-1,' multiplier'),0,WACV)
1024        backMult = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data['background image'],1,nDig=(10,3),
1025            typeHint=float,OnLeave=OnMult)
1026        backSizer.Add(backMult,0,WACV)
1027        return backSizer
1028                       
1029    def CalibSizer():
1030               
1031        def OnNewCalibrant(event):
1032            data['calibrant'] = calSel.GetValue().strip()
1033            if data['calibrant']:
1034                G2frame.dataWindow.ImageEdit.Enable(id=G2G.wxID_IMRECALIBRATE,enable=True)
1035                G2frame.dataWindow.ImageEdit.Enable(id=G2G.wxID_IMCALIBRATE,enable=True)
1036                G2frame.dataWindow.ImageEdit.Enable(id=G2G.wxID_IMRECALIBALL,enable=True)
1037                data['calibskip'] = calFile.Calibrants[data['calibrant']][3]
1038                limits = calFile.Calibrants[data['calibrant']][4]
1039                data['calibdmin'],data['pixLimit'],data['cutoff'] = limits
1040                pixLimit.SetValue(str(limits[1]))
1041                cutOff.SetValue(limits[2])
1042                calibSkip.SetValue(str(data['calibskip']))
1043                G2frame.calibDmin.SetValue(limits[0])
1044            else:
1045                G2frame.dataWindow.ImageEdit.Enable(id=G2G.wxID_IMRECALIBRATE,enable=False)
1046                G2frame.dataWindow.ImageEdit.Enable(id=G2G.wxID_IMCALIBRATE,enable=False)
1047                G2frame.dataWindow.ImageEdit.Enable(id=G2G.wxID_IMRECALIBALL,enable=False)
1048               
1049        def OnCalibSkip(event):
1050            data['calibskip'] = int(calibSkip.GetValue())
1051           
1052        def OnPixLimit(event):
1053            data['pixLimit'] = int(pixLimit.GetValue())
1054           
1055        def OnSetRings(event):
1056            data['setRings'] = not data['setRings']
1057            G2plt.PlotExposedImage(G2frame,event=event)
1058   
1059        calibSizer = wx.FlexGridSizer(0,3,5,5)
1060        comboSizer = wx.BoxSizer(wx.HORIZONTAL)   
1061        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Calibrant '),0,WACV)
1062        calSel = wx.ComboBox(parent=G2frame.dataWindow,value=data['calibrant'],choices=calList,
1063            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1064        calSel.Bind(wx.EVT_COMBOBOX, OnNewCalibrant)
1065        comboSizer.Add(calSel,0,WACV)
1066        calibSizer.Add(comboSizer,0)
1067       
1068        comboSizer = wx.BoxSizer(wx.HORIZONTAL)   
1069        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Calib lines to skip   '),0,WACV)
1070        calibSkip  = wx.ComboBox(parent=G2frame.dataWindow,value=str(data['calibskip']),choices=[str(i) for i in range(25)],
1071            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1072        calibSkip.Bind(wx.EVT_COMBOBOX, OnCalibSkip)
1073        comboSizer.Add(calibSkip,0,WACV)
1074        calibSizer.Add(comboSizer,0)
1075       
1076        comboSizer = wx.BoxSizer(wx.HORIZONTAL)       
1077        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Min calib d-spacing '),0,WACV)
1078        G2frame.calibDmin = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'calibdmin',nDig=(10,2),typeHint=float,min=0.25)
1079        comboSizer.Add(G2frame.calibDmin,0,WACV)
1080        calibSizer.Add(comboSizer,0)
1081       
1082        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1083        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Min ring I/Ib '),0,WACV)
1084        cutOff = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'cutoff',nDig=(10,2),min=0.1)
1085        comboSizer.Add(cutOff,0,WACV)
1086        calibSizer.Add(comboSizer,0)
1087       
1088        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1089        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Pixel search range '),0,WACV)
1090        pixLimit = wx.ComboBox(parent=G2frame.dataWindow,value=str(data['pixLimit']),choices=['1','2','5','10','15','20'],
1091            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1092        pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
1093        comboSizer.Add(pixLimit,0,WACV)
1094        calibSizer.Add(comboSizer,0)
1095       
1096        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1097        setRings = wx.CheckBox(parent=G2frame.dataWindow,label='Show ring picks?')
1098        comboSizer.Add(setRings,0)
1099        setRings.Bind(wx.EVT_CHECKBOX, OnSetRings)
1100        setRings.SetValue(data['setRings'])
1101        calibSizer.Add(comboSizer,0)
1102        return calibSizer
1103       
1104    def GonioSizer():
1105       
1106        def OnGlobalEdit(event):
1107            Names = []
1108            Items = []
1109            if G2frame.GPXtree.GetCount():
1110                id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
1111                while id:
1112                    name = G2frame.GPXtree.GetItemText(id)
1113                    if 'IMG' in name:
1114                        ctrls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,id,'Image Controls'))
1115                        Names.append(name)
1116                        Items.append(ctrls['GonioAngles'])
1117                    id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
1118                if len(Names) == 1:
1119                    G2frame.ErrorDialog('Nothing for global editing','There must be more than one "IMG" pattern')
1120                    return
1121                dlg = G2G.G2HistoDataDialog(G2frame,' Edit sample goniometer data:',
1122                    'Edit data',['Omega','Chi','Phi'],['%.2f','%.2f','%.2f'],Names,Items)
1123                try:
1124                    if dlg.ShowModal() == wx.ID_OK:
1125                        id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
1126                        while id:
1127                            name = G2frame.GPXtree.GetItemText(id)
1128                            if 'IMG' in name:
1129                                ctrls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,id,'Image Controls'))
1130                                vals = Items[Names.index(name)]
1131                                ctrls['GonioAngles'] = vals
1132                            id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
1133                finally:
1134                    dlg.Destroy()
1135                    G2frame.GPXtree.SelectItem(G2frame.PickId)
1136       
1137        gonioSizer = wx.BoxSizer(wx.HORIZONTAL)
1138        names = ['Omega','Chi','Phi']
1139        gonioSizer.Add(wx.StaticText(G2frame.dataWindow,-1,'Sample goniometer angles: '),0,WACV)
1140        for i,name in enumerate(names):
1141            gonioSizer.Add(wx.StaticText(G2frame.dataWindow,-1,name),0,WACV)
1142            angle = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data['GonioAngles'],i,nDig=(8,2),typeHint=float)
1143            gonioSizer.Add(angle,0,WACV)
1144        globEdit = wx.Button(G2frame.dataWindow,-1,'Global edit')
1145        globEdit.Bind(wx.EVT_BUTTON,OnGlobalEdit)
1146        gonioSizer.Add(globEdit,0,WACV)
1147        return gonioSizer
1148       
1149# Image Controls main code             
1150                           
1151    #fix for old files:
1152    if 'azmthOff' not in data:
1153        data['azmthOff'] = 0.0
1154    if 'background image' not in data:
1155        data['background image'] = ['',-1.0]
1156    if 'dark image' not in data:
1157        data['dark image'] = ['',-1.0]
1158    if 'centerAzm' not in data:
1159        data['centerAzm'] = False
1160    if 'Oblique' not in data:
1161        data['Oblique'] = [0.5,False]
1162    if 'PolaVal' not in data:
1163        data['PolaVal'] = [0.99,False]
1164    #end fix
1165   
1166    if IntegrateOnly:
1167        OnIntegrate(None)
1168        return
1169   
1170    colorList = sorted([m for m in mpl.cm.datad.keys() ],key=lambda s: s.lower())   #if not m.endswith("_r")
1171    calList = sorted([m for m in calFile.Calibrants.keys()],key=lambda s: s.lower())
1172    typeList = ['PWDR - powder diffraction data','SASD - small angle scattering data',]
1173    if not data.get('type'):                        #patch for old project files
1174        data['type'] = 'PWDR'
1175    typeDict = {'PWDR':typeList[0],'SASD':typeList[1],}
1176    G2frame.dataWindow.ClearData()
1177    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.ImageMenu)
1178    G2frame.Bind(wx.EVT_MENU, OnCalibrate, id=G2G.wxID_IMCALIBRATE)
1179    G2frame.Bind(wx.EVT_MENU, OnRecalibrate, id=G2G.wxID_IMRECALIBRATE)
1180    G2frame.Bind(wx.EVT_MENU, OnRecalibAll, id=G2G.wxID_IMRECALIBALL)
1181    G2frame.Bind(wx.EVT_MENU, OnClearCalib, id=G2G.wxID_IMCLEARCALIB)
1182#    if data.get('calibrant'):
1183#        mode = True
1184#    else:
1185#        mode = False
1186#    G2frame.Enable(id=G2G.wxID_IMRECALIBRATE,enable=mode)
1187#    G2frame.Enable(id=G2G.wxID_IMCALIBRATE,enable=mode)
1188#    G2frame.Enable(id=G2G.wxID_IMRECALIBALL,enable=mode)
1189    G2frame.Bind(wx.EVT_MENU, OnIntegrate, id=G2G.wxID_IMINTEGRATE)
1190    G2frame.Bind(wx.EVT_MENU, OnIntegrateAll, id=G2G.wxID_INTEGRATEALL)
1191    G2frame.Bind(wx.EVT_MENU, OnCopyControls, id=G2G.wxID_IMCOPYCONTROLS)
1192    G2frame.Bind(wx.EVT_MENU, OnCopySelected, id=G2G.wxID_IMCOPYSELECTED)
1193    G2frame.Bind(wx.EVT_MENU, OnSaveControls, id=G2G.wxID_IMSAVECONTROLS)
1194    G2frame.Bind(wx.EVT_MENU, OnSaveMultiControls, id=G2G.wxID_SAVESELECTEDCONTROLS)
1195    G2frame.Bind(wx.EVT_MENU, OnLoadControls, id=G2G.wxID_IMLOADCONTROLS)
1196    G2frame.Bind(wx.EVT_MENU, OnTransferAngles, id=G2G.wxID_IMXFERCONTROLS)
1197    G2frame.Bind(wx.EVT_MENU, OnResetDist, id=G2G.wxID_IMRESETDIST)
1198    def OnDestroy(event):
1199        G2frame.autoIntFrame = None
1200    def OnAutoInt(event):
1201        if G2frame.autoIntFrame: # ensure only one open at a time
1202            G2frame.autoIntFrame.Raise()
1203            return
1204        PollTime = GSASIIpath.GetConfigValue('Autoint_PollTime',30.)
1205        G2frame.autoIntFrame = AutoIntFrame(G2frame,PollTime=PollTime)
1206        G2frame.autoIntFrame.Bind(wx.EVT_WINDOW_DESTROY,OnDestroy) # clean up name on window close
1207    G2frame.Bind(wx.EVT_MENU, OnAutoInt, id=G2G.wxID_IMAUTOINTEG)
1208
1209    mainSizer = G2frame.dataWindow.GetSizer()
1210    mainSizer.Add((5,10),0)   
1211    mainSizer.Add(ComboSizer(),0,wx.ALIGN_LEFT)
1212    mainSizer.Add((5,5),0)
1213    Range = data['range'] # allows code to be same in Masks
1214    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1215    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1216   
1217    mainSizer.Add((5,5),0)
1218    DataSizer = wx.FlexGridSizer(0,2,5,0)
1219    DataSizer.Add(CalibCoeffSizer(),0)
1220    DataSizer.Add(IntegrateSizer(),0)       
1221    mainSizer.Add(DataSizer,0)
1222    mainSizer.Add((5,5),0)           
1223    mainSizer.Add(BackSizer(),0)
1224    mainSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Calibration controls:'),0,WACV)
1225    mainSizer.Add((5,5),0)
1226    mainSizer.Add(CalibSizer(),0,WACV)
1227    mainSizer.Add((5,5),0)
1228    mainSizer.Add(GonioSizer(),0,WACV)   
1229    G2frame.dataWindow.SetDataSize()
1230   
1231################################################################################
1232##### Masks
1233################################################################################
1234def CleanupMasks(data):
1235    '''If a mask creation is not completed, an empty mask entry is created in the
1236    masks array. This cleans them out. It is called when the masks page is first loaded
1237    and before saving them or after reading them in. This should also probably be done
1238    before they are used for integration.
1239    '''
1240    for key in ['Points','Rings','Arcs','Polygons']:
1241        data[key] = data.get(key,[])
1242        l1 = len(data[key])
1243        data[key] = [i for i in data[key] if len(i)]
1244        l2 = len(data[key])
1245        if GSASIIpath.GetConfigValue('debug') and l1 != l2:
1246            print ('Mask Cleanup: %s was %d entries, now %d'%(key,l1,l2))
1247   
1248def UpdateMasks(G2frame,data):
1249    '''Shows and handles the controls on the "Masks" data tree entry
1250    '''
1251   
1252    def OnTextMsg(event):
1253        Obj = event.GetEventObject()
1254        Obj.SetToolTipString('Drag this mask on 2D Powder Image with mouse to change ')
1255
1256    def Replot(*args,**kwargs):
1257        wx.CallAfter(G2plt.PlotExposedImage,G2frame)
1258
1259    def newReplot(*args,**kwargs):
1260        wx.CallAfter(G2plt.PlotExposedImage,G2frame,newPlot=True)
1261
1262    def onDeleteMask(event):
1263        Obj = event.GetEventObject()
1264        typ = Obj.locationcode.split('+')[1]
1265        num = int(Obj.locationcode.split('+')[2])
1266        del(data[typ][num])
1267        wx.CallAfter(UpdateMasks,G2frame,data)
1268        G2plt.PlotExposedImage(G2frame,event=event)
1269       
1270    def OnSpotChange(event):
1271        r,c = event.GetRow(),event.GetCol()
1272        if c == 2:
1273            del Spots[r]
1274            SpotTable.DeleteRow(r)
1275        else:
1276            Spots[r][2] = float(SpotGrid.GetCellValue(r,c))
1277        SpotGrid.ForceRefresh()
1278        wx.CallAfter(UpdateMasks,G2frame,data)
1279        G2plt.PlotExposedImage(G2frame,event=event)
1280        event.Skip()
1281
1282    def onDeleteFrame(event):
1283        data['Frames'] = []
1284        wx.CallAfter(UpdateMasks,G2frame,data)
1285        G2plt.PlotExposedImage(G2frame,event=event)
1286
1287    def OnCopyMask(event):
1288        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1289        if len(Names) == 1:
1290            G2frame.ErrorDialog('Nothing to copy masks to','There must be more than one "IMG" pattern')
1291            return
1292        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
1293        Names.pop(Names.index(Source))
1294        Data = copy.deepcopy(data)
1295        Thresh = Data.pop('Thresholds')     # & remove it as well
1296        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy mask data','Copy masks from '+Source+' to:',Names)
1297        try:
1298            if dlg.ShowModal() == wx.ID_OK:
1299                items = dlg.GetSelections()
1300                for item in items:
1301                    name = Names[item]
1302                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1303                    MId = G2gd.GetGPXtreeItemId(G2frame,Id,'Masks')
1304                    Mask = G2frame.GPXtree.GetItemPyData(MId)
1305                    Mask.update(copy.deepcopy(Data))
1306                    Mask['Thresholds'][1][0] = Thresh[1][0]  #copy only lower threshold
1307                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Masks'),Mask)
1308        finally:
1309            dlg.Destroy()
1310
1311    def OnSaveMask(event):
1312        CleanupMasks(data)
1313        pth = G2G.GetExportPath(G2frame)
1314        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1315            'image mask files (*.immask)|*.immask',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1316        try:
1317            if dlg.ShowModal() == wx.ID_OK:
1318                filename = dlg.GetPath()
1319                filename = os.path.splitext(filename)[0]+'.immask'
1320                File = open(filename,'w')
1321                keys = ['Points','Rings','Arcs','Polygons','Frames','Thresholds']
1322                for key in keys:
1323                    File.write(key+':'+str(data[key])+'\n')
1324                File.close()
1325        finally:
1326            dlg.Destroy()
1327       
1328    def OnLoadMask(event):
1329        if event.Id == G2G.wxID_MASKLOADNOT:
1330            ignoreThreshold = True
1331        else:
1332            ignoreThreshold = False
1333        pth = G2G.GetImportPath(G2frame)
1334        if not pth: pth = '.'
1335        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1336            'image mask files (*.immask)|*.immask',wx.FD_OPEN)
1337        try:
1338            if dlg.ShowModal() == wx.ID_OK:
1339                filename = dlg.GetPath()
1340                File = open(filename,'r')
1341                save = {}
1342                oldThreshold = data['Thresholds'][0]
1343                S = File.readline()
1344                while S:
1345                    if S[0] == '#':
1346                        S = File.readline()
1347                        continue
1348                    [key,val] = S.strip().split(':',1)
1349                    if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
1350                        if ignoreThreshold and key == 'Thresholds':
1351                            S = File.readline() 
1352                            continue
1353                        save[key] = eval(val)
1354                        if key == 'Thresholds':
1355                            save[key][0] = oldThreshold
1356                            save[key][1][1] = min(oldThreshold[1],save[key][1][1])
1357                    S = File.readline()
1358                File.close()
1359                data.update(save)
1360                CleanupMasks(data)
1361                wx.CallAfter(UpdateMasks,G2frame,data)
1362                G2plt.PlotExposedImage(G2frame,event=event)               
1363        finally:
1364            dlg.Destroy()
1365           
1366    def OnAutoSpotMask(event):
1367        'Do auto search for spot masks'
1368        if wx.MessageDialog(G2frame.dataWindow,'NB: This will clear any old spot masks',
1369            'Auto Spot Masks', wx.OK|wx.CANCEL).ShowModal() == wx.ID_OK:
1370            Controls = copy.deepcopy(G2frame.GPXtree.GetItemPyData( 
1371                G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')))
1372            Error = G2img.AutoSpotMasks(G2frame.ImageZ,data,Controls)
1373            if not Error is None:
1374                G2frame.ErrorDialog('Auto spot search error',Error)
1375            wx.CallAfter(UpdateMasks,G2frame,data)
1376            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
1377
1378    def OnDeleteSpotMask(event):
1379        data['Points'] = []
1380        wx.CallAfter(UpdateMasks,G2frame,data)
1381        wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)         
1382           
1383    def ToggleSpotMaskMode(event):
1384        G2plt.ToggleMultiSpotMask(G2frame)
1385       
1386    def OnNewArcMask(event):
1387        'Start a new arc mask'
1388        G2frame.MaskKey = 'a'
1389        G2plt.OnStartMask(G2frame)
1390       
1391    def OnNewRingMask(event):
1392        'Start a new ring mask'
1393        G2frame.MaskKey = 'r'
1394        G2plt.OnStartMask(G2frame)
1395       
1396    def OnNewPolyMask(event):
1397        'Start a new polygon mask'
1398        G2frame.MaskKey = 'p'
1399        G2plt.OnStartMask(G2frame)
1400       
1401    def OnNewFrameMask(event):
1402        'Start a new Frame mask'
1403        G2frame.MaskKey = 'f'
1404        G2plt.OnStartMask(G2frame)
1405       
1406    def MaxSizer():
1407        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
1408        for the image, as well as callback routines.
1409        '''
1410        def OnNewVal(invalid,value,tc):
1411            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
1412            the value into the data['range'] nested list).
1413            This adjusts the slider positions to match the current values
1414            '''
1415            scaleSel.SetSelection(len(scaleChoices)-1)
1416            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
1417            if r11 != Range[1][1]:
1418                Range[1][1] = r11
1419                maxVal.SetValue(int(Range[1][1]))
1420            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
1421            if r10 != Range[1][0]:
1422                Range[1][0] = r10
1423                minVal.SetValue(int(Range[1][0]))
1424            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1425            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1426            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1427            maxSel.SetValue(sv1)
1428            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1429            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1430            minSel.SetValue(sv0)
1431            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1432            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1433            if mplOld:
1434                Page.canvas.draw()
1435            else:
1436                Page.canvas.draw_idle()
1437           
1438        G2frame.prevMaxValue = None   
1439        def OnMaxSlider(event):
1440            val = maxSel.GetValue()
1441            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
1442            scaleSel.SetSelection(len(scaleChoices)-1)
1443            G2frame.prevMaxValue = val
1444            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1445            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
1446            maxVal.SetValue(int(0.5+Range[1][1]))
1447            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1448            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
1449            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1450            minSel.SetValue(sv0)
1451            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1452            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1453            if mplOld:
1454                Page.canvas.draw()
1455            else:
1456                Page.canvas.draw_idle()
1457           
1458        G2frame.prevMinValue = None   
1459        def OnMinSlider(event):
1460            val = minSel.GetValue()
1461            scaleSel.SetSelection(len(scaleChoices)-1)
1462            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
1463            G2frame.prevMinValue = val
1464            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1465            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
1466            minVal.SetValue(int(Range[1][0]))
1467            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1468            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1469            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1470            maxSel.SetValue(sv1)
1471            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1472            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1473            if mplOld:
1474                Page.canvas.draw()
1475            else:
1476                Page.canvas.draw_idle()
1477           
1478        def OnAutoSet(event):
1479            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
1480            for the image so that 95% (etc.) of pixels are inside the color map limits.
1481            An equal number of pixels are dropped at the minimum and maximum levels.
1482            '''
1483            try:
1484                val = int(event.GetEventObject().GetStringSelection()[:-1])
1485                margin = (100-val)/2.
1486            except:
1487                margin = 0
1488                event.GetEventObject().SetSelection(0)
1489            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1490            if margin == 0:
1491                Range[1] = list(Range[0])
1492            else:
1493                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
1494                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
1495            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1496            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1497            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1498            maxSel.SetValue(sv1)
1499            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1500            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1501            minSel.SetValue(sv0)
1502            minVal.SetValue(int(Range[1][0]))
1503            maxVal.SetValue(int(Range[1][1]))
1504            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1505            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1506            if mplOld:
1507                Page.canvas.draw()
1508            else:
1509                Page.canvas.draw_idle()
1510
1511        mplv = mpl.__version__.split('.')
1512        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
1513        # Plot color scaling uses limits as below:
1514        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
1515        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
1516        maxSizer = wx.GridBagSizer(0,0)
1517        r = c = 0
1518        maxSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Max intensity'),(r,c))
1519        c += 1
1520        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
1521        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
1522        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
1523        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1524        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1525        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1526        maxSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv1,size=(300,-1))
1527        maxSizer.Add(maxSel,(r,c),flag=wx.EXPAND)
1528        maxSizer.AddGrowableCol(c)
1529        c += 1
1530        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
1531        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],1,min=Range[0][0]+1,
1532            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1533        maxSizer.Add(maxVal,(r,c))
1534        c += 1
1535        scaleChoices = ("100%","99%","95%","90%","80%","?")
1536        scaleSel = wx.Choice(G2frame.dataWindow,choices=scaleChoices,size=(-1,-1))
1537        if (Range[1][0] == Range[0][0] and
1538            Range[1][1] == Range[0][1]):
1539            scaleSel.SetSelection(0)
1540        else:
1541            scaleSel.SetSelection(len(scaleChoices)-1)
1542        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
1543        maxSizer.Add(scaleSel,(r,c),(2,1),flag=wx.ALIGN_CENTER)
1544        c = 0
1545        r = 1
1546        maxSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Min intensity'),(r,c))
1547        c += 1
1548        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
1549        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
1550        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
1551        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1552        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1553        minSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv0,size=(300,-1))
1554        maxSizer.Add(minSel,(r,c),flag=wx.EXPAND|wx.ALL)
1555        c += 1
1556        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
1557        minVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],0,
1558            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1559        maxSizer.Add(minVal,(r,c))
1560        return maxSizer
1561
1562    G2frame.dataWindow.ClearData()
1563    startScroll = None
1564    if G2frame.dataWindow:
1565        startScroll = G2frame.dataWindow.GetScrollPos(wx.VERTICAL) # save scroll position
1566    else:
1567        CleanupMasks(data) # posting page for 1st time; clean out anything unfinished
1568    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.MaskMenu)
1569    G2frame.Bind(wx.EVT_MENU, OnCopyMask, id=G2G.wxID_MASKCOPY)
1570    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOAD)
1571    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOADNOT)
1572    G2frame.Bind(wx.EVT_MENU, OnSaveMask, id=G2G.wxID_MASKSAVE)
1573    G2frame.Bind(wx.EVT_MENU, OnAutoSpotMask, id=G2G.wxID_FINDSPOTS)
1574    G2frame.Bind(wx.EVT_MENU, OnDeleteSpotMask, id=G2G.wxID_DELETESPOTS)
1575    G2frame.Bind(wx.EVT_MENU, ToggleSpotMaskMode, id=G2G.wxID_NEWMASKSPOT)
1576    G2frame.Bind(wx.EVT_MENU, OnNewArcMask, id=G2G.wxID_NEWMASKARC)
1577    G2frame.Bind(wx.EVT_MENU, OnNewRingMask, id=G2G.wxID_NEWMASKRING)
1578    G2frame.Bind(wx.EVT_MENU, OnNewPolyMask, id=G2G.wxID_NEWMASKPOLY)
1579    G2frame.Bind(wx.EVT_MENU, OnNewFrameMask, id=G2G.wxID_NEWMASKFRAME)
1580    if G2frame.MaskKey == 'f':
1581        G2frame.GetStatusBar().SetStatusText('Frame mask active - LB pick next point, RB close polygon',1)
1582    elif G2frame.MaskKey == 'p':
1583        G2frame.GetStatusBar().SetStatusText('Polygon mask active - LB pick next point, RB close polygon',1)
1584    elif G2frame.MaskKey == 'a':
1585        G2frame.GetStatusBar().SetStatusText('Arc mask active - LB pick arc location',1)
1586    elif G2frame.MaskKey == 'r':
1587        G2frame.GetStatusBar().SetStatusText('Ring mask active - LB pick ring location',1)
1588    else:
1589        G2frame.GetStatusBar().SetStatusText("To add mask: press a,r,s,p or f on 2D image for arc/ring/spot/polygon/frame",1)
1590    mainSizer = G2frame.dataWindow.GetSizer()
1591    mainSizer.Add((5,10),0)
1592
1593    thresh = data['Thresholds']         #min/max intensity range
1594    Spots = data['Points']               #x,y,radius in mm
1595    Rings = data['Rings']               #radius, thickness
1596    Polygons = data['Polygons']         #3+ x,y pairs
1597    if 'Frames' not in data:
1598        data['Frames'] = []
1599    frame = data['Frames']             #3+ x,y pairs
1600    Arcs = data['Arcs']                 #radius, start/end azimuth, thickness
1601
1602    ######################################################################
1603    CId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')
1604    controlData = G2frame.GPXtree.GetItemPyData(CId)
1605    Range = controlData['range']
1606    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1607    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1608
1609    littleSizer = wx.FlexGridSizer(0,3,0,5)
1610    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper limits '),0,WACV)
1611    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][0]),style=wx.TE_READONLY)
1612    littleSizer.Add(Text,0,WACV)
1613    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1614    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][1]),style=wx.TE_READONLY)
1615    littleSizer.Add(Text,0,WACV)
1616    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1617    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper thresholds '),0,WACV)
1618    lowerThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=0,
1619        min=thresh[0][0],OnLeave=newReplot,typeHint=int)
1620    littleSizer.Add(lowerThreshold,0,WACV)
1621    upperThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=1,
1622        max=thresh[0][1],OnLeave=newReplot,typeHint=int)
1623    littleSizer.Add(upperThreshold,0,WACV)
1624    mainSizer.Add(littleSizer,0,)
1625    if len(Spots):
1626        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Spot masks')
1627        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1628        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1629        colTypes = [wg.GRID_VALUE_STRING,wg.GRID_VALUE_FLOAT+':10,2',wg.GRID_VALUE_BOOL]
1630        colIds = ['position, mm','diameter, mm','Delete?']
1631        rowIds = [str(i) for i in range(len(Spots))]
1632        table = [['%.2f,%.2f'%(item[0],item[1]),item[2],False] for item in Spots]
1633        SpotTable = G2G.Table(table,rowLabels=rowIds,colLabels=colIds,types=colTypes)
1634        SpotGrid = G2G.GSGrid(G2frame.dataWindow)
1635        SpotGrid.SetTable(SpotTable,True)
1636        SpotGrid.AutoSizeColumns(True)
1637        SpotGrid.SetColSize(1,80)
1638        for r in range(len(Spots)):
1639            SpotGrid.SetCellStyle(r,0,VERY_LIGHT_GREY,True)
1640        if 'phoenix' in wx.version():
1641            SpotGrid.Bind(wg.EVT_GRID_CELL_CHANGED, OnSpotChange)
1642        else:
1643            SpotGrid.Bind(wg.EVT_GRID_CELL_CHANGE, OnSpotChange)
1644        mainSizer.Add(SpotGrid,0,)
1645    if Rings:
1646        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Ring masks')
1647        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1648        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1649        littleSizer = wx.FlexGridSizer(0,3,0,5)
1650        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' 2-theta,deg'),0,WACV)
1651        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
1652        littleSizer.Add((5,0),0)
1653        for i in range(len(Rings)):
1654            if Rings[i]:
1655                ringText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (Rings[i][0])),
1656                    style=wx.TE_READONLY)
1657                ringText.SetBackgroundColour(VERY_LIGHT_GREY)
1658                ringText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1659                littleSizer.Add(ringText,0,WACV)
1660                ringThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Rings[i],key=1,
1661                    min=0.001,max=1.,OnLeave=Replot,nDig=[8,3])
1662                littleSizer.Add(ringThick,0,WACV)
1663                ringDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1664                    locationcode='Delete+Rings+'+str(i),handler=onDeleteMask)
1665                littleSizer.Add(ringDelete,0,WACV)
1666        mainSizer.Add(littleSizer,0,)
1667    if Arcs:
1668        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Arc masks')
1669        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1670        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1671        littleSizer = wx.FlexGridSizer(0,4,0,5)
1672        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' 2-theta,deg'),0,WACV)
1673        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' azimuth, deg'),0,WACV)
1674        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
1675        littleSizer.Add((5,0),0)
1676        for i in range(len(Arcs)):
1677            if Arcs[i]:
1678                tth,azimuth,thick = Arcs[i]
1679                arcText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (tth)),
1680                    style=wx.TE_READONLY)
1681                arcText.SetBackgroundColour(VERY_LIGHT_GREY)
1682                arcText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1683                littleSizer.Add(arcText,0,WACV)
1684                azmText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%d,%d" % (azimuth[0],azimuth[1])),
1685                    style=wx.TE_READONLY)
1686                azmText.SetBackgroundColour(VERY_LIGHT_GREY)
1687                azmText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1688                littleSizer.Add(azmText,0,WACV)
1689                arcThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Arcs[i],key=2,
1690                    min=0.001,max=20.,OnLeave=Replot,nDig=[8,3])
1691                littleSizer.Add(arcThick,0,WACV)
1692                arcDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1693                    locationcode='Delete+Arcs+'+str(i),handler=onDeleteMask)
1694                littleSizer.Add(arcDelete,0,WACV)
1695        mainSizer.Add(littleSizer,0,)
1696    if Polygons:
1697        lbl = wx.StaticText(parent=G2frame.dataWindow,
1698            label=' Polygon masks (on plot RB vertex drag to move, LB vertex drag to insert)')
1699        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1700        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1701        littleSizer = wx.FlexGridSizer(0,2,0,5)
1702        for i in range(len(Polygons)):
1703            if Polygons[i]:
1704                polyList = []
1705                for x,y in Polygons[i]:
1706                    polyList.append("%.2f, %.2f"%(x,y))
1707                polyText = wx.ComboBox(G2frame.dataWindow,value=polyList[0],choices=polyList,style=wx.CB_READONLY)
1708                littleSizer.Add(polyText,0,WACV)
1709                polyDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1710                    locationcode='Delete+Polygons+'+str(i),handler=onDeleteMask)
1711                littleSizer.Add(polyDelete,0,WACV)
1712        mainSizer.Add(littleSizer,0,)
1713    if frame:
1714        lbl = wx.StaticText(parent=G2frame.dataWindow,
1715            label=' Frame mask (on plot RB vertex drag to move, LB vertex drag to insert)')
1716        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1717        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1718        littleSizer = wx.FlexGridSizer(0,2,0,5)
1719        frameList = []
1720        for x,y in frame:
1721            frameList.append("%.2f, %.2f"%(x,y))
1722        frameText = wx.ComboBox(G2frame.dataWindow,value=frameList[0],choices=frameList,style=wx.CB_READONLY)
1723        littleSizer.Add(frameText,0,WACV)
1724        frameDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1725            locationcode='Delete+Frame',handler=onDeleteFrame)
1726        littleSizer.Add(frameDelete,0,WACV)
1727        mainSizer.Add(littleSizer,0,)
1728    G2frame.dataWindow.SetDataSize()
1729    if startScroll: # reset scroll to saved position
1730        G2frame.dataWindow.Scroll(0,startScroll) # set to saved scroll position
1731        wx.Yield()
1732
1733################################################################################
1734##### Stress/Strain
1735################################################################################
1736
1737def UpdateStressStrain(G2frame,data):
1738    '''Shows and handles the controls on the "Stress/Strain"
1739    data tree entry
1740    '''
1741   
1742    def OnAppendDzero(event):
1743        data['d-zero'].append({'Dset':1.0,'Dcalc':0.0,'pixLimit':10,'cutoff':1.0,
1744            'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0],'fixDset':False})
1745        UpdateStressStrain(G2frame,data)
1746       
1747    def OnUpdateDzero(event):
1748        for item in data['d-zero']:
1749            if item['Dcalc']:   #skip unrefined ones
1750                item['Dset'] = item['Dcalc']
1751        UpdateStressStrain(G2frame,data)
1752           
1753    def OnCopyStrSta(event):
1754        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1755        if len(Names) == 1:
1756            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
1757            return
1758        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
1759        Names.pop(Names.index(Source))
1760        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy stress/strain controls','Copy controls from '+Source+' to:',Names)
1761        try:
1762            if dlg.ShowModal() == wx.ID_OK:
1763                items = dlg.GetSelections()
1764                for item in items:
1765                    name = Names[item]
1766                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1767                    CId = G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain')
1768                    oldData = G2frame.GPXtree.GetItemPyData(CId)
1769                    load = oldData.get('Sample load',0.0)
1770                    Data = copy.deepcopy(data)
1771                    Data['Sample load'] = load
1772                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'),Data)
1773        finally:
1774            dlg.Destroy()
1775            G2frame.GPXtree.SelectItem(G2frame.PickId)
1776
1777    def OnLoadStrSta(event):
1778        pth = G2G.GetImportPath(G2frame)
1779        if not pth: pth = '.'
1780        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1781            'image control files (*.strsta)|*.strsta',wx.FD_OPEN)
1782        try:
1783            if dlg.ShowModal() == wx.ID_OK:
1784                filename = dlg.GetPath()
1785                File = open(filename,'r')
1786                S = File.read()
1787                data = eval(S)
1788                Controls = G2frame.GPXtree.GetItemPyData(
1789                    G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1790                G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1791                UpdateStressStrain(G2frame,data)
1792                G2plt.PlotExposedImage(G2frame,event=event)
1793                G2plt.PlotStrain(G2frame,data,newPlot=True)
1794                File.close()
1795        finally:
1796            dlg.Destroy()
1797
1798    def OnSaveStrSta(event):
1799        pth = G2G.GetExportPath(G2frame)
1800        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1801            'image control files (*.strsta)|*.strsta',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1802        try:
1803            if dlg.ShowModal() == wx.ID_OK:
1804                filename = dlg.GetPath()
1805                File = open(filename,'w')
1806                keys = ['Type','Sample phi','Sample z','Sample load']
1807                keys2 = ['Dset','Dcalc','pixLimit','cutoff','Emat','fixDset']
1808                File.write('{\n\t')
1809                for key in keys:
1810                    if key in 'Type':
1811                        File.write("'"+key+"':'"+data[key]+"',")
1812                    else:
1813                        File.write("'"+key+"':"+str(data[key])+',')
1814                File.write('\n\t'+"'d-zero':[\n")
1815                for data2 in data['d-zero']:
1816                    File.write('\t\t{')
1817                    for key in keys2:
1818                        File.write("'"+key+"':"+str(data2[key])+',')
1819                    File.write("'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]]},\n")
1820                File.write('\t]\n}')
1821                File.close()
1822        finally:
1823            dlg.Destroy()
1824           
1825    def OnStrStaSample(event):
1826        filename = ''
1827        pth = G2G.GetImportPath(G2frame)
1828        if not pth: pth = '.'
1829        dlg = wx.FileDialog(G2frame, 'Choose multihistogram metadata text file', pth, '', 
1830            'metadata file (*.*)|*.*',wx.FD_OPEN)
1831        try:
1832            if dlg.ShowModal() == wx.ID_OK:
1833                filename = dlg.GetPath()
1834                File = open(filename,'r')
1835                S = File.readline()
1836                newItems = []
1837                itemNames = []
1838                Comments = []
1839                while S:
1840                    if S[0] == '#':
1841                        Comments.append(S)
1842                        S = File.readline()
1843                        continue
1844                    S = S.replace(',',' ').replace('\t',' ')
1845                    Stuff = S[:-1].split()
1846                    itemNames.append(Stuff[0])
1847                    newItems.append(Stuff[1:])
1848                    S = File.readline()               
1849                File.close()
1850        finally:
1851            dlg.Destroy()
1852        if not filename:
1853            G2frame.ErrorDialog('Nothing to do','No file selected')
1854            return
1855        dataDict = dict(zip(itemNames,newItems))
1856        ifany = False
1857        Names = [' ','Sample phi','Sample z','Sample load']
1858        dlg = G2G.G2ColumnIDDialog( G2frame,' Choose multihistogram metadata columns:',
1859            'Select columns',Comments,Names,np.array(newItems).T)
1860        try:
1861            if dlg.ShowModal() == wx.ID_OK:
1862                colNames,newData = dlg.GetSelection()
1863                dataDict = dict(zip(itemNames,newData.T))
1864                for item in colNames:
1865                    if item != ' ':
1866                        ifany = True
1867        finally:
1868            dlg.Destroy()
1869        if not ifany:
1870            G2frame.ErrorDialog('Nothing to do','No columns identified')
1871            return
1872        histList = []
1873        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)       
1874        while item:
1875            name = G2frame.GPXtree.GetItemText(item)
1876            if name.startswith('IMG'):
1877                histList.append(name)
1878            item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
1879        colIds = {}
1880        for i,name in enumerate(colNames):
1881            if name != ' ':
1882                colIds[name] = i
1883        for hist in histList:
1884            name = hist.split()[1]  #this is file name
1885            if name in dataDict:
1886                newItems = {}
1887                for item in colIds:
1888                    newItems[item] = float(dataDict[name][colIds[item]])
1889                Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,hist)
1890                stsrData = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'))
1891                stsrData.update(newItems)       
1892        UpdateStressStrain(G2frame,data)       
1893   
1894    def OnPlotStrSta(event):
1895        Controls = G2frame.GPXtree.GetItemPyData(
1896            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1897        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1898        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1899        G2plt.PlotExposedImage(G2frame,event=event)
1900        G2frame.G2plotNB.Delete('Ring Intensities')
1901        G2plt.PlotXY(G2frame,RingInt,labelX='Azimuth',
1902            labelY='MRD',newPlot=True,Title='Ring Intensities',
1903            names=Names,lines=True)
1904       
1905    def OnSaveStrRing(event):
1906        Controls = G2frame.GPXtree.GetItemPyData(
1907            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1908        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1909        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1910        pth = G2G.GetExportPath(G2frame)
1911        dlg = wx.FileDialog(G2frame, 'Choose strain ring intensity file', pth, '', 
1912            'ring intensity file (*.txt)|*.txt',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1913        try:
1914            if dlg.ShowModal() == wx.ID_OK:
1915                filename = dlg.GetPath()
1916                File = open(filename,'w')
1917                for i,name in enumerate(Names):
1918                    File.write('%s%s\n'%(' Ring intensity for ',name))
1919                    File.write('%12s %12s\n'%('Azimuth','RMD'))
1920                    for item in RingInt[i].T:
1921                        File.write(' %12.3f %12.3f\n'%(item[0],item[1]))
1922                    File.write('\n')
1923                File.close()
1924        finally:
1925            dlg.Destroy()
1926               
1927               
1928    def OnFitStrSta(event):
1929        Controls = G2frame.GPXtree.GetItemPyData(
1930            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1931        G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1932        print ('Strain fitting finished')
1933        UpdateStressStrain(G2frame,data)
1934        G2plt.PlotExposedImage(G2frame,event=event)
1935        G2plt.PlotStrain(G2frame,data,newPlot=True)
1936       
1937    def OnFitAllStrSta(event):
1938        choices = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1939        od = {'label_1':'Copy to next','value_1':False,'label_2':'Reverse order','value_2':False}
1940        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices,extraOpts=od)
1941        names = []
1942        if dlg.ShowModal() == wx.ID_OK:
1943            for sel in dlg.GetSelections():
1944                names.append(choices[sel])
1945            Id =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Sequential strain fit results')
1946            if Id:
1947                SeqResult = G2frame.GPXtree.GetItemPyData(Id)
1948            else:
1949                SeqResult = {}
1950                Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text='Sequential strain fit results')
1951            SeqResult.update({'SeqPseudoVars':{},'SeqParFitEqList':[]})
1952        else:
1953            dlg.Destroy()
1954            return
1955        dlg.Destroy()
1956        if not names:
1957            return
1958        dlg = wx.ProgressDialog('Sequential IMG Strain fit','Data set name = '+names[0],len(names), 
1959            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT)         
1960        wx.BeginBusyCursor()
1961        goodnames = []
1962        if od['value_2']:
1963            names.reverse()
1964        try:
1965            varyList = []
1966            variables = []
1967            for i,name in enumerate(names):
1968                print (' Sequential strain fit for '+name)
1969                GoOn = dlg.Update(i,newmsg='Data set name = '+name)[0]
1970                if not GoOn:
1971                    break
1972                sId =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1973                G2frame.Image = sId
1974                Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Image Controls'))
1975                StaCtrls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Stress/Strain'))
1976                if not len(StaCtrls['d-zero']):
1977                    continue
1978                goodnames.append(name)
1979                Npix,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(sId)
1980                image = GetImageZ(G2frame,Controls)
1981                sig = []
1982                if i and od['value_1']:
1983                    for j,ring in enumerate(StaCtrls['d-zero']):
1984                        ring['Emat'] = copy.copy(variables[4*j:4*j+3])
1985                varyList = []
1986                variables = []
1987                #get results from previous & put in StaCtrls
1988                G2img.FitStrSta(image,StaCtrls,Controls)
1989                G2plt.PlotStrain(G2frame,StaCtrls,newPlot=True)
1990                parmDict = {'Sample load':StaCtrls['Sample load'],}
1991                varyNames = ['e11','e12','e22']
1992                Nvar = 5*len(StaCtrls['d-zero'])
1993                coVar = np.zeros((Nvar,Nvar))
1994                for j,item in enumerate(StaCtrls['d-zero']):
1995                    variables += item['Emat']
1996                    sig += item['Esig']
1997                    varylist = ['%d;%s'%(j,Name) for Name in varyNames]
1998                    varyList += varylist
1999                    parmDict.update(dict(zip(varylist,item['Emat'])))
2000                    parmDict['%d;Dcalc'%(j)] = item['Dcalc']
2001                    variables.append(1.e6*(item['Dcalc']/item['Dset']-1.))
2002                    varyList.append('%d;h-mstrain'%(j))
2003                    sig.append(0)
2004                    parmDict['%d;Ivar'%(j)] = item['Ivar']
2005                    variables.append(item['Ivar'])
2006                    varyList.append('%d;Ivar'%(j))
2007                    sig.append(0)
2008                    j4 = j*5
2009                    coVar[j4:j4+3,j4:j4+3] = item['covMat']
2010                SeqResult[name] = {'variables':variables,'varyList':varyList,'sig':sig,'Rvals':[],
2011                    'covMatrix':coVar,'title':name,'parmDict':parmDict}
2012            else:
2013                SeqResult['histNames'] = goodnames
2014                dlg.Destroy()
2015                print (' ***** Sequential strain refinement successful *****')
2016        finally:
2017            wx.EndBusyCursor()   
2018        SeqResult['histNames'] = choices
2019        G2frame.GPXtree.SetItemPyData(Id,SeqResult)
2020        G2frame.GPXtree.SelectItem(Id)
2021        print ('All images fitted')
2022       
2023    def SamSizer():
2024       
2025        def OnStrainType(event):
2026            data['Type'] = strType.GetValue()
2027       
2028        samSizer = wx.FlexGridSizer(0,4,0,5)
2029        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Strain type: '),0,WACV)
2030        strType = wx.ComboBox(G2frame.dataWindow,value=data['Type'],choices=['True','Conventional'],
2031            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2032        strType.SetValue(data['Type'])
2033        strType.Bind(wx.EVT_COMBOBOX, OnStrainType)
2034        samSizer.Add(strType,0,WACV)
2035        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample phi: '),0,WACV)
2036        samPhi = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample phi',nDig=(10,3),typeHint=float,min=-360.,max=360.)
2037        samSizer.Add(samPhi,0,WACV)
2038        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample delta-z(mm): '),0,WACV)
2039        samZ = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample z',nDig=(10,3),typeHint=float)
2040        samSizer.Add(samZ,0,WACV)
2041        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample load(MPa): '),0,WACV)
2042        samLoad = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample load',
2043                nDig=[8,3],typeHint=float,)
2044        samSizer.Add(samLoad,0,WACV)
2045
2046        return samSizer
2047       
2048    def DzeroSizer():
2049               
2050        def OnStrainChange(event):
2051#            print (event)
2052            r,c = event.GetRow(),event.GetCol()
2053            if c == 0:
2054                data['d-zero'][r]['Dset'] = min(max(float(StrainGrid.GetCellValue(r,c)),0.25),20.)
2055            elif c == 1:
2056                data['d-zero'][r]['fixDset'] = bool(StrainGrid.GetCellValue(r,c))
2057            elif c == 3:
2058                data['d-zero'][r]['cutoff'] = min(max(float(StrainGrid.GetCellValue(r,c)),0.5),20.)
2059            elif c == 4:
2060                data['d-zero'][r]['pixLimit'] = int(StrainGrid.GetCellValue(r,c))
2061            elif c == 8:
2062                del data['d-zero'][r]
2063                StrainTable.DeleteRow(r)
2064                wx.CallAfter(UpdateStressStrain,G2frame,data)
2065            G2plt.PlotExposedImage(G2frame,event=event)
2066            G2plt.PlotStrain(G2frame,data,newPlot=True)
2067           
2068        def OnSetCol(event):
2069            c = event.GetCol()
2070            if c == 1:
2071                StrainGrid.ClearSelection()
2072                StrainGrid.SelectCol(c,True)
2073                choice = ['Y - set all','N - set none',]
2074                dlg = wx.SingleChoiceDialog(G2frame,'Select option for '+StrainGrid.GetColLabelValue(c-1),
2075                    'Strain controls',choice)
2076                dlg.CenterOnParent()
2077                if dlg.ShowModal() == wx.ID_OK:
2078                    sel = dlg.GetSelection()
2079                    if sel == 0:
2080                        for row in range(StrainGrid.GetNumberRows()): data['d-zero'][row]['fixDset']=True
2081                    else:
2082                        for row in range(StrainGrid.GetNumberRows()): data['d-zero'][row]['fixDset']=False
2083                wx.CallAfter(UpdateStressStrain,G2frame,data)
2084           
2085        colTypes = [wg.GRID_VALUE_FLOAT+':10,5',wg.GRID_VALUE_BOOL,wg.GRID_VALUE_FLOAT+':10,5',wg.GRID_VALUE_FLOAT+':10,1',
2086            wg.GRID_VALUE_CHOICE+':1,2,5,10,15,20',]+3*[wg.GRID_VALUE_FLOAT+':10,2',]+[wg.GRID_VALUE_BOOL,]+2*[wg.GRID_VALUE_FLOAT+':10,2',]
2087        colIds = ['d-zero','Poisson\n mean?','d-zero ave','I/Ib','nPix','e11','e12','e22','Delete?','h-mustrain','Ivar']
2088        rowIds = [str(i) for i in range(len(data['d-zero']))]
2089        table = [[item['Dset'],item.get('fixDset',False),item['Dcalc'],item['cutoff'],item['pixLimit'], 
2090            item['Emat'][0],item['Emat'][1],item['Emat'][2],False,1.e6*(item['Dcalc']/item['Dset']-1.),item['Ivar']] for item in data['d-zero']]
2091        StrainTable = G2G.Table(table,rowLabels=rowIds,colLabels=colIds,types=colTypes)
2092        StrainGrid = G2G.GSGrid(G2frame.dataWindow)
2093        StrainGrid.SetTable(StrainTable,True)
2094        StrainGrid.AutoSizeColumns(True)
2095        for r in range(len(data['d-zero'])):
2096            StrainGrid.SetCellStyle(r,2,VERY_LIGHT_GREY,True)
2097            StrainGrid.SetCellStyle(r,5,VERY_LIGHT_GREY,True)
2098            StrainGrid.SetCellStyle(r,6,VERY_LIGHT_GREY,True)
2099            StrainGrid.SetCellStyle(r,7,VERY_LIGHT_GREY,True)
2100            StrainGrid.SetCellStyle(r,9,VERY_LIGHT_GREY,True)
2101            StrainGrid.SetCellStyle(r,10,VERY_LIGHT_GREY,True)
2102        if 'phoenix' in wx.version():
2103            StrainGrid.Bind(wg.EVT_GRID_CELL_CHANGED, OnStrainChange)
2104        else:
2105            StrainGrid.Bind(wg.EVT_GRID_CELL_CHANGE, OnStrainChange)
2106        StrainGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnSetCol)
2107        return StrainGrid
2108# patches
2109    if 'Sample load' not in data:
2110        data['Sample load'] = 0.0
2111# end patches
2112   
2113    G2frame.dataWindow.ClearData()
2114    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.StrStaMenu)
2115    G2frame.Bind(wx.EVT_MENU, OnAppendDzero, id=G2G.wxID_APPENDDZERO)
2116    G2frame.Bind(wx.EVT_MENU, OnUpdateDzero, id=G2G.wxID_UPDATEDZERO)
2117    G2frame.Bind(wx.EVT_MENU, OnFitStrSta, id=G2G.wxID_STRSTAFIT)
2118    G2frame.Bind(wx.EVT_MENU, OnPlotStrSta, id=G2G.wxID_STRSTAPLOT) 
2119    G2frame.Bind(wx.EVT_MENU, OnSaveStrRing, id=G2G.wxID_STRRINGSAVE) 
2120    G2frame.Bind(wx.EVT_MENU, OnFitAllStrSta, id=G2G.wxID_STRSTAALLFIT)
2121    G2frame.Bind(wx.EVT_MENU, OnCopyStrSta, id=G2G.wxID_STRSTACOPY)
2122    G2frame.Bind(wx.EVT_MENU, OnLoadStrSta, id=G2G.wxID_STRSTALOAD)
2123    G2frame.Bind(wx.EVT_MENU, OnSaveStrSta, id=G2G.wxID_STRSTASAVE)
2124    G2frame.Bind(wx.EVT_MENU, OnStrStaSample, id=G2G.wxID_STRSTSAMPLE)       
2125    if G2frame.StrainKey == 'a':    #probably doesn't happen
2126        G2frame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value',1)
2127    else:
2128        G2frame.GetStatusBar().SetStatusText("To add strain data: On 2D Powder Image, key a:add ring",1)
2129       
2130    mainSizer = G2frame.dataWindow.GetSizer()
2131    mainSizer.Add((5,10),0)
2132    mainSizer.Add(SamSizer())
2133    mainSizer.Add((5,10),0)
2134    mainSizer.Add(DzeroSizer())
2135    G2frame.dataWindow.SetDataSize()
2136   
2137###########################################################################
2138# Autointegration
2139###########################################################################
2140def ReadMask(filename):
2141    'Read a mask (.immask) file'
2142    File = open(filename,'r')
2143    save = {}
2144    S = File.readline()
2145    while S:
2146        if S[0] == '#':
2147            S = File.readline()
2148            continue
2149        [key,val] = S.strip().split(':',1)
2150        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
2151            save[key] = eval(val)
2152        S = File.readline()
2153    File.close()
2154    CleanupMasks(save)
2155    return save
2156
2157def ReadControls(filename):
2158    'read an image controls (.imctrl) file'
2159    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2160            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2161            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2162            'PolaVal','SampleAbs','dark image','background image']
2163    File = open(filename,'r')
2164    save = {}
2165    S = File.readline()
2166    while S:
2167        if S[0] == '#':
2168            S = File.readline()
2169            continue
2170        [key,val] = S.strip().split(':',1)
2171        if key in ['type','calibrant','binType','SampleShape',]:    #strings
2172            save[key] = val
2173        elif key in ['rotation']:
2174            save[key] = float(val)
2175        elif key in ['center',]:
2176            if ',' in val:
2177                save[key] = eval(val)
2178            else:
2179                vals = val.strip('[] ').split()
2180                save[key] = [float(vals[0]),float(vals[1])] 
2181        elif key in cntlList:
2182            save[key] = eval(val)
2183        S = File.readline()
2184    File.close()
2185    return save
2186
2187def Read_imctrl(imctrl_file):
2188    '''Read an image control file and record control parms into a dict, with some simple
2189    type conversions
2190    '''
2191    save = {'filename':imctrl_file}
2192    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
2193    if os.path.exists(immask_file):
2194        save['maskfile'] = immask_file
2195    else:
2196        save['maskfile'] = '(none)'
2197    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2198                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2199                        'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2200                        'PolaVal','SampleAbs','dark image','background image','setdist']
2201    File = open(imctrl_file,'r')
2202    fullIntegrate = False
2203    try:
2204        S = File.readline()
2205        while S:
2206            if S[0] == '#':
2207                S = File.readline()
2208                continue
2209            [key,val] = S.strip().split(':',1)
2210            if val.find(':') != -1:
2211                #print 'rejecting ',key,val
2212                S = File.readline()
2213                continue
2214            if key in ['type','calibrant','binType','SampleShape',]:    #strings
2215                save[key] = val
2216            elif key == 'rotation':
2217                save[key] = float(val)
2218            elif key == 'fullIntegrate':
2219                fullIntegrate = eval(val)
2220            elif key == 'LRazimuth':
2221                vals = eval(val)
2222                save['LRazimuth_min'] = float(vals[0])
2223                save['LRazimuth_max'] = float(vals[1])
2224            elif key == 'IOtth':
2225                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
2226            elif key == 'center':
2227                if ',' in val:
2228                    vals = eval(val)
2229                else:
2230                    vals = val.strip('[] ').split()
2231                    vals = [float(vals[0]),float(vals[1])] 
2232                save['center_x'],save['center_y'] = vals[0:2]
2233            elif key in cntlList:
2234                save[key] = eval(val)
2235            S = File.readline()
2236    finally:
2237        File.close()
2238        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0.,360.
2239    return save
2240   
2241class AutoIntFrame(wx.Frame):
2242    '''Creates a wx.Frame window for the Image AutoIntegration.
2243    The intent is that this will be used as a non-modal dialog window.
2244   
2245    Implements a Start button that morphs into a pause and resume button.
2246    This button starts a processing loop that is repeated every
2247    :meth:`PollTime` seconds.
2248
2249    :param wx.Frame G2frame: main GSAS-II frame
2250    :param float PollTime: frequency in seconds to repeat calling the
2251      processing loop. (Default is 30.0 seconds.)
2252    '''
2253    def __init__(self,G2frame,PollTime=30.0):
2254        def OnStart(event):
2255            '''Called when the start button is pressed. Changes button label
2256            to Pause. When Pause is pressed the label changes to Resume.
2257            When either Start or Resume is pressed, the processing loop
2258            is started. When Pause is pressed, the loop is stopped.
2259            '''
2260            # check inputs for errors before starting
2261            #err = ''
2262            #if not any([self.params[fmt] for fmt in self.fmtlist]):
2263            #    err += '\nPlease select at least one output format\n'
2264            #if err:
2265            #    G2G.G2MessageBox(self,err)
2266            #    return
2267            self.Pause = False
2268            # change button label
2269            if self.btnstart.GetLabel() != 'Pause':
2270                self.btnstart.SetLabel('Pause')
2271                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
2272                if self.timer.IsRunning(): self.timer.Stop()
2273                self.PreventReEntryTimer = False
2274                if self.StartLoop():
2275                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
2276                    return
2277                self.OnTimerLoop(None) # run once immediately
2278                if not self.Pause:
2279                    # no pause, so start timer to check for new files
2280                    self.timer.Start(int(1000*PollTime),oneShot=False)
2281                    return
2282            # we will get to this point if Paused
2283            self.OnPause()
2284           
2285        def OnReset(event):
2286            '''Called when Reset button is pressed. This stops the
2287            processing loop and resets the list of integrated files so
2288            all images can be reintegrated.
2289            '''
2290            self.btnstart.SetLabel('Restart')
2291            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
2292            if self.timer.IsRunning(): self.timer.Stop()
2293            self.Reset = True
2294            self.Pause = True
2295           
2296        def OnQuit(event):
2297            '''Stop the processing loop and close the Frame
2298            '''
2299            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
2300            wx.CallAfter(self.Destroy)
2301           
2302        def OnBrowse(event):
2303            '''Responds when the Browse button is pressed to load a file.
2304            The routine determines which button was pressed and gets the
2305            appropriate file type and loads it into the appropriate place
2306            in the dict.
2307            '''
2308            if btn3 == event.GetEventObject():
2309                dlg = wx.DirDialog(
2310                    self, 'Select directory for output files',
2311                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
2312                dlg.CenterOnParent()
2313                try:
2314                    if dlg.ShowModal() == wx.ID_OK:
2315                        self.params['outdir'] = dlg.GetPath()
2316                        fInp3.SetValue(self.params['outdir'])
2317                finally:
2318                    dlg.Destroy()
2319                return
2320            elif btn4 == event.GetEventObject():
2321                msg = ''
2322                pth = G2G.GetExportPath(G2frame)
2323                dlg = wx.FileDialog(
2324                    self, 'Select a PDF parameter file',
2325                    pth, self.params['pdfprm'], 
2326                    "PDF controls file (*.pdfprm)|*.pdfprm",
2327                    wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
2328                dlg.CenterOnParent()
2329                try:
2330                    if dlg.ShowModal() == wx.ID_OK:
2331                        self.params['pdfprm'] = dlg.GetPath()
2332                        fInp4.SetValue(self.params['pdfprm'])
2333                        scanPDFprm()
2334                        msg = self.checkPDFprm(True)
2335                finally:
2336                    dlg.Destroy()
2337                if 'Error' in msg:
2338                    print(msg)
2339                    lbl = 'PDFPRM error'
2340                else:
2341                    msg = 'Information from file {}\n\n{}'.format(self.params['pdfprm'],msg)
2342                    lbl = 'PDFPRM information'
2343                G2G.G2MessageBox(self,msg,lbl)
2344                return
2345               
2346        def OnRadioSelect(event):
2347            '''Respond to a radiobutton selection and when in table
2348            mode, get distance-dependent parameters from user.
2349            '''
2350            self.Evaluator = None
2351            if self.useTable.GetValue():
2352                dlg = None
2353                try:
2354                    dlg = IntegParmTable(self) # create the dialog
2355                    dlg.CenterOnParent()
2356                    if dlg.ShowModal() == wx.ID_OK:
2357                        self.ImgTblParms = dlg.parms
2358                        self.IMfileList = dlg.IMfileList
2359                        self.Evaluator = DefineEvaluator(dlg)
2360                        self.params['Mode'] = 'table'
2361                        self.editTable.Enable(True)
2362                    else:
2363                        self.useActive.SetValue(True)
2364                finally:
2365                    if dlg: dlg.Destroy()
2366            elif self.useActive.GetValue():
2367                self.params['Mode'] = 'active'
2368                self.imageBase = G2frame.Image
2369                self.useActive.SetLabel("Active Image: "+
2370                        G2frame.GPXtree.GetItemText(self.imageBase))
2371                self.editTable.Enable(False)
2372            else:
2373                print('unexpected mode in OnRadioSelect')
2374
2375        def OnEditTable(event):
2376            '''Called to edit the distance-dependent parameter look-up table.
2377            Should be called only when table is defined and active.
2378            '''
2379            dlg = None
2380            try:
2381                dlg = IntegParmTable(self,self.ImgTblParms,self.IMfileList)
2382                dlg.CenterOnParent()
2383                if dlg.ShowModal() == wx.ID_OK:
2384                    self.ImgTblParms = dlg.parms
2385                    self.IMfileList = dlg.IMfileList
2386                    self.Evaluator = DefineEvaluator(dlg)
2387                    self.params['Mode'] = 'table'
2388                    self.editTable.Enable(True)
2389                else:
2390                    self.useActive.SetValue(True)
2391                    self.params['Mode'] = 'active'
2392                    self.imageBase = G2frame.Image
2393                    self.useActive.SetLabel("Active Image: "+
2394                            G2frame.GPXtree.GetItemText(self.imageBase))
2395                    self.editTable.Enable(False)
2396            finally:
2397                if dlg: dlg.Destroy()
2398               
2399        def showPDFctrls(event):
2400            '''Called to show or hide AutoPDF widgets. Note that fInp4 must be included in the
2401            sizer layout with .Show(True) before .Show(False) will work properly.
2402            '''
2403            fInp4.Enable(self.params['ComputePDF'])
2404            fInp4.Show(self.params['ComputePDF'])
2405            fInp4a.Enable(self.params['ComputePDF'])
2406            btn4.Enable(self.params['ComputePDF'])
2407            cOpt.Enable(self.params['ComputePDF'])
2408            if self.params['ComputePDF']:
2409                lbl4.SetForegroundColour("black")
2410                lbl4a.SetForegroundColour("black")
2411            else:
2412                lbl4.SetForegroundColour("gray")
2413                lbl4a.SetForegroundColour("gray")
2414                                   
2415        def scanPDFprm(**kw):
2416            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2417            fInp4._IndicateValidity()
2418           
2419        def OnAutoScale(event):
2420            self.AutoScale = autoscale.GetValue()
2421           
2422        def OnAutoScaleName(event):
2423            self.AutoScaleName = scalename.GetValue()
2424            self.Scale[0] = self.AutoScales[self.AutoScaleName]
2425            scaleval.SetValue(self.Scale[0])
2426               
2427        ##################################################
2428        # beginning of __init__ processing
2429        ##################################################
2430        self.G2frame = G2frame
2431        self.ImgTblParms = None
2432        self.IMfileList = None
2433        self.Evaluator = None
2434        self.params = {}
2435        self.Reset = False
2436        self.Pause = False
2437        self.PreventReEntryShowMatch = False
2438        self.PreventReEntryTimer = False
2439        self.params['IMGfile'] = ''
2440        self.params['MaskFile'] = ''
2441        self.params['IgnoreMask'] = True
2442        self.fmtlist = G2IO.ExportPowderList(G2frame)
2443        self.timer = wx.Timer()
2444        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2445        self.imageBase = G2frame.Image
2446        self.params['ComputePDF'] = False
2447        self.params['pdfDmax'] = 0.0
2448        self.params['pdfprm'] = ''
2449        self.params['optPDF'] = True
2450        self.pdfControls = {}
2451        self.AutoScale = False
2452        self.Scale = [1.0,]
2453
2454        G2frame.GPXtree.GetSelection()
2455        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(self.imageBase) 
2456        self.imagedir,fileroot = os.path.split(imagefile)
2457        Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2458            G2frame,self.imageBase, 'Comments'))
2459        DefaultAutoScaleNames = GSASIIpath.GetConfigValue('Autoscale_ParmNames')
2460        self.AutoScaleName = GSASIIpath.GetConfigValue('DefaultAutoScale')
2461        self.AutoScales = {}
2462        if DefaultAutoScaleNames is not None:
2463            for comment in Comments:
2464                if '=' in comment:
2465                    name,val = comment.split('=',1) 
2466                    if name in DefaultAutoScaleNames:
2467                        try:
2468                            self.AutoScales[name] = float(val)
2469                            if name == self.AutoScaleName:
2470                                self.Scale[0] = float(val)
2471                        except ValueError:
2472                            continue
2473        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2474        self.params['outdir'] = os.path.abspath(self.imagedir)
2475        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2476                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2477        self.Status = self.CreateStatusBar()
2478        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2479        mnpnl = wx.Panel(self)
2480        mnsizer = wx.BoxSizer(wx.VERTICAL)
2481        # box for integration controls & masks input
2482        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2483        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2484        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2485        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2486                            style = wx.RB_GROUP)
2487        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2488        self.useActive.SetLabel("Active Image: "+
2489                                G2frame.GPXtree.GetItemText(self.imageBase))
2490        lblsizr.Add(self.useActive,1,wx.EXPAND,1)
2491        self.useActive.SetValue(True)
2492        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2493        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2494        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2495        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2496        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2497        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2498        self.editTable.Enable(False)
2499        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2500        # bind button and deactivate be default
2501        lblsizr.Add(minisizer)
2502        mnsizer.Add(lblsizr,1,wx.EXPAND,1)
2503
2504        # file filter stuff
2505        sizer = wx.BoxSizer(wx.HORIZONTAL)
2506        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Image filter'))
2507        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',OnLeave=self.ShowMatchingFiles)
2508        sizer.Add(flterInp)
2509        mnsizer.Add(sizer,0,wx.ALIGN_RIGHT,1)
2510        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2511        mnsizer.Add(self.ListBox,0,wx.EXPAND,1)
2512        self.ShowMatchingFiles(self.params['filter'])
2513        # box for output selections
2514        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2515        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2516        sizer = wx.BoxSizer(wx.HORIZONTAL)
2517        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2518        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2519        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2520        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2521        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2522        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2523        lblsizr.Add(sizer,0,wx.EXPAND)
2524        sizer = wx.BoxSizer(wx.HORIZONTAL)
2525        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
2526        for dfmt in self.fmtlist:
2527            fmt = dfmt[1:]
2528            self.params[fmt] = False
2529            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2530            sizer.Add(btn)
2531        lblsizr.Add(sizer)
2532        sizer = wx.BoxSizer(wx.HORIZONTAL)
2533        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2534        self.params['SeparateDir'] = False
2535        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2536        lblsizr.Add(sizer)
2537        if self.AutoScales:
2538            sizer = wx.BoxSizer(wx.HORIZONTAL)
2539            autoscale = wx.CheckBox(mnpnl,label='Do autoscaling with:')
2540            autoscale.Bind(wx.EVT_CHECKBOX,OnAutoScale)
2541            sizer.Add(autoscale,0,WACV)
2542            scalename = wx.ComboBox(mnpnl,value=self.AutoScaleName,choices=list(self.AutoScales.keys()),
2543                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2544            scalename.Bind(wx.EVT_COMBOBOX,OnAutoScaleName)
2545            sizer.Add(scalename,0,WACV)
2546            sizer.Add(wx.StaticText(mnpnl,label=' to '),0,WACV)
2547            scaleval = G2G.ValidatedTxtCtrl(mnpnl,self.Scale,0,nDig=(10,2),min=1.)
2548            sizer.Add(scaleval,0,WACV)
2549            lblsizr.Add(sizer,0)
2550        #ToDO: Autonormalize, parm name?, scaling value?
2551        sizer = wx.BoxSizer(wx.HORIZONTAL)
2552        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2553        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2554        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2555        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2556        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2557        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2558        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2559        sizer.Add(cOpt)
2560        lblsizr.Add(sizer,0)
2561        sizer = wx.BoxSizer(wx.HORIZONTAL)
2562        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2563        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2564        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2565                                     OnLeave=scanPDFprm)
2566        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2567        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2568        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2569        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2570        lblsizr.Add(sizer,0,wx.EXPAND)
2571        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2572        # buttons on bottom
2573        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2574        sizer = wx.BoxSizer(wx.HORIZONTAL)
2575        sizer.Add((20,-1))
2576        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2577        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2578        sizer.Add(self.btnstart)
2579        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2580        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2581        sizer.Add(self.btnreset)
2582        sizer.Add((20,-1),wx.EXPAND,1)
2583        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2584        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2585        sizer.Add(self.btnclose)
2586        sizer.Add((20,-1))
2587        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2588        # finish up window
2589        mnpnl.SetSizer(mnsizer)
2590        OnRadioSelect(None) # disable widgets
2591        mnsizer.Fit(self)
2592        self.CenterOnParent()
2593        self.Show()
2594        showPDFctrls(None)
2595
2596    def checkPDFprm(self,ShowContents=False):
2597        '''Read in the PDF (.pdfprm) parameter file and check for problems.
2598        If ShowContents is True, a formatted text version of some of the file
2599        contents is returned. If errors are found, the return string will contain
2600        the string "Error:" at least once.
2601        '''
2602        self.pdfControls = {}
2603        msg = ''
2604        File = None
2605        try:
2606            File = open(self.params['pdfprm'],'r')
2607            S = File.readline()
2608            while S:
2609                if '#' in S:
2610                    S = File.readline()
2611                    continue
2612                key,val = S.split(':',1)
2613                try:
2614                    self.pdfControls[key] = eval(val)
2615                except:
2616                    self.pdfControls[key] = val
2617                S = File.readline()
2618        except Exception as err:
2619            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
2620            if GSASIIpath.GetConfigValue('debug'):
2621                print(msg)
2622                print(err)
2623            self.pdfControls = {}
2624            return msg
2625        finally:
2626            if File: File.close()
2627        formula = ''
2628        for el in self.pdfControls['ElList']:
2629            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
2630            if formula: formula += ' '
2631            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
2632        if not formula:
2633            msg += 'Error: no chemical formula in file'
2634        for key in ['Sample Bkg.','Container','Container Bkg.']:
2635            if key not in self.pdfControls:
2636                if msg: msg += '\n'
2637                msg += 'Error: missing key in self.pdfControls: '+key
2638                continue
2639        if msg or not ShowContents: return msg  # stop on error
2640        msg += 'Default formula: '+formula+'\n'
2641        for key in ['Sample Bkg.','Container','Container Bkg.']:
2642            name = self.pdfControls[key]['Name']
2643            mult = self.pdfControls[key].get('Mult',0.0)
2644            if not name: continue
2645            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
2646            if not G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,name):
2647                msg += ' *** missing ***'
2648        return msg
2649   
2650    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2651        G2frame = self.G2frame
2652        if invalid: return
2653        msg = ''
2654        if self.PreventReEntryShowMatch: return
2655        self.PreventReEntryShowMatch = True
2656        imageFileList = []
2657        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
2658            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
2659            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
2660            if imagefile not in imageFileList: imageFileList.append(imagefile)
2661            if img not in G2frame.IntegratedList:
2662                if msg: msg += '\n'
2663                msg += '  ' + img
2664        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
2665        msg1 = ""
2666        try:
2667            imageList = sorted(
2668                glob.glob(os.path.join(self.imagedir,value)))
2669            if not imageList:
2670                msg1 = 'Warning: No files match search string '+os.path.join(self.imagedir,value)
2671            else:
2672                for fil in imageList:
2673                    if fil not in imageFileList: msg1 += '\n  '+fil
2674                if msg1:
2675                    msg += 'Files to integrate from '+os.path.join(self.imagedir,value)+msg1
2676                else:
2677                    msg += 'No files found to read'
2678        except IndexError:
2679            msg += 'Error searching for files named '+os.path.join(self.imagedir,value)
2680        self.ListBox.Clear()
2681        self.ListBox.AppendItems(msg.split('\n'))
2682        self.PreventReEntryShowMatch = False
2683        return
2684       
2685    def OnPause(self):
2686        '''Respond to Pause, changes text on button/Status line, if needed
2687        Stops timer
2688        self.Pause should already be True
2689        '''
2690        if self.timer.IsRunning(): self.timer.Stop()
2691        if self.btnstart.GetLabel() == 'Restart':
2692            return
2693        if self.btnstart.GetLabel() != 'Resume':
2694            print('\nPausing autointegration\n')
2695            self.btnstart.SetLabel('Resume')
2696            self.Status.SetStatusText(
2697                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
2698        self.Pause = True
2699           
2700    def IntegrateImage(self,img):
2701        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
2702        are placed in G2frame.IntgOutList
2703        '''
2704        G2frame = self.G2frame
2705        imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
2706        G2frame.Image = imgId
2707        G2frame.PickId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls')
2708        # do integration
2709        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
2710        if self.AutoScale:
2711            Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2712                G2frame,imgId, 'Comments'))
2713            for comment in Comments:
2714                if '=' in comment:
2715                    name,val = comment.split('=',1) 
2716                    if name == self.AutoScaleName:
2717                        val = float(val)
2718                        if val > 0.:
2719                            Scale = self.Scale[0]/val
2720                        break
2721        masks = G2frame.GPXtree.GetItemPyData(
2722            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Masks'))
2723        data = G2frame.GPXtree.GetItemPyData(G2frame.PickId)
2724        # simulate a Image Controls press, since that is where the
2725        # integration is hidden
2726        UpdateImageControls(G2frame,data,masks,IntegrateOnly=True)
2727        G2frame.IntegratedList.append(img) # note this as integrated
2728        # split name and control number
2729        s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
2730        namepre = s[0]
2731        if len(s) > 1:
2732            namenum = s[1]
2733        else:
2734            namenum = ''
2735        for Id in G2frame.IntgOutList: # loop over newly created PDWR entry(ies)
2736            # save the created PWDR tree names so that a reset can delete them
2737            G2frame.Image = Id
2738            treename = G2frame.GPXtree.GetItemText(Id)
2739            G2frame.AutointPWDRnames.append(treename)
2740            # write out the images in the selected formats
2741            Sdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Sample Parameters'))
2742            if self.AutoScale:
2743                print ('Rescale by %.4f'%(Scale))
2744                y,w = G2frame.GPXtree.GetItemPyData(Id)[1][1:3]
2745                y *= Scale
2746                w /= Scale**2
2747            # determine the name for the current file
2748            fileroot = namepre
2749            if len(G2frame.IntgOutList) > 1:
2750                fileroot += "_AZM"
2751                if 'Azimuth' in Sdata:
2752                    fileroot += str(int(10*Sdata['Azimuth']))
2753                fileroot += "_" 
2754            fileroot += namenum
2755            # loop over selected formats
2756            for dfmt in self.fmtlist:
2757                if not self.params[dfmt[1:]]: continue
2758                if self.params['SeparateDir']:
2759                    subdir = dfmt[1:]
2760                else:
2761                    subdir = ''
2762                fil = os.path.join(self.params['outdir'],subdir,fileroot)
2763                print('writing file '+fil+dfmt)
2764                G2IO.ExportPowder(G2frame,treename,fil,dfmt)
2765               
2766    def EnableButtons(self,flag):
2767        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
2768        '''
2769        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
2770        # on windows
2771        if sys.platform != "win32":
2772            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
2773        self.btnstart.SetLabel('Pause')
2774        wx.Yield()
2775               
2776    def ResetFromTable(self,dist):
2777        '''Sets integration parameters based on values from
2778        the lookup table
2779        '''
2780        #dist = self.controlsDict['distance']
2781        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
2782        if GSASIIpath.GetConfigValue('debug'):
2783            print ('interpolated values: ',interpDict)
2784        self.ImageControls = ReadControls(imgctrl)
2785        self.ImageControls.update(interpDict)
2786        self.ImageControls['showLines'] = True
2787        self.ImageControls['ring'] = []
2788        self.ImageControls['rings'] = []
2789        self.ImageControls['ellipses'] = []
2790        self.ImageControls['setDefault'] = False
2791        for i in 'range','size','GonioAngles':
2792            if i in self.ImageControls:
2793                del self.ImageControls[i]
2794        # load copy of Image Masks
2795        if immask:
2796            self.ImageMasks = ReadMask(immask)
2797            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
2798                del self.ImageMasks['Thresholds']
2799        else:
2800            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
2801       
2802    def StartLoop(self):
2803        '''Prepare to start autointegration timer loop.
2804        Save current Image params for use in future integrations
2805        also label the window so users understand what is being used
2806        '''
2807        print('\nStarting new autointegration\n')
2808        G2frame = self.G2frame
2809        # show current IMG base
2810        if self.params['Mode'] != 'table':
2811            self.useActive.SetLabel("Active Image: "+
2812                                    G2frame.GPXtree.GetItemText(self.imageBase))
2813            # load copy of Image Controls from current image and clean up
2814            # items that should not be copied
2815            self.ImageControls = copy.deepcopy(
2816                G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2817                    G2frame,self.imageBase, 'Image Controls')))
2818            self.ImageControls['showLines'] = True
2819            self.ImageControls['ring'] = []
2820            self.ImageControls['rings'] = []
2821            self.ImageControls['ellipses'] = []
2822            self.ImageControls['setDefault'] = False
2823            del self.ImageControls['range']
2824            del self.ImageControls['size']
2825            del self.ImageControls['GonioAngles']
2826            # load copy of Image Masks, keep thresholds
2827            self.ImageMasks = copy.deepcopy(
2828                G2frame.GPXtree.GetItemPyData(
2829                    G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks')))
2830            self.Thresholds = self.ImageMasks['Thresholds'][:]
2831            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
2832                del self.ImageMasks['Thresholds']   
2833        # make sure all output directories exist
2834        if self.params['SeparateDir']:
2835            for dfmt in self.fmtlist:
2836                if not self.params[dfmt[1:]]: continue
2837                dir = os.path.join(self.params['outdir'],dfmt[1:])
2838                if not os.path.exists(dir): os.makedirs(dir)
2839        else:
2840            if not os.path.exists(self.params['outdir']):
2841                os.makedirs(self.params['outdir'])
2842        if self.Reset: # special things to do after Reset has been pressed
2843            self.G2frame.IntegratedList = []
2844           
2845            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
2846                for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
2847                    # update controls from master
2848                    controlsDict = G2frame.GPXtree.GetItemPyData(
2849                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Image Controls'))
2850                    controlsDict.update(self.ImageControls)
2851                    # update masks from master
2852                    ImageMasks = G2frame.GPXtree.GetItemPyData(
2853                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks'))
2854                    ImageMasks.update(self.ImageMasks)
2855            # delete all PWDR items created after last Start was pressed
2856            idlist = []
2857            item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
2858            while item:
2859                itemName = G2frame.GPXtree.GetItemText(item)
2860                if itemName in G2frame.AutointPWDRnames:
2861                    idlist.append(item)
2862                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
2863            for item in idlist:
2864                G2frame.GPXtree.Delete(item)
2865            wx.Yield()
2866            self.Reset = False
2867        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
2868        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
2869        # check that AutoPDF input is OK, offer chance to use alternate PDWRs if referenced ones
2870        # are not present
2871        if self.params['ComputePDF']:
2872            msg = self.checkPDFprm()
2873            if 'Error:' in msg:
2874                print(msg)
2875                return True
2876            fileList = []
2877            id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
2878            while id:
2879                name = G2frame.GPXtree.GetItemText(id)
2880                if name.startswith('PWDR '): fileList.append(name)
2881                id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
2882            if not fileList:
2883                print(msg)
2884                print('No PWDR entries to select')
2885                return True
2886            for key in ['Sample Bkg.','Container','Container Bkg.']:
2887                name = self.pdfControls[key]['Name']
2888                if not name: continue
2889                if not G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name):
2890                    indx = G2G.ItemSelector(fileList, self, header='Select PDWR item',
2891                                    title='Select a PDWR tree item for '+key+'\n(or cancel to quit)')
2892                    if indx is None:
2893                        print('No PWDR entry selected for '+key)
2894                        return True
2895                    self.pdfControls[key]['Name'] = fileList[indx]
2896        return False
2897               
2898    def OnTimerLoop(self,event):
2899        '''A method that is called every :meth:`PollTime` seconds that is
2900        used to check for new files and process them. Integrates new images.
2901        Also optionally sets up and computes PDF.
2902        This is called only after the "Start" button is pressed (then its label reads "Pause").
2903        '''
2904        def AutoIntegrateImage(imgId):
2905            '''Integrates an image that has been read into the data tree and updates the
2906            AutoInt window.
2907            '''
2908            img = G2frame.GPXtree.GetItemText(imgId)
2909            controlsDict = G2frame.GPXtree.GetItemPyData(
2910                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
2911            ImageMasks = G2frame.GPXtree.GetItemPyData(
2912                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Masks'))
2913            if self.params['Mode'] == 'table': # look up parameter values from table
2914                self.ResetFromTable(controlsDict['setdist'])
2915            # update controls from master
2916            controlsDict.update(self.ImageControls)
2917            # update masks from master w/o Thresholds
2918            ImageMasks.update(self.ImageMasks)
2919            self.EnableButtons(False)
2920            try:
2921                self.IntegrateImage(img)
2922            finally:
2923                self.EnableButtons(True)
2924            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
2925            wx.Yield()
2926            self.ShowMatchingFiles(self.params['filter'])
2927            wx.Yield()
2928           
2929        def AutoComputePDF(imgId):
2930            '''Computes a PDF for a PWDR data tree tree item
2931            '''
2932            for pwdr in G2frame.AutointPWDRnames[:]:
2933                if not pwdr.startswith('PWDR '): continue
2934                if pwdr in G2frame.AutointPDFnames: continue
2935                PWid = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,pwdr)
2936                controlsDict = G2frame.GPXtree.GetItemPyData(
2937                    G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
2938                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
2939                    print('Skipping PDF for '+pwdr+' due to detector position')
2940                    continue
2941                # Setup PDF
2942                Data = G2frame.GPXtree.GetItemPyData(PWid)[1]
2943                pwdrMin = np.min(Data[1])
2944                Parms = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2945                    G2frame,PWid,'Instrument Parameters'))[0]
2946                fullLimits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2947                    G2frame,PWid,'Limits'))[0]
2948                if 'C' in Parms['Type'][0]:
2949                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
2950                else:
2951                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
2952                Qlimits = [0.9*qMax,qMax]
2953
2954                item = pwdr
2955                Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2956                    G2frame,imgId, 'Comments'))
2957                ElList = {}
2958                sumnum = 1.0
2959                for item in Comments:           #grab chemical formula from Comments, if there
2960                    if 'formula' in item[:15].lower():
2961                        formula = item.split('=')[1].split()
2962                        elems = formula[::2]
2963                        nums = formula[1::2]
2964                        formula = zip(elems,nums)
2965                        sumnum = 0.
2966                        for [elem,num] in formula:
2967                            ElData = G2elem.GetElInfo(elem,Parms)
2968                            ElData['FormulaNo'] = float(num)
2969                            sumnum += float(num)
2970                            ElList[elem] = ElData
2971                PDFnames = G2gd.GetGPXtreeDataNames(G2frame,['PDF ',])
2972                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
2973                if not PDFid: continue
2974                PDFdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2975                    G2frame,PDFid, 'PDF Controls'))
2976                PDFdata.update(self.pdfControls)
2977                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
2978                PDFdata['Sample']['Name'] = pwdr
2979                # compute PDF
2980                wx.Yield()
2981                G2pdG.computePDF(G2frame,PDFdata)
2982                wx.Yield()
2983                G2frame.PatternId = PDFid
2984                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
2985                if self.params['optPDF']:
2986                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
2987                    wx.Yield()
2988                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
2989                G2frame.AutointPDFnames.append(pwdr)
2990                # save names of PDF entry to be deleted later if needed
2991                G2frame.AutointPWDRnames.append(G2frame.GPXtree.GetItemText(PDFid))
2992           
2993        G2frame = self.G2frame
2994        try:
2995            self.currImageList = sorted(
2996                glob.glob(os.path.join(self.imagedir,self.params['filter'])))
2997            self.ShowMatchingFiles(self.params['filter'])
2998        except IndexError:
2999            self.currImageList = []
3000            return
3001
3002        if self.PreventReEntryTimer: return
3003        self.PreventReEntryTimer = True
3004        imageFileList = []
3005        # integrate the images that have already been read in, but
3006        # have not yet been processed           
3007        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
3008            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3009            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3010            # Create a list of image files that have been read in
3011            if imagefile not in imageFileList: imageFileList.append(imagefile)
3012            # skip if already integrated
3013            if img in G2frame.IntegratedList: continue
3014            AutoIntegrateImage(imgId)
3015            #self.Pause |= G2frame.PauseIntegration
3016            #if self.Pause:
3017            #    self.OnPause()
3018            #    self.PreventReEntryTimer = False
3019            #    return
3020            if self.pdfControls: AutoComputePDF(imgId)
3021            self.Pause |= G2frame.PauseIntegration
3022            if self.Pause:
3023                self.OnPause()
3024                self.PreventReEntryTimer = False
3025                return
3026
3027        # loop over image files matching glob, reading in any new ones
3028        for newImage in self.currImageList:
3029            if newImage in imageFileList or self.Pause: continue # already read?
3030            for imgId in G2IO.ReadImages(G2frame,newImage):
3031                AutoIntegrateImage(imgId)           
3032                #self.Pause |= G2frame.PauseIntegration
3033                #if self.Pause:
3034                #    self.OnPause()
3035                #    self.PreventReEntryTimer = False
3036                #    return
3037                if self.pdfControls: AutoComputePDF(imgId)
3038                self.Pause |= G2frame.PauseIntegration
3039                if self.Pause:
3040                    self.OnPause()
3041                    self.PreventReEntryTimer = False
3042                    return
3043        if GSASIIpath.GetConfigValue('debug'):
3044            import datetime
3045            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3046        self.PreventReEntryTimer = False
3047
3048def DefineEvaluator(dlg):
3049    '''Creates a function that provides interpolated values for a given distance value
3050    '''
3051    def Evaluator(dist):
3052        '''Interpolate image parameters for a supplied distance value
3053
3054        :param float dist: distance to use for interpolation
3055        :returns: a list with 3 items:
3056
3057          * a dict with parameter values,
3058          * the closest imctrl and
3059          * the closest maskfile (or None)
3060        '''           
3061        x = np.array([float(i) for i in parms[0]])
3062        closest = abs(x-dist).argmin()
3063        D = {'setdist':dist}
3064        imctfile = IMfileList[closest]
3065        if parms[-1][closest].lower() != '(none)':
3066            maskfile = parms[-1][closest]
3067        else:
3068            maskfile = None
3069        for c in range(1,cols-1):
3070            lbl = ParmList[c]
3071            if lbl in nonInterpVars:
3072                if lbl in ['outChannels',]:
3073                    D[lbl] = int(float(parms[c][closest]))
3074                else:
3075                    D[lbl] = float(parms[c][closest])
3076            else:
3077                y = np.array([float(i) for i in parms[c]])
3078                D[lbl] = np.interp(dist,x,y)
3079        # full integration when angular range is 0
3080        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3081        # conversion for paired values
3082        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3083            r = a.split('_')[0]
3084            D[r] = [D[a],D[b]]
3085            if r in ['LRazimuth',]:
3086                D[r] = [int(D[a]),int(D[b])]
3087            del D[a]
3088            del D[b]
3089        return D,imctfile,maskfile
3090    # save local copies of values needed in Evaluator
3091    parms = dlg.ReadImageParmTable()
3092    IMfileList = dlg.IMfileList
3093    cols = dlg.list.GetColumnCount()
3094    ParmList = dlg.ParmList
3095    nonInterpVars = dlg.nonInterpVars
3096    return Evaluator
3097
3098class IntegParmTable(wx.Dialog):
3099    '''Creates a dialog window with a table of integration parameters.
3100    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3101    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3102    creates a dictionary with interpolated parameter values.
3103    '''
3104    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3105            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3106            'maskfile',
3107            )
3108    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3109                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3110    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3111            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3112            'Mask File',
3113            )
3114    def __init__(self,parent,parms=None,IMfileList=None):
3115        self.G2frame = parent.G2frame
3116        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3117        if parms:
3118            self.parms = parms # list of values by column
3119            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3120        else:
3121            self.parms = [] # list of values by column
3122            self.IMfileList = [] # list of .imctrl file names for each entry in table
3123            files = []
3124            try:
3125                pth = G2G.GetImportPath(self.G2frame)
3126                if not pth: pth = '.'
3127                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3128                    style=wx.FD_OPEN| wx.MULTIPLE,
3129                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3130                dlg.CenterOnParent()
3131                if dlg.ShowModal() == wx.ID_OK:
3132                    files = dlg.GetPaths()
3133                    self.parms,self.IMfileList = self.ReadFiles(files)
3134            finally:
3135                dlg.Destroy()
3136            if not files:
3137                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3138                return
3139        mainSizer = wx.BoxSizer(wx.VERTICAL)
3140        self.list = ImgIntLstCtrl(self, wx.ID_ANY,style=wx.LC_REPORT| wx.BORDER_SUNKEN)
3141        mainSizer.Add(self.list,1,wx.EXPAND,1)
3142        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3143        btn = wx.Button(self, wx.ID_OK)
3144        btnsizer.Add(btn)
3145        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3146        btn.Bind(wx.EVT_BUTTON,self._onSave)
3147        btnsizer.Add(btn)
3148        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3149        btn.Bind(wx.EVT_BUTTON,self._onClose)
3150        btnsizer.Add(btn)
3151        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3152        self.SetSizer(mainSizer)
3153        self.list.FillList(self.parms)
3154       
3155    def ReadFiles(self,files):
3156        '''Reads a list of .imctrl files or a single .imtbl file
3157        '''
3158        tmpDict = {}
3159        if not files: return
3160        # option 1, a dump from a previous save
3161        if os.path.splitext(files[0])[1] == '.imtbl':
3162            fp = open(files[0],'r')
3163            S = fp.readline()
3164            while S:
3165                if S[0] != '#':
3166                    [key,val] = S[:-1].split(':',1)
3167                    tmpDict[key] = eval(val)
3168                S = fp.readline()
3169            fp.close()
3170            # delete entries where files do not exist
3171            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3172            if m1:
3173                print('\nimctrl file not found:')
3174                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3175            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3176            if m2:
3177                print('\nmask file not found')
3178                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3179            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3180            if m3:
3181                print('\nDropping entries due to negative distance: '+str(m3))
3182            m = sorted(set(m1 + m2 + m3))
3183            m.reverse()
3184            for c in m:
3185                for key in tmpDict:
3186                    del tmpDict[key][c]
3187            fileList = tmpDict.get('filenames','[]')
3188            parms = []
3189            if 'setdist' not in tmpDict:
3190                print(u'Old file, recreate before using: {}'.format(files[0]))
3191                return [[]],[]
3192            for key in self.ParmList:
3193                try:
3194                    float(tmpDict[key][0])
3195                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3196                except ValueError:
3197                    parms.append(tmpDict[key])
3198                except IndexError:
3199                    print('No valid image control entries read')
3200                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3201                    return [[]],[]
3202            return parms,fileList
3203        # option 2, read in a list of files
3204        for file in files: # read all files; place in dict by distance
3205            imgDict = Read_imctrl(file)
3206            dist = imgDict.get('setdist',imgDict['distance'])
3207            if dist is None:
3208                print('Skipping old file, redo: {}'.format(file))
3209            tmpDict[dist] = imgDict
3210        parms = [[] for key in self.ParmList]
3211        fileList = []
3212        for d in sorted(tmpDict):
3213            fileList.append(tmpDict[d].get('filename'))
3214            if d is None: continue
3215            if d < 0: continue
3216            for i,key in enumerate(self.ParmList):
3217                val = tmpDict[d].get(key)
3218                try:
3219                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3220                except:
3221                    val = str(val)
3222                parms[i].append(val)
3223        return parms,fileList
3224   
3225    def ReadImageParmTable(self):
3226        '''Reads possibly edited values from the ListCtrl table and returns a list
3227        of values for each column.
3228        '''
3229        rows = self.list.GetItemCount()
3230        cols = self.list.GetColumnCount()
3231        parms = []
3232        for c in range(cols):
3233            parms.append([])
3234            for r in range(rows):
3235                parms[c].append(self.list.GetItem(r,c).GetText())
3236        return parms
3237
3238    def _onClose(self,event):
3239        'Called when Cancel button is pressed'
3240        self.EndModal(wx.ID_CANCEL)
3241       
3242    def _onSave(self,event):
3243        'Called when save button is pressed; creates a .imtbl file'
3244        fil = ''
3245        if self.G2frame.GSASprojectfile:
3246            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3247        dir,f = os.path.split(fil)
3248        pth = G2G.GetExportPath(self.G2frame)
3249        try:
3250            dlg = wx.FileDialog(self, 'Save table data as',
3251                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3252                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3253            dlg.CenterOnParent()
3254            if dlg.ShowModal() != wx.ID_OK: return
3255            fil = dlg.GetPath()
3256            fil = os.path.splitext(fil)[0]+'.imtbl'
3257        finally:
3258            dlg.Destroy()       
3259        parms = self.ReadImageParmTable()
3260        print('Writing image parameter table as '+fil)
3261        fp = open(fil,'w')
3262        for c in range(len(parms)-1):
3263            lbl = self.ParmList[c]
3264            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3265        lbl = self.ParmList[c+1]
3266        fp.write(lbl+': '+str(parms[c+1])+'\n')
3267        lbl = 'filenames'
3268        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3269        fp.close()
3270   
3271class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3272    '''Creates a custom ListCtrl for editing Image Integration parameters
3273    '''
3274    def __init__(self, parent, ID, pos=wx.DefaultPosition,size=(1000,200),style=0):
3275        self.parent=parent
3276        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3277        listmix.ListCtrlAutoWidthMixin.__init__(self)
3278        listmix.TextEditMixin.__init__(self)
3279        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3280        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3281       
3282    def FillList(self,parms):
3283        'Places the current parms into the table'
3284        self.ClearAll()
3285        self.rowlen = len(self.parent.ParmList)
3286        for i,lbl in enumerate(self.parent.HeaderList):
3287            self.InsertColumn(i, lbl)
3288        for r,d in enumerate(parms[0]):
3289            if d is None: continue
3290            if d == 'None': continue
3291            if float(d) < 0: continue
3292            index = self.InsertStringItem(sys.maxsize, d)
3293            for j in range(1,len(parms)):
3294                self.SetStringItem(index, j, parms[j][r])
3295        for i,lbl in enumerate(self.parent.ParmList):
3296            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3297
3298    def OnDouble(self,evt):
3299        'respond to a double-click'
3300        self.CloseEditor()
3301        fil = '(none)'
3302        pth = G2G.GetImportPath(self.parent.G2frame)
3303        if not pth: pth = '.'
3304        try:
3305            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3306                style=wx.FD_OPEN,wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3307            dlg.CenterOnParent()
3308            if dlg.ShowModal() == wx.ID_OK:
3309                fil = dlg.GetPath()
3310        finally:
3311            dlg.Destroy()
3312        if os.path.splitext(fil)[1] != '.imctrl':
3313            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3314            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3315        else:
3316            # insert or overwrite an instrument parameter set
3317            if not os.path.exists(fil):
3318                print('Does not exist: '+fil)
3319                return
3320            imgDict = Read_imctrl(fil)
3321            dist = imgDict['distance']
3322            parms = self.parent.ReadImageParmTable()
3323            x = np.array([float(i) for i in parms[0]])
3324            closest = abs(x-dist).argmin()
3325            closeX = x[closest]
3326            # fix IMfileList
3327            for c,lbl in enumerate(self.parent.ParmList):
3328                try:
3329                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3330                except ValueError:
3331                    vali = imgDict[lbl]
3332                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3333                    parms[c][closest] = vali
3334                elif dist > closeX: # insert after
3335                    parms[c].insert(closest+1,vali)
3336                else:
3337                    parms[c].insert(closest,vali)
3338            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3339                self.parent.IMfileList[closest] = fil
3340            elif dist > closeX: # insert after
3341                self.parent.IMfileList.insert(closest+1,fil)
3342            else:
3343                self.parent.IMfileList.insert(closest,fil)
3344            self.FillList(parms)
3345# Autointegration end
3346###########################################################################
Note: See TracBrowser for help on using the repository browser.