source: trunk/GSASIIimgGUI.py @ 4031

Last change on this file since 4031 was 4031, checked in by toby, 4 years ago

introduce external autoInt

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 174.1 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2019-06-18 23:21:39 +0000 (Tue, 18 Jun 2019) $
5# $Author: toby $
6# $Revision: 4031 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 4031 2019-06-18 23:21:39Z toby $
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: 4031 $")
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.CallLater(100,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        wx.BeginBusyCursor()
272        try:
273            G2frame.Integrate = G2img.ImageIntegrate(sumImg,data,masks,blkSize,useTA=useTA,useMask=useMask)           
274        finally:
275            wx.EndBusyCursor()   
276        G2frame.PauseIntegration = G2frame.Integrate[-1]
277        del sumImg  #force cleanup
278        Id = G2IO.SaveIntegration(G2frame,G2frame.PickId,data,(event is None))
279        G2frame.PatternId = Id
280        G2frame.GPXtree.SelectItem(Id)
281        G2frame.GPXtree.Expand(Id)
282        for item in G2frame.MakePDF: item.Enable(True)
283       
284    def OnIntegrateAll(event):
285        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
286        dlg = G2G.G2MultiChoiceDialog(G2frame,'Image integration controls','Select images to integrate:',Names)
287        try:
288            if dlg.ShowModal() == wx.ID_OK:
289                items = dlg.GetSelections()
290                G2frame.EnablePlot = False
291                dlgp = wx.ProgressDialog("Elapsed time","2D image integrations",len(items)+1,
292                    style = wx.PD_ELAPSED_TIME|wx.PD_CAN_ABORT)
293                try:
294                    pId = 0
295                    oldData = {'tilt':0.,'distance':0.,'rotation':0.,'center':[0.,0.],'DetDepth':0.,'azmthOff':0.}
296                    oldMhash = 0
297                    for icnt,item in enumerate(items):
298                        GoOn = dlgp.Update(icnt)
299                        if not GoOn[0]:
300                            break
301                        name = Names[item]
302                        G2frame.Image = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
303                        CId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')
304                        Data = G2frame.GPXtree.GetItemPyData(CId)
305                        same = True
306                        for item in ['tilt','distance','rotation','DetDepth','azmthOff']:
307                            if Data[item] != oldData[item]:
308                                same = False
309                        if (Data['center'][0] != oldData['center'][0] or
310                            Data['center'][1] != oldData['center'][1]):
311                                same = False
312                        if not same:
313                            t0 = time.time()
314                            useTA = G2img.MakeUseTA(Data,blkSize)
315                            print(' Use new image controls; new xy -> th,azm time %.3f'%(time.time()-t0))
316                        Masks = G2frame.GPXtree.GetItemPyData(
317                            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Masks'))
318                        Mhash = copy.deepcopy(Masks)
319                        Mhash.pop('Thresholds')
320                        Mhash = hash(str(Mhash))
321                        if  Mhash != oldMhash:
322                            t0 = time.time()
323                            useMask = G2img.MakeUseMask(Data,Masks,blkSize)
324                            print(' Use new mask; make mask time: %.3f'%(time.time()-t0))
325                            oldMhash = Mhash
326                        image = GetImageZ(G2frame,Data)
327                        G2frame.Integrate = G2img.ImageIntegrate(image,Data,Masks,blkSize,useTA=useTA,useMask=useMask)
328                        del image   #force cleanup
329                        pId = G2IO.SaveIntegration(G2frame,CId,Data)
330                        oldData = Data
331                finally:   
332                    dlgp.Destroy()
333                    G2frame.EnablePlot = True
334                    if pId:
335                        G2frame.GPXtree.SelectItem(pId)
336                        G2frame.GPXtree.Expand(pId)
337                        G2frame.PatternId = pId
338        finally:
339            dlg.Destroy()
340       
341    def OnCopyControls(event):
342        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
343        if len(Names) == 1:
344            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
345            return
346        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
347        Names.pop(Names.index(Source))
348# select targets & do copy
349        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy image controls','Copy controls from '+Source+' to:',Names)
350        try:
351            if dlg.ShowModal() == wx.ID_OK:
352                items = dlg.GetSelections()
353                G2frame.EnablePlot = False
354                for item in items:
355                    name = Names[item]
356                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
357                    CId = G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls')
358                    oldData = copy.deepcopy(G2frame.GPXtree.GetItemPyData(CId))
359                    Data = copy.deepcopy(data)
360                    Data['range'][0] = oldData['range'][0]
361                    Data['size'] = oldData['size']
362                    Data['GonioAngles'] = oldData.get('GonioAngles', [0.,0.,0.])
363                    Data['samplechangerpos'] = oldData.get('samplechangerpos',0.0)
364                    Data['ring'] = []
365                    Data['rings'] = []
366                    Data['ellipses'] = []
367                    if name == Data['dark image'][0]:
368                        Data['dark image'] = ['',-1.]
369                    if name == Data['background image'][0]:
370                        Data['background image'] = ['',-1.]
371                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Image Controls'),Data)
372        finally:
373            dlg.Destroy()
374            G2frame.GPXtree.SelectItem(G2frame.PickId)
375           
376    def OnCopySelected(event):
377        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
378        if len(Names) == 1:
379            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
380            return
381        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
382        # Assemble a list of item labels
383        keyList = ['type','color','wavelength','calibrant','distance','center','Oblique',
384                    'tilt','rotation','azmthOff','fullIntegrate','LRazimuth','setdist',
385                    'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
386                    'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList',
387                    'binType','SampleShape','PolaVal','SampleAbs','dark image','background image']
388        keyList.sort(key=lambda s: s.lower())
389        keyText = [i+' = '+str(data[i]) for i in keyList]
390        # sort both lists together, ordered by keyText
391        selectedKeys = []
392        dlg = G2G.G2MultiChoiceDialog(G2frame,'Select which image controls\nto copy',
393            'Select image controls', keyText)
394        try:
395            if dlg.ShowModal() == wx.ID_OK:
396                selectedKeys = [keyList[i] for i in dlg.GetSelections()]
397        finally:
398            dlg.Destroy()
399        if not selectedKeys: return # nothing to copy
400        copyDict = {}
401        for parm in selectedKeys:
402            copyDict[parm] = data[parm]
403        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy image controls from\n'+Source+' to...',
404            'Copy image controls', Names)
405        try:
406            if dlg.ShowModal() == wx.ID_OK:
407                result = dlg.GetSelections()
408                for i in result: 
409                    item = Names[i]
410                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,item)
411                    Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
412                    Controls.update(copy.deepcopy(copyDict))
413        finally:
414            dlg.Destroy()           
415               
416    def OnSaveControls(event):
417        pth = G2G.GetExportPath(G2frame)
418        dlg = wx.FileDialog(G2frame, 'Choose image controls file', pth, '', 
419            'image control files (*.imctrl)|*.imctrl',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
420        try:
421            if dlg.ShowModal() == wx.ID_OK:
422                filename = dlg.GetPath()
423                # make sure extension is .imctrl
424                filename = os.path.splitext(filename)[0]+'.imctrl'
425                G2fil.WriteControls(filename,data)
426        finally:
427            dlg.Destroy()
428       
429    def OnSaveMultiControls(event):
430        '''Save controls from multiple images
431        '''
432        imglist = []
433        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
434        while item:
435            name = G2frame.GPXtree.GetItemText(item)
436            if name.startswith('IMG '): 
437                imglist.append(name)               
438            item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
439        if not imglist:
440            print('No images!')
441            return
442        dlg = G2G.G2MultiChoiceDialog(G2frame, 'Which images to select?',
443            'Select images', imglist, wx.CHOICEDLG_STYLE)
444        try:
445            if dlg.ShowModal() == wx.ID_OK:
446                treeEntries = [imglist[i] for i in dlg.GetSelections()]
447        finally:
448            dlg.Destroy()
449        if not treeEntries:
450            print('No images selected!')
451            return
452        pth = G2G.GetExportPath(G2frame)
453        dlg = wx.DirDialog(
454            G2frame, 'Select directory for output files',pth,wx.DD_DEFAULT_STYLE)
455        dlg.CenterOnParent()
456        outdir = None
457        try:
458            if dlg.ShowModal() == wx.ID_OK:
459                outdir = dlg.GetPath()
460        finally:
461            dlg.Destroy()
462        if not outdir:
463            print('No directory')
464            return
465        for img in treeEntries:
466            item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
467            data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
468                G2frame,item,'Image Controls'))
469            Npix,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(item)
470            filename = os.path.join(outdir,
471                                    os.path.splitext(os.path.split(imagefile)[1])[0]
472                                    + '.imctrl')
473            print('writing '+filename)
474            G2fil.WriteControls(filename,data)
475           
476    def OnLoadControls(event):
477        pth = G2G.GetImportPath(G2frame)
478        if not pth: pth = '.'
479        dlg = wx.FileDialog(G2frame, 'Choose image controls file', pth, '', 
480            'image control files (*.imctrl)|*.imctrl',wx.FD_OPEN)
481        try:
482            if dlg.ShowModal() == wx.ID_OK:
483                filename = dlg.GetPath()
484                File = open(filename,'r')
485                Slines = File.readlines()
486                File.close()
487                G2fil.LoadControls(Slines,data)
488        finally:
489            dlg.Destroy()
490        G2frame.ImageZ = GetImageZ(G2frame,data)
491        ResetThresholds()
492        G2plt.PlotExposedImage(G2frame,event=event)
493        wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
494       
495    def OnLoadMultiControls(event):         #TODO: how read in multiple image controls & match them by 'twoth' tag?
496        print('This is not implemented yet, sorry')
497        G2G.G2MessageBox(G2frame,'This is not implemented yet, sorry')
498        return
499        pth = G2G.GetImportPath(G2frame)
500        if not pth: pth = '.'
501        controlsDict = {}
502        dlg = wx.FileDialog(G2frame, 'Choose image control files', pth, '', 
503            'image control files (*.imctrl)|*.imctrl',wx.FD_OPEN|wx.FD_MULTIPLE)
504        try:
505            if dlg.ShowModal() == wx.ID_OK:
506                filelist = dlg.GetPaths()
507                if len(filelist) == 0: return
508                for filename in filelist:
509                    File = open(filename,'r')
510                    Slines = File.readlines()
511                    for S in Slines:
512                        if S.find('twoth') == 0:
513                            indx = S.split(':')[1][:-1]     #remove '\n'!
514                    controlsDict[indx] = Slines
515                    File.close()
516        finally:
517            dlg.Destroy()
518        if not len(controlsDict):
519            return
520        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
521        dlg = G2G.G2MultiChoiceDialog(G2frame,'Select images','Select images for updating controls:',
522            Names)
523        try:
524            if dlg.ShowModal() == wx.ID_OK:
525                images = dlg.GetSelections()
526                if not len(images):
527                    return
528                for image in images:
529                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Names[image])
530                    imctrls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
531                    Slines = controlsDict[imctrls['twoth']]
532                    G2fil.LoadControls(Slines,imctrls)
533        finally:
534            dlg.Destroy()
535       
536    def OnTransferAngles(event):
537        '''Sets the integration range for the selected Images based on the difference in detector distance
538        '''
539        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
540        if len(Names) == 1:
541            G2frame.ErrorDialog('No images to transfer integration angles to','Need more "IMG"s')
542            return
543        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
544        Names.pop(Names.index(Source))
545        # select targets & do copy
546        extraopts = {"label_1":"Xfer scaled calib d-min", "value_1":False,
547                     "label_2":"Xfer scaled 2-theta min", "value_2":False,
548                     "label_3":"Xfer scaled 2-theta max", "value_3":True,
549                     }
550        dlg = G2G.G2MultiChoiceDialog(G2frame,'Xfer angles','Transfer integration range from '+Source+' to:',
551            Names,extraOpts=extraopts)
552        try:
553            if dlg.ShowModal() == wx.ID_OK:
554                for i in '_1','_2','_3':
555                    if extraopts['value'+i]: break
556                else:
557                    G2G.G2MessageBox(G2frame,'Nothing to do!')
558                    return
559                xferAng = lambda tth,dist1,dist2: atand(dist1 * tand(tth) / dist2)
560                items = dlg.GetSelections()
561                G2frame.EnablePlot = False
562                Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,Source)
563                data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
564                ttmin0,ttmax0 = data['IOtth']
565                dist0 = data['distance']
566                wave0 = data['wavelength']
567                dsp0 = data['calibdmin']
568                print('distance = {:.2f} integration range: [{:.4f}, {:.4f}], calib dmin {:.3f}'
569                            .format(dist0,ttmin0,ttmax0,dsp0))
570                for item in items:
571                    name = Names[item]
572                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
573                    data = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
574                    dist1 = data['distance']
575                    if extraopts["value_2"]:
576                        data['IOtth'][0] = xferAng(ttmin0,dist0,dist1)
577                    if extraopts["value_3"]:
578                        data['IOtth'][1] = xferAng(ttmax0,dist0,dist1)
579                    if extraopts["value_1"]:
580                        ang1 = xferAng(2.0*asind(wave0/(2.*dsp0)),dist0,dist1)
581                        data['calibdmin'] = data['wavelength']/(2.*sind(ang1/2.))
582                        print('distance = {:.2f} integration range: [{:.4f}, {:.4f}], calib dmin {:.3f}'
583                            .format(dist1,data['IOtth'][0],data['IOtth'][1],data['calibdmin']))
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','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 OnNewPolyMask(event):
1502        'Start a new polygon mask'
1503        G2frame.MaskKey = 'p'
1504        G2plt.OnStartMask(G2frame)
1505       
1506    def OnNewFrameMask(event):
1507        'Start a new Frame mask'
1508        G2frame.MaskKey = 'f'
1509        G2plt.OnStartMask(G2frame)
1510       
1511    def MaxSizer():
1512        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
1513        for the image, as well as callback routines.
1514        '''
1515        def OnNewVal(invalid,value,tc):
1516            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
1517            the value into the data['range'] nested list).
1518            This adjusts the slider positions to match the current values
1519            '''
1520            scaleSel.SetSelection(len(scaleChoices)-1)
1521            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
1522            if r11 != Range[1][1]:
1523                Range[1][1] = r11
1524                maxVal.SetValue(int(Range[1][1]))
1525            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
1526            if r10 != Range[1][0]:
1527                Range[1][0] = r10
1528                minVal.SetValue(int(Range[1][0]))
1529            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1530            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1531            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1532            maxSel.SetValue(sv1)
1533            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1534            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1535            minSel.SetValue(sv0)
1536            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1537            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1538            if mplOld:
1539                Page.canvas.draw()
1540            else:
1541                Page.canvas.draw_idle()
1542           
1543        G2frame.prevMaxValue = None   
1544        def OnMaxSlider(event):
1545            val = maxSel.GetValue()
1546            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
1547            scaleSel.SetSelection(len(scaleChoices)-1)
1548            G2frame.prevMaxValue = val
1549            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1550            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
1551            maxVal.SetValue(int(0.5+Range[1][1]))
1552            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1553            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
1554            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1555            minSel.SetValue(sv0)
1556            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1557            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1558            if mplOld:
1559                Page.canvas.draw()
1560            else:
1561                Page.canvas.draw_idle()
1562           
1563        G2frame.prevMinValue = None   
1564        def OnMinSlider(event):
1565            val = minSel.GetValue()
1566            scaleSel.SetSelection(len(scaleChoices)-1)
1567            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
1568            G2frame.prevMinValue = val
1569            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1570            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
1571            minVal.SetValue(int(Range[1][0]))
1572            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1573            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1574            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1575            maxSel.SetValue(sv1)
1576            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1577            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1578            if mplOld:
1579                Page.canvas.draw()
1580            else:
1581                Page.canvas.draw_idle()
1582           
1583        def OnAutoSet(event):
1584            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
1585            for the image so that 95% (etc.) of pixels are inside the color map limits.
1586            An equal number of pixels are dropped at the minimum and maximum levels.
1587            '''
1588            try:
1589                val = int(event.GetEventObject().GetStringSelection()[:-1])
1590                margin = (100-val)/2.
1591            except:
1592                margin = 0
1593                event.GetEventObject().SetSelection(0)
1594            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1595            if margin == 0:
1596                Range[1] = list(Range[0])
1597            else:
1598                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
1599                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
1600            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1601            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1602            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1603            maxSel.SetValue(sv1)
1604            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1605            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1606            minSel.SetValue(sv0)
1607            minVal.SetValue(int(Range[1][0]))
1608            maxVal.SetValue(int(Range[1][1]))
1609            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1610            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1611            if mplOld:
1612                Page.canvas.draw()
1613            else:
1614                Page.canvas.draw_idle()
1615
1616        mplv = mpl.__version__.split('.')
1617        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
1618        # Plot color scaling uses limits as below:
1619        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
1620        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
1621       
1622        maxSizer = wx.BoxSizer(wx.VERTICAL)
1623        slideSizer = wx.FlexGridSizer(2,3,5,5)
1624        slideSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Max intensity'),0,WACV)
1625        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
1626        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
1627        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
1628        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1629        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1630        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1631        maxSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv1)
1632        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],1,min=Range[0][0]+1,
1633            max=Range[0][1],OnLeave=OnNewVal)
1634        slideSizer.Add(maxVal,0,WACV)
1635        slideSizer.Add(maxSel,flag=wx.EXPAND|wx.ALL)
1636        slideSizer.AddGrowableCol(2)
1637        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
1638        slideSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Min intensity'),0,WACV)
1639        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
1640        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
1641        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
1642        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1643        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1644        minVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],0,
1645            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1646        slideSizer.Add(minVal,0,WACV)
1647        minSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv0)
1648        slideSizer.Add(minSel,flag=wx.EXPAND|wx.ALL)
1649        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
1650        maxSizer.Add(slideSizer,flag=wx.EXPAND|wx.ALL)
1651        autoSizer = wx.BoxSizer(wx.HORIZONTAL)
1652        autoSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Auto scaler '),0,WACV)
1653        scaleChoices = ("100%","99%","95%","90%","80%","?")
1654        scaleSel = wx.Choice(G2frame.dataWindow,choices=scaleChoices,size=(-1,-1))
1655        if (Range[1][0] == Range[0][0] and
1656            Range[1][1] == Range[0][1]):
1657            scaleSel.SetSelection(0)
1658        else:
1659            scaleSel.SetSelection(len(scaleChoices)-1)
1660        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
1661        autoSizer.Add(scaleSel,0,WACV)
1662        maxSizer.Add(autoSizer)
1663        return maxSizer
1664
1665    G2frame.dataWindow.ClearData()
1666    startScroll = None
1667    if G2frame.dataWindow:
1668        startScroll = G2frame.dataWindow.GetScrollPos(wx.VERTICAL) # save scroll position
1669    else:
1670        CleanupMasks(data) # posting page for 1st time; clean out anything unfinished
1671    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.MaskMenu)
1672    G2frame.Bind(wx.EVT_MENU, OnCopyMask, id=G2G.wxID_MASKCOPY)
1673    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOAD)
1674    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOADNOT)
1675    G2frame.Bind(wx.EVT_MENU, OnSaveMask, id=G2G.wxID_MASKSAVE)
1676    G2frame.Bind(wx.EVT_MENU, OnAutoSpotMask, id=G2G.wxID_FINDSPOTS)
1677    G2frame.Bind(wx.EVT_MENU, OnDeleteSpotMask, id=G2G.wxID_DELETESPOTS)
1678    G2frame.Bind(wx.EVT_MENU, ToggleSpotMaskMode, id=G2G.wxID_NEWMASKSPOT)
1679    G2frame.Bind(wx.EVT_MENU, OnNewArcMask, id=G2G.wxID_NEWMASKARC)
1680    G2frame.Bind(wx.EVT_MENU, OnNewRingMask, id=G2G.wxID_NEWMASKRING)
1681    G2frame.Bind(wx.EVT_MENU, OnNewPolyMask, id=G2G.wxID_NEWMASKPOLY)
1682    G2frame.Bind(wx.EVT_MENU, OnNewFrameMask, id=G2G.wxID_NEWMASKFRAME)
1683    if G2frame.MaskKey == 'f':
1684        G2frame.GetStatusBar().SetStatusText('Frame mask active - LB pick next point, RB close polygon',1)
1685    elif G2frame.MaskKey == 'p':
1686        G2frame.GetStatusBar().SetStatusText('Polygon mask active - LB pick next point, RB close polygon',1)
1687    elif G2frame.MaskKey == 'a':
1688        G2frame.GetStatusBar().SetStatusText('Arc mask active - LB pick arc location',1)
1689    elif G2frame.MaskKey == 'r':
1690        G2frame.GetStatusBar().SetStatusText('Ring mask active - LB pick ring location',1)
1691    else:
1692        G2frame.GetStatusBar().SetStatusText("To add mask: press a,r,s,p or f on 2D image for arc/ring/spot/polygon/frame",1)
1693    mainSizer = G2frame.dataWindow.GetSizer()
1694    mainSizer.Add((5,10),0)
1695
1696    thresh = data['Thresholds']         #min/max intensity range
1697    Spots = data['Points']               #x,y,radius in mm
1698    Rings = data['Rings']               #radius, thickness
1699    Polygons = data['Polygons']         #3+ x,y pairs
1700    if 'Frames' not in data:
1701        data['Frames'] = []
1702    frame = data['Frames']             #3+ x,y pairs
1703    Arcs = data['Arcs']                 #radius, start/end azimuth, thickness
1704
1705    ######################################################################
1706    CId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')
1707    controlData = G2frame.GPXtree.GetItemPyData(CId)
1708    Range = controlData['range']
1709    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1710    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1711
1712    littleSizer = wx.FlexGridSizer(0,3,0,5)
1713    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper limits '),0,WACV)
1714    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][0]),style=wx.TE_READONLY)
1715    littleSizer.Add(Text,0,WACV)
1716    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1717    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][1]),style=wx.TE_READONLY)
1718    littleSizer.Add(Text,0,WACV)
1719    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1720    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper thresholds '),0,WACV)
1721    lowerThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=0,
1722        min=thresh[0][0],OnLeave=newReplot,typeHint=int)
1723    littleSizer.Add(lowerThreshold,0,WACV)
1724    upperThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=1,
1725        max=thresh[0][1],OnLeave=newReplot,typeHint=int)
1726    littleSizer.Add(upperThreshold,0,WACV)
1727    mainSizer.Add(littleSizer,0,)
1728    if len(Spots):
1729        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Spot masks(on plot LB drag to move, shift-LB drag to resize, RB to delete)')
1730        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1731        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1732        colTypes = [wg.GRID_VALUE_STRING,wg.GRID_VALUE_FLOAT+':10,2',wg.GRID_VALUE_BOOL]
1733        colIds = ['position, mm','diameter, mm','Delete?']
1734        rowIds = [str(i) for i in range(len(Spots))]
1735        table = [['%.2f,%.2f'%(item[0],item[1]),item[2],False] for item in Spots]
1736        SpotTable = G2G.Table(table,rowLabels=rowIds,colLabels=colIds,types=colTypes)
1737        SpotGrid = G2G.GSGrid(G2frame.dataWindow)
1738        SpotGrid.SetTable(SpotTable,True)
1739        SpotGrid.AutoSizeColumns(True)
1740        SpotGrid.SetColSize(1,80)
1741        for r in range(len(Spots)):
1742            SpotGrid.SetCellStyle(r,0,VERY_LIGHT_GREY,True)
1743        if 'phoenix' in wx.version():
1744            SpotGrid.Bind(wg.EVT_GRID_CELL_CHANGED, OnSpotChange)
1745        else:
1746            SpotGrid.Bind(wg.EVT_GRID_CELL_CHANGE, OnSpotChange)
1747        mainSizer.Add(SpotGrid,0,)
1748    if Rings:
1749        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Ring masks')
1750        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1751        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1752        littleSizer = wx.FlexGridSizer(0,3,0,5)
1753        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' 2-theta,deg'),0,WACV)
1754        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
1755        littleSizer.Add((5,0),0)
1756        for i in range(len(Rings)):
1757            if Rings[i]:
1758                ringText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (Rings[i][0])),
1759                    style=wx.TE_READONLY)
1760                ringText.SetBackgroundColour(VERY_LIGHT_GREY)
1761                ringText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1762                littleSizer.Add(ringText,0,WACV)
1763                ringThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Rings[i],key=1,
1764                    min=0.001,max=1.,OnLeave=Replot,nDig=[8,3])
1765                littleSizer.Add(ringThick,0,WACV)
1766                ringDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1767                    locationcode='Delete+Rings+'+str(i),handler=onDeleteMask)
1768                littleSizer.Add(ringDelete,0,WACV)
1769        mainSizer.Add(littleSizer,0,)
1770    if Arcs:
1771        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Arc 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,4,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=' azimuth, deg'),0,WACV)
1777        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
1778        littleSizer.Add((5,0),0)
1779        for i in range(len(Arcs)):
1780            if Arcs[i]:
1781                tth,azimuth,thick = Arcs[i]
1782                arcText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (tth)),
1783                    style=wx.TE_READONLY)
1784                arcText.SetBackgroundColour(VERY_LIGHT_GREY)
1785                arcText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1786                littleSizer.Add(arcText,0,WACV)
1787                azmText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%d,%d" % (azimuth[0],azimuth[1])),
1788                    style=wx.TE_READONLY)
1789                azmText.SetBackgroundColour(VERY_LIGHT_GREY)
1790                azmText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1791                littleSizer.Add(azmText,0,WACV)
1792                arcThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Arcs[i],key=2,
1793                    min=0.001,max=20.,OnLeave=Replot,nDig=[8,3])
1794                littleSizer.Add(arcThick,0,WACV)
1795                arcDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1796                    locationcode='Delete+Arcs+'+str(i),handler=onDeleteMask)
1797                littleSizer.Add(arcDelete,0,WACV)
1798        mainSizer.Add(littleSizer,0,)
1799    if Polygons:
1800        lbl = wx.StaticText(parent=G2frame.dataWindow,
1801            label=' Polygon masks (on plot LB vertex drag to move, RB vertex drag to insert)')
1802        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1803        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1804        littleSizer = wx.FlexGridSizer(0,2,0,5)
1805        for i in range(len(Polygons)):
1806            if Polygons[i]:
1807                polyList = []
1808                for x,y in Polygons[i]:
1809                    polyList.append("%.2f, %.2f"%(x,y))
1810                polyText = wx.ComboBox(G2frame.dataWindow,value=polyList[0],choices=polyList,style=wx.CB_READONLY)
1811                littleSizer.Add(polyText,0,WACV)
1812                polyDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1813                    locationcode='Delete+Polygons+'+str(i),handler=onDeleteMask)
1814                littleSizer.Add(polyDelete,0,WACV)
1815        mainSizer.Add(littleSizer,0,)
1816    if frame:
1817        lbl = wx.StaticText(parent=G2frame.dataWindow,
1818            label=' Frame mask (on plot LB vertex drag to move, RB vertex drag to insert)')
1819        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1820        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1821        littleSizer = wx.FlexGridSizer(0,2,0,5)
1822        frameList = []
1823        for x,y in frame:
1824            frameList.append("%.2f, %.2f"%(x,y))
1825        frameText = wx.ComboBox(G2frame.dataWindow,value=frameList[0],choices=frameList,style=wx.CB_READONLY)
1826        littleSizer.Add(frameText,0,WACV)
1827        frameDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1828            locationcode='Delete+Frame',handler=onDeleteFrame)
1829        littleSizer.Add(frameDelete,0,WACV)
1830        mainSizer.Add(littleSizer,0,)
1831    G2frame.dataWindow.SetDataSize()
1832    if startScroll: # reset scroll to saved position
1833        G2frame.dataWindow.Scroll(0,startScroll) # set to saved scroll position
1834        wx.Yield()
1835
1836################################################################################
1837##### Stress/Strain
1838################################################################################
1839
1840def UpdateStressStrain(G2frame,data):
1841    '''Shows and handles the controls on the "Stress/Strain"
1842    data tree entry
1843    '''
1844   
1845    def OnAppendDzero(event):
1846        data['d-zero'].append({'Dset':1.0,'Dcalc':0.0,'pixLimit':10,'cutoff':1.0,
1847            'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0],'fixDset':False})
1848        UpdateStressStrain(G2frame,data)
1849       
1850    def OnUpdateDzero(event):
1851        for item in data['d-zero']:
1852            if item['Dcalc']:   #skip unrefined ones
1853                item['Dset'] = item['Dcalc']
1854        UpdateStressStrain(G2frame,data)
1855           
1856    def OnCopyStrSta(event):
1857        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1858        if len(Names) == 1:
1859            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
1860            return
1861        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
1862        Names.pop(Names.index(Source))
1863        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy stress/strain controls','Copy controls from '+Source+' to:',Names)
1864        try:
1865            if dlg.ShowModal() == wx.ID_OK:
1866                items = dlg.GetSelections()
1867                for item in items:
1868                    name = Names[item]
1869                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1870                    CId = G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain')
1871                    oldData = G2frame.GPXtree.GetItemPyData(CId)
1872                    load = oldData.get('Sample load',0.0)
1873                    Data = copy.deepcopy(data)
1874                    Data['Sample load'] = load
1875                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'),Data)
1876        finally:
1877            dlg.Destroy()
1878            G2frame.GPXtree.SelectItem(G2frame.PickId)
1879
1880    def OnLoadStrSta(event):
1881        pth = G2G.GetImportPath(G2frame)
1882        if not pth: pth = '.'
1883        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1884            'image control files (*.strsta)|*.strsta',wx.FD_OPEN)
1885        try:
1886            if dlg.ShowModal() == wx.ID_OK:
1887                filename = dlg.GetPath()
1888                File = open(filename,'r')
1889                S = File.read()
1890                data = eval(S)
1891                Controls = G2frame.GPXtree.GetItemPyData(
1892                    G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1893                G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1894                UpdateStressStrain(G2frame,data)
1895                G2plt.PlotExposedImage(G2frame,event=event)
1896                G2plt.PlotStrain(G2frame,data,newPlot=True)
1897                File.close()
1898        finally:
1899            dlg.Destroy()
1900
1901    def OnSaveStrSta(event):
1902        pth = G2G.GetExportPath(G2frame)
1903        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1904            'image control files (*.strsta)|*.strsta',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1905        try:
1906            if dlg.ShowModal() == wx.ID_OK:
1907                filename = dlg.GetPath()
1908                File = open(filename,'w')
1909                keys = ['Type','Sample phi','Sample z','Sample load']
1910                keys2 = ['Dset','Dcalc','pixLimit','cutoff','Emat','fixDset']
1911                File.write('{\n\t')
1912                for key in keys:
1913                    if key in 'Type':
1914                        File.write("'"+key+"':'"+data[key]+"',")
1915                    else:
1916                        File.write("'"+key+"':"+str(data[key])+',')
1917                File.write('\n\t'+"'d-zero':[\n")
1918                for data2 in data['d-zero']:
1919                    File.write('\t\t{')
1920                    for key in keys2:
1921                        File.write("'"+key+"':"+str(data2[key])+',')
1922                    File.write("'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]]},\n")
1923                File.write('\t]\n}')
1924                File.close()
1925        finally:
1926            dlg.Destroy()
1927           
1928    def OnStrStaSample(event):
1929        filename = ''
1930        pth = G2G.GetImportPath(G2frame)
1931        if not pth: pth = '.'
1932        dlg = wx.FileDialog(G2frame, 'Choose multihistogram metadata text file', pth, '', 
1933            'metadata file (*.*)|*.*',wx.FD_OPEN)
1934        try:
1935            if dlg.ShowModal() == wx.ID_OK:
1936                filename = dlg.GetPath()
1937                File = open(filename,'r')
1938                S = File.readline()
1939                newItems = []
1940                itemNames = []
1941                Comments = []
1942                while S:
1943                    if S[0] == '#':
1944                        Comments.append(S)
1945                        S = File.readline()
1946                        continue
1947                    S = S.replace(',',' ').replace('\t',' ')
1948                    Stuff = S[:-1].split()
1949                    itemNames.append(Stuff[0])
1950                    newItems.append(Stuff[1:])
1951                    S = File.readline()               
1952                File.close()
1953        finally:
1954            dlg.Destroy()
1955        if not filename:
1956            G2frame.ErrorDialog('Nothing to do','No file selected')
1957            return
1958        dataDict = dict(zip(itemNames,newItems))
1959        ifany = False
1960        Names = [' ','Sample phi','Sample z','Sample load']
1961        dlg = G2G.G2ColumnIDDialog( G2frame,' Choose multihistogram metadata columns:',
1962            'Select columns',Comments,Names,np.array(newItems).T)
1963        try:
1964            if dlg.ShowModal() == wx.ID_OK:
1965                colNames,newData = dlg.GetSelection()
1966                dataDict = dict(zip(itemNames,newData.T))
1967                for item in colNames:
1968                    if item != ' ':
1969                        ifany = True
1970        finally:
1971            dlg.Destroy()
1972        if not ifany:
1973            G2frame.ErrorDialog('Nothing to do','No columns identified')
1974            return
1975        histList = []
1976        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)       
1977        while item:
1978            name = G2frame.GPXtree.GetItemText(item)
1979            if name.startswith('IMG'):
1980                histList.append(name)
1981            item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
1982        colIds = {}
1983        for i,name in enumerate(colNames):
1984            if name != ' ':
1985                colIds[name] = i
1986        for hist in histList:
1987            name = hist.split()[1]  #this is file name
1988            if name in dataDict:
1989                newItems = {}
1990                for item in colIds:
1991                    newItems[item] = float(dataDict[name][colIds[item]])
1992                Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,hist)
1993                stsrData = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'))
1994                stsrData.update(newItems)       
1995        UpdateStressStrain(G2frame,data)       
1996   
1997    def OnPlotStrSta(event):
1998        Controls = G2frame.GPXtree.GetItemPyData(
1999            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
2000        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
2001        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
2002        G2plt.PlotExposedImage(G2frame,event=event)
2003        G2frame.G2plotNB.Delete('Ring Intensities')
2004        G2plt.PlotXY(G2frame,RingInt,labelX='Azimuth',
2005            labelY='MRD',newPlot=True,Title='Ring Intensities',
2006            names=Names,lines=True)
2007       
2008    def OnSaveStrRing(event):
2009        Controls = G2frame.GPXtree.GetItemPyData(
2010            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
2011        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
2012        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
2013        pth = G2G.GetExportPath(G2frame)
2014        dlg = wx.FileDialog(G2frame, 'Choose strain ring intensity file', pth, '', 
2015            'ring intensity file (*.txt)|*.txt',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2016        try:
2017            if dlg.ShowModal() == wx.ID_OK:
2018                filename = dlg.GetPath()
2019                File = open(filename,'w')
2020                for i,name in enumerate(Names):
2021                    File.write('%s%s\n'%(' Ring intensity for ',name))
2022                    File.write('%12s %12s\n'%('Azimuth','RMD'))
2023                    for item in RingInt[i].T:
2024                        File.write(' %12.3f %12.3f\n'%(item[0],item[1]))
2025                    File.write('\n')
2026                File.close()
2027        finally:
2028            dlg.Destroy()
2029               
2030               
2031    def OnFitStrSta(event):
2032        Controls = G2frame.GPXtree.GetItemPyData(
2033            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
2034        G2img.FitStrSta(G2frame.ImageZ,data,Controls)
2035        print ('Strain fitting finished')
2036        UpdateStressStrain(G2frame,data)
2037        G2plt.PlotExposedImage(G2frame,event=event)
2038        G2plt.PlotStrain(G2frame,data,newPlot=True)
2039       
2040    def OnFitAllStrSta(event):
2041        choices = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
2042        od = {'label_1':'Copy to next','value_1':False,'label_2':'Reverse order','value_2':False}
2043        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices,extraOpts=od)
2044        names = []
2045        if dlg.ShowModal() == wx.ID_OK:
2046            for sel in dlg.GetSelections():
2047                names.append(choices[sel])
2048            Id =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Sequential strain fit results')
2049            if Id:
2050                SeqResult = G2frame.GPXtree.GetItemPyData(Id)
2051            else:
2052                SeqResult = {}
2053                Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text='Sequential strain fit results')
2054            SeqResult.update({'SeqPseudoVars':{},'SeqParFitEqList':[]})
2055        else:
2056            dlg.Destroy()
2057            return
2058        dlg.Destroy()
2059        if not names:
2060            return
2061        dlg = wx.ProgressDialog('Sequential IMG Strain fit','Data set name = '+names[0],len(names), 
2062            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT)         
2063        wx.BeginBusyCursor()
2064        goodnames = []
2065        if od['value_2']:
2066            names.reverse()
2067        try:
2068            varyList = []
2069            variables = []
2070            for i,name in enumerate(names):
2071                print (' Sequential strain fit for '+name)
2072                GoOn = dlg.Update(i,newmsg='Data set name = '+name)[0]
2073                if not GoOn:
2074                    break
2075                sId =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
2076                G2frame.Image = sId
2077                Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Image Controls'))
2078                StaCtrls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Stress/Strain'))
2079                if not len(StaCtrls['d-zero']):
2080                    continue
2081                goodnames.append(name)
2082                Npix,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(sId)
2083                image = GetImageZ(G2frame,Controls)
2084                sig = []
2085                if i and od['value_1']:
2086                    for j,ring in enumerate(StaCtrls['d-zero']):
2087                        ring['Emat'] = copy.copy(variables[4*j:4*j+3])
2088                varyList = []
2089                variables = []
2090                #get results from previous & put in StaCtrls
2091                G2img.FitStrSta(image,StaCtrls,Controls)
2092                G2plt.PlotStrain(G2frame,StaCtrls,newPlot=True)
2093                parmDict = {'Sample load':StaCtrls['Sample load'],}
2094                varyNames = ['e11','e12','e22']
2095                Nvar = 5*len(StaCtrls['d-zero'])
2096                coVar = np.zeros((Nvar,Nvar))
2097                for j,item in enumerate(StaCtrls['d-zero']):
2098                    variables += item['Emat']
2099                    sig += item['Esig']
2100                    varylist = ['%d;%s'%(j,Name) for Name in varyNames]
2101                    varyList += varylist
2102                    parmDict.update(dict(zip(varylist,item['Emat'])))
2103                    parmDict['%d;Dcalc'%(j)] = item['Dcalc']
2104                    variables.append(1.e6*(item['Dcalc']/item['Dset']-1.))
2105                    varyList.append('%d;h-mstrain'%(j))
2106                    sig.append(0)
2107                    parmDict['%d;Ivar'%(j)] = item['Ivar']
2108                    variables.append(item['Ivar'])
2109                    varyList.append('%d;Ivar'%(j))
2110                    sig.append(0)
2111                    j4 = j*5
2112                    coVar[j4:j4+3,j4:j4+3] = item['covMat']
2113                SeqResult[name] = {'variables':variables,'varyList':varyList,'sig':sig,'Rvals':[],
2114                    'covMatrix':coVar,'title':name,'parmDict':parmDict}
2115            else:
2116                SeqResult['histNames'] = goodnames
2117                dlg.Destroy()
2118                print (' ***** Sequential strain refinement successful *****')
2119        finally:
2120            wx.EndBusyCursor()   
2121        SeqResult['histNames'] = choices
2122        G2frame.GPXtree.SetItemPyData(Id,SeqResult)
2123        G2frame.GPXtree.SelectItem(Id)
2124        print ('All images fitted')
2125       
2126    def SamSizer():
2127       
2128        def OnStrainType(event):
2129            data['Type'] = strType.GetValue()
2130       
2131        samSizer = wx.FlexGridSizer(0,4,0,5)
2132        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Strain type: '),0,WACV)
2133        strType = wx.ComboBox(G2frame.dataWindow,value=data['Type'],choices=['True','Conventional'],
2134            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2135        strType.SetValue(data['Type'])
2136        strType.Bind(wx.EVT_COMBOBOX, OnStrainType)
2137        samSizer.Add(strType,0,WACV)
2138        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample phi: '),0,WACV)
2139        samPhi = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample phi',nDig=(10,3),typeHint=float,min=-360.,max=360.)
2140        samSizer.Add(samPhi,0,WACV)
2141        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample delta-z(mm): '),0,WACV)
2142        samZ = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample z',nDig=(10,3),typeHint=float)
2143        samSizer.Add(samZ,0,WACV)
2144        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample load(MPa): '),0,WACV)
2145        samLoad = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample load',
2146                nDig=[8,3],typeHint=float,)
2147        samSizer.Add(samLoad,0,WACV)
2148
2149        return samSizer
2150       
2151    def DzeroSizer():
2152               
2153        def OnStrainChange(event):
2154#            print (event)
2155            r,c = event.GetRow(),event.GetCol()
2156            if c == 0:
2157                data['d-zero'][r]['Dset'] = min(max(float(StrainGrid.GetCellValue(r,c)),0.25),20.)
2158            elif c == 1:
2159                data['d-zero'][r]['fixDset'] = bool(StrainGrid.GetCellValue(r,c))
2160            elif c == 3:
2161                data['d-zero'][r]['cutoff'] = min(max(float(StrainGrid.GetCellValue(r,c)),0.5),20.)
2162            elif c == 4:
2163                data['d-zero'][r]['pixLimit'] = int(StrainGrid.GetCellValue(r,c))
2164            elif c == 8:
2165                del data['d-zero'][r]
2166                StrainTable.DeleteRow(r)
2167                wx.CallAfter(UpdateStressStrain,G2frame,data)
2168            G2plt.PlotExposedImage(G2frame,event=event)
2169            G2plt.PlotStrain(G2frame,data,newPlot=True)
2170           
2171        def OnSetCol(event):
2172            c = event.GetCol()
2173            if c == 1:
2174                StrainGrid.ClearSelection()
2175                StrainGrid.SelectCol(c,True)
2176                choice = ['Y - set all','N - set none',]
2177                dlg = wx.SingleChoiceDialog(G2frame,'Select option for '+StrainGrid.GetColLabelValue(c-1),
2178                    'Strain controls',choice)
2179                dlg.CenterOnParent()
2180                if dlg.ShowModal() == wx.ID_OK:
2181                    sel = dlg.GetSelection()
2182                    if sel == 0:
2183                        for row in range(StrainGrid.GetNumberRows()): data['d-zero'][row]['fixDset']=True
2184                    else:
2185                        for row in range(StrainGrid.GetNumberRows()): data['d-zero'][row]['fixDset']=False
2186                wx.CallAfter(UpdateStressStrain,G2frame,data)
2187           
2188        colTypes = [wg.GRID_VALUE_FLOAT+':10,5',wg.GRID_VALUE_BOOL,wg.GRID_VALUE_FLOAT+':10,5',wg.GRID_VALUE_FLOAT+':10,1',
2189            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',]
2190        colIds = ['d-zero','Poisson\n mean?','d-zero ave','I/Ib','nPix','e11','e12','e22','Delete?','h-mustrain','Ivar']
2191        rowIds = [str(i) for i in range(len(data['d-zero']))]
2192        table = [[item['Dset'],item.get('fixDset',False),item['Dcalc'],item['cutoff'],item['pixLimit'], 
2193            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']]
2194        StrainTable = G2G.Table(table,rowLabels=rowIds,colLabels=colIds,types=colTypes)
2195        StrainGrid = G2G.GSGrid(G2frame.dataWindow)
2196        StrainGrid.SetTable(StrainTable,True)
2197        StrainGrid.AutoSizeColumns(True)
2198        for r in range(len(data['d-zero'])):
2199            StrainGrid.SetCellStyle(r,2,VERY_LIGHT_GREY,True)
2200            StrainGrid.SetCellStyle(r,5,VERY_LIGHT_GREY,True)
2201            StrainGrid.SetCellStyle(r,6,VERY_LIGHT_GREY,True)
2202            StrainGrid.SetCellStyle(r,7,VERY_LIGHT_GREY,True)
2203            StrainGrid.SetCellStyle(r,9,VERY_LIGHT_GREY,True)
2204            StrainGrid.SetCellStyle(r,10,VERY_LIGHT_GREY,True)
2205        if 'phoenix' in wx.version():
2206            StrainGrid.Bind(wg.EVT_GRID_CELL_CHANGED, OnStrainChange)
2207        else:
2208            StrainGrid.Bind(wg.EVT_GRID_CELL_CHANGE, OnStrainChange)
2209        StrainGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnSetCol)
2210        return StrainGrid
2211# patches
2212    if 'Sample load' not in data:
2213        data['Sample load'] = 0.0
2214# end patches
2215   
2216    G2frame.dataWindow.ClearData()
2217    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.StrStaMenu)
2218    G2frame.Bind(wx.EVT_MENU, OnAppendDzero, id=G2G.wxID_APPENDDZERO)
2219    G2frame.Bind(wx.EVT_MENU, OnUpdateDzero, id=G2G.wxID_UPDATEDZERO)
2220    G2frame.Bind(wx.EVT_MENU, OnFitStrSta, id=G2G.wxID_STRSTAFIT)
2221    G2frame.Bind(wx.EVT_MENU, OnPlotStrSta, id=G2G.wxID_STRSTAPLOT) 
2222    G2frame.Bind(wx.EVT_MENU, OnSaveStrRing, id=G2G.wxID_STRRINGSAVE) 
2223    G2frame.Bind(wx.EVT_MENU, OnFitAllStrSta, id=G2G.wxID_STRSTAALLFIT)
2224    G2frame.Bind(wx.EVT_MENU, OnCopyStrSta, id=G2G.wxID_STRSTACOPY)
2225    G2frame.Bind(wx.EVT_MENU, OnLoadStrSta, id=G2G.wxID_STRSTALOAD)
2226    G2frame.Bind(wx.EVT_MENU, OnSaveStrSta, id=G2G.wxID_STRSTASAVE)
2227    G2frame.Bind(wx.EVT_MENU, OnStrStaSample, id=G2G.wxID_STRSTSAMPLE)       
2228    if G2frame.StrainKey == 'a':    #probably doesn't happen
2229        G2frame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value',1)
2230    else:
2231        G2frame.GetStatusBar().SetStatusText("To add strain data: On 2D Powder Image, key a:add ring",1)
2232       
2233    mainSizer = G2frame.dataWindow.GetSizer()
2234    mainSizer.Add((5,10),0)
2235    mainSizer.Add(SamSizer())
2236    mainSizer.Add((5,10),0)
2237    mainSizer.Add(DzeroSizer())
2238    G2frame.dataWindow.SetDataSize()
2239   
2240###########################################################################
2241# Autointegration
2242###########################################################################
2243def ReadMask(filename):
2244    'Read a mask (.immask) file'
2245    File = open(filename,'r')
2246    save = {}
2247    S = File.readline()
2248    while S:
2249        if S[0] == '#':
2250            S = File.readline()
2251            continue
2252        [key,val] = S.strip().split(':',1)
2253        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
2254            save[key] = eval(val)
2255        S = File.readline()
2256    File.close()
2257    CleanupMasks(save)
2258    return save
2259
2260def ReadControls(filename):
2261    'read an image controls (.imctrl) file'
2262    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2263            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2264            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2265            'PolaVal','SampleAbs','dark image','background image']
2266    File = open(filename,'r')
2267    save = {}
2268    S = File.readline()
2269    while S:
2270        if S[0] == '#':
2271            S = File.readline()
2272            continue
2273        [key,val] = S.strip().split(':',1)
2274        if key in ['type','calibrant','binType','SampleShape',]:    #strings
2275            save[key] = val
2276        elif key in ['rotation']:
2277            save[key] = float(val)
2278        elif key in ['center',]:
2279            if ',' in val:
2280                save[key] = eval(val)
2281            else:
2282                vals = val.strip('[] ').split()
2283                save[key] = [float(vals[0]),float(vals[1])] 
2284        elif key in cntlList:
2285            save[key] = eval(val)
2286        S = File.readline()
2287    File.close()
2288    return save
2289
2290def Read_imctrl(imctrl_file):
2291    '''Read an image control file and record control parms into a dict, with some simple
2292    type conversions
2293    '''
2294    save = {'filename':imctrl_file}
2295    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
2296    if os.path.exists(immask_file):
2297        save['maskfile'] = immask_file
2298    else:
2299        save['maskfile'] = '(none)'
2300    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2301                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2302                        'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2303                        'PolaVal','SampleAbs','dark image','background image','setdist']
2304    File = open(imctrl_file,'r')
2305    fullIntegrate = False
2306    try:
2307        S = File.readline()
2308        while S:
2309            if S[0] == '#':
2310                S = File.readline()
2311                continue
2312            [key,val] = S.strip().split(':',1)
2313            if val.find(':') != -1:
2314                #print 'rejecting ',key,val
2315                S = File.readline()
2316                continue
2317            if key in ['type','calibrant','binType','SampleShape',]:    #strings
2318                save[key] = val
2319            elif key == 'rotation':
2320                save[key] = float(val)
2321            elif key == 'fullIntegrate':
2322                fullIntegrate = eval(val)
2323            elif key == 'LRazimuth':
2324                vals = eval(val)
2325                save['LRazimuth_min'] = float(vals[0])
2326                save['LRazimuth_max'] = float(vals[1])
2327            elif key == 'IOtth':
2328                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
2329            elif key == 'center':
2330                if ',' in val:
2331                    vals = eval(val)
2332                else:
2333                    vals = val.strip('[] ').split()
2334                    vals = [float(vals[0]),float(vals[1])] 
2335                save['center_x'],save['center_y'] = vals[0:2]
2336            elif key in cntlList:
2337                save[key] = eval(val)
2338            S = File.readline()
2339    finally:
2340        File.close()
2341        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0.,360.
2342    return save
2343   
2344class AutoIntFrame(wx.Frame):
2345    '''Creates a wx.Frame window for the Image AutoIntegration.
2346    The intent is that this will be used as a non-modal dialog window.
2347   
2348    Implements a Start button that morphs into a pause and resume button.
2349    This button starts a processing loop that is repeated every
2350    :meth:`PollTime` seconds.
2351
2352    :param wx.Frame G2frame: main GSAS-II frame
2353    :param float PollTime: frequency in seconds to repeat calling the
2354      processing loop. (Default is 30.0 seconds.)
2355    '''
2356    def __init__(self,G2frame,PollTime=30.0):
2357        def OnStart(event):
2358            '''Called when the start button is pressed. Changes button label
2359            to Pause. When Pause is pressed the label changes to Resume.
2360            When either Start or Resume is pressed, the processing loop
2361            is started. When Pause is pressed, the loop is stopped.
2362            '''
2363            # check inputs for errors before starting
2364            #err = ''
2365            #if not any([self.params[fmt] for fmt in self.fmtlist]):
2366            #    err += '\nPlease select at least one output format\n'
2367            #if err:
2368            #    G2G.G2MessageBox(self,err)
2369            #    return
2370            self.Pause = False
2371            # change button label
2372            if self.btnstart.GetLabel() != 'Pause':
2373                self.btnstart.SetLabel('Pause')
2374                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
2375                if self.timer.IsRunning(): self.timer.Stop()
2376                self.PreventReEntryTimer = False
2377                if self.StartLoop():
2378                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
2379                    return
2380                self.OnTimerLoop(None) # run once immediately
2381                if not self.Pause:
2382                    # no pause, so start timer to check for new files
2383                    self.timer.Start(int(1000*PollTime),oneShot=False)
2384                    return
2385            # we will get to this point if Paused
2386            self.OnPause()
2387           
2388        def OnReset(event):
2389            '''Called when Reset button is pressed. This stops the
2390            processing loop and resets the list of integrated files so
2391            all images can be reintegrated.
2392            '''
2393            self.btnstart.SetLabel('Restart')
2394            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
2395            if self.timer.IsRunning(): self.timer.Stop()
2396            self.Reset = True
2397            self.Pause = True
2398           
2399        def OnQuit(event):
2400            '''Stop the processing loop and close the Frame
2401            '''
2402            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
2403            wx.CallAfter(self.Destroy)
2404           
2405        def OnBrowse(event):
2406            '''Responds when the Browse button is pressed to load a file.
2407            The routine determines which button was pressed and gets the
2408            appropriate file type and loads it into the appropriate place
2409            in the dict.
2410            '''
2411            if btn3 == event.GetEventObject():
2412                dlg = wx.DirDialog(
2413                    self, 'Select directory for output files',
2414                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
2415                dlg.CenterOnParent()
2416                try:
2417                    if dlg.ShowModal() == wx.ID_OK:
2418                        self.params['outdir'] = dlg.GetPath()
2419                        fInp3.SetValue(self.params['outdir'])
2420                finally:
2421                    dlg.Destroy()
2422                return
2423            elif btn4 == event.GetEventObject():
2424                msg = ''
2425                pth = G2G.GetExportPath(G2frame)
2426                dlg = wx.FileDialog(
2427                    self, 'Select a PDF parameter file',
2428                    pth, self.params['pdfprm'], 
2429                    "PDF controls file (*.pdfprm)|*.pdfprm",
2430                    wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
2431                dlg.CenterOnParent()
2432                try:
2433                    if dlg.ShowModal() == wx.ID_OK:
2434                        self.params['pdfprm'] = dlg.GetPath()
2435                        fInp4.SetValue(self.params['pdfprm'])
2436                        scanPDFprm()
2437                        msg = self.checkPDFprm(True)
2438                finally:
2439                    dlg.Destroy()
2440                if 'Error' in msg:
2441                    print(msg)
2442                    lbl = 'PDFPRM error'
2443                else:
2444                    msg = 'Information from file {}\n\n{}'.format(self.params['pdfprm'],msg)
2445                    lbl = 'PDFPRM information'
2446                G2G.G2MessageBox(self,msg,lbl)
2447                return
2448               
2449        def OnRadioSelect(event):
2450            '''Respond to a radiobutton selection and when in table
2451            mode, get distance-dependent parameters from user.
2452            '''
2453            self.Evaluator = None
2454            if self.useTable.GetValue():
2455                dlg = None
2456                try:
2457                    dlg = IntegParmTable(self) # create the dialog
2458                    dlg.CenterOnParent()
2459                    if dlg.ShowModal() == wx.ID_OK:
2460                        self.ImgTblParms = dlg.parms
2461                        self.IMfileList = dlg.IMfileList
2462                        self.Evaluator = DefineEvaluator(dlg)
2463                        self.params['Mode'] = 'table'
2464                        self.editTable.Enable(True)
2465                    else:
2466                        self.useActive.SetValue(True)
2467                finally:
2468                    if dlg: dlg.Destroy()
2469            elif self.useActive.GetValue():
2470                self.params['Mode'] = 'active'
2471                self.imageBase = G2frame.Image
2472                self.useActive.SetLabel("Active Image: "+
2473                        G2frame.GPXtree.GetItemText(self.imageBase))
2474                self.editTable.Enable(False)
2475            else:
2476                print('unexpected mode in OnRadioSelect')
2477
2478        def OnEditTable(event):
2479            '''Called to edit the distance-dependent parameter look-up table.
2480            Should be called only when table is defined and active.
2481            '''
2482            dlg = None
2483            try:
2484                dlg = IntegParmTable(self,self.ImgTblParms,self.IMfileList)
2485                dlg.CenterOnParent()
2486                if dlg.ShowModal() == wx.ID_OK:
2487                    self.ImgTblParms = dlg.parms
2488                    self.IMfileList = dlg.IMfileList
2489                    self.Evaluator = DefineEvaluator(dlg)
2490                    self.params['Mode'] = 'table'
2491                    self.editTable.Enable(True)
2492                else:
2493                    self.useActive.SetValue(True)
2494                    self.params['Mode'] = 'active'
2495                    self.imageBase = G2frame.Image
2496                    self.useActive.SetLabel("Active Image: "+
2497                            G2frame.GPXtree.GetItemText(self.imageBase))
2498                    self.editTable.Enable(False)
2499            finally:
2500                if dlg: dlg.Destroy()
2501               
2502        def showPDFctrls(event):
2503            '''Called to show or hide AutoPDF widgets. Note that fInp4 must be included in the
2504            sizer layout with .Show(True) before .Show(False) will work properly.
2505            '''
2506            fInp4.Enable(self.params['ComputePDF'])
2507            fInp4.Show(self.params['ComputePDF'])
2508            fInp4a.Enable(self.params['ComputePDF'])
2509            btn4.Enable(self.params['ComputePDF'])
2510            cOpt.Enable(self.params['ComputePDF'])
2511            if self.params['ComputePDF']:
2512                lbl4.SetForegroundColour("black")
2513                lbl4a.SetForegroundColour("black")
2514            else:
2515                lbl4.SetForegroundColour("gray")
2516                lbl4a.SetForegroundColour("gray")
2517                                   
2518        def scanPDFprm(**kw):
2519            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2520            fInp4._IndicateValidity()
2521           
2522        def OnAutoScale(event):
2523            self.AutoScale = autoscale.GetValue()
2524           
2525        def OnAutoScaleName(event):
2526            self.AutoScaleName = scalename.GetValue()
2527            self.Scale[0] = self.AutoScales[self.AutoScaleName]
2528            scaleval.SetValue(self.Scale[0])
2529               
2530        ##################################################
2531        # beginning of __init__ processing
2532        ##################################################
2533        self.G2frame = G2frame
2534        self.ImgTblParms = None
2535        self.IMfileList = None
2536        self.Evaluator = None
2537        self.params = {}
2538        self.Reset = False
2539        self.Pause = False
2540        self.PreventReEntryShowMatch = False
2541        self.PreventReEntryTimer = False
2542        self.params['IMGfile'] = ''
2543        self.params['MaskFile'] = ''
2544        self.params['IgnoreMask'] = True
2545        self.fmtlist = G2IO.ExportPowderList(G2frame)
2546        self.timer = wx.Timer()
2547        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2548        self.imageBase = G2frame.Image
2549        self.params['ComputePDF'] = False
2550        self.params['pdfDmax'] = 0.0
2551        self.params['pdfprm'] = ''
2552        self.params['optPDF'] = True
2553        self.pdfControls = {}
2554        self.AutoScale = False
2555        self.Scale = [1.0,]
2556
2557        G2frame.GPXtree.GetSelection()
2558        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(self.imageBase) 
2559        self.params['readdir'],fileroot = os.path.split(imagefile)
2560        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2561        self.params['outdir'] = os.path.abspath(self.params['readdir'])
2562        Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2563            G2frame,self.imageBase, 'Comments'))
2564        DefaultAutoScaleNames = GSASIIpath.GetConfigValue('Autoscale_ParmNames')
2565        self.AutoScaleName = GSASIIpath.GetConfigValue('DefaultAutoScale')
2566        self.AutoScales = {}
2567        if DefaultAutoScaleNames is not None:
2568            for comment in Comments:
2569                if '=' in comment:
2570                    name,val = comment.split('=',1) 
2571                    if name in DefaultAutoScaleNames:
2572                        try:
2573                            self.AutoScales[name] = float(val)
2574                            if name == self.AutoScaleName:
2575                                self.Scale[0] = float(val)
2576                        except ValueError:
2577                            continue
2578        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2579                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2580        self.Status = self.CreateStatusBar()
2581        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2582        mnpnl = wx.Panel(self)
2583        mnsizer = wx.BoxSizer(wx.VERTICAL)
2584        # box for integration controls & masks input
2585        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2586        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2587        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2588        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2589                            style = wx.RB_GROUP)
2590        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2591        self.useActive.SetLabel("Active Image: "+
2592                                G2frame.GPXtree.GetItemText(self.imageBase))
2593        lblsizr.Add(self.useActive,0,wx.EXPAND,1)
2594        self.useActive.SetValue(True)
2595        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2596        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2597        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2598        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2599        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2600        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2601        self.editTable.Enable(False)
2602        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2603        # bind button and deactivate be default
2604        lblsizr.Add(minisizer)
2605        mnsizer.Add(lblsizr,0,wx.EXPAND,0)
2606
2607        # file filter stuff
2608        sizer = wx.BoxSizer(wx.HORIZONTAL)
2609        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Read images from '))
2610        self.readDir = G2G.ValidatedTxtCtrl(mnpnl,self.params,'readdir',
2611                            OnLeave=self.ShowMatchingFiles,size=(200,-1))
2612        sizer.Add(self.readDir,1,wx.EXPAND,1)
2613        btn3 = wx.Button(mnpnl, wx.ID_ANY, "Browse")
2614        btn3.Bind(wx.EVT_BUTTON, self.SetSourceDir)
2615        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2616        mnsizer.Add(sizer,0,wx.EXPAND,0)
2617        # not yet implemented
2618        sizer = wx.BoxSizer(wx.HORIZONTAL)
2619        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Keep read images in tree '))
2620        self.params['keepReadImage'] = True
2621        keepImage = G2G.G2CheckBox(mnpnl,'',self.params,'keepReadImage')
2622        sizer.Add(keepImage)
2623        keepImage.Enable(False)
2624        sizer.Add((-1,-1),1,wx.EXPAND,1)
2625        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'  Image filter'))
2626        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',
2627                                        OnLeave=self.ShowMatchingFiles)
2628        sizer.Add(flterInp)
2629        mnsizer.Add(sizer,0,wx.EXPAND,0)
2630       
2631        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2632        mnsizer.Add(self.ListBox,1,wx.EXPAND,1)
2633        self.ShowMatchingFiles(self.params['filter'])
2634
2635        # box for output selections
2636        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2637        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2638        sizer = wx.BoxSizer(wx.HORIZONTAL)
2639        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2640        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2641        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2642        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2643        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2644        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2645        lblsizr.Add(sizer,0,wx.EXPAND)
2646        sizer = wx.BoxSizer(wx.HORIZONTAL)
2647        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s):'))
2648        for dfmt in self.fmtlist:
2649            sizer.Add((6,2)) # add a bit of extra space
2650            fmt = dfmt[1:]
2651            self.params[fmt] = False
2652            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2653            sizer.Add(btn)
2654        lblsizr.Add(sizer)
2655        sizer = wx.BoxSizer(wx.HORIZONTAL)
2656        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2657        self.params['SeparateDir'] = False
2658        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2659        lblsizr.Add(sizer)
2660        if self.AutoScales:
2661            sizer = wx.BoxSizer(wx.HORIZONTAL)
2662            autoscale = wx.CheckBox(mnpnl,label='Do autoscaling with:')
2663            autoscale.Bind(wx.EVT_CHECKBOX,OnAutoScale)
2664            sizer.Add(autoscale,0,WACV)
2665            scalename = wx.ComboBox(mnpnl,value=self.AutoScaleName,choices=list(self.AutoScales.keys()),
2666                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2667            scalename.Bind(wx.EVT_COMBOBOX,OnAutoScaleName)
2668            sizer.Add(scalename,0,WACV)
2669            sizer.Add(wx.StaticText(mnpnl,label=' to '),0,WACV)
2670            scaleval = G2G.ValidatedTxtCtrl(mnpnl,self.Scale,0,nDig=(10,2),min=1.)
2671            sizer.Add(scaleval,0,WACV)
2672            lblsizr.Add(sizer,0)
2673        #ToDO: Autonormalize, parm name?, scaling value?
2674        sizer = wx.BoxSizer(wx.HORIZONTAL)
2675        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2676        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2677        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2678        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2679        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2680        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2681        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2682        sizer.Add(cOpt)
2683        lblsizr.Add(sizer,0)
2684        sizer = wx.BoxSizer(wx.HORIZONTAL)
2685        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2686        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2687        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2688                                     OnLeave=scanPDFprm)
2689        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2690        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2691        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2692        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2693        lblsizr.Add(sizer,0,wx.EXPAND)
2694        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2695        # buttons on bottom
2696        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2697        sizer = wx.BoxSizer(wx.HORIZONTAL)
2698        sizer.Add((20,-1))
2699        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2700        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2701        sizer.Add(self.btnstart)
2702        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2703        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2704        sizer.Add(self.btnreset)
2705        sizer.Add((20,-1),wx.EXPAND,1)
2706        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2707        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2708        sizer.Add(self.btnclose)
2709        sizer.Add((20,-1))
2710        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2711        # finish up window
2712        mnpnl.SetSizer(mnsizer)
2713        OnRadioSelect(None) # disable widgets
2714        mnsizer.Fit(self)
2715        self.CenterOnParent()
2716        self.Show()
2717        showPDFctrls(None)
2718
2719    def checkPDFprm(self,ShowContents=False):
2720        '''Read in the PDF (.pdfprm) parameter file and check for problems.
2721        If ShowContents is True, a formatted text version of some of the file
2722        contents is returned. If errors are found, the return string will contain
2723        the string "Error:" at least once.
2724        '''
2725        self.pdfControls = {}
2726        msg = ''
2727        File = None
2728        try:
2729            File = open(self.params['pdfprm'],'r')
2730            S = File.readline()
2731            while S:
2732                if '#' in S:
2733                    S = File.readline()
2734                    continue
2735                key,val = S.split(':',1)
2736                try:
2737                    self.pdfControls[key] = eval(val)
2738                except:
2739                    self.pdfControls[key] = val
2740                S = File.readline()
2741        except Exception as err:
2742            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
2743            if GSASIIpath.GetConfigValue('debug'):
2744                print('DBG_'+msg)
2745                print('DBG_'+err)
2746            self.pdfControls = {}
2747            return msg
2748        finally:
2749            if File: File.close()
2750        formula = ''
2751        for el in self.pdfControls['ElList']:
2752            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
2753            if formula: formula += ' '
2754            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
2755        if not formula:
2756            msg += 'Error: no chemical formula in file'
2757        for key in ['Sample Bkg.','Container','Container Bkg.']:
2758            if key not in self.pdfControls:
2759                if msg: msg += '\n'
2760                msg += 'Error: missing key in self.pdfControls: '+key
2761                continue
2762        if msg or not ShowContents: return msg  # stop on error
2763        msg += 'Default formula: '+formula+'\n'
2764        for key in ['Sample Bkg.','Container','Container Bkg.']:
2765            name = self.pdfControls[key]['Name']
2766            mult = self.pdfControls[key].get('Mult',0.0)
2767            if not name: continue
2768            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
2769            if not G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,name):
2770                msg += ' *** missing ***'
2771        return msg
2772   
2773    def SetSourceDir(self,event):
2774        '''Use a dialog to get a directory for image files
2775        '''
2776        dlg = wx.DirDialog(self, 'Select directory for image files',
2777                        self.params['readdir'],wx.DD_DEFAULT_STYLE)
2778        dlg.CenterOnParent()
2779        try:
2780            if dlg.ShowModal() == wx.ID_OK:
2781                self.params['readdir'] = dlg.GetPath()
2782            self.readDir.SetValue(self.params['readdir'])
2783            self.ShowMatchingFiles(None)
2784        finally:
2785            dlg.Destroy()
2786        return
2787       
2788    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2789        '''Find and show images in the tree and the image files matching the image
2790        file directory (self.params['readdir']) and the image file filter
2791        (self.params['filter']) and add this information to the GUI list box
2792        '''
2793        G2frame = self.G2frame
2794        if invalid: return
2795        msg = ''
2796        if self.PreventReEntryShowMatch: return
2797        self.PreventReEntryShowMatch = True
2798        imageFileList = []
2799        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
2800            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
2801            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
2802            if imagefile not in imageFileList: imageFileList.append(imagefile)
2803            if img not in G2frame.IntegratedList:
2804                if msg: msg += '\n'
2805                msg += '  ' + img
2806        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
2807        msg1 = ""
2808        try:
2809            if os.path.exists(self.params['readdir']): 
2810                imageList = sorted(
2811                    glob.glob(os.path.join(self.params['readdir'],self.params['filter'])))
2812                if not imageList:
2813                    msg1 = 'Warning: No files match search string '+os.path.join(self.params['readdir'],self.params['filter'])
2814                else:
2815                    for fil in imageList:
2816                        if fil not in imageFileList: msg1 += '\n  '+fil
2817                    if msg1:
2818                        msg += 'Files to integrate from '+os.path.join(self.params['readdir'],self.params['filter'])+msg1
2819                    else:
2820                        msg += 'No files found to read in '+self.params['readdir']
2821            else:
2822                msg += 'Warning: does not exist: '+self.params['readdir']
2823        except IndexError:
2824            msg += 'Error searching for files named '+os.path.join(self.params['readdir'],self.params['filter'])
2825        self.ListBox.Clear()
2826        self.ListBox.AppendItems(msg.split('\n'))
2827        self.PreventReEntryShowMatch = False
2828        return
2829       
2830    def OnPause(self):
2831        '''Respond to Pause, changes text on button/Status line, if needed
2832        Stops timer
2833        self.Pause should already be True
2834        '''
2835        if self.timer.IsRunning(): self.timer.Stop()
2836        if self.btnstart.GetLabel() == 'Restart':
2837            return
2838        if self.btnstart.GetLabel() != 'Resume':
2839            print('\nPausing autointegration\n')
2840            self.btnstart.SetLabel('Resume')
2841            self.Status.SetStatusText(
2842                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
2843        self.Pause = True
2844           
2845    def IntegrateImage(self,img,useTA=None,useMask=None):
2846        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
2847        are placed in G2frame.IntgOutList
2848        '''
2849        G2frame = self.G2frame
2850        imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
2851        G2frame.Image = imgId
2852        G2frame.PickId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls')
2853        # do integration
2854        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
2855        if self.AutoScale:
2856            Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2857                G2frame,imgId, 'Comments'))
2858            for comment in Comments:
2859                if '=' in comment:
2860                    name,val = comment.split('=',1) 
2861                    if name == self.AutoScaleName:
2862                        val = float(val)
2863                        if val > 0.:
2864                            Scale = self.Scale[0]/val
2865                        break
2866        masks = G2frame.GPXtree.GetItemPyData(
2867            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Masks'))
2868        data = G2frame.GPXtree.GetItemPyData(G2frame.PickId)
2869        # simulate a Image Controls press, since that is where the
2870        # integration is hidden
2871        UpdateImageControls(G2frame,data,masks,useTA=useTA,useMask=useMask,IntegrateOnly=True)
2872        G2frame.IntegratedList.append(img) # note this as integrated
2873        # split name and control number
2874        try:
2875            s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
2876        except AttributeError: # not sure why, but sometimes imagefile is a list here (should not be)!
2877            s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile[0])[0])[1])
2878        namepre = s[0]
2879        if len(s) > 1:
2880            namenum = s[1]
2881        else:
2882            namenum = ''
2883        for Id in G2frame.IntgOutList: # loop over newly created PWDR entry(ies)
2884            # save the created PWDR tree names so that a reset can delete them
2885            G2frame.Image = Id
2886            treename = G2frame.GPXtree.GetItemText(Id)
2887            G2frame.AutointPWDRnames.append(treename)
2888            # write out the images in the selected formats
2889            Sdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Sample Parameters'))
2890            if self.AutoScale:
2891                print ('Rescale by %.4f'%(Scale))
2892                y,w = G2frame.GPXtree.GetItemPyData(Id)[1][1:3]
2893                y *= Scale
2894                w /= Scale**2
2895            # determine the name for the current file
2896            fileroot = namepre
2897            if len(G2frame.IntgOutList) > 1:
2898                fileroot += "_AZM"
2899                if 'Azimuth' in Sdata:
2900                    fileroot += str(int(10*Sdata['Azimuth']))
2901                fileroot += "_" 
2902            fileroot += namenum
2903            # loop over selected formats
2904            for dfmt in self.fmtlist:
2905                if not self.params[dfmt[1:]]: continue
2906                if self.params['SeparateDir']:
2907                    subdir = dfmt[1:]
2908                else:
2909                    subdir = ''
2910                fil = os.path.join(self.params['outdir'],subdir,fileroot)
2911                G2IO.ExportPowder(G2frame,treename,fil+'.x',dfmt) # dummy extension (.x) is replaced before write)
2912               
2913    def EnableButtons(self,flag):
2914        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
2915        '''
2916        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
2917        # on windows
2918        if sys.platform != "win32":
2919            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
2920        self.btnstart.SetLabel('Pause')
2921        wx.Yield()
2922               
2923    def ResetFromTable(self,dist):
2924        '''Sets integration parameters based on values from
2925        the lookup table
2926        '''
2927        #dist = self.controlsDict['distance']
2928        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
2929        if GSASIIpath.GetConfigValue('debug'):
2930            print ('DBG_interpolated values: ',interpDict)
2931        self.ImageControls = ReadControls(imgctrl)
2932        self.ImageControls.update(interpDict)
2933        self.ImageControls['showLines'] = True
2934        self.ImageControls['ring'] = []
2935        self.ImageControls['rings'] = []
2936        self.ImageControls['ellipses'] = []
2937        self.ImageControls['setDefault'] = False
2938        for i in 'range','size','GonioAngles':
2939            if i in self.ImageControls:
2940                del self.ImageControls[i]
2941        # load copy of Image Masks
2942        if immask:
2943            self.ImageMasks = ReadMask(immask)
2944            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
2945                del self.ImageMasks['Thresholds']
2946        else:
2947            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
2948       
2949    def StartLoop(self):
2950        '''Prepare to start autointegration timer loop.
2951        Save current Image params for use in future integrations
2952        also label the window so users understand what is being used
2953        '''
2954        print('\nStarting new autointegration\n')
2955        G2frame = self.G2frame
2956        # show current IMG base
2957        if self.params['Mode'] != 'table':
2958            self.useActive.SetLabel("Active Image: "+
2959                                    G2frame.GPXtree.GetItemText(self.imageBase))
2960            # load copy of Image Controls from current image and clean up
2961            # items that should not be copied
2962            self.ImageControls = copy.deepcopy(
2963                G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2964                    G2frame,self.imageBase, 'Image Controls')))
2965            self.ImageControls['showLines'] = True
2966            self.ImageControls['ring'] = []
2967            self.ImageControls['rings'] = []
2968            self.ImageControls['ellipses'] = []
2969            self.ImageControls['setDefault'] = False
2970            del self.ImageControls['range']
2971            del self.ImageControls['size']
2972            del self.ImageControls['GonioAngles']
2973            # load copy of Image Masks, keep thresholds
2974            self.ImageMasks = copy.deepcopy(
2975                G2frame.GPXtree.GetItemPyData(
2976                    G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks')))
2977            self.Thresholds = self.ImageMasks['Thresholds'][:]
2978            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
2979                del self.ImageMasks['Thresholds']   
2980        # make sure all output directories exist
2981        if self.params['SeparateDir']:
2982            for dfmt in self.fmtlist:
2983                if not self.params[dfmt[1:]]: continue
2984                dir = os.path.join(self.params['outdir'],dfmt[1:])
2985                if not os.path.exists(dir): os.makedirs(dir)
2986        else:
2987            if not os.path.exists(self.params['outdir']):
2988                os.makedirs(self.params['outdir'])
2989        if self.Reset: # special things to do after Reset has been pressed
2990            self.G2frame.IntegratedList = []
2991           
2992            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
2993                for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
2994                    # update controls from master
2995                    controlsDict = G2frame.GPXtree.GetItemPyData(
2996                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Image Controls'))
2997                    controlsDict.update(self.ImageControls)
2998                    # update masks from master
2999                    ImageMasks = G2frame.GPXtree.GetItemPyData(
3000                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks'))
3001                    ImageMasks.update(self.ImageMasks)
3002            # delete all PWDR items created after last Start was pressed
3003            idlist = []
3004            item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
3005            while item:
3006                itemName = G2frame.GPXtree.GetItemText(item)
3007                if itemName in G2frame.AutointPWDRnames:
3008                    idlist.append(item)
3009                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
3010            for item in idlist:
3011                G2frame.GPXtree.Delete(item)
3012            wx.Yield()
3013            self.Reset = False
3014        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
3015        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
3016        # check that AutoPDF input is OK, offer chance to use alternate PWDRs if referenced ones
3017        # are not present
3018        if self.params['ComputePDF']:
3019            msg = self.checkPDFprm()
3020            if 'Error:' in msg:
3021                print(msg)
3022                return True
3023            fileList = []
3024            Id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
3025            while Id:
3026                name = G2frame.GPXtree.GetItemText(Id)
3027                if name.startswith('PWDR '): fileList.append(name)
3028                Id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
3029            if not fileList:
3030                print(msg)
3031                print('No PWDR entries to select')
3032                return True
3033            for key in ['Sample Bkg.','Container','Container Bkg.']:
3034                name = self.pdfControls[key]['Name']
3035                if not name: continue
3036                if not G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name):
3037                    indx = G2G.ItemSelector(fileList, self, header='Select PWDR item',
3038                                    title='Select a PWDR tree item for '+key+'\n(or cancel to quit)')
3039                    if indx is None:
3040                        print('No PWDR entry selected for '+key)
3041                        return True
3042                    self.pdfControls[key]['Name'] = fileList[indx]
3043        return False
3044               
3045    def OnTimerLoop(self,event):
3046        '''A method that is called every :meth:`PollTime` seconds that is
3047        used to check for new files and process them. Integrates new images.
3048        Also optionally sets up and computes PDF.
3049        This is called only after the "Start" button is pressed (then its label reads "Pause").
3050        '''
3051        def AutoIntegrateImage(imgId,useTA=None,useMask=None):
3052            '''Integrates an image that has been read into the data tree and updates the
3053            AutoInt window.
3054            '''
3055            img = G2frame.GPXtree.GetItemText(imgId)
3056            controlsDict = G2frame.GPXtree.GetItemPyData(
3057                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3058            ImageMasks = G2frame.GPXtree.GetItemPyData(
3059                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Masks'))
3060            if self.params['Mode'] == 'table': # look up parameter values from table
3061                useTA = None        #force remake of x,y-->2th,azm map
3062                self.ResetFromTable(controlsDict['setdist'])
3063            # update controls from master
3064            controlsDict.update(self.ImageControls)
3065            # update masks from master w/o Thresholds
3066            ImageMasks.update(self.ImageMasks)
3067            self.EnableButtons(False)
3068            try:
3069                self.IntegrateImage(img,useTA=useTA,useMask=useMask)
3070            finally:
3071                self.EnableButtons(True)
3072            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
3073            wx.Yield()
3074            self.ShowMatchingFiles(self.params['filter'])
3075            wx.Yield()
3076           
3077        def AutoComputePDF(imgId):
3078            '''Computes a PDF for a PWDR data tree tree item
3079            '''
3080            for pwdr in G2frame.AutointPWDRnames[:]:
3081                if not pwdr.startswith('PWDR '): continue
3082                if pwdr in G2frame.AutointPDFnames: continue
3083                PWid = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,pwdr)
3084                controlsDict = G2frame.GPXtree.GetItemPyData(
3085                    G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3086                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
3087                    print('Skipping PDF for '+pwdr+' due to detector position')
3088                    continue
3089                # Setup PDF
3090                Data = G2frame.GPXtree.GetItemPyData(PWid)[1]
3091                pwdrMin = np.min(Data[1])
3092                Parms = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3093                    G2frame,PWid,'Instrument Parameters'))[0]
3094                fullLimits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3095                    G2frame,PWid,'Limits'))[0]
3096                if 'C' in Parms['Type'][0]:
3097                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
3098                else:
3099                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
3100                Qlimits = [0.9*qMax,qMax]
3101
3102                item = pwdr
3103                Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3104                    G2frame,imgId, 'Comments'))
3105                ElList = {}
3106                sumnum = 1.0
3107                for item in Comments:           #grab chemical formula from Comments, if there
3108                    if 'formula' in item[:15].lower():
3109                        formula = item.split('=')[1].split()
3110                        elems = formula[::2]
3111                        nums = formula[1::2]
3112                        formula = zip(elems,nums)
3113                        sumnum = 0.
3114                        for [elem,num] in formula:
3115                            ElData = G2elem.GetElInfo(elem,Parms)
3116                            ElData['FormulaNo'] = float(num)
3117                            sumnum += float(num)
3118                            ElList[elem] = ElData
3119                PDFnames = G2gd.GetGPXtreeDataNames(G2frame,['PDF ',])
3120                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
3121                if not PDFid: continue
3122                PDFdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3123                    G2frame,PDFid, 'PDF Controls'))
3124                PDFdata.update(copy.deepcopy(self.pdfControls))
3125                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
3126                PDFdata['Sample']['Name'] = pwdr
3127                # compute PDF
3128                wx.Yield()
3129                G2pdG.computePDF(G2frame,PDFdata)
3130                wx.Yield()
3131                G2frame.PatternId = PDFid
3132                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3133                if self.params['optPDF']:
3134                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
3135                    wx.Yield()
3136                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3137                G2frame.AutointPDFnames.append(pwdr)
3138                # save names of PDF entry to be deleted later if needed
3139                G2frame.AutointPWDRnames.append(G2frame.GPXtree.GetItemText(PDFid))
3140           
3141        G2frame = self.G2frame
3142        try:
3143            self.currImageList = sorted(
3144                glob.glob(os.path.join(self.params['readdir'],self.params['filter'])))
3145            self.ShowMatchingFiles(self.params['filter'])
3146        except IndexError:
3147            self.currImageList = []
3148            return
3149
3150        if self.PreventReEntryTimer: return
3151        self.PreventReEntryTimer = True
3152        imageFileList = []
3153        # integrate the images that have already been read in, but
3154        # have not yet been processed           
3155        oldData = {'tilt':0.,'distance':0.,'rotation':0.,'center':[0.,0.],'DetDepth':0.,'azmthOff':0.}
3156        oldMhash = 0
3157        if 'useTA' not in dir(self):    #initial definition; reuse if after Resume
3158            self.useTA = None
3159            self.useMask = None
3160        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
3161            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3162            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3163            # Create a list of image files that have been read in
3164            if imagefile not in imageFileList: imageFileList.append(imagefile)
3165            # skip if already integrated
3166            if img in G2frame.IntegratedList: continue
3167            Data = G2frame.GPXtree.GetItemPyData(
3168                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3169            sameTA = True
3170            for item in ['tilt','distance','rotation','center','DetDepth','azmthOff']:
3171                if Data[item] != oldData[item]:
3172                    sameTA = False
3173            if not sameTA:
3174                t0 = time.time()
3175                self.useTA = G2img.MakeUseTA(Data,blkSize)
3176                print(' Use new image controls; xy->th,azm mtime: %.3f'%(time.time()-t0))
3177            Mask = G2frame.GPXtree.GetItemPyData(
3178                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Masks'))
3179            Mhash = copy.deepcopy(Mask)
3180            Mhash.pop('Thresholds')
3181            Mhash = hash(str(Mhash))
3182            if  Mhash != oldMhash:
3183                t0 = time.time()
3184                self.useMask = G2img.MakeUseMask(Data,Mask,blkSize)
3185                print(' Use new mask; make mask time: %.3f'%(time.time()-t0))
3186                oldMhash = Mhash
3187            AutoIntegrateImage(imgId,self.useTA,self.useMask)
3188            oldData = Data
3189            if self.pdfControls: AutoComputePDF(imgId)
3190            self.Pause |= G2frame.PauseIntegration
3191            if self.Pause:
3192                self.OnPause()
3193                self.PreventReEntryTimer = False
3194                self.Raise()
3195                return
3196
3197        # loop over image files matching glob, reading in any new ones
3198        if self.useTA is None or self.useMask is None:
3199            print('Integration will not be fast; there is no beginning image controls')     #TODO: work this out??
3200        for newImage in self.currImageList:
3201            if newImage in imageFileList or self.Pause: continue # already read?
3202            for imgId in G2IO.ReadImages(G2frame,newImage):
3203                AutoIntegrateImage(imgId,self.useTA,self.useMask)
3204                if self.pdfControls: AutoComputePDF(imgId)
3205                self.Pause |= G2frame.PauseIntegration
3206                if self.Pause:
3207                    self.OnPause()
3208                    self.PreventReEntryTimer = False
3209                    self.Raise()
3210                    return
3211        if GSASIIpath.GetConfigValue('debug'):
3212            import datetime
3213            print ("DBG_Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3214        self.PreventReEntryTimer = False
3215        self.Raise()
3216
3217def DefineEvaluator(dlg):
3218    '''Creates a function that provides interpolated values for a given distance value
3219    '''
3220    def Evaluator(dist):
3221        '''Interpolate image parameters for a supplied distance value
3222
3223        :param float dist: distance to use for interpolation
3224        :returns: a list with 3 items:
3225
3226          * a dict with interpolated parameter values,
3227          * the closest imctrl and
3228          * the closest maskfile (or None)
3229        '''           
3230        x = np.array([float(i) for i in parms[0]])
3231        closest = abs(x-dist).argmin()
3232        D = {'setdist':dist}
3233        imctfile = IMfileList[closest]
3234        if parms[-1][closest].lower() != '(none)':
3235            maskfile = parms[-1][closest]
3236        else:
3237            maskfile = None
3238        for c in range(1,cols-1):
3239            lbl = ParmList[c]
3240            if lbl in nonInterpVars:
3241                if lbl in ['outChannels',]:
3242                    D[lbl] = int(float(parms[c][closest]))
3243                else:
3244                    D[lbl] = float(parms[c][closest])
3245            else:
3246                y = np.array([float(i) for i in parms[c]])
3247                D[lbl] = np.interp(dist,x,y)
3248        # full integration when angular range is 0
3249        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3250        # conversion for paired values
3251        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3252            r = a.split('_')[0]
3253            D[r] = [D[a],D[b]]
3254            if r in ['LRazimuth',]:
3255                D[r] = [int(D[a]),int(D[b])]
3256            del D[a]
3257            del D[b]
3258        return D,imctfile,maskfile
3259    # save local copies of values needed in Evaluator
3260    parms = dlg.ReadImageParmTable()
3261    IMfileList = dlg.IMfileList
3262    cols = dlg.list.GetColumnCount()
3263    ParmList = dlg.ParmList
3264    nonInterpVars = dlg.nonInterpVars
3265    return Evaluator
3266
3267class IntegParmTable(wx.Dialog):
3268    '''Creates a dialog window with a table of integration parameters.
3269    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3270    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3271    creates a dictionary with interpolated parameter values.
3272    '''
3273    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3274            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3275            'maskfile',
3276            )
3277    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3278                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3279    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3280            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3281            'Mask File',
3282            )
3283    def __init__(self,parent,parms=None,IMfileList=None,readFileList=None):
3284        self.G2frame = parent.G2frame
3285        dlg = None
3286        pth = ''
3287        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3288        if readFileList:
3289            self.parms,self.IMfileList = self.ReadFiles(readFileList)
3290        elif parms:
3291            self.parms = parms # list of values by column
3292            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3293        else:
3294            self.parms = [] # list of values by column
3295            self.IMfileList = [] # list of .imctrl file names for each entry in table
3296            files = []
3297            try:
3298                pth = G2G.GetImportPath(self.G2frame)
3299                if not pth: pth = '.'
3300                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3301                    style=wx.FD_OPEN| wx.FD_MULTIPLE,
3302                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3303                dlg.CenterOnParent()
3304                if dlg.ShowModal() == wx.ID_OK:
3305                    files = dlg.GetPaths()
3306                    self.parms,self.IMfileList = self.ReadFiles(files)
3307            finally:
3308                if dlg: dlg.Destroy()
3309            if not files:
3310                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3311                return
3312        mainSizer = wx.BoxSizer(wx.VERTICAL)
3313        self.list = ImgIntLstCtrl(self, wx.ID_ANY,style=wx.LC_REPORT| wx.BORDER_SUNKEN)
3314        mainSizer.Add(self.list,1,wx.EXPAND,1)
3315        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3316        btn = wx.Button(self, wx.ID_OK)
3317        btnsizer.Add(btn)
3318        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3319        btn.Bind(wx.EVT_BUTTON,self._onSave)
3320        btnsizer.Add(btn)
3321        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3322        btn.Bind(wx.EVT_BUTTON,self._onClose)
3323        btnsizer.Add(btn)
3324        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3325        self.SetSizer(mainSizer)
3326        self.list.FillList(self.parms)
3327       
3328    def ReadFiles(self,files):
3329        '''Reads a list of .imctrl files or a single .imtbl file
3330        '''
3331        tmpDict = {}
3332        if not files: return
3333        # option 1, a dump from a previous save
3334        if os.path.splitext(files[0])[1] == '.imtbl':
3335            fp = open(files[0],'r')
3336            S = fp.readline()
3337            while S:
3338                if S[0] != '#':
3339                    [key,val] = S[:-1].split(':',1)
3340                    tmpDict[key] = eval(val)
3341                S = fp.readline()
3342            fp.close()
3343            # delete entries where files do not exist
3344            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3345            if m1:
3346                print('\nimctrl file not found:')
3347                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3348            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3349            if m2:
3350                print('\nmask file not found')
3351                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3352            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3353            if m3:
3354                print('\nDropping entries due to negative distance: '+str(m3))
3355            m = sorted(set(m1 + m2 + m3))
3356            m.reverse()
3357            for c in m:
3358                for key in tmpDict:
3359                    del tmpDict[key][c]
3360            fileList = tmpDict.get('filenames','[]')
3361            parms = []
3362            if 'setdist' not in tmpDict:
3363                print(u'Old file, recreate before using: {}'.format(files[0]))
3364                return [[]],[]
3365            for key in self.ParmList:
3366                try:
3367                    float(tmpDict[key][0])
3368                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3369                except ValueError:
3370                    parms.append(tmpDict[key])
3371                except IndexError:
3372                    print('No valid image control entries read')
3373                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3374                    return [[]],[]
3375            return parms,fileList
3376        # option 2, read in a list of files
3377        for file in files: # read all files; place in dict by distance
3378            imgDict = Read_imctrl(file)
3379            dist = imgDict.get('setdist',imgDict['distance'])
3380            if dist is None:
3381                print('Skipping old file, redo: {}'.format(file))
3382            tmpDict[dist] = imgDict
3383        parms = [[] for key in self.ParmList]
3384        fileList = []
3385        for d in sorted(tmpDict):
3386            fileList.append(tmpDict[d].get('filename'))
3387            if d is None: continue
3388            if d < 0: continue
3389            for i,key in enumerate(self.ParmList):
3390                val = tmpDict[d].get(key)
3391                try:
3392                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3393                except:
3394                    val = str(val)
3395                parms[i].append(val)
3396        return parms,fileList
3397   
3398    def ReadImageParmTable(self):
3399        '''Reads possibly edited values from the ListCtrl table and returns a list
3400        of values for each column.
3401        '''
3402        rows = self.list.GetItemCount()
3403        cols = self.list.GetColumnCount()
3404        parms = []
3405        for c in range(cols):
3406            parms.append([])
3407            for r in range(rows):
3408                parms[c].append(self.list.GetItem(r,c).GetText())
3409        return parms
3410
3411    def _onClose(self,event):
3412        'Called when Cancel button is pressed'
3413        self.EndModal(wx.ID_CANCEL)
3414       
3415    def _onSave(self,event):
3416        'Called when save button is pressed; creates a .imtbl file'
3417        fil = ''
3418        if self.G2frame.GSASprojectfile:
3419            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3420        dir,f = os.path.split(fil)
3421        pth = G2G.GetExportPath(self.G2frame)
3422        try:
3423            dlg = wx.FileDialog(self, 'Save table data as',
3424                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3425                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3426            dlg.CenterOnParent()
3427            if dlg.ShowModal() != wx.ID_OK: return
3428            fil = dlg.GetPath()
3429            fil = os.path.splitext(fil)[0]+'.imtbl'
3430        finally:
3431            dlg.Destroy()       
3432        parms = self.ReadImageParmTable()
3433        print('Writing image parameter table as '+fil)
3434        fp = open(fil,'w')
3435        for c in range(len(parms)-1):
3436            lbl = self.ParmList[c]
3437            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3438        lbl = self.ParmList[c+1]
3439        fp.write(lbl+': '+str(parms[c+1])+'\n')
3440        lbl = 'filenames'
3441        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3442        fp.close()
3443   
3444class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3445    '''Creates a custom ListCtrl for editing Image Integration parameters
3446    '''
3447    def __init__(self, parent, ID, pos=wx.DefaultPosition,size=(1000,200),style=0):
3448        self.parent=parent
3449        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3450        listmix.ListCtrlAutoWidthMixin.__init__(self)
3451        listmix.TextEditMixin.__init__(self)
3452        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3453        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3454       
3455    def FillList(self,parms):
3456        'Places the current parms into the table'
3457        # the use of InsertStringItem and SetStringItem are depricated in 4.0 but
3458        # I am not quite sure how to replace them with InsertItem and SetItem yet.
3459        # Perhaps switch to  ULC.UltimateListCtrl?
3460        #
3461        maxint = 2**31-1
3462        self.ClearAll()
3463        self.rowlen = len(self.parent.ParmList)
3464        for i,lbl in enumerate(self.parent.HeaderList):
3465            self.InsertColumn(i, lbl)
3466        for r,d in enumerate(parms[0]):
3467            if d is None: continue
3468            if d == 'None': continue
3469            if float(d) < 0: continue
3470            index = self.InsertStringItem(maxint, d)
3471            for j in range(1,len(parms)):
3472                self.SetStringItem(index, j, parms[j][r])
3473        for i,lbl in enumerate(self.parent.ParmList):
3474            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3475
3476    def OnDouble(self,evt):
3477        'respond to a double-click'
3478        self.CloseEditor()
3479        fil = '(none)'
3480        pth = G2G.GetImportPath(self.parent.G2frame)
3481        if not pth: pth = '.'
3482        try:
3483            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3484                style=wx.FD_OPEN,wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3485            dlg.CenterOnParent()
3486            if dlg.ShowModal() == wx.ID_OK:
3487                fil = dlg.GetPath()
3488        finally:
3489            dlg.Destroy()
3490        if os.path.splitext(fil)[1] != '.imctrl':
3491            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3492            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3493        else:
3494            # insert or overwrite an instrument parameter set
3495            if not os.path.exists(fil):
3496                print('Does not exist: '+fil)
3497                return
3498            imgDict = Read_imctrl(fil)
3499            dist = imgDict['distance']
3500            parms = self.parent.ReadImageParmTable()
3501            x = np.array([float(i) for i in parms[0]])
3502            closest = abs(x-dist).argmin()
3503            closeX = x[closest]
3504            # fix IMfileList
3505            for c,lbl in enumerate(self.parent.ParmList):
3506                try:
3507                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3508                except ValueError:
3509                    vali = imgDict[lbl]
3510                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3511                    parms[c][closest] = vali
3512                elif dist > closeX: # insert after
3513                    parms[c].insert(closest+1,vali)
3514                else:
3515                    parms[c].insert(closest,vali)
3516            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3517                self.parent.IMfileList[closest] = fil
3518            elif dist > closeX: # insert after
3519                self.parent.IMfileList.insert(closest+1,fil)
3520            else:
3521                self.parent.IMfileList.insert(closest,fil)
3522            self.FillList(parms)
3523# Autointegration end
3524###########################################################################
3525
3526def testColumnMetadata(G2frame):
3527    '''Test the column-oriented metadata parsing, as implemented at 1-ID, by showing results
3528    when using a .par and .lbls pair.
3529   
3530     * Select a .par file, if more than one in selected dir.
3531     * Select the .*lbls file, if more than one matching .par file.
3532     * Parse the .lbls file, showing errors if encountered; loop until errors are fixed.
3533     * Search for an image or a line in the .par file and show the results when interpreted
3534     
3535    See :func:`GSASIIfiles.readColMetadata` for more details.
3536    '''
3537    if not GSASIIpath.GetConfigValue('Column_Metadata_directory'):
3538        G2G.G2MessageBox(G2frame,'The configuration option for I-ID Metadata is not set.\n'+
3539                         'Please use the File/Preferences menu to set Column_Metadata_directory',
3540                         'Warning')
3541        return
3542    parFiles = glob.glob(os.path.join(GSASIIpath.GetConfigValue('Column_Metadata_directory'),'*.par'))
3543    if not parFiles: 
3544        G2G.G2MessageBox(G2frame,'No .par files found in directory {}. '
3545                         .format(GSASIIpath.GetConfigValue('Column_Metadata_directory'))+
3546                         '\nThis is set by config variable Column_Metadata_directory '+
3547                         '(Set in File/Preferences menu).',
3548                         'Warning')
3549        return
3550    parList = []
3551    for parFile in parFiles:
3552        lblList = []
3553        parRoot = os.path.splitext(parFile)[0]
3554        for f in glob.glob(parRoot+'.*lbls'):
3555            if os.path.exists(f): lblList.append(f)
3556        if not len(lblList):
3557            continue
3558        parList.append(parFile)
3559    if len(parList) == 0:
3560        G2G.G2MessageBox(G2frame,'No .lbls or .EXT_lbls file found for .par file(s) in directory {}. '
3561                         .format(GSASIIpath.GetConfigValue('Column_Metadata_directory'))+
3562                         '\nThis is set by config variable Column_Metadata_directory '+
3563                         '(Set in File/Preferences menu).',
3564                         'Warning')
3565        return
3566    elif len(parList) == 1:
3567        parFile = parList[0]
3568    else:
3569        dlg = G2G.G2SingleChoiceDialog(G2frame,
3570                'More than 1 .par file found. (Better if only 1!). Choose the one to test in '+
3571                GSASIIpath.GetConfigValue('Column_Metadata_directory'),
3572                'Choose .par file', [os.path.split(i)[1] for i in parList])
3573        if dlg.ShowModal() == wx.ID_OK:
3574            parFile = parList[dlg.GetSelection()]
3575            dlg.Destroy()
3576        else:
3577            dlg.Destroy()
3578            return
3579    # got .par file; now work on .*lbls file
3580    lblList = []
3581    parRoot = os.path.splitext(parFile)[0]
3582    for f in glob.glob(parRoot+'.*lbls'):
3583        if os.path.exists(f): lblList.append(f)
3584    if not len(lblList):
3585        raise Exception('How did this happen! No .*lbls files for '+parFile)
3586    elif len(lblList) == 1:
3587        lblFile = lblList[0]
3588    else:
3589        dlg = G2G.G2SingleChoiceDialog(G2frame,
3590                'Select label file for .par file '+parFile,
3591                'Choose label file', [os.path.split(i)[1] for i in lblList])
3592        if dlg.ShowModal() == wx.ID_OK:
3593            lblFile = lblList[dlg.GetSelection()]
3594            dlg.Destroy()
3595        else:
3596            dlg.Destroy()
3597            return
3598    # parse the labels file
3599    errors = True
3600    while errors:
3601        labels,lbldict,keyCols,keyExp,errors = G2fil.readColMetadataLabels(lblFile)
3602        if errors:
3603            t = "Error reading file "+lblFile
3604            for l in errors:
3605                t += '\n'
3606                t += l
3607            t += "\n\nPlease edit the file and press OK (or Cancel to quit)"
3608            dlg = wx.MessageDialog(G2frame,message=t,
3609                caption="Read Error",style=wx.ICON_ERROR| wx.OK|wx.STAY_ON_TOP|wx.CANCEL)
3610            if dlg.ShowModal() != wx.ID_OK: return           
3611    # request a line number, read that line
3612    dlg = G2G.SingleStringDialog(G2frame,'Read what',
3613                                 'Enter a line number or an image file name (-1=last line)',
3614                                 '-1',size=(400,-1))
3615    if dlg.Show():
3616        fileorline = dlg.GetValue()
3617        dlg.Destroy()
3618    else:
3619        dlg.Destroy()
3620        return
3621    # and report the generated key pairs in metadata dict
3622    linenum = None
3623    try:
3624        linenum = int(fileorline)
3625    except:
3626        imageName = os.path.splitext(os.path.split(fileorline)[1])[0]
3627
3628    fp = open(parFile,'Ur')
3629    for iline,line in enumerate(fp):
3630        if linenum is not None:
3631            if iline == linenum:
3632                items = line.strip().split(' ')
3633                n = "Line {}".format(iline)
3634                break
3635            else:
3636                continue
3637        else:
3638            items = line.strip().split(' ')
3639            nameList = keyExp['filename'](*[items[j] for j in keyCols['filename']])
3640            if type(nameList) is str:
3641                if nameList != imageName: continue
3642                name = nameList
3643                break
3644            else:
3645                for name in nameList:
3646                    print (name,name == imageName)
3647                    if name == imageName:
3648                        n = "Image {} found in line {}".format(imageName,iline)
3649                        break # got a match
3650                else:
3651                    continue
3652                break
3653    else:
3654        if linenum is not None:
3655            n = "Line {}".format(iline)
3656        else:
3657            n = "Image {} not found. Reporting line {}".format(imageName,iline)
3658        items = line.strip().split(' ')
3659    fp.close()
3660    metadata = G2fil.evalColMetadataDicts(items,labels,lbldict,keyCols,keyExp,True)
3661    title = "Results: ("+n+")"
3662    t = ['Files: '+parFile,lblFile,' ']
3663    n = ["Named parameters:"]
3664    l = ['',"Labeled columns:"]
3665    for key in sorted(metadata):
3666        if key == "filename" or key.startswith('label_prm'): continue
3667        if key in keyCols:
3668            n += [{} = {}".format(key,metadata[key])]
3669        elif key in lbldict.values():
3670            l += [{} = {}".format(key,metadata[key])]
3671        else:
3672            t += ["** Unexpected:  {}".format(key,metadata[key])]
3673    if type(metadata['filename']) is str:
3674        l += ["","Filename: "+ metadata['filename']]
3675    else:
3676        l += ["","Filename(s): "]
3677        for i,j in enumerate(metadata['filename']):
3678            if i: l[-1] += ', '
3679            l[-1] += j
3680    t += n + l + ['','Unused columns:']
3681    usedCols = list(lbldict.keys())
3682    for i in keyCols.values(): usedCols += i
3683    for i in range(len(items)):
3684        if i in usedCols: continue
3685        t += [{}: {}".format(i,items[i])]
3686    dlg = G2G.G2SingleChoiceDialog(None,title,'Column metadata parse results',t,
3687                                   monoFont=True,filterBox=False,size=(400,600),
3688                                   style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE|wx.OK)
3689    dlg.ShowModal()
Note: See TracBrowser for help on using the repository browser.