source: trunk/GSASIIimgGUI.py @ 3172

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

change from wx.DefaultSize? to our defaults if window size is 'None'
A TODO in Integrate for saving the x,y --> 2th,azm map between images
complete fix to allowed super symmetries by lattice + pt. grp lookup
also fix operator check for complete super symmetry
finish cif import of super symmetry cases

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