source: branch/2frame/GSASIIimgGUI.py @ 2915

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

split status bar in 2; one for tree & the other for data window status

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