source: trunk/GSASIIimgGUI.py @ 2673

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

moved GetFileList? to top of G2pwdGUI - needed in two places. The one on GSASII doesn't seem to work & isn't used anywhere.
Put missing import of G2elem & tof2q lambda in G2imgGUI.

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