source: trunk/GSASIIimgGUI.py @ 3926

Last change on this file since 3926 was 3926, checked in by toby, 3 years ago

bugfix for plot updates: conflict for single xtal

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