source: trunk/GSASIIimgGUI.py @ 2695

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

use min(intensity) as default for PDF Flat Bkg - gives better start for optimize PDF
refactor sequential refinements so that old results are updated (not erased)

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