source: trunk/autoint.py @ 2082

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

provide autoint interpolation table; merge into std GSAS-II version

  • Property svn:eol-style set to native
File size: 32.6 KB
Line 
1import os
2import sys
3import copy
4import glob
5import re
6import bisect
7import numpy as np
8import wx
9import wx.lib.mixins.listctrl  as  listmix
10import GSASIIpath
11import GSASIIIO as G2IO
12import GSASIIctrls as G2G
13import GSASIIgrid as G2gd
14import GSASIIimgGUI as G2imG
15import GSASIIpy3 as G2py3
16
17print 'loading autoint'
18
19def ReadMask(filename):
20    'Read a mask (.immask) file'
21    File = open(filename,'r')
22    save = {}
23    S = File.readline()
24    while S:
25        if S[0] == '#':
26            S = File.readline()
27            continue
28        [key,val] = S[:-1].split(':')
29        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
30            save[key] = eval(val)
31        S = File.readline()
32    File.close()
33    G2imG.CleanupMasks(save)
34    return save
35
36def ReadControls(filename):
37    'read an image controls (.imctrl) file'
38    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
39            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
40            'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg',
41            'PolaVal','SampleAbs','dark image','background image']
42    File = open(filename,'r')
43    save = {}
44    S = File.readline()
45    while S:
46        if S[0] == '#':
47            S = File.readline()
48            continue
49        [key,val] = S[:-1].split(':')
50        if key in ['type','calibrant','binType','SampleShape',]:    #strings
51            save[key] = val
52        elif key in ['rotation']:
53            save[key] = float(val)
54        elif key in ['center',]:
55            if ',' in val:
56                save[key] = eval(val)
57            else:
58                vals = val.strip('[] ').split()
59                save[key] = [float(vals[0]),float(vals[1])] 
60        elif key in cntlList:
61            save[key] = eval(val)
62        S = File.readline()
63    File.close()
64    return save
65
66def Read_imctrl(imctrl_file):
67    '''Read an image control file and record control parms into a dict, with some simple
68    type conversions
69    '''
70    file_opt = options = {}
71    save = {'filename':imctrl_file}
72    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
73    if os.path.exists(immask_file):
74        save['maskfile'] = immask_file
75    else:
76        save['maskfile'] = '(none)'
77    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
78                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
79                        'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg',
80                        'PolaVal','SampleAbs','dark image','background image']
81    File = open(imctrl_file,'r')
82    fullIntegrate = False
83    try:
84        S = File.readline()
85        while S:
86            if S[0] == '#':
87                S = File.readline()
88                continue
89            [key,val] = S[:-1].split(':')
90            if key in ['type','calibrant','binType','SampleShape',]:    #strings
91                save[key] = val
92            elif key == 'rotation':
93                save[key] = float(val)
94            elif key == 'fullIntegrate':
95                fullIntegrate = eval(val)
96            elif key == 'LRazimuth':
97                save['LRazimuth_min'],save['LRazimuth_max'] = eval(val)[0:2]
98            elif key == 'IOtth':
99                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
100            elif key == 'center':
101                if ',' in val:
102                    vals = eval(val)
103                else:
104                    vals = val.strip('[] ').split()
105                    vals = [float(vals[0]),float(vals[1])] 
106                save['center_x'],save['center_y'] = vals[0:2]
107            elif key in cntlList:
108                save[key] = eval(val)
109            S = File.readline()
110    finally:
111        File.close()
112        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0,0
113    return save
114   
115class AutoIntFrame(wx.Frame):
116    '''Creates a wx.Frame window for the Image AutoIntegration.
117    The intent is that this will be used as a non-modal dialog window.
118   
119    Implements a Start button that morphs into a pause and resume button.
120    This button starts a processing loop that is repeated every
121    :meth:`PollTime` seconds.
122
123    :param wx.Frame G2frame: main GSAS-II frame
124    :param float PollTime: frequency in seconds to repeat calling the
125      processing loop. (Default is 3.0 seconds.)
126    '''
127    def OnTimerLoop(self,event):
128        '''A method that is called every :meth:`PollTime` seconds that is
129        used to check for new files and process them. This is called only
130        after the "Start" button is pressed (when its label reads "Pause").
131        '''
132        G2frame = self.G2frame
133        try:
134            self.currImageList = sorted(
135                glob.glob(os.path.join(self.imagedir,self.params['filter'])))
136        except IndexError:
137            self.currImageList = []
138            return
139
140        # Create a list of image files that have already been read
141        imageFileList = []
142        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
143            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
144            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
145            if imagefile not in imageFileList: imageFileList.append(imagefile)
146        # loop over image files matching glob, reading in new ones
147        for newImage in self.currImageList:
148            if newImage in imageFileList: continue # already read
149            for imgId in G2IO.ReadImages(G2frame,newImage):
150                controlsDict = G2frame.PatternTree.GetItemPyData(
151                    G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
152                ImageMasks = G2frame.PatternTree.GetItemPyData(
153                    G2gd.GetPatternTreeItemId(G2frame,imgId, 'Masks'))
154                if self.params['Mode'] == 'table':
155                    dist = controlsDict['distance']
156                    interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
157                    self.ImageControls = ReadControls(imgctrl)
158                    self.ImageControls.update(interpDict)
159                    self.ImageControls['showLines'] = True
160                    self.ImageControls['ring'] = []
161                    self.ImageControls['rings'] = []
162                    self.ImageControls['ellipses'] = []
163                    self.ImageControls['setDefault'] = False
164                    for i in 'range','size','GonioAngles':
165                        if i in self.ImageControls:
166                            del self.ImageControls[i]
167                    # load copy of Image Masks
168                    if immask:
169                        self.ImageMasks = ReadMask(immask)
170                        del self.Thresholds['Thresholds']
171                    else:
172                        self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
173                # update controls from master
174                controlsDict.update(self.ImageControls)
175                # update masks from master w/o Thresholds
176                ImageMasks.update(self.ImageMasks)
177        # now integrate the images that have not already been processed before
178        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
179            if img in G2frame.IntegratedList: continue
180            G2frame.IntegratedList.append(img)
181            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
182            G2frame.Image = imgId
183            G2frame.PickId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls')
184            #  integrate in this entry
185            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
186            G2frame.ImageZ = G2IO.GetImageData(G2frame,imagefile,True,imagetag)
187            masks = G2frame.PatternTree.GetItemPyData(
188                G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
189            data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
190            self.oldImagefile = '' # mark image as changed; reread as needed
191            # simulate a Image Controls press, since that is where the
192            # integration is hidden
193            G2imG.UpdateImageControls(G2frame,data,masks,IntegrateOnly=True)
194            # split name and control number
195            s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
196            namepre = s[0]
197            if len(s) > 1:
198                namenum = s[1]
199            else:
200                namenum = ''
201            # write out the images in the selected formats and save the names,
202            # reset will delete them
203            for Id in G2frame.IntgOutList:
204                treename = G2frame.PatternTree.GetItemText(Id)
205                G2frame.AutointPWDRnames.append(treename)
206                Sdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Sample Parameters'))
207                # determine the name for the current file
208                fileroot = namepre
209                if len(G2frame.IntgOutList) > 1:
210                    fileroot += "_AZM"
211                    if 'Azimuth' in Sdata:
212                        fileroot += str(int(10*Sdata['Azimuth']))
213                    fileroot += "_" 
214                fileroot += namenum
215                # loop over selected formats
216                for dfmt in self.fmtlist:
217                    if not self.params[dfmt[1:]]: continue
218                    if self.params['SeparateDir']:
219                        subdir = dfmt[1:]
220                    else:
221                        subdir = ''
222                    fil = os.path.join(self.params['outdir'],subdir,fileroot)
223                    print('writing file '+fil+dfmt)
224                    G2IO.ExportPowder(G2frame,treename,fil,dfmt)
225       
226        if GSASIIpath.GetConfigValue('debug'):
227            import datetime
228            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
229
230    def StartLoop(self):
231        '''Save current Image params for use in future integrations
232        also label the window so users understand whatis being used
233        '''
234        print '\nStarting new autointegration\n'
235        G2frame = self.G2frame
236        # show current IMG base
237        self.ControlBaseLbl.SetLabel(G2frame.PatternTree.GetItemText(G2frame.Image))
238        if self.params['Mode'] != 'table':
239            # load copy of Image Controls from current image and clean up
240            # items that should not be copied
241            self.ImageControls = copy.deepcopy(
242                G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
243                    G2frame,G2frame.Image, 'Image Controls')))
244            self.ImageControls['showLines'] = True
245            self.ImageControls['ring'] = []
246            self.ImageControls['rings'] = []
247            self.ImageControls['ellipses'] = []
248            self.ImageControls['setDefault'] = False
249            del self.ImageControls['range']
250            del self.ImageControls['size']
251            del self.ImageControls['GonioAngles']
252            # load copy of Image Masks, keep thresholds
253            self.ImageMasks = copy.deepcopy(
254                G2frame.PatternTree.GetItemPyData(
255                    G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks')))
256            self.Thresholds = self.ImageMasks['Thresholds'][:]
257        # make sure all output directories exist
258        if self.params['SeparateDir']:
259            for dfmt in self.fmtlist:
260                if not self.params[dfmt[1:]]: continue
261                dir = os.path.join(self.params['outdir'],dfmt[1:])
262                if not os.path.exists(dir): os.makedirs(dir)
263        else:
264            if not os.path.exists(self.params['outdir']):
265                os.makedirs(self.params['outdir'])
266        if self.Reset: # special things to do after Reset has been pressed
267            # reset controls and masks for all IMG items in tree to master
268            for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
269                # update controls from master
270                controlsDict = G2frame.PatternTree.GetItemPyData(
271                    G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
272                controlsDict.update(self.ImageControls)
273                # update masks from master
274                ImageMasks = G2frame.PatternTree.GetItemPyData(
275                    G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
276                ImageMasks.update(self.ImageMasks)
277            # delete all PWDR items created after last Start was pressed
278            idlist = []
279            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
280            while item:
281                itemName = G2frame.PatternTree.GetItemText(item)
282                if itemName in G2frame.AutointPWDRnames:
283                    idlist.append(item)
284                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
285            for item in idlist:
286                G2frame.PatternTree.Delete(item)
287            self.Reset = False
288        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
289
290    def __init__(self,G2frame,PollTime=60.0):
291        def OnStart(event):
292            '''Called when the start button is pressed. Changes button label
293            to Pause. When Pause is pressed the label changes to Resume.
294            When either Start or Resume is pressed, the processing loop
295            is started. When Pause is pressed, the loop is stopped.
296            '''
297            # check inputs for errors before starting
298            #err = ''
299            #if not any([self.params[fmt] for fmt in self.fmtlist]):
300            #    err += '\nPlease select at least one output format\n'
301            #if err:
302            #    G2G.G2MessageBox(self,err)
303            #    return
304            # change button label
305            if btnstart.GetLabel() != 'Pause':
306                btnstart.SetLabel('Pause')
307                if self.timer.IsRunning(): self.timer.Stop()
308                self.StartLoop()
309                self.OnTimerLoop(None) # run once immediately and again after delay
310                self.timer.Start(int(1000*PollTime),oneShot=False)
311                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
312            else:
313                btnstart.SetLabel('Resume')
314                if self.timer.IsRunning(): self.timer.Stop()
315                print('\nPausing autointegration\n')
316                self.Status.SetStatusText('Press Resume to continue integration or Reset to prepare to reintegrate all images')
317
318        def OnReset(event):
319            '''Called when Reset button is pressed. This stops the
320            processing loop and resets the list of integrated files so
321            all images can be reintegrated.
322            '''
323            btnstart.SetLabel('Restart')
324            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
325            if self.timer.IsRunning(): self.timer.Stop()
326            self.Reset = True
327            self.G2frame.IntegratedList = []
328           
329        def OnQuit(event):
330            '''Stop the processing loop and close the Frame
331            '''
332            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
333            wx.CallAfter(self.Destroy)
334           
335        def OnBrowse(event):
336            '''Responds when the Browse button is pressed to load a file.
337            The routine determines which button was pressed and gets the
338            appropriate file type and loads it into the appropriate place
339            in the dict.
340            '''
341            if btn3 == event.GetEventObject():
342                dlg = wx.DirDialog(
343                    self, 'Select directory for output files',
344                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
345                dlg.CenterOnParent()
346                try:
347                    if dlg.ShowModal() == wx.ID_OK:
348                        self.params['outdir'] = dlg.GetPath()
349                        fInp3.SetValue(self.params['outdir'])
350                finally:
351                    dlg.Destroy()
352                return
353               
354        def OnRadioSelect(event):
355            '''Respond to a radiobutton selection and when in table
356            mode, get parameters from user.
357            '''
358            self.Evaluator = None
359            if r2.GetValue():
360                self.params['Mode'] = 'table'
361                try:
362                    dlg = IntegParmTable(self.G2frame) # create the dialog
363                    if dlg.ShowModal() == wx.ID_OK:
364                        self.Evaluator = DefineEvaluator(dlg)
365                    else:
366                        r1.SetValue(True)
367                finally:
368                    dlg.Destroy()
369            else:
370                self.params['Mode'] = 'active'
371        ##################################################
372        # beginning of __init__ processing
373        ##################################################
374        self.G2frame = G2frame
375        self.Evaluator = None
376        self.params = {}
377        self.Reset = False
378        self.params['IMGfile'] = ''
379        self.params['MaskFile'] = ''
380        self.params['IgnoreMask'] = True
381        self.fmtlist = G2IO.ExportPowderList(G2frame)
382        self.timer = wx.Timer()
383        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
384
385        controlsId = G2frame.PatternTree.GetSelection()
386        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(G2frame.Image)       
387        self.imagedir,fileroot = os.path.split(imagefile)
388        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
389        self.params['outdir'] = os.path.abspath(self.imagedir)
390        wx.Frame.__init__(self, G2frame,title='Automatic Integration')
391        self.Status = self.CreateStatusBar()
392        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
393        mnpnl = wx.Panel(self)
394        mnsizer = wx.BoxSizer(wx.VERTICAL)
395        sizer = wx.BoxSizer(wx.HORIZONTAL)
396        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Integration based on: '))
397        self.ControlBaseLbl = wx.StaticText(mnpnl, wx.ID_ANY,'?')
398        self.ControlBaseLbl.SetLabel(G2frame.PatternTree.GetItemText(G2frame.Image))
399        sizer.Add(self.ControlBaseLbl)
400        mnsizer.Add(sizer,0,wx.ALIGN_LEFT,1)
401        # file filter stuff
402        sizer = wx.BoxSizer(wx.HORIZONTAL)
403        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Image filter'))
404        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter')
405        sizer.Add(flterInp)
406        mnsizer.Add(sizer,0,wx.ALIGN_RIGHT,1)
407        # box for integration controls & masks input
408        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Controls/Masks source")
409        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
410        r1 = wx.RadioButton(mnpnl, wx.ID_ANY, "Use Active Image",
411                            style = wx.RB_GROUP)
412        r1.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
413        lblsizr.Add(r1)
414        r1.SetValue(True)
415        r2 = wx.RadioButton(mnpnl, wx.ID_ANY, "Use from table")
416        lblsizr.Add(r2)
417        r2.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
418        mnsizer.Add(lblsizr)
419
420        # box for output selections
421        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
422        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
423        sizer = wx.BoxSizer(wx.HORIZONTAL)
424        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '))
425        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',
426                                       notBlank=False,size=(300,-1))
427        sizer.Add(fInp3)
428        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
429        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
430        sizer.Add(btn3)
431        lblsizr.Add(sizer)
432        #lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
433        sizer = wx.BoxSizer(wx.HORIZONTAL)
434        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
435        for dfmt in self.fmtlist:
436            fmt = dfmt[1:]
437            self.params[fmt] = False
438            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
439            sizer.Add(btn)
440        lblsizr.Add(sizer)
441        sizer = wx.BoxSizer(wx.HORIZONTAL)
442        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
443        self.params['SeparateDir'] = False
444        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
445        lblsizr.Add(sizer)
446        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER,1)
447
448        # buttons on bottom
449        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
450        sizer = wx.BoxSizer(wx.HORIZONTAL)
451        sizer.Add((20,-1))
452        btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
453        btnstart.Bind(wx.EVT_BUTTON, OnStart)
454        sizer.Add(btnstart)
455        btnstop = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
456        btnstop.Bind(wx.EVT_BUTTON, OnReset)
457        sizer.Add(btnstop)
458        sizer.Add((20,-1),wx.EXPAND,1)
459        btnquit = wx.Button(mnpnl,  wx.ID_ANY, "Close")
460        btnquit.Bind(wx.EVT_BUTTON, OnQuit)
461        sizer.Add(btnquit)
462        sizer.Add((20,-1))
463        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
464       
465        # finish up window
466        mnpnl.SetSizer(mnsizer)
467        OnRadioSelect(None) # disable widgets
468        mnsizer.Fit(self)
469        self.CenterOnParent()
470        self.Show()
471
472def DefineEvaluator(dlg):
473    '''Creates a function that provides interpolated values for a given distance value
474    '''
475    def Evaluator(dist):
476        '''Interpolate image parameters for a supplied distance value
477
478        :param float dist: distance to use for interpolation
479        :returns: a list with 3 items:
480
481          * a dict with parameter values,
482          * the closest imctrl and
483          * the closest maskfile (or None)
484        '''           
485        x = np.array([float(i) for i in parms[0]])
486        closest = abs(x-dist).argmin()
487        closeX = x[closest]
488        D = {'distance':dist}
489        imctfile = IMfileList[closest]
490        if parms[-1][closest].lower() != '(none)':
491            maskfile = parms[-1][closest]
492        else:
493            maskfile = None
494        for c in range(1,cols-1):
495            lbl = ParmList[c]
496            if lbl in nonInterpVars:
497                D[lbl] = float(parms[c][closest])
498            else:
499                y = np.array([float(i) for i in parms[c]])
500                D[lbl] = np.interp(dist,x,y)
501        # full integration when angular range is 0
502        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
503        # conversion for paired values
504        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
505            r = a.split('_')[0]
506            D[r] = [D[a],D[b]]
507            del D[a]
508            del D[b]
509        return D,imctfile,maskfile
510    # save local copies of values needed in Evaluator
511    parms = dlg.ReadImageParmTable()
512    IMfileList = dlg.IMfileList
513    cols = dlg.list.GetColumnCount()
514    ParmList = dlg.ParmList
515    nonInterpVars = dlg.nonInterpVars
516    return Evaluator
517
518class IntegParmTable(wx.Dialog):
519    '''Creates a dialog window with a table of integration parameters.
520    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
521    In this case, :func:`DefineEvaluator` should be called to obtain a function that
522    creates a dictionary with interpolated parameter values.
523    '''
524    ParmList = ('distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
525            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
526            'maskfile',
527            )
528    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
529                     'outChannels')  # values in this list are taken from nearest rather than interpolated
530    HeaderList = ('Det Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
531            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
532            'Mask File',
533            )
534    def __init__(self,G2frame):
535        self.G2frame = G2frame
536        self.parms = [] # list of values by column
537        self.IMfileList = [] # list of .imctrl file names for each entry in table
538        wx.Dialog.__init__(self,G2frame,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
539        files = []
540        try:
541            dlg = wx.FileDialog(self, 'Select image control files or previous table', 
542                                style=wx.OPEN| wx.MULTIPLE,
543                                wildcard='image control files (.imctrl)|*.imctrl|Integration table (*.imtbl)|*.imtbl')
544            if dlg.ShowModal() == wx.ID_OK:
545                files = dlg.GetPaths()
546                self.parms,self.IMfileList = self.ReadFiles(files)
547        finally:
548            dlg.Destroy()
549        if not files:
550            wx.CallAfter(self.EndModal,wx.ID_CANCEL)
551            return
552        mainSizer = wx.BoxSizer(wx.VERTICAL)
553        self.list = ImgIntLstCtrl(self, wx.ID_ANY,
554                      style=wx.LC_REPORT
555                          | wx.BORDER_SUNKEN
556                         #| wx.BORDER_NONE
557                         )
558        mainSizer.Add(self.list,1,wx.EXPAND,1)
559        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
560        btn = wx.Button(self, wx.ID_OK)
561        btnsizer.Add(btn)
562        btn = wx.Button(self, wx.ID_ANY,'Save')
563        btn.Bind(wx.EVT_BUTTON,self._onSave)
564        btnsizer.Add(btn)
565        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
566        btn.Bind(wx.EVT_BUTTON,self._onClose)
567        btnsizer.Add(btn)
568        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
569        self.SetSizer(mainSizer)
570        self.list.FillList(self.parms)
571        mainSizer.Layout()
572        mainSizer.Fit(self)
573       
574    def ReadFiles(self,files):
575        '''Reads a list of .imctrl files or a single .imtbl file
576        '''
577        tmpDict = {}
578        if not files: return
579        # option 1, a dump from a previous save
580        if os.path.splitext(files[0])[1] == '.imtbl':
581            fp = open(files[0],'r')
582            S = fp.readline()
583            while S:
584                if S[0] != '#':
585                    [key,val] = S[:-1].split(':')
586                    tmpDict[key] = eval(val)
587                S = fp.readline()
588            fp.close()
589            # delete entries
590            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
591            if m1:
592                print('\nimctrl file not found:')
593                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
594            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
595            if m2:
596                print('\nmask file not found')
597                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
598            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
599            if m3:
600                print('\nDropping entries due to negative distance: '+str(m3))
601            m = sorted(set(m1 + m2 + m3))
602            m.reverse()
603            for c in m:
604                for key in tmpDict:
605                    del tmpDict[key][c]
606            fileList = tmpDict.get('filenames','[]')
607            parms = []
608            for key in self.ParmList:
609                try:
610                    float(tmpDict[key][0])
611                    parms.append([str(G2py3.FormatSigFigs(val,sigfigs=5)) for val in tmpDict[key]])
612                except ValueError:
613                    parms.append(tmpDict[key])
614            return parms,fileList
615        # option 2, read in a list of files
616        for file in files: # read all files; place in dict by distance
617            imgDict = Read_imctrl(file)
618            tmpDict[imgDict.get('distance')] = imgDict
619        parms = [[] for key in self.ParmList]
620        fileList = []
621        for d in sorted(tmpDict):
622            fileList.append(tmpDict[d].get('filename'))
623            if d is None: continue
624            if d < 0: continue
625            for i,key in enumerate(self.ParmList):
626                val = tmpDict[d].get(key)
627                try:
628                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
629                except:
630                    val = str(val)
631                parms[i].append(val)
632        return parms,fileList
633   
634    def ReadImageParmTable(self):
635        '''Reads possibly edited values from the ListCtrl table and returns a list
636        of values for each column.
637        '''
638        rows = self.list.GetItemCount()
639        cols = self.list.GetColumnCount()
640        parms = []
641        for c in range(cols):
642            lbl = self.ParmList[c]
643            parms.append([])
644            for r in range(rows):
645                parms[c].append(self.list.GetItem(r,c).GetText())
646        return parms
647
648    def _onClose(self,event):
649        'Called when Cancel button is pressed'
650        self.EndModal(wx.ID_CANCEL)
651       
652    def _onSave(self,event):
653        'Called when save button is pressed; creates a .imtbl file'
654        fil = ''
655        if self.G2frame.GSASprojectfile:
656            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
657        dir,f = os.path.split(fil)
658        try:
659            dlg = wx.FileDialog(self, 'Save table data as',
660                        defaultDir=dir, defaultFile=f, style=wx.SAVE)
661            if dlg.ShowModal() != wx.ID_OK: return
662            fil = dlg.GetPath()
663            fil = os.path.splitext(fil)[0]+'.imtbl'
664        finally:
665            dlg.Destroy()       
666        parms = self.ReadImageParmTable()
667        print('Writing image parameter table as '+fil)
668        fp = open(fil,'w')
669        for c in range(len(parms)-1):
670            lbl = self.ParmList[c]
671            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
672        lbl = self.ParmList[c+1]
673        fp.write(lbl+': '+str(parms[c+1])+'\n')
674        lbl = 'filenames'
675        fp.write(lbl+': '+str(self.IMfileList)+'\n')
676        fp.close()
677   
678class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
679    '''Creates a custom ListCtrl for editing Image Integration parameters
680    '''
681    def __init__(self, parent, ID, pos=wx.DefaultPosition,
682                 size=(1000,200), style=0):
683        self.parent=parent
684        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
685        listmix.ListCtrlAutoWidthMixin.__init__(self)
686        listmix.TextEditMixin.__init__(self)
687        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
688        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
689    def FillList(self,parms):
690        'Places the current parms into the table'
691        self.ClearAll()
692        self.rowlen = len(self.parent.ParmList)
693        for i,lbl in enumerate(self.parent.HeaderList):
694            self.InsertColumn(i, lbl)
695        for r,d in enumerate(parms[0]):
696            if float(d) < 0: continue
697            index = self.InsertStringItem(sys.maxint, d)
698            for j in range(1,len(parms)):
699                self.SetStringItem(index, j, parms[j][r])
700        for i,lbl in enumerate(self.parent.ParmList):
701            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
702
703    def OnDouble(self,evt):
704        'respond to a double-click'
705        self.CloseEditor()
706        fil = '(none)'
707        try:
708            dlg = wx.FileDialog(G2frame, 'Select mask or control file to add (Press cancel if none)', 
709                                style=wx.OPEN,
710                                wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
711            if dlg.ShowModal() == wx.ID_OK:
712                fil = dlg.GetPath()
713        finally:
714            dlg.Destroy()
715        if os.path.splitext(fil)[1] != '.imctrl':
716            self.SetStringItem(self.curRow, self.rowlen-1, fil)
717            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
718        else:
719            # insert or overwrite an instrument parameter set
720            if not os.path.exists(fil):
721                print('Does not exist: '+fil)
722                return
723            imgDict = Read_imctrl(fil)
724            dist = imgDict['distance']
725            parms = self.parent.ReadImageParmTable()
726            x = np.array([float(i) for i in parms[0]])
727            closest = abs(x-dist).argmin()
728            closeX = x[closest]
729            # fix IMfileList
730            for c,lbl in enumerate(self.parent.ParmList):
731                try:
732                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
733                except ValueError:
734                    vali = imgDict[lbl]
735                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
736                    parms[c][closest] = vali
737                elif dist > closeX: # insert after
738                    parms[c].insert(closest+1,vali)
739                else:
740                    parms[c].insert(closest,vali)
741            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
742                self.parent.IMfileList[closest] = fil
743            elif dist > closeX: # insert after
744                self.parent.IMfileList.insert(closest+1,fil)
745            else:
746                self.parent.IMfileList.insert(closest,fil)
747            self.FillList(parms)
748
749if __name__ == '__main__':
750    app = wx.PySimpleApp()
751    G2frame = wx.Frame(None) # create a top-level frame as a stand-in for the GSAS-II data tree
752    G2frame.Show() 
753    frm = AutoIntFrame(G2frame) # test the one above
754    app.MainLoop()
755 
Note: See TracBrowser for help on using the repository browser.