source: trunk/GSASIIimgGUI.py @ 3358

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

implement new feature for image plotting - linescan; produces 1D plot of intensity along line originating at beam center.

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