source: trunk/GSASIIimgGUI.py @ 2685

Last change on this file since 2685 was 2685, checked in by vondreele, 8 years ago

save image recalibrate all in sequential results table
allow global setting of PDFpeak parameter refinement flags

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