source: trunk/GSASIIimgGUI.py @ 3848

Last change on this file since 3848 was 3848, checked in by vondreele, 3 years ago

since id is a python routine which could be useful in debugging, replaced id with Id (or pid in one place)

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