source: trunk/GSASIIimgGUI.py @ 4104

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

new mode for combined fit of wavelength to a set of images

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