source: trunk/GSASIIimgGUI.py @ 3333

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

add new Parm menu item Load Multi Contols for load image controls for a suite of images based on 2-theta setting for detector position.

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