source: trunk/GSASIIimgGUI.py @ 3454

Last change on this file since 3454 was 3454, checked in by toby, 4 years ago

note that OnLoadMultiControls? looks for twoth, not in images; warn not available

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