source: trunk/GSASIIIntPDFtool.py @ 4046

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

introduce external autoInt

  • Property svn:eol-style set to native
File size: 40.9 KB
Line 
1'''Independent-running GSAS-II based auto-integration program with minimal
2GUI, no visualization but intended to implement significant levels of
3parallelization.
4'''
5# Autointegration from
6# $Id: GSASIIimgGUI.py 3926 2019-04-23 18:11:07Z toby $
7# hacked for stand-alone use
8#
9# idea: select image file type & set filter from that
10#
11from __future__ import division, print_function
12import os
13import copy
14import glob
15import time
16import re
17import math
18import sys
19import wx
20import wx.lib.mixins.listctrl  as  listmix
21import wx.grid as wg
22import numpy as np
23import GSASIIpath
24GSASIIpath.SetBinaryPath(True)
25GSASIIpath.SetVersionNumber("$Revision: $")
26import GSASIIIO as G2IO
27import GSASIIctrlGUI as G2G
28import GSASIIobj as G2obj
29import GSASIIpy3 as G2py3
30import GSASIIimgGUI as G2imG
31import GSASIIfiles as G2fil
32import GSASIIscriptable as G2sc
33
34class AutoIntFrame(wx.Frame):
35    '''Creates a wx.Frame window for the Image AutoIntegration.
36    The intent is that this will be used as a non-modal dialog window.
37   
38    Implements a Start button that morphs into a pause and resume button.
39    This button starts a processing loop that is repeated every
40    :meth:`PollTime` seconds.
41
42    :param wx.Frame G2frame: main GSAS-II frame
43    :param float PollTime: frequency in seconds to repeat calling the
44      processing loop. (Default is 30.0 seconds.)
45    '''
46    def __init__(self,G2frame,PollTime=30.0):
47        def OnStart(event):
48            '''Called when the start button is pressed. Changes button label
49            to Pause. When Pause is pressed the label changes to Resume.
50            When either Start or Resume is pressed, the processing loop
51            is started. When Pause is pressed, the loop is stopped.
52            '''
53            self.Pause = False
54            # change button label
55            if self.btnstart.GetLabel() != 'Pause':
56                self.btnstart.SetLabel('Pause')
57                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
58                if self.timer.IsRunning(): self.timer.Stop()
59                self.PreventTimerReEntry = False
60                if self.StartLoop():
61                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
62                    return
63                self.OnTimerLoop(None) # run once immediately
64                if not self.Pause:
65                    # no pause, so start timer to check for new files
66                    self.timer.Start(int(1000*PollTime),oneShot=False)
67                    return
68            # we will get to this point if Paused
69            self.OnPause()
70           
71        def OnReset(event):
72            '''Called when Reset button is pressed. This stops the
73            processing loop and resets the list of integrated files so
74            all images can be reintegrated.
75            '''
76            self.btnstart.SetLabel('Restart')
77            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
78            if self.timer.IsRunning(): self.timer.Stop()
79            self.Reset = True
80            self.Pause = True
81            self.ProcessedList = []
82            self.ShowMatchingFiles(None)
83           
84        def OnQuit(event):
85            '''Stop the processing loop and close the Frame
86            '''
87            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
88            wx.CallAfter(self.Destroy)
89           
90        def OnBrowse(event):
91            '''Responds when the Browse button is pressed to load a file.
92            The routine determines which button was pressed and gets the
93            appropriate file type and loads it into the appropriate place
94            in the dict.
95            '''
96            if btn3 == event.GetEventObject():
97                dlg = wx.DirDialog(
98                    self, 'Select directory for output files',
99                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
100                dlg.CenterOnParent()
101                try:
102                    if dlg.ShowModal() == wx.ID_OK:
103                        self.params['outdir'] = dlg.GetPath()
104                        fInp3.SetValue(self.params['outdir'])
105                finally:
106                    dlg.Destroy()
107                return
108                               
109        def showPDFctrls(event):
110            '''Called to show or hide AutoPDF widgets. Note that TextCtrl's
111            must be included in the sizer layout with .Show(True) before
112            .Show(False) will work properly.
113            '''
114            TestInput()
115            if len(self.pdfList) == 0 or self.params['TableMode']:
116                lbl4b.SetValue(False)
117                self.params['ComputePDF'] = False
118                lbl4b.Enable(False)
119            else:
120                lbl4b.Enable(True)
121
122            for w in [self.pbkg[i][4] for i in (0,1,2)]:
123                w.Enable(self.params['ComputePDF'])
124                w.Show(self.params['ComputePDF'])
125            #for o in pdfwidgets+[self.pbkg[i][2] for i in (0,1,2)]:
126            for o in pdfwidgets+[self.pdfSel]+[self.pbkg[i][1] for i in range(3)]:
127                o.Enable(self.params['ComputePDF'])
128            if self.params['ComputePDF']:
129                c = "black"
130            else:
131                c = "gray"
132            for l in ([lbl4,lbl4a,lbl5,lbl5a,lbl5b]
133                          +[self.pbkg[i][0] for i in (0,1,2)]
134                          +[self.pbkg[i][5] for i in (0,1,2)]):
135                l.SetForegroundColour(c)
136            checkPDFselection()
137            self.SendSizeEvent()
138                                   
139        def GetGPXInputFile(event):
140            'Get and read input from input GPX file'
141            pth = self.params['readdir']
142            lbl = 'Select a project input file'
143            filtyp = 'project file (.gpx)|*.gpx'
144            if event is not None:
145                dlg = wx.FileDialog(self,lbl, pth,
146                        style=wx.FD_OPEN,wildcard=filtyp)
147                dlg.CenterOnParent()
148                if dlg.ShowModal() == wx.ID_OK:
149                    self.gpxin[3] = dlg.GetPath()
150                dlg.Destroy()
151            if not os.path.exists(self.gpxin[3]):
152                G2G.G2MessageBox(self,'Error: file {} not found.'.format(self.gpxin[3]))
153                return
154            SetGPXInputFile()
155        def SetGPXInputFile():
156            gpx = G2sc.G2Project(self.gpxin[3])
157            self.imgList = gpx.images()
158            self.histList = gpx.histograms()
159            self.pdfList = gpx.pdfs()
160            if not self.imgList:
161                G2G.G2MessageBox(self,'Error: no images in {}.'.format(self.gpxin[3]))
162                return
163            self.gpxin[1].SetValue(self.gpxin[3])           
164            self.imprm[1].Clear()
165            self.imprm[1].AppendItems([i.name for i in self.imgList])
166            if len(self.imgList) == 1:
167                self.imprm[1].SetSelection(0)
168            self.maskfl[1].Clear()
169            self.maskfl[1].AppendItems(['']+[i.name for i in self.imgList])
170            for i in range(3):
171                self.pbkg[i][1].Clear()
172                self.pbkg[i][1].AppendItems(['']+[i.name for i in self.histList])
173            self.pdfSel.Clear()
174            self.pdfSel.AppendItems([i.name for i in self.pdfList])
175            showPDFctrls(None)
176
177        def TestInput(*args,**kwargs):
178            'Determine if the start button should be enabled'
179            writingSomething = False
180            writingPDF = False
181            # is at least one output file selected?
182            for dfmt in self.fmtlist:
183                fmt = dfmt[1:]
184                if self.params['outsel'][fmt]:
185                    writingSomething = True
186                    break
187            if self.params['ComputePDF']:
188                for fmt in self.PDFformats:
189                    if self.params['outsel'][fmt]:
190                        writingPDF = writingSomething = True
191                        break
192            if not writingSomething: 
193                self.EnableIntButtons(False)
194                return
195            # do we have integration input?
196            if not self.params['TableMode']:
197                if not self.gpxin[3]:
198                    self.EnableIntButtons(False)
199                    return
200                elif not os.path.exists(self.gpxin[3]):
201                    self.EnableIntButtons(False)
202                    return
203                elif len(self.imgList) == 0:
204                    self.EnableIntButtons(False)
205                    return
206                else:
207                    self.EnableIntButtons(True)
208            else:
209                if self.ImgTblParms:
210                    self.EnableIntButtons(True)
211                else:
212                    self.EnableIntButtons(False)
213                    return
214            # do we have PDF input, if requested
215            if self.params['ComputePDF']:
216                if len(self.pdfList) == 0 or not writingPDF:
217                    self.EnableIntButtons(False)
218                elif 'Error' in self.formula:
219                    self.EnableIntButtons(False)
220               
221        def checkPDFselection():
222            'Read PDF entry from input GPX file & show in GUI'
223            pdfEntry = self.pdfSel.GetStringSelection()
224            if not self.pdfList:
225                self.params['ComputePDF'] = False
226                lbl4b.Enable(False)
227                return
228            else:
229                lbl4b.Enable(True)
230            if pdfEntry not in [i.name for i in self.pdfList]:
231                # strange something -- not selected
232                self.pdfSel.SetSelection(0)
233                pdfEntry = self.pdfSel.GetStringSelection()
234            if self.gpxInp is None or self.gpxInp.filename != self.gpxin[3]:
235                self.gpxInp = G2sc.G2Project(self.gpxin[3])
236            try: 
237                PDFobj = self.gpxInp.pdf(pdfEntry)
238            except KeyError:
239                print("PDF entry not found: {}".format(pdfEntry))
240                return
241            histNames = [i.name for i in self.histList]
242            for i,lbl in enumerate(('Sample Bkg.','Container',
243                                    'Container Bkg.')):
244                self.pbkg[i][4].SetValue(str(PDFobj.data['PDF Controls'][lbl]['Mult']))
245                self.pbkg[i][6] = PDFobj.data['PDF Controls'][lbl]['Mult']
246                try:
247                    i = 1 + histNames.index(PDFobj.data['PDF Controls'][lbl]['Name'])
248                    self.pbkg[i][1].SetSelection(i)
249                except ValueError:
250                    i = 0
251                    self.pbkg[i][1].SetSelection(0)
252                    if PDFobj.data['PDF Controls'][lbl]['Name']:
253                        print('PDF {} hist entry {} not found'.format(
254                            lbl,PDFobj.data['PDF Controls'][lbl]['Name']))
255                        PDFobj.data['PDF Controls'][lbl]['Name'] = ''
256            self.formula = ''
257            for el in PDFobj.data['PDF Controls']['ElList']:
258                i = PDFobj.data['PDF Controls']['ElList'][el]['FormulaNo']
259                if i <= 0:
260                    continue
261                elif i == 1:
262                    if self.formula: self.formula += ' '
263                    self.formula += '{}'.format(el)
264                else:
265                    if self.formula: self.formula += ' '
266                    self.formula += '{}({:.1f})'.format(el,i)
267            if not self.formula:
268                self.formula = 'Error: no chemical formula'
269            lbl5b.SetLabel(self.formula)
270            TestInput()
271                   
272        def ShowbyMode():
273            'create table or non-table integration section of GUI'
274            intPrmSizer.Clear(True)
275            if not self.params['TableMode']:
276                sizer = wx.BoxSizer(wx.HORIZONTAL)
277                self.gpxin[0] = wx.StaticText(mnpnl, wx.ID_ANY,'Project (gpx) file:')
278                sizer.Add(self.gpxin[0])
279                self.gpxin[1] = G2G.ValidatedTxtCtrl(mnpnl,self.gpxin,3,
280                                OKcontrol=TestInput,OnLeave=TestInput)
281                sizer.Add(self.gpxin[1],1,wx.EXPAND,1)
282                self.gpxin[2] = wx.Button(mnpnl, wx.ID_ANY, "Browse")
283                self.gpxin[2].Bind(wx.EVT_BUTTON, GetGPXInputFile)
284                sizer.Add(self.gpxin[2],0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
285                intPrmSizer.Add(sizer,0,wx.EXPAND,0)
286
287                sizer = wx.BoxSizer(wx.HORIZONTAL)
288                self.imprm[0] = wx.StaticText(mnpnl, wx.ID_ANY,'Image parms from:')
289                sizer.Add(self.imprm[0])
290                self.imprm[3] = 0
291                self.imprm[1] = G2G.G2ChoiceButton(mnpnl,[''],self.imprm,3,onChoice=TestInput)
292                sizer.Add(self.imprm[1],1,wx.EXPAND,1)
293                intPrmSizer.Add(sizer,0,wx.EXPAND,0)
294
295                sizer = wx.BoxSizer(wx.HORIZONTAL)
296                self.maskfl[0] = wx.StaticText(mnpnl, wx.ID_ANY,'Mask parms from:')
297                sizer.Add(self.maskfl[0])
298                self.maskfl[3] = 0
299                self.maskfl[1] = G2G.G2ChoiceButton(mnpnl,[''],self.maskfl,3,onChoice=TestInput)
300                sizer.Add(self.maskfl[1],1,wx.EXPAND,1)
301                intPrmSizer.Add(sizer,0,wx.EXPAND,0)
302            else:
303                sizer = wx.BoxSizer(wx.HORIZONTAL)
304                self.table = [None,None,None,None]
305                self.table[0] = wx.Button(mnpnl,  wx.ID_ANY, "Create table")
306                sizer.Add(self.table[0],0,wx.ALIGN_LEFT|wx.ALL,5)
307                self.table[0].Bind(wx.EVT_BUTTON, OnTableButton)
308                self.table[1] = wx.Button(mnpnl,  wx.ID_ANY, "Read table")
309                sizer.Add(self.table[1],0,wx.ALIGN_LEFT|wx.ALL,5)
310                self.table[1].Bind(wx.EVT_BUTTON, OnTableButton)
311                self.table[2] = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
312                sizer.Add(self.table[2],0,wx.ALIGN_LEFT|wx.ALL,5)
313                self.table[2].Bind(wx.EVT_BUTTON, OnTableButton)
314                #self.table[3] = wx.Button(mnpnl,  wx.ID_ANY, "Save table")
315                #sizer.Add(self.table[3],0,wx.ALIGN_LEFT|wx.ALL,5)
316                #self.table[3].Bind(wx.EVT_BUTTON, OnTableButton)
317                intPrmSizer.Add(sizer,0,wx.EXPAND,0)
318            # enable/disable based on status of files/table
319            TestInput()
320            mnsizer.Fit(self)
321           
322        def OnTableButton(event):
323            '''Called to edit/create the distance-dependent parameter look-up table.
324            '''
325            pth = self.params['readdir']
326            readFileList = []
327            parms,fileList = [], []
328            if event.GetEventObject() == self.table[0]:
329                dlg = wx.FileDialog(self, 'Build new table by selecting image control files', pth,
330                    style=wx.FD_OPEN| wx.FD_MULTIPLE,
331                    wildcard='Image control files (.imctrl)|*.imctrl')
332                dlg.CenterOnParent()
333                if dlg.ShowModal() == wx.ID_OK:
334                    readFileList = dlg.GetPaths()
335                dlg.Destroy()
336                if len(readFileList) <= 0: return
337            elif event.GetEventObject() == self.table[1]:
338                dlg = wx.FileDialog(self, 'Reload table by selecting saved file', pth,
339                    style=wx.FD_OPEN,
340                    wildcard='Integration table (*.imtbl)|*.imtbl')
341                dlg.CenterOnParent()
342                if dlg.ShowModal() == wx.ID_OK:
343                    readFileList = [dlg.GetPath()]
344                dlg.Destroy()
345                if len(readFileList) <= 0: return
346            elif event.GetEventObject() == self.table[2]:
347                parms = copy.deepcopy(self.ImgTblParms)
348                fileList = copy.copy(self.IMfileList)
349                if not parms:
350                    G2G.G2MessageBox(self,'Create or Read table first')
351                    return
352            dlg = None
353            try:
354                dlg = G2imG.IntegParmTable(self,parms,fileList,readFileList)
355                dlg.CenterOnParent()
356                if dlg.ShowModal() == wx.ID_OK:
357                    self.params['InterVals'] = SetupInterpolation(dlg)
358                    self.ImgTblParms = dlg.ReadImageParmTable()
359                    self.IMfileList = dlg.IMfileList
360                    self.params['TableMode'] = True
361                    self.params['ControlsTable'] = {}
362                    self.params['MaskTable'] = {}
363                    for f,m in zip(self.IMfileList,self.ImgTblParms[-1]):
364                        n = os.path.split(f)[1]
365                        if n in self.params['ControlsTable']:
366                            print('Warning overwriting entry {}'.format(n))
367                        self.params['ControlsTable'][n] = G2imG.ReadControls(f)
368                        if m and os.path.exists(m):
369                            self.params['MaskTable'][n] = G2imG.ReadMask(m)
370                        elif m != "(none)":
371                            print("Error: Mask file {} not found".format(m))
372                else:
373                    self.params['TableMode'] = False
374                    self.params['ControlsTable'] = {}
375                    self.params['MaskTable'] = {}
376                    self.imageBase = G2frame.Image
377            finally:
378                if dlg: dlg.Destroy()
379            TestInput()
380           
381        ##################################################
382        # beginning of __init__ processing
383        ##################################################
384        self.G2frame = G2frame
385        self.ImgTblParms = None
386        self.IMfileList = None
387        self.params = {}
388        self.Reset = False
389        self.Pause = False
390        self.PreventReEntryShowMatch = False
391        self.PreventTimerReEntry = False
392        self.params['ControlsTable'] = {}
393        self.params['MaskTable'] = {}
394        G2sc.LoadG2fil()
395        self.fmtlist = G2sc.exportersByExtension.get('powder',[])
396        self.timer = wx.Timer()
397        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
398        self.imageBase = G2frame.Image
399        self.params['ComputePDF'] = False
400        self.params['pdfDmax'] = 0.0
401        self.params['pdfprm'] = ''
402        self.params['optPDF'] = True
403        self.params['TableMode'] = False
404        self.params['outsel'] = {}
405        self.formula = 'Error'
406        self.pdfControls = {}
407        self.imgList = []
408        self.histList = []
409        self.pdfList = []
410        self.Scale = [1.0,]
411        self.ProcessedList = [] # files that have been integrated
412        self.currImageList = [] # files that will be integrated
413        self.gpxInp = None
414        self.gpxin = [None,None,None,'']
415        self.imprm = [None,None,None,'']
416        self.maskfl = [None,None,None,'']
417
418        self.params['readdir'] = os.getcwd()
419        self.params['filter'] = '*.tif'
420        self.params['outdir'] = os.getcwd()
421        self.PDFformats = ('I(Q)', 'S(Q)', 'F(Q)', 'G(r)', 'PDFgui')
422        #GSASIIpath.IPyBreak_base()
423       
424        wx.Frame.__init__(self, None, title='Automatic Integration',
425                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
426        self.Status = self.CreateStatusBar()
427        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
428        mnpnl = wx.Panel(self)
429        mnsizer = wx.BoxSizer(wx.VERTICAL)
430        # box for integration controls & masks input
431        intSizer = wx.BoxSizer(wx.VERTICAL)
432        sizer = wx.BoxSizer(wx.HORIZONTAL)
433        sizer.Add((-1,-1),1,wx.EXPAND,1)
434        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Calibration/Integration parameters'))
435        sizer.Add((-1,-1),1,wx.EXPAND,1)
436        intSizer.Add(sizer,1,wx.EXPAND,1)
437        def ontblModeBtn(event):
438            if tblModeBtn.GetValue():
439                self.params['TableMode'] = True
440            else:
441                self.params['TableMode'] = False
442            ShowbyMode()
443        tblModeBtn = wx.CheckBox(mnpnl,label='Use distance lookup table')
444        tblModeBtn.SetValue(False)
445        tblModeBtn.Bind(wx.EVT_CHECKBOX, ontblModeBtn)
446        intSizer.Add(tblModeBtn)
447        mnsizer.Add(intSizer,0,wx.EXPAND,0)
448        intPrmSizer = wx.BoxSizer(wx.VERTICAL)
449       
450        mnsizer.Add(intPrmSizer,0,wx.EXPAND,0)
451        # file filter stuff
452        mnsizer.Add((-1,15))
453        sizer = wx.BoxSizer(wx.HORIZONTAL)
454        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Read images from '))
455        self.readDir = G2G.ValidatedTxtCtrl(mnpnl,self.params,'readdir',
456                            OnLeave=self.ShowMatchingFiles,size=(200,-1))
457        sizer.Add(self.readDir,1,wx.EXPAND,1)
458        btn3 = wx.Button(mnpnl, wx.ID_ANY, "Browse")
459        btn3.Bind(wx.EVT_BUTTON, self.SetSourceDir)
460        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
461        mnsizer.Add(sizer,0,wx.EXPAND,0)
462        sizer = wx.BoxSizer(wx.HORIZONTAL)
463        sizer.Add((-1,-1),1,wx.EXPAND,1)
464        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'  Image filter'))
465        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',
466                                        OnLeave=self.ShowMatchingFiles)
467        sizer.Add(flterInp)
468        mnsizer.Add(sizer,0,wx.EXPAND,0)
469       
470        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
471        mnsizer.Add(self.ListBox,1,wx.EXPAND,1)
472        self.ShowMatchingFiles(None)
473
474        # box for output selections
475        mnsizer.Add((-1,15))
476        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration output")
477        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
478        sizer = wx.BoxSizer(wx.HORIZONTAL)
479        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
480        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
481        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
482        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
483        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
484        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
485        lblsizr.Add(sizer,0,wx.EXPAND)
486        sizer = wx.BoxSizer(wx.HORIZONTAL)
487        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s):'))
488        for dfmt in self.fmtlist:
489            sizer.Add((6,2)) # add a bit of extra space
490            fmt = dfmt[1:]
491            if fmt not in self.params['outsel']: self.params['outsel'][fmt] = False
492            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params['outsel'],fmt,
493                                     OnChange=TestInput)
494            sizer.Add(btn)
495        lblsizr.Add(sizer)
496        sizer = wx.BoxSizer(wx.HORIZONTAL)
497        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
498        self.params['SeparateDir'] = False
499        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
500        lblsizr.Add(sizer)
501        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
502       
503        mnsizer.Add((-1,15))
504        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "PDF settings")
505        pdfwidgets = []
506        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
507        sizer = wx.BoxSizer(wx.HORIZONTAL)
508        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
509        lbl4b = G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',
510                                     OnChange=showPDFctrls)
511        sizer.Add(lbl4b)
512        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
513        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
514        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
515        pdfwidgets.append(fInp4a)
516        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
517        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
518        pdfwidgets.append(cOpt)
519        sizer.Add(cOpt)
520        lblsizr.Add(sizer,0)
521       
522        sizer = wx.BoxSizer(wx.HORIZONTAL)
523        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
524        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
525        self.pdfSel = G2G.G2ChoiceButton(mnpnl,[''],self.params,'pdfprm',
526                                       onChoice=checkPDFselection)
527        sizer.Add(self.pdfSel,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,1)
528        lblsizr.Add(sizer,0,wx.EXPAND)
529        sizer = wx.BoxSizer(wx.HORIZONTAL)
530        lbl5a = wx.StaticText(mnpnl, wx.ID_ANY,'Chemical formula: ')
531        sizer.Add(lbl5a)
532        lbl5b = wx.StaticText(mnpnl, wx.ID_ANY,'(formula)')
533        sizer.Add(lbl5b)
534        lblsizr.Add(sizer,0,wx.EXPAND)
535
536        sizer = wx.BoxSizer(wx.HORIZONTAL)
537        lbl5 = wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s):')
538        sizer.Add(lbl5)
539        for fmt in self.PDFformats:
540            sizer.Add((6,2)) # add a bit of extra space
541            if fmt not in self.params['outsel']: self.params['outsel'][fmt] = False
542            btn = G2G.G2CheckBox(mnpnl,fmt,self.params['outsel'],fmt,
543                                     OnChange=TestInput)
544            sizer.Add(btn)
545            pdfwidgets.append(btn)
546        lblsizr.Add(sizer,0,wx.EXPAND)
547
548        self.pbkg = 3*[None]
549        for i,lbl in enumerate((' Sample bkg:',' Container:',
550                                   'Container bkg:')):
551            self.pbkg[i] = [None,None,None,'',None,None,-1.0]
552            sizer = wx.BoxSizer(wx.HORIZONTAL)
553            self.pbkg[i][0] = wx.StaticText(mnpnl, wx.ID_ANY,lbl)
554            sizer.Add(self.pbkg[i][0])
555            self.pbkg[i][1] = G2G.G2ChoiceButton(mnpnl,[''],self.pbkg[i],3)
556            sizer.Add(self.pbkg[i][1],1,wx.EXPAND,1)
557            self.pbkg[i][5] = wx.StaticText(mnpnl, wx.ID_ANY,' mult:')
558            sizer.Add(self.pbkg[i][5])
559            self.pbkg[i][4] = G2G.ValidatedTxtCtrl(mnpnl,self.pbkg[i],6,
560                            (6,3),typeHint=float,size=(50,-1),
561                            OnLeave=TestInput,notBlank=False)
562            sizer.Add(self.pbkg[i][4])
563            lblsizr.Add(sizer,0,wx.EXPAND,0)
564       
565        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
566
567        # buttons on bottom
568        sizer = wx.BoxSizer(wx.HORIZONTAL)
569        sizer.Add((20,-1))
570        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
571        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
572        sizer.Add(self.btnstart)
573        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
574        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
575        sizer.Add(self.btnreset)
576        sizer.Add((20,-1),wx.EXPAND,1)
577        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Exit")
578        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
579        self.EnableIntButtons(False)
580        sizer.Add(self.btnclose)
581        sizer.Add((20,-1))
582        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
583        # finish up window
584        mnpnl.SetSizer(mnsizer)
585        #OnRadioSelect(None) # disable widgets
586        mnsizer.Fit(self)
587        ShowbyMode()
588        if len(sys.argv) > 1:
589            if os.path.exists(sys.argv[1]):
590                self.gpxin[3] = sys.argv[1]
591                SetGPXInputFile()
592        showPDFctrls(None)
593   
594    def SetSourceDir(self,event):
595        '''Use a dialog to get a directory for image files
596        '''
597        dlg = wx.DirDialog(self, 'Select directory for image files',
598                        self.params['readdir'],wx.DD_DEFAULT_STYLE)
599        dlg.CenterOnParent()
600        try:
601            if dlg.ShowModal() == wx.ID_OK:
602                self.params['readdir'] = dlg.GetPath()
603            self.readDir.SetValue(self.params['readdir'])
604            self.ShowMatchingFiles(None)
605        finally:
606            dlg.Destroy()
607        return
608       
609    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
610        '''Find and image files matching the image
611        file directory (self.params['readdir']) and the image file filter
612        (self.params['filter']) and add this information to the GUI list box
613        '''
614        if invalid: return
615        if self.PreventReEntryShowMatch: return
616        self.PreventReEntryShowMatch = True
617        filmsg = ""
618        self.currImageList = []
619        if os.path.exists(self.params['readdir']): 
620            imageList = sorted(
621                glob.glob(os.path.join(self.params['readdir'],self.params['filter'])))
622            if not imageList:
623                msg = 'Warning: No files match search string '+os.path.join(
624                        self.params['readdir'],self.params['filter'])
625            else:
626                for fil in imageList:
627                    if fil not in self.ProcessedList:
628                        filmsg += '\n  '+fil
629                        self.currImageList.append(fil)
630                if filmsg:
631                    msg = 'Files to integrate from '+os.path.join(
632                            self.params['readdir'],self.params['filter'])+filmsg
633                else:
634                    msg = 'No files found to process in '+self.params['readdir']
635        else:
636            msg = 'Warning, does not exist: '+self.params['readdir']
637        if self.ProcessedList:
638            msg += '\nIntegrated files:'
639            for fil in self.ProcessedList:
640                msg += '\n  '+fil
641        self.ListBox.Clear()
642        self.ListBox.AppendItems(msg.split('\n'))
643        self.PreventReEntryShowMatch = False
644        return
645       
646    def OnPause(self):
647        '''Respond to Pause, changes text on button/Status line, if needed
648        Stops timer
649        self.Pause should already be True
650        '''
651        if self.timer.IsRunning(): self.timer.Stop()
652        if self.btnstart.GetLabel() == 'Restart':
653            return
654        if self.btnstart.GetLabel() != 'Resume':
655            print('\nPausing autointegration\n')
656            self.btnstart.SetLabel('Resume')
657            self.Status.SetStatusText(
658                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
659        self.Pause = True
660           
661    def EnableIntButtons(self,flag):
662        for item in (self.btnstart,self.btnreset): item.Enable(flag)
663           
664    def StartLoop(self):
665        '''Prepare to start autointegration timer loop.
666        Save current Image params for use in future integrations
667        also label the window so users understand what is being used
668        '''
669        print('\nStarting new autointegration\n')
670        # make sure all output directories exist
671        if self.params['SeparateDir']:
672            for dfmt in self.fmtlist:
673                if not self.params['outsel'][dfmt[1:]]: continue
674                dir = os.path.join(self.params['outdir'],dfmt[1:])
675                if not os.path.exists(dir): os.makedirs(dir)
676        else:
677            if not os.path.exists(self.params['outdir']):
678                os.makedirs(self.params['outdir'])
679        if self.Reset: # special things to do after Reset has been pressed
680            self.G2frame.IntegratedList = []
681            wx.Yield()
682            self.Reset = False
683        if self.params['ComputePDF'] and self.params['SeparateDir']:
684            for fmt in self.PDFformats:
685                if not self.params['outsel'][fmt]: continue
686                dir = os.path.join(self.params['outdir'],
687                                   fmt.replace("(","_").replace(")",""))
688                if not os.path.exists(dir): os.makedirs(dir)
689        return False
690               
691    def OnTimerLoop(self,event):
692        '''A method that is called every :meth:`PollTime` seconds that is
693        used to check for new files and process them. Integrates new images.
694        Also optionally sets up and computes PDF.
695        This is called only after the "Start" button is pressed (then its label reads "Pause").
696        '''
697           
698        if GSASIIpath.GetConfigValue('debug'):
699            import datetime
700            print ("DBG_Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
701        if self.PreventTimerReEntry: return
702        self.PreventTimerReEntry = True
703        self.ShowMatchingFiles(None)
704        if not self.currImageList:
705            self.PreventTimerReEntry = False
706            return
707        updateList = False
708
709        # get input for integration
710        imgprms = mskprms = None
711        if not self.params['TableMode']:
712            # read in image controls/masks, used below in loop. In Table mode
713            # we will get this image-by image.
714            gpxinp = G2sc.G2Project(self.gpxin[3])
715            print('reading template project',gpxinp.filename)
716            img = gpxinp.image(self.imprm[1].GetStringSelection())
717            imgprms = img.getControls(True)
718            if self.maskfl[1].GetStringSelection().strip():
719                img = gpxinp.image(self.maskfl[1].GetStringSelection())
720                mskprms = img.getMasks()
721        # setup shared input for PDF computation (for now will not be table mode)
722        xydata = {}
723        if self.params['ComputePDF']:
724            pdfEntry = self.pdfSel.GetStringSelection()
725            try: 
726                PDFobj = gpxinp.pdf(pdfEntry)
727            except KeyError:
728                print("PDF entry not found: {}".format(pdfEntry))
729            # update with GUI input
730            for i,lbl in enumerate(('Sample Bkg.','Container',
731                                    'Container Bkg.')):
732                name = self.pbkg[i][1].GetStringSelection()
733                try:
734                    xydata[lbl] = gpxinp.histogram(name).data['data']
735                except AttributeError:
736                    pass
737                PDFobj.data['PDF Controls'][lbl]['Mult'] = self.pbkg[i][6]
738                PDFobj.data['PDF Controls'][lbl]['Name'] = name
739        else:
740            PDFobj = None
741        # loop over image files
742        for newImage in self.currImageList:
743            self.Pause |= self.G2frame.PauseIntegration
744            if self.Pause:
745                self.OnPause()
746                self.PreventTimerReEntry = False
747                self.Raise()
748                return
749            print('processing ',newImage)
750            TableMode = self.params['TableMode']
751            ComputePDF = self.params['ComputePDF']
752            SeparateDir = self.params['SeparateDir']
753            optPDF = self.params['optPDF']
754            outdir = self.params['outdir']
755            calcModes = (TableMode,ComputePDF,SeparateDir,optPDF)
756            InterpVals = self.params.get('InterVals')
757            outputSelect = self.params['outsel']
758            PDFformats = self.PDFformats
759            fmtlist = self.fmtlist
760            outputModes = (outputSelect,PDFformats,fmtlist,outdir)
761            if PDFobj:
762                PDFdict = PDFobj.data
763            else:
764                PDFdict = None
765            ProcessImage(newImage,imgprms,mskprms,xydata,PDFdict,InterpVals,calcModes,outputModes)
766            updateList = True
767            self.ProcessedList.append(newImage)
768        if updateList: self.ShowMatchingFiles(None)
769        self.PreventTimerReEntry = False
770        self.Raise()
771       
772def ProcessImage(newImage,imgprms,mskprms,xydata,PDFdict,InterpVals,calcModes,outputModes):
773    '''Process one image that is read from file newImage and is integrated into
774    one or more diffraction patterns and optionally each diffraction pattern can
775    be transformed into a pair distribution function.
776
777    :param str newImage: file name (full path) for input image
778    :param dict imgprms: dict with some nested lists & dicts describing the image
779      settings and integration parameters
780    :param dict mskprms: dict with areas of image to be masked
781    :param dict xydata: contains histogram information with about background
782      contributions, used for PDF computation (used if ComputePDF is True)
783    :param PDFdict: contains PDF parameters (used if ComputePDF is True)
784    :param InterpVals: contains interpolation table (used if TableMode is True)
785    :param tuple calcModes: set of values for which computations are
786      performed and how
787    :param tuple outputModes: determines which files are written and where
788    '''
789    (TableMode,ComputePDF,SeparateDir,optPDF) = calcModes
790    (outputSelect,PDFformats,fmtlist,outdir) = outputModes           
791    gpxout = G2sc.G2Project(filename=os.path.splitext(newImage)[0]+'.gpx')
792    print('creating',gpxout.filename)
793    # looped because a file can contain multiple images
794    for im in gpxout.add_image(newImage):
795        if TableMode: # look up parameter values from table
796            imgprms,mskprms = LookupFromTable(im.data['Image Controls'].get('setdist'),
797                                                  InterpVals)
798        # apply image & mask parameters & integrate
799        im.setControls(imgprms)
800        if mskprms:
801            im.setMasks(mskprms)
802        else:
803            im.initMasks()                   
804        hists = im.Integrate()
805        # write requested files
806        for dfmt in fmtlist:
807            fmt = dfmt[1:]
808            if not outputSelect[fmt]: continue
809            if SeparateDir:
810                savedir = os.path.join(outdir,fmt)
811            else:
812                savedir = outdir
813            if not os.path.exists(savedir): os.makedirs(savedir)
814            # loop over created histgrams (multiple if caked), writing them as requested
815            for i,h in enumerate(hists):
816                fname = h.name[5:].replace(' ','_')
817                try:
818                    fil = os.path.join(savedir,fname)
819                    print('Wrote',h.Export(fil,dfmt))
820                except Exception as msg:
821                    print('Failed to write {} as {}. Error msg\n{}'
822                              .format(fname,dfmt,msg))
823        if ComputePDF:  # compute PDF
824            for h in hists:
825                pdf = gpxout.copy_PDF(PDFdict,h)
826                pdf.data['PDF Controls']['Sample']['Name'] = h.name
827                xydata['Sample'] = h.data['data']
828                fname = h.name[5:].replace(' ','_')
829                limits = h.data['Limits'][1]
830                inst = h.data['Instrument Parameters'][0]
831                pdf.calculate(copy.deepcopy(xydata),limits,inst)
832                if optPDF:
833                    for i in range(5):
834                        if pdf.optimize(True,5,copy.deepcopy(xydata),limits,inst):
835                            break
836                    pdf.calculate(copy.deepcopy(xydata),limits,inst)
837                for fmt in PDFformats:
838                    if not outputSelect[fmt]: continue
839                    if SeparateDir:
840                        savedir = os.path.join(outdir,fmt.replace("(","_").replace(")",""))
841                    else:
842                        savedir = outdir
843                    pdf.export(os.path.join(savedir,fname),fmt)
844    gpxout.save()
845# Autointegration end
846def SetupInterpolation(dlg):
847    '''Creates an object for interpolating image parameters at a given distance value
848    '''
849    parms = dlg.ReadImageParmTable()
850    IMfileList = dlg.IMfileList
851    cols = dlg.list.GetColumnCount()
852    ParmList = dlg.ParmList
853    nonInterpVars = dlg.nonInterpVars
854    ControlsTable = {}
855    MaskTable = {}
856    for f,m in zip(IMfileList,parms[-1]):
857        n = os.path.split(f)[1]
858        if n in ControlsTable:
859            print('Warning overwriting entry {}'.format(n))
860        ControlsTable[n] = G2imG.ReadControls(f)
861        if m and os.path.exists(m):
862            MaskTable[n] = G2imG.ReadMask(m)
863        elif m != "(none)":
864            print("Error: Mask file {} not found".format(m))
865    return copy.deepcopy([cols, parms, IMfileList, ParmList, nonInterpVars,ControlsTable,MaskTable])
866
867def LookupFromTable(dist,parmList):
868    '''Interpolate image parameters for a supplied distance value
869
870    :param float dist: distance to use for interpolation
871    :returns: a list with 2 items:
872      * a dict with interpolated parameter values,
873      * the closest imctrl
874    '''
875    cols, parms, IMfileList, ParmList, nonInterpVars,ControlsTable,MaskTable = parmList
876    x = np.array([float(i) for i in parms[0]])
877    closest = abs(x-dist).argmin()
878    D = {'setdist':dist}
879    imctfile = IMfileList[closest]
880    for c in range(1,cols-1):
881        lbl = ParmList[c]
882        if lbl in nonInterpVars:
883            if lbl in ['outChannels',]:
884                D[lbl] = int(float(parms[c][closest]))
885            else:
886                D[lbl] = float(parms[c][closest])
887        else:
888            y = np.array([float(i) for i in parms[c]])
889            D[lbl] = np.interp(dist,x,y)
890    # full integration when angular range is 0
891    D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
892    # conversion for paired values
893    for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
894        r = a.split('_')[0]
895        D[r] = [D[a],D[b]]
896        if r in ['LRazimuth',]:
897            D[r] = [int(D[a]),int(D[b])]
898        del D[a]
899        del D[b]
900    interpDict,imgctrl = D,imctfile
901    if GSASIIpath.GetConfigValue('debug'):
902        print ('DBG_interpolated values: ',interpDict)
903    f = os.path.split(imgctrl)[1]
904    ImageControls = ControlsTable[f]
905    ImageControls.update(interpDict)
906    ImageControls['showLines'] = True
907    ImageControls['ring'] = []
908    ImageControls['rings'] = []
909    ImageControls['ellipses'] = []
910    ImageControls['setDefault'] = False
911    for i in 'range','size','GonioAngles':
912        if i in ImageControls: del ImageControls[i]
913    ImageMasks = MaskTable.get(f)
914    return ImageControls,ImageMasks
915   
916###########################################################################
917if __name__ == "__main__":
918    GSASIIpath.InvokeDebugOpts()
919    App = wx.App()
920    class dummyClass(object):
921        '''An empty class where a few values needed from parent are placed
922        '''
923        def __init__(self): 
924            self.Image = None
925            self.PauseIntegration = False
926            self.TutorialImportDir = None
927            self.GSASprojectfile = ''
928            self.LastExportDir = ''
929            self.LastGPXdir = ''
930           
931    G2frame = dummyClass()
932    frm = AutoIntFrame(G2frame,5)
933    App.GetTopWindow().Show(True)
934    App.MainLoop()
Note: See TracBrowser for help on using the repository browser.