source: trunk/GSASIIimgGUI.py @ 3463

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

process all masks in autointegrate

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