source: trunk/GSASIIimgGUI.py @ 3180

Last change on this file since 3180 was 3180, checked in by toby, 5 years ago

autointegration updates

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