From 809afa3b2d602eeb7882877d94fe1c64569116a9 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Mon, 19 Aug 2024 10:54:23 -0500 Subject: [PATCH 01/26] more pathlib usage, (wip) --- larch/utils/paths.py | 12 ++++++++++-- larch/wxxas/feffit_panel.py | 23 +++++++++++++---------- larch/wxxas/xasgui.py | 29 +++++++++++++++++------------ 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/larch/utils/paths.py b/larch/utils/paths.py index fef2f12ec..bd8d6844f 100644 --- a/larch/utils/paths.py +++ b/larch/utils/paths.py @@ -35,10 +35,18 @@ def winpath(d): if uname.startswith('linux'): uname = 'linux' +def path_split(path): + "emulate os.path.split" + p = Path(path).absolute() + return p.parent.as_posix(), p.name + # bindir = location of local binaries nbits = platform.architecture()[0].replace('bit', '') -topdir = os.path.split(os.path.split(os.path.abspath(__file__))[0])[0] -bindir = os.path.abspath(os.path.join(topdir, 'bin', '%s%s' % (uname, nbits))) + +_here = Path(__file__).absolute() +topdir = _here.parents[1].as_posix() +bindir = Path(topdir, 'bin', f"{uname}{nbits}").as_posix() + def get_homedir(): "determine home directory" diff --git a/larch/wxxas/feffit_panel.py b/larch/wxxas/feffit_panel.py index edbf217d4..d277202ca 100644 --- a/larch/wxxas/feffit_panel.py +++ b/larch/wxxas/feffit_panel.py @@ -1,5 +1,4 @@ import time -import os import sys import ast import shutil @@ -10,6 +9,7 @@ from sys import exc_info from string import printable from functools import partial +from pathlib import Path import numpy as np np.seterr(all='ignore') @@ -595,8 +595,9 @@ def __init__(self, parent, feffit_panel, filename, title, user_label, panel = GridPanel(self, ncols=4, nrows=4, pad=2, itemstyle=LEFT) self.fullpath = filename - par, feffdat_file = os.path.split(filename) - parent_folder, dirname = os.path.split(par) + pfile = Path(filename).absolute() + feffdat_file = pfile.name + dirname = pfile.parent.name self.user_label = user_label @@ -1329,8 +1330,9 @@ def add_path(self, filename, pathinfo=None, feffpath=None, resize=True): if pathinfo is None and feffpath is None: raise ValueError("add_path needs a Feff Path or Path information") self.params_need_update = True - parent, fname = os.path.split(filename) - parent, feffrun = os.path.split(parent) + pfile = Path(filenam).absolute() + fname = pfile.name + feffrun = pfile.parent.name feffcache = getattr(self.larch.symtable, '_feffcache', None) if feffcache is None: @@ -1426,7 +1428,7 @@ def add_path(self, filename, pathinfo=None, feffpath=None, resize=True): pdat.update(pathpanel.get_expressions()) if title not in feffcache['paths']: - if os.path.exists(filename): + if Path(filename).exists(): self.larch_eval(COMMANDS['cache_path'].format(**pdat)) else: print(f"cannot file Feff data file '{filename}'") @@ -1571,6 +1573,7 @@ def build_fitmodel(self, groupname=None, opts=None): def get_used_params(self): used_syms = [] path_pages = {} + for i in range(self.paths_nb.GetPageCount()): text = self.paths_nb.GetPageText(i).strip() path_pages[text] = self.paths_nb.GetPage(i) @@ -1721,10 +1724,10 @@ def autosave_script(self, text, fname='feffit_script.lar'): confdir = self.controller.larix_folder if fname is None: fname = 'feffit_script.lar' - fullname = os.path.join(confdir, fname) - if os.path.exists(fullname): - backup = os.path.join(confdir, 'feffit_script_BAK.lar') - shutil.copy(fullname, backup) + fullpath = Path(confdir, fname) + fullnane = fullpath.as_posix() + if fulllpath.exists(): + shutil.copy(fullname, Path(confdir, 'feffit_script_BAK.lar').as_posix()) with open(fullname, 'w', encoding=sys.getdefaultencoding()) as fh: fh.write(text) return fullname diff --git a/larch/wxxas/xasgui.py b/larch/wxxas/xasgui.py index 84aaf820a..d4539129e 100644 --- a/larch/wxxas/xasgui.py +++ b/larch/wxxas/xasgui.py @@ -7,6 +7,7 @@ import time import copy import platform +from pathlib import Path from importlib import import_module from threading import Thread import numpy as np @@ -386,7 +387,7 @@ def version_checker(): self.larch = self.larch_buffer.larchshell self.controller = XASController(wxparent=self, _larch=self.larch) - iconfile = os.path.join(icondir, ICON_FILE) + iconfile = Path(icondir, ICON_FILE).as_posix() self.SetIcon(wx.Icon(iconfile, wx.BITMAP_TYPE_ICO)) self.last_autosave = 0 @@ -844,7 +845,7 @@ def onExportCSV(self, evt=None): if outfile is None: return - if os.path.exists(outfile) and uname != 'darwin': # darwin prompts in FileSave! + if Path(outfile).exists() and uname != 'darwin': # darwin prompts in FileSave! if wx.ID_YES != Popup(self, "Overwrite existing CSV File?", "Overwrite existing file?", style=wx.YES_NO): @@ -913,14 +914,14 @@ def save_athena_project(self, filename, grouplist, prompt=True, return savegroups = [self.controller.get_group(gname) for gname in grouplist] if prompt: - _, filename = os.path.split(filename) + filename = Path(filename).name wcards = 'Project Files (*.prj)|*.prj|All files (*.*)|*.*' filename = FileSave(self, 'Save Groups to Project File', default_file=filename, wildcard=wcards) if filename is None: return - if (os.path.exists(filename) and warn_overwrite and + if (Path(filename).exists() and warn_overwrite and uname != 'darwin'): # darwin prompts in FileSave! if wx.ID_YES != Popup(self, "Overwrite existing Project File?", @@ -967,7 +968,9 @@ def onLoadSession(self, evt=None, path=None): LoadSessionDialog(self, _session, path, self.controller).Show() self.last_session_read = path - fdir, fname = os.path.split(path) + fpath = Path(fpath).absolute() + fname = fpath.name + fdir = fpath.parent.as_posix() if self.controller.chdir_on_fileopen() and len(fdir) > 0: os.chdir(fdir) self.controller.set_workdir() @@ -991,14 +994,14 @@ def onSaveSession(self, evt=None): if fname is None: fname = time.strftime('%Y%b%d_%H%M') + '.larix' - _, fname = os.path.split(fname) + fname = Path(fname).name wcards = 'Larch Project Files (*.larix)|*.larix|All files (*.*)|*.*' fname = FileSave(self, 'Save Larch Session File', default_file=fname, wildcard=wcards) if fname is None: return - if os.path.exists(fname) and uname != 'darwin': # darwin prompts in FileSave! + if Path(fname).exists() and uname != 'darwin': # darwin prompts in FileSave! if wx.ID_YES != Popup(self, "Overwrite existing Project File?", "Overwrite existing file?", style=wx.YES_NO): return @@ -1012,8 +1015,8 @@ def onSaveSession(self, evt=None): def onClearSession(self, evt=None): conf = self.controller.get_config('autosave', {'fileroot': 'autosave'}) - afile = os.path.join(self.controller.larix_folder, - conf['fileroot']+'.larix') + afile = Path(self.controller.larix_folder, + conf['fileroot']+'.larix').as_posix() msg = f"""Session will be saved to '{afile:s}' @@ -1343,7 +1346,9 @@ def file_mtime(x): self.onRead(path) def onRead(self, path): - filedir, filename = os.path.split(os.path.abspath(path)) + fpath = Path(path).absolute() + filedir = fpath.parent.as_posix() + filename = fpath.name if self.controller.chdir_on_fileopen() and len(filedir) > 0: os.chdir(filedir) self.controller.set_workdir() @@ -1380,7 +1385,7 @@ def onReadSpecfile_OK(self, script, path, scanlist, config=None): """read groups from a list of scans from a specfile""" self.larch.eval("_specfile = specfile('{path:s}')".format(path=path)) dgroup = None - _path, fname = os.path.split(path) + fname = Path(path).name first_group = None cur_panel = self.nb.GetCurrentPage() cur_panel.skip_plotting = True @@ -1461,7 +1466,7 @@ def onReadXasDataSource_OK(self, script, path, scanlist, array_sel=None, extra_s """read groups from a list of scans from a xas data source""" self.larch.eval("_data_source = open_xas_source('{path:s}')".format(path=path)) dgroup = None - _path, fname = os.path.split(path) + fname = Path(path).name first_group = None cur_panel = self.nb.GetCurrentPage() cur_panel.skip_plotting = True From b55c89a61f384a3d2ab3c9e2fd2ef40f7bd4978b Mon Sep 17 00:00:00 2001 From: Matt Newville Date: Mon, 19 Aug 2024 11:26:09 -0500 Subject: [PATCH 02/26] some updates to pymatgen interface... --- larch/xrd/amcsd.py | 20 +++++++++++++------- larch/xrd/amcsd_utils.py | 4 +++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/larch/xrd/amcsd.py b/larch/xrd/amcsd.py index f87ea699d..5c5616a3a 100755 --- a/larch/xrd/amcsd.py +++ b/larch/xrd/amcsd.py @@ -19,7 +19,6 @@ c) getting atomic clustes as for feff files d) saving Feff.inp files - """ import sys @@ -44,7 +43,7 @@ from .amcsd_utils import (make_engine, isAMCSD, put_optarray, get_optarray, - PMG_CIF_OPTS, CifParser, SpacegroupAnalyzer) + PMG_CIF_OPTS, CifParser, SpacegroupAnalyzer, pmg_version) from xraydb.chemparser import chemparse from xraydb import f0, f1_chantler, f2_chantler @@ -549,16 +548,23 @@ def calculate_f2(self, hkls, qhkls=None, energy=None, wavelength=None): def get_pmg_struct(self): if self.pmg_cstruct is not None and self.pmg_pstruct is not None: return - + err = f"pymatgen {pmg_version} could not" try: pmcif = CifParser(StringIO(self.ciftext), **PMG_CIF_OPTS) - self.pmg_cstruct = pmcif.get_structures()[0] - self.pmg_pstruct = SpacegroupAnalyzer(self.pmg_cstruct - ).get_conventional_standard_structure() except: - print(f"pymatgen could not parse CIF structure for CIF {self.ams_id}") + print(f"{err} parse CIF text for CIF {self.ams_id}") + try: + self.pmg_cstruct = pmcif.parse_structures()[0] + except: + print(f"{err} parse structure for CIF {self.ams_id}") + try: + self.pmg_pstruct = SpacegroupAnalyzer(self.pmg_cstruct + ).get_conventional_standard_structure() + except: + print(f"{err} could not analyze spacegroup for CIF {self.ams_id}") + def get_unitcell(self): "unitcell as dict, from PMG structure" self.get_pmg_struct() diff --git a/larch/xrd/amcsd_utils.py b/larch/xrd/amcsd_utils.py index b2831afc9..e3d291d92 100644 --- a/larch/xrd/amcsd_utils.py +++ b/larch/xrd/amcsd_utils.py @@ -13,10 +13,12 @@ from pymatgen.io.cif import CifParser from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.core import Molecule, IMolecule, IStructure + from pymatgen.core import __version__ as pmg_version except: CifParser = SpacegroupAnalyzer = None Molecule = IMolecule = IStructure = None - + pmg_version = None + from larch.utils.physical_constants import ATOM_SYMS, ATOM_NAMES __version__ = '1' From 68745e1705bfe9361facc383e8460868dbbb4cd9 Mon Sep 17 00:00:00 2001 From: Matt Newville Date: Mon, 19 Aug 2024 11:28:27 -0500 Subject: [PATCH 03/26] typos --- larch/wxxas/feffit_panel.py | 2 +- larch/xafs/feffrunner.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/larch/wxxas/feffit_panel.py b/larch/wxxas/feffit_panel.py index d277202ca..ce9101145 100644 --- a/larch/wxxas/feffit_panel.py +++ b/larch/wxxas/feffit_panel.py @@ -1330,7 +1330,7 @@ def add_path(self, filename, pathinfo=None, feffpath=None, resize=True): if pathinfo is None and feffpath is None: raise ValueError("add_path needs a Feff Path or Path information") self.params_need_update = True - pfile = Path(filenam).absolute() + pfile = Path(filename).absolute() fname = pfile.name feffrun = pfile.parent.name diff --git a/larch/xafs/feffrunner.py b/larch/xafs/feffrunner.py index 9842800ce..53e1723a3 100644 --- a/larch/xafs/feffrunner.py +++ b/larch/xafs/feffrunner.py @@ -157,7 +157,7 @@ def run(self, feffinp=None, folder=None, exe='feff8l'): copy('feff.inp', savefile) copy(feffinp_file, 'feff.inp') - logname = Path(program).name.as_posix() + logname = Path(program).name if logname.endswith('.exe'): logname = logname[:4] From 2015476888639845e7e69034c45375ead96ec479 Mon Sep 17 00:00:00 2001 From: Matt Newville Date: Mon, 19 Aug 2024 11:29:47 -0500 Subject: [PATCH 04/26] need to avoid pymatgen 2024.8.* (so far) on Windows --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 574c007ec..c04c642e0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,7 +63,7 @@ install_requires = scikit-image scikit-learn psutil - pymatgen + pymatgen<=2024.7.18 mp_api pycifrw fabio From 3fd4782980516f35616899b185a88c0c49b19ae6 Mon Sep 17 00:00:00 2001 From: Matt Newville Date: Mon, 19 Aug 2024 11:59:06 -0500 Subject: [PATCH 05/26] add path_split() utility, more os.path cleanup --- larch/utils/__init__.py | 2 +- larch/utils/paths.py | 2 +- larch/wxxas/larix_app.py | 4 ++-- larch/wxxas/prepeak_panel.py | 6 +++--- larch/wxxas/regress_panel.py | 6 +++--- larch/wxxas/xas_dialogs.py | 18 +++++++++--------- larch/wxxas/xasgui.py | 10 +++++----- larch/wxxas/xasnorm_panel.py | 5 ++--- larch/wxxas/xydata_panel.py | 5 ++--- 9 files changed, 28 insertions(+), 30 deletions(-) diff --git a/larch/utils/__init__.py b/larch/utils/__init__.py index 46dbc6bc6..176dc7cba 100644 --- a/larch/utils/__init__.py +++ b/larch/utils/__init__.py @@ -11,7 +11,7 @@ from charset_normalizer import from_bytes from .gformat import gformat, getfloat_attr -from .paths import uname, bindir, unixpath, get_homedir, get_cwd +from .paths import uname, bindir, unixpath, get_homedir, path_split, get_cwd from .debugtimer import debugtimer from .strutils import (fixName, isValidName, isNumber, bytes2str, diff --git a/larch/utils/paths.py b/larch/utils/paths.py index bd8d6844f..2fe3941fe 100644 --- a/larch/utils/paths.py +++ b/larch/utils/paths.py @@ -36,7 +36,7 @@ def winpath(d): uname = 'linux' def path_split(path): - "emulate os.path.split" + "emulate os.path.split, returning posix path and filename" p = Path(path).absolute() return p.parent.as_posix(), p.name diff --git a/larch/wxxas/larix_app.py b/larch/wxxas/larix_app.py index 574f2874b..b76adb5a0 100644 --- a/larch/wxxas/larix_app.py +++ b/larch/wxxas/larix_app.py @@ -1,8 +1,8 @@ import time import sys -import os import locale from threading import Thread +from pathlib import Path import wx import wx.adv, wx.richtext from wx.lib.mixins.inspection import InspectionMixin @@ -48,7 +48,7 @@ def InitLocale(self): class LarixSplashScreen(wx.adv.SplashScreen): def __init__(self, **kws): self.kws = kws - bmp = wx.Image(os.path.join(icondir, ICON_FILE)).ConvertToBitmap() + bmp = wx.Image(Path(icondir, ICON_FILE).as_posix()).ConvertToBitmap() wx.adv.SplashScreen.__init__(self, bmp, SPLASH_STYLE, 9000, None, -1) self.import_thread = Thread(target=self.importer) self.import_thread.start() diff --git a/larch/wxxas/prepeak_panel.py b/larch/wxxas/prepeak_panel.py index 83d10403a..a5e9eff8d 100644 --- a/larch/wxxas/prepeak_panel.py +++ b/larch/wxxas/prepeak_panel.py @@ -1,6 +1,6 @@ import time -import os import sys +from pathlib import Path import numpy as np np.seterr(all='ignore') @@ -446,7 +446,7 @@ def onSaveAllStats(self, evt=None): default_file=deffile, wildcard=wcards) if path is None: return - if os.path.exists(path) and uname != 'darwin': # darwin prompts in FileSave! + if Path(path).exists() and uname != 'darwin': # darwin prompts in FileSave! if wx.ID_YES != Popup(self, "Overwrite existing Statistics File?", "Overwrite existing file?", style=wx.YES_NO): @@ -1601,4 +1601,4 @@ def autosave_modelresult(self, result, fname=None): confdir = self.controller.larix_folder if fname is None: fname = 'autosave_peakfile.modl' - save_groups(os.path.join(confdir, fname), ['#peakfit 1.0', result]) + save_groups(Path(confdir, fname).as_posix(), ['#peakfit 1.0', result]) diff --git a/larch/wxxas/regress_panel.py b/larch/wxxas/regress_panel.py index 7b8b80f44..2c184713d 100644 --- a/larch/wxxas/regress_panel.py +++ b/larch/wxxas/regress_panel.py @@ -2,9 +2,9 @@ """ Linear Combination panel """ -import os import sys import time +from pathlib import Path import wx import wx.grid as wxgrid import numpy as np @@ -479,7 +479,7 @@ def onLoadCSV(self, event=None): if fname is None: return - self.save_csvfile = os.path.split(fname)[1] + self.save_csvfile = Path(fname).name varname = fix_varname(self.wids['varname'].GetValue()) csvgroup = read_csv(fname) script = [] @@ -504,7 +504,7 @@ def onSaveCSV(self, event=None): default_file=self.save_csvfile) if fname is None: return - self.save_csvfile = os.path.split(fname)[1] + self.save_csvfile = Path(fname).name buff = [] for row in self.wids['table'].table.data: buff.append("%s, %s, %s" % (row[0], gformat(row[1]), gformat(row[2]))) diff --git a/larch/wxxas/xas_dialogs.py b/larch/wxxas/xas_dialogs.py index ea2f593e9..f533524ce 100644 --- a/larch/wxxas/xas_dialogs.py +++ b/larch/wxxas/xas_dialogs.py @@ -1,9 +1,9 @@ import sys -import os import copy import time from collections import namedtuple from functools import partial +from pathlib import Path import numpy as np from lmfit import Parameters, minimize, fit_report from matplotlib.ticker import FuncFormatter @@ -14,7 +14,7 @@ from larch import Group, isgroup from larch.math import index_of, index_nearest, interp from larch.utils.strutils import file2groupname, unique_name -from larch.utils import unixpath +from larch.utils import path_split from larch.wxlib import (GridPanel, BitmapButton, FloatCtrl, FloatSpin, set_color, FloatSpinWithPin, get_icon, SimpleText, @@ -217,7 +217,7 @@ def plot_results(self, event=None, keep_limits=True): dgroup = self.dgroup xlim, ylim = get_view_limits(ppanel) - path, fname = os.path.split(dgroup.filename) + path, fname = path_split(dgroup.filename) opts = dict(linewidth=3, ylabel=plotlabels.norm, xlabel=plotlabels.energy, delay_draw=True, @@ -485,7 +485,7 @@ def plot_results(self, event=None, keep_limits=True): dgroup = self.dgroup xlim, ylim = get_view_limits(ppanel) - path, fname = os.path.split(dgroup.filename) + path, fname = path_split(dgroup.filename) wids = self.wids eshift = wids['eshift'].GetValue() @@ -724,7 +724,7 @@ def plot_results(self, event=None, keep_limits=True): xnew, ynew, yerr, e0 = self.data dgroup = self.dgroup xlim, ylim = get_view_limits(ppanel) - path, fname = os.path.split(dgroup.filename) + path, fname = path_split(dgroup.filename) opts = {'delay_draw': True} if self.controller.plot_erange is not None: @@ -924,7 +924,7 @@ def plot_results(self, event=None, keep_limits=True): ppanel = self.controller.get_display(stacked=False).panel xnew, ynew = self.data dgroup = self.dgroup - path, fname = os.path.split(dgroup.filename) + path, fname = path_split(dgroup.filename) opts = {'delay_draw': True} xlim, ylim = get_view_limits(ppanel) @@ -1067,7 +1067,7 @@ def plot_results(self, event=None, keep_limits=True): xnew, ynew = self.data dgroup = self.dgroup xlim, ylim = get_view_limits(ppanel) - path, fname = os.path.split(dgroup.filename) + path, fname = path_split(dgroup.filename) opts = {'delay_draw': True} if self.controller.plot_erange is not None: @@ -1337,7 +1337,7 @@ def plot_results(self, event=None, keep_limits=True): dgroup = self.dgroup - path, fname = os.path.split(dgroup.filename) + path, fname = path_split(dgroup.filename) plotstr = self.wids['plotopts'].GetStringSelection() plottype = DEGLITCH_PLOTS[plotstr] @@ -2047,7 +2047,7 @@ def onImport(self, event=None): if fname not in sel_groups: ignore.append(gname) - fname = unixpath(self.filename) + fname = Path(self.filename).as_posix() if fname.endswith('/'): fname = fname[:-1] lcmd = [f"load_session('{fname}'"] diff --git a/larch/wxxas/xasgui.py b/larch/wxxas/xasgui.py index d4539129e..280f31ce6 100644 --- a/larch/wxxas/xasgui.py +++ b/larch/wxxas/xasgui.py @@ -27,7 +27,7 @@ from larch.io import save_session, read_session from larch.math import index_of from larch.utils import (isotime, time_ago, get_cwd, - is_gzip, uname, unixpath) + is_gzip, uname, path_split) from larch.utils.strutils import (file2groupname, unique_name, common_startstring, asfloat) @@ -1332,7 +1332,7 @@ def onReadDialog(self, event=None): def file_mtime(x): return os.stat(x).st_mtime - self.paths2read = [unixpath(p) for p in self.paths2read] + self.paths2read = [Path(p).as_posix() for p in self.paths2read] self.paths2read = sorted(self.paths2read, key=file_mtime) path = self.paths2read.pop(0) @@ -1510,7 +1510,7 @@ def onReadAthenaProject_OK(self, path, namelist): script = "{group:s} = _prj.{prjgroup:s}" cur_panel = self.nb.GetCurrentPage() cur_panel.skip_plotting = True - parent, spath = os.path.split(path) + parent, spath = path_split(path) labels = [] groups_added = [] @@ -1622,7 +1622,7 @@ def onRead_OK(self, script, path, config): overwrite: whether to overwrite the current datagroup, as when editing a datagroup """ - filedir, spath = os.path.split(path) + filedir, spath = path_split(path) filename = config.get('filename', spath) groupname = config.get('groupname', None) if groupname is None: @@ -1750,7 +1750,7 @@ def install_multichans(config): gname = None for path in self.paths2read: - filedir, spath = os.path.split(path) + filedir, spath = path_split(path) fname = spath if len(multi_chans) > 0: yname = config['array_labels'][config['iy1']] diff --git a/larch/wxxas/xasnorm_panel.py b/larch/wxxas/xasnorm_panel.py index 015df73f5..3b95a41fa 100644 --- a/larch/wxxas/xasnorm_panel.py +++ b/larch/wxxas/xasnorm_panel.py @@ -2,7 +2,6 @@ """ XANES Normalization panel """ -import os import time import wx from copy import deepcopy @@ -11,7 +10,7 @@ from functools import partial from xraydb import guess_edge, atomic_number -from larch.utils import gformat +from larch.utils import gformat, path_split from larch.math import index_of from larch.xafs.xafsutils import guess_energy_units from larch.xafs.pre_edge import find_e0 @@ -1094,7 +1093,7 @@ def plot(self, dgroup, title=None, plot_yarrays=None, yoff=0, popts['grid'] = popts.pop('show_grid') popts['fullbox'] = popts.pop('show_fullbox') - path, fname = os.path.split(dgroup.filename) + path, fname = path_split(dgroup.filename) if 'label' not in popts: popts['label'] = dgroup.plot_ylabel diff --git a/larch/wxxas/xydata_panel.py b/larch/wxxas/xydata_panel.py index e864c9e13..c8cbd9c84 100644 --- a/larch/wxxas/xydata_panel.py +++ b/larch/wxxas/xydata_panel.py @@ -2,7 +2,6 @@ """ uncategorized XY data Panel """ -import os import time import wx import numpy as np @@ -10,7 +9,7 @@ from functools import partial from xraydb import guess_edge, atomic_number -from larch.utils import gformat +from larch.utils import gformat, path_split from larch.math import index_of from larch.xafs.xafsutils import guess_energy_units @@ -515,7 +514,7 @@ def plot(self, dgroup, title=None, plot_yarrays=None, yoff=0, popts['grid'] = popts.pop('show_grid') popts['fullbox'] = popts.pop('show_fullbox') - path, fname = os.path.split(dgroup.filename) + path, fname = path_split(dgroup.filename) if 'label' not in popts: popts['label'] = dgroup.plot_ylabel From 3a3951cce54b7602cbd014205c51b027ec6d5651 Mon Sep 17 00:00:00 2001 From: Matt Newville Date: Mon, 19 Aug 2024 12:08:57 -0500 Subject: [PATCH 06/26] typos, cleanups --- larch/wxxas/feffit_panel.py | 4 ++-- larch/wxxas/xas_controller.py | 1 - larch/wxxas/xasgui.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/larch/wxxas/feffit_panel.py b/larch/wxxas/feffit_panel.py index ce9101145..66ecf83a6 100644 --- a/larch/wxxas/feffit_panel.py +++ b/larch/wxxas/feffit_panel.py @@ -1725,8 +1725,8 @@ def autosave_script(self, text, fname='feffit_script.lar'): if fname is None: fname = 'feffit_script.lar' fullpath = Path(confdir, fname) - fullnane = fullpath.as_posix() - if fulllpath.exists(): + fullname = fullpath.as_posix() + if fullpath.exists(): shutil.copy(fullname, Path(confdir, 'feffit_script_BAK.lar').as_posix()) with open(fullname, 'w', encoding=sys.getdefaultencoding()) as fh: fh.write(text) diff --git a/larch/wxxas/xas_controller.py b/larch/wxxas/xas_controller.py index b0d981e1b..40816c353 100644 --- a/larch/wxxas/xas_controller.py +++ b/larch/wxxas/xas_controller.py @@ -256,7 +256,6 @@ def autosave_session(self): if Path(savefile).exists(): curf = savefile.replace('.larix', '_1.larix' ) shutil.move(savefile, curf) - print("SAVE SESSION ", savefile) save_session(savefile, _larch=self.larch) return savefile diff --git a/larch/wxxas/xasgui.py b/larch/wxxas/xasgui.py index 280f31ce6..4a0fcb126 100644 --- a/larch/wxxas/xasgui.py +++ b/larch/wxxas/xasgui.py @@ -968,7 +968,7 @@ def onLoadSession(self, evt=None, path=None): LoadSessionDialog(self, _session, path, self.controller).Show() self.last_session_read = path - fpath = Path(fpath).absolute() + fpath = Path(path).absolute() fname = fpath.name fdir = fpath.parent.as_posix() if self.controller.chdir_on_fileopen() and len(fdir) > 0: From b338cff9ae317097aef74702ac9c3e122496ab72 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Tue, 20 Aug 2024 15:29:11 -0500 Subject: [PATCH 07/26] update versions for asteval and uncertainties, add bokeh --- setup.cfg | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index c04c642e0..3ee74b92b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,11 +35,11 @@ include_package_data = True python_requires = >=3.8 setup_requires = setuptools_scm install_requires = - asteval>=0.9.31 numpy>=1.22,<2 scipy>=1.7 - uncertainties>=3.1.7 lmfit>=1.2.2 + asteval>=1.0 + uncertainties>=3.2.1 pyshortcuts>=1.9.0 xraydb>=4.5 silx>=0.15.2 @@ -90,10 +90,11 @@ jupyter = jupyter_core>=5.0 jupyter_client jupyter_server - notebook<7.0 + notebook nbformat ipywidgets plotly + bokeh py3dmol doc = From 2e245f32975cd4eecae30a7884a2c50348f440ff Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sat, 24 Aug 2024 15:07:46 -0500 Subject: [PATCH 08/26] remove old epics-spyk code --- larch/epics/spyk.py | 326 -------------------------------------------- 1 file changed, 326 deletions(-) delete mode 100644 larch/epics/spyk.py diff --git a/larch/epics/spyk.py b/larch/epics/spyk.py deleted file mode 100644 index 7d1bb90dc..000000000 --- a/larch/epics/spyk.py +++ /dev/null @@ -1,326 +0,0 @@ -#!/usr/bin/env python -""" -Spyk is a Spec emulator, providing Spec-like scanning functions to Larch - - spec = SpykScan() - spec.add_motors(x='XX:m1', y='XX:m2') - spec.add_detector('XX:scaler1') - spec.set_scanfile(outputfile) - - spec.ascan('x', start, finish, npts, time) - spec.a2scan('x', s1, f1, 'y', s1, f1, npts, time) - spec.a3scan('x', s1, f1, 'y', s2, f2, 'z', s3, f3, npts, time) - spec.mesh('x', s1, f1, npts1, 'y', s2, f2, npts2, time) - - spec.lup('x', start, finish, npts, time) - spec.dscan('x', start, finish, npts, time) - spec.d2scan('x', s1, f1, 'y', s1, f1, npts, time) - spec.d3scan('x', s1, f1, 'y', s2, f2, 'z', s3, f3, npts, time) - -yet to be implemented: - -- th2th tth_start_rel tth_finish_rel intervals time - -- automatic plotting - -- save/read configuration -""" -import os -import sys -import time -import numpy as np -from io import StringIO -from configparser import ConfigParser - - -from larch import Group - -from larch.utils import get_homedir -from larch.io import get_timestamp - -try: - from epics import PV, caget, caput, get_pv, poll - HAS_EPICS = True -except ImportError: - HAS_EPICS = False - -try: - from .larchscan import LarchStepScan - import epicsscan - from epicsscan.positioner import Positioner - from epicsscan.detectors import get_detector, Counter - HAS_EPICSSCAN = True -except ImportError: - HAS_EPICSSCAN = False - - -class SpykConfig(object): - """ - Configuration file (INI format) for Spyk scanning - """ - LEGEND = '# index = label || PVname' - DET_LEGEND = '# index = label || DetectorPV || options ' - SPYK_DIR = '.spyk' - SPYK_INI = 'spyk.ini' - - __sects = ('setup', 'motors', 'detectors', 'extra_pvs', 'counters') - - def __init__(self, filename=None, text=None): - for s in self.__sects: - setattr(self, s, {}) - self._cp = ConfigParser() - self.filename = filename - if self.filename is None: - cfile = self.get_default_configfile() - if (os.path.exists(cfile) and os.path.isfile(cfile)): - self.filename = cfile - if self.filename is not None: - self.Read(self.filename) - - def get_default_configfile(self): - return os.path.join(get_homedir(), self.SPYK_DIR, self.SPYK_INI) - - def Read(self, fname=None): - "read config" - if fname is None: - return - ret = self._cp.read(fname) - if len(ret) == 0: - time.sleep(0.25) - ret = self._cp.read(fname) - self.filename = fname - # process sections - for sect in self.__sects: - if not self._cp.has_section(sect): - continue - thissect = {} - for opt in self._cp.options(sect): - val = self._cp.get(sect, opt) - if '||' in val: - words = [i.strip() for i in val.split('||')] - label = words.pop(0) - if len(words) == 1: - words = words[0] - else: - words = tuple(words) - thissect[label] = words - else: - thissect[opt] = val - setattr(self, sect, thissect) - - def Save(self, fname=None): - "save config file" - if fname is None: - fname = self.get_default_configfile() - path, fn = os.path.split(fname) - if not os.path.exists(path): - os.makedirs(path, mode=755) - - out = ['###Spyke Configuration: %s' % (get_timestamp())] - for sect in self.__sects: - out.append('#-----------------------#\n[%s]' % sect) - if sect == 'setup': - for name, val in self.setup.items(): - out.append("%s = %s" % (name, val)) - elif sect == 'detectors': - out.append(self.DET_LEGEND) - idx = 0 - for key, val in getattr(self, sect).items(): - idx = idx + 1 - if isinstance(val, (list, tuple)): - val = ' || '.join(val) - out.append("%i = %s || %s" % (idx, key, val)) - - else: - out.append(self.LEGEND) - idx = 0 - for key, val in getattr(self, sect).items(): - idx = idx + 1 - if isinstance(val, (list, tuple)): - val = ' || '.join(val) - out.append("%i = %s || %s" % (idx, key, val)) - out.append('#-----------------------#') - with open(fname, 'w') as fh: - fh.write('\n'.join(out)) - - def sections(self): - return self.__sects.keys() - - -class Spyk(Group): - """Spyk is a set of Spec-like scanning tools for Larch""" - def __init__(self, filename='spykscan.001', configfile=None, - auto_increment=True, _larch=None, **kwargs): - Group.__init__(self, **kwargs) - self.motors = {} - self.detectors = [] - self.bare_counters = [] - self._scan = LarchStepScan(filename=filename, - auto_increment=auto_increment, - _larch=_larch) - self.datafilename = filename - if configfile is not None: - self.configfile = configfile - self.read_config(filename=configfile) - self.lup = self.dscan - - def read_config(self, filename=None): - "read Spyk configuration file" - self.config = SpykConfig(filename=filename) - self.configfile = self.config.filename - for label, pvname in self.config.motors.items(): - self.motors[label] = Positioner(pvname, label=label) - - for label, pvname in self.config.counters.items(): - self.bare_counters.append(Counter(pvname, label=label)) - - self.add_extra_pvs(self.config.extra_pvs.items()) - for label, val in self.config.detectors.items(): - opts = {'label': label} - prefix = val[0] - if len(val) > 1: - pairs = [w.strip() for w in val[1].split(',')] - for s in pairs: - skey, sval = s.split('=') - opts[skey.strip()] = sval.strip() - self.add_detector(prefix, **opts) - - def save_config(self, filename=None): - "save Spyk configuration file" - print( 'save spyk config....') - - def add_motors(self, **motors): - """add motors as keyword=value pairs: label=EpicsPVName""" - for label, pvname in motors.items(): - self.motors[label] = Positioner(pvname, label=label) - - def add_counter(self, name, label=None): - # self._scan.add_counter(name, label=label) - self.bare_counters.append(Counter(name, label=label)) - - def add_detector(self, name, kind=None, **kws): - "add detector, giving base name and detector type" - self.detectors.append(get_detector(name, kind=kind, **kws)) - - def add_extra_pvs(self, extra_pvs): - """add extra PVs to be recorded prior to each scan - extra_pvs should be list or tuple of (label, PVname) - """ - self._scan.add_extra_pvs(extra_pvs) - - def set_scanfile(self, filename): - "set file name" - self.datafilename = filename - - def _checkmotors(self, *args): - "check that all args are motor names" - for mname in args: - if mname not in self.motors: - raise Exception("Error: unknown motor name '%s'" % mname) - - def _run(self, dwelltime): - """internal function to start scans""" - self._scan.counters = [] - self._scan.triggers = [] - self._scan.dwelltime = dwelltime - for d in self.detectors: - self._scan.add_detector(d) - d.dwelltime = dwelltime - for c in self.bare_counters: - self._scan.add_counter(c) - - self._scan.run(filename=self.datafilename) - - def ascan(self, motor, start, finish, npts, dtime): - "ascan: absolute scan" - self._checkmotors(motor) - self._scan.positioners = [self.motors[motor]] - self._scan.positioners[0].array = np.linspace(start, finish, npts) - self._run(dtime) - - def dscan(self, motor, start, finish, npts, dtime): - "dscan: relative scan" - self._checkmotors(motor) - current = self.motors[motor].current() - start += current - finish += current - self.ascan(motor, start, finish, npts, dtime) - - def a2scan(self, motor1, start1, finish1, - motor2, start2, finish2, npts, dtime): - "a2scan: absolute scan of 2 motors" - self._checkmotors(motor1, motor2) - self._scan.positioners = [self.motors[motor1], self.motors[motor2]] - self._scan.positioners[0].array = np.linspace(start1, finish1, npts) - self._scan.positioners[1].array = np.linspace(start2, finish2, npts) - self._run(dtime) - - def d2scan(self, motor1, start1, finish1, - motor2, start2, finish2, npts, dtime): - "d2scan: relative scan of 2 motors" - self._checkmotors(motor1, motor2) - current1 = self.motors[motor1].current() - start1 += current1 - finish1 += current1 - - current2 = self.motors[motor2].current() - start2 += current2 - finish2 += current2 - - self.a2scan(motor1, start1, finish1, - motor2, start2, finish2, npts, dtime) - - def a3scan(self, motor1, start1, finish1, motor2, start2, finish2, - motor3, start3, finish3, npts, dtime): - "a3scan: absolute scan of 3 motors" - self._checkmotors(motor1, motor2, motor3) - - self._scan.positioners = [self.motors[motor1], - self.motors[motor2], - self.motors[motor3]] - self._scan.positioners[0].array = np.linspace(start1, finish1, npts) - self._scan.positioners[1].array = np.linspace(start2, finish2, npts) - self._scan.positioners[2].array = np.linspace(start3, finish3, npts) - self._run(dtime) - - def d3scan(self, motor1, start1, finish1, motor2, start2, finish2, - motor3, start3, finish3, npts, dtime): - "d3scan: relative scan of 3 motors" - self._checkmotors(motor1, motor2, motor3) - - current1 = self.motors[motor1].current() - start1 += current1 - finish1 += current1 - - current2 = self.motors[motor2].current() - start2 += current2 - finish2 += current2 - - current3 = self.motors[motor2].current() - start3 += current3 - finish3 += current3 - - self.a3scan(motor1, start1, finish1, - motor2, start2, finish2, - motor3, start3, finish3, npts, dtime) - - def mesh(self, motor1, start1, finish1, npts1, - motor2, start2, finish2, npts2, dtime): - """mesh scan: absolute scan of motor1 at each - position for motor2""" - self._checkmotors(motor1, motor2) - - self._scan.positioners = [self.motors[motor1], self.motors[motor2]] - - fast = npts2* [np.linspace(start1, finish1, npts1)] - slow = [[i]*npts1 for i in np.linspace(start2, finish2, npts2)] - - self._scan.positioners[0].array = np.array(fast).flatten() - self._scan.positioners[1].array = np.array(slow).flatten() - - # set breakpoints to be the end of each row - self._scan.breakpoints = [(i+1)*npts1 - 1 for i in range(npts2-1)] - - # add print statement at end of each row - def show_meshstatus(breakpoint=None): - print('finished row %i of %i' % (1+(breakpoint/npts1), npts2)) - time.sleep(0.25) - self._scan.at_break_methods.append(show_meshstatus) - self._run(dtime) From 044bed5cda8f6ac9330bfecf5fd81826b4c0e0d4 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sat, 24 Aug 2024 15:08:09 -0500 Subject: [PATCH 09/26] cleanup fileutils to use pathlib, remove cruft --- larch/io/__init__.py | 7 +-- larch/io/fileutils.py | 120 +++++++++--------------------------------- 2 files changed, 26 insertions(+), 101 deletions(-) diff --git a/larch/io/__init__.py b/larch/io/__init__.py index 72ee7b2e3..11eb4ecda 100644 --- a/larch/io/__init__.py +++ b/larch/io/__init__.py @@ -7,8 +7,7 @@ ''' from .fileutils import (increment_filename, new_filename, new_dirname, - fix_filename, fix_varname, pathOf, unixpath, - strip_quotes, get_timestamp, asciikeys) + fix_filename, fix_varname, strip_quotes) from .columnfile import (read_ascii, write_ascii, write_group, set_array_labels, guess_filereader, look_for_nans, read_fdmnes, sum_fluor_channels) @@ -55,11 +54,7 @@ def read_tiff(fname, *args, **kws): new_dirname=new_dirname, fix_filename=fix_filename, fix_varname=fix_varname, - pathOf=pathOf, - unixpath=unixpath, strip_quotes=strip_quotes, - get_timestamp=get_timestamp, - asciikeys=asciikeys, read_ascii=read_ascii, look_for_nans=look_for_nans, set_array_labels=set_array_labels, diff --git a/larch/io/fileutils.py b/larch/io/fileutils.py index 89d20d9fc..edc7cf559 100644 --- a/larch/io/fileutils.py +++ b/larch/io/fileutils.py @@ -2,33 +2,12 @@ """ general purpose file utilities """ -import time -import os -import sys - +from pathlib import Path from random import Random -from string import printable -from ..utils.strutils import fix_filename, fix_varname, strip_quotes - -rng = Random() -def asciikeys(adict): - """ensure a dictionary has ASCII keys (and so can be an **kwargs)""" - return dict((k.encode('ascii'), v) for k, v in adict.items()) +alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789' -def get_timestamp(with_t=False): - """return ISO format of current timestamp: - argument - -------- - with_t boolean (False) - - when with_t is True, the returned string - will match 'YYYY-mm-ddTHH:MM:SS' - otherwise 'YYYY-mm-dd HH:MM:SS' - """ - if with_t: - time.strftime('%Y-%m-%dT%H:%M:%S') - return time.strftime('%Y-%m-%d %H:%M:%S') +rng = Random() def random_string(n, rng_seed=None): """ random_string(n) @@ -37,55 +16,10 @@ def random_string(n, rng_seed=None): """ if rng_seed is not None: rng.seed(rng_seed) - s = [printable[rng.randrange(0, 36)] for i in range(n-1)] - s.insert(0, printable[rng.randrange(10, 36)]) + s = [nng.choice(alphanum[:26])] + s.extend([rng.choice(alphanum) for i in range(n-2)]) return ''.join(s) -def pathOf(dir, base, ext, delim='.'): - """return the normalized path name of file created with - a directory, base, extension, and delimiter""" - p = os.path - return p.normpath(p.join(dir,"%s%s%s" % (base, delim, ext))) - -def unixpath(d): - "ensure path uses unix delimiters" - d = d.replace('\\','/') - if not d.endswith('/'): d = '%s/' % d - return d - -def winpath(d): - "ensure path uses windows delimiters" - if d.startswith('//'): d = d[1:] - d = d.replace('/','\\') - if not d.endswith('\\'): d = '%s\\' % d - return d - -def nativepath(d): - "ensure path uses delimiters for current OS" - if os.name == 'nt': - return winpath(d) - return unixpath(d) - -def get_homedir(): - """return home directory, or best approximation - On Windows, this returns the Roaming Profile APPDATA - (use CSIDL_LOCAL_APPDATA for Local Profile) - """ - homedir = '.' - if os.name == 'nt': - # For Windows, ask for parent of Roaming 'Application Data' directory - try: - from win32com.shell import shellcon, shell - homedir = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, 0, 0) - except ImportError: # if win32com is not found - homedir = os.get_environ('HOME', '.') - else: - try: - os.path.expanduser("~") - except: - pass - return homedir - def increment_filename(inpfile, ndigits=3, delim='.'): """ @@ -128,27 +62,26 @@ def increment_filename(inpfile, ndigits=3, delim='.'): 'path/a.004' """ - dirname, filename = os.path.split(inpfile) - base, ext = os.path.splitext(filename) + pinp = Path(inpfile) + dirname, filename = pinp.parent.as_posix(), pinp.name + base, ext = pinp.stem, pinp.suffix if ext == '': ext = '.000' if ext.startswith('.'): - ext = ext[1:] - if ndigits < 3: - ndigits = 3 - form = "%%.%ii" % (ndigits) + ext = ext[1:] + ndigits = max(3, ndigits) def _incr(base, ext): if ext.isdigit(): - ext = form % (int(ext)+1) + ext = f"{(int(ext)+1):0{ndigits}d}" else: found = False if '_' in base: parts = base.split('_') for iw, word in enumerate(parts[::-1]): if word.isdigit(): - parts[len(parts)-iw-1] = form % (int(word)+1) + parts[len(parts)-iw-1] = f"{(int(word)+1):0{ndigits}d}" found = True break base = '_'.join(parts) @@ -156,24 +89,24 @@ def _incr(base, ext): parts = base.split('.') for iw, word in enumerate(parts[::-1]): if word.isdigit(): - parts[len(parts)-iw-1] = form % (int(word)+1) + parts[len(parts)-iw-1] = f"{(int(word)+1):0{ndigits}d}" found = True break base = '.'.join(parts) if not found: - base = "%s_001" % base + base = f"{base}_001" return (base, ext) # increment once base, ext = _incr(base, ext) - fout = pathOf(dirname, base, ext, delim=delim) + fout = Path(dirname,f"{base}{delim}{ext}") # then gaurantee that file does not exist, # continuing to increment if necessary - while (os.path.exists(fout)): + while fout.exists(): base, ext = _incr(base, ext) - fout = pathOf(dirname, base, ext, delim=delim) - return fout + fout = Path(dirname,f"{base}{delim}{ext}") + return fout.as_posix() def new_filename(fname=None, ndigits=3): """ generate a new file name, either based on @@ -184,12 +117,10 @@ def new_filename(fname=None, ndigits=3): # if 'x.001' exists """ if fname is None: - ext = ("%%.%ii" % ndigits) % 1 - fname = "%s.%s" % (random_string(6), ext) + fname = f"{random_string(6)}.{1:0{ndigits}d}" - if os.path.exists(fname): + if Path(fname).exists(): fname = increment_filename(fname, ndigits=ndigits) - return fname def new_dirname(dirname=None, ndigits=3): @@ -201,11 +132,10 @@ def new_dirname(dirname=None, ndigits=3): # if 'x_001' exists """ if dirname is None: - ext = ("%%_%ii" % ndigits) % 1 - dirname = "%s_%s" % (random_string(6), ext) + dirname = f"{random_string(6)}_{1:0{ndigits}d}" dirname = dirname.replace('.', '_') - if os.path.exists(dirname): + if Path(dirname).exists(): dirname = increment_filename(dirname, ndigits=ndigits, delim='_') return dirname @@ -225,9 +155,9 @@ def test_incrementfilename(): for inp,out in tests: tval = increment_filename(inp) if tval != out: - print( "Error converting " , inp) - print( "Got '%s' expected '%s'" % (tval, out)) + print(f"Error converting {inp}") + print(f"Got '{tval}' expected '{out}'") nfail = nfail + 1 else: npass = npass + 1 - print('Passed %i of %i tests' % (npass, npass+nfail)) + print(f'Passed {npass} of {npass+nfail} tests') From 58026cd0c02c30b9b1d62e4971e424b09411acb3 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sat, 24 Aug 2024 18:39:30 -0500 Subject: [PATCH 10/26] work on more uniform imports of fix_varname function --- larch/io/__init__.py | 7 ++----- larch/io/columnfile.py | 3 +-- larch/io/xafs_beamlines.py | 2 +- larch/utils/__init__.py | 14 ++++++++------ larch/wxlib/larchframe.py | 4 ++-- larch/wxxas/xas_controller.py | 3 +-- larch/wxxas/xasgui.py | 6 ++++-- larch/xrmmap/xrm_mapfile.py | 6 +++--- 8 files changed, 22 insertions(+), 23 deletions(-) diff --git a/larch/io/__init__.py b/larch/io/__init__.py index 11eb4ecda..c3d114da3 100644 --- a/larch/io/__init__.py +++ b/larch/io/__init__.py @@ -6,8 +6,8 @@ of scientific data files. ''' -from .fileutils import (increment_filename, new_filename, new_dirname, - fix_filename, fix_varname, strip_quotes) +from .fileutils import (increment_filename, new_filename, new_dirname) + from .columnfile import (read_ascii, write_ascii, write_group, set_array_labels, guess_filereader, look_for_nans, read_fdmnes, sum_fluor_channels) @@ -52,9 +52,6 @@ def read_tiff(fname, *args, **kws): __exports__ = dict(increment_filename=increment_filename, new_filename=new_filename, new_dirname=new_dirname, - fix_filename=fix_filename, - fix_varname=fix_varname, - strip_quotes=strip_quotes, read_ascii=read_ascii, look_for_nans=look_for_nans, set_array_labels=set_array_labels, diff --git a/larch/io/columnfile.py b/larch/io/columnfile.py index 95877c6f0..69b904bc5 100644 --- a/larch/io/columnfile.py +++ b/larch/io/columnfile.py @@ -13,8 +13,7 @@ from math import log10 from larch import Group from larch.symboltable import isgroup -from ..utils import read_textfile, format_exception, gformat -from .fileutils import fix_varname +from ..utils import read_textfile, format_exception, gformat, fix_varname from .xafs_beamlines import guess_beamline nanresult = namedtuple('NanResult', ('file_ok', 'message', 'nan_rows', diff --git a/larch/io/xafs_beamlines.py b/larch/io/xafs_beamlines.py index 21098df4c..522b95d41 100644 --- a/larch/io/xafs_beamlines.py +++ b/larch/io/xafs_beamlines.py @@ -29,7 +29,7 @@ """ import numpy as np -from .fileutils import fix_varname +from ..utils import fix_varname def guess_beamline(header=None): """ diff --git a/larch/utils/__init__.py b/larch/utils/__init__.py index 176dc7cba..ff9580856 100644 --- a/larch/utils/__init__.py +++ b/larch/utils/__init__.py @@ -14,11 +14,10 @@ from .paths import uname, bindir, unixpath, get_homedir, path_split, get_cwd from .debugtimer import debugtimer -from .strutils import (fixName, isValidName, isNumber, bytes2str, - str2bytes, fix_filename, fix_varname, - isLiteralStr, strip_comments, asfloat, - find_delims, version_ge, unique_name, - get_sessionid, strict_ascii) +from .strutils import (fixName, isValidName, isNumber, bytes2str, str2bytes, + fix_filename, fix_varname, strip_quotes, isLiteralStr, + strip_comments, asfloat, find_delims, version_ge, + unique_name, get_sessionid, strict_ascii) from .shellutils import (_more, _parent, ls, cd, cwd, mkdir) @@ -222,4 +221,7 @@ def _larch_init(_larch): copy_group=copy_group, copy_xafs_group=copy_xafs_group, dict2group=dict2group, debugtimer=debugtimer, isotime=isotime, json_dump=json_dump, - json_load=json_load, gformat=gformat) + json_load=json_load, gformat=gformat, + fix_filename=fix_filename, + fix_varname=fix_varname, + strip_quotes=strip_quotes) diff --git a/larch/wxlib/larchframe.py b/larch/wxlib/larchframe.py index 64316038f..58076aa17 100644 --- a/larch/wxlib/larchframe.py +++ b/larch/wxlib/larchframe.py @@ -24,10 +24,10 @@ from . import inputhook from larch.io import (read_ascii, read_xdi, read_gsexdi, - gsescan_group, fix_varname, + gsescan_group, is_athena_project, AthenaProject) from larch.version import make_banner, version_data -from larch.utils import get_cwd +from larch.utils import get_cwd, fix_varname FILE_WILDCARDS = "Data Files(*.0*,*.dat,*.xdi)|*.0*;*.dat;*.xdi|All files (*.*)|*.*" diff --git a/larch/wxxas/xas_controller.py b/larch/wxxas/xas_controller.py index 40816c353..b2d8f1218 100644 --- a/larch/wxxas/xas_controller.py +++ b/larch/wxxas/xas_controller.py @@ -15,7 +15,7 @@ asfloat, get_sessionid, mkdir, unixpath) from larch.wxlib.plotter import last_cursor_pos from larch.wxlib import ExceptionPopup -from larch.io import fix_varname, save_session +from larch.io import save_session from larch.site_config import home_dir, user_larchdir from .config import XASCONF, CONF_FILE, OLDCONF_FILE @@ -351,7 +351,6 @@ def set_focus(self, topwin=None): flist = getattr(topwin, 'filelist', topwin) time.sleep(0.025) topwin.Raise() - flist.SetFocus() def get_group(self, groupname=None): if groupname is None: diff --git a/larch/wxxas/xasgui.py b/larch/wxxas/xasgui.py index 4a0fcb126..b5caae8d5 100644 --- a/larch/wxxas/xasgui.py +++ b/larch/wxxas/xasgui.py @@ -29,7 +29,7 @@ from larch.utils import (isotime, time_ago, get_cwd, is_gzip, uname, path_split) from larch.utils.strutils import (file2groupname, unique_name, - common_startstring, asfloat) + common_startstring, asfloat, fix_varname) from larch.larchlib import read_workdir, save_workdir, read_config, save_config @@ -63,7 +63,7 @@ fit_dialog_window) from larch.io import (read_ascii, read_xdi, read_gsexdi, gsescan_group, - fix_varname, groups2csv, is_athena_project, + groups2csv, is_athena_project, is_larch_session_file, AthenaProject, make_hashkey, is_specfile, open_specfile) from larch.io.xas_data_source import open_xas_source @@ -435,6 +435,8 @@ def version_checker(): xsiz, ysiz = self.GetSize() plotpos = (xpos+xsiz+2, ypos) self.controller.get_display(stacked=False, position=plotpos) + self.Raise() + def createMainPanel(self): display0 = wx.Display(0) diff --git a/larch/xrmmap/xrm_mapfile.py b/larch/xrmmap/xrm_mapfile.py index 8f64bc860..a7c725757 100644 --- a/larch/xrmmap/xrm_mapfile.py +++ b/larch/xrmmap/xrm_mapfile.py @@ -10,10 +10,10 @@ from functools import partial import larch -from larch.utils import isotime -from larch.utils.strutils import fix_varname, fix_filename, bytes2str, version_ge +from larch.utils import (isotime, fix_varname, fix_filename, + bytes2str, version_ge, unixpath) -from larch.io import (unixpath, new_filename, read_xrf_netcdf, +from larch.io import (new_filename, read_xrf_netcdf, read_xsp3_hdf5, read_xrd_netcdf, read_xrd_hdf5) from larch.xrf import MCA, ROI From 65b774909be36968e22e76984c314beca7e6144d Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sun, 25 Aug 2024 21:52:51 -0500 Subject: [PATCH 11/26] larix dialogs: fix name of notebook page --- larch/wxxas/xas_dialogs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/larch/wxxas/xas_dialogs.py b/larch/wxxas/xas_dialogs.py index f533524ce..323c1cbdf 100644 --- a/larch/wxxas/xas_dialogs.py +++ b/larch/wxxas/xas_dialogs.py @@ -437,7 +437,7 @@ def on_apply_one(self, event=None): ensure_en_orig(dgroup) - idx, norm_page = self.parent.get_nbpage('norm') + idx, norm_page = self.parent.get_nbpage('xasnorm') norm_page.wids['energy_shift'].SetValue(eshift) dgroup.energy_shift = eshift @@ -448,7 +448,7 @@ def on_apply_one(self, event=None): def on_apply_sel(self, event=None): eshift = self.wids['eshift'].GetValue() - idx, norm_page = self.parent.get_nbpage('norm') + idx, norm_page = self.parent.get_nbpage('xasnorm') for checked in self.controller.filelist.GetCheckedStrings(): fname = self.controller.file_groups[str(checked)] dgroup = self.controller.get_group(fname) From 95daa49b903dc5114b369f90f4d07a9386bd64a1 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sun, 25 Aug 2024 21:53:46 -0500 Subject: [PATCH 12/26] file dialogs need to return path as string --- larch/wxlib/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/larch/wxlib/__init__.py b/larch/wxlib/__init__.py index 472a5faf8..8d056dfc5 100644 --- a/larch/wxlib/__init__.py +++ b/larch/wxlib/__init__.py @@ -13,6 +13,7 @@ ''' import locale +from pathlib import Path from pyshortcuts import platform import os @@ -52,7 +53,6 @@ def DarwinHLine(parent, size=(700, 3)): from . import larchframe from . import larchfilling from . import readlinetextctrl - from larch.utils import unixpath import wxutils as wxu from wxutils import (set_sizer, pack, SetTip, Font, HLine, Check, MenuItem, Popup, RIGHT, LEFT, CEN , LTEXT, FRAMESTYLE, hms, @@ -69,15 +69,15 @@ def DarwinHLine(parent, size=(700, 3)): def FileOpen(parent, message, **kws): "File Open dialog wrapper." - return unixpath(wxu.FileOpen(parent, message, **kws)) + return Path(wxu.FileOpen(parent, message, **kws)).absolute().as_posix() def FileSave(parent, message, **kws): "File Save dialog" - return unixpath(wxu.FileSave(parent, message, **kws)) + return Path(wxu.FileSave(parent, message, **kws)).absolute().as_posix() def SelectWorkdir(parent, **kws): "prompt for and change into a working directory " - return unixpath(wxu.SelectWorkdir(parent, **kws)) + return Path(wxu.SelectWorkdir(parent, **kws)).absolute().as_posix() from .larchframe import LarchFrame, LarchPanel from .columnframe import ColumnDataFileFrame, EditColumnFrame From 3987b43d1bdcccc9ce678596fcc9d8b8a3b7bc98 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sun, 25 Aug 2024 23:09:25 -0500 Subject: [PATCH 13/26] better support for saving/restoring energy shifts in Athena Project files --- larch/io/athena_project.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/larch/io/athena_project.py b/larch/io/athena_project.py index cf850e8e3..ec6b0f40b 100644 --- a/larch/io/athena_project.py +++ b/larch/io/athena_project.py @@ -3,7 +3,6 @@ Code to read and write Athena Project files """ - import io import sys import time @@ -179,6 +178,7 @@ def make_athena_args(group, hashkey=None, **kws): args['bkg_spl2e'] = '%.5f' % emax args['bkg_spl1'] = '0' args['bkg_spl2'] = '%.5f' % etok(emax) + args['bkg_eshift'] = getattr(group, 'energy_shift', 0.0) autobk_details = getattr(group, 'autobk_details', None) autobk_args = getattr(autobk_details, 'call_args', None) @@ -632,6 +632,8 @@ def add_group(self, group, signal=None): from larch.xafs import pre_edge x = athena_array(group, 'energy') + if hasattr(group, 'energy_orig'): + x = athena_array(group, 'energy_orig') yname = None for _name in ('mu', 'mutrans', 'mufluor'): if hasattr(group, _name): @@ -809,9 +811,7 @@ def read(self, filename=None, match=None, do_preedge=True, do_bkg=False, if is_xmu and (do_preedge or do_bkg): pars = clean_bkg_params(this.athena_params.bkg) - eshift = getattr(this.athena_params.bkg, 'eshift', None) - if eshift is not None: - this.energy = this.energy + eshift + this.energy_shift = getattr(this.athena_params.bkg, 'eshift', 0.) pre_edge(this, e0=float(pars.e0), pre1=float(pars.pre1), pre2=float(pars.pre2), norm1=float(pars.nor1), norm2=float(pars.nor2), From 077d8a18902ad82bc9fb80f1e12f049f18864d8e Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sun, 25 Aug 2024 23:11:38 -0500 Subject: [PATCH 14/26] some more Path-as-string fixes --- larch/wxxas/xasgui.py | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/larch/wxxas/xasgui.py b/larch/wxxas/xasgui.py index b5caae8d5..fc64ee938 100644 --- a/larch/wxxas/xasgui.py +++ b/larch/wxxas/xasgui.py @@ -562,7 +562,7 @@ def get_nbpage(self, name): name = name.lower() out = 0 if name not in LARIX_PANELS: - print("unknown panel : ", name) + print("unknown panel : ", name, LARIX_PANELS) return 0, self.nb.GetPage(0) # print("GET NB PAGE ", name, LARIX_PANELS.get(name, 'gg')) @@ -670,6 +670,18 @@ def createMenus(self): MenuItem(self, file_menu, "&Open Data File\tCtrl+O", "Open Data File", self.onReadDialog) + file_menu.AppendSeparator() + + MenuItem(self, file_menu, "&Read Larch Session\tCtrl+R", + "Read Previously Saved Session", self.onLoadSession) + + MenuItem(self, file_menu, "&Save Larch Session\tCtrl+S", + "Save Session to a File", self.onSaveSession) + + MenuItem(self, file_menu, "&Save Larch Session As ...\tCtrl+A", + "Save Session to a File", self.onSaveSessionAs) + + file_menu.AppendSeparator() MenuItem(self, file_menu, "Save Selected Groups to Athena Project File", "Save Selected Groups to an Athena Project File", @@ -682,13 +694,13 @@ def createMenus(self): MenuItem(self, file_menu, "&Quit\tCtrl+Q", "Quit program", self.onClose) - MenuItem(self, session_menu, "&Read Larch Session\tCtrl+R", + MenuItem(self, session_menu, "&Read Larch Session", "Read Previously Saved Session", self.onLoadSession) - MenuItem(self, session_menu, "&Save Larch Session\tCtrl+S", + MenuItem(self, session_menu, "&Save Larch Session", "Save Session to a File", self.onSaveSession) - MenuItem(self, session_menu, "&Save Larch Session As ...\tCtrl+A", + MenuItem(self, session_menu, "&Save Larch Session As ...", "Save Session to a File", self.onSaveSessionAs) MenuItem(self, session_menu, "Clear Larch Session", @@ -1351,34 +1363,35 @@ def onRead(self, path): fpath = Path(path).absolute() filedir = fpath.parent.as_posix() filename = fpath.name + fullpath = fpath.as_posix() if self.controller.chdir_on_fileopen() and len(filedir) > 0: os.chdir(filedir) self.controller.set_workdir() # check for athena projects - if is_athena_project(path): + if is_athena_project(fullpath): self.show_subframe('athena_import', AthenaImporter, - controller=self.controller, filename=path, + controller=self.controller, filename=fullpath, read_ok_cb=self.onReadAthenaProject_OK) return # check for Spec File - if is_specfile(path): + if is_specfile(fullpath): self.show_subframe('spec_import', SpecfileImporter, - filename=path, + filename=fullpath, _larch=self.larch_buffer.larchshell, config=self.last_spec_config, read_ok_cb=self.onReadSpecfile_OK) return # check for Larch Session File - if is_larch_session_file(path): - self.onLoadSession(path=path) + if is_larch_session_file(fullpath): + self.onLoadSession(path=fullpath) return # default to Column File - self.show_subframe('readfile', ColumnDataFileFrame, filename=path, + self.show_subframe('readfile', ColumnDataFileFrame, filename=fullpath, config=self.last_col_config, _larch=self.larch_buffer.larchshell, read_ok_cb=self.onRead_OK) @@ -1531,6 +1544,8 @@ def onReadAthenaProject_OK(self, path, namelist): jrnl = {'source_desc': f'{spath:s}: {gname:s}'} self.larch.eval(script.format(group=gid, prjgroup=gname)) + + print("## ATHENA -> INSTALL GROUP ", ig, gid, label, path) dgroup = self.install_group(gid, label, process=False, source=path, journal=jrnl) groups_added.append(gid) @@ -1818,8 +1833,6 @@ def install_group(self, groupname, filename=None, source=None, journal=None, dtype = getattr(dgroup, 'datatype', 'xydata') startpage = 'xasnorm' if dtype == 'xas' else 'xydata' ipage, pagepanel = self.get_nbpage(startpage) - # print("START PAGE ", dgroup, dtype, startpage) - # print("..get_nbpage says:: ", startpage, ipage, pagepanel) self.nb.SetSelection(ipage) self.ShowFile(groupname=groupname, filename=filename, process=process, plot=plot) From 9cc0fa2896563a14f4159ec3d367ca20304acb22 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Mon, 26 Aug 2024 10:39:08 -0500 Subject: [PATCH 15/26] purge mixing of larchscan/epicsscan --- larch/epics/larchscan.py | 234 -------------------------------------- larch/epics/xrfcontrol.py | 53 +-------- 2 files changed, 6 insertions(+), 281 deletions(-) delete mode 100644 larch/epics/larchscan.py diff --git a/larch/epics/larchscan.py b/larch/epics/larchscan.py deleted file mode 100644 index ce9edf7a9..000000000 --- a/larch/epics/larchscan.py +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function - -MODDOC = """ -=== Epics Scanning Functions for Larch === - - -This does not used the Epics SScan Record, and the scan is intended to run -as a python application, but many concepts from the Epics SScan Record are -borrowed. Where appropriate, the difference will be noted here. - -A Step Scan consists of the following objects: - a list of Positioners - a list of Triggers - a list of Counters - -Each Positioner will have a list (or numpy array) of position values -corresponding to the steps in the scan. As there is a fixed number of -steps in the scan, the position list for each positioners must have the -same length -- the number of points in the scan. Note that, unlike the -SScan Record, the list of points (not start, stop, step, npts) must be -given. Also note that the number of positioners or number of points is not -limited. - -A Trigger is simply an Epics PV that will start a particular detector, -usually by having 1 written to its field. It is assumed that when the -Epics ca.put() to the trigger completes, the Counters associated with the -triggered detector will be ready to read. - -A Counter is simple a PV whose value should be recorded at every step in -the scan. Any PV can be a Counter, including waveform records. For many -detector types, it is possible to build a specialized class that creates -many counters. - -Because Triggers and Counters are closely associated with detectors, a -Detector is also defined, which simply contains a single Trigger and a list -of Counters, and will cover most real use cases. - -In addition to the core components (Positioners, Triggers, Counters, Detectors), -a Step Scan contains the following objects: - - breakpoints a list of scan indices at which to pause and write data - collected so far to disk. - extra_pvs a list of (description, PV) tuples that are recorded at - the beginning of scan, and at each breakpoint, to be - recorded to disk file as metadata. - pre_scan() method to run prior to scan. - post_scan() method to run after scan. - at_break() method to run at each breakpoint. - -Note that Postioners and Detectors may add their own pieces into extra_pvs, -pre_scan(), post_scan(), and at_break(). - -With these concepts, a Step Scan ends up being a fairly simple loop, going -roughly (that is, skipping error checking) as: - - pos = - det = - run_pre_scan(pos, det) - [p.move_to_start() for p in pos] - record_extra_pvs(pos, det) - for i in range(len(pos[0].array)): - [p.move_to_pos(i) for p in pos] - while not all([p.done for p in pos]): - time.sleep(0.001) - [trig.start() for trig in det.triggers] - while not all([trig.done for trig in det.triggers]): - time.sleep(0.001) - [det.read() for det in det.counters] - - if i in breakpoints: - write_data(pos, det) - record_exrta_pvs(pos, det) - run_at_break(pos, det) - write_data(pos, det) - run_post_scan(pos, det) - -Note that multi-dimensional mesh scans over a rectangular grid is not -explicitly supported, but these can be easily emulated with the more -flexible mechanism of unlimited list of positions and breakpoints. -Non-mesh scans are also possible. - -A step scan can have an Epics SScan Record or StepScan database associated -with it. It will use these for PVs to post data at each point of the scan. -""" -import os, shutil -import time -from threading import Thread -import json -import numpy as np -import random - -from datetime import timedelta - -from larch import Group -from larch.utils import fix_varname - -try: - from epics import PV, caget, caput, get_pv, poll - HAS_EPICS = True -except ImportError: - HAS_EPICS = False - -try: - import epicsscan - from epicsscan import (Counter, Trigger, AreaDetector, get_detector, - ASCIIScanFile, Positioner) - from epicsscan.scandb import ScanDB, InstrumentDB, ScanDBException, ScanDBAbort - from epics.devices.struck import Struck - - HAS_EPICSSCAN = True -except ImportError: - HAS_EPICSSCAN = False - - -SCANDB_NAME = '_scan._scandb' -INSTDB_NAME = '_scan._instdb' -_scandb = None -_instdb = None - -def scan_from_db(scanname, filename='scan.001', _larch=None): - """ - get scan definition from ScanDB by name - """ - global _scandb - if _scandb is None: - print('scan_from_db: need to connect to scandb!') - return - try: - scan = _scandb.make_scan(scanname, larch=_larch) - scan.filename = filename - except ScanDBException: - raise ScanDBException("no scan definition '%s' found" % scanname) - return scan - -def do_scan(scanname, filename='scan.001', nscans=1, comments='', _larch=None): - """do_scan(scanname, filename='scan.001', nscans=1, comments='') - - execute a step scan as defined in Scan database - - Parameters - ---------- - scanname: string, name of scan - filename: string, name of output data file - comments: string, user comments for file - nscans: integer (default 1) number of repeats to make. - - Examples - -------- - do_scan('cu_xafs', 'cu_sample1.001', nscans=3) - - Notes - ------ - 1. The filename will be incremented so that each scan uses a new filename. - """ - global _scandb - if _scandb is None: - print('do_scan: need to connect to scandb!') - return - if nscans is not None: - _scandb.set_info('nscans', nscans) - - scan = scan_from_db(scanname, filename=filename, _larch=_larch) - scan.comments = comments - if scan.scantype == 'slew': - return scan.run(filename=filename, comments=comments) - else: - scans_completed = 0 - nscans = int(_scandb.get_info('nscans')) - abort = _scandb.get_info('request_abort', as_bool=True) - while (scans_completed < nscans) and not abort: - scan.run() - scans_completed += 1 - nscans = int(_scandb.get_info('nscans')) - abort = _scandb.get_info('request_abort', as_bool=True) - return scan - -def get_dbinfo(key, default=None, as_int=False, as_bool=False, - full_row=False, _larch=None, **kws): - """get a value for a keyword in the scan info table, - where most status information is kept. - - Arguments - --------- - key name of data to look up - default (default None) value to return if key is not found - as_int (default False) convert to integer - as_bool (default False) convert to bool - full_row (default False) return full row, not just value - - Notes - ----- - 1. if this key doesn't exist, it will be added with the default - value and the default value will be returned. - 2. the full row will include notes, create_time, modify_time - - """ - global _scandb - if _scandb is None: - print('get_dbinfo: need to connect to scandb!') - return - return _scandb.get_info(key, default=default, full_row=full_row, - as_int=as_int, as_bool=as_bool, **kws) - -def set_dbinfo(key, value, notes=None, _larch=None, **kws): - """ - set a value for a keyword in the scan info table. - """ - global _scandb - if _scandb is None: - print('set_dbinfo: need to connect to scandb!') - return - return _scandb.set_info(key, value, notes=notes) - -def connect_scandb(scandb=None, dbname=None, _larch=None, **kwargs): - from epicsscan.scandb import ScanDB, InstrumentDB - global _scandb, _instdb - if _scandb is not None: - return _scandb - if scandb is None: - scandb = ScanDB(dbname=dbname, **kwargs) - - if scandb is not None: - _scandb = scandb - - if _larch is not None: - _larch.symtable.set_symbol(SCANDB_NAME, _scandb) - - if _instdb is None: - _instdb = InstrumentDB(_scandb) - - if _larch is not None: - _larch.symtable.set_symbol(INSTDB_NAME, _instdb) - return _scandb diff --git a/larch/epics/xrfcontrol.py b/larch/epics/xrfcontrol.py index c2e809ac9..d1637c148 100644 --- a/larch/epics/xrfcontrol.py +++ b/larch/epics/xrfcontrol.py @@ -38,13 +38,6 @@ except: pass -HAS_SCANDB = False -try: - from epicsscan import ScanDB - HAS_SCANDB = True -except: - pass - class DetectorSelectDialog(wx.Dialog): """Connect to an Epics MCA detector Can be either XIA xMAP or Quantum XSPress3 @@ -125,7 +118,7 @@ class EpicsXRFDisplayFrame(XRFDisplayFrame): def __init__(self, parent=None, _larch=None, prefix=None, det_type='ME-4', ioc_type='Xspress3', nmca=4, - size=(725, 580), environ_file=None, scandb_conn=None, + size=(725, 580), environ_file=None, title='Epics XRF Display', output_title='XRF', **kws): self.det_type = det_type @@ -133,12 +126,9 @@ def __init__(self, parent=None, _larch=None, prefix=None, self.nmca = nmca self.det_fore = 1 self.det_back = 0 - self.scandb = None self.environ = [] if environ_file is not None: self.read_environfile(environ_file) - if HAS_SCANDB and scandb_conn is not None: - self.ConnectScanDB(**scandb_conn) self.onConnectEpics(event=None, prefix=prefix) @@ -177,26 +167,6 @@ def onConnectEpics(self, event=None, prefix=None, **kws): self.connect_to_detector(prefix=self.prefix, ioc_type=self.ioc_type, det_type=self.det_type, nmca=self.nmca) - def ConnectScanDB(self, **kws): - if not HAS_SCANDB: - return - self.scandb = ScanDB(**kws) - if self.scandb is not None: - basedir = self.scandb.get_info('user_folder') - fileroot = self.scandb.get_info('server_fileroot') - basedir = str(basedir) - fileroot = str(fileroot) - if basedir.startswith(fileroot): - basedir = basedir[len(fileroot):] - fullpath = os.path.join(fileroot, basedir) - fullpath = fullpath.replace('\\', '/').replace('//', '/') - curdir = get_cwd() - try: - os.chdir(fullpath) - except: - os.chdir(curdir) - self.scandb.connect_pvs() - def onSaveMCAFile(self, event=None, **kws): tmp = ''' # print('SaveMCA File') @@ -216,23 +186,14 @@ def onSaveMCAFile(self, event=None, **kws): default_file=deffile, wildcard=FILE_WILDCARDS) - environ = [] - if HAS_SCANDB and self.scandb is not None: - c, table = self.scandb.get_table('pvs') - pvrows = self.scandb.query(table).all() - for row in pvrows: - addr = str(row.name) - desc = str(row.notes) - val = self.scandb.pvs[addr].get(as_string=True) - environ.append((addr, val, desc)) - - elif len(self.environ) > 0: + env = [] + if len(self.environ) > 0: for pvname, desc in self.environ: val = caget(pvname, as_string=True) - environ.append((pvname, val, desc)) + env.append((pvname, val, desc)) if outfile is not None: - self.det.save_mcafile(outfile, environ=environ) + self.det.save_mcafile(outfile, environ=env) def onSaveColumnFile(self, event=None, **kws): print( ' EPICS-XRFDisplay onSaveColumnFile not yet implemented ') @@ -679,7 +640,7 @@ def onExit(self, event=None): class EpicsXRFApp(LarchWxApp): def __init__(self, _larch=None, prefix=None, det_type='ME-4', ioc_type='Xspress3', nmca=4, - size=(725, 580), environ_file=None, scandb_conn=None, + size=(725, 580), environ_file=None, title='Epics XRF Display', output_title='XRF', **kws): self.prefix = prefix self.det_type = det_type @@ -687,7 +648,6 @@ def __init__(self, _larch=None, prefix=None, self.nmca = nmca self.size = size self.environ_file = environ_file - self.scandb_conn = scandb_conn self.title = title self.output_title = output_title LarchWxApp.__init__(self, _larch=_larch, **kws) @@ -698,7 +658,6 @@ def createApp(self): ioc_type=self.ioc_type, nmca=self.nmca, size=self.size, environ_file=self.environ_file, - scandb_conn=self.scandb_conn, title=self.title, output_title=self.output_title, _larch=self._larch) From 1e57d5a7acea99968d3d391946948a52494ff06b Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Tue, 27 Aug 2024 10:37:18 -0500 Subject: [PATCH 16/26] removed larchscan --- larch/epics/__init__.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/larch/epics/__init__.py b/larch/epics/__init__.py index a4aeca6f5..7dba23e1f 100644 --- a/larch/epics/__init__.py +++ b/larch/epics/__init__.py @@ -11,7 +11,6 @@ except ImportError: HAS_PYEPICS = False -from . import larchscan def pv_fullname(name): """ make sure an Epics PV name ends with .VAL or .SOMETHING! @@ -104,11 +103,3 @@ def nullfcn(*args, **kws): _larch_builtins = {'_epics': dict(PV=PV, caget=caget, caput=caput, cainifo=cainfo, pv_units=pv_units, pv_fullname=pv_fullname)} - - if larchscan.HAS_EPICSSCAN: - _larch_builtins['_scan'] = dict(scan_from_db=larchscan.scan_from_db, - connect_scandb=larchscan.connect_scandb, - do_scan=larchscan.do_scan, - do_slewscan=larchscan.do_scan, - get_dbinfo=larchscan.get_dbinfo, - set_dbinfo=larchscan.set_dbinfo) From 168df7f182675341275984621d3c2c0ecae64770 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Tue, 27 Aug 2024 10:37:43 -0500 Subject: [PATCH 17/26] fixes for Paths --- larch/wxmap/mapviewer.py | 112 ++++++++++++++++++------------------- larch/wxxrd/XRD2Dviewer.py | 8 +-- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/larch/wxmap/mapviewer.py b/larch/wxmap/mapviewer.py index fca0b7907..7190fb6a3 100644 --- a/larch/wxmap/mapviewer.py +++ b/larch/wxmap/mapviewer.py @@ -14,6 +14,7 @@ from functools import partial from threading import Thread from collections import namedtuple +from pathlib import Path import wx from wx.adv import AboutBox, AboutDialogInfo @@ -46,7 +47,6 @@ from larch.wxxas.xas_dialogs import fit_dialog_window from larch.utils.strutils import bytes2str, version_ge from larch.utils import get_cwd -from larch.io import unixpath from larch.site_config import icondir from larch.version import check_larchversion @@ -384,7 +384,7 @@ def ShowMap(self, xrmfile=None, new=True): x = xrmfile.get_pos(0, mean=True) y = xrmfile.get_pos(1, mean=True) - pref, fname = os.path.split(xrmfile.filename) + fname = Path(xrmfile.filename).name if plt3: map = np.array([r_map/mapx, g_map/mapx, b_map/mapx]) @@ -450,13 +450,13 @@ def ShowCorrel(self, xrmfile=None, new=True): xdet = self.det_choice[0].GetStringSelection() xroi = self.roi_choice[0].GetStringSelection() - xlab = "%s(%s)" % (xroi, xdet) + xlab = f"{xroi}({xdet})" if 'scalar' in xdet.lower(): xlab = xroi ydet = self.det_choice[1].GetStringSelection() yroi = self.roi_choice[1].GetStringSelection() - ylab = "%s(%s)" % (yroi, ydet) + ylab = f"{yroi}({ydet})" if 'scalar' in ydet.lower(): ylab = yroi @@ -466,8 +466,8 @@ def ShowCorrel(self, xrmfile=None, new=True): x = xrmfile.get_pos(0, mean=True) y = xrmfile.get_pos(1, mean=True) - pref, fname = os.path.split(xrmfile.filename) - title ='%s: %s vs. %s' %(fname, ylab, xlab) + fname = Path(xrmfile.filename).name + title = f'{fname}: {ylabl} vs. {xlab}' correl_plot = CorrelatedMapFrame(parent=self.owner, xrmfile=xrmfile) correl_plot.display(map1, map2, name1=xlab, name2=ylab, @@ -480,7 +480,7 @@ def onProcessMap(self, event=None, max_new_rows=None): xrmfile = self.owner.current_file if xrmfile is None: return - pref, fname = os.path.split(xrmfile.filename) + fname = Path(xrmfile.filename).name if max_new_rows is None: max_new_rows = self.mapproc_nrows.GetStringSelection().lower() if max_new_rows.lower() == 'all': @@ -518,9 +518,9 @@ def onShowXRF(self, event=None): owner.show_XRFDisplay() self._mca = owner.current_file.get_mca_rect(ymin, ymax, xmin, xmax, det=detname, dtcorrect=owner.dtcor) - pref, fname = os.path.split(self.owner.current_file.filename) + fname = Path(self.owner.current_file.filename).name self._mca.filename = fname - self._mca.title = "(%d x %d pixels)" % (mx, my) + self._mca.title = f"({mx} x {my} pixels)" self._mca.npixels = my*mx self.owner.message("Plotting Full XRF Spectra (%d x %d) for '%s'" % (mx, my, fname)) @@ -710,9 +710,9 @@ def time_between(d1, d2): xrd_calibration = '' if 'xrd1d' in xrmmap: xrd_calibration = bytes2str(xrmmap['xrd1d'].attrs.get('calfile','')) - if not os.path.exists(xrd_calibration): + if not Path(xrd_calibration).exists(): xrd_calibration = '' - self.wids['XRD Calibration'].SetLabel(os.path.split(xrd_calibration)[-1]) + self.wids['XRD Calibration'].SetLabel(Path(xrd_calibration).name) notes = {} config_grp = ensure_subgroup('config',xrmmap) @@ -1057,8 +1057,8 @@ def set_area_choices(self, xrmmap, show_last=False): def onReport(self, event=None): aname = self._getarea() - path, fname = os.path.split(self.owner.current_file.filename) - deffile = '%s_%s' % (fname, aname) + fname = Path(self.owner.current_file.filename).fname + deffile = f'{fname}_{aname}' deffile = deffile.replace('.', '_') + '.dat' outfile = FileSave(self, 'Save Area XRF Statistics File', default_file=deffile, @@ -1221,14 +1221,14 @@ def _getmca_area(aname): self.owner.show_XRFDisplay() mca_thread.join() - pref, fname = os.path.split(self.owner.current_file.filename) + fname = Path(self.owner.current_file.filename).fname npix = area[()].sum() self._mca.filename = fname self._mca.title = label self._mca.npixels = npix - self.owner.message("Plotting XRF Spectra for area '%s'..." % aname) - self.owner.subframes['xrfdisplay'].add_mca(self._mca, label="%s:%s" % (fname, label), + self.owner.message(f"Plotting XRF Spectra for area '{aname}'...") + self.owner.subframes['xrfdisplay'].add_mca(self._mca, label=f"{fname}:{label}", plot=not as_mca2) if as_mca2: self.owner.subframes['xrfdisplay'].swap_mcas() @@ -1259,14 +1259,14 @@ def onXRD(self, event=None, save=False, show=False, return ponifile = bytes2str(xrmfile.xrmmap['xrd1d'].attrs.get('calfile','')) - ponifile = ponifile if os.path.exists(ponifile) else None + ponifile = ponifile if Path(ponifile).exists() else None if show: - self.owner.message('Plotting XRD pattern for \'%s\'...' % title) + self.owner.message(f"Plotting XRD pattern for '{title}'") if save: - self.owner.message('Saving XRD pattern for \'%s\'...' % title) - path,stem = os.path.split(self.owner.current_file.filename) - stem = '%s_%s' % (stem,title) + self.owner.message(f"Saving XRD pattern for '{title}'") + stem = Path(self.owner.current_file.filename).name + stem = f"{stem}_{title}" kwargs = dict(filename=self.owner.current_file.filename, npixels=area[()].sum(), @@ -1277,7 +1277,7 @@ def onXRD(self, event=None, save=False, show=False, self._xrd = xrmfile.get_xrd1d_area(aname, **kwargs) if show: - label = '%s: %s' % (os.path.split(self._xrd.filename)[-1], title) + label = f'{Path(self._xrd.filename).name}: {title}' self.owner.display_xrd1d(self._xrd.data1D, self._xrd.q, self._xrd.energy, label=label) if save: @@ -1309,7 +1309,7 @@ def onXRD(self, event=None, save=False, show=False, print("no 2D XRD Data") return - label = '%s: %s' % (os.path.split(_xrd.filename)[-1], title) + label = f'{Path(_xrd.filename).name}: {title}' self.owner.display_2Dxrd(_xrd.data2D, label=label, xrmfile=xrmfile) wildcards = '2D XRD file (*.tiff)|*.tif;*.tiff;*.edf|All files (*.*)|*.*' fname = xrmfile.filename + '_' + aname @@ -1319,7 +1319,7 @@ def onXRD(self, event=None, save=False, show=False, wildcard=wildcards, style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: - filename = os.path.abspath(dlg.GetPath().replace('\\', '/')) + filename = Path(dlg.GetPath()).absolute().as_posix() _xrd.save_2D(file=filename, verbose=True) dlg.Destroy() @@ -1432,7 +1432,7 @@ def createMainPanel(self): sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(splitter, 1, wx.GROW|wx.ALL, 5) pack(self, sizer) - fico = os.path.join(icondir, XRF_ICON_FILE) + fico = Path(icondir, XRF_ICON_FILE).absolute().as_posix() try: self.SetIcon(wx.Icon(fico, wx.BITMAP_TYPE_ICO)) except: @@ -1520,7 +1520,7 @@ def lassoHandler(self, mask=None, xrmfile=None, xoff=0, yoff=0, self.show_XRFDisplay() mca_thread.join() if hasattr(self, 'sel_mca'): - path, fname = os.path.split(xrmfile.filename) + fname = Path(xrmfile.filename).name aname = self.sel_mca.areaname if self.sel_mca.npixels is None: try: @@ -1764,11 +1764,11 @@ def display_2Dxrd(self, map, label='image 0', xrmfile=None, flip=True): ''' xrmfile = self.current_file ponifile = bytes2str(xrmfile.xrmmap['xrd1d'].attrs.get('calfile','')) - if len(ponifile) < 2 or not os.path.exists(ponifile): - t_ponifile = os.path.join(xrmfile.folder, 'XRD.poni') - if os.path.exists(t_ponifile): - ponifile = t_ponifile - if os.path.exists(ponifile): + if len(ponifile) < 2 or not Path(ponifile).exists(): + t_ponifile = Path(xrmfile.folder, 'XRD.poni') + if t_ponifile.exists(): + ponifile = t_ponifile.as_posix() + if Path(ponifile).exists(): self.current_file.xrmmap['xrd1d'].attrs['calfile'] = ponifile self.show_XRD2D() @@ -1790,10 +1790,10 @@ def display_xrd1d(self, counts, q, energy, label='dataset 0', xrmfile=None): ponidata = json.loads(bytes2str(xrmfile.xrmmap['xrd1d'].attrs.get('caldata','{}'))) if 'rot1' not in ponidata: # invalid poni data ponifile = bytes2str(xrmfile.xrmmap['xrd1d'].attrs.get('calfile','')) - if len(ponifile) < 2 or not os.path.exists(ponifile): - t_ponifile = os.path.join(xrmfile.folder, 'XRD.poni') - if os.path.exists(t_ponifile): - ponifile = t_ponifile + if len(ponifile) < 2 or not Path(ponifile).exists(): + t_ponifile = Path(xrmfile.folder, 'XRD.poni').absolute() + if t_ponifile.exists(): + ponifile = t_ponifile.as_posix() if len(ponifile) > 1: ponidata = read_poni(ponifile) if 'rot1' in ponidata: @@ -1914,11 +1914,11 @@ def onFolderSelect(self, evt=None): style=wx.DD_DIR_MUST_EXIST|wx.DD_DEFAULT_STYLE) if dlg.ShowModal() == wx.ID_OK: - basedir = os.path.abspath(str(dlg.GetPath())) + basedir = Path(dlg.GetPath()).absolute().as_posix() try: if len(basedir) > 0: - os.chdir(unixpath(basedir)) - save_workdir(unixpath(basedir)) + os.chdir(basedir) + save_workdir(basedir) except OSError: print( 'Changed folder failed') pass @@ -2012,10 +2012,10 @@ def onReadFile(self, evt=None): return for path in paths: - parent, fname = os.path.split(path) + fname = Path(path).name read = True if fname in self.filemap: - read = (wx.ID_YES == Popup(self, "Re-read file '%s'?" % path, + read = (wx.ID_YES == Popup(self, f"Re-read file '{path}'?", 'Re-read file?', style=wx.YES_NO)) if read: xrmfile = GSEXRM_MapFile(filename=str(path), scandb=self.scandb) @@ -2036,7 +2036,7 @@ def onReadFolder(self, evt=None): style=wx.DD_DIR_MUST_EXIST|wx.DD_DEFAULT_STYLE) if dlg.ShowModal() == wx.ID_OK: - folder = os.path.abspath(dlg.GetPath()) + folder = Path(dlg.GetPath()).absolute().as_posix() dlg.Destroy() xrmfile = GSEXRM_MapFile(folder=folder, scandb=self.scandb) @@ -2044,7 +2044,7 @@ def onReadFolder(self, evt=None): def add_xrmfile(self, xrmfile): - parent, fname = os.path.split(xrmfile.filename) + fname = Path(xrmfile.filename).name # print("Add XRM File ", fname) # look for group with this name or for next available group for i in range(1000): @@ -2052,7 +2052,7 @@ def add_xrmfile(self, xrmfile): xgroup = getattr(self.datagroups, gname, None) if xgroup is None: break - gpar, gfname = os.path.split(xgroup.filename) + gfname = Path(xgroup.filename).name if gfname == fname: break @@ -2146,8 +2146,8 @@ def add1DXRDFile(self, event=None): path = dlg.GetPath().replace('\\', '/') dlg.Destroy() - if read and os.path.exists(path): - time.sleep(1) ## will hopefully allow time for dialog window to close + if read and Path(path).exists(): + time.sleep(1) self.current_file.read_xrd1D_ROIFile(path) def add1DXRD(self, event=None): @@ -2156,11 +2156,11 @@ def add1DXRD(self, event=None): xrd1Dgrp = ensure_subgroup('xrd1d',self.current_file.xrmmap) poni_path = bytes2str(xrd1Dgrp.attrs.get('calfile','')) - if not os.path.exists(poni_path): + if not Path(poni_path).exists(): self.openPONI() poni_path = bytes2str(xrd1Dgrp.attrs.get('calfile','')) - if os.path.exists(poni_path): + if Path(poni_path).exists(): self.current_file.add_xrd1d() def onShow1DXRD(self, event=None): @@ -2203,7 +2203,7 @@ def onWatchFiles(self, event=None): def onFileWatchTimer(self, event=None): if self.current_file is not None and len(self.files_in_progress) == 0: if self.current_file.folder_has_newdata(): - path, fname = os.path.split(self.current_file.filename) + fname = Path(self.current_file.filename).name self.process_file(fname, max_new_rows=1e6) def process_file(self, filename, max_new_rows=None, on_complete=None): @@ -2260,8 +2260,8 @@ def onTimer(self, event=None): self.htimer.Stop() self.h5convert_thread.join() self.files_in_progress = [] - self.message('MapViewer processing %s: complete!' % fname) - _path, _fname = os.path.split(fname) + self.message(f'MapViewer processing {fname}: complete!') + _fname = Path(fname).name if _fname in self.filemap: cfile = self.current_file = self.filemap[_fname] ny, nx = cfile.get_shape() @@ -2340,14 +2340,14 @@ def __init__(self): def checkOK(self,event=None): - if os.path.exists(self.PoniInfo[1].GetValue()): + if Path(self.PoniInfo[1].GetValue()).exists(): self.FindWindowById(wx.ID_OK).Enable() else: self.FindWindowById(wx.ID_OK).Disable() def onBROWSEponi(self,event=None): wildcards = 'XRD calibration file (*.poni)|*.poni|All files (*.*)|*.*' - if os.path.exists(self.PoniInfo[1].GetValue()): + if Path(self.PoniInfo[1].GetValue()).exists(): dfltDIR = self.PoniInfo[1].GetValue() else: dfltDIR = get_cwd() @@ -2514,8 +2514,8 @@ class OpenMapFolder(wx.Dialog): def __init__(self, folder): """Constructor""" self.folder = folder - pref, f = os.path.split(folder) - title = "Read XRM Map Folder: %s" % f + f = Path(folder).name + title = f"Read XRM Map Folder: {f}" wx.Dialog.__init__(self, None, title=title, size=(475, 750)) @@ -2686,7 +2686,7 @@ def __init__(self, folder): self.info[0].SetValue(FACILITY) self.info[1].SetValue(BEAMLINE) - for line in open(os.path.join(self.folder, 'Scan.ini'), 'r'): + for line in open(Path(self.folder, 'Scan.ini'), 'r'): if line.split()[0] == 'basedir': npath = line.split()[-1].replace('\\', '/').split('/') cycle, usr = npath[-2], npath[-1] @@ -2728,7 +2728,7 @@ def onBROWSEfile(self,event=None,i=1): else: ## elif i == 1: wldcd = 'XRD calibration file (*.poni)|*.poni|All files (*.*)|*.*' - if os.path.exists(self.XRDInfo[i].GetValue()): + if Path(self.XRDInfo[i].GetValue()).exists(): dfltDIR = self.XRDInfo[i].GetValue() else: dfltDIR = get_cwd() diff --git a/larch/wxxrd/XRD2Dviewer.py b/larch/wxxrd/XRD2Dviewer.py index 87b3c389a..4afce557a 100644 --- a/larch/wxxrd/XRD2Dviewer.py +++ b/larch/wxxrd/XRD2Dviewer.py @@ -30,7 +30,7 @@ from larch.utils.strutils import bytes2str from larch.utils import get_cwd from larch.wxlib import LarchWxApp -from larch.io import tifffile, unixpath +from larch.io import tifffile from larch.xrd import (integrate_xrd,E_from_lambda,xrd1d,read_lambda, calc_cake,twth_from_q,twth_from_d, return_ai,twth_from_xy,q_from_xy,eta_from_xy) @@ -908,11 +908,11 @@ def onFolderSelect(self, evt=None): style=style) if dlg.ShowModal() == wx.ID_OK: - basedir = os.path.abspath(str(dlg.GetPath())) + basedir = Path(dlg.GetPath()).absolute().as_posix() try: if len(basedir) > 0: - os.chdir(unixpath(basedir)) - save_workdir(unixpath(basedir)) + os.chdir(basedir) + save_workdir(basedir) except OSError: print( 'Changed folder failed') pass From 4a9b57ba7aa8030bad98970f545ceaacfeb49b5c Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Wed, 28 Aug 2024 12:59:30 -0500 Subject: [PATCH 18/26] more path fixes for mapviewer --- larch/wxmap/mapviewer.py | 4 +++- larch/xrmmap/xrm_mapfile.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/larch/wxmap/mapviewer.py b/larch/wxmap/mapviewer.py index 7190fb6a3..2d122d296 100644 --- a/larch/wxmap/mapviewer.py +++ b/larch/wxmap/mapviewer.py @@ -2044,7 +2044,9 @@ def onReadFolder(self, evt=None): def add_xrmfile(self, xrmfile): - fname = Path(xrmfile.filename).name + fpath = Path(xrmfile.filename) + fname = fpath.name + parent = fpath.parent.as_posix() # print("Add XRM File ", fname) # look for group with this name or for next available group for i in range(1000): diff --git a/larch/xrmmap/xrm_mapfile.py b/larch/xrmmap/xrm_mapfile.py index a7c725757..c01001aea 100644 --- a/larch/xrmmap/xrm_mapfile.py +++ b/larch/xrmmap/xrm_mapfile.py @@ -4,6 +4,7 @@ import time import h5py import numpy as np +from pathlib import Path import scipy.stats as stats import json import multiprocessing as mp From f8c03edfecf3af420b6f440abd0d639b55bb1c13 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Wed, 28 Aug 2024 13:09:43 -0500 Subject: [PATCH 19/26] more path fixes for mapviewer --- larch/xrmmap/xrm_mapfile.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/larch/xrmmap/xrm_mapfile.py b/larch/xrmmap/xrm_mapfile.py index c01001aea..f82cf4eab 100644 --- a/larch/xrmmap/xrm_mapfile.py +++ b/larch/xrmmap/xrm_mapfile.py @@ -76,7 +76,7 @@ def strlist(alist): def isGSEXRM_MapFolder(fname): "return whether folder a valid Scan Folder (raw data)" if (fname is None or not Path(fname).exists() or - not Path(fname).isdir()): + not Path(fname).is_dir()): return False flist = os.listdir(fname) for f in ('Master.dat', 'Environ.dat', 'Scan.ini'): @@ -176,8 +176,7 @@ def ensure_subgroup(subgroup, group, dtype='virtual detector'): def toppath(pname, n=4): words = [] for i in range(n): - pname, f = os.path.split(pname) - words.append(f) + words.append(Path(pname).name) return '/'.join(words) @@ -434,7 +433,7 @@ def getFileStatus(self, filename=None, root=None, folder=None): if root not in ('', None): self.root = root # see if file exists: - if not (os.path.exists(filename) and os.path.isfile(filename)): + if not (Path(filename).exists() and Path(filename).is_file()): return # see if file is empty/too small(signifies "read from folder") if os.stat(filename).st_size < 1024: @@ -555,18 +554,20 @@ def add_XRDfiles(self, flip=None, xrdcalfile=None, xrd2dmaskfile=None, if xrdcalfile is not None: self.xrdcalfile = xrdcalfile - if os.path.exists(str(self.xrdcalfile)): - print('Calibration file loaded: %s' % self.xrdcalfile) - xrd1dgrp.attrs['calfile'] = str(self.xrdcalfile) + pcal = Path(self.xrdcalfile).absolute() + if pcal.exists(): + self.xrdcalfile = pcal.as_posix() + print(f'Calibration file loaded: {self.xrdcalfile}') + xrd1dgrp.attrs['calfile'] = self.xrdcalfile self.flip = flip if flip is not None else self.flip if xrd1dbkgdfile is not None: self.xrd1dbkgdfile= xrd1dbkgdfile - if os.path.exists(str(self.xrd1dbkgdfile)): - print('xrd1d background file loaded: %s' % self.xrd1dbkgdfile) - xrd1dgrp.attrs['1Dbkgdfile'] = '%s' % (self.xrd1dbkgdfile) + if Path(Self.xrd1dbkgdfile).exists(): + print(f'xrd1d background file loaded: {self.xrd1dbkgdfile}') + xrd1dgrp.attrs['1Dbkgdfile'] = f'{self.xrd1dbkgdfile}' self.bkgd_xrd1d = read_xrd_data(self.xrd1dbkgdfile)*self.bkgdscale if xrd2dbkgdfile is not None: @@ -3362,7 +3363,7 @@ def del_roi(self, name): def read_xrmmap(filename, root=None, **kws): '''read GSE XRF FastMap data from HDF5 file or raw map folder''' key = 'filename' - if os.path.isdir(filename): + if Path(filename).is_dir(): key = 'folder' kws.update({key: filename, 'root': root}) @@ -3376,7 +3377,7 @@ def process_mapfolder(path, take_ownership=False, **kws): kws['xrdcal'] = kws.pop('poni') except: pass - if os.path.isdir(path) and isGSEXRM_MapFolder(path): + if Path(path).is_dir() and isGSEXRM_MapFolder(path): print( '\n build map for: %s' % path) try: g = GSEXRM_MapFile(folder=path, **kws) From b4208e6aab409d9a23d9bd5b1049da82979b557d Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Wed, 28 Aug 2024 13:11:46 -0500 Subject: [PATCH 20/26] more path fixes for mapviewer --- larch/xrmmap/xrm_mapfile.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/larch/xrmmap/xrm_mapfile.py b/larch/xrmmap/xrm_mapfile.py index f82cf4eab..22b3200dd 100644 --- a/larch/xrmmap/xrm_mapfile.py +++ b/larch/xrmmap/xrm_mapfile.py @@ -554,11 +554,12 @@ def add_XRDfiles(self, flip=None, xrdcalfile=None, xrd2dmaskfile=None, if xrdcalfile is not None: self.xrdcalfile = xrdcalfile - pcal = Path(self.xrdcalfile).absolute() - if pcal.exists(): - self.xrdcalfile = pcal.as_posix() - print(f'Calibration file loaded: {self.xrdcalfile}') - xrd1dgrp.attrs['calfile'] = self.xrdcalfile + if self.xrdcalfile is not None: + pcal = Path(self.xrdcalfile).absolute() + if pcal.exists(): + self.xrdcalfile = pcal.as_posix() + print(f'Calibration file loaded: {self.xrdcalfile}') + xrd1dgrp.attrs['calfile'] = self.xrdcalfile self.flip = flip if flip is not None else self.flip From e2d2889744a4385a0186438845c5d5dde4305ae7 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Wed, 28 Aug 2024 15:26:53 -0500 Subject: [PATCH 21/26] more cleanups --- larch/xrmmap/configfile.py | 4 +--- larch/xrmmap/xrm_mapfile.py | 9 +++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/larch/xrmmap/configfile.py b/larch/xrmmap/configfile.py index 7b6b89990..83ed5ab1f 100644 --- a/larch/xrmmap/configfile.py +++ b/larch/xrmmap/configfile.py @@ -4,8 +4,6 @@ import sys import time from configparser import ConfigParser -from io import StringIO - conf_sects = {'general': {}, 'xps':{'bools':('use_ftp',)}, @@ -76,7 +74,7 @@ def __init__(self, filename=None, conftext=None): conf_found = True break if not conf_found: - self.cp.readfp(StringIO(default_conf)) + self.cp.read_string(default_conf) self._process_data() def Read(self,fname=None): diff --git a/larch/xrmmap/xrm_mapfile.py b/larch/xrmmap/xrm_mapfile.py index 22b3200dd..1f0e4555f 100644 --- a/larch/xrmmap/xrm_mapfile.py +++ b/larch/xrmmap/xrm_mapfile.py @@ -566,10 +566,11 @@ def add_XRDfiles(self, flip=None, xrdcalfile=None, xrd2dmaskfile=None, if xrd1dbkgdfile is not None: self.xrd1dbkgdfile= xrd1dbkgdfile - if Path(Self.xrd1dbkgdfile).exists(): - print(f'xrd1d background file loaded: {self.xrd1dbkgdfile}') - xrd1dgrp.attrs['1Dbkgdfile'] = f'{self.xrd1dbkgdfile}' - self.bkgd_xrd1d = read_xrd_data(self.xrd1dbkgdfile)*self.bkgdscale + if self.xrd1dbkgdfile is not None: + if Path(self.xrd1dbkgdfile).exists(): + print(f'xrd1d background file loaded: {self.xrd1dbkgdfile}') + xrd1dgrp.attrs['1Dbkgdfile'] = f'{self.xrd1dbkgdfile}' + self.bkgd_xrd1d = read_xrd_data(self.xrd1dbkgdfile)*self.bkgdscale if xrd2dbkgdfile is not None: self.xrd2dbkgdfile= xrd2dbkgdfile From edab29177840b36473f69ed3dcd5c504a08637e6 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Wed, 28 Aug 2024 23:03:21 -0500 Subject: [PATCH 22/26] more tweaks --- larch/xrmmap/asciifiles.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/larch/xrmmap/asciifiles.py b/larch/xrmmap/asciifiles.py index dc66cde7f..87dd96001 100644 --- a/larch/xrmmap/asciifiles.py +++ b/larch/xrmmap/asciifiles.py @@ -55,12 +55,16 @@ def parseEnviron(text): return env_desc, env_addr, env_vals def readScanConfig(folder): - sfile = os.path.join(folder, 'Scan.ini') - if not os.path.exists(sfile): - raise IOError('No configuration file found') + sfile = Path(folder, 'Scan.ini') + text = None + if sfile.exists(): + with open(sfile, 'r') as fh: + text = fh.read() + if text is None: + raise IOError('No configuration file found: ', sfile.as_posix()) cp = ConfigParser() - cp.read(sfile) + cp.read_string(text) timestamp = os.stat(sfile).st_mtime scan = {'timestamp': timestamp} for key in cp.sections(): @@ -71,10 +75,11 @@ def readScanConfig(folder): # return scan, general, timestamp return scan -def readROIFile(hfile,xrd=False): - +def readROIFile(hfile, xrd=False): + with open(hfile, 'r') as fh: + text = fh.read() cp = ConfigParser() - cp.read(hfile) + cp.read_string(text) output = [] if xrd: From 7b8a1b4c3abd9983007bc3c9b8964d5eaa2cb6a2 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Tue, 3 Sep 2024 13:38:01 -0500 Subject: [PATCH 23/26] fix typo --- larch/xrmmap/xrm_mapfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/larch/xrmmap/xrm_mapfile.py b/larch/xrmmap/xrm_mapfile.py index 1f0e4555f..cc0a28b0a 100644 --- a/larch/xrmmap/xrm_mapfile.py +++ b/larch/xrmmap/xrm_mapfile.py @@ -2702,7 +2702,7 @@ def _getmca(self, dgroup, counts, name, npixels=None, **kws): _mca.areaname = _mca.title = name fname = Path(self.filename).name _mca.filename = fix_filename(fname) - mca.info = f"Data from File '{self.filename}', detector '{dgroup}', area '{name}'" + _mca.info = f"Data from File '{self.filename}', detector '{dgroup}', area '{name}'" return _mca From 040be852ff7b1bc05aea384f738aaeba7c8d047f Mon Sep 17 00:00:00 2001 From: Matt Newville Date: Wed, 4 Sep 2024 11:44:59 -0500 Subject: [PATCH 24/26] fix some links --- doc/installation.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/installation.rst b/doc/installation.rst index b5205f98a..2bca68a96 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -5,12 +5,11 @@ Downloading and Installation ==================================== .. _Larch Repository (github.com): https://github.com/xraypy/xraylarch -.. _Anaconda Python: https://www.continuum.io/ +.. _Anaconda Python: https://www.anaconda.com/ .. _PyPI: https://pypi.org .. _Conda: https://conda.io .. _Python.org: https://python.org/ -.. _Anaconda Downloads: https://www.continuum.io/downloads -.. _Miniconda Downloads: https://docs.conda.io/en/latest/miniconda.html +.. _Mambaforge Python: https://github.com/conda-forge/miniforge/releases .. _lmfit: https://lmfit.github.io/lmfit-py/ .. _xraydb: https://xraypy.github.io/XrayDB/ .. _Larch Releases (github.com): https://github.com/xraypy/xraylarch/releases @@ -24,7 +23,8 @@ Downloading and Installation .. _Larch for Linux: https://millenia.cars.aps.anl.gov/xraylarch/downloads/xraylarch-2023-10-Linux-x86_64.sh .. _Docs and Examples: https://millenia.cars.aps.anl.gov/xraylarch/downloads/xraylarch-2023-10_docs-examples.zip -.. _Ifeffit Mailing List: https://millenia.cars.aps.anl.gov/mailman/listinfo/ifeffit/ +.. _Ifeffit Mailing List: https://millenia.cars.aps.anl.gov/mailman3/lists/ifeffit.millenia.cars.aps.anl.gov/ + .. _Demeter: https://bruceravel.github.io/demeter/ .. _Larch Github Pages: https://github.com/xraypy/xraylarch .. _Larch Github Issues: https://github.com/xraypy/xraylarch/issues @@ -202,7 +202,7 @@ macOS), and then type:: If this script fails, report it to the `Larch Github Issues`_ (including the error trace and the `GetLarch.log` file). -The scripts will download and install `Mambaforge Python` which uses Anaconda +The scripts will download and install `Mambaforge Python`_ which uses Anaconda Python and the `conda-forge` channel as the basis of an installation that will be essentially identical to the environment installed by the binary installers, that is, the whole environment is stored in a folder called `xraylarch` in your From 9c2448b170efe298b612e6e27e1a0ab3e27ac78d Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sun, 8 Sep 2024 17:28:34 -0500 Subject: [PATCH 25/26] need to add CIF id to CIF list --- larch/wxlib/cif_browser.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/larch/wxlib/cif_browser.py b/larch/wxlib/cif_browser.py index 5b2b0bb00..d9b618952 100644 --- a/larch/wxlib/cif_browser.py +++ b/larch/wxlib/cif_browser.py @@ -410,10 +410,17 @@ def onSearch(self, event=None): mineral = cif.get_mineralname() year = cif.publication.year journal= cif.publication.journalname - label = f'{label}: {mineral}, {year} {journal}' + cid = cif.ams_id + label = f'{label}: {mineral}, {year} {journal} ({cid})' except: label = None if label is not None: + if label in self.cif_selections: + lorig, n = label, 1 + while label in self.cif_selections and n < 10: + n += 1 + label = f'{lorig} (v{n})' + self.cif_selections[label] = cif.ams_id self.ciflist.Append(label) From 4ca134d6291d6fb0a16e3e59af03e1856fca10e3 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sun, 8 Sep 2024 18:00:56 -0500 Subject: [PATCH 26/26] more tweaks to CIF browser --- larch/wxlib/cif_browser.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/larch/wxlib/cif_browser.py b/larch/wxlib/cif_browser.py index d9b618952..ef057448c 100644 --- a/larch/wxlib/cif_browser.py +++ b/larch/wxlib/cif_browser.py @@ -46,7 +46,7 @@ FNB_STYLE = fnb.FNB_NO_X_BUTTON|fnb.FNB_SMART_TABS FNB_STYLE |= fnb.FNB_NO_NAV_BUTTONS|fnb.FNB_NODRAG -MAINSIZE = (1000, 650) +MAINSIZE = (1150, 650) class CIFFrame(wx.Frame): _about = """Larch Crystallographic Information File Browser @@ -112,9 +112,8 @@ def createMainPanel(self): splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) splitter.SetMinimumPaneSize(250) - leftpanel = wx.Panel(splitter) - self.ciflist = EditableListBox(leftpanel, - self.onShowCIF, size=(300,-1)) + leftpanel = wx.Panel(splitter, size=(375, -1)) + self.ciflist = EditableListBox(leftpanel, self.onShowCIF, size=(375, -1)) set_color(self.ciflist, 'list_fg', bg='list_bg') self.cif_selections = {} @@ -124,7 +123,11 @@ def createMainPanel(self): # right hand side rightpanel = scrolled.ScrolledPanel(splitter) - panel = wx.Panel(rightpanel) + panel = wx.Panel(rightpanel, size=(725, -1)) + + self.ciflist.SetMinSize((375, 250)) + rightpanel.SetMinSize((400, 250)) + sizer = wx.GridBagSizer(2, 2) self.title = SimpleText(panel, 'Search American Mineralogical CIF Database:', @@ -132,7 +135,6 @@ def createMainPanel(self): self.title.SetFont(Font(FONTSIZE+2)) wids = self.wids = {} - minlab = SimpleText(panel, ' Mineral Name: ') minhint= SimpleText(panel, ' example: hem* ') wids['mineral'] = wx.TextCtrl(panel, value='', size=(250, -1), @@ -172,7 +174,6 @@ def createMainPanel(self): wids['search'] = Button(panel, 'Search for CIFs', action=self.onSearch) - ir = 0 sizer.Add(self.title, (0, 0), (1, 6), LEFT, 2) @@ -348,10 +349,12 @@ def _swallow_plot_messages(s, panel=0): r_sizer = wx.BoxSizer(wx.VERTICAL) r_sizer.Add(panel, 0, LEFT|wx.GROW|wx.ALL) r_sizer.Add(self.nb, 1, LEFT|wx.GROW, 2) + pack(rightpanel, r_sizer) rightpanel.SetupScrolling() splitter.SplitVertically(leftpanel, rightpanel, 1) + def get_nbpage(self, name): "get nb page by name" name = name.lower() @@ -411,7 +414,7 @@ def onSearch(self, event=None): year = cif.publication.year journal= cif.publication.journalname cid = cif.ams_id - label = f'{label}: {mineral}, {year} {journal} ({cid})' + label = f'{label}: {mineral}, {year} {journal} [{cid}]' except: label = None if label is not None: