source: trunk/GSASIIimgGUI.py @ 3170

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

implement saving of data & plot window sizes (along with positions)
fix issues with imageGUI displays; arrange Mask sliders to match Image Controls sliders

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