source: trunk/GSASIIimgGUI.py @ 3814

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

refactor to move some IO-only routines; add initial image support to scriptable

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