Changeset 2082


Ignore:
Timestamp:
Dec 4, 2015 10:50:58 PM (6 years ago)
Author:
toby
Message:

provide autoint interpolation table; merge into std GSAS-II version

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/GSASIIgrid.py

    r2036 r2082  
    11101110        self.ImageEdit.Append(help='Load image controls from file',
    11111111            id=wxID_IMLOADCONTROLS, kind=wx.ITEM_NORMAL,text='Load Controls')
    1112         if GSASIIpath.GetConfigValue('debug'):
    1113             import autoint
    1114             self.ImageEdit.Append(help='Open Auto-integration window to integrate a series of images',
    1115                 id=wxID_IMAUTOINTEG, kind=wx.ITEM_NORMAL,text='Auto Integrate')
     1112        self.ImageEdit.Append(help='Open Auto-integration window to integrate a series of images',
     1113            id=wxID_IMAUTOINTEG, kind=wx.ITEM_NORMAL,text='Auto Integrate')
    11161114        self.PostfillDataMenu()
    11171115           
  • trunk/GSASIIimgGUI.py

    r2065 r2082  
    1515
    1616'''
    17 import os.path
    18 import wx
    19 import wx.lib.scrolledpanel as wxscroll
    20 import matplotlib as mpl
     17import os
     18import copy
     19import glob
     20import re
     21import bisect
    2122import math
    2223import time
    2324import copy
     25import sys
     26import wx
     27import wx.lib.scrolledpanel as wxscroll
     28import wx.lib.mixins.listctrl  as  listmix
     29import matplotlib as mpl
     30import numpy as np
    2431import GSASIIpath
    2532GSASIIpath.SetVersionNumber("$Revision$")
     
    3037import GSASIIgrid as G2gd
    3138import GSASIIctrls as G2G
    32 import numpy as np
     39import GSASIIpy3 as G2py3
    3340
    3441VERY_LIGHT_GREY = wx.Colour(235,235,235)
     
    220227                                        G2gd.GetPatternTreeItemId(G2frame,id, 'Masks'))
    221228                                except TypeError:       #missing Masks
     229                                    # I think the next line should be 'range'! (BHT)
    222230                                    Imin,Imax = Data['Range']
    223231                                    Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
     
    10581066    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveControls, id=G2gd.wxID_IMSAVECONTROLS)
    10591067    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadControls, id=G2gd.wxID_IMLOADCONTROLS)
    1060     if GSASIIpath.GetConfigValue('debug'):
    1061         import autoint
    1062         def OnDestroy(event):
    1063             G2frame.autoIntFrame = None
    1064         def OnAutoInt(event):
    1065             reload(autoint)
    1066             if G2frame.autoIntFrame: # ensure only one open at a time
    1067                 G2frame.autoIntFrame.Raise()
    1068                 return
    1069             G2frame.autoIntFrame = autoint.AutoIntFrame(G2frame,PollTime=5.0)
    1070             G2frame.autoIntFrame.Bind(wx.EVT_WINDOW_DESTROY,OnDestroy) # clean up name on window close
    1071         G2frame.dataFrame.Bind(wx.EVT_MENU, OnAutoInt, id=G2gd.wxID_IMAUTOINTEG)
     1068    def OnDestroy(event):
     1069        G2frame.autoIntFrame = None
     1070    def OnAutoInt(event):
     1071        if G2frame.autoIntFrame: # ensure only one open at a time
     1072            G2frame.autoIntFrame.Raise()
     1073            return
     1074        G2frame.autoIntFrame = AutoIntFrame(G2frame,PollTime=10.0)
     1075        G2frame.autoIntFrame.Bind(wx.EVT_WINDOW_DESTROY,OnDestroy) # clean up name on window close
     1076    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAutoInt, id=G2gd.wxID_IMAUTOINTEG)
    10721077    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
    10731078
     
    11831188            if dlg.ShowModal() == wx.ID_OK:
    11841189                filename = dlg.GetPath()
     1190                filename = os.path.splitext(filename)[0]+'.immask'
    11851191                File = open(filename,'w')
    11861192                save = {}
     
    18721878    G2frame.dataDisplay.SetSize(Size)
    18731879    G2frame.dataFrame.setSizePosLeft(Size)   
     1880
     1881###########################################################################
     1882# Autointegration follows
     1883def ReadMask(filename):
     1884    'Read a mask (.immask) file'
     1885    File = open(filename,'r')
     1886    save = {}
     1887    S = File.readline()
     1888    while S:
     1889        if S[0] == '#':
     1890            S = File.readline()
     1891            continue
     1892        [key,val] = S[:-1].split(':')
     1893        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
     1894            save[key] = eval(val)
     1895        S = File.readline()
     1896    File.close()
     1897    CleanupMasks(save)
     1898    return save
     1899
     1900def ReadControls(filename):
     1901    'read an image controls (.imctrl) file'
     1902    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
     1903            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
     1904            'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg',
     1905            'PolaVal','SampleAbs','dark image','background image']
     1906    File = open(filename,'r')
     1907    save = {}
     1908    S = File.readline()
     1909    while S:
     1910        if S[0] == '#':
     1911            S = File.readline()
     1912            continue
     1913        [key,val] = S[:-1].split(':')
     1914        if key in ['type','calibrant','binType','SampleShape',]:    #strings
     1915            save[key] = val
     1916        elif key in ['rotation']:
     1917            save[key] = float(val)
     1918        elif key in ['center',]:
     1919            if ',' in val:
     1920                save[key] = eval(val)
     1921            else:
     1922                vals = val.strip('[] ').split()
     1923                save[key] = [float(vals[0]),float(vals[1])]
     1924        elif key in cntlList:
     1925            save[key] = eval(val)
     1926        S = File.readline()
     1927    File.close()
     1928    return save
     1929
     1930def Read_imctrl(imctrl_file):
     1931    '''Read an image control file and record control parms into a dict, with some simple
     1932    type conversions
     1933    '''
     1934    file_opt = options = {}
     1935    save = {'filename':imctrl_file}
     1936    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
     1937    if os.path.exists(immask_file):
     1938        save['maskfile'] = immask_file
     1939    else:
     1940        save['maskfile'] = '(none)'
     1941    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
     1942                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
     1943                        'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg',
     1944                        'PolaVal','SampleAbs','dark image','background image']
     1945    File = open(imctrl_file,'r')
     1946    fullIntegrate = False
     1947    try:
     1948        S = File.readline()
     1949        while S:
     1950            if S[0] == '#':
     1951                S = File.readline()
     1952                continue
     1953            [key,val] = S[:-1].split(':')
     1954            if key in ['type','calibrant','binType','SampleShape',]:    #strings
     1955                save[key] = val
     1956            elif key == 'rotation':
     1957                save[key] = float(val)
     1958            elif key == 'fullIntegrate':
     1959                fullIntegrate = eval(val)
     1960            elif key == 'LRazimuth':
     1961                save['LRazimuth_min'],save['LRazimuth_max'] = eval(val)[0:2]
     1962            elif key == 'IOtth':
     1963                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
     1964            elif key == 'center':
     1965                if ',' in val:
     1966                    vals = eval(val)
     1967                else:
     1968                    vals = val.strip('[] ').split()
     1969                    vals = [float(vals[0]),float(vals[1])]
     1970                save['center_x'],save['center_y'] = vals[0:2]
     1971            elif key in cntlList:
     1972                save[key] = eval(val)
     1973            S = File.readline()
     1974    finally:
     1975        File.close()
     1976        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0,0
     1977    return save
     1978   
     1979class AutoIntFrame(wx.Frame):
     1980    '''Creates a wx.Frame window for the Image AutoIntegration.
     1981    The intent is that this will be used as a non-modal dialog window.
     1982   
     1983    Implements a Start button that morphs into a pause and resume button.
     1984    This button starts a processing loop that is repeated every
     1985    :meth:`PollTime` seconds.
     1986
     1987    :param wx.Frame G2frame: main GSAS-II frame
     1988    :param float PollTime: frequency in seconds to repeat calling the
     1989      processing loop. (Default is 3.0 seconds.)
     1990    '''
     1991    def OnTimerLoop(self,event):
     1992        '''A method that is called every :meth:`PollTime` seconds that is
     1993        used to check for new files and process them. This is called only
     1994        after the "Start" button is pressed (when its label reads "Pause").
     1995        '''
     1996        G2frame = self.G2frame
     1997        try:
     1998            self.currImageList = sorted(
     1999                glob.glob(os.path.join(self.imagedir,self.params['filter'])))
     2000        except IndexError:
     2001            self.currImageList = []
     2002            return
     2003
     2004        # Create a list of image files that have already been read
     2005        imageFileList = []
     2006        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
     2007            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
     2008            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
     2009            if imagefile not in imageFileList: imageFileList.append(imagefile)
     2010        # loop over image files matching glob, reading in new ones
     2011        for newImage in self.currImageList:
     2012            if newImage in imageFileList: continue # already read
     2013            for imgId in G2IO.ReadImages(G2frame,newImage):
     2014                controlsDict = G2frame.PatternTree.GetItemPyData(
     2015                    G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
     2016                ImageMasks = G2frame.PatternTree.GetItemPyData(
     2017                    G2gd.GetPatternTreeItemId(G2frame,imgId, 'Masks'))
     2018                if self.params['Mode'] == 'table':
     2019                    dist = controlsDict['distance']
     2020                    interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
     2021                    if GSASIIpath.GetConfigValue('debug'):
     2022                        print 'interpolated: ',interpDict
     2023                    self.ImageControls = ReadControls(imgctrl)
     2024                    self.ImageControls.update(interpDict)
     2025                    self.ImageControls['showLines'] = True
     2026                    self.ImageControls['ring'] = []
     2027                    self.ImageControls['rings'] = []
     2028                    self.ImageControls['ellipses'] = []
     2029                    self.ImageControls['setDefault'] = False
     2030                    for i in 'range','size','GonioAngles':
     2031                        if i in self.ImageControls:
     2032                            del self.ImageControls[i]
     2033                    # load copy of Image Masks
     2034                    if immask:
     2035                        self.ImageMasks = ReadMask(immask)
     2036                        del self.Thresholds['Thresholds']
     2037                    else:
     2038                        self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
     2039                # update controls from master
     2040                controlsDict.update(self.ImageControls)
     2041                # update masks from master w/o Thresholds
     2042                ImageMasks.update(self.ImageMasks)
     2043        # now integrate the images that have not already been processed before
     2044        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
     2045            if img in G2frame.IntegratedList: continue
     2046            G2frame.IntegratedList.append(img)
     2047            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
     2048            G2frame.Image = imgId
     2049            G2frame.PickId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls')
     2050            #  integrate in this entry
     2051            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
     2052            G2frame.ImageZ = G2IO.GetImageData(G2frame,imagefile,True,imagetag)
     2053            masks = G2frame.PatternTree.GetItemPyData(
     2054                G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
     2055            data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
     2056            self.oldImagefile = '' # mark image as changed; reread as needed
     2057            # simulate a Image Controls press, since that is where the
     2058            # integration is hidden
     2059            UpdateImageControls(G2frame,data,masks,IntegrateOnly=True)
     2060            # split name and control number
     2061            s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
     2062            namepre = s[0]
     2063            if len(s) > 1:
     2064                namenum = s[1]
     2065            else:
     2066                namenum = ''
     2067            # write out the images in the selected formats and save the names,
     2068            # reset will delete them
     2069            for Id in G2frame.IntgOutList:
     2070                treename = G2frame.PatternTree.GetItemText(Id)
     2071                G2frame.AutointPWDRnames.append(treename)
     2072                Sdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Sample Parameters'))
     2073                # determine the name for the current file
     2074                fileroot = namepre
     2075                if len(G2frame.IntgOutList) > 1:
     2076                    fileroot += "_AZM"
     2077                    if 'Azimuth' in Sdata:
     2078                        fileroot += str(int(10*Sdata['Azimuth']))
     2079                    fileroot += "_"
     2080                fileroot += namenum
     2081                # loop over selected formats
     2082                for dfmt in self.fmtlist:
     2083                    if not self.params[dfmt[1:]]: continue
     2084                    if self.params['SeparateDir']:
     2085                        subdir = dfmt[1:]
     2086                    else:
     2087                        subdir = ''
     2088                    fil = os.path.join(self.params['outdir'],subdir,fileroot)
     2089                    print('writing file '+fil+dfmt)
     2090                    G2IO.ExportPowder(G2frame,treename,fil,dfmt)
     2091       
     2092        if GSASIIpath.GetConfigValue('debug'):
     2093            import datetime
     2094            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
     2095
     2096    def StartLoop(self):
     2097        '''Save current Image params for use in future integrations
     2098        also label the window so users understand whatis being used
     2099        '''
     2100        print '\nStarting new autointegration\n'
     2101        G2frame = self.G2frame
     2102        # show current IMG base
     2103        self.ControlBaseLbl.SetLabel(G2frame.PatternTree.GetItemText(G2frame.Image))
     2104        if self.params['Mode'] != 'table':
     2105            # load copy of Image Controls from current image and clean up
     2106            # items that should not be copied
     2107            self.ImageControls = copy.deepcopy(
     2108                G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
     2109                    G2frame,G2frame.Image, 'Image Controls')))
     2110            self.ImageControls['showLines'] = True
     2111            self.ImageControls['ring'] = []
     2112            self.ImageControls['rings'] = []
     2113            self.ImageControls['ellipses'] = []
     2114            self.ImageControls['setDefault'] = False
     2115            del self.ImageControls['range']
     2116            del self.ImageControls['size']
     2117            del self.ImageControls['GonioAngles']
     2118            # load copy of Image Masks, keep thresholds
     2119            self.ImageMasks = copy.deepcopy(
     2120                G2frame.PatternTree.GetItemPyData(
     2121                    G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks')))
     2122            self.Thresholds = self.ImageMasks['Thresholds'][:]
     2123        # make sure all output directories exist
     2124        if self.params['SeparateDir']:
     2125            for dfmt in self.fmtlist:
     2126                if not self.params[dfmt[1:]]: continue
     2127                dir = os.path.join(self.params['outdir'],dfmt[1:])
     2128                if not os.path.exists(dir): os.makedirs(dir)
     2129        else:
     2130            if not os.path.exists(self.params['outdir']):
     2131                os.makedirs(self.params['outdir'])
     2132        if self.Reset: # special things to do after Reset has been pressed
     2133            # reset controls and masks for all IMG items in tree to master
     2134            for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
     2135                # update controls from master
     2136                controlsDict = G2frame.PatternTree.GetItemPyData(
     2137                    G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
     2138                controlsDict.update(self.ImageControls)
     2139                # update masks from master
     2140                ImageMasks = G2frame.PatternTree.GetItemPyData(
     2141                    G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
     2142                ImageMasks.update(self.ImageMasks)
     2143            # delete all PWDR items created after last Start was pressed
     2144            idlist = []
     2145            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
     2146            while item:
     2147                itemName = G2frame.PatternTree.GetItemText(item)
     2148                if itemName in G2frame.AutointPWDRnames:
     2149                    idlist.append(item)
     2150                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
     2151            for item in idlist:
     2152                G2frame.PatternTree.Delete(item)
     2153            self.Reset = False
     2154        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
     2155
     2156    def __init__(self,G2frame,PollTime=60.0):
     2157        def OnStart(event):
     2158            '''Called when the start button is pressed. Changes button label
     2159            to Pause. When Pause is pressed the label changes to Resume.
     2160            When either Start or Resume is pressed, the processing loop
     2161            is started. When Pause is pressed, the loop is stopped.
     2162            '''
     2163            # check inputs for errors before starting
     2164            #err = ''
     2165            #if not any([self.params[fmt] for fmt in self.fmtlist]):
     2166            #    err += '\nPlease select at least one output format\n'
     2167            #if err:
     2168            #    G2G.G2MessageBox(self,err)
     2169            #    return
     2170            # change button label
     2171            if btnstart.GetLabel() != 'Pause':
     2172                btnstart.SetLabel('Pause')
     2173                if self.timer.IsRunning(): self.timer.Stop()
     2174                self.StartLoop()
     2175                self.OnTimerLoop(None) # run once immediately and again after delay
     2176                self.timer.Start(int(1000*PollTime),oneShot=False)
     2177                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
     2178            else:
     2179                btnstart.SetLabel('Resume')
     2180                if self.timer.IsRunning(): self.timer.Stop()
     2181                print('\nPausing autointegration\n')
     2182                self.Status.SetStatusText('Press Resume to continue integration or Reset to prepare to reintegrate all images')
     2183
     2184        def OnReset(event):
     2185            '''Called when Reset button is pressed. This stops the
     2186            processing loop and resets the list of integrated files so
     2187            all images can be reintegrated.
     2188            '''
     2189            btnstart.SetLabel('Restart')
     2190            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
     2191            if self.timer.IsRunning(): self.timer.Stop()
     2192            self.Reset = True
     2193            self.G2frame.IntegratedList = []
     2194           
     2195        def OnQuit(event):
     2196            '''Stop the processing loop and close the Frame
     2197            '''
     2198            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
     2199            wx.CallAfter(self.Destroy)
     2200           
     2201        def OnBrowse(event):
     2202            '''Responds when the Browse button is pressed to load a file.
     2203            The routine determines which button was pressed and gets the
     2204            appropriate file type and loads it into the appropriate place
     2205            in the dict.
     2206            '''
     2207            if btn3 == event.GetEventObject():
     2208                dlg = wx.DirDialog(
     2209                    self, 'Select directory for output files',
     2210                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
     2211                dlg.CenterOnParent()
     2212                try:
     2213                    if dlg.ShowModal() == wx.ID_OK:
     2214                        self.params['outdir'] = dlg.GetPath()
     2215                        fInp3.SetValue(self.params['outdir'])
     2216                finally:
     2217                    dlg.Destroy()
     2218                return
     2219               
     2220        def OnRadioSelect(event):
     2221            '''Respond to a radiobutton selection and when in table
     2222            mode, get parameters from user.
     2223            '''
     2224            self.Evaluator = None
     2225            if r2.GetValue():
     2226                self.params['Mode'] = 'table'
     2227                try:
     2228                    dlg = IntegParmTable(self.G2frame) # create the dialog
     2229                    if dlg.ShowModal() == wx.ID_OK:
     2230                        self.Evaluator = DefineEvaluator(dlg)
     2231                    else:
     2232                        r1.SetValue(True)
     2233                finally:
     2234                    dlg.Destroy()
     2235            else:
     2236                self.params['Mode'] = 'active'
     2237        ##################################################
     2238        # beginning of __init__ processing
     2239        ##################################################
     2240        self.G2frame = G2frame
     2241        self.Evaluator = None
     2242        self.params = {}
     2243        self.Reset = False
     2244        self.params['IMGfile'] = ''
     2245        self.params['MaskFile'] = ''
     2246        self.params['IgnoreMask'] = True
     2247        self.fmtlist = G2IO.ExportPowderList(G2frame)
     2248        self.timer = wx.Timer()
     2249        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
     2250
     2251        controlsId = G2frame.PatternTree.GetSelection()
     2252        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(G2frame.Image)       
     2253        self.imagedir,fileroot = os.path.split(imagefile)
     2254        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
     2255        self.params['outdir'] = os.path.abspath(self.imagedir)
     2256        wx.Frame.__init__(self, G2frame,title='Automatic Integration')
     2257        self.Status = self.CreateStatusBar()
     2258        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
     2259        mnpnl = wx.Panel(self)
     2260        mnsizer = wx.BoxSizer(wx.VERTICAL)
     2261        sizer = wx.BoxSizer(wx.HORIZONTAL)
     2262        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Integration based on: '))
     2263        self.ControlBaseLbl = wx.StaticText(mnpnl, wx.ID_ANY,'?')
     2264        self.ControlBaseLbl.SetLabel(G2frame.PatternTree.GetItemText(G2frame.Image))
     2265        sizer.Add(self.ControlBaseLbl)
     2266        mnsizer.Add(sizer,0,wx.ALIGN_LEFT,1)
     2267        # file filter stuff
     2268        sizer = wx.BoxSizer(wx.HORIZONTAL)
     2269        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Image filter'))
     2270        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter')
     2271        sizer.Add(flterInp)
     2272        mnsizer.Add(sizer,0,wx.ALIGN_RIGHT,1)
     2273        # box for integration controls & masks input
     2274        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Controls/Masks source")
     2275        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
     2276        r1 = wx.RadioButton(mnpnl, wx.ID_ANY, "Use Active Image",
     2277                            style = wx.RB_GROUP)
     2278        r1.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
     2279        lblsizr.Add(r1)
     2280        r1.SetValue(True)
     2281        r2 = wx.RadioButton(mnpnl, wx.ID_ANY, "Use from table")
     2282        lblsizr.Add(r2)
     2283        r2.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
     2284        mnsizer.Add(lblsizr)
     2285
     2286        # box for output selections
     2287        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
     2288        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
     2289        sizer = wx.BoxSizer(wx.HORIZONTAL)
     2290        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '))
     2291        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',
     2292                                       notBlank=False,size=(300,-1))
     2293        sizer.Add(fInp3)
     2294        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
     2295        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
     2296        sizer.Add(btn3)
     2297        lblsizr.Add(sizer)
     2298        #lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
     2299        sizer = wx.BoxSizer(wx.HORIZONTAL)
     2300        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
     2301        for dfmt in self.fmtlist:
     2302            fmt = dfmt[1:]
     2303            self.params[fmt] = False
     2304            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
     2305            sizer.Add(btn)
     2306        lblsizr.Add(sizer)
     2307        sizer = wx.BoxSizer(wx.HORIZONTAL)
     2308        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
     2309        self.params['SeparateDir'] = False
     2310        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
     2311        lblsizr.Add(sizer)
     2312        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER,1)
     2313
     2314        # buttons on bottom
     2315        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
     2316        sizer = wx.BoxSizer(wx.HORIZONTAL)
     2317        sizer.Add((20,-1))
     2318        btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
     2319        btnstart.Bind(wx.EVT_BUTTON, OnStart)
     2320        sizer.Add(btnstart)
     2321        btnstop = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
     2322        btnstop.Bind(wx.EVT_BUTTON, OnReset)
     2323        sizer.Add(btnstop)
     2324        sizer.Add((20,-1),wx.EXPAND,1)
     2325        btnquit = wx.Button(mnpnl,  wx.ID_ANY, "Close")
     2326        btnquit.Bind(wx.EVT_BUTTON, OnQuit)
     2327        sizer.Add(btnquit)
     2328        sizer.Add((20,-1))
     2329        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
     2330       
     2331        # finish up window
     2332        mnpnl.SetSizer(mnsizer)
     2333        OnRadioSelect(None) # disable widgets
     2334        mnsizer.Fit(self)
     2335        self.CenterOnParent()
     2336        self.Show()
     2337
     2338def DefineEvaluator(dlg):
     2339    '''Creates a function that provides interpolated values for a given distance value
     2340    '''
     2341    def Evaluator(dist):
     2342        '''Interpolate image parameters for a supplied distance value
     2343
     2344        :param float dist: distance to use for interpolation
     2345        :returns: a list with 3 items:
     2346
     2347          * a dict with parameter values,
     2348          * the closest imctrl and
     2349          * the closest maskfile (or None)
     2350        '''           
     2351        x = np.array([float(i) for i in parms[0]])
     2352        closest = abs(x-dist).argmin()
     2353        closeX = x[closest]
     2354        D = {'distance':dist}
     2355        imctfile = IMfileList[closest]
     2356        if parms[-1][closest].lower() != '(none)':
     2357            maskfile = parms[-1][closest]
     2358        else:
     2359            maskfile = None
     2360        for c in range(1,cols-1):
     2361            lbl = ParmList[c]
     2362            if lbl in nonInterpVars:
     2363                D[lbl] = float(parms[c][closest])
     2364            else:
     2365                y = np.array([float(i) for i in parms[c]])
     2366                D[lbl] = np.interp(dist,x,y)
     2367        # full integration when angular range is 0
     2368        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
     2369        # conversion for paired values
     2370        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
     2371            r = a.split('_')[0]
     2372            D[r] = [D[a],D[b]]
     2373            del D[a]
     2374            del D[b]
     2375        return D,imctfile,maskfile
     2376    # save local copies of values needed in Evaluator
     2377    parms = dlg.ReadImageParmTable()
     2378    IMfileList = dlg.IMfileList
     2379    cols = dlg.list.GetColumnCount()
     2380    ParmList = dlg.ParmList
     2381    nonInterpVars = dlg.nonInterpVars
     2382    return Evaluator
     2383
     2384class IntegParmTable(wx.Dialog):
     2385    '''Creates a dialog window with a table of integration parameters.
     2386    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
     2387    In this case, :func:`DefineEvaluator` should be called to obtain a function that
     2388    creates a dictionary with interpolated parameter values.
     2389    '''
     2390    ParmList = ('distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
     2391            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
     2392            'maskfile',
     2393            )
     2394    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
     2395                     'outChannels')  # values in this list are taken from nearest rather than interpolated
     2396    HeaderList = ('Det Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
     2397            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
     2398            'Mask File',
     2399            )
     2400    def __init__(self,G2frame):
     2401        self.G2frame = G2frame
     2402        self.parms = [] # list of values by column
     2403        self.IMfileList = [] # list of .imctrl file names for each entry in table
     2404        wx.Dialog.__init__(self,G2frame,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
     2405        files = []
     2406        try:
     2407            dlg = wx.FileDialog(self, 'Select image control files or previous table',
     2408                                style=wx.OPEN| wx.MULTIPLE,
     2409                                wildcard='image control files (.imctrl)|*.imctrl|Integration table (*.imtbl)|*.imtbl')
     2410            if dlg.ShowModal() == wx.ID_OK:
     2411                files = dlg.GetPaths()
     2412                self.parms,self.IMfileList = self.ReadFiles(files)
     2413        finally:
     2414            dlg.Destroy()
     2415        if not files:
     2416            wx.CallAfter(self.EndModal,wx.ID_CANCEL)
     2417            return
     2418        mainSizer = wx.BoxSizer(wx.VERTICAL)
     2419        self.list = ImgIntLstCtrl(self, wx.ID_ANY,
     2420                      style=wx.LC_REPORT
     2421                          | wx.BORDER_SUNKEN
     2422                         #| wx.BORDER_NONE
     2423                         )
     2424        mainSizer.Add(self.list,1,wx.EXPAND,1)
     2425        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
     2426        btn = wx.Button(self, wx.ID_OK)
     2427        btnsizer.Add(btn)
     2428        btn = wx.Button(self, wx.ID_ANY,'Save')
     2429        btn.Bind(wx.EVT_BUTTON,self._onSave)
     2430        btnsizer.Add(btn)
     2431        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
     2432        btn.Bind(wx.EVT_BUTTON,self._onClose)
     2433        btnsizer.Add(btn)
     2434        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
     2435        self.SetSizer(mainSizer)
     2436        self.list.FillList(self.parms)
     2437        mainSizer.Layout()
     2438        mainSizer.Fit(self)
     2439       
     2440    def ReadFiles(self,files):
     2441        '''Reads a list of .imctrl files or a single .imtbl file
     2442        '''
     2443        tmpDict = {}
     2444        if not files: return
     2445        # option 1, a dump from a previous save
     2446        if os.path.splitext(files[0])[1] == '.imtbl':
     2447            fp = open(files[0],'r')
     2448            S = fp.readline()
     2449            while S:
     2450                if S[0] != '#':
     2451                    [key,val] = S[:-1].split(':')
     2452                    tmpDict[key] = eval(val)
     2453                S = fp.readline()
     2454            fp.close()
     2455            # delete entries
     2456            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
     2457            if m1:
     2458                print('\nimctrl file not found:')
     2459                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
     2460            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
     2461            if m2:
     2462                print('\nmask file not found')
     2463                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
     2464            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
     2465            if m3:
     2466                print('\nDropping entries due to negative distance: '+str(m3))
     2467            m = sorted(set(m1 + m2 + m3))
     2468            m.reverse()
     2469            for c in m:
     2470                for key in tmpDict:
     2471                    del tmpDict[key][c]
     2472            fileList = tmpDict.get('filenames','[]')
     2473            parms = []
     2474            for key in self.ParmList:
     2475                try:
     2476                    float(tmpDict[key][0])
     2477                    parms.append([str(G2py3.FormatSigFigs(val,sigfigs=5)) for val in tmpDict[key]])
     2478                except ValueError:
     2479                    parms.append(tmpDict[key])
     2480            return parms,fileList
     2481        # option 2, read in a list of files
     2482        for file in files: # read all files; place in dict by distance
     2483            imgDict = Read_imctrl(file)
     2484            tmpDict[imgDict.get('distance')] = imgDict
     2485        parms = [[] for key in self.ParmList]
     2486        fileList = []
     2487        for d in sorted(tmpDict):
     2488            fileList.append(tmpDict[d].get('filename'))
     2489            if d is None: continue
     2490            if d < 0: continue
     2491            for i,key in enumerate(self.ParmList):
     2492                val = tmpDict[d].get(key)
     2493                try:
     2494                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
     2495                except:
     2496                    val = str(val)
     2497                parms[i].append(val)
     2498        return parms,fileList
     2499   
     2500    def ReadImageParmTable(self):
     2501        '''Reads possibly edited values from the ListCtrl table and returns a list
     2502        of values for each column.
     2503        '''
     2504        rows = self.list.GetItemCount()
     2505        cols = self.list.GetColumnCount()
     2506        parms = []
     2507        for c in range(cols):
     2508            lbl = self.ParmList[c]
     2509            parms.append([])
     2510            for r in range(rows):
     2511                parms[c].append(self.list.GetItem(r,c).GetText())
     2512        return parms
     2513
     2514    def _onClose(self,event):
     2515        'Called when Cancel button is pressed'
     2516        self.EndModal(wx.ID_CANCEL)
     2517       
     2518    def _onSave(self,event):
     2519        'Called when save button is pressed; creates a .imtbl file'
     2520        fil = ''
     2521        if self.G2frame.GSASprojectfile:
     2522            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
     2523        dir,f = os.path.split(fil)
     2524        try:
     2525            dlg = wx.FileDialog(self, 'Save table data as',
     2526                        defaultDir=dir, defaultFile=f, style=wx.SAVE)
     2527            if dlg.ShowModal() != wx.ID_OK: return
     2528            fil = dlg.GetPath()
     2529            fil = os.path.splitext(fil)[0]+'.imtbl'
     2530        finally:
     2531            dlg.Destroy()       
     2532        parms = self.ReadImageParmTable()
     2533        print('Writing image parameter table as '+fil)
     2534        fp = open(fil,'w')
     2535        for c in range(len(parms)-1):
     2536            lbl = self.ParmList[c]
     2537            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
     2538        lbl = self.ParmList[c+1]
     2539        fp.write(lbl+': '+str(parms[c+1])+'\n')
     2540        lbl = 'filenames'
     2541        fp.write(lbl+': '+str(self.IMfileList)+'\n')
     2542        fp.close()
     2543   
     2544class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
     2545    '''Creates a custom ListCtrl for editing Image Integration parameters
     2546    '''
     2547    def __init__(self, parent, ID, pos=wx.DefaultPosition,
     2548                 size=(1000,200), style=0):
     2549        self.parent=parent
     2550        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
     2551        listmix.ListCtrlAutoWidthMixin.__init__(self)
     2552        listmix.TextEditMixin.__init__(self)
     2553        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
     2554        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
     2555    def FillList(self,parms):
     2556        'Places the current parms into the table'
     2557        self.ClearAll()
     2558        self.rowlen = len(self.parent.ParmList)
     2559        for i,lbl in enumerate(self.parent.HeaderList):
     2560            self.InsertColumn(i, lbl)
     2561        for r,d in enumerate(parms[0]):
     2562            if float(d) < 0: continue
     2563            index = self.InsertStringItem(sys.maxint, d)
     2564            for j in range(1,len(parms)):
     2565                self.SetStringItem(index, j, parms[j][r])
     2566        for i,lbl in enumerate(self.parent.ParmList):
     2567            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
     2568
     2569    def OnDouble(self,evt):
     2570        'respond to a double-click'
     2571        self.CloseEditor()
     2572        fil = '(none)'
     2573        try:
     2574            dlg = wx.FileDialog(G2frame, 'Select mask or control file to add (Press cancel if none)',
     2575                                style=wx.OPEN,
     2576                                wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
     2577            if dlg.ShowModal() == wx.ID_OK:
     2578                fil = dlg.GetPath()
     2579        finally:
     2580            dlg.Destroy()
     2581        if os.path.splitext(fil)[1] != '.imctrl':
     2582            self.SetStringItem(self.curRow, self.rowlen-1, fil)
     2583            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
     2584        else:
     2585            # insert or overwrite an instrument parameter set
     2586            if not os.path.exists(fil):
     2587                print('Does not exist: '+fil)
     2588                return
     2589            imgDict = Read_imctrl(fil)
     2590            dist = imgDict['distance']
     2591            parms = self.parent.ReadImageParmTable()
     2592            x = np.array([float(i) for i in parms[0]])
     2593            closest = abs(x-dist).argmin()
     2594            closeX = x[closest]
     2595            # fix IMfileList
     2596            for c,lbl in enumerate(self.parent.ParmList):
     2597                try:
     2598                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
     2599                except ValueError:
     2600                    vali = imgDict[lbl]
     2601                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
     2602                    parms[c][closest] = vali
     2603                elif dist > closeX: # insert after
     2604                    parms[c].insert(closest+1,vali)
     2605                else:
     2606                    parms[c].insert(closest,vali)
     2607            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
     2608                self.parent.IMfileList[closest] = fil
     2609            elif dist > closeX: # insert after
     2610                self.parent.IMfileList.insert(closest+1,fil)
     2611            else:
     2612                self.parent.IMfileList.insert(closest,fil)
     2613            self.FillList(parms)
     2614# Autointegration end
     2615###########################################################################
  • trunk/autoint.py

    r2065 r2082  
    11import os
    2 import wx
     2import sys
    33import copy
    44import glob
    55import re
     6import bisect
     7import numpy as np
     8import wx
     9import wx.lib.mixins.listctrl  as  listmix
    610import GSASIIpath
    711import GSASIIIO as G2IO
     
    913import GSASIIgrid as G2gd
    1014import GSASIIimgGUI as G2imG
    11 '''
    12 Define a class to be used for Andrey's AutoIntegration process
    13 '''
    14 
     15import GSASIIpy3 as G2py3
     16
     17print 'loading autoint'
     18
     19def ReadMask(filename):
     20    'Read a mask (.immask) file'
     21    File = open(filename,'r')
     22    save = {}
     23    S = File.readline()
     24    while S:
     25        if S[0] == '#':
     26            S = File.readline()
     27            continue
     28        [key,val] = S[:-1].split(':')
     29        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
     30            save[key] = eval(val)
     31        S = File.readline()
     32    File.close()
     33    G2imG.CleanupMasks(save)
     34    return save
     35
     36def ReadControls(filename):
     37    'read an image controls (.imctrl) file'
     38    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
     39            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
     40            'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg',
     41            'PolaVal','SampleAbs','dark image','background image']
     42    File = open(filename,'r')
     43    save = {}
     44    S = File.readline()
     45    while S:
     46        if S[0] == '#':
     47            S = File.readline()
     48            continue
     49        [key,val] = S[:-1].split(':')
     50        if key in ['type','calibrant','binType','SampleShape',]:    #strings
     51            save[key] = val
     52        elif key in ['rotation']:
     53            save[key] = float(val)
     54        elif key in ['center',]:
     55            if ',' in val:
     56                save[key] = eval(val)
     57            else:
     58                vals = val.strip('[] ').split()
     59                save[key] = [float(vals[0]),float(vals[1])]
     60        elif key in cntlList:
     61            save[key] = eval(val)
     62        S = File.readline()
     63    File.close()
     64    return save
     65
     66def Read_imctrl(imctrl_file):
     67    '''Read an image control file and record control parms into a dict, with some simple
     68    type conversions
     69    '''
     70    file_opt = options = {}
     71    save = {'filename':imctrl_file}
     72    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
     73    if os.path.exists(immask_file):
     74        save['maskfile'] = immask_file
     75    else:
     76        save['maskfile'] = '(none)'
     77    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
     78                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
     79                        'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg',
     80                        'PolaVal','SampleAbs','dark image','background image']
     81    File = open(imctrl_file,'r')
     82    fullIntegrate = False
     83    try:
     84        S = File.readline()
     85        while S:
     86            if S[0] == '#':
     87                S = File.readline()
     88                continue
     89            [key,val] = S[:-1].split(':')
     90            if key in ['type','calibrant','binType','SampleShape',]:    #strings
     91                save[key] = val
     92            elif key == 'rotation':
     93                save[key] = float(val)
     94            elif key == 'fullIntegrate':
     95                fullIntegrate = eval(val)
     96            elif key == 'LRazimuth':
     97                save['LRazimuth_min'],save['LRazimuth_max'] = eval(val)[0:2]
     98            elif key == 'IOtth':
     99                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
     100            elif key == 'center':
     101                if ',' in val:
     102                    vals = eval(val)
     103                else:
     104                    vals = val.strip('[] ').split()
     105                    vals = [float(vals[0]),float(vals[1])]
     106                save['center_x'],save['center_y'] = vals[0:2]
     107            elif key in cntlList:
     108                save[key] = eval(val)
     109            S = File.readline()
     110    finally:
     111        File.close()
     112        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0,0
     113    return save
     114   
    15115class AutoIntFrame(wx.Frame):
    16116    '''Creates a wx.Frame window for the Image AutoIntegration.
     
    48148            if newImage in imageFileList: continue # already read
    49149            for imgId in G2IO.ReadImages(G2frame,newImage):
    50                 # update controls from master
    51150                controlsDict = G2frame.PatternTree.GetItemPyData(
    52151                    G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
    53                 controlsDict.update(self.ImageControls)
    54                 # update masks from master
    55152                ImageMasks = G2frame.PatternTree.GetItemPyData(
    56153                    G2gd.GetPatternTreeItemId(G2frame,imgId, 'Masks'))
     154                if self.params['Mode'] == 'table':
     155                    dist = controlsDict['distance']
     156                    interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
     157                    self.ImageControls = ReadControls(imgctrl)
     158                    self.ImageControls.update(interpDict)
     159                    self.ImageControls['showLines'] = True
     160                    self.ImageControls['ring'] = []
     161                    self.ImageControls['rings'] = []
     162                    self.ImageControls['ellipses'] = []
     163                    self.ImageControls['setDefault'] = False
     164                    for i in 'range','size','GonioAngles':
     165                        if i in self.ImageControls:
     166                            del self.ImageControls[i]
     167                    # load copy of Image Masks
     168                    if immask:
     169                        self.ImageMasks = ReadMask(immask)
     170                        del self.Thresholds['Thresholds']
     171                    else:
     172                        self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
     173                # update controls from master
     174                controlsDict.update(self.ImageControls)
     175                # update masks from master w/o Thresholds
    57176                ImageMasks.update(self.ImageMasks)
    58177        # now integrate the images that have not already been processed before
     
    117236        # show current IMG base
    118237        self.ControlBaseLbl.SetLabel(G2frame.PatternTree.GetItemText(G2frame.Image))
    119         if self.params['Mode'] == 'file':
    120             'get file info'
    121             GSASIIpath.IPyBreak()
    122         else:
     238        if self.params['Mode'] != 'table':
    123239            # load copy of Image Controls from current image and clean up
    124240            # items that should not be copied
     
    179295            is started. When Pause is pressed, the loop is stopped.
    180296            '''
    181             #print self.params # for debug
    182 
    183             # check inputs before starting
    184             err = ''
     297            # check inputs for errors before starting
     298            #err = ''
    185299            #if not any([self.params[fmt] for fmt in self.fmtlist]):
    186300            #    err += '\nPlease select at least one output format\n'
    187             if (self.params['Mode'] == 'file' and not
    188                     os.path.exists(self.params['IMGfile'])):
    189                 err += '\nThe image controls file could not be found\n'
    190             if (self.params['Mode'] == 'file' and
    191                 not self.params['IgnoreMask']
    192                 ) and not os.path.exists(self.params['MaskFile']):
    193                 err += '\nThe mask file could not be found\n'
    194             if err:
    195                 G2G.G2MessageBox(self,err)
    196                 return
     301            #if err:
     302            #    G2G.G2MessageBox(self,err)
     303            #    return
    197304            # change button label
    198305            if btnstart.GetLabel() != 'Pause':
     
    244351                    dlg.Destroy()
    245352                return
    246             if btn1 == event.GetEventObject():
    247                 ext = '.imctrl'
    248                 title = 'Image control'
    249             else:
    250                 ext = '.immask'
    251                 title = 'Image masks'               
    252             dlg = wx.FileDialog(
    253                 self, 'Select name for '+title+' file to read',
    254                 '.', '',
    255                 title+'file (*'+ext+')|*'+ext,
    256                 wx.OPEN|wx.CHANGE_DIR)
    257             dlg.CenterOnParent()
    258             try:
    259                 if dlg.ShowModal() == wx.ID_OK:
    260                     filename = dlg.GetPath()
    261                     # make sure extension is correct
    262                     #filename = os.path.splitext(filename)[0]+ext
    263                     if btn1 == event.GetEventObject():
    264                         fInp1.SetValue(filename)
    265                     else:
    266                         fInp2.SetValue(filename)
    267                 else:
    268                     filename = None
    269             finally:
    270                 dlg.Destroy()
    271353               
    272354        def OnRadioSelect(event):
    273             '''Respond to a radiobutton selection and enable or
    274             disable widgets accordingly. Also gets called when the
    275             "Don't Use" flag for Mask use is called.
     355            '''Respond to a radiobutton selection and when in table
     356            mode, get parameters from user.
    276357            '''
    277             lbl1.Disable()
    278             fInp1.Disable()
    279             btn1.Disable()
    280             lbl2.Disable()
    281             fInp2.Disable()
    282             ign2.Disable()
    283             btn2.Disable()
     358            self.Evaluator = None
    284359            if r2.GetValue():
    285                 self.params['Mode'] = 'file'
    286                 fInp1.Enable()
    287                 btn1.Enable()
    288                 lbl1.Enable()
    289                 ign2.Enable()
    290                 if not self.params['IgnoreMask']:
    291                     fInp2.Enable()
    292                     btn2.Enable()
    293                     lbl2.Enable()
     360                self.params['Mode'] = 'table'
     361                try:
     362                    dlg = IntegParmTable(self.G2frame) # create the dialog
     363                    if dlg.ShowModal() == wx.ID_OK:
     364                        self.Evaluator = DefineEvaluator(dlg)
     365                    else:
     366                        r1.SetValue(True)
     367                finally:
     368                    dlg.Destroy()
    294369            else:
    295370                self.params['Mode'] = 'active'
     
    298373        ##################################################
    299374        self.G2frame = G2frame
     375        self.Evaluator = None
    300376        self.params = {}
    301377        self.Reset = False
     
    337413        lblsizr.Add(r1)
    338414        r1.SetValue(True)
    339         r2 = wx.RadioButton(mnpnl, wx.ID_ANY, "Use from file(s)")
     415        r2 = wx.RadioButton(mnpnl, wx.ID_ANY, "Use from table")
    340416        lblsizr.Add(r2)
    341417        r2.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
    342         r2.Disable()         # deactivate this until implemented
    343         # Image controls file
    344         sizer = wx.BoxSizer(wx.HORIZONTAL)
    345         sizer.Add((20,-1))
    346         lbl1 = wx.StaticText(mnpnl, wx.ID_ANY,'IMG control file: ')
    347         sizer.Add(lbl1)
    348         fInp1 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'IMGfile',
    349                                        notBlank=False,size=(300,-1))
    350         sizer.Add(fInp1)
    351         btn1 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
    352         btn1.Bind(wx.EVT_BUTTON, OnBrowse)
    353         sizer.Add(btn1)
    354         lblsizr.Add(sizer)
    355         # Masks input file
    356         sizer = wx.BoxSizer(wx.HORIZONTAL)
    357         sizer.Add((20,-1))
    358         lbl2 = wx.StaticText(mnpnl, wx.ID_ANY,'Mask file: ')
    359         sizer.Add(lbl2)
    360         fInp2 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'MaskFile',
    361                                        notBlank=False,size=(300,-1))
    362         sizer.Add(fInp2)
    363         ign2 = G2G.G2CheckBox(mnpnl,"Don't use",self.params,'IgnoreMask',
    364                               OnChange=OnRadioSelect)
    365         sizer.Add(ign2)
    366         btn2 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
    367         btn2.Bind(wx.EVT_BUTTON, OnBrowse)
    368         sizer.Add(btn2)
    369         lblsizr.Add(sizer)
    370418        mnsizer.Add(lblsizr)
    371419
     
    420468        mnsizer.Fit(self)
    421469        self.CenterOnParent()
    422         self.Show() 
     470        self.Show()
     471
     472def DefineEvaluator(dlg):
     473    '''Creates a function that provides interpolated values for a given distance value
     474    '''
     475    def Evaluator(dist):
     476        '''Interpolate image parameters for a supplied distance value
     477
     478        :param float dist: distance to use for interpolation
     479        :returns: a list with 3 items:
     480
     481          * a dict with parameter values,
     482          * the closest imctrl and
     483          * the closest maskfile (or None)
     484        '''           
     485        x = np.array([float(i) for i in parms[0]])
     486        closest = abs(x-dist).argmin()
     487        closeX = x[closest]
     488        D = {'distance':dist}
     489        imctfile = IMfileList[closest]
     490        if parms[-1][closest].lower() != '(none)':
     491            maskfile = parms[-1][closest]
     492        else:
     493            maskfile = None
     494        for c in range(1,cols-1):
     495            lbl = ParmList[c]
     496            if lbl in nonInterpVars:
     497                D[lbl] = float(parms[c][closest])
     498            else:
     499                y = np.array([float(i) for i in parms[c]])
     500                D[lbl] = np.interp(dist,x,y)
     501        # full integration when angular range is 0
     502        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
     503        # conversion for paired values
     504        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
     505            r = a.split('_')[0]
     506            D[r] = [D[a],D[b]]
     507            del D[a]
     508            del D[b]
     509        return D,imctfile,maskfile
     510    # save local copies of values needed in Evaluator
     511    parms = dlg.ReadImageParmTable()
     512    IMfileList = dlg.IMfileList
     513    cols = dlg.list.GetColumnCount()
     514    ParmList = dlg.ParmList
     515    nonInterpVars = dlg.nonInterpVars
     516    return Evaluator
     517
     518class IntegParmTable(wx.Dialog):
     519    '''Creates a dialog window with a table of integration parameters.
     520    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
     521    In this case, :func:`DefineEvaluator` should be called to obtain a function that
     522    creates a dictionary with interpolated parameter values.
     523    '''
     524    ParmList = ('distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
     525            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
     526            'maskfile',
     527            )
     528    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
     529                     'outChannels')  # values in this list are taken from nearest rather than interpolated
     530    HeaderList = ('Det Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
     531            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
     532            'Mask File',
     533            )
     534    def __init__(self,G2frame):
     535        self.G2frame = G2frame
     536        self.parms = [] # list of values by column
     537        self.IMfileList = [] # list of .imctrl file names for each entry in table
     538        wx.Dialog.__init__(self,G2frame,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
     539        files = []
     540        try:
     541            dlg = wx.FileDialog(self, 'Select image control files or previous table',
     542                                style=wx.OPEN| wx.MULTIPLE,
     543                                wildcard='image control files (.imctrl)|*.imctrl|Integration table (*.imtbl)|*.imtbl')
     544            if dlg.ShowModal() == wx.ID_OK:
     545                files = dlg.GetPaths()
     546                self.parms,self.IMfileList = self.ReadFiles(files)
     547        finally:
     548            dlg.Destroy()
     549        if not files:
     550            wx.CallAfter(self.EndModal,wx.ID_CANCEL)
     551            return
     552        mainSizer = wx.BoxSizer(wx.VERTICAL)
     553        self.list = ImgIntLstCtrl(self, wx.ID_ANY,
     554                      style=wx.LC_REPORT
     555                          | wx.BORDER_SUNKEN
     556                         #| wx.BORDER_NONE
     557                         )
     558        mainSizer.Add(self.list,1,wx.EXPAND,1)
     559        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
     560        btn = wx.Button(self, wx.ID_OK)
     561        btnsizer.Add(btn)
     562        btn = wx.Button(self, wx.ID_ANY,'Save')
     563        btn.Bind(wx.EVT_BUTTON,self._onSave)
     564        btnsizer.Add(btn)
     565        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
     566        btn.Bind(wx.EVT_BUTTON,self._onClose)
     567        btnsizer.Add(btn)
     568        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
     569        self.SetSizer(mainSizer)
     570        self.list.FillList(self.parms)
     571        mainSizer.Layout()
     572        mainSizer.Fit(self)
     573       
     574    def ReadFiles(self,files):
     575        '''Reads a list of .imctrl files or a single .imtbl file
     576        '''
     577        tmpDict = {}
     578        if not files: return
     579        # option 1, a dump from a previous save
     580        if os.path.splitext(files[0])[1] == '.imtbl':
     581            fp = open(files[0],'r')
     582            S = fp.readline()
     583            while S:
     584                if S[0] != '#':
     585                    [key,val] = S[:-1].split(':')
     586                    tmpDict[key] = eval(val)
     587                S = fp.readline()
     588            fp.close()
     589            # delete entries
     590            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
     591            if m1:
     592                print('\nimctrl file not found:')
     593                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
     594            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
     595            if m2:
     596                print('\nmask file not found')
     597                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
     598            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
     599            if m3:
     600                print('\nDropping entries due to negative distance: '+str(m3))
     601            m = sorted(set(m1 + m2 + m3))
     602            m.reverse()
     603            for c in m:
     604                for key in tmpDict:
     605                    del tmpDict[key][c]
     606            fileList = tmpDict.get('filenames','[]')
     607            parms = []
     608            for key in self.ParmList:
     609                try:
     610                    float(tmpDict[key][0])
     611                    parms.append([str(G2py3.FormatSigFigs(val,sigfigs=5)) for val in tmpDict[key]])
     612                except ValueError:
     613                    parms.append(tmpDict[key])
     614            return parms,fileList
     615        # option 2, read in a list of files
     616        for file in files: # read all files; place in dict by distance
     617            imgDict = Read_imctrl(file)
     618            tmpDict[imgDict.get('distance')] = imgDict
     619        parms = [[] for key in self.ParmList]
     620        fileList = []
     621        for d in sorted(tmpDict):
     622            fileList.append(tmpDict[d].get('filename'))
     623            if d is None: continue
     624            if d < 0: continue
     625            for i,key in enumerate(self.ParmList):
     626                val = tmpDict[d].get(key)
     627                try:
     628                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
     629                except:
     630                    val = str(val)
     631                parms[i].append(val)
     632        return parms,fileList
     633   
     634    def ReadImageParmTable(self):
     635        '''Reads possibly edited values from the ListCtrl table and returns a list
     636        of values for each column.
     637        '''
     638        rows = self.list.GetItemCount()
     639        cols = self.list.GetColumnCount()
     640        parms = []
     641        for c in range(cols):
     642            lbl = self.ParmList[c]
     643            parms.append([])
     644            for r in range(rows):
     645                parms[c].append(self.list.GetItem(r,c).GetText())
     646        return parms
     647
     648    def _onClose(self,event):
     649        'Called when Cancel button is pressed'
     650        self.EndModal(wx.ID_CANCEL)
     651       
     652    def _onSave(self,event):
     653        'Called when save button is pressed; creates a .imtbl file'
     654        fil = ''
     655        if self.G2frame.GSASprojectfile:
     656            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
     657        dir,f = os.path.split(fil)
     658        try:
     659            dlg = wx.FileDialog(self, 'Save table data as',
     660                        defaultDir=dir, defaultFile=f, style=wx.SAVE)
     661            if dlg.ShowModal() != wx.ID_OK: return
     662            fil = dlg.GetPath()
     663            fil = os.path.splitext(fil)[0]+'.imtbl'
     664        finally:
     665            dlg.Destroy()       
     666        parms = self.ReadImageParmTable()
     667        print('Writing image parameter table as '+fil)
     668        fp = open(fil,'w')
     669        for c in range(len(parms)-1):
     670            lbl = self.ParmList[c]
     671            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
     672        lbl = self.ParmList[c+1]
     673        fp.write(lbl+': '+str(parms[c+1])+'\n')
     674        lbl = 'filenames'
     675        fp.write(lbl+': '+str(self.IMfileList)+'\n')
     676        fp.close()
     677   
     678class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
     679    '''Creates a custom ListCtrl for editing Image Integration parameters
     680    '''
     681    def __init__(self, parent, ID, pos=wx.DefaultPosition,
     682                 size=(1000,200), style=0):
     683        self.parent=parent
     684        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
     685        listmix.ListCtrlAutoWidthMixin.__init__(self)
     686        listmix.TextEditMixin.__init__(self)
     687        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
     688        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
     689    def FillList(self,parms):
     690        'Places the current parms into the table'
     691        self.ClearAll()
     692        self.rowlen = len(self.parent.ParmList)
     693        for i,lbl in enumerate(self.parent.HeaderList):
     694            self.InsertColumn(i, lbl)
     695        for r,d in enumerate(parms[0]):
     696            if float(d) < 0: continue
     697            index = self.InsertStringItem(sys.maxint, d)
     698            for j in range(1,len(parms)):
     699                self.SetStringItem(index, j, parms[j][r])
     700        for i,lbl in enumerate(self.parent.ParmList):
     701            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
     702
     703    def OnDouble(self,evt):
     704        'respond to a double-click'
     705        self.CloseEditor()
     706        fil = '(none)'
     707        try:
     708            dlg = wx.FileDialog(G2frame, 'Select mask or control file to add (Press cancel if none)',
     709                                style=wx.OPEN,
     710                                wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
     711            if dlg.ShowModal() == wx.ID_OK:
     712                fil = dlg.GetPath()
     713        finally:
     714            dlg.Destroy()
     715        if os.path.splitext(fil)[1] != '.imctrl':
     716            self.SetStringItem(self.curRow, self.rowlen-1, fil)
     717            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
     718        else:
     719            # insert or overwrite an instrument parameter set
     720            if not os.path.exists(fil):
     721                print('Does not exist: '+fil)
     722                return
     723            imgDict = Read_imctrl(fil)
     724            dist = imgDict['distance']
     725            parms = self.parent.ReadImageParmTable()
     726            x = np.array([float(i) for i in parms[0]])
     727            closest = abs(x-dist).argmin()
     728            closeX = x[closest]
     729            # fix IMfileList
     730            for c,lbl in enumerate(self.parent.ParmList):
     731                try:
     732                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
     733                except ValueError:
     734                    vali = imgDict[lbl]
     735                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
     736                    parms[c][closest] = vali
     737                elif dist > closeX: # insert after
     738                    parms[c].insert(closest+1,vali)
     739                else:
     740                    parms[c].insert(closest,vali)
     741            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
     742                self.parent.IMfileList[closest] = fil
     743            elif dist > closeX: # insert after
     744                self.parent.IMfileList.insert(closest+1,fil)
     745            else:
     746                self.parent.IMfileList.insert(closest,fil)
     747            self.FillList(parms)
    423748
    424749if __name__ == '__main__':
Note: See TracChangeset for help on using the changeset viewer.