source: trunk/GSASIIimgGUI.py @ 3326

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

fix missing setdist error in Copy selected for image controls
set Bruker image reader to give Ka1 for wavelength (better than Ka-bar for calibration)
modify SumDialog? to do upon request PWDR average instead of sum - works nicely with Bruker image data
some menu changes with this

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