source: trunk/GSASIIimgGUI.py @ 4032

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

fix 'Resolution' in old gpx to 'GridStep?'
fix to bad logical operator in MakeFrameMask?
apply polarization pixel by pixel in integration; then put back on for azm=0 for powder diffraction
remove a wasted busycursor
add Xlines & Ylines to masks to mask whole row/columns of pixels (not quite complete)
make picktype = None for unknown picks ('?' causes crash)

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