source: trunk/GSASIIimgGUI.py @ 3184

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

improve spacing of AutoInt? output formats

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 162.2 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2017-12-08 21:54:39 +0000 (Fri, 08 Dec 2017) $
5# $Author: toby $
6# $Revision: 3184 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 3184 2017-12-08 21:54:39Z toby $
9########### SVN repository information ###################
10'''
11*GSASIIimgGUI: Image GUI*
12-------------------------
13
14Control image display and processing
15
16'''
17from __future__ import division, print_function
18import os
19import copy
20import glob
21import 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: 3184 $")
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        G2frame.slideSizer.GetChildren()[1].Window.SetValue(Imax)   #tricky
238        G2frame.slideSizer.GetChildren()[4].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        G2frame.slideSizer = wx.FlexGridSizer(2,3,5,5)
726        G2frame.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        G2frame.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        G2frame.slideSizer.Add(maxVal,0,WACV)
739        G2frame.slideSizer.Add(maxSel,flag=wx.EXPAND|wx.ALL)
740        G2frame.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        G2frame.slideSizer.Add(minVal,0,WACV)
751        G2frame.slideSizer.Add(minSel,flag=wx.EXPAND|wx.ALL)
752        maxSizer.Add(G2frame.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        # debug code to reload code for window on each use
1235        #import GSASIIimgGUI
1236        #reload(GSASIIimgGUI)
1237        #G2frame.autoIntFrame = GSASIIimgGUI.AutoIntFrame(G2frame,PollTime=PollTime)
1238
1239        G2frame.autoIntFrame.Bind(wx.EVT_WINDOW_DESTROY,OnDestroy) # clean up name on window close
1240    G2frame.Bind(wx.EVT_MENU, OnAutoInt, id=G2G.wxID_IMAUTOINTEG)
1241
1242    mainSizer = G2frame.dataWindow.GetSizer()
1243    mainSizer.Add((5,10),0)   
1244    mainSizer.Add(ComboSizer(),0,wx.ALIGN_LEFT)
1245    mainSizer.Add((5,5),0)
1246    Range = data['range'] # allows code to be same in Masks
1247    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1248    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1249   
1250    mainSizer.Add((5,5),0)
1251    DataSizer = wx.FlexGridSizer(0,2,5,0)
1252    DataSizer.Add(CalibCoeffSizer(),0)
1253    DataSizer.Add(IntegrateSizer(),0)       
1254    mainSizer.Add(DataSizer,0)
1255    mainSizer.Add((5,5),0)           
1256    mainSizer.Add(BackSizer(),0)
1257    mainSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Calibration controls:'),0,WACV)
1258    mainSizer.Add((5,5),0)
1259    mainSizer.Add(CalibSizer(),0,WACV)
1260    mainSizer.Add((5,5),0)
1261    mainSizer.Add(GonioSizer(),0,WACV)   
1262    G2frame.dataWindow.SetDataSize()
1263   
1264################################################################################
1265##### Masks
1266################################################################################
1267def CleanupMasks(data):
1268    '''If a mask creation is not completed, an empty mask entry is created in the
1269    masks array. This cleans them out. It is called when the masks page is first loaded
1270    and before saving them or after reading them in. This should also probably be done
1271    before they are used for integration.
1272    '''
1273    for key in ['Points','Rings','Arcs','Polygons']:
1274        data[key] = data.get(key,[])
1275        l1 = len(data[key])
1276        data[key] = [i for i in data[key] if len(i)]
1277        l2 = len(data[key])
1278        if GSASIIpath.GetConfigValue('debug') and l1 != l2:
1279            print ('Mask Cleanup: %s was %d entries, now %d'%(key,l1,l2))
1280   
1281def UpdateMasks(G2frame,data):
1282    '''Shows and handles the controls on the "Masks" data tree entry
1283    '''
1284   
1285    def OnTextMsg(event):
1286        Obj = event.GetEventObject()
1287        Obj.SetToolTipString('Drag this mask on 2D Powder Image with mouse to change ')
1288
1289    def Replot(*args,**kwargs):
1290        wx.CallAfter(G2plt.PlotExposedImage,G2frame)
1291
1292    def newReplot(*args,**kwargs):
1293        wx.CallAfter(G2plt.PlotExposedImage,G2frame,newPlot=True)
1294
1295    def onDeleteMask(event):
1296        Obj = event.GetEventObject()
1297        typ = Obj.locationcode.split('+')[1]
1298        num = int(Obj.locationcode.split('+')[2])
1299        del(data[typ][num])
1300        wx.CallAfter(UpdateMasks,G2frame,data)
1301        G2plt.PlotExposedImage(G2frame,event=event)
1302       
1303    def OnSpotChange(event):
1304        r,c = event.GetRow(),event.GetCol()
1305        if c == 2:
1306            del Spots[r]
1307            SpotTable.DeleteRow(r)
1308        else:
1309            Spots[r][2] = float(SpotGrid.GetCellValue(r,c))
1310        SpotGrid.ForceRefresh()
1311        wx.CallAfter(UpdateMasks,G2frame,data)
1312        G2plt.PlotExposedImage(G2frame,event=event)
1313        event.Skip()
1314
1315    def onDeleteFrame(event):
1316        data['Frames'] = []
1317        wx.CallAfter(UpdateMasks,G2frame,data)
1318        G2plt.PlotExposedImage(G2frame,event=event)
1319
1320    def OnCopyMask(event):
1321        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1322        if len(Names) == 1:
1323            G2frame.ErrorDialog('Nothing to copy masks to','There must be more than one "IMG" pattern')
1324            return
1325        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
1326        Names.pop(Names.index(Source))
1327        Data = copy.deepcopy(data)
1328        Thresh = Data.pop('Thresholds')     # & remove it as well
1329        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy mask data','Copy masks from '+Source+' to:',Names)
1330        try:
1331            if dlg.ShowModal() == wx.ID_OK:
1332                items = dlg.GetSelections()
1333                for item in items:
1334                    name = Names[item]
1335                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1336                    MId = G2gd.GetGPXtreeItemId(G2frame,Id,'Masks')
1337                    Mask = G2frame.GPXtree.GetItemPyData(MId)
1338                    Mask.update(copy.deepcopy(Data))
1339                    Mask['Thresholds'][1][0] = Thresh[1][0]  #copy only lower threshold
1340                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Masks'),Mask)
1341        finally:
1342            dlg.Destroy()
1343
1344    def OnSaveMask(event):
1345        CleanupMasks(data)
1346        pth = G2G.GetExportPath(G2frame)
1347        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1348            'image mask files (*.immask)|*.immask',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1349        try:
1350            if dlg.ShowModal() == wx.ID_OK:
1351                filename = dlg.GetPath()
1352                filename = os.path.splitext(filename)[0]+'.immask'
1353                File = open(filename,'w')
1354                keys = ['Points','Rings','Arcs','Polygons','Frames','Thresholds']
1355                for key in keys:
1356                    File.write(key+':'+str(data[key])+'\n')
1357                File.close()
1358        finally:
1359            dlg.Destroy()
1360       
1361    def OnLoadMask(event):
1362        if event.Id == G2G.wxID_MASKLOADNOT:
1363            ignoreThreshold = True
1364        else:
1365            ignoreThreshold = False
1366        pth = G2G.GetImportPath(G2frame)
1367        if not pth: pth = '.'
1368        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1369            'image mask files (*.immask)|*.immask',wx.FD_OPEN)
1370        try:
1371            if dlg.ShowModal() == wx.ID_OK:
1372                filename = dlg.GetPath()
1373                File = open(filename,'r')
1374                save = {}
1375                oldThreshold = data['Thresholds'][0]
1376                S = File.readline()
1377                while S:
1378                    if S[0] == '#':
1379                        S = File.readline()
1380                        continue
1381                    [key,val] = S.strip().split(':',1)
1382                    if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
1383                        if ignoreThreshold and key == 'Thresholds':
1384                            S = File.readline() 
1385                            continue
1386                        save[key] = eval(val)
1387                        if key == 'Thresholds':
1388                            save[key][0] = oldThreshold
1389                            save[key][1][1] = min(oldThreshold[1],save[key][1][1])
1390                    S = File.readline()
1391                File.close()
1392                data.update(save)
1393                CleanupMasks(data)
1394                wx.CallAfter(UpdateMasks,G2frame,data)
1395                G2plt.PlotExposedImage(G2frame,event=event)               
1396        finally:
1397            dlg.Destroy()
1398           
1399    def OnAutoSpotMask(event):
1400        'Do auto search for spot masks'
1401        if wx.MessageDialog(G2frame.dataWindow,'NB: This will clear any old spot masks',
1402            'Auto Spot Masks', wx.OK|wx.CANCEL).ShowModal() == wx.ID_OK:
1403            Controls = copy.deepcopy(G2frame.GPXtree.GetItemPyData( 
1404                G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')))
1405            Error = G2img.AutoSpotMasks(G2frame.ImageZ,data,Controls)
1406            if not Error is None:
1407                G2frame.ErrorDialog('Auto spot search error',Error)
1408            wx.CallAfter(UpdateMasks,G2frame,data)
1409            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
1410
1411    def OnDeleteSpotMask(event):
1412        data['Points'] = []
1413        wx.CallAfter(UpdateMasks,G2frame,data)
1414        wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)         
1415           
1416    def ToggleSpotMaskMode(event):
1417        G2plt.ToggleMultiSpotMask(G2frame)
1418       
1419    def OnNewArcMask(event):
1420        'Start a new arc mask'
1421        G2frame.MaskKey = 'a'
1422        G2plt.OnStartMask(G2frame)
1423       
1424    def OnNewRingMask(event):
1425        'Start a new ring mask'
1426        G2frame.MaskKey = 'r'
1427        G2plt.OnStartMask(G2frame)
1428       
1429    def OnNewPolyMask(event):
1430        'Start a new polygon mask'
1431        G2frame.MaskKey = 'p'
1432        G2plt.OnStartMask(G2frame)
1433       
1434    def OnNewFrameMask(event):
1435        'Start a new Frame mask'
1436        G2frame.MaskKey = 'f'
1437        G2plt.OnStartMask(G2frame)
1438       
1439    def MaxSizer():
1440        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
1441        for the image, as well as callback routines.
1442        '''
1443        def OnNewVal(invalid,value,tc):
1444            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
1445            the value into the data['range'] nested list).
1446            This adjusts the slider positions to match the current values
1447            '''
1448            scaleSel.SetSelection(len(scaleChoices)-1)
1449            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
1450            if r11 != Range[1][1]:
1451                Range[1][1] = r11
1452                maxVal.SetValue(int(Range[1][1]))
1453            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
1454            if r10 != Range[1][0]:
1455                Range[1][0] = r10
1456                minVal.SetValue(int(Range[1][0]))
1457            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1458            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1459            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1460            maxSel.SetValue(sv1)
1461            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1462            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1463            minSel.SetValue(sv0)
1464            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1465            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1466            if mplOld:
1467                Page.canvas.draw()
1468            else:
1469                Page.canvas.draw_idle()
1470           
1471        G2frame.prevMaxValue = None   
1472        def OnMaxSlider(event):
1473            val = maxSel.GetValue()
1474            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
1475            scaleSel.SetSelection(len(scaleChoices)-1)
1476            G2frame.prevMaxValue = val
1477            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1478            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
1479            maxVal.SetValue(int(0.5+Range[1][1]))
1480            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1481            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
1482            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1483            minSel.SetValue(sv0)
1484            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1485            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1486            if mplOld:
1487                Page.canvas.draw()
1488            else:
1489                Page.canvas.draw_idle()
1490           
1491        G2frame.prevMinValue = None   
1492        def OnMinSlider(event):
1493            val = minSel.GetValue()
1494            scaleSel.SetSelection(len(scaleChoices)-1)
1495            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
1496            G2frame.prevMinValue = val
1497            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1498            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
1499            minVal.SetValue(int(Range[1][0]))
1500            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1501            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1502            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1503            maxSel.SetValue(sv1)
1504            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1505            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1506            if mplOld:
1507                Page.canvas.draw()
1508            else:
1509                Page.canvas.draw_idle()
1510           
1511        def OnAutoSet(event):
1512            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
1513            for the image so that 95% (etc.) of pixels are inside the color map limits.
1514            An equal number of pixels are dropped at the minimum and maximum levels.
1515            '''
1516            try:
1517                val = int(event.GetEventObject().GetStringSelection()[:-1])
1518                margin = (100-val)/2.
1519            except:
1520                margin = 0
1521                event.GetEventObject().SetSelection(0)
1522            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1523            if margin == 0:
1524                Range[1] = list(Range[0])
1525            else:
1526                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
1527                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
1528            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1529            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1530            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1531            maxSel.SetValue(sv1)
1532            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1533            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1534            minSel.SetValue(sv0)
1535            minVal.SetValue(int(Range[1][0]))
1536            maxVal.SetValue(int(Range[1][1]))
1537            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1538            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1539            if mplOld:
1540                Page.canvas.draw()
1541            else:
1542                Page.canvas.draw_idle()
1543
1544        mplv = mpl.__version__.split('.')
1545        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
1546        # Plot color scaling uses limits as below:
1547        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
1548        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
1549       
1550        maxSizer = wx.BoxSizer(wx.VERTICAL)
1551        slideSizer = wx.FlexGridSizer(2,3,5,5)
1552        slideSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Max intensity'),0,WACV)
1553        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
1554        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
1555        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
1556        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1557        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1558        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1559        maxSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv1)
1560        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],1,min=Range[0][0]+1,
1561            max=Range[0][1],OnLeave=OnNewVal)
1562        slideSizer.Add(maxVal,0,WACV)
1563        slideSizer.Add(maxSel,flag=wx.EXPAND|wx.ALL)
1564        slideSizer.AddGrowableCol(2)
1565        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
1566        slideSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Min intensity'),0,WACV)
1567        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
1568        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
1569        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
1570        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1571        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1572        minVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],0,
1573            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1574        slideSizer.Add(minVal,0,WACV)
1575        minSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv0)
1576        slideSizer.Add(minSel,flag=wx.EXPAND|wx.ALL)
1577        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
1578        maxSizer.Add(slideSizer,flag=wx.EXPAND|wx.ALL)
1579        autoSizer = wx.BoxSizer(wx.HORIZONTAL)
1580        autoSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Auto scaler '),0,WACV)
1581        scaleChoices = ("100%","99%","95%","90%","80%","?")
1582        scaleSel = wx.Choice(G2frame.dataWindow,choices=scaleChoices,size=(-1,-1))
1583        if (Range[1][0] == Range[0][0] and
1584            Range[1][1] == Range[0][1]):
1585            scaleSel.SetSelection(0)
1586        else:
1587            scaleSel.SetSelection(len(scaleChoices)-1)
1588        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
1589        autoSizer.Add(scaleSel,0,WACV)
1590        maxSizer.Add(autoSizer)
1591        return maxSizer
1592
1593    G2frame.dataWindow.ClearData()
1594    startScroll = None
1595    if G2frame.dataWindow:
1596        startScroll = G2frame.dataWindow.GetScrollPos(wx.VERTICAL) # save scroll position
1597    else:
1598        CleanupMasks(data) # posting page for 1st time; clean out anything unfinished
1599    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.MaskMenu)
1600    G2frame.Bind(wx.EVT_MENU, OnCopyMask, id=G2G.wxID_MASKCOPY)
1601    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOAD)
1602    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOADNOT)
1603    G2frame.Bind(wx.EVT_MENU, OnSaveMask, id=G2G.wxID_MASKSAVE)
1604    G2frame.Bind(wx.EVT_MENU, OnAutoSpotMask, id=G2G.wxID_FINDSPOTS)
1605    G2frame.Bind(wx.EVT_MENU, OnDeleteSpotMask, id=G2G.wxID_DELETESPOTS)
1606    G2frame.Bind(wx.EVT_MENU, ToggleSpotMaskMode, id=G2G.wxID_NEWMASKSPOT)
1607    G2frame.Bind(wx.EVT_MENU, OnNewArcMask, id=G2G.wxID_NEWMASKARC)
1608    G2frame.Bind(wx.EVT_MENU, OnNewRingMask, id=G2G.wxID_NEWMASKRING)
1609    G2frame.Bind(wx.EVT_MENU, OnNewPolyMask, id=G2G.wxID_NEWMASKPOLY)
1610    G2frame.Bind(wx.EVT_MENU, OnNewFrameMask, id=G2G.wxID_NEWMASKFRAME)
1611    if G2frame.MaskKey == 'f':
1612        G2frame.GetStatusBar().SetStatusText('Frame mask active - LB pick next point, RB close polygon',1)
1613    elif G2frame.MaskKey == 'p':
1614        G2frame.GetStatusBar().SetStatusText('Polygon mask active - LB pick next point, RB close polygon',1)
1615    elif G2frame.MaskKey == 'a':
1616        G2frame.GetStatusBar().SetStatusText('Arc mask active - LB pick arc location',1)
1617    elif G2frame.MaskKey == 'r':
1618        G2frame.GetStatusBar().SetStatusText('Ring mask active - LB pick ring location',1)
1619    else:
1620        G2frame.GetStatusBar().SetStatusText("To add mask: press a,r,s,p or f on 2D image for arc/ring/spot/polygon/frame",1)
1621    mainSizer = G2frame.dataWindow.GetSizer()
1622    mainSizer.Add((5,10),0)
1623
1624    thresh = data['Thresholds']         #min/max intensity range
1625    Spots = data['Points']               #x,y,radius in mm
1626    Rings = data['Rings']               #radius, thickness
1627    Polygons = data['Polygons']         #3+ x,y pairs
1628    if 'Frames' not in data:
1629        data['Frames'] = []
1630    frame = data['Frames']             #3+ x,y pairs
1631    Arcs = data['Arcs']                 #radius, start/end azimuth, thickness
1632
1633    ######################################################################
1634    CId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')
1635    controlData = G2frame.GPXtree.GetItemPyData(CId)
1636    Range = controlData['range']
1637    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1638    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1639
1640    littleSizer = wx.FlexGridSizer(0,3,0,5)
1641    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper limits '),0,WACV)
1642    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][0]),style=wx.TE_READONLY)
1643    littleSizer.Add(Text,0,WACV)
1644    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1645    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][1]),style=wx.TE_READONLY)
1646    littleSizer.Add(Text,0,WACV)
1647    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1648    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper thresholds '),0,WACV)
1649    lowerThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=0,
1650        min=thresh[0][0],OnLeave=newReplot,typeHint=int)
1651    littleSizer.Add(lowerThreshold,0,WACV)
1652    upperThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=1,
1653        max=thresh[0][1],OnLeave=newReplot,typeHint=int)
1654    littleSizer.Add(upperThreshold,0,WACV)
1655    mainSizer.Add(littleSizer,0,)
1656    if len(Spots):
1657        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Spot masks(on plot LB drag to move, shift-LB drag to resize, RB to delete)')
1658        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1659        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1660        colTypes = [wg.GRID_VALUE_STRING,wg.GRID_VALUE_FLOAT+':10,2',wg.GRID_VALUE_BOOL]
1661        colIds = ['position, mm','diameter, mm','Delete?']
1662        rowIds = [str(i) for i in range(len(Spots))]
1663        table = [['%.2f,%.2f'%(item[0],item[1]),item[2],False] for item in Spots]
1664        SpotTable = G2G.Table(table,rowLabels=rowIds,colLabels=colIds,types=colTypes)
1665        SpotGrid = G2G.GSGrid(G2frame.dataWindow)
1666        SpotGrid.SetTable(SpotTable,True)
1667        SpotGrid.AutoSizeColumns(True)
1668        SpotGrid.SetColSize(1,80)
1669        for r in range(len(Spots)):
1670            SpotGrid.SetCellStyle(r,0,VERY_LIGHT_GREY,True)
1671        if 'phoenix' in wx.version():
1672            SpotGrid.Bind(wg.EVT_GRID_CELL_CHANGED, OnSpotChange)
1673        else:
1674            SpotGrid.Bind(wg.EVT_GRID_CELL_CHANGE, OnSpotChange)
1675        mainSizer.Add(SpotGrid,0,)
1676    if Rings:
1677        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Ring masks')
1678        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1679        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1680        littleSizer = wx.FlexGridSizer(0,3,0,5)
1681        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' 2-theta,deg'),0,WACV)
1682        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
1683        littleSizer.Add((5,0),0)
1684        for i in range(len(Rings)):
1685            if Rings[i]:
1686                ringText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (Rings[i][0])),
1687                    style=wx.TE_READONLY)
1688                ringText.SetBackgroundColour(VERY_LIGHT_GREY)
1689                ringText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1690                littleSizer.Add(ringText,0,WACV)
1691                ringThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Rings[i],key=1,
1692                    min=0.001,max=1.,OnLeave=Replot,nDig=[8,3])
1693                littleSizer.Add(ringThick,0,WACV)
1694                ringDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1695                    locationcode='Delete+Rings+'+str(i),handler=onDeleteMask)
1696                littleSizer.Add(ringDelete,0,WACV)
1697        mainSizer.Add(littleSizer,0,)
1698    if Arcs:
1699        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Arc masks')
1700        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1701        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1702        littleSizer = wx.FlexGridSizer(0,4,0,5)
1703        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' 2-theta,deg'),0,WACV)
1704        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' azimuth, deg'),0,WACV)
1705        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
1706        littleSizer.Add((5,0),0)
1707        for i in range(len(Arcs)):
1708            if Arcs[i]:
1709                tth,azimuth,thick = Arcs[i]
1710                arcText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (tth)),
1711                    style=wx.TE_READONLY)
1712                arcText.SetBackgroundColour(VERY_LIGHT_GREY)
1713                arcText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1714                littleSizer.Add(arcText,0,WACV)
1715                azmText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%d,%d" % (azimuth[0],azimuth[1])),
1716                    style=wx.TE_READONLY)
1717                azmText.SetBackgroundColour(VERY_LIGHT_GREY)
1718                azmText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1719                littleSizer.Add(azmText,0,WACV)
1720                arcThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Arcs[i],key=2,
1721                    min=0.001,max=20.,OnLeave=Replot,nDig=[8,3])
1722                littleSizer.Add(arcThick,0,WACV)
1723                arcDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1724                    locationcode='Delete+Arcs+'+str(i),handler=onDeleteMask)
1725                littleSizer.Add(arcDelete,0,WACV)
1726        mainSizer.Add(littleSizer,0,)
1727    if Polygons:
1728        lbl = wx.StaticText(parent=G2frame.dataWindow,
1729            label=' Polygon masks (on plot RB vertex drag to move, LB vertex drag to insert)')
1730        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1731        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1732        littleSizer = wx.FlexGridSizer(0,2,0,5)
1733        for i in range(len(Polygons)):
1734            if Polygons[i]:
1735                polyList = []
1736                for x,y in Polygons[i]:
1737                    polyList.append("%.2f, %.2f"%(x,y))
1738                polyText = wx.ComboBox(G2frame.dataWindow,value=polyList[0],choices=polyList,style=wx.CB_READONLY)
1739                littleSizer.Add(polyText,0,WACV)
1740                polyDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1741                    locationcode='Delete+Polygons+'+str(i),handler=onDeleteMask)
1742                littleSizer.Add(polyDelete,0,WACV)
1743        mainSizer.Add(littleSizer,0,)
1744    if frame:
1745        lbl = wx.StaticText(parent=G2frame.dataWindow,
1746            label=' Frame mask (on plot RB vertex drag to move, LB vertex drag to insert)')
1747        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1748        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1749        littleSizer = wx.FlexGridSizer(0,2,0,5)
1750        frameList = []
1751        for x,y in frame:
1752            frameList.append("%.2f, %.2f"%(x,y))
1753        frameText = wx.ComboBox(G2frame.dataWindow,value=frameList[0],choices=frameList,style=wx.CB_READONLY)
1754        littleSizer.Add(frameText,0,WACV)
1755        frameDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1756            locationcode='Delete+Frame',handler=onDeleteFrame)
1757        littleSizer.Add(frameDelete,0,WACV)
1758        mainSizer.Add(littleSizer,0,)
1759    G2frame.dataWindow.SetDataSize()
1760    if startScroll: # reset scroll to saved position
1761        G2frame.dataWindow.Scroll(0,startScroll) # set to saved scroll position
1762        wx.Yield()
1763
1764################################################################################
1765##### Stress/Strain
1766################################################################################
1767
1768def UpdateStressStrain(G2frame,data):
1769    '''Shows and handles the controls on the "Stress/Strain"
1770    data tree entry
1771    '''
1772   
1773    def OnAppendDzero(event):
1774        data['d-zero'].append({'Dset':1.0,'Dcalc':0.0,'pixLimit':10,'cutoff':1.0,
1775            'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0],'fixDset':False})
1776        UpdateStressStrain(G2frame,data)
1777       
1778    def OnUpdateDzero(event):
1779        for item in data['d-zero']:
1780            if item['Dcalc']:   #skip unrefined ones
1781                item['Dset'] = item['Dcalc']
1782        UpdateStressStrain(G2frame,data)
1783           
1784    def OnCopyStrSta(event):
1785        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1786        if len(Names) == 1:
1787            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
1788            return
1789        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
1790        Names.pop(Names.index(Source))
1791        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy stress/strain controls','Copy controls from '+Source+' to:',Names)
1792        try:
1793            if dlg.ShowModal() == wx.ID_OK:
1794                items = dlg.GetSelections()
1795                for item in items:
1796                    name = Names[item]
1797                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1798                    CId = G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain')
1799                    oldData = G2frame.GPXtree.GetItemPyData(CId)
1800                    load = oldData.get('Sample load',0.0)
1801                    Data = copy.deepcopy(data)
1802                    Data['Sample load'] = load
1803                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'),Data)
1804        finally:
1805            dlg.Destroy()
1806            G2frame.GPXtree.SelectItem(G2frame.PickId)
1807
1808    def OnLoadStrSta(event):
1809        pth = G2G.GetImportPath(G2frame)
1810        if not pth: pth = '.'
1811        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1812            'image control files (*.strsta)|*.strsta',wx.FD_OPEN)
1813        try:
1814            if dlg.ShowModal() == wx.ID_OK:
1815                filename = dlg.GetPath()
1816                File = open(filename,'r')
1817                S = File.read()
1818                data = eval(S)
1819                Controls = G2frame.GPXtree.GetItemPyData(
1820                    G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1821                G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1822                UpdateStressStrain(G2frame,data)
1823                G2plt.PlotExposedImage(G2frame,event=event)
1824                G2plt.PlotStrain(G2frame,data,newPlot=True)
1825                File.close()
1826        finally:
1827            dlg.Destroy()
1828
1829    def OnSaveStrSta(event):
1830        pth = G2G.GetExportPath(G2frame)
1831        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1832            'image control files (*.strsta)|*.strsta',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1833        try:
1834            if dlg.ShowModal() == wx.ID_OK:
1835                filename = dlg.GetPath()
1836                File = open(filename,'w')
1837                keys = ['Type','Sample phi','Sample z','Sample load']
1838                keys2 = ['Dset','Dcalc','pixLimit','cutoff','Emat','fixDset']
1839                File.write('{\n\t')
1840                for key in keys:
1841                    if key in 'Type':
1842                        File.write("'"+key+"':'"+data[key]+"',")
1843                    else:
1844                        File.write("'"+key+"':"+str(data[key])+',')
1845                File.write('\n\t'+"'d-zero':[\n")
1846                for data2 in data['d-zero']:
1847                    File.write('\t\t{')
1848                    for key in keys2:
1849                        File.write("'"+key+"':"+str(data2[key])+',')
1850                    File.write("'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]]},\n")
1851                File.write('\t]\n}')
1852                File.close()
1853        finally:
1854            dlg.Destroy()
1855           
1856    def OnStrStaSample(event):
1857        filename = ''
1858        pth = G2G.GetImportPath(G2frame)
1859        if not pth: pth = '.'
1860        dlg = wx.FileDialog(G2frame, 'Choose multihistogram metadata text file', pth, '', 
1861            'metadata file (*.*)|*.*',wx.FD_OPEN)
1862        try:
1863            if dlg.ShowModal() == wx.ID_OK:
1864                filename = dlg.GetPath()
1865                File = open(filename,'r')
1866                S = File.readline()
1867                newItems = []
1868                itemNames = []
1869                Comments = []
1870                while S:
1871                    if S[0] == '#':
1872                        Comments.append(S)
1873                        S = File.readline()
1874                        continue
1875                    S = S.replace(',',' ').replace('\t',' ')
1876                    Stuff = S[:-1].split()
1877                    itemNames.append(Stuff[0])
1878                    newItems.append(Stuff[1:])
1879                    S = File.readline()               
1880                File.close()
1881        finally:
1882            dlg.Destroy()
1883        if not filename:
1884            G2frame.ErrorDialog('Nothing to do','No file selected')
1885            return
1886        dataDict = dict(zip(itemNames,newItems))
1887        ifany = False
1888        Names = [' ','Sample phi','Sample z','Sample load']
1889        dlg = G2G.G2ColumnIDDialog( G2frame,' Choose multihistogram metadata columns:',
1890            'Select columns',Comments,Names,np.array(newItems).T)
1891        try:
1892            if dlg.ShowModal() == wx.ID_OK:
1893                colNames,newData = dlg.GetSelection()
1894                dataDict = dict(zip(itemNames,newData.T))
1895                for item in colNames:
1896                    if item != ' ':
1897                        ifany = True
1898        finally:
1899            dlg.Destroy()
1900        if not ifany:
1901            G2frame.ErrorDialog('Nothing to do','No columns identified')
1902            return
1903        histList = []
1904        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)       
1905        while item:
1906            name = G2frame.GPXtree.GetItemText(item)
1907            if name.startswith('IMG'):
1908                histList.append(name)
1909            item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
1910        colIds = {}
1911        for i,name in enumerate(colNames):
1912            if name != ' ':
1913                colIds[name] = i
1914        for hist in histList:
1915            name = hist.split()[1]  #this is file name
1916            if name in dataDict:
1917                newItems = {}
1918                for item in colIds:
1919                    newItems[item] = float(dataDict[name][colIds[item]])
1920                Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,hist)
1921                stsrData = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'))
1922                stsrData.update(newItems)       
1923        UpdateStressStrain(G2frame,data)       
1924   
1925    def OnPlotStrSta(event):
1926        Controls = G2frame.GPXtree.GetItemPyData(
1927            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1928        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1929        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1930        G2plt.PlotExposedImage(G2frame,event=event)
1931        G2frame.G2plotNB.Delete('Ring Intensities')
1932        G2plt.PlotXY(G2frame,RingInt,labelX='Azimuth',
1933            labelY='MRD',newPlot=True,Title='Ring Intensities',
1934            names=Names,lines=True)
1935       
1936    def OnSaveStrRing(event):
1937        Controls = G2frame.GPXtree.GetItemPyData(
1938            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1939        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1940        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1941        pth = G2G.GetExportPath(G2frame)
1942        dlg = wx.FileDialog(G2frame, 'Choose strain ring intensity file', pth, '', 
1943            'ring intensity file (*.txt)|*.txt',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1944        try:
1945            if dlg.ShowModal() == wx.ID_OK:
1946                filename = dlg.GetPath()
1947                File = open(filename,'w')
1948                for i,name in enumerate(Names):
1949                    File.write('%s%s\n'%(' Ring intensity for ',name))
1950                    File.write('%12s %12s\n'%('Azimuth','RMD'))
1951                    for item in RingInt[i].T:
1952                        File.write(' %12.3f %12.3f\n'%(item[0],item[1]))
1953                    File.write('\n')
1954                File.close()
1955        finally:
1956            dlg.Destroy()
1957               
1958               
1959    def OnFitStrSta(event):
1960        Controls = G2frame.GPXtree.GetItemPyData(
1961            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1962        G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1963        print ('Strain fitting finished')
1964        UpdateStressStrain(G2frame,data)
1965        G2plt.PlotExposedImage(G2frame,event=event)
1966        G2plt.PlotStrain(G2frame,data,newPlot=True)
1967       
1968    def OnFitAllStrSta(event):
1969        choices = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1970        od = {'label_1':'Copy to next','value_1':False,'label_2':'Reverse order','value_2':False}
1971        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices,extraOpts=od)
1972        names = []
1973        if dlg.ShowModal() == wx.ID_OK:
1974            for sel in dlg.GetSelections():
1975                names.append(choices[sel])
1976            Id =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Sequential strain fit results')
1977            if Id:
1978                SeqResult = G2frame.GPXtree.GetItemPyData(Id)
1979            else:
1980                SeqResult = {}
1981                Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text='Sequential strain fit results')
1982            SeqResult.update({'SeqPseudoVars':{},'SeqParFitEqList':[]})
1983        else:
1984            dlg.Destroy()
1985            return
1986        dlg.Destroy()
1987        if not names:
1988            return
1989        dlg = wx.ProgressDialog('Sequential IMG Strain fit','Data set name = '+names[0],len(names), 
1990            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT)         
1991        wx.BeginBusyCursor()
1992        goodnames = []
1993        if od['value_2']:
1994            names.reverse()
1995        try:
1996            varyList = []
1997            variables = []
1998            for i,name in enumerate(names):
1999                print (' Sequential strain fit for '+name)
2000                GoOn = dlg.Update(i,newmsg='Data set name = '+name)[0]
2001                if not GoOn:
2002                    break
2003                sId =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
2004                G2frame.Image = sId
2005                Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Image Controls'))
2006                StaCtrls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Stress/Strain'))
2007                if not len(StaCtrls['d-zero']):
2008                    continue
2009                goodnames.append(name)
2010                Npix,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(sId)
2011                image = GetImageZ(G2frame,Controls)
2012                sig = []
2013                if i and od['value_1']:
2014                    for j,ring in enumerate(StaCtrls['d-zero']):
2015                        ring['Emat'] = copy.copy(variables[4*j:4*j+3])
2016                varyList = []
2017                variables = []
2018                #get results from previous & put in StaCtrls
2019                G2img.FitStrSta(image,StaCtrls,Controls)
2020                G2plt.PlotStrain(G2frame,StaCtrls,newPlot=True)
2021                parmDict = {'Sample load':StaCtrls['Sample load'],}
2022                varyNames = ['e11','e12','e22']
2023                Nvar = 5*len(StaCtrls['d-zero'])
2024                coVar = np.zeros((Nvar,Nvar))
2025                for j,item in enumerate(StaCtrls['d-zero']):
2026                    variables += item['Emat']
2027                    sig += item['Esig']
2028                    varylist = ['%d;%s'%(j,Name) for Name in varyNames]
2029                    varyList += varylist
2030                    parmDict.update(dict(zip(varylist,item['Emat'])))
2031                    parmDict['%d;Dcalc'%(j)] = item['Dcalc']
2032                    variables.append(1.e6*(item['Dcalc']/item['Dset']-1.))
2033                    varyList.append('%d;h-mstrain'%(j))
2034                    sig.append(0)
2035                    parmDict['%d;Ivar'%(j)] = item['Ivar']
2036                    variables.append(item['Ivar'])
2037                    varyList.append('%d;Ivar'%(j))
2038                    sig.append(0)
2039                    j4 = j*5
2040                    coVar[j4:j4+3,j4:j4+3] = item['covMat']
2041                SeqResult[name] = {'variables':variables,'varyList':varyList,'sig':sig,'Rvals':[],
2042                    'covMatrix':coVar,'title':name,'parmDict':parmDict}
2043            else:
2044                SeqResult['histNames'] = goodnames
2045                dlg.Destroy()
2046                print (' ***** Sequential strain refinement successful *****')
2047        finally:
2048            wx.EndBusyCursor()   
2049        SeqResult['histNames'] = choices
2050        G2frame.GPXtree.SetItemPyData(Id,SeqResult)
2051        G2frame.GPXtree.SelectItem(Id)
2052        print ('All images fitted')
2053       
2054    def SamSizer():
2055       
2056        def OnStrainType(event):
2057            data['Type'] = strType.GetValue()
2058       
2059        samSizer = wx.FlexGridSizer(0,4,0,5)
2060        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Strain type: '),0,WACV)
2061        strType = wx.ComboBox(G2frame.dataWindow,value=data['Type'],choices=['True','Conventional'],
2062            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2063        strType.SetValue(data['Type'])
2064        strType.Bind(wx.EVT_COMBOBOX, OnStrainType)
2065        samSizer.Add(strType,0,WACV)
2066        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample phi: '),0,WACV)
2067        samPhi = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample phi',nDig=(10,3),typeHint=float,min=-360.,max=360.)
2068        samSizer.Add(samPhi,0,WACV)
2069        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample delta-z(mm): '),0,WACV)
2070        samZ = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample z',nDig=(10,3),typeHint=float)
2071        samSizer.Add(samZ,0,WACV)
2072        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample load(MPa): '),0,WACV)
2073        samLoad = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample load',
2074                nDig=[8,3],typeHint=float,)
2075        samSizer.Add(samLoad,0,WACV)
2076
2077        return samSizer
2078       
2079    def DzeroSizer():
2080               
2081        def OnStrainChange(event):
2082#            print (event)
2083            r,c = event.GetRow(),event.GetCol()
2084            if c == 0:
2085                data['d-zero'][r]['Dset'] = min(max(float(StrainGrid.GetCellValue(r,c)),0.25),20.)
2086            elif c == 1:
2087                data['d-zero'][r]['fixDset'] = bool(StrainGrid.GetCellValue(r,c))
2088            elif c == 3:
2089                data['d-zero'][r]['cutoff'] = min(max(float(StrainGrid.GetCellValue(r,c)),0.5),20.)
2090            elif c == 4:
2091                data['d-zero'][r]['pixLimit'] = int(StrainGrid.GetCellValue(r,c))
2092            elif c == 8:
2093                del data['d-zero'][r]
2094                StrainTable.DeleteRow(r)
2095                wx.CallAfter(UpdateStressStrain,G2frame,data)
2096            G2plt.PlotExposedImage(G2frame,event=event)
2097            G2plt.PlotStrain(G2frame,data,newPlot=True)
2098           
2099        def OnSetCol(event):
2100            c = event.GetCol()
2101            if c == 1:
2102                StrainGrid.ClearSelection()
2103                StrainGrid.SelectCol(c,True)
2104                choice = ['Y - set all','N - set none',]
2105                dlg = wx.SingleChoiceDialog(G2frame,'Select option for '+StrainGrid.GetColLabelValue(c-1),
2106                    'Strain controls',choice)
2107                dlg.CenterOnParent()
2108                if dlg.ShowModal() == wx.ID_OK:
2109                    sel = dlg.GetSelection()
2110                    if sel == 0:
2111                        for row in range(StrainGrid.GetNumberRows()): data['d-zero'][row]['fixDset']=True
2112                    else:
2113                        for row in range(StrainGrid.GetNumberRows()): data['d-zero'][row]['fixDset']=False
2114                wx.CallAfter(UpdateStressStrain,G2frame,data)
2115           
2116        colTypes = [wg.GRID_VALUE_FLOAT+':10,5',wg.GRID_VALUE_BOOL,wg.GRID_VALUE_FLOAT+':10,5',wg.GRID_VALUE_FLOAT+':10,1',
2117            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',]
2118        colIds = ['d-zero','Poisson\n mean?','d-zero ave','I/Ib','nPix','e11','e12','e22','Delete?','h-mustrain','Ivar']
2119        rowIds = [str(i) for i in range(len(data['d-zero']))]
2120        table = [[item['Dset'],item.get('fixDset',False),item['Dcalc'],item['cutoff'],item['pixLimit'], 
2121            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']]
2122        StrainTable = G2G.Table(table,rowLabels=rowIds,colLabels=colIds,types=colTypes)
2123        StrainGrid = G2G.GSGrid(G2frame.dataWindow)
2124        StrainGrid.SetTable(StrainTable,True)
2125        StrainGrid.AutoSizeColumns(True)
2126        for r in range(len(data['d-zero'])):
2127            StrainGrid.SetCellStyle(r,2,VERY_LIGHT_GREY,True)
2128            StrainGrid.SetCellStyle(r,5,VERY_LIGHT_GREY,True)
2129            StrainGrid.SetCellStyle(r,6,VERY_LIGHT_GREY,True)
2130            StrainGrid.SetCellStyle(r,7,VERY_LIGHT_GREY,True)
2131            StrainGrid.SetCellStyle(r,9,VERY_LIGHT_GREY,True)
2132            StrainGrid.SetCellStyle(r,10,VERY_LIGHT_GREY,True)
2133        if 'phoenix' in wx.version():
2134            StrainGrid.Bind(wg.EVT_GRID_CELL_CHANGED, OnStrainChange)
2135        else:
2136            StrainGrid.Bind(wg.EVT_GRID_CELL_CHANGE, OnStrainChange)
2137        StrainGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnSetCol)
2138        return StrainGrid
2139# patches
2140    if 'Sample load' not in data:
2141        data['Sample load'] = 0.0
2142# end patches
2143   
2144    G2frame.dataWindow.ClearData()
2145    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.StrStaMenu)
2146    G2frame.Bind(wx.EVT_MENU, OnAppendDzero, id=G2G.wxID_APPENDDZERO)
2147    G2frame.Bind(wx.EVT_MENU, OnUpdateDzero, id=G2G.wxID_UPDATEDZERO)
2148    G2frame.Bind(wx.EVT_MENU, OnFitStrSta, id=G2G.wxID_STRSTAFIT)
2149    G2frame.Bind(wx.EVT_MENU, OnPlotStrSta, id=G2G.wxID_STRSTAPLOT) 
2150    G2frame.Bind(wx.EVT_MENU, OnSaveStrRing, id=G2G.wxID_STRRINGSAVE) 
2151    G2frame.Bind(wx.EVT_MENU, OnFitAllStrSta, id=G2G.wxID_STRSTAALLFIT)
2152    G2frame.Bind(wx.EVT_MENU, OnCopyStrSta, id=G2G.wxID_STRSTACOPY)
2153    G2frame.Bind(wx.EVT_MENU, OnLoadStrSta, id=G2G.wxID_STRSTALOAD)
2154    G2frame.Bind(wx.EVT_MENU, OnSaveStrSta, id=G2G.wxID_STRSTASAVE)
2155    G2frame.Bind(wx.EVT_MENU, OnStrStaSample, id=G2G.wxID_STRSTSAMPLE)       
2156    if G2frame.StrainKey == 'a':    #probably doesn't happen
2157        G2frame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value',1)
2158    else:
2159        G2frame.GetStatusBar().SetStatusText("To add strain data: On 2D Powder Image, key a:add ring",1)
2160       
2161    mainSizer = G2frame.dataWindow.GetSizer()
2162    mainSizer.Add((5,10),0)
2163    mainSizer.Add(SamSizer())
2164    mainSizer.Add((5,10),0)
2165    mainSizer.Add(DzeroSizer())
2166    G2frame.dataWindow.SetDataSize()
2167   
2168###########################################################################
2169# Autointegration
2170###########################################################################
2171def ReadMask(filename):
2172    'Read a mask (.immask) file'
2173    File = open(filename,'r')
2174    save = {}
2175    S = File.readline()
2176    while S:
2177        if S[0] == '#':
2178            S = File.readline()
2179            continue
2180        [key,val] = S.strip().split(':',1)
2181        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
2182            save[key] = eval(val)
2183        S = File.readline()
2184    File.close()
2185    CleanupMasks(save)
2186    return save
2187
2188def ReadControls(filename):
2189    'read an image controls (.imctrl) file'
2190    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2191            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2192            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2193            'PolaVal','SampleAbs','dark image','background image']
2194    File = open(filename,'r')
2195    save = {}
2196    S = File.readline()
2197    while S:
2198        if S[0] == '#':
2199            S = File.readline()
2200            continue
2201        [key,val] = S.strip().split(':',1)
2202        if key in ['type','calibrant','binType','SampleShape',]:    #strings
2203            save[key] = val
2204        elif key in ['rotation']:
2205            save[key] = float(val)
2206        elif key in ['center',]:
2207            if ',' in val:
2208                save[key] = eval(val)
2209            else:
2210                vals = val.strip('[] ').split()
2211                save[key] = [float(vals[0]),float(vals[1])] 
2212        elif key in cntlList:
2213            save[key] = eval(val)
2214        S = File.readline()
2215    File.close()
2216    return save
2217
2218def Read_imctrl(imctrl_file):
2219    '''Read an image control file and record control parms into a dict, with some simple
2220    type conversions
2221    '''
2222    save = {'filename':imctrl_file}
2223    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
2224    if os.path.exists(immask_file):
2225        save['maskfile'] = immask_file
2226    else:
2227        save['maskfile'] = '(none)'
2228    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2229                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2230                        'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2231                        'PolaVal','SampleAbs','dark image','background image','setdist']
2232    File = open(imctrl_file,'r')
2233    fullIntegrate = False
2234    try:
2235        S = File.readline()
2236        while S:
2237            if S[0] == '#':
2238                S = File.readline()
2239                continue
2240            [key,val] = S.strip().split(':',1)
2241            if val.find(':') != -1:
2242                #print 'rejecting ',key,val
2243                S = File.readline()
2244                continue
2245            if key in ['type','calibrant','binType','SampleShape',]:    #strings
2246                save[key] = val
2247            elif key == 'rotation':
2248                save[key] = float(val)
2249            elif key == 'fullIntegrate':
2250                fullIntegrate = eval(val)
2251            elif key == 'LRazimuth':
2252                vals = eval(val)
2253                save['LRazimuth_min'] = float(vals[0])
2254                save['LRazimuth_max'] = float(vals[1])
2255            elif key == 'IOtth':
2256                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
2257            elif key == 'center':
2258                if ',' in val:
2259                    vals = eval(val)
2260                else:
2261                    vals = val.strip('[] ').split()
2262                    vals = [float(vals[0]),float(vals[1])] 
2263                save['center_x'],save['center_y'] = vals[0:2]
2264            elif key in cntlList:
2265                save[key] = eval(val)
2266            S = File.readline()
2267    finally:
2268        File.close()
2269        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0.,360.
2270    return save
2271   
2272class AutoIntFrame(wx.Frame):
2273    '''Creates a wx.Frame window for the Image AutoIntegration.
2274    The intent is that this will be used as a non-modal dialog window.
2275   
2276    Implements a Start button that morphs into a pause and resume button.
2277    This button starts a processing loop that is repeated every
2278    :meth:`PollTime` seconds.
2279
2280    :param wx.Frame G2frame: main GSAS-II frame
2281    :param float PollTime: frequency in seconds to repeat calling the
2282      processing loop. (Default is 30.0 seconds.)
2283    '''
2284    def __init__(self,G2frame,PollTime=30.0):
2285        def OnStart(event):
2286            '''Called when the start button is pressed. Changes button label
2287            to Pause. When Pause is pressed the label changes to Resume.
2288            When either Start or Resume is pressed, the processing loop
2289            is started. When Pause is pressed, the loop is stopped.
2290            '''
2291            # check inputs for errors before starting
2292            #err = ''
2293            #if not any([self.params[fmt] for fmt in self.fmtlist]):
2294            #    err += '\nPlease select at least one output format\n'
2295            #if err:
2296            #    G2G.G2MessageBox(self,err)
2297            #    return
2298            self.Pause = False
2299            # change button label
2300            if self.btnstart.GetLabel() != 'Pause':
2301                self.btnstart.SetLabel('Pause')
2302                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
2303                if self.timer.IsRunning(): self.timer.Stop()
2304                self.PreventReEntryTimer = False
2305                if self.StartLoop():
2306                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
2307                    return
2308                self.OnTimerLoop(None) # run once immediately
2309                if not self.Pause:
2310                    # no pause, so start timer to check for new files
2311                    self.timer.Start(int(1000*PollTime),oneShot=False)
2312                    return
2313            # we will get to this point if Paused
2314            self.OnPause()
2315           
2316        def OnReset(event):
2317            '''Called when Reset button is pressed. This stops the
2318            processing loop and resets the list of integrated files so
2319            all images can be reintegrated.
2320            '''
2321            self.btnstart.SetLabel('Restart')
2322            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
2323            if self.timer.IsRunning(): self.timer.Stop()
2324            self.Reset = True
2325            self.Pause = True
2326           
2327        def OnQuit(event):
2328            '''Stop the processing loop and close the Frame
2329            '''
2330            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
2331            wx.CallAfter(self.Destroy)
2332           
2333        def OnBrowse(event):
2334            '''Responds when the Browse button is pressed to load a file.
2335            The routine determines which button was pressed and gets the
2336            appropriate file type and loads it into the appropriate place
2337            in the dict.
2338            '''
2339            if btn3 == event.GetEventObject():
2340                dlg = wx.DirDialog(
2341                    self, 'Select directory for output files',
2342                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
2343                dlg.CenterOnParent()
2344                try:
2345                    if dlg.ShowModal() == wx.ID_OK:
2346                        self.params['outdir'] = dlg.GetPath()
2347                        fInp3.SetValue(self.params['outdir'])
2348                finally:
2349                    dlg.Destroy()
2350                return
2351            elif btn4 == event.GetEventObject():
2352                msg = ''
2353                pth = G2G.GetExportPath(G2frame)
2354                dlg = wx.FileDialog(
2355                    self, 'Select a PDF parameter file',
2356                    pth, self.params['pdfprm'], 
2357                    "PDF controls file (*.pdfprm)|*.pdfprm",
2358                    wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
2359                dlg.CenterOnParent()
2360                try:
2361                    if dlg.ShowModal() == wx.ID_OK:
2362                        self.params['pdfprm'] = dlg.GetPath()
2363                        fInp4.SetValue(self.params['pdfprm'])
2364                        scanPDFprm()
2365                        msg = self.checkPDFprm(True)
2366                finally:
2367                    dlg.Destroy()
2368                if 'Error' in msg:
2369                    print(msg)
2370                    lbl = 'PDFPRM error'
2371                else:
2372                    msg = 'Information from file {}\n\n{}'.format(self.params['pdfprm'],msg)
2373                    lbl = 'PDFPRM information'
2374                G2G.G2MessageBox(self,msg,lbl)
2375                return
2376               
2377        def OnRadioSelect(event):
2378            '''Respond to a radiobutton selection and when in table
2379            mode, get distance-dependent parameters from user.
2380            '''
2381            self.Evaluator = None
2382            if self.useTable.GetValue():
2383                dlg = None
2384                try:
2385                    dlg = IntegParmTable(self) # create the dialog
2386                    dlg.CenterOnParent()
2387                    if dlg.ShowModal() == wx.ID_OK:
2388                        self.ImgTblParms = dlg.parms
2389                        self.IMfileList = dlg.IMfileList
2390                        self.Evaluator = DefineEvaluator(dlg)
2391                        self.params['Mode'] = 'table'
2392                        self.editTable.Enable(True)
2393                    else:
2394                        self.useActive.SetValue(True)
2395                finally:
2396                    if dlg: dlg.Destroy()
2397            elif self.useActive.GetValue():
2398                self.params['Mode'] = 'active'
2399                self.imageBase = G2frame.Image
2400                self.useActive.SetLabel("Active Image: "+
2401                        G2frame.GPXtree.GetItemText(self.imageBase))
2402                self.editTable.Enable(False)
2403            else:
2404                print('unexpected mode in OnRadioSelect')
2405
2406        def OnEditTable(event):
2407            '''Called to edit the distance-dependent parameter look-up table.
2408            Should be called only when table is defined and active.
2409            '''
2410            dlg = None
2411            try:
2412                dlg = IntegParmTable(self,self.ImgTblParms,self.IMfileList)
2413                dlg.CenterOnParent()
2414                if dlg.ShowModal() == wx.ID_OK:
2415                    self.ImgTblParms = dlg.parms
2416                    self.IMfileList = dlg.IMfileList
2417                    self.Evaluator = DefineEvaluator(dlg)
2418                    self.params['Mode'] = 'table'
2419                    self.editTable.Enable(True)
2420                else:
2421                    self.useActive.SetValue(True)
2422                    self.params['Mode'] = 'active'
2423                    self.imageBase = G2frame.Image
2424                    self.useActive.SetLabel("Active Image: "+
2425                            G2frame.GPXtree.GetItemText(self.imageBase))
2426                    self.editTable.Enable(False)
2427            finally:
2428                if dlg: dlg.Destroy()
2429               
2430        def showPDFctrls(event):
2431            '''Called to show or hide AutoPDF widgets. Note that fInp4 must be included in the
2432            sizer layout with .Show(True) before .Show(False) will work properly.
2433            '''
2434            fInp4.Enable(self.params['ComputePDF'])
2435            fInp4.Show(self.params['ComputePDF'])
2436            fInp4a.Enable(self.params['ComputePDF'])
2437            btn4.Enable(self.params['ComputePDF'])
2438            cOpt.Enable(self.params['ComputePDF'])
2439            if self.params['ComputePDF']:
2440                lbl4.SetForegroundColour("black")
2441                lbl4a.SetForegroundColour("black")
2442            else:
2443                lbl4.SetForegroundColour("gray")
2444                lbl4a.SetForegroundColour("gray")
2445                                   
2446        def scanPDFprm(**kw):
2447            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2448            fInp4._IndicateValidity()
2449           
2450        def OnAutoScale(event):
2451            self.AutoScale = autoscale.GetValue()
2452           
2453        def OnAutoScaleName(event):
2454            self.AutoScaleName = scalename.GetValue()
2455            self.Scale[0] = self.AutoScales[self.AutoScaleName]
2456            scaleval.SetValue(self.Scale[0])
2457               
2458        ##################################################
2459        # beginning of __init__ processing
2460        ##################################################
2461        self.G2frame = G2frame
2462        self.ImgTblParms = None
2463        self.IMfileList = None
2464        self.Evaluator = None
2465        self.params = {}
2466        self.Reset = False
2467        self.Pause = False
2468        self.PreventReEntryShowMatch = False
2469        self.PreventReEntryTimer = False
2470        self.params['IMGfile'] = ''
2471        self.params['MaskFile'] = ''
2472        self.params['IgnoreMask'] = True
2473        self.fmtlist = G2IO.ExportPowderList(G2frame)
2474        self.timer = wx.Timer()
2475        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2476        self.imageBase = G2frame.Image
2477        self.params['ComputePDF'] = False
2478        self.params['pdfDmax'] = 0.0
2479        self.params['pdfprm'] = ''
2480        self.params['optPDF'] = True
2481        self.pdfControls = {}
2482        self.AutoScale = False
2483        self.Scale = [1.0,]
2484
2485        G2frame.GPXtree.GetSelection()
2486        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(self.imageBase) 
2487        self.params['readdir'],fileroot = os.path.split(imagefile)
2488        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2489        self.params['outdir'] = os.path.abspath(self.params['readdir'])
2490        Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2491            G2frame,self.imageBase, 'Comments'))
2492        DefaultAutoScaleNames = GSASIIpath.GetConfigValue('Autoscale_ParmNames')
2493        self.AutoScaleName = GSASIIpath.GetConfigValue('DefaultAutoScale')
2494        self.AutoScales = {}
2495        if DefaultAutoScaleNames is not None:
2496            for comment in Comments:
2497                if '=' in comment:
2498                    name,val = comment.split('=',1) 
2499                    if name in DefaultAutoScaleNames:
2500                        try:
2501                            self.AutoScales[name] = float(val)
2502                            if name == self.AutoScaleName:
2503                                self.Scale[0] = float(val)
2504                        except ValueError:
2505                            continue
2506        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2507                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2508        self.Status = self.CreateStatusBar()
2509        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2510        mnpnl = wx.Panel(self)
2511        mnsizer = wx.BoxSizer(wx.VERTICAL)
2512        # box for integration controls & masks input
2513        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2514        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2515        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2516        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2517                            style = wx.RB_GROUP)
2518        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2519        self.useActive.SetLabel("Active Image: "+
2520                                G2frame.GPXtree.GetItemText(self.imageBase))
2521        lblsizr.Add(self.useActive,0,wx.EXPAND,1)
2522        self.useActive.SetValue(True)
2523        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2524        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2525        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2526        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2527        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2528        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2529        self.editTable.Enable(False)
2530        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2531        # bind button and deactivate be default
2532        lblsizr.Add(minisizer)
2533        mnsizer.Add(lblsizr,0,wx.EXPAND,0)
2534
2535        # file filter stuff
2536        sizer = wx.BoxSizer(wx.HORIZONTAL)
2537        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Read images from '))
2538        self.readDir = G2G.ValidatedTxtCtrl(mnpnl,self.params,'readdir',
2539                            OnLeave=self.ShowMatchingFiles,size=(200,-1))
2540        sizer.Add(self.readDir,1,wx.EXPAND,1)
2541        btn3 = wx.Button(mnpnl, wx.ID_ANY, "Browse")
2542        btn3.Bind(wx.EVT_BUTTON, self.SetSourceDir)
2543        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2544        mnsizer.Add(sizer,0,wx.EXPAND,0)
2545        # not yet implemented
2546        sizer = wx.BoxSizer(wx.HORIZONTAL)
2547        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Keep read images in tree '))
2548        self.params['keepReadImage'] = True
2549        keepImage = G2G.G2CheckBox(mnpnl,'',self.params,'keepReadImage')
2550        sizer.Add(keepImage)
2551        keepImage.Enable(False)
2552        sizer.Add((-1,-1),1,wx.EXPAND,1)
2553        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'  Image filter'))
2554        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',
2555                                        OnLeave=self.ShowMatchingFiles)
2556        sizer.Add(flterInp)
2557        mnsizer.Add(sizer,0,wx.EXPAND,0)
2558       
2559        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2560        mnsizer.Add(self.ListBox,1,wx.EXPAND,1)
2561        self.ShowMatchingFiles(self.params['filter'])
2562
2563        # box for output selections
2564        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2565        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2566        sizer = wx.BoxSizer(wx.HORIZONTAL)
2567        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2568        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2569        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2570        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2571        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2572        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2573        lblsizr.Add(sizer,0,wx.EXPAND)
2574        sizer = wx.BoxSizer(wx.HORIZONTAL)
2575        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s):'))
2576        for dfmt in self.fmtlist:
2577            sizer.Add((6,2)) # add a bit of extra space
2578            fmt = dfmt[1:]
2579            self.params[fmt] = False
2580            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2581            sizer.Add(btn)
2582        lblsizr.Add(sizer)
2583        sizer = wx.BoxSizer(wx.HORIZONTAL)
2584        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2585        self.params['SeparateDir'] = False
2586        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2587        lblsizr.Add(sizer)
2588        if self.AutoScales:
2589            sizer = wx.BoxSizer(wx.HORIZONTAL)
2590            autoscale = wx.CheckBox(mnpnl,label='Do autoscaling with:')
2591            autoscale.Bind(wx.EVT_CHECKBOX,OnAutoScale)
2592            sizer.Add(autoscale,0,WACV)
2593            scalename = wx.ComboBox(mnpnl,value=self.AutoScaleName,choices=list(self.AutoScales.keys()),
2594                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2595            scalename.Bind(wx.EVT_COMBOBOX,OnAutoScaleName)
2596            sizer.Add(scalename,0,WACV)
2597            sizer.Add(wx.StaticText(mnpnl,label=' to '),0,WACV)
2598            scaleval = G2G.ValidatedTxtCtrl(mnpnl,self.Scale,0,nDig=(10,2),min=1.)
2599            sizer.Add(scaleval,0,WACV)
2600            lblsizr.Add(sizer,0)
2601        #ToDO: Autonormalize, parm name?, scaling value?
2602        sizer = wx.BoxSizer(wx.HORIZONTAL)
2603        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2604        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2605        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2606        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2607        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2608        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2609        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2610        sizer.Add(cOpt)
2611        lblsizr.Add(sizer,0)
2612        sizer = wx.BoxSizer(wx.HORIZONTAL)
2613        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2614        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2615        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2616                                     OnLeave=scanPDFprm)
2617        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2618        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2619        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2620        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2621        lblsizr.Add(sizer,0,wx.EXPAND)
2622        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2623        # buttons on bottom
2624        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2625        sizer = wx.BoxSizer(wx.HORIZONTAL)
2626        sizer.Add((20,-1))
2627        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2628        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2629        sizer.Add(self.btnstart)
2630        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2631        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2632        sizer.Add(self.btnreset)
2633        sizer.Add((20,-1),wx.EXPAND,1)
2634        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2635        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2636        sizer.Add(self.btnclose)
2637        sizer.Add((20,-1))
2638        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2639        # finish up window
2640        mnpnl.SetSizer(mnsizer)
2641        OnRadioSelect(None) # disable widgets
2642        mnsizer.Fit(self)
2643        self.CenterOnParent()
2644        self.Show()
2645        showPDFctrls(None)
2646
2647    def checkPDFprm(self,ShowContents=False):
2648        '''Read in the PDF (.pdfprm) parameter file and check for problems.
2649        If ShowContents is True, a formatted text version of some of the file
2650        contents is returned. If errors are found, the return string will contain
2651        the string "Error:" at least once.
2652        '''
2653        self.pdfControls = {}
2654        msg = ''
2655        File = None
2656        try:
2657            File = open(self.params['pdfprm'],'r')
2658            S = File.readline()
2659            while S:
2660                if '#' in S:
2661                    S = File.readline()
2662                    continue
2663                key,val = S.split(':',1)
2664                try:
2665                    self.pdfControls[key] = eval(val)
2666                except:
2667                    self.pdfControls[key] = val
2668                S = File.readline()
2669        except Exception as err:
2670            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
2671            if GSASIIpath.GetConfigValue('debug'):
2672                print(msg)
2673                print(err)
2674            self.pdfControls = {}
2675            return msg
2676        finally:
2677            if File: File.close()
2678        formula = ''
2679        for el in self.pdfControls['ElList']:
2680            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
2681            if formula: formula += ' '
2682            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
2683        if not formula:
2684            msg += 'Error: no chemical formula in file'
2685        for key in ['Sample Bkg.','Container','Container Bkg.']:
2686            if key not in self.pdfControls:
2687                if msg: msg += '\n'
2688                msg += 'Error: missing key in self.pdfControls: '+key
2689                continue
2690        if msg or not ShowContents: return msg  # stop on error
2691        msg += 'Default formula: '+formula+'\n'
2692        for key in ['Sample Bkg.','Container','Container Bkg.']:
2693            name = self.pdfControls[key]['Name']
2694            mult = self.pdfControls[key].get('Mult',0.0)
2695            if not name: continue
2696            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
2697            if not G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,name):
2698                msg += ' *** missing ***'
2699        return msg
2700   
2701    def SetSourceDir(self,event):
2702        '''Use a dialog to get a directory for image files
2703        '''
2704        dlg = wx.DirDialog(self, 'Select directory for image files',
2705                        self.params['readdir'],wx.DD_DEFAULT_STYLE)
2706        dlg.CenterOnParent()
2707        try:
2708            if dlg.ShowModal() == wx.ID_OK:
2709                self.params['readdir'] = dlg.GetPath()
2710            self.readDir.SetValue(self.params['readdir'])
2711            self.ShowMatchingFiles(None)
2712        finally:
2713            dlg.Destroy()
2714        return
2715       
2716    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2717        '''Find and show images in the tree and the image files matching the image
2718        file directory (self.params['readdir']) and the image file filter
2719        (self.params['filter']) and add this information to the GUI list box
2720        '''
2721        G2frame = self.G2frame
2722        if invalid: return
2723        msg = ''
2724        if self.PreventReEntryShowMatch: return
2725        self.PreventReEntryShowMatch = True
2726        imageFileList = []
2727        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
2728            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
2729            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
2730            if imagefile not in imageFileList: imageFileList.append(imagefile)
2731            if img not in G2frame.IntegratedList:
2732                if msg: msg += '\n'
2733                msg += '  ' + img
2734        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
2735        msg1 = ""
2736        try:
2737            if os.path.exists(self.params['readdir']): 
2738                imageList = sorted(
2739                    glob.glob(os.path.join(self.params['readdir'],self.params['filter'])))
2740                if not imageList:
2741                    msg1 = 'Warning: No files match search string '+os.path.join(self.params['readdir'],self.params['filter'])
2742                else:
2743                    for fil in imageList:
2744                        if fil not in imageFileList: msg1 += '\n  '+fil
2745                    if msg1:
2746                        msg += 'Files to integrate from '+os.path.join(self.params['readdir'],self.params['filter'])+msg1
2747                    else:
2748                        msg += 'No files found to read in '+self.params['readdir']
2749            else:
2750                msg += 'Warning: does not exist: '+self.params['readdir']
2751        except IndexError:
2752            msg += 'Error searching for files named '+os.path.join(self.params['readdir'],self.params['filter'])
2753        self.ListBox.Clear()
2754        self.ListBox.AppendItems(msg.split('\n'))
2755        self.PreventReEntryShowMatch = False
2756        return
2757       
2758    def OnPause(self):
2759        '''Respond to Pause, changes text on button/Status line, if needed
2760        Stops timer
2761        self.Pause should already be True
2762        '''
2763        if self.timer.IsRunning(): self.timer.Stop()
2764        if self.btnstart.GetLabel() == 'Restart':
2765            return
2766        if self.btnstart.GetLabel() != 'Resume':
2767            print('\nPausing autointegration\n')
2768            self.btnstart.SetLabel('Resume')
2769            self.Status.SetStatusText(
2770                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
2771        self.Pause = True
2772           
2773    def IntegrateImage(self,img,useTA=None):
2774        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
2775        are placed in G2frame.IntgOutList
2776        '''
2777        G2frame = self.G2frame
2778        imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
2779        G2frame.Image = imgId
2780        G2frame.PickId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls')
2781        # do integration
2782        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
2783        if self.AutoScale:
2784            Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2785                G2frame,imgId, 'Comments'))
2786            for comment in Comments:
2787                if '=' in comment:
2788                    name,val = comment.split('=',1) 
2789                    if name == self.AutoScaleName:
2790                        val = float(val)
2791                        if val > 0.:
2792                            Scale = self.Scale[0]/val
2793                        break
2794        masks = G2frame.GPXtree.GetItemPyData(
2795            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Masks'))
2796        data = G2frame.GPXtree.GetItemPyData(G2frame.PickId)
2797        # simulate a Image Controls press, since that is where the
2798        # integration is hidden
2799        UpdateImageControls(G2frame,data,masks,useTA=useTA,IntegrateOnly=True)
2800        G2frame.IntegratedList.append(img) # note this as integrated
2801        # split name and control number
2802        s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
2803        namepre = s[0]
2804        if len(s) > 1:
2805            namenum = s[1]
2806        else:
2807            namenum = ''
2808        for Id in G2frame.IntgOutList: # loop over newly created PDWR entry(ies)
2809            # save the created PWDR tree names so that a reset can delete them
2810            G2frame.Image = Id
2811            treename = G2frame.GPXtree.GetItemText(Id)
2812            G2frame.AutointPWDRnames.append(treename)
2813            # write out the images in the selected formats
2814            Sdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Sample Parameters'))
2815            if self.AutoScale:
2816                print ('Rescale by %.4f'%(Scale))
2817                y,w = G2frame.GPXtree.GetItemPyData(Id)[1][1:3]
2818                y *= Scale
2819                w /= Scale**2
2820            # determine the name for the current file
2821            fileroot = namepre
2822            if len(G2frame.IntgOutList) > 1:
2823                fileroot += "_AZM"
2824                if 'Azimuth' in Sdata:
2825                    fileroot += str(int(10*Sdata['Azimuth']))
2826                fileroot += "_" 
2827            fileroot += namenum
2828            # loop over selected formats
2829            for dfmt in self.fmtlist:
2830                if not self.params[dfmt[1:]]: continue
2831                if self.params['SeparateDir']:
2832                    subdir = dfmt[1:]
2833                else:
2834                    subdir = ''
2835                fil = os.path.join(self.params['outdir'],subdir,fileroot)
2836                G2IO.ExportPowder(G2frame,treename,fil+'.x',dfmt) # dummy extension (.x) is replaced before write)
2837               
2838    def EnableButtons(self,flag):
2839        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
2840        '''
2841        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
2842        # on windows
2843        if sys.platform != "win32":
2844            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
2845        self.btnstart.SetLabel('Pause')
2846        wx.Yield()
2847               
2848    def ResetFromTable(self,dist):
2849        '''Sets integration parameters based on values from
2850        the lookup table
2851        '''
2852        #dist = self.controlsDict['distance']
2853        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
2854        if GSASIIpath.GetConfigValue('debug'):
2855            print ('interpolated values: ',interpDict)
2856        self.ImageControls = ReadControls(imgctrl)
2857        self.ImageControls.update(interpDict)
2858        self.ImageControls['showLines'] = True
2859        self.ImageControls['ring'] = []
2860        self.ImageControls['rings'] = []
2861        self.ImageControls['ellipses'] = []
2862        self.ImageControls['setDefault'] = False
2863        for i in 'range','size','GonioAngles':
2864            if i in self.ImageControls:
2865                del self.ImageControls[i]
2866        # load copy of Image Masks
2867        if immask:
2868            self.ImageMasks = ReadMask(immask)
2869            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
2870                del self.ImageMasks['Thresholds']
2871        else:
2872            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
2873       
2874    def StartLoop(self):
2875        '''Prepare to start autointegration timer loop.
2876        Save current Image params for use in future integrations
2877        also label the window so users understand what is being used
2878        '''
2879        print('\nStarting new autointegration\n')
2880        G2frame = self.G2frame
2881        # show current IMG base
2882        if self.params['Mode'] != 'table':
2883            self.useActive.SetLabel("Active Image: "+
2884                                    G2frame.GPXtree.GetItemText(self.imageBase))
2885            # load copy of Image Controls from current image and clean up
2886            # items that should not be copied
2887            self.ImageControls = copy.deepcopy(
2888                G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2889                    G2frame,self.imageBase, 'Image Controls')))
2890            self.ImageControls['showLines'] = True
2891            self.ImageControls['ring'] = []
2892            self.ImageControls['rings'] = []
2893            self.ImageControls['ellipses'] = []
2894            self.ImageControls['setDefault'] = False
2895            del self.ImageControls['range']
2896            del self.ImageControls['size']
2897            del self.ImageControls['GonioAngles']
2898            # load copy of Image Masks, keep thresholds
2899            self.ImageMasks = copy.deepcopy(
2900                G2frame.GPXtree.GetItemPyData(
2901                    G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks')))
2902            self.Thresholds = self.ImageMasks['Thresholds'][:]
2903            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
2904                del self.ImageMasks['Thresholds']   
2905        # make sure all output directories exist
2906        if self.params['SeparateDir']:
2907            for dfmt in self.fmtlist:
2908                if not self.params[dfmt[1:]]: continue
2909                dir = os.path.join(self.params['outdir'],dfmt[1:])
2910                if not os.path.exists(dir): os.makedirs(dir)
2911        else:
2912            if not os.path.exists(self.params['outdir']):
2913                os.makedirs(self.params['outdir'])
2914        if self.Reset: # special things to do after Reset has been pressed
2915            self.G2frame.IntegratedList = []
2916           
2917            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
2918                for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
2919                    # update controls from master
2920                    controlsDict = G2frame.GPXtree.GetItemPyData(
2921                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Image Controls'))
2922                    controlsDict.update(self.ImageControls)
2923                    # update masks from master
2924                    ImageMasks = G2frame.GPXtree.GetItemPyData(
2925                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks'))
2926                    ImageMasks.update(self.ImageMasks)
2927            # delete all PWDR items created after last Start was pressed
2928            idlist = []
2929            item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
2930            while item:
2931                itemName = G2frame.GPXtree.GetItemText(item)
2932                if itemName in G2frame.AutointPWDRnames:
2933                    idlist.append(item)
2934                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
2935            for item in idlist:
2936                G2frame.GPXtree.Delete(item)
2937            wx.Yield()
2938            self.Reset = False
2939        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
2940        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
2941        # check that AutoPDF input is OK, offer chance to use alternate PDWRs if referenced ones
2942        # are not present
2943        if self.params['ComputePDF']:
2944            msg = self.checkPDFprm()
2945            if 'Error:' in msg:
2946                print(msg)
2947                return True
2948            fileList = []
2949            id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
2950            while id:
2951                name = G2frame.GPXtree.GetItemText(id)
2952                if name.startswith('PWDR '): fileList.append(name)
2953                id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
2954            if not fileList:
2955                print(msg)
2956                print('No PWDR entries to select')
2957                return True
2958            for key in ['Sample Bkg.','Container','Container Bkg.']:
2959                name = self.pdfControls[key]['Name']
2960                if not name: continue
2961                if not G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name):
2962                    indx = G2G.ItemSelector(fileList, self, header='Select PDWR item',
2963                                    title='Select a PDWR tree item for '+key+'\n(or cancel to quit)')
2964                    if indx is None:
2965                        print('No PWDR entry selected for '+key)
2966                        return True
2967                    self.pdfControls[key]['Name'] = fileList[indx]
2968        return False
2969               
2970    def OnTimerLoop(self,event):
2971        '''A method that is called every :meth:`PollTime` seconds that is
2972        used to check for new files and process them. Integrates new images.
2973        Also optionally sets up and computes PDF.
2974        This is called only after the "Start" button is pressed (then its label reads "Pause").
2975        '''
2976        def AutoIntegrateImage(imgId,useTA=None):
2977            '''Integrates an image that has been read into the data tree and updates the
2978            AutoInt window.
2979            '''
2980            img = G2frame.GPXtree.GetItemText(imgId)
2981            controlsDict = G2frame.GPXtree.GetItemPyData(
2982                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
2983            ImageMasks = G2frame.GPXtree.GetItemPyData(
2984                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Masks'))
2985            if self.params['Mode'] == 'table': # look up parameter values from table
2986                useTA = None        #force remake of x,y-->2th,azm map
2987                self.ResetFromTable(controlsDict['setdist'])
2988            # update controls from master
2989            controlsDict.update(self.ImageControls)
2990            # update masks from master w/o Thresholds
2991            ImageMasks.update(self.ImageMasks)
2992            self.EnableButtons(False)
2993            try:
2994                self.IntegrateImage(img,useTA=useTA)
2995            finally:
2996                self.EnableButtons(True)
2997            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
2998            wx.Yield()
2999            self.ShowMatchingFiles(self.params['filter'])
3000            wx.Yield()
3001           
3002        def AutoComputePDF(imgId):
3003            '''Computes a PDF for a PWDR data tree tree item
3004            '''
3005            for pwdr in G2frame.AutointPWDRnames[:]:
3006                if not pwdr.startswith('PWDR '): continue
3007                if pwdr in G2frame.AutointPDFnames: continue
3008                PWid = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,pwdr)
3009                controlsDict = G2frame.GPXtree.GetItemPyData(
3010                    G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3011                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
3012                    print('Skipping PDF for '+pwdr+' due to detector position')
3013                    continue
3014                # Setup PDF
3015                Data = G2frame.GPXtree.GetItemPyData(PWid)[1]
3016                pwdrMin = np.min(Data[1])
3017                Parms = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3018                    G2frame,PWid,'Instrument Parameters'))[0]
3019                fullLimits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3020                    G2frame,PWid,'Limits'))[0]
3021                if 'C' in Parms['Type'][0]:
3022                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
3023                else:
3024                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
3025                Qlimits = [0.9*qMax,qMax]
3026
3027                item = pwdr
3028                Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3029                    G2frame,imgId, 'Comments'))
3030                ElList = {}
3031                sumnum = 1.0
3032                for item in Comments:           #grab chemical formula from Comments, if there
3033                    if 'formula' in item[:15].lower():
3034                        formula = item.split('=')[1].split()
3035                        elems = formula[::2]
3036                        nums = formula[1::2]
3037                        formula = zip(elems,nums)
3038                        sumnum = 0.
3039                        for [elem,num] in formula:
3040                            ElData = G2elem.GetElInfo(elem,Parms)
3041                            ElData['FormulaNo'] = float(num)
3042                            sumnum += float(num)
3043                            ElList[elem] = ElData
3044                PDFnames = G2gd.GetGPXtreeDataNames(G2frame,['PDF ',])
3045                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
3046                if not PDFid: continue
3047                PDFdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3048                    G2frame,PDFid, 'PDF Controls'))
3049                PDFdata.update(self.pdfControls)
3050                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
3051                PDFdata['Sample']['Name'] = pwdr
3052                # compute PDF
3053                wx.Yield()
3054                G2pdG.computePDF(G2frame,PDFdata)
3055                wx.Yield()
3056                G2frame.PatternId = PDFid
3057                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3058                if self.params['optPDF']:
3059                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
3060                    wx.Yield()
3061                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3062                G2frame.AutointPDFnames.append(pwdr)
3063                # save names of PDF entry to be deleted later if needed
3064                G2frame.AutointPWDRnames.append(G2frame.GPXtree.GetItemText(PDFid))
3065           
3066        G2frame = self.G2frame
3067        try:
3068            self.currImageList = sorted(
3069                glob.glob(os.path.join(self.params['readdir'],self.params['filter'])))
3070            self.ShowMatchingFiles(self.params['filter'])
3071        except IndexError:
3072            self.currImageList = []
3073            return
3074
3075        if self.PreventReEntryTimer: return
3076        self.PreventReEntryTimer = True
3077        imageFileList = []
3078        # integrate the images that have already been read in, but
3079        # have not yet been processed           
3080        oldData = {'tilt':0.,'distance':0.,'rotation':0.,'center':[0.,0.],'DetDepth':0.,'azmthOff':0.}
3081        self.useTA = None
3082        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
3083            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3084            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3085            # Create a list of image files that have been read in
3086            if imagefile not in imageFileList: imageFileList.append(imagefile)
3087            # skip if already integrated
3088            if img in G2frame.IntegratedList: continue
3089            Data = G2frame.GPXtree.GetItemPyData(
3090                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3091            same = True
3092            for item in ['tilt','distance','rotation','center','DetDepth','azmthOff']:
3093                if Data[item] != oldData[item]:
3094                    same = False
3095            if not same:
3096                print('Use new image controls')
3097                self.useTA = G2img.MakeUseTA(Data,blkSize)
3098            AutoIntegrateImage(imgId,self.useTA)
3099            oldData = Data
3100            if self.pdfControls: AutoComputePDF(imgId)
3101            self.Pause |= G2frame.PauseIntegration
3102            if self.Pause:
3103                self.OnPause()
3104                self.PreventReEntryTimer = False
3105                self.Raise()
3106                return
3107
3108        # loop over image files matching glob, reading in any new ones
3109        for newImage in self.currImageList:
3110            if newImage in imageFileList or self.Pause: continue # already read?
3111            for imgId in G2IO.ReadImages(G2frame,newImage):
3112                AutoIntegrateImage(imgId,self.useTA)
3113                if self.pdfControls: AutoComputePDF(imgId)
3114                self.Pause |= G2frame.PauseIntegration
3115                if self.Pause:
3116                    self.OnPause()
3117                    self.PreventReEntryTimer = False
3118                    self.Raise()
3119                    return
3120        if GSASIIpath.GetConfigValue('debug'):
3121            import datetime
3122            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3123        self.PreventReEntryTimer = False
3124        self.Raise()
3125
3126def DefineEvaluator(dlg):
3127    '''Creates a function that provides interpolated values for a given distance value
3128    '''
3129    def Evaluator(dist):
3130        '''Interpolate image parameters for a supplied distance value
3131
3132        :param float dist: distance to use for interpolation
3133        :returns: a list with 3 items:
3134
3135          * a dict with parameter values,
3136          * the closest imctrl and
3137          * the closest maskfile (or None)
3138        '''           
3139        x = np.array([float(i) for i in parms[0]])
3140        closest = abs(x-dist).argmin()
3141        D = {'setdist':dist}
3142        imctfile = IMfileList[closest]
3143        if parms[-1][closest].lower() != '(none)':
3144            maskfile = parms[-1][closest]
3145        else:
3146            maskfile = None
3147        for c in range(1,cols-1):
3148            lbl = ParmList[c]
3149            if lbl in nonInterpVars:
3150                if lbl in ['outChannels',]:
3151                    D[lbl] = int(float(parms[c][closest]))
3152                else:
3153                    D[lbl] = float(parms[c][closest])
3154            else:
3155                y = np.array([float(i) for i in parms[c]])
3156                D[lbl] = np.interp(dist,x,y)
3157        # full integration when angular range is 0
3158        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3159        # conversion for paired values
3160        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3161            r = a.split('_')[0]
3162            D[r] = [D[a],D[b]]
3163            if r in ['LRazimuth',]:
3164                D[r] = [int(D[a]),int(D[b])]
3165            del D[a]
3166            del D[b]
3167        return D,imctfile,maskfile
3168    # save local copies of values needed in Evaluator
3169    parms = dlg.ReadImageParmTable()
3170    IMfileList = dlg.IMfileList
3171    cols = dlg.list.GetColumnCount()
3172    ParmList = dlg.ParmList
3173    nonInterpVars = dlg.nonInterpVars
3174    return Evaluator
3175
3176class IntegParmTable(wx.Dialog):
3177    '''Creates a dialog window with a table of integration parameters.
3178    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3179    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3180    creates a dictionary with interpolated parameter values.
3181    '''
3182    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3183            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3184            'maskfile',
3185            )
3186    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3187                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3188    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3189            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3190            'Mask File',
3191            )
3192    def __init__(self,parent,parms=None,IMfileList=None):
3193        self.G2frame = parent.G2frame
3194        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3195        if parms:
3196            self.parms = parms # list of values by column
3197            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3198        else:
3199            self.parms = [] # list of values by column
3200            self.IMfileList = [] # list of .imctrl file names for each entry in table
3201            files = []
3202            try:
3203                pth = G2G.GetImportPath(self.G2frame)
3204                if not pth: pth = '.'
3205                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3206                    style=wx.FD_OPEN| wx.FD_MULTIPLE,
3207                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3208                dlg.CenterOnParent()
3209                if dlg.ShowModal() == wx.ID_OK:
3210                    files = dlg.GetPaths()
3211                    self.parms,self.IMfileList = self.ReadFiles(files)
3212            finally:
3213                if dlg: dlg.Destroy()
3214            if not files:
3215                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3216                return
3217        mainSizer = wx.BoxSizer(wx.VERTICAL)
3218        self.list = ImgIntLstCtrl(self, wx.ID_ANY,style=wx.LC_REPORT| wx.BORDER_SUNKEN)
3219        mainSizer.Add(self.list,1,wx.EXPAND,1)
3220        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3221        btn = wx.Button(self, wx.ID_OK)
3222        btnsizer.Add(btn)
3223        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3224        btn.Bind(wx.EVT_BUTTON,self._onSave)
3225        btnsizer.Add(btn)
3226        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3227        btn.Bind(wx.EVT_BUTTON,self._onClose)
3228        btnsizer.Add(btn)
3229        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3230        self.SetSizer(mainSizer)
3231        self.list.FillList(self.parms)
3232       
3233    def ReadFiles(self,files):
3234        '''Reads a list of .imctrl files or a single .imtbl file
3235        '''
3236        tmpDict = {}
3237        if not files: return
3238        # option 1, a dump from a previous save
3239        if os.path.splitext(files[0])[1] == '.imtbl':
3240            fp = open(files[0],'r')
3241            S = fp.readline()
3242            while S:
3243                if S[0] != '#':
3244                    [key,val] = S[:-1].split(':',1)
3245                    tmpDict[key] = eval(val)
3246                S = fp.readline()
3247            fp.close()
3248            # delete entries where files do not exist
3249            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3250            if m1:
3251                print('\nimctrl file not found:')
3252                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3253            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3254            if m2:
3255                print('\nmask file not found')
3256                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3257            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3258            if m3:
3259                print('\nDropping entries due to negative distance: '+str(m3))
3260            m = sorted(set(m1 + m2 + m3))
3261            m.reverse()
3262            for c in m:
3263                for key in tmpDict:
3264                    del tmpDict[key][c]
3265            fileList = tmpDict.get('filenames','[]')
3266            parms = []
3267            if 'setdist' not in tmpDict:
3268                print(u'Old file, recreate before using: {}'.format(files[0]))
3269                return [[]],[]
3270            for key in self.ParmList:
3271                try:
3272                    float(tmpDict[key][0])
3273                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3274                except ValueError:
3275                    parms.append(tmpDict[key])
3276                except IndexError:
3277                    print('No valid image control entries read')
3278                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3279                    return [[]],[]
3280            return parms,fileList
3281        # option 2, read in a list of files
3282        for file in files: # read all files; place in dict by distance
3283            imgDict = Read_imctrl(file)
3284            dist = imgDict.get('setdist',imgDict['distance'])
3285            if dist is None:
3286                print('Skipping old file, redo: {}'.format(file))
3287            tmpDict[dist] = imgDict
3288        parms = [[] for key in self.ParmList]
3289        fileList = []
3290        for d in sorted(tmpDict):
3291            fileList.append(tmpDict[d].get('filename'))
3292            if d is None: continue
3293            if d < 0: continue
3294            for i,key in enumerate(self.ParmList):
3295                val = tmpDict[d].get(key)
3296                try:
3297                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3298                except:
3299                    val = str(val)
3300                parms[i].append(val)
3301        return parms,fileList
3302   
3303    def ReadImageParmTable(self):
3304        '''Reads possibly edited values from the ListCtrl table and returns a list
3305        of values for each column.
3306        '''
3307        rows = self.list.GetItemCount()
3308        cols = self.list.GetColumnCount()
3309        parms = []
3310        for c in range(cols):
3311            parms.append([])
3312            for r in range(rows):
3313                parms[c].append(self.list.GetItem(r,c).GetText())
3314        return parms
3315
3316    def _onClose(self,event):
3317        'Called when Cancel button is pressed'
3318        self.EndModal(wx.ID_CANCEL)
3319       
3320    def _onSave(self,event):
3321        'Called when save button is pressed; creates a .imtbl file'
3322        fil = ''
3323        if self.G2frame.GSASprojectfile:
3324            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3325        dir,f = os.path.split(fil)
3326        pth = G2G.GetExportPath(self.G2frame)
3327        try:
3328            dlg = wx.FileDialog(self, 'Save table data as',
3329                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3330                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3331            dlg.CenterOnParent()
3332            if dlg.ShowModal() != wx.ID_OK: return
3333            fil = dlg.GetPath()
3334            fil = os.path.splitext(fil)[0]+'.imtbl'
3335        finally:
3336            dlg.Destroy()       
3337        parms = self.ReadImageParmTable()
3338        print('Writing image parameter table as '+fil)
3339        fp = open(fil,'w')
3340        for c in range(len(parms)-1):
3341            lbl = self.ParmList[c]
3342            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3343        lbl = self.ParmList[c+1]
3344        fp.write(lbl+': '+str(parms[c+1])+'\n')
3345        lbl = 'filenames'
3346        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3347        fp.close()
3348   
3349class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3350    '''Creates a custom ListCtrl for editing Image Integration parameters
3351    '''
3352    def __init__(self, parent, ID, pos=wx.DefaultPosition,size=(1000,200),style=0):
3353        self.parent=parent
3354        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3355        listmix.ListCtrlAutoWidthMixin.__init__(self)
3356        listmix.TextEditMixin.__init__(self)
3357        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3358        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3359       
3360    def FillList(self,parms):
3361        'Places the current parms into the table'
3362        maxint = 2**31-1
3363        self.ClearAll()
3364        self.rowlen = len(self.parent.ParmList)
3365        for i,lbl in enumerate(self.parent.HeaderList):
3366            self.InsertColumn(i, lbl)
3367        for r,d in enumerate(parms[0]):
3368            if d is None: continue
3369            if d == 'None': continue
3370            if float(d) < 0: continue
3371            index = self.InsertStringItem(maxint, d)
3372            for j in range(1,len(parms)):
3373                self.SetStringItem(index, j, parms[j][r])
3374        for i,lbl in enumerate(self.parent.ParmList):
3375            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3376
3377    def OnDouble(self,evt):
3378        'respond to a double-click'
3379        self.CloseEditor()
3380        fil = '(none)'
3381        pth = G2G.GetImportPath(self.parent.G2frame)
3382        if not pth: pth = '.'
3383        try:
3384            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3385                style=wx.FD_OPEN,wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3386            dlg.CenterOnParent()
3387            if dlg.ShowModal() == wx.ID_OK:
3388                fil = dlg.GetPath()
3389        finally:
3390            dlg.Destroy()
3391        if os.path.splitext(fil)[1] != '.imctrl':
3392            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3393            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3394        else:
3395            # insert or overwrite an instrument parameter set
3396            if not os.path.exists(fil):
3397                print('Does not exist: '+fil)
3398                return
3399            imgDict = Read_imctrl(fil)
3400            dist = imgDict['distance']
3401            parms = self.parent.ReadImageParmTable()
3402            x = np.array([float(i) for i in parms[0]])
3403            closest = abs(x-dist).argmin()
3404            closeX = x[closest]
3405            # fix IMfileList
3406            for c,lbl in enumerate(self.parent.ParmList):
3407                try:
3408                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3409                except ValueError:
3410                    vali = imgDict[lbl]
3411                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3412                    parms[c][closest] = vali
3413                elif dist > closeX: # insert after
3414                    parms[c].insert(closest+1,vali)
3415                else:
3416                    parms[c].insert(closest,vali)
3417            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3418                self.parent.IMfileList[closest] = fil
3419            elif dist > closeX: # insert after
3420                self.parent.IMfileList.insert(closest+1,fil)
3421            else:
3422                self.parent.IMfileList.insert(closest,fil)
3423            self.FillList(parms)
3424# Autointegration end
3425###########################################################################
Note: See TracBrowser for help on using the repository browser.