source: trunk/GSASIIimgGUI.py @ 4009

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

add convariance to recalibrate all, so esd's are computed properly seq. ref. table

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