source: trunk/GSASIIimgGUI.py @ 3779

Last change on this file since 3779 was 3779, checked in by vondreele, 3 years ago

fix problems with image sum
fix problem with samplechangerpos

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