source: trunk/GSASIIimgGUI.py @ 3757

Last change on this file since 3757 was 3737, checked in by vondreele, 7 years ago

reorder descriptions in front of G2ctrlGUI to alpha order - now easier to find stuff & add a couple more to list
use copy.deepcopy on PDF controls inside autointegrate
fixes to supersymmetry stuff & magnetic stuff to get correct cell multiplicities etc.
A fix t G2strIO to make ssymetry refinements work

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