source: trunk/GSASIIimgGUI.py @ 3173

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

ensure limits on multiple powder patterns from integration are independent
Integrate All & Auto integrate now reuse x,y --> 2th,azm maps if image controls unchanged - much faster multiple integrations

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 159.7 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2017-12-05 21:23:50 +0000 (Tue, 05 Dec 2017) $
5# $Author: vondreele $
6# $Revision: 3173 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 3173 2017-12-05 21:23:50Z 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: 3173 $")
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                self.ResetFromTable(controlsDict['setdist'])
2941            # update controls from master
2942            controlsDict.update(self.ImageControls)
2943            # update masks from master w/o Thresholds
2944            ImageMasks.update(self.ImageMasks)
2945            self.EnableButtons(False)
2946            try:
2947                self.IntegrateImage(img,useTA=useTA)
2948            finally:
2949                self.EnableButtons(True)
2950            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
2951            wx.Yield()
2952            self.ShowMatchingFiles(self.params['filter'])
2953            wx.Yield()
2954           
2955        def AutoComputePDF(imgId):
2956            '''Computes a PDF for a PWDR data tree tree item
2957            '''
2958            for pwdr in G2frame.AutointPWDRnames[:]:
2959                if not pwdr.startswith('PWDR '): continue
2960                if pwdr in G2frame.AutointPDFnames: continue
2961                PWid = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,pwdr)
2962                controlsDict = G2frame.GPXtree.GetItemPyData(
2963                    G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
2964                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
2965                    print('Skipping PDF for '+pwdr+' due to detector position')
2966                    continue
2967                # Setup PDF
2968                Data = G2frame.GPXtree.GetItemPyData(PWid)[1]
2969                pwdrMin = np.min(Data[1])
2970                Parms = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2971                    G2frame,PWid,'Instrument Parameters'))[0]
2972                fullLimits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2973                    G2frame,PWid,'Limits'))[0]
2974                if 'C' in Parms['Type'][0]:
2975                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
2976                else:
2977                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
2978                Qlimits = [0.9*qMax,qMax]
2979
2980                item = pwdr
2981                Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2982                    G2frame,imgId, 'Comments'))
2983                ElList = {}
2984                sumnum = 1.0
2985                for item in Comments:           #grab chemical formula from Comments, if there
2986                    if 'formula' in item[:15].lower():
2987                        formula = item.split('=')[1].split()
2988                        elems = formula[::2]
2989                        nums = formula[1::2]
2990                        formula = zip(elems,nums)
2991                        sumnum = 0.
2992                        for [elem,num] in formula:
2993                            ElData = G2elem.GetElInfo(elem,Parms)
2994                            ElData['FormulaNo'] = float(num)
2995                            sumnum += float(num)
2996                            ElList[elem] = ElData
2997                PDFnames = G2gd.GetGPXtreeDataNames(G2frame,['PDF ',])
2998                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
2999                if not PDFid: continue
3000                PDFdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3001                    G2frame,PDFid, 'PDF Controls'))
3002                PDFdata.update(self.pdfControls)
3003                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
3004                PDFdata['Sample']['Name'] = pwdr
3005                # compute PDF
3006                wx.Yield()
3007                G2pdG.computePDF(G2frame,PDFdata)
3008                wx.Yield()
3009                G2frame.PatternId = PDFid
3010                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3011                if self.params['optPDF']:
3012                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
3013                    wx.Yield()
3014                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3015                G2frame.AutointPDFnames.append(pwdr)
3016                # save names of PDF entry to be deleted later if needed
3017                G2frame.AutointPWDRnames.append(G2frame.GPXtree.GetItemText(PDFid))
3018           
3019        G2frame = self.G2frame
3020        try:
3021            self.currImageList = sorted(
3022                glob.glob(os.path.join(self.imagedir,self.params['filter'])))
3023            self.ShowMatchingFiles(self.params['filter'])
3024        except IndexError:
3025            self.currImageList = []
3026            return
3027
3028        if self.PreventReEntryTimer: return
3029        self.PreventReEntryTimer = True
3030        imageFileList = []
3031        # integrate the images that have already been read in, but
3032        # have not yet been processed           
3033        oldData = {'tilt':0.,'distance':0.,'rotation':0.,'center':[0.,0.],'DetDepth':0.,'azmthOff':0.}
3034        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
3035            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3036            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3037            # Create a list of image files that have been read in
3038            if imagefile not in imageFileList: imageFileList.append(imagefile)
3039            # skip if already integrated
3040            if img in G2frame.IntegratedList: continue
3041            Data = G2frame.GPXtree.GetItemPyData(
3042                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3043            same = True
3044            for item in ['tilt','distance','rotation','center','DetDepth','azmthOff']:
3045                if Data[item] != oldData[item]:
3046                    same = False
3047            if not same:
3048                print('Use new image controls')
3049                self.useTA = G2img.MakeUseTA(Data,blkSize)
3050            AutoIntegrateImage(imgId,self.useTA)
3051            oldData = Data
3052            if self.pdfControls: AutoComputePDF(imgId)
3053            self.Pause |= G2frame.PauseIntegration
3054            if self.Pause:
3055                self.OnPause()
3056                self.PreventReEntryTimer = False
3057                return
3058
3059        # loop over image files matching glob, reading in any new ones
3060        for newImage in self.currImageList:
3061            if newImage in imageFileList or self.Pause: continue # already read?
3062            for imgId in G2IO.ReadImages(G2frame,newImage):
3063                AutoIntegrateImage(imgId,self.useTA)
3064                if self.pdfControls: AutoComputePDF(imgId)
3065                self.Pause |= G2frame.PauseIntegration
3066                if self.Pause:
3067                    self.OnPause()
3068                    self.PreventReEntryTimer = False
3069                    return
3070        if GSASIIpath.GetConfigValue('debug'):
3071            import datetime
3072            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3073        self.PreventReEntryTimer = False
3074
3075def DefineEvaluator(dlg):
3076    '''Creates a function that provides interpolated values for a given distance value
3077    '''
3078    def Evaluator(dist):
3079        '''Interpolate image parameters for a supplied distance value
3080
3081        :param float dist: distance to use for interpolation
3082        :returns: a list with 3 items:
3083
3084          * a dict with parameter values,
3085          * the closest imctrl and
3086          * the closest maskfile (or None)
3087        '''           
3088        x = np.array([float(i) for i in parms[0]])
3089        closest = abs(x-dist).argmin()
3090        D = {'setdist':dist}
3091        imctfile = IMfileList[closest]
3092        if parms[-1][closest].lower() != '(none)':
3093            maskfile = parms[-1][closest]
3094        else:
3095            maskfile = None
3096        for c in range(1,cols-1):
3097            lbl = ParmList[c]
3098            if lbl in nonInterpVars:
3099                if lbl in ['outChannels',]:
3100                    D[lbl] = int(float(parms[c][closest]))
3101                else:
3102                    D[lbl] = float(parms[c][closest])
3103            else:
3104                y = np.array([float(i) for i in parms[c]])
3105                D[lbl] = np.interp(dist,x,y)
3106        # full integration when angular range is 0
3107        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3108        # conversion for paired values
3109        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3110            r = a.split('_')[0]
3111            D[r] = [D[a],D[b]]
3112            if r in ['LRazimuth',]:
3113                D[r] = [int(D[a]),int(D[b])]
3114            del D[a]
3115            del D[b]
3116        return D,imctfile,maskfile
3117    # save local copies of values needed in Evaluator
3118    parms = dlg.ReadImageParmTable()
3119    IMfileList = dlg.IMfileList
3120    cols = dlg.list.GetColumnCount()
3121    ParmList = dlg.ParmList
3122    nonInterpVars = dlg.nonInterpVars
3123    return Evaluator
3124
3125class IntegParmTable(wx.Dialog):
3126    '''Creates a dialog window with a table of integration parameters.
3127    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3128    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3129    creates a dictionary with interpolated parameter values.
3130    '''
3131    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3132            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3133            'maskfile',
3134            )
3135    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3136                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3137    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3138            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3139            'Mask File',
3140            )
3141    def __init__(self,parent,parms=None,IMfileList=None):
3142        self.G2frame = parent.G2frame
3143        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3144        if parms:
3145            self.parms = parms # list of values by column
3146            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3147        else:
3148            self.parms = [] # list of values by column
3149            self.IMfileList = [] # list of .imctrl file names for each entry in table
3150            files = []
3151            try:
3152                pth = G2G.GetImportPath(self.G2frame)
3153                if not pth: pth = '.'
3154                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3155                    style=wx.FD_OPEN| wx.FD_MULTIPLE,
3156                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3157                dlg.CenterOnParent()
3158                if dlg.ShowModal() == wx.ID_OK:
3159                    files = dlg.GetPaths()
3160                    self.parms,self.IMfileList = self.ReadFiles(files)
3161            finally:
3162                if dlg: dlg.Destroy()
3163            if not files:
3164                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3165                return
3166        mainSizer = wx.BoxSizer(wx.VERTICAL)
3167        self.list = ImgIntLstCtrl(self, wx.ID_ANY,style=wx.LC_REPORT| wx.BORDER_SUNKEN)
3168        mainSizer.Add(self.list,1,wx.EXPAND,1)
3169        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3170        btn = wx.Button(self, wx.ID_OK)
3171        btnsizer.Add(btn)
3172        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3173        btn.Bind(wx.EVT_BUTTON,self._onSave)
3174        btnsizer.Add(btn)
3175        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3176        btn.Bind(wx.EVT_BUTTON,self._onClose)
3177        btnsizer.Add(btn)
3178        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3179        self.SetSizer(mainSizer)
3180        self.list.FillList(self.parms)
3181       
3182    def ReadFiles(self,files):
3183        '''Reads a list of .imctrl files or a single .imtbl file
3184        '''
3185        tmpDict = {}
3186        if not files: return
3187        # option 1, a dump from a previous save
3188        if os.path.splitext(files[0])[1] == '.imtbl':
3189            fp = open(files[0],'r')
3190            S = fp.readline()
3191            while S:
3192                if S[0] != '#':
3193                    [key,val] = S[:-1].split(':',1)
3194                    tmpDict[key] = eval(val)
3195                S = fp.readline()
3196            fp.close()
3197            # delete entries where files do not exist
3198            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3199            if m1:
3200                print('\nimctrl file not found:')
3201                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3202            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3203            if m2:
3204                print('\nmask file not found')
3205                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3206            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3207            if m3:
3208                print('\nDropping entries due to negative distance: '+str(m3))
3209            m = sorted(set(m1 + m2 + m3))
3210            m.reverse()
3211            for c in m:
3212                for key in tmpDict:
3213                    del tmpDict[key][c]
3214            fileList = tmpDict.get('filenames','[]')
3215            parms = []
3216            if 'setdist' not in tmpDict:
3217                print(u'Old file, recreate before using: {}'.format(files[0]))
3218                return [[]],[]
3219            for key in self.ParmList:
3220                try:
3221                    float(tmpDict[key][0])
3222                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3223                except ValueError:
3224                    parms.append(tmpDict[key])
3225                except IndexError:
3226                    print('No valid image control entries read')
3227                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3228                    return [[]],[]
3229            return parms,fileList
3230        # option 2, read in a list of files
3231        for file in files: # read all files; place in dict by distance
3232            imgDict = Read_imctrl(file)
3233            dist = imgDict.get('setdist',imgDict['distance'])
3234            if dist is None:
3235                print('Skipping old file, redo: {}'.format(file))
3236            tmpDict[dist] = imgDict
3237        parms = [[] for key in self.ParmList]
3238        fileList = []
3239        for d in sorted(tmpDict):
3240            fileList.append(tmpDict[d].get('filename'))
3241            if d is None: continue
3242            if d < 0: continue
3243            for i,key in enumerate(self.ParmList):
3244                val = tmpDict[d].get(key)
3245                try:
3246                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3247                except:
3248                    val = str(val)
3249                parms[i].append(val)
3250        return parms,fileList
3251   
3252    def ReadImageParmTable(self):
3253        '''Reads possibly edited values from the ListCtrl table and returns a list
3254        of values for each column.
3255        '''
3256        rows = self.list.GetItemCount()
3257        cols = self.list.GetColumnCount()
3258        parms = []
3259        for c in range(cols):
3260            parms.append([])
3261            for r in range(rows):
3262                parms[c].append(self.list.GetItem(r,c).GetText())
3263        return parms
3264
3265    def _onClose(self,event):
3266        'Called when Cancel button is pressed'
3267        self.EndModal(wx.ID_CANCEL)
3268       
3269    def _onSave(self,event):
3270        'Called when save button is pressed; creates a .imtbl file'
3271        fil = ''
3272        if self.G2frame.GSASprojectfile:
3273            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3274        dir,f = os.path.split(fil)
3275        pth = G2G.GetExportPath(self.G2frame)
3276        try:
3277            dlg = wx.FileDialog(self, 'Save table data as',
3278                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3279                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3280            dlg.CenterOnParent()
3281            if dlg.ShowModal() != wx.ID_OK: return
3282            fil = dlg.GetPath()
3283            fil = os.path.splitext(fil)[0]+'.imtbl'
3284        finally:
3285            dlg.Destroy()       
3286        parms = self.ReadImageParmTable()
3287        print('Writing image parameter table as '+fil)
3288        fp = open(fil,'w')
3289        for c in range(len(parms)-1):
3290            lbl = self.ParmList[c]
3291            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3292        lbl = self.ParmList[c+1]
3293        fp.write(lbl+': '+str(parms[c+1])+'\n')
3294        lbl = 'filenames'
3295        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3296        fp.close()
3297   
3298class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3299    '''Creates a custom ListCtrl for editing Image Integration parameters
3300    '''
3301    def __init__(self, parent, ID, pos=wx.DefaultPosition,size=(1000,200),style=0):
3302        self.parent=parent
3303        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3304        listmix.ListCtrlAutoWidthMixin.__init__(self)
3305        listmix.TextEditMixin.__init__(self)
3306        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3307        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3308       
3309    def FillList(self,parms):
3310        'Places the current parms into the table'
3311        maxint = 2**31-1
3312        self.ClearAll()
3313        self.rowlen = len(self.parent.ParmList)
3314        for i,lbl in enumerate(self.parent.HeaderList):
3315            self.InsertColumn(i, lbl)
3316        for r,d in enumerate(parms[0]):
3317            if d is None: continue
3318            if d == 'None': continue
3319            if float(d) < 0: continue
3320            index = self.InsertStringItem(maxint, d)
3321            for j in range(1,len(parms)):
3322                self.SetStringItem(index, j, parms[j][r])
3323        for i,lbl in enumerate(self.parent.ParmList):
3324            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3325
3326    def OnDouble(self,evt):
3327        'respond to a double-click'
3328        self.CloseEditor()
3329        fil = '(none)'
3330        pth = G2G.GetImportPath(self.parent.G2frame)
3331        if not pth: pth = '.'
3332        try:
3333            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3334                style=wx.FD_OPEN,wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3335            dlg.CenterOnParent()
3336            if dlg.ShowModal() == wx.ID_OK:
3337                fil = dlg.GetPath()
3338        finally:
3339            dlg.Destroy()
3340        if os.path.splitext(fil)[1] != '.imctrl':
3341            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3342            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3343        else:
3344            # insert or overwrite an instrument parameter set
3345            if not os.path.exists(fil):
3346                print('Does not exist: '+fil)
3347                return
3348            imgDict = Read_imctrl(fil)
3349            dist = imgDict['distance']
3350            parms = self.parent.ReadImageParmTable()
3351            x = np.array([float(i) for i in parms[0]])
3352            closest = abs(x-dist).argmin()
3353            closeX = x[closest]
3354            # fix IMfileList
3355            for c,lbl in enumerate(self.parent.ParmList):
3356                try:
3357                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3358                except ValueError:
3359                    vali = imgDict[lbl]
3360                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3361                    parms[c][closest] = vali
3362                elif dist > closeX: # insert after
3363                    parms[c].insert(closest+1,vali)
3364                else:
3365                    parms[c].insert(closest,vali)
3366            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3367                self.parent.IMfileList[closest] = fil
3368            elif dist > closeX: # insert after
3369                self.parent.IMfileList.insert(closest+1,fil)
3370            else:
3371                self.parent.IMfileList.insert(closest,fil)
3372            self.FillList(parms)
3373# Autointegration end
3374###########################################################################
Note: See TracBrowser for help on using the repository browser.