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

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

set up a local version of basinhopping.py in MCSA routine
implement frame position saving - NB: doesn't check if frame outside screen!
fix display of image controls after calibrate/recalibrate, etc.
occupancy --> site fraction in Afrac display
replace a bunch of TextCtrls? with ValidatedTxtCtrls? in phaseGUI

  • 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-11 21:29:11 +0000 (Tue, 11 Jul 2017) $
5# $Author: vondreele $
6# $Revision: 2927 $
7# $URL: branch/2frame/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 2927 2017-07-11 21:29:11Z 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: 2927 $")
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=G2G.wxID_IMRECALIBRATE,enable=True)
1033                G2frame.dataWindow.ImageEdit.Enable(id=G2G.wxID_IMCALIBRATE,enable=True)
1034                G2frame.dataWindow.ImageEdit.Enable(id=G2G.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=G2G.wxID_IMRECALIBRATE,enable=False)
1044                G2frame.dataWindow.ImageEdit.Enable(id=G2G.wxID_IMCALIBRATE,enable=False)
1045                G2frame.dataWindow.ImageEdit.Enable(id=G2G.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=G2G.wxID_IMCALIBRATE)
1177    G2frame.Bind(wx.EVT_MENU, OnRecalibrate, id=G2G.wxID_IMRECALIBRATE)
1178    G2frame.Bind(wx.EVT_MENU, OnRecalibAll, id=G2G.wxID_IMRECALIBALL)
1179    G2frame.Bind(wx.EVT_MENU, OnClearCalib, id=G2G.wxID_IMCLEARCALIB)
1180    if data.get('calibrant'):
1181        mode = True
1182    else:
1183        mode = False
1184#    G2frame.Enable(id=G2G.wxID_IMRECALIBRATE,enable=mode)
1185#    G2frame.Enable(id=G2G.wxID_IMCALIBRATE,enable=mode)
1186#    G2frame.Enable(id=G2G.wxID_IMRECALIBALL,enable=mode)
1187    G2frame.Bind(wx.EVT_MENU, OnIntegrate, id=G2G.wxID_IMINTEGRATE)
1188    G2frame.Bind(wx.EVT_MENU, OnIntegrateAll, id=G2G.wxID_INTEGRATEALL)
1189    G2frame.Bind(wx.EVT_MENU, OnCopyControls, id=G2G.wxID_IMCOPYCONTROLS)
1190    G2frame.Bind(wx.EVT_MENU, OnCopySelected, id=G2G.wxID_IMCOPYSELECTED)
1191    G2frame.Bind(wx.EVT_MENU, OnSaveControls, id=G2G.wxID_IMSAVECONTROLS)
1192    G2frame.Bind(wx.EVT_MENU, OnSaveMultiControls, id=G2G.wxID_SAVESELECTEDCONTROLS)
1193    G2frame.Bind(wx.EVT_MENU, OnLoadControls, id=G2G.wxID_IMLOADCONTROLS)
1194    G2frame.Bind(wx.EVT_MENU, OnTransferAngles, id=G2G.wxID_IMXFERCONTROLS)
1195    G2frame.Bind(wx.EVT_MENU, OnResetDist, id=G2G.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=G2G.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    G2frame.dataWindow.SetDataSize()
1228   
1229################################################################################
1230##### Masks
1231################################################################################
1232def CleanupMasks(data):
1233    '''If a mask creation is not completed, an empty mask entry is created in the
1234    masks array. This cleans them out. It is called when the masks page is first loaded
1235    and before saving them or after reading them in. This should also probably be done
1236    before they are used for integration.
1237    '''
1238    for key in ['Points','Rings','Arcs','Polygons']:
1239        data[key] = data.get(key,[])
1240        l1 = len(data[key])
1241        data[key] = [i for i in data[key] if len(i)]
1242        l2 = len(data[key])
1243        if GSASIIpath.GetConfigValue('debug') and l1 != l2:
1244            print 'Mask Cleanup:',key,'was',l1,'entries','now',l2
1245   
1246def UpdateMasks(G2frame,data):
1247    '''Shows and handles the controls on the "Masks" data tree entry
1248    '''
1249   
1250    def OnTextMsg(event):
1251        Obj = event.GetEventObject()
1252        Obj.SetToolTipString('Drag this mask on 2D Powder Image with mouse to change ')
1253
1254    def Replot(*args,**kwargs):
1255        wx.CallAfter(G2plt.PlotExposedImage,G2frame)
1256
1257    def newReplot(*args,**kwargs):
1258        wx.CallAfter(G2plt.PlotExposedImage,G2frame,newPlot=True)
1259
1260    def onDeleteMask(event):
1261        Obj = event.GetEventObject()
1262        typ = Obj.locationcode.split('+')[1]
1263        num = int(Obj.locationcode.split('+')[2])
1264        del(data[typ][num])
1265        wx.CallAfter(UpdateMasks,G2frame,data)
1266        G2plt.PlotExposedImage(G2frame,event=event)
1267
1268    def onDeleteFrame(event):
1269        data['Frames'] = []
1270        wx.CallAfter(UpdateMasks,G2frame,data)
1271        G2plt.PlotExposedImage(G2frame,event=event)
1272
1273    def OnCopyMask(event):
1274        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1275        if len(Names) == 1:
1276            G2frame.ErrorDialog('Nothing to copy masks to','There must be more than one "IMG" pattern')
1277            return
1278        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
1279        Names.pop(Names.index(Source))
1280        Data = copy.deepcopy(data)
1281        Thresh = Data.pop('Thresholds')     # & remove it as well
1282        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy mask data','Copy masks from '+Source+' to:',Names)
1283        try:
1284            if dlg.ShowModal() == wx.ID_OK:
1285                items = dlg.GetSelections()
1286                for item in items:
1287                    name = Names[item]
1288                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1289                    MId = G2gd.GetGPXtreeItemId(G2frame,Id,'Masks')
1290                    Mask = G2frame.GPXtree.GetItemPyData(MId)
1291                    Mask.update(copy.deepcopy(Data))
1292                    Mask['Thresholds'][1][0] = Thresh[1][0]  #copy only lower threshold
1293                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Masks'),Mask)
1294        finally:
1295            dlg.Destroy()
1296
1297    def OnSaveMask(event):
1298        CleanupMasks(data)
1299        pth = G2G.GetExportPath(G2frame)
1300        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1301            'image mask files (*.immask)|*.immask',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1302        try:
1303            if dlg.ShowModal() == wx.ID_OK:
1304                filename = dlg.GetPath()
1305                filename = os.path.splitext(filename)[0]+'.immask'
1306                File = open(filename,'w')
1307                keys = ['Points','Rings','Arcs','Polygons','Frames','Thresholds']
1308                for key in keys:
1309                    File.write(key+':'+str(data[key])+'\n')
1310                File.close()
1311        finally:
1312            dlg.Destroy()
1313       
1314    def OnLoadMask(event):
1315        if event.Id == G2G.wxID_MASKLOADNOT:
1316            ignoreThreshold = True
1317        else:
1318            ignoreThreshold = False
1319        pth = G2G.GetImportPath(G2frame)
1320        if not pth: pth = '.'
1321        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1322            'image mask files (*.immask)|*.immask',wx.OPEN)
1323        try:
1324            if dlg.ShowModal() == wx.ID_OK:
1325                filename = dlg.GetPath()
1326                File = open(filename,'r')
1327                save = {}
1328                oldThreshold = data['Thresholds'][0]
1329                S = File.readline()
1330                while S:
1331                    if S[0] == '#':
1332                        S = File.readline()
1333                        continue
1334                    [key,val] = S.strip().split(':',1)
1335                    if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
1336                        if ignoreThreshold and key == 'Thresholds':
1337                            S = File.readline() 
1338                            continue
1339                        save[key] = eval(val)
1340                        if key == 'Thresholds':
1341                            save[key][0] = oldThreshold
1342                            save[key][1][1] = min(oldThreshold[1],save[key][1][1])
1343                    S = File.readline()
1344                File.close()
1345                data.update(save)
1346                CleanupMasks(data)
1347                wx.CallAfter(UpdateMasks,G2frame,data)
1348                G2plt.PlotExposedImage(G2frame,event=event)               
1349        finally:
1350            dlg.Destroy()
1351           
1352    def OnAutoSpotMask(event):
1353        'Do auto search for spot masks'
1354        if wx.MessageDialog(G2frame.dataWindow,'NB: This will clear any old spot masks',
1355            'Auto Spot Masks', wx.OK|wx.CANCEL).ShowModal() == wx.ID_OK:
1356            Controls = copy.deepcopy(G2frame.GPXtree.GetItemPyData( 
1357                G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')))
1358            Error = G2img.AutoSpotMasks(G2frame.ImageZ,data,Controls)
1359            if not Error is None:
1360                G2frame.ErrorDialog('Auto spot search error',Error)
1361            wx.CallAfter(UpdateMasks,G2frame,data)
1362            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
1363
1364    def OnDeleteSpotMask(event):
1365        data['Points'] = []
1366        wx.CallAfter(UpdateMasks,G2frame,data)
1367        wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)         
1368           
1369    def ToggleSpotMaskMode(event):
1370        G2plt.ToggleMultiSpotMask(G2frame)
1371       
1372    def OnNewArcMask(event):
1373        'Start a new arc mask'
1374        G2frame.MaskKey = 'a'
1375        G2plt.OnStartMask(G2frame)
1376       
1377    def OnNewRingMask(event):
1378        'Start a new ring mask'
1379        G2frame.MaskKey = 'r'
1380        G2plt.OnStartMask(G2frame)
1381       
1382    def OnNewPolyMask(event):
1383        'Start a new polygon mask'
1384        G2frame.MaskKey = 'p'
1385        G2plt.OnStartMask(G2frame)
1386       
1387    def OnNewFrameMask(event):
1388        'Start a new Frame mask'
1389        G2frame.MaskKey = 'f'
1390        G2plt.OnStartMask(G2frame)
1391       
1392    def MaxSizer():
1393        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
1394        for the image, as well as callback routines.
1395        '''
1396        def OnNewVal(invalid,value,tc):
1397            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
1398            the value into the data['range'] nested list).
1399            This adjusts the slider positions to match the current values
1400            '''
1401            scaleSel.SetSelection(len(scaleChoices)-1)
1402            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
1403            if r11 != Range[1][1]:
1404                Range[1][1] = r11
1405                maxVal.SetValue(int(Range[1][1]))
1406            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
1407            if r10 != Range[1][0]:
1408                Range[1][0] = r10
1409                minVal.SetValue(int(Range[1][0]))
1410            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1411            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1412            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1413            maxSel.SetValue(sv1)
1414            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1415            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1416            minSel.SetValue(sv0)
1417            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1418            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1419            if mplOld:
1420                Page.canvas.draw()
1421            else:
1422                Page.canvas.draw_idle()
1423           
1424        G2frame.prevMaxValue = None   
1425        def OnMaxSlider(event):
1426            val = maxSel.GetValue()
1427            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
1428            scaleSel.SetSelection(len(scaleChoices)-1)
1429            G2frame.prevMaxValue = val
1430            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1431            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
1432            maxVal.SetValue(int(0.5+Range[1][1]))
1433            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1434            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
1435            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1436            minSel.SetValue(sv0)
1437            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1438            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1439            if mplOld:
1440                Page.canvas.draw()
1441            else:
1442                Page.canvas.draw_idle()
1443           
1444        G2frame.prevMinValue = None   
1445        def OnMinSlider(event):
1446            val = minSel.GetValue()
1447            scaleSel.SetSelection(len(scaleChoices)-1)
1448            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
1449            G2frame.prevMinValue = val
1450            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1451            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
1452            minVal.SetValue(int(Range[1][0]))
1453            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1454            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1455            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1456            maxSel.SetValue(sv1)
1457            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1458            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1459            if mplOld:
1460                Page.canvas.draw()
1461            else:
1462                Page.canvas.draw_idle()
1463           
1464        def OnAutoSet(event):
1465            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
1466            for the image so that 95% (etc.) of pixels are inside the color map limits.
1467            An equal number of pixels are dropped at the minimum and maximum levels.
1468            '''
1469            try:
1470                val = int(event.GetEventObject().GetStringSelection()[:-1])
1471                margin = (100-val)/2.
1472            except:
1473                margin = 0
1474                event.GetEventObject().SetSelection(0)
1475            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1476            if margin == 0:
1477                Range[1] = list(Range[0])
1478            else:
1479                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
1480                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
1481            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1482            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1483            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1484            maxSel.SetValue(sv1)
1485            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1486            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1487            minSel.SetValue(sv0)
1488            minVal.SetValue(int(Range[1][0]))
1489            maxVal.SetValue(int(Range[1][1]))
1490            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1491            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1492            if mplOld:
1493                Page.canvas.draw()
1494            else:
1495                Page.canvas.draw_idle()
1496
1497        mplv = mpl.__version__.split('.')
1498        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
1499        # Plot color scaling uses limits as below:
1500        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
1501        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
1502        maxSizer = wx.GridBagSizer(0,0)
1503        r = c = 0
1504        maxSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Max intensity'),(r,c))
1505        c += 1
1506        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
1507        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
1508        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
1509        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1510        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1511        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1512        maxSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv1,size=(300,-1))
1513        maxSizer.Add(maxSel,(r,c),flag=wx.EXPAND)
1514        maxSizer.AddGrowableCol(c)
1515        c += 1
1516        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
1517        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],1,min=Range[0][0]+1,
1518            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1519        maxSizer.Add(maxVal,(r,c))
1520        c += 1
1521        scaleChoices = ("100%","99%","95%","90%","80%","?")
1522        scaleSel = wx.Choice(G2frame.dataWindow,choices=scaleChoices,size=(-1,-1))
1523        if (Range[1][0] == Range[0][0] and
1524            Range[1][1] == Range[0][1]):
1525            scaleSel.SetSelection(0)
1526        else:
1527            scaleSel.SetSelection(len(scaleChoices)-1)
1528        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
1529        maxSizer.Add(scaleSel,(r,c),(2,1),flag=wx.ALIGN_CENTER)
1530        c = 0
1531        r = 1
1532        maxSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Min intensity'),(r,c))
1533        c += 1
1534        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
1535        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
1536        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
1537        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1538        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1539        minSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv0,size=(300,-1))
1540        maxSizer.Add(minSel,(r,c),flag=wx.EXPAND|wx.ALL)
1541        c += 1
1542        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
1543        minVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],0,
1544            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1545        maxSizer.Add(minVal,(r,c))
1546        return maxSizer
1547
1548    G2frame.dataWindow.ClearData()
1549    startScroll = None
1550    if G2frame.dataWindow:
1551        startScroll = G2frame.dataWindow.GetScrollPos(wx.VERTICAL) # save scroll position
1552    else:
1553        CleanupMasks(data) # posting page for 1st time; clean out anything unfinished
1554    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.MaskMenu)
1555    G2frame.Bind(wx.EVT_MENU, OnCopyMask, id=G2G.wxID_MASKCOPY)
1556    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOAD)
1557    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOADNOT)
1558    G2frame.Bind(wx.EVT_MENU, OnSaveMask, id=G2G.wxID_MASKSAVE)
1559    G2frame.Bind(wx.EVT_MENU, OnAutoSpotMask, id=G2G.wxID_FINDSPOTS)
1560    G2frame.Bind(wx.EVT_MENU, OnDeleteSpotMask, id=G2G.wxID_DELETESPOTS)
1561    G2frame.Bind(wx.EVT_MENU, ToggleSpotMaskMode, id=G2G.wxID_NEWMASKSPOT)
1562    G2frame.Bind(wx.EVT_MENU, OnNewArcMask, id=G2G.wxID_NEWMASKARC)
1563    G2frame.Bind(wx.EVT_MENU, OnNewRingMask, id=G2G.wxID_NEWMASKRING)
1564    G2frame.Bind(wx.EVT_MENU, OnNewPolyMask, id=G2G.wxID_NEWMASKPOLY)
1565    G2frame.Bind(wx.EVT_MENU, OnNewFrameMask, id=G2G.wxID_NEWMASKFRAME)
1566    if G2frame.MaskKey == 'f':
1567        G2frame.GetStatusBar().SetStatusText('Frame mask active - LB pick next point, RB close polygon',1)
1568    elif G2frame.MaskKey == 'p':
1569        G2frame.GetStatusBar().SetStatusText('Polygon mask active - LB pick next point, RB close polygon',1)
1570    elif G2frame.MaskKey == 'a':
1571        G2frame.GetStatusBar().SetStatusText('Arc mask active - LB pick arc location',1)
1572    elif G2frame.MaskKey == 'r':
1573        G2frame.GetStatusBar().SetStatusText('Ring mask active - LB pick ring location',1)
1574    else:
1575        G2frame.GetStatusBar().SetStatusText("To add mask: press a,r,s,p or f on 2D image for arc/ring/spot/polygon/frame",1)
1576    mainSizer = G2frame.dataWindow.GetSizer()
1577    mainSizer.Add((5,10),0)
1578
1579    thresh = data['Thresholds']         #min/max intensity range
1580    Spots = data['Points']               #x,y,radius in mm
1581    Rings = data['Rings']               #radius, thickness
1582    Polygons = data['Polygons']         #3+ x,y pairs
1583    if 'Frames' not in data:
1584        data['Frames'] = []
1585    frame = data['Frames']             #3+ x,y pairs
1586    Arcs = data['Arcs']                 #radius, start/end azimuth, thickness
1587
1588    ######################################################################
1589    CId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')
1590    controlData = G2frame.GPXtree.GetItemPyData(CId)
1591    Range = controlData['range']
1592    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1593    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1594
1595    littleSizer = wx.FlexGridSizer(0,3,0,5)
1596    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper limits '),0,WACV)
1597    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][0]),style=wx.TE_READONLY)
1598    littleSizer.Add(Text,0,WACV)
1599    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1600    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][1]),style=wx.TE_READONLY)
1601    littleSizer.Add(Text,0,WACV)
1602    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1603    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper thresholds '),0,WACV)
1604    lowerThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=0,
1605        min=thresh[0][0],OnLeave=newReplot,typeHint=int)
1606    littleSizer.Add(lowerThreshold,0,WACV)
1607    upperThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=1,
1608        max=thresh[0][1],OnLeave=newReplot,typeHint=int)
1609    littleSizer.Add(upperThreshold,0,WACV)
1610    mainSizer.Add(littleSizer,0,)
1611    if len(Spots):
1612        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Spot masks')
1613        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1614        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1615        littleSizer = wx.FlexGridSizer(0,3,0,5)
1616        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' position, mm'),0,WACV)
1617        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' diameter, mm'),0,WACV)
1618        littleSizer.Add((5,0),0)
1619        for i in range(len(Spots)):
1620            if len(Spots[i]):
1621                x,y,d = Spots[i]
1622                spotText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.2f,%.2f" % (x,y)),
1623                    style=wx.TE_READONLY)
1624                spotText.SetBackgroundColour(VERY_LIGHT_GREY)
1625                littleSizer.Add(spotText,0,WACV)
1626                spotText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1627                spotDiameter = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Spots[i],key=2,
1628                    max=100.,OnLeave=Replot,nDig=[8,2])
1629                littleSizer.Add(spotDiameter,0,WACV)
1630                spotDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1631                    locationcode='Delete+Points+'+str(i),handler=onDeleteMask)
1632                littleSizer.Add(spotDelete,0,WACV)
1633        mainSizer.Add(littleSizer,0,)
1634    if Rings:
1635        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Ring masks')
1636        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1637        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1638        littleSizer = wx.FlexGridSizer(0,3,0,5)
1639        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' 2-theta,deg'),0,WACV)
1640        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
1641        littleSizer.Add((5,0),0)
1642        for i in range(len(Rings)):
1643            if Rings[i]:
1644                ringText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (Rings[i][0])),
1645                    style=wx.TE_READONLY)
1646                ringText.SetBackgroundColour(VERY_LIGHT_GREY)
1647                ringText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1648                littleSizer.Add(ringText,0,WACV)
1649                ringThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Rings[i],key=1,
1650                    min=0.001,max=1.,OnLeave=Replot,nDig=[8,3])
1651                littleSizer.Add(ringThick,0,WACV)
1652                ringDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1653                    locationcode='Delete+Rings+'+str(i),handler=onDeleteMask)
1654                littleSizer.Add(ringDelete,0,WACV)
1655        mainSizer.Add(littleSizer,0,)
1656    if Arcs:
1657        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Arc masks')
1658        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1659        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1660        littleSizer = wx.FlexGridSizer(0,4,0,5)
1661        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' 2-theta,deg'),0,WACV)
1662        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' azimuth, deg'),0,WACV)
1663        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
1664        littleSizer.Add((5,0),0)
1665        for i in range(len(Arcs)):
1666            if Arcs[i]:
1667                tth,azimuth,thick = Arcs[i]
1668                arcText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (tth)),
1669                    style=wx.TE_READONLY)
1670                arcText.SetBackgroundColour(VERY_LIGHT_GREY)
1671                arcText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1672                littleSizer.Add(arcText,0,WACV)
1673                azmText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%d,%d" % (azimuth[0],azimuth[1])),
1674                    style=wx.TE_READONLY)
1675                azmText.SetBackgroundColour(VERY_LIGHT_GREY)
1676                azmText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1677                littleSizer.Add(azmText,0,WACV)
1678                arcThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Arcs[i],key=2,
1679                    min=0.001,max=20.,OnLeave=Replot,nDig=[8,3])
1680                littleSizer.Add(arcThick,0,WACV)
1681                arcDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1682                    locationcode='Delete+Arcs+'+str(i),handler=onDeleteMask)
1683                littleSizer.Add(arcDelete,0,WACV)
1684        mainSizer.Add(littleSizer,0,)
1685    if Polygons:
1686        lbl = wx.StaticText(parent=G2frame.dataWindow,
1687            label=' Polygon masks (on plot RB vertex drag to move, LB vertex drag to insert)')
1688        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1689        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1690        littleSizer = wx.FlexGridSizer(0,2,0,5)
1691        for i in range(len(Polygons)):
1692            if Polygons[i]:
1693                polyList = []
1694                for x,y in Polygons[i]:
1695                    polyList.append("%.2f, %.2f"%(x,y))
1696                polyText = wx.ComboBox(G2frame.dataWindow,value=polyList[0],choices=polyList,style=wx.CB_READONLY)
1697                littleSizer.Add(polyText,0,WACV)
1698                polyDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1699                    locationcode='Delete+Polygons+'+str(i),handler=onDeleteMask)
1700                littleSizer.Add(polyDelete,0,WACV)
1701        mainSizer.Add(littleSizer,0,)
1702    if frame:
1703        lbl = wx.StaticText(parent=G2frame.dataWindow,
1704            label=' Frame mask (on plot RB vertex drag to move, LB vertex drag to insert)')
1705        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1706        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1707        littleSizer = wx.FlexGridSizer(0,2,0,5)
1708        frameList = []
1709        for x,y in frame:
1710            frameList.append("%.2f, %.2f"%(x,y))
1711        frameText = wx.ComboBox(G2frame.dataWindow,value=frameList[0],choices=frameList,style=wx.CB_READONLY)
1712        littleSizer.Add(frameText,0,WACV)
1713        frameDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1714            locationcode='Delete+Frame',handler=onDeleteFrame)
1715        littleSizer.Add(frameDelete,0,WACV)
1716        mainSizer.Add(littleSizer,0,)
1717    G2frame.dataWindow.SetDataSize()
1718    if startScroll: # reset scroll to saved position
1719        G2frame.dataWindow.Scroll(0,startScroll) # set to saved scroll position
1720        wx.Yield()
1721
1722################################################################################
1723##### Stress/Strain
1724################################################################################
1725
1726def UpdateStressStrain(G2frame,data):
1727    '''Shows and handles the controls on the "Stress/Strain"
1728    data tree entry
1729    '''
1730   
1731    def OnAppendDzero(event):
1732        data['d-zero'].append({'Dset':1.0,'Dcalc':0.0,'pixLimit':10,'cutoff':1.0,
1733            'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0],'fixDset':False})
1734        UpdateStressStrain(G2frame,data)
1735       
1736    def OnUpdateDzero(event):
1737        for item in data['d-zero']:
1738            if item['Dcalc']:   #skip unrefined ones
1739                item['Dset'] = item['Dcalc']
1740        UpdateStressStrain(G2frame,data)
1741           
1742    def OnCopyStrSta(event):
1743        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1744        if len(Names) == 1:
1745            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
1746            return
1747        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
1748        Names.pop(Names.index(Source))
1749        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy stress/strain controls','Copy controls from '+Source+' to:',Names)
1750        try:
1751            if dlg.ShowModal() == wx.ID_OK:
1752                items = dlg.GetSelections()
1753                for item in items:
1754                    name = Names[item]
1755                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1756                    CId = G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain')
1757                    oldData = G2frame.GPXtree.GetItemPyData(CId)
1758                    load = oldData.get('Sample load',0.0)
1759                    Data = copy.deepcopy(data)
1760                    Data['Sample load'] = load
1761                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'),Data)
1762        finally:
1763            dlg.Destroy()
1764            G2frame.GPXtree.SelectItem(G2frame.PickId)
1765
1766    def OnLoadStrSta(event):
1767        pth = G2G.GetImportPath(G2frame)
1768        if not pth: pth = '.'
1769        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1770            'image control files (*.strsta)|*.strsta',wx.OPEN)
1771        try:
1772            if dlg.ShowModal() == wx.ID_OK:
1773                filename = dlg.GetPath()
1774                File = open(filename,'r')
1775                S = File.read()
1776                data = eval(S)
1777                Controls = G2frame.GPXtree.GetItemPyData(
1778                    G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1779                G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1780                UpdateStressStrain(G2frame,data)
1781                G2plt.PlotExposedImage(G2frame,event=event)
1782                G2plt.PlotStrain(G2frame,data,newPlot=True)
1783                File.close()
1784        finally:
1785            dlg.Destroy()
1786
1787    def OnSaveStrSta(event):
1788        pth = G2G.GetExportPath(G2frame)
1789        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1790            'image control files (*.strsta)|*.strsta',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1791        try:
1792            if dlg.ShowModal() == wx.ID_OK:
1793                filename = dlg.GetPath()
1794                File = open(filename,'w')
1795                keys = ['Type','Sample phi','Sample z','Sample load']
1796                keys2 = ['Dset','Dcalc','pixLimit','cutoff','Emat','fixDset']
1797                File.write('{\n\t')
1798                for key in keys:
1799                    if key in 'Type':
1800                        File.write("'"+key+"':'"+data[key]+"',")
1801                    else:
1802                        File.write("'"+key+"':"+str(data[key])+',')
1803                File.write('\n\t'+"'d-zero':[\n")
1804                for data2 in data['d-zero']:
1805                    File.write('\t\t{')
1806                    for key in keys2:
1807                        File.write("'"+key+"':"+str(data2[key])+',')
1808                    File.write("'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]]},\n")
1809                File.write('\t]\n}')
1810                File.close()
1811        finally:
1812            dlg.Destroy()
1813           
1814    def OnStrStaSample(event):
1815        filename = ''
1816        pth = G2G.GetImportPath(G2frame)
1817        if not pth: pth = '.'
1818        dlg = wx.FileDialog(G2frame, 'Choose multihistogram metadata text file', pth, '', 
1819            'metadata file (*.*)|*.*',wx.OPEN)
1820        try:
1821            if dlg.ShowModal() == wx.ID_OK:
1822                filename = dlg.GetPath()
1823                File = open(filename,'r')
1824                S = File.readline()
1825                newItems = []
1826                itemNames = []
1827                Comments = []
1828                while S:
1829                    if S[0] == '#':
1830                        Comments.append(S)
1831                        S = File.readline()
1832                        continue
1833                    S = S.replace(',',' ').replace('\t',' ')
1834                    Stuff = S[:-1].split()
1835                    itemNames.append(Stuff[0])
1836                    newItems.append(Stuff[1:])
1837                    S = File.readline()               
1838                File.close()
1839        finally:
1840            dlg.Destroy()
1841        if not filename:
1842            G2frame.ErrorDialog('Nothing to do','No file selected')
1843            return
1844        dataDict = dict(zip(itemNames,newItems))
1845        ifany = False
1846        Names = [' ','Sample phi','Sample z','Sample load']
1847        dlg = G2G.G2ColumnIDDialog( G2frame,' Choose multihistogram metadata columns:',
1848            'Select columns',Comments,Names,np.array(newItems).T)
1849        try:
1850            if dlg.ShowModal() == wx.ID_OK:
1851                colNames,newData = dlg.GetSelection()
1852                dataDict = dict(zip(itemNames,newData.T))
1853                for item in colNames:
1854                    if item != ' ':
1855                        ifany = True
1856        finally:
1857            dlg.Destroy()
1858        if not ifany:
1859            G2frame.ErrorDialog('Nothing to do','No columns identified')
1860            return
1861        histList = []
1862        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)       
1863        while item:
1864            name = G2frame.GPXtree.GetItemText(item)
1865            if name.startswith('IMG'):
1866                histList.append(name)
1867            item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
1868        colIds = {}
1869        for i,name in enumerate(colNames):
1870            if name != ' ':
1871                colIds[name] = i
1872        for hist in histList:
1873            name = hist.split()[1]  #this is file name
1874            if name in dataDict:
1875                newItems = {}
1876                for item in colIds:
1877                    newItems[item] = float(dataDict[name][colIds[item]])
1878                Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,hist)
1879                stsrData = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'))
1880                stsrData.update(newItems)       
1881        UpdateStressStrain(G2frame,data)       
1882   
1883    def OnPlotStrSta(event):
1884        Controls = G2frame.GPXtree.GetItemPyData(
1885            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1886        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1887        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1888        G2plt.PlotExposedImage(G2frame,event=event)
1889        G2frame.G2plotNB.Delete('Ring Intensities')
1890        G2plt.PlotXY(G2frame,RingInt,labelX='Azimuth',
1891            labelY='MRD',newPlot=True,Title='Ring Intensities',
1892            names=Names,lines=True)
1893       
1894    def OnSaveStrRing(event):
1895        Controls = G2frame.GPXtree.GetItemPyData(
1896            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1897        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1898        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1899        pth = G2G.GetExportPath(G2frame)
1900        dlg = wx.FileDialog(G2frame, 'Choose strain ring intensity file', pth, '', 
1901            'ring intensity file (*.txt)|*.txt',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1902        try:
1903            if dlg.ShowModal() == wx.ID_OK:
1904                filename = dlg.GetPath()
1905                File = open(filename,'w')
1906                for i,name in enumerate(Names):
1907                    File.write('%s%s\n'%(' Ring intensity for ',name))
1908                    File.write('%12s %12s\n'%('Azimuth','RMD'))
1909                    for item in RingInt[i].T:
1910                        File.write(' %12.3f %12.3f\n'%(item[0],item[1]))
1911                    File.write('\n')
1912                File.close()
1913        finally:
1914            dlg.Destroy()
1915               
1916               
1917    def OnFitStrSta(event):
1918        Controls = G2frame.GPXtree.GetItemPyData(
1919            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1920        G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1921        print 'Strain fitting finished'
1922        UpdateStressStrain(G2frame,data)
1923        G2plt.PlotExposedImage(G2frame,event=event)
1924        G2plt.PlotStrain(G2frame,data,newPlot=True)
1925       
1926    def OnFitAllStrSta(event):
1927        choices = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1928        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices)
1929        names = []
1930        if dlg.ShowModal() == wx.ID_OK:
1931            for sel in dlg.GetSelections():
1932                names.append(choices[sel])
1933            Id =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Sequential strain fit results')
1934            if Id:
1935                SeqResult = G2frame.GPXtree.GetItemPyData(Id)
1936            else:
1937                SeqResult = {}
1938                Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text='Sequential strain fit results')
1939            SeqResult.update({'SeqPseudoVars':{},'SeqParFitEqList':[]})
1940        else:
1941            dlg.Destroy()
1942            return
1943        dlg.Destroy()
1944        if not names:
1945            return
1946        Reverse = False
1947        CopyForward = False
1948        choice = ['Reverse sequence','Copy from prev.',]
1949        dlg = wx.MultiChoiceDialog(G2frame,'Sequential controls','Select controls',choice)
1950        if dlg.ShowModal() == wx.ID_OK:
1951            for sel in dlg.GetSelections():
1952                if sel:
1953                    CopyForward = True
1954                else:
1955                    Reverse = True
1956        dlg.Destroy()
1957        dlg = wx.ProgressDialog('Sequential IMG Strain fit','Data set name = '+names[0],len(names), 
1958            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT)         
1959        wx.BeginBusyCursor()
1960        goodnames = []
1961        if Reverse:
1962            names.reverse()
1963        try:
1964            for i,name in enumerate(names):
1965                print ' Sequential strain fit for ',name
1966                GoOn = dlg.Update(i,newmsg='Data set name = '+name)[0]
1967                if not GoOn:
1968                    break
1969                sId =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1970                G2frame.Image = sId
1971                Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Image Controls'))
1972                StaCtrls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Stress/Strain'))
1973                if not len(StaCtrls['d-zero']):
1974                    continue
1975                goodnames.append(name)
1976                Npix,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(sId)
1977                image = GetImageZ(G2frame,Controls)
1978                sig = []
1979                varyList = []
1980                variables = []
1981                if i and CopyForward:
1982                    for j,ring in enumerate(StaCtrls['d-zero']):
1983                        ring['Emat'] = variables[4*j:4*j+3]
1984                #get results from previous & put in StaCtrls
1985                G2img.FitStrSta(image,StaCtrls,Controls)
1986                G2plt.PlotStrain(G2frame,StaCtrls,newPlot=True)
1987                parmDict = {'Sample load':StaCtrls['Sample load'],}
1988                varyNames = ['e11','e12','e22']
1989                Nvar = 5*len(StaCtrls['d-zero'])
1990                coVar = np.zeros((Nvar,Nvar))
1991                for j,item in enumerate(StaCtrls['d-zero']):
1992                    variables += item['Emat']
1993                    sig += item['Esig']
1994                    varylist = ['%d;%s'%(j,Name) for Name in varyNames]
1995                    varyList += varylist
1996                    parmDict.update(dict(zip(varylist,item['Emat'])))
1997                    parmDict['%d;Dcalc'%(j)] = item['Dcalc']
1998                    variables.append(1.e6*(item['Dcalc']/item['Dset']-1.))
1999                    varyList.append('%d;h-mstrain'%(j))
2000                    sig.append(0)
2001                    parmDict['%d;Ivar'%(j)] = item['Ivar']
2002                    variables.append(item['Ivar'])
2003                    varyList.append('%d;Ivar'%(j))
2004                    sig.append(0)
2005                    j4 = j*5
2006                    coVar[j4:j4+3,j4:j4+3] = item['covMat']
2007                SeqResult[name] = {'variables':variables,'varyList':varyList,'sig':sig,'Rvals':[],
2008                    'covMatrix':coVar,'title':name,'parmDict':parmDict}
2009            else:
2010                SeqResult['histNames'] = goodnames
2011                dlg.Destroy()
2012                print ' ***** Sequential strain refinement successful *****'
2013        finally:
2014            wx.EndBusyCursor()   
2015        SeqResult['histNames'] = choices
2016        G2frame.GPXtree.SetItemPyData(Id,SeqResult)
2017        G2frame.GPXtree.SelectItem(Id)
2018        print 'All images fitted'
2019       
2020    def SamSizer():
2021       
2022        def OnStrainType(event):
2023            data['Type'] = strType.GetValue()
2024       
2025        samSizer = wx.BoxSizer(wx.HORIZONTAL)
2026        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Strain type: '),0,WACV)
2027        strType = wx.ComboBox(G2frame.dataWindow,value=data['Type'],choices=['True','Conventional'],
2028            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2029        strType.SetValue(data['Type'])
2030        strType.Bind(wx.EVT_COMBOBOX, OnStrainType)
2031        samSizer.Add(strType,0,WACV)
2032       
2033        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample phi: '),0,WACV)
2034        samPhi = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample phi',nDig=(10,3),typeHint=float,min=-360.,max=360.)
2035        samSizer.Add(samPhi,0,WACV)
2036        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample delta-z(mm): '),0,WACV)
2037        samZ = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample z',nDig=(10,3),typeHint=float)
2038        samSizer.Add(samZ,0,WACV)
2039        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample load(MPa): '),0,WACV)
2040        samLoad = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample load',
2041                nDig=[8,3],typeHint=float,)
2042        samSizer.Add(samLoad,0,WACV)
2043
2044        return samSizer
2045       
2046    def DzeroSizer():
2047   
2048        def OnDzero(invalid,value,tc):
2049            data['d-zero'] = G2mth.sortArray(data['d-zero'],'Dset',reverse=True)
2050            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
2051            if len(Ring):
2052                data['d-zero'][Indx[tc.GetId()]].update(R)
2053            else:
2054                G2frame.ErrorDialog('Strain peak selection','WARNING - No points found for this ring selection')
2055               
2056            wx.CallAfter(UpdateStressStrain,G2frame,data)
2057            G2plt.PlotExposedImage(G2frame,event=tc.event,newPlot=False)
2058            G2plt.PlotStrain(G2frame,data,newPlot=True)
2059           
2060        def OnDeleteDzero(event):
2061            Obj = event.GetEventObject()
2062            del(data['d-zero'][delIndx.index(Obj)])
2063            UpdateStressStrain(G2frame,data)
2064            G2plt.PlotExposedImage(G2frame,event=event,newPlot=True)
2065            G2plt.PlotStrain(G2frame,data,newPlot=True)
2066       
2067        def OnCutOff(invalid,value,tc):
2068            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
2069            G2plt.PlotExposedImage(G2frame,event=tc.event)
2070            G2plt.PlotStrain(G2frame,data,newPlot=True)
2071       
2072        def OnPixLimit(event):
2073            Obj = event.GetEventObject()
2074            data['d-zero'][Indx[Obj.GetId()]]['pixLimit'] = int(Obj.GetValue())
2075            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[Obj.GetId()]],G2frame.ImageZ,Controls)
2076            G2plt.PlotExposedImage(G2frame,event=event)
2077            G2plt.PlotStrain(G2frame,data,newPlot=True)
2078           
2079        def OnFixDset(event):
2080            Obj = event.GetEventObject()
2081            data['d-zero'][Indx[Obj.GetId()]]['fixDset'] = Obj.GetValue()
2082           
2083        Indx = {}
2084        delIndx = []   
2085        dzeroSizer = wx.FlexGridSizer(0,8,5,5)
2086        for id,dzero in enumerate(data['d-zero']):
2087            dzeroSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=(' d-zero #%d: '%(id))),0,WACV)
2088            dZero = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data['d-zero'][id],'Dset',
2089                min=0.25,max=20.,nDig=(10,5),typeHint=float,OnLeave=OnDzero)
2090            dzeroSizer.Add(dZero,0,WACV)
2091            Indx[dZero.GetId()] = id
2092            dfix = wx.CheckBox(G2frame.dataWindow,label='Use Poisson mean?')
2093            dfix.SetValue(dzero.get('fixDset',False))
2094            dfix.Bind(wx.EVT_CHECKBOX,OnFixDset)
2095            Indx[dfix.GetId()] = id
2096            dzeroSizer.Add(dfix,0,WACV)
2097            dzeroSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=(' d-zero ave: %.5f'%(dzero['Dcalc']))),0,WACV)
2098               
2099            dzeroSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Min ring I/Ib '),0,WACV)
2100            cutOff = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data['d-zero'][id],'cutoff',
2101                    min=0.5,max=20.,nDig=(10,1),typeHint=float,OnLeave=OnCutOff)
2102            Indx[cutOff.GetId()] = id
2103            dzeroSizer.Add(cutOff,0,WACV)
2104       
2105            dzeroSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Pixel search range '),0,WACV)
2106            pixLimit = wx.ComboBox(parent=G2frame.dataWindow,value=str(dzero['pixLimit']),choices=['1','2','5','10','15','20'],
2107                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2108            pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
2109            Indx[pixLimit.GetId()] = id
2110            dzeroSizer.Add(pixLimit,0,WACV)               
2111               
2112            dzeroSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=(' Strain tensor:')),WACV)
2113            names = ['e11','e12','e22']
2114            for i in range(3):
2115                dzeroSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=names[i]),0,WACV)
2116                tensorElem = wx.TextCtrl(G2frame.dataWindow,-1,value='%.2f'%(dzero['Emat'][i]),style=wx.TE_READONLY)
2117                tensorElem.SetBackgroundColour(VERY_LIGHT_GREY)
2118                dzeroSizer.Add(tensorElem,0,WACV)
2119            dzeroDelete = wx.CheckBox(parent=G2frame.dataWindow,label='delete?')
2120            dzeroDelete.Bind(wx.EVT_CHECKBOX,OnDeleteDzero)
2121            delIndx.append(dzeroDelete)
2122            dzeroSizer.Add(dzeroDelete,0,WACV)
2123           
2124        return dzeroSizer
2125       
2126# patches
2127    if 'Sample load' not in data:
2128        data['Sample load'] = 0.0
2129# end patches
2130   
2131    G2frame.dataWindow.ClearData()
2132    Controls = G2frame.GPXtree.GetItemPyData(
2133        G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))       
2134    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.StrStaMenu)
2135    G2frame.Bind(wx.EVT_MENU, OnAppendDzero, id=G2G.wxID_APPENDDZERO)
2136    G2frame.Bind(wx.EVT_MENU, OnUpdateDzero, id=G2G.wxID_UPDATEDZERO)
2137    G2frame.Bind(wx.EVT_MENU, OnFitStrSta, id=G2G.wxID_STRSTAFIT)
2138    G2frame.Bind(wx.EVT_MENU, OnPlotStrSta, id=G2G.wxID_STRSTAPLOT) 
2139    G2frame.Bind(wx.EVT_MENU, OnSaveStrRing, id=G2G.wxID_STRRINGSAVE) 
2140    G2frame.Bind(wx.EVT_MENU, OnFitAllStrSta, id=G2G.wxID_STRSTAALLFIT)
2141    G2frame.Bind(wx.EVT_MENU, OnCopyStrSta, id=G2G.wxID_STRSTACOPY)
2142    G2frame.Bind(wx.EVT_MENU, OnLoadStrSta, id=G2G.wxID_STRSTALOAD)
2143    G2frame.Bind(wx.EVT_MENU, OnSaveStrSta, id=G2G.wxID_STRSTASAVE)
2144    G2frame.Bind(wx.EVT_MENU, OnStrStaSample, id=G2G.wxID_STRSTSAMPLE)       
2145    if G2frame.StrainKey == 'a':    #probably doesn't happen
2146        G2frame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value',1)
2147    else:
2148        G2frame.GetStatusBar().SetStatusText("To add strain data: On 2D Powder Image, key a:add ring",1)
2149       
2150    mainSizer = G2frame.dataWindow.GetSizer()
2151    mainSizer.Add((5,10),0)
2152    mainSizer.Add(SamSizer())
2153    mainSizer.Add((5,10),0)
2154    mainSizer.Add(DzeroSizer())
2155    G2frame.dataWindow.SetDataSize()
2156   
2157###########################################################################
2158# Autointegration
2159###########################################################################
2160def ReadMask(filename):
2161    'Read a mask (.immask) file'
2162    File = open(filename,'r')
2163    save = {}
2164    S = File.readline()
2165    while S:
2166        if S[0] == '#':
2167            S = File.readline()
2168            continue
2169        [key,val] = S.strip().split(':',1)
2170        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
2171            save[key] = eval(val)
2172        S = File.readline()
2173    File.close()
2174    CleanupMasks(save)
2175    return save
2176
2177def ReadControls(filename):
2178    'read an image controls (.imctrl) file'
2179    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2180            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2181            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2182            'PolaVal','SampleAbs','dark image','background image']
2183    File = open(filename,'r')
2184    save = {}
2185    S = File.readline()
2186    while S:
2187        if S[0] == '#':
2188            S = File.readline()
2189            continue
2190        [key,val] = S.strip().split(':',1)
2191        if key in ['type','calibrant','binType','SampleShape',]:    #strings
2192            save[key] = val
2193        elif key in ['rotation']:
2194            save[key] = float(val)
2195        elif key in ['center',]:
2196            if ',' in val:
2197                save[key] = eval(val)
2198            else:
2199                vals = val.strip('[] ').split()
2200                save[key] = [float(vals[0]),float(vals[1])] 
2201        elif key in cntlList:
2202            save[key] = eval(val)
2203        S = File.readline()
2204    File.close()
2205    return save
2206
2207def Read_imctrl(imctrl_file):
2208    '''Read an image control file and record control parms into a dict, with some simple
2209    type conversions
2210    '''
2211    save = {'filename':imctrl_file}
2212    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
2213    if os.path.exists(immask_file):
2214        save['maskfile'] = immask_file
2215    else:
2216        save['maskfile'] = '(none)'
2217    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2218                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2219                        'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2220                        'PolaVal','SampleAbs','dark image','background image','setdist']
2221    File = open(imctrl_file,'r')
2222    fullIntegrate = False
2223    try:
2224        S = File.readline()
2225        while S:
2226            if S[0] == '#':
2227                S = File.readline()
2228                continue
2229            [key,val] = S.strip().split(':',1)
2230            if val.find(':') != -1:
2231                #print 'rejecting ',key,val
2232                S = File.readline()
2233                continue
2234            if key in ['type','calibrant','binType','SampleShape',]:    #strings
2235                save[key] = val
2236            elif key == 'rotation':
2237                save[key] = float(val)
2238            elif key == 'fullIntegrate':
2239                fullIntegrate = eval(val)
2240            elif key == 'LRazimuth':
2241                save['LRazimuth_min'],save['LRazimuth_max'] = eval(val)[0:2]
2242            elif key == 'IOtth':
2243                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
2244            elif key == 'center':
2245                if ',' in val:
2246                    vals = eval(val)
2247                else:
2248                    vals = val.strip('[] ').split()
2249                    vals = [float(vals[0]),float(vals[1])] 
2250                save['center_x'],save['center_y'] = vals[0:2]
2251            elif key in cntlList:
2252                save[key] = eval(val)
2253            S = File.readline()
2254    finally:
2255        File.close()
2256        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0.,0.
2257    return save
2258   
2259class AutoIntFrame(wx.Frame):
2260    '''Creates a wx.Frame window for the Image AutoIntegration.
2261    The intent is that this will be used as a non-modal dialog window.
2262   
2263    Implements a Start button that morphs into a pause and resume button.
2264    This button starts a processing loop that is repeated every
2265    :meth:`PollTime` seconds.
2266
2267    :param wx.Frame G2frame: main GSAS-II frame
2268    :param float PollTime: frequency in seconds to repeat calling the
2269      processing loop. (Default is 30.0 seconds.)
2270    '''
2271    def __init__(self,G2frame,PollTime=30.0):
2272        def OnStart(event):
2273            '''Called when the start button is pressed. Changes button label
2274            to Pause. When Pause is pressed the label changes to Resume.
2275            When either Start or Resume is pressed, the processing loop
2276            is started. When Pause is pressed, the loop is stopped.
2277            '''
2278            # check inputs for errors before starting
2279            #err = ''
2280            #if not any([self.params[fmt] for fmt in self.fmtlist]):
2281            #    err += '\nPlease select at least one output format\n'
2282            #if err:
2283            #    G2G.G2MessageBox(self,err)
2284            #    return
2285            self.Pause = False
2286            # change button label
2287            if self.btnstart.GetLabel() != 'Pause':
2288                self.btnstart.SetLabel('Pause')
2289                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
2290                if self.timer.IsRunning(): self.timer.Stop()
2291                self.PreventReEntryTimer = False
2292                if self.StartLoop():
2293                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
2294                    return
2295                self.OnTimerLoop(None) # run once immediately
2296                if not self.Pause:
2297                    # no pause, so start timer to check for new files
2298                    self.timer.Start(int(1000*PollTime),oneShot=False)
2299                    return
2300            # we will get to this point if Paused
2301            self.OnPause()
2302           
2303        def OnReset(event):
2304            '''Called when Reset button is pressed. This stops the
2305            processing loop and resets the list of integrated files so
2306            all images can be reintegrated.
2307            '''
2308            self.btnstart.SetLabel('Restart')
2309            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
2310            if self.timer.IsRunning(): self.timer.Stop()
2311            self.Reset = True
2312            self.Pause = True
2313           
2314        def OnQuit(event):
2315            '''Stop the processing loop and close the Frame
2316            '''
2317            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
2318            wx.CallAfter(self.Destroy)
2319           
2320        def OnBrowse(event):
2321            '''Responds when the Browse button is pressed to load a file.
2322            The routine determines which button was pressed and gets the
2323            appropriate file type and loads it into the appropriate place
2324            in the dict.
2325            '''
2326            if btn3 == event.GetEventObject():
2327                dlg = wx.DirDialog(
2328                    self, 'Select directory for output files',
2329                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
2330                dlg.CenterOnParent()
2331                try:
2332                    if dlg.ShowModal() == wx.ID_OK:
2333                        self.params['outdir'] = dlg.GetPath()
2334                        fInp3.SetValue(self.params['outdir'])
2335                finally:
2336                    dlg.Destroy()
2337                return
2338            elif btn4 == event.GetEventObject():
2339                msg = ''
2340                pth = G2G.GetExportPath(G2frame)
2341                dlg = wx.FileDialog(
2342                    self, 'Select a PDF parameter file',
2343                    pth, self.params['pdfprm'], 
2344                    "PDF controls file (*.pdfprm)|*.pdfprm",
2345                    wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
2346                dlg.CenterOnParent()
2347                try:
2348                    if dlg.ShowModal() == wx.ID_OK:
2349                        self.params['pdfprm'] = dlg.GetPath()
2350                        fInp4.SetValue(self.params['pdfprm'])
2351                        scanPDFprm()
2352                        msg = self.checkPDFprm(True)
2353                finally:
2354                    dlg.Destroy()
2355                if 'Error' in msg:
2356                    print(msg)
2357                    lbl = 'PDFPRM error'
2358                else:
2359                    msg = 'Information from file {}\n\n{}'.format(self.params['pdfprm'],msg)
2360                    lbl = 'PDFPRM information'
2361                G2G.G2MessageBox(self,msg,lbl)
2362                return
2363               
2364        def OnRadioSelect(event):
2365            '''Respond to a radiobutton selection and when in table
2366            mode, get distance-dependent parameters from user.
2367            '''
2368            self.Evaluator = None
2369            if self.useTable.GetValue():
2370                dlg = None
2371                try:
2372                    dlg = IntegParmTable(self) # create the dialog
2373                    dlg.CenterOnParent()
2374                    if dlg.ShowModal() == wx.ID_OK:
2375                        self.ImgTblParms = dlg.parms
2376                        self.IMfileList = dlg.IMfileList
2377                        self.Evaluator = DefineEvaluator(dlg)
2378                        self.params['Mode'] = 'table'
2379                        self.editTable.Enable(True)
2380                    else:
2381                        self.useActive.SetValue(True)
2382                finally:
2383                    if dlg: dlg.Destroy()
2384            elif self.useActive.GetValue():
2385                self.params['Mode'] = 'active'
2386                self.imageBase = G2frame.Image
2387                self.useActive.SetLabel("Active Image: "+
2388                        G2frame.GPXtree.GetItemText(self.imageBase))
2389                self.editTable.Enable(False)
2390            else:
2391                print('unexpected mode in OnRadioSelect')
2392
2393        def OnEditTable(event):
2394            '''Called to edit the distance-dependent parameter look-up table.
2395            Should be called only when table is defined and active.
2396            '''
2397            dlg = None
2398            try:
2399                dlg = IntegParmTable(self,self.ImgTblParms,self.IMfileList)
2400                dlg.CenterOnParent()
2401                if dlg.ShowModal() == wx.ID_OK:
2402                    self.ImgTblParms = dlg.parms
2403                    self.IMfileList = dlg.IMfileList
2404                    self.Evaluator = DefineEvaluator(dlg)
2405                    self.params['Mode'] = 'table'
2406                    self.editTable.Enable(True)
2407                else:
2408                    self.useActive.SetValue(True)
2409                    self.params['Mode'] = 'active'
2410                    self.imageBase = G2frame.Image
2411                    self.useActive.SetLabel("Active Image: "+
2412                            G2frame.GPXtree.GetItemText(self.imageBase))
2413                    self.editTable.Enable(False)
2414            finally:
2415                if dlg: dlg.Destroy()
2416               
2417        def showPDFctrls(event):
2418            '''Called to show or hide AutoPDF widgets. Note that fInp4 must be included in the
2419            sizer layout with .Show(True) before .Show(False) will work properly.
2420            '''
2421            fInp4.Enable(self.params['ComputePDF'])
2422            fInp4.Show(self.params['ComputePDF'])
2423            fInp4a.Enable(self.params['ComputePDF'])
2424            btn4.Enable(self.params['ComputePDF'])
2425            cOpt.Enable(self.params['ComputePDF'])
2426            if self.params['ComputePDF']:
2427                lbl4.SetForegroundColour("black")
2428                lbl4a.SetForegroundColour("black")
2429            else:
2430                lbl4.SetForegroundColour("gray")
2431                lbl4a.SetForegroundColour("gray")
2432                                   
2433        def scanPDFprm(**kw):
2434            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2435            fInp4._IndicateValidity()
2436           
2437        def OnAutoScale(event):
2438            self.AutoScale = autoscale.GetValue()
2439           
2440        def OnAutoScaleName(event):
2441            self.AutoScaleName = scalename.GetValue()
2442            self.Scale[0] = self.AutoScales[self.AutoScaleName]
2443            scaleval.SetValue(self.Scale[0])
2444               
2445        ##################################################
2446        # beginning of __init__ processing
2447        ##################################################
2448        self.G2frame = G2frame
2449        self.ImgTblParms = None
2450        self.IMfileList = None
2451        self.Evaluator = None
2452        self.params = {}
2453        self.Reset = False
2454        self.Pause = False
2455        self.PreventReEntryShowMatch = False
2456        self.PreventReEntryTimer = False
2457        self.params['IMGfile'] = ''
2458        self.params['MaskFile'] = ''
2459        self.params['IgnoreMask'] = True
2460        self.fmtlist = G2IO.ExportPowderList(G2frame)
2461        self.timer = wx.Timer()
2462        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2463        self.imageBase = G2frame.Image
2464        self.params['ComputePDF'] = False
2465        self.params['pdfDmax'] = 0.0
2466        self.params['pdfprm'] = ''
2467        self.params['optPDF'] = True
2468        self.pdfControls = {}
2469        self.AutoScale = False
2470        self.Scale = [1.0,]
2471
2472        G2frame.GPXtree.GetSelection()
2473        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(self.imageBase) 
2474        self.imagedir,fileroot = os.path.split(imagefile)
2475        Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2476            G2frame,self.imageBase, 'Comments'))
2477        DefaultAutoScaleNames = GSASIIpath.GetConfigValue('Autoscale_ParmNames')
2478        self.AutoScaleName = GSASIIpath.GetConfigValue('DefaultAutoScale')
2479        self.AutoScales = {}
2480        if DefaultAutoScaleNames is not None:
2481            for comment in Comments:
2482                if '=' in comment:
2483                    name,val = comment.split('=',1) 
2484                    if name in DefaultAutoScaleNames:
2485                        try:
2486                            self.AutoScales[name] = float(val)
2487                            if name == self.AutoScaleName:
2488                                self.Scale[0] = float(val)
2489                        except ValueError:
2490                            continue
2491        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2492        self.params['outdir'] = os.path.abspath(self.imagedir)
2493        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2494                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2495        self.Status = self.CreateStatusBar()
2496        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2497        mnpnl = wx.Panel(self)
2498        mnsizer = wx.BoxSizer(wx.VERTICAL)
2499        # box for integration controls & masks input
2500        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2501        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2502        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2503        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2504                            style = wx.RB_GROUP)
2505        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2506        self.useActive.SetLabel("Active Image: "+
2507                                G2frame.GPXtree.GetItemText(self.imageBase))
2508        lblsizr.Add(self.useActive,1,wx.EXPAND,1)
2509        self.useActive.SetValue(True)
2510        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2511        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2512        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2513        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2514        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2515        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2516        self.editTable.Enable(False)
2517        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2518        # bind button and deactivate be default
2519        lblsizr.Add(minisizer)
2520        mnsizer.Add(lblsizr,1,wx.EXPAND,1)
2521
2522        # file filter stuff
2523        sizer = wx.BoxSizer(wx.HORIZONTAL)
2524        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Image filter'))
2525        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',OnLeave=self.ShowMatchingFiles)
2526        sizer.Add(flterInp)
2527        mnsizer.Add(sizer,0,wx.ALIGN_RIGHT,1)
2528        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2529        mnsizer.Add(self.ListBox,0,wx.EXPAND,1)
2530        self.ShowMatchingFiles(self.params['filter'])
2531        # box for output selections
2532        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2533        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2534        sizer = wx.BoxSizer(wx.HORIZONTAL)
2535        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2536        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2537        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2538        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2539        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2540        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2541        lblsizr.Add(sizer,0,wx.EXPAND)
2542        sizer = wx.BoxSizer(wx.HORIZONTAL)
2543        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
2544        for dfmt in self.fmtlist:
2545            fmt = dfmt[1:]
2546            self.params[fmt] = False
2547            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2548            sizer.Add(btn)
2549        lblsizr.Add(sizer)
2550        sizer = wx.BoxSizer(wx.HORIZONTAL)
2551        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2552        self.params['SeparateDir'] = False
2553        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2554        lblsizr.Add(sizer)
2555        if self.AutoScales:
2556            sizer = wx.BoxSizer(wx.HORIZONTAL)
2557            autoscale = wx.CheckBox(mnpnl,label='Do autoscaling with:')
2558            autoscale.Bind(wx.EVT_CHECKBOX,OnAutoScale)
2559            sizer.Add(autoscale,0,WACV)
2560            scalename = wx.ComboBox(mnpnl,value=self.AutoScaleName,choices=self.AutoScales.keys(),
2561                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2562            scalename.Bind(wx.EVT_COMBOBOX,OnAutoScaleName)
2563            sizer.Add(scalename,0,WACV)
2564            sizer.Add(wx.StaticText(mnpnl,label=' to '),0,WACV)
2565            scaleval = G2G.ValidatedTxtCtrl(mnpnl,self.Scale,0,nDig=(10,2),min=1.)
2566            sizer.Add(scaleval,0,WACV)
2567            lblsizr.Add(sizer,0)
2568        #ToDO: Autonormalize, parm name?, scaling value?
2569        sizer = wx.BoxSizer(wx.HORIZONTAL)
2570        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2571        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2572        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2573        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2574        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2575        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2576        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2577        sizer.Add(cOpt)
2578        lblsizr.Add(sizer,0)
2579        sizer = wx.BoxSizer(wx.HORIZONTAL)
2580        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2581        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2582        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2583                                     OnLeave=scanPDFprm)
2584        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2585        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2586        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2587        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2588        lblsizr.Add(sizer,0,wx.EXPAND)
2589        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2590        # buttons on bottom
2591        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2592        sizer = wx.BoxSizer(wx.HORIZONTAL)
2593        sizer.Add((20,-1))
2594        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2595        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2596        sizer.Add(self.btnstart)
2597        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2598        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2599        sizer.Add(self.btnreset)
2600        sizer.Add((20,-1),wx.EXPAND,1)
2601        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2602        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2603        sizer.Add(self.btnclose)
2604        sizer.Add((20,-1))
2605        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2606        # finish up window
2607        mnpnl.SetSizer(mnsizer)
2608        OnRadioSelect(None) # disable widgets
2609        mnsizer.Fit(self)
2610        self.CenterOnParent()
2611        self.Show()
2612        showPDFctrls(None)
2613
2614    def checkPDFprm(self,ShowContents=False):
2615        '''Read in the PDF (.pdfprm) parameter file and check for problems.
2616        If ShowContents is True, a formatted text version of some of the file
2617        contents is returned. If errors are found, the return string will contain
2618        the string "Error:" at least once.
2619        '''
2620        self.pdfControls = {}
2621        msg = ''
2622        File = None
2623        try:
2624            File = open(self.params['pdfprm'],'r')
2625            S = File.readline()
2626            while S:
2627                if '#' in S:
2628                    S = File.readline()
2629                    continue
2630                key,val = S.split(':',1)
2631                try:
2632                    self.pdfControls[key] = eval(val)
2633                except:
2634                    self.pdfControls[key] = val
2635                S = File.readline()
2636        except Exception as err:
2637            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
2638            if GSASIIpath.GetConfigValue('debug'):
2639                print(msg)
2640                print(err)
2641            self.pdfControls = {}
2642            return msg
2643        finally:
2644            if File: File.close()
2645        formula = ''
2646        for el in self.pdfControls['ElList']:
2647            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
2648            if formula: formula += ' '
2649            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
2650        if not formula:
2651            msg += 'Error: no chemical formula in file'
2652        for key in ['Sample Bkg.','Container','Container Bkg.']:
2653            if key not in self.pdfControls:
2654                if msg: msg += '\n'
2655                msg += 'Error: missing key in self.pdfControls: '+key
2656                continue
2657        if msg or not ShowContents: return msg  # stop on error
2658        msg += 'Default formula: '+formula+'\n'
2659        for key in ['Sample Bkg.','Container','Container Bkg.']:
2660            name = self.pdfControls[key]['Name']
2661            mult = self.pdfControls[key].get('Mult',0.0)
2662            if not name: continue
2663            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
2664            if not G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,name):
2665                msg += ' *** missing ***'
2666        return msg
2667   
2668    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2669        G2frame = self.G2frame
2670        if invalid: return
2671        msg = ''
2672        if self.PreventReEntryShowMatch: return
2673        self.PreventReEntryShowMatch = True
2674        imageFileList = []
2675        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
2676            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
2677            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
2678            if imagefile not in imageFileList: imageFileList.append(imagefile)
2679            if img not in G2frame.IntegratedList:
2680                if msg: msg += '\n'
2681                msg += '  ' + img
2682        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
2683        msg1 = ""
2684        try:
2685            imageList = sorted(
2686                glob.glob(os.path.join(self.imagedir,value)))
2687            if not imageList:
2688                msg1 = 'Warning: No files match search string '+os.path.join(self.imagedir,value)
2689            else:
2690                for fil in imageList:
2691                    if fil not in imageFileList: msg1 += '\n  '+fil
2692                if msg1:
2693                    msg += 'Files to integrate from '+os.path.join(self.imagedir,value)+msg1
2694                else:
2695                    msg += 'No files found to read'
2696        except IndexError:
2697            msg += 'Error searching for files named '+os.path.join(self.imagedir,value)
2698        self.ListBox.Clear()
2699        self.ListBox.AppendItems(msg.split('\n'))
2700        self.PreventReEntryShowMatch = False
2701        return
2702       
2703    def OnPause(self):
2704        '''Respond to Pause, changes text on button/Status line, if needed
2705        Stops timer
2706        self.Pause should already be True
2707        '''
2708        if self.timer.IsRunning(): self.timer.Stop()
2709        if self.btnstart.GetLabel() == 'Restart':
2710            return
2711        if self.btnstart.GetLabel() != 'Resume':
2712            print('\nPausing autointegration\n')
2713            self.btnstart.SetLabel('Resume')
2714            self.Status.SetStatusText(
2715                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
2716        self.Pause = True
2717           
2718    def IntegrateImage(self,img):
2719        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
2720        are placed in G2frame.IntgOutList
2721        '''
2722        G2frame = self.G2frame
2723        imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
2724        G2frame.Image = imgId
2725        G2frame.PickId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls')
2726        # do integration
2727        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
2728        if self.AutoScale:
2729            Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2730                G2frame,imgId, 'Comments'))
2731            for comment in Comments:
2732                if '=' in comment:
2733                    name,val = comment.split('=',1) 
2734                    if name == self.AutoScaleName:
2735                        val = float(val)
2736                        if val > 0.:
2737                            Scale = self.Scale[0]/val
2738                        break
2739        masks = G2frame.GPXtree.GetItemPyData(
2740            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Masks'))
2741        data = G2frame.GPXtree.GetItemPyData(G2frame.PickId)
2742        # simulate a Image Controls press, since that is where the
2743        # integration is hidden
2744        UpdateImageControls(G2frame,data,masks,IntegrateOnly=True)
2745        G2frame.IntegratedList.append(img) # note this as integrated
2746        # split name and control number
2747        s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
2748        namepre = s[0]
2749        if len(s) > 1:
2750            namenum = s[1]
2751        else:
2752            namenum = ''
2753        for Id in G2frame.IntgOutList: # loop over newly created PDWR entry(ies)
2754            # save the created PWDR tree names so that a reset can delete them
2755            G2frame.Image = Id
2756            treename = G2frame.GPXtree.GetItemText(Id)
2757            G2frame.AutointPWDRnames.append(treename)
2758            # write out the images in the selected formats
2759            Sdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Sample Parameters'))
2760            if self.AutoScale:
2761                print 'Rescale by %.4f'%(Scale)
2762                y,w = G2frame.GPXtree.GetItemPyData(Id)[1][1:3]
2763                y *= Scale
2764                w /= Scale**2
2765            # determine the name for the current file
2766            fileroot = namepre
2767            if len(G2frame.IntgOutList) > 1:
2768                fileroot += "_AZM"
2769                if 'Azimuth' in Sdata:
2770                    fileroot += str(int(10*Sdata['Azimuth']))
2771                fileroot += "_" 
2772            fileroot += namenum
2773            # loop over selected formats
2774            for dfmt in self.fmtlist:
2775                if not self.params[dfmt[1:]]: continue
2776                if self.params['SeparateDir']:
2777                    subdir = dfmt[1:]
2778                else:
2779                    subdir = ''
2780                fil = os.path.join(self.params['outdir'],subdir,fileroot)
2781                print('writing file '+fil+dfmt)
2782                G2IO.ExportPowder(G2frame,treename,fil,dfmt)
2783               
2784    def EnableButtons(self,flag):
2785        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
2786        '''
2787        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
2788        # on windows
2789        if sys.platform != "win32":
2790            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
2791        self.btnstart.SetLabel('Pause')
2792        wx.Yield()
2793               
2794    def ResetFromTable(self,dist):
2795        '''Sets integration parameters based on values from
2796        the lookup table
2797        '''
2798        #dist = self.controlsDict['distance']
2799        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
2800        if GSASIIpath.GetConfigValue('debug'):
2801            print 'interpolated values: ',interpDict
2802        self.ImageControls = ReadControls(imgctrl)
2803        self.ImageControls.update(interpDict)
2804        self.ImageControls['showLines'] = True
2805        self.ImageControls['ring'] = []
2806        self.ImageControls['rings'] = []
2807        self.ImageControls['ellipses'] = []
2808        self.ImageControls['setDefault'] = False
2809        for i in 'range','size','GonioAngles':
2810            if i in self.ImageControls:
2811                del self.ImageControls[i]
2812        # load copy of Image Masks
2813        if immask:
2814            self.ImageMasks = ReadMask(immask)
2815            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
2816                del self.ImageMasks['Thresholds']
2817        else:
2818            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
2819       
2820    def StartLoop(self):
2821        '''Prepare to start autointegration timer loop.
2822        Save current Image params for use in future integrations
2823        also label the window so users understand what is being used
2824        '''
2825        print('\nStarting new autointegration\n')
2826        G2frame = self.G2frame
2827        # show current IMG base
2828        if self.params['Mode'] != 'table':
2829            self.useActive.SetLabel("Active Image: "+
2830                                    G2frame.GPXtree.GetItemText(self.imageBase))
2831            # load copy of Image Controls from current image and clean up
2832            # items that should not be copied
2833            self.ImageControls = copy.deepcopy(
2834                G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2835                    G2frame,self.imageBase, 'Image Controls')))
2836            self.ImageControls['showLines'] = True
2837            self.ImageControls['ring'] = []
2838            self.ImageControls['rings'] = []
2839            self.ImageControls['ellipses'] = []
2840            self.ImageControls['setDefault'] = False
2841            del self.ImageControls['range']
2842            del self.ImageControls['size']
2843            del self.ImageControls['GonioAngles']
2844            # load copy of Image Masks, keep thresholds
2845            self.ImageMasks = copy.deepcopy(
2846                G2frame.GPXtree.GetItemPyData(
2847                    G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks')))
2848            self.Thresholds = self.ImageMasks['Thresholds'][:]
2849            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
2850                del self.ImageMasks['Thresholds']   
2851        # make sure all output directories exist
2852        if self.params['SeparateDir']:
2853            for dfmt in self.fmtlist:
2854                if not self.params[dfmt[1:]]: continue
2855                dir = os.path.join(self.params['outdir'],dfmt[1:])
2856                if not os.path.exists(dir): os.makedirs(dir)
2857        else:
2858            if not os.path.exists(self.params['outdir']):
2859                os.makedirs(self.params['outdir'])
2860        if self.Reset: # special things to do after Reset has been pressed
2861            self.G2frame.IntegratedList = []
2862           
2863            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
2864                for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
2865                    # update controls from master
2866                    controlsDict = G2frame.GPXtree.GetItemPyData(
2867                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Image Controls'))
2868                    controlsDict.update(self.ImageControls)
2869                    # update masks from master
2870                    ImageMasks = G2frame.GPXtree.GetItemPyData(
2871                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks'))
2872                    ImageMasks.update(self.ImageMasks)
2873            # delete all PWDR items created after last Start was pressed
2874            idlist = []
2875            item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
2876            while item:
2877                itemName = G2frame.GPXtree.GetItemText(item)
2878                if itemName in G2frame.AutointPWDRnames:
2879                    idlist.append(item)
2880                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
2881            for item in idlist:
2882                G2frame.GPXtree.Delete(item)
2883            wx.Yield()
2884            self.Reset = False
2885        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
2886        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
2887        # check that AutoPDF input is OK, offer chance to use alternate PDWRs if referenced ones
2888        # are not present
2889        if self.params['ComputePDF']:
2890            msg = self.checkPDFprm()
2891            if 'Error:' in msg:
2892                print(msg)
2893                return True
2894            fileList = []
2895            id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
2896            while id:
2897                name = G2frame.GPXtree.GetItemText(id)
2898                if name.startswith('PWDR '): fileList.append(name)
2899                id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
2900            if not fileList:
2901                print(msg)
2902                print('No PWDR entries to select')
2903                return True
2904            for key in ['Sample Bkg.','Container','Container Bkg.']:
2905                name = self.pdfControls[key]['Name']
2906                if not name: continue
2907                if not G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name):
2908                    indx = G2G.ItemSelector(fileList, self, header='Select PDWR item',
2909                                    title='Select a PDWR tree item for '+key+'\n(or cancel to quit)')
2910                    if indx is None:
2911                        print('No PWDR entry selected for '+key)
2912                        return True
2913                    self.pdfControls[key]['Name'] = fileList[indx]
2914        return False
2915               
2916    def OnTimerLoop(self,event):
2917        '''A method that is called every :meth:`PollTime` seconds that is
2918        used to check for new files and process them. Integrates new images.
2919        Also optionally sets up and computes PDF.
2920        This is called only after the "Start" button is pressed (then its label reads "Pause").
2921        '''
2922        def AutoIntegrateImage(imgId):
2923            '''Integrates an image that has been read into the data tree and updates the
2924            AutoInt window.
2925            '''
2926            img = G2frame.GPXtree.GetItemText(imgId)
2927            controlsDict = G2frame.GPXtree.GetItemPyData(
2928                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
2929            ImageMasks = G2frame.GPXtree.GetItemPyData(
2930                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Masks'))
2931            if self.params['Mode'] == 'table': # look up parameter values from table
2932                self.ResetFromTable(controlsDict['setdist'])
2933            # update controls from master
2934            controlsDict.update(self.ImageControls)
2935            # update masks from master w/o Thresholds
2936            ImageMasks.update(self.ImageMasks)
2937            self.EnableButtons(False)
2938            try:
2939                self.IntegrateImage(img)
2940            finally:
2941                self.EnableButtons(True)
2942            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
2943            wx.Yield()
2944            self.ShowMatchingFiles(self.params['filter'])
2945            wx.Yield()
2946           
2947        def AutoComputePDF(imgId):
2948            '''Computes a PDF for a PWDR data tree tree item
2949            '''
2950            for pwdr in G2frame.AutointPWDRnames[:]:
2951                if not pwdr.startswith('PWDR '): continue
2952                if pwdr in G2frame.AutointPDFnames: continue
2953                PWid = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,pwdr)
2954                controlsDict = G2frame.GPXtree.GetItemPyData(
2955                    G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
2956                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
2957                    print('Skipping PDF for '+pwdr+' due to detector position')
2958                    continue
2959                # Setup PDF
2960                Data = G2frame.GPXtree.GetItemPyData(PWid)[1]
2961                pwdrMin = np.min(Data[1])
2962                Parms = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2963                    G2frame,PWid,'Instrument Parameters'))[0]
2964                fullLimits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2965                    G2frame,PWid,'Limits'))[0]
2966                if 'C' in Parms['Type'][0]:
2967                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
2968                else:
2969                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
2970                Qlimits = [0.9*qMax,qMax]
2971
2972                item = pwdr
2973                Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2974                    G2frame,imgId, 'Comments'))
2975                ElList = {}
2976                sumnum = 1.0
2977                for item in Comments:           #grab chemical formula from Comments, if there
2978                    if 'formula' in item[:15].lower():
2979                        formula = item.split('=')[1].split()
2980                        elems = formula[::2]
2981                        nums = formula[1::2]
2982                        formula = zip(elems,nums)
2983                        sumnum = 0.
2984                        for [elem,num] in formula:
2985                            ElData = G2elem.GetElInfo(elem,Parms)
2986                            ElData['FormulaNo'] = float(num)
2987                            sumnum += float(num)
2988                            ElList[elem] = ElData
2989                PDFnames = G2gd.GetGPXtreeDataNames(G2frame,['PDF ',])
2990                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
2991                if not PDFid: continue
2992                PDFdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2993                    G2frame,PDFid, 'PDF Controls'))
2994                PDFdata.update(self.pdfControls)
2995                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
2996                PDFdata['Sample']['Name'] = pwdr
2997                # compute PDF
2998                wx.Yield()
2999                G2pdG.computePDF(G2frame,PDFdata)
3000                wx.Yield()
3001                G2frame.PatternId = PDFid
3002                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3003                if self.params['optPDF']:
3004                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
3005                    wx.Yield()
3006                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3007                G2frame.AutointPDFnames.append(pwdr)
3008                # save names of PDF entry to be deleted later if needed
3009                G2frame.AutointPWDRnames.append(G2frame.GPXtree.GetItemText(PDFid))
3010           
3011        G2frame = self.G2frame
3012        try:
3013            self.currImageList = sorted(
3014                glob.glob(os.path.join(self.imagedir,self.params['filter'])))
3015            self.ShowMatchingFiles(self.params['filter'])
3016        except IndexError:
3017            self.currImageList = []
3018            return
3019
3020        if self.PreventReEntryTimer: return
3021        self.PreventReEntryTimer = True
3022        imageFileList = []
3023        # integrate the images that have already been read in, but
3024        # have not yet been processed           
3025        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
3026            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3027            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3028            # Create a list of image files that have been read in
3029            if imagefile not in imageFileList: imageFileList.append(imagefile)
3030            # skip if already integrated
3031            if img in G2frame.IntegratedList: continue
3032            AutoIntegrateImage(imgId)
3033            #self.Pause |= G2frame.PauseIntegration
3034            #if self.Pause:
3035            #    self.OnPause()
3036            #    self.PreventReEntryTimer = False
3037            #    return
3038            if self.pdfControls: AutoComputePDF(imgId)
3039            self.Pause |= G2frame.PauseIntegration
3040            if self.Pause:
3041                self.OnPause()
3042                self.PreventReEntryTimer = False
3043                return
3044
3045        # loop over image files matching glob, reading in any new ones
3046        for newImage in self.currImageList:
3047            if newImage in imageFileList or self.Pause: continue # already read?
3048            for imgId in G2IO.ReadImages(G2frame,newImage):
3049                AutoIntegrateImage(imgId)           
3050                #self.Pause |= G2frame.PauseIntegration
3051                #if self.Pause:
3052                #    self.OnPause()
3053                #    self.PreventReEntryTimer = False
3054                #    return
3055                if self.pdfControls: AutoComputePDF(imgId)
3056                self.Pause |= G2frame.PauseIntegration
3057                if self.Pause:
3058                    self.OnPause()
3059                    self.PreventReEntryTimer = False
3060                    return
3061        if GSASIIpath.GetConfigValue('debug'):
3062            import datetime
3063            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3064        self.PreventReEntryTimer = False
3065
3066def DefineEvaluator(dlg):
3067    '''Creates a function that provides interpolated values for a given distance value
3068    '''
3069    def Evaluator(dist):
3070        '''Interpolate image parameters for a supplied distance value
3071
3072        :param float dist: distance to use for interpolation
3073        :returns: a list with 3 items:
3074
3075          * a dict with parameter values,
3076          * the closest imctrl and
3077          * the closest maskfile (or None)
3078        '''           
3079        x = np.array([float(i) for i in parms[0]])
3080        closest = abs(x-dist).argmin()
3081        D = {'setdist':dist}
3082        imctfile = IMfileList[closest]
3083        if parms[-1][closest].lower() != '(none)':
3084            maskfile = parms[-1][closest]
3085        else:
3086            maskfile = None
3087        for c in range(1,cols-1):
3088            lbl = ParmList[c]
3089            if lbl in nonInterpVars:
3090                if lbl in ['outChannels',]:
3091                    D[lbl] = int(float(parms[c][closest]))
3092                else:
3093                    D[lbl] = float(parms[c][closest])
3094            else:
3095                y = np.array([float(i) for i in parms[c]])
3096                D[lbl] = np.interp(dist,x,y)
3097        # full integration when angular range is 0
3098        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3099        # conversion for paired values
3100        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3101            r = a.split('_')[0]
3102            D[r] = [D[a],D[b]]
3103            if r in ['LRazimuth',]:
3104                D[r] = [int(D[a]),int(D[b])]
3105            del D[a]
3106            del D[b]
3107        return D,imctfile,maskfile
3108    # save local copies of values needed in Evaluator
3109    parms = dlg.ReadImageParmTable()
3110    IMfileList = dlg.IMfileList
3111    cols = dlg.list.GetColumnCount()
3112    ParmList = dlg.ParmList
3113    nonInterpVars = dlg.nonInterpVars
3114    return Evaluator
3115
3116class IntegParmTable(wx.Dialog):
3117    '''Creates a dialog window with a table of integration parameters.
3118    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3119    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3120    creates a dictionary with interpolated parameter values.
3121    '''
3122    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3123            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3124            'maskfile',
3125            )
3126    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3127                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3128    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3129            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3130            'Mask File',
3131            )
3132    def __init__(self,parent,parms=None,IMfileList=None):
3133        self.G2frame = parent.G2frame
3134        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3135        if parms:
3136            self.parms = parms # list of values by column
3137            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3138        else:
3139            self.parms = [] # list of values by column
3140            self.IMfileList = [] # list of .imctrl file names for each entry in table
3141            files = []
3142            try:
3143                pth = G2G.GetImportPath(self.G2frame)
3144                if not pth: pth = '.'
3145                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3146                    style=wx.OPEN| wx.MULTIPLE,
3147                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3148                dlg.CenterOnParent()
3149                if dlg.ShowModal() == wx.ID_OK:
3150                    files = dlg.GetPaths()
3151                    self.parms,self.IMfileList = self.ReadFiles(files)
3152            finally:
3153                dlg.Destroy()
3154            if not files:
3155                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3156                return
3157        mainSizer = G2frame.dataWindow.GetSizer()
3158        self.list = ImgIntLstCtrl(self, wx.ID_ANY,
3159                      style=wx.LC_REPORT
3160                          | wx.BORDER_SUNKEN
3161                         #| wx.BORDER_NONE
3162                         )
3163        mainSizer.Add(self.list,1,wx.EXPAND,1)
3164        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3165        btn = wx.Button(self, wx.ID_OK)
3166        btnsizer.Add(btn)
3167        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3168        btn.Bind(wx.EVT_BUTTON,self._onSave)
3169        btnsizer.Add(btn)
3170        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3171        btn.Bind(wx.EVT_BUTTON,self._onClose)
3172        btnsizer.Add(btn)
3173        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3174        self.SetSizer(mainSizer)
3175        self.list.FillList(self.parms)
3176       
3177    def ReadFiles(self,files):
3178        '''Reads a list of .imctrl files or a single .imtbl file
3179        '''
3180        tmpDict = {}
3181        if not files: return
3182        # option 1, a dump from a previous save
3183        if os.path.splitext(files[0])[1] == '.imtbl':
3184            fp = open(files[0],'r')
3185            S = fp.readline()
3186            while S:
3187                if S[0] != '#':
3188                    [key,val] = S[:-1].split(':',1)
3189                    tmpDict[key] = eval(val)
3190                S = fp.readline()
3191            fp.close()
3192            # delete entries where files do not exist
3193            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3194            if m1:
3195                print('\nimctrl file not found:')
3196                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3197            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3198            if m2:
3199                print('\nmask file not found')
3200                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3201            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3202            if m3:
3203                print('\nDropping entries due to negative distance: '+str(m3))
3204            m = sorted(set(m1 + m2 + m3))
3205            m.reverse()
3206            for c in m:
3207                for key in tmpDict:
3208                    del tmpDict[key][c]
3209            fileList = tmpDict.get('filenames','[]')
3210            parms = []
3211            if 'setdist' not in tmpDict:
3212                print(u'Old file, recreate before using: {}'.format(files[0]))
3213                return [[]],[]
3214            for key in self.ParmList:
3215                try:
3216                    float(tmpDict[key][0])
3217                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3218                except ValueError:
3219                    parms.append(tmpDict[key])
3220                except IndexError:
3221                    print('No valid image control entries read')
3222                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3223                    return [[]],[]
3224            return parms,fileList
3225        # option 2, read in a list of files
3226        for file in files: # read all files; place in dict by distance
3227            imgDict = Read_imctrl(file)
3228            dist = imgDict.get('setdist')
3229            if dist is None:
3230                print('Skipping old file, redo: {}'.format(file))
3231            tmpDict[dist] = imgDict
3232        parms = [[] for key in self.ParmList]
3233        fileList = []
3234        for d in sorted(tmpDict):
3235            fileList.append(tmpDict[d].get('filename'))
3236            if d is None: continue
3237            if d < 0: continue
3238            for i,key in enumerate(self.ParmList):
3239                val = tmpDict[d].get(key)
3240                try:
3241                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3242                except:
3243                    val = str(val)
3244                parms[i].append(val)
3245        return parms,fileList
3246   
3247    def ReadImageParmTable(self):
3248        '''Reads possibly edited values from the ListCtrl table and returns a list
3249        of values for each column.
3250        '''
3251        rows = self.list.GetItemCount()
3252        cols = self.list.GetColumnCount()
3253        parms = []
3254        for c in range(cols):
3255            parms.append([])
3256            for r in range(rows):
3257                parms[c].append(self.list.GetItem(r,c).GetText())
3258        return parms
3259
3260    def _onClose(self,event):
3261        'Called when Cancel button is pressed'
3262        self.EndModal(wx.ID_CANCEL)
3263       
3264    def _onSave(self,event):
3265        'Called when save button is pressed; creates a .imtbl file'
3266        fil = ''
3267        if self.G2frame.GSASprojectfile:
3268            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3269        dir,f = os.path.split(fil)
3270        pth = G2G.GetExportPath(self.G2frame)
3271        try:
3272            dlg = wx.FileDialog(self, 'Save table data as',
3273                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3274                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3275            dlg.CenterOnParent()
3276            if dlg.ShowModal() != wx.ID_OK: return
3277            fil = dlg.GetPath()
3278            fil = os.path.splitext(fil)[0]+'.imtbl'
3279        finally:
3280            dlg.Destroy()       
3281        parms = self.ReadImageParmTable()
3282        print('Writing image parameter table as '+fil)
3283        fp = open(fil,'w')
3284        for c in range(len(parms)-1):
3285            lbl = self.ParmList[c]
3286            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3287        lbl = self.ParmList[c+1]
3288        fp.write(lbl+': '+str(parms[c+1])+'\n')
3289        lbl = 'filenames'
3290        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3291        fp.close()
3292   
3293class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3294    '''Creates a custom ListCtrl for editing Image Integration parameters
3295    '''
3296    def __init__(self, parent, ID, pos=wx.DefaultPosition,
3297                 size=(1000,200), style=0):
3298        self.parent=parent
3299        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3300        listmix.ListCtrlAutoWidthMixin.__init__(self)
3301        listmix.TextEditMixin.__init__(self)
3302        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3303        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3304    def FillList(self,parms):
3305        'Places the current parms into the table'
3306        self.ClearAll()
3307        self.rowlen = len(self.parent.ParmList)
3308        for i,lbl in enumerate(self.parent.HeaderList):
3309            self.InsertColumn(i, lbl)
3310        for r,d in enumerate(parms[0]):
3311            if d is None: continue
3312            if float(d) < 0: continue
3313            index = self.InsertStringItem(sys.maxint, d)
3314            for j in range(1,len(parms)):
3315                self.SetStringItem(index, j, parms[j][r])
3316        for i,lbl in enumerate(self.parent.ParmList):
3317            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3318
3319    def OnDouble(self,evt):
3320        'respond to a double-click'
3321        self.CloseEditor()
3322        fil = '(none)'
3323        pth = G2G.GetImportPath(self.parent.G2frame)
3324        if not pth: pth = '.'
3325        try:
3326            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3327                                style=wx.OPEN,
3328                                wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3329            dlg.CenterOnParent()
3330            if dlg.ShowModal() == wx.ID_OK:
3331                fil = dlg.GetPath()
3332        finally:
3333            dlg.Destroy()
3334        if os.path.splitext(fil)[1] != '.imctrl':
3335            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3336            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3337        else:
3338            # insert or overwrite an instrument parameter set
3339            if not os.path.exists(fil):
3340                print('Does not exist: '+fil)
3341                return
3342            imgDict = Read_imctrl(fil)
3343            dist = imgDict['distance']
3344            parms = self.parent.ReadImageParmTable()
3345            x = np.array([float(i) for i in parms[0]])
3346            closest = abs(x-dist).argmin()
3347            closeX = x[closest]
3348            # fix IMfileList
3349            for c,lbl in enumerate(self.parent.ParmList):
3350                try:
3351                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3352                except ValueError:
3353                    vali = imgDict[lbl]
3354                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3355                    parms[c][closest] = vali
3356                elif dist > closeX: # insert after
3357                    parms[c].insert(closest+1,vali)
3358                else:
3359                    parms[c].insert(closest,vali)
3360            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3361                self.parent.IMfileList[closest] = fil
3362            elif dist > closeX: # insert after
3363                self.parent.IMfileList.insert(closest+1,fil)
3364            else:
3365                self.parent.IMfileList.insert(closest,fil)
3366            self.FillList(parms)
3367# Autointegration end
3368###########################################################################
Note: See TracBrowser for help on using the repository browser.