source: trunk/GSASIIimgGUI.py @ 2592

Last change on this file since 2592 was 2592, checked in by toby, 6 years ago

redo scaling for color map sliders

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