source: trunk/GSASIIimgGUI.py @ 3174

Last change on this file since 3174 was 3174, checked in by vondreele, 5 years ago

force remake of x,y-->2th,azm map if table mode used for auto integration

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