source: trunk/GSASIIimgGUI.py @ 3154

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

new & imporved spot mask routines:
manual spot mask now fits position & size for selected spot.
changed mouse controls LB drag moves spot, shift-LB changes size & RB deletes spot
autospotmask improved - new algorithm

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