source: trunk/GSASIIIntPDFtool.py @ 4197

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

complete multiprocessing & caching of maps; results not yet tested

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