source: trunk/GSASIIimgGUI.py @ 2566

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

AutoInt? win-only problem; update for IPython 5 debug

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