source: trunk/GSASIIimgGUI.py @ 2692

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

don't allow duplicate PDF entries; PWDR and PDF names must match.

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