source: trunk/GSASIIIntPDFtool.py @ 4047

Last change on this file since 4047 was 4047, checked in by toby, 4 years ago

remove reflections if hist dropped from phase

  • Property svn:eol-style set to native
File size: 41.2 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)
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            fil = os.path.splitext(sys.argv[1])[0]+'.gpx'
590            if os.path.exists(fil):
591                self.gpxin[3] = fil
592                SetGPXInputFile()
593        showPDFctrls(None)
594   
595    def SetSourceDir(self,event):
596        '''Use a dialog to get a directory for image files
597        '''
598        dlg = wx.DirDialog(self, 'Select directory for image files',
599                        self.params['readdir'],wx.DD_DEFAULT_STYLE)
600        dlg.CenterOnParent()
601        try:
602            if dlg.ShowModal() == wx.ID_OK:
603                self.params['readdir'] = dlg.GetPath()
604            self.readDir.SetValue(self.params['readdir'])
605            self.ShowMatchingFiles(None)
606        finally:
607            dlg.Destroy()
608        return
609       
610    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
611        '''Find and image files matching the image
612        file directory (self.params['readdir']) and the image file filter
613        (self.params['filter']) and add this information to the GUI list box
614        '''
615        if invalid: return
616        if self.PreventReEntryShowMatch: return
617        self.PreventReEntryShowMatch = True
618        filmsg = ""
619        self.currImageList = []
620        if os.path.exists(self.params['readdir']): 
621            imageList = sorted(
622                glob.glob(os.path.join(self.params['readdir'],self.params['filter'])))
623            if not imageList:
624                msg = 'Warning: No files match search string '+os.path.join(
625                        self.params['readdir'],self.params['filter'])
626            else:
627                for fil in imageList:
628                    if fil not in self.ProcessedList:
629                        filmsg += '\n  '+fil
630                        self.currImageList.append(fil)
631                if filmsg:
632                    msg = 'Files to integrate from '+os.path.join(
633                            self.params['readdir'],self.params['filter'])+filmsg
634                else:
635                    msg = 'No files found to process in '+self.params['readdir']
636        else:
637            msg = 'Warning, does not exist: '+self.params['readdir']
638        if self.ProcessedList:
639            msg += '\nIntegrated files:'
640            for fil in self.ProcessedList:
641                msg += '\n  '+fil
642        self.ListBox.Clear()
643        self.ListBox.AppendItems(msg.split('\n'))
644        self.PreventReEntryShowMatch = False
645        return
646       
647    def OnPause(self):
648        '''Respond to Pause, changes text on button/Status line, if needed
649        Stops timer
650        self.Pause should already be True
651        '''
652        if self.timer.IsRunning(): self.timer.Stop()
653        if self.btnstart.GetLabel() == 'Restart':
654            return
655        if self.btnstart.GetLabel() != 'Resume':
656            print('\nPausing autointegration\n')
657            self.btnstart.SetLabel('Resume')
658            self.Status.SetStatusText(
659                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
660        self.Pause = True
661           
662    def EnableIntButtons(self,flag):
663        for item in (self.btnstart,self.btnreset): item.Enable(flag)
664           
665    def StartLoop(self):
666        '''Prepare to start autointegration timer loop.
667        Save current Image params for use in future integrations
668        also label the window so users understand what is being used
669        '''
670        print('\nStarting new autointegration\n')
671        # make sure all output directories exist
672        if self.params['SeparateDir']:
673            for dfmt in self.fmtlist:
674                if not self.params['outsel'][dfmt[1:]]: continue
675                dir = os.path.join(self.params['outdir'],dfmt[1:])
676                if not os.path.exists(dir): os.makedirs(dir)
677        else:
678            if not os.path.exists(self.params['outdir']):
679                os.makedirs(self.params['outdir'])
680        if self.Reset: # special things to do after Reset has been pressed
681            self.G2frame.IntegratedList = []
682            wx.Yield()
683            self.Reset = False
684        if self.params['ComputePDF'] and self.params['SeparateDir']:
685            for fmt in self.PDFformats:
686                if not self.params['outsel'][fmt]: continue
687                dir = os.path.join(self.params['outdir'],
688                                   fmt.replace("(","_").replace(")",""))
689                if not os.path.exists(dir): os.makedirs(dir)
690        return False
691               
692    def ArgGen(self,PDFobj,imgprms,mskprms,xydata):
693        '''generator for arguments for integration/PDF calc
694        '''
695        for newImage in self.currImageList:
696            self.Pause |= self.G2frame.PauseIntegration
697            if self.Pause:
698                self.OnPause()
699                self.PreventTimerReEntry = False
700                self.Raise()
701                return
702            print('generating ',newImage)
703            TableMode = self.params['TableMode']
704            ComputePDF = self.params['ComputePDF']
705            SeparateDir = self.params['SeparateDir']
706            optPDF = self.params['optPDF']
707            outdir = self.params['outdir']
708            calcModes = (TableMode,ComputePDF,SeparateDir,optPDF)
709            InterpVals = self.params.get('InterVals')
710            outputSelect = self.params['outsel']
711            PDFformats = self.PDFformats
712            fmtlist = self.fmtlist
713            outputModes = (outputSelect,PDFformats,fmtlist,outdir)
714            if PDFobj:
715                PDFdict = PDFobj.data
716            else:
717                PDFdict = None
718            yield (newImage,imgprms,mskprms,xydata,PDFdict,InterpVals,calcModes,outputModes)
719    def OnTimerLoop(self,event):
720        '''A method that is called every :meth:`PollTime` seconds that is
721        used to check for new files and process them. Integrates new images.
722        Also optionally sets up and computes PDF.
723        This is called only after the "Start" button is pressed (then its label reads "Pause").
724        '''
725           
726        if GSASIIpath.GetConfigValue('debug'):
727            import datetime
728            print ("DBG_Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
729        if self.PreventTimerReEntry: return
730        self.PreventTimerReEntry = True
731        self.ShowMatchingFiles(None)
732        if not self.currImageList:
733            self.PreventTimerReEntry = False
734            return
735        updateList = False
736
737        # get input for integration
738        imgprms = mskprms = None
739        if not self.params['TableMode']:
740            # read in image controls/masks, used below in loop. In Table mode
741            # we will get this image-by image.
742            gpxinp = G2sc.G2Project(self.gpxin[3])
743            print('reading template project',gpxinp.filename)
744            img = gpxinp.image(self.imprm[1].GetStringSelection())
745            imgprms = img.getControls(True)
746            if self.maskfl[1].GetStringSelection().strip():
747                img = gpxinp.image(self.maskfl[1].GetStringSelection())
748                mskprms = img.getMasks()
749        # setup shared input for PDF computation (for now will not be table mode)
750        xydata = {}
751        if self.params['ComputePDF']:
752            pdfEntry = self.pdfSel.GetStringSelection()
753            try: 
754                PDFobj = gpxinp.pdf(pdfEntry)
755            except KeyError:
756                print("PDF entry not found: {}".format(pdfEntry))
757            # update with GUI input
758            for i,lbl in enumerate(('Sample Bkg.','Container',
759                                    'Container Bkg.')):
760                name = self.pbkg[i][1].GetStringSelection()
761                try:
762                    xydata[lbl] = gpxinp.histogram(name).data['data']
763                except AttributeError:
764                    pass
765                PDFobj.data['PDF Controls'][lbl]['Mult'] = self.pbkg[i][6]
766                PDFobj.data['PDF Controls'][lbl]['Name'] = name
767        else:
768            PDFobj = None
769        for intArgs in self.ArgGen(PDFobj,imgprms,mskprms,xydata):
770            newImage = intArgs[0]
771            print('processing ',newImage)
772            ProcessImage(*intArgs)
773            updateList = True
774            self.ProcessedList.append(newImage)
775        if updateList: self.ShowMatchingFiles(None)
776        self.PreventTimerReEntry = False
777        self.Raise()
778       
779def ProcessImage(newImage,imgprms,mskprms,xydata,PDFdict,InterpVals,calcModes,outputModes):
780    '''Process one image that is read from file newImage and is integrated into
781    one or more diffraction patterns and optionally each diffraction pattern can
782    be transformed into a pair distribution function.
783
784    :param str newImage: file name (full path) for input image
785    :param dict imgprms: dict with some nested lists & dicts describing the image
786      settings and integration parameters
787    :param dict mskprms: dict with areas of image to be masked
788    :param dict xydata: contains histogram information with about background
789      contributions, used for PDF computation (used if ComputePDF is True)
790    :param PDFdict: contains PDF parameters (used if ComputePDF is True)
791    :param InterpVals: contains interpolation table (used if TableMode is True)
792    :param tuple calcModes: set of values for which computations are
793      performed and how
794    :param tuple outputModes: determines which files are written and where
795    '''
796    (TableMode,ComputePDF,SeparateDir,optPDF) = calcModes
797    (outputSelect,PDFformats,fmtlist,outdir) = outputModes           
798    gpxout = G2sc.G2Project(filename=os.path.splitext(newImage)[0]+'.gpx')
799    print('creating',gpxout.filename)
800    # looped because a file can contain multiple images
801    for im in gpxout.add_image(newImage):
802        if TableMode: # look up parameter values from table
803            imgprms,mskprms = LookupFromTable(im.data['Image Controls'].get('setdist'),
804                                                  InterpVals)
805        # apply image & mask parameters & integrate
806        im.setControls(imgprms)
807        if mskprms:
808            im.setMasks(mskprms)
809        else:
810            im.initMasks()                   
811        hists = im.Integrate()
812        # write requested files
813        for dfmt in fmtlist:
814            fmt = dfmt[1:]
815            if not outputSelect[fmt]: continue
816            if SeparateDir:
817                savedir = os.path.join(outdir,fmt)
818            else:
819                savedir = outdir
820            if not os.path.exists(savedir): os.makedirs(savedir)
821            # loop over created histgrams (multiple if caked), writing them as requested
822            for i,h in enumerate(hists):
823                fname = h.name[5:].replace(' ','_')
824                try:
825                    fil = os.path.join(savedir,fname)
826                    print('Wrote',h.Export(fil,dfmt))
827                except Exception as msg:
828                    print('Failed to write {} as {}. Error msg\n{}'
829                              .format(fname,dfmt,msg))
830        if ComputePDF:  # compute PDF
831            for h in hists:
832                pdf = gpxout.copy_PDF(PDFdict,h)
833                pdf.data['PDF Controls']['Sample']['Name'] = h.name
834                xydata['Sample'] = h.data['data']
835                fname = h.name[5:].replace(' ','_')
836                limits = h.data['Limits'][1]
837                inst = h.data['Instrument Parameters'][0]
838                pdf.calculate(copy.deepcopy(xydata),limits,inst)
839                if optPDF:
840                    for i in range(5):
841                        if pdf.optimize(True,5,copy.deepcopy(xydata),limits,inst):
842                            break
843                    pdf.calculate(copy.deepcopy(xydata),limits,inst)
844                for fmt in PDFformats:
845                    if not outputSelect[fmt]: continue
846                    if SeparateDir:
847                        savedir = os.path.join(outdir,fmt.replace("(","_").replace(")",""))
848                    else:
849                        savedir = outdir
850                    pdf.export(os.path.join(savedir,fname),fmt)
851    gpxout.save()
852# Autointegration end
853def SetupInterpolation(dlg):
854    '''Creates an object for interpolating image parameters at a given distance value
855    '''
856    parms = dlg.ReadImageParmTable()
857    IMfileList = dlg.IMfileList
858    cols = dlg.list.GetColumnCount()
859    ParmList = dlg.ParmList
860    nonInterpVars = dlg.nonInterpVars
861    ControlsTable = {}
862    MaskTable = {}
863    for f,m in zip(IMfileList,parms[-1]):
864        n = os.path.split(f)[1]
865        if n in ControlsTable:
866            print('Warning overwriting entry {}'.format(n))
867        ControlsTable[n] = G2imG.ReadControls(f)
868        if m and os.path.exists(m):
869            MaskTable[n] = G2imG.ReadMask(m)
870        elif m != "(none)":
871            print("Error: Mask file {} not found".format(m))
872    return copy.deepcopy([cols, parms, IMfileList, ParmList, nonInterpVars,ControlsTable,MaskTable])
873
874def LookupFromTable(dist,parmList):
875    '''Interpolate image parameters for a supplied distance value
876
877    :param float dist: distance to use for interpolation
878    :returns: a list with 2 items:
879      * a dict with interpolated parameter values,
880      * the closest imctrl
881    '''
882    cols, parms, IMfileList, ParmList, nonInterpVars,ControlsTable,MaskTable = parmList
883    x = np.array([float(i) for i in parms[0]])
884    closest = abs(x-dist).argmin()
885    D = {'setdist':dist}
886    imctfile = IMfileList[closest]
887    for c in range(1,cols-1):
888        lbl = ParmList[c]
889        if lbl in nonInterpVars:
890            if lbl in ['outChannels',]:
891                D[lbl] = int(float(parms[c][closest]))
892            else:
893                D[lbl] = float(parms[c][closest])
894        else:
895            y = np.array([float(i) for i in parms[c]])
896            D[lbl] = np.interp(dist,x,y)
897    # full integration when angular range is 0
898    D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
899    # conversion for paired values
900    for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
901        r = a.split('_')[0]
902        D[r] = [D[a],D[b]]
903        if r in ['LRazimuth',]:
904            D[r] = [int(D[a]),int(D[b])]
905        del D[a]
906        del D[b]
907    interpDict,imgctrl = D,imctfile
908    if GSASIIpath.GetConfigValue('debug'):
909        print ('DBG_interpolated values: ',interpDict)
910    f = os.path.split(imgctrl)[1]
911    ImageControls = ControlsTable[f]
912    ImageControls.update(interpDict)
913    ImageControls['showLines'] = True
914    ImageControls['ring'] = []
915    ImageControls['rings'] = []
916    ImageControls['ellipses'] = []
917    ImageControls['setDefault'] = False
918    for i in 'range','size','GonioAngles':
919        if i in ImageControls: del ImageControls[i]
920    ImageMasks = MaskTable.get(f)
921    return ImageControls,ImageMasks
922   
923###########################################################################
924if __name__ == "__main__":
925    GSASIIpath.InvokeDebugOpts()
926    App = wx.App()
927    class dummyClass(object):
928        '''An empty class where a few values needed from parent are placed
929        '''
930        def __init__(self): 
931            self.Image = None
932            self.PauseIntegration = False
933            self.TutorialImportDir = None
934            self.GSASprojectfile = ''
935            self.LastExportDir = ''
936            self.LastGPXdir = ''
937           
938    G2frame = dummyClass()
939    frm = AutoIntFrame(G2frame,5)
940    App.GetTopWindow().Show(True)
941    App.MainLoop()
Note: See TracBrowser for help on using the repository browser.