source: trunk/GSASIIIntPDFtool.py @ 4363

Last change on this file since 4363 was 4363, checked in by toby, 21 months ago

remove debug line

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