source: trunk/GSASIIautoInt.py @ 4345

Last change on this file since 4345 was 4345, checked in by toby, 23 months ago

Add more image settings to preferences; prevent save of config.py after "no save"

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