source: trunk/GSASIIimgGUI.py @ 2694

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

after a repeat of a sequential fit, clear away old plot. Removes possibly bad parameter references
make note in G2grid of possible problem of pseudo vars storage in Controls
set new patch value for penetration corr to 0.5; above that convert to new value
fix peak picking problem when pattern has excluded regions
remove RaisePageNoRefresh? from Seq result plotting - irrelevant if plot is deleted

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