source: trunk/GSASIIimgGUI.py @ 2684

Last change on this file since 2684 was 2684, checked in by toby, 5 years ago

Image Parameters: split menus, more xfer angles options

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