source: trunk/GSASIIimgGUI.py @ 4102

Last change on this file since 4102 was 4102, checked in by vondreele, 4 years ago

add transfer of flat background to images corrected for 1/dist2
correct error in recalibrate; eliminate duplicate reflections in e.g. silicon from genHKLpeak

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