forked from IPKM/nmreval
Compare commits
262 Commits
Author | SHA1 | Date | |
---|---|---|---|
9baf1e44d3 | |||
8fb17b22fd | |||
f04cadf780 | |||
ae45ff2e55 | |||
735896e68d | |||
aff9cd31a7 | |||
8772fa9e7a | |||
9c5d91918f | |||
dbb35cdba4 | |||
070509c691 | |||
5ea1ed6d0a | |||
a1691f11a6 | |||
f956c111c2 | |||
cb2bc78a2a | |||
9e9751cd9d | |||
5a8ef8c9c7 | |||
45f5eb9bef | |||
5116622e42 | |||
661ab81d7e | |||
185dd160f1 | |||
7fe564a61e | |||
6339bdc2cc | |||
37a6d789bb | |||
cde794cb0d | |||
2a69147ed4 | |||
902a35b71a | |||
6f76349932 | |||
f9bb466cb3 | |||
a97c31325f | |||
207ee5bffd | |||
0046d04683 | |||
9463ed1e6c | |||
ea62a05bd3 | |||
c5706084bf | |||
04037d6b4d | |||
dedb130163 | |||
6ecc4a4126 | |||
067857eda2 | |||
41d90bb15f | |||
8d994bb9b4 | |||
2cbc7e8d75 | |||
1d22f22901 | |||
bd1a227e4c | |||
03d172bade | |||
e51a02d277 | |||
3af5cb0301 | |||
22f317da8d | |||
24b56cbd2a | |||
5fd52a1a44 | |||
d8e5de6b7a | |||
869901596b | |||
e4dbaf2b91 | |||
dee1271fe1 | |||
b8bab2af7b | |||
a406908a69 | |||
5e55f06723 | |||
e2e52cebde | |||
53c58b2bbb | |||
4e865cd0c6 | |||
311157a01a | |||
f0ee2073ad | |||
d2e63a5ee3 | |||
5a153585ee | |||
d17d0f251e | |||
0b8f4932b2 | |||
5d43ccb05d | |||
88a32ea7fd | |||
7febe55929 | |||
783fe505ba | |||
2fed4bb0bf | |||
ec8fafcc9c | |||
7762758958 | |||
258922772e | |||
e612c607e2 | |||
b27d9b55ff | |||
3475650899 | |||
0eca306ebe | |||
d7eaff5f0b | |||
a5216b1eed | |||
cf1565f7d3 | |||
c601e77cec | |||
63f4f82228 | |||
bae7ee2db6 | |||
dde7b7006d | |||
141e9f810a | |||
b6136bc8ce | |||
7762e299e4 | |||
ca130eaa14 | |||
2b2c6e932d | |||
402994e09b | |||
7a50028202 | |||
becc1a15a9 | |||
13e6112c99 | |||
1ff462c4b1 | |||
0cf5d7eef9 | |||
609f135855 | |||
5db6a9f2c5 | |||
3149a3f958 | |||
3321d85203 | |||
76cd4acfb0 | |||
33afc2ca94 | |||
ac039c1b6d | |||
ef21d7c7f2 | |||
3ed7368bb0 | |||
fbf4246c7e | |||
91afe8224f | |||
ead9751541 | |||
1d9b641f0a | |||
46ca50845b | |||
a310a78fb9 | |||
9756bf958b | |||
8de4a0cbd3 | |||
bc946e1027 | |||
1d9bf600ba | |||
4a04502012 | |||
6bcf42d6a8 | |||
8a96e0472d | |||
567fcfe37e | |||
058551cc72 | |||
6fd3053964 | |||
3ee95a1990 | |||
1a2f178f58 | |||
c39ffb8a51 | |||
a26595695c | |||
c9ea32629d | |||
032ffb72b1 | |||
354d5cbc99 | |||
22f8bc80ed | |||
84d588cf80 | |||
5b146433f3 | |||
1a225b2cd2 | |||
89c640e591 | |||
1703b8d514 | |||
7732544f69 | |||
988d2ccbda | |||
4268bdc5ae | |||
775b5e7e8a | |||
30e148de14 | |||
2499aac7a1 | |||
f934104587 | |||
6e976e1404 | |||
92c29bec2a | |||
9f6d4e0d0c | |||
90bd33a680 | |||
26fea8f69f | |||
25f7ff5616 | |||
bec789318d | |||
edc6af2762 | |||
eb1e657dab | |||
79a424c4bd | |||
f74dd982ba | |||
a5a9561b54 | |||
c8287c2dbf | |||
a8a7e75501 | |||
30750cc4ed | |||
af14793286 | |||
2906c17472 | |||
5590b5cd16 | |||
09e415babf | |||
4e2938b2a2 | |||
685b9d2316 | |||
b91fbf7621 | |||
8d55430246 | |||
8d06240205 | |||
65034d4518 | |||
2adf15104f | |||
cd9c85c12b | |||
84d136dd4c | |||
f60e125487 | |||
1e9a390ae2 | |||
8673e5acdb | |||
66a0e40a23 | |||
a1ab6335c5 | |||
09a2b61160 | |||
681b49a22f | |||
3dcd44c3ee | |||
6e9dd4d45f | |||
888d86d9fe | |||
67d60949b5 | |||
fcaf43b3eb | |||
49101565a3 | |||
255d7c7862 | |||
12dacb73b3 | |||
b127cc15e2 | |||
bc215ce32b | |||
996b0b2ae2 | |||
753cd06dd1 | |||
45d319834b | |||
11ed3c16eb | |||
edf858da29 | |||
e10b85b904 | |||
267554b252 | |||
233cdd9f80 | |||
15c959fd71 | |||
1378cf6ac7 | |||
a964e02612 | |||
a72e4ba833 | |||
75ec462efd | |||
adcd98fc31 | |||
d8cc99cea4 | |||
7671a56b6f | |||
8086dd5276 | |||
9479364a64 | |||
def2a99ed8 | |||
ac4a4d3b8e | |||
dd1c26e285 | |||
eba7869c4d | |||
efd5b34b13 | |||
2d472bd44e | |||
2042148d0f | |||
4a9d50e8a4 | |||
56b588293d | |||
59625c1581 | |||
bb3d5ac58b | |||
48861c3f80 | |||
8e2ebfc765 | |||
924b58beda | |||
bd8a4f16ea | |||
e41c42d573 | |||
c94231f9d9 | |||
99bb196e5c | |||
e5563d55d5 | |||
abe6ca42c7 | |||
40961a89df | |||
29518b9ea0 | |||
3793f67951 | |||
0251dea563 | |||
bc034cf4f4 | |||
3e513a1231 | |||
ead30d127a | |||
3b79c571fb | |||
ee8ea4f2c5 | |||
2c0cdfbd68 | |||
767fa5f6fb | |||
02f8a3bb31 | |||
ffecc9c873 | |||
0ec0021727 | |||
6b71de8265 | |||
43285b4bd5 | |||
2fbaa94109 | |||
ebf3e9635d | |||
79befb50f1 | |||
0adb530dc2 | |||
d7fd8395e5 | |||
75f9bc84f8 | |||
1c98676bee | |||
8a35a5e6cb | |||
df9b302f9d | |||
4e0ff4eddd | |||
de9464ef9f | |||
466a63990d | |||
8690cb222e | |||
28c15ff565 | |||
1601f1455c | |||
b73d6f08b8 | |||
5577adabec | |||
57ecff478b | |||
2229b2905a | |||
60c93f6bc9 | |||
626804783e | |||
a182d55b98 | |||
ee2a91813c |
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ AppDir
|
|||||||
NMReval*.zsync
|
NMReval*.zsync
|
||||||
.idea
|
.idea
|
||||||
*.zs-old
|
*.zs-old
|
||||||
|
docs
|
||||||
|
@ -12,7 +12,9 @@ script:
|
|||||||
# Copy the python application code into the AppDir
|
# Copy the python application code into the AppDir
|
||||||
- cp bin/evaluate.py $TARGET_APPDIR/usr/bin/
|
- cp bin/evaluate.py $TARGET_APPDIR/usr/bin/
|
||||||
- cp -r src/* $TARGET_APPDIR/usr/src/
|
- cp -r src/* $TARGET_APPDIR/usr/src/
|
||||||
- cp src/pkm.vogel.nmreval.desktop $TARGET_APPDIR/usr/share/applications
|
- cp src/resources/pkm.vogel.nmreval.desktop $TARGET_APPDIR/usr/share/applications
|
||||||
|
# set current date as version info
|
||||||
|
- sed -i "s/CURRENT_DATE/$(date +'%Y-%m-%d')/" $TARGET_APPDIR/usr/src/nmreval/version.py
|
||||||
|
|
||||||
|
|
||||||
AppDir:
|
AppDir:
|
||||||
@ -30,35 +32,50 @@ AppDir:
|
|||||||
arch: amd64
|
arch: amd64
|
||||||
allow_unauthenticated: true
|
allow_unauthenticated: true
|
||||||
sources:
|
sources:
|
||||||
- sourceline: 'deb [arch=amd64] http://mirror.infra.pkm/ bullseye main contrib non-free'
|
- sourceline: 'deb [arch=amd64] http://ftp.uni-mainz.de/debian bullseye main contrib non-free'
|
||||||
|
|
||||||
include:
|
include:
|
||||||
# for /usr/bin/env
|
# for /usr/bin/env
|
||||||
- coreutils
|
# - coreutils
|
||||||
- dash
|
# - dash
|
||||||
- zsync
|
# - zsync
|
||||||
- hicolor-icon-theme
|
# - hicolor-icon-theme
|
||||||
- libatlas3-base
|
- libatlas3-base
|
||||||
- python3.9-minimal
|
- gnuplot-nox
|
||||||
|
- python3-minimal
|
||||||
- python3-numpy
|
- python3-numpy
|
||||||
- python3-scipy
|
- python3-scipy
|
||||||
# - python3-matplotlib
|
|
||||||
# - python-matplotlib-data
|
|
||||||
- python3-bsddb3
|
- python3-bsddb3
|
||||||
- python3-h5py
|
- python3-h5py
|
||||||
- python3-pyqt5
|
- python3-pyqt5
|
||||||
- python3-pyqtgraph
|
- python3-pyqtgraph
|
||||||
- python3-requests
|
|
||||||
- python3-urllib3
|
|
||||||
# - python3-tk
|
|
||||||
exclude:
|
exclude:
|
||||||
|
# lots of qt stuff we do not use
|
||||||
|
- libqt5designer5
|
||||||
|
- libqt5help5
|
||||||
|
- libqt5network5
|
||||||
|
- libqt5sql5
|
||||||
|
- libqt5test5
|
||||||
|
- libqt5xml5
|
||||||
|
- qtbase5-dev-tools
|
||||||
|
- pyqt5-dev-tools
|
||||||
- libavahi-client3
|
- libavahi-client3
|
||||||
- libavahi-common-data
|
- libavahi-common-data
|
||||||
- libavahi-common3
|
- libavahi-common3
|
||||||
after_bundle: |
|
- libwacom2
|
||||||
echo "MONSTER SED FOLLOWING...(uncomment if needed for mpl-data)"
|
- libwacom-common
|
||||||
#sed -i s,\'/usr/share/matplotlib/mpl-data\',"f\"\{os.environ.get\('APPDIR'\,'/'\)\}/usr/share/matplotlib/mpl-data\"", ${TARGET_APPDIR}/usr/lib/python3/dist-packages/matplotlib/__init__.py
|
|
||||||
|
files:
|
||||||
|
exclude:
|
||||||
|
- usr/share/man
|
||||||
|
- usr/share/doc/*/README.*
|
||||||
|
- usr/share/doc/*/changelog.*
|
||||||
|
- usr/share/doc/*/NEWS.*
|
||||||
|
- usr/share/doc/*/TODO.*
|
||||||
runtime:
|
runtime:
|
||||||
|
# if needed, apparently replaces hardcoded location with APPDIR location
|
||||||
|
# path_mappings:
|
||||||
|
# - /usr/share/matplotlib/mpl-data:$APPDIR/usr/share/matplotlib/mpl-data
|
||||||
version: "continuous"
|
version: "continuous"
|
||||||
env:
|
env:
|
||||||
PATH: '${APPDIR}/usr/bin:${PATH}'
|
PATH: '${APPDIR}/usr/bin:${PATH}'
|
||||||
@ -87,6 +104,6 @@ AppDir:
|
|||||||
command: ./AppRun
|
command: ./AppRun
|
||||||
|
|
||||||
AppImage:
|
AppImage:
|
||||||
update-information: 'zsync|https://gitea.pkm.physik.tu-darmstadt.de/api/packages/IPKM-Public/generic/NMReval/latest/NMReval-latest-x86_64.Appimage.zsync'
|
update-information: 'zsync|https://gitea.pkm.physik.tu-darmstadt.de/api/packages/IPKM/generic/NMReval/latest/NMReval-latest-x86_64.Appimage.zsync'
|
||||||
sign-key: 976AC9D78688B628B00D4944D319B98C2D6CE5D3
|
sign-key: 976AC9D78688B628B00D4944D319B98C2D6CE5D3
|
||||||
arch: x86_64
|
arch: x86_64
|
||||||
|
13
LICENSE
Normal file
13
LICENSE
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Dominik Demuth.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
Makefile
22
Makefile
@ -5,30 +5,36 @@ PYUIC = pyuic5
|
|||||||
PYRCC = pyrcc5
|
PYRCC = pyrcc5
|
||||||
|
|
||||||
#Directory with ui files
|
#Directory with ui files
|
||||||
RESOURCE_DIR = resources/_ui
|
RESOURCE_DIR = src/resources/_ui
|
||||||
|
|
||||||
#Directory for compiled resources
|
#Directory for compiled resources
|
||||||
COMPILED_DIR = nmreval/gui_qt/_py
|
PYQT_DIR = src/gui_qt/_py
|
||||||
|
|
||||||
#UI files to compile, uses every *.ui found in RESOURCE_DIR
|
#UI files to compile, uses every *.ui found in RESOURCE_DIR
|
||||||
UI_FILES = $(foreach dir, $(RESOURCE_DIR), $(notdir $(wildcard $(dir)/*.ui)))
|
UI_FILES = $(foreach dir, $(RESOURCE_DIR), $(notdir $(wildcard $(dir)/*.ui)))
|
||||||
COMPILED_UI = $(UI_FILES:%.ui=$(COMPILED_DIR)/%.py)
|
PYQT_UI = $(UI_FILES:%.ui=$(PYQT_DIR)/%.py)
|
||||||
|
|
||||||
SVG_FILES = $(foreach dir, $(RCC_DIR), $(notdir $(wildcard $(dir)/*.svg)))
|
SVG_FILES = $(foreach dir, $(RCC_DIR), $(notdir $(wildcard $(dir)/*.svg)))
|
||||||
PNG_FILES = $(SVG_FILES:%.svg=$(RCC_DIR)/%.png)
|
PNG_FILES = $(SVG_FILES:%.svg=$(RCC_DIR)/%.png)
|
||||||
|
|
||||||
all : ui
|
CC = gcc
|
||||||
|
CFLAGS = -O2 -fPIC
|
||||||
|
LDFLAGS = -shared
|
||||||
|
|
||||||
ui : $(COMPILED_UI)
|
C_DIR = src/nmreval/clib
|
||||||
|
|
||||||
|
all : ui compile
|
||||||
|
|
||||||
|
ui : $(PYQT_UI)
|
||||||
|
|
||||||
rcc : $(PNG_FILES)
|
rcc : $(PNG_FILES)
|
||||||
|
|
||||||
|
# only one C file at the moment
|
||||||
|
compile : $(C_DIR)/integrate.c
|
||||||
|
$(CC) $(LDFLAGS) $(CFLAGS) -o $(C_DIR)/integrate.so $<
|
||||||
|
|
||||||
$(COMPILED_DIR)/%.py : $(RESOURCE_DIR)/%.ui
|
$(COMPILED_DIR)/%.py : $(RESOURCE_DIR)/%.ui
|
||||||
$(PYUIC) $< -o $@
|
$(PYUIC) $< -o $@
|
||||||
# replace import of ressource to correct path
|
|
||||||
# @sed -i s/images_rc/nmrevalqt.$(COMPILED_DIR).images_rc/g $@
|
|
||||||
# @sed -i /images_rc/d $@
|
|
||||||
|
|
||||||
$(RCC_DIR)/%.png : $(RCC_DIR)/%.svg
|
$(RCC_DIR)/%.png : $(RCC_DIR)/%.svg
|
||||||
convert -background none $< $@
|
convert -background none $< $@
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import pathlib
|
|
||||||
sys.path.append(str(pathlib.Path(__file__).absolute().parent.parent / 'src'))
|
|
||||||
|
|
||||||
from nmreval.configs import check_for_config
|
|
||||||
|
|
||||||
# does a directory for config stuff exist? create it if not
|
|
||||||
check_for_config()
|
|
||||||
|
|
||||||
# pyqtgraph warns on Mac if QApplication is created when it is imported
|
|
||||||
# import pyqtgraph
|
|
||||||
|
|
||||||
from nmreval.lib.logger import handle_exception
|
|
||||||
sys.excepthook = handle_exception
|
|
||||||
|
|
||||||
from gui_qt import App
|
|
||||||
|
|
||||||
app = App(['Team Rocket FTW!'])
|
|
||||||
|
|
||||||
from gui_qt.main.mainwindow import NMRMainWindow
|
|
||||||
|
|
||||||
mplQt = NMRMainWindow()
|
|
||||||
mplQt.show()
|
|
||||||
|
|
||||||
sys.exit(app.exec())
|
|
@ -1,54 +1,53 @@
|
|||||||
[metadata]
|
[build-system]
|
||||||
name = nmreval
|
requires = ["setuptools", "wheel", "setuptools-scm"]
|
||||||
version = 0.1
|
build-backend = "setuptools.build_meta"
|
||||||
description = Evaluation of data
|
|
||||||
long_description = file: README.md
|
[project]
|
||||||
author = Dominik Demuth
|
name = "nmreval"
|
||||||
author_email = dominik.demuth@physik.tu-darmstadt.de
|
version = "0.1"
|
||||||
install_requires = [
|
description = "Evaluation of NMR and orther data"
|
||||||
'numpy',
|
authors = [
|
||||||
'scipy',
|
{ name = "Dominik Demuth", email = "dominik.demuth@pkm.tu-darmstadt.de" },
|
||||||
'matplotlib',
|
|
||||||
'bsddb3',
|
|
||||||
'pyqtgraph',
|
|
||||||
'pyqt',
|
|
||||||
'h5py',
|
|
||||||
]
|
]
|
||||||
keywords = ['nmr', 'physics', 'science']
|
maintainers = [
|
||||||
|
{ name = "Dominik Demuth", email = "dominik.demuth@pkm.tu-darmstadt.de" },
|
||||||
|
]
|
||||||
|
requires-python = ">=3.7"
|
||||||
classifiers = [
|
classifiers = [
|
||||||
'Development Status :: 3 - Alpha',
|
"Development Status :: 3 - Alpha",
|
||||||
'Intended Audience :: End Users/Desktop',
|
"Intended Audience :: Science/Research",
|
||||||
'Intended Audience :: Science/Research',
|
"Topic :: Scientific/Engineering :: Physics",
|
||||||
'Topic :: Scientific/Engineering :: Physics',
|
"License :: OSI Approved :: BSD License",
|
||||||
'Topic :: Scientific/Engineering :: Visualization',
|
"Programming Language :: Python :: 3",
|
||||||
'Programming Language :: Python :: 3',
|
"Programming Language :: Python :: 3.7",
|
||||||
'Programming Language :: Python :: 3.7',
|
"Programming Language :: Python :: 3.8",
|
||||||
'Programming Language :: Python :: 3.8',
|
"Programming Language :: Python :: 3.9",
|
||||||
'Programming Language :: Python :: 3.9',
|
"Programming Language :: Python :: 3.10",
|
||||||
'Programming Language :: Python :: 3.10',
|
"Programming Language :: Python :: 3.10",
|
||||||
'Programming Language :: Python :: 3 :: only',
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Programming Language :: Python :: 3 :: only",
|
||||||
|
"Operating System :: POSIX :: Linux"
|
||||||
]
|
]
|
||||||
|
keywords = ["nmr", "physics", "science"]
|
||||||
license = { text = "BSD 3-Clause License" }
|
license = { text = "BSD 3-Clause License" }
|
||||||
|
dependencies = [
|
||||||
|
"numpy",
|
||||||
|
"scipy",
|
||||||
|
"h5py",
|
||||||
|
"PyQt5",
|
||||||
|
"pyqtgraph",
|
||||||
|
]
|
||||||
|
|
||||||
[tool.setuptools]
|
[project.optional-dependencies]
|
||||||
include_package_data = true
|
legacy = ["bsddb3"]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Repository = "https://gitea.pkm.physik.tu-darmstadt.de/IPKM/nmreval"
|
||||||
|
Issues = "https://gitea.pkm.physik.tu-darmstadt.de/IPKM/nmreval/issues"
|
||||||
|
|
||||||
[tool.setuptools.packages]
|
[project.gui-scripts]
|
||||||
find = {}
|
nmreval = "gui_qt.cli:main"
|
||||||
scripts = bin/evaluate.py
|
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
include =[
|
where = ["src"]
|
||||||
'nmreval*',
|
|
||||||
'gui_qt*',
|
|
||||||
'resources*',
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.setuptools.package_data]
|
|
||||||
* = *.txt, *.npz, *.png, *.json
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
matplotlib
|
|
||||||
numpy
|
|
||||||
scipy
|
|
||||||
PyQt5
|
|
||||||
h5py
|
|
||||||
pyqtgraph
|
|
||||||
bsddb3
|
|
||||||
requests
|
|
4
setup.py
4
setup.py
@ -1,4 +0,0 @@
|
|||||||
# Always prefer setuptools over distutils
|
|
||||||
from setuptools import setup
|
|
||||||
|
|
||||||
setup()
|
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file '_ui/apod_dialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/apod_dialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.12.3
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
@ -13,62 +14,223 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
|||||||
class Ui_ApodEdit(object):
|
class Ui_ApodEdit(object):
|
||||||
def setupUi(self, ApodEdit):
|
def setupUi(self, ApodEdit):
|
||||||
ApodEdit.setObjectName("ApodEdit")
|
ApodEdit.setObjectName("ApodEdit")
|
||||||
ApodEdit.resize(784, 484)
|
ApodEdit.resize(1144, 655)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(ApodEdit.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(ApodEdit.sizePolicy().hasHeightForWidth())
|
||||||
ApodEdit.setSizePolicy(sizePolicy)
|
ApodEdit.setSizePolicy(sizePolicy)
|
||||||
self.gridLayout = QtWidgets.QGridLayout(ApodEdit)
|
self.gridLayout = QtWidgets.QGridLayout(ApodEdit)
|
||||||
self.gridLayout.setContentsMargins(3, 3, 3, 3)
|
self.gridLayout.setContentsMargins(9, 9, 9, 9)
|
||||||
self.gridLayout.setSpacing(3)
|
self.gridLayout.setSpacing(3)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
self.graphicsView = PlotWidget(ApodEdit)
|
self.freq_graph = NMRPlotWidget(ApodEdit)
|
||||||
|
self.freq_graph.setObjectName("freq_graph")
|
||||||
|
self.gridLayout.addWidget(self.freq_graph, 2, 2, 1, 1)
|
||||||
|
self.time_graph = NMRPlotWidget(ApodEdit)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.time_graph.sizePolicy().hasHeightForWidth())
|
||||||
self.graphicsView.setSizePolicy(sizePolicy)
|
self.time_graph.setSizePolicy(sizePolicy)
|
||||||
self.graphicsView.setObjectName("graphicsView")
|
self.time_graph.setObjectName("time_graph")
|
||||||
self.gridLayout.addWidget(self.graphicsView, 2, 0, 1, 1)
|
self.gridLayout.addWidget(self.time_graph, 2, 1, 1, 1)
|
||||||
self.graphicsView_2 = PlotWidget(ApodEdit)
|
self.widget = QtWidgets.QWidget(ApodEdit)
|
||||||
self.graphicsView_2.setObjectName("graphicsView_2")
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
|
||||||
self.gridLayout.addWidget(self.graphicsView_2, 2, 1, 1, 1)
|
|
||||||
self.apodcombobox = QtWidgets.QComboBox(ApodEdit)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.apodcombobox.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
|
||||||
self.apodcombobox.setSizePolicy(sizePolicy)
|
self.widget.setSizePolicy(sizePolicy)
|
||||||
|
self.widget.setObjectName("widget")
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(self.widget)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
self.baseline_box = QtWidgets.QCheckBox(self.widget)
|
||||||
|
self.baseline_box.setObjectName("baseline_box")
|
||||||
|
self.verticalLayout.addWidget(self.baseline_box)
|
||||||
|
self.shift_box = QtWidgets.QGroupBox(self.widget)
|
||||||
|
self.shift_box.setFlat(True)
|
||||||
|
self.shift_box.setCheckable(True)
|
||||||
|
self.shift_box.setChecked(False)
|
||||||
|
self.shift_box.setObjectName("shift_box")
|
||||||
|
self.gridLayout_4 = QtWidgets.QGridLayout(self.shift_box)
|
||||||
|
self.gridLayout_4.setContentsMargins(3, 3, 3, 3)
|
||||||
|
self.gridLayout_4.setSpacing(3)
|
||||||
|
self.gridLayout_4.setObjectName("gridLayout_4")
|
||||||
|
self.ls_lineedit = QtWidgets.QLineEdit(self.shift_box)
|
||||||
|
self.ls_lineedit.setObjectName("ls_lineedit")
|
||||||
|
self.gridLayout_4.addWidget(self.ls_lineedit, 1, 1, 1, 1)
|
||||||
|
self.ls_spinbox = QtWidgets.QSpinBox(self.shift_box)
|
||||||
|
self.ls_spinbox.setMaximum(999999)
|
||||||
|
self.ls_spinbox.setObjectName("ls_spinbox")
|
||||||
|
self.gridLayout_4.addWidget(self.ls_spinbox, 0, 1, 1, 1)
|
||||||
|
self.ls_combobox = QtWidgets.QComboBox(self.shift_box)
|
||||||
|
self.ls_combobox.setObjectName("ls_combobox")
|
||||||
|
self.ls_combobox.addItem("")
|
||||||
|
self.ls_combobox.addItem("")
|
||||||
|
self.gridLayout_4.addWidget(self.ls_combobox, 0, 0, 2, 1)
|
||||||
|
self.verticalLayout.addWidget(self.shift_box)
|
||||||
|
self.apod_box = QtWidgets.QGroupBox(self.widget)
|
||||||
|
self.apod_box.setFlat(True)
|
||||||
|
self.apod_box.setCheckable(True)
|
||||||
|
self.apod_box.setChecked(False)
|
||||||
|
self.apod_box.setObjectName("apod_box")
|
||||||
|
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.apod_box)
|
||||||
|
self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
|
||||||
|
self.verticalLayout_2.setSpacing(3)
|
||||||
|
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||||
|
self.apodcombobox = QtWidgets.QComboBox(self.apod_box)
|
||||||
self.apodcombobox.setObjectName("apodcombobox")
|
self.apodcombobox.setObjectName("apodcombobox")
|
||||||
self.gridLayout.addWidget(self.apodcombobox, 0, 0, 1, 1)
|
self.verticalLayout_2.addWidget(self.apodcombobox)
|
||||||
|
self.eqn_label = QtWidgets.QLabel(self.apod_box)
|
||||||
|
self.eqn_label.setIndent(3)
|
||||||
|
self.eqn_label.setObjectName("eqn_label")
|
||||||
|
self.verticalLayout_2.addWidget(self.eqn_label)
|
||||||
self.widget_layout = QtWidgets.QHBoxLayout()
|
self.widget_layout = QtWidgets.QHBoxLayout()
|
||||||
self.widget_layout.setContentsMargins(-1, 6, -1, -1)
|
self.widget_layout.setContentsMargins(-1, 6, -1, -1)
|
||||||
self.widget_layout.setSpacing(20)
|
self.widget_layout.setSpacing(20)
|
||||||
self.widget_layout.setObjectName("widget_layout")
|
self.widget_layout.setObjectName("widget_layout")
|
||||||
self.gridLayout.addLayout(self.widget_layout, 1, 0, 1, 2)
|
self.verticalLayout_2.addLayout(self.widget_layout)
|
||||||
|
self.verticalLayout.addWidget(self.apod_box)
|
||||||
|
self.zerofill_box = QtWidgets.QGroupBox(self.widget)
|
||||||
|
self.zerofill_box.setFlat(True)
|
||||||
|
self.zerofill_box.setCheckable(True)
|
||||||
|
self.zerofill_box.setChecked(False)
|
||||||
|
self.zerofill_box.setObjectName("zerofill_box")
|
||||||
|
self.horizontalLayout = QtWidgets.QHBoxLayout(self.zerofill_box)
|
||||||
|
self.horizontalLayout.setContentsMargins(3, 3, 3, 3)
|
||||||
|
self.horizontalLayout.setSpacing(3)
|
||||||
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
|
self.label = QtWidgets.QLabel(self.zerofill_box)
|
||||||
|
self.label.setObjectName("label")
|
||||||
|
self.horizontalLayout.addWidget(self.label)
|
||||||
|
self.zf_spinbox = QtWidgets.QSpinBox(self.zerofill_box)
|
||||||
|
self.zf_spinbox.setMinimum(1)
|
||||||
|
self.zf_spinbox.setMaximum(3)
|
||||||
|
self.zf_spinbox.setObjectName("zf_spinbox")
|
||||||
|
self.horizontalLayout.addWidget(self.zf_spinbox)
|
||||||
|
self.verticalLayout.addWidget(self.zerofill_box)
|
||||||
|
self.phase_box = QtWidgets.QGroupBox(self.widget)
|
||||||
|
self.phase_box.setFlat(True)
|
||||||
|
self.phase_box.setCheckable(True)
|
||||||
|
self.phase_box.setChecked(False)
|
||||||
|
self.phase_box.setObjectName("phase_box")
|
||||||
|
self.gridLayout_2 = QtWidgets.QGridLayout(self.phase_box)
|
||||||
|
self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
|
||||||
|
self.gridLayout_2.setSpacing(3)
|
||||||
|
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||||
|
self.label_3 = QtWidgets.QLabel(self.phase_box)
|
||||||
|
self.label_3.setObjectName("label_3")
|
||||||
|
self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1)
|
||||||
|
self.label_4 = QtWidgets.QLabel(self.phase_box)
|
||||||
|
self.label_4.setObjectName("label_4")
|
||||||
|
self.gridLayout_2.addWidget(self.label_4, 2, 0, 1, 1)
|
||||||
|
self.ph0_spinbox = QtWidgets.QDoubleSpinBox(self.phase_box)
|
||||||
|
self.ph0_spinbox.setWrapping(True)
|
||||||
|
self.ph0_spinbox.setDecimals(1)
|
||||||
|
self.ph0_spinbox.setMinimum(-180.0)
|
||||||
|
self.ph0_spinbox.setMaximum(180.0)
|
||||||
|
self.ph0_spinbox.setSingleStep(0.5)
|
||||||
|
self.ph0_spinbox.setObjectName("ph0_spinbox")
|
||||||
|
self.gridLayout_2.addWidget(self.ph0_spinbox, 0, 1, 1, 1)
|
||||||
|
self.pivot_lineedit = QtWidgets.QLineEdit(self.phase_box)
|
||||||
|
self.pivot_lineedit.setObjectName("pivot_lineedit")
|
||||||
|
self.gridLayout_2.addWidget(self.pivot_lineedit, 2, 1, 1, 1)
|
||||||
|
self.label_2 = QtWidgets.QLabel(self.phase_box)
|
||||||
|
self.label_2.setObjectName("label_2")
|
||||||
|
self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
|
||||||
|
self.ph1_spinbox = QtWidgets.QDoubleSpinBox(self.phase_box)
|
||||||
|
self.ph1_spinbox.setWrapping(True)
|
||||||
|
self.ph1_spinbox.setDecimals(2)
|
||||||
|
self.ph1_spinbox.setMinimum(-720.0)
|
||||||
|
self.ph1_spinbox.setMaximum(720.0)
|
||||||
|
self.ph1_spinbox.setSingleStep(0.05)
|
||||||
|
self.ph1_spinbox.setObjectName("ph1_spinbox")
|
||||||
|
self.gridLayout_2.addWidget(self.ph1_spinbox, 1, 1, 1, 1)
|
||||||
|
self.verticalLayout.addWidget(self.phase_box)
|
||||||
|
self.ft_box = QtWidgets.QGroupBox(self.widget)
|
||||||
|
self.ft_box.setCheckable(True)
|
||||||
|
self.ft_box.setChecked(False)
|
||||||
|
self.ft_box.setObjectName("ft_box")
|
||||||
|
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.ft_box)
|
||||||
|
self.verticalLayout_3.setContentsMargins(3, 3, 3, 3)
|
||||||
|
self.verticalLayout_3.setSpacing(3)
|
||||||
|
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||||
|
self.phase_before_button = QtWidgets.QRadioButton(self.ft_box)
|
||||||
|
self.phase_before_button.setChecked(True)
|
||||||
|
self.phase_before_button.setObjectName("phase_before_button")
|
||||||
|
self.buttonGroup = QtWidgets.QButtonGroup(ApodEdit)
|
||||||
|
self.buttonGroup.setObjectName("buttonGroup")
|
||||||
|
self.buttonGroup.addButton(self.phase_before_button)
|
||||||
|
self.verticalLayout_3.addWidget(self.phase_before_button)
|
||||||
|
self.phase_after_button = QtWidgets.QRadioButton(self.ft_box)
|
||||||
|
self.phase_after_button.setObjectName("phase_after_button")
|
||||||
|
self.buttonGroup.addButton(self.phase_after_button)
|
||||||
|
self.verticalLayout_3.addWidget(self.phase_after_button)
|
||||||
|
self.verticalLayout.addWidget(self.ft_box)
|
||||||
|
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||||
|
self.verticalLayout.addItem(spacerItem)
|
||||||
|
self.gridLayout.addWidget(self.widget, 2, 0, 1, 1)
|
||||||
self.buttonBox = QtWidgets.QDialogButtonBox(ApodEdit)
|
self.buttonBox = QtWidgets.QDialogButtonBox(ApodEdit)
|
||||||
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
|
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||||
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||||
self.buttonBox.setObjectName("buttonBox")
|
self.buttonBox.setObjectName("buttonBox")
|
||||||
self.gridLayout.addWidget(self.buttonBox, 4, 0, 1, 2)
|
self.gridLayout.addWidget(self.buttonBox, 4, 1, 1, 2)
|
||||||
self.eqn_label = QtWidgets.QLabel(ApodEdit)
|
self.log_freq_widget = QtWidgets.QWidget(ApodEdit)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
|
self.log_freq_widget.setObjectName("log_freq_widget")
|
||||||
sizePolicy.setHorizontalStretch(0)
|
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.log_freq_widget)
|
||||||
sizePolicy.setVerticalStretch(0)
|
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
||||||
sizePolicy.setHeightForWidth(self.eqn_label.sizePolicy().hasHeightForWidth())
|
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||||
self.eqn_label.setSizePolicy(sizePolicy)
|
self.horizontalLayout_3.addItem(spacerItem1)
|
||||||
self.eqn_label.setIndent(3)
|
self.logx_freq = QtWidgets.QCheckBox(self.log_freq_widget)
|
||||||
self.eqn_label.setObjectName("eqn_label")
|
self.logx_freq.setObjectName("logx_freq")
|
||||||
self.gridLayout.addWidget(self.eqn_label, 0, 1, 1, 1)
|
self.horizontalLayout_3.addWidget(self.logx_freq)
|
||||||
|
self.logy_freq = QtWidgets.QCheckBox(self.log_freq_widget)
|
||||||
|
self.logy_freq.setObjectName("logy_freq")
|
||||||
|
self.horizontalLayout_3.addWidget(self.logy_freq)
|
||||||
|
self.gridLayout.addWidget(self.log_freq_widget, 3, 2, 1, 1)
|
||||||
|
self.logtime_widget = QtWidgets.QWidget(ApodEdit)
|
||||||
|
self.logtime_widget.setObjectName("logtime_widget")
|
||||||
|
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.logtime_widget)
|
||||||
|
self.horizontalLayout_2.setContentsMargins(-1, 1, -1, -1)
|
||||||
|
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||||
|
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
self.horizontalLayout_2.addItem(spacerItem2)
|
||||||
|
self.logx_time = QtWidgets.QCheckBox(self.logtime_widget)
|
||||||
|
self.logx_time.setObjectName("logx_time")
|
||||||
|
self.horizontalLayout_2.addWidget(self.logx_time)
|
||||||
|
self.logy_time = QtWidgets.QCheckBox(self.logtime_widget)
|
||||||
|
self.logy_time.setObjectName("logy_time")
|
||||||
|
self.horizontalLayout_2.addWidget(self.logy_time)
|
||||||
|
self.gridLayout.addWidget(self.logtime_widget, 3, 1, 1, 1)
|
||||||
|
|
||||||
self.retranslateUi(ApodEdit)
|
self.retranslateUi(ApodEdit)
|
||||||
self.buttonBox.accepted.connect(ApodEdit.accept)
|
self.buttonBox.accepted.connect(ApodEdit.accept) # type: ignore
|
||||||
self.buttonBox.rejected.connect(ApodEdit.close)
|
self.buttonBox.rejected.connect(ApodEdit.close) # type: ignore
|
||||||
QtCore.QMetaObject.connectSlotsByName(ApodEdit)
|
QtCore.QMetaObject.connectSlotsByName(ApodEdit)
|
||||||
|
|
||||||
def retranslateUi(self, ApodEdit):
|
def retranslateUi(self, ApodEdit):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
_translate = QtCore.QCoreApplication.translate
|
||||||
ApodEdit.setWindowTitle(_translate("ApodEdit", "Apodization"))
|
ApodEdit.setWindowTitle(_translate("ApodEdit", "Apodization"))
|
||||||
|
self.baseline_box.setText(_translate("ApodEdit", "Baseline"))
|
||||||
|
self.shift_box.setTitle(_translate("ApodEdit", "Shift"))
|
||||||
|
self.ls_lineedit.setText(_translate("ApodEdit", "0"))
|
||||||
|
self.ls_combobox.setItemText(0, _translate("ApodEdit", "Points"))
|
||||||
|
self.ls_combobox.setItemText(1, _translate("ApodEdit", "Seconds"))
|
||||||
|
self.apod_box.setTitle(_translate("ApodEdit", "Apodization"))
|
||||||
self.eqn_label.setText(_translate("ApodEdit", "TextLabel"))
|
self.eqn_label.setText(_translate("ApodEdit", "TextLabel"))
|
||||||
from pyqtgraph import PlotWidget
|
self.zerofill_box.setTitle(_translate("ApodEdit", "Zero fill"))
|
||||||
|
self.label.setText(_translate("ApodEdit", "Double length"))
|
||||||
|
self.zf_spinbox.setSuffix(_translate("ApodEdit", "x"))
|
||||||
|
self.phase_box.setTitle(_translate("ApodEdit", "Phase correction"))
|
||||||
|
self.label_3.setText(_translate("ApodEdit", "Phase 1"))
|
||||||
|
self.label_4.setText(_translate("ApodEdit", "Pivot"))
|
||||||
|
self.pivot_lineedit.setText(_translate("ApodEdit", "0"))
|
||||||
|
self.label_2.setText(_translate("ApodEdit", "Phase 0"))
|
||||||
|
self.ft_box.setTitle(_translate("ApodEdit", "Fourier transform"))
|
||||||
|
self.phase_before_button.setText(_translate("ApodEdit", "before phase correction"))
|
||||||
|
self.phase_after_button.setText(_translate("ApodEdit", "after phase correction"))
|
||||||
|
self.logx_freq.setText(_translate("ApodEdit", "Log X"))
|
||||||
|
self.logy_freq.setText(_translate("ApodEdit", "Log Y"))
|
||||||
|
self.logx_time.setText(_translate("ApodEdit", "Log X"))
|
||||||
|
self.logy_time.setText(_translate("ApodEdit", "Log Y"))
|
||||||
|
from ..lib.graph_items import NMRPlotWidget
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Form implementation generated from reading ui file 'src/resources/_ui/asciidialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/asciidialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.15.7
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
|||||||
class Ui_ascii_reader(object):
|
class Ui_ascii_reader(object):
|
||||||
def setupUi(self, ascii_reader):
|
def setupUi(self, ascii_reader):
|
||||||
ascii_reader.setObjectName("ascii_reader")
|
ascii_reader.setObjectName("ascii_reader")
|
||||||
ascii_reader.resize(627, 703)
|
ascii_reader.resize(665, 904)
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout(ascii_reader)
|
self.verticalLayout = QtWidgets.QVBoxLayout(ascii_reader)
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
self.tabWidget = QtWidgets.QTabWidget(ascii_reader)
|
self.tabWidget = QtWidgets.QTabWidget(ascii_reader)
|
||||||
@ -28,92 +28,25 @@ class Ui_ascii_reader(object):
|
|||||||
self.header_widget.setObjectName("header_widget")
|
self.header_widget.setObjectName("header_widget")
|
||||||
self.verticalLayout_3.addWidget(self.header_widget)
|
self.verticalLayout_3.addWidget(self.header_widget)
|
||||||
self.groupBox = QtWidgets.QGroupBox(self.tabWidgetPage1)
|
self.groupBox = QtWidgets.QGroupBox(self.tabWidgetPage1)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth())
|
||||||
|
self.groupBox.setSizePolicy(sizePolicy)
|
||||||
self.groupBox.setObjectName("groupBox")
|
self.groupBox.setObjectName("groupBox")
|
||||||
self.gridLayout = QtWidgets.QGridLayout(self.groupBox)
|
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.groupBox)
|
||||||
self.gridLayout.setContentsMargins(3, 3, 3, 3)
|
self.horizontalLayout_4.setContentsMargins(3, 3, 3, 3)
|
||||||
self.gridLayout.setHorizontalSpacing(9)
|
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.verticalLayout_6 = QtWidgets.QVBoxLayout()
|
||||||
self.FID_radioButton = QtWidgets.QRadioButton(self.groupBox)
|
self.verticalLayout_6.setObjectName("verticalLayout_6")
|
||||||
self.FID_radioButton.setAutoExclusive(True)
|
|
||||||
self.FID_radioButton.setObjectName("FID_radioButton")
|
|
||||||
self.buttonGroup = QtWidgets.QButtonGroup(ascii_reader)
|
|
||||||
self.buttonGroup.setObjectName("buttonGroup")
|
|
||||||
self.buttonGroup.addButton(self.FID_radioButton)
|
|
||||||
self.gridLayout.addWidget(self.FID_radioButton, 2, 1, 1, 1)
|
|
||||||
self.x_lineedit = QtWidgets.QLineEdit(self.groupBox)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.x_lineedit.sizePolicy().hasHeightForWidth())
|
|
||||||
self.x_lineedit.setSizePolicy(sizePolicy)
|
|
||||||
self.x_lineedit.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhPreferNumbers)
|
|
||||||
self.x_lineedit.setObjectName("x_lineedit")
|
|
||||||
self.gridLayout.addWidget(self.x_lineedit, 1, 3, 1, 1)
|
|
||||||
self.y_label = QtWidgets.QLabel(self.groupBox)
|
|
||||||
self.y_label.setObjectName("y_label")
|
|
||||||
self.gridLayout.addWidget(self.y_label, 2, 2, 1, 1)
|
|
||||||
self.pts_radioButton = QtWidgets.QRadioButton(self.groupBox)
|
|
||||||
self.pts_radioButton.setChecked(True)
|
|
||||||
self.pts_radioButton.setAutoExclusive(True)
|
|
||||||
self.pts_radioButton.setObjectName("pts_radioButton")
|
|
||||||
self.buttonGroup.addButton(self.pts_radioButton)
|
|
||||||
self.gridLayout.addWidget(self.pts_radioButton, 1, 1, 1, 1)
|
|
||||||
self.deltay_lineEdit = QtWidgets.QLineEdit(self.groupBox)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.deltay_lineEdit.sizePolicy().hasHeightForWidth())
|
|
||||||
self.deltay_lineEdit.setSizePolicy(sizePolicy)
|
|
||||||
self.deltay_lineEdit.setObjectName("deltay_lineEdit")
|
|
||||||
self.gridLayout.addWidget(self.deltay_lineEdit, 3, 3, 1, 1)
|
|
||||||
self.label_5 = QtWidgets.QLabel(self.groupBox)
|
|
||||||
self.label_5.setObjectName("label_5")
|
|
||||||
self.gridLayout.addWidget(self.label_5, 3, 2, 1, 1)
|
|
||||||
self.column_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
self.column_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.column_checkBox.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.column_checkBox.sizePolicy().hasHeightForWidth())
|
||||||
self.column_checkBox.setSizePolicy(sizePolicy)
|
self.column_checkBox.setSizePolicy(sizePolicy)
|
||||||
self.column_checkBox.setObjectName("column_checkBox")
|
self.column_checkBox.setObjectName("column_checkBox")
|
||||||
self.gridLayout.addWidget(self.column_checkBox, 0, 0, 1, 1)
|
self.verticalLayout_6.addWidget(self.column_checkBox)
|
||||||
self.label = QtWidgets.QLabel(self.groupBox)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
|
|
||||||
self.label.setSizePolicy(sizePolicy)
|
|
||||||
self.label.setObjectName("label")
|
|
||||||
self.gridLayout.addWidget(self.label, 0, 1, 1, 1)
|
|
||||||
self.label_7 = QtWidgets.QLabel(self.groupBox)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth())
|
|
||||||
self.label_7.setSizePolicy(sizePolicy)
|
|
||||||
self.label_7.setObjectName("label_7")
|
|
||||||
self.gridLayout.addWidget(self.label_7, 0, 2, 1, 2)
|
|
||||||
self.y_lineedit = QtWidgets.QLineEdit(self.groupBox)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.y_lineedit.sizePolicy().hasHeightForWidth())
|
|
||||||
self.y_lineedit.setSizePolicy(sizePolicy)
|
|
||||||
self.y_lineedit.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhPreferNumbers)
|
|
||||||
self.y_lineedit.setObjectName("y_lineedit")
|
|
||||||
self.gridLayout.addWidget(self.y_lineedit, 2, 3, 1, 1)
|
|
||||||
self.spectrum_radioButton = QtWidgets.QRadioButton(self.groupBox)
|
|
||||||
self.spectrum_radioButton.setObjectName("spectrum_radioButton")
|
|
||||||
self.buttonGroup.addButton(self.spectrum_radioButton)
|
|
||||||
self.gridLayout.addWidget(self.spectrum_radioButton, 3, 1, 1, 1)
|
|
||||||
self.x_label = QtWidgets.QLabel(self.groupBox)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.x_label.sizePolicy().hasHeightForWidth())
|
|
||||||
self.x_label.setSizePolicy(sizePolicy)
|
|
||||||
self.x_label.setObjectName("x_label")
|
|
||||||
self.gridLayout.addWidget(self.x_label, 1, 2, 1, 1)
|
|
||||||
self.line_spinBox = QtWidgets.QSpinBox(self.groupBox)
|
self.line_spinBox = QtWidgets.QSpinBox(self.groupBox)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
@ -122,17 +55,158 @@ class Ui_ascii_reader(object):
|
|||||||
self.line_spinBox.setSizePolicy(sizePolicy)
|
self.line_spinBox.setSizePolicy(sizePolicy)
|
||||||
self.line_spinBox.setMinimum(1)
|
self.line_spinBox.setMinimum(1)
|
||||||
self.line_spinBox.setObjectName("line_spinBox")
|
self.line_spinBox.setObjectName("line_spinBox")
|
||||||
self.gridLayout.addWidget(self.line_spinBox, 1, 0, 1, 1)
|
self.verticalLayout_6.addWidget(self.line_spinBox)
|
||||||
|
self.label_6 = QtWidgets.QLabel(self.groupBox)
|
||||||
|
self.label_6.setObjectName("label_6")
|
||||||
|
self.verticalLayout_6.addWidget(self.label_6)
|
||||||
self.preview_spinBox = QtWidgets.QSpinBox(self.groupBox)
|
self.preview_spinBox = QtWidgets.QSpinBox(self.groupBox)
|
||||||
self.preview_spinBox.setMinimum(1)
|
self.preview_spinBox.setMinimum(1)
|
||||||
self.preview_spinBox.setProperty("value", 10)
|
self.preview_spinBox.setProperty("value", 10)
|
||||||
self.preview_spinBox.setObjectName("preview_spinBox")
|
self.preview_spinBox.setObjectName("preview_spinBox")
|
||||||
self.gridLayout.addWidget(self.preview_spinBox, 3, 0, 1, 1)
|
self.verticalLayout_6.addWidget(self.preview_spinBox)
|
||||||
self.label_6 = QtWidgets.QLabel(self.groupBox)
|
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||||
self.label_6.setObjectName("label_6")
|
self.verticalLayout_6.addItem(spacerItem)
|
||||||
self.gridLayout.addWidget(self.label_6, 2, 0, 1, 1)
|
self.horizontalLayout_4.addLayout(self.verticalLayout_6)
|
||||||
|
self.verticalLayout_5 = QtWidgets.QVBoxLayout()
|
||||||
|
self.verticalLayout_5.setObjectName("verticalLayout_5")
|
||||||
|
self.label = QtWidgets.QLabel(self.groupBox)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
|
||||||
|
self.label.setSizePolicy(sizePolicy)
|
||||||
|
self.label.setObjectName("label")
|
||||||
|
self.verticalLayout_5.addWidget(self.label)
|
||||||
|
self.pts_radioButton = QtWidgets.QRadioButton(self.groupBox)
|
||||||
|
self.pts_radioButton.setChecked(True)
|
||||||
|
self.pts_radioButton.setAutoExclusive(True)
|
||||||
|
self.pts_radioButton.setObjectName("pts_radioButton")
|
||||||
|
self.buttonGroup = QtWidgets.QButtonGroup(ascii_reader)
|
||||||
|
self.buttonGroup.setObjectName("buttonGroup")
|
||||||
|
self.buttonGroup.addButton(self.pts_radioButton)
|
||||||
|
self.verticalLayout_5.addWidget(self.pts_radioButton)
|
||||||
|
self.dsc_radioButton = QtWidgets.QRadioButton(self.groupBox)
|
||||||
|
self.dsc_radioButton.setObjectName("dsc_radioButton")
|
||||||
|
self.buttonGroup.addButton(self.dsc_radioButton)
|
||||||
|
self.verticalLayout_5.addWidget(self.dsc_radioButton)
|
||||||
|
self.FID_radioButton = QtWidgets.QRadioButton(self.groupBox)
|
||||||
|
self.FID_radioButton.setAutoExclusive(True)
|
||||||
|
self.FID_radioButton.setObjectName("FID_radioButton")
|
||||||
|
self.buttonGroup.addButton(self.FID_radioButton)
|
||||||
|
self.verticalLayout_5.addWidget(self.FID_radioButton)
|
||||||
|
self.spectrum_radioButton = QtWidgets.QRadioButton(self.groupBox)
|
||||||
|
self.spectrum_radioButton.setObjectName("spectrum_radioButton")
|
||||||
|
self.buttonGroup.addButton(self.spectrum_radioButton)
|
||||||
|
self.verticalLayout_5.addWidget(self.spectrum_radioButton)
|
||||||
|
self.bds_radioButton = QtWidgets.QRadioButton(self.groupBox)
|
||||||
|
self.bds_radioButton.setObjectName("bds_radioButton")
|
||||||
|
self.buttonGroup.addButton(self.bds_radioButton)
|
||||||
|
self.verticalLayout_5.addWidget(self.bds_radioButton)
|
||||||
|
self.horizontalLayout_4.addLayout(self.verticalLayout_5)
|
||||||
|
self.gridLayout_2 = QtWidgets.QGridLayout()
|
||||||
|
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||||
|
self.deltay_lineEdit = QtWidgets.QLineEdit(self.groupBox)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.deltay_lineEdit.sizePolicy().hasHeightForWidth())
|
||||||
|
self.deltay_lineEdit.setSizePolicy(sizePolicy)
|
||||||
|
self.deltay_lineEdit.setObjectName("deltay_lineEdit")
|
||||||
|
self.gridLayout_2.addWidget(self.deltay_lineEdit, 3, 1, 1, 1)
|
||||||
|
self.label_7 = QtWidgets.QLabel(self.groupBox)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth())
|
||||||
|
self.label_7.setSizePolicy(sizePolicy)
|
||||||
|
self.label_7.setObjectName("label_7")
|
||||||
|
self.gridLayout_2.addWidget(self.label_7, 0, 0, 1, 2)
|
||||||
|
self.y_lineedit = QtWidgets.QLineEdit(self.groupBox)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.y_lineedit.sizePolicy().hasHeightForWidth())
|
||||||
|
self.y_lineedit.setSizePolicy(sizePolicy)
|
||||||
|
self.y_lineedit.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhPreferNumbers)
|
||||||
|
self.y_lineedit.setObjectName("y_lineedit")
|
||||||
|
self.gridLayout_2.addWidget(self.y_lineedit, 2, 1, 1, 1)
|
||||||
|
self.y_label = QtWidgets.QLabel(self.groupBox)
|
||||||
|
self.y_label.setObjectName("y_label")
|
||||||
|
self.gridLayout_2.addWidget(self.y_label, 2, 0, 1, 1)
|
||||||
|
self.x_lineedit = QtWidgets.QLineEdit(self.groupBox)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.x_lineedit.sizePolicy().hasHeightForWidth())
|
||||||
|
self.x_lineedit.setSizePolicy(sizePolicy)
|
||||||
|
self.x_lineedit.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhPreferNumbers)
|
||||||
|
self.x_lineedit.setObjectName("x_lineedit")
|
||||||
|
self.gridLayout_2.addWidget(self.x_lineedit, 1, 1, 1, 1)
|
||||||
|
self.label_5 = QtWidgets.QLabel(self.groupBox)
|
||||||
|
self.label_5.setObjectName("label_5")
|
||||||
|
self.gridLayout_2.addWidget(self.label_5, 3, 0, 1, 1)
|
||||||
|
self.x_label = QtWidgets.QLabel(self.groupBox)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.x_label.sizePolicy().hasHeightForWidth())
|
||||||
|
self.x_label.setSizePolicy(sizePolicy)
|
||||||
|
self.x_label.setObjectName("x_label")
|
||||||
|
self.gridLayout_2.addWidget(self.x_label, 1, 0, 1, 1)
|
||||||
|
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||||
|
self.gridLayout_2.addItem(spacerItem1, 4, 1, 1, 1)
|
||||||
|
self.horizontalLayout_4.addLayout(self.gridLayout_2)
|
||||||
self.verticalLayout_3.addWidget(self.groupBox)
|
self.verticalLayout_3.addWidget(self.groupBox)
|
||||||
|
self.dsdfsf = QtWidgets.QLabel(self.tabWidgetPage1)
|
||||||
|
self.dsdfsf.setObjectName("dsdfsf")
|
||||||
|
self.verticalLayout_3.addWidget(self.dsdfsf)
|
||||||
|
self.gridLayout = QtWidgets.QGridLayout()
|
||||||
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
|
self.re_match_index = QtWidgets.QSpinBox(self.tabWidgetPage1)
|
||||||
|
self.re_match_index.setMinimum(1)
|
||||||
|
self.re_match_index.setObjectName("re_match_index")
|
||||||
|
self.gridLayout.addWidget(self.re_match_index, 1, 3, 1, 1)
|
||||||
|
self.label_9 = QtWidgets.QLabel(self.tabWidgetPage1)
|
||||||
|
self.label_9.setObjectName("label_9")
|
||||||
|
self.gridLayout.addWidget(self.label_9, 1, 2, 1, 1)
|
||||||
|
self.regex_input = QtWidgets.QLineEdit(self.tabWidgetPage1)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.regex_input.sizePolicy().hasHeightForWidth())
|
||||||
|
self.regex_input.setSizePolicy(sizePolicy)
|
||||||
|
self.regex_input.setObjectName("regex_input")
|
||||||
|
self.gridLayout.addWidget(self.regex_input, 1, 1, 1, 1)
|
||||||
|
self.re_button = QtWidgets.QRadioButton(self.tabWidgetPage1)
|
||||||
|
self.re_button.setChecked(True)
|
||||||
|
self.re_button.setObjectName("re_button")
|
||||||
|
self.buttonGroup_2 = QtWidgets.QButtonGroup(ascii_reader)
|
||||||
|
self.buttonGroup_2.setObjectName("buttonGroup_2")
|
||||||
|
self.buttonGroup_2.addButton(self.re_button)
|
||||||
|
self.gridLayout.addWidget(self.re_button, 1, 0, 1, 1)
|
||||||
|
self.custom_input = QtWidgets.QLineEdit(self.tabWidgetPage1)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.custom_input.sizePolicy().hasHeightForWidth())
|
||||||
|
self.custom_input.setSizePolicy(sizePolicy)
|
||||||
|
self.custom_input.setObjectName("custom_input")
|
||||||
|
self.gridLayout.addWidget(self.custom_input, 2, 1, 1, 1)
|
||||||
|
self.custom_button = QtWidgets.QRadioButton(self.tabWidgetPage1)
|
||||||
|
self.custom_button.setObjectName("custom_button")
|
||||||
|
self.buttonGroup_2.addButton(self.custom_button)
|
||||||
|
self.gridLayout.addWidget(self.custom_button, 2, 0, 1, 1)
|
||||||
|
self.label_8 = QtWidgets.QLabel(self.tabWidgetPage1)
|
||||||
|
self.label_8.setWordWrap(True)
|
||||||
|
self.label_8.setObjectName("label_8")
|
||||||
|
self.gridLayout.addWidget(self.label_8, 0, 0, 1, 4)
|
||||||
|
self.verticalLayout_3.addLayout(self.gridLayout)
|
||||||
self.groupBox_2 = QtWidgets.QGroupBox(self.tabWidgetPage1)
|
self.groupBox_2 = QtWidgets.QGroupBox(self.tabWidgetPage1)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.groupBox_2.sizePolicy().hasHeightForWidth())
|
||||||
|
self.groupBox_2.setSizePolicy(sizePolicy)
|
||||||
self.groupBox_2.setObjectName("groupBox_2")
|
self.groupBox_2.setObjectName("groupBox_2")
|
||||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_2)
|
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_2)
|
||||||
self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
|
self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
|
||||||
@ -215,24 +289,19 @@ class Ui_ascii_reader(object):
|
|||||||
self.pushButton.setSizePolicy(sizePolicy)
|
self.pushButton.setSizePolicy(sizePolicy)
|
||||||
self.pushButton.setObjectName("pushButton")
|
self.pushButton.setObjectName("pushButton")
|
||||||
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.pushButton)
|
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.pushButton)
|
||||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||||
self.formLayout.setItem(6, QtWidgets.QFormLayout.FieldRole, spacerItem)
|
self.formLayout.setItem(6, QtWidgets.QFormLayout.FieldRole, spacerItem2)
|
||||||
self.horizontalLayout_3.addLayout(self.formLayout)
|
self.horizontalLayout_3.addLayout(self.formLayout)
|
||||||
self.tabWidget.addTab(self.tabWidgetPage2, "")
|
self.tabWidget.addTab(self.tabWidgetPage2, "")
|
||||||
self.verticalLayout.addWidget(self.tabWidget)
|
self.verticalLayout.addWidget(self.tabWidget)
|
||||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
|
||||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
|
||||||
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
|
||||||
self.horizontalLayout.addItem(spacerItem1)
|
|
||||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
|
||||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||||
self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
|
self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
|
||||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||||
|
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
self.horizontalLayout_2.addItem(spacerItem3)
|
||||||
self.skippy_checkbox = QtWidgets.QCheckBox(ascii_reader)
|
self.skippy_checkbox = QtWidgets.QCheckBox(ascii_reader)
|
||||||
self.skippy_checkbox.setObjectName("skippy_checkbox")
|
self.skippy_checkbox.setObjectName("skippy_checkbox")
|
||||||
self.horizontalLayout_2.addWidget(self.skippy_checkbox)
|
self.horizontalLayout_2.addWidget(self.skippy_checkbox)
|
||||||
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
|
||||||
self.horizontalLayout_2.addItem(spacerItem2)
|
|
||||||
self.verticalLayout.addLayout(self.horizontalLayout_2)
|
self.verticalLayout.addLayout(self.horizontalLayout_2)
|
||||||
self.buttonbox = QtWidgets.QDialogButtonBox(ascii_reader)
|
self.buttonbox = QtWidgets.QDialogButtonBox(ascii_reader)
|
||||||
self.buttonbox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
self.buttonbox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||||
@ -247,14 +316,20 @@ class Ui_ascii_reader(object):
|
|||||||
ascii_reader.setTabOrder(self.column_checkBox, self.line_spinBox)
|
ascii_reader.setTabOrder(self.column_checkBox, self.line_spinBox)
|
||||||
ascii_reader.setTabOrder(self.line_spinBox, self.preview_spinBox)
|
ascii_reader.setTabOrder(self.line_spinBox, self.preview_spinBox)
|
||||||
ascii_reader.setTabOrder(self.preview_spinBox, self.pts_radioButton)
|
ascii_reader.setTabOrder(self.preview_spinBox, self.pts_radioButton)
|
||||||
ascii_reader.setTabOrder(self.pts_radioButton, self.FID_radioButton)
|
ascii_reader.setTabOrder(self.pts_radioButton, self.dsc_radioButton)
|
||||||
|
ascii_reader.setTabOrder(self.dsc_radioButton, self.FID_radioButton)
|
||||||
ascii_reader.setTabOrder(self.FID_radioButton, self.spectrum_radioButton)
|
ascii_reader.setTabOrder(self.FID_radioButton, self.spectrum_radioButton)
|
||||||
ascii_reader.setTabOrder(self.spectrum_radioButton, self.x_lineedit)
|
ascii_reader.setTabOrder(self.spectrum_radioButton, self.bds_radioButton)
|
||||||
|
ascii_reader.setTabOrder(self.bds_radioButton, self.x_lineedit)
|
||||||
ascii_reader.setTabOrder(self.x_lineedit, self.y_lineedit)
|
ascii_reader.setTabOrder(self.x_lineedit, self.y_lineedit)
|
||||||
ascii_reader.setTabOrder(self.y_lineedit, self.deltay_lineEdit)
|
ascii_reader.setTabOrder(self.y_lineedit, self.deltay_lineEdit)
|
||||||
ascii_reader.setTabOrder(self.deltay_lineEdit, self.ascii_table)
|
ascii_reader.setTabOrder(self.deltay_lineEdit, self.re_button)
|
||||||
ascii_reader.setTabOrder(self.ascii_table, self.skippy_checkbox)
|
ascii_reader.setTabOrder(self.re_button, self.regex_input)
|
||||||
ascii_reader.setTabOrder(self.skippy_checkbox, self.delay_textfield)
|
ascii_reader.setTabOrder(self.regex_input, self.re_match_index)
|
||||||
|
ascii_reader.setTabOrder(self.re_match_index, self.custom_button)
|
||||||
|
ascii_reader.setTabOrder(self.custom_button, self.custom_input)
|
||||||
|
ascii_reader.setTabOrder(self.custom_input, self.ascii_table)
|
||||||
|
ascii_reader.setTabOrder(self.ascii_table, self.delay_textfield)
|
||||||
ascii_reader.setTabOrder(self.delay_textfield, self.delay_lineedit)
|
ascii_reader.setTabOrder(self.delay_textfield, self.delay_lineedit)
|
||||||
ascii_reader.setTabOrder(self.delay_lineedit, self.start_lineedit)
|
ascii_reader.setTabOrder(self.delay_lineedit, self.start_lineedit)
|
||||||
ascii_reader.setTabOrder(self.start_lineedit, self.end_lineedit)
|
ascii_reader.setTabOrder(self.start_lineedit, self.end_lineedit)
|
||||||
@ -262,24 +337,33 @@ class Ui_ascii_reader(object):
|
|||||||
ascii_reader.setTabOrder(self.log_checkBox, self.staggered_checkBox)
|
ascii_reader.setTabOrder(self.log_checkBox, self.staggered_checkBox)
|
||||||
ascii_reader.setTabOrder(self.staggered_checkBox, self.stag_lineEdit)
|
ascii_reader.setTabOrder(self.staggered_checkBox, self.stag_lineEdit)
|
||||||
ascii_reader.setTabOrder(self.stag_lineEdit, self.pushButton)
|
ascii_reader.setTabOrder(self.stag_lineEdit, self.pushButton)
|
||||||
|
ascii_reader.setTabOrder(self.pushButton, self.skippy_checkbox)
|
||||||
|
|
||||||
def retranslateUi(self, ascii_reader):
|
def retranslateUi(self, ascii_reader):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
_translate = QtCore.QCoreApplication.translate
|
||||||
ascii_reader.setWindowTitle(_translate("ascii_reader", "Read text file"))
|
ascii_reader.setWindowTitle(_translate("ascii_reader", "Read text file"))
|
||||||
self.groupBox.setTitle(_translate("ascii_reader", "Options"))
|
self.groupBox.setTitle(_translate("ascii_reader", "Options"))
|
||||||
self.FID_radioButton.setText(_translate("ascii_reader", "FID"))
|
|
||||||
self.x_lineedit.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Specify which column is used as x-value.</p></body></html>"))
|
|
||||||
self.y_label.setText(_translate("ascii_reader", "y"))
|
|
||||||
self.pts_radioButton.setText(_translate("ascii_reader", "Points"))
|
|
||||||
self.label_5.setText(_translate("ascii_reader", "<html><head/><body><p>Δy</p></body></html>"))
|
|
||||||
self.column_checkBox.setText(_translate("ascii_reader", "Column name"))
|
self.column_checkBox.setText(_translate("ascii_reader", "Column name"))
|
||||||
self.label.setText(_translate("ascii_reader", "Import as"))
|
|
||||||
self.label_7.setText(_translate("ascii_reader", "Use columns as"))
|
|
||||||
self.y_lineedit.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Specify which columns are read for y-values. (\'Points\': Every number creates a new data set;\'FID\'/\'Spectrum\': Numbers at even positions are used for real parts, at odd positions for imaginary parts.)</p></body></html>"))
|
|
||||||
self.spectrum_radioButton.setText(_translate("ascii_reader", "Spectrum"))
|
|
||||||
self.x_label.setText(_translate("ascii_reader", "x"))
|
|
||||||
self.line_spinBox.setPrefix(_translate("ascii_reader", "header line "))
|
self.line_spinBox.setPrefix(_translate("ascii_reader", "header line "))
|
||||||
self.label_6.setText(_translate("ascii_reader", "Preview length"))
|
self.label_6.setText(_translate("ascii_reader", "Preview length"))
|
||||||
|
self.label.setText(_translate("ascii_reader", "Import as"))
|
||||||
|
self.pts_radioButton.setText(_translate("ascii_reader", "Points"))
|
||||||
|
self.dsc_radioButton.setText(_translate("ascii_reader", "DSC"))
|
||||||
|
self.FID_radioButton.setText(_translate("ascii_reader", "FID"))
|
||||||
|
self.spectrum_radioButton.setText(_translate("ascii_reader", "Spectrum"))
|
||||||
|
self.bds_radioButton.setText(_translate("ascii_reader", "BDS"))
|
||||||
|
self.label_7.setText(_translate("ascii_reader", "Use columns as"))
|
||||||
|
self.y_lineedit.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Specify which columns are used for y values.</p><p>- \'Points\': Every number creates a new data set;<br/>- \'FID\'/\'Spectrum\': Numbers at even positions are used for real parts, at odd positions for imaginary parts.</p></body></html>"))
|
||||||
|
self.y_label.setText(_translate("ascii_reader", "y"))
|
||||||
|
self.x_lineedit.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Specify which column is used for x values, write <span style=\" font-style:italic;\">index</span> to use row numbers (starting with 0). </p></body></html>"))
|
||||||
|
self.label_5.setText(_translate("ascii_reader", "<html><head/><body><p>Δy</p></body></html>"))
|
||||||
|
self.x_label.setText(_translate("ascii_reader", "x"))
|
||||||
|
self.dsdfsf.setText(_translate("ascii_reader", "Numerical value"))
|
||||||
|
self.label_9.setText(_translate("ascii_reader", "Match index"))
|
||||||
|
self.regex_input.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Token:<br/>[abc]: Matches any of a, b, or c<br/>[a-z]: Matches any digit in the range a-z<br/>\\d: Matches any digit in the range 0-9 (equal to [0-9}</p><p>Quantifiers:<br/>a*: 0 or more of a<br/>a*: 1 or more of a<br/>a?: 0 or 1 of a</p></body></html>"))
|
||||||
|
self.re_button.setText(_translate("ascii_reader", "Regex"))
|
||||||
|
self.custom_button.setText(_translate("ascii_reader", "Custom value"))
|
||||||
|
self.label_8.setText(_translate("ascii_reader", "Filename"))
|
||||||
self.groupBox_2.setTitle(_translate("ascii_reader", "Preview"))
|
self.groupBox_2.setTitle(_translate("ascii_reader", "Preview"))
|
||||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabWidgetPage1), _translate("ascii_reader", "Data"))
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabWidgetPage1), _translate("ascii_reader", "Data"))
|
||||||
self.label_2.setText(_translate("ascii_reader", "Number of delays"))
|
self.label_2.setText(_translate("ascii_reader", "Number of delays"))
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file '_ui/baseline_dialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/baseline_dialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.12.3
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
@ -44,7 +45,7 @@ class Ui_SignalEdit(object):
|
|||||||
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||||
self.buttonBox.setObjectName("buttonBox")
|
self.buttonBox.setObjectName("buttonBox")
|
||||||
self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 3)
|
self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 3)
|
||||||
self.graphicsView = PlotWidget(SignalEdit)
|
self.graphicsView = NMRPlotWidget(SignalEdit)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -54,11 +55,11 @@ class Ui_SignalEdit(object):
|
|||||||
self.gridLayout.addWidget(self.graphicsView, 0, 2, 1, 1)
|
self.gridLayout.addWidget(self.graphicsView, 0, 2, 1, 1)
|
||||||
|
|
||||||
self.retranslateUi(SignalEdit)
|
self.retranslateUi(SignalEdit)
|
||||||
self.buttonBox.accepted.connect(SignalEdit.accept)
|
self.buttonBox.accepted.connect(SignalEdit.accept) # type: ignore
|
||||||
self.buttonBox.rejected.connect(SignalEdit.close)
|
self.buttonBox.rejected.connect(SignalEdit.close) # type: ignore
|
||||||
QtCore.QMetaObject.connectSlotsByName(SignalEdit)
|
QtCore.QMetaObject.connectSlotsByName(SignalEdit)
|
||||||
|
|
||||||
def retranslateUi(self, SignalEdit):
|
def retranslateUi(self, SignalEdit):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
_translate = QtCore.QCoreApplication.translate
|
||||||
SignalEdit.setWindowTitle(_translate("SignalEdit", "Dialog"))
|
SignalEdit.setWindowTitle(_translate("SignalEdit", "Dialog"))
|
||||||
from pyqtgraph import PlotWidget
|
from ..lib.graph_items import NMRPlotWidget
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Form implementation generated from reading ui file 'src/resources/_ui/basewindow.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/basewindow.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.15.7
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
@ -70,7 +70,7 @@ class Ui_BaseWindow(object):
|
|||||||
self.integralwidget = IntegralWidget()
|
self.integralwidget = IntegralWidget()
|
||||||
self.integralwidget.setObjectName("integralwidget")
|
self.integralwidget.setObjectName("integralwidget")
|
||||||
self.tabWidget.addTab(self.integralwidget, "")
|
self.tabWidget.addTab(self.integralwidget, "")
|
||||||
self.area = QtWidgets.QMdiArea(self.splitter)
|
self.area = MdiAreaTile(self.splitter)
|
||||||
self.area.setObjectName("area")
|
self.area.setObjectName("area")
|
||||||
self.horizontalLayout.addWidget(self.splitter)
|
self.horizontalLayout.addWidget(self.splitter)
|
||||||
BaseWindow.setCentralWidget(self.centralwidget)
|
BaseWindow.setCentralWidget(self.centralwidget)
|
||||||
@ -117,6 +117,8 @@ class Ui_BaseWindow(object):
|
|||||||
self.menuStuff = QtWidgets.QMenu(self.menubar)
|
self.menuStuff = QtWidgets.QMenu(self.menubar)
|
||||||
self.menuStuff.setTitle("")
|
self.menuStuff.setTitle("")
|
||||||
self.menuStuff.setObjectName("menuStuff")
|
self.menuStuff.setObjectName("menuStuff")
|
||||||
|
self.menuDSC = QtWidgets.QMenu(self.menubar)
|
||||||
|
self.menuDSC.setObjectName("menuDSC")
|
||||||
BaseWindow.setMenuBar(self.menubar)
|
BaseWindow.setMenuBar(self.menubar)
|
||||||
self.toolBar = QtWidgets.QToolBar(BaseWindow)
|
self.toolBar = QtWidgets.QToolBar(BaseWindow)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||||
@ -261,6 +263,10 @@ class Ui_BaseWindow(object):
|
|||||||
self.actionMaximize.setObjectName("actionMaximize")
|
self.actionMaximize.setObjectName("actionMaximize")
|
||||||
self.actionTile = QtWidgets.QAction(BaseWindow)
|
self.actionTile = QtWidgets.QAction(BaseWindow)
|
||||||
self.actionTile.setObjectName("actionTile")
|
self.actionTile.setObjectName("actionTile")
|
||||||
|
self.actionTileVertical = QtWidgets.QAction(BaseWindow)
|
||||||
|
self.actionTileVertical.setObjectName("actionTileVertical")
|
||||||
|
self.actionTileHorizontal = QtWidgets.QAction(BaseWindow)
|
||||||
|
self.actionTileHorizontal.setObjectName("actionTileHorizontal")
|
||||||
self.actionMinimize = QtWidgets.QAction(BaseWindow)
|
self.actionMinimize = QtWidgets.QAction(BaseWindow)
|
||||||
self.actionMinimize.setCheckable(True)
|
self.actionMinimize.setCheckable(True)
|
||||||
self.actionMinimize.setVisible(False)
|
self.actionMinimize.setVisible(False)
|
||||||
@ -358,6 +364,16 @@ class Ui_BaseWindow(object):
|
|||||||
self.actionBugs.setObjectName("actionBugs")
|
self.actionBugs.setObjectName("actionBugs")
|
||||||
self.actionShow_error_log = QtWidgets.QAction(BaseWindow)
|
self.actionShow_error_log = QtWidgets.QAction(BaseWindow)
|
||||||
self.actionShow_error_log.setObjectName("actionShow_error_log")
|
self.actionShow_error_log.setObjectName("actionShow_error_log")
|
||||||
|
self.actionCreate_starter = QtWidgets.QAction(BaseWindow)
|
||||||
|
self.actionCreate_starter.setObjectName("actionCreate_starter")
|
||||||
|
self.actionAbout = QtWidgets.QAction(BaseWindow)
|
||||||
|
self.actionAbout.setObjectName("actionAbout")
|
||||||
|
self.actionTNMH_model = QtWidgets.QAction(BaseWindow)
|
||||||
|
self.actionTNMH_model.setObjectName("actionTNMH_model")
|
||||||
|
self.actionBinning = QtWidgets.QAction(BaseWindow)
|
||||||
|
self.actionBinning.setObjectName("actionBinning")
|
||||||
|
self.actionTNMH = QtWidgets.QAction(BaseWindow)
|
||||||
|
self.actionTNMH.setObjectName("actionTNMH")
|
||||||
self.menuSave.addAction(self.actionSave)
|
self.menuSave.addAction(self.actionSave)
|
||||||
self.menuSave.addAction(self.actionExportGraphic)
|
self.menuSave.addAction(self.actionExportGraphic)
|
||||||
self.menuSave.addAction(self.action_save_fit_parameter)
|
self.menuSave.addAction(self.action_save_fit_parameter)
|
||||||
@ -383,9 +399,9 @@ class Ui_BaseWindow(object):
|
|||||||
self.menuData.addSeparator()
|
self.menuData.addSeparator()
|
||||||
self.menuData.addAction(self.actionChange_datatypes)
|
self.menuData.addAction(self.actionChange_datatypes)
|
||||||
self.menuHelp.addAction(self.actionShow_error_log)
|
self.menuHelp.addAction(self.actionShow_error_log)
|
||||||
self.menuHelp.addAction(self.actionDocumentation)
|
|
||||||
self.menuHelp.addAction(self.actionUpdate)
|
self.menuHelp.addAction(self.actionUpdate)
|
||||||
self.menuHelp.addAction(self.actionBugs)
|
self.menuHelp.addAction(self.actionBugs)
|
||||||
|
self.menuHelp.addAction(self.actionAbout)
|
||||||
self.menuNormalize.addAction(self.action_norm_max)
|
self.menuNormalize.addAction(self.action_norm_max)
|
||||||
self.menuNormalize.addAction(self.action_norm_max_abs)
|
self.menuNormalize.addAction(self.action_norm_max_abs)
|
||||||
self.menuNormalize.addSeparator()
|
self.menuNormalize.addSeparator()
|
||||||
@ -401,6 +417,7 @@ class Ui_BaseWindow(object):
|
|||||||
self.menuExtra.addSeparator()
|
self.menuExtra.addSeparator()
|
||||||
self.menuExtra.addAction(self.menuNormalize.menuAction())
|
self.menuExtra.addAction(self.menuNormalize.menuAction())
|
||||||
self.menuExtra.addAction(self.actionInterpolation)
|
self.menuExtra.addAction(self.actionInterpolation)
|
||||||
|
self.menuExtra.addAction(self.actionBinning)
|
||||||
self.menuExtra.addAction(self.actionRunning_values)
|
self.menuExtra.addAction(self.actionRunning_values)
|
||||||
self.menuExtra.addAction(self.actionShift)
|
self.menuExtra.addAction(self.actionShift)
|
||||||
self.menuExtra.addSeparator()
|
self.menuExtra.addSeparator()
|
||||||
@ -422,7 +439,10 @@ class Ui_BaseWindow(object):
|
|||||||
self.menuOptions.addSeparator()
|
self.menuOptions.addSeparator()
|
||||||
self.menuOptions.addAction(self.action_colorcycle)
|
self.menuOptions.addAction(self.action_colorcycle)
|
||||||
self.menuOptions.addAction(self.actionConfiguration)
|
self.menuOptions.addAction(self.actionConfiguration)
|
||||||
|
self.menuOptions.addAction(self.actionCreate_starter)
|
||||||
self.menuView.addAction(self.actionTile)
|
self.menuView.addAction(self.actionTile)
|
||||||
|
self.menuView.addAction(self.actionTileVertical)
|
||||||
|
self.menuView.addAction(self.actionTileHorizontal)
|
||||||
self.menuView.addAction(self.actionCascade_windows)
|
self.menuView.addAction(self.actionCascade_windows)
|
||||||
self.menuWindow.addAction(self.actionNew_window)
|
self.menuWindow.addAction(self.actionNew_window)
|
||||||
self.menuWindow.addAction(self.actionDelete_window)
|
self.menuWindow.addAction(self.actionDelete_window)
|
||||||
@ -451,6 +471,7 @@ class Ui_BaseWindow(object):
|
|||||||
self.menuStuff.addAction(self.actionLife)
|
self.menuStuff.addAction(self.actionLife)
|
||||||
self.menuStuff.addAction(self.actionTetris)
|
self.menuStuff.addAction(self.actionTetris)
|
||||||
self.menuStuff.addAction(self.actionMine)
|
self.menuStuff.addAction(self.actionMine)
|
||||||
|
self.menuDSC.addAction(self.actionTNMH_model)
|
||||||
self.menubar.addAction(self.menuFile.menuAction())
|
self.menubar.addAction(self.menuFile.menuAction())
|
||||||
self.menubar.addAction(self.menuWindow.menuAction())
|
self.menubar.addAction(self.menuWindow.menuAction())
|
||||||
self.menubar.addAction(self.menuData.menuAction())
|
self.menubar.addAction(self.menuData.menuAction())
|
||||||
@ -459,6 +480,7 @@ class Ui_BaseWindow(object):
|
|||||||
self.menubar.addAction(self.menuFit.menuAction())
|
self.menubar.addAction(self.menuFit.menuAction())
|
||||||
self.menubar.addAction(self.menuNMR.menuAction())
|
self.menubar.addAction(self.menuNMR.menuAction())
|
||||||
self.menubar.addAction(self.menuBDS.menuAction())
|
self.menubar.addAction(self.menuBDS.menuAction())
|
||||||
|
self.menubar.addAction(self.menuDSC.menuAction())
|
||||||
self.menubar.addAction(self.menuOptions.menuAction())
|
self.menubar.addAction(self.menuOptions.menuAction())
|
||||||
self.menubar.addAction(self.menuHelp.menuAction())
|
self.menubar.addAction(self.menuHelp.menuAction())
|
||||||
self.menubar.addAction(self.menuStuff.menuAction())
|
self.menubar.addAction(self.menuStuff.menuAction())
|
||||||
@ -511,6 +533,7 @@ class Ui_BaseWindow(object):
|
|||||||
self.menuNMR.setTitle(_translate("BaseWindow", "NMR"))
|
self.menuNMR.setTitle(_translate("BaseWindow", "NMR"))
|
||||||
self.menuBDS.setTitle(_translate("BaseWindow", "BDS"))
|
self.menuBDS.setTitle(_translate("BaseWindow", "BDS"))
|
||||||
self.menuSpectrum.setTitle(_translate("BaseWindow", "Spectrum"))
|
self.menuSpectrum.setTitle(_translate("BaseWindow", "Spectrum"))
|
||||||
|
self.menuDSC.setTitle(_translate("BaseWindow", "DSC"))
|
||||||
self.toolBar.setWindowTitle(_translate("BaseWindow", "Main"))
|
self.toolBar.setWindowTitle(_translate("BaseWindow", "Main"))
|
||||||
self.toolbar_edit.setWindowTitle(_translate("BaseWindow", "Math"))
|
self.toolbar_edit.setWindowTitle(_translate("BaseWindow", "Math"))
|
||||||
self.toolBar_nmr.setWindowTitle(_translate("BaseWindow", "NMR"))
|
self.toolBar_nmr.setWindowTitle(_translate("BaseWindow", "NMR"))
|
||||||
@ -566,6 +589,8 @@ class Ui_BaseWindow(object):
|
|||||||
self.actionGuide_lines.setText(_translate("BaseWindow", "Draw lines..."))
|
self.actionGuide_lines.setText(_translate("BaseWindow", "Draw lines..."))
|
||||||
self.actionMaximize.setText(_translate("BaseWindow", "Maximize"))
|
self.actionMaximize.setText(_translate("BaseWindow", "Maximize"))
|
||||||
self.actionTile.setText(_translate("BaseWindow", "Tile windows"))
|
self.actionTile.setText(_translate("BaseWindow", "Tile windows"))
|
||||||
|
self.actionTileVertical.setText(_translate("BaseWindow", "Tile windows vertically"))
|
||||||
|
self.actionTileHorizontal.setText(_translate("BaseWindow", "Tile windows horizontally"))
|
||||||
self.actionMinimize.setText(_translate("BaseWindow", "Minimize"))
|
self.actionMinimize.setText(_translate("BaseWindow", "Minimize"))
|
||||||
self.actionNew_window.setText(_translate("BaseWindow", "New graph"))
|
self.actionNew_window.setText(_translate("BaseWindow", "New graph"))
|
||||||
self.actionDelete_window.setText(_translate("BaseWindow", "Delete graph"))
|
self.actionDelete_window.setText(_translate("BaseWindow", "Delete graph"))
|
||||||
@ -612,6 +637,11 @@ class Ui_BaseWindow(object):
|
|||||||
self.action_draw_object.setText(_translate("BaseWindow", "Draw objects..."))
|
self.action_draw_object.setText(_translate("BaseWindow", "Draw objects..."))
|
||||||
self.actionBugs.setText(_translate("BaseWindow", "Bugs! Problems! Wishes!"))
|
self.actionBugs.setText(_translate("BaseWindow", "Bugs! Problems! Wishes!"))
|
||||||
self.actionShow_error_log.setText(_translate("BaseWindow", "Show error log"))
|
self.actionShow_error_log.setText(_translate("BaseWindow", "Show error log"))
|
||||||
|
self.actionCreate_starter.setText(_translate("BaseWindow", "Create starter.."))
|
||||||
|
self.actionAbout.setText(_translate("BaseWindow", "About..."))
|
||||||
|
self.actionTNMH_model.setText(_translate("BaseWindow", "Tg , Hodge, TNMH,,,"))
|
||||||
|
self.actionBinning.setText(_translate("BaseWindow", "Binning..."))
|
||||||
|
self.actionTNMH.setText(_translate("BaseWindow", "TNMH..."))
|
||||||
from ..data.datawidget.datawidget import DataWidget
|
from ..data.datawidget.datawidget import DataWidget
|
||||||
from ..data.integral_widget import IntegralWidget
|
from ..data.integral_widget import IntegralWidget
|
||||||
from ..data.point_select import PointSelectWidget
|
from ..data.point_select import PointSelectWidget
|
||||||
@ -619,4 +649,5 @@ from ..data.signaledit.editsignalwidget import EditSignalWidget
|
|||||||
from ..data.valueeditwidget import ValueEditWidget
|
from ..data.valueeditwidget import ValueEditWidget
|
||||||
from ..fit.fitwindow import QFitDialog
|
from ..fit.fitwindow import QFitDialog
|
||||||
from ..graphs.drawings import DrawingsWidget
|
from ..graphs.drawings import DrawingsWidget
|
||||||
|
from ..lib.mdiarea import MdiAreaTile
|
||||||
from ..nmr.t1widget import QT1Widget
|
from ..nmr.t1widget import QT1Widget
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/bdsdialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/bdsdialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.9.2
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
class Ui_Dialog(object):
|
class Ui_Dialog(object):
|
||||||
def setupUi(self, Dialog):
|
def setupUi(self, Dialog):
|
||||||
Dialog.setObjectName("Dialog")
|
Dialog.setObjectName("Dialog")
|
||||||
@ -16,12 +19,13 @@ class Ui_Dialog(object):
|
|||||||
self.gridLayout.setContentsMargins(3, 3, 3, 3)
|
self.gridLayout.setContentsMargins(3, 3, 3, 3)
|
||||||
self.gridLayout.setSpacing(3)
|
self.gridLayout.setSpacing(3)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
self.listWidget = QtWidgets.QListWidget(Dialog)
|
self.listWidget = QListWidgetSelect(Dialog)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
|
||||||
self.listWidget.setSizePolicy(sizePolicy)
|
self.listWidget.setSizePolicy(sizePolicy)
|
||||||
|
self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
self.listWidget.setObjectName("listWidget")
|
self.listWidget.setObjectName("listWidget")
|
||||||
self.gridLayout.addWidget(self.listWidget, 1, 0, 2, 1)
|
self.gridLayout.addWidget(self.listWidget, 1, 0, 2, 1)
|
||||||
self.groupBox_2 = QtWidgets.QGroupBox(Dialog)
|
self.groupBox_2 = QtWidgets.QGroupBox(Dialog)
|
||||||
@ -93,8 +97,8 @@ class Ui_Dialog(object):
|
|||||||
self.gridLayout.addWidget(self.label, 0, 0, 1, 2)
|
self.gridLayout.addWidget(self.label, 0, 0, 1, 2)
|
||||||
|
|
||||||
self.retranslateUi(Dialog)
|
self.retranslateUi(Dialog)
|
||||||
self.buttonBox.accepted.connect(Dialog.accept)
|
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
|
||||||
self.buttonBox.rejected.connect(Dialog.reject)
|
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
|
||||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
||||||
Dialog.setTabOrder(self.freq_button, self.temp_button)
|
Dialog.setTabOrder(self.freq_button, self.temp_button)
|
||||||
Dialog.setTabOrder(self.temp_button, self.eps_checkBox)
|
Dialog.setTabOrder(self.temp_button, self.eps_checkBox)
|
||||||
@ -117,4 +121,4 @@ class Ui_Dialog(object):
|
|||||||
self.temp_checkBox.setText(_translate("Dialog", "Meas. temperature"))
|
self.temp_checkBox.setText(_translate("Dialog", "Meas. temperature"))
|
||||||
self.time_checkBox.setText(_translate("Dialog", "Meas. time"))
|
self.time_checkBox.setText(_translate("Dialog", "Meas. time"))
|
||||||
self.label.setText(_translate("Dialog", "Found entries"))
|
self.label.setText(_translate("Dialog", "Found entries"))
|
||||||
|
from ..lib.listwidget import QListWidgetSelect
|
||||||
|
@ -1,29 +1,113 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/dscfile_dialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/dscfile_dialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.9.2
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
class Ui_Dialog(object):
|
class Ui_Dialog(object):
|
||||||
def setupUi(self, Dialog):
|
def setupUi(self, Dialog):
|
||||||
Dialog.setObjectName("Dialog")
|
Dialog.setObjectName("Dialog")
|
||||||
Dialog.resize(962, 662)
|
Dialog.resize(1341, 799)
|
||||||
self.gridLayout_2 = QtWidgets.QGridLayout(Dialog)
|
self.verticalLayout_5 = QtWidgets.QVBoxLayout(Dialog)
|
||||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
self.verticalLayout_5.setObjectName("verticalLayout_5")
|
||||||
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
|
self.splitter = QtWidgets.QSplitter(Dialog)
|
||||||
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
|
self.splitter.setOrientation(QtCore.Qt.Horizontal)
|
||||||
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Save)
|
self.splitter.setObjectName("splitter")
|
||||||
self.buttonBox.setObjectName("buttonBox")
|
self.verticalLayoutWidget = QtWidgets.QWidget(self.splitter)
|
||||||
self.gridLayout_2.addWidget(self.buttonBox, 1, 1, 1, 1)
|
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
|
||||||
self.gridLayout_4 = QtWidgets.QGridLayout()
|
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
|
||||||
self.gridLayout_4.setContentsMargins(-1, 0, 0, -1)
|
self.verticalLayout_4.setContentsMargins(6, 6, 6, 6)
|
||||||
self.gridLayout_4.setSpacing(3)
|
self.verticalLayout_4.setObjectName("verticalLayout_4")
|
||||||
self.gridLayout_4.setObjectName("gridLayout_4")
|
self.groupBox = QtWidgets.QGroupBox(self.verticalLayoutWidget)
|
||||||
self.cp_checkBox = QtWidgets.QCheckBox(Dialog)
|
self.groupBox.setFlat(False)
|
||||||
|
self.groupBox.setObjectName("groupBox")
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox)
|
||||||
|
self.verticalLayout.setContentsMargins(6, 6, 6, 6)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
self.step_listWidget = QtWidgets.QListWidget(self.groupBox)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.step_listWidget.sizePolicy().hasHeightForWidth())
|
||||||
|
self.step_listWidget.setSizePolicy(sizePolicy)
|
||||||
|
self.step_listWidget.setMinimumSize(QtCore.QSize(0, 0))
|
||||||
|
self.step_listWidget.setObjectName("step_listWidget")
|
||||||
|
self.verticalLayout.addWidget(self.step_listWidget)
|
||||||
|
self.verticalLayout_4.addWidget(self.groupBox)
|
||||||
|
self.groupBox_2 = QtWidgets.QGroupBox(self.verticalLayoutWidget)
|
||||||
|
self.groupBox_2.setObjectName("groupBox_2")
|
||||||
|
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_2)
|
||||||
|
self.verticalLayout_2.setContentsMargins(6, 6, 6, 6)
|
||||||
|
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||||
|
self.groupBox_4 = QtWidgets.QGroupBox(self.groupBox_2)
|
||||||
|
self.groupBox_4.setObjectName("groupBox_4")
|
||||||
|
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox_4)
|
||||||
|
self.verticalLayout_6.setContentsMargins(6, 6, 6, 6)
|
||||||
|
self.verticalLayout_6.setObjectName("verticalLayout_6")
|
||||||
|
self.empty_label = QtWidgets.QLabel(self.groupBox_4)
|
||||||
|
self.empty_label.setObjectName("empty_label")
|
||||||
|
self.verticalLayout_6.addWidget(self.empty_label)
|
||||||
|
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||||
|
self.horizontalLayout_2.setSpacing(3)
|
||||||
|
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||||
|
self.loadempty_button = QtWidgets.QPushButton(self.groupBox_4)
|
||||||
|
self.loadempty_button.setObjectName("loadempty_button")
|
||||||
|
self.horizontalLayout_2.addWidget(self.loadempty_button)
|
||||||
|
self.delempty_button = QtWidgets.QPushButton(self.groupBox_4)
|
||||||
|
self.delempty_button.setObjectName("delempty_button")
|
||||||
|
self.horizontalLayout_2.addWidget(self.delempty_button)
|
||||||
|
self.verticalLayout_6.addLayout(self.horizontalLayout_2)
|
||||||
|
self.verticalLayout_2.addWidget(self.groupBox_4)
|
||||||
|
self.groupBox_5 = QtWidgets.QGroupBox(self.groupBox_2)
|
||||||
|
self.groupBox_5.setObjectName("groupBox_5")
|
||||||
|
self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.groupBox_5)
|
||||||
|
self.verticalLayout_7.setContentsMargins(6, 6, 6, 6)
|
||||||
|
self.verticalLayout_7.setSpacing(3)
|
||||||
|
self.verticalLayout_7.setObjectName("verticalLayout_7")
|
||||||
|
self.none_radioButton = QtWidgets.QRadioButton(self.groupBox_5)
|
||||||
|
self.none_radioButton.setObjectName("none_radioButton")
|
||||||
|
self.buttonGroup = QtWidgets.QButtonGroup(Dialog)
|
||||||
|
self.buttonGroup.setObjectName("buttonGroup")
|
||||||
|
self.buttonGroup.addButton(self.none_radioButton)
|
||||||
|
self.verticalLayout_7.addWidget(self.none_radioButton)
|
||||||
|
self.isotherm_radioButton = QtWidgets.QRadioButton(self.groupBox_5)
|
||||||
|
self.isotherm_radioButton.setChecked(True)
|
||||||
|
self.isotherm_radioButton.setObjectName("isotherm_radioButton")
|
||||||
|
self.buttonGroup.addButton(self.isotherm_radioButton)
|
||||||
|
self.verticalLayout_7.addWidget(self.isotherm_radioButton)
|
||||||
|
self.slope_radioButton = QtWidgets.QRadioButton(self.groupBox_5)
|
||||||
|
self.slope_radioButton.setObjectName("slope_radioButton")
|
||||||
|
self.buttonGroup.addButton(self.slope_radioButton)
|
||||||
|
self.verticalLayout_7.addWidget(self.slope_radioButton)
|
||||||
|
self.widget = QtWidgets.QWidget(self.groupBox_5)
|
||||||
|
self.widget.setMinimumSize(QtCore.QSize(0, 33))
|
||||||
|
self.widget.setObjectName("widget")
|
||||||
|
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget)
|
||||||
|
self.horizontalLayout_3.setContentsMargins(3, 3, 3, 3)
|
||||||
|
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
||||||
|
self.limit1_lineedit = QtWidgets.QLineEdit(self.widget)
|
||||||
|
self.limit1_lineedit.setObjectName("limit1_lineedit")
|
||||||
|
self.horizontalLayout_3.addWidget(self.limit1_lineedit)
|
||||||
|
self.limit2_lineedit = QtWidgets.QLineEdit(self.widget)
|
||||||
|
self.limit2_lineedit.setText("")
|
||||||
|
self.limit2_lineedit.setObjectName("limit2_lineedit")
|
||||||
|
self.horizontalLayout_3.addWidget(self.limit2_lineedit)
|
||||||
|
self.verticalLayout_7.addWidget(self.widget)
|
||||||
|
self.verticalLayout_2.addWidget(self.groupBox_5)
|
||||||
|
self.verticalLayout_4.addWidget(self.groupBox_2)
|
||||||
|
self.groupBox_3 = QtWidgets.QGroupBox(self.verticalLayoutWidget)
|
||||||
|
self.groupBox_3.setObjectName("groupBox_3")
|
||||||
|
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_3)
|
||||||
|
self.verticalLayout_3.setContentsMargins(6, 6, 6, 6)
|
||||||
|
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||||
|
self.cp_checkBox = QtWidgets.QCheckBox(self.groupBox_3)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -31,36 +115,8 @@ class Ui_Dialog(object):
|
|||||||
self.cp_checkBox.setSizePolicy(sizePolicy)
|
self.cp_checkBox.setSizePolicy(sizePolicy)
|
||||||
self.cp_checkBox.setChecked(True)
|
self.cp_checkBox.setChecked(True)
|
||||||
self.cp_checkBox.setObjectName("cp_checkBox")
|
self.cp_checkBox.setObjectName("cp_checkBox")
|
||||||
self.gridLayout_4.addWidget(self.cp_checkBox, 11, 0, 1, 4)
|
self.verticalLayout_3.addWidget(self.cp_checkBox)
|
||||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
self.reference_tableWidget = QtWidgets.QTableWidget(self.groupBox_3)
|
||||||
self.horizontalLayout_2.setSpacing(3)
|
|
||||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
|
||||||
self.loadempty_button = QtWidgets.QPushButton(Dialog)
|
|
||||||
self.loadempty_button.setObjectName("loadempty_button")
|
|
||||||
self.horizontalLayout_2.addWidget(self.loadempty_button)
|
|
||||||
self.delempty_button = QtWidgets.QPushButton(Dialog)
|
|
||||||
self.delempty_button.setObjectName("delempty_button")
|
|
||||||
self.horizontalLayout_2.addWidget(self.delempty_button)
|
|
||||||
self.gridLayout_4.addLayout(self.horizontalLayout_2, 5, 0, 1, 4)
|
|
||||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
|
||||||
self.gridLayout_4.addItem(spacerItem, 12, 0, 1, 1)
|
|
||||||
self.isotherm_radioButton = QtWidgets.QRadioButton(Dialog)
|
|
||||||
self.isotherm_radioButton.setChecked(True)
|
|
||||||
self.isotherm_radioButton.setObjectName("isotherm_radioButton")
|
|
||||||
self.buttonGroup = QtWidgets.QButtonGroup(Dialog)
|
|
||||||
self.buttonGroup.setObjectName("buttonGroup")
|
|
||||||
self.buttonGroup.addButton(self.isotherm_radioButton)
|
|
||||||
self.gridLayout_4.addWidget(self.isotherm_radioButton, 6, 1, 1, 1)
|
|
||||||
self.label_4 = QtWidgets.QLabel(Dialog)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
|
|
||||||
self.label_4.setSizePolicy(sizePolicy)
|
|
||||||
self.label_4.setStyleSheet("font-weight: bold")
|
|
||||||
self.label_4.setObjectName("label_4")
|
|
||||||
self.gridLayout_4.addWidget(self.label_4, 0, 0, 1, 4)
|
|
||||||
self.reference_tableWidget = QtWidgets.QTableWidget(Dialog)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -75,57 +131,11 @@ class Ui_Dialog(object):
|
|||||||
self.reference_tableWidget.horizontalHeader().setVisible(False)
|
self.reference_tableWidget.horizontalHeader().setVisible(False)
|
||||||
self.reference_tableWidget.horizontalHeader().setStretchLastSection(True)
|
self.reference_tableWidget.horizontalHeader().setStretchLastSection(True)
|
||||||
self.reference_tableWidget.verticalHeader().setVisible(False)
|
self.reference_tableWidget.verticalHeader().setVisible(False)
|
||||||
self.gridLayout_4.addWidget(self.reference_tableWidget, 9, 0, 1, 4)
|
self.verticalLayout_3.addWidget(self.reference_tableWidget)
|
||||||
self.step_listWidget = QtWidgets.QListWidget(Dialog)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.step_listWidget.sizePolicy().hasHeightForWidth())
|
|
||||||
self.step_listWidget.setSizePolicy(sizePolicy)
|
|
||||||
self.step_listWidget.setMinimumSize(QtCore.QSize(0, 0))
|
|
||||||
self.step_listWidget.setObjectName("step_listWidget")
|
|
||||||
self.gridLayout_4.addWidget(self.step_listWidget, 1, 0, 1, 4)
|
|
||||||
self.label = QtWidgets.QLabel(Dialog)
|
|
||||||
self.label.setObjectName("label")
|
|
||||||
self.gridLayout_4.addWidget(self.label, 6, 0, 1, 1)
|
|
||||||
self.slope_radioButton = QtWidgets.QRadioButton(Dialog)
|
|
||||||
self.slope_radioButton.setObjectName("slope_radioButton")
|
|
||||||
self.buttonGroup.addButton(self.slope_radioButton)
|
|
||||||
self.gridLayout_4.addWidget(self.slope_radioButton, 6, 2, 1, 1)
|
|
||||||
self.empty_label = QtWidgets.QLabel(Dialog)
|
|
||||||
self.empty_label.setObjectName("empty_label")
|
|
||||||
self.gridLayout_4.addWidget(self.empty_label, 4, 0, 1, 4)
|
|
||||||
self.label_3 = QtWidgets.QLabel(Dialog)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
|
|
||||||
self.label_3.setSizePolicy(sizePolicy)
|
|
||||||
self.label_3.setStyleSheet("font-weight: bold")
|
|
||||||
self.label_3.setObjectName("label_3")
|
|
||||||
self.gridLayout_4.addWidget(self.label_3, 8, 0, 1, 4)
|
|
||||||
self.label_2 = QtWidgets.QLabel(Dialog)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
|
|
||||||
self.label_2.setSizePolicy(sizePolicy)
|
|
||||||
self.label_2.setStyleSheet("font-weight: bold")
|
|
||||||
self.label_2.setObjectName("label_2")
|
|
||||||
self.gridLayout_4.addWidget(self.label_2, 3, 0, 1, 4)
|
|
||||||
self.line = QtWidgets.QFrame(Dialog)
|
|
||||||
self.line.setFrameShape(QtWidgets.QFrame.HLine)
|
|
||||||
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
|
||||||
self.line.setObjectName("line")
|
|
||||||
self.gridLayout_4.addWidget(self.line, 7, 0, 1, 4)
|
|
||||||
self.none_radioButton = QtWidgets.QRadioButton(Dialog)
|
|
||||||
self.none_radioButton.setObjectName("none_radioButton")
|
|
||||||
self.buttonGroup.addButton(self.none_radioButton)
|
|
||||||
self.gridLayout_4.addWidget(self.none_radioButton, 6, 3, 1, 1)
|
|
||||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||||
self.horizontalLayout.setSpacing(3)
|
self.horizontalLayout.setSpacing(3)
|
||||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
self.ref_add_pushButton = QtWidgets.QPushButton(Dialog)
|
self.ref_add_pushButton = QtWidgets.QPushButton(self.groupBox_3)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -133,7 +143,7 @@ class Ui_Dialog(object):
|
|||||||
self.ref_add_pushButton.setSizePolicy(sizePolicy)
|
self.ref_add_pushButton.setSizePolicy(sizePolicy)
|
||||||
self.ref_add_pushButton.setObjectName("ref_add_pushButton")
|
self.ref_add_pushButton.setObjectName("ref_add_pushButton")
|
||||||
self.horizontalLayout.addWidget(self.ref_add_pushButton)
|
self.horizontalLayout.addWidget(self.ref_add_pushButton)
|
||||||
self.ref_remove_pushButton = QtWidgets.QPushButton(Dialog)
|
self.ref_remove_pushButton = QtWidgets.QPushButton(self.groupBox_3)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -141,16 +151,22 @@ class Ui_Dialog(object):
|
|||||||
self.ref_remove_pushButton.setSizePolicy(sizePolicy)
|
self.ref_remove_pushButton.setSizePolicy(sizePolicy)
|
||||||
self.ref_remove_pushButton.setObjectName("ref_remove_pushButton")
|
self.ref_remove_pushButton.setObjectName("ref_remove_pushButton")
|
||||||
self.horizontalLayout.addWidget(self.ref_remove_pushButton)
|
self.horizontalLayout.addWidget(self.ref_remove_pushButton)
|
||||||
self.gridLayout_4.addLayout(self.horizontalLayout, 10, 0, 1, 4)
|
self.verticalLayout_3.addLayout(self.horizontalLayout)
|
||||||
self.line_2 = QtWidgets.QFrame(Dialog)
|
self.verticalLayout_4.addWidget(self.groupBox_3)
|
||||||
self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
|
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||||
self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
|
self.verticalLayout_4.addItem(spacerItem)
|
||||||
self.line_2.setObjectName("line_2")
|
self.buttonBox = QtWidgets.QDialogButtonBox(self.verticalLayoutWidget)
|
||||||
self.gridLayout_4.addWidget(self.line_2, 2, 0, 1, 4)
|
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||||
self.gridLayout_2.addLayout(self.gridLayout_4, 0, 0, 1, 1)
|
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Save)
|
||||||
self.gridLayout = QtWidgets.QGridLayout()
|
self.buttonBox.setCenterButtons(True)
|
||||||
|
self.buttonBox.setObjectName("buttonBox")
|
||||||
|
self.verticalLayout_4.addWidget(self.buttonBox)
|
||||||
|
self.layoutWidget = QtWidgets.QWidget(self.splitter)
|
||||||
|
self.layoutWidget.setObjectName("layoutWidget")
|
||||||
|
self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget)
|
||||||
|
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
self.raw_graph = PlotWidget(Dialog)
|
self.raw_graph = NMRPlotWidget(self.layoutWidget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -159,7 +175,7 @@ class Ui_Dialog(object):
|
|||||||
self.raw_graph.setMinimumSize(QtCore.QSize(300, 200))
|
self.raw_graph.setMinimumSize(QtCore.QSize(300, 200))
|
||||||
self.raw_graph.setObjectName("raw_graph")
|
self.raw_graph.setObjectName("raw_graph")
|
||||||
self.gridLayout.addWidget(self.raw_graph, 0, 0, 1, 1)
|
self.gridLayout.addWidget(self.raw_graph, 0, 0, 1, 1)
|
||||||
self.calib_graph = PlotWidget(Dialog)
|
self.calib_graph = NMRPlotWidget(self.layoutWidget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -168,7 +184,7 @@ class Ui_Dialog(object):
|
|||||||
self.calib_graph.setMinimumSize(QtCore.QSize(300, 200))
|
self.calib_graph.setMinimumSize(QtCore.QSize(300, 200))
|
||||||
self.calib_graph.setObjectName("calib_graph")
|
self.calib_graph.setObjectName("calib_graph")
|
||||||
self.gridLayout.addWidget(self.calib_graph, 1, 0, 1, 1)
|
self.gridLayout.addWidget(self.calib_graph, 1, 0, 1, 1)
|
||||||
self.baseline_graph = PlotWidget(Dialog)
|
self.baseline_graph = NMRPlotWidget(self.layoutWidget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -177,7 +193,7 @@ class Ui_Dialog(object):
|
|||||||
self.baseline_graph.setMinimumSize(QtCore.QSize(300, 200))
|
self.baseline_graph.setMinimumSize(QtCore.QSize(300, 200))
|
||||||
self.baseline_graph.setObjectName("baseline_graph")
|
self.baseline_graph.setObjectName("baseline_graph")
|
||||||
self.gridLayout.addWidget(self.baseline_graph, 0, 1, 1, 1)
|
self.gridLayout.addWidget(self.baseline_graph, 0, 1, 1, 1)
|
||||||
self.end_graph = PlotWidget(Dialog)
|
self.end_graph = NMRPlotWidget(self.layoutWidget)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -186,28 +202,30 @@ class Ui_Dialog(object):
|
|||||||
self.end_graph.setMinimumSize(QtCore.QSize(0, 0))
|
self.end_graph.setMinimumSize(QtCore.QSize(0, 0))
|
||||||
self.end_graph.setObjectName("end_graph")
|
self.end_graph.setObjectName("end_graph")
|
||||||
self.gridLayout.addWidget(self.end_graph, 1, 1, 1, 1)
|
self.gridLayout.addWidget(self.end_graph, 1, 1, 1, 1)
|
||||||
self.gridLayout_2.addLayout(self.gridLayout, 0, 1, 1, 1)
|
self.verticalLayout_5.addWidget(self.splitter)
|
||||||
|
|
||||||
self.retranslateUi(Dialog)
|
self.retranslateUi(Dialog)
|
||||||
self.buttonBox.accepted.connect(Dialog.accept)
|
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
|
||||||
self.buttonBox.rejected.connect(Dialog.reject)
|
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
|
||||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
||||||
|
|
||||||
def retranslateUi(self, Dialog):
|
def retranslateUi(self, Dialog):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
_translate = QtCore.QCoreApplication.translate
|
||||||
Dialog.setWindowTitle(_translate("Dialog", "Read DSC file"))
|
Dialog.setWindowTitle(_translate("Dialog", "Read DSC file"))
|
||||||
self.cp_checkBox.setText(_translate("Dialog", "Convert to heat capacity"))
|
self.groupBox.setTitle(_translate("Dialog", "Detected steps"))
|
||||||
|
self.groupBox_2.setTitle(_translate("Dialog", "Baseline corrections"))
|
||||||
|
self.groupBox_4.setTitle(_translate("Dialog", "Empty measurement"))
|
||||||
|
self.empty_label.setText(_translate("Dialog", "No emtpy measurement"))
|
||||||
self.loadempty_button.setText(_translate("Dialog", "Load empty"))
|
self.loadempty_button.setText(_translate("Dialog", "Load empty"))
|
||||||
self.delempty_button.setText(_translate("Dialog", "Remove empty"))
|
self.delempty_button.setText(_translate("Dialog", "Remove empty"))
|
||||||
self.isotherm_radioButton.setText(_translate("Dialog", "Isotherms"))
|
self.groupBox_5.setTitle(_translate("Dialog", "Slope correction"))
|
||||||
self.label_4.setText(_translate("Dialog", "Detected steps"))
|
|
||||||
self.label.setText(_translate("Dialog", "Slope"))
|
|
||||||
self.slope_radioButton.setText(_translate("Dialog", "Initial slope"))
|
|
||||||
self.empty_label.setText(_translate("Dialog", "Empty measurement"))
|
|
||||||
self.label_3.setText(_translate("Dialog", "Calibration"))
|
|
||||||
self.label_2.setText(_translate("Dialog", "Baseline"))
|
|
||||||
self.none_radioButton.setText(_translate("Dialog", "None"))
|
self.none_radioButton.setText(_translate("Dialog", "None"))
|
||||||
|
self.isotherm_radioButton.setText(_translate("Dialog", "Isotherms"))
|
||||||
|
self.slope_radioButton.setText(_translate("Dialog", "Curve slope"))
|
||||||
|
self.limit1_lineedit.setPlaceholderText(_translate("Dialog", "start (in min)"))
|
||||||
|
self.limit2_lineedit.setPlaceholderText(_translate("Dialog", "stop (in min)"))
|
||||||
|
self.groupBox_3.setTitle(_translate("Dialog", "References"))
|
||||||
|
self.cp_checkBox.setText(_translate("Dialog", "Use reference to convert to heat capacity"))
|
||||||
self.ref_add_pushButton.setText(_translate("Dialog", "Add reference"))
|
self.ref_add_pushButton.setText(_translate("Dialog", "Add reference"))
|
||||||
self.ref_remove_pushButton.setText(_translate("Dialog", "Remove reference"))
|
self.ref_remove_pushButton.setText(_translate("Dialog", "Remove reference"))
|
||||||
|
from ..lib.graph_items import NMRPlotWidget
|
||||||
from pyqtgraph import PlotWidget
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/eval_expr_dialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/eval_expr_dialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.12.3
|
# Created by: PyQt5 UI code generator 5.15.2
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
@ -36,14 +37,14 @@ class Ui_CalcDialog(object):
|
|||||||
self.label_2 = QtWidgets.QLabel(self.page)
|
self.label_2 = QtWidgets.QLabel(self.page)
|
||||||
self.label_2.setObjectName("label_2")
|
self.label_2.setObjectName("label_2")
|
||||||
self.verticalLayout_2.addWidget(self.label_2)
|
self.verticalLayout_2.addWidget(self.label_2)
|
||||||
self.listWidget = QtWidgets.QListWidget(self.page)
|
self.listWidget = QListWidgetSelect(self.page)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
|
||||||
self.listWidget.setSizePolicy(sizePolicy)
|
self.listWidget.setSizePolicy(sizePolicy)
|
||||||
self.listWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
self.listWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||||
self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
|
self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
self.listWidget.setObjectName("listWidget")
|
self.listWidget.setObjectName("listWidget")
|
||||||
self.verticalLayout_2.addWidget(self.listWidget)
|
self.verticalLayout_2.addWidget(self.listWidget)
|
||||||
self.overwrite_checkbox = QtWidgets.QCheckBox(self.page)
|
self.overwrite_checkbox = QtWidgets.QCheckBox(self.page)
|
||||||
@ -202,7 +203,7 @@ class Ui_CalcDialog(object):
|
|||||||
self.label_8.setBuddy(self.line_doubleSpinBox)
|
self.label_8.setBuddy(self.line_doubleSpinBox)
|
||||||
|
|
||||||
self.retranslateUi(CalcDialog)
|
self.retranslateUi(CalcDialog)
|
||||||
self.stackedWidget.setCurrentIndex(2)
|
self.stackedWidget.setCurrentIndex(0)
|
||||||
QtCore.QMetaObject.connectSlotsByName(CalcDialog)
|
QtCore.QMetaObject.connectSlotsByName(CalcDialog)
|
||||||
CalcDialog.setTabOrder(self.calc_edit, self.listWidget)
|
CalcDialog.setTabOrder(self.calc_edit, self.listWidget)
|
||||||
CalcDialog.setTabOrder(self.listWidget, self.overwrite_checkbox)
|
CalcDialog.setTabOrder(self.listWidget, self.overwrite_checkbox)
|
||||||
@ -237,4 +238,5 @@ class Ui_CalcDialog(object):
|
|||||||
self.label_11.setText(_translate("CalcDialog", "Style"))
|
self.label_11.setText(_translate("CalcDialog", "Style"))
|
||||||
self.label.setText(_translate("CalcDialog", "Expressions are evaluated line by line and change previous values"))
|
self.label.setText(_translate("CalcDialog", "Expressions are evaluated line by line and change previous values"))
|
||||||
from ..lib.delegates import ColorListEditor, LineStyleEditor, SymbolStyleEditor
|
from ..lib.delegates import ColorListEditor, LineStyleEditor, SymbolStyleEditor
|
||||||
|
from ..lib.listwidget import QListWidgetSelect
|
||||||
from ..lib.namespace import QNamespaceWidget
|
from ..lib.namespace import QNamespaceWidget
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/fitdialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/fitdialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.12.3
|
# Created by: PyQt5 UI code generator 5.15.2
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
@ -37,7 +38,6 @@ class Ui_FitDialog(object):
|
|||||||
self.weight_combobox.addItem("")
|
self.weight_combobox.addItem("")
|
||||||
self.weight_combobox.addItem("")
|
self.weight_combobox.addItem("")
|
||||||
self.weight_combobox.addItem("")
|
self.weight_combobox.addItem("")
|
||||||
self.weight_combobox.addItem("")
|
|
||||||
self.gridLayout_2.addWidget(self.weight_combobox, 6, 1, 1, 1)
|
self.gridLayout_2.addWidget(self.weight_combobox, 6, 1, 1, 1)
|
||||||
self.newmodel_button = QtWidgets.QPushButton(self.scrollAreaWidgetContents_2)
|
self.newmodel_button = QtWidgets.QPushButton(self.scrollAreaWidgetContents_2)
|
||||||
self.newmodel_button.setEnabled(False)
|
self.newmodel_button.setEnabled(False)
|
||||||
@ -143,7 +143,6 @@ class Ui_FitDialog(object):
|
|||||||
self.weight_combobox.setItemText(1, _translate("FitDialog", "y"))
|
self.weight_combobox.setItemText(1, _translate("FitDialog", "y"))
|
||||||
self.weight_combobox.setItemText(2, _translate("FitDialog", "y²"))
|
self.weight_combobox.setItemText(2, _translate("FitDialog", "y²"))
|
||||||
self.weight_combobox.setItemText(3, _translate("FitDialog", "Δy"))
|
self.weight_combobox.setItemText(3, _translate("FitDialog", "Δy"))
|
||||||
self.weight_combobox.setItemText(4, _translate("FitDialog", "log(y)"))
|
|
||||||
self.newmodel_button.setText(_translate("FitDialog", "New model"))
|
self.newmodel_button.setText(_translate("FitDialog", "New model"))
|
||||||
self.deletemodel_button.setText(_translate("FitDialog", "Delete model"))
|
self.deletemodel_button.setText(_translate("FitDialog", "Delete model"))
|
||||||
self.label_3.setText(_translate("FitDialog", "Weight"))
|
self.label_3.setText(_translate("FitDialog", "Weight"))
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/fitmodelwidget.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/fitmodelwidget.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.12.3
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
@ -13,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
|||||||
class Ui_FitParameter(object):
|
class Ui_FitParameter(object):
|
||||||
def setupUi(self, FitParameter):
|
def setupUi(self, FitParameter):
|
||||||
FitParameter.setObjectName("FitParameter")
|
FitParameter.setObjectName("FitParameter")
|
||||||
FitParameter.resize(365, 78)
|
FitParameter.resize(365, 66)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -36,7 +37,7 @@ class Ui_FitParameter(object):
|
|||||||
self.parametername.setObjectName("parametername")
|
self.parametername.setObjectName("parametername")
|
||||||
self.horizontalLayout_2.addWidget(self.parametername)
|
self.horizontalLayout_2.addWidget(self.parametername)
|
||||||
self.parameter_line = LineEdit(FitParameter)
|
self.parameter_line = LineEdit(FitParameter)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth())
|
||||||
@ -44,20 +45,12 @@ class Ui_FitParameter(object):
|
|||||||
self.parameter_line.setText("")
|
self.parameter_line.setText("")
|
||||||
self.parameter_line.setObjectName("parameter_line")
|
self.parameter_line.setObjectName("parameter_line")
|
||||||
self.horizontalLayout_2.addWidget(self.parameter_line)
|
self.horizontalLayout_2.addWidget(self.parameter_line)
|
||||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
|
||||||
self.horizontalLayout_2.addItem(spacerItem)
|
|
||||||
self.fixed_check = QtWidgets.QCheckBox(FitParameter)
|
self.fixed_check = QtWidgets.QCheckBox(FitParameter)
|
||||||
self.fixed_check.setObjectName("fixed_check")
|
self.fixed_check.setObjectName("fixed_check")
|
||||||
self.horizontalLayout_2.addWidget(self.fixed_check)
|
self.horizontalLayout_2.addWidget(self.fixed_check)
|
||||||
self.global_checkbox = QtWidgets.QCheckBox(FitParameter)
|
self.global_checkbox = QtWidgets.QCheckBox(FitParameter)
|
||||||
self.global_checkbox.setObjectName("global_checkbox")
|
self.global_checkbox.setObjectName("global_checkbox")
|
||||||
self.horizontalLayout_2.addWidget(self.global_checkbox)
|
self.horizontalLayout_2.addWidget(self.global_checkbox)
|
||||||
self.toolButton = QtWidgets.QToolButton(FitParameter)
|
|
||||||
self.toolButton.setText("")
|
|
||||||
self.toolButton.setPopupMode(QtWidgets.QToolButton.InstantPopup)
|
|
||||||
self.toolButton.setArrowType(QtCore.Qt.RightArrow)
|
|
||||||
self.toolButton.setObjectName("toolButton")
|
|
||||||
self.horizontalLayout_2.addWidget(self.toolButton)
|
|
||||||
self.verticalLayout.addLayout(self.horizontalLayout_2)
|
self.verticalLayout.addLayout(self.horizontalLayout_2)
|
||||||
self.frame = QtWidgets.QFrame(FitParameter)
|
self.frame = QtWidgets.QFrame(FitParameter)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file './resources/_ui/fitresult.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/fitresult.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.15.4
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
@ -14,117 +14,41 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
|||||||
class Ui_Dialog(object):
|
class Ui_Dialog(object):
|
||||||
def setupUi(self, Dialog):
|
def setupUi(self, Dialog):
|
||||||
Dialog.setObjectName("Dialog")
|
Dialog.setObjectName("Dialog")
|
||||||
Dialog.resize(817, 584)
|
Dialog.resize(969, 974)
|
||||||
self.gridLayout = QtWidgets.QGridLayout(Dialog)
|
self.gridLayout = QtWidgets.QGridLayout(Dialog)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
self.sets_comboBox = ElideComboBox(Dialog)
|
self.stack = QtWidgets.QTabWidget(Dialog)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.sets_comboBox.sizePolicy().hasHeightForWidth())
|
|
||||||
self.sets_comboBox.setSizePolicy(sizePolicy)
|
|
||||||
self.sets_comboBox.setMaximumSize(QtCore.QSize(400, 16777215))
|
|
||||||
self.sets_comboBox.setBaseSize(QtCore.QSize(200, 0))
|
|
||||||
self.sets_comboBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
|
|
||||||
self.sets_comboBox.setObjectName("sets_comboBox")
|
|
||||||
self.gridLayout.addWidget(self.sets_comboBox, 0, 0, 1, 1)
|
|
||||||
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
|
|
||||||
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Retry)
|
|
||||||
self.buttonBox.setObjectName("buttonBox")
|
|
||||||
self.gridLayout.addWidget(self.buttonBox, 6, 0, 1, 2)
|
|
||||||
self.param_tableWidget = QtWidgets.QTableWidget(Dialog)
|
|
||||||
self.param_tableWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
|
||||||
self.param_tableWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored)
|
|
||||||
self.param_tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
|
||||||
self.param_tableWidget.setAlternatingRowColors(True)
|
|
||||||
self.param_tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
|
|
||||||
self.param_tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectColumns)
|
|
||||||
self.param_tableWidget.setShowGrid(False)
|
|
||||||
self.param_tableWidget.setColumnCount(0)
|
|
||||||
self.param_tableWidget.setObjectName("param_tableWidget")
|
|
||||||
self.param_tableWidget.setRowCount(0)
|
|
||||||
self.param_tableWidget.horizontalHeader().setStretchLastSection(False)
|
|
||||||
self.gridLayout.addWidget(self.param_tableWidget, 1, 0, 1, 1)
|
|
||||||
self.groupBox = QtWidgets.QGroupBox(Dialog)
|
|
||||||
self.groupBox.setObjectName("groupBox")
|
|
||||||
self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox)
|
|
||||||
self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
|
|
||||||
self.gridLayout_2.setSpacing(3)
|
|
||||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
|
||||||
self.graph_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.graph_checkBox.sizePolicy().hasHeightForWidth())
|
|
||||||
self.graph_checkBox.setSizePolicy(sizePolicy)
|
|
||||||
self.graph_checkBox.setChecked(True)
|
|
||||||
self.graph_checkBox.setObjectName("graph_checkBox")
|
|
||||||
self.gridLayout_2.addWidget(self.graph_checkBox, 1, 1, 1, 1)
|
|
||||||
self.graph_comboBox = QtWidgets.QComboBox(self.groupBox)
|
|
||||||
self.graph_comboBox.setEnabled(False)
|
|
||||||
self.graph_comboBox.setObjectName("graph_comboBox")
|
|
||||||
self.gridLayout_2.addWidget(self.graph_comboBox, 1, 2, 1, 1)
|
|
||||||
self.curve_checkbox = QtWidgets.QCheckBox(self.groupBox)
|
|
||||||
self.curve_checkbox.setChecked(True)
|
|
||||||
self.curve_checkbox.setObjectName("curve_checkbox")
|
|
||||||
self.gridLayout_2.addWidget(self.curve_checkbox, 0, 0, 1, 1)
|
|
||||||
self.partial_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
|
||||||
self.partial_checkBox.setObjectName("partial_checkBox")
|
|
||||||
self.gridLayout_2.addWidget(self.partial_checkBox, 1, 0, 1, 1)
|
|
||||||
self.parameter_checkbox = QtWidgets.QCheckBox(self.groupBox)
|
|
||||||
self.parameter_checkbox.setChecked(True)
|
|
||||||
self.parameter_checkbox.setObjectName("parameter_checkbox")
|
|
||||||
self.gridLayout_2.addWidget(self.parameter_checkbox, 0, 1, 1, 1)
|
|
||||||
self.gridLayout.addWidget(self.groupBox, 5, 0, 1, 2)
|
|
||||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
|
||||||
self.horizontalLayout_2.setSpacing(3)
|
|
||||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
|
||||||
self.reject_fit_checkBox = QtWidgets.QCheckBox(Dialog)
|
|
||||||
self.reject_fit_checkBox.setObjectName("reject_fit_checkBox")
|
|
||||||
self.horizontalLayout_2.addWidget(self.reject_fit_checkBox)
|
|
||||||
self.del_prev_checkBox = QtWidgets.QCheckBox(Dialog)
|
|
||||||
self.del_prev_checkBox.setObjectName("del_prev_checkBox")
|
|
||||||
self.horizontalLayout_2.addWidget(self.del_prev_checkBox)
|
|
||||||
self.gridLayout.addLayout(self.horizontalLayout_2, 2, 0, 1, 1)
|
|
||||||
self.line = QtWidgets.QFrame(Dialog)
|
|
||||||
self.line.setFrameShape(QtWidgets.QFrame.HLine)
|
|
||||||
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
|
||||||
self.line.setObjectName("line")
|
|
||||||
self.gridLayout.addWidget(self.line, 3, 0, 1, 2)
|
|
||||||
self.stack = QtWidgets.QToolBox(Dialog)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.stack.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.stack.sizePolicy().hasHeightForWidth())
|
||||||
self.stack.setSizePolicy(sizePolicy)
|
self.stack.setSizePolicy(sizePolicy)
|
||||||
self.stack.setObjectName("stack")
|
self.stack.setObjectName("stack")
|
||||||
self.page = QtWidgets.QWidget()
|
self.stackPage1 = QtWidgets.QWidget()
|
||||||
self.page.setGeometry(QtCore.QRect(0, 0, 399, 346))
|
self.stackPage1.setObjectName("stackPage1")
|
||||||
self.page.setObjectName("page")
|
self.gridLayout_3 = QtWidgets.QGridLayout(self.stackPage1)
|
||||||
self.gridLayout_3 = QtWidgets.QGridLayout(self.page)
|
|
||||||
self.gridLayout_3.setContentsMargins(3, 3, 3, 3)
|
self.gridLayout_3.setContentsMargins(3, 3, 3, 3)
|
||||||
self.gridLayout_3.setSpacing(3)
|
self.gridLayout_3.setSpacing(3)
|
||||||
self.gridLayout_3.setObjectName("gridLayout_3")
|
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||||
self.logy_box = QtWidgets.QCheckBox(self.page)
|
self.logy_box = QtWidgets.QCheckBox(self.stackPage1)
|
||||||
self.logy_box.setLayoutDirection(QtCore.Qt.RightToLeft)
|
self.logy_box.setLayoutDirection(QtCore.Qt.RightToLeft)
|
||||||
self.logy_box.setObjectName("logy_box")
|
self.logy_box.setObjectName("logy_box")
|
||||||
self.gridLayout_3.addWidget(self.logy_box, 2, 1, 1, 1)
|
self.gridLayout_3.addWidget(self.logy_box, 2, 1, 1, 1)
|
||||||
self.logx_box = QtWidgets.QCheckBox(self.page)
|
self.logx_box = QtWidgets.QCheckBox(self.stackPage1)
|
||||||
self.logx_box.setLayoutDirection(QtCore.Qt.RightToLeft)
|
self.logx_box.setLayoutDirection(QtCore.Qt.RightToLeft)
|
||||||
self.logx_box.setObjectName("logx_box")
|
self.logx_box.setObjectName("logx_box")
|
||||||
self.gridLayout_3.addWidget(self.logx_box, 2, 0, 1, 1)
|
self.gridLayout_3.addWidget(self.logx_box, 2, 0, 1, 1)
|
||||||
self.graphicsView = GraphicsLayoutWidget(self.page)
|
self.graphicsView = GraphicsLayoutWidget(self.stackPage1)
|
||||||
self.graphicsView.setObjectName("graphicsView")
|
self.graphicsView.setObjectName("graphicsView")
|
||||||
self.gridLayout_3.addWidget(self.graphicsView, 0, 0, 1, 2)
|
self.gridLayout_3.addWidget(self.graphicsView, 0, 0, 1, 2)
|
||||||
self.stack.addItem(self.page, "")
|
self.stack.addTab(self.stackPage1, "")
|
||||||
self.page_2 = QtWidgets.QWidget()
|
self.stackPage2 = QtWidgets.QWidget()
|
||||||
self.page_2.setGeometry(QtCore.QRect(0, 0, 399, 346))
|
self.stackPage2.setObjectName("stackPage2")
|
||||||
self.page_2.setObjectName("page_2")
|
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.stackPage2)
|
||||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.page_2)
|
|
||||||
self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
|
self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
|
||||||
self.verticalLayout_2.setSpacing(3)
|
self.verticalLayout_2.setSpacing(3)
|
||||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||||
self.stats_tableWidget = QtWidgets.QTableWidget(self.page_2)
|
self.stats_tableWidget = QtWidgets.QTableWidget(self.stackPage2)
|
||||||
self.stats_tableWidget.setFrameShape(QtWidgets.QFrame.Box)
|
self.stats_tableWidget.setFrameShape(QtWidgets.QFrame.Box)
|
||||||
self.stats_tableWidget.setGridStyle(QtCore.Qt.NoPen)
|
self.stats_tableWidget.setGridStyle(QtCore.Qt.NoPen)
|
||||||
self.stats_tableWidget.setColumnCount(1)
|
self.stats_tableWidget.setColumnCount(1)
|
||||||
@ -133,15 +57,14 @@ class Ui_Dialog(object):
|
|||||||
self.stats_tableWidget.horizontalHeader().setVisible(False)
|
self.stats_tableWidget.horizontalHeader().setVisible(False)
|
||||||
self.stats_tableWidget.horizontalHeader().setSortIndicatorShown(True)
|
self.stats_tableWidget.horizontalHeader().setSortIndicatorShown(True)
|
||||||
self.verticalLayout_2.addWidget(self.stats_tableWidget)
|
self.verticalLayout_2.addWidget(self.stats_tableWidget)
|
||||||
self.stack.addItem(self.page_2, "")
|
self.stack.addTab(self.stackPage2, "")
|
||||||
self.page_3 = QtWidgets.QWidget()
|
self.stackPage3 = QtWidgets.QWidget()
|
||||||
self.page_3.setGeometry(QtCore.QRect(0, 0, 399, 346))
|
self.stackPage3.setObjectName("stackPage3")
|
||||||
self.page_3.setObjectName("page_3")
|
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.stackPage3)
|
||||||
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.page_3)
|
|
||||||
self.verticalLayout_3.setContentsMargins(3, 3, 3, 3)
|
self.verticalLayout_3.setContentsMargins(3, 3, 3, 3)
|
||||||
self.verticalLayout_3.setSpacing(3)
|
self.verticalLayout_3.setSpacing(3)
|
||||||
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||||
self.corr_tableWidget = QtWidgets.QTableWidget(self.page_3)
|
self.corr_tableWidget = QtWidgets.QTableWidget(self.stackPage3)
|
||||||
self.corr_tableWidget.setFrameShape(QtWidgets.QFrame.Box)
|
self.corr_tableWidget.setFrameShape(QtWidgets.QFrame.Box)
|
||||||
self.corr_tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
self.corr_tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||||
self.corr_tableWidget.setGridStyle(QtCore.Qt.NoPen)
|
self.corr_tableWidget.setGridStyle(QtCore.Qt.NoPen)
|
||||||
@ -159,28 +82,131 @@ class Ui_Dialog(object):
|
|||||||
self.corr_tableWidget.horizontalHeader().setStretchLastSection(True)
|
self.corr_tableWidget.horizontalHeader().setStretchLastSection(True)
|
||||||
self.corr_tableWidget.verticalHeader().setVisible(False)
|
self.corr_tableWidget.verticalHeader().setVisible(False)
|
||||||
self.verticalLayout_3.addWidget(self.corr_tableWidget)
|
self.verticalLayout_3.addWidget(self.corr_tableWidget)
|
||||||
self.stack.addItem(self.page_3, "")
|
self.stack.addTab(self.stackPage3, "")
|
||||||
self.gridLayout.addWidget(self.stack, 0, 1, 3, 1)
|
self.gridLayout.addWidget(self.stack, 0, 1, 5, 1)
|
||||||
|
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
|
||||||
|
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Retry)
|
||||||
|
self.buttonBox.setObjectName("buttonBox")
|
||||||
|
self.gridLayout.addWidget(self.buttonBox, 8, 0, 1, 2)
|
||||||
|
self.param_tableWidget = QtWidgets.QTableWidget(Dialog)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.param_tableWidget.sizePolicy().hasHeightForWidth())
|
||||||
|
self.param_tableWidget.setSizePolicy(sizePolicy)
|
||||||
|
self.param_tableWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||||
|
self.param_tableWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored)
|
||||||
|
self.param_tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||||
|
self.param_tableWidget.setAlternatingRowColors(True)
|
||||||
|
self.param_tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||||
|
self.param_tableWidget.setColumnCount(1)
|
||||||
|
self.param_tableWidget.setObjectName("param_tableWidget")
|
||||||
|
self.param_tableWidget.setRowCount(0)
|
||||||
|
self.param_tableWidget.horizontalHeader().setVisible(False)
|
||||||
|
self.param_tableWidget.horizontalHeader().setStretchLastSection(True)
|
||||||
|
self.gridLayout.addWidget(self.param_tableWidget, 1, 0, 1, 1)
|
||||||
|
self.del_prev_checkBox = QtWidgets.QCheckBox(Dialog)
|
||||||
|
self.del_prev_checkBox.setObjectName("del_prev_checkBox")
|
||||||
|
self.gridLayout.addWidget(self.del_prev_checkBox, 3, 0, 1, 1)
|
||||||
|
self.line = QtWidgets.QFrame(Dialog)
|
||||||
|
self.line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
|
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
|
self.line.setObjectName("line")
|
||||||
|
self.gridLayout.addWidget(self.line, 5, 0, 1, 2)
|
||||||
|
self.groupBox = QtWidgets.QGroupBox(Dialog)
|
||||||
|
self.groupBox.setObjectName("groupBox")
|
||||||
|
self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox)
|
||||||
|
self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
|
||||||
|
self.gridLayout_2.setSpacing(3)
|
||||||
|
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||||
|
self.extrapolate_box = QtWidgets.QCheckBox(self.groupBox)
|
||||||
|
self.extrapolate_box.setObjectName("extrapolate_box")
|
||||||
|
self.gridLayout_2.addWidget(self.extrapolate_box, 1, 0, 1, 1)
|
||||||
|
self.parameter_checkbox = QtWidgets.QCheckBox(self.groupBox)
|
||||||
|
self.parameter_checkbox.setObjectName("parameter_checkbox")
|
||||||
|
self.gridLayout_2.addWidget(self.parameter_checkbox, 0, 5, 1, 1)
|
||||||
|
self.graph_comboBox = QtWidgets.QComboBox(self.groupBox)
|
||||||
|
self.graph_comboBox.setEnabled(False)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.graph_comboBox.sizePolicy().hasHeightForWidth())
|
||||||
|
self.graph_comboBox.setSizePolicy(sizePolicy)
|
||||||
|
self.graph_comboBox.setObjectName("graph_comboBox")
|
||||||
|
self.gridLayout_2.addWidget(self.graph_comboBox, 1, 6, 1, 1)
|
||||||
|
self.graph_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.graph_checkBox.sizePolicy().hasHeightForWidth())
|
||||||
|
self.graph_checkBox.setSizePolicy(sizePolicy)
|
||||||
|
self.graph_checkBox.setChecked(True)
|
||||||
|
self.graph_checkBox.setObjectName("graph_checkBox")
|
||||||
|
self.gridLayout_2.addWidget(self.graph_checkBox, 1, 5, 1, 1)
|
||||||
|
self.minx_line = QtWidgets.QLineEdit(self.groupBox)
|
||||||
|
self.minx_line.setEnabled(False)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.minx_line.sizePolicy().hasHeightForWidth())
|
||||||
|
self.minx_line.setSizePolicy(sizePolicy)
|
||||||
|
self.minx_line.setObjectName("minx_line")
|
||||||
|
self.gridLayout_2.addWidget(self.minx_line, 1, 1, 1, 1)
|
||||||
|
self.line_2 = QtWidgets.QFrame(self.groupBox)
|
||||||
|
self.line_2.setFrameShape(QtWidgets.QFrame.VLine)
|
||||||
|
self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
|
self.line_2.setObjectName("line_2")
|
||||||
|
self.gridLayout_2.addWidget(self.line_2, 0, 4, 2, 1)
|
||||||
|
self.maxx_line = QtWidgets.QLineEdit(self.groupBox)
|
||||||
|
self.maxx_line.setEnabled(False)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.maxx_line.sizePolicy().hasHeightForWidth())
|
||||||
|
self.maxx_line.setSizePolicy(sizePolicy)
|
||||||
|
self.maxx_line.setObjectName("maxx_line")
|
||||||
|
self.gridLayout_2.addWidget(self.maxx_line, 1, 2, 1, 1)
|
||||||
|
self.numx_line = QtWidgets.QLineEdit(self.groupBox)
|
||||||
|
self.numx_line.setEnabled(False)
|
||||||
|
self.numx_line.setObjectName("numx_line")
|
||||||
|
self.gridLayout_2.addWidget(self.numx_line, 1, 3, 1, 1)
|
||||||
|
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||||
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
|
self.curve_checkbox = QtWidgets.QCheckBox(self.groupBox)
|
||||||
|
self.curve_checkbox.setChecked(True)
|
||||||
|
self.curve_checkbox.setObjectName("curve_checkbox")
|
||||||
|
self.horizontalLayout.addWidget(self.curve_checkbox)
|
||||||
|
self.partial_checkBox = QtWidgets.QCheckBox(self.groupBox)
|
||||||
|
self.partial_checkBox.setObjectName("partial_checkBox")
|
||||||
|
self.horizontalLayout.addWidget(self.partial_checkBox)
|
||||||
|
self.gridLayout_2.addLayout(self.horizontalLayout, 0, 0, 1, 4)
|
||||||
|
self.gridLayout.addWidget(self.groupBox, 7, 0, 1, 2)
|
||||||
|
self.sets_comboBox = ElideComboBox(Dialog)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.sets_comboBox.sizePolicy().hasHeightForWidth())
|
||||||
|
self.sets_comboBox.setSizePolicy(sizePolicy)
|
||||||
|
self.sets_comboBox.setMaximumSize(QtCore.QSize(400, 16777215))
|
||||||
|
self.sets_comboBox.setBaseSize(QtCore.QSize(200, 0))
|
||||||
|
self.sets_comboBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
|
||||||
|
self.sets_comboBox.setObjectName("sets_comboBox")
|
||||||
|
self.gridLayout.addWidget(self.sets_comboBox, 0, 0, 1, 1)
|
||||||
|
self.reject_fit_checkBox = QtWidgets.QCheckBox(Dialog)
|
||||||
|
self.reject_fit_checkBox.setObjectName("reject_fit_checkBox")
|
||||||
|
self.gridLayout.addWidget(self.reject_fit_checkBox, 2, 0, 1, 1)
|
||||||
|
|
||||||
self.retranslateUi(Dialog)
|
self.retranslateUi(Dialog)
|
||||||
self.stack.setCurrentIndex(0)
|
self.stack.setCurrentIndex(0)
|
||||||
self.stack.layout().setSpacing(0)
|
|
||||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
||||||
|
|
||||||
def retranslateUi(self, Dialog):
|
def retranslateUi(self, Dialog):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
_translate = QtCore.QCoreApplication.translate
|
||||||
Dialog.setWindowTitle(_translate("Dialog", "Fit results"))
|
Dialog.setWindowTitle(_translate("Dialog", "Fit results"))
|
||||||
self.groupBox.setTitle(_translate("Dialog", "Output"))
|
|
||||||
self.graph_checkBox.setText(_translate("Dialog", "New graph"))
|
|
||||||
self.curve_checkbox.setText(_translate("Dialog", "Plot fit curve"))
|
|
||||||
self.partial_checkBox.setText(_translate("Dialog", "Plot partial functions"))
|
|
||||||
self.parameter_checkbox.setText(_translate("Dialog", "Plot parameter"))
|
|
||||||
self.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit"))
|
|
||||||
self.del_prev_checkBox.setText(_translate("Dialog", "Delete previous fits"))
|
|
||||||
self.logy_box.setText(_translate("Dialog", "logarithmic y axis"))
|
self.logy_box.setText(_translate("Dialog", "logarithmic y axis"))
|
||||||
self.logx_box.setText(_translate("Dialog", "logarithmic x axis"))
|
self.logx_box.setText(_translate("Dialog", "logarithmic x axis"))
|
||||||
self.stack.setItemText(self.stack.indexOf(self.page), _translate("Dialog", "Plot"))
|
self.stack.setTabText(self.stack.indexOf(self.stackPage1), _translate("Dialog", "Plot"))
|
||||||
self.stack.setItemText(self.stack.indexOf(self.page_2), _translate("Dialog", "Statistics"))
|
self.stack.setTabText(self.stack.indexOf(self.stackPage2), _translate("Dialog", "Statistics"))
|
||||||
item = self.corr_tableWidget.horizontalHeaderItem(0)
|
item = self.corr_tableWidget.horizontalHeaderItem(0)
|
||||||
item.setText(_translate("Dialog", "Parameter 1"))
|
item.setText(_translate("Dialog", "Parameter 1"))
|
||||||
item = self.corr_tableWidget.horizontalHeaderItem(1)
|
item = self.corr_tableWidget.horizontalHeaderItem(1)
|
||||||
@ -189,6 +215,20 @@ class Ui_Dialog(object):
|
|||||||
item.setText(_translate("Dialog", "Corr."))
|
item.setText(_translate("Dialog", "Corr."))
|
||||||
item = self.corr_tableWidget.horizontalHeaderItem(3)
|
item = self.corr_tableWidget.horizontalHeaderItem(3)
|
||||||
item.setText(_translate("Dialog", "Partial Corr."))
|
item.setText(_translate("Dialog", "Partial Corr."))
|
||||||
self.stack.setItemText(self.stack.indexOf(self.page_3), _translate("Dialog", "Correlations"))
|
self.stack.setTabText(self.stack.indexOf(self.stackPage3), _translate("Dialog", "Correlations"))
|
||||||
|
self.del_prev_checkBox.setText(_translate("Dialog", "Delete previous fits of this set"))
|
||||||
|
self.groupBox.setTitle(_translate("Dialog", "Output"))
|
||||||
|
self.extrapolate_box.setToolTip(_translate("Dialog", "Extrapolates only main function"))
|
||||||
|
self.extrapolate_box.setText(_translate("Dialog", "Extrapolate curves"))
|
||||||
|
self.parameter_checkbox.setText(_translate("Dialog", "Plot parameter"))
|
||||||
|
self.graph_checkBox.setText(_translate("Dialog", "New graph for parameter"))
|
||||||
|
self.minx_line.setToolTip(_translate("Dialog", "Leave empty to start at lowest point"))
|
||||||
|
self.minx_line.setPlaceholderText(_translate("Dialog", "min x"))
|
||||||
|
self.maxx_line.setToolTip(_translate("Dialog", "Leave empty to start at highest point"))
|
||||||
|
self.maxx_line.setPlaceholderText(_translate("Dialog", "max x"))
|
||||||
|
self.numx_line.setPlaceholderText(_translate("Dialog", "# pts"))
|
||||||
|
self.curve_checkbox.setText(_translate("Dialog", "Plot fit curve"))
|
||||||
|
self.partial_checkBox.setText(_translate("Dialog", "Plot partial functions"))
|
||||||
|
self.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit"))
|
||||||
from ..lib.forms import ElideComboBox
|
from ..lib.forms import ElideComboBox
|
||||||
from pyqtgraph import GraphicsLayoutWidget
|
from pyqtgraph import GraphicsLayoutWidget
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/graph.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/graph.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.12.3
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
@ -196,19 +197,20 @@ class Ui_GraphWindow(object):
|
|||||||
self.gridLayout.setHorizontalSpacing(3)
|
self.gridLayout.setHorizontalSpacing(3)
|
||||||
self.gridLayout.setVerticalSpacing(0)
|
self.gridLayout.setVerticalSpacing(0)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
self.listWidget = QtWidgets.QListWidget(GraphWindow)
|
self.listWidget = QListWidgetSelect(GraphWindow)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
|
||||||
self.listWidget.setSizePolicy(sizePolicy)
|
self.listWidget.setSizePolicy(sizePolicy)
|
||||||
|
self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
self.listWidget.setObjectName("listWidget")
|
self.listWidget.setObjectName("listWidget")
|
||||||
self.gridLayout.addWidget(self.listWidget, 1, 1, 1, 1)
|
self.gridLayout.addWidget(self.listWidget, 1, 1, 1, 1)
|
||||||
self.checkBox = QtWidgets.QCheckBox(GraphWindow)
|
self.checkBox = QtWidgets.QCheckBox(GraphWindow)
|
||||||
self.checkBox.setChecked(True)
|
self.checkBox.setChecked(True)
|
||||||
self.checkBox.setObjectName("checkBox")
|
self.checkBox.setObjectName("checkBox")
|
||||||
self.gridLayout.addWidget(self.checkBox, 0, 1, 1, 1)
|
self.gridLayout.addWidget(self.checkBox, 0, 1, 1, 1)
|
||||||
self.graphic = PlotWidget(GraphWindow)
|
self.graphic = NMRPlotWidget(GraphWindow)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -272,4 +274,5 @@ class Ui_GraphWindow(object):
|
|||||||
self.label_6.setText(_translate("GraphWindow", "X Axis"))
|
self.label_6.setText(_translate("GraphWindow", "X Axis"))
|
||||||
self.label_7.setText(_translate("GraphWindow", "Y Axis"))
|
self.label_7.setText(_translate("GraphWindow", "Y Axis"))
|
||||||
self.checkBox.setText(_translate("GraphWindow", "Show legend"))
|
self.checkBox.setText(_translate("GraphWindow", "Show legend"))
|
||||||
from pyqtgraph import PlotWidget
|
from ..lib.graph_items import NMRPlotWidget
|
||||||
|
from ..lib.listwidget import QListWidgetSelect
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file '/autohome/dominik/nmreval/src/resources/_ui/integral_widget.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/integral_widget.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.15.4
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
@ -26,7 +26,6 @@ class Ui_Form(object):
|
|||||||
self.set_combobox.setObjectName("set_combobox")
|
self.set_combobox.setObjectName("set_combobox")
|
||||||
self.verticalLayout.addWidget(self.set_combobox)
|
self.verticalLayout.addWidget(self.set_combobox)
|
||||||
self.treeWidget = QtWidgets.QTreeWidget(Form)
|
self.treeWidget = QtWidgets.QTreeWidget(Form)
|
||||||
self.treeWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
|
||||||
self.treeWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
self.treeWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||||
self.treeWidget.setHeaderHidden(True)
|
self.treeWidget.setHeaderHidden(True)
|
||||||
self.treeWidget.setObjectName("treeWidget")
|
self.treeWidget.setObjectName("treeWidget")
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/integratederive_dialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/integratederive_dialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.12.3
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
@ -13,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
|||||||
class Ui_Dialog(object):
|
class Ui_Dialog(object):
|
||||||
def setupUi(self, Dialog):
|
def setupUi(self, Dialog):
|
||||||
Dialog.setObjectName("Dialog")
|
Dialog.setObjectName("Dialog")
|
||||||
Dialog.resize(400, 308)
|
Dialog.resize(400, 375)
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
|
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
self.listWidget = QtWidgets.QListWidget(Dialog)
|
self.listWidget = QtWidgets.QListWidget(Dialog)
|
||||||
@ -49,6 +50,9 @@ class Ui_Dialog(object):
|
|||||||
self.log_checkbox = QtWidgets.QCheckBox(Dialog)
|
self.log_checkbox = QtWidgets.QCheckBox(Dialog)
|
||||||
self.log_checkbox.setObjectName("log_checkbox")
|
self.log_checkbox.setObjectName("log_checkbox")
|
||||||
self.verticalLayout.addWidget(self.log_checkbox)
|
self.verticalLayout.addWidget(self.log_checkbox)
|
||||||
|
self.freq_box = QtWidgets.QCheckBox(Dialog)
|
||||||
|
self.freq_box.setObjectName("freq_box")
|
||||||
|
self.verticalLayout.addWidget(self.freq_box)
|
||||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||||
self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
|
self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
|
||||||
self.horizontalLayout_2.setSpacing(3)
|
self.horizontalLayout_2.setSpacing(3)
|
||||||
@ -67,8 +71,8 @@ class Ui_Dialog(object):
|
|||||||
self.verticalLayout.addWidget(self.buttonBox)
|
self.verticalLayout.addWidget(self.buttonBox)
|
||||||
|
|
||||||
self.retranslateUi(Dialog)
|
self.retranslateUi(Dialog)
|
||||||
self.buttonBox.accepted.connect(Dialog.accept)
|
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
|
||||||
self.buttonBox.rejected.connect(Dialog.reject)
|
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
|
||||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
||||||
|
|
||||||
def retranslateUi(self, Dialog):
|
def retranslateUi(self, Dialog):
|
||||||
@ -80,4 +84,5 @@ class Ui_Dialog(object):
|
|||||||
self.ft_comboBox.setItemText(1, _translate("Dialog", "Imag"))
|
self.ft_comboBox.setItemText(1, _translate("Dialog", "Imag"))
|
||||||
self.ft_comboBox.setItemText(2, _translate("Dialog", "Complex"))
|
self.ft_comboBox.setItemText(2, _translate("Dialog", "Complex"))
|
||||||
self.log_checkbox.setText(_translate("Dialog", "use logarithmic x axis"))
|
self.log_checkbox.setText(_translate("Dialog", "use logarithmic x axis"))
|
||||||
|
self.freq_box.setText(_translate("Dialog", "return x axis as f, not omega"))
|
||||||
self.newgraph_checkbox.setText(_translate("Dialog", "New graph"))
|
self.newgraph_checkbox.setText(_translate("Dialog", "New graph"))
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/interpol_dialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/interpol_dialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.12.3
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
@ -65,12 +66,13 @@ class Ui_Dialog(object):
|
|||||||
self.label_2 = QtWidgets.QLabel(Dialog)
|
self.label_2 = QtWidgets.QLabel(Dialog)
|
||||||
self.label_2.setObjectName("label_2")
|
self.label_2.setObjectName("label_2")
|
||||||
self.gridLayout.addWidget(self.label_2, 6, 0, 1, 1)
|
self.gridLayout.addWidget(self.label_2, 6, 0, 1, 1)
|
||||||
self.listWidget = QtWidgets.QListWidget(Dialog)
|
self.listWidget = QListWidgetSelect(Dialog)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
|
||||||
self.listWidget.setSizePolicy(sizePolicy)
|
self.listWidget.setSizePolicy(sizePolicy)
|
||||||
|
self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
||||||
self.listWidget.setObjectName("listWidget")
|
self.listWidget.setObjectName("listWidget")
|
||||||
self.gridLayout.addWidget(self.listWidget, 1, 0, 1, 2)
|
self.gridLayout.addWidget(self.listWidget, 1, 0, 1, 2)
|
||||||
self.sampling_widget = QtWidgets.QWidget(Dialog)
|
self.sampling_widget = QtWidgets.QWidget(Dialog)
|
||||||
@ -130,8 +132,8 @@ class Ui_Dialog(object):
|
|||||||
self.label_8.setBuddy(self.dest_combobox)
|
self.label_8.setBuddy(self.dest_combobox)
|
||||||
|
|
||||||
self.retranslateUi(Dialog)
|
self.retranslateUi(Dialog)
|
||||||
self.buttonBox.accepted.connect(Dialog.accept)
|
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
|
||||||
self.buttonBox.rejected.connect(Dialog.reject)
|
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
|
||||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
||||||
Dialog.setTabOrder(self.listWidget, self.ylog_checkBox)
|
Dialog.setTabOrder(self.listWidget, self.ylog_checkBox)
|
||||||
Dialog.setTabOrder(self.ylog_checkBox, self.interp_comboBox)
|
Dialog.setTabOrder(self.ylog_checkBox, self.interp_comboBox)
|
||||||
@ -164,3 +166,4 @@ class Ui_Dialog(object):
|
|||||||
self.xaxis_comboBox.setItemText(1, _translate("Dialog", "from data"))
|
self.xaxis_comboBox.setItemText(1, _translate("Dialog", "from data"))
|
||||||
self.label_8.setText(_translate("Dialog", "Add interpolated data to"))
|
self.label_8.setText(_translate("Dialog", "Add interpolated data to"))
|
||||||
self.xlog_checkBox.setText(_translate("Dialog", "use log(x)"))
|
self.xlog_checkBox.setText(_translate("Dialog", "use log(x)"))
|
||||||
|
from ..lib.listwidget import QListWidgetSelect
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/phase_corr_dialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/phase_corr_dialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.15.4
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
@ -24,7 +24,7 @@ class Ui_SignalEdit(object):
|
|||||||
self.gridLayout.setContentsMargins(6, 6, 6, 6)
|
self.gridLayout.setContentsMargins(6, 6, 6, 6)
|
||||||
self.gridLayout.setSpacing(3)
|
self.gridLayout.setSpacing(3)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
self.graphicsView = PlotWidget(SignalEdit)
|
self.graphicsView = NMRPlotWidget(SignalEdit)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -83,8 +83,8 @@ class Ui_SignalEdit(object):
|
|||||||
self.gridLayout.addItem(spacerItem1, 1, 5, 1, 1)
|
self.gridLayout.addItem(spacerItem1, 1, 5, 1, 1)
|
||||||
|
|
||||||
self.retranslateUi(SignalEdit)
|
self.retranslateUi(SignalEdit)
|
||||||
self.buttonBox.accepted.connect(SignalEdit.accept)
|
self.buttonBox.accepted.connect(SignalEdit.accept) # type: ignore
|
||||||
self.buttonBox.rejected.connect(SignalEdit.close)
|
self.buttonBox.rejected.connect(SignalEdit.close) # type: ignore
|
||||||
QtCore.QMetaObject.connectSlotsByName(SignalEdit)
|
QtCore.QMetaObject.connectSlotsByName(SignalEdit)
|
||||||
|
|
||||||
def retranslateUi(self, SignalEdit):
|
def retranslateUi(self, SignalEdit):
|
||||||
@ -94,4 +94,4 @@ class Ui_SignalEdit(object):
|
|||||||
self.label_8.setText(_translate("SignalEdit", "Pivot"))
|
self.label_8.setText(_translate("SignalEdit", "Pivot"))
|
||||||
self.label_6.setText(_translate("SignalEdit", "Phase 1"))
|
self.label_6.setText(_translate("SignalEdit", "Phase 1"))
|
||||||
self.label.setText(_translate("SignalEdit", "Phase 0"))
|
self.label.setText(_translate("SignalEdit", "Phase 0"))
|
||||||
from pyqtgraph import PlotWidget
|
from ..lib.graph_items import NMRPlotWidget
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/shift_scale_dialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/shift_scale_dialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.12.3
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
@ -162,7 +163,7 @@ class Ui_shift_dialog(object):
|
|||||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalFrame_2)
|
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalFrame_2)
|
||||||
self.verticalLayout_2.setSpacing(3)
|
self.verticalLayout_2.setSpacing(3)
|
||||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||||
self.graphicsView = PlotWidget(self.verticalFrame_2)
|
self.graphicsView = NMRPlotWidget(self.verticalFrame_2)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
@ -267,8 +268,8 @@ class Ui_shift_dialog(object):
|
|||||||
|
|
||||||
self.retranslateUi(shift_dialog)
|
self.retranslateUi(shift_dialog)
|
||||||
self.tabWidget.setCurrentIndex(0)
|
self.tabWidget.setCurrentIndex(0)
|
||||||
self.buttonBox.accepted.connect(shift_dialog.accept)
|
self.buttonBox.accepted.connect(shift_dialog.accept) # type: ignore
|
||||||
self.buttonBox.rejected.connect(shift_dialog.reject)
|
self.buttonBox.rejected.connect(shift_dialog.reject) # type: ignore
|
||||||
QtCore.QMetaObject.connectSlotsByName(shift_dialog)
|
QtCore.QMetaObject.connectSlotsByName(shift_dialog)
|
||||||
shift_dialog.setTabOrder(self.tabWidget, self.shift_table)
|
shift_dialog.setTabOrder(self.tabWidget, self.shift_table)
|
||||||
shift_dialog.setTabOrder(self.shift_table, self.x_shift_spinbox)
|
shift_dialog.setTabOrder(self.shift_table, self.x_shift_spinbox)
|
||||||
@ -310,5 +311,5 @@ class Ui_shift_dialog(object):
|
|||||||
self.overwrite_checkbox.setText(_translate("shift_dialog", "Overwrite data"))
|
self.overwrite_checkbox.setText(_translate("shift_dialog", "Overwrite data"))
|
||||||
self.data_newgraph.setText(_translate("shift_dialog", "New graph"))
|
self.data_newgraph.setText(_translate("shift_dialog", "New graph"))
|
||||||
self.values_newgraph.setText(_translate("shift_dialog", "New graph"))
|
self.values_newgraph.setText(_translate("shift_dialog", "New graph"))
|
||||||
|
from ..lib.graph_items import NMRPlotWidget
|
||||||
from ..lib.spinboxes import SciSpinBox
|
from ..lib.spinboxes import SciSpinBox
|
||||||
from pyqtgraph import PlotWidget
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/t1dialog.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/t1dialog.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.12.3
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
@ -160,6 +161,7 @@ class Ui_t1dialog(object):
|
|||||||
self.tau_combox.addItem("")
|
self.tau_combox.addItem("")
|
||||||
self.gridLayout_4.addWidget(self.tau_combox, 1, 0, 1, 2)
|
self.gridLayout_4.addWidget(self.tau_combox, 1, 0, 1, 2)
|
||||||
self.checkBox_interpol = QtWidgets.QCheckBox(self.groupBox_3)
|
self.checkBox_interpol = QtWidgets.QCheckBox(self.groupBox_3)
|
||||||
|
self.checkBox_interpol.setEnabled(False)
|
||||||
self.checkBox_interpol.setObjectName("checkBox_interpol")
|
self.checkBox_interpol.setObjectName("checkBox_interpol")
|
||||||
self.gridLayout_4.addWidget(self.checkBox_interpol, 2, 0, 1, 2)
|
self.gridLayout_4.addWidget(self.checkBox_interpol, 2, 0, 1, 2)
|
||||||
self.graph_checkbox = QtWidgets.QCheckBox(self.groupBox_3)
|
self.graph_checkbox = QtWidgets.QCheckBox(self.groupBox_3)
|
||||||
|
253
src/gui_qt/_py/tnmh_dialog.py
Normal file
253
src/gui_qt/_py/tnmh_dialog.py
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'src/resources/_ui/tnmh_dialog.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
|
#
|
||||||
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_DSCEvalDialog(object):
|
||||||
|
def setupUi(self, DSCEvalDialog):
|
||||||
|
DSCEvalDialog.setObjectName("DSCEvalDialog")
|
||||||
|
DSCEvalDialog.resize(996, 712)
|
||||||
|
self.gridLayout = QtWidgets.QGridLayout(DSCEvalDialog)
|
||||||
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
|
self.listWidget = QListWidgetSelect(DSCEvalDialog)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Expanding)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
|
||||||
|
self.listWidget.setSizePolicy(sizePolicy)
|
||||||
|
self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
|
self.listWidget.setObjectName("listWidget")
|
||||||
|
self.gridLayout.addWidget(self.listWidget, 0, 0, 1, 1)
|
||||||
|
self.stackedWidget = QtWidgets.QStackedWidget(DSCEvalDialog)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth())
|
||||||
|
self.stackedWidget.setSizePolicy(sizePolicy)
|
||||||
|
self.stackedWidget.setFrameShape(QtWidgets.QFrame.Box)
|
||||||
|
self.stackedWidget.setObjectName("stackedWidget")
|
||||||
|
self.page = QtWidgets.QWidget()
|
||||||
|
self.page.setObjectName("page")
|
||||||
|
self.gridLayout_2 = QtWidgets.QGridLayout(self.page)
|
||||||
|
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||||
|
self.gridLayout_9 = QtWidgets.QGridLayout()
|
||||||
|
self.gridLayout_9.setObjectName("gridLayout_9")
|
||||||
|
self.tg_export_check = QtWidgets.QCheckBox(self.page)
|
||||||
|
self.tg_export_check.setChecked(True)
|
||||||
|
self.tg_export_check.setObjectName("tg_export_check")
|
||||||
|
self.gridLayout_9.addWidget(self.tg_export_check, 0, 0, 1, 1)
|
||||||
|
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||||
|
self.gridLayout_9.addItem(spacerItem, 3, 0, 1, 1)
|
||||||
|
self.tglines_export_check = QtWidgets.QCheckBox(self.page)
|
||||||
|
self.tglines_export_check.setChecked(True)
|
||||||
|
self.tglines_export_check.setObjectName("tglines_export_check")
|
||||||
|
self.gridLayout_9.addWidget(self.tglines_export_check, 0, 1, 1, 1)
|
||||||
|
self.tg_export_button = QtWidgets.QPushButton(self.page)
|
||||||
|
self.tg_export_button.setObjectName("tg_export_button")
|
||||||
|
self.gridLayout_9.addWidget(self.tg_export_button, 2, 0, 1, 2)
|
||||||
|
self.gridLayout_2.addLayout(self.gridLayout_9, 2, 1, 1, 1)
|
||||||
|
self.tg_tree = QtWidgets.QTreeWidget(self.page)
|
||||||
|
self.tg_tree.setObjectName("tg_tree")
|
||||||
|
self.tg_tree.headerItem().setText(0, "1")
|
||||||
|
self.tg_tree.header().setVisible(False)
|
||||||
|
self.gridLayout_2.addWidget(self.tg_tree, 2, 0, 1, 1)
|
||||||
|
self.dsc_plot = NMRPlotWidget(self.page)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.dsc_plot.sizePolicy().hasHeightForWidth())
|
||||||
|
self.dsc_plot.setSizePolicy(sizePolicy)
|
||||||
|
self.dsc_plot.setObjectName("dsc_plot")
|
||||||
|
self.gridLayout_2.addWidget(self.dsc_plot, 1, 0, 1, 2)
|
||||||
|
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
|
||||||
|
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
|
||||||
|
self.label = QtWidgets.QLabel(self.page)
|
||||||
|
self.label.setObjectName("label")
|
||||||
|
self.horizontalLayout_4.addWidget(self.label)
|
||||||
|
self.calctg_button = QtWidgets.QPushButton(self.page)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.calctg_button.sizePolicy().hasHeightForWidth())
|
||||||
|
self.calctg_button.setSizePolicy(sizePolicy)
|
||||||
|
self.calctg_button.setObjectName("calctg_button")
|
||||||
|
self.horizontalLayout_4.addWidget(self.calctg_button)
|
||||||
|
self.gridLayout_2.addLayout(self.horizontalLayout_4, 0, 0, 1, 2)
|
||||||
|
self.stackedWidget.addWidget(self.page)
|
||||||
|
self.page_2 = QtWidgets.QWidget()
|
||||||
|
self.page_2.setObjectName("page_2")
|
||||||
|
self.gridLayout_3 = QtWidgets.QGridLayout(self.page_2)
|
||||||
|
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||||
|
self.label_4 = QtWidgets.QLabel(self.page_2)
|
||||||
|
self.label_4.setObjectName("label_4")
|
||||||
|
self.gridLayout_3.addWidget(self.label_4, 2, 0, 1, 1)
|
||||||
|
self.tghodge_graph = NMRPlotWidget(self.page_2)
|
||||||
|
self.tghodge_graph.setObjectName("tghodge_graph")
|
||||||
|
self.gridLayout_3.addWidget(self.tghodge_graph, 1, 0, 1, 1)
|
||||||
|
self.tau_plot = NMRPlotWidget(self.page_2)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.tau_plot.sizePolicy().hasHeightForWidth())
|
||||||
|
self.tau_plot.setSizePolicy(sizePolicy)
|
||||||
|
self.tau_plot.setObjectName("tau_plot")
|
||||||
|
self.gridLayout_3.addWidget(self.tau_plot, 1, 1, 1, 1)
|
||||||
|
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
|
||||||
|
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
||||||
|
self.label_2 = QtWidgets.QLabel(self.page_2)
|
||||||
|
self.label_2.setObjectName("label_2")
|
||||||
|
self.horizontalLayout_3.addWidget(self.label_2)
|
||||||
|
self.hodge_button = QtWidgets.QPushButton(self.page_2)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.hodge_button.sizePolicy().hasHeightForWidth())
|
||||||
|
self.hodge_button.setSizePolicy(sizePolicy)
|
||||||
|
self.hodge_button.setObjectName("hodge_button")
|
||||||
|
self.horizontalLayout_3.addWidget(self.hodge_button)
|
||||||
|
self.gridLayout_3.addLayout(self.horizontalLayout_3, 0, 0, 1, 2)
|
||||||
|
self.export_hodge_button = QtWidgets.QPushButton(self.page_2)
|
||||||
|
self.export_hodge_button.setObjectName("export_hodge_button")
|
||||||
|
self.gridLayout_3.addWidget(self.export_hodge_button, 5, 1, 1, 1)
|
||||||
|
self.hodge_graph_combo = QtWidgets.QComboBox(self.page_2)
|
||||||
|
self.hodge_graph_combo.setEnabled(False)
|
||||||
|
self.hodge_graph_combo.setObjectName("hodge_graph_combo")
|
||||||
|
self.gridLayout_3.addWidget(self.hodge_graph_combo, 4, 1, 1, 1)
|
||||||
|
self.hodge_graph_check = QtWidgets.QCheckBox(self.page_2)
|
||||||
|
self.hodge_graph_check.setChecked(True)
|
||||||
|
self.hodge_graph_check.setObjectName("hodge_graph_check")
|
||||||
|
self.gridLayout_3.addWidget(self.hodge_graph_check, 4, 0, 1, 1)
|
||||||
|
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
|
||||||
|
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
|
||||||
|
self.onset_check = QtWidgets.QCheckBox(self.page_2)
|
||||||
|
self.onset_check.setChecked(True)
|
||||||
|
self.onset_check.setObjectName("onset_check")
|
||||||
|
self.horizontalLayout_5.addWidget(self.onset_check)
|
||||||
|
self.mid_check = QtWidgets.QCheckBox(self.page_2)
|
||||||
|
self.mid_check.setChecked(True)
|
||||||
|
self.mid_check.setObjectName("mid_check")
|
||||||
|
self.horizontalLayout_5.addWidget(self.mid_check)
|
||||||
|
self.end_check = QtWidgets.QCheckBox(self.page_2)
|
||||||
|
self.end_check.setChecked(True)
|
||||||
|
self.end_check.setObjectName("end_check")
|
||||||
|
self.horizontalLayout_5.addWidget(self.end_check)
|
||||||
|
self.inflection_check = QtWidgets.QCheckBox(self.page_2)
|
||||||
|
self.inflection_check.setChecked(True)
|
||||||
|
self.inflection_check.setObjectName("inflection_check")
|
||||||
|
self.horizontalLayout_5.addWidget(self.inflection_check)
|
||||||
|
self.fictive_check = QtWidgets.QCheckBox(self.page_2)
|
||||||
|
self.fictive_check.setChecked(True)
|
||||||
|
self.fictive_check.setObjectName("fictive_check")
|
||||||
|
self.horizontalLayout_5.addWidget(self.fictive_check)
|
||||||
|
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
self.horizontalLayout_5.addItem(spacerItem1)
|
||||||
|
self.gridLayout_3.addLayout(self.horizontalLayout_5, 3, 0, 1, 2)
|
||||||
|
self.stackedWidget.addWidget(self.page_2)
|
||||||
|
self.page_3 = QtWidgets.QWidget()
|
||||||
|
self.page_3.setObjectName("page_3")
|
||||||
|
self.gridLayout_6 = QtWidgets.QGridLayout(self.page_3)
|
||||||
|
self.gridLayout_6.setObjectName("gridLayout_6")
|
||||||
|
self.tnmh_graphics = NMRPlotWidget(self.page_3)
|
||||||
|
self.tnmh_graphics.setObjectName("tnmh_graphics")
|
||||||
|
self.gridLayout_6.addWidget(self.tnmh_graphics, 1, 0, 1, 2)
|
||||||
|
self.tnmh_tree = QtWidgets.QTreeWidget(self.page_3)
|
||||||
|
self.tnmh_tree.setObjectName("tnmh_tree")
|
||||||
|
self.tnmh_tree.headerItem().setText(0, "1")
|
||||||
|
self.tnmh_tree.header().setVisible(False)
|
||||||
|
self.gridLayout_6.addWidget(self.tnmh_tree, 2, 0, 1, 1)
|
||||||
|
self.gridLayout_5 = QtWidgets.QGridLayout()
|
||||||
|
self.gridLayout_5.setObjectName("gridLayout_5")
|
||||||
|
self.tnmh_graph_check = QtWidgets.QCheckBox(self.page_3)
|
||||||
|
self.tnmh_graph_check.setChecked(True)
|
||||||
|
self.tnmh_graph_check.setObjectName("tnmh_graph_check")
|
||||||
|
self.gridLayout_5.addWidget(self.tnmh_graph_check, 1, 0, 1, 1)
|
||||||
|
self.tnmhfit_export_check = QtWidgets.QCheckBox(self.page_3)
|
||||||
|
self.tnmhfit_export_check.setChecked(True)
|
||||||
|
self.tnmhfit_export_check.setObjectName("tnmhfit_export_check")
|
||||||
|
self.gridLayout_5.addWidget(self.tnmhfit_export_check, 0, 0, 1, 1)
|
||||||
|
self.tnmh_export_button = QtWidgets.QPushButton(self.page_3)
|
||||||
|
self.tnmh_export_button.setObjectName("tnmh_export_button")
|
||||||
|
self.gridLayout_5.addWidget(self.tnmh_export_button, 2, 0, 1, 2)
|
||||||
|
self.fictive_export_check = QtWidgets.QCheckBox(self.page_3)
|
||||||
|
self.fictive_export_check.setChecked(True)
|
||||||
|
self.fictive_export_check.setObjectName("fictive_export_check")
|
||||||
|
self.gridLayout_5.addWidget(self.fictive_export_check, 0, 1, 1, 1)
|
||||||
|
self.tnmh_graph_combo = QtWidgets.QComboBox(self.page_3)
|
||||||
|
self.tnmh_graph_combo.setEnabled(False)
|
||||||
|
self.tnmh_graph_combo.setObjectName("tnmh_graph_combo")
|
||||||
|
self.gridLayout_5.addWidget(self.tnmh_graph_combo, 1, 1, 1, 1)
|
||||||
|
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||||
|
self.gridLayout_5.addItem(spacerItem2, 3, 0, 1, 1)
|
||||||
|
self.gridLayout_6.addLayout(self.gridLayout_5, 2, 1, 1, 1)
|
||||||
|
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
||||||
|
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||||
|
self.label_3 = QtWidgets.QLabel(self.page_3)
|
||||||
|
self.label_3.setObjectName("label_3")
|
||||||
|
self.horizontalLayout_2.addWidget(self.label_3)
|
||||||
|
self.tnhm_fitbutton = QtWidgets.QPushButton(self.page_3)
|
||||||
|
self.tnhm_fitbutton.setObjectName("tnhm_fitbutton")
|
||||||
|
self.horizontalLayout_2.addWidget(self.tnhm_fitbutton)
|
||||||
|
self.gridLayout_6.addLayout(self.horizontalLayout_2, 0, 0, 1, 2)
|
||||||
|
self.stackedWidget.addWidget(self.page_3)
|
||||||
|
self.gridLayout.addWidget(self.stackedWidget, 0, 1, 1, 1)
|
||||||
|
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||||
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
|
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
self.horizontalLayout.addItem(spacerItem3)
|
||||||
|
self.back_button = QtWidgets.QToolButton(DSCEvalDialog)
|
||||||
|
self.back_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
|
||||||
|
self.back_button.setArrowType(QtCore.Qt.LeftArrow)
|
||||||
|
self.back_button.setObjectName("back_button")
|
||||||
|
self.horizontalLayout.addWidget(self.back_button)
|
||||||
|
self.next_button = QtWidgets.QToolButton(DSCEvalDialog)
|
||||||
|
self.next_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
|
||||||
|
self.next_button.setArrowType(QtCore.Qt.RightArrow)
|
||||||
|
self.next_button.setObjectName("next_button")
|
||||||
|
self.horizontalLayout.addWidget(self.next_button)
|
||||||
|
self.close_button = QtWidgets.QPushButton(DSCEvalDialog)
|
||||||
|
self.close_button.setObjectName("close_button")
|
||||||
|
self.horizontalLayout.addWidget(self.close_button)
|
||||||
|
self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 2)
|
||||||
|
|
||||||
|
self.retranslateUi(DSCEvalDialog)
|
||||||
|
self.stackedWidget.setCurrentIndex(0)
|
||||||
|
self.close_button.clicked.connect(DSCEvalDialog.close) # type: ignore
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(DSCEvalDialog)
|
||||||
|
|
||||||
|
def retranslateUi(self, DSCEvalDialog):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
DSCEvalDialog.setWindowTitle(_translate("DSCEvalDialog", "To boldly go where no man has gone before"))
|
||||||
|
self.tg_export_check.setText(_translate("DSCEvalDialog", "Export Tg"))
|
||||||
|
self.tglines_export_check.setText(_translate("DSCEvalDialog", "Export lines"))
|
||||||
|
self.tg_export_button.setText(_translate("DSCEvalDialog", "Export"))
|
||||||
|
self.label.setText(_translate("DSCEvalDialog", "<html><head/><body><p><span style=\" font-weight:600;\">Glass transition</span></p></body></html>"))
|
||||||
|
self.calctg_button.setText(_translate("DSCEvalDialog", "Calculate Tg"))
|
||||||
|
self.label_4.setText(_translate("DSCEvalDialog", "Export tau:"))
|
||||||
|
self.label_2.setText(_translate("DSCEvalDialog", "<html><head/><body><p><span style=\" font-weight:600;\">Hodge tau</span></p></body></html>"))
|
||||||
|
self.hodge_button.setText(_translate("DSCEvalDialog", "Make it so."))
|
||||||
|
self.export_hodge_button.setText(_translate("DSCEvalDialog", "Export"))
|
||||||
|
self.hodge_graph_check.setText(_translate("DSCEvalDialog", "New graph"))
|
||||||
|
self.onset_check.setText(_translate("DSCEvalDialog", "Onset"))
|
||||||
|
self.mid_check.setText(_translate("DSCEvalDialog", "Midpoint"))
|
||||||
|
self.end_check.setText(_translate("DSCEvalDialog", "End"))
|
||||||
|
self.inflection_check.setText(_translate("DSCEvalDialog", "Inflection"))
|
||||||
|
self.fictive_check.setText(_translate("DSCEvalDialog", "Fictive"))
|
||||||
|
self.tnmh_graph_check.setText(_translate("DSCEvalDialog", "New graph"))
|
||||||
|
self.tnmhfit_export_check.setText(_translate("DSCEvalDialog", "Export fit"))
|
||||||
|
self.tnmh_export_button.setText(_translate("DSCEvalDialog", "Export"))
|
||||||
|
self.fictive_export_check.setText(_translate("DSCEvalDialog", "Export dTf / dT"))
|
||||||
|
self.label_3.setText(_translate("DSCEvalDialog", "<html><head/><body><p><span style=\" font-weight:600;\">dTf/dT and TNMH </span></p></body></html>"))
|
||||||
|
self.tnhm_fitbutton.setText(_translate("DSCEvalDialog", "Engage!"))
|
||||||
|
self.back_button.setText(_translate("DSCEvalDialog", "Back"))
|
||||||
|
self.next_button.setText(_translate("DSCEvalDialog", "Next"))
|
||||||
|
self.close_button.setText(_translate("DSCEvalDialog", "Close"))
|
||||||
|
from ..lib.graph_items import NMRPlotWidget
|
||||||
|
from ..lib.listwidget import QListWidgetSelect
|
@ -1,8 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'resources/_ui/valueeditor.ui'
|
# Form implementation generated from reading ui file 'src/resources/_ui/valueeditor.ui'
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.15.4
|
# Created by: PyQt5 UI code generator 5.15.9
|
||||||
#
|
#
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
28
src/gui_qt/cli.py
Normal file
28
src/gui_qt/cli.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
def main():
|
||||||
|
import sys
|
||||||
|
from nmreval.configs import check_for_config
|
||||||
|
|
||||||
|
# does a directory for config stuff exist? create it if not
|
||||||
|
check_for_config()
|
||||||
|
|
||||||
|
# pyqtgraph warns on Mac if QApplication is created when it is imported
|
||||||
|
# import pyqtgraph
|
||||||
|
|
||||||
|
from nmreval.lib.logger import handle_exception
|
||||||
|
sys.excepthook = handle_exception
|
||||||
|
|
||||||
|
from gui_qt import App
|
||||||
|
|
||||||
|
app = App(['Team Rocket FTW!'])
|
||||||
|
|
||||||
|
from gui_qt.main.mainwindow import NMRMainWindow
|
||||||
|
|
||||||
|
mplQt = NMRMainWindow()
|
||||||
|
mplQt.show()
|
||||||
|
|
||||||
|
sys.exit(app.exec())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -8,8 +8,10 @@ from pyqtgraph import mkPen
|
|||||||
|
|
||||||
from nmreval.data.points import Points
|
from nmreval.data.points import Points
|
||||||
from nmreval.data.signals import Signal
|
from nmreval.data.signals import Signal
|
||||||
|
from nmreval.lib.logger import logger
|
||||||
from nmreval.utils.text import convert
|
from nmreval.utils.text import convert
|
||||||
from nmreval.data.bds import BDS
|
from nmreval.data.bds import BDS
|
||||||
|
from nmreval.data.dsc import DSC
|
||||||
from nmreval.lib.colors import BaseColor, TUColors
|
from nmreval.lib.colors import BaseColor, TUColors
|
||||||
from nmreval.lib.lines import LineStyle
|
from nmreval.lib.lines import LineStyle
|
||||||
from nmreval.lib.symbols import SymbolStyle, symbolcycle
|
from nmreval.lib.symbols import SymbolStyle, symbolcycle
|
||||||
@ -32,6 +34,7 @@ class ExperimentContainer(QtCore.QObject):
|
|||||||
self.id = str(identifier)
|
self.id = str(identifier)
|
||||||
|
|
||||||
self._fits = []
|
self._fits = []
|
||||||
|
self._relations = kwargs.get('relations', {})
|
||||||
self._data = data
|
self._data = data
|
||||||
self._manager = kwargs.get('manager')
|
self._manager = kwargs.get('manager')
|
||||||
self.graph = ''
|
self.graph = ''
|
||||||
@ -44,6 +47,7 @@ class ExperimentContainer(QtCore.QObject):
|
|||||||
self.actions = {}
|
self.actions = {}
|
||||||
self._update_actions()
|
self._update_actions()
|
||||||
|
|
||||||
|
@plot_update
|
||||||
def _init_plot(self):
|
def _init_plot(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@ -226,6 +230,8 @@ class ExperimentContainer(QtCore.QObject):
|
|||||||
return self.plot_real, self.plot_imag, self.plot_error
|
return self.plot_real, self.plot_imag, self.plot_error
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
|
# TODO preserve relationships
|
||||||
|
|
||||||
ret_dic = {
|
ret_dic = {
|
||||||
'id': self.id,
|
'id': self.id,
|
||||||
'data': self._data.get_state(),
|
'data': self._data.get_state(),
|
||||||
@ -267,6 +273,32 @@ class ExperimentContainer(QtCore.QObject):
|
|||||||
else:
|
else:
|
||||||
self._fits.extend(value)
|
self._fits.extend(value)
|
||||||
|
|
||||||
|
def has_relation(self, relation_type):
|
||||||
|
return relation_type in self._relations
|
||||||
|
|
||||||
|
def get_relation(self, relation_type: int):
|
||||||
|
return self._relations.get(relation_type, [])
|
||||||
|
|
||||||
|
def add_relation(self, relation_type: int, value: str):
|
||||||
|
if relation_type not in self._relations:
|
||||||
|
self._relations[relation_type] = []
|
||||||
|
|
||||||
|
self._relations[relation_type].append(value)
|
||||||
|
|
||||||
|
def remove_relation(self, relation_type: int, value: str):
|
||||||
|
if relation_type not in self._relations:
|
||||||
|
raise ValueError(f'Relationship {relation_type!r} with id {value!r} doe not exist for {self.name}')
|
||||||
|
|
||||||
|
related_id_value = self._relations[relation_type]
|
||||||
|
|
||||||
|
idx = related_id_value.index(value)
|
||||||
|
if idx == -1:
|
||||||
|
raise ValueError(f'Relationship {relation_type!r} with id {value!r} doe not exist for {self.name}')
|
||||||
|
|
||||||
|
related_id_value.pop(idx)
|
||||||
|
if len(related_id_value) == 0:
|
||||||
|
self._relations.pop(relation_type)
|
||||||
|
|
||||||
def _update_actions(self):
|
def _update_actions(self):
|
||||||
self.actions.update({'sort': self._data.sort,
|
self.actions.update({'sort': self._data.sort,
|
||||||
'cut': self._data.cut,
|
'cut': self._data.cut,
|
||||||
@ -312,12 +344,10 @@ class ExperimentContainer(QtCore.QObject):
|
|||||||
err_pen.setColor(QtGui.QColor(*self.plot_real.symbolcolor.rgb()))
|
err_pen.setColor(QtGui.QColor(*self.plot_real.symbolcolor.rgb()))
|
||||||
self.plot_error.setData(pen=err_pen)
|
self.plot_error.setData(pen=err_pen)
|
||||||
|
|
||||||
elif mode == 'imag' and self.plot_imag is not None:
|
if mode in ['imag', 'all'] and self.plot_imag is not None:
|
||||||
self.plot_imag.set_color(color, symbol=symbol, line=line)
|
self.plot_imag.set_color(color, symbol=symbol, line=line)
|
||||||
else:
|
|
||||||
print('Updating color failed for ' + str(self.id))
|
|
||||||
|
|
||||||
def setSymbol(self, symbol=None, color=None, size=None, mode='real'):
|
def setSymbol(self, *, symbol=None, color=None, size=None, mode='real'):
|
||||||
if mode in ['real', 'all']:
|
if mode in ['real', 'all']:
|
||||||
self.plot_real.set_symbol(symbol=symbol, size=size, color=color)
|
self.plot_real.set_symbol(symbol=symbol, size=size, color=color)
|
||||||
if color is not None and self.plot_error is not None and self.plot_real.symbol != SymbolStyle.No:
|
if color is not None and self.plot_error is not None and self.plot_real.symbol != SymbolStyle.No:
|
||||||
@ -327,9 +357,9 @@ class ExperimentContainer(QtCore.QObject):
|
|||||||
elif mode in ['imag', 'all'] and self.plot_imag is not None:
|
elif mode in ['imag', 'all'] and self.plot_imag is not None:
|
||||||
self.plot_imag.set_symbol(symbol=symbol, size=size, color=color)
|
self.plot_imag.set_symbol(symbol=symbol, size=size, color=color)
|
||||||
else:
|
else:
|
||||||
print('Updating symbol failed for ' + str(self.id))
|
logger.warning(f'Updating symbol failed for {self.id}')
|
||||||
|
|
||||||
def setLine(self, width=None, style=None, color=None, mode='real'):
|
def setLine(self, *, width=None, style=None, color=None, mode='real'):
|
||||||
if mode in ['real', 'all']:
|
if mode in ['real', 'all']:
|
||||||
self.plot_real.set_line(width=width, style=style, color=color)
|
self.plot_real.set_line(width=width, style=style, color=color)
|
||||||
if color is not None and self.plot_error is not None and self.plot_real.symbol == SymbolStyle.No:
|
if color is not None and self.plot_error is not None and self.plot_real.symbol == SymbolStyle.No:
|
||||||
@ -339,7 +369,7 @@ class ExperimentContainer(QtCore.QObject):
|
|||||||
elif mode in ['imag', 'all'] and self.plot_imag is not None:
|
elif mode in ['imag', 'all'] and self.plot_imag is not None:
|
||||||
self.plot_imag.set_line(width=width, style=style, color=color)
|
self.plot_imag.set_line(width=width, style=style, color=color)
|
||||||
else:
|
else:
|
||||||
print('Updating line failed for ' + str(self.id))
|
logger.warning(f'Updating line failed for {self.id}')
|
||||||
|
|
||||||
def update_property(self, key1: str, key2: str, value: Any):
|
def update_property(self, key1: str, key2: str, value: Any):
|
||||||
keykey = key2.split()
|
keykey = key2.split()
|
||||||
@ -434,11 +464,21 @@ class ExperimentContainer(QtCore.QObject):
|
|||||||
|
|
||||||
return offset
|
return offset
|
||||||
|
|
||||||
|
@plot_update
|
||||||
|
def shift_scale(self, shift_factor: tuple[float, float], scaling_factor: tuple[float, float]):
|
||||||
|
scale_x, scale_y = scaling_factor
|
||||||
|
shift_x, shift_y = shift_factor
|
||||||
|
self.data.x = self.data.x * scale_x + shift_x
|
||||||
|
self.data.y = self.data.y * scale_y + shift_y
|
||||||
|
self.data.y_err = self.data.y_err * scale_y
|
||||||
|
|
||||||
|
self.update({'shift': scaling_factor, 'scale': shift_factor})
|
||||||
|
|
||||||
def get_namespace(self, i: int = None, j: int = None) -> dict:
|
def get_namespace(self, i: int = None, j: int = None) -> dict:
|
||||||
if (i is None) and (j is None):
|
if (i is None) and (j is None):
|
||||||
prefix = ''
|
prefix = ''
|
||||||
else:
|
else:
|
||||||
prefix = 'g[%i].s[%i].' % (i, j)
|
prefix = f'g[{i}].s[{j}].'
|
||||||
|
|
||||||
namespace = {prefix + 'x': (self.x, 'x values'),
|
namespace = {prefix + 'x': (self.x, 'x values'),
|
||||||
prefix + 'y': [self.y, 'y values'],
|
prefix + 'y': [self.y, 'y values'],
|
||||||
@ -459,27 +499,50 @@ class ExperimentContainer(QtCore.QObject):
|
|||||||
|
|
||||||
return namespace
|
return namespace
|
||||||
|
|
||||||
def eval_expression(self, cmds, namespace):
|
def eval_expression(self, cmds, namespace, i=None, j=None):
|
||||||
namespace.update({'x': self.x, 'y': self.y, 'y_err': self.y_err, 'value': self.value})
|
if i is not None:
|
||||||
|
namespace['i'] = i
|
||||||
|
|
||||||
if len(self._fits) == 1:
|
if j is not None:
|
||||||
namespace.update({"fit['%s']" % (convert(pname, old='tex', new='str')): pvalue.value
|
namespace['j'] = j
|
||||||
for (pname, pvalue) in self._manager[self._fits[0]].parameter.items()})
|
|
||||||
|
namespace.update({'x': self._data.x, 'y': self._data.y, 'y_err': self._data.y_err, 'value': self.value})
|
||||||
|
namespace['fit'] = {}
|
||||||
|
|
||||||
|
if isinstance(self, FitContainer):
|
||||||
|
namespace['fit'].update({
|
||||||
|
'%s' % convert(pname, old='latex', new='plain'): pvalue.value
|
||||||
|
for (pname, pvalue) in self._data.parameter.items()
|
||||||
|
})
|
||||||
|
|
||||||
|
elif len(self._fits) == 1:
|
||||||
|
namespace['fit'].update({
|
||||||
|
'%s' % convert(pname, old='tex', new='str'): pvalue.value
|
||||||
|
for (pname, pvalue) in self._manager[self._fits[0]].parameter.items()
|
||||||
|
})
|
||||||
else:
|
else:
|
||||||
for k, f in enumerate(self._fits):
|
for k, f in enumerate(self._fits):
|
||||||
namespace.update({"fit['%s_%i']" % (convert(pname, old='tex', new='str'), k): pvalue.value
|
namespace['fit'].update({
|
||||||
for (pname, pvalue) in self._manager[f].parameter.items()})
|
"%s_%i" % (convert(pname, old='tex', new='str'), k): pvalue.value
|
||||||
|
for (pname, pvalue) in self._manager[f].parameter.items()
|
||||||
|
})
|
||||||
|
|
||||||
new_data = self.copy()
|
new_data = self.copy()
|
||||||
for c in cmds:
|
for c in cmds:
|
||||||
if c:
|
if c:
|
||||||
exec(c, globals(), namespace)
|
exec(c, globals(), namespace)
|
||||||
|
|
||||||
new_data.set_data(x=namespace['x'], y=namespace['y'], y_err=namespace['y_err'])
|
new_data.set_data(x=namespace['x'], y=namespace['y'], y_err=namespace['y_err'], replace_mask=False)
|
||||||
new_data.value = namespace['value']
|
new_data.value = namespace['value']
|
||||||
|
|
||||||
return new_data
|
return new_data
|
||||||
|
|
||||||
|
def binning(self, digits: float):
|
||||||
|
new_data = self.copy(full=True)
|
||||||
|
new_data.data = self._data.binning(value=digits)
|
||||||
|
|
||||||
|
return new_data
|
||||||
|
|
||||||
|
|
||||||
class PointContainer(ExperimentContainer):
|
class PointContainer(ExperimentContainer):
|
||||||
symbols = symbolcycle()
|
symbols = symbolcycle()
|
||||||
@ -490,6 +553,9 @@ class PointContainer(ExperimentContainer):
|
|||||||
self.mode = 'pts'
|
self.mode = 'pts'
|
||||||
self._init_plot(**kwargs)
|
self._init_plot(**kwargs)
|
||||||
|
|
||||||
|
if isinstance(self._data, DSC):
|
||||||
|
self.mode = 'dsc'
|
||||||
|
|
||||||
def _init_plot(self, **kwargs):
|
def _init_plot(self, **kwargs):
|
||||||
self.plot_imag = None
|
self.plot_imag = None
|
||||||
|
|
||||||
@ -526,17 +592,17 @@ class PointContainer(ExperimentContainer):
|
|||||||
line_kwargs['style'] = LineStyle.No
|
line_kwargs['style'] = LineStyle.No
|
||||||
sym_kwargs['symbol'] = next(PointContainer.symbols)
|
sym_kwargs['symbol'] = next(PointContainer.symbols)
|
||||||
|
|
||||||
self.plot_real = PlotItem(x=self._data.x, y=self._data.y, name=self.name,
|
self.plot_real = PlotItem(x=self.x, y=self.y, name=self.name,
|
||||||
symbol=None, pen=None, connect='finite')
|
symbol=None, pen=None, connect='finite')
|
||||||
|
|
||||||
self.setSymbol(mode='real', **sym_kwargs)
|
self.setSymbol(mode='real', **sym_kwargs)
|
||||||
self.setLine(mode='real', **line_kwargs)
|
self.setLine(mode='real', **line_kwargs)
|
||||||
|
|
||||||
if sym_kwargs['symbol'] != SymbolStyle.No:
|
if sym_kwargs['symbol'] != SymbolStyle.No:
|
||||||
self.plot_error = ErrorBars(x=self._data.x, y=self._data.y, top=self._data.y_err, bottom=self._data.y_err,
|
self.plot_error = ErrorBars(x=self.x, y=self.y, top=self.y_err, bottom=self.y_err,
|
||||||
pen=mkPen({'color': self.plot_real.symbolcolor.rgb()}))
|
pen=mkPen({'color': self.plot_real.symbolcolor.rgb()}))
|
||||||
else:
|
else:
|
||||||
self.plot_error = ErrorBars(x=self._data.x, y=self._data.y, top=self._data.y_err, bottom=self._data.y_err,
|
self.plot_error = ErrorBars(x=self.x, y=self.y, top=self.y_err, bottom=self.y_err,
|
||||||
pen=mkPen({'color': self.plot_real.linecolor.rgb()}))
|
pen=mkPen({'color': self.plot_real.linecolor.rgb()}))
|
||||||
|
|
||||||
|
|
||||||
@ -553,16 +619,18 @@ class FitContainer(ExperimentContainer):
|
|||||||
setattr(self, n, getattr(data, n))
|
setattr(self, n, getattr(data, n))
|
||||||
|
|
||||||
def _init_plot(self, **kwargs):
|
def _init_plot(self, **kwargs):
|
||||||
color = kwargs.get('color', (0, 0, 0))
|
color = kwargs.get('color')
|
||||||
|
if color is None:
|
||||||
|
color = kwargs.get('linecolor', (0, 0, 0))
|
||||||
if isinstance(color, BaseColor):
|
if isinstance(color, BaseColor):
|
||||||
color = color.rgb()
|
color = color.rgb()
|
||||||
|
|
||||||
self.plot_real = PlotItem(x=self._data.x, y=self._data.y.real, name=self.name,
|
self.plot_real = PlotItem(x=self.x, y=self.y.real, name=self.name,
|
||||||
pen=mkPen({'color': color}),
|
pen=mkPen({'color': color}),
|
||||||
connect='finite', symbol=None)
|
connect='finite', symbol=None)
|
||||||
|
|
||||||
if np.iscomplexobj(self._data.y):
|
if np.iscomplexobj(self._data.y):
|
||||||
self.plot_imag = PlotItem(x=self._data.x, y=self._data.y.imag, name=self.name,
|
self.plot_imag = PlotItem(x=self.x, y=self.y.imag, name=self.name,
|
||||||
pen=mkPen({'color': color}),
|
pen=mkPen({'color': color}),
|
||||||
connect='finite', symbol=None)
|
connect='finite', symbol=None)
|
||||||
|
|
||||||
@ -595,9 +663,9 @@ class SignalContainer(ExperimentContainer):
|
|||||||
self._init_plot(symbol=symbol, **kwargs)
|
self._init_plot(symbol=symbol, **kwargs)
|
||||||
|
|
||||||
def _init_plot(self, **kwargs):
|
def _init_plot(self, **kwargs):
|
||||||
self.plot_real = PlotItem(x=self._data.x, y=self._data.y.real, name=self.name,
|
self.plot_real = PlotItem(x=self.x, y=self.y.real, name=self.name,
|
||||||
symbol=None, pen=None, connect='finite')
|
symbol=None, pen=None, connect='finite')
|
||||||
self.plot_imag = PlotItem(x=self._data.x, y=self._data.y.imag, name=self.name,
|
self.plot_imag = PlotItem(x=self.x, y=self.y.imag, name=self.name,
|
||||||
symbol=None, pen=None, connect='finite')
|
symbol=None, pen=None, connect='finite')
|
||||||
|
|
||||||
color = kwargs.get('color', None)
|
color = kwargs.get('color', None)
|
||||||
@ -605,7 +673,7 @@ class SignalContainer(ExperimentContainer):
|
|||||||
linecolor = kwargs.get('linecolor', color)
|
linecolor = kwargs.get('linecolor', color)
|
||||||
|
|
||||||
if symcolor is None and linecolor is None:
|
if symcolor is None and linecolor is None:
|
||||||
color = next(self.colors)
|
color = next(self.colors) if color is None else color
|
||||||
symcolor = color
|
symcolor = color
|
||||||
linecolor = color
|
linecolor = color
|
||||||
elif symcolor is None:
|
elif symcolor is None:
|
||||||
@ -681,3 +749,46 @@ class SignalContainer(ExperimentContainer):
|
|||||||
self._update_actions()
|
self._update_actions()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@plot_update
|
||||||
|
def edit_signal(
|
||||||
|
self: SignalContainer,
|
||||||
|
baseline: tuple[None | bool],
|
||||||
|
leftshift: tuple[None | float, str],
|
||||||
|
zerofill: tuple[None, int],
|
||||||
|
apod: tuple[None, list[float], type[object]],
|
||||||
|
phase: tuple[None | float, float, float],
|
||||||
|
fourier: tuple[None | bool]
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Function for EditUndoCommand to call if a timesignal or spectra must be worked on.
|
||||||
|
This avoids to update the plot for every action we do and makes it slightly faster.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if baseline[0] is not None:
|
||||||
|
self._data.baseline()
|
||||||
|
|
||||||
|
if leftshift[0] is not None:
|
||||||
|
self._data.shift(*leftshift)
|
||||||
|
|
||||||
|
if zerofill[0] is not None:
|
||||||
|
self._data.zerofill(*zerofill)
|
||||||
|
|
||||||
|
if apod[0] is not None:
|
||||||
|
self._data.apod(*apod)
|
||||||
|
|
||||||
|
# ft with three options: None, True, False
|
||||||
|
if fourier[0] is None:
|
||||||
|
# ft None -> only phase correct
|
||||||
|
if phase[0] is not None:
|
||||||
|
self._data.manual_phase(*phase)
|
||||||
|
elif fourier[0] == True:
|
||||||
|
# ft True -> first phase correct then fft
|
||||||
|
if phase[0] is not None:
|
||||||
|
self._data.manual_phase(*phase)
|
||||||
|
self.fourier()
|
||||||
|
else:
|
||||||
|
# ft False -> first fft then phase correct
|
||||||
|
self.fourier()
|
||||||
|
if phase[0] is not None:
|
||||||
|
self._data.manual_phase(*phase)
|
@ -5,7 +5,7 @@ from nmreval.lib.colors import available_cycles
|
|||||||
from .properties import PropWidget
|
from .properties import PropWidget
|
||||||
from ...Qt import QtWidgets, QtGui, QtCore
|
from ...Qt import QtWidgets, QtGui, QtCore
|
||||||
from ..._py.datawidget import Ui_DataWidget
|
from ..._py.datawidget import Ui_DataWidget
|
||||||
from ...lib import make_action_icons
|
from ...lib.iconloading import make_action_icons
|
||||||
from ...lib.delegates import HeaderDelegate
|
from ...lib.delegates import HeaderDelegate
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,9 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
moveItem = QtCore.pyqtSignal(list, str, str, int) # items, from, to, new row
|
moveItem = QtCore.pyqtSignal(list, str, str, int) # items, from, to, new row
|
||||||
copyItem = QtCore.pyqtSignal(list, str)
|
copyItem = QtCore.pyqtSignal(list, str)
|
||||||
saveFits = QtCore.pyqtSignal(list)
|
saveFits = QtCore.pyqtSignal(list)
|
||||||
|
extendFits = QtCore.pyqtSignal(list)
|
||||||
|
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
@ -59,15 +61,16 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
|
|
||||||
self.update_indexes()
|
self.update_indexes()
|
||||||
|
|
||||||
def add_item(self, items: (tuple | list[tuple]), gid: str):
|
def add_item(self, items: (tuple | list[tuple]), gid: str, update: bool = True):
|
||||||
if isinstance(items, tuple):
|
if isinstance(items, tuple):
|
||||||
items = [items]
|
items = [items]
|
||||||
|
|
||||||
for row in range(self.invisibleRootItem().childCount()):
|
for row in range(self.invisibleRootItem().childCount()):
|
||||||
graph = self.invisibleRootItem().child(row)
|
graph = self.invisibleRootItem().child(row)
|
||||||
if graph.data(0, QtCore.Qt.UserRole) == gid:
|
if graph.data(0, QtCore.Qt.UserRole) == gid:
|
||||||
for (idd, name) in items:
|
for (idd, name, value) in items:
|
||||||
item = QtWidgets.QTreeWidgetItem([name])
|
item = QtWidgets.QTreeWidgetItem([name])
|
||||||
|
item.setToolTip(0, f'Value: {value}')
|
||||||
item.setData(0, QtCore.Qt.UserRole, idd)
|
item.setData(0, QtCore.Qt.UserRole, idd)
|
||||||
item.setCheckState(0, QtCore.Qt.Checked)
|
item.setCheckState(0, QtCore.Qt.Checked)
|
||||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsEditable |
|
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsEditable |
|
||||||
@ -77,11 +80,11 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
|
|
||||||
self.resizeColumnToContents(0)
|
self.resizeColumnToContents(0)
|
||||||
break
|
break
|
||||||
|
if update:
|
||||||
self.update_indexes()
|
self.update_indexes()
|
||||||
|
|
||||||
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem)
|
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem)
|
||||||
def data_change(self, item: QtWidgets.QTreeWidgetItem) -> tuple[set, set]:
|
def data_change(self, item: QtWidgets.QTreeWidgetItem, emit: bool = True) -> tuple[set, set]:
|
||||||
idd = item.data(0, QtCore.Qt.UserRole)
|
idd = item.data(0, QtCore.Qt.UserRole)
|
||||||
is_selected = item.checkState(0) == QtCore.Qt.Checked
|
is_selected = item.checkState(0) == QtCore.Qt.Checked
|
||||||
to_be_hidden = set()
|
to_be_hidden = set()
|
||||||
@ -139,9 +142,10 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
if emit:
|
||||||
self.keyChanged.emit(idd, item.text(0))
|
self.keyChanged.emit(idd, item.text(0))
|
||||||
|
|
||||||
if to_be_shown or to_be_hidden:
|
if (to_be_shown or to_be_hidden) and emit:
|
||||||
self.stateChanged.emit(list(to_be_shown), list(to_be_hidden))
|
self.stateChanged.emit(list(to_be_shown), list(to_be_hidden))
|
||||||
|
|
||||||
return to_be_shown, to_be_hidden
|
return to_be_shown, to_be_hidden
|
||||||
@ -246,11 +250,11 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
|
|
||||||
self.blockSignals(False)
|
self.blockSignals(False)
|
||||||
|
|
||||||
|
|
||||||
def update_indexes(self):
|
def update_indexes(self):
|
||||||
graph_cnt = -1
|
graph_cnt = -1
|
||||||
set_cnt = 0
|
set_cnt = 0
|
||||||
iterator = QtWidgets.QTreeWidgetItemIterator(self)
|
iterator = QtWidgets.QTreeWidgetItemIterator(self)
|
||||||
|
self.blockSignals(True)
|
||||||
while iterator.value():
|
while iterator.value():
|
||||||
item = iterator.value()
|
item = iterator.value()
|
||||||
if item is not None:
|
if item is not None:
|
||||||
@ -265,6 +269,7 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
iterator += 1
|
iterator += 1
|
||||||
|
|
||||||
self.resizeColumnToContents(1)
|
self.resizeColumnToContents(1)
|
||||||
|
self.blockSignals(False)
|
||||||
|
|
||||||
def set_name(self, sid, name):
|
def set_name(self, sid, name):
|
||||||
iterator = QtWidgets.QTreeWidgetItemIterator(self)
|
iterator = QtWidgets.QTreeWidgetItemIterator(self)
|
||||||
@ -286,13 +291,20 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
for idx in self.selectedIndexes():
|
for idx in self.selectedIndexes():
|
||||||
if idx.column() == 1:
|
if idx.column() == 1:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
item = self.itemFromIndex(idx)
|
item = self.itemFromIndex(idx)
|
||||||
if item.parent() is None:
|
if item.parent() is None:
|
||||||
for c_i in range(item.childCount()):
|
for c_i in range(item.childCount()):
|
||||||
rm_sets.append(item.child(c_i).data(0, QtCore.Qt.UserRole))
|
# add sets inside graph to removal
|
||||||
|
child_data = item.child(c_i).data(0, QtCore.Qt.UserRole)
|
||||||
|
if child_data not in rm_sets:
|
||||||
|
rm_sets.append(child_data)
|
||||||
rm_graphs.append(item.data(0, QtCore.Qt.UserRole))
|
rm_graphs.append(item.data(0, QtCore.Qt.UserRole))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
rm_sets.append(item.data(0, QtCore.Qt.UserRole))
|
item_data = item.data(0, QtCore.Qt.UserRole)
|
||||||
|
if item_data not in rm_sets:
|
||||||
|
rm_sets.append(item_data)
|
||||||
|
|
||||||
# self.deleteItem.emit(rm_sets+rm_graphs)
|
# self.deleteItem.emit(rm_sets+rm_graphs)
|
||||||
self.management.delete_sets(rm_sets+rm_graphs)
|
self.management.delete_sets(rm_sets+rm_graphs)
|
||||||
@ -305,26 +317,24 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
if idx.column() != 0:
|
if idx.column() != 0:
|
||||||
continue
|
continue
|
||||||
item = self.itemFromIndex(idx)
|
item = self.itemFromIndex(idx)
|
||||||
|
|
||||||
if item.parent() is None:
|
if item.parent() is None:
|
||||||
is_selected = item.checkState(0)
|
|
||||||
self.blockSignals(True)
|
|
||||||
for i in range(item.childCount()):
|
for i in range(item.childCount()):
|
||||||
child = item.child(i)
|
child = item.child(i)
|
||||||
from_parent.append(child)
|
from_parent.append(child)
|
||||||
self.blockSignals(False)
|
|
||||||
if is_selected == QtCore.Qt.Checked:
|
|
||||||
item.setCheckState(0, QtCore.Qt.Unchecked)
|
|
||||||
else:
|
|
||||||
item.setCheckState(0, QtCore.Qt.Checked)
|
|
||||||
|
|
||||||
else:
|
|
||||||
sets.append(item)
|
sets.append(item)
|
||||||
|
|
||||||
|
to_be_hidden = set()
|
||||||
|
to_be_shown = set()
|
||||||
|
self.blockSignals(True)
|
||||||
for it in sets:
|
for it in sets:
|
||||||
if it in from_parent:
|
if it in from_parent:
|
||||||
continue
|
continue
|
||||||
it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked)
|
it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked)
|
||||||
|
s1, s2 = self.data_change(it, emit=False)
|
||||||
|
to_be_hidden |= s2
|
||||||
|
to_be_shown |= s1
|
||||||
|
self.blockSignals(False)
|
||||||
|
self.stateChanged.emit(list(to_be_shown), list(to_be_hidden))
|
||||||
else:
|
else:
|
||||||
super().keyPressEvent(evt)
|
super().keyPressEvent(evt)
|
||||||
|
|
||||||
@ -334,24 +344,47 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
self.setDragEnabled(idx.column() == 0)
|
self.setDragEnabled(idx.column() == 0)
|
||||||
super().mousePressEvent(evt)
|
super().mousePressEvent(evt)
|
||||||
|
|
||||||
def remove_item(self, ids: list):
|
def remove_item(self, ids: list[str]):
|
||||||
iterator = QtWidgets.QTreeWidgetItemIterator(self)
|
iterator = QtWidgets.QTreeWidgetItemIterator(self)
|
||||||
|
|
||||||
|
toberemoved = []
|
||||||
|
graph_removal = []
|
||||||
|
|
||||||
|
# find all items that have to be removed
|
||||||
while iterator.value():
|
while iterator.value():
|
||||||
item = iterator.value()
|
item = iterator.value()
|
||||||
_id = item.data(0, QtCore.Qt.UserRole)
|
_id = item.data(0, QtCore.Qt.UserRole)
|
||||||
if _id in ids:
|
if _id in ids:
|
||||||
try:
|
try:
|
||||||
idx = item.parent().indexOfChild(item)
|
item_parent = item.parent()
|
||||||
item.parent().takeChild(idx)
|
if item_parent is None:
|
||||||
|
raise AttributeError
|
||||||
|
|
||||||
|
idx = item_parent.indexOfChild(item)
|
||||||
|
# item.parent().takeChild(idx)
|
||||||
|
|
||||||
|
toberemoved.append((item_parent, idx))
|
||||||
|
|
||||||
if _id in self._checked_sets:
|
if _id in self._checked_sets:
|
||||||
self._checked_sets.remove(_id)
|
self._checked_sets.remove(_id)
|
||||||
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
idx = self.invisibleRootItem().indexOfChild(item)
|
idx = self.invisibleRootItem().indexOfChild(item)
|
||||||
self.invisibleRootItem().takeChild(idx)
|
# self.invisibleRootItem().takeChild(idx)
|
||||||
|
|
||||||
|
graph_removal.append(idx)
|
||||||
|
|
||||||
|
if _id in self._checked_graphs:
|
||||||
self._checked_graphs.remove(_id)
|
self._checked_graphs.remove(_id)
|
||||||
|
|
||||||
iterator += 1
|
iterator += 1
|
||||||
|
|
||||||
|
for (item, set_idx) in sorted(toberemoved, key=lambda x: x[1], reverse=True):
|
||||||
|
item.takeChild(set_idx)
|
||||||
|
|
||||||
|
for graph_idx in sorted(graph_removal, reverse=True):
|
||||||
|
self.invisibleRootItem().takeChild(graph_idx)
|
||||||
|
|
||||||
self.update_indexes()
|
self.update_indexes()
|
||||||
|
|
||||||
def contextMenuEvent(self, evt):
|
def contextMenuEvent(self, evt):
|
||||||
@ -387,6 +420,10 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
for c in available_cycles.keys():
|
for c in available_cycles.keys():
|
||||||
col_menu.addAction(c)
|
col_menu.addAction(c)
|
||||||
|
|
||||||
|
action = menu.exec(evt.globalPos())
|
||||||
|
if action is None:
|
||||||
|
return
|
||||||
|
|
||||||
graphs = []
|
graphs = []
|
||||||
items = []
|
items = []
|
||||||
for i in self.selectedIndexes():
|
for i in self.selectedIndexes():
|
||||||
@ -395,7 +432,6 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
items.append(self.itemFromIndex(i))
|
items.append(self.itemFromIndex(i))
|
||||||
graphs.append(self.itemFromIndex(i).data(0, QtCore.Qt.UserRole))
|
graphs.append(self.itemFromIndex(i).data(0, QtCore.Qt.UserRole))
|
||||||
|
|
||||||
action = menu.exec(evt.globalPos())
|
|
||||||
if action == del_action:
|
if action == del_action:
|
||||||
for gid in graphs:
|
for gid in graphs:
|
||||||
self.management.delete_graph(gid)
|
self.management.delete_graph(gid)
|
||||||
@ -414,8 +450,7 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
del_action = menu.addAction('Exterminate sets')
|
del_action = menu.addAction('Exterminate sets')
|
||||||
cp_action = menu.addAction('Replicate sets')
|
cp_action = menu.addAction('Replicate sets')
|
||||||
cat_action = menu.addAction('Join us!')
|
cat_action = menu.addAction('Join us!')
|
||||||
plt_action = None
|
plt_action = save_action = extend_action = None
|
||||||
save_action = None
|
|
||||||
menu.addSeparator()
|
menu.addSeparator()
|
||||||
col_menu = menu.addMenu('Color cycle')
|
col_menu = menu.addMenu('Color cycle')
|
||||||
for c in available_cycles.keys():
|
for c in available_cycles.keys():
|
||||||
@ -446,6 +481,7 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
menu.addSeparator()
|
menu.addSeparator()
|
||||||
plt_action = menu.addAction('Plot fit parameter')
|
plt_action = menu.addAction('Plot fit parameter')
|
||||||
save_action = menu.addAction('Save fit parameter')
|
save_action = menu.addAction('Save fit parameter')
|
||||||
|
extend_action = menu.addAction('Extrapolate fit')
|
||||||
|
|
||||||
action = menu.exec(evt.globalPos())
|
action = menu.exec(evt.globalPos())
|
||||||
|
|
||||||
@ -469,6 +505,9 @@ class DataTree(QtWidgets.QTreeWidget):
|
|||||||
elif action == save_action:
|
elif action == save_action:
|
||||||
self.saveFits.emit(s)
|
self.saveFits.emit(s)
|
||||||
|
|
||||||
|
elif action == extend_action:
|
||||||
|
self.extendFits.emit(s)
|
||||||
|
|
||||||
elif action.parent() == col_menu:
|
elif action.parent() == col_menu:
|
||||||
self.management.set_cycle(s, action.text())
|
self.management.set_cycle(s, action.text())
|
||||||
|
|
||||||
@ -509,6 +548,7 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
|
|||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.tree = DataTree(self)
|
self.tree = DataTree(self)
|
||||||
self.verticalLayout.addWidget(self.tree)
|
self.verticalLayout.addWidget(self.tree)
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
self.tree.selectionModel().selectionChanged.connect(lambda x, y: self.show_property(x))
|
self.tree.selectionModel().selectionChanged.connect(lambda x, y: self.show_property(x))
|
||||||
|
|
||||||
self.tree.keyChanged.connect(lambda x, y: self.keyChanged.emit(x, y))
|
self.tree.keyChanged.connect(lambda x, y: self.keyChanged.emit(x, y))
|
||||||
@ -526,9 +566,9 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
|
|||||||
self.tree.add_graph(idd, name)
|
self.tree.add_graph(idd, name)
|
||||||
self.tree.blockSignals(False)
|
self.tree.blockSignals(False)
|
||||||
|
|
||||||
def add_item(self, idd: str, name: str, gid: str):
|
def add_item(self, idd: str, name: str, value: str, gid: str, update: bool= True):
|
||||||
self.tree.blockSignals(True)
|
self.tree.blockSignals(True)
|
||||||
self.tree.add_item((idd, name), gid)
|
self.tree.add_item((idd, name, value), gid, update=update)
|
||||||
self.tree.blockSignals(False)
|
self.tree.blockSignals(False)
|
||||||
|
|
||||||
def add_item_list(self, loi: list, gid: str):
|
def add_item_list(self, loi: list, gid: str):
|
||||||
@ -536,7 +576,7 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
|
|||||||
self.tree.add_item(loi, gid)
|
self.tree.add_item(loi, gid)
|
||||||
self.tree.blockSignals(False)
|
self.tree.blockSignals(False)
|
||||||
|
|
||||||
def remove_item(self, key):
|
def remove_item(self, key: list[str]):
|
||||||
self.tree.remove_item(key)
|
self.tree.remove_item(key)
|
||||||
|
|
||||||
def show_property(self, _: QtCore.QModelIndex = None):
|
def show_property(self, _: QtCore.QModelIndex = None):
|
||||||
|
@ -42,6 +42,8 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
self.max_y = inf
|
self.max_y = inf
|
||||||
self.min_y = -inf
|
self.min_y = -inf
|
||||||
|
|
||||||
|
self.treeWidget.itemChanged.connect(self._update_by_tree)
|
||||||
|
|
||||||
def __call__(self, graph_name, items):
|
def __call__(self, graph_name, items):
|
||||||
self.label_2.setText(f'Connected to {graph_name}\nChanging tab will remove all integration limits.')
|
self.label_2.setText(f'Connected to {graph_name}\nChanging tab will remove all integration limits.')
|
||||||
|
|
||||||
@ -77,7 +79,6 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
for idx, rnge in enumerate(self.ranges):
|
for idx, rnge in enumerate(self.ranges):
|
||||||
self._update_values(idx, rnge)
|
self._update_values(idx, rnge)
|
||||||
|
|
||||||
|
|
||||||
def add(self, pos):
|
def add(self, pos):
|
||||||
x = pos[0]
|
x = pos[0]
|
||||||
self.ranges.append((x, x*1.1))
|
self.ranges.append((x, x*1.1))
|
||||||
@ -108,6 +109,9 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
item_list = []
|
item_list = []
|
||||||
for text, val in [('Start', pts_i[0]), ('Stop', pts_i[1]), ('Areas', 0), ('Ratio', 1.)]:
|
for text, val in [('Start', pts_i[0]), ('Stop', pts_i[1]), ('Areas', 0), ('Ratio', 1.)]:
|
||||||
child = QtWidgets.QTreeWidgetItem()
|
child = QtWidgets.QTreeWidgetItem()
|
||||||
|
if text.startswith('S'):
|
||||||
|
child.setFlags(child.flags() | QtCore.Qt.ItemIsEditable)
|
||||||
|
else:
|
||||||
child.setFlags(QtCore.Qt.NoItemFlags)
|
child.setFlags(QtCore.Qt.NoItemFlags)
|
||||||
child.setText(0, f'{text}: {val:.5g}')
|
child.setText(0, f'{text}: {val:.5g}')
|
||||||
child.setForeground(0, QtGui.QBrush(QtGui.QColor('black')))
|
child.setForeground(0, QtGui.QBrush(QtGui.QColor('black')))
|
||||||
@ -121,8 +125,27 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
|
|
||||||
self._update_values(len(self.ranges) - 1, pts_i)
|
self._update_values(len(self.ranges) - 1, pts_i)
|
||||||
|
|
||||||
|
def _update_by_tree(self, item: QtWidgets.QTreeWidgetItem) -> None:
|
||||||
|
parent_item = item.parent()
|
||||||
|
idx = self.treeWidget.invisibleRootItem().indexOfChild(parent_item)
|
||||||
|
|
||||||
|
is_left_border = parent_item.indexOfChild(item) == 0
|
||||||
|
current_region = self.lines[idx][0]
|
||||||
|
current_limits = current_region.getRegion()
|
||||||
|
|
||||||
|
new_value = item.text(0)
|
||||||
|
try:
|
||||||
|
new_value = float(new_value)
|
||||||
|
if is_left_border:
|
||||||
|
current_region.setRegion((new_value, current_limits[1]))
|
||||||
|
else:
|
||||||
|
current_region.setRegion((current_limits[0], new_value))
|
||||||
|
except ValueError:
|
||||||
|
self._update_values(idx, current_limits)
|
||||||
|
|
||||||
def _update_integral(self):
|
def _update_integral(self):
|
||||||
idx = None
|
idx = None
|
||||||
|
reg = None
|
||||||
sender = self.sender()
|
sender = self.sender()
|
||||||
for i, (reg, _) in enumerate(self.lines):
|
for i, (reg, _) in enumerate(self.lines):
|
||||||
if sender == reg:
|
if sender == reg:
|
||||||
@ -132,19 +155,20 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
if idx is None:
|
if idx is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._update_values(idx, sender.getRegion())
|
self._update_values(idx, reg.getRegion())
|
||||||
|
|
||||||
def _update_values(self, idx, new_range):
|
def _update_values(self, idx, new_range):
|
||||||
self.ranges[idx] = new_range
|
self.ranges[idx] = new_range
|
||||||
|
|
||||||
area = self.make_integral(idx, *new_range)
|
area = self.make_integral(idx, *new_range)
|
||||||
|
|
||||||
|
self.treeWidget.blockSignals(True)
|
||||||
|
|
||||||
item = self.treeWidget.topLevelItem(idx)
|
item = self.treeWidget.topLevelItem(idx)
|
||||||
item.child(0).setText(0, f'Start: {new_range[0]:.5g}')
|
item.child(0).setText(0, f'Start: {new_range[0]:.5g}')
|
||||||
item.child(1).setText(0, f'Stop: {new_range[1]:.5g}')
|
item.child(1).setText(0, f'Stop: {new_range[1]:.5g}')
|
||||||
|
|
||||||
if area is not None:
|
if area is not None:
|
||||||
self.areas[idx] = area
|
|
||||||
item.child(2).setText(0, f'Area: {area:.5g}')
|
item.child(2).setText(0, f'Area: {area:.5g}')
|
||||||
if self.max_area > 0:
|
if self.max_area > 0:
|
||||||
self._set_ratios(idx, self.max_area)
|
self._set_ratios(idx, self.max_area)
|
||||||
@ -157,9 +181,12 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
self._set_ratios(i, curr_max)
|
self._set_ratios(i, curr_max)
|
||||||
self.max_area = curr_max
|
self.max_area = curr_max
|
||||||
|
|
||||||
|
self.treeWidget.blockSignals(False)
|
||||||
|
|
||||||
def _set_ratios(self, idx, max_value):
|
def _set_ratios(self, idx, max_value):
|
||||||
item = self.treeWidget.invisibleRootItem().child(idx)
|
item = self.treeWidget.invisibleRootItem().child(idx)
|
||||||
area_i = self.areas[idx]
|
area_i = self.areas[idx]
|
||||||
|
|
||||||
item.child(3).setText(0, f'Ratio: {area_i / max_value:.3g}')
|
item.child(3).setText(0, f'Ratio: {area_i / max_value:.3g}')
|
||||||
|
|
||||||
integral_line = self.lines[idx][1]
|
integral_line = self.lines[idx][1]
|
||||||
@ -178,6 +205,8 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
scale = (self.max_y-self.min_y) / area
|
scale = (self.max_y-self.min_y) / area
|
||||||
self.lines[idx][1].setData(x=integral[:, 0], y=integral[:, 1]*scale + self.min_y)
|
self.lines[idx][1].setData(x=integral[:, 0], y=integral[:, 1]*scale + self.min_y)
|
||||||
|
|
||||||
|
self.areas[idx] = area
|
||||||
|
|
||||||
return area
|
return area
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -177,7 +177,7 @@ class PointSelectWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
it.setText(f'{pos[0]:.5g} - {pos[1]:.5g}')
|
it.setText(f'{pos[0]:.5g} - {pos[1]:.5g}')
|
||||||
self.peaktable.blockSignals(False)
|
self.peaktable.blockSignals(False)
|
||||||
it_pts.blockSignals(True)
|
it_pts.blockSignals(True)
|
||||||
it_pts.setRegion(pos)
|
it_pts.setRegion(pos, use_log=True)
|
||||||
it_pts.blockSignals(False)
|
it_pts.blockSignals(False)
|
||||||
undo = False
|
undo = False
|
||||||
|
|
||||||
@ -187,9 +187,15 @@ class PointSelectWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
self.peaktable.blockSignals(False)
|
self.peaktable.blockSignals(False)
|
||||||
|
|
||||||
def set_graphs(self, graphs: list):
|
def set_graphs(self, graphs: list):
|
||||||
|
last_graph = self.graph_combobox.currentData()
|
||||||
self.graph_combobox.clear()
|
self.graph_combobox.clear()
|
||||||
for g in graphs:
|
idx = 0
|
||||||
|
for i, g in enumerate(graphs):
|
||||||
self.graph_combobox.addItem(g[1], userData=g[0])
|
self.graph_combobox.addItem(g[1], userData=g[0])
|
||||||
|
if g[0] == last_graph:
|
||||||
|
idx = i
|
||||||
|
|
||||||
|
self.graph_combobox.setCurrentIndex(idx)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged')
|
@QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged')
|
||||||
def changed_state(self, checked):
|
def changed_state(self, checked):
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
|
|
||||||
from pyqtgraph import mkColor, mkPen
|
from pyqtgraph import mkColor, mkPen, mkBrush
|
||||||
|
|
||||||
from nmreval.lib.colors import Tab10
|
from nmreval.lib.colors import Tab10
|
||||||
|
|
||||||
@ -42,11 +42,17 @@ class QShift(QtWidgets.QDialog, Ui_shift_dialog):
|
|||||||
|
|
||||||
def add_item(self, idx, name, x, y):
|
def add_item(self, idx, name, x, y):
|
||||||
color = mkColor(next(self._colors).rgb())
|
color = mkColor(next(self._colors).rgb())
|
||||||
if np.iscomplexobj(y):
|
|
||||||
pl = [PlotItem(x=x, y=y.real, name=name, pen=mkPen(color=color)),
|
if len(y) == 1:
|
||||||
PlotItem(x=x, y=y.imag, name=name, pen=mkPen(color=color))]
|
sym_kwds = {'symbol': 'o', 'symbolBrush': mkBrush(color=color), 'symbolPen': mkPen(color=color)}
|
||||||
else:
|
else:
|
||||||
pl = [PlotItem(x=x, y=y, name=name, pen=mkPen(color=color))]
|
sym_kwds = {'symbol': None, 'symbolBrush': mkBrush(color=color), 'symbolPen': mkPen(color=color)}
|
||||||
|
|
||||||
|
if np.iscomplexobj(y):
|
||||||
|
pl = [PlotItem(x=x, y=y.real, name=name, pen=mkPen(color=color), **sym_kwds),
|
||||||
|
PlotItem(x=x, y=y.imag, name=name, pen=mkPen(color=color), **sym_kwds)]
|
||||||
|
else:
|
||||||
|
pl = [PlotItem(x=x, y=y, name=name, pen=mkPen(color=color), **sym_kwds)]
|
||||||
|
|
||||||
self.data[idx] = (pl, x, y)
|
self.data[idx] = (pl, x, y)
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
from .phase_dialog import QApodDialog, QPhasedialog
|
from .phase_dialog import QPreviewDialog
|
||||||
from .baseline_dialog import QBaselineDialog
|
from .baseline_dialog import QBaselineDialog
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from nmreval.lib.logger import logger
|
||||||
from nmreval.math import apodization
|
from nmreval.math import apodization
|
||||||
from nmreval.lib.importer import find_models
|
from nmreval.lib.importer import find_models
|
||||||
from nmreval.utils.text import convert
|
from nmreval.utils.text import convert
|
||||||
@ -46,7 +47,7 @@ class EditSignalWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
stype = 'pts'
|
stype = 'pts'
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
_nop = float(self.lineEdit.text())
|
_nop = float(self.ls_lineEdit.text())
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_nop = 0.0
|
_nop = 0.0
|
||||||
stype = 'time'
|
stype = 'time'
|
||||||
@ -67,7 +68,7 @@ class EditSignalWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
self.do_something.emit(sender, (ph0, ph1, pvt))
|
self.do_something.emit(sender, (ph0, ph1, pvt))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print('You should never reach this by accident.')
|
logger.warning(f'You should never reach this by accident, invalid sender {sender!r}')
|
||||||
|
|
||||||
@QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged')
|
@QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged')
|
||||||
def change_apodization(self, index):
|
def change_apodization(self, index):
|
||||||
|
@ -1,119 +1,43 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
from pyqtgraph import mkPen
|
from pyqtgraph import mkPen
|
||||||
from numpy import inf, linspace
|
import numpy as np
|
||||||
from numpy.fft import fft, fftfreq, fftshift
|
from numpy import pi
|
||||||
|
from numpy.fft import fft, fftshift, fftfreq
|
||||||
|
|
||||||
|
from nmreval.data import FID, Spectrum
|
||||||
from ...lib.pg_objects import PlotItem, LogInfiniteLine
|
from ...lib.pg_objects import PlotItem, LogInfiniteLine
|
||||||
from nmreval.lib.importer import find_models
|
from nmreval.lib.importer import find_models
|
||||||
from nmreval.math import apodization as apodization
|
from nmreval.math import apodization as apodization
|
||||||
from nmreval.utils.text import convert
|
from nmreval.utils.text import convert
|
||||||
|
|
||||||
from ...Qt import QtCore, QtWidgets
|
from ...Qt import QtCore, QtWidgets, QtGui
|
||||||
from ..._py.apod_dialog import Ui_ApodEdit
|
from ..._py.apod_dialog import Ui_ApodEdit
|
||||||
from ..._py.phase_corr_dialog import Ui_SignalEdit
|
|
||||||
from ...lib.forms import FormWidget
|
from ...lib.forms import FormWidget
|
||||||
|
|
||||||
|
|
||||||
class QPreviewDialogs(QtWidgets.QDialog):
|
class QPreviewDialog(QtWidgets.QDialog, Ui_ApodEdit):
|
||||||
finished = QtCore.pyqtSignal(str, tuple)
|
finished = QtCore.pyqtSignal(str, tuple)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
self.data = []
|
|
||||||
self.graphs = []
|
|
||||||
|
|
||||||
self.mode = ''
|
|
||||||
|
|
||||||
def setRange(self, xlim: list, ylim: list, logmode: list[bool]):
|
|
||||||
self.graphicsView.getPlotItem().setLogMode(x=logmode[0], y=logmode[1])
|
|
||||||
if logmode[0]:
|
|
||||||
xlim = [np.log10(x) for x in xlim]
|
|
||||||
if logmode[1]:
|
|
||||||
ylim = [np.log10(y) for y in ylim]
|
|
||||||
|
|
||||||
self.graphicsView.setRange(xRange=xlim, yRange=ylim, padding=0, disableAutoRange=True)
|
|
||||||
|
|
||||||
def add_data(self, x, y):
|
|
||||||
self.data.append((x, y))
|
|
||||||
real_plt = PlotItem(x=x, y=y.real, pen=mkPen('b'), )
|
|
||||||
imag_plt = PlotItem(x=x, y=y.imag, pen=mkPen('r'))
|
|
||||||
self.graphs.append((real_plt, imag_plt))
|
|
||||||
self.graphicsView.addItem(real_plt)
|
|
||||||
self.graphicsView.addItem(imag_plt)
|
|
||||||
|
|
||||||
def done(self, val):
|
|
||||||
self.cleanup()
|
|
||||||
super().done(val)
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.cleanup()
|
|
||||||
super().close()
|
|
||||||
|
|
||||||
def accept(self):
|
|
||||||
self.finished.emit(self.mode, self.get_value())
|
|
||||||
super().accept()
|
|
||||||
|
|
||||||
def get_value(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def cleanup(self):
|
|
||||||
self.blockSignals(True)
|
|
||||||
|
|
||||||
for line in self.graphs:
|
|
||||||
for g in line:
|
|
||||||
self.graphicsView.removeItem(g)
|
|
||||||
del g
|
|
||||||
|
|
||||||
self.graphicsView.clear()
|
|
||||||
|
|
||||||
self.data = []
|
|
||||||
self.graphs = []
|
|
||||||
|
|
||||||
self.blockSignals(False)
|
|
||||||
|
|
||||||
|
|
||||||
class QPhasedialog(QPreviewDialogs, Ui_SignalEdit):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
self.mode = 'ph'
|
self.data = []
|
||||||
|
self.graphs = []
|
||||||
|
self._tmp_data_bl = []
|
||||||
|
self._tmp_data_zf = []
|
||||||
|
self._tmp_data_ls = []
|
||||||
|
self._tmp_data_ap = []
|
||||||
|
self._tmp_data_ph = []
|
||||||
|
|
||||||
self.pvt_line = LogInfiniteLine(pos=0, movable=True)
|
self.pvt_line = LogInfiniteLine(pos=0, movable=True)
|
||||||
self.graphicsView.addItem(self.pvt_line)
|
self.freq_graph.addItem(self.pvt_line)
|
||||||
self.pvt_line.sigPositionChanged.connect(self.move_line)
|
self.pvt_line.sigPositionChanged.connect(self.move_line)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(float, name='on_ph1slider_valueChanged')
|
self.ls_lineedit.hide()
|
||||||
@QtCore.pyqtSlot(float, name='on_ph0slider_valueChanged')
|
|
||||||
def _temp_phase(self, *args):
|
|
||||||
ph0, ph1, pvt = self.get_value()
|
|
||||||
self.pvt_line.setValue(pvt)
|
|
||||||
|
|
||||||
for i, (x, y) in enumerate(self.data):
|
|
||||||
phasecorr = np.exp(-1j * (ph0 + ph1*(x-pvt)/np.max(x))*np.pi/180.)
|
|
||||||
_y = y * phasecorr
|
|
||||||
|
|
||||||
self.graphs[i][0].setData(x=x, y=_y.real)
|
|
||||||
self.graphs[i][1].setData(x=x, y=_y.imag)
|
|
||||||
|
|
||||||
def get_value(self):
|
|
||||||
return float(self.ph0slider.text()), float(self.ph1slider.text()), float(self.pivot_lineedit.text())
|
|
||||||
|
|
||||||
def move_line(self, evt):
|
|
||||||
self.pivot_lineedit.setText(f'{evt.value():.5g}')
|
|
||||||
|
|
||||||
|
|
||||||
class QApodDialog(QPreviewDialogs, Ui_ApodEdit):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self.setupUi(self)
|
|
||||||
|
|
||||||
self._limits = (-inf, inf), -inf
|
|
||||||
|
|
||||||
self.apods = []
|
|
||||||
self.apods = find_models(apodization)
|
self.apods = find_models(apodization)
|
||||||
|
|
||||||
self.apodcombobox.blockSignals(True)
|
self.apodcombobox.blockSignals(True)
|
||||||
@ -122,72 +46,262 @@ class QApodDialog(QPreviewDialogs, Ui_ApodEdit):
|
|||||||
self.apodcombobox.blockSignals(False)
|
self.apodcombobox.blockSignals(False)
|
||||||
|
|
||||||
self.apod_graph = PlotItem(x=[], y=[])
|
self.apod_graph = PlotItem(x=[], y=[])
|
||||||
self.graphicsView.addItem(self.apod_graph)
|
self.time_graph.addItem(self.apod_graph)
|
||||||
|
|
||||||
self.mode = 'ap'
|
for g in [self.freq_graph, self.time_graph]:
|
||||||
|
pl = g.getPlotItem()
|
||||||
|
pl.hideButtons()
|
||||||
|
pl.setMenuEnabled(False)
|
||||||
|
|
||||||
|
self._all_time = None
|
||||||
|
self._all_freq = None
|
||||||
|
|
||||||
self.change_apodization(0)
|
self.change_apodization(0)
|
||||||
|
|
||||||
def add_data(self, x, y):
|
self.shift_box.clicked.connect(self._update_shift)
|
||||||
real_plt = PlotItem(x=x, y=y.real, pen=mkPen('b'))
|
self.ls_spinbox.valueChanged.connect(self._update_shift)
|
||||||
# imag_plt = (x=x, y=y.imag, pen=pg.mkPen('r'))
|
self.ls_lineedit.setValidator(QtGui.QDoubleValidator())
|
||||||
self.graphicsView.addItem(real_plt)
|
self.ls_lineedit.textChanged.connect(self._update_shift)
|
||||||
# self.graphicsView.addItem(imag_plt)
|
|
||||||
|
|
||||||
|
self.zerofill_box.clicked.connect(self._update_zf)
|
||||||
|
self.zf_spinbox.valueChanged.connect(self._update_zf)
|
||||||
|
|
||||||
|
self.apod_box.clicked.connect(self._update_apod)
|
||||||
|
|
||||||
|
self.phase_box.clicked.connect(self._update_phase)
|
||||||
|
self.ph0_spinbox.valueChanged.connect(self._update_phase)
|
||||||
|
self.ph1_spinbox.valueChanged.connect(self._update_phase)
|
||||||
|
self.pivot_lineedit.setValidator(QtGui.QDoubleValidator())
|
||||||
|
self.pivot_lineedit.textChanged.connect(self._update_phase)
|
||||||
|
self.pivot_lineedit.textEdited.connect(lambda x: self.pvt_line.setValue(float(x)))
|
||||||
|
|
||||||
|
def add_data(self: QPreviewDialog, data: FID | Spectrum) -> bool:
|
||||||
|
|
||||||
|
if isinstance(data, FID):
|
||||||
|
if self._all_freq:
|
||||||
|
msg = QtWidgets.QMessageBox.warning(self, 'Mixed types',
|
||||||
|
'Timesignals and spectra cannot be edited at the same time.')
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self._all_time = True
|
||||||
|
self._all_freq = False
|
||||||
|
|
||||||
|
elif isinstance(data, Spectrum):
|
||||||
|
if self._all_time:
|
||||||
|
msg = QtWidgets.QMessageBox.warning(self, 'Mixed types',
|
||||||
|
'Timesignals and spectra cannot be edited at the same time.')
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self._all_time = False
|
||||||
|
self._all_freq = True
|
||||||
|
|
||||||
|
fid = data.copy()
|
||||||
|
spec = self._temp_fft_time(fid.x, fid.y, self.baseline_box.isChecked())
|
||||||
|
|
||||||
|
x_len = data.x.size
|
||||||
|
self.zf_spinbox.setMaximum(min(2**17//x_len, 3))
|
||||||
|
|
||||||
|
real_plt = PlotItem(x=fid.x, y=fid.y.real, pen=mkPen('b'))
|
||||||
|
imag_plt = PlotItem(x=fid.x, y=fid.y.imag, pen=mkPen('r'))
|
||||||
|
self.time_graph.addItem(imag_plt)
|
||||||
|
self.time_graph.addItem(real_plt)
|
||||||
|
|
||||||
|
real_plt_fft = PlotItem(x=spec[0], y=spec[1].real, pen=mkPen('b'))
|
||||||
|
imag_plt_fft = PlotItem(x=spec[0], y=spec[1].imag, pen=mkPen('r'))
|
||||||
|
self.freq_graph.addItem(imag_plt_fft)
|
||||||
|
self.freq_graph.addItem(real_plt_fft)
|
||||||
|
|
||||||
|
self.data.append(data)
|
||||||
|
for p in [self._tmp_data_bl, self._tmp_data_ls]:
|
||||||
|
p.append(data.y.copy())
|
||||||
|
|
||||||
|
for p in [self._tmp_data_zf, self._tmp_data_ap]:
|
||||||
|
p.append((data.x, data.y.copy()))
|
||||||
|
|
||||||
|
self._tmp_data_ph.append((data.x, data.y, spec[0], spec[1]))
|
||||||
|
|
||||||
|
self.graphs.append((real_plt, imag_plt, real_plt_fft, imag_plt_fft))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(name='on_baseline_box_clicked')
|
||||||
|
def _update_bl(self):
|
||||||
|
if self.baseline_box.isChecked():
|
||||||
|
for y in self._tmp_data_bl:
|
||||||
|
self._temp_baseline(y)
|
||||||
|
else:
|
||||||
|
for i, d in enumerate(self.data):
|
||||||
|
self._tmp_data_bl[i] = d.y.copy()
|
||||||
|
|
||||||
|
self._update_shift()
|
||||||
|
|
||||||
|
def _update_shift(self):
|
||||||
|
if self.shift_box.isChecked():
|
||||||
|
if self.ls_combobox.currentIndex() == 0:
|
||||||
|
num_points = self.ls_spinbox.value()
|
||||||
|
is_time = False
|
||||||
|
else:
|
||||||
|
num_points = float(self.ls_lineedit.text())
|
||||||
|
is_time = True
|
||||||
|
|
||||||
|
for i, y in enumerate(self._tmp_data_bl):
|
||||||
|
self._tmp_data_ls[i] = self._temp_leftshift(self.data[i].dx, y, num_points, is_time)
|
||||||
|
|
||||||
|
else:
|
||||||
|
for i, y in enumerate(self._tmp_data_bl):
|
||||||
|
self._tmp_data_ls[i] = y
|
||||||
|
|
||||||
|
self._update_zf()
|
||||||
|
|
||||||
|
def _update_zf(self):
|
||||||
|
zf_padding = self.zf_spinbox.value()
|
||||||
|
|
||||||
|
if self.zerofill_box.isChecked():
|
||||||
|
for i, y in enumerate(self._tmp_data_ls):
|
||||||
|
self._tmp_data_zf[i] = self._temp_zerofill(self.data[i].x, y, zf_padding)
|
||||||
|
|
||||||
|
else:
|
||||||
|
for i, y in enumerate(self._tmp_data_ls):
|
||||||
|
self._tmp_data_zf[i] = self.data[i].x, y
|
||||||
|
|
||||||
|
self._update_apod()
|
||||||
|
|
||||||
|
def _update_apod(self):
|
||||||
|
if self.apod_box.isChecked():
|
||||||
|
model = self.apods[self.apodcombobox.currentIndex()]
|
||||||
|
p = self._get_parameter()
|
||||||
|
|
||||||
|
x_limit = np.inf, -np.inf
|
||||||
|
y_limit = -np.inf
|
||||||
|
|
||||||
|
for i, (x, y) in enumerate(self._tmp_data_zf):
|
||||||
|
self._tmp_data_ap[i] = x, y * model.apod(x, *p)
|
||||||
|
y_limit = max(y.real.max(), y_limit)
|
||||||
|
x_limit = min(x_limit[0], x.min()), max(x_limit[1], x.max())
|
||||||
|
|
||||||
|
_x_apod = np.linspace(*x_limit, num=150)
|
||||||
|
_y_apod = model.apod(_x_apod, *p)
|
||||||
|
self.apod_graph.setData(x=_x_apod, y=y_limit * _y_apod)
|
||||||
|
self.apod_graph.show()
|
||||||
|
|
||||||
|
else:
|
||||||
|
for i, (x, y) in enumerate(self._tmp_data_zf):
|
||||||
|
self._tmp_data_ap[i] = x, y
|
||||||
|
|
||||||
|
self.apod_graph.hide()
|
||||||
|
|
||||||
|
self._update_phase()
|
||||||
|
|
||||||
|
def _update_phase(self):
|
||||||
|
if self.phase_box.isChecked():
|
||||||
|
pvt = float(self.pivot_lineedit.text())
|
||||||
|
self.pvt_line.show()
|
||||||
|
ph0 = self.ph0_spinbox.value()
|
||||||
|
ph1 = self.ph1_spinbox.value()
|
||||||
|
|
||||||
|
for i, (x, y) in enumerate(self._tmp_data_ap):
|
||||||
|
x_fft, y_fft = self._temp_fft_time(x, y, self.baseline_box.isChecked())
|
||||||
|
|
||||||
|
if ph0 != 0:
|
||||||
|
y = self._temp_phase(x, y, ph0, 0, 0)
|
||||||
|
y_fft = self._temp_phase(x, y_fft, ph0, ph1, pvt)
|
||||||
|
elif ph1 != 0:
|
||||||
|
y_fft = self._temp_phase(x, y_fft, ph0, ph1, pvt)
|
||||||
|
|
||||||
|
self._tmp_data_ph[i] = x, y, x_fft, y_fft
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.pvt_line.hide()
|
||||||
|
for i, (x, y) in enumerate(self._tmp_data_ap):
|
||||||
|
self._tmp_data_ph[i] = x, y, *self._temp_fft_time(x, y, self.baseline_box.isChecked())
|
||||||
|
|
||||||
|
self._update_plots()
|
||||||
|
|
||||||
|
def _update_plots(self):
|
||||||
|
for i, (x, y, xf, yf) in enumerate(self._tmp_data_ph):
|
||||||
|
self.graphs[i][0].setData(x=x, y=y.real)
|
||||||
|
self.graphs[i][1].setData(x=x, y=y.imag)
|
||||||
|
|
||||||
|
self.graphs[i][2].setData(x=xf, y=yf.real)
|
||||||
|
self.graphs[i][3].setData(x=xf, y=yf.imag)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _temp_baseline_time(y):
|
||||||
|
y -= y[int(-0.12 * y.size):].mean()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _temp_baseline_freq(y):
|
||||||
|
region = int(0.12 * y.size)
|
||||||
|
y -= np.mean([y[-region:], y[:region]])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _temp_phase(x: np.ndarray, y: np.ndarray, ph0: float, ph1: float, pvt: float) -> np.ndarray:
|
||||||
|
phase_correction = np.exp(-1j * (ph0 + ph1 * (x - pvt) / x.max()) * pi / 180.)
|
||||||
|
_y = y * phase_correction
|
||||||
|
|
||||||
|
return _y
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _temp_zerofill(x: np.ndarray, y: np.ndarray, num_padding: int) -> tuple[np.ndarray, np.ndarray]:
|
||||||
|
length = x.size
|
||||||
|
factor = 2**num_padding
|
||||||
|
|
||||||
|
_y = np.r_[y, np.zeros((factor-1) * length)]
|
||||||
|
|
||||||
|
_temp_x = np.arange(1, (factor-1) * length+1) * (x[1]-x[0]) + np.max(x)
|
||||||
|
_x = np.r_[x, _temp_x]
|
||||||
|
|
||||||
|
return _x, _y
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _temp_leftshift(dx: np.ndarray, y: np.ndarray, points: float | int, is_time: bool) -> np.ndarray:
|
||||||
|
if is_time:
|
||||||
|
points = int(points//dx)
|
||||||
|
_y = np.roll(y, -points)
|
||||||
|
_y[-points-1:] = 0
|
||||||
|
|
||||||
|
return _y
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _temp_fft_time(x: np.ndarray, y: np.ndarray, baseline: bool = False) -> tuple[np.ndarray, np.ndarray]:
|
||||||
y_fft = fftshift(fft(y))
|
y_fft = fftshift(fft(y))
|
||||||
x_fft = fftshift(fftfreq(len(x), d=x[1]-x[0]))
|
x_fft = fftshift(fftfreq(len(x), d=x[1]-x[0]))
|
||||||
real_plt_fft = PlotItem(x=x_fft, y=y_fft.real, pen=mkPen('b'))
|
|
||||||
# imag_plt_fft = pg.PlotDataItem(x=x_fft, y=y_fft.imag, pen=pg.mkPen('b'))
|
|
||||||
self.graphicsView_2.addItem(real_plt_fft)
|
|
||||||
# self.graphicsView_2.addItem(imag_plt_fft)
|
|
||||||
|
|
||||||
self.graphs.append((real_plt, real_plt_fft))
|
if baseline:
|
||||||
self.data.append((x, y, x_fft))
|
QPreviewDialog._temp_baseline_freq(y_fft)
|
||||||
|
|
||||||
xlimits = (max(x.min(), self._limits[0][0]), min(x.max(), self._limits[0][1]))
|
return x_fft, y_fft
|
||||||
ylimit = max(self._limits[1], y.real.max())
|
|
||||||
self._limits = xlimits, ylimit
|
@staticmethod
|
||||||
|
def _temp_fft_freq(x: np.ndarray, y: np.ndarray, _=None):
|
||||||
|
return x, y
|
||||||
|
|
||||||
|
def move_line(self, evt):
|
||||||
|
self.pivot_lineedit.setText(f'{evt.value():.5g}')
|
||||||
|
|
||||||
@QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged')
|
@QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged')
|
||||||
def change_apodization(self, index):
|
def change_apodization(self, index: int) -> None:
|
||||||
# delete old widgets
|
# delete old widgets
|
||||||
self.eqn_label.setText(convert(self.apods[index].equation))
|
self.eqn_label.setText(convert(self.apods[index].equation))
|
||||||
while self.widget_layout.count():
|
while self.widget_layout.count():
|
||||||
item = self.widget_layout.takeAt(0)
|
item = self.widget_layout.takeAt(0)
|
||||||
|
if isinstance(item, FormWidget):
|
||||||
|
item.disconnect()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item.widget().deleteLater()
|
item.widget().deleteLater()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# set up parameter widgets for new model
|
# set up parameter widgets for new model
|
||||||
for k, v in enumerate(self.apods[index]().params):
|
for k, v in enumerate(self.apods[index].params):
|
||||||
widgt = FormWidget(name=v)
|
widget = FormWidget(name=v)
|
||||||
widgt.valueChanged.connect(self._temp_apod)
|
widget.value = 1
|
||||||
self.widget_layout.addWidget(widgt)
|
widget.valueChanged.connect(self._update_apod)
|
||||||
|
self.widget_layout.addWidget(widget)
|
||||||
|
|
||||||
self.widget_layout.addStretch()
|
self.widget_layout.addStretch()
|
||||||
self._temp_apod()
|
self._update_apod()
|
||||||
|
|
||||||
def _temp_apod(self):
|
|
||||||
apodmodel = self.apods[self.apodcombobox.currentIndex()]
|
|
||||||
p = self._get_parameter()
|
|
||||||
|
|
||||||
if self.data:
|
|
||||||
for i, (x, y, x_fft) in enumerate(self.data):
|
|
||||||
y2 = apodmodel.apod(x, *p)
|
|
||||||
_y = y2 * y
|
|
||||||
self.graphs[i][0].setData(x=x, y=_y.real)
|
|
||||||
# self.graphs[i][1].setData(y=_y.imag)
|
|
||||||
y_fft = fftshift(fft(_y))
|
|
||||||
self.graphs[i][1].setData(x=x_fft, y=y_fft.real)
|
|
||||||
# self.graphs[i][3].setData(y=y_fft.imag)
|
|
||||||
|
|
||||||
_x_apod = linspace(self._limits[0][0], self._limits[0][1])
|
|
||||||
try:
|
|
||||||
_y_apod = apodmodel.apod(_x_apod, *p)
|
|
||||||
self.apod_graph.setData(x=_x_apod, y=self._limits[1]*_y_apod)
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _get_parameter(self):
|
def _get_parameter(self):
|
||||||
p = []
|
p = []
|
||||||
@ -201,8 +315,113 @@ class QApodDialog(QPreviewDialogs, Ui_ApodEdit):
|
|||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def get_value(self):
|
@QtCore.pyqtSlot(int, name='on_ls_combobox_currentIndexChanged')
|
||||||
apodmodel = self.apods[self.apodcombobox.currentIndex()]
|
def change_ls(self, idx: int) -> None:
|
||||||
p = self._get_parameter()
|
self.ls_lineedit.setVisible(bool(idx))
|
||||||
|
self.ls_spinbox.setVisible(not bool(idx))
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(bool, name='on_ft_checkbox_stateChanged')
|
||||||
|
def change_ft(self, state: bool):
|
||||||
|
self.ph1_spinbox.setEnabled(state)
|
||||||
|
self.pivot_lineedit.setEnabled(state)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.blockSignals(True)
|
||||||
|
|
||||||
|
for line in self.graphs:
|
||||||
|
for g in line:
|
||||||
|
self.time_graph.removeItem(g)
|
||||||
|
self.freq_graph.removeItem(g)
|
||||||
|
del g
|
||||||
|
|
||||||
|
self.time_graph.clear()
|
||||||
|
self.freq_graph.clear()
|
||||||
|
|
||||||
|
self._tmp_data_ap = []
|
||||||
|
self._tmp_data_bl = []
|
||||||
|
self._tmp_data_ls = []
|
||||||
|
self._tmp_data_ph = []
|
||||||
|
self._tmp_data_zf = []
|
||||||
|
|
||||||
|
self.data = []
|
||||||
|
self.graphs = []
|
||||||
|
self.freq_graph.removeItem(self.pvt_line)
|
||||||
|
self.time_graph.removeItem(self.pvt_line)
|
||||||
|
|
||||||
|
self.blockSignals(False)
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
edits = [(None,), (None,), (None,), (None,), (None,), (None,)]
|
||||||
|
|
||||||
|
if self.baseline_box.isChecked():
|
||||||
|
edits[0] = (True,)
|
||||||
|
|
||||||
|
if self.zerofill_box.isChecked():
|
||||||
|
edits[2] = (self.zf_spinbox.value(),)
|
||||||
|
|
||||||
|
if self.shift_box.isChecked():
|
||||||
|
if self.ls_combobox.currentIndex() == 0:
|
||||||
|
edits[1] = (self.ls_spinbox.value(), 'pts')
|
||||||
|
else:
|
||||||
|
edits[1] = (float(self.ls_lineedit.text()), 'time')
|
||||||
|
|
||||||
|
if self.apod_box.isChecked():
|
||||||
|
edits[3] = (self._get_parameter(), self.apods[self.apodcombobox.currentIndex()])
|
||||||
|
|
||||||
|
if self.phase_box.isChecked():
|
||||||
|
edits[4] = (self.ph0_spinbox.value(), self.ph1_spinbox.value(), float(self.pivot_lineedit.text()))
|
||||||
|
|
||||||
|
if self.ft_box.isChecked():
|
||||||
|
edits[5] = (self.phase_before_button.isChecked(),)
|
||||||
|
|
||||||
|
return edits
|
||||||
|
|
||||||
|
def exec(self):
|
||||||
|
self._prepare_ui()
|
||||||
|
return super().exec()
|
||||||
|
|
||||||
|
def _prepare_ui(self):
|
||||||
|
"""Stuff we have to do before showing the window but after all the data was added"""
|
||||||
|
|
||||||
|
vb = self.freq_graph.getPlotItem().getViewBox()
|
||||||
|
vb.disableAutoRange(axis=vb.YAxis)
|
||||||
|
|
||||||
|
vb = self.time_graph.getPlotItem().getViewBox()
|
||||||
|
vb.disableAutoRange(axis=vb.YAxis)
|
||||||
|
|
||||||
|
self.zerofill_box.setVisible(self._all_time)
|
||||||
|
self.apod_box.setVisible(self._all_time)
|
||||||
|
self.shift_box.setVisible(self._all_time)
|
||||||
|
self.time_graph.setVisible(self._all_time)
|
||||||
|
self.logtime_widget.setVisible(self._all_time)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int, name='on_logx_time_stateChanged')
|
||||||
|
@QtCore.pyqtSlot(int, name='on_logy_time_stateChanged')
|
||||||
|
@QtCore.pyqtSlot(int, name='on_logx_freq_stateChanged')
|
||||||
|
@QtCore.pyqtSlot(int, name='on_logy_freq_stateChanged')
|
||||||
|
def set_log(self, state: int):
|
||||||
|
switch = {
|
||||||
|
self.logx_time: lambda _x: self.time_graph.setLogMode(x=_x),
|
||||||
|
self.logy_time: lambda _x: self.time_graph.setLogMode(y=_x),
|
||||||
|
self.logx_freq: lambda _x: self.freq_graph.setLogMode(x=_x),
|
||||||
|
self.logy_freq: lambda _x: self.freq_graph.setLogMode(y=_x),
|
||||||
|
}[self.sender()]
|
||||||
|
switch(state == QtCore.Qt.Checked)
|
||||||
|
|
||||||
|
vb = self.freq_graph.getPlotItem().getViewBox()
|
||||||
|
vb.disableAutoRange(axis=vb.YAxis)
|
||||||
|
|
||||||
|
vb = self.time_graph.getPlotItem().getViewBox()
|
||||||
|
vb.disableAutoRange(axis=vb.YAxis)
|
||||||
|
|
||||||
|
|
||||||
|
self._temp_baseline = self._temp_baseline_time if self._all_time else self._temp_baseline_freq
|
||||||
|
self._temp_fft = self._temp_fft_time if self._all_time else self._temp_fft_freq
|
||||||
|
|
||||||
|
self.freq_graph.setVisible(self._all_time)
|
||||||
|
if self._all_freq:
|
||||||
|
self.time_graph.addItem(self.pvt_line)
|
||||||
|
else:
|
||||||
|
self.freq_graph.addItem(self.pvt_line)
|
||||||
|
|
||||||
|
|
||||||
return p, apodmodel
|
|
||||||
|
@ -188,7 +188,15 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog):
|
|||||||
new_value = complex(val)
|
new_value = complex(val)
|
||||||
new_value = new_value.real if new_value.imag == 0 else new_value
|
new_value = new_value.real if new_value.imag == 0 else new_value
|
||||||
|
|
||||||
|
# table view loses focus when itemChanged is emitted
|
||||||
|
# if edit of item is cause of change resume editing at next item
|
||||||
|
prev_state = self.tableView.state()
|
||||||
|
idx = self.tableView.currentIndex()
|
||||||
|
idx = idx.sibling((col+1)//3+row, (col+1) % 3)
|
||||||
self.itemChanged.emit(sid, (col, row), new_value)
|
self.itemChanged.emit(sid, (col, row), new_value)
|
||||||
|
if prev_state == self.tableView.State.EditingState:
|
||||||
|
self.tableView.setCurrentIndex(idx)
|
||||||
|
self.tableView.edit(idx)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(QtCore.QItemSelection, QtCore.QItemSelection)
|
@QtCore.pyqtSlot(QtCore.QItemSelection, QtCore.QItemSelection)
|
||||||
def show_position(self, *_):
|
def show_position(self, *_):
|
||||||
@ -259,10 +267,7 @@ class ValueModel(QtCore.QAbstractTableModel):
|
|||||||
row = idx.row()
|
row = idx.row()
|
||||||
if role in [QtCore.Qt.DisplayRole, QtCore.Qt.EditRole]:
|
if role in [QtCore.Qt.DisplayRole, QtCore.Qt.EditRole]:
|
||||||
val = self._data[row][idx.column()]
|
val = self._data[row][idx.column()]
|
||||||
if isinstance(val, complex):
|
return self.as_string(val)
|
||||||
return f'{val.real:.8g}{val.imag:+.8g}j'
|
|
||||||
else:
|
|
||||||
return f'{val:.8g}'
|
|
||||||
|
|
||||||
elif role == QtCore.Qt.BackgroundRole:
|
elif role == QtCore.Qt.BackgroundRole:
|
||||||
pal = QtGui.QGuiApplication.palette()
|
pal = QtGui.QGuiApplication.palette()
|
||||||
@ -295,11 +300,16 @@ class ValueModel(QtCore.QAbstractTableModel):
|
|||||||
|
|
||||||
if value:
|
if value:
|
||||||
if role == QtCore.Qt.EditRole:
|
if role == QtCore.Qt.EditRole:
|
||||||
|
if value == self.as_string(self._data[row][col]):
|
||||||
|
return True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
value = complex(value)
|
value = complex(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# not a number
|
# not a number
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
value = value.real if value.imag == 0 else value
|
||||||
self._data[row][col] = value.real if value.imag == 0 else value
|
self._data[row][col] = value.real if value.imag == 0 else value
|
||||||
self.itemChanged.emit(col, row, str(value))
|
self.itemChanged.emit(col, row, str(value))
|
||||||
self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [role])
|
self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [role])
|
||||||
@ -368,3 +378,10 @@ class ValueModel(QtCore.QAbstractTableModel):
|
|||||||
def unmask(self):
|
def unmask(self):
|
||||||
self.mask = [True] * self.total_rows
|
self.mask = [True] * self.total_rows
|
||||||
self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [ValueModel.maskRole])
|
self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [ValueModel.maskRole])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def as_string(value) -> str:
|
||||||
|
if isinstance(value, complex):
|
||||||
|
return f'{value.real:.8g}{value.imag:+.8g}j'
|
||||||
|
else:
|
||||||
|
return f'{value:.8g}'
|
||||||
|
0
src/gui_qt/dsc/__init__.py
Normal file
0
src/gui_qt/dsc/__init__.py
Normal file
375
src/gui_qt/dsc/glass_dialog.py
Normal file
375
src/gui_qt/dsc/glass_dialog.py
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
from itertools import cycle
|
||||||
|
|
||||||
|
from numpy import array, nan, isnan
|
||||||
|
from pyqtgraph import mkPen, mkBrush
|
||||||
|
|
||||||
|
from nmreval.dsc.hodge import tau_hodge
|
||||||
|
from nmreval.lib.colors import Tab10
|
||||||
|
from ..Qt import QtWidgets, QtCore
|
||||||
|
from .._py.tnmh_dialog import Ui_DSCEvalDialog
|
||||||
|
from ..lib.pg_objects import PlotItem, RegionItem
|
||||||
|
|
||||||
|
from nmreval.data import DSC, Points
|
||||||
|
|
||||||
|
|
||||||
|
class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
|
||||||
|
newData = QtCore.pyqtSignal(dict, str)
|
||||||
|
|
||||||
|
def __init__(self, management, parent=None):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self._management = management
|
||||||
|
self._colors = cycle(Tab10)
|
||||||
|
|
||||||
|
self._dsc = {}
|
||||||
|
self._plots = {}
|
||||||
|
self._tg_value = {}
|
||||||
|
self._fit = {}
|
||||||
|
self._hodge = {
|
||||||
|
'onset': (
|
||||||
|
PlotItem(x=[], y=[], pen=None, symbol='o', symbolBrush=Tab10.TabBlue.rgb(), name='Onset'),
|
||||||
|
None,
|
||||||
|
(PlotItem(x=[], y=[], pen=mkPen({'color': Tab10.TabBlue.rgb()})),
|
||||||
|
PlotItem(x=[], y=[], pen=None, symbol='o', symbolBrush=Tab10.TabBlue.rgb())),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
'midpoint': (
|
||||||
|
PlotItem(x=[], y=[], pen=None, symbol='s', symbolBrush=Tab10.TabOrange.rgb(), name='Midpoint'),
|
||||||
|
None,
|
||||||
|
(PlotItem(x=[], y=[], pen=mkPen({'color': Tab10.TabOrange.rgb()})),
|
||||||
|
PlotItem(x=[], y=[], pen=None, symbol='s', symbolBrush=Tab10.TabOrange.rgb())),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
'end': (
|
||||||
|
PlotItem(x=[], y=[], pen=None, symbol='t', symbolBrush=Tab10.TabGreen.rgb(), name='End'),
|
||||||
|
None,
|
||||||
|
(PlotItem(x=[], y=[], pen=mkPen({'color': Tab10.TabGreen.rgb()})),
|
||||||
|
PlotItem(x=[], y=[], pen=None, symbol='t', symbolBrush=Tab10.TabGreen.rgb())),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
'inflection': (
|
||||||
|
PlotItem(x=[], y=[], pen=None, symbol='d', symbolBrush=Tab10.TabRed.rgb(), name='Inflection'),
|
||||||
|
None,
|
||||||
|
(PlotItem(x=[], y=[], pen=mkPen({'color': Tab10.TabRed.rgb()})),
|
||||||
|
PlotItem(x=[], y=[], pen=None, symbol='d', symbolBrush=Tab10.TabRed.rgb())),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
'fictive': (
|
||||||
|
PlotItem(x=[], y=[], pen=None, symbol='t1', symbolBrush=Tab10.TabPurple.rgb(), name='Fictive'),
|
||||||
|
None,
|
||||||
|
(PlotItem(x=[], y=[], pen=mkPen({'color': Tab10.TabPurple.rgb()})),
|
||||||
|
PlotItem(x=[], y=[], pen=None, symbol='t1', symbolBrush=Tab10.TabPurple.rgb())),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
self._lines = {}
|
||||||
|
self.tau_plot.getPlotItem().addLegend()
|
||||||
|
for plt, _, fitplt, _ in self._hodge.values():
|
||||||
|
self.tau_plot.addItem(plt)
|
||||||
|
self.tghodge_graph.addItem(fitplt[0])
|
||||||
|
self.tghodge_graph.addItem(fitplt[1])
|
||||||
|
self.tau_plot.setLogMode(y=True)
|
||||||
|
self.tghodge_graph.setLogMode(y=True)
|
||||||
|
|
||||||
|
self.limits = RegionItem(), RegionItem()
|
||||||
|
for lim in self.limits:
|
||||||
|
self.dsc_plot.addItem(lim)
|
||||||
|
self._limitless = True
|
||||||
|
|
||||||
|
self.add_sets()
|
||||||
|
|
||||||
|
self.tnmh_graph_check.stateChanged.connect(lambda state: self.tnmh_graph_combo.setEnabled(not bool(state)))
|
||||||
|
self.hodge_graph_check.stateChanged.connect(lambda state: self.hodge_graph_combo.setEnabled(not bool(state)))
|
||||||
|
|
||||||
|
self.next_button.clicked.connect(lambda: self.stackedWidget.setCurrentIndex((self.stackedWidget.currentIndex() + 1) % 3))
|
||||||
|
self.back_button.clicked.connect(lambda: self.stackedWidget.setCurrentIndex((self.stackedWidget.currentIndex() + 2) % 3))
|
||||||
|
|
||||||
|
self.listWidget.itemChanged.connect(self.change_visibility)
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
self.clear()
|
||||||
|
self._colors = cycle(Tab10)
|
||||||
|
self.add_sets()
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.listWidget.clear()
|
||||||
|
self.tg_tree.clear()
|
||||||
|
self.tnmh_tree.clear()
|
||||||
|
|
||||||
|
for plots in self._plots.values():
|
||||||
|
for val in plots:
|
||||||
|
self.dsc_plot.removeItem(val)
|
||||||
|
self.tnmh_graphics.removeItem(val)
|
||||||
|
|
||||||
|
for key, plt in self._hodge.items():
|
||||||
|
plt[0].setData(x=[], y=[])
|
||||||
|
plt[2][0].setData(x=[], y=[])
|
||||||
|
plt[2][1].setData(x=[], y=[])
|
||||||
|
self._hodge[key] = (plt[0], None, plt[2], None)
|
||||||
|
|
||||||
|
self._dsc = {}
|
||||||
|
self._plots = {}
|
||||||
|
self._tg_value = {}
|
||||||
|
self._lines = {}
|
||||||
|
self._fit = {}
|
||||||
|
|
||||||
|
self.stackedWidget.setCurrentIndex(0)
|
||||||
|
|
||||||
|
def add_sets(self):
|
||||||
|
for w in (self.tnmh_graph_combo, self.hodge_graph_combo):
|
||||||
|
w.clear()
|
||||||
|
for graphs in self._management.graphs.list():
|
||||||
|
w.addItem(graphs[1], userData=graphs[0])
|
||||||
|
|
||||||
|
min_x = 10_000_000
|
||||||
|
max_x = -10_000_000
|
||||||
|
for (key, name), c in zip(self._management.active_sets, self._colors):
|
||||||
|
data = self._management[key].data
|
||||||
|
if not isinstance(data, DSC):
|
||||||
|
continue
|
||||||
|
|
||||||
|
min_x = min(min_x, data.x.min())
|
||||||
|
max_x = max(max_x, data.x.max())
|
||||||
|
|
||||||
|
item = QtWidgets.QListWidgetItem(name)
|
||||||
|
item.setCheckState(QtCore.Qt.Checked)
|
||||||
|
item.setData(QtCore.Qt.UserRole, key)
|
||||||
|
item.setForeground(mkBrush(c.rgb()))
|
||||||
|
self.listWidget.addItem(item)
|
||||||
|
|
||||||
|
self._dsc[key] = (data, None)
|
||||||
|
|
||||||
|
data_plot = PlotItem(x=data.x, y=data.y, pen=mkPen(c.rgb()))
|
||||||
|
self.dsc_plot.addItem(data_plot)
|
||||||
|
|
||||||
|
glass = PlotItem()
|
||||||
|
glass.set_line(style=2, color=c)
|
||||||
|
self.dsc_plot.addItem(glass)
|
||||||
|
|
||||||
|
liquid = PlotItem()
|
||||||
|
liquid.set_line(style=2, color=c)
|
||||||
|
self.dsc_plot.addItem(liquid)
|
||||||
|
|
||||||
|
tangent = PlotItem()
|
||||||
|
tangent.set_line(style=2, color=c)
|
||||||
|
self.dsc_plot.addItem(tangent)
|
||||||
|
|
||||||
|
tg_plot = PlotItem(pen=None, symbolBrush=c.rgb(), symbol='o')
|
||||||
|
self.dsc_plot.addItem(tg_plot)
|
||||||
|
|
||||||
|
fictive_cp = PlotItem(pen=mkPen(c.rgb()))
|
||||||
|
self.tnmh_graphics.addItem(fictive_cp)
|
||||||
|
|
||||||
|
tnmh_fit = PlotItem()
|
||||||
|
tnmh_fit.set_line(style=2, color=c)
|
||||||
|
self.tnmh_graphics.addItem(tnmh_fit)
|
||||||
|
|
||||||
|
self._plots[key] = (data_plot, tg_plot, glass, liquid, tangent, fictive_cp, tnmh_fit)
|
||||||
|
self._tg_value[key] = {
|
||||||
|
'onset': (nan, nan),
|
||||||
|
'midpoint': (nan, nan),
|
||||||
|
'end': (nan, nan),
|
||||||
|
'inflection': (nan, nan),
|
||||||
|
# 'fictive': (nan, nan),
|
||||||
|
}
|
||||||
|
|
||||||
|
if self._limitless and max_x != -10000000 and min_x != 10000000 :
|
||||||
|
dist = max_x - min_x
|
||||||
|
self.limits[0].setRegion((min_x, min_x+min(0.1*dist, 5)))
|
||||||
|
self.limits[1].setRegion((max_x-min(5, 0.1*dist), max_x))
|
||||||
|
self._limitless = False
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(name='on_calctg_button_clicked')
|
||||||
|
def calc_tg(self):
|
||||||
|
baselines = tuple(lim.getRegion() for lim in self.limits)
|
||||||
|
if baselines[0][0] > baselines[1][0]:
|
||||||
|
baselines = baselines[1], baselines[0]
|
||||||
|
|
||||||
|
for idx in range(self.listWidget.count()):
|
||||||
|
item = self.listWidget.item(idx)
|
||||||
|
if item.checkState() == QtCore.Qt.Unchecked:
|
||||||
|
continue
|
||||||
|
|
||||||
|
key = item.data(QtCore.Qt.UserRole)
|
||||||
|
plot = self._plots[key]
|
||||||
|
data, _ = self._dsc[key]
|
||||||
|
|
||||||
|
tg_results, glass, liquid, tangent = data.glass_transition(*baselines)
|
||||||
|
self._lines[key] = (glass, liquid, tangent)
|
||||||
|
|
||||||
|
for i, line in enumerate((glass, liquid, tangent)):
|
||||||
|
plot[i+2].setData(x=line.x, y=line.y)
|
||||||
|
|
||||||
|
self._tg_value[key].update(tg_results)
|
||||||
|
|
||||||
|
self._update_tg_plots()
|
||||||
|
|
||||||
|
def _update_tg_plots(self):
|
||||||
|
self.tg_tree.clear()
|
||||||
|
for idx in range(self.listWidget.count()):
|
||||||
|
item = self.listWidget.item(idx)
|
||||||
|
|
||||||
|
tree_item = QtWidgets.QTreeWidgetItem([item.text()])
|
||||||
|
values = self._tg_value.get(item.data(QtCore.Qt.UserRole))
|
||||||
|
|
||||||
|
if values is not None:
|
||||||
|
for name, pos in values.items():
|
||||||
|
child_item = QtWidgets.QTreeWidgetItem([f'{name.capitalize()}: {pos[0]:.2f} K'])
|
||||||
|
tree_item.addChild(child_item)
|
||||||
|
|
||||||
|
self.tg_tree.addTopLevelItem(tree_item)
|
||||||
|
|
||||||
|
key = item.data(QtCore.Qt.UserRole)
|
||||||
|
plot = self._plots[key]
|
||||||
|
data, _ = self._dsc[key]
|
||||||
|
|
||||||
|
plot[1].setData(array(list(self._tg_value[key].values())))
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(name='on_tg_export_button_clicked')
|
||||||
|
def export_tg(self):
|
||||||
|
ret_dic = {}
|
||||||
|
for key, tg in self._tg_value.items():
|
||||||
|
tgx = [x for x, y in tg.values()]
|
||||||
|
tgy = [y for x, y in tg.values()]
|
||||||
|
if self.tg_export_check.isChecked():
|
||||||
|
tg_pts = Points(x=tgx, y=tgy, name=self._management[key].name + ' (Tg)', value=self._management[key].value)
|
||||||
|
else:
|
||||||
|
tg_pts = None
|
||||||
|
|
||||||
|
line = []
|
||||||
|
if self.tglines_export_check.isChecked():
|
||||||
|
line = self._lines.get(key, [])
|
||||||
|
|
||||||
|
ret_dic[key] = (tg_pts, line)
|
||||||
|
|
||||||
|
self.newData.emit(ret_dic, 'tg')
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
|
||||||
|
def change_visibility(self, item: QtWidgets.QListWidgetItem):
|
||||||
|
is_checked = bool(item.checkState())
|
||||||
|
plot = self._plots[item.data(QtCore.Qt.UserRole)]
|
||||||
|
for val in plot:
|
||||||
|
val.setVisible(is_checked)
|
||||||
|
|
||||||
|
def get_fictive(self, key, baselines):
|
||||||
|
plot = self._plots[key]
|
||||||
|
data, _ = self._dsc[key]
|
||||||
|
|
||||||
|
cp, tg = data.get_fictive_cp(*baselines)
|
||||||
|
|
||||||
|
plot[5].setData(cp.x, cp.y)
|
||||||
|
self._dsc[key] = (data, cp)
|
||||||
|
|
||||||
|
return cp
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(name='on_tnhm_fitbutton_clicked')
|
||||||
|
def make_tnmh(self):
|
||||||
|
baselines = tuple(lim.getRegion() for lim in self.limits)
|
||||||
|
if baselines[0][0] > baselines[1][0]:
|
||||||
|
baselines = baselines[1], baselines[0]
|
||||||
|
|
||||||
|
self.tnmh_tree.clear()
|
||||||
|
for idx in range(self.listWidget.count()):
|
||||||
|
item = self.listWidget.item(idx)
|
||||||
|
if item.checkState() == QtCore.Qt.Unchecked:
|
||||||
|
continue
|
||||||
|
|
||||||
|
key = item.data(QtCore.Qt.UserRole)
|
||||||
|
|
||||||
|
data = self.get_fictive(key, baselines)
|
||||||
|
|
||||||
|
res = data.calculate_tnmh([60, 0.5, 1, 2e5], *baselines, return_fictive=False)
|
||||||
|
self._fit[key] = res
|
||||||
|
|
||||||
|
plot = self._plots[key]
|
||||||
|
plot[-1].setData(res.x, res.y)
|
||||||
|
|
||||||
|
for idx in range(self.listWidget.count()):
|
||||||
|
item = self.listWidget.item(idx)
|
||||||
|
|
||||||
|
tree_item = QtWidgets.QTreeWidgetItem([item.text()])
|
||||||
|
values = self._fit.get(item.data(QtCore.Qt.UserRole))
|
||||||
|
|
||||||
|
if values is not None:
|
||||||
|
child_item = QtWidgets.QTreeWidgetItem([values.parameter_string()])
|
||||||
|
tree_item.addChild(child_item)
|
||||||
|
|
||||||
|
self.tnmh_tree.addTopLevelItem(tree_item)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(name='on_tnmh_export_button_clicked')
|
||||||
|
def export_tnmh(self):
|
||||||
|
ret_dic = {}
|
||||||
|
for idx in range(self.listWidget.count()):
|
||||||
|
item = self.listWidget.item(idx)
|
||||||
|
if item.checkState() == QtCore.Qt.Unchecked:
|
||||||
|
continue
|
||||||
|
|
||||||
|
key = item.data(QtCore.Qt.UserRole)
|
||||||
|
|
||||||
|
cp = None
|
||||||
|
if self.fictive_export_check.isChecked():
|
||||||
|
_, cp = self._dsc[key]
|
||||||
|
|
||||||
|
line = None
|
||||||
|
|
||||||
|
if self.tnmhfit_export_check.isChecked():
|
||||||
|
line = self._fit.get(key)
|
||||||
|
|
||||||
|
ret_dic[key] = (cp, line)
|
||||||
|
|
||||||
|
ret_dic['graph'] = '' if self.tnmh_graph_check.isChecked() else self.tnmh_graph_combo.currentData()
|
||||||
|
|
||||||
|
self.newData.emit(ret_dic, 'tnmh')
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(name='on_hodge_button_clicked')
|
||||||
|
def hodge(self):
|
||||||
|
for tg_type, (plot, data, fitplots, fit) in self._hodge.items():
|
||||||
|
|
||||||
|
m = []
|
||||||
|
for idx in range(self.listWidget.count()):
|
||||||
|
item = self.listWidget.item(idx)
|
||||||
|
if item.checkState() == QtCore.Qt.Unchecked:
|
||||||
|
continue
|
||||||
|
|
||||||
|
key = item.data(QtCore.Qt.UserRole)
|
||||||
|
data, _ = self._dsc[key]
|
||||||
|
try:
|
||||||
|
tg_value = self._tg_value[key][tg_type][0]
|
||||||
|
if isnan(tg_value):
|
||||||
|
continue
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
m.append([tg_value, data.value])
|
||||||
|
|
||||||
|
if len(m) > 1:
|
||||||
|
data, fit = tau_hodge(*array(m).T)
|
||||||
|
data.name = f'{data.name} ({tg_type.capitalize()})'
|
||||||
|
plot.setData(data.x, data.y)
|
||||||
|
fitplots[0].setData(fit.x, fit.y)
|
||||||
|
fitplots[1].setData(fit.x_data, fit.y_data)
|
||||||
|
|
||||||
|
self._hodge[tg_type] = (plot, data, fitplots, fit)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(name='on_export_hodge_button_clicked')
|
||||||
|
def export_hodge(self):
|
||||||
|
ret_dic = {}
|
||||||
|
|
||||||
|
for cb in (self.onset_check, self.mid_check, self.end_check, self.inflection_check, self.fictive_check):
|
||||||
|
if cb.isChecked():
|
||||||
|
item = cb.text().lower()
|
||||||
|
data = self._hodge.get(item)
|
||||||
|
if data[1] is not None:
|
||||||
|
ret_dic[item] = data[1]
|
||||||
|
|
||||||
|
ret_dic['graph'] = '' if self.hodge_graph_check.isChecked() else self.hodge_graph_combo.currentData()
|
||||||
|
|
||||||
|
self.newData.emit(ret_dic, 'hodge')
|
||||||
|
|
||||||
|
def close(self) -> bool:
|
||||||
|
self.clear()
|
||||||
|
return super().close()
|
@ -5,51 +5,48 @@ from nmreval.utils.text import convert
|
|||||||
from ..Qt import QtCore, QtWidgets, QtGui
|
from ..Qt import QtCore, QtWidgets, QtGui
|
||||||
from .._py.fitmodelwidget import Ui_FitParameter
|
from .._py.fitmodelwidget import Ui_FitParameter
|
||||||
from .._py.save_fitmodel_dialog import Ui_SaveDialog
|
from .._py.save_fitmodel_dialog import Ui_SaveDialog
|
||||||
from ..lib import get_icon
|
from ..lib.iconloading import get_icon
|
||||||
|
from ..lib.tables import TableWidget
|
||||||
|
|
||||||
|
|
||||||
class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
||||||
value_requested = QtCore.pyqtSignal(object)
|
value_requested = QtCore.pyqtSignal(object)
|
||||||
value_changed = QtCore.pyqtSignal(str)
|
value_changed = QtCore.pyqtSignal(str)
|
||||||
state_changed = QtCore.pyqtSignal()
|
state_changed = QtCore.pyqtSignal()
|
||||||
|
replace_single_value = QtCore.pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self, label: str = 'Fitparameter', parent=None, fixed: bool = False):
|
def __init__(self, label: str = 'Fitparameter', parent=None, fixed: bool = False):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
self.parametername.setText(label + ' ')
|
self.name = label
|
||||||
|
|
||||||
|
self.parametername.setText(convert(label) + ' ')
|
||||||
|
|
||||||
validator = QtGui.QDoubleValidator()
|
|
||||||
validator.setDecimals(9)
|
|
||||||
self.parameter_line.setValidator(validator)
|
|
||||||
self.parameter_line.setText('1')
|
self.parameter_line.setText('1')
|
||||||
self.parameter_line.setMaximumWidth(60)
|
self.parameter_line.setMaximumWidth(160)
|
||||||
self.lineEdit.setMaximumWidth(60)
|
self.lineEdit.setMaximumWidth(100)
|
||||||
self.lineEdit_2.setMaximumWidth(60)
|
self.lineEdit_2.setMaximumWidth(100)
|
||||||
|
|
||||||
self.label_3.setText(f'< {label} <')
|
self.label_3.setText(f'< {convert(label)} <')
|
||||||
|
|
||||||
self.checkBox.stateChanged.connect(self.enableBounds)
|
self.checkBox.stateChanged.connect(self.enableBounds)
|
||||||
self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit())
|
self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit())
|
||||||
|
self.parameter_line.editingFinished.connect(self.update_parameter)
|
||||||
self.parameter_line.values_requested.connect(lambda: self.value_requested.emit(self))
|
self.parameter_line.values_requested.connect(lambda: self.value_requested.emit(self))
|
||||||
|
self.parameter_line.replace_single_values.connect(lambda: self.replace_single_value.emit(None))
|
||||||
self.parameter_line.editingFinished.connect(lambda: self.value_changed.emit(self.parameter_line.text()))
|
self.parameter_line.editingFinished.connect(lambda: self.value_changed.emit(self.parameter_line.text()))
|
||||||
self.fixed_check.toggled.connect(self.set_fixed)
|
self.fixed_check.toggled.connect(self.set_fixed)
|
||||||
|
|
||||||
if fixed:
|
if fixed:
|
||||||
self.fixed_check.hide()
|
self.fixed_check.hide()
|
||||||
|
|
||||||
self.menu = QtWidgets.QMenu(self)
|
|
||||||
self.add_links()
|
|
||||||
|
|
||||||
self.is_linked = None
|
|
||||||
self.parameter_pos = None
|
self.parameter_pos = None
|
||||||
self.func_idx = None
|
self.func_idx = None
|
||||||
|
|
||||||
self._linetext = '1'
|
self._linetext = '1'
|
||||||
|
|
||||||
@property
|
self.menu = QtWidgets.QMenu(self)
|
||||||
def name(self):
|
|
||||||
return convert(self.parametername.text().strip(), old='html', new='str')
|
|
||||||
|
|
||||||
def set_parameter_string(self, p: str):
|
def set_parameter_string(self, p: str):
|
||||||
self.parameter_line.setText(p)
|
self.parameter_line.setText(p)
|
||||||
@ -69,11 +66,6 @@ class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
|||||||
|
|
||||||
def set_parameter(self, p: float | None, bds: tuple[float, float, bool] = None,
|
def set_parameter(self, p: float | None, bds: tuple[float, float, bool] = None,
|
||||||
fixed: bool = None, glob: bool = None):
|
fixed: bool = None, glob: bool = None):
|
||||||
if p is None:
|
|
||||||
# bad hack: linked parameter return (None, linked parameter)
|
|
||||||
# if p is None -> parameter is linked to argument given by bds
|
|
||||||
self.link_parameter(linkto=bds)
|
|
||||||
else:
|
|
||||||
ptext = f'{p:.4g}'
|
ptext = f'{p:.4g}'
|
||||||
|
|
||||||
self.set_parameter_string(ptext)
|
self.set_parameter_string(ptext)
|
||||||
@ -88,104 +80,53 @@ class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
|||||||
self.global_checkbox.setCheckState(QtCore.Qt.Checked if glob else QtCore.Qt.Unchecked)
|
self.global_checkbox.setCheckState(QtCore.Qt.Checked if glob else QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
def get_parameter(self):
|
def get_parameter(self):
|
||||||
if self.is_linked:
|
|
||||||
try:
|
|
||||||
p = float(self._linetext)
|
|
||||||
except ValueError:
|
|
||||||
p = 1.0
|
|
||||||
else:
|
|
||||||
try:
|
try:
|
||||||
p = float(self.parameter_line.text().replace(',', '.'))
|
p = float(self.parameter_line.text().replace(',', '.'))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_ = QtWidgets.QMessageBox().warning(self, 'Invalid value',
|
p = self.parameter_line.text().replace(',', '.')
|
||||||
f'{self.parametername.text()} contains invalid values',
|
|
||||||
QtWidgets.QMessageBox.Cancel)
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self.checkBox.isChecked():
|
if self.checkBox.isChecked():
|
||||||
try:
|
lb_text = self.lineEdit.text()
|
||||||
lb = float(self.lineEdit.text().replace(',', '.'))
|
|
||||||
except ValueError:
|
|
||||||
lb = None
|
lb = None
|
||||||
|
if lb_text:
|
||||||
try:
|
try:
|
||||||
rb = float(self.lineEdit_2.text().replace(',', '.'))
|
lb = float(lb_text.replace(',', '.'))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
lb = lb_text
|
||||||
|
|
||||||
|
ub_text = self.lineEdit_2.text()
|
||||||
rb = None
|
rb = None
|
||||||
|
if ub_text:
|
||||||
|
try:
|
||||||
|
rb = float(ub_text.replace(',', '.'))
|
||||||
|
except ValueError:
|
||||||
|
rb = ub_text
|
||||||
else:
|
else:
|
||||||
lb = rb = None
|
lb = rb = None
|
||||||
|
|
||||||
bounds = (lb, rb)
|
bounds = (lb, rb)
|
||||||
|
|
||||||
return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked(), self.is_linked
|
return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked()
|
||||||
|
|
||||||
@QtCore.pyqtSlot(bool)
|
@QtCore.pyqtSlot(bool)
|
||||||
def set_fixed(self, state: bool):
|
def set_fixed(self, state: bool):
|
||||||
# self.global_checkbox.setVisible(not state)
|
# self.global_checkbox.setVisible(not state)
|
||||||
self.frame.setVisible(not state)
|
self.frame.setVisible(not state)
|
||||||
|
|
||||||
def add_links(self, parameter: dict = None):
|
|
||||||
if parameter is None:
|
|
||||||
parameter = {}
|
|
||||||
self.menu.clear()
|
|
||||||
|
|
||||||
ac = QtWidgets.QAction('Link to...', self)
|
|
||||||
ac.triggered.connect(self.link_parameter)
|
|
||||||
self.menu.addAction(ac)
|
|
||||||
|
|
||||||
for model_key, model_funcs in parameter.items():
|
|
||||||
m = QtWidgets.QMenu('Model ' + model_key, self)
|
|
||||||
for func_name, func_params in model_funcs.items():
|
|
||||||
m2 = QtWidgets.QMenu(func_name, m)
|
|
||||||
for p_name, idx in func_params:
|
|
||||||
ac = QtWidgets.QAction(p_name, m2)
|
|
||||||
ac.setData((model_key, *idx))
|
|
||||||
ac.triggered.connect(self.link_parameter)
|
|
||||||
m2.addAction(ac)
|
|
||||||
m.addMenu(m2)
|
|
||||||
self.menu.addMenu(m)
|
|
||||||
|
|
||||||
self.toolButton.setMenu(self.menu)
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot()
|
@QtCore.pyqtSlot()
|
||||||
def link_parameter(self, linkto=None):
|
def update_parameter(self):
|
||||||
if linkto is None:
|
new_value = self.parameter_line.text()
|
||||||
action = self.sender()
|
if not new_value:
|
||||||
else:
|
self.parameter_line.setText('1')
|
||||||
action = False
|
|
||||||
for m in self.menu.actions():
|
|
||||||
if m.menu():
|
|
||||||
for a in m.menu().actions():
|
|
||||||
if a.data() == linkto:
|
|
||||||
action = a
|
|
||||||
break
|
|
||||||
if action:
|
|
||||||
break
|
|
||||||
|
|
||||||
if (self.func_idx, self.parameter_pos) == action.data():
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
new_text = f'Linked to {action.parentWidget().title()}.{action.text()}'
|
float(new_value)
|
||||||
self._linetext = self.parameter_line.text()
|
is_text = False
|
||||||
self.parameter_line.setText(new_text)
|
except ValueError:
|
||||||
self.parameter_line.setEnabled(False)
|
is_text = True
|
||||||
self.global_checkbox.hide()
|
self.global_checkbox.setCheckState(False)
|
||||||
self.global_checkbox.blockSignals(True)
|
|
||||||
self.global_checkbox.setCheckState(QtCore.Qt.Checked)
|
|
||||||
self.global_checkbox.blockSignals(False)
|
|
||||||
self.frame.hide()
|
|
||||||
self.is_linked = action.data()
|
|
||||||
|
|
||||||
except AttributeError:
|
self.set_fixed(is_text)
|
||||||
self.parameter_line.setText(self._linetext)
|
|
||||||
self.parameter_line.setEnabled(True)
|
|
||||||
if self.fixed_check.isEnabled():
|
|
||||||
self.global_checkbox.show()
|
|
||||||
self.frame.show()
|
|
||||||
self.is_linked = None
|
|
||||||
|
|
||||||
self.state_changed.emit()
|
|
||||||
|
|
||||||
|
|
||||||
class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog):
|
class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog):
|
||||||
@ -280,8 +221,17 @@ class FitModelTree(QtWidgets.QTreeWidget):
|
|||||||
idx = item.data(0, self.counterRole)
|
idx = item.data(0, self.counterRole)
|
||||||
self.itemRemoved.emit(idx)
|
self.itemRemoved.emit(idx)
|
||||||
|
|
||||||
def add_function(self, idx: int, cnt: int, op: int, name: str, color: QtGui.QColor | str | tuple,
|
def add_function(self,
|
||||||
parent: QtWidgets.QTreeWidgetItem = None, children: list = None, active: bool = True, **kwargs):
|
idx: int,
|
||||||
|
cnt: int,
|
||||||
|
op: int,
|
||||||
|
name: str,
|
||||||
|
color: QtGui.QColor | str | tuple,
|
||||||
|
parent: QtWidgets.QTreeWidgetItem = None,
|
||||||
|
children: list = None,
|
||||||
|
active: bool = True,
|
||||||
|
param_names: list[str] = None,
|
||||||
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Add function to tree and dictionary of functions.
|
Add function to tree and dictionary of functions.
|
||||||
"""
|
"""
|
||||||
@ -296,6 +246,10 @@ class FitModelTree(QtWidgets.QTreeWidget):
|
|||||||
it.setData(0, self.counterRole, cnt)
|
it.setData(0, self.counterRole, cnt)
|
||||||
it.setData(0, self.operatorRole, op)
|
it.setData(0, self.operatorRole, op)
|
||||||
it.setText(0, name)
|
it.setText(0, name)
|
||||||
|
if param_names is not None:
|
||||||
|
it.setToolTip(0,
|
||||||
|
'Parameter names:\n' +
|
||||||
|
'\n'.join(f'{pn}({cnt})' for pn in param_names))
|
||||||
it.setForeground(0, QtGui.QBrush(color))
|
it.setForeground(0, QtGui.QBrush(color))
|
||||||
|
|
||||||
it.setIcon(0, get_icon(self.icons[op]))
|
it.setIcon(0, get_icon(self.icons[op]))
|
||||||
@ -365,7 +319,7 @@ class FitModelTree(QtWidgets.QTreeWidget):
|
|||||||
return funcs
|
return funcs
|
||||||
|
|
||||||
|
|
||||||
class FitTableWidget(QtWidgets.QTableWidget):
|
class FitTableWidget(TableWidget):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from nmreval.fit.parameter import Parameter
|
||||||
from nmreval.utils.text import convert
|
from nmreval.utils.text import convert
|
||||||
|
|
||||||
from ..Qt import QtWidgets, QtCore, QtGui
|
from ..Qt import QtWidgets, QtCore, QtGui
|
||||||
@ -62,8 +65,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
|||||||
self.glob_values = [1] * len(func.params)
|
self.glob_values = [1] * len(func.params)
|
||||||
|
|
||||||
for k, v in enumerate(func.params):
|
for k, v in enumerate(func.params):
|
||||||
name = convert(v)
|
widgt = FitModelWidget(label=v, parent=self.scrollwidget)
|
||||||
widgt = FitModelWidget(label=name, parent=self.scrollwidget)
|
|
||||||
widgt.parameter_pos = k
|
widgt.parameter_pos = k
|
||||||
widgt.func_idx = idx
|
widgt.func_idx = idx
|
||||||
try:
|
try:
|
||||||
@ -78,11 +80,12 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
|||||||
widgt.state_changed.connect(self.make_global)
|
widgt.state_changed.connect(self.make_global)
|
||||||
widgt.value_requested.connect(self.look_for_value)
|
widgt.value_requested.connect(self.look_for_value)
|
||||||
widgt.value_changed.connect(self.change_global_parameter)
|
widgt.value_changed.connect(self.change_global_parameter)
|
||||||
|
widgt.replace_single_value.connect(self.delete_single_parameter)
|
||||||
|
|
||||||
self.global_parameter.append(widgt)
|
self.global_parameter.append(widgt)
|
||||||
self.scrollwidget.layout().addWidget(widgt)
|
self.scrollwidget.layout().addWidget(widgt)
|
||||||
|
|
||||||
widgt2 = ParameterSingleWidget(name=name, parent=self.scrollwidget2)
|
widgt2 = ParameterSingleWidget(name=v, parent=self.scrollwidget2)
|
||||||
widgt2.valueChanged.connect(self.change_single_parameter)
|
widgt2.valueChanged.connect(self.change_single_parameter)
|
||||||
widgt2.removeSingleValue.connect(self.change_single_parameter)
|
widgt2.removeSingleValue.connect(self.change_single_parameter)
|
||||||
widgt2.installEventFilter(self)
|
widgt2.installEventFilter(self)
|
||||||
@ -114,20 +117,22 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
|||||||
self.scrollwidget.layout().addStretch(1)
|
self.scrollwidget.layout().addStretch(1)
|
||||||
self.scrollwidget2.layout().addStretch(1)
|
self.scrollwidget2.layout().addStretch(1)
|
||||||
|
|
||||||
def set_links(self, parameter):
|
# def set_links(self, parameter):
|
||||||
for w in self.global_parameter:
|
# for w in self.global_parameter:
|
||||||
if isinstance(w, FitModelWidget):
|
# if isinstance(w, FitModelWidget):
|
||||||
w.add_links(parameter)
|
# w.add_links(parameter)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot(str)
|
||||||
def change_global_parameter(self, value: str, idx: int = None):
|
def change_global_parameter(self, value: str, idx: int = None):
|
||||||
if idx is None:
|
if idx is None:
|
||||||
idx = self.global_parameter.index(self.sender())
|
idx = self.global_parameter.index(self.sender())
|
||||||
|
|
||||||
self.glob_values[idx] = float(value)
|
# self.glob_values[idx] = float(value)
|
||||||
|
self.glob_values[idx] = value
|
||||||
if self.data_values[self.comboBox.currentData()][idx] is None:
|
if self.data_values[self.comboBox.currentData()][idx] is None:
|
||||||
self.data_parameter[idx].blockSignals(True)
|
self.data_parameter[idx].blockSignals(True)
|
||||||
self.data_parameter[idx].value = float(value)
|
# self.data_parameter[idx].value = float(value)
|
||||||
|
self.data_parameter[idx].value = value
|
||||||
self.data_parameter[idx].blockSignals(False)
|
self.data_parameter[idx].blockSignals(False)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str, object)
|
@QtCore.pyqtSlot(str, object)
|
||||||
@ -148,6 +153,13 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
|||||||
if value is None:
|
if value is None:
|
||||||
self.change_data(self.comboBox.currentIndex())
|
self.change_data(self.comboBox.currentIndex())
|
||||||
|
|
||||||
|
def delete_single_parameter(self):
|
||||||
|
idx = self.global_parameter.index(self.sender())
|
||||||
|
for i in range(self.comboBox.count()):
|
||||||
|
set_id = self.comboBox.itemData(i)
|
||||||
|
self.data_values[set_id][idx] = None
|
||||||
|
self.change_data(self.comboBox.currentIndex())
|
||||||
|
|
||||||
def change_single_choice(self, _, value, sender=None):
|
def change_single_choice(self, _, value, sender=None):
|
||||||
if sender is None:
|
if sender is None:
|
||||||
sender = self.sender()
|
sender = self.sender()
|
||||||
@ -163,7 +175,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
|||||||
# disable single parameter if it is set global, enable if global is unset
|
# disable single parameter if it is set global, enable if global is unset
|
||||||
widget = self.sender()
|
widget = self.sender()
|
||||||
idx = self.global_parameter.index(widget)
|
idx = self.global_parameter.index(widget)
|
||||||
enable = (widget.global_checkbox.checkState() == QtCore.Qt.Unchecked) and (widget.is_linked is None)
|
enable = (widget.global_checkbox.checkState() == QtCore.Qt.Unchecked)
|
||||||
self.data_parameter[idx].setEnabled(enable)
|
self.data_parameter[idx].setEnabled(enable)
|
||||||
|
|
||||||
def select_next_preview(self, direction):
|
def select_next_preview(self, direction):
|
||||||
@ -181,6 +193,11 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
|||||||
for i, value in enumerate(self.data_values[sid]):
|
for i, value in enumerate(self.data_values[sid]):
|
||||||
w = self.data_parameter[i]
|
w = self.data_parameter[i]
|
||||||
w.blockSignals(True)
|
w.blockSignals(True)
|
||||||
|
try:
|
||||||
|
w.show_as_local_parameter(value is not None)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
if value is None:
|
if value is None:
|
||||||
w.value = self.glob_values[i]
|
w.value = self.glob_values[i]
|
||||||
else:
|
else:
|
||||||
@ -191,64 +208,50 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
|||||||
if sid not in self.data_values:
|
if sid not in self.data_values:
|
||||||
self.data_values[sid] = [None] * len(self.data_parameter)
|
self.data_values[sid] = [None] * len(self.data_parameter)
|
||||||
|
|
||||||
def get_parameter(self, use_func=None):
|
def get_parameter(self, use_func=None) -> tuple[dict, list]:
|
||||||
bds = []
|
bds = []
|
||||||
is_global = []
|
is_global = []
|
||||||
is_fixed = []
|
is_fixed = []
|
||||||
globs = []
|
param_general = []
|
||||||
is_linked = []
|
|
||||||
|
|
||||||
for g in self.global_parameter:
|
for g in self.global_parameter:
|
||||||
if isinstance(g, FitModelWidget):
|
if isinstance(g, FitModelWidget):
|
||||||
p_i, bds_i, fixed_i, global_i, link_i = g.get_parameter()
|
p_i, bds_i, fixed_i, global_i = g.get_parameter()
|
||||||
|
parameter_i = Parameter(name=g.name, value=p_i, lb=bds_i[0], ub=bds_i[1], var=fixed_i)
|
||||||
|
param_general.append(parameter_i)
|
||||||
|
|
||||||
globs.append(p_i)
|
|
||||||
bds.append(bds_i)
|
bds.append(bds_i)
|
||||||
is_fixed.append(fixed_i)
|
is_fixed.append(fixed_i)
|
||||||
is_global.append(global_i)
|
is_global.append(global_i)
|
||||||
is_linked.append(link_i)
|
|
||||||
|
|
||||||
lb, ub = list(zip(*bds))
|
|
||||||
|
|
||||||
data_parameter = {}
|
data_parameter = {}
|
||||||
if use_func is None:
|
if use_func is None:
|
||||||
use_func = list(self.data_values.keys())
|
use_func = list(self.data_values.keys())
|
||||||
|
|
||||||
global_p = None
|
|
||||||
for sid, parameter in self.data_values.items():
|
for sid, parameter in self.data_values.items():
|
||||||
if sid not in use_func:
|
if sid not in use_func:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
kw_p = {}
|
kw_p = {}
|
||||||
p = []
|
p = []
|
||||||
if global_p is None:
|
|
||||||
global_p = {'p': [], 'idx': [], 'var': [], 'ub': [], 'lb': []}
|
|
||||||
|
|
||||||
for i, (p_i, g) in enumerate(zip(parameter, self.global_parameter)):
|
for i, (p_i, g) in enumerate(zip(parameter, self.global_parameter)):
|
||||||
if isinstance(g, FitModelWidget):
|
if isinstance(g, FitModelWidget):
|
||||||
if (p_i is None) or is_global[i]:
|
if (p_i is None) or is_global[i]:
|
||||||
p.append(globs[i])
|
# set has no oen value
|
||||||
if is_global[i]:
|
p.append(param_general[i].copy())
|
||||||
if i not in global_p['idx']:
|
|
||||||
global_p['p'].append(globs[i])
|
|
||||||
global_p['idx'].append(i)
|
|
||||||
global_p['var'].append(is_fixed[i])
|
|
||||||
global_p['ub'].append(ub[i])
|
|
||||||
global_p['lb'].append(lb[i])
|
|
||||||
else:
|
else:
|
||||||
p.append(p_i)
|
lb, ub = bds[i]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if p[i] > ub[i]:
|
if not ((lb < p_i < ub) or (not is_fixed[i])):
|
||||||
raise ValueError(f'Parameter {g.name} is outside bounds ({lb[i]}, {ub[i]})')
|
raise ValueError(f'Parameter {g.name} is outside bounds ({lb}, {ub})')
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
# create Parameter
|
||||||
if p[i] < lb[i]:
|
p.append(
|
||||||
raise ValueError(f'Parameter {g.name} is outside bounds ({lb[i]}, {ub[i]})')
|
Parameter(name=g.name, value=p_i, lb=lb, ub=ub, var=is_fixed[i])
|
||||||
except TypeError:
|
)
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if p_i is None:
|
if p_i is None:
|
||||||
@ -260,20 +263,28 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
|||||||
|
|
||||||
data_parameter[sid] = (p, kw_p)
|
data_parameter[sid] = (p, kw_p)
|
||||||
|
|
||||||
return data_parameter, lb, ub, is_fixed, global_p, is_linked
|
global_parameter = []
|
||||||
|
for param, global_flag in zip(param_general, is_global):
|
||||||
|
if global_flag:
|
||||||
|
param.is_global = True
|
||||||
|
global_parameter.append(param)
|
||||||
|
else:
|
||||||
|
global_parameter.append(None)
|
||||||
|
|
||||||
|
return data_parameter, global_parameter
|
||||||
|
|
||||||
def set_parameter(self, set_id: str | None, parameter: list[float]) -> int:
|
def set_parameter(self, set_id: str | None, parameter: list[float]) -> int:
|
||||||
param_len = len(list(filter(lambda g: not isinstance(g, SelectionWidget), self.global_parameter)))
|
num_parameter = list(filter(lambda g: not isinstance(g, SelectionWidget), self.global_parameter))
|
||||||
|
param_len = len(num_parameter)
|
||||||
if set_id is None:
|
if set_id is None:
|
||||||
for val, g in zip(parameter, self.global_parameter):
|
for i, g in enumerate(num_parameter):
|
||||||
if isinstance(g, SelectionWidget):
|
val = parameter[i]
|
||||||
continue
|
|
||||||
g.set_parameter(val)
|
g.set_parameter(val)
|
||||||
|
self.glob_values[i] = val
|
||||||
|
|
||||||
else:
|
else:
|
||||||
new_param = self.data_values[set_id]
|
new_param = self.data_values[set_id]
|
||||||
min_len = min(param_len, len(new_param), len(new_param))
|
min_len = min(param_len, len(new_param))
|
||||||
for i in range(min_len):
|
for i in range(min_len):
|
||||||
new_param[i] = parameter[i]
|
new_param[i] = parameter[i]
|
||||||
|
|
||||||
@ -291,10 +302,12 @@ class ParameterSingleWidget(QtWidgets.QWidget):
|
|||||||
|
|
||||||
self._init_ui()
|
self._init_ui()
|
||||||
|
|
||||||
self._name = name
|
self.name = name
|
||||||
self.label.setText(convert(name))
|
self.label.setText(convert(name))
|
||||||
|
self.label.setToolTip('If this is bold then this parameter is only for this data. '
|
||||||
|
'Otherwise, the general parameter is used and displayed')
|
||||||
|
|
||||||
self.value_line.setValidator(QtGui.QDoubleValidator())
|
# self.value_line.setValidator(QtGui.QDoubleValidator())
|
||||||
self.value_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0)
|
self.value_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0)
|
||||||
self.reset_button.clicked.connect(lambda x: self.removeSingleValue.emit())
|
self.reset_button.clicked.connect(lambda x: self.removeSingleValue.emit())
|
||||||
|
|
||||||
@ -309,10 +322,12 @@ class ParameterSingleWidget(QtWidgets.QWidget):
|
|||||||
layout.addSpacerItem(QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum))
|
layout.addSpacerItem(QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum))
|
||||||
|
|
||||||
self.value_line = QtWidgets.QLineEdit(self)
|
self.value_line = QtWidgets.QLineEdit(self)
|
||||||
|
self.value_line.textEdited.connect(lambda x: self.show_as_local_parameter(True))
|
||||||
layout.addWidget(self.value_line)
|
layout.addWidget(self.value_line)
|
||||||
|
|
||||||
self.reset_button = QtWidgets.QToolButton(self)
|
self.reset_button = QtWidgets.QToolButton(self)
|
||||||
self.reset_button.setText('Use global')
|
self.reset_button.setText('Use global')
|
||||||
|
self.reset_button.clicked.connect(lambda: self.show_as_local_parameter(False))
|
||||||
layout.addWidget(self.reset_button)
|
layout.addWidget(self.reset_button)
|
||||||
|
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
@ -326,4 +341,11 @@ class ParameterSingleWidget(QtWidgets.QWidget):
|
|||||||
|
|
||||||
@value.setter
|
@value.setter
|
||||||
def value(self, val):
|
def value(self, val):
|
||||||
self.value_line.setText(f'{float(val):.5g}')
|
# self.value_line.setText(f'{float(val):.5g}')
|
||||||
|
self.value_line.setText(f'{val}')
|
||||||
|
|
||||||
|
def show_as_local_parameter(self, is_local: bool):
|
||||||
|
if is_local:
|
||||||
|
self.label.setStyleSheet('font-weight: bold;')
|
||||||
|
else:
|
||||||
|
self.label.setStyleSheet('')
|
||||||
|
@ -8,10 +8,11 @@ from nmreval.lib.importer import find_models
|
|||||||
from nmreval.lib.colors import BaseColor, Tab10
|
from nmreval.lib.colors import BaseColor, Tab10
|
||||||
from nmreval.utils.text import convert
|
from nmreval.utils.text import convert
|
||||||
|
|
||||||
from ..lib import get_icon
|
from ..lib.iconloading import get_icon
|
||||||
from .._py.fitfunctionwidget import Ui_Form
|
from .._py.fitfunctionwidget import Ui_Form
|
||||||
from ..Qt import QtWidgets, QtCore, QtGui
|
from ..Qt import QtWidgets, QtCore, QtGui
|
||||||
|
|
||||||
|
|
||||||
class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
|
class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
|
||||||
func_cnt = count()
|
func_cnt = count()
|
||||||
func_colors = cycle(Tab10)
|
func_colors = cycle(Tab10)
|
||||||
@ -127,7 +128,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
|
|
||||||
self.newFunction.emit(idx, cnt)
|
self.newFunction.emit(idx, cnt)
|
||||||
|
|
||||||
self.add_function(idx, cnt, op, name, col)
|
self.add_function(idx, cnt, op, name, col, param_names=self.functions[idx].params)
|
||||||
|
|
||||||
def add_function(self, idx: int, cnt: int, op: int,
|
def add_function(self, idx: int, cnt: int, op: int,
|
||||||
name: str, color: str | tuple[float, float, float] | BaseColor, **kwargs):
|
name: str, color: str | tuple[float, float, float] | BaseColor, **kwargs):
|
||||||
@ -140,6 +141,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
qcolor = QtGui.QColor.fromRgbF(*color)
|
qcolor = QtGui.QColor.fromRgbF(*color)
|
||||||
else:
|
else:
|
||||||
qcolor = QtGui.QColor(color)
|
qcolor = QtGui.QColor(color)
|
||||||
|
|
||||||
self.functree.add_function(idx, cnt, op, name, qcolor, **kwargs)
|
self.functree.add_function(idx, cnt, op, name, qcolor, **kwargs)
|
||||||
|
|
||||||
f = self.functions[idx]
|
f = self.functions[idx]
|
||||||
|
@ -4,16 +4,19 @@ from functools import reduce
|
|||||||
from itertools import count, cycle
|
from itertools import count, cycle
|
||||||
from operator import add
|
from operator import add
|
||||||
from string import ascii_letters
|
from string import ascii_letters
|
||||||
from typing import Dict, List, Tuple
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from pyqtgraph import mkPen
|
from pyqtgraph import mkPen
|
||||||
|
|
||||||
from nmreval.fit._meta import MultiModel, ModelFactory
|
from nmreval.fit._meta import MultiModel, ModelFactory
|
||||||
|
from nmreval.fit.data import Data
|
||||||
|
from nmreval.fit.model import Model
|
||||||
|
from nmreval.fit.parameter import Parameters
|
||||||
from nmreval.fit.result import FitResult
|
from nmreval.fit.result import FitResult
|
||||||
|
|
||||||
from .fit_forms import FitTableWidget
|
from .fit_forms import FitTableWidget
|
||||||
from .fit_parameter import QFitParameterWidget
|
from .fit_parameter import QFitParameterWidget
|
||||||
|
from ..lib import Relations
|
||||||
from ..lib.pg_objects import PlotItem
|
from ..lib.pg_objects import PlotItem
|
||||||
from ..Qt import QtGui, QtCore, QtWidgets
|
from ..Qt import QtGui, QtCore, QtWidgets
|
||||||
from .._py.fitdialog import Ui_FitDialog
|
from .._py.fitdialog import Ui_FitDialog
|
||||||
@ -50,7 +53,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
|||||||
self._func_list = {}
|
self._func_list = {}
|
||||||
self._complex = {}
|
self._complex = {}
|
||||||
|
|
||||||
self.connected_figure = ''
|
self.connected_figure = None
|
||||||
|
|
||||||
self.model_frame.hide()
|
self.model_frame.hide()
|
||||||
self.preview_button.hide()
|
self.preview_button.hide()
|
||||||
@ -78,16 +81,11 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
|||||||
w.deleteLater()
|
w.deleteLater()
|
||||||
del self.param_widgets[idx]
|
del self.param_widgets[idx]
|
||||||
|
|
||||||
if len(self.functionwidget) == 0:
|
self._current_function = None
|
||||||
|
if len(self.param_widgets) == 0:
|
||||||
# empty model
|
# empty model
|
||||||
self.newmodel_button.setEnabled(False)
|
self.newmodel_button.setEnabled(False)
|
||||||
self.deletemodel_button.setEnabled(False)
|
self.deletemodel_button.setEnabled(False)
|
||||||
self._current_function = None
|
|
||||||
|
|
||||||
else:
|
|
||||||
f_tree = self.functionwidget.functree
|
|
||||||
func_idx = f_tree.currentItem().data(0, f_tree.counterRole)
|
|
||||||
self._current_function = self.functionwidget.functions[func_idx]
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(int)
|
@QtCore.pyqtSlot(int)
|
||||||
def show_function_parameter(self, function_id: int, function_idx: int = None):
|
def show_function_parameter(self, function_id: int, function_idx: int = None):
|
||||||
@ -121,7 +119,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
|||||||
|
|
||||||
# collect parameter names etc. to allow linkage
|
# collect parameter names etc. to allow linkage
|
||||||
self._func_list[self._current_model] = self.functionwidget.get_parameter_list()
|
self._func_list[self._current_model] = self.functionwidget.get_parameter_list()
|
||||||
dialog.set_links(self._func_list)
|
# dialog.set_links(self._func_list)
|
||||||
|
|
||||||
# show same tab (general parameter/Data parameter)
|
# show same tab (general parameter/Data parameter)
|
||||||
tab_idx = 0
|
tab_idx = 0
|
||||||
@ -144,11 +142,18 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
|||||||
self._complex[self._current_model] = self.functionwidget.get_complex_state()
|
self._complex[self._current_model] = self.functionwidget.get_complex_state()
|
||||||
self._func_list[self._current_model] = self.functionwidget.get_parameter_list()
|
self._func_list[self._current_model] = self.functionwidget.get_parameter_list()
|
||||||
|
|
||||||
def load(self, ids: List[str]):
|
def load(self, ids: list[str]):
|
||||||
"""
|
"""
|
||||||
Add name and id of dataset to list.
|
Add name and id of dataset to list.
|
||||||
"""
|
"""
|
||||||
self.data_table.load(ids)
|
self.data_table.load(ids)
|
||||||
|
|
||||||
|
# deselect all fit sets
|
||||||
|
for i in range(self.data_table.rowCount()):
|
||||||
|
data_id = self.data_table.item(i, 0).data(QtCore.Qt.UserRole+1)
|
||||||
|
if self._management[data_id].mode == 'fit' or self._management[data_id].has_relation(Relations.isFitPartOf):
|
||||||
|
self.data_table.item(i, 0).setCheckState(QtCore.Qt.Unchecked)
|
||||||
|
|
||||||
if self.models:
|
if self.models:
|
||||||
for m in self.models.keys():
|
for m in self.models.keys():
|
||||||
self.data_table.add_model(m)
|
self.data_table.add_model(m)
|
||||||
@ -216,56 +221,50 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
|||||||
self.model_frame.hide()
|
self.model_frame.hide()
|
||||||
|
|
||||||
def _prepare(self, model: list, function_use: list = None,
|
def _prepare(self, model: list, function_use: list = None,
|
||||||
parameter: dict = None, add_idx: bool = False, cnt: int = 0) -> Tuple[dict, int]:
|
parameter: dict = None, add_idx: bool = False, cnt: int = 0) -> tuple[dict, int]:
|
||||||
|
|
||||||
if parameter is None:
|
if parameter is None:
|
||||||
parameter = {'parameter': {}, 'lb': (), 'ub': (), 'var': [],
|
parameter = {
|
||||||
'glob': {'idx': [], 'p': [], 'var': [], 'lb': [], 'ub': []},
|
'data_parameter': {},
|
||||||
'links': [], 'color': []}
|
'global_parameter': [],
|
||||||
|
'links': [],
|
||||||
|
'color': [],
|
||||||
|
}
|
||||||
|
|
||||||
for i, f in enumerate(model):
|
for i, f in enumerate(model):
|
||||||
if not f['active']:
|
if not f['active']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
p, lb, ub, var, glob, links = self.param_widgets[f['cnt']].get_parameter(function_use)
|
p, glob = self.param_widgets[f['cnt']].get_parameter(function_use)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
_ = QtWidgets.QMessageBox().warning(self, 'Invalid value', str(e),
|
_ = QtWidgets.QMessageBox().warning(self, 'Invalid value', str(e),
|
||||||
QtWidgets.QMessageBox.Ok)
|
QtWidgets.QMessageBox.Ok)
|
||||||
return {}, -1
|
return {}, -1
|
||||||
|
|
||||||
p_len = len(parameter['lb'])
|
parameter['color'].append(f['color'])
|
||||||
|
parameter['global_parameter'].extend(glob)
|
||||||
parameter['lb'] += lb
|
|
||||||
parameter['ub'] += ub
|
|
||||||
parameter['var'] += var
|
|
||||||
parameter['links'] += links
|
|
||||||
parameter['color'] += [f['color']]
|
|
||||||
|
|
||||||
|
cnt = f['cnt']
|
||||||
for p_k, v_k in p.items():
|
for p_k, v_k in p.items():
|
||||||
if add_idx:
|
if add_idx:
|
||||||
kw_k = {f'{k}_{cnt}': v for k, v in v_k[1].items()}
|
kw_k = {f'{k}_{cnt}': v for k, v in v_k[1].items()}
|
||||||
else:
|
else:
|
||||||
kw_k = v_k[1]
|
kw_k = v_k[1]
|
||||||
|
|
||||||
if p_k in parameter['parameter']:
|
if p_k in parameter['data_parameter']:
|
||||||
params, kw = parameter['parameter'][p_k]
|
params, kw = parameter['data_parameter'][p_k]
|
||||||
params += v_k[0]
|
params += v_k[0]
|
||||||
kw.update(kw_k)
|
kw.update(kw_k)
|
||||||
else:
|
else:
|
||||||
parameter['parameter'][p_k] = (v_k[0], kw_k)
|
parameter['data_parameter'][p_k] = (v_k[0], kw_k)
|
||||||
|
|
||||||
for g_k, g_v in glob.items():
|
|
||||||
if g_k != 'idx':
|
|
||||||
parameter['glob'][g_k] += g_v
|
|
||||||
else:
|
|
||||||
parameter['glob']['idx'] += [idx_i + p_len for idx_i in g_v]
|
|
||||||
|
|
||||||
if add_idx:
|
if add_idx:
|
||||||
cnt += 1
|
cnt += 1
|
||||||
|
|
||||||
if f['children']:
|
if f['children']:
|
||||||
# recurse for children
|
# recurse for children
|
||||||
child_parameter, cnt = self._prepare(f['children'], parameter=parameter, add_idx=add_idx, cnt=cnt)
|
_, cnt = self._prepare(f['children'], parameter=parameter, add_idx=add_idx, cnt=cnt)
|
||||||
|
|
||||||
return parameter, cnt
|
return parameter, cnt
|
||||||
|
|
||||||
@ -276,30 +275,43 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
|||||||
data = self.data_table.collect_data(default=self.default_combobox.currentData())
|
data = self.data_table.collect_data(default=self.default_combobox.currentData())
|
||||||
|
|
||||||
func_dict = {}
|
func_dict = {}
|
||||||
for k, mod in self.models.items():
|
for model_name, model_parameter in self.models.items():
|
||||||
func, order, param_len = ModelFactory.create_from_list(mod)
|
func, order, param_len = ModelFactory.create_from_list(model_parameter)
|
||||||
|
|
||||||
if func is None:
|
if func is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if k in data:
|
func = Model(func)
|
||||||
parameter, _ = self._prepare(mod, function_use=data[k], add_idx=isinstance(func, MultiModel))
|
|
||||||
|
if model_name in data:
|
||||||
|
parameter, _ = self._prepare(model_parameter, function_use=data[model_name], add_idx=isinstance(func, MultiModel))
|
||||||
|
|
||||||
if parameter is None:
|
if parameter is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
for (data_parameter, _) in parameter['data_parameter'].values():
|
||||||
|
for pname, param in zip(func.params, data_parameter):
|
||||||
|
param.name = pname
|
||||||
|
|
||||||
|
if self._complex[model_name] is not None:
|
||||||
|
for p_k, p_v in parameter['data_parameter'].items():
|
||||||
|
p_v[1].update({'complex_mode': self._complex[model_name]})
|
||||||
|
parameter['data_parameter'][p_k] = p_v[0], p_v[1]
|
||||||
|
|
||||||
|
for pname, param_value in zip(func.params, parameter['global_parameter']):
|
||||||
|
if param_value is not None:
|
||||||
|
param_value.name = pname
|
||||||
|
func.set_global_parameter(param_value)
|
||||||
|
|
||||||
parameter['func'] = func
|
parameter['func'] = func
|
||||||
parameter['order'] = order
|
parameter['order'] = order
|
||||||
parameter['len'] = param_len
|
parameter['len'] = param_len
|
||||||
parameter['complex'] = self._complex[k]
|
parameter['complex'] = self._complex[model_name]
|
||||||
if self._complex[k] is not None:
|
|
||||||
for p_k, p_v in parameter['parameter'].items():
|
|
||||||
p_v[1].update({'complex_mode': self._complex[k]})
|
|
||||||
parameter['parameter'][p_k] = p_v[0], p_v[1]
|
|
||||||
|
|
||||||
func_dict[k] = parameter
|
func_dict[model_name] = parameter
|
||||||
|
|
||||||
replaceable = []
|
replaceable = []
|
||||||
for k, v in func_dict.items():
|
for model_name, v in func_dict.items():
|
||||||
for i, link_i in enumerate(v['links']):
|
for i, link_i in enumerate(v['links']):
|
||||||
if link_i is None:
|
if link_i is None:
|
||||||
continue
|
continue
|
||||||
@ -330,7 +342,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
|||||||
QtWidgets.QMessageBox.Ok)
|
QtWidgets.QMessageBox.Ok)
|
||||||
return
|
return
|
||||||
|
|
||||||
replaceable.append((k, i, rep_model, repl_idx))
|
replaceable.append((model_name, i, rep_model, repl_idx))
|
||||||
|
|
||||||
replace_value = None
|
replace_value = None
|
||||||
for p_k in f['parameter'].values():
|
for p_k in f['parameter'].values():
|
||||||
@ -408,31 +420,37 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
|||||||
def make_previews(self, x, models_parameters: dict):
|
def make_previews(self, x, models_parameters: dict):
|
||||||
self.preview_lines = []
|
self.preview_lines = []
|
||||||
|
|
||||||
|
# needed to create namespace
|
||||||
|
param_dict = Parameters()
|
||||||
|
|
||||||
|
cnt = 0
|
||||||
|
for model in models_parameters.values():
|
||||||
|
f = model['func']
|
||||||
|
for parameter_list in model['data_parameter'].values():
|
||||||
|
for i, p_value in enumerate(parameter_list[0]):
|
||||||
|
p_value.name = f.params[i]
|
||||||
|
param_dict.add_parameter(f'a{cnt}', p_value)
|
||||||
|
cnt += 1
|
||||||
|
|
||||||
for k, model in models_parameters.items():
|
for k, model in models_parameters.items():
|
||||||
f = model['func']
|
f = model['func']
|
||||||
is_complex = self._complex[k]
|
is_complex = self._complex[k]
|
||||||
|
|
||||||
parameters = model['parameter']
|
parameters = model['data_parameter']
|
||||||
color = model['color']
|
color = model['color']
|
||||||
|
|
||||||
seen_parameter = []
|
|
||||||
|
|
||||||
for p, kwargs in parameters.values():
|
for p, kwargs in parameters.values():
|
||||||
if (p, kwargs) in seen_parameter:
|
p_value = [pp.value for pp in p]
|
||||||
# plot only previews with different parameter
|
|
||||||
continue
|
|
||||||
|
|
||||||
seen_parameter.append((p, kwargs))
|
|
||||||
|
|
||||||
if is_complex is not None:
|
if is_complex is not None:
|
||||||
y = f.func(x, *p, complex_mode=is_complex, **kwargs)
|
y = f.func(x, *p_value, complex_mode=is_complex, **kwargs)
|
||||||
if np.iscomplexobj(y):
|
if np.iscomplexobj(y):
|
||||||
self.preview_lines.append(PlotItem(x=x, y=y.real, pen=mkPen(width=3)))
|
self.preview_lines.append(PlotItem(x=x, y=y.real, pen=mkPen(width=3)))
|
||||||
self.preview_lines.append(PlotItem(x=x, y=y.imag, pen=mkPen(width=3)))
|
self.preview_lines.append(PlotItem(x=x, y=y.imag, pen=mkPen(width=3)))
|
||||||
else:
|
else:
|
||||||
self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3)))
|
self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3)))
|
||||||
else:
|
else:
|
||||||
y = f.func(x, *p, **kwargs)
|
y = f.func(x, *p_value, **kwargs)
|
||||||
self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3)))
|
self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3)))
|
||||||
|
|
||||||
if isinstance(f, MultiModel):
|
if isinstance(f, MultiModel):
|
||||||
@ -440,7 +458,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
|||||||
if is_complex is not None:
|
if is_complex is not None:
|
||||||
sub_kwargs.update({'complex_mode': is_complex})
|
sub_kwargs.update({'complex_mode': is_complex})
|
||||||
|
|
||||||
for i, s in enumerate(f.subs(x, *p, **sub_kwargs)):
|
for i, s in enumerate(f.subs(x, *p_value, **sub_kwargs)):
|
||||||
pen_i = mkPen(QtGui.QColor.fromRgbF(*color[i]))
|
pen_i = mkPen(QtGui.QColor.fromRgbF(*color[i]))
|
||||||
if np.iscomplexobj(s):
|
if np.iscomplexobj(s):
|
||||||
self.preview_lines.append(PlotItem(x=x, y=s.real, pen=pen_i))
|
self.preview_lines.append(PlotItem(x=x, y=s.real, pen=pen_i))
|
||||||
@ -448,15 +466,17 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
|||||||
else:
|
else:
|
||||||
self.preview_lines.append(PlotItem(x=x, y=s, pen=pen_i))
|
self.preview_lines.append(PlotItem(x=x, y=s, pen=pen_i))
|
||||||
|
|
||||||
|
param_dict.clear()
|
||||||
|
|
||||||
return self.preview_lines
|
return self.preview_lines
|
||||||
|
|
||||||
def set_parameter(self, parameter: Dict[str, FitResult]):
|
def set_parameter(self, parameter: dict[str, FitResult]):
|
||||||
# which data uses which model
|
# which data uses which model
|
||||||
data = self.data_table.collect_data(default=self.default_combobox.currentData())
|
data = self.data_table.collect_data(default=self.default_combobox.currentData())
|
||||||
|
|
||||||
|
for fitted_model, fitted_data in data.items():
|
||||||
glob_fit_parameter = []
|
glob_fit_parameter = []
|
||||||
|
|
||||||
for fitted_model, fitted_data in data.items():
|
|
||||||
for fit_id, fit_curve in parameter.items():
|
for fit_id, fit_curve in parameter.items():
|
||||||
if fit_id in fitted_data:
|
if fit_id in fitted_data:
|
||||||
fit_parameter = list(fit_curve.parameter.values())
|
fit_parameter = list(fit_curve.parameter.values())
|
||||||
@ -468,7 +488,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
|||||||
|
|
||||||
self.set_parameter_iter(None, mean_parameter, self.models[fitted_model])
|
self.set_parameter_iter(None, mean_parameter, self.models[fitted_model])
|
||||||
|
|
||||||
def set_parameter_iter(self, fit_id: str | None, param: List[float], functions: List, cnt: int = 0):
|
def set_parameter_iter(self, fit_id: str | None, param: list[float], functions: list, cnt: int = 0):
|
||||||
for model_p in functions:
|
for model_p in functions:
|
||||||
if model_p['active']:
|
if model_p['active']:
|
||||||
cnt += self.param_widgets[model_p['cnt']].set_parameter(fit_id, param[cnt:])
|
cnt += self.param_widgets[model_p['cnt']].set_parameter(fit_id, param[cnt:])
|
||||||
|
@ -14,7 +14,7 @@ from gui_qt.lib.namespace import QNamespaceWidget
|
|||||||
|
|
||||||
__all__ = ['QUserFitCreator']
|
__all__ = ['QUserFitCreator']
|
||||||
|
|
||||||
validator = QtGui.QRegExpValidator(QtCore.QRegExp('[A-Za-z]\S*'))
|
validator = QtGui.QRegExpValidator(QtCore.QRegExp('[_A-Za-z][_A-Za-z0-9]*'))
|
||||||
pattern = re.compile(r'def func\(.*\):', flags=re.MULTILINE)
|
pattern = re.compile(r'def func\(.*\):', flags=re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
@ -145,6 +145,7 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.classCreated.emit()
|
self.classCreated.emit()
|
||||||
super().accept()
|
super().accept()
|
||||||
|
|
||||||
|
|
||||||
class KwargsWidget(QtWidgets.QWidget):
|
class KwargsWidget(QtWidgets.QWidget):
|
||||||
Changed = QtCore.pyqtSignal()
|
Changed = QtCore.pyqtSignal()
|
||||||
|
|
||||||
@ -209,7 +210,7 @@ class KwargsWidget(QtWidgets.QWidget):
|
|||||||
def get_strings(self) -> str:
|
def get_strings(self) -> str:
|
||||||
kwargs = []
|
kwargs = []
|
||||||
if self.use_nuclei.isChecked():
|
if self.use_nuclei.isChecked():
|
||||||
kwargs.append("(r'\gamma', 'nucleus', gamma)")
|
kwargs.append(r"(r'\gamma', 'nucleus', gamma)")
|
||||||
|
|
||||||
for i in range(self.choices.count()):
|
for i in range(self.choices.count()):
|
||||||
kwargs.append(self.choices.widget(i).get_strings())
|
kwargs.append(self.choices.widget(i).get_strings())
|
||||||
@ -300,7 +301,7 @@ class ChoiceWidget(QtWidgets.QWidget):
|
|||||||
def get_strings(self) -> str:
|
def get_strings(self) -> str:
|
||||||
opts = []
|
opts = []
|
||||||
for i in range(self.table.rowCount()):
|
for i in range(self.table.rowCount()):
|
||||||
name = self.table.item(i, 0).text()
|
name = self.table.cellWidget(i, 0).text()
|
||||||
val = self._make_value(i)
|
val = self._make_value(i)
|
||||||
opts.append(f'{name!r}: {val!r}')
|
opts.append(f'{name!r}: {val!r}')
|
||||||
|
|
||||||
@ -471,7 +472,7 @@ class DescWidget(QtWidgets.QWidget):
|
|||||||
stringi = f'class {self.klass_lineedit.text()}:\n' \
|
stringi = f'class {self.klass_lineedit.text()}:\n' \
|
||||||
f' name = {self.name_lineedit.text()!r}\n' \
|
f' name = {self.name_lineedit.text()!r}\n' \
|
||||||
f' type = {self.group_lineedit.text()!r}\n' \
|
f' type = {self.group_lineedit.text()!r}\n' \
|
||||||
f' equation = {self.eq_lineedit.text()!r}\n'
|
f" equation = r'{self.eq_lineedit.text()}'\n"
|
||||||
|
|
||||||
return stringi
|
return stringi
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from math import isnan
|
from math import isnan
|
||||||
|
|
||||||
from pyqtgraph import mkBrush
|
from pyqtgraph import mkBrush, mkPen
|
||||||
|
|
||||||
from nmreval.utils.text import convert
|
from nmreval.utils.text import convert
|
||||||
|
from ..lib.graph_items import logTickValues
|
||||||
|
|
||||||
from ..lib.utils import RdBuCMap
|
from ..lib.utils import RdBuCMap
|
||||||
from ..Qt import QtWidgets, QtGui, QtCore
|
from ..Qt import QtWidgets, QtGui, QtCore
|
||||||
@ -11,7 +12,7 @@ from ..lib.pg_objects import PlotItem
|
|||||||
|
|
||||||
|
|
||||||
class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
||||||
closed = QtCore.pyqtSignal(dict, list, str, bool, dict)
|
closed = QtCore.pyqtSignal(dict, list, str, bool, bool, list)
|
||||||
redoFit = QtCore.pyqtSignal(dict)
|
redoFit = QtCore.pyqtSignal(dict)
|
||||||
|
|
||||||
def __init__(self, results: list, management, parent=None):
|
def __init__(self, results: list, management, parent=None):
|
||||||
@ -20,71 +21,89 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
self._management = management
|
self._management = management
|
||||||
|
|
||||||
self._prevs = {}
|
self.maxx_line.setValidator(QtGui.QDoubleValidator())
|
||||||
self._models = {}
|
self.minx_line.setValidator(QtGui.QDoubleValidator())
|
||||||
|
self.numx_line.setValidator(QtGui.QIntValidator())
|
||||||
|
self.extrapolate_box.stateChanged.connect(lambda x: self.maxx_line.setEnabled(x))
|
||||||
|
self.extrapolate_box.stateChanged.connect(lambda x: self.minx_line.setEnabled(x))
|
||||||
|
self.extrapolate_box.stateChanged.connect(lambda x: self.numx_line.setEnabled(x))
|
||||||
|
|
||||||
for (res, parts) in results:
|
self._previous_fits = {}
|
||||||
idx = res.idx
|
self._opts = []
|
||||||
data_k = management.data[idx]
|
self._results = {}
|
||||||
|
self.graph_opts = {}
|
||||||
|
self.last_idx = None
|
||||||
|
|
||||||
if res.name not in self._models:
|
self.fit_plot = self.graphicsView.addPlot(row=1, col=0, title='Fit')
|
||||||
self._models[res.name] = []
|
self.resid_plot = self.graphicsView.addPlot(row=0, col=0, title='Residual')
|
||||||
|
|
||||||
self._models[res.name].append(idx)
|
for orient in ['top', 'bottom', 'left', 'right']:
|
||||||
|
self.fit_plot.getAxis(orient).logTickValues = logTickValues
|
||||||
|
self.resid_plot.getAxis(orient).logTickValues = logTickValues
|
||||||
|
|
||||||
self._prevs[idx] = []
|
self.graphicsView.ci.layout.setRowStretchFactor(0, 1)
|
||||||
for fit in data_k.get_fits():
|
self.graphicsView.ci.layout.setRowStretchFactor(1, 2)
|
||||||
self._prevs[idx].append((fit.name, fit.statistics, fit.nobs-fit.nvar))
|
|
||||||
|
|
||||||
self._results = {res.idx: res for (res, _) in results}
|
|
||||||
self._parts = {res.idx: parts for (res, parts) in results}
|
|
||||||
self._opts = [(False, False) for _ in range(len(self._results))]
|
|
||||||
|
|
||||||
self.residplot = self.graphicsView.addPlot(row=0, col=0)
|
|
||||||
self.resid_graph = PlotItem(x=[], y=[],
|
self.resid_graph = PlotItem(x=[], y=[],
|
||||||
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
|
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
|
||||||
pen=None)
|
pen=None)
|
||||||
self.resid_graph_imag = PlotItem(x=[], y=[],
|
self.resid_graph_imag = PlotItem(x=[], y=[],
|
||||||
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
|
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
|
||||||
pen=None)
|
pen=None)
|
||||||
self.residplot.addItem(self.resid_graph)
|
self.resid_plot.addItem(self.resid_graph)
|
||||||
self.residplot.addItem(self.resid_graph_imag)
|
self.resid_plot.addItem(self.resid_graph_imag)
|
||||||
self.residplot.setLabel('left', 'Residual')
|
|
||||||
|
|
||||||
self.fitplot = self.graphicsView.addPlot(row=1, col=0)
|
|
||||||
self.data_graph = PlotItem(x=[], y=[],
|
self.data_graph = PlotItem(x=[], y=[],
|
||||||
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
|
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
|
||||||
pen=None)
|
pen=None)
|
||||||
self.data_graph_imag = PlotItem(x=[], y=[],
|
self.data_graph_imag = PlotItem(x=[], y=[],
|
||||||
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
|
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
|
||||||
pen=None)
|
pen=None)
|
||||||
self.fitplot.addItem(self.data_graph)
|
self.fit_plot.addItem(self.data_graph)
|
||||||
self.fitplot.addItem(self.data_graph_imag)
|
self.fit_plot.addItem(self.data_graph_imag)
|
||||||
self.fitplot.setLabel('left', 'Function')
|
|
||||||
|
|
||||||
self.fit_graph = PlotItem(x=[], y=[])
|
self.fit_graph = PlotItem(x=[], y=[])
|
||||||
self.fit_graph_imag = PlotItem(x=[], y=[])
|
self.fit_graph_imag = PlotItem(x=[], y=[])
|
||||||
self.fitplot.addItem(self.fit_graph)
|
self.fit_plot.addItem(self.fit_graph)
|
||||||
self.fitplot.addItem(self.fit_graph_imag)
|
self.fit_plot.addItem(self.fit_graph_imag)
|
||||||
|
|
||||||
self.cmap = RdBuCMap(vmin=-1, vmax=1)
|
self.cmap = RdBuCMap(vmin=-1, vmax=1)
|
||||||
|
|
||||||
self.sets_comboBox.blockSignals(True)
|
|
||||||
for n in self._models.keys():
|
|
||||||
self.sets_comboBox.addItem(n)
|
|
||||||
self.sets_comboBox.blockSignals(False)
|
|
||||||
|
|
||||||
self.set_parameter(0)
|
|
||||||
self.buttonBox.accepted.connect(self.accept)
|
|
||||||
|
|
||||||
self.param_tableWidget.itemClicked.connect(self.show_results)
|
|
||||||
self.param_tableWidget.horizontalHeader().sectionClicked.connect(lambda i: self.show_results(None, idx=i))
|
|
||||||
|
|
||||||
self.graph_checkBox.stateChanged.connect(lambda x: self.graph_comboBox.setEnabled(x == QtCore.Qt.Unchecked))
|
self.graph_checkBox.stateChanged.connect(lambda x: self.graph_comboBox.setEnabled(x == QtCore.Qt.Unchecked))
|
||||||
|
|
||||||
self.logy_box.stateChanged.connect(lambda x: self.fitplot.setLogMode(y=bool(x)))
|
self.logy_box.stateChanged.connect(lambda x: self.fit_plot.setLogMode(y=bool(x)))
|
||||||
self.logx_box.stateChanged.connect(lambda x: self.fitplot.setLogMode(x=bool(x)))
|
self.logx_box.stateChanged.connect(lambda x: self.fit_plot.setLogMode(x=bool(x)))
|
||||||
self.residplot.setXLink(self.fitplot)
|
self.resid_plot.setXLink(self.fit_plot)
|
||||||
|
|
||||||
|
self.set_results(results)
|
||||||
|
|
||||||
|
def __call__(self, results: list):
|
||||||
|
self._previous_fits = {}
|
||||||
|
self.sets_comboBox.blockSignals(True)
|
||||||
|
self.sets_comboBox.clear()
|
||||||
|
self.sets_comboBox.blockSignals(False)
|
||||||
|
self._results = {}
|
||||||
|
self._opts = {}
|
||||||
|
|
||||||
|
self.set_results(results)
|
||||||
|
|
||||||
|
def set_results(self, results: list):
|
||||||
|
self.sets_comboBox.blockSignals(True)
|
||||||
|
for res in results:
|
||||||
|
idx = res.idx
|
||||||
|
data_k = self._management.data[idx]
|
||||||
|
|
||||||
|
self._previous_fits[idx] = []
|
||||||
|
for fit in data_k.get_fits():
|
||||||
|
self._previous_fits[idx].append((fit.name, fit.statistics, fit.nobs - fit.nvar))
|
||||||
|
|
||||||
|
self.sets_comboBox.addItem(data_k.name, userData=idx)
|
||||||
|
self.sets_comboBox.blockSignals(False)
|
||||||
|
|
||||||
|
self._results = {res.idx: res for res in results}
|
||||||
|
self._opts = [(False, False) for _ in range(len(self._results))]
|
||||||
|
|
||||||
|
self.set_parameter(0)
|
||||||
|
|
||||||
def add_graphs(self, graphs: list):
|
def add_graphs(self, graphs: list):
|
||||||
self.graph_comboBox.clear()
|
self.graph_comboBox.clear()
|
||||||
@ -93,56 +112,40 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
@QtCore.pyqtSlot(int, name='on_sets_comboBox_currentIndexChanged')
|
@QtCore.pyqtSlot(int, name='on_sets_comboBox_currentIndexChanged')
|
||||||
def set_parameter(self, idx: int):
|
def set_parameter(self, idx: int):
|
||||||
model_name = self.sets_comboBox.itemText(idx)
|
set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.UserRole)
|
||||||
sets = self._models[model_name]
|
|
||||||
self.param_tableWidget.setColumnCount(len(sets))
|
|
||||||
|
|
||||||
r = self._results[sets[0]]
|
|
||||||
self.param_tableWidget.setRowCount(len(r.parameter))
|
|
||||||
|
|
||||||
for i, pval in enumerate(r.parameter.values()):
|
|
||||||
name = pval.full_name
|
|
||||||
p_header = QtWidgets.QTableWidgetItem(convert(name, 'tex', 'html', brackets=False))
|
|
||||||
self.param_tableWidget.setVerticalHeaderItem(i, p_header)
|
|
||||||
|
|
||||||
for i, set_id in enumerate(sets):
|
|
||||||
data_i = self._management[set_id]
|
|
||||||
header_item = QtWidgets.QTableWidgetItem(data_i.name)
|
|
||||||
header_item.setData(QtCore.Qt.UserRole, set_id)
|
|
||||||
self.param_tableWidget.setHorizontalHeaderItem(i, header_item)
|
|
||||||
|
|
||||||
res = self._results[set_id]
|
res = self._results[set_id]
|
||||||
for j, pvalue in enumerate(res.parameter.values()):
|
self.param_tableWidget.setRowCount(len(res.parameter))
|
||||||
|
for j, (pkey, pvalue) in enumerate(res.parameter.items()):
|
||||||
|
name = pkey
|
||||||
|
p_header = QtWidgets.QTableWidgetItem(convert(name, 'tex', 'str', brackets=True))
|
||||||
|
self.param_tableWidget.setVerticalHeaderItem(j, p_header)
|
||||||
|
|
||||||
item_text = f'{pvalue.value:.4g}'
|
item_text = f'{pvalue.value:.4g}'
|
||||||
if pvalue.error is not None:
|
if pvalue.error is not None:
|
||||||
item_text += f' \u00b1 {pvalue.error:.4g}'
|
item_text += f' \u00b1 {pvalue.error:.4g}'
|
||||||
self.param_tableWidget.setItem(2*j+1, i, QtWidgets.QTableWidgetItem('-'))
|
self.param_tableWidget.setItem(2*j+1, 0, QtWidgets.QTableWidgetItem('-'))
|
||||||
else:
|
else:
|
||||||
self.param_tableWidget.setItem(2*j+1, i, QtWidgets.QTableWidgetItem())
|
self.param_tableWidget.setItem(2*j+1, 0, QtWidgets.QTableWidgetItem())
|
||||||
item = QtWidgets.QTableWidgetItem(item_text)
|
item = QtWidgets.QTableWidgetItem(item_text)
|
||||||
self.param_tableWidget.setItem(j, i, item)
|
self.param_tableWidget.setItem(j, 0, item)
|
||||||
|
|
||||||
self.param_tableWidget.resizeColumnsToContents()
|
self.param_tableWidget.resizeColumnToContents(0)
|
||||||
self.param_tableWidget.selectColumn(0)
|
self.show_results(idx)
|
||||||
self.show_results(None, idx=0)
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(int, name='on_reject_fit_checkBox_stateChanged')
|
@QtCore.pyqtSlot(int, name='on_reject_fit_checkBox_stateChanged')
|
||||||
@QtCore.pyqtSlot(int, name='on_del_prev_checkBox_stateChanged')
|
@QtCore.pyqtSlot(int, name='on_del_prev_checkBox_stateChanged')
|
||||||
def change_opts(self, _):
|
def change_opts(self, _):
|
||||||
idx = self.param_tableWidget.currentIndex().column()
|
idx = self.sets_comboBox.currentIndex()
|
||||||
|
|
||||||
self._opts[idx] = (self.reject_fit_checkBox.checkState() == QtCore.Qt.Checked,
|
self._opts[idx] = (self.reject_fit_checkBox.checkState() == QtCore.Qt.Checked,
|
||||||
self.del_prev_checkBox.checkState() == QtCore.Qt.Checked)
|
self.del_prev_checkBox.checkState() == QtCore.Qt.Checked)
|
||||||
|
|
||||||
def show_results(self, item, idx=None):
|
def show_results(self, idx):
|
||||||
if item is not None:
|
set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.UserRole)
|
||||||
idx = self.param_tableWidget.indexFromItem(item).column()
|
|
||||||
|
|
||||||
set_id = self.param_tableWidget.horizontalHeaderItem(idx).data(QtCore.Qt.UserRole)
|
|
||||||
self.set_plot(set_id)
|
self.set_plot(set_id)
|
||||||
self.set_correlation(set_id)
|
self.set_correlation(set_id)
|
||||||
self.set_statistics(set_id)
|
self.set_statistics(set_id)
|
||||||
|
|
||||||
self.reject_fit_checkBox.blockSignals(True)
|
self.reject_fit_checkBox.blockSignals(True)
|
||||||
self.reject_fit_checkBox.setChecked(self._opts[idx][0])
|
self.reject_fit_checkBox.setChecked(self._opts[idx][0])
|
||||||
self.reject_fit_checkBox.blockSignals(False)
|
self.reject_fit_checkBox.blockSignals(False)
|
||||||
@ -151,9 +154,22 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.del_prev_checkBox.blockSignals(False)
|
self.del_prev_checkBox.blockSignals(False)
|
||||||
|
|
||||||
def set_plot(self, idx: str):
|
def set_plot(self, idx: str):
|
||||||
|
if self.last_idx is not None:
|
||||||
|
self.graph_opts[self.last_idx] = (
|
||||||
|
self.fit_plot.viewRange(),
|
||||||
|
self.logx_box.isChecked(),
|
||||||
|
self.logy_box.isChecked(),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.last_idx = idx
|
||||||
res = self._results[idx]
|
res = self._results[idx]
|
||||||
iscomplex = res.iscomplex
|
iscomplex = res.iscomplex
|
||||||
|
|
||||||
|
sub_funcs = res.sub(res.x)
|
||||||
|
for item in self.fit_plot.items[::-1]:
|
||||||
|
if item not in [self.data_graph, self.data_graph_imag, self.fit_graph, self.fit_graph_imag]:
|
||||||
|
self.fit_plot.removeItem(item)
|
||||||
|
|
||||||
if iscomplex:
|
if iscomplex:
|
||||||
self.data_graph.setData(x=res.x_data, y=res.y_data.real)
|
self.data_graph.setData(x=res.x_data, y=res.y_data.real)
|
||||||
self.data_graph_imag.setData(x=res.x_data, y=res.y_data.imag)
|
self.data_graph_imag.setData(x=res.x_data, y=res.y_data.imag)
|
||||||
@ -161,6 +177,13 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.fit_graph_imag.setData(x=res.x, y=res.y.imag)
|
self.fit_graph_imag.setData(x=res.x, y=res.y.imag)
|
||||||
self.resid_graph.setData(x=res.x_data, y=res.residual.real)
|
self.resid_graph.setData(x=res.x_data, y=res.residual.real)
|
||||||
self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag)
|
self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag)
|
||||||
|
|
||||||
|
for i, f in enumerate(sub_funcs):
|
||||||
|
item = PlotItem(x=f.x, y=f.y.real, pen=mkPen({'color': i, 'style': 2}))
|
||||||
|
self.fit_plot.addItem(item)
|
||||||
|
item = PlotItem(x=f.x, y=f.y.imag, pen=mkPen({'color': i, 'style': 2}))
|
||||||
|
self.fit_plot.addItem(item)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.resid_graph.setData(x=res.x_data, y=res.residual)
|
self.resid_graph.setData(x=res.x_data, y=res.residual)
|
||||||
self.resid_graph_imag.setData(x=[], y=[])
|
self.resid_graph_imag.setData(x=[], y=[])
|
||||||
@ -169,8 +192,27 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.fit_graph.setData(x=res.x, y=res.y)
|
self.fit_graph.setData(x=res.x, y=res.y)
|
||||||
self.fit_graph_imag.setData(x=[], y=[])
|
self.fit_graph_imag.setData(x=[], y=[])
|
||||||
|
|
||||||
self.fitplot.setLogMode(x=res.islog)
|
for i, f in enumerate(sub_funcs):
|
||||||
self.residplot.setLogMode(x=res.islog)
|
item = PlotItem(x=f.x, y=f.y, pen=mkPen({'color': i, 'style': 2}))
|
||||||
|
self.fit_plot.addItem(item)
|
||||||
|
|
||||||
|
self.logx_box.blockSignals(True)
|
||||||
|
self.logx_box.setChecked(res.islog)
|
||||||
|
self.logx_box.blockSignals(False)
|
||||||
|
|
||||||
|
self.fit_plot.setLogMode(x=res.islog)
|
||||||
|
self.resid_plot.setLogMode(x=res.islog)
|
||||||
|
|
||||||
|
if idx in self.graph_opts:
|
||||||
|
view_range, logx, logy = self.graph_opts[idx]
|
||||||
|
self.fit_plot.setRange(xRange=view_range[0], yRange=view_range[1], padding=0)
|
||||||
|
self.fit_plot.setLogMode(x=logx, y=logy)
|
||||||
|
self.logx_box.blockSignals(True)
|
||||||
|
self.logx_box.setChecked(logx)
|
||||||
|
self.logx_box.blockSignals(False)
|
||||||
|
self.logy_box.blockSignals(True)
|
||||||
|
self.logy_box.setChecked(logy)
|
||||||
|
self.logy_box.blockSignals(False)
|
||||||
|
|
||||||
def set_correlation(self, idx: str):
|
def set_correlation(self, idx: str):
|
||||||
while self.corr_tableWidget.rowCount():
|
while self.corr_tableWidget.rowCount():
|
||||||
@ -201,7 +243,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
res = self._results[idx]
|
res = self._results[idx]
|
||||||
|
|
||||||
self.stats_tableWidget.setColumnCount(1 + len(self._prevs[idx]))
|
self.stats_tableWidget.setColumnCount(1 + len(self._previous_fits[idx]))
|
||||||
self.stats_tableWidget.setRowCount(len(res.statistics)+3)
|
self.stats_tableWidget.setRowCount(len(res.statistics)+3)
|
||||||
|
|
||||||
it = QtWidgets.QTableWidgetItem(f'{res.dof}')
|
it = QtWidgets.QTableWidgetItem(f'{res.dof}')
|
||||||
@ -209,7 +251,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.stats_tableWidget.setVerticalHeaderItem(0, QtWidgets.QTableWidgetItem('DoF'))
|
self.stats_tableWidget.setVerticalHeaderItem(0, QtWidgets.QTableWidgetItem('DoF'))
|
||||||
self.stats_tableWidget.setItem(0, 0, it)
|
self.stats_tableWidget.setItem(0, 0, it)
|
||||||
|
|
||||||
for col, (name, _, dof) in enumerate(self._prevs[idx], start=1):
|
for col, (name, _, dof) in enumerate(self._previous_fits[idx], start=1):
|
||||||
self.stats_tableWidget.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem(name))
|
self.stats_tableWidget.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem(name))
|
||||||
it = QtWidgets.QTableWidgetItem(f'{dof}')
|
it = QtWidgets.QTableWidgetItem(f'{dof}')
|
||||||
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
|
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
|
||||||
@ -223,7 +265,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
best_idx = -1
|
best_idx = -1
|
||||||
best_val = v
|
best_val = v
|
||||||
for col, (_, stats, _) in enumerate(self._prevs[idx], start=1):
|
for col, (_, stats, _) in enumerate(self._previous_fits[idx], start=1):
|
||||||
if k in ['adj. R^2', 'R^2']:
|
if k in ['adj. R^2', 'R^2']:
|
||||||
best_idx = col if best_val < stats[k] else max(0, best_idx)
|
best_idx = col if best_val < stats[k] else max(0, best_idx)
|
||||||
else:
|
else:
|
||||||
@ -243,7 +285,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.stats_tableWidget.setVerticalHeaderItem(row+1, QtWidgets.QTableWidgetItem('Pr(>F)'))
|
self.stats_tableWidget.setVerticalHeaderItem(row+1, QtWidgets.QTableWidgetItem('Pr(>F)'))
|
||||||
self.stats_tableWidget.setItem(row+1, 0, QtWidgets.QTableWidgetItem('-'))
|
self.stats_tableWidget.setItem(row+1, 0, QtWidgets.QTableWidgetItem('-'))
|
||||||
|
|
||||||
for col, (_, stats, dof) in enumerate(self._prevs[idx], start=1):
|
for col, (_, stats, dof) in enumerate(self._previous_fits[idx], start=1):
|
||||||
f_value, prob_f = res.f_test(stats['chi^2'], dof)
|
f_value, prob_f = res.f_test(stats['chi^2'], dof)
|
||||||
it = QtWidgets.QTableWidgetItem(f'{f_value:.4g}')
|
it = QtWidgets.QTableWidgetItem(f'{f_value:.4g}')
|
||||||
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
|
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
|
||||||
@ -273,12 +315,78 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
plot_fits = self.curve_checkbox.isChecked()
|
plot_fits = self.curve_checkbox.isChecked()
|
||||||
|
|
||||||
if self.partial_checkBox.checkState() == QtCore.Qt.Checked:
|
parts = self.partial_checkBox.checkState() == QtCore.Qt.Checked
|
||||||
self.closed.emit(self._results, self._opts, graph, plot_fits, self._parts)
|
|
||||||
else:
|
|
||||||
self.closed.emit(self._results, self._opts, graph, plot_fits, {})
|
|
||||||
|
|
||||||
|
extrapolate = [None, None, None]
|
||||||
|
error = []
|
||||||
|
if self.extrapolate_box.isChecked():
|
||||||
|
try:
|
||||||
|
extrapolate[0] = float(self.minx_line.text())
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
error.append('Start value is missing')
|
||||||
|
try:
|
||||||
|
extrapolate[1] = float(self.maxx_line.text())
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
error.append('End value is missing')
|
||||||
|
try:
|
||||||
|
extrapolate[2] = int(self.numx_line.text())
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
error.append('Number of points is missing')
|
||||||
|
|
||||||
|
if error:
|
||||||
|
msg = QtWidgets.QMessageBox.warning(self, 'Error', 'Extrapolation failed because:\n' + '\n'.join(error))
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.closed.emit(self._results, self._opts, graph, plot_fits, parts, extrapolate)
|
||||||
self.accept()
|
self.accept()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.reject()
|
self.reject()
|
||||||
|
|
||||||
|
|
||||||
|
class FitExtension(QtWidgets.QDialog):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
gridLayout = QtWidgets.QGridLayout(self)
|
||||||
|
|
||||||
|
self.label = QtWidgets.QLabel('Minimum value')
|
||||||
|
gridLayout.addWidget(self.label, 0, 0, 1, 1)
|
||||||
|
|
||||||
|
self.min_line = QtWidgets.QLineEdit()
|
||||||
|
self.min_line.setValidator(QtGui.QDoubleValidator())
|
||||||
|
gridLayout.addWidget(self.min_line, 0, 1, 1, 1)
|
||||||
|
|
||||||
|
self.label_2 = QtWidgets.QLabel('Maximum value')
|
||||||
|
gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
|
||||||
|
|
||||||
|
self.max_line = QtWidgets.QLineEdit()
|
||||||
|
self.max_line.setValidator(QtGui.QDoubleValidator())
|
||||||
|
gridLayout.addWidget(self.max_line, 1, 1, 1, 1)
|
||||||
|
|
||||||
|
self.label_3 = QtWidgets.QLabel('Number of pts.')
|
||||||
|
gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
|
||||||
|
|
||||||
|
self.num_pts = QtWidgets.QLineEdit()
|
||||||
|
self.num_pts.setValidator(QtGui.QIntValidator())
|
||||||
|
gridLayout.addWidget(self.num_pts, 2, 1, 1, 1)
|
||||||
|
|
||||||
|
self.buttonBox = QtWidgets.QDialogButtonBox()
|
||||||
|
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok)
|
||||||
|
gridLayout.addWidget(self.buttonBox, 3, 0, 1, 2)
|
||||||
|
|
||||||
|
self.setLayout(gridLayout)
|
||||||
|
|
||||||
|
self.buttonBox.accepted.connect(self.accept)
|
||||||
|
self.buttonBox.rejected.connect(self.reject)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self):
|
||||||
|
try:
|
||||||
|
xmin = float(self.min_line.text())
|
||||||
|
xmax = float(self.max_line.text())
|
||||||
|
nums = int(self.num_pts.text())
|
||||||
|
except TypeError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return xmin, xmax, nums
|
||||||
|
@ -138,9 +138,7 @@ class DrawingsWidget(QtWidgets.QWidget, Ui_Form):
|
|||||||
graph_id = self.graph_comboBox.currentData()
|
graph_id = self.graph_comboBox.currentData()
|
||||||
current_lines = self.lines[graph_id]
|
current_lines = self.lines[graph_id]
|
||||||
|
|
||||||
print(remove_rows)
|
|
||||||
for i in reversed(remove_rows):
|
for i in reversed(remove_rows):
|
||||||
print(i)
|
|
||||||
self.tableWidget.removeRow(i)
|
self.tableWidget.removeRow(i)
|
||||||
self.line_deleted.emit(current_lines[i], graph_id)
|
self.line_deleted.emit(current_lines[i], graph_id)
|
||||||
|
|
||||||
|
@ -4,19 +4,21 @@ import itertools
|
|||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from math import isnan
|
from math import isfinite
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
from numpy import errstate, floor, log10
|
from numpy import errstate, floor, log10
|
||||||
from pyqtgraph import GraphicsObject, getConfigOption, mkColor
|
from pyqtgraph import GraphicsObject, getConfigOption, mkColor
|
||||||
|
|
||||||
|
from nmreval.lib.logger import logger
|
||||||
from nmreval.utils.text import convert
|
from nmreval.utils.text import convert
|
||||||
from ..io.filedialog import FileDialog
|
from ..io.filedialog import FileDialog
|
||||||
|
|
||||||
from ..lib.pg_objects import LegendItemBlock, RegionItem
|
from ..lib.pg_objects import LegendItemBlock, RegionItem
|
||||||
from ..Qt import QtCore, QtWidgets, QtGui
|
from ..Qt import QtCore, QtWidgets, QtGui
|
||||||
from .._py.graph import Ui_GraphWindow
|
from .._py.graph import Ui_GraphWindow
|
||||||
from ..lib import make_action_icons
|
from ..lib.iconloading import make_action_icons
|
||||||
from ..lib.configurations import GraceMsgBox
|
from ..lib.configurations import GraceMsgBox
|
||||||
|
|
||||||
|
|
||||||
@ -24,7 +26,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
mousePositionChanged = QtCore.pyqtSignal(float, float)
|
mousePositionChanged = QtCore.pyqtSignal(float, float)
|
||||||
mouseDoubleClicked = QtCore.pyqtSignal()
|
mouseDoubleClicked = QtCore.pyqtSignal()
|
||||||
positionClicked = QtCore.pyqtSignal(tuple, bool)
|
positionClicked = QtCore.pyqtSignal(tuple, bool)
|
||||||
aboutToClose = QtCore.pyqtSignal(str)
|
aboutToClose = QtCore.pyqtSignal(list)
|
||||||
|
|
||||||
counter = itertools.count()
|
counter = itertools.count()
|
||||||
|
|
||||||
@ -43,7 +45,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
self.id = str(uuid.uuid4())
|
self.id = str(uuid.uuid4())
|
||||||
|
|
||||||
self.sets = []
|
self.sets = []
|
||||||
self.active = []
|
self._active = []
|
||||||
|
|
||||||
self.real_plots = {}
|
self.real_plots = {}
|
||||||
self.imag_plots = {}
|
self.imag_plots = {}
|
||||||
@ -53,6 +55,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
self._external_items = []
|
self._external_items = []
|
||||||
self.closable = True
|
self.closable = True
|
||||||
|
|
||||||
|
self._block = False
|
||||||
|
|
||||||
self.log = [False, False]
|
self.log = [False, False]
|
||||||
|
|
||||||
self.scene = self.plotItem.scene()
|
self.scene = self.plotItem.scene()
|
||||||
@ -71,6 +75,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
self.scene.contextMenu[0].disconnect()
|
self.scene.contextMenu[0].disconnect()
|
||||||
self.scene.contextMenu[0].triggered.connect(self.export_dialog)
|
self.scene.contextMenu[0].triggered.connect(self.export_dialog)
|
||||||
|
|
||||||
|
self.bwbutton.toggled.connect(self.change_background)
|
||||||
|
|
||||||
def _init_gui(self):
|
def _init_gui(self):
|
||||||
self.setWindowTitle('Graph ' + str(next(QGraphWindow.counter)))
|
self.setWindowTitle('Graph ' + str(next(QGraphWindow.counter)))
|
||||||
|
|
||||||
@ -110,11 +116,11 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
return iter(self.active)
|
return iter(self.active)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.active)
|
return len(self._active)
|
||||||
|
|
||||||
def curves(self) -> tuple:
|
def curves(self) -> tuple:
|
||||||
for set_id in self.sets:
|
for set_id in self.sets:
|
||||||
if set_id in self.active:
|
if set_id in self._active:
|
||||||
if self.real_button.isChecked():
|
if self.real_button.isChecked():
|
||||||
if self.error_plots[set_id] is not None:
|
if self.error_plots[set_id] is not None:
|
||||||
yield self.real_plots[set_id], self.error_plots[set_id]
|
yield self.real_plots[set_id], self.error_plots[set_id]
|
||||||
@ -137,19 +143,43 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
r = self.plotItem.getViewBox().viewRange()
|
r = self.plotItem.getViewBox().viewRange()
|
||||||
for i in [0, 1]:
|
for i in [0, 1]:
|
||||||
if self.log[i]:
|
if self.log[i]:
|
||||||
r[i] = tuple([10**x for x in r[i]])
|
tmp = [np.nan, np.nan]
|
||||||
|
for j, x in enumerate(r[i]):
|
||||||
|
try:
|
||||||
|
tmp[j] = 10**min(x, 199)
|
||||||
|
except OverflowError:
|
||||||
|
pass
|
||||||
|
r[i] = tuple(tmp)
|
||||||
else:
|
else:
|
||||||
r[i] = tuple(r[i])
|
r[i] = tuple(r[i])
|
||||||
|
|
||||||
return tuple(r)
|
return tuple(r)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active(self) -> list:
|
||||||
|
return [set_id for set_id in self.sets if set_id in self._active]
|
||||||
|
|
||||||
|
@active.setter
|
||||||
|
def active(self, value: list):
|
||||||
|
self._active = value
|
||||||
|
|
||||||
|
def block(self, state: bool):
|
||||||
|
self._block = state
|
||||||
|
|
||||||
|
if not self._block:
|
||||||
|
self.graphic.enableAutoRange()
|
||||||
|
self._update_zorder()
|
||||||
|
self.show_legend()
|
||||||
|
else:
|
||||||
|
self.graphic.disableAutoRange()
|
||||||
|
|
||||||
def add(self, name: str | list, plots: list):
|
def add(self, name: str | list, plots: list):
|
||||||
if isinstance(name, str):
|
if isinstance(name, str):
|
||||||
name = [name]
|
name = [name]
|
||||||
plots = [plots]
|
plots = [plots]
|
||||||
|
|
||||||
for (real_plot, imag_plot, err_plot), n in zip(plots, name):
|
|
||||||
toplevel = len(self.sets)
|
toplevel = len(self.sets)
|
||||||
|
self.listWidget.blockSignals(True)
|
||||||
|
for (real_plot, imag_plot, err_plot), n in zip(plots, name):
|
||||||
self.sets.append(n)
|
self.sets.append(n)
|
||||||
|
|
||||||
if real_plot:
|
if real_plot:
|
||||||
@ -169,7 +199,16 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
list_item.setCheckState(QtCore.Qt.Checked)
|
list_item.setCheckState(QtCore.Qt.Checked)
|
||||||
self.listWidget.addItem(list_item)
|
self.listWidget.addItem(list_item)
|
||||||
|
|
||||||
|
toplevel += 1
|
||||||
|
self.listWidget.blockSignals(False)
|
||||||
|
|
||||||
|
if len(name) < 200:
|
||||||
self.show_item(name)
|
self.show_item(name)
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.warning(self, 'Display disabled',
|
||||||
|
'If more than 200 sets are added at once, they are not displayed to avoid major performance issues.\n'
|
||||||
|
'The checkmark in the data tree is invalid.\n'
|
||||||
|
'Please display them manually in smaller batches, thank you!')
|
||||||
|
|
||||||
def remove(self, name: str | list):
|
def remove(self, name: str | list):
|
||||||
if isinstance(name, str):
|
if isinstance(name, str):
|
||||||
@ -181,8 +220,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
for plot in [self.real_plots, self.imag_plots, self.error_plots]:
|
for plot in [self.real_plots, self.imag_plots, self.error_plots]:
|
||||||
self.graphic.removeItem(plot[n])
|
self.graphic.removeItem(plot[n])
|
||||||
|
|
||||||
if n in self.active:
|
if n in self._active:
|
||||||
self.active.remove(n)
|
self._active.remove(n)
|
||||||
|
|
||||||
# remove from label list
|
# remove from label list
|
||||||
self.listWidget.blockSignals(True)
|
self.listWidget.blockSignals(True)
|
||||||
@ -194,6 +233,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
|
|
||||||
self.listWidget.blockSignals(False)
|
self.listWidget.blockSignals(False)
|
||||||
|
|
||||||
|
if not self._block:
|
||||||
self._update_zorder()
|
self._update_zorder()
|
||||||
self.show_legend()
|
self.show_legend()
|
||||||
|
|
||||||
@ -225,8 +265,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
for a in idlist:
|
for a in idlist:
|
||||||
if a not in self.active:
|
if a not in self._active:
|
||||||
self.active.append(a)
|
self._active.append(a)
|
||||||
|
|
||||||
for (bttn, plot_dic) in [
|
for (bttn, plot_dic) in [
|
||||||
(self.real_button, self.real_plots),
|
(self.real_button, self.real_plots),
|
||||||
@ -245,8 +285,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
for r in idlist:
|
for r in idlist:
|
||||||
if r in self.active:
|
if r in self._active:
|
||||||
self.active.remove(r)
|
self._active.remove(r)
|
||||||
|
|
||||||
for plt in [self.real_plots, self.imag_plots, self.error_plots]:
|
for plt in [self.real_plots, self.imag_plots, self.error_plots]:
|
||||||
item = plt[r]
|
item = plt[r]
|
||||||
@ -268,7 +308,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
else:
|
else:
|
||||||
func = self.graphic.removeItem
|
func = self.graphic.removeItem
|
||||||
|
|
||||||
for a in self.active:
|
for a in self._active:
|
||||||
item = plots[a]
|
item = plots[a]
|
||||||
if item is not None:
|
if item is not None:
|
||||||
func(item)
|
func(item)
|
||||||
@ -285,12 +325,12 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if visible:
|
if visible:
|
||||||
for a in self.active:
|
for a in self._active:
|
||||||
item = self.error_plots[a]
|
item = self.error_plots[a]
|
||||||
if (item is not None) and (item not in self.graphic.items()):
|
if (item is not None) and (item not in self.graphic.items()):
|
||||||
self.graphic.addItem(item)
|
self.graphic.addItem(item)
|
||||||
else:
|
else:
|
||||||
for a in self.active:
|
for a in self._active:
|
||||||
item = self.error_plots[a]
|
item = self.error_plots[a]
|
||||||
if (item is not None) and (item in self.graphic.items()):
|
if (item is not None) and (item in self.graphic.items()):
|
||||||
self.graphic.removeItem(item)
|
self.graphic.removeItem(item)
|
||||||
@ -354,7 +394,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
|
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
|
||||||
|
|
||||||
if res == QtWidgets.QMessageBox.Yes:
|
if res == QtWidgets.QMessageBox.Yes:
|
||||||
self.aboutToClose.emit(self.id)
|
self.aboutToClose.emit([self.id])
|
||||||
evt.accept()
|
evt.accept()
|
||||||
else:
|
else:
|
||||||
evt.ignore()
|
evt.ignore()
|
||||||
@ -402,6 +442,9 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
def set_logmode(self, xmode: bool = None, ymode: bool = None):
|
def set_logmode(self, xmode: bool = None, ymode: bool = None):
|
||||||
r = self.ranges
|
r = self.ranges
|
||||||
|
|
||||||
|
self.plotItem.setXRange(*r[0])
|
||||||
|
self.plotItem.setYRange(*r[1])
|
||||||
|
|
||||||
if xmode is None:
|
if xmode is None:
|
||||||
xmode = self.plotItem.ctrl.logXCheck.isChecked()
|
xmode = self.plotItem.ctrl.logXCheck.isChecked()
|
||||||
else:
|
else:
|
||||||
@ -418,6 +461,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
item.logmode[0] = self.log[:]
|
item.logmode[0] = self.log[:]
|
||||||
|
|
||||||
self.plotItem.updateLogMode()
|
self.plotItem.updateLogMode()
|
||||||
|
self.set_range(x=r[0], y=r[1])
|
||||||
|
|
||||||
self.plotItem.enableAutoRange()
|
self.plotItem.enableAutoRange()
|
||||||
|
|
||||||
@ -460,9 +504,9 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
with errstate(all='ignore'):
|
with errstate(all='ignore'):
|
||||||
xy = [log10(val) for val in xy]
|
xy = [log10(val) for val in xy]
|
||||||
|
|
||||||
if isnan(xy[1]):
|
if not isfinite(xy[1]):
|
||||||
xy = [-1, 1]
|
xy = [-1, 1]
|
||||||
elif isnan(xy[0]):
|
elif not isfinite(xy[0]):
|
||||||
xy[0] = xy[1]-4
|
xy[0] = xy[1]-4
|
||||||
|
|
||||||
func(xy[0], xy[1], padding=0)
|
func(xy[0], xy[1], padding=0)
|
||||||
@ -494,6 +538,9 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
self.checkBox.setVisible(visible)
|
self.checkBox.setVisible(visible)
|
||||||
|
|
||||||
def update_legend(self, sid, name):
|
def update_legend(self, sid, name):
|
||||||
|
if self._block:
|
||||||
|
return
|
||||||
|
|
||||||
self.listWidget.blockSignals(True)
|
self.listWidget.blockSignals(True)
|
||||||
|
|
||||||
for i in range(self.listWidget.count()):
|
for i in range(self.listWidget.count()):
|
||||||
@ -520,16 +567,14 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
elif other_item in self.graphic.items():
|
elif other_item in self.graphic.items():
|
||||||
self.legend.addItem(other_item, convert(other_item.opts.get('name', ''), old='tex', new='html'))
|
self.legend.addItem(other_item, convert(other_item.opts.get('name', ''), old='tex', new='html'))
|
||||||
|
|
||||||
def export_dialog(self, path=None):
|
def export_dialog(self, _=None):
|
||||||
filters = 'All files (*.*);;AGR (*.agr);;SVG (*.svg);;PDF (*.pdf)'
|
filters = 'All files (*.*);;AGR (*.agr);;SVG (*.svg);;PDF (*.pdf)'
|
||||||
for imgformat in QtGui.QImageWriter.supportedImageFormats():
|
for imgformat in QtGui.QImageWriter.supportedImageFormats():
|
||||||
str_format = imgformat.data().decode('utf-8')
|
str_format = imgformat.data().decode('utf-8')
|
||||||
filters += ';;' + str_format.upper() + ' (*.' + str_format + ')'
|
filters += ';;' + str_format.upper() + ' (*.' + str_format + ')'
|
||||||
|
|
||||||
if path is None:
|
|
||||||
path = ''
|
|
||||||
outfile = None
|
outfile = None
|
||||||
f = FileDialog(caption='Export graphic', directory=str(path), filter=filters, mode='save')
|
f = FileDialog(caption='Export graphic', filter=filters, mode='save')
|
||||||
f.setOption(FileDialog.DontConfirmOverwrite)
|
f.setOption(FileDialog.DontConfirmOverwrite)
|
||||||
mode = f.exec()
|
mode = f.exec()
|
||||||
if mode == QtWidgets.QDialog.Accepted:
|
if mode == QtWidgets.QDialog.Accepted:
|
||||||
@ -608,7 +653,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
try:
|
try:
|
||||||
item_dic = plot_item.get_data_opts()
|
item_dic = plot_item.get_data_opts()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'{item} could not exported because {e.args}')
|
logger.exception(f'{item} could not exported because {e.args}')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if len(item) == 2:
|
if len(item) == 2:
|
||||||
@ -622,7 +667,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
try:
|
try:
|
||||||
dic['items'].append(item.get_data_opts())
|
dic['items'].append(item.get_data_opts())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'{item} could not be exported because {e.args}')
|
logger.exception(f'{item} could not be exported because {e.args}')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
in_legend.append(False)
|
in_legend.append(False)
|
||||||
@ -645,7 +690,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
'legend': self.legend.isVisible(),
|
'legend': self.legend.isVisible(),
|
||||||
'plots': (self.real_button.isChecked(), self.imag_button.isChecked(), self.error_button.isChecked()),
|
'plots': (self.real_button.isChecked(), self.imag_button.isChecked(), self.error_button.isChecked()),
|
||||||
'children': self.sets,
|
'children': self.sets,
|
||||||
'active': self.active,
|
'active': self._active,
|
||||||
}
|
}
|
||||||
|
|
||||||
in_legend = []
|
in_legend = []
|
||||||
@ -708,7 +753,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
if background is not None:
|
if background is not None:
|
||||||
self._bgcolor = mkColor(background)
|
self._bgcolor = mkColor(background)
|
||||||
self.graphic.setBackground(self._bgcolor)
|
self.graphic.setBackground(self._bgcolor)
|
||||||
self.legend.setBrush(self._bgcolor)
|
# self.legend.setBrush(self._bgcolor)
|
||||||
|
|
||||||
if foreground is not None:
|
if foreground is not None:
|
||||||
self._fgcolor = mkColor(foreground)
|
self._fgcolor = mkColor(foreground)
|
||||||
@ -725,7 +770,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
self.show_legend()
|
self.show_legend()
|
||||||
|
|
||||||
title = self.plotItem.titleLabel.text
|
title = self.plotItem.titleLabel.text
|
||||||
if title is not None:
|
if title is not None and title != '':
|
||||||
self.plotItem.setTitle(title, **{'size': '10pt', 'color': self._fgcolor})
|
self.plotItem.setTitle(title, **{'size': '10pt', 'color': self._fgcolor})
|
||||||
|
|
||||||
x = self.plotItem.getAxis('bottom').labelText
|
x = self.plotItem.getAxis('bottom').labelText
|
||||||
@ -736,8 +781,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
if y is not None:
|
if y is not None:
|
||||||
self.plotItem.setLabel('left', y, **{'font-size': '10pt', 'color': self._fgcolor.name()})
|
self.plotItem.setLabel('left', y, **{'font-size': '10pt', 'color': self._fgcolor.name()})
|
||||||
|
|
||||||
@QtCore.pyqtSlot(bool, name='on_bwbutton_toggled')
|
|
||||||
def change_background(self, _):
|
def change_background(self, _):
|
||||||
temp = self._fgcolor, self._bgcolor
|
temp = self._fgcolor, self._bgcolor
|
||||||
self.set_color(foreground=self._prev_colors[0], background=self._prev_colors[1])
|
self.set_color(foreground=self._prev_colors[0], background=self._prev_colors[1])
|
||||||
self._prev_colors = temp
|
self._prev_colors = temp
|
||||||
|
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from nmreval.io.asciireader import AsciiReader
|
from nmreval.io.asciireader import AsciiReader
|
||||||
|
from nmreval.utils import NUMBER_RE
|
||||||
|
|
||||||
from ..Qt import QtGui, QtCore, QtWidgets
|
from ..Qt import QtGui, QtCore, QtWidgets
|
||||||
from .._py.asciidialog import Ui_ascii_reader
|
from .._py.asciidialog import Ui_ascii_reader
|
||||||
@ -13,6 +18,9 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
|
|||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
self.reader = None
|
self.reader = None
|
||||||
|
self._matches = []
|
||||||
|
self.regex_input.setText(NUMBER_RE.pattern)
|
||||||
|
self.custom_input.setValidator(QtGui.QDoubleValidator())
|
||||||
|
|
||||||
self.comment_textfield = QtWidgets.QPlainTextEdit(self.header_widget)
|
self.comment_textfield = QtWidgets.QPlainTextEdit(self.header_widget)
|
||||||
self.comment_textfield.setReadOnly(True)
|
self.comment_textfield.setReadOnly(True)
|
||||||
@ -41,6 +49,8 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
|
|||||||
def __call__(self, fname, *args, **kwargs):
|
def __call__(self, fname, *args, **kwargs):
|
||||||
self.reader = AsciiReader(fname)
|
self.reader = AsciiReader(fname)
|
||||||
|
|
||||||
|
self.check_filename(self.regex_input.text())
|
||||||
|
|
||||||
if self.skip:
|
if self.skip:
|
||||||
self.accept()
|
self.accept()
|
||||||
return
|
return
|
||||||
@ -52,6 +62,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
|
|||||||
self.log_checkBox.setChecked(False)
|
self.log_checkBox.setChecked(False)
|
||||||
|
|
||||||
self.set_gui()
|
self.set_gui()
|
||||||
|
self.set_column_names(1)
|
||||||
|
|
||||||
self.skippy_checkbox.blockSignals(True)
|
self.skippy_checkbox.blockSignals(True)
|
||||||
self.skippy_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
self.skippy_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||||
@ -63,12 +74,14 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
|
|||||||
for text in self.reader.header:
|
for text in self.reader.header:
|
||||||
self.comment_textfield.appendPlainText(text)
|
self.comment_textfield.appendPlainText(text)
|
||||||
|
|
||||||
|
tmp = self.line_spinBox.value()
|
||||||
if self.reader.header:
|
if self.reader.header:
|
||||||
self.line_spinBox.setMaximum(len(self.reader.header))
|
self.line_spinBox.setMaximum(len(self.reader.header))
|
||||||
else:
|
else:
|
||||||
self.line_spinBox.setValue(0)
|
self.line_spinBox.setValue(0)
|
||||||
self.line_spinBox.setEnabled(False)
|
self.line_spinBox.setEnabled(False)
|
||||||
self.show_preview(10)
|
self.show_preview(10)
|
||||||
|
self.line_spinBox.setValue(tmp)
|
||||||
|
|
||||||
if self.reader.delays is not None:
|
if self.reader.delays is not None:
|
||||||
set_string = ''.join(str(d) + '\n' for d in self.reader.delays)
|
set_string = ''.join(str(d) + '\n' for d in self.reader.delays)
|
||||||
@ -98,22 +111,26 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
|
|||||||
|
|
||||||
@QtCore.pyqtSlot(int, name='on_preview_spinBox_valueChanged')
|
@QtCore.pyqtSlot(int, name='on_preview_spinBox_valueChanged')
|
||||||
def show_preview(self, line_no: int):
|
def show_preview(self, line_no: int):
|
||||||
preview, width = self.reader.make_preview(line_no)
|
preview, width, comments = self.reader.make_preview(line_no)
|
||||||
self.ascii_table.setRowCount(min(line_no, len(preview)))
|
self.ascii_table.setRowCount(min(line_no, len(preview)))
|
||||||
self.ascii_table.setColumnCount(width)
|
self.ascii_table.setColumnCount(width + 1)
|
||||||
|
|
||||||
for i, line in enumerate(preview):
|
for i, line in enumerate(preview):
|
||||||
|
comment_line = comments[i]
|
||||||
for j, field in enumerate(line):
|
for j, field in enumerate(line):
|
||||||
it = QtWidgets.QTableWidgetItem(field)
|
it = QtWidgets.QTableWidgetItem(field)
|
||||||
self.ascii_table.setItem(i, j, it)
|
self.ascii_table.setItem(i, j, it)
|
||||||
|
|
||||||
|
it = QtWidgets.QTableWidgetItem(comment_line)
|
||||||
|
self.ascii_table.setItem(i, len(line), it)
|
||||||
|
|
||||||
self.ascii_table.resizeColumnsToContents()
|
self.ascii_table.resizeColumnsToContents()
|
||||||
|
|
||||||
@QtCore.pyqtSlot(int, name='on_column_checkBox_stateChanged')
|
@QtCore.pyqtSlot(int, name='on_column_checkBox_stateChanged')
|
||||||
@QtCore.pyqtSlot(int, name='on_line_spinBox_valueChanged')
|
@QtCore.pyqtSlot(int, name='on_line_spinBox_valueChanged')
|
||||||
def set_column_names(self, _):
|
def set_column_names(self, _):
|
||||||
self.ascii_table.setHorizontalHeaderLabels(map(str, range(1, self.ascii_table.columnCount() + 1)))
|
self.ascii_table.setHorizontalHeaderLabels(map(str, range(1, self.ascii_table.columnCount() + 1)))
|
||||||
if self.column_checkBox.isChecked():
|
if self.column_checkBox.isChecked() and self.line_spinBox.isEnabled():
|
||||||
header_line = self.reader.header[self.line_spinBox.value()-1]
|
header_line = self.reader.header[self.line_spinBox.value()-1]
|
||||||
self.ascii_table.setHorizontalHeaderLabels(header_line.split())
|
self.ascii_table.setHorizontalHeaderLabels(header_line.split())
|
||||||
|
|
||||||
@ -165,10 +182,13 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
|
|||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
# default row for x is the first row, it will be superseded if an integer number is given.
|
# default row for x is the first row, it will be superseded if an integer number is given.
|
||||||
|
x = self.x_lineedit.text()
|
||||||
|
if x:
|
||||||
try:
|
try:
|
||||||
x = int(self.x_lineedit.text())-1
|
x = int(x)-1
|
||||||
print(x)
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
x = None
|
x = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -194,12 +214,17 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
|
|||||||
col_header = [col_header[i] for i in range(len(col_header)) if i in y]
|
col_header = [col_header[i] for i in range(len(col_header)) if i in y]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ret_dic = self.reader.export(x=x, y=y, yerr=y_err,
|
ret_dic = self.reader.export(
|
||||||
|
x=x,
|
||||||
|
y=y,
|
||||||
|
yerr=y_err,
|
||||||
mode=self.buttonGroup.checkedButton().text(),
|
mode=self.buttonGroup.checkedButton().text(),
|
||||||
col_names=col_header)
|
col_names=col_header,
|
||||||
|
num_value=self.get_numerical_value(),
|
||||||
|
)
|
||||||
self.data_read.emit(ret_dic)
|
self.data_read.emit(ret_dic)
|
||||||
|
|
||||||
except Exception as e:
|
except ImportError as e:
|
||||||
_ = QtWidgets.QMessageBox.information(self, 'Reading failed',
|
_ = QtWidgets.QMessageBox.information(self, 'Reading failed',
|
||||||
f'Import data failed with {e.args}')
|
f'Import data failed with {e.args}')
|
||||||
|
|
||||||
@ -211,5 +236,45 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
|
|||||||
|
|
||||||
@QtCore.pyqtSlot(int, name='on_skippy_checkbox_stateChanged')
|
@QtCore.pyqtSlot(int, name='on_skippy_checkbox_stateChanged')
|
||||||
def skip_next_dial(self, _: int):
|
def skip_next_dial(self, _: int):
|
||||||
print('skippy das buschkänguru', _)
|
|
||||||
self.skip = self.skippy_checkbox.isChecked()
|
self.skip = self.skippy_checkbox.isChecked()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str, name='on_regex_input_textChanged')
|
||||||
|
def check_filename(self, pattern: str = NUMBER_RE.pattern):
|
||||||
|
if self.reader is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
pattern = re.compile(pattern)
|
||||||
|
self.regex_input.setStyleSheet('color: rgb(0, 0, 0)')
|
||||||
|
self._matches = [m for m in pattern.finditer(str(self.reader.fname.stem))]
|
||||||
|
except re.error:
|
||||||
|
self._matches = []
|
||||||
|
|
||||||
|
if self._matches:
|
||||||
|
self.re_match_index.blockSignals(True)
|
||||||
|
self.re_match_index.setMaximum(len(self._matches))
|
||||||
|
self.re_match_index.blockSignals(False)
|
||||||
|
else:
|
||||||
|
self.regex_input.setStyleSheet('color: rgb(255, 0, 0)')
|
||||||
|
|
||||||
|
self.show_match(self.re_match_index.value())
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int, name='on_re_match_index_valueChanged')
|
||||||
|
def show_match(self, idx: int = 0):
|
||||||
|
fname = str(self.reader.fname.stem)
|
||||||
|
|
||||||
|
if self._matches:
|
||||||
|
m = self._matches[idx-1]
|
||||||
|
self.label_8.setText(f'{fname[:m.start()]}<b>{fname[m.start():m.end()]}</b>{fname[m.end():]}')
|
||||||
|
else:
|
||||||
|
self.label_8.setText(fname)
|
||||||
|
|
||||||
|
def get_numerical_value(self):
|
||||||
|
val = 0
|
||||||
|
if self.re_button.isChecked() and self._matches:
|
||||||
|
m = self._matches[self.re_match_index.value()-1]
|
||||||
|
val = float(NUMBER_RE.search(m.group()).group().replace('p', '.'))
|
||||||
|
elif self.custom_button.isChecked():
|
||||||
|
val = float(self.custom_input.text())
|
||||||
|
|
||||||
|
return val
|
||||||
|
@ -5,7 +5,7 @@ from pathlib import Path
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from pyqtgraph import PlotDataItem
|
from pyqtgraph import PlotDataItem
|
||||||
|
|
||||||
from nmreval.data.points import Points
|
from nmreval.data import DSC
|
||||||
from nmreval.io.dsc import Cyclohexane, DSCCalibrator, DSCSample
|
from nmreval.io.dsc import Cyclohexane, DSCCalibrator, DSCSample
|
||||||
|
|
||||||
from ..Qt import QtWidgets, QtCore
|
from ..Qt import QtWidgets, QtCore
|
||||||
@ -83,7 +83,7 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
if opts[0] == 'i':
|
if opts[0] == 'i':
|
||||||
item.setFlags(QtCore.Qt.NoItemFlags)
|
item.setFlags(QtCore.Qt.NoItemFlags)
|
||||||
item.setText(f'{opts[1]:.2f} K for {opts[1] / 60:.0f} min')
|
item.setText(f'{opts[1]:.2f} K for {opts[2] / 60:.0f} min')
|
||||||
else:
|
else:
|
||||||
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable)
|
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable)
|
||||||
item.setText(f'{opts[2]:.2f} K to {opts[3]:.2f} K with {opts[1]} K/min')
|
item.setText(f'{opts[2]:.2f} K to {opts[3]:.2f} K with {opts[1]} K/min')
|
||||||
@ -97,7 +97,7 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
if empty:
|
if empty:
|
||||||
self.empty = self.calibrator.set_measurement(empty, mode='empty')
|
self.empty = self.calibrator.set_measurement(empty, mode='empty')
|
||||||
self.empty_label.setText(str(self.empty.fname.name))
|
self.empty_label.setText('~/' + str(self.empty.fname.relative_to(Path.home())))
|
||||||
|
|
||||||
self.update_plots()
|
self.update_plots()
|
||||||
|
|
||||||
@ -158,20 +158,28 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.sample_idx = None
|
self.sample_idx = None
|
||||||
self.clear_plots()
|
self.clear_plots()
|
||||||
|
|
||||||
def get_data(self, ):
|
def get_data(self):
|
||||||
if self.sample_idx is None:
|
if self.sample_idx is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
rate = self.current_run[0]
|
rate = self.current_run[0]
|
||||||
slope_type = {self.none_radioButton: None,
|
slope_type = {
|
||||||
|
self.none_radioButton: None,
|
||||||
self.isotherm_radioButton: 'iso',
|
self.isotherm_radioButton: 'iso',
|
||||||
self.slope_radioButton: 'curve'}[self.buttonGroup.checkedButton()]
|
self.slope_radioButton: 'curve',
|
||||||
|
}[self.buttonGroup.checkedButton()]
|
||||||
|
|
||||||
|
limit = None
|
||||||
|
if slope_type == 'curve':
|
||||||
|
try:
|
||||||
|
limit = float(self.limit1_lineedit.text())*60, float(self.limit2_lineedit.text())*60
|
||||||
|
except ValueError:
|
||||||
|
limit = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
raw_sample, drift_value, sample_data, empty_data, slope = self.calibrator.get_data(self.sample_idx,
|
raw_sample, drift_value, sample_data, empty_data, slope = self.calibrator.get_data(self.sample_idx, slope=slope_type, limits=limit)
|
||||||
slope=slope_type)
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
_msg = QtWidgets.QMessageBox.warning(self, 'No rate found', e.args[0])
|
_msg = QtWidgets.QMessageBox.warning(self, f'Data collection with error', e.args[0])
|
||||||
return
|
return
|
||||||
|
|
||||||
self.calibrator.ref_list = []
|
self.calibrator.ref_list = []
|
||||||
@ -194,8 +202,14 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonGroup_buttonClicked')
|
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonGroup_buttonClicked')
|
||||||
@QtCore.pyqtSlot(int, name='on_cp_checkBox_stateChanged')
|
@QtCore.pyqtSlot(int, name='on_cp_checkBox_stateChanged')
|
||||||
|
@QtCore.pyqtSlot(str, name='on_limit1_lineedit_textChanged')
|
||||||
|
@QtCore.pyqtSlot(str, name='on_limit2_lineedit_textChanged')
|
||||||
def update_plots(self, _=None):
|
def update_plots(self, _=None):
|
||||||
sample_data, raw_sample, empty_data, drift_value, slope, calib_x, calib_y, regions = self.get_data()
|
res = self.get_data()
|
||||||
|
if res is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
sample_data, raw_sample, empty_data, drift_value, slope, calib_x, calib_y, regions = res
|
||||||
|
|
||||||
self.raw_sample.setData(x=raw_sample[0], y=raw_sample[1])
|
self.raw_sample.setData(x=raw_sample[0], y=raw_sample[1])
|
||||||
self.drift_sample.setData(x=drift_value[0], y=drift_value[1])
|
self.drift_sample.setData(x=drift_value[0], y=drift_value[1])
|
||||||
@ -236,10 +250,11 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
return
|
return
|
||||||
|
|
||||||
rate, mode = self.current_run
|
rate, mode = self.current_run
|
||||||
new_val = Points(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate} ({mode})')
|
new_val = DSC(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate} ({mode})')
|
||||||
|
|
||||||
if filesave:
|
if filesave:
|
||||||
new_val.savetxt(self.fname.with_name(f'{self.fname.stem} {rate}K-min {mode}.dat'.replace(' ', '_')))
|
new_val.savetxt(self.fname.with_name(f'{self.fname.stem} {rate}K-min {mode}.dat'.replace(' ', '_')))
|
||||||
|
close_after = False
|
||||||
else:
|
else:
|
||||||
self.data_read.emit([new_val])
|
self.data_read.emit([new_val])
|
||||||
|
|
||||||
|
@ -55,9 +55,9 @@ class GraceExporter:
|
|||||||
break
|
break
|
||||||
|
|
||||||
if c_num == -1:
|
if c_num == -1:
|
||||||
c_num = max(colors.keys())
|
c_num = max(colors.keys())+1
|
||||||
colors[c_num + 1] = (f'color{c_num + 1}', sc)
|
colors[c_num] = (f'color{c_num}', sc)
|
||||||
new_colors.append((c_num + 1, f'color{c_num + 1}', sc))
|
new_colors.append((c_num, f'color{c_num}', sc))
|
||||||
|
|
||||||
new_s.set_symbol(**{'symbol': item['symbol'].value, 'size': item['symbolsize'] / 10., 'color': c_num,
|
new_s.set_symbol(**{'symbol': item['symbol'].value, 'size': item['symbolsize'] / 10., 'color': c_num,
|
||||||
'fill color': c_num, 'fill pattern': 1})
|
'fill color': c_num, 'fill pattern': 1})
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
from nmreval.io.fcbatchreader import FCReader
|
from nmreval.io.fcbatchreader import FCReader
|
||||||
@ -31,6 +33,10 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
|
|||||||
|
|
||||||
return super().eventFilter(src, evt)
|
return super().eventFilter(src, evt)
|
||||||
|
|
||||||
|
def add_graphs(self, graphs: list[tuple[str, str]]):
|
||||||
|
for gid, graph_name in graphs:
|
||||||
|
self.graph_comboBox.addItem(graph_name, gid)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(int, name='on_region_checkBox_stateChanged')
|
@QtCore.pyqtSlot(int, name='on_region_checkBox_stateChanged')
|
||||||
def use_region(self, state: int):
|
def use_region(self, state: int):
|
||||||
self.start_lineedit.setEnabled(state == QtCore.Qt.Checked)
|
self.start_lineedit.setEnabled(state == QtCore.Qt.Checked)
|
||||||
@ -43,17 +49,17 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
|
|||||||
infiles, _ = QtWidgets.QFileDialog.getOpenFileNames(caption='Select HDF files',
|
infiles, _ = QtWidgets.QFileDialog.getOpenFileNames(caption='Select HDF files',
|
||||||
directory=str(self.path),
|
directory=str(self.path),
|
||||||
filter='HDF files (*.h5)')
|
filter='HDF files (*.h5)')
|
||||||
if infiles:
|
|
||||||
self.listWidget.addItems(infiles)
|
|
||||||
self.label.setText(str(pathlib.Path(infiles[-1]).parent))
|
|
||||||
else:
|
else:
|
||||||
infiles = QtWidgets.QFileDialog.getExistingDirectory(caption='Select input directory',
|
infiles = QtWidgets.QFileDialog.getExistingDirectory(caption='Select input directory',
|
||||||
directory=str(self.path),
|
directory=str(self.path),
|
||||||
options=QtWidgets.QFileDialog.ShowDirsOnly)
|
options=QtWidgets.QFileDialog.ShowDirsOnly)
|
||||||
|
infiles = [infiles] if infiles else infiles
|
||||||
|
|
||||||
if infiles:
|
if infiles:
|
||||||
self.listWidget.addItem(infiles)
|
self.listWidget.addItems(infiles)
|
||||||
self.label.setText(str(pathlib.Path(infiles).parent))
|
self.path = pathlib.Path(infiles[-1]).parent
|
||||||
|
self.label.setText(str(self.path))
|
||||||
|
|
||||||
@QtCore.pyqtSlot(name='on_savebutton_clicked')
|
@QtCore.pyqtSlot(name='on_savebutton_clicked')
|
||||||
def save_path(self):
|
def save_path(self):
|
||||||
@ -72,7 +78,7 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
|
|||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def read(self, items):
|
def read(self, items):
|
||||||
region = (None, None)
|
region = None
|
||||||
if self.region_box.isChecked():
|
if self.region_box.isChecked():
|
||||||
start = None
|
start = None
|
||||||
if self.start_lineedit.text():
|
if self.start_lineedit.text():
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
from nmreval.lib.lines import LineStyle
|
from nmreval.lib.lines import LineStyle
|
||||||
from nmreval.lib.symbols import SymbolStyle
|
from nmreval.lib.symbols import SymbolStyle
|
||||||
from nmreval.data.points import Points
|
from nmreval.data.points import Points
|
||||||
from nmreval.io.graceeditor import GraceEditor
|
from nmreval.io.graceeditor import GraceEditor
|
||||||
|
from nmreval.utils.text import convert
|
||||||
|
|
||||||
from ..Qt import QtCore, QtWidgets, QtGui
|
from ..Qt import QtCore, QtWidgets, QtGui
|
||||||
from .._py.gracereader import Ui_Dialog
|
from .._py.gracereader import Ui_Dialog
|
||||||
@ -53,8 +56,12 @@ class QGraceReader(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
if ds is None:
|
if ds is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
item_2 = QtWidgets.QTreeWidgetItem([f'Set {gset.idx} (Label: {gset.get_property("legend")}, '
|
legend = gset.get_property('legend')
|
||||||
|
legend_str = convert(legend, old='agr', new='str') if legend is not None else ''
|
||||||
|
|
||||||
|
item_2 = QtWidgets.QTreeWidgetItem([f'Set {gset.idx} (Label: {legend_str}, '
|
||||||
f'shape: {ds.shape})'])
|
f'shape: {ds.shape})'])
|
||||||
|
|
||||||
item_2.setCheckState(0, QtCore.Qt.Checked)
|
item_2.setCheckState(0, QtCore.Qt.Checked)
|
||||||
item_2.setData(0, QtCore.Qt.UserRole, (graphs.idx, gset.idx))
|
item_2.setData(0, QtCore.Qt.UserRole, (graphs.idx, gset.idx))
|
||||||
item.addChild(item_2)
|
item.addChild(item_2)
|
||||||
@ -87,9 +94,14 @@ class QGraceReader(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
item = iterator.value()
|
item = iterator.value()
|
||||||
key = (item.data(0, QtCore.Qt.UserRole))
|
key = (item.data(0, QtCore.Qt.UserRole))
|
||||||
s = self._reader.dataset(*key)
|
s = self._reader.dataset(*key)
|
||||||
label = self._reader.get_property(*key, 'legend').replace('"', '')
|
label = self._reader.get_property(*key, 'legend')
|
||||||
# label = self._reader.graphs[key[0]].sets[key[1]]['legend'].replace('"', '')
|
if label is None:
|
||||||
|
label = ''
|
||||||
|
else:
|
||||||
|
label = label.replace('"', '')
|
||||||
|
label = convert(label, old='agr', new='str')
|
||||||
sd = s.data
|
sd = s.data
|
||||||
|
sd = np.atleast_2d(sd)
|
||||||
if s.type == 'xydy':
|
if s.type == 'xydy':
|
||||||
data.append(Points(x=sd[:, 0], y=sd[:, 1], y_err=sd[:, 2], name=label))
|
data.append(Points(x=sd[:, 0], y=sd[:, 1], y_err=sd[:, 2], name=label))
|
||||||
else:
|
else:
|
||||||
|
@ -1,80 +1 @@
|
|||||||
import sys
|
from .enums import Relations
|
||||||
|
|
||||||
if sys.version_info < (3, 7):
|
|
||||||
HAS_IMPORTLIB_RESOURCE = False
|
|
||||||
from pkg_resources import resource_filename
|
|
||||||
else:
|
|
||||||
HAS_IMPORTLIB_RESOURCE = True
|
|
||||||
from importlib.resources import path
|
|
||||||
|
|
||||||
from ..Qt import QtGui, QtWidgets
|
|
||||||
|
|
||||||
|
|
||||||
# def get_path_importlib(package, resource):
|
|
||||||
# return path(package, resource)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# def _get_path_pkg(package, resource):
|
|
||||||
# return resource_filename(package, resource)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# if HAS_IMPORTLIB_RESOURCE:
|
|
||||||
# get_path = get_path_importlib
|
|
||||||
# else:
|
|
||||||
# get_path = _get_path_pkg
|
|
||||||
|
|
||||||
|
|
||||||
def make_action_icons(widget):
|
|
||||||
global HAS_IMPORTLIB_RESOURCE
|
|
||||||
icon_type = QtWidgets.QApplication.instance().theme
|
|
||||||
from json import loads
|
|
||||||
|
|
||||||
if HAS_IMPORTLIB_RESOURCE:
|
|
||||||
with path('resources.icons', 'icons.json') as fp:
|
|
||||||
with fp.open('r') as f:
|
|
||||||
icon_list = loads(f.read())
|
|
||||||
|
|
||||||
for ac, img in icon_list[widget.objectName()].items():
|
|
||||||
dirname = 'resources.icons.%s_light' % icon_type
|
|
||||||
with path(dirname, img+'.png') as imgpath:
|
|
||||||
icon = QtGui.QIcon()
|
|
||||||
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
||||||
getattr(widget, ac).setIcon(icon)
|
|
||||||
|
|
||||||
else:
|
|
||||||
with open(resource_filename('resources.icons', 'icons.json'), 'r') as f:
|
|
||||||
icon_list = loads(f.read())
|
|
||||||
|
|
||||||
for ac, img in icon_list[widget.objectName()].items():
|
|
||||||
dirname = 'resources.icons.%s_light' % icon_type
|
|
||||||
imgpath = resource_filename(dirname, img+'.png')
|
|
||||||
icon = QtGui.QIcon()
|
|
||||||
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
||||||
getattr(widget, ac).setIcon(icon)
|
|
||||||
|
|
||||||
|
|
||||||
def get_icon(icon_name):
|
|
||||||
try:
|
|
||||||
icon_type = QtWidgets.QApplication.instance().theme
|
|
||||||
except AttributeError:
|
|
||||||
icon_type = 'normal'
|
|
||||||
|
|
||||||
global HAS_IMPORTLIB_RESOURCE
|
|
||||||
|
|
||||||
if icon_name != 'logo':
|
|
||||||
dirname = f'resources.icons.{icon_type}_light'
|
|
||||||
else:
|
|
||||||
dirname = 'resources.icons'
|
|
||||||
|
|
||||||
if HAS_IMPORTLIB_RESOURCE:
|
|
||||||
with path(dirname, icon_name+'.png') as imgpath:
|
|
||||||
icon = QtGui.QIcon()
|
|
||||||
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
||||||
|
|
||||||
return icon
|
|
||||||
else:
|
|
||||||
imgpath = resource_filename(dirname, icon_name+'.png')
|
|
||||||
icon = QtGui.QIcon()
|
|
||||||
icon.addPixmap(QtGui.QPixmap(imgpath), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
|
||||||
|
|
||||||
return icon
|
|
||||||
|
@ -237,7 +237,7 @@ class CodeEditor(QtWidgets.QPlainTextEdit):
|
|||||||
if block.isVisible() and (bottom >= evt.rect().top()):
|
if block.isVisible() and (bottom >= evt.rect().top()):
|
||||||
number = str(block_number + 1)
|
number = str(block_number + 1)
|
||||||
painter.setPen(QtCore.Qt.black)
|
painter.setPen(QtCore.Qt.black)
|
||||||
painter.drawText(0, top, self.current_linenumber.width() - 3, height,
|
painter.drawText(0, int(top), self.current_linenumber.width() - 3, height,
|
||||||
QtCore.Qt.AlignRight, number)
|
QtCore.Qt.AlignRight, number)
|
||||||
|
|
||||||
block = block.next()
|
block = block.next()
|
||||||
|
@ -30,7 +30,7 @@ class PropertyDelegate(QtWidgets.QStyledItemDelegate):
|
|||||||
|
|
||||||
rect = options.rect
|
rect = options.rect
|
||||||
rect.adjust(5, 0, -5, 0)
|
rect.adjust(5, 0, -5, 0)
|
||||||
mid = (rect.bottom()+rect.top()) / 2
|
mid = int((rect.bottom()+rect.top()) / 2)
|
||||||
painter.drawLine(rect.left(), mid, rect.right(), mid)
|
painter.drawLine(rect.left(), mid, rect.right(), mid)
|
||||||
painter.restore()
|
painter.restore()
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ class PropertyDelegate(QtWidgets.QStyledItemDelegate):
|
|||||||
painter.setPen(pen)
|
painter.setPen(pen)
|
||||||
|
|
||||||
pm = make_symbol_pixmap(r)
|
pm = make_symbol_pixmap(r)
|
||||||
painter.drawPixmap(options.rect.topLeft()+QtCore.QPoint(3, (options.rect.height()-pm.height())/2), pm)
|
painter.drawPixmap(options.rect.topLeft()+QtCore.QPoint(3, int((options.rect.height()-pm.height())/2)), pm)
|
||||||
|
|
||||||
style = QtWidgets.QApplication.style()
|
style = QtWidgets.QApplication.style()
|
||||||
text_rect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, options, None)
|
text_rect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, options, None)
|
||||||
@ -171,7 +171,7 @@ class LineStyleEditor(QtWidgets.QComboBox):
|
|||||||
rect = painter.style().subControlRect(QtWidgets.QStyle.CC_ComboBox,
|
rect = painter.style().subControlRect(QtWidgets.QStyle.CC_ComboBox,
|
||||||
opt, QtWidgets.QStyle.SC_ComboBoxEditField, None)
|
opt, QtWidgets.QStyle.SC_ComboBoxEditField, None)
|
||||||
rect.adjust(+10, 0, -10, 0)
|
rect.adjust(+10, 0, -10, 0)
|
||||||
mid = (rect.bottom() + rect.top()) / 2
|
mid = int((rect.bottom() + rect.top()) / 2)
|
||||||
painter.drawLine(rect.left(), mid, rect.right(), mid)
|
painter.drawLine(rect.left(), mid, rect.right(), mid)
|
||||||
painter.end()
|
painter.end()
|
||||||
else:
|
else:
|
||||||
@ -193,7 +193,7 @@ class LineStyleDelegate(QtWidgets.QStyledItemDelegate):
|
|||||||
|
|
||||||
rect = option.rect
|
rect = option.rect
|
||||||
rect.adjust(+10, 0, -10, 0)
|
rect.adjust(+10, 0, -10, 0)
|
||||||
mid = (rect.bottom()+rect.top()) / 2
|
mid = int((rect.bottom()+rect.top()) / 2)
|
||||||
painter.drawLine(rect.left(), mid, rect.right(), mid)
|
painter.drawLine(rect.left(), mid, rect.right(), mid)
|
||||||
else:
|
else:
|
||||||
QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)
|
QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)
|
||||||
|
8
src/gui_qt/lib/enums.py
Normal file
8
src/gui_qt/lib/enums.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from enum import IntEnum, auto
|
||||||
|
|
||||||
|
|
||||||
|
class Relations(IntEnum):
|
||||||
|
isFitOf = auto()
|
||||||
|
hasFit = auto()
|
||||||
|
isFitPartOf = auto()
|
||||||
|
hasFitPart = auto()
|
@ -1,3 +1,5 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
from numpy import inf
|
from numpy import inf
|
||||||
|
|
||||||
from nmreval.utils.text import convert
|
from nmreval.utils.text import convert
|
||||||
@ -50,19 +52,24 @@ class QDelayWidget(QtWidgets.QWidget):
|
|||||||
|
|
||||||
class LineEdit(QtWidgets.QLineEdit):
|
class LineEdit(QtWidgets.QLineEdit):
|
||||||
values_requested = QtCore.pyqtSignal()
|
values_requested = QtCore.pyqtSignal()
|
||||||
|
replace_single_values = QtCore.pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
def contextMenuEvent(self, evt):
|
def contextMenuEvent(self, evt):
|
||||||
menu = self.createStandardContextMenu()
|
menu = self.createStandardContextMenu()
|
||||||
request_action = menu.addAction('Use value of sets')
|
request_action = menu.addAction('Use numeric value of sets')
|
||||||
|
set_value_action = menu.addAction('Replace single set values')
|
||||||
|
|
||||||
action = menu.exec(evt.globalPos())
|
action = menu.exec(evt.globalPos())
|
||||||
|
|
||||||
if action == request_action:
|
if action == request_action:
|
||||||
self.values_requested.emit()
|
self.values_requested.emit()
|
||||||
|
|
||||||
|
elif action == set_value_action:
|
||||||
|
self.replace_single_values.emit()
|
||||||
|
|
||||||
|
|
||||||
class LineEditPost(QtWidgets.QLineEdit):
|
class LineEditPost(QtWidgets.QLineEdit):
|
||||||
values_requested = QtCore.pyqtSignal()
|
values_requested = QtCore.pyqtSignal()
|
||||||
@ -404,3 +411,21 @@ class ElideComboBox(QtWidgets.QComboBox):
|
|||||||
|
|
||||||
opt.currentText = painter.fontMetrics().elidedText(opt.currentText, QtCore.Qt.ElideRight, rect.width())
|
opt.currentText = painter.fontMetrics().elidedText(opt.currentText, QtCore.Qt.ElideRight, rect.width())
|
||||||
painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, opt)
|
painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, opt)
|
||||||
|
|
||||||
|
|
||||||
|
class CheckCombobox(QtWidgets.QComboBox):
|
||||||
|
|
||||||
|
def addItem(self, text: str, userData: Any=None) -> None:
|
||||||
|
super().addItem(text, userData=userData)
|
||||||
|
|
||||||
|
item = self.model().item(self.count()-1)
|
||||||
|
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable)
|
||||||
|
item.setCheckState(QtCore.Qt.Checked)
|
||||||
|
|
||||||
|
def addItems(self, text):
|
||||||
|
for text_i in text:
|
||||||
|
self.addItem(text_i)
|
||||||
|
|
||||||
|
def isChecked(self, idx: int) -> bool:
|
||||||
|
return bool(self.model().item(idx).checkState())
|
||||||
|
|
||||||
|
53
src/gui_qt/lib/graph_items.py
Normal file
53
src/gui_qt/lib/graph_items.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
from numpy import log10, arange, floor, ceil
|
||||||
|
from pyqtgraph import PlotWidget, PlotItem
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['NMRPlotWidget', 'logTickValues']
|
||||||
|
|
||||||
|
|
||||||
|
class NMRPlotWidget(PlotWidget):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
for orient in ['top', 'bottom', 'left', 'right']:
|
||||||
|
# BAD HACK!!! but seems to work, see function for explanation
|
||||||
|
self.plotItem.getAxis(orient).logTickValues = logTickValues
|
||||||
|
|
||||||
|
|
||||||
|
class NMRPlotItem(PlotItem):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
for orient in ['top', 'bottom', 'left', 'right']:
|
||||||
|
self.plotItem.getAxis(orient).logTickValues = logTickValues
|
||||||
|
|
||||||
|
|
||||||
|
def logTickValues(minVal, maxVal, size, stdTicks):
|
||||||
|
# TODO FIND A BETTER SOLUTION!!!
|
||||||
|
# Sometimes minVal and maxVal are not log-scaled values and the loop from v1 to v2 is humongous,
|
||||||
|
# The minor list then fills the RAM completely and freezes everything
|
||||||
|
# Until there is a better solution, we overwrite this function for every AxesItem
|
||||||
|
# and do not draw minor ticks at all if there are too many
|
||||||
|
|
||||||
|
# start with the tick spacing given by tickValues().
|
||||||
|
# Any level whose spacing is < 1 needs to be converted to log scale
|
||||||
|
ticks = []
|
||||||
|
for (spacing, t) in stdTicks:
|
||||||
|
if spacing >= 1.0:
|
||||||
|
ticks.append((spacing, t))
|
||||||
|
|
||||||
|
if len(ticks) < 3:
|
||||||
|
v1 = int(floor(minVal))
|
||||||
|
v2 = int(ceil(maxVal))
|
||||||
|
# major = list(range(v1+1, v2))
|
||||||
|
minor = []
|
||||||
|
|
||||||
|
if v2 - v1 < 400:
|
||||||
|
for v in range(v1, v2):
|
||||||
|
minor.extend(v + log10(arange(1, 10)))
|
||||||
|
minor = [x for x in minor if minVal < x < maxVal]
|
||||||
|
ticks.append((None, minor))
|
||||||
|
return ticks
|
||||||
|
|
66
src/gui_qt/lib/iconloading.py
Normal file
66
src/gui_qt/lib/iconloading.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
if sys.version_info < (3, 7):
|
||||||
|
HAS_IMPORTLIB_RESOURCE = False
|
||||||
|
from pkg_resources import resource_filename
|
||||||
|
else:
|
||||||
|
HAS_IMPORTLIB_RESOURCE = True
|
||||||
|
from importlib.resources import path
|
||||||
|
|
||||||
|
from ..Qt import QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
|
def make_action_icons(widget):
|
||||||
|
global HAS_IMPORTLIB_RESOURCE
|
||||||
|
icon_type = QtWidgets.QApplication.instance().theme
|
||||||
|
from json import loads
|
||||||
|
|
||||||
|
if HAS_IMPORTLIB_RESOURCE:
|
||||||
|
with path('resources.icons', 'icons.json') as fp:
|
||||||
|
with fp.open('r') as f:
|
||||||
|
icon_list = loads(f.read())
|
||||||
|
|
||||||
|
for ac, img in icon_list[widget.objectName()].items():
|
||||||
|
dirname = 'resources.icons.%s_light' % icon_type
|
||||||
|
with path(dirname, img+'.png') as imgpath:
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
getattr(widget, ac).setIcon(icon)
|
||||||
|
|
||||||
|
else:
|
||||||
|
with open(resource_filename('resources.icons', 'icons.json'), 'r') as f:
|
||||||
|
icon_list = loads(f.read())
|
||||||
|
|
||||||
|
for ac, img in icon_list[widget.objectName()].items():
|
||||||
|
dirname = 'resources.icons.%s_light' % icon_type
|
||||||
|
imgpath = resource_filename(dirname, img+'.png')
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
getattr(widget, ac).setIcon(icon)
|
||||||
|
|
||||||
|
|
||||||
|
def get_icon(icon_name):
|
||||||
|
try:
|
||||||
|
icon_type = QtWidgets.QApplication.instance().theme
|
||||||
|
except AttributeError:
|
||||||
|
icon_type = 'normal'
|
||||||
|
|
||||||
|
global HAS_IMPORTLIB_RESOURCE
|
||||||
|
|
||||||
|
if icon_name != 'logo':
|
||||||
|
dirname = f'resources.icons.{icon_type}_light'
|
||||||
|
else:
|
||||||
|
dirname = 'resources.icons'
|
||||||
|
|
||||||
|
if HAS_IMPORTLIB_RESOURCE:
|
||||||
|
with path(dirname, icon_name+'.png') as imgpath:
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
|
||||||
|
return icon
|
||||||
|
else:
|
||||||
|
imgpath = resource_filename(dirname, icon_name+'.png')
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(imgpath), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
|
||||||
|
return icon
|
20
src/gui_qt/lib/listwidget.py
Normal file
20
src/gui_qt/lib/listwidget.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from ..Qt import QtWidgets, QtGui, QtCore
|
||||||
|
|
||||||
|
|
||||||
|
class QListWidgetSelect(QtWidgets.QListWidget):
|
||||||
|
"""
|
||||||
|
Extension of QListWidget to change the check state of all selected QListWidgetItems with space key
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
def keyPressEvent(self, evt: QtGui.QKeyEvent):
|
||||||
|
if evt.key() == QtCore.Qt.Key.Key_Space:
|
||||||
|
for idx in self.selectedIndexes():
|
||||||
|
item = self.itemFromIndex(idx)
|
||||||
|
cs = item.checkState()
|
||||||
|
item.setCheckState(QtCore.Qt.CheckState.Unchecked if cs == QtCore.Qt.CheckState.Checked
|
||||||
|
else QtCore.Qt.CheckState.Checked)
|
||||||
|
else:
|
||||||
|
super().keyPressEvent(evt)
|
49
src/gui_qt/lib/mdiarea.py
Normal file
49
src/gui_qt/lib/mdiarea.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from ..Qt import QtWidgets, QtCore
|
||||||
|
|
||||||
|
from nmreval.lib.logger import logger
|
||||||
|
from ..graphs.graphwindow import QGraphWindow
|
||||||
|
|
||||||
|
|
||||||
|
class MdiAreaTile(QtWidgets.QMdiArea):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
def tileSubWindowsVertically(self):
|
||||||
|
window_list = self.subWindowList()
|
||||||
|
rect = QtCore.QRect(0, 0, self.width(), int(self.height() / len(window_list)))
|
||||||
|
pos = QtCore.QPoint(0, 0)
|
||||||
|
|
||||||
|
for win in window_list:
|
||||||
|
win.setGeometry(rect)
|
||||||
|
win.move(pos)
|
||||||
|
|
||||||
|
pos.setY(pos.y() + win.height())
|
||||||
|
|
||||||
|
def tileSubWindowsHorizontally(self):
|
||||||
|
window_list = self.subWindowList()
|
||||||
|
rect = QtCore.QRect(0, 0, int(self.width() / len(window_list)), self.height())
|
||||||
|
pos = QtCore.QPoint(0, 0)
|
||||||
|
|
||||||
|
for win in window_list:
|
||||||
|
win.setGeometry(rect)
|
||||||
|
win.move(pos)
|
||||||
|
|
||||||
|
pos.setX(pos.x() + win.width())
|
||||||
|
|
||||||
|
def addSubWindow(self, widget: QtWidgets.QWidget, flags: QtCore.Qt.WindowFlags = QtCore.Qt.WindowFlags()) -> QtWidgets.QMdiSubWindow | None:
|
||||||
|
subwindow = super().addSubWindow(widget)
|
||||||
|
subwindow.setOption(QtWidgets.QMdiSubWindow.RubberBandMove, True)
|
||||||
|
subwindow.setOption(QtWidgets.QMdiSubWindow.RubberBandResize, True)
|
||||||
|
subwindow.setMinimumHeight(240)
|
||||||
|
subwindow.setMinimumWidth(360)
|
||||||
|
|
||||||
|
return subwindow
|
||||||
|
|
||||||
|
def setActiveSubWidget(self, key: str):
|
||||||
|
for win in self.subWindowList():
|
||||||
|
wdgt = win.widget()
|
||||||
|
if isinstance(wdgt, QGraphWindow) and wdgt.id == key:
|
||||||
|
self.setActiveSubWindow(win)
|
||||||
|
break
|
@ -21,36 +21,53 @@ class Namespace:
|
|||||||
self.top_levels = {}
|
self.top_levels = {}
|
||||||
|
|
||||||
if basic:
|
if basic:
|
||||||
self.add_namespace({'x': (None, 'x values'), 'y': (None, 'x values'), 'y_err': (None, 'y error values'),
|
self.add_namespace(
|
||||||
'fit': (None, 'dictionary of fit parameter', 'fit["PIKA"]'), 'np': (np, 'numpy module')},
|
{'x': (None, 'x values'),
|
||||||
parents=('Basic', 'General'))
|
'y': (None, 'x values'),
|
||||||
|
'y_err': (None, 'y error values'),
|
||||||
|
'fit': (None, 'dictionary of fit parameter', 'fit["PIKA"]'),
|
||||||
|
'np': (np, 'numpy module'),
|
||||||
|
},
|
||||||
|
parents=('Basic', 'General'),
|
||||||
|
)
|
||||||
|
|
||||||
self.add_namespace({'sin': (np.sin, 'Sine', 'sin(PIKA)'), 'cos': (np.cos, 'Cosine', 'cos(PIKA)'),
|
self.add_namespace(
|
||||||
'tan': (np.tan, 'Tangens', 'tan(PIKA)'), 'ln': (np.log, 'Natural Logarithm', 'ln(PIKA)'),
|
{'sin': (np.sin, 'Sine', 'sin(PIKA)'),
|
||||||
|
'cos': (np.cos, 'Cosine', 'cos(PIKA)'),
|
||||||
|
'tan': (np.tan, 'Tangens', 'tan(PIKA)'),
|
||||||
|
'ln': (np.log, 'Natural Logarithm', 'ln(PIKA)'),
|
||||||
'log': (np.log10, 'Logarithm (base 10)', 'log(PIKA)'),
|
'log': (np.log10, 'Logarithm (base 10)', 'log(PIKA)'),
|
||||||
'exp': (np.exp, 'Exponential', 'exp(PIKA)'), 'sqrt': (np.sqrt, 'Root', 'sqrt(PIKA)'),
|
'exp': (np.exp, 'Exponential', 'exp(PIKA)'),
|
||||||
'lin_range': (np.linspace, 'N evenly spaced over interval [start, stop]',
|
'sqrt': (np.sqrt, 'Root', 'sqrt(PIKA)'),
|
||||||
'lin_range(start, stop, N)'),
|
'lin_range': (np.linspace, 'N evenly spaced over interval [start, stop]', 'lin_range(start, stop, N)'),
|
||||||
'log_range': (np.geomspace, 'N evenly spaced (log-scale) over interval [start, stop]',
|
'log_range': (np.geomspace, 'N evenly spaced (log-scale) over interval [start, stop]', 'lin_range(start, stop, N)'),
|
||||||
'lin_range(start, stop, N)')},
|
},
|
||||||
parents=('Basic', 'Functions'))
|
parents=('Basic', 'Functions'))
|
||||||
|
|
||||||
self.add_namespace({'max': (np.max, 'Maximum value', 'max(PIKA)'),
|
self.add_namespace(
|
||||||
|
{'max': (np.max, 'Maximum value', 'max(PIKA)'),
|
||||||
'min': (np.min, 'Minimum value', 'min(PIKA)'),
|
'min': (np.min, 'Minimum value', 'min(PIKA)'),
|
||||||
'argmax': (np.argmax, 'Index of maximum value', 'argmax(PIKA)'),
|
'argmax': (np.argmax, 'Index of maximum value', 'argmax(PIKA)'),
|
||||||
'argmin': (np.argmax, 'Index of minimum value', 'argmin(PIKA)')},
|
'argmin': (np.argmax, 'Index of minimum value', 'argmin(PIKA)'),
|
||||||
parents=('Basic', 'Values'))
|
},
|
||||||
|
parents=('Basic', 'Values')),
|
||||||
|
|
||||||
if const:
|
if const:
|
||||||
self.add_namespace({'e': (constants.e, 'e / As'), 'eps0': (constants.epsilon0, 'epsilon0 / As/Vm'),
|
self.add_namespace(
|
||||||
|
{'e': (constants.e, 'e / As'),
|
||||||
|
'eps0': (constants.epsilon0, 'epsilon0 / As/Vm'),
|
||||||
'Eu': (constants.Eu,), 'h': (constants.h, 'h / eVs'),
|
'Eu': (constants.Eu,), 'h': (constants.h, 'h / eVs'),
|
||||||
'hbar': (constants.hbar, 'hbar / eVs'), 'kB': (constants.kB, 'kB / eV/K'),
|
'hbar': (constants.hbar, 'hbar / eVs'), 'kB': (constants.kB, 'kB / eV/K'),
|
||||||
'mu0': (constants.mu0, 'mu0 / Vs/Am'), 'NA': (constants.NA, 'NA / 1/mol'),
|
'mu0': (constants.mu0, 'mu0 / Vs/Am'), 'NA': (constants.NA, 'NA / 1/mol'),
|
||||||
'pi': (constants.pi,), 'R': (constants.R, 'R / eV')},
|
'pi': (constants.pi,), 'R': (constants.R, 'R / eV'),
|
||||||
parents=('Constants', 'Maybe useful'))
|
},
|
||||||
|
parents=('Constants', 'Maybe useful'),
|
||||||
|
)
|
||||||
|
|
||||||
self.add_namespace({f'gamma["{k}"]': (v, k, f'gamma["{k}"]') for k, v in constants.gamma.items()},
|
self.add_namespace(
|
||||||
parents=('Constants', 'Magnetogyric ratios (in 1/(sT))'))
|
{f'gamma["{k}"]': (v, k, f'gamma["{k}"]') for k, v in constants.gamma.items()},
|
||||||
|
parents=('Constants', 'Magnetogyric ratios (in 1/(sT))')
|
||||||
|
)
|
||||||
|
|
||||||
if fitfuncs:
|
if fitfuncs:
|
||||||
self.make_dict_from_fitmodule(models)
|
self.make_dict_from_fitmodule(models)
|
||||||
@ -104,9 +121,18 @@ class Namespace:
|
|||||||
graph = namedtuple('graphs', ['s'])
|
graph = namedtuple('graphs', ['s'])
|
||||||
sets = namedtuple('sets', ['x', 'y', 'y_err', 'value', 'fit'], defaults=(None,))
|
sets = namedtuple('sets', ['x', 'y', 'y_err', 'value', 'fit'], defaults=(None,))
|
||||||
|
|
||||||
|
gamma = {}
|
||||||
|
|
||||||
gs = re.compile(r'g\[(\d+)].s\[(\d+)].(x|y(?:_err)*|value|fit)')
|
gs = re.compile(r'g\[(\d+)].s\[(\d+)].(x|y(?:_err)*|value|fit)')
|
||||||
|
gamma_re = re.compile(r'gamma\["(\w+)"\]')
|
||||||
|
|
||||||
for k, v in self.namespace.items():
|
for k, v in self.namespace.items():
|
||||||
|
m = gamma_re.match(k)
|
||||||
|
if m:
|
||||||
|
gamma[m.group(1)] = v[0]
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
m = gs.match(k)
|
m = gs.match(k)
|
||||||
if m:
|
if m:
|
||||||
if 'g' not in ret_dic:
|
if 'g' not in ret_dic:
|
||||||
@ -118,7 +144,7 @@ class Namespace:
|
|||||||
|
|
||||||
gg = ret_dic['g'][int(g_num)]
|
gg = ret_dic['g'][int(g_num)]
|
||||||
if int(s_num) not in gg.s:
|
if int(s_num) not in gg.s:
|
||||||
gg.s[int(s_num)] = sets('', '', '', '')
|
gg.s[int(s_num)] = sets('', '', '', '', {})
|
||||||
|
|
||||||
ss = gg.s[int(s_num)]
|
ss = gg.s[int(s_num)]
|
||||||
if pos == 'fit':
|
if pos == 'fit':
|
||||||
@ -130,9 +156,12 @@ class Namespace:
|
|||||||
else:
|
else:
|
||||||
ret_dic['g'][int(g_num)].s[int(s_num)] = ss._replace(**{pos: v[0]})
|
ret_dic['g'][int(g_num)].s[int(s_num)] = ss._replace(**{pos: v[0]})
|
||||||
|
|
||||||
else:
|
continue
|
||||||
|
|
||||||
ret_dic[k] = v[0]
|
ret_dic[k] = v[0]
|
||||||
|
|
||||||
|
ret_dic['gamma'] = gamma
|
||||||
|
|
||||||
return ret_dic
|
return ret_dic
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,6 +174,7 @@ class PlotItem(PlotDataItem):
|
|||||||
pen = self.opts['pen']
|
pen = self.opts['pen']
|
||||||
if isinstance(pen, tuple):
|
if isinstance(pen, tuple):
|
||||||
self.opts['linecolor'] = pen
|
self.opts['linecolor'] = pen
|
||||||
|
self.opts['pen'] = mkPen(color=pen)
|
||||||
else:
|
else:
|
||||||
c = pen.color()
|
c = pen.color()
|
||||||
self.opts['linecolor'] = c.red(), c.green(), c.blue()
|
self.opts['linecolor'] = c.red(), c.green(), c.blue()
|
||||||
@ -279,7 +280,7 @@ class PlotItem(PlotDataItem):
|
|||||||
else:
|
else:
|
||||||
self.scatter.hide()
|
self.scatter.hide()
|
||||||
|
|
||||||
def set_symbol(self, symbol=None, size=None, color=None):
|
def set_symbol(self, *, symbol=None, size=None, color=None):
|
||||||
if symbol is not None:
|
if symbol is not None:
|
||||||
if isinstance(symbol, int):
|
if isinstance(symbol, int):
|
||||||
self.setSymbol(SymbolStyle(symbol).to_str())
|
self.setSymbol(SymbolStyle(symbol).to_str())
|
||||||
@ -313,14 +314,13 @@ class PlotItem(PlotDataItem):
|
|||||||
self.opts['pen'] = pen
|
self.opts['pen'] = pen
|
||||||
self.updateItems()
|
self.updateItems()
|
||||||
|
|
||||||
def set_line(self, style=None, width=None, color=None):
|
def set_line(self, *, style=None, width=None, color=None):
|
||||||
pen = self.opts['pen']
|
pen = self.opts['pen']
|
||||||
if pen is None:
|
if pen is None:
|
||||||
pen = mkPen(style=QtCore.Qt.NoPen)
|
pen = mkPen(style=QtCore.Qt.NoPen)
|
||||||
|
|
||||||
if width is not None:
|
if width is not None:
|
||||||
pen.setWidthF(width)
|
pen.setWidthF(width)
|
||||||
|
|
||||||
if style is not None:
|
if style is not None:
|
||||||
if isinstance(style, LineStyle):
|
if isinstance(style, LineStyle):
|
||||||
style = style.value
|
style = style.value
|
||||||
@ -375,7 +375,6 @@ class PlotItem(PlotDataItem):
|
|||||||
class RegionItem(LinearRegionItem):
|
class RegionItem(LinearRegionItem):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.mode = kwargs.pop('mode', 'half')
|
self.mode = kwargs.pop('mode', 'half')
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.logmode = False
|
self.logmode = False
|
||||||
@ -383,6 +382,10 @@ class RegionItem(LinearRegionItem):
|
|||||||
if not hasattr(self, '_bounds') and hasattr(self, '_boundingRectCache'):
|
if not hasattr(self, '_bounds') and hasattr(self, '_boundingRectCache'):
|
||||||
self._bounds = self._boundingRectCache
|
self._bounds = self._boundingRectCache
|
||||||
|
|
||||||
|
for l in self.lines:
|
||||||
|
# higher z for borderlines improves chances that you can move it when multiple regions overlap
|
||||||
|
l.setZValue(self.zValue() + 1)
|
||||||
|
|
||||||
def setLogMode(self, xmode, _):
|
def setLogMode(self, xmode, _):
|
||||||
if self.logmode == xmode:
|
if self.logmode == xmode:
|
||||||
return
|
return
|
||||||
@ -418,6 +421,15 @@ class RegionItem(LinearRegionItem):
|
|||||||
else:
|
else:
|
||||||
return region
|
return region
|
||||||
|
|
||||||
|
def setRegion(self, region, use_log=False):
|
||||||
|
if self.logmode and use_log:
|
||||||
|
region = np.log10(region[0]), np.log10(region[1])
|
||||||
|
|
||||||
|
if not np.all(np.isfinite(region)):
|
||||||
|
raise ValueError(f'Invalid region limits ({region[0]}, {region[1]})')
|
||||||
|
else:
|
||||||
|
super().setRegion(region)
|
||||||
|
|
||||||
def boundingRect(self):
|
def boundingRect(self):
|
||||||
# overwrite to draw correct rect in logmode
|
# overwrite to draw correct rect in logmode
|
||||||
|
|
||||||
@ -460,11 +472,18 @@ class LegendItemBlock(LegendItem):
|
|||||||
def mouseDragEvent(self, ev):
|
def mouseDragEvent(self, ev):
|
||||||
if ev.button() == QtCore.Qt.LeftButton:
|
if ev.button() == QtCore.Qt.LeftButton:
|
||||||
ev.accept()
|
ev.accept()
|
||||||
|
|
||||||
dpos = ev.pos() - ev.lastPos()
|
dpos = ev.pos() - ev.lastPos()
|
||||||
|
|
||||||
|
upper_left = self.pos()
|
||||||
|
lower_right = self.pos()
|
||||||
|
lower_right.setX(lower_right.x() + self.width())
|
||||||
|
lower_right.setY(lower_right.y() + self.height())
|
||||||
|
|
||||||
vb_rect = self.parentItem().rect()
|
vb_rect = self.parentItem().rect()
|
||||||
pos = self.pos()
|
|
||||||
# upper left corner and a point a little more to the bottom right must be inside
|
# upper left and lower right corner must be inside viewbox
|
||||||
if vb_rect.contains(pos+dpos) and vb_rect.contains(pos+dpos+QtCore.QPointF(20., 20.)):
|
if vb_rect.contains(upper_left + dpos) and vb_rect.contains(lower_right + dpos):
|
||||||
self.autoAnchor(pos + dpos)
|
self.autoAnchor(upper_left + dpos)
|
||||||
else:
|
else:
|
||||||
self.autoAnchor(pos)
|
self.autoAnchor(upper_left)
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
import os.path
|
|
||||||
import json
|
|
||||||
import urllib.request
|
|
||||||
import webbrowser
|
|
||||||
import random
|
|
||||||
|
|
||||||
from ..Qt import QtGui, QtCore, QtWidgets
|
|
||||||
from .._py.pokemon import Ui_Dialog
|
|
||||||
|
|
||||||
random.seed()
|
|
||||||
|
|
||||||
|
|
||||||
class QPokemon(QtWidgets.QDialog, Ui_Dialog):
|
|
||||||
def __init__(self, number=None, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self.setupUi(self)
|
|
||||||
self._js = json.load(open(os.path.join(path_to_module, 'utils', 'pokemon.json'), 'r'), encoding='UTF-8')
|
|
||||||
self._id = 0
|
|
||||||
|
|
||||||
if number is not None and number in range(1, len(self._js)+1):
|
|
||||||
poke_nr = f'{number:03d}'
|
|
||||||
self._id = number
|
|
||||||
else:
|
|
||||||
poke_nr = f'{random.randint(1, len(self._js)):03d}'
|
|
||||||
self._id = int(poke_nr)
|
|
||||||
|
|
||||||
self._pokemon = None
|
|
||||||
self.show_pokemon(poke_nr)
|
|
||||||
self.label_15.linkActivated.connect(lambda x: webbrowser.open(x))
|
|
||||||
|
|
||||||
self.buttonBox.clicked.connect(self.randomize)
|
|
||||||
self.next_button.clicked.connect(self.show_next)
|
|
||||||
self.prev_button.clicked.connect(self.show_prev)
|
|
||||||
|
|
||||||
def show_pokemon(self, nr):
|
|
||||||
self._pokemon = self._js[nr]
|
|
||||||
self.setWindowTitle('Pokémon: ' + self._pokemon['Deutsch'])
|
|
||||||
|
|
||||||
for i in range(self.tabWidget.count(), -1, -1):
|
|
||||||
print('i', self.tabWidget.count(), i)
|
|
||||||
try:
|
|
||||||
self.tabWidget.widget(i).deleteLater()
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
for n, img in self._pokemon['Bilder']:
|
|
||||||
w = QtWidgets.QWidget()
|
|
||||||
vl = QtWidgets.QVBoxLayout()
|
|
||||||
l = QtWidgets.QLabel(self)
|
|
||||||
l.setAlignment(QtCore.Qt.AlignHCenter)
|
|
||||||
pixmap = QtGui.QPixmap()
|
|
||||||
|
|
||||||
try:
|
|
||||||
pixmap.loadFromData(urllib.request.urlopen(img, timeout=0.5).read())
|
|
||||||
except IOError:
|
|
||||||
l.setText(n)
|
|
||||||
else:
|
|
||||||
sc_pixmap = pixmap.scaled(256, 256, QtCore.Qt.KeepAspectRatio)
|
|
||||||
l.setPixmap(sc_pixmap)
|
|
||||||
|
|
||||||
vl.addWidget(l)
|
|
||||||
w.setLayout(vl)
|
|
||||||
self.tabWidget.addTab(w, n)
|
|
||||||
|
|
||||||
if len(self._pokemon['Bilder']) <= 1:
|
|
||||||
self.tabWidget.tabBar().setVisible(False)
|
|
||||||
else:
|
|
||||||
self.tabWidget.tabBar().setVisible(True)
|
|
||||||
self.tabWidget.adjustSize()
|
|
||||||
|
|
||||||
self.name.clear()
|
|
||||||
keys = ['National-Dex', 'Kategorie', 'Typ', 'Größe', 'Gewicht', 'Farbe', 'Link']
|
|
||||||
label_list = [self.pokedex_nr, self.category, self.poketype, self.weight, self.height, self.color, self.info]
|
|
||||||
for (k, label) in zip(keys, label_list):
|
|
||||||
v = self._pokemon[k]
|
|
||||||
if isinstance(v, list):
|
|
||||||
v = os.path.join('', *v)
|
|
||||||
|
|
||||||
if k == 'Link':
|
|
||||||
v = '<a href={}>{}</a>'.format(v, v)
|
|
||||||
|
|
||||||
label.setText(v)
|
|
||||||
|
|
||||||
for k in ['Deutsch', 'Japanisch', 'Englisch', 'Französisch']:
|
|
||||||
v = self._pokemon[k]
|
|
||||||
self.name.addItem(k + ': ' + v)
|
|
||||||
|
|
||||||
self.adjustSize()
|
|
||||||
|
|
||||||
def randomize(self, idd):
|
|
||||||
if idd.text() == 'Retry':
|
|
||||||
new_number = f'{random.randint(1, len(self._js)):03d}'
|
|
||||||
self._id = int(new_number)
|
|
||||||
self.show_pokemon(new_number)
|
|
||||||
else:
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def show_next(self):
|
|
||||||
new_number = self._id + 1
|
|
||||||
if new_number > len(self._js):
|
|
||||||
new_number = 1
|
|
||||||
self._id = new_number
|
|
||||||
self.show_pokemon(f'{new_number:03d}')
|
|
||||||
|
|
||||||
def show_prev(self):
|
|
||||||
new_number = self._id - 1
|
|
||||||
if new_number == 0:
|
|
||||||
new_number = len(self._js)
|
|
||||||
self._id = new_number
|
|
||||||
self.show_pokemon(f'{new_number:03d}')
|
|
100
src/gui_qt/lib/starter.py
Normal file
100
src/gui_qt/lib/starter.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from shutil import copyfile
|
||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
from configparser import ConfigParser
|
||||||
|
from importlib.resources import path as resource_path
|
||||||
|
|
||||||
|
from nmreval.configs import config_paths
|
||||||
|
from nmreval.lib.logger import logger
|
||||||
|
|
||||||
|
|
||||||
|
def make_starter(app_file: str | None):
|
||||||
|
if app_file is not None:
|
||||||
|
make_starter_appimage(Path(app_file))
|
||||||
|
else:
|
||||||
|
make_starter_src()
|
||||||
|
|
||||||
|
|
||||||
|
def make_starter_appimage(app_file: Path):
|
||||||
|
new_path = Path.home() / '.local' / 'bin'
|
||||||
|
|
||||||
|
if not new_path.exists():
|
||||||
|
new_path.mkdir(parents=True)
|
||||||
|
|
||||||
|
new_path /= app_file.name
|
||||||
|
|
||||||
|
if app_file != new_path:
|
||||||
|
app_file.rename(new_path)
|
||||||
|
|
||||||
|
create_desktop_file(new_path)
|
||||||
|
|
||||||
|
|
||||||
|
def make_starter_src():
|
||||||
|
home = Path.home()
|
||||||
|
p = Path.home()
|
||||||
|
for p in Path(__file__).parents:
|
||||||
|
if p.stem == 'src':
|
||||||
|
break
|
||||||
|
elif p == home:
|
||||||
|
break
|
||||||
|
|
||||||
|
success = p != Path.home()
|
||||||
|
if success:
|
||||||
|
bin_path = p.with_name('bin') / 'evaluate.py'
|
||||||
|
success = bin_path.exists()
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
logger.warning('Location of evaluate.py could not be determined')
|
||||||
|
return False
|
||||||
|
|
||||||
|
create_desktop_file(bin_path)
|
||||||
|
|
||||||
|
|
||||||
|
def create_desktop_file(new_path: Path):
|
||||||
|
logo_path = config_paths() / 'logo.png'
|
||||||
|
if not logo_path.exists():
|
||||||
|
with resource_path('resources', 'logo.png') as fp:
|
||||||
|
copyfile(fp, logo_path)
|
||||||
|
desktop_entry = f"""\
|
||||||
|
[Desktop Entry]
|
||||||
|
Name=NMReval
|
||||||
|
Comment=Best program ever (maybe)
|
||||||
|
Exec={new_path}
|
||||||
|
Icon={logo_path}
|
||||||
|
Type=Application
|
||||||
|
Terminal=false
|
||||||
|
Categories=Science;NumericalAnalysis;Physics;DataVisualization;Other;
|
||||||
|
NoDisplay=false
|
||||||
|
"""
|
||||||
|
file_name = 'pkm.vogel.nmreval.desktop'
|
||||||
|
with Path('~/.local/share/applications/', file_name).expanduser().open('w') as f:
|
||||||
|
f.write(desktop_entry)
|
||||||
|
|
||||||
|
desktop_dir = get_xkg_user_dirs('desktop')
|
||||||
|
if desktop_dir is not None:
|
||||||
|
desk_file = Path(desktop_dir, file_name)
|
||||||
|
with desk_file.open('w') as f:
|
||||||
|
f.write(desktop_entry)
|
||||||
|
|
||||||
|
desk_file.chmod(0o755)
|
||||||
|
|
||||||
|
|
||||||
|
def get_xkg_user_dirs(dir_type: str) -> str | None:
|
||||||
|
xdg_conf_home = os.getenv('XDG_CONFIG_HOME') or str(Path.home() / '.config')
|
||||||
|
|
||||||
|
with Path(xdg_conf_home, 'user-dirs.dirs').open('r') as f:
|
||||||
|
conf_string = '[XDG_USER_DIRS]\n' + f.read()
|
||||||
|
conf_string = re.sub(r'\$HOME', str(Path.home()), conf_string)
|
||||||
|
conf_string = re.sub('"', '', conf_string)
|
||||||
|
|
||||||
|
config = ConfigParser()
|
||||||
|
config.read_string(conf_string)
|
||||||
|
|
||||||
|
return config['XDG_USER_DIRS'].get(f'xdg_{dir_type}_dir')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
make_starter()
|
@ -6,7 +6,7 @@ import numpy as np
|
|||||||
from ..Qt import QtWidgets, QtCore, QtGui
|
from ..Qt import QtWidgets, QtCore, QtGui
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['Game']
|
__all__ = ['Game', 'QMines']
|
||||||
|
|
||||||
|
|
||||||
class Game(QtWidgets.QDialog):
|
class Game(QtWidgets.QDialog):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from . import HAS_IMPORTLIB_RESOURCE
|
from .iconloading import HAS_IMPORTLIB_RESOURCE
|
||||||
from ..Qt import QtGui, QtWidgets, QtCore
|
from ..Qt import QtGui, QtWidgets, QtCore
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,3 +29,16 @@ class TreeWidget(QtWidgets.QTreeWidget):
|
|||||||
it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked)
|
it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked)
|
||||||
else:
|
else:
|
||||||
super().keyPressEvent(evt)
|
super().keyPressEvent(evt)
|
||||||
|
|
||||||
|
|
||||||
|
class TableWidget(QtWidgets.QTableWidget):
|
||||||
|
def keyPressEvent(self, evt: QtGui.QKeyEvent):
|
||||||
|
if evt.key() == QtCore.Qt.Key.Key_Space:
|
||||||
|
for idx in self.selectedIndexes():
|
||||||
|
item = self.itemFromIndex(idx)
|
||||||
|
cs = item.checkState()
|
||||||
|
item.setCheckState(QtCore.Qt.CheckState.Unchecked if cs == QtCore.Qt.CheckState.Checked
|
||||||
|
else QtCore.Qt.CheckState.Checked)
|
||||||
|
else:
|
||||||
|
super().keyPressEvent(evt)
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from numpy import argsort
|
from numpy import argsort
|
||||||
|
|
||||||
|
from . import Relations
|
||||||
from ..Qt import QtWidgets, QtCore
|
from ..Qt import QtWidgets, QtCore
|
||||||
from ..data.container import FitContainer
|
from ..data.container import FitContainer
|
||||||
from ..graphs.graphwindow import QGraphWindow
|
from ..graphs.graphwindow import QGraphWindow
|
||||||
@ -84,6 +87,21 @@ class ShiftCommand(QtWidgets.QUndoCommand):
|
|||||||
self.__data.apply('ls', self.__args)
|
self.__data.apply('ls', self.__args)
|
||||||
|
|
||||||
|
|
||||||
|
class EditCommand(QtWidgets.QUndoCommand):
|
||||||
|
def __init__(self, data, *args):
|
||||||
|
super().__init__('Edit signal')
|
||||||
|
|
||||||
|
self.__data = data
|
||||||
|
self.__arguments = args
|
||||||
|
self.__original = copy.deepcopy(self.__data.data)
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self.__data.data = copy.deepcopy(self.__original)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self.__data.edit_signal(*self.__arguments)
|
||||||
|
|
||||||
|
|
||||||
class NormCommand(QtWidgets.QUndoCommand):
|
class NormCommand(QtWidgets.QUndoCommand):
|
||||||
def __init__(self, data, mode):
|
def __init__(self, data, mode):
|
||||||
super().__init__('Normalize')
|
super().__init__('Normalize')
|
||||||
@ -216,33 +234,69 @@ class DeleteGraphCommand(QtWidgets.QUndoCommand):
|
|||||||
|
|
||||||
|
|
||||||
class DeleteCommand(QtWidgets.QUndoCommand):
|
class DeleteCommand(QtWidgets.QUndoCommand):
|
||||||
def __init__(self, container, key, signal1, signal2):
|
def __init__(self, container: dict, keys: list[str], graphs: dict, graphid: str,
|
||||||
|
signal1: QtCore.pyqtSignal, signal2: QtCore.pyqtSignal):
|
||||||
super().__init__('Delete data')
|
super().__init__('Delete data')
|
||||||
|
|
||||||
self.__container = container
|
self.__container = container
|
||||||
self.__value = self.__container[key]
|
self.__graph_container = graphs
|
||||||
self.__key = key
|
self.__graph_key = graphid
|
||||||
|
self.__value = {}
|
||||||
|
for k in keys:
|
||||||
|
self.__value[k] = self.__container[k]
|
||||||
|
self.__keys = tuple(keys)
|
||||||
self.__signal_add = signal1
|
self.__signal_add = signal1
|
||||||
self.__signal_remove = signal2
|
self.__signal_remove = signal2
|
||||||
|
|
||||||
def redo(self):
|
def redo(self):
|
||||||
self.__signal_remove.emit(self.__key)
|
# stop graph from rescaling and updating legend
|
||||||
if isinstance(self.__value, FitContainer):
|
self.__graph_container[self.__graph_key].block(True)
|
||||||
|
|
||||||
|
self.__signal_remove.emit(list(self.__keys[::-1]))
|
||||||
|
for sid in self.__keys[::-1]:
|
||||||
|
val = self.__value[sid]
|
||||||
|
|
||||||
|
if isinstance(val, FitContainer):
|
||||||
try:
|
try:
|
||||||
self.__container[self.__value.fitted_key]._fits.remove(self.__key)
|
self.__container[val.fitted_key]._fits.remove(sid)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
del self.__container[self.__key]
|
|
||||||
|
for (flag1, flag2) in ((Relations.isFitPartOf, Relations.hasFitPart), (Relations.hasFitPart, Relations.isFitPartOf)):
|
||||||
|
for related_item in val.get_relation(flag1):
|
||||||
|
try:
|
||||||
|
self.__container[related_item].remove_relation(flag2, sid)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
del self.__container[sid]
|
||||||
|
|
||||||
|
self.__graph_container[self.__graph_key].block(False)
|
||||||
|
|
||||||
def undo(self):
|
def undo(self):
|
||||||
self.__container[self.__key] = self.__value
|
# stop graph from rescaling and updating legend
|
||||||
if isinstance(self.__value, FitContainer):
|
self.__graph_container[self.__graph_key].block(True)
|
||||||
|
|
||||||
|
for sid in self.__keys:
|
||||||
|
val = self.__value[sid]
|
||||||
|
self.__container[sid] = val
|
||||||
|
|
||||||
|
if isinstance(val, FitContainer):
|
||||||
try:
|
try:
|
||||||
self.__container[self.__value.fitted_key]._fits.append(self.__key)
|
self.__container[val.fitted_key]._fits.append(sid)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.__signal_add.emit([self.__key], self.__value.graph)
|
for (flag1, flag2) in (('isFitPartOf', 'hasFitPartOf'), ('hasFitPartOf', 'isFitPartOf')):
|
||||||
|
for related_item in val.get_relation(flag1):
|
||||||
|
try:
|
||||||
|
self.__container[related_item].add_relation(flag2, sid)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.__signal_add.emit(list(self.__keys), self.__graph_key)
|
||||||
|
|
||||||
|
self.__graph_container[self.__graph_key].block(False)
|
||||||
|
|
||||||
|
|
||||||
class EvalCommand(QtWidgets.QUndoCommand):
|
class EvalCommand(QtWidgets.QUndoCommand):
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import sys
|
import os
|
||||||
from functools import lru_cache
|
import urllib.request
|
||||||
from os import getenv, stat
|
from os import getenv, stat
|
||||||
from os.path import exists
|
from os.path import exists
|
||||||
import hashlib
|
import hashlib
|
||||||
@ -9,8 +9,7 @@ import subprocess
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from urllib.error import HTTPError
|
||||||
import requests
|
|
||||||
from numpy import linspace
|
from numpy import linspace
|
||||||
from scipy.interpolate import interp1d
|
from scipy.interpolate import interp1d
|
||||||
|
|
||||||
@ -59,13 +58,13 @@ class RdBuCMap:
|
|||||||
elif val < self.min:
|
elif val < self.min:
|
||||||
col = QtGui.QColor.fromRgb(*RdBuCMap._rdbu[-1])
|
col = QtGui.QColor.fromRgb(*RdBuCMap._rdbu[-1])
|
||||||
else:
|
else:
|
||||||
col = QtGui.QColor.fromRgb(*(float(self.spline[i](val)) for i in range(3)))
|
col = QtGui.QColor.fromRgb(*(int(self.spline[i](val)) for i in range(3)))
|
||||||
|
|
||||||
return col
|
return col
|
||||||
|
|
||||||
|
|
||||||
class UpdateDialog(QtWidgets.QDialog):
|
class UpdateDialog(QtWidgets.QDialog):
|
||||||
startDownload = QtCore.pyqtSignal(list)
|
startDownload = QtCore.pyqtSignal(tuple)
|
||||||
|
|
||||||
def __init__(self, filename: str = None, parent=None):
|
def __init__(self, filename: str = None, parent=None):
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
@ -75,7 +74,6 @@ class UpdateDialog(QtWidgets.QDialog):
|
|||||||
filename = getenv('APPIMAGE')
|
filename = getenv('APPIMAGE')
|
||||||
self._appfile = filename
|
self._appfile = filename
|
||||||
|
|
||||||
self.success = False
|
|
||||||
self.updater = Updater()
|
self.updater = Updater()
|
||||||
|
|
||||||
self.thread = QtCore.QThread(self)
|
self.thread = QtCore.QThread(self)
|
||||||
@ -116,12 +114,22 @@ class UpdateDialog(QtWidgets.QDialog):
|
|||||||
# Download zsync file of latest Appimage, look for SHA-1 hash and compare with hash of AppImage
|
# Download zsync file of latest Appimage, look for SHA-1 hash and compare with hash of AppImage
|
||||||
is_updateble, m_time_file, m_time_zsync = self.updater.get_update_information(filename)
|
is_updateble, m_time_file, m_time_zsync = self.updater.get_update_information(filename)
|
||||||
|
|
||||||
if m_time_zsync is None:
|
label_text = ''
|
||||||
label_text = '<p>Retrieval of version information failed.</p>' \
|
|
||||||
'<p>Please try later (or complain to people that it does not work).</p>'
|
if is_updateble is None:
|
||||||
|
label_text += '<p>Could not determine if this version is newer, please update manually (if necessary).</p>'
|
||||||
dialog_bttns = QtWidgets.QDialogButtonBox.Close
|
dialog_bttns = QtWidgets.QDialogButtonBox.Close
|
||||||
|
elif is_updateble:
|
||||||
|
label_text += '<p>Newer version available. Press Ok to download new version, Cancel to ignore.</p>'
|
||||||
|
dialog_bttns = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
|
||||||
else:
|
else:
|
||||||
label_text = f'<p>Date of most recent AppImage: {m_time_zsync.strftime("%d %B %Y %H:%M")}</p>'
|
label_text += '<p>Version may be already up-to-date.</p>'
|
||||||
|
dialog_bttns = QtWidgets.QDialogButtonBox.Close
|
||||||
|
|
||||||
|
if m_time_zsync is None:
|
||||||
|
label_text += '<p>Creation date of remote version is unknown.</p>'
|
||||||
|
else:
|
||||||
|
label_text += f'<p>Date of most recent AppImage: {m_time_zsync.strftime("%d %B %Y %H:%M")}</p>'
|
||||||
|
|
||||||
if m_time_file is None:
|
if m_time_file is None:
|
||||||
label_text += 'No AppImage file found, press Ok to download latest version.'
|
label_text += 'No AppImage file found, press Ok to download latest version.'
|
||||||
@ -129,40 +137,26 @@ class UpdateDialog(QtWidgets.QDialog):
|
|||||||
else:
|
else:
|
||||||
label_text += f'<p>Date of used AppImage: {m_time_file.strftime("%d %B %Y %H:%M")}</p>'
|
label_text += f'<p>Date of used AppImage: {m_time_file.strftime("%d %B %Y %H:%M")}</p>'
|
||||||
|
|
||||||
if is_updateble is None:
|
|
||||||
self.status.setText('Could not determine if this version is newer, please update manually (if necessary).')
|
|
||||||
dialog_bttns = QtWidgets.QDialogButtonBox.Close
|
|
||||||
elif is_updateble:
|
|
||||||
self.status.setText(f'<p>Newer version available. Press Ok to download new version, Cancel to ignore.')
|
|
||||||
dialog_bttns = QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel
|
|
||||||
else:
|
|
||||||
self.status.setText(f'Version may be already up-to-date.')
|
|
||||||
dialog_bttns = QtWidgets.QDialogButtonBox.Close
|
|
||||||
|
|
||||||
self.label.setText(label_text)
|
self.label.setText(label_text)
|
||||||
self.dialog_button.setStandardButtons(dialog_bttns)
|
self.dialog_button.setStandardButtons(dialog_bttns)
|
||||||
|
|
||||||
@QtCore.pyqtSlot()
|
@QtCore.pyqtSlot()
|
||||||
def update_appimage(self):
|
def update_appimage(self):
|
||||||
if self._appfile is None:
|
if self._appfile is None:
|
||||||
args = [self.updater.zsync_url]
|
args = (self.updater.zsync_url,)
|
||||||
else:
|
else:
|
||||||
# this breaks the download for some reason
|
# this breaks the download for some reason
|
||||||
args = ['-i', self._appfile, self.updater.zsync_url]
|
args = (self.updater.zsync_url, self._appfile)
|
||||||
|
|
||||||
args = [self.updater.zsync_url]
|
|
||||||
|
|
||||||
self.dialog_button.setEnabled(False)
|
self.dialog_button.setEnabled(False)
|
||||||
|
|
||||||
self.startDownload.emit(args)
|
self.startDownload.emit(args)
|
||||||
self.status.show()
|
self.status.show()
|
||||||
|
|
||||||
@QtCore.pyqtSlot(int)
|
@QtCore.pyqtSlot(int, str)
|
||||||
def finish_update(self, retcode: int):
|
def finish_update(self, retcode: int, file_loc: str):
|
||||||
# print('finished with', retcode)
|
|
||||||
self.success = retcode == 0
|
|
||||||
if retcode == 0:
|
if retcode == 0:
|
||||||
self.status.setText('Download complete.')
|
self.status.setText(f'Download complete.New AppImage lies in <p><em>{file_loc}</em>.</p>')
|
||||||
else:
|
else:
|
||||||
self.status.setText(f'Download failed :( with return code {retcode}.')
|
self.status.setText(f'Download failed :( with return code {retcode}.')
|
||||||
self.dialog_button.setStandardButtons(QtWidgets.QDialogButtonBox.Close)
|
self.dialog_button.setStandardButtons(QtWidgets.QDialogButtonBox.Close)
|
||||||
@ -172,76 +166,85 @@ class UpdateDialog(QtWidgets.QDialog):
|
|||||||
self.thread.quit()
|
self.thread.quit()
|
||||||
self.thread.wait()
|
self.thread.wait()
|
||||||
|
|
||||||
if self.success:
|
|
||||||
appname = self.updater.get_zsync()[2]
|
|
||||||
if self._appfile is not None:
|
|
||||||
appimage_path = appname
|
|
||||||
old_version = Path(self._appfile).rename(self._appfile+'.old')
|
|
||||||
appimage_path = Path(appimage_path).replace(self._appfile)
|
|
||||||
else:
|
|
||||||
appimage_path = Path().cwd() / appname
|
|
||||||
# rename to version-agnostic name
|
|
||||||
appimage_path = appimage_path.rename('NMReval.AppImage')
|
|
||||||
appimage_path.chmod(appimage_path.stat().st_mode | 73) # 73 = 0o111 = a+x
|
|
||||||
|
|
||||||
_ = QtWidgets.QMessageBox.information(self, 'Complete',
|
|
||||||
f'New AppImage available at<br>{appimage_path}')
|
|
||||||
|
|
||||||
super().closeEvent(evt)
|
super().closeEvent(evt)
|
||||||
|
|
||||||
|
|
||||||
class Downloader(QtCore.QObject):
|
class Downloader(QtCore.QObject):
|
||||||
started = QtCore.pyqtSignal()
|
started = QtCore.pyqtSignal()
|
||||||
finished = QtCore.pyqtSignal(int)
|
finished = QtCore.pyqtSignal(int, str)
|
||||||
progressChanged = QtCore.pyqtSignal(str)
|
progressChanged = QtCore.pyqtSignal(str)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(list)
|
@QtCore.pyqtSlot(tuple)
|
||||||
def run_download(self, args: list[str]):
|
def run_download(self, args: tuple[str]):
|
||||||
logger.info(f'Download with args {args}')
|
status = 0
|
||||||
process = subprocess.Popen(['zsync'] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1, universal_newlines=True)
|
appimage_location = args[0][:-6]
|
||||||
while True:
|
logger.info(f'Download {appimage_location}')
|
||||||
nextline = process.stdout.readline().strip()
|
if len(args) == 2:
|
||||||
if nextline:
|
new_file = Path(args[1])
|
||||||
self.progressChanged.emit(nextline)
|
else:
|
||||||
|
new_file = Path.home() / 'Downloads' / 'NMReval-latest-x86_64.AppImage'
|
||||||
|
|
||||||
# line = process.stderr.readline().strip()
|
if new_file.exists():
|
||||||
|
os.rename(new_file, new_file.with_suffix('.AppImage.old'))
|
||||||
|
|
||||||
if process.poll() is not None:
|
try:
|
||||||
break
|
with urllib.request.urlopen(appimage_location) as response:
|
||||||
|
with new_file.open('wb') as f:
|
||||||
|
f.write(response.read())
|
||||||
|
|
||||||
self.finished.emit(process.returncode)
|
new_file.chmod(0o755)
|
||||||
|
except HTTPError as e:
|
||||||
|
logger.exception(f'Download failed with {e}')
|
||||||
|
status = 3
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(f'Download failed with {e.args}')
|
||||||
|
status = 1
|
||||||
|
|
||||||
|
if status != 0:
|
||||||
|
logger.warning('Download failed, restore previous AppImage')
|
||||||
|
try:
|
||||||
|
os.rename(new_file.with_suffix('.AppImage.old'), new_file)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
# zsync does not support https
|
||||||
|
|
||||||
|
self.finished.emit(status, str(new_file))
|
||||||
|
|
||||||
|
|
||||||
class Updater:
|
class Updater:
|
||||||
host = 'mirror.infra.pkm'
|
host = 'gitea.pkm.physik.tu-darmstadt.de/api/packages/IPKM/generic/NMReval/latest/'
|
||||||
bucket = 'nmreval'
|
|
||||||
version = 'NMReval-latest-x86_64'
|
version = 'NMReval-latest-x86_64'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def zsync_url(self):
|
def zsync_url(self):
|
||||||
return f'http://{Updater.host}/{Updater.bucket}/{Updater.version}.AppImage.zsync'
|
return f'https://{Updater.host}/{Updater.version}.AppImage.zsync'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@lru_cache(3)
|
|
||||||
def get_zsync():
|
def get_zsync():
|
||||||
url_zsync = f'http://{Updater.host}/{Updater.bucket}/{Updater.version}.AppImage.zsync'
|
url_zsync = f'https://{Updater.host}/{Updater.version}.AppImage.zsync'
|
||||||
m_time_zsync = None
|
m_time_zsync = None
|
||||||
checksum_zsync = None
|
checksum_zsync = None
|
||||||
zsync_file = None
|
zsync_file = None
|
||||||
filename = None
|
filename = None
|
||||||
try:
|
try:
|
||||||
response = requests.get(url_zsync)
|
with urllib.request.urlopen(url_zsync) as response:
|
||||||
if response.status_code == requests.codes['\o/']:
|
zsync_file = response.read()
|
||||||
zsync_file = response.content
|
except HTTPError as e:
|
||||||
|
logger.error(f'Request for zsync returned code {e}')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
logger.exception(f'Download of zsync failed with exception {e.args}')
|
||||||
|
|
||||||
if zsync_file is not None:
|
if zsync_file is not None:
|
||||||
for line in zsync_file.split(b'\n'):
|
for line in zsync_file.split(b'\n'):
|
||||||
try:
|
try:
|
||||||
kw, val = line.split(b': ')
|
kw, val = line.split(b': ')
|
||||||
|
time_string = str(val, encoding='utf-8')
|
||||||
|
time_format = '%a, %d %b %Y %H:%M:%S %z'
|
||||||
if kw == b'MTime':
|
if kw == b'MTime':
|
||||||
m_time_zsync = datetime.strptime(str(val, encoding='utf-8'), '%a, %d %b %Y %H:%M:%S %z').astimezone(None)
|
try:
|
||||||
|
m_time_zsync = datetime.strptime(time_string, time_format).astimezone(None)
|
||||||
|
except ValueError:
|
||||||
|
logger.warning(f'zsync time "{time_string}" does not match "{time_format}"')
|
||||||
elif kw == b'SHA-1':
|
elif kw == b'SHA-1':
|
||||||
checksum_zsync = str(val, encoding='utf-8')
|
checksum_zsync = str(val, encoding='utf-8')
|
||||||
elif kw == b'Filename':
|
elif kw == b'Filename':
|
||||||
@ -265,6 +268,8 @@ class Updater:
|
|||||||
m_time_file = datetime.fromtimestamp(stat_mtime).replace(microsecond=0)
|
m_time_file = datetime.fromtimestamp(stat_mtime).replace(microsecond=0)
|
||||||
with open(filename, 'rb') as f:
|
with open(filename, 'rb') as f:
|
||||||
checksum_file = hashlib.sha1(f.read()).hexdigest()
|
checksum_file = hashlib.sha1(f.read()).hexdigest()
|
||||||
|
if checksum_file is None:
|
||||||
|
logger.warning('No checksum for AppImage calculated')
|
||||||
|
|
||||||
return m_time_file, checksum_file
|
return m_time_file, checksum_file
|
||||||
|
|
||||||
@ -273,30 +278,10 @@ class Updater:
|
|||||||
m_time_zsync, checksum_zsync, appname = Updater.get_zsync()
|
m_time_zsync, checksum_zsync, appname = Updater.get_zsync()
|
||||||
m_time_file, checksum_file = Updater.get_appimage_info(filename)
|
m_time_file, checksum_file = Updater.get_appimage_info(filename)
|
||||||
|
|
||||||
logger.info(f'zsync information {m_time_zsync}, {checksum_zsync}, {appname}')
|
logger.debug(f'zsync information {m_time_zsync}, {checksum_zsync}, {appname}')
|
||||||
logger.info(f'file information {m_time_file}, {checksum_zsync}')
|
logger.debug(f'file information {m_time_file}, {checksum_file}')
|
||||||
|
|
||||||
if not ((checksum_file is not None) and (checksum_zsync is not None)):
|
if not ((checksum_file is not None) and (checksum_zsync is not None)):
|
||||||
return None, m_time_file, m_time_zsync
|
return None, m_time_file, m_time_zsync
|
||||||
else:
|
else:
|
||||||
return checksum_file != checksum_zsync, m_time_file, m_time_zsync
|
return checksum_file != checksum_zsync, m_time_file, m_time_zsync
|
||||||
|
|
||||||
|
|
||||||
def open_bug_report():
|
|
||||||
form_entries = {
|
|
||||||
'description': 'Please state the nature of the medical emergency.',
|
|
||||||
'title': 'Everything is awesome?',
|
|
||||||
'assign[0]': 'dominik',
|
|
||||||
'subscribers[0]': 'dominik',
|
|
||||||
'tag': 'nmreval',
|
|
||||||
'priority': 'normal',
|
|
||||||
'status': 'open',
|
|
||||||
}
|
|
||||||
full_url = 'https://chaos3.fkp.physik.tu-darmstadt.de/maniphest/task/edit/?'
|
|
||||||
|
|
||||||
for k, v in form_entries.items():
|
|
||||||
full_url += f'{k}={v}&'
|
|
||||||
full_url.replace(' ', '+')
|
|
||||||
|
|
||||||
import webbrowser
|
|
||||||
webbrowser.open(full_url)
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime
|
||||||
import os
|
import os
|
||||||
import pathlib
|
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -9,18 +9,23 @@ from numpy import geomspace, linspace
|
|||||||
from pyqtgraph import ViewBox
|
from pyqtgraph import ViewBox
|
||||||
|
|
||||||
from nmreval.configs import *
|
from nmreval.configs import *
|
||||||
|
from nmreval.lib.logger import logger
|
||||||
|
from nmreval.io.sessionwriter import NMRWriter
|
||||||
|
|
||||||
from .management import UpperManagement
|
from .management import UpperManagement
|
||||||
from ..Qt import QtCore, QtGui, QtPrintSupport, QtWidgets
|
from ..Qt import QtGui, QtPrintSupport
|
||||||
from ..data.shift_graphs import QShift
|
from ..data.shift_graphs import QShift
|
||||||
from ..data.signaledit import QApodDialog, QBaselineDialog, QPhasedialog
|
from ..data.signaledit import QPreviewDialog, QBaselineDialog
|
||||||
from ..fit.result import QFitResult
|
from ..dsc.glass_dialog import TgCalculator
|
||||||
|
from ..fit.result import FitExtension, QFitResult
|
||||||
from ..graphs.graphwindow import QGraphWindow
|
from ..graphs.graphwindow import QGraphWindow
|
||||||
from ..graphs.movedialog import QMover
|
from ..graphs.movedialog import QMover
|
||||||
from ..io.fcbatchreader import QFCReader
|
from ..io.fcbatchreader import QFCReader
|
||||||
from ..io.filedialog import *
|
from ..io.filedialog import *
|
||||||
from ..lib import get_icon, make_action_icons
|
from ..lib.iconloading import make_action_icons, get_icon
|
||||||
from ..lib.pg_objects import RegionItem
|
from ..lib.pg_objects import RegionItem
|
||||||
|
from ..lib.starter import make_starter
|
||||||
|
from ..math.binning import BinningWindow
|
||||||
from ..math.evaluation import QEvalDialog
|
from ..math.evaluation import QEvalDialog
|
||||||
from ..math.interpol import InterpolDialog
|
from ..math.interpol import InterpolDialog
|
||||||
from ..math.mean_dialog import QMeanTimes
|
from ..math.mean_dialog import QMeanTimes
|
||||||
@ -28,7 +33,7 @@ from ..math.smooth import QSmooth
|
|||||||
from ..nmr.coupling_calc import QCoupCalcDialog
|
from ..nmr.coupling_calc import QCoupCalcDialog
|
||||||
from ..nmr.t1_from_tau import QRelaxCalc
|
from ..nmr.t1_from_tau import QRelaxCalc
|
||||||
from .._py.basewindow import Ui_BaseWindow
|
from .._py.basewindow import Ui_BaseWindow
|
||||||
from ..lib.utils import UpdateDialog, open_bug_report, Updater
|
from ..lib.utils import UpdateDialog, Updater
|
||||||
|
|
||||||
|
|
||||||
class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
||||||
@ -53,6 +58,8 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.fitpreview = []
|
self.fitpreview = []
|
||||||
self._fit_plot_id = None
|
self._fit_plot_id = None
|
||||||
self.savefitdialog = None
|
self.savefitdialog = None
|
||||||
|
self._tg_dialog = None
|
||||||
|
self.fitresult_dialog = None
|
||||||
self.eval = None
|
self.eval = None
|
||||||
self.editor = None
|
self.editor = None
|
||||||
|
|
||||||
@ -63,7 +70,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self._block_window_change = False
|
self._block_window_change = False
|
||||||
|
|
||||||
self.fname = None
|
self.fname = None
|
||||||
self.tim = QtCore.QTimer()
|
|
||||||
|
|
||||||
self.settings = QtCore.QSettings('NMREVal', 'settings')
|
self.settings = QtCore.QSettings('NMREVal', 'settings')
|
||||||
self._init_gui()
|
self._init_gui()
|
||||||
@ -73,6 +79,18 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
if Updater.get_update_information(os.getenv('APPIMAGE'))[0]:
|
if Updater.get_update_information(os.getenv('APPIMAGE'))[0]:
|
||||||
self.look_for_update()
|
self.look_for_update()
|
||||||
|
|
||||||
|
self.check_for_backup()
|
||||||
|
|
||||||
|
self.__timer = QtCore.QTimer()
|
||||||
|
self.__backup_path = config_paths() / f'{datetime.datetime.now().strftime("%Y-%m-%d_%H%M%S")}.nmr'
|
||||||
|
self.__timer.start(3*60*1000) # every three minutes
|
||||||
|
self.__timer.timeout.connect(self._autosave)
|
||||||
|
|
||||||
|
self.fit_timer = QtCore.QTimer()
|
||||||
|
self.fit_timer.setInterval(500)
|
||||||
|
self.fit_timer.timeout.connect(
|
||||||
|
lambda: self.status.setText(f'Fit running... ({self.management.fitter.step} evaluations)'))
|
||||||
|
|
||||||
def _init_gui(self):
|
def _init_gui(self):
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
make_action_icons(self)
|
make_action_icons(self)
|
||||||
@ -103,7 +121,10 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
|
|
||||||
self.mousepos = QtWidgets.QLabel('')
|
self.mousepos = QtWidgets.QLabel('')
|
||||||
self.status = QtWidgets.QLabel('')
|
self.status = QtWidgets.QLabel('')
|
||||||
|
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
self.statusBar.addWidget(self.status)
|
self.statusBar.addWidget(self.status)
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
self.statusBar.addWidget(self.mousepos)
|
self.statusBar.addWidget(self.mousepos)
|
||||||
|
|
||||||
self.fitregion = RegionItem()
|
self.fitregion = RegionItem()
|
||||||
@ -114,7 +135,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
|
|
||||||
self.datawidget.management = self.management
|
self.datawidget.management = self.management
|
||||||
self.integralwidget.management = self.management
|
self.integralwidget.management = self.management
|
||||||
self.drawingswidget.graphs = self.management.graphs
|
# self.drawingswidget.graphs = self.management.graphs
|
||||||
|
|
||||||
self.ac_group = QtWidgets.QActionGroup(self)
|
self.ac_group = QtWidgets.QActionGroup(self)
|
||||||
self.ac_group.addAction(self.action_lm_fit)
|
self.ac_group.addAction(self.action_lm_fit)
|
||||||
@ -139,20 +160,23 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.actionUndo.setIcon(icon)
|
self.actionUndo.setIcon(icon)
|
||||||
self.menuData.insertAction(self.actionRedo, self.actionUndo)
|
self.menuData.insertAction(self.actionRedo, self.actionUndo)
|
||||||
|
|
||||||
# # self.actionSave.triggered.connect(lambda: self.management.save('/autohome/dominik/nmreval/testdata/test.nmr', ''))
|
|
||||||
# self.actionSave.triggered.connect(self.save)
|
|
||||||
self.action_save_fit_parameter.triggered.connect(self.save_fit_parameter)
|
self.action_save_fit_parameter.triggered.connect(self.save_fit_parameter)
|
||||||
self.ac_group2.triggered.connect(self.change_fit_limits)
|
self.ac_group2.triggered.connect(self.change_fit_limits)
|
||||||
|
|
||||||
self.t1action.triggered.connect(lambda: self._show_tab('t1_temp'))
|
self.t1action.triggered.connect(lambda: self._show_tab('t1_temp'))
|
||||||
self.action_edit.triggered.connect(lambda: self._show_tab('signal'))
|
self.action_edit.triggered.connect(self.do_preview)
|
||||||
self.actionPick_position.triggered.connect(lambda: self._show_tab('pick'))
|
self.actionPick_position.triggered.connect(lambda: self._show_tab('pick'))
|
||||||
self.actionIntegration.triggered.connect(lambda: self._show_tab('integrate'))
|
self.actionIntegration.triggered.connect(lambda: self._show_tab('integrate'))
|
||||||
self.action_FitWidget.triggered.connect(lambda: self._show_tab('fit'))
|
self.action_FitWidget.triggered.connect(lambda: self._show_tab('fit'))
|
||||||
self.action_draw_object.triggered.connect(lambda: self._show_tab('drawing'))
|
|
||||||
|
|
||||||
self.action_new_set.triggered.connect(self.management.create_empty)
|
self.action_new_set.triggered.connect(self.management.create_empty)
|
||||||
|
|
||||||
|
self.actionDelete_window.triggered.connect(self.management.delete_sets)
|
||||||
|
self.actionCascade_windows.triggered.connect(self.area.cascadeSubWindows)
|
||||||
|
self.actionTile.triggered.connect(self.area.tileSubWindows)
|
||||||
|
self.actionTileHorizontal.triggered.connect(self.area.tileSubWindowsHorizontally)
|
||||||
|
self.actionTileVertical.triggered.connect(self.area.tileSubWindowsVertically)
|
||||||
|
|
||||||
self.datawidget.keyChanged.connect(self.management.change_keys)
|
self.datawidget.keyChanged.connect(self.management.change_keys)
|
||||||
self.datawidget.tree.deleteItem.connect(self.management.delete_sets)
|
self.datawidget.tree.deleteItem.connect(self.management.delete_sets)
|
||||||
self.datawidget.tree.moveItem.connect(self.management.move_sets)
|
self.datawidget.tree.moveItem.connect(self.management.move_sets)
|
||||||
@ -164,8 +188,10 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.datawidget.startShowProperty.connect(self.management.get_properties)
|
self.datawidget.startShowProperty.connect(self.management.get_properties)
|
||||||
self.datawidget.propertyChanged.connect(self.management.update_property)
|
self.datawidget.propertyChanged.connect(self.management.update_property)
|
||||||
self.datawidget.tree.saveFits.connect(self.save_fit_parameter)
|
self.datawidget.tree.saveFits.connect(self.save_fit_parameter)
|
||||||
|
self.datawidget.tree.extendFits.connect(self.extend_fit)
|
||||||
|
|
||||||
self.management.newData.connect(self.show_new_data)
|
self.management.newData[list, str].connect(self.show_new_data)
|
||||||
|
self.management.newData[list, str, bool].connect(self.show_new_data)
|
||||||
self.management.newGraph.connect(self.new_graph)
|
self.management.newGraph.connect(self.new_graph)
|
||||||
self.management.dataChanged.connect(self.update_data)
|
self.management.dataChanged.connect(self.update_data)
|
||||||
self.management.deleteData.connect(self.delete_data)
|
self.management.deleteData.connect(self.delete_data)
|
||||||
@ -188,7 +214,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.t1tauwidget.newData.connect(self.management.add_new_data)
|
self.t1tauwidget.newData.connect(self.management.add_new_data)
|
||||||
|
|
||||||
self.editsignalwidget.do_something.connect(self.management.apply)
|
self.editsignalwidget.do_something.connect(self.management.apply)
|
||||||
self.editsignalwidget.preview_triggered.connect(self.do_preview)
|
# self.editsignalwidget.preview_triggered.connect(self.do_preview)
|
||||||
|
|
||||||
self.action_sort_pts.triggered.connect(lambda: self.management.apply('sort', ()))
|
self.action_sort_pts.triggered.connect(lambda: self.management.apply('sort', ()))
|
||||||
self.action_calc_eps_derivative.triggered.connect(self.management.bds_deriv)
|
self.action_calc_eps_derivative.triggered.connect(self.management.bds_deriv)
|
||||||
@ -218,6 +244,8 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
|
|
||||||
self.actionConcatenate_sets.triggered.connect(lambda: self.management.cat())
|
self.actionConcatenate_sets.triggered.connect(lambda: self.management.cat())
|
||||||
|
|
||||||
|
self.management.graphs.valueChanged.connect(self.update_graph_list)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(name='on_action_open_triggered')
|
@QtCore.pyqtSlot(name='on_action_open_triggered')
|
||||||
def open(self):
|
def open(self):
|
||||||
filedialog = OpenFileDialog(directory=self.path, caption='Open files',
|
filedialog = OpenFileDialog(directory=self.path, caption='Open files',
|
||||||
@ -241,6 +269,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
@QtCore.pyqtSlot(name='on_actionOpen_FC_triggered')
|
@QtCore.pyqtSlot(name='on_actionOpen_FC_triggered')
|
||||||
def read_fc(self):
|
def read_fc(self):
|
||||||
reader = QFCReader(path=self.path, parent=self)
|
reader = QFCReader(path=self.path, parent=self)
|
||||||
|
reader.add_graphs(self.management.graphs.list())
|
||||||
reader.data_read.connect(self.management.add_new_data)
|
reader.data_read.connect(self.management.add_new_data)
|
||||||
reader.exec()
|
reader.exec()
|
||||||
|
|
||||||
@ -261,10 +290,11 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
selected_filter = save_dialog.selectedNameFilter()
|
selected_filter = save_dialog.selectedNameFilter()
|
||||||
|
|
||||||
if savefile is not None:
|
if savefile is not None:
|
||||||
|
self.path = savefile.parent
|
||||||
use_underscore = save_dialog.checkBox.isChecked()
|
use_underscore = save_dialog.checkBox.isChecked()
|
||||||
self.management.save(savefile, selected_filter, strip_spaces=use_underscore)
|
self.management.save(savefile, selected_filter, strip_spaces=use_underscore)
|
||||||
|
|
||||||
param_outfile = re.sub('[_\s-]?<label>[_\s-]?', '', savefile.stem)
|
param_outfile = re.sub(r'[_\s-]?<label>[_\s-]?', '', savefile.stem)
|
||||||
|
|
||||||
bad_character = r'/*<>\|:"'
|
bad_character = r'/*<>\|:"'
|
||||||
for c in bad_character:
|
for c in bad_character:
|
||||||
@ -306,10 +336,15 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
return w.id
|
return w.id
|
||||||
|
|
||||||
@QtCore.pyqtSlot(list, str)
|
@QtCore.pyqtSlot(list, str)
|
||||||
def show_new_data(self, sets: list, graph: str):
|
@QtCore.pyqtSlot(list, str, bool)
|
||||||
|
def show_new_data(self, sets: list, graph: str, skip_change: bool = False):
|
||||||
if len(sets) == 0:
|
if len(sets) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
prev_graph = ''
|
||||||
|
if skip_change:
|
||||||
|
prev_graph = self.management.current_graph
|
||||||
|
|
||||||
if graph == '':
|
if graph == '':
|
||||||
graph = self.new_graph()
|
graph = self.new_graph()
|
||||||
|
|
||||||
@ -318,22 +353,21 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
for idd in sets:
|
for idd in sets:
|
||||||
new_item = self.management[idd]
|
new_item = self.management[idd]
|
||||||
self.datawidget.blockSignals(True)
|
self.datawidget.blockSignals(True)
|
||||||
self.datawidget.add_item(new_item.id, new_item.name, graph)
|
self.datawidget.add_item(new_item.id, new_item.name, new_item.value, graph, update=False)
|
||||||
self.datawidget.blockSignals(False)
|
self.datawidget.blockSignals(False)
|
||||||
|
self.datawidget.tree.update_indexes()
|
||||||
|
|
||||||
if graph == self.fit_dialog.connected_figure:
|
# if graph == self.fit_dialog.connected_figure:
|
||||||
self.fit_dialog.load(self.management.graphs.active(graph))
|
# self.fit_dialog.load(self.management.graphs.active(graph))
|
||||||
|
if skip_change:
|
||||||
|
self.area.setActiveSubWidget(prev_graph)
|
||||||
|
|
||||||
if self.valuewidget.isVisible():
|
if self.valuewidget.isVisible():
|
||||||
self.valuewidget(self.management.graphs.tree())
|
self.valuewidget(self.management.graphs.tree())
|
||||||
|
|
||||||
@QtCore.pyqtSlot(name='on_actionDelete_window_triggered')
|
|
||||||
def delete_windows(self):
|
|
||||||
self.management.delete_sets()
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot(str)
|
||||||
def remove_graph(self, gid: str):
|
def remove_graph(self, gid: str):
|
||||||
self.datawidget.remove_item(gid)
|
self.datawidget.remove_item([gid])
|
||||||
val_figure = self.valuewidget.connected_figure
|
val_figure = self.valuewidget.connected_figure
|
||||||
self.valuewidget.remove_graph()
|
self.valuewidget.remove_graph()
|
||||||
|
|
||||||
@ -346,10 +380,15 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
if wdgt == self.current_graph_widget:
|
if wdgt == self.current_graph_widget:
|
||||||
if self.ptsselectwidget.connected_figure == gid:
|
if self.ptsselectwidget.connected_figure == gid:
|
||||||
self.ptsselectwidget.connected_figure = None
|
self.ptsselectwidget.connected_figure = None
|
||||||
|
for line in self.ptsselectwidget.pts_lines:
|
||||||
|
self.current_graph_widget.remove_external(line)
|
||||||
|
|
||||||
self.tabWidget.removeTab(self.tabWidget.indexOf(self.ptsselectwidget))
|
self.tabWidget.removeTab(self.tabWidget.indexOf(self.ptsselectwidget))
|
||||||
|
|
||||||
if self.t1tauwidget.connected_figure == gid:
|
if self.t1tauwidget.connected_figure == gid:
|
||||||
self.t1tauwidget.connected_figure = None
|
self.t1tauwidget.connected_figure = None
|
||||||
|
self.current_graph_widget.add_external(self.t1tauwidget.min_pos)
|
||||||
|
self.current_graph_widget.add_external(self.t1tauwidget.parabola)
|
||||||
self.tabWidget.removeTab(self.tabWidget.indexOf(self.t1tauwidget))
|
self.tabWidget.removeTab(self.tabWidget.indexOf(self.t1tauwidget))
|
||||||
|
|
||||||
if self.fit_dialog.connected_figure == gid:
|
if self.fit_dialog.connected_figure == gid:
|
||||||
@ -358,12 +397,14 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.current_graph_widget.remove_external(item)
|
self.current_graph_widget.remove_external(item)
|
||||||
|
|
||||||
if val_figure == gid:
|
if val_figure == gid:
|
||||||
|
self.current_graph_widget.remove_external(self.valuewidget.selection_real)
|
||||||
|
self.current_graph_widget.remove_external(self.valuewidget.selection_imag)
|
||||||
self.tabWidget.setCurrentIndex(0)
|
self.tabWidget.setCurrentIndex(0)
|
||||||
|
|
||||||
self.current_graph_widget.enable_picking(False)
|
self.current_graph_widget.enable_picking(False)
|
||||||
|
|
||||||
self.current_graph_widget = None
|
self.current_graph_widget = None
|
||||||
self.management.current_graph = ''
|
self.management.current_graph = None
|
||||||
self.current_plotitem = None
|
self.current_plotitem = None
|
||||||
|
|
||||||
wdgt.setParent(None)
|
wdgt.setParent(None)
|
||||||
@ -374,7 +415,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
if w is not None:
|
if w is not None:
|
||||||
self.area.removeSubWindow(w)
|
self.area.removeSubWindow(w)
|
||||||
w.close()
|
w.close()
|
||||||
@ -387,6 +427,12 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
if self.area.subWindowList():
|
if self.area.subWindowList():
|
||||||
self.area.activateNextSubWindow()
|
self.area.activateNextSubWindow()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def update_graph_list(self):
|
||||||
|
graph_list = self.management.graphs.list()
|
||||||
|
self.t1tauwidget.set_graphs(graph_list)
|
||||||
|
self.ptsselectwidget.set_graphs(graph_list)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot(str)
|
||||||
def set_graph(self, key: str):
|
def set_graph(self, key: str):
|
||||||
w = self.management.graphs[key]
|
w = self.management.graphs[key]
|
||||||
@ -404,7 +450,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.datawidget.blockSignals(False)
|
self.datawidget.blockSignals(False)
|
||||||
|
|
||||||
w.mousePositionChanged.connect(self.mousemoved)
|
w.mousePositionChanged.connect(self.mousemoved)
|
||||||
w.aboutToClose.connect(self.delete_windows)
|
w.aboutToClose.connect(self.management.delete_sets)
|
||||||
w.positionClicked.connect(self.point_selected)
|
w.positionClicked.connect(self.point_selected)
|
||||||
w.show()
|
w.show()
|
||||||
|
|
||||||
@ -415,7 +461,9 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
@QtCore.pyqtSlot(QtWidgets.QMdiSubWindow, name='on_area_subWindowActivated')
|
@QtCore.pyqtSlot(QtWidgets.QMdiSubWindow, name='on_area_subWindowActivated')
|
||||||
def change_window(self, wd):
|
def change_window(self, wd):
|
||||||
""" Called every time focus moves from or to a subwindow. Returns None if current focus is not on a subwindow"""
|
""" Called every time focus moves from or to a subwindow. Returns None if current focus is not on a subwindow"""
|
||||||
if wd is not None:
|
if wd is None:
|
||||||
|
return
|
||||||
|
|
||||||
if self.current_graph_widget is not None:
|
if self.current_graph_widget is not None:
|
||||||
self.current_graph_widget.closable = True
|
self.current_graph_widget.closable = True
|
||||||
|
|
||||||
@ -444,14 +492,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.datawidget.tree.highlight(self.management.current_graph)
|
self.datawidget.tree.highlight(self.management.current_graph)
|
||||||
self.datawidget.tree.blockSignals(False)
|
self.datawidget.tree.blockSignals(False)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(name='on_actionCascade_windows_triggered')
|
|
||||||
@QtCore.pyqtSlot(name='on_actionTile_triggered')
|
|
||||||
def change_window_size(self):
|
|
||||||
if self.sender() == self.actionCascade_windows:
|
|
||||||
self.area.cascadeSubWindows()
|
|
||||||
elif self.sender() == self.actionTile:
|
|
||||||
self.area.tileSubWindows()
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(name='on_actionChange_datatypes_triggered')
|
@QtCore.pyqtSlot(name='on_actionChange_datatypes_triggered')
|
||||||
def type_change_dialog(self):
|
def type_change_dialog(self):
|
||||||
from ..data.conversion import ConversionDialog
|
from ..data.conversion import ConversionDialog
|
||||||
@ -474,7 +514,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
'signal': (self.editsignalwidget, 'Signals'),
|
'signal': (self.editsignalwidget, 'Signals'),
|
||||||
'pick': (self.ptsselectwidget, 'Pick points'),
|
'pick': (self.ptsselectwidget, 'Pick points'),
|
||||||
'fit': (self.fit_dialog, 'Fit'),
|
'fit': (self.fit_dialog, 'Fit'),
|
||||||
'drawing': (self.drawingswidget, 'Draw'),
|
|
||||||
'integrate': (self.integralwidget, 'Integrate'),
|
'integrate': (self.integralwidget, 'Integrate'),
|
||||||
}[mode]
|
}[mode]
|
||||||
|
|
||||||
@ -510,7 +549,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self._select_valuewidget(widget == self.valuewidget)
|
self._select_valuewidget(widget == self.valuewidget)
|
||||||
pick_required, block_window = self._select_t1tauwidget(widget == self.t1tauwidget, pick_required, block_window)
|
pick_required, block_window = self._select_t1tauwidget(widget == self.t1tauwidget, pick_required, block_window)
|
||||||
block_window = self._select_fitwidget(widget == self.fit_dialog, block_window)
|
block_window = self._select_fitwidget(widget == self.fit_dialog, block_window)
|
||||||
self._select_drawingswidget(widget == self.drawingswidget)
|
|
||||||
pick_required = self._select_integralwidget(widget == self.integralwidget, pick_required, block_window)
|
pick_required = self._select_integralwidget(widget == self.integralwidget, pick_required, block_window)
|
||||||
|
|
||||||
self._set_pick_block(pick_required, block_window)
|
self._set_pick_block(pick_required, block_window)
|
||||||
@ -543,8 +581,9 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.management.graphs[current_graph].add_external(self.valuewidget.selection_imag)
|
self.management.graphs[current_graph].add_external(self.valuewidget.selection_imag)
|
||||||
else:
|
else:
|
||||||
if self.valuewidget.connected_figure is not None:
|
if self.valuewidget.connected_figure is not None:
|
||||||
self.management.graphs[self.valuewidget.connected_figure].remove_external(self.valuewidget.selection_real)
|
conn_fig = self.valuewidget.connected_figure
|
||||||
self.management.graphs[self.valuewidget.connected_figure].remove_external(self.valuewidget.selection_imag)
|
self.management.graphs[conn_fig].remove_external(self.valuewidget.selection_real)
|
||||||
|
self.management.graphs[conn_fig].remove_external(self.valuewidget.selection_imag)
|
||||||
|
|
||||||
def _select_integralwidget(self, onoff: bool, pick_required: bool, block_window: bool) -> tuple[bool, bool]:
|
def _select_integralwidget(self, onoff: bool, pick_required: bool, block_window: bool) -> tuple[bool, bool]:
|
||||||
if self.current_graph_widget is None:
|
if self.current_graph_widget is None:
|
||||||
@ -598,16 +637,9 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
|
|
||||||
return pick_required, block_window
|
return pick_required, block_window
|
||||||
|
|
||||||
def _select_drawingswidget(self, onoff):
|
|
||||||
if onoff:
|
|
||||||
if self.drawingswidget.graphs is None:
|
|
||||||
self.drawingswidget.graphs = self.management.graphs
|
|
||||||
self.drawingswidget.update_tree()
|
|
||||||
else:
|
|
||||||
self.drawingswidget.clear()
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot(str)
|
||||||
def get_data(self, key: str):
|
def get_data(self, key: str):
|
||||||
|
if hasattr(self.sender(), 'set_data'):
|
||||||
self.sender().set_data(self.management[key])
|
self.sender().set_data(self.management[key])
|
||||||
|
|
||||||
@QtCore.pyqtSlot(name='on_actionCalculateT1_triggered')
|
@QtCore.pyqtSlot(name='on_actionCalculateT1_triggered')
|
||||||
@ -682,6 +714,14 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
|
|
||||||
self.eval.exec()
|
self.eval.exec()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(name='on_actionBinning_triggered')
|
||||||
|
def open_binning(self):
|
||||||
|
dialog = BinningWindow(self)
|
||||||
|
res = dialog.exec()
|
||||||
|
if res:
|
||||||
|
digits = float(dialog.spinbox.text())
|
||||||
|
self.management.binning(digits)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(name='on_actionDerivation_triggered')
|
@QtCore.pyqtSlot(name='on_actionDerivation_triggered')
|
||||||
# @QtCore.pyqtSlot(name='on_actionIntegration_triggered')
|
# @QtCore.pyqtSlot(name='on_actionIntegration_triggered')
|
||||||
@QtCore.pyqtSlot(name='on_actionFilon_triggered')
|
@QtCore.pyqtSlot(name='on_actionFilon_triggered')
|
||||||
@ -718,9 +758,10 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
|
|
||||||
self.datawidget.set_name(sid, self.management[sid].name)
|
self.datawidget.set_name(sid, self.management[sid].name)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot(list)
|
||||||
def delete_data(self, sid):
|
def delete_data(self, sid: list[str]):
|
||||||
if self.valuewidget.shown_set == sid:
|
for key in sid:
|
||||||
|
if self.valuewidget.shown_set == key:
|
||||||
self.tabWidget.setCurrentIndex(0)
|
self.tabWidget.setCurrentIndex(0)
|
||||||
|
|
||||||
self.datawidget.remove_item(sid)
|
self.datawidget.remove_item(sid)
|
||||||
@ -743,29 +784,22 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
editor.finished.connect(self.management.apply)
|
editor.finished.connect(self.management.apply)
|
||||||
editor.exec()
|
editor.exec()
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot()
|
||||||
def do_preview(self, mode):
|
def do_preview(self):
|
||||||
|
dialog = QPreviewDialog(self)
|
||||||
|
|
||||||
if mode == 'ap':
|
success = True
|
||||||
dialog = QApodDialog(parent=self)
|
|
||||||
elif mode == 'ph':
|
|
||||||
dialog = QPhasedialog(parent=self)
|
|
||||||
else:
|
|
||||||
raise ValueError('Unknown preview mode %s' % str(mode))
|
|
||||||
|
|
||||||
dialog.setRange(*self.current_graph_widget.ranges, self.current_graph_widget.log)
|
|
||||||
|
|
||||||
for sid in self.current_graph_widget.active:
|
for sid in self.current_graph_widget.active:
|
||||||
data_mode = self.management[sid].mode
|
data_mode = self.management[sid].mode
|
||||||
tobeadded = False
|
if data_mode in ('fid', 'spectrum'):
|
||||||
if (data_mode == 'fid') or (data_mode == 'spectrum' and mode == 'ph'):
|
success = dialog.add_data(self.management[sid].data)
|
||||||
tobeadded = True
|
|
||||||
|
|
||||||
if tobeadded:
|
if not success:
|
||||||
dialog.add_data(*self.management.get_data(sid, xy_only=True))
|
break
|
||||||
|
|
||||||
if dialog.exec() == QtWidgets.QDialog.Accepted:
|
if success and dialog.exec() == QtWidgets.QDialog.Accepted:
|
||||||
self.management.apply(mode, dialog.get_value())
|
self.management.edit_signals(dialog.get_value())
|
||||||
|
|
||||||
@QtCore.pyqtSlot(name='on_actionMove_between_plots_triggered')
|
@QtCore.pyqtSlot(name='on_actionMove_between_plots_triggered')
|
||||||
def move_sets_dialog(self):
|
def move_sets_dialog(self):
|
||||||
@ -816,6 +850,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
ret_val = None
|
ret_val = None
|
||||||
|
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
self.sender().receive_data(ret_val)
|
self.sender().receive_data(ret_val)
|
||||||
|
|
||||||
return ret_val
|
return ret_val
|
||||||
@ -875,12 +910,18 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.action_odr_fit: 'odr'
|
self.action_odr_fit: 'odr'
|
||||||
}[self.ac_group.checkedAction()]
|
}[self.ac_group.checkedAction()]
|
||||||
|
|
||||||
|
fit_is_ready = self.management.prepare_fit(parameter, links, fit_options)
|
||||||
|
if fit_is_ready:
|
||||||
|
self.management.start_fit()
|
||||||
self.fit_dialog.fit_button.setEnabled(False)
|
self.fit_dialog.fit_button.setEnabled(False)
|
||||||
self.management.start_fit(parameter, links, fit_options)
|
self.status.setText('Fit running...'.format(self.management.fitter.step))
|
||||||
|
self.fit_timer.start(500)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(dict, int, bool)
|
@QtCore.pyqtSlot(dict, int, bool)
|
||||||
def show_fit_preview(self, funcs: dict, num: int, show: bool):
|
def show_fit_preview(self, funcs: dict, num: int, show: bool):
|
||||||
if self.fit_dialog.connected_figure is None:
|
if not self.fit_dialog.connected_figure:
|
||||||
|
logger.warning(f'Fit dialog is not connected graph: Fit {self.fit_dialog.connected_figure}, '
|
||||||
|
f'current graph: {self.management.current_graph} ({self.management.current_graph in self.management.graphs})')
|
||||||
return
|
return
|
||||||
|
|
||||||
g = self.management.graphs[self.fit_dialog.connected_figure]
|
g = self.management.graphs[self.fit_dialog.connected_figure]
|
||||||
@ -900,17 +941,24 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
@QtCore.pyqtSlot(list)
|
@QtCore.pyqtSlot(list)
|
||||||
def show_fit_results(self, results: list):
|
def show_fit_results(self, results: list):
|
||||||
self.fit_dialog.fit_button.setEnabled(True)
|
self.fit_dialog.fit_button.setEnabled(True)
|
||||||
|
self.fit_timer.stop()
|
||||||
|
self.status.setText('')
|
||||||
if results:
|
if results:
|
||||||
res_dialog = QFitResult(results, self.management, parent=self)
|
if self.fitresult_dialog is None:
|
||||||
res_dialog.add_graphs(self.management.graphs.list())
|
self.fitresult_dialog = QFitResult(results, self.management, parent=self)
|
||||||
res_dialog.closed.connect(self.accepts_fit)
|
self.fitresult_dialog.add_graphs(self.management.graphs.list())
|
||||||
res_dialog.redoFit.connect(self.management.redo_fits)
|
self.fitresult_dialog.closed.connect(self.accepts_fit)
|
||||||
res_dialog.show()
|
self.fitresult_dialog.redoFit.connect(self.management.redo_fits)
|
||||||
|
else:
|
||||||
|
self.fitresult_dialog(results)
|
||||||
|
self.fitresult_dialog.add_graphs(self.management.graphs.list())
|
||||||
|
self.fitresult_dialog.show()
|
||||||
|
|
||||||
@QtCore.pyqtSlot(dict, list, str, bool, dict)
|
@QtCore.pyqtSlot(dict, list, str, bool, bool, list)
|
||||||
def accepts_fit(self, res: dict, opts: list, param_graph: str, show_fit: bool, parts: dict) -> None:
|
def accepts_fit(self, res: dict, opts: list, param_graph: str,
|
||||||
|
show_fit: bool, parts: bool, extrapolate: list) -> None:
|
||||||
self.fit_dialog.set_parameter(res)
|
self.fit_dialog.set_parameter(res)
|
||||||
self.management.make_fits(res, opts, param_graph, show_fit, parts)
|
self.management.make_fits(res, opts, param_graph, show_fit, parts, extrapolate)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(name='on_actionFunction_editor_triggered')
|
@QtCore.pyqtSlot(name='on_actionFunction_editor_triggered')
|
||||||
def edit_models(self):
|
def edit_models(self):
|
||||||
@ -922,6 +970,15 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.editor.setWindowModality(QtCore.Qt.ApplicationModal)
|
self.editor.setWindowModality(QtCore.Qt.ApplicationModal)
|
||||||
self.editor.show()
|
self.editor.show()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(list)
|
||||||
|
def extend_fit(self, sets: list):
|
||||||
|
w = FitExtension(self)
|
||||||
|
res = w.exec()
|
||||||
|
if res:
|
||||||
|
p = w.values
|
||||||
|
x = linspace(p[0], p[1], num=p[2])
|
||||||
|
self.management.extend_fits(sets, x)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(name='on_action_create_fit_function_triggered')
|
@QtCore.pyqtSlot(name='on_action_create_fit_function_triggered')
|
||||||
def open_fitmodel_wizard(self):
|
def open_fitmodel_wizard(self):
|
||||||
from ..fit.function_creation_dialog import QUserFitCreator
|
from ..fit.function_creation_dialog import QUserFitCreator
|
||||||
@ -931,7 +988,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
|
|
||||||
helper.show()
|
helper.show()
|
||||||
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(name='on_actionShift_triggered')
|
@QtCore.pyqtSlot(name='on_actionShift_triggered')
|
||||||
def shift_dialog(self):
|
def shift_dialog(self):
|
||||||
s = QShift(self)
|
s = QShift(self)
|
||||||
@ -1010,6 +1066,9 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
def close(self):
|
def close(self):
|
||||||
write_state({'History': {'recent path': str(self.path)}})
|
write_state({'History': {'recent path': str(self.path)}})
|
||||||
|
|
||||||
|
# remove backup file when closing
|
||||||
|
self.__backup_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
super().close()
|
super().close()
|
||||||
|
|
||||||
def read_state(self):
|
def read_state(self):
|
||||||
@ -1033,3 +1092,56 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
|
|
||||||
QLog(parent=self).show()
|
QLog(parent=self).show()
|
||||||
|
|
||||||
|
def _autosave(self):
|
||||||
|
# TODO better separate thread may it takes some time to save
|
||||||
|
self.status.setText('Autosave...')
|
||||||
|
success = NMRWriter(self.management.graphs, self.management.data).export(self.__backup_path.with_suffix('.nmr.0'))
|
||||||
|
if success:
|
||||||
|
self.__backup_path.with_suffix('.nmr.0').rename(self.__backup_path)
|
||||||
|
|
||||||
|
self.status.setText('')
|
||||||
|
|
||||||
|
def check_for_backup(self):
|
||||||
|
backups = []
|
||||||
|
for filename in config_paths().glob('*.nmr'):
|
||||||
|
try:
|
||||||
|
backups.append((filename, datetime.datetime.strptime(filename.stem, "%Y-%m-%d_%H%M%S")))
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if backups:
|
||||||
|
backup_by_date = sorted(backups, key=lambda x: x[1])
|
||||||
|
msg = QtWidgets.QMessageBox.information(
|
||||||
|
self, 'Backup found',
|
||||||
|
f'{len(backups)} backup files in directory {backup_by_date[-1][0].parent} found.\n\n'
|
||||||
|
f'Latest backup date: {backup_by_date[-1][1]}\n\n'
|
||||||
|
f'Press Ok to load, Cancel to delete backup, Close to do nothing.',
|
||||||
|
QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Close
|
||||||
|
)
|
||||||
|
|
||||||
|
if msg == QtWidgets.QMessageBox.Ok:
|
||||||
|
self.management.load_files([str(backup_by_date[-1][0])])
|
||||||
|
backup_by_date[-1][0].unlink()
|
||||||
|
elif msg == QtWidgets.QMessageBox.Cancel:
|
||||||
|
backup_by_date[-1][0].unlink()
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(name='on_actionCreate_starter_triggered')
|
||||||
|
def create_starter(self):
|
||||||
|
make_starter(os.getenv('APPIMAGE'))
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(name='on_actionAbout_triggered')
|
||||||
|
def show_version(self):
|
||||||
|
from nmreval.version import __version__
|
||||||
|
QtWidgets.QMessageBox.about(self, 'Version', f'Build date of AppImage: {__version__}')
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(name='on_actionTNMH_model_triggered')
|
||||||
|
@QtCore.pyqtSlot(name='on_actionTNMH_triggered')
|
||||||
|
def show_tg_dialog(self):
|
||||||
|
if self._tg_dialog is None:
|
||||||
|
self._tg_dialog = TgCalculator(self.management, parent=self)
|
||||||
|
self._tg_dialog.newData.connect(self.management.addTg)
|
||||||
|
else:
|
||||||
|
self._tg_dialog()
|
||||||
|
self._tg_dialog.show()
|
||||||
|
@ -4,6 +4,8 @@ import pathlib
|
|||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from nmreval.fit import data as fit_d
|
from nmreval.fit import data as fit_d
|
||||||
from nmreval.fit.model import Model
|
from nmreval.fit.model import Model
|
||||||
from nmreval.fit.result import FitResult
|
from nmreval.fit.result import FitResult
|
||||||
@ -16,6 +18,7 @@ from nmreval.math.smooth import smooth
|
|||||||
from nmreval.nmr.relaxation import Relaxation
|
from nmreval.nmr.relaxation import Relaxation
|
||||||
|
|
||||||
from ..Qt import QtCore, QtWidgets
|
from ..Qt import QtCore, QtWidgets
|
||||||
|
from ..lib import Relations
|
||||||
from ..lib.undos import *
|
from ..lib.undos import *
|
||||||
from ..data.container import *
|
from ..data.container import *
|
||||||
from ..io.filereaders import QFileReader
|
from ..io.filereaders import QFileReader
|
||||||
@ -55,11 +58,18 @@ class GraphDict(OrderedDict):
|
|||||||
def list(self):
|
def list(self):
|
||||||
return [(k, v.title) for k, v in self.items()]
|
return [(k, v.title) for k, v in self.items()]
|
||||||
|
|
||||||
def active(self, key: str):
|
def active(self, key: str, return_val: str = 'both'):
|
||||||
if key:
|
if not key:
|
||||||
return [(self._data[i].id, self._data[i].name) for i in self[key]]
|
|
||||||
else:
|
|
||||||
return []
|
return []
|
||||||
|
else:
|
||||||
|
if return_val == 'both':
|
||||||
|
return [(self._data[i].id, self._data[i].name) for i in self[key]]
|
||||||
|
elif return_val == 'id':
|
||||||
|
return [self._data[i].id for i in self[key]]
|
||||||
|
elif return_val == 'name':
|
||||||
|
return [self._data[i].name for i in self[key]]
|
||||||
|
else:
|
||||||
|
raise ValueError(f'return_val got wrong value {return_val!r}')
|
||||||
|
|
||||||
def current_sets(self, key: str):
|
def current_sets(self, key: str):
|
||||||
if key:
|
if key:
|
||||||
@ -72,8 +82,8 @@ class UpperManagement(QtCore.QObject):
|
|||||||
newGraph = QtCore.pyqtSignal()
|
newGraph = QtCore.pyqtSignal()
|
||||||
restoreGraph = QtCore.pyqtSignal(str)
|
restoreGraph = QtCore.pyqtSignal(str)
|
||||||
deleteGraph = QtCore.pyqtSignal(str)
|
deleteGraph = QtCore.pyqtSignal(str)
|
||||||
newData = QtCore.pyqtSignal(list, str)
|
newData = QtCore.pyqtSignal([list, str], [list, str, bool])
|
||||||
deleteData = QtCore.pyqtSignal(str)
|
deleteData = QtCore.pyqtSignal(list)
|
||||||
dataChanged = QtCore.pyqtSignal(str)
|
dataChanged = QtCore.pyqtSignal(str)
|
||||||
fitFinished = QtCore.pyqtSignal(list)
|
fitFinished = QtCore.pyqtSignal(list)
|
||||||
stopFit = QtCore.pyqtSignal()
|
stopFit = QtCore.pyqtSignal()
|
||||||
@ -108,7 +118,7 @@ class UpperManagement(QtCore.QObject):
|
|||||||
self.counter = 0
|
self.counter = 0
|
||||||
self.data = OrderedDict()
|
self.data = OrderedDict()
|
||||||
self.window = window
|
self.window = window
|
||||||
self.current_graph = ''
|
self.current_graph = None
|
||||||
self.graphs = GraphDict(self.data)
|
self.graphs = GraphDict(self.data)
|
||||||
self.namespace = None
|
self.namespace = None
|
||||||
self.undostack = QtWidgets.QUndoStack()
|
self.undostack = QtWidgets.QUndoStack()
|
||||||
@ -145,6 +155,10 @@ class UpperManagement(QtCore.QObject):
|
|||||||
def active_sets(self):
|
def active_sets(self):
|
||||||
return self.graphs.active(self.current_graph)
|
return self.graphs.active(self.current_graph)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active_id(self):
|
||||||
|
return self.graphs.active(self.current_graph, return_val='id')
|
||||||
|
|
||||||
def get_attributes(self, graph_id: str, attr: str) -> dict[str, Any]:
|
def get_attributes(self, graph_id: str, attr: str) -> dict[str, Any]:
|
||||||
return {self.data[i].id: getattr(self.data[i], attr) for i in self.graphs[graph_id].sets}
|
return {self.data[i].id: getattr(self.data[i], attr) for i in self.graphs[graph_id].sets}
|
||||||
|
|
||||||
@ -178,7 +192,10 @@ class UpperManagement(QtCore.QObject):
|
|||||||
graph.active = active
|
graph.active = active
|
||||||
graph.listWidget.blockSignals(True)
|
graph.listWidget.blockSignals(True)
|
||||||
for i, l in enumerate(g['in_legend']):
|
for i, l in enumerate(g['in_legend']):
|
||||||
graph.listWidget.item(i).setCheckState(l)
|
try:
|
||||||
|
graph.listWidget.item(i).setCheckState(QtCore.Qt.Checked if l else QtCore.Qt.Unchecked)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
graph.listWidget.blockSignals(False)
|
graph.listWidget.blockSignals(False)
|
||||||
|
|
||||||
# set unchecked in tree and hide/show in plot
|
# set unchecked in tree and hide/show in plot
|
||||||
@ -229,9 +246,17 @@ class UpperManagement(QtCore.QObject):
|
|||||||
for k in plotkeys:
|
for k in plotkeys:
|
||||||
self.data[k].graph = gid
|
self.data[k].graph = gid
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot(list)
|
||||||
def plot_from_graph(self, key: str):
|
def plot_from_graph(self, key: list[str]):
|
||||||
self.graphs[self.data[key].graph].remove(key)
|
sort_graph = {}
|
||||||
|
for sid in key:
|
||||||
|
v = self.data[sid].graph
|
||||||
|
if v not in sort_graph:
|
||||||
|
sort_graph[v] = []
|
||||||
|
sort_graph[v].append(sid)
|
||||||
|
|
||||||
|
for gid, sets in sort_graph.items():
|
||||||
|
self.graphs[gid].remove(sets)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(list, str, str)
|
@QtCore.pyqtSlot(list, str, str)
|
||||||
def move_sets(self, sets: list, dest: str, src: (str|list), pos: int = -1):
|
def move_sets(self, sets: list, dest: str, src: (str|list), pos: int = -1):
|
||||||
@ -274,6 +299,7 @@ class UpperManagement(QtCore.QObject):
|
|||||||
|
|
||||||
@QtCore.pyqtSlot(list)
|
@QtCore.pyqtSlot(list)
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot(str)
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
def delete_sets(self, rm_sets: list = None):
|
def delete_sets(self, rm_sets: list = None):
|
||||||
rm_graphs = []
|
rm_graphs = []
|
||||||
|
|
||||||
@ -282,13 +308,26 @@ class UpperManagement(QtCore.QObject):
|
|||||||
|
|
||||||
self.undostack.beginMacro('Delete')
|
self.undostack.beginMacro('Delete')
|
||||||
|
|
||||||
|
rm_set_by_graph = {}
|
||||||
|
|
||||||
for k in rm_sets[::-1]:
|
for k in rm_sets[::-1]:
|
||||||
if k in self.data:
|
if k in self.data:
|
||||||
cmd = DeleteCommand(self.data, k, self.newData, self.deleteData)
|
parent_graph = self.data[k].graph
|
||||||
self.undostack.push(cmd)
|
if parent_graph not in rm_set_by_graph:
|
||||||
else:
|
rm_set_by_graph[parent_graph] = []
|
||||||
|
|
||||||
|
rm_set_by_graph[parent_graph].append(k)
|
||||||
|
|
||||||
|
elif k in self.graphs:
|
||||||
rm_graphs.append(k)
|
rm_graphs.append(k)
|
||||||
|
|
||||||
|
else:
|
||||||
|
logger.warning(f'delete_sets: {k} is not in data or graph found')
|
||||||
|
|
||||||
|
for gid, sid_list in rm_set_by_graph.items():
|
||||||
|
cmd = DeleteCommand(self.data, sid_list, self.graphs, gid, self.newData, self.deleteData)
|
||||||
|
self.undostack.push(cmd)
|
||||||
|
|
||||||
for k in rm_graphs:
|
for k in rm_graphs:
|
||||||
cmd = DeleteGraphCommand(self.graphs, k, self.restoreGraph, self.deleteGraph)
|
cmd = DeleteGraphCommand(self.graphs, k, self.restoreGraph, self.deleteGraph)
|
||||||
self.undostack.push(cmd)
|
self.undostack.push(cmd)
|
||||||
@ -316,7 +355,7 @@ class UpperManagement(QtCore.QObject):
|
|||||||
if joined is None:
|
if joined is None:
|
||||||
joined = data_i.copy()
|
joined = data_i.copy()
|
||||||
else:
|
else:
|
||||||
joined.append(data_i.x, data_i.y, data_i.y_err)
|
joined.append(data_i.data.x, data_i.data.y, y_err=data_i.data.y_err, mask=data_i.data.mask)
|
||||||
|
|
||||||
name_set.add(data_i.name)
|
name_set.add(data_i.name)
|
||||||
group_set.add(data_i.group)
|
group_set.add(data_i.group)
|
||||||
@ -346,12 +385,16 @@ class UpperManagement(QtCore.QObject):
|
|||||||
|
|
||||||
def change_visibility(self, selected: list, deselected: list):
|
def change_visibility(self, selected: list, deselected: list):
|
||||||
"""Change status of list of ids after status change in datawidget"""
|
"""Change status of list of ids after status change in datawidget"""
|
||||||
|
for item_list, func in [(selected, 'show_item'), (deselected, 'hide_item')]:
|
||||||
|
grouping = {}
|
||||||
|
for s in item_list:
|
||||||
|
g = self.data[s].graph
|
||||||
|
if g not in grouping:
|
||||||
|
grouping[g] = []
|
||||||
|
grouping[g].append(s)
|
||||||
|
|
||||||
for s in selected:
|
for k, v in grouping.items():
|
||||||
self.graphs[self.data[s].graph].show_item([s])
|
getattr(self.graphs[k], func)(v)
|
||||||
|
|
||||||
for d in deselected:
|
|
||||||
self.graphs[self.data[d].graph].hide_item([d])
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str, str)
|
@QtCore.pyqtSlot(str, str)
|
||||||
def change_keys(self, identifier: str, name: str):
|
def change_keys(self, identifier: str, name: str):
|
||||||
@ -375,6 +418,13 @@ class UpperManagement(QtCore.QObject):
|
|||||||
self.undostack.push(single_undo)
|
self.undostack.push(single_undo)
|
||||||
self.undostack.endMacro()
|
self.undostack.endMacro()
|
||||||
|
|
||||||
|
def edit_signals(self: UpperManagement, args: list[tuple]) -> None:
|
||||||
|
self.undostack.beginMacro('Edit signals')
|
||||||
|
for sid in self.graphs[self.current_graph]:
|
||||||
|
single_undo = EditCommand(self.data[sid], *args)
|
||||||
|
self.undostack.push(single_undo)
|
||||||
|
self.undostack.endMacro()
|
||||||
|
|
||||||
def cut(self):
|
def cut(self):
|
||||||
if self.current_graph:
|
if self.current_graph:
|
||||||
xlim, _ = self.graphs[self.current_graph].ranges
|
xlim, _ = self.graphs[self.current_graph].ranges
|
||||||
@ -385,28 +435,54 @@ class UpperManagement(QtCore.QObject):
|
|||||||
for d in self.data.values():
|
for d in self.data.values():
|
||||||
d.mask = np.ones_like(d.mask, dtype=bool)
|
d.mask = np.ones_like(d.mask, dtype=bool)
|
||||||
|
|
||||||
def start_fit(self, parameter: dict, links: list, fit_options: dict):
|
def prepare_fit(self, parameter: dict, links: list, fit_options: dict) -> bool:
|
||||||
if self._fit_active:
|
if self._fit_active:
|
||||||
return
|
return False
|
||||||
|
|
||||||
self.__fit_options = (parameter, links, fit_options)
|
self.__fit_options = (parameter, links, fit_options)
|
||||||
|
|
||||||
fitter = FitRoutine()
|
self.fitter = FitRoutine()
|
||||||
models = {}
|
models = {}
|
||||||
fit_limits = fit_options['limits']
|
fit_limits = fit_options['limits']
|
||||||
fit_mode = fit_options['fit_mode']
|
fit_mode = fit_options['fit_mode']
|
||||||
we = fit_options['we']
|
we_option = fit_options['we']
|
||||||
|
|
||||||
|
self.fitter.fitmethod = fit_mode
|
||||||
|
|
||||||
|
# all-encompassing error catch
|
||||||
|
try:
|
||||||
for model_id, model_p in parameter.items():
|
for model_id, model_p in parameter.items():
|
||||||
m = Model(model_p['func'])
|
m = model_p['func']
|
||||||
models[model_id] = m
|
models[model_id] = m
|
||||||
|
|
||||||
m_complex = model_p['complex']
|
m_complex = model_p['complex']
|
||||||
|
|
||||||
for set_id, set_params in model_p['parameter'].items():
|
# sets are not in active order but in order they first appeared in fit dialog
|
||||||
|
# iterate over order of set id in active order and access parameter inside loop
|
||||||
|
# instead of directly looping
|
||||||
|
try:
|
||||||
|
list_ids = list(self.active_id)
|
||||||
|
set_order = [self.active_id.index(i) for i in model_p['data_parameter'].keys()]
|
||||||
|
except ValueError as e:
|
||||||
|
raise Exception('Getting order failed') from e
|
||||||
|
|
||||||
|
for pos in set_order:
|
||||||
|
set_id = list_ids[pos]
|
||||||
|
|
||||||
|
try:
|
||||||
data_i = self.data[set_id]
|
data_i = self.data[set_id]
|
||||||
if we.lower() == 'deltay':
|
except KeyError as e:
|
||||||
|
raise KeyError(f'{set_id} not found') from e
|
||||||
|
|
||||||
|
try:
|
||||||
|
set_params = model_p['data_parameter'][set_id]
|
||||||
|
except KeyError as e:
|
||||||
|
raise KeyError(f'No parameter found for {set_id}') from e
|
||||||
|
|
||||||
|
if we_option.lower() == 'deltay':
|
||||||
we = data_i.y_err**2
|
we = data_i.y_err**2
|
||||||
|
else:
|
||||||
|
we = we_option
|
||||||
|
|
||||||
if m_complex is None or m_complex == 1:
|
if m_complex is None or m_complex == 1:
|
||||||
_y = data_i.y.real
|
_y = data_i.y.real
|
||||||
@ -425,28 +501,39 @@ class UpperManagement(QtCore.QObject):
|
|||||||
else:
|
else:
|
||||||
inside = np.where((_x >= fit_limits[0]) & (_x <= fit_limits[1]))
|
inside = np.where((_x >= fit_limits[0]) & (_x <= fit_limits[1]))
|
||||||
|
|
||||||
|
try:
|
||||||
if isinstance(we, str):
|
if isinstance(we, str):
|
||||||
d = fit_d.Data(_x[inside], _y[inside], we=we, idx=set_id)
|
d = fit_d.Data(_x[inside], _y[inside], we=we, idx=set_id)
|
||||||
else:
|
else:
|
||||||
d = fit_d.Data(_x[inside], _y[inside], we=we[inside], idx=set_id)
|
d = fit_d.Data(_x[inside], _y[inside], we=we[inside], idx=set_id)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f'Setting data failed for {set_id}')
|
||||||
|
|
||||||
d.set_model(m)
|
d.set_model(m)
|
||||||
d.set_parameter(set_params[0], var=model_p['var'],
|
try:
|
||||||
lb=model_p['lb'], ub=model_p['ub'],
|
d.set_parameter(set_params[0], fun_kwargs=set_params[1])
|
||||||
fun_kwargs=set_params[1])
|
except Exception as e:
|
||||||
|
raise Exception('Setting parameter failed') from e
|
||||||
|
|
||||||
fitter.add_data(d)
|
self.fitter.add_data(d)
|
||||||
|
|
||||||
model_globs = model_p['glob']
|
|
||||||
if model_globs:
|
|
||||||
m.set_global_parameter(**model_p['glob'])
|
|
||||||
|
|
||||||
for links_i in links:
|
for links_i in links:
|
||||||
fitter.set_link_parameter((models[links_i[0]], links_i[1]),
|
self.fitter.set_link_parameter((models[links_i[0]], links_i[1]),
|
||||||
(models[links_i[2]], links_i[3]))
|
(models[links_i[2]], links_i[3]))
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error('Fit preparation failed', *e.args)
|
||||||
|
QtWidgets.QMessageBox.warning(QtWidgets.QWidget(),
|
||||||
|
'Fit prep failed',
|
||||||
|
f'Fit preparation failed:\n'
|
||||||
|
'Message:\n'
|
||||||
|
f'{e.args}:\n')
|
||||||
|
return False
|
||||||
|
|
||||||
|
def start_fit(self):
|
||||||
with busy_cursor():
|
with busy_cursor():
|
||||||
self.fit_worker = FitWorker(fitter, fit_mode)
|
self.fit_worker = FitWorker(self.fitter)
|
||||||
self.fit_thread = QtCore.QThread()
|
self.fit_thread = QtCore.QThread()
|
||||||
self.fit_worker.moveToThread(self.fit_thread)
|
self.fit_worker.moveToThread(self.fit_thread)
|
||||||
|
|
||||||
@ -462,12 +549,14 @@ class UpperManagement(QtCore.QObject):
|
|||||||
|
|
||||||
@QtCore.pyqtSlot(list, bool)
|
@QtCore.pyqtSlot(list, bool)
|
||||||
def end_fit(self, result: list, success: bool):
|
def end_fit(self, result: list, success: bool):
|
||||||
print('FIT FINISHED')
|
|
||||||
if success:
|
if success:
|
||||||
|
logger.info('Successful fit')
|
||||||
self.fitFinished.emit(result)
|
self.fitFinished.emit(result)
|
||||||
else:
|
else:
|
||||||
|
e = result[0]
|
||||||
|
logger.exception(e, exc_info=True)
|
||||||
QtWidgets.QMessageBox.warning(QtWidgets.QWidget(), 'Fit failed',
|
QtWidgets.QMessageBox.warning(QtWidgets.QWidget(), 'Fit failed',
|
||||||
'Fit kaput with exception: \n' + "\n".join(result[0]))
|
f'Fit kaput with exception: \n\n{e!r}')
|
||||||
self.fitFinished.emit([])
|
self.fitFinished.emit([])
|
||||||
self._fit_active = False
|
self._fit_active = False
|
||||||
|
|
||||||
@ -480,9 +569,10 @@ class UpperManagement(QtCore.QObject):
|
|||||||
for set_id, set_parameter in parameter.items():
|
for set_id, set_parameter in parameter.items():
|
||||||
new_values = [v.value for v in res[set_id].parameter.values()]
|
new_values = [v.value for v in res[set_id].parameter.values()]
|
||||||
parameter[set_id] = (new_values, set_parameter[1])
|
parameter[set_id] = (new_values, set_parameter[1])
|
||||||
self.start_fit(*self.__fit_options)
|
if self.prepare_fit(*self.__fit_options):
|
||||||
|
self.start_fit()
|
||||||
|
|
||||||
def make_fits(self, res: dict, opts: list, param_graph: str, show_fit: bool, parts: dict) -> None:
|
def make_fits(self, res: dict, opts: list, param_graph: str, show_fit: bool, parts: bool, extrapolate: list) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -491,6 +581,7 @@ class UpperManagement(QtCore.QObject):
|
|||||||
param_graph: None if no parameter to plot, '' for new graph, or id of existig graph
|
param_graph: None if no parameter to plot, '' for new graph, or id of existig graph
|
||||||
show_fit: plot fit curve?
|
show_fit: plot fit curve?
|
||||||
parts: key is that of original data, value is list of subplots
|
parts: key is that of original data, value is list of subplots
|
||||||
|
extrapolate:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
f_id_list = []
|
f_id_list = []
|
||||||
@ -503,6 +594,26 @@ class UpperManagement(QtCore.QObject):
|
|||||||
if reject:
|
if reject:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if not all(e is None for e in extrapolate):
|
||||||
|
spacefunc = np.geomspace if fit.islog else np.linspace
|
||||||
|
|
||||||
|
xmin = fit.x.min()
|
||||||
|
xmax = fit.x.max()
|
||||||
|
|
||||||
|
len_data = len(fit.x_data)
|
||||||
|
num_pts = 20*len_data-9 if len_data < 51 else 3*len_data
|
||||||
|
|
||||||
|
if extrapolate[0] is not None:
|
||||||
|
xmin = extrapolate[0]
|
||||||
|
if extrapolate[1] is not None:
|
||||||
|
xmax = extrapolate[1]
|
||||||
|
if extrapolate[2] is not None:
|
||||||
|
num_pts = extrapolate[2]
|
||||||
|
|
||||||
|
_x = spacefunc(xmin, xmax, num=num_pts)
|
||||||
|
|
||||||
|
fit = fit.with_new_x(_x)
|
||||||
|
|
||||||
data_k = self.data[k]
|
data_k = self.data[k]
|
||||||
if delete_prev:
|
if delete_prev:
|
||||||
tobedeleted.extend([f.id for f in data_k.get_fits()])
|
tobedeleted.extend([f.id for f in data_k.get_fits()])
|
||||||
@ -527,18 +638,19 @@ class UpperManagement(QtCore.QObject):
|
|||||||
f_id_list.append(f_id)
|
f_id_list.append(f_id)
|
||||||
data_k.set_fits(f_id)
|
data_k.set_fits(f_id)
|
||||||
|
|
||||||
gid = data_k.graph
|
if parts:
|
||||||
|
|
||||||
if k in parts and show_fit:
|
|
||||||
color_scheme = available_cycles['colorblind']
|
color_scheme = available_cycles['colorblind']
|
||||||
for subfunc, col in zip(parts[k], cycle(color_scheme)):
|
for subfunc, col in zip(fit.sub(fit.x), cycle(color_scheme)):
|
||||||
subfunc.value = data_k.value
|
subfunc.value = data_k.value
|
||||||
subfunc.group = data_k.group
|
subfunc.group = data_k.group
|
||||||
subfunc.name += data_name
|
subfunc.name += data_name
|
||||||
sub_f_id = self.add(subfunc, color=col, linestyle=LineStyle.Dashed, symbol=SymbolStyle.No)
|
sub_f_id = self.add(subfunc, color=col, linestyle=LineStyle.Dashed, symbol=SymbolStyle.No)
|
||||||
|
|
||||||
f_id_list.append(sub_f_id)
|
self[sub_f_id].add_relation(Relations.isFitPartOf, f_id)
|
||||||
|
self[f_id].add_relation(Relations.hasFitPart, sub_f_id)
|
||||||
|
|
||||||
|
f_id_list.append(sub_f_id)
|
||||||
|
gid = data_k.graph
|
||||||
self.delete_sets(tobedeleted)
|
self.delete_sets(tobedeleted)
|
||||||
|
|
||||||
if accepted and (param_graph != '-1'):
|
if accepted and (param_graph != '-1'):
|
||||||
@ -546,6 +658,27 @@ class UpperManagement(QtCore.QObject):
|
|||||||
|
|
||||||
self.newData.emit(f_id_list, gid)
|
self.newData.emit(f_id_list, gid)
|
||||||
|
|
||||||
|
def extend_fits(self, set_id: list, x_range: np.ndarray):
|
||||||
|
graphs = {}
|
||||||
|
for sid in set_id:
|
||||||
|
data = self[sid]
|
||||||
|
fit = data.copy(full=True, keep_color=True)
|
||||||
|
fit.data = fit.data.with_new_x(x_range)
|
||||||
|
|
||||||
|
graph_id = data.graph
|
||||||
|
if graph_id not in graphs:
|
||||||
|
graphs[graph_id] = []
|
||||||
|
graphs[graph_id].append(self.add(fit))
|
||||||
|
|
||||||
|
color_scheme = available_cycles['colorblind']
|
||||||
|
for subfunc, col in zip(fit.data.sub(fit.x), cycle(color_scheme)):
|
||||||
|
subfunc.value = fit.value
|
||||||
|
subfunc.group = fit.group
|
||||||
|
graphs[graph_id].append(self.add(subfunc, color=col, linestyle=LineStyle.Dashed, symbol=SymbolStyle.No))
|
||||||
|
|
||||||
|
for k, v in graphs.items():
|
||||||
|
self.newData.emit(v, k)
|
||||||
|
|
||||||
def make_fit_parameter(self, fit_sets: list[str | FitResult], graph_id: str = None):
|
def make_fit_parameter(self, fit_sets: list[str | FitResult], graph_id: str = None):
|
||||||
fit_dict = self._collect_fit_parameter(fit_sets)
|
fit_dict = self._collect_fit_parameter(fit_sets)
|
||||||
|
|
||||||
@ -558,11 +691,11 @@ class UpperManagement(QtCore.QObject):
|
|||||||
if not graph_id:
|
if not graph_id:
|
||||||
graph_id = ''
|
graph_id = ''
|
||||||
|
|
||||||
self.newData.emit(p_id_list, graph_id)
|
self.newData[list, str, bool].emit(p_id_list, graph_id, True)
|
||||||
|
|
||||||
def save_fit_parameter(self, fname: str | pathlib.Path, fit_sets: list[str] = None):
|
def save_fit_parameter(self, fname: str | pathlib.Path, fit_sets: list[str] = None):
|
||||||
if fit_sets is None:
|
if fit_sets is None:
|
||||||
fit_sets = [s for (s, _) in self.active_sets]
|
fit_sets = [s for s in self.active_id]
|
||||||
|
|
||||||
for set_id in fit_sets:
|
for set_id in fit_sets:
|
||||||
data = self.data[set_id]
|
data = self.data[set_id]
|
||||||
@ -584,12 +717,10 @@ class UpperManagement(QtCore.QObject):
|
|||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for key, pvalue in data.parameter.items():
|
for fit_key, pvalue in data.parameter.items():
|
||||||
name = pvalue.full_name
|
|
||||||
fit_key = key + data.model_name
|
|
||||||
|
|
||||||
if fit_key not in fit_dict:
|
if fit_key not in fit_dict:
|
||||||
fit_dict[fit_key] = [[], name]
|
fit_dict[fit_key] = [[], fit_key]
|
||||||
|
|
||||||
err = 0 if pvalue.error is None else pvalue.error
|
err = 0 if pvalue.error is None else pvalue.error
|
||||||
|
|
||||||
@ -626,7 +757,7 @@ class UpperManagement(QtCore.QObject):
|
|||||||
key = self.add(Points(x=new_x_axis, y=_temp[:, i, 1], y_err=_temp[:, i, 2], name=label))
|
key = self.add(Points(x=new_x_axis, y=_temp[:, i, 1], y_err=_temp[:, i, 2], name=label))
|
||||||
key_list.append(key)
|
key_list.append(key)
|
||||||
|
|
||||||
self.newData.emit(key_list, gid)
|
self.newData[list, str, bool].emit(key_list, gid, True)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(list)
|
@QtCore.pyqtSlot(list)
|
||||||
def get_properties(self, sid: list) -> dict:
|
def get_properties(self, sid: list) -> dict:
|
||||||
@ -697,6 +828,47 @@ class UpperManagement(QtCore.QObject):
|
|||||||
|
|
||||||
self.newData.emit(new_key, dest_graph)
|
self.newData.emit(new_key, dest_graph)
|
||||||
|
|
||||||
|
def binning(self, digits: float):
|
||||||
|
_active = self.graphs[self.current_graph].active
|
||||||
|
new_data = []
|
||||||
|
for sid in _active:
|
||||||
|
key = self.add(self.data[sid].binning(digits=digits))
|
||||||
|
new_data.append(key)
|
||||||
|
|
||||||
|
self.newData.emit(new_data, self.current_graph)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(dict, str)
|
||||||
|
def addTg(self, dic: dict, dtype: str):
|
||||||
|
graph_id = self.current_graph if dtype == 'tg' else dic.pop('graph')
|
||||||
|
|
||||||
|
set_id_list = []
|
||||||
|
|
||||||
|
if dtype == 'hodge':
|
||||||
|
for v in dic.values():
|
||||||
|
set_id_list.append(self.add(v))
|
||||||
|
|
||||||
|
else:
|
||||||
|
for k, (data, lines) in dic.items():
|
||||||
|
p: ExperimentContainer = self[k]
|
||||||
|
col = p.plot_real.linecolor
|
||||||
|
|
||||||
|
if data is not None:
|
||||||
|
set_id_list.append(self.add(data, color=col))
|
||||||
|
|
||||||
|
if dtype == 'tnmh':
|
||||||
|
if lines is not None:
|
||||||
|
lines = [lines]
|
||||||
|
else:
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
set_id = self.add(line, color=col)
|
||||||
|
self[set_id].setLine(style=LineStyle.Dashed)
|
||||||
|
self[set_id].setSymbol(symbol=SymbolStyle.No)
|
||||||
|
set_id_list.append(set_id)
|
||||||
|
|
||||||
|
self.newData.emit(set_id_list, graph_id)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(int, dict)
|
@QtCore.pyqtSlot(int, dict)
|
||||||
def smooth_data(self, npoints, param_kwargs):
|
def smooth_data(self, npoints, param_kwargs):
|
||||||
_active = self.graphs[self.current_graph].active
|
_active = self.graphs[self.current_graph].active
|
||||||
@ -729,13 +901,10 @@ class UpperManagement(QtCore.QObject):
|
|||||||
d_k = self.data[k]
|
d_k = self.data[k]
|
||||||
|
|
||||||
if copy_data is None:
|
if copy_data is None:
|
||||||
d_k.x = d_k.x*v[1][0] + v[0][0]
|
d_k.shift_scale(v[0], v[1])
|
||||||
d_k.y = d_k.y*v[1][1] + v[0][1]
|
|
||||||
else:
|
else:
|
||||||
new_data = d_k.copy(full=True)
|
new_data = d_k.copy(full=True)
|
||||||
new_data.update({'shift': v[0], 'scale': v[1]})
|
new_data.shift_scale(v[0], v[1])
|
||||||
new_data.data.x = new_data.x*v[1][0] + v[0][0]
|
|
||||||
new_data.y = new_data.y*v[1][1] + v[0][1]
|
|
||||||
|
|
||||||
sid = self.add(new_data)
|
sid = self.add(new_data)
|
||||||
sid_list.append(sid)
|
sid_list.append(sid)
|
||||||
@ -831,11 +1000,16 @@ class UpperManagement(QtCore.QObject):
|
|||||||
self.undostack.beginMacro('Evaluate expression')
|
self.undostack.beginMacro('Evaluate expression')
|
||||||
|
|
||||||
failures = []
|
failures = []
|
||||||
for sid in set_ids:
|
for i, g in enumerate(self.graphs.values()):
|
||||||
|
for j, sid in enumerate(g.sets):
|
||||||
|
|
||||||
|
if sid not in set_ids:
|
||||||
|
continue
|
||||||
|
|
||||||
data_i = self.data[sid]
|
data_i = self.data[sid]
|
||||||
try:
|
try:
|
||||||
# use a copy of original namespace
|
# use a copy of original namespace
|
||||||
new_data = data_i.eval_expression(cmds, dict(ns))
|
new_data = data_i.eval_expression(cmds, dict(ns), i=i, j=j)
|
||||||
if overwrite:
|
if overwrite:
|
||||||
cmd = EvalCommand(self.data, sid, new_data, 'Evaluate expression')
|
cmd = EvalCommand(self.data, sid, new_data, 'Evaluate expression')
|
||||||
self.undostack.push(cmd)
|
self.undostack.push(cmd)
|
||||||
@ -844,7 +1018,7 @@ class UpperManagement(QtCore.QObject):
|
|||||||
self.data[new_id[0]].data = new_data
|
self.data[new_id[0]].data = new_data
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
failures.append((data_i, e))
|
failures.append((data_i, e))
|
||||||
print(str(data_i) + ' failed with Exception: ' + ''.join(e.args))
|
logger.warning(str(data_i) + ' failed with Exception: ' + ''.join(e.args))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if overwrite:
|
if overwrite:
|
||||||
@ -879,9 +1053,9 @@ class UpperManagement(QtCore.QObject):
|
|||||||
self.newData.emit([s_id], graph)
|
self.newData.emit([s_id], graph)
|
||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
print('Creation failed with error: ' + ', '.join(err.args))
|
logger.exception('Creation failed with error: ' + ', '.join(err.args))
|
||||||
err_msg = QtWidgets.QMessageBox(parent=self.sender())
|
err_msg = QtWidgets.QMessageBox(parent=self.sender())
|
||||||
err_msg.setText('One or more errors occured during evaluation.')
|
err_msg.setText('One or more errors occurred during evaluation.')
|
||||||
err_msg.setDetailedText('Creation failed with error: ' + ', '.join(err.args))
|
err_msg.setDetailedText('Creation failed with error: ' + ', '.join(err.args))
|
||||||
err_msg.exec()
|
err_msg.exec()
|
||||||
|
|
||||||
@ -890,7 +1064,7 @@ class UpperManagement(QtCore.QObject):
|
|||||||
def show_statistics(self, mode):
|
def show_statistics(self, mode):
|
||||||
x, y, = [], []
|
x, y, = [], []
|
||||||
|
|
||||||
for i, _ in self.active_sets:
|
for i in self.active_id:
|
||||||
_temp = self.data[i]
|
_temp = self.data[i]
|
||||||
try:
|
try:
|
||||||
x.append(float(_temp.name))
|
x.append(float(_temp.name))
|
||||||
@ -901,7 +1075,7 @@ class UpperManagement(QtCore.QObject):
|
|||||||
@QtCore.pyqtSlot()
|
@QtCore.pyqtSlot()
|
||||||
def calc_magn(self):
|
def calc_magn(self):
|
||||||
new_id = []
|
new_id = []
|
||||||
for k, _ in self.active_sets:
|
for k in self.active_id:
|
||||||
dataset = self.data[k]
|
dataset = self.data[k]
|
||||||
if isinstance(dataset, SignalContainer):
|
if isinstance(dataset, SignalContainer):
|
||||||
new_value = dataset.copy(full=True)
|
new_value = dataset.copy(full=True)
|
||||||
@ -913,7 +1087,7 @@ class UpperManagement(QtCore.QObject):
|
|||||||
@QtCore.pyqtSlot()
|
@QtCore.pyqtSlot()
|
||||||
def center(self):
|
def center(self):
|
||||||
new_id = []
|
new_id = []
|
||||||
for k, _ in self.active_sets:
|
for k in self.active_id:
|
||||||
new_value = self.data[k].copy(full=True)
|
new_value = self.data[k].copy(full=True)
|
||||||
new_value.x -= new_value.x[np.argmax(new_value.y.real)]
|
new_value.x -= new_value.x[np.argmax(new_value.y.real)]
|
||||||
new_id.append(self.add(new_value))
|
new_id.append(self.add(new_value))
|
||||||
@ -952,7 +1126,7 @@ class UpperManagement(QtCore.QObject):
|
|||||||
def bds_deriv(self):
|
def bds_deriv(self):
|
||||||
new_sets = []
|
new_sets = []
|
||||||
|
|
||||||
for (set_id, _) in self.active_sets:
|
for set_id in self.active_id:
|
||||||
data_i = self.data[set_id]
|
data_i = self.data[set_id]
|
||||||
diff = data_i.data.diff(log=True)
|
diff = data_i.data.diff(log=True)
|
||||||
new_data = Points(x=diff.x, y=-np.pi/2*diff.y.real)
|
new_data = Points(x=diff.x, y=-np.pi/2*diff.y.real)
|
||||||
@ -965,13 +1139,14 @@ class UpperManagement(QtCore.QObject):
|
|||||||
def logft(self, **kwargs):
|
def logft(self, **kwargs):
|
||||||
new_sets = []
|
new_sets = []
|
||||||
ft_mode = kwargs['ft_mode']
|
ft_mode = kwargs['ft_mode']
|
||||||
|
return_f = kwargs['return_f']
|
||||||
|
|
||||||
for set_id in kwargs['sets']:
|
for set_id in kwargs['sets']:
|
||||||
data_i = self.data[set_id]
|
data_i = self.data[set_id]
|
||||||
if ft_mode in ['cos', 'sin']:
|
if ft_mode in ['cos', 'sin']:
|
||||||
new_data = Points(*logft(data_i.x, data_i.y, mode=ft_mode))
|
new_data = Points(*logft(data_i.x, data_i.y, mode=ft_mode, return_f=return_f))
|
||||||
else:
|
else:
|
||||||
new_data = Signal(*logft(data_i.x, data_i.y, mode=ft_mode))
|
new_data = Signal(*logft(data_i.x, data_i.y, mode=ft_mode, return_f=return_f))
|
||||||
|
|
||||||
new_sets.append(self.add(new_data, color=data_i['color'], symbol=data_i['symbol'], line=data_i['line']))
|
new_sets.append(self.add(new_data, color=data_i['color'], symbol=data_i['symbol'], line=data_i['line']))
|
||||||
self.data[new_sets[-1]].update(data_i.data.meta)
|
self.data[new_sets[-1]].update(data_i.data.meta)
|
||||||
@ -979,7 +1154,7 @@ class UpperManagement(QtCore.QObject):
|
|||||||
self.newData.emit(new_sets, kwargs['graph'])
|
self.newData.emit(new_sets, kwargs['graph'])
|
||||||
|
|
||||||
def skip_points(self, offset: int, step: int, invert: bool = False, copy: bool = False):
|
def skip_points(self, offset: int, step: int, invert: bool = False, copy: bool = False):
|
||||||
for k, _ in self.active_sets:
|
for k in self.active_id:
|
||||||
src = self.data[k]
|
src = self.data[k]
|
||||||
if invert:
|
if invert:
|
||||||
mask = np.mod(np.arange(offset, src.x.size+offset), step) != 0
|
mask = np.mod(np.arange(offset, src.x.size+offset), step) != 0
|
||||||
@ -1005,9 +1180,11 @@ class UpperManagement(QtCore.QObject):
|
|||||||
params = opts['pts']
|
params = opts['pts']
|
||||||
if len(params) == 4:
|
if len(params) == 4:
|
||||||
if params[3]:
|
if params[3]:
|
||||||
_x = x1 = np.geomspace(params[0], params[1], num=params[2])
|
_x = np.geomspace(params[0], params[1], num=params[2])
|
||||||
|
x1 = np.geomspace(params[0], params[1], num=params[2])
|
||||||
else:
|
else:
|
||||||
_x = x1 = np.linspace(params[0], params[1], num=params[2])
|
_x = np.linspace(params[0], params[1], num=params[2])
|
||||||
|
x1 = np.linspace(params[0], params[1], num=params[2])
|
||||||
|
|
||||||
if opts['axis1'] in ['t', 'invt1000']:
|
if opts['axis1'] in ['t', 'invt1000']:
|
||||||
t_p = opts['t_param']
|
t_p = opts['t_param']
|
||||||
@ -1026,21 +1203,23 @@ class UpperManagement(QtCore.QObject):
|
|||||||
_x = x1 = self.data[params[0]].x
|
_x = x1 = self.data[params[0]].x
|
||||||
|
|
||||||
x2 = opts['val2']
|
x2 = opts['val2']
|
||||||
|
|
||||||
sd = opts['spec_dens']
|
sd = opts['spec_dens']
|
||||||
sd_param = [self.data[p].y.real if isinstance(p, str) else p for p in opts['sd_param'][0]]
|
sd_param = list(zip(*[self.data[p].y.real if isinstance(p, str) else [p]*len(_x) for p in opts['sd_param'][0]]))
|
||||||
sd.convert(_x, *sd_param, from_=opts['tau_type'], to_='raw')
|
|
||||||
|
|
||||||
relax = Relaxation()
|
relax = Relaxation()
|
||||||
relax.set_distribution(sd, parameter=sd_param, keywords=opts['sd_param'][1])
|
relax.set_distribution(sd, keywords=opts['sd_param'][1])
|
||||||
|
|
||||||
cp_param = [self.data[p].y.real if isinstance(p, str) else p for p in opts['cp_param'][0]]
|
cp_param = list(zip(*[self.data[p].y.real if isinstance(p, str) else [p]*len(_x) for p in opts['cp_param'][0]]))
|
||||||
relax.set_coupling(opts['coup'], parameter=cp_param, keywords=opts['cp_param'][1])
|
# relax.set_coupling(opts['coup'], parameter=cp_param, keywords=opts['cp_param'][1])
|
||||||
|
|
||||||
if opts['out'] == 't1':
|
relax_func = relax.t1 if opts['out'] == 't1' else relax.t2
|
||||||
y = relax.t1(x2, _x)
|
y = np.zeros(_x.size)
|
||||||
else:
|
|
||||||
y = relax.t2(x2, _x)
|
for i in range(_x.size):
|
||||||
|
_x_i = sd.convert(_x[i], *sd_param[i], from_=opts['tau_type'], to_='raw')
|
||||||
|
relax.dist_parameter = sd_param[i]
|
||||||
|
relax.set_coupling(opts['coup'], parameter=cp_param[i], keywords=opts['cp_param'][1])
|
||||||
|
y[i] = relax_func(x2, _x_i)
|
||||||
|
|
||||||
pts = Points(x1, y, name=sd.name)
|
pts = Points(x1, y, name=sd.name)
|
||||||
pts.meta.update(opts)
|
pts.meta.update(opts)
|
||||||
@ -1134,18 +1313,17 @@ class UpperManagement(QtCore.QObject):
|
|||||||
class FitWorker(QtCore.QObject):
|
class FitWorker(QtCore.QObject):
|
||||||
finished = QtCore.pyqtSignal(list, bool)
|
finished = QtCore.pyqtSignal(list, bool)
|
||||||
|
|
||||||
def __init__(self, fitter, mode):
|
def __init__(self, fitter):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.fitter = fitter
|
self.fitter = fitter
|
||||||
self.mode = mode
|
|
||||||
|
|
||||||
@QtCore.pyqtSlot()
|
@QtCore.pyqtSlot()
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
res = self.fitter.run(mode=self.mode)
|
res = self.fitter.run()
|
||||||
success = True
|
success = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
res = [e.args]
|
res = [e]
|
||||||
success = False
|
success = False
|
||||||
self.finished.emit(res, success)
|
self.finished.emit(res, success)
|
||||||
|
21
src/gui_qt/math/binning.py
Normal file
21
src/gui_qt/math/binning.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from ..Qt import QtWidgets, QtGui
|
||||||
|
|
||||||
|
|
||||||
|
class BinningWindow(QtWidgets.QDialog):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
layout = QtWidgets.QFormLayout()
|
||||||
|
|
||||||
|
self.label = QtWidgets.QLabel('Digits (negative values position left of decimal point)')
|
||||||
|
self.spinbox = QtWidgets.QLineEdit()
|
||||||
|
self.spinbox.setValidator(QtGui.QDoubleValidator())
|
||||||
|
self.spinbox.setText('1')
|
||||||
|
layout.addRow(self.label, self.spinbox)
|
||||||
|
|
||||||
|
self.dialogbox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
|
||||||
|
self.dialogbox.accepted.connect(self.accept)
|
||||||
|
self.dialogbox.rejected.connect(self.reject)
|
||||||
|
layout.addWidget(self.dialogbox)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
@ -31,14 +31,36 @@ class QEvalDialog(QtWidgets.QDialog, Ui_CalcDialog):
|
|||||||
self.namespace_widget.set_namespace(self.namespace)
|
self.namespace_widget.set_namespace(self.namespace)
|
||||||
|
|
||||||
def add_data(self, data):
|
def add_data(self, data):
|
||||||
self.listWidget.clear()
|
# self.listWidget.clear()
|
||||||
|
tmp = []
|
||||||
|
while self.listWidget.count():
|
||||||
|
tmp.append(self.listWidget.takeItem(0))
|
||||||
|
|
||||||
for set_id, name in data:
|
for set_id, name in data:
|
||||||
|
# search if set was used before
|
||||||
|
new_one = True
|
||||||
|
for i in range(len(tmp)):
|
||||||
|
w = tmp[i]
|
||||||
|
if w.data(QtCore.Qt.UserRole) == set_id:
|
||||||
|
w.setText(name)
|
||||||
|
self.listWidget.addItem(w)
|
||||||
|
tmp.pop(i)
|
||||||
|
new_one = False
|
||||||
|
break
|
||||||
|
|
||||||
|
# new set, create item
|
||||||
|
if new_one:
|
||||||
item = QtWidgets.QListWidgetItem(name)
|
item = QtWidgets.QListWidgetItem(name)
|
||||||
item.setData(QtCore.Qt.UserRole, set_id)
|
item.setData(QtCore.Qt.UserRole, set_id)
|
||||||
item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
|
item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
|
||||||
item.setCheckState(QtCore.Qt.Checked)
|
item.setCheckState(QtCore.Qt.Checked)
|
||||||
self.listWidget.addItem(item)
|
self.listWidget.addItem(item)
|
||||||
|
|
||||||
|
while len(tmp):
|
||||||
|
# delete remaining ListWidgetItems
|
||||||
|
w = tmp.pop()
|
||||||
|
del w
|
||||||
|
|
||||||
def set_graphs(self, graphs: list):
|
def set_graphs(self, graphs: list):
|
||||||
self.graph_comboBox.clear()
|
self.graph_comboBox.clear()
|
||||||
self.graph_comboBox.addItem('New graph', userData='')
|
self.graph_comboBox.addItem('New graph', userData='')
|
||||||
|
@ -16,16 +16,19 @@ class QDeriveIntegrate(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.start_lineedit.setValidator(QtGui.QDoubleValidator())
|
self.start_lineedit.setValidator(QtGui.QDoubleValidator())
|
||||||
self.stop_lineedit.setValidator(QtGui.QDoubleValidator())
|
self.stop_lineedit.setValidator(QtGui.QDoubleValidator())
|
||||||
self.ft_comboBox.hide()
|
self.ft_comboBox.hide()
|
||||||
|
self.freq_box.hide()
|
||||||
|
|
||||||
elif self.mode == 'd':
|
elif self.mode == 'd':
|
||||||
self.setWindowTitle('Differentiation dialog')
|
self.setWindowTitle('Differentiation dialog')
|
||||||
self.widget.hide()
|
self.widget.hide()
|
||||||
self.ft_comboBox.hide()
|
self.ft_comboBox.hide()
|
||||||
|
self.freq_box.hide()
|
||||||
|
|
||||||
elif self.mode == 'l':
|
elif self.mode == 'l':
|
||||||
self.setWindowTitle('Logarithmic FT dialog')
|
self.setWindowTitle('Logarithmic FT dialog')
|
||||||
self.log_checkbox.hide()
|
self.log_checkbox.hide()
|
||||||
self.widget.hide()
|
self.widget.hide()
|
||||||
|
self.freq_box.show()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValueError(f'Unknown mode {mode}, use "d", "i", or "l".')
|
raise ValueError(f'Unknown mode {mode}, use "d", "i", or "l".')
|
||||||
@ -54,8 +57,10 @@ class QDeriveIntegrate(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.stop_lineedit.setEnabled(full_range != QtCore.Qt.Checked)
|
self.stop_lineedit.setEnabled(full_range != QtCore.Qt.Checked)
|
||||||
|
|
||||||
def get_options(self):
|
def get_options(self):
|
||||||
opts = {'graph': '' if self.newgraph_checkbox.isChecked() else self.graph_combobox.currentData(),
|
opts = {
|
||||||
'mode': self.mode, 'sets': []}
|
'graph': '' if self.newgraph_checkbox.isChecked() else self.graph_combobox.currentData(),
|
||||||
|
'mode': self.mode, 'sets': []
|
||||||
|
}
|
||||||
|
|
||||||
if self.mode == 'i':
|
if self.mode == 'i':
|
||||||
start = None
|
start = None
|
||||||
@ -75,6 +80,7 @@ class QDeriveIntegrate(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
if self.mode == 'l':
|
if self.mode == 'l':
|
||||||
opts['ft_mode'] = ['cos', 'sin', 'complex'][self.ft_comboBox.currentIndex()]
|
opts['ft_mode'] = ['cos', 'sin', 'complex'][self.ft_comboBox.currentIndex()]
|
||||||
|
opts['return_f'] = self.freq_box.isChecked()
|
||||||
else:
|
else:
|
||||||
opts['log'] = self.log_checkbox.isChecked()
|
opts['log'] = self.log_checkbox.isChecked()
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ class QRelaxCalc(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
self.graphs = {}
|
self.graphs = {}
|
||||||
|
|
||||||
self.specdens = [ColeCole, ColeDavidson, HavriliakNegami, KWW]
|
self.specdens = [ColeCole, ColeDavidson, HavriliakNegami, KWW, LogGaussian]
|
||||||
self.coupling = [Quadrupolar, HomoDipolar, Czjzek]
|
self.coupling = [Quadrupolar, HomoDipolar, Czjzek]
|
||||||
self.tau_parameter = []
|
self.tau_parameter = []
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ class QRelaxCalc(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.verticalLayout_3.addWidget(_temp)
|
self.verticalLayout_3.addWidget(_temp)
|
||||||
|
|
||||||
def get_taus(self, dic: dict):
|
def get_taus(self, dic: dict):
|
||||||
dic['tau_type'] = {0: 'raw', 1: 'mean', 2: 'peak', 3: 'logmean'}[self.xtype_combobox.currentIndex()]
|
dic['tau_type'] = {0: 'raw', 1: 'peak', 2: 'mean', 3: 'logmean'}[self.xtype_combobox.currentIndex()]
|
||||||
dic['axis1'] = {self.radioButton: 'tau', self.radioButton_2: 'omega',
|
dic['axis1'] = {self.radioButton: 'tau', self.radioButton_2: 'omega',
|
||||||
self.radioButton_3: 't', self.radioButton_4: 'invt1000'}[self.buttonGroup.checkedButton()]
|
self.radioButton_3: 't', self.radioButton_4: 'invt1000'}[self.buttonGroup.checkedButton()]
|
||||||
|
|
||||||
@ -199,3 +199,9 @@ class QRelaxCalc(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
def accept(self):
|
def accept(self):
|
||||||
self.calc_relaxation()
|
self.calc_relaxation()
|
||||||
super().accept()
|
super().accept()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(QtWidgets.QAbstractButton)
|
||||||
|
def on_buttonBox_clicked(self, button: QtWidgets.QAbstractButton):
|
||||||
|
role = self.buttonBox.buttonRole(button)
|
||||||
|
if role == self.buttonBox.ApplyRole:
|
||||||
|
self.calc_relaxation()
|
||||||
|
@ -61,6 +61,8 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
|
|||||||
self.freq_combox.currentIndexChanged.connect(lambda x: self.update_model())
|
self.freq_combox.currentIndexChanged.connect(lambda x: self.update_model())
|
||||||
self.freq_spinbox.valueChanged.connect(lambda x: self.update_model())
|
self.freq_spinbox.valueChanged.connect(lambda x: self.update_model())
|
||||||
|
|
||||||
|
self.checkBox_interpol.setVisible(False)
|
||||||
|
|
||||||
self.update_specdens(0)
|
self.update_specdens(0)
|
||||||
self.update_coupling(0)
|
self.update_coupling(0)
|
||||||
|
|
||||||
|
158
src/nmreval/clib/integrate.c
Normal file
158
src/nmreval/clib/integrate.c
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/* integrands used in quadrature integration with scipy's LowLevelCallables */
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
const double KB = 8.617333262145179e-05;
|
||||||
|
|
||||||
|
/* FFHS functions */
|
||||||
|
double ffhsSD(double x, void *user_data) {
|
||||||
|
double *c = (double *)user_data;
|
||||||
|
|
||||||
|
double omega = c[0];
|
||||||
|
double tau = c[1];
|
||||||
|
|
||||||
|
double u = x*x;
|
||||||
|
double res = u*u * tau;
|
||||||
|
|
||||||
|
res /= 81. + 9.*u - 2.*u*u + u*u*u;
|
||||||
|
res /= u*u + omega*omega * tau*tau;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* log-gaussian functions */
|
||||||
|
double logNormalDist(double tau, double tau0, double sigma) {
|
||||||
|
return exp(- pow((log(tau/tau0) / sigma), 2) / 2.) / sqrt(2*M_PI)/sigma;
|
||||||
|
}
|
||||||
|
|
||||||
|
double logGaussian_imag_high(double u, void *user_data) {
|
||||||
|
double *c = (double *)user_data;
|
||||||
|
|
||||||
|
double omega = c[0];
|
||||||
|
double tau = c[1];
|
||||||
|
double sigma = c[2];
|
||||||
|
|
||||||
|
double uu = exp(-u);
|
||||||
|
double dist = logNormalDist(1./uu, tau, sigma);
|
||||||
|
|
||||||
|
return dist * omega * uu / (pow(uu, 2) + pow(omega, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
double logGaussian_imag_low(double u, void *user_data) {
|
||||||
|
double *c = (double *)user_data;
|
||||||
|
|
||||||
|
double omega = c[0];
|
||||||
|
double tau = c[1];
|
||||||
|
double sigma = c[2];
|
||||||
|
|
||||||
|
double uu = exp(u);
|
||||||
|
|
||||||
|
double dist = logNormalDist(uu, tau, sigma);
|
||||||
|
|
||||||
|
return dist * omega * uu / (1. + pow(omega*uu, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
double logGaussian_real_high(double u, void *user_data) {
|
||||||
|
double *c = (double *)user_data;
|
||||||
|
|
||||||
|
double omega = c[0];
|
||||||
|
double tau = c[1];
|
||||||
|
double sigma = c[2];
|
||||||
|
|
||||||
|
double uu = exp(-2.*u);
|
||||||
|
double dist = logNormalDist(exp(uu), tau, sigma);
|
||||||
|
|
||||||
|
return dist * uu / (uu + pow(omega, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
double logGaussian_real_low(double u, void *user_data) {
|
||||||
|
double *c = (double *)user_data;
|
||||||
|
|
||||||
|
double omega = c[0];
|
||||||
|
double tau = c[1];
|
||||||
|
double sigma = c[2];
|
||||||
|
|
||||||
|
double uu = exp(u);
|
||||||
|
|
||||||
|
double dist = logNormalDist(uu, tau, sigma);
|
||||||
|
|
||||||
|
return dist / (1. + pow(omega*uu, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
double logGaussianCorrelation(double x, void *user_data) {
|
||||||
|
double *c = (double *)user_data;
|
||||||
|
|
||||||
|
double t = c[0];
|
||||||
|
double tau = c[1];
|
||||||
|
double sigma = c[2];
|
||||||
|
|
||||||
|
double uu = exp(x);
|
||||||
|
|
||||||
|
double dist = logNormalDist(uu, tau, sigma);
|
||||||
|
|
||||||
|
return dist * exp(-t/uu);
|
||||||
|
}
|
||||||
|
|
||||||
|
// functions for distribution of energy
|
||||||
|
double normalDist(double x, double x0, double sigma) {
|
||||||
|
return exp(- pow((x-x0) / sigma, 2) / 2.) / sqrt(2 * M_PI) / sigma;
|
||||||
|
}
|
||||||
|
|
||||||
|
double rate(double tau0, double ea, double t) {
|
||||||
|
return exp(-ea / t / KB) / tau0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double energyDist_SD(double x, void *user_data) {
|
||||||
|
double *c = (double *)user_data;
|
||||||
|
|
||||||
|
double omega = c[0];
|
||||||
|
double tau0 = c[1];
|
||||||
|
double e_m = c[2];
|
||||||
|
double e_b = c[3];
|
||||||
|
double temp = c[4];
|
||||||
|
|
||||||
|
double r = rate(tau0, x, temp);
|
||||||
|
|
||||||
|
return r/(pow(r, 2) + pow(omega, 2)) * normalDist(x, e_m, e_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
double energyDistSuscReal(double x, void *user_data) {
|
||||||
|
double *c = (double *)user_data;
|
||||||
|
|
||||||
|
double omega = c[0];
|
||||||
|
double tau0 = c[1];
|
||||||
|
double e_m = c[2];
|
||||||
|
double e_b = c[3];
|
||||||
|
double temp = c[4];
|
||||||
|
|
||||||
|
double r = rate(tau0, x, temp);
|
||||||
|
|
||||||
|
return 1 / (pow(r, 2) + pow(omega, 2)) * normalDist(x, e_m, e_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
double energyDistSuscImag(double x, void *user_data) {
|
||||||
|
double *c = (double *)user_data;
|
||||||
|
|
||||||
|
double omega = c[0];
|
||||||
|
double tau0 = c[1];
|
||||||
|
double e_m = c[2];
|
||||||
|
double e_b = c[3];
|
||||||
|
double temp = c[4];
|
||||||
|
|
||||||
|
double r = rate(tau0, x, temp);
|
||||||
|
|
||||||
|
return omega * r / (pow(r, 2) + pow(omega, 2)) * normalDist(x, e_m, e_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
double energyDistCorrelation(double x, void *user_data) {
|
||||||
|
double *c = (double *)user_data;
|
||||||
|
|
||||||
|
double t = c[0];
|
||||||
|
double tau0 = c[1];
|
||||||
|
double e_m = c[2];
|
||||||
|
double e_b = c[3];
|
||||||
|
double temp = c[4];
|
||||||
|
|
||||||
|
double r = rate(tau0, x, temp);
|
||||||
|
|
||||||
|
return normalDist(x, e_m, e_b) * exp(-t * r);
|
||||||
|
}
|
||||||
|
|
BIN
src/nmreval/clib/integrate.so
Executable file
BIN
src/nmreval/clib/integrate.so
Executable file
Binary file not shown.
@ -10,19 +10,25 @@ __all__ = ['config_paths', 'check_for_config', 'read_configuration', 'write_conf
|
|||||||
|
|
||||||
def check_for_config(make=True):
|
def check_for_config(make=True):
|
||||||
try:
|
try:
|
||||||
config_paths()
|
conf_path = config_paths()
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
if make:
|
if make:
|
||||||
conf_path = pathlib.Path('~/.auswerten').expanduser()
|
conf_path = pathlib.Path('~/.auswerten').expanduser()
|
||||||
conf_path.mkdir(parents=True)
|
conf_path.mkdir(parents=True)
|
||||||
cwd = pathlib.Path(__file__).parent
|
|
||||||
copyfile(cwd / 'models' / 'usermodels.py', conf_path / 'usermodels.py')
|
|
||||||
with resource_path('resources', 'Default.agr') as fp:
|
|
||||||
copyfile(fp, conf_path / 'Default.agr')
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
for filename in ('Default.agr', 'logo.png'):
|
||||||
|
_file = conf_path / filename
|
||||||
|
if not _file.exists():
|
||||||
|
with resource_path('resources', filename) as fp:
|
||||||
|
copyfile(fp, _file)
|
||||||
|
|
||||||
|
if not (conf_path / 'usermodels.py').exists():
|
||||||
|
cwd = pathlib.Path(__file__).parent
|
||||||
|
copyfile(cwd / 'models' / 'usermodels.py', conf_path / 'usermodels.py')
|
||||||
|
|
||||||
|
|
||||||
def config_paths() -> pathlib.Path:
|
def config_paths() -> pathlib.Path:
|
||||||
searchpaths = ['~/.config/nmreval', '~/.auswerten', '/usr/share/nmreval']
|
searchpaths = ['~/.config/nmreval', '~/.auswerten', '/usr/share/nmreval']
|
||||||
|
@ -1,6 +1,138 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from scipy.optimize import fsolve
|
||||||
|
from scipy.signal import savgol_filter
|
||||||
|
|
||||||
|
try:
|
||||||
|
from scipy.integrate import cumulative_trapezoid
|
||||||
|
except ImportError:
|
||||||
|
from scipy.integrate import cumtrapz as cumulative_trapezoid
|
||||||
|
from scipy.stats import linregress
|
||||||
|
|
||||||
from .points import Points
|
from .points import Points
|
||||||
|
|
||||||
|
from ..dsc.tnmh_model import TNMH
|
||||||
|
|
||||||
|
|
||||||
class DSC(Points):
|
class DSC(Points):
|
||||||
def __init__(self, x, y, **kwargs):
|
def __init__(self, x, y, **kwargs):
|
||||||
super().__init__(x, y, **kwargs)
|
|
||||||
|
y = np.asarray(y).reshape(np.asarray(x).shape)
|
||||||
|
|
||||||
|
x, unique = np.unique(x, return_index=True)
|
||||||
|
if kwargs.get('y_err', None) is not None:
|
||||||
|
_yerr = np.asarray(kwargs['y_err']).reshape(np.asarray(x).shape)
|
||||||
|
kwargs['y_err'] = _yerr[unique]
|
||||||
|
|
||||||
|
self.tg = {'onset': np.nan, 'mid': np.nan, 'end': np.nan, 'inflection': np.nan, 'fictive': np.nan}
|
||||||
|
|
||||||
|
super().__init__(x, y[unique], **kwargs)
|
||||||
|
|
||||||
|
def get_fictive_cp(self, glass: tuple[float, float], liquid: tuple[float, float]) -> ('DSC', float):
|
||||||
|
min_glass, max_glass = min(glass), max(glass)
|
||||||
|
min_liquid, max_liquid = min(liquid), max(liquid)
|
||||||
|
|
||||||
|
region = self.copy()
|
||||||
|
region.cut(min_glass, max_liquid)
|
||||||
|
|
||||||
|
glass_regime = (min_glass < region.x) & (region.x < max_glass)
|
||||||
|
regress = linregress(region.x[glass_regime], region.y[glass_regime])
|
||||||
|
glass_extrapolation = regress.slope * region.x + regress.intercept
|
||||||
|
|
||||||
|
liquid_regime = (min_liquid < region.x) & (region.x < max_liquid)
|
||||||
|
regress2 = linregress(region.x[liquid_regime], region.y[liquid_regime])
|
||||||
|
|
||||||
|
region.y -= glass_extrapolation
|
||||||
|
real_area = cumulative_trapezoid(region.y, region.x, initial=0)
|
||||||
|
real_area -= real_area[-1]
|
||||||
|
|
||||||
|
t = regress2.intercept - regress.intercept
|
||||||
|
m = regress2.slope - regress.slope
|
||||||
|
c0 = 0.5 * m * region.x.max() ** 2 + t * region.x.max()
|
||||||
|
|
||||||
|
def equiv(_x, _i):
|
||||||
|
return (0.5 * m * _x ** 2 + t * _x - c0) - real_area[_i]
|
||||||
|
|
||||||
|
def equiv_prime(_x, _i):
|
||||||
|
return m * _x + t
|
||||||
|
|
||||||
|
fictive_temperature = np.array(
|
||||||
|
[fsolve(equiv, region.x[i], fprime=equiv_prime, args=(i,))[0] for i in range(len(region.x))])
|
||||||
|
|
||||||
|
t_g_fictive = fictive_temperature[:20].mean()
|
||||||
|
region.y = np.gradient(fictive_temperature, region.x)
|
||||||
|
|
||||||
|
return region, t_g_fictive
|
||||||
|
|
||||||
|
def calculate_tnmh(self, p0: list, glass: tuple[float, float], liquid: tuple[float, float],
|
||||||
|
tg: float = None, num_points: int = 200, return_fictive: bool = True) \
|
||||||
|
-> ('FitResult', Optional[float], Optional[DSC]):
|
||||||
|
|
||||||
|
dtf_dt, fictive_tg = self.get_fictive_cp(glass, liquid)
|
||||||
|
if tg is None:
|
||||||
|
tg = fictive_tg
|
||||||
|
|
||||||
|
temp_equidist = np.linspace(dtf_dt.x[0], dtf_dt.x[-1], num_points)
|
||||||
|
dtf_dt_equidist = np.interp(temp_equidist, dtf_dt.x, dtf_dt.y)
|
||||||
|
|
||||||
|
from ..fit.minimizer import FitRoutine
|
||||||
|
fitter = FitRoutine()
|
||||||
|
fitter.set_model(TNMH)
|
||||||
|
data = fitter.add_data(temp_equidist, dtf_dt_equidist)
|
||||||
|
data.set_parameter(p0 + [tg, self.value], var=[True] * 4 + [False] * 2, default_bounds=True)
|
||||||
|
|
||||||
|
res = fitter.run()[0]
|
||||||
|
|
||||||
|
if return_fictive:
|
||||||
|
return res, tg, dtf_dt
|
||||||
|
else:
|
||||||
|
return res
|
||||||
|
|
||||||
|
def glass_transition(self, glass, liquid):
|
||||||
|
low_idx = tuple(np.argmin(np.abs(self.x - g)) for g in glass)
|
||||||
|
high_idx = tuple(np.argmin(np.abs(self.x - l)) for l in liquid)
|
||||||
|
|
||||||
|
x = self.x[low_idx[0]:high_idx[1]]
|
||||||
|
y = self.y[low_idx[0]:high_idx[1]]
|
||||||
|
|
||||||
|
win_len = min(len(x) // 20, 51)
|
||||||
|
if win_len % 2 == 0:
|
||||||
|
win_len += 1
|
||||||
|
yy = savgol_filter(y, window_length=win_len, polyorder=1, deriv=1) / np.mean(np.diff(x))
|
||||||
|
|
||||||
|
high_idx = (high_idx[0] - low_idx[0], high_idx[1] - low_idx[0])
|
||||||
|
low_idx = (0, low_idx[1] - low_idx[0])
|
||||||
|
|
||||||
|
inflection = np.argmax(yy)
|
||||||
|
|
||||||
|
p1 = linregress(x[low_idx[0]:low_idx[1]], y[low_idx[0]:low_idx[1]])
|
||||||
|
glass_baseline = p1.slope * x + p1.intercept
|
||||||
|
|
||||||
|
p2 = linregress(x[high_idx[0]:high_idx[1]], y[high_idx[0]:high_idx[1]])
|
||||||
|
liquid_baseline = p2.slope * x + p2.intercept
|
||||||
|
|
||||||
|
tangent_line = yy[inflection] * (x - x[inflection]) + y[inflection]
|
||||||
|
|
||||||
|
onset = np.argmin(np.abs(tangent_line - glass_baseline))
|
||||||
|
end = np.argmin(np.abs(tangent_line - liquid_baseline))
|
||||||
|
|
||||||
|
midpoint = np.argmin(np.abs(y - 0.5 * (liquid_baseline[end] - glass_baseline[onset])))
|
||||||
|
cut_tangent = np.where((tangent_line > y.min() - 1) & (tangent_line < y.max() + 1))
|
||||||
|
|
||||||
|
glass = Points(x, glass_baseline, name=f'Glass baseline ({self.name})', value=self.value)
|
||||||
|
tangent = Points(x[cut_tangent], tangent_line[cut_tangent], name=f'Tangent ({self.name})', value=self.value)
|
||||||
|
liquid = Points(x, liquid_baseline, name=f'Liquid baseline ({self.name})', value=self.value)
|
||||||
|
|
||||||
|
ret_dic = {
|
||||||
|
'onset': (x[onset], glass_baseline[onset]),
|
||||||
|
'midpoint': (x[midpoint], y[midpoint]),
|
||||||
|
'end': (x[end], liquid_baseline[end]),
|
||||||
|
'inflection': (x[inflection], y[inflection]),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tg.update(ret_dic)
|
||||||
|
|
||||||
|
return ret_dic, glass, liquid, tangent
|
||||||
|
@ -62,6 +62,10 @@ class FID(Signal):
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def manual_phase(self, ph0: float = 0., ph1: float = 0., pvt: float = 0):
|
||||||
|
"""FID knows only how to phase correct in zeroth order"""
|
||||||
|
super().manual_phase(ph0=ph0)
|
||||||
|
|
||||||
def fourier(self) -> 'Spectrum':
|
def fourier(self) -> 'Spectrum':
|
||||||
ft = np.fft.fftshift(np.fft.fft(self._y)) / self.dx
|
ft = np.fft.fftshift(np.fft.fft(self._y)) / self.dx
|
||||||
freq = np.fft.fftshift(np.fft.fftfreq(self.length, self.dx))
|
freq = np.fft.fftshift(np.fft.fftfreq(self.length, self.dx))
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
from math import log10
|
||||||
from numbers import Number, Real
|
from numbers import Number, Real
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, TypeVar
|
from typing import Any, TypeVar
|
||||||
@ -318,6 +319,7 @@ class Points:
|
|||||||
if pts is None:
|
if pts is None:
|
||||||
pts = []
|
pts = []
|
||||||
|
|
||||||
|
if idx is not None:
|
||||||
for x in idx:
|
for x in idx:
|
||||||
if isinstance(x, tuple):
|
if isinstance(x, tuple):
|
||||||
x_idx = np.argmin(np.abs(self._x[self.mask] - (x[0]+x[1])/2))
|
x_idx = np.argmin(np.abs(self._x[self.mask] - (x[0]+x[1])/2))
|
||||||
@ -483,20 +485,25 @@ class Points:
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_data(self, x: np.ndarray = None, y: np.ndarray = None, y_err: np.ndarray = None) -> PointLike:
|
def set_data(self, x: np.ndarray = None, y: np.ndarray = None, y_err: np.ndarray | float = None, replace_mask: bool = True) -> PointLike:
|
||||||
if x is None:
|
if x is None:
|
||||||
x = self._x
|
x = self._x
|
||||||
if y is None:
|
if y is None:
|
||||||
y = self._y
|
y = self._y
|
||||||
if y_err is not None:
|
if y_err is None:
|
||||||
y_err = self._y_err
|
y_err = self._y_err
|
||||||
|
|
||||||
self._x, self._y, self._y_err, self.mask = self._prepare_xy(x, y, y_err)
|
self._x, self._y, self._y_err, mask = self._prepare_xy(x, y, y_err)
|
||||||
|
if replace_mask:
|
||||||
|
self.mask = mask
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def append(self, x: ArrayLike, y: ArrayLike, y_err: ArrayLike = None):
|
def append(self, x: ArrayLike, y: ArrayLike, y_err: ArrayLike = None, mask: ArrayLike = None):
|
||||||
|
if mask is None:
|
||||||
x, y, y_err, mask = self._prepare_xy(x, y, y_err)
|
x, y, y_err, mask = self._prepare_xy(x, y, y_err)
|
||||||
|
else:
|
||||||
|
x, y, y_err, _ = self._prepare_xy(x, y, y_err)
|
||||||
|
|
||||||
self._x = np.r_[self._x, x]
|
self._x = np.r_[self._x, x]
|
||||||
self._y = np.r_[self._y, y]
|
self._y = np.r_[self._y, y]
|
||||||
@ -541,6 +548,34 @@ class Points:
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def binning(self, value: float):
|
||||||
|
if value <= 0:
|
||||||
|
raise ValueError('value must be a positive number')
|
||||||
|
|
||||||
|
copy = self.copy()
|
||||||
|
|
||||||
|
upper_lim = (self.x[-1]//value + 1) * value
|
||||||
|
lower_lim = (self.x[0]//value) * value
|
||||||
|
|
||||||
|
offset = value / 2
|
||||||
|
|
||||||
|
xbins = np.linspace(lower_lim - offset, upper_lim + offset, num=int((upper_lim-lower_lim)/value + 2))
|
||||||
|
|
||||||
|
n, _ = np.histogram(copy.x, bins=xbins)
|
||||||
|
sum_y, _ = np.histogram(copy.x, bins=xbins, weights=copy.y)
|
||||||
|
sum_yerr_2, _ = np.histogram(copy.x, bins=xbins, weights=copy.y_err**2)
|
||||||
|
|
||||||
|
isnan = n != 0
|
||||||
|
|
||||||
|
n = n[isnan]
|
||||||
|
sum_y = sum_y[isnan]
|
||||||
|
sum_yerr_2 = sum_yerr_2[isnan]
|
||||||
|
xaxis = (xbins[:-1] + offset)[isnan]
|
||||||
|
|
||||||
|
copy.set_data(xaxis, sum_y/n, y_err=np.sqrt(sum_yerr_2/n))
|
||||||
|
|
||||||
|
return copy
|
||||||
|
|
||||||
def shift(self, points: int) -> PointLike:
|
def shift(self, points: int) -> PointLike:
|
||||||
"""
|
"""
|
||||||
Shift indexes of y values.
|
Shift indexes of y values.
|
||||||
|
@ -29,7 +29,7 @@ class Distribution(abc.ABC):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def susceptibility(omega, tau, *args):
|
def susceptibility(omega: ArrayLike, tau: ArrayLike, *args: Any):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -54,6 +54,9 @@ class ColeCole(Distribution):
|
|||||||
tau (array_like):
|
tau (array_like):
|
||||||
alpha (float):
|
alpha (float):
|
||||||
"""
|
"""
|
||||||
|
if alpha == 1:
|
||||||
|
return tau / (1 + omega**2 * tau**2)
|
||||||
|
|
||||||
omtau = (omega*tau)**alpha
|
omtau = (omega*tau)**alpha
|
||||||
return np.sin(alpha*np.pi/2) * omtau / (1 + omtau**2 + 2*np.cos(alpha*np.pi/2)*omtau) / omega
|
return np.sin(alpha*np.pi/2) * omtau / (1 + omtau**2 + 2*np.cos(alpha*np.pi/2)*omtau) / omega
|
||||||
|
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
from itertools import product
|
from itertools import product
|
||||||
|
from ctypes import c_double, cast, pointer, c_void_p
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from scipy import LowLevelCallable
|
||||||
from scipy.integrate import quad, simps as simpson
|
from scipy.integrate import quad, simps as simpson
|
||||||
|
|
||||||
from .base import Distribution
|
from .base import Distribution
|
||||||
from ..lib.utils import ArrayLike
|
from ..lib.utils import ArrayLike
|
||||||
from ..utils.constants import kB
|
from ..utils.constants import kB
|
||||||
|
|
||||||
|
from .helper import HAS_C_FUNCS, lib
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyMethodOverriding
|
||||||
class EnergyBarriers(Distribution):
|
class EnergyBarriers(Distribution):
|
||||||
name = 'Energy barriers'
|
name = 'Energy barriers'
|
||||||
parameter = [r'\tau_{0}', r'E_{m}', r'\Delta E']
|
parameter = [r'\tau_{0}', r'E_{m}', r'\Delta E']
|
||||||
@ -23,24 +28,30 @@ class EnergyBarriers(Distribution):
|
|||||||
return np.exp(-e_a / (kB * te)) / t0
|
return np.exp(-e_a / (kB * te)) / t0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def energydistribution(e_a, mu, sigma):
|
def energy_distribution(e_a, mu, sigma):
|
||||||
return np.exp(-0.5 * ((mu-e_a) / sigma) ** 2) / (np.sqrt(2 * np.pi) * sigma)
|
return np.exp(-0.5 * ((mu-e_a) / sigma) ** 2) / (np.sqrt(2 * np.pi) * sigma)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def correlation(t, temperature, *args):
|
def correlation(t: ArrayLike, temperature: ArrayLike, tau0: float, e_m: float, e_b: float) -> ArrayLike:
|
||||||
tau0, e_m, e_b = args
|
|
||||||
|
|
||||||
def integrand(e_a, ti, t0, mu, sigma, te):
|
|
||||||
# correlation time would go to inf for higher energies, so we use rate
|
|
||||||
return np.exp(-ti*EnergyBarriers.rate(t0, e_a, te)) * EnergyBarriers.energydistribution(e_a, mu, sigma)
|
|
||||||
|
|
||||||
t = np.atleast_1d(t)
|
t = np.atleast_1d(t)
|
||||||
temperature = np.atleast_1d(temperature)
|
temperature = np.atleast_1d(temperature)
|
||||||
|
|
||||||
e_axis = np.linspace(max(0, e_m-50*e_b), e_m+50*e_b, num=5001)
|
if HAS_C_FUNCS:
|
||||||
|
ret_val = _integrate_c(lib.energyDistCorrelation, t, temperature, tau0, e_m, e_b)
|
||||||
|
else:
|
||||||
|
ret_val = _integrate_py(_integrand_time, t, temperature, tau0, e_m, e_b)
|
||||||
|
|
||||||
ret_val = np.array([simpson(integrand(e_axis, o, tau0, e_m, e_b, tt), e_axis)
|
return ret_val
|
||||||
for o in t for tt in temperature])
|
|
||||||
|
@staticmethod
|
||||||
|
def specdens(omega: ArrayLike, temperature: ArrayLike, tau0: float, e_m: float, e_b: float) -> ArrayLike:
|
||||||
|
omega = np.atleast_1d(omega)
|
||||||
|
temperature = np.atleast_1d(temperature)
|
||||||
|
|
||||||
|
if HAS_C_FUNCS:
|
||||||
|
ret_val = _integrate_c(lib.energyDist_SD, omega, temperature, tau0, e_m, e_b)
|
||||||
|
else:
|
||||||
|
ret_val = _integrate_py(_integrand_sd, omega, temperature, tau0, e_m, e_b)
|
||||||
|
|
||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
@ -51,70 +62,73 @@ class EnergyBarriers(Distribution):
|
|||||||
omega = np.atleast_1d(omega)
|
omega = np.atleast_1d(omega)
|
||||||
temperature = np.atleast_1d(temperature)
|
temperature = np.atleast_1d(temperature)
|
||||||
|
|
||||||
e_axis = np.linspace(max(0, e_m-50*e_b), e_m+50*e_b, num=5001)
|
if HAS_C_FUNCS:
|
||||||
ret_val = []
|
ret_val = _integrate_c(lib.energyDistSuscReal, omega, temperature, tau0, e_m, e_b) + \
|
||||||
for o, tt in product(omega, temperature):
|
1j * _integrate_c(lib.energyDistSuscImag, omega, temperature, tau0, e_m, e_b)
|
||||||
ret_val.append(simpson(_integrand_freq_real(e_axis, o, tau0, e_m, e_b, tt), e_axis) -
|
else:
|
||||||
1j * simpson(_integrand_freq_imag(e_axis, o, tau0, e_m, e_b, tt), e_axis))
|
ret_val = _integrate_py(_integrand_susc_real, omega, temperature, tau0, e_m, e_b) + \
|
||||||
|
1j * _integrate_py(_integrand_susc_imag, omega, temperature, tau0, e_m, e_b)
|
||||||
return np.array(ret_val)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def specdens(omega, temperature, *args):
|
|
||||||
# in contrast to other spectral densities, it's omega and temperature
|
|
||||||
tau0, e_m, e_b = args
|
|
||||||
|
|
||||||
def integrand(e_a, w, t0, mu, sigma, t):
|
|
||||||
r = EnergyBarriers.rate(t0, e_a, t)
|
|
||||||
return r/(r**2 + w**2) * EnergyBarriers.energydistribution(e_a, mu, sigma)
|
|
||||||
|
|
||||||
omega = np.atleast_1d(omega)
|
|
||||||
temperature = np.atleast_1d(temperature)
|
|
||||||
|
|
||||||
e_axis = np.linspace(max(0, e_m-50*e_b), e_m+50*e_b, num=5001)
|
|
||||||
|
|
||||||
ret_val = np.array([simpson(integrand(e_axis, o, tau0, e_m, e_b, tt), e_axis)
|
|
||||||
for o in omega for tt in temperature])
|
|
||||||
|
|
||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mean(*args):
|
def mean(temperature, tau0, ea):
|
||||||
return args[1]*np.exp(args[2]/(kB*args[0]))
|
return tau0*np.exp(ea/(kB*temperature))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def logmean(*args):
|
def logmean(temperature, tau0, ea):
|
||||||
return args[1] + args[2] / (kB * args[0])
|
return tau0 + ea / (kB * temperature)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def max(*args):
|
def max(*args):
|
||||||
return args[1] * np.exp(args[2] / (kB * args[0]))
|
return args[1] * np.exp(args[2] / (kB * args[0]))
|
||||||
|
|
||||||
|
|
||||||
def _integrate_process_imag(args):
|
# helper functions
|
||||||
pass
|
def _integrate_c(func, omega: np.ndarray, temperature: np.ndarray, tau0: float, e_m: float, e_b: float) -> np.ndarray:
|
||||||
|
res = []
|
||||||
|
for o, t in product(omega, temperature):
|
||||||
|
c = (c_double * 5)(o, tau0, e_m, e_b, t)
|
||||||
|
user_data = cast(pointer(c), c_void_p)
|
||||||
|
area = quad(LowLevelCallable(func, user_data), 0, np.infty, epsabs=1e-13)[0]
|
||||||
|
|
||||||
|
res.append(area)
|
||||||
|
|
||||||
|
ret_val = np.array(res).reshape(omega.shape[0], temperature.shape[0])
|
||||||
|
|
||||||
|
return ret_val.squeeze()
|
||||||
|
|
||||||
|
|
||||||
def _integrate_process_real(args):
|
def _integrate_py(func, axis, temp, tau0, e_m, e_b):
|
||||||
omega_i, t, tau0, mu, sigma, temp_j = args
|
x = np.atleast_1d(axis)
|
||||||
return quad(_integrand_freq_real(), 0, 10, args=(omega_i, t, tau0, mu, sigma, temp_j))[0]
|
temperature = np.atleast_1d(temp)
|
||||||
|
|
||||||
|
e_axis = np.linspace(max(0., e_m - 50*e_b), e_m + 50*e_b, num=5001)
|
||||||
|
ret_val = []
|
||||||
|
for o, tt in product(x, temperature):
|
||||||
|
ret_val.append(simpson(func(e_axis, o, tau0, e_m, e_b, tt), e_axis))
|
||||||
|
|
||||||
|
ret_val = np.array(ret_val).reshape(x.shape[0], temperature.shape[0])
|
||||||
|
|
||||||
|
return ret_val.squeeze()
|
||||||
|
|
||||||
|
|
||||||
def _integrate_process_time(args):
|
# python integrands
|
||||||
omega_i, t, tau0, mu, sigma, temp_j = args
|
def _integrand_sd(u, omega, tau0, mu, sigma, temp):
|
||||||
return quad(_integrand_time, 0, 10, args=(omega_i, t, tau0, mu, sigma, temp_j))[0]
|
|
||||||
|
|
||||||
|
|
||||||
def _integrand_freq_real(u, omega, tau0, mu, sigma, temp):
|
|
||||||
r = EnergyBarriers.rate(tau0, u, temp)
|
r = EnergyBarriers.rate(tau0, u, temp)
|
||||||
return 1 / (r**2 + omega**2) * EnergyBarriers.energydistribution(u, mu, sigma)
|
return r / (r**2 + omega**2) * EnergyBarriers.energy_distribution(u, mu, sigma)
|
||||||
|
|
||||||
|
|
||||||
def _integrand_freq_imag(u, omega, tau0, mu, sigma, temp):
|
def _integrand_susc_real(u, omega, tau0, mu, sigma, temp):
|
||||||
|
r = EnergyBarriers.rate(tau0, u, temp)
|
||||||
|
return 1 / (r**2 + omega**2) * EnergyBarriers.energy_distribution(u, mu, sigma)
|
||||||
|
|
||||||
|
|
||||||
|
def _integrand_susc_imag(u, omega, tau0, mu, sigma, temp):
|
||||||
rate = EnergyBarriers.rate(tau0, u, temp)
|
rate = EnergyBarriers.rate(tau0, u, temp)
|
||||||
return omega * rate / (rate**2 + omega**2) * EnergyBarriers.energydistribution(u, mu, sigma)
|
return omega * rate / (rate**2 + omega**2) * EnergyBarriers.energy_distribution(u, mu, sigma)
|
||||||
|
|
||||||
|
|
||||||
def _integrand_time(u, t, tau0, mu, sigma, temp):
|
def _integrand_time(u, t, tau0, mu, sigma, temp):
|
||||||
rate = EnergyBarriers.rate(tau0, u, temp)
|
rate = EnergyBarriers.rate(tau0, u, temp)
|
||||||
return EnergyBarriers.energydistribution(u, mu, sigma) * np.exp(-t*rate)
|
return EnergyBarriers.energy_distribution(u, mu, sigma) * np.exp(-t*rate)
|
||||||
|
48
src/nmreval/distributions/helper.py
Normal file
48
src/nmreval/distributions/helper.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from ctypes import CDLL, c_double, c_void_p
|
||||||
|
|
||||||
|
from ..lib.logger import logger
|
||||||
|
|
||||||
|
|
||||||
|
lib = None
|
||||||
|
try:
|
||||||
|
lib = CDLL(str(Path(__file__).parents[1] / 'clib' / 'integrate.so'))
|
||||||
|
|
||||||
|
# FFHS integrand
|
||||||
|
lib.ffhsSD.restype = c_double
|
||||||
|
lib.ffhsSD.argtypes = (c_double, c_void_p)
|
||||||
|
|
||||||
|
# Log-Gaussian integrands
|
||||||
|
lib.logGaussian_imag_high.restype = c_double
|
||||||
|
lib.logGaussian_imag_high.argtypes = (c_double, c_void_p)
|
||||||
|
lib.logGaussian_imag_low.restype = c_double
|
||||||
|
lib.logGaussian_imag_low.argtypes = (c_double, c_void_p)
|
||||||
|
|
||||||
|
lib.logGaussian_real_high.restype = c_double
|
||||||
|
lib.logGaussian_real_high.argtypes = (c_double, c_void_p)
|
||||||
|
lib.logGaussian_real_low.restype = c_double
|
||||||
|
lib.logGaussian_real_low.argtypes = (c_double, c_void_p)
|
||||||
|
|
||||||
|
lib.logGaussianCorrelation.restype = c_double
|
||||||
|
lib.logGaussianCorrelation.argtypes = (c_double, c_void_p)
|
||||||
|
|
||||||
|
# integrands for distribution of energies
|
||||||
|
lib.energyDist_SD.restype = c_double
|
||||||
|
lib.energyDist_SD.argtypes = (c_double, c_void_p)
|
||||||
|
|
||||||
|
lib.energyDistCorrelation.restype = c_double
|
||||||
|
lib.energyDistCorrelation.argtypes = (c_double, c_void_p)
|
||||||
|
|
||||||
|
lib.energyDistSuscReal.restype = c_double
|
||||||
|
lib.energyDistSuscReal.argtypes = (c_double, c_void_p)
|
||||||
|
lib.energyDistSuscImag.restype = c_double
|
||||||
|
lib.energyDistSuscImag.argtypes = (c_double, c_void_p)
|
||||||
|
|
||||||
|
|
||||||
|
HAS_C_FUNCS = True
|
||||||
|
logger.info('Use C functions')
|
||||||
|
except OSError:
|
||||||
|
HAS_C_FUNCS = False
|
||||||
|
logger.info('Use python functions')
|
||||||
|
|
@ -1,9 +1,16 @@
|
|||||||
|
import ctypes
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from scipy import LowLevelCallable
|
||||||
from scipy.integrate import quad
|
from scipy.integrate import quad
|
||||||
|
|
||||||
|
from .helper import HAS_C_FUNCS, lib
|
||||||
from .base import Distribution
|
from .base import Distribution
|
||||||
|
|
||||||
|
|
||||||
|
# Everything except spectral density is implemented in Python only because the only use case of FFHS is NMR
|
||||||
|
# field cycling measurements with T1 results
|
||||||
|
|
||||||
class FFHS(Distribution):
|
class FFHS(Distribution):
|
||||||
name = 'Intermolecular (FFHS)'
|
name = 'Intermolecular (FFHS)'
|
||||||
parameter = None
|
parameter = None
|
||||||
@ -19,19 +26,30 @@ class FFHS(Distribution):
|
|||||||
def integrand(u, tt, tau0):
|
def integrand(u, tt, tau0):
|
||||||
return FFHS.distribution(u, tau0) * np.exp(-tt/u) / u
|
return FFHS.distribution(u, tau0) * np.exp(-tt/u) / u
|
||||||
|
|
||||||
ret_val = np.array([quad(integrand, 0, np.infty, args=(tt, tau0), epsabs=1e-12, epsrel=1e-12)[0] for tt in t])
|
ret_val = np.array([quad(integrand, 0, np.infty, args=(tt, tau0))[0] for tt in t])
|
||||||
|
|
||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def specdens(omega, tau0, *args):
|
def specdens_py(omega, tau0):
|
||||||
def integrand(u, o, tau0):
|
def integrand(u, o, tau0):
|
||||||
return u**4 * tau0 / (81 + 9*u**2 - 2*u**4 + u**6) / (u**4 + (o*tau0)**2)
|
return u**4 * tau0 / (81 + 9*u**2 - 2*u**4 + u**6) / (u**4 + (o*tau0)**2)
|
||||||
# return FFHS.distribution(u, tau0) * u / (1+o**2 * u**2)
|
# return FFHS.distribution(u, tau0) * u / (1+o**2 * u**2)
|
||||||
|
|
||||||
ret_val = np.array([quad(integrand, 0, np.infty, args=(o, tau0), epsabs=1e-12, epsrel=1e-12)[0] for o in omega])
|
ret_val = np.array([quad(integrand, 0, np.infty, args=(o, tau0))[0] for o in omega])
|
||||||
|
|
||||||
return ret_val
|
return ret_val * 54 / np.pi
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def specdens_c(omega, tau0):
|
||||||
|
res = []
|
||||||
|
for o in omega:
|
||||||
|
c = (ctypes.c_double * 2)(o, tau0)
|
||||||
|
user_data = ctypes.cast(ctypes.pointer(c), ctypes.c_void_p)
|
||||||
|
func = LowLevelCallable(lib.ffhsSD, user_data)
|
||||||
|
res.append(quad(func, 0, np.infty)[0])
|
||||||
|
|
||||||
|
return np.array(res) * 54 / np.pi
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def susceptibility(omega, tau0, *args):
|
def susceptibility(omega, tau0, *args):
|
||||||
@ -41,15 +59,16 @@ class FFHS(Distribution):
|
|||||||
def integrand_imag(u, o, tau0):
|
def integrand_imag(u, o, tau0):
|
||||||
return FFHS.distribution(u, tau0) * o*u / (1+o**2 * u**2)
|
return FFHS.distribution(u, tau0) * o*u / (1+o**2 * u**2)
|
||||||
|
|
||||||
ret_val = np.array([quad(integrand_real, 0, np.infty, args=(o, tau0),
|
ret_val = np.array([quad(integrand_real, 0, np.infty, args=(o, tau0))[0] for o in omega], dtype=complex)
|
||||||
epsabs=1e-12, epsrel=1e-12)[0] for o in omega], dtype=complex)
|
|
||||||
|
|
||||||
ret_val.imag += np.array([quad(integrand_imag, 0, np.infty, args=(o, tau0),
|
ret_val.imag += np.array([quad(integrand_imag, 0, np.infty, args=(o, tau0))[0] for o in omega])
|
||||||
epsabs=1e-12, epsrel=1e-12)[0] for o in omega])
|
|
||||||
|
|
||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
|
|
||||||
|
FFHS.specdens = FFHS.specdens_c if HAS_C_FUNCS else FFHS.specdens_py
|
||||||
|
|
||||||
|
|
||||||
# class Bessel(Distribution):
|
# class Bessel(Distribution):
|
||||||
# name = 'Intermolecular (Bessel)'
|
# name = 'Intermolecular (Bessel)'
|
||||||
# parameter = None
|
# parameter = None
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
|
import ctypes
|
||||||
from multiprocessing import Pool, cpu_count
|
from multiprocessing import Pool, cpu_count
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from scipy import LowLevelCallable
|
||||||
|
from scipy.special import erf
|
||||||
|
|
||||||
|
from nmreval.lib.utils import ArrayLike
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from scipy.integrate import simpson
|
from scipy.integrate import simpson
|
||||||
@ -9,89 +15,146 @@ except ImportError:
|
|||||||
from scipy.integrate import simps as simpson
|
from scipy.integrate import simps as simpson
|
||||||
from scipy.integrate import quad
|
from scipy.integrate import quad
|
||||||
|
|
||||||
from .base import Distribution
|
from nmreval.distributions.helper import HAS_C_FUNCS, lib
|
||||||
|
from nmreval.distributions.base import Distribution
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['LogGaussian']
|
__all__ = ['LogGaussian']
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyMethodOverriding
|
||||||
class LogGaussian(Distribution):
|
class LogGaussian(Distribution):
|
||||||
name = 'Log-Gaussian'
|
name = 'Log-Gaussian'
|
||||||
parameter = [r'\sigma']
|
parameter = [r'\sigma']
|
||||||
bounds = [(0, 10)]
|
bounds = [(0, 10)]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def distribution(tau, tau0, sigma: float):
|
def distribution(tau: ArrayLike, tau0: ArrayLike, sigma: float) -> ArrayLike:
|
||||||
return np.exp(-0.5*(np.log(tau/tau0)/sigma)**2)/np.sqrt(2*np.pi)/sigma
|
return np.exp(-0.5*(np.log(tau/tau0)/sigma)**2)/np.sqrt(2*np.pi)/sigma
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def correlation(t, tau0, sigma: float):
|
def correlation(t: ArrayLike, tau0: ArrayLike, sigma: float):
|
||||||
_t = np.atleast_1d(t)
|
_t = np.atleast_1d(t)
|
||||||
_tau = np.atleast_1d(tau0)
|
_tau = np.atleast_1d(tau0)
|
||||||
|
|
||||||
pool = Pool(processes=min(cpu_count(), 4))
|
if HAS_C_FUNCS:
|
||||||
integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_t, _tau)]
|
res = _integrate_correlation_c(_t, _tau, sigma)
|
||||||
|
else:
|
||||||
|
res = _integration_parallel(_t, _tau, sigma, _integrate_process_time)
|
||||||
|
|
||||||
with np.errstate(divide='ignore'):
|
return res.squeeze()
|
||||||
res = np.array(pool.map(_integrate_process_time, integration_ranges))
|
|
||||||
ret_val = res.reshape((_t.shape[0], _tau.shape[0]))
|
|
||||||
|
|
||||||
return ret_val.squeeze()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def susceptibility(omega, tau0, sigma: float):
|
def susceptibility(omega: ArrayLike, tau0: ArrayLike, sigma: float):
|
||||||
_omega = np.atleast_1d(omega)
|
_omega = np.atleast_1d(omega)
|
||||||
_tau = np.atleast_1d(tau0)
|
_tau = np.atleast_1d(tau0)
|
||||||
|
|
||||||
pool = Pool(processes=min(cpu_count(), 4))
|
if HAS_C_FUNCS:
|
||||||
integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_omega, _tau)]
|
res_real = _integrate_susc_real_c(_omega, _tau, sigma)
|
||||||
|
res_imag = _integrate_susc_imag_c(_omega, _tau, sigma)
|
||||||
|
else:
|
||||||
|
res_real = _integration_parallel(_omega, _tau, sigma, _integrate_process_imag)
|
||||||
|
res_imag = _integration_parallel(_omega, _tau, sigma, _integrate_process_real)
|
||||||
|
|
||||||
with np.errstate(divide='ignore'):
|
return (res_real + 1j * res_imag).squeeze()
|
||||||
res_real = np.array(pool.map(_integrate_process_imag, integration_ranges))
|
|
||||||
res_imag = np.array(pool.map(_integrate_process_real, integration_ranges))
|
|
||||||
ret_val = (res_real+1j*res_imag).reshape((_omega.shape[0], _tau.shape[0]))
|
|
||||||
|
|
||||||
return ret_val.squeeze()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def specdens(omega, tau0, sigma):
|
def specdens(omega: ArrayLike, tau: ArrayLike, sigma: float) -> np.ndarray:
|
||||||
_omega = np.atleast_1d(omega)
|
_omega = np.atleast_1d(omega)
|
||||||
_tau = np.atleast_1d(tau0)
|
_tau = np.atleast_1d(tau)
|
||||||
|
|
||||||
pool = Pool(processes=min(cpu_count(), 4))
|
if HAS_C_FUNCS:
|
||||||
integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_omega, _tau)]
|
ret_val = _integrate_susc_imag_c(_omega, _tau, sigma)
|
||||||
|
else:
|
||||||
with np.errstate(divide='ignore'):
|
ret_val = _integration_parallel(_omega, _tau, sigma, _integrate_process_imag)
|
||||||
res = np.array(pool.map(_integrate_process_imag, integration_ranges))
|
|
||||||
ret_val = res.reshape((_omega.shape[0], _tau.shape[0]))
|
|
||||||
|
|
||||||
ret_val /= _omega[:, None]
|
ret_val /= _omega[:, None]
|
||||||
|
ret_val[_omega == 0, :] = _tau[None, :] * np.exp(sigma**2 / 2)
|
||||||
|
|
||||||
return ret_val.squeeze()
|
return ret_val.squeeze()
|
||||||
|
|
||||||
def mean(*args):
|
@staticmethod
|
||||||
return args[0]*np.exp(args[1]**2 / 2)
|
def mean(tau, sigma):
|
||||||
|
return tau*np.exp(sigma**2 / 2)
|
||||||
|
|
||||||
|
|
||||||
def _integrate_process_imag(args):
|
def _integration_parallel(x: np.ndarray, tau: np.ndarray, sigma: float, func: Callable) -> np.ndarray:
|
||||||
omega_i, tau_j, sigma = args
|
pool = Pool(processes=min(cpu_count(), 4))
|
||||||
area = quad(_integrand_freq_imag_high, 0, 50, args=(omega_i, tau_j, sigma))[0]
|
integration_ranges = [(x_i, tau_j, sigma) for (x_i, tau_j) in product(x, tau)]
|
||||||
area += quad(_integrand_freq_imag_low, -50, 0, args=(omega_i, tau_j, sigma))[0]
|
|
||||||
|
with np.errstate(divide='ignore'):
|
||||||
|
res = pool.map(func, integration_ranges)
|
||||||
|
|
||||||
|
res = np.array(res).reshape((x.shape[0], tau.shape[0]))
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def _integrate_susc_imag_c(omega: np.ndarray, tau: np.ndarray, sigma: float) -> np.ndarray:
|
||||||
|
return _integrate_susc_c(lib.logGaussian_imag_low, lib.logGaussian_imag_high, omega, tau, sigma)
|
||||||
|
|
||||||
|
|
||||||
|
def _integrate_susc_real_c(omega: np.ndarray, tau: np.ndarray, sigma: float) -> np.ndarray:
|
||||||
|
return _integrate_susc_c(lib.logGaussian_real_low, lib.logGaussian_real_high, omega, tau, sigma)
|
||||||
|
|
||||||
|
|
||||||
|
def _integrate_susc_c(lowfunc, highfunc, omega, tau, sigma):
|
||||||
|
res = []
|
||||||
|
|
||||||
|
for o, t in product(omega, tau):
|
||||||
|
c = (ctypes.c_double * 3)(o, t, sigma)
|
||||||
|
user_data = ctypes.cast(ctypes.pointer(c), ctypes.c_void_p)
|
||||||
|
|
||||||
|
area = 0
|
||||||
|
for (func, limits) in [(highfunc, (0, np.inf)), (lowfunc, (-np.infty, 0))]:
|
||||||
|
epsabs = 1e-12
|
||||||
|
while epsabs > 1e-25:
|
||||||
|
a = quad(LowLevelCallable(func, user_data), *limits, epsabs=epsabs, epsrel=1e-12, full_output=1)
|
||||||
|
if a[2]['last'] > 2 or a[0] < 1e-48:
|
||||||
|
break
|
||||||
|
epsabs /= 10.
|
||||||
|
|
||||||
|
area += a[0]
|
||||||
|
|
||||||
|
res.append(area)
|
||||||
|
|
||||||
|
res = np.asanyarray(res).reshape((omega.shape[0], tau.shape[0]))
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def _integrate_process_imag(omega, tau, sigma):
|
||||||
|
area = quad(_integrand_freq_imag_high, 0, 50, args=(omega, tau, sigma), epsabs=1e-12, epsrel=1e-12)[0]
|
||||||
|
area += quad(_integrand_freq_imag_low, -50, 0, args=(omega, tau, sigma), epsabs=1e-12, epsrel=1e-12)[0]
|
||||||
|
|
||||||
return area
|
return area
|
||||||
|
|
||||||
|
|
||||||
def _integrate_process_real(args):
|
def _integrate_process_real(omega: float, tau: float, sigma: float):
|
||||||
omega_i, tau_j, sigma = args
|
area = quad(_integrand_freq_real_high, 0, 50, args=(omega, tau, sigma))[0]
|
||||||
area = quad(_integrand_freq_real_high, 0, 50, args=(omega_i, tau_j, sigma))[0]
|
area += quad(_integrand_freq_real_low, -50, 0, args=(omega, tau, sigma))[0]
|
||||||
area += quad(_integrand_freq_real_low, -50, 0, args=(omega_i, tau_j, sigma))[0]
|
|
||||||
|
|
||||||
return area
|
return area
|
||||||
|
|
||||||
|
|
||||||
def _integrate_process_time(args):
|
def _integrate_correlation_c(t, tau, sigma):
|
||||||
omega_i, tau_j, sigma = args
|
res = []
|
||||||
return quad(_integrand_time, -50, 50, args=(omega_i, tau_j, sigma))[0]
|
|
||||||
|
for t_i, tau_i in product(t, tau):
|
||||||
|
c = (ctypes.c_double * 3)(t_i, tau_i, sigma)
|
||||||
|
user_data = ctypes.cast(ctypes.pointer(c), ctypes.c_void_p)
|
||||||
|
|
||||||
|
area = quad(LowLevelCallable(lib.logGaussianCorrelation, user_data), -np.infty, np.infty)[0]
|
||||||
|
|
||||||
|
res.append(area)
|
||||||
|
|
||||||
|
res = np.asanyarray(res).reshape((t.shape[0], tau.shape[0]))
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def _integrate_process_time(omega, tau, sigma):
|
||||||
|
return quad(_integrand_time, -50, 50, args=(omega, tau, sigma), epsabs=1e-12, epsrel=1e-12)[0]
|
||||||
|
|
||||||
|
|
||||||
def _integrand_time(u, t, tau, sigma):
|
def _integrand_time(u, t, tau, sigma):
|
||||||
|
@ -1,289 +0,0 @@
|
|||||||
import os
|
|
||||||
import argparse
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import scipy.interpolate
|
|
||||||
from scipy.integrate import simps
|
|
||||||
|
|
||||||
from ..io.dsc import DSCSample, Cyclohexane
|
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Calibrate DSC data')
|
|
||||||
parser.add_argument('sample', type=str, help='filename of DSC sample')
|
|
||||||
parser.add_argument('empty', type=str, help='filename of empty pan')
|
|
||||||
parser.add_argument('reference', help='filename of reference', type=str)
|
|
||||||
parser.add_argument('--cooling', help='Show figure of found cooling rates', action='store_true')
|
|
||||||
|
|
||||||
|
|
||||||
def evaluate(sample, empty, reference, ref_points=Cyclohexane, show_cooling=False):
|
|
||||||
sample = DSCSample(sample)
|
|
||||||
empty = DSCSample(empty)
|
|
||||||
reference = DSCSample(reference)
|
|
||||||
|
|
||||||
if show_cooling:
|
|
||||||
fig, ax = plt.subplots()
|
|
||||||
print('\n')
|
|
||||||
for k, v in sample.cooling.items():
|
|
||||||
print('Plot run {} with cooling rate {} K/min'.format(k, v))
|
|
||||||
c = sample.flow_data(v, mode='c')
|
|
||||||
ax.plot(c[0], c[1], label=str(v)+' K/min')
|
|
||||||
ax.set_xlabel('T / K')
|
|
||||||
plt.legend()
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
run_list = []
|
|
||||||
if len(sample.heating) > 1:
|
|
||||||
run = None
|
|
||||||
print('\nMultiple heat rates found:')
|
|
||||||
for k, v in sample.heating.items():
|
|
||||||
print(' run {}: {} K/min'.format(k, v))
|
|
||||||
while run not in sample.heating:
|
|
||||||
# choose your own adventure!!!
|
|
||||||
value = input('\nPlease select a run (press Enter for all heat rates): ')
|
|
||||||
if value == '':
|
|
||||||
run_list = list(sample.heating.keys())
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
run = int(value)
|
|
||||||
run_list = [run]
|
|
||||||
else:
|
|
||||||
run_list = list(sample.heating.keys())
|
|
||||||
|
|
||||||
for run in run_list:
|
|
||||||
rate = sample.heating[run]
|
|
||||||
|
|
||||||
print('\nProcessing heat rate {} K/min'.format(rate))
|
|
||||||
|
|
||||||
print('Load data of heating data')
|
|
||||||
len_sample = sample.length(run)
|
|
||||||
|
|
||||||
# sanity checks
|
|
||||||
try:
|
|
||||||
reference_data = reference.flow_data(rate)
|
|
||||||
except IndexError:
|
|
||||||
print('ERROR: Reference measurement has no heat rate {} K/min'.format(rate))
|
|
||||||
print('Stop evaluation')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
try:
|
|
||||||
run_baseline = empty.get_run(rate)
|
|
||||||
except ValueError:
|
|
||||||
print('ERROR: Empty measurement has no heat rate {} K/min'.format(rate))
|
|
||||||
print('Stop evaluation')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
len_baseline = empty.length(run_baseline)
|
|
||||||
if len_baseline != len_sample:
|
|
||||||
print('WARNING: measurements differ by {} points'.format(abs(len_baseline - len_sample)))
|
|
||||||
# max_length = min(len_baseline, len_sample)
|
|
||||||
|
|
||||||
sample_data = sample.flow_data(rate, length=None)
|
|
||||||
empty_data = empty.flow_data(rate, length=None)
|
|
||||||
|
|
||||||
# plot input data
|
|
||||||
fig1, ax1 = plt.subplots(2, 3, **{'figsize': (10, 6)})
|
|
||||||
ax1[0, 0].set_title('raw data')
|
|
||||||
ax1[0, 0].set_xlabel('T / K')
|
|
||||||
|
|
||||||
ax1[0, 0].plot(sample_data[0], sample_data[1], 'k-', label='Sample')
|
|
||||||
ax1[0, 0].plot(empty_data[0], empty_data[1], 'b-', label='Empty')
|
|
||||||
ax1[0, 0].plot(reference_data[0], reference_data[1], 'r-', label='Reference')
|
|
||||||
ax1[0, 0].legend()
|
|
||||||
|
|
||||||
print('Substract empty data\n')
|
|
||||||
sample_baseline = sample_data.copy()
|
|
||||||
empty_y = empty_data[1]
|
|
||||||
if len_sample != len_baseline:
|
|
||||||
with np.errstate(all='ignore'):
|
|
||||||
empty_y = scipy.interpolate.interp1d(empty_data[0], empty_data[1],
|
|
||||||
fill_value='extrapolate')(sample_data[0])
|
|
||||||
sample_baseline[1] = sample_data[1] - empty_y
|
|
||||||
|
|
||||||
# plot baseline correction
|
|
||||||
ax1[0, 1].set_title('baseline correction')
|
|
||||||
ax1[0, 1].set_xlabel('T / K')
|
|
||||||
|
|
||||||
ax1[0, 1].plot(sample_data[0], sample_data[1], 'k--', label='Raw')
|
|
||||||
ax1[0, 1].plot(sample_baseline[0], sample_baseline[1], 'k-', label='Baseline corrected')
|
|
||||||
ax1[0, 1].plot(empty_data[0], empty_data[1], 'b-', label='Empty')
|
|
||||||
ax1[0, 1].legend()
|
|
||||||
|
|
||||||
print('Load isothermal data around heat rate')
|
|
||||||
mean_isotherms = []
|
|
||||||
for offset, where, ls in [(-1, 'low', '-'), (1, 'high', '--')]:
|
|
||||||
# read isotherms and baseline correct
|
|
||||||
len_baseline = empty.length(run_baseline+offset)
|
|
||||||
len_sample = sample.length(run+offset)
|
|
||||||
if len_baseline != len_sample:
|
|
||||||
print('WARNING: {} T isotherms differ by {} points'.format(where, abs(len_baseline-len_sample)))
|
|
||||||
|
|
||||||
max_length = min(len_baseline, len_sample)
|
|
||||||
isotherm_sample = sample.isotherm_data(run_baseline+offset, length=max_length)
|
|
||||||
isotherm_empty = empty.isotherm_data(run+offset, length=max_length)
|
|
||||||
|
|
||||||
isotherm_sample[1] -= isotherm_empty[1]
|
|
||||||
|
|
||||||
# get mean isotherm value
|
|
||||||
m = np.polyfit(isotherm_sample[0, 200:-200], isotherm_sample[1, 200:-200], 0)[0]
|
|
||||||
mean_isotherms.append(m)
|
|
||||||
print('Calculated {} heat flow: {:.4} mW'.format(where, m))
|
|
||||||
|
|
||||||
ax1[0, 2].plot(isotherm_sample[0], isotherm_sample[1], 'k--')
|
|
||||||
|
|
||||||
# calculate slope from difference between isotherms
|
|
||||||
slope = (mean_isotherms[1]-mean_isotherms[0]) / (sample_data[2, -1] - empty_data[2, 0])
|
|
||||||
print('Heat flow slope from isotherms: {:.4} per minute'.format(slope*60))
|
|
||||||
|
|
||||||
# calculate mean slope of heat flow at points in the beginning
|
|
||||||
slope_baseline = np.gradient(sample_baseline[1, int(4000/rate):int(9000/rate)],
|
|
||||||
sample_baseline[2, 300]-sample_baseline[2, 299]).mean()
|
|
||||||
print('Heat flow slope from initial heating: {:.4f} per minute\n'.format(slope_baseline*60))
|
|
||||||
|
|
||||||
drift_corrected = sample_baseline[1] - mean_isotherms[0] - (sample_baseline[2]-empty_data[2, 0])*slope
|
|
||||||
drift_from_slope = sample_baseline[1] - mean_isotherms[0] - (sample_baseline[2]-empty_data[2, 0])*slope_baseline
|
|
||||||
|
|
||||||
# plot
|
|
||||||
ax1[0, 2].axhline(mean_isotherms[0], linestyle=':')
|
|
||||||
ax1[0, 2].axhline(mean_isotherms[1], linestyle=':')
|
|
||||||
|
|
||||||
ax1[0, 2].plot(sample_baseline[2], sample_baseline[1], 'k-', label='Baseline corrected')
|
|
||||||
ax1[0, 2].plot(sample_baseline[2], drift_corrected, 'g-', label='Corrected (isotherm)')
|
|
||||||
ax1[0, 2].plot(sample_baseline[2], drift_from_slope, 'b-', label='Corrected (heating)')
|
|
||||||
|
|
||||||
ax1[0, 2].plot(sample_baseline[2], mean_isotherms[0] + (sample_baseline[2]-empty_data[2, 0])*slope, 'g--')
|
|
||||||
ax1[0, 2].plot(sample_baseline[2], mean_isotherms[0] + slope_baseline*(sample_baseline[2]-empty_data[2, 0]),
|
|
||||||
'b--')
|
|
||||||
|
|
||||||
ax1[0, 2].plot(sample_baseline[2, int(4000/rate):int(9000/rate)],
|
|
||||||
sample_baseline[1, int(4000/rate):int(9000/rate)], 'r--')
|
|
||||||
|
|
||||||
ax1[0, 2].set_title('time dependence')
|
|
||||||
ax1[0, 2].set_xlabel('t / s')
|
|
||||||
ax1[0, 2].legend()
|
|
||||||
|
|
||||||
melts = []
|
|
||||||
for i, (ref_temp, enthalpy) in enumerate(ref_points.transitions):
|
|
||||||
# region around reference peaks
|
|
||||||
t_low_lim = ref_temp - 15
|
|
||||||
t_high_lim = ref_temp + 15
|
|
||||||
low_border = np.argmin(np.abs(reference_data[0]-t_low_lim))
|
|
||||||
high_border = np.argmin(np.abs(reference_data[0]-t_high_lim))
|
|
||||||
ref_zoom = reference_data[:, low_border:high_border]
|
|
||||||
x_val = np.array([[ref_zoom[0, 0], 1],
|
|
||||||
[ref_zoom[0, -1], 1]])
|
|
||||||
y_val = np.array([ref_zoom[1, 0],
|
|
||||||
ref_zoom[1, -1]])
|
|
||||||
print('Baseline correct reference of %.2f transition' % ref_temp)
|
|
||||||
sol = np.linalg.solve(x_val, y_val)
|
|
||||||
ref_zoom[1] -= (ref_zoom[0] * sol[0] + sol[1])
|
|
||||||
peak_max = ref_zoom[:, np.argmax(ref_zoom[1])]
|
|
||||||
integration_limit = np.argmin(abs(ref_zoom[0]-peak_max[0]+3)), np.argmin(abs(ref_zoom[0]-peak_max[0]-3))
|
|
||||||
|
|
||||||
# substract baseline around reference peaks
|
|
||||||
x_val = np.array([[ref_zoom[0, integration_limit[0]], 1],
|
|
||||||
[ref_zoom[0, integration_limit[1]], 1]])
|
|
||||||
y_val = np.array([ref_zoom[1, integration_limit[0]],
|
|
||||||
ref_zoom[1, integration_limit[1]]])
|
|
||||||
|
|
||||||
print('Baseline correct reference of %.2f transition' % ref_temp)
|
|
||||||
sol = np.linalg.solve(x_val, y_val)
|
|
||||||
ref_zoom[1] -= (ref_zoom[0] * sol[0] + sol[1])
|
|
||||||
|
|
||||||
# calculate onset slope (use points at position of maximum gradient +/- 100/rate)
|
|
||||||
ref_grad = np.gradient(ref_zoom[1])
|
|
||||||
max_grad = np.argmax(ref_grad)
|
|
||||||
|
|
||||||
x_val = np.array([[ref_zoom[0, max_grad-int(100/rate)], 1],
|
|
||||||
[ref_zoom[0, max_grad+int(100/rate)+1], 1]])
|
|
||||||
y_val = np.array([ref_zoom[1, max_grad-int(100/rate)],
|
|
||||||
ref_zoom[1, max_grad+int(100/rate)+1]])
|
|
||||||
sol = np.linalg.solve(x_val, y_val)
|
|
||||||
onset = sol[0]*ref_zoom[0] + sol[1]
|
|
||||||
|
|
||||||
melts.append(-sol[1]/sol[0])
|
|
||||||
|
|
||||||
# plot
|
|
||||||
ax1[1, i].set_title(f'reference: {ref_temp:.2f}')
|
|
||||||
ax1[1, i].set_xlabel('T / K')
|
|
||||||
|
|
||||||
ax1[1, i].plot(reference_data[0], reference_data[1], 'r-')
|
|
||||||
ax1[1, i].plot(ref_zoom[0, max_grad], ref_zoom[0, max_grad], 'kx')
|
|
||||||
ax1[1, i].plot(ref_zoom[0], onset, 'k--')
|
|
||||||
ax1[1, i].axhline(0, color='k', linestyle='--')
|
|
||||||
|
|
||||||
ax1[1, i].set_xlim(ref_zoom[0, integration_limit[0]], ref_zoom[0, integration_limit[1]])
|
|
||||||
ax1[1, i].set_ylim(-max(ref_zoom[1])/10, max(ref_zoom[1])*1.1)
|
|
||||||
|
|
||||||
print('Onset of transition: %.2f K found at %.2f' % (ref_temp, melts[-1]))
|
|
||||||
if enthalpy is not None:
|
|
||||||
# integrate over low temperature peak to calibrate y axis
|
|
||||||
# delta H in J/g: Integrate Peak over time and divide by weight
|
|
||||||
area = 1e-3 * simps(ref_zoom[1, integration_limit[0]:integration_limit[1]],
|
|
||||||
ref_zoom[2, integration_limit[0]:integration_limit[1]],
|
|
||||||
even='avg')
|
|
||||||
calib_y_axis = enthalpy / (area / reference.weight)
|
|
||||||
print("Calibration factor of peak: %f\n" % calib_y_axis)
|
|
||||||
|
|
||||||
sample_baseline[1] *= calib_y_axis
|
|
||||||
|
|
||||||
fig1.delaxes(ax1[1, 2])
|
|
||||||
fig1.tight_layout()
|
|
||||||
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
# give a choice how to compensate for long-time drift
|
|
||||||
mode = None
|
|
||||||
while mode not in ['i', 'h']:
|
|
||||||
mode = input('\nUse [i]sotherms or initial [h]eating for long-time correction? (Default: i) ')
|
|
||||||
if mode == '':
|
|
||||||
mode = 'i'
|
|
||||||
|
|
||||||
if mode == 'h':
|
|
||||||
print('\nCorrect slope from initial heating')
|
|
||||||
sample_baseline[1] = drift_from_slope
|
|
||||||
else:
|
|
||||||
print('\nCorrect slope from isotherm')
|
|
||||||
sample_baseline[1] = drift_corrected
|
|
||||||
|
|
||||||
# calibrate T axis
|
|
||||||
print('\nCalibrate temperature')
|
|
||||||
real_trans = np.array([temp for (temp, _) in ref_points.transitions])
|
|
||||||
t_vals = np.array([[melts[0], 1],
|
|
||||||
[melts[1], 1]])
|
|
||||||
calibration_temp = np.linalg.solve(t_vals, real_trans)
|
|
||||||
print('T_real = {:.4f} * T_meas {:+.4f}'.format(*calibration_temp))
|
|
||||||
|
|
||||||
sample_baseline[0] = calibration_temp[0] * sample_baseline[0] + calibration_temp[1]
|
|
||||||
|
|
||||||
print('Convert to capacity')
|
|
||||||
cp = sample_baseline[1] * 60. / rate / sample.weight / 1000.
|
|
||||||
if sample.weight is None:
|
|
||||||
raise ValueError('No sample weight given')
|
|
||||||
|
|
||||||
# plot final results in separate figure
|
|
||||||
fig2, ax2 = plt.subplots()
|
|
||||||
ax2.set_title('{} K/min: Heat flow vs. heat capacity (close to cont.)'.format(rate))
|
|
||||||
ax2.set_xlabel('Temperature / K')
|
|
||||||
|
|
||||||
ax2.plot(sample_baseline[0], sample_baseline[1], label='heat flow')
|
|
||||||
ax2.plot(sample_baseline[0], cp, label='heat capacity')
|
|
||||||
|
|
||||||
plt.legend()
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
outname = os.path.splitext(sample.name)[0] + '_' + str(rate) + 'K-min.dat'
|
|
||||||
header = 'Made with version: {}\n'.format(__version__)
|
|
||||||
header += 'T/K\tCp/J/(gK)\theat flow/mW'
|
|
||||||
|
|
||||||
print()
|
|
||||||
print('Save to', outname)
|
|
||||||
np.savetxt(outname, np.c_[sample_baseline[0], cp, sample_baseline[1]], header=header)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
args = parser.parse_args()
|
|
||||||
evaluate(args.sample, args.empty, args.reference, show_cooling=args.cooling)
|
|
@ -1,292 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
__version__ = '0.1.2'
|
|
||||||
|
|
||||||
import os
|
|
||||||
from argparse import ArgumentParser
|
|
||||||
from pathlib import Path
|
|
||||||
import sys
|
|
||||||
import numpy as np
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from scipy.integrate import simps
|
|
||||||
|
|
||||||
from nmreval.io.dsc import DSCReader, Cyclohexane, ReferenceValue
|
|
||||||
|
|
||||||
parser = ArgumentParser(description='Calibrate DSC data')
|
|
||||||
parser.add_argument('sample', type=str, help='filename of DSC sample')
|
|
||||||
parser.add_argument('empty', type=str, help='filename of empty pan')
|
|
||||||
parser.add_argument('reference', help='filename of reference', type=str)
|
|
||||||
parser.add_argument('--cooling', help='Plot found cooling rates', action='store_true')
|
|
||||||
|
|
||||||
|
|
||||||
def evaluate(sample: str|Path, empty: str|Path, reference: str|Path,
|
|
||||||
ref_points: ReferenceValue = Cyclohexane, show_cooling: bool = False):
|
|
||||||
|
|
||||||
sample = DSCReader(sample)
|
|
||||||
empty = DSCReader(empty)
|
|
||||||
reference = DSCReader(reference)
|
|
||||||
print(sample)
|
|
||||||
|
|
||||||
if show_cooling:
|
|
||||||
fig, ax = plt.subplots()
|
|
||||||
print('\n')
|
|
||||||
for k, v in sample.cooling.items():
|
|
||||||
print('Plot run {} with cooling rate {} K/min'.format(k, v))
|
|
||||||
c = sample.flow_data(v, mode='c')
|
|
||||||
ax.plot(c[0], c[1], label=str(v)+' K/min')
|
|
||||||
ax.set_xlabel('T / K')
|
|
||||||
plt.legend()
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
run_list = []
|
|
||||||
if len(sample.heating) > 1:
|
|
||||||
run = None
|
|
||||||
print('\nMultiple heat rates found:')
|
|
||||||
for k, v in sample.heating.items():
|
|
||||||
print(' run {}: {} K/min'.format(k, v))
|
|
||||||
while run not in sample.heating:
|
|
||||||
# choose your own adventure!!!
|
|
||||||
value = input('\nPlease select a run (press Enter for all heat rates): ')
|
|
||||||
if value == '':
|
|
||||||
run_list = list(sample.heating.keys())
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
run = int(value)
|
|
||||||
run_list = [run]
|
|
||||||
else:
|
|
||||||
run_list = list(sample.heating.keys())
|
|
||||||
|
|
||||||
for run in run_list:
|
|
||||||
rate = sample.heating[run]
|
|
||||||
|
|
||||||
print('\nProcessing heat rate {} K/min'.format(rate))
|
|
||||||
|
|
||||||
print('Load data of heating data')
|
|
||||||
len_sample = sample.length(run)
|
|
||||||
|
|
||||||
# sanity checks
|
|
||||||
try:
|
|
||||||
reference_data = reference.flow_data(rate)
|
|
||||||
except IndexError:
|
|
||||||
print('ERROR: Reference measurement has no heat rate {} K/min'.format(rate))
|
|
||||||
print('Stop evaluation')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
try:
|
|
||||||
run_baseline = empty.get_run(rate)
|
|
||||||
except ValueError:
|
|
||||||
print('ERROR: Empty measurement has no heat rate {} K/min'.format(rate))
|
|
||||||
print('Stop evaluation')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
len_baseline = empty.length(run_baseline)
|
|
||||||
max_length = None
|
|
||||||
if len_baseline != len_sample:
|
|
||||||
print('WARNING: measurements differ by {} points'.format(abs(len_baseline - len_sample)))
|
|
||||||
max_length = min(len_baseline, len_sample)
|
|
||||||
|
|
||||||
sample_data = sample.flow_data(rate, length=max_length)
|
|
||||||
empty_data = empty.flow_data(rate, length=max_length)
|
|
||||||
|
|
||||||
# plot input data
|
|
||||||
fig1, ax1 = plt.subplots(2, 3, **{'figsize': (10, 6)})
|
|
||||||
ax1[0, 0].set_title('raw data')
|
|
||||||
ax1[0, 0].set_xlabel('T / K')
|
|
||||||
|
|
||||||
ax1[0, 0].plot(sample_data[0], sample_data[1], 'k-', label='Sample')
|
|
||||||
ax1[0, 0].plot(empty_data[0], empty_data[1], 'b-', label='Empty')
|
|
||||||
ax1[0, 0].plot(reference_data[0], reference_data[1], 'r-', label='Reference')
|
|
||||||
ax1[0, 0].legend()
|
|
||||||
|
|
||||||
print('Substract empty data')
|
|
||||||
sample_baseline = sample_data.copy()
|
|
||||||
sample_baseline[1] = sample_data[1] - empty_data[1]
|
|
||||||
|
|
||||||
# plot baseline correction
|
|
||||||
ax1[0, 1].set_title('baseline correction')
|
|
||||||
ax1[0, 1].set_xlabel('T / K')
|
|
||||||
|
|
||||||
ax1[0, 1].plot(sample_data[0], sample_data[1], 'k--', label='Raw')
|
|
||||||
ax1[0, 1].plot(sample_baseline[0], sample_baseline[1], 'k-', label='Baseline corrected')
|
|
||||||
ax1[0, 1].plot(empty_data[0], empty_data[1], 'b-', label='Empty')
|
|
||||||
ax1[0, 1].legend()
|
|
||||||
|
|
||||||
print('Load isothermal data around heat rate')
|
|
||||||
mean_isotherms = []
|
|
||||||
for offset, where, ls in [(-1, 'low', '-'), (1, 'high', '--')]:
|
|
||||||
# read isotherms and baseline correct
|
|
||||||
len_baseline = empty.length(run_baseline+offset)
|
|
||||||
len_sample = sample.length(run+offset)
|
|
||||||
if len_baseline != len_sample:
|
|
||||||
print('WARNING: {} T isotherms differ by {} points'.format(where, abs(len_baseline-len_sample)))
|
|
||||||
|
|
||||||
max_length = min(len_baseline, len_sample)
|
|
||||||
isotherm_sample = sample.isotherm_data(run_baseline+offset, length=max_length)
|
|
||||||
isotherm_empty = empty.isotherm_data(run+offset, length=max_length)
|
|
||||||
|
|
||||||
isotherm_sample[1] -= isotherm_empty[1]
|
|
||||||
|
|
||||||
# get mean isotherm value
|
|
||||||
m = np.polyfit(isotherm_sample[0, 200:-200], isotherm_sample[1, 200:-200], 0)[0]
|
|
||||||
mean_isotherms.append(m)
|
|
||||||
print('Calculated {} heat flow: {} mW'.format(where, m))
|
|
||||||
|
|
||||||
ax1[0, 2].plot(isotherm_sample[0], isotherm_sample[1], 'k--')
|
|
||||||
|
|
||||||
# calculate slope from difference between isotherms
|
|
||||||
slope = (mean_isotherms[1]-mean_isotherms[0]) / (sample_data[2, -1] - empty_data[2, 0])
|
|
||||||
print('Heat flow slope from isotherms: {} per minute'.format(slope*60))
|
|
||||||
|
|
||||||
# calculate mean slope of heat flow at points in the beginning
|
|
||||||
slope_baseline = np.gradient(sample_baseline[1, int(4000/rate):int(9000/rate)],
|
|
||||||
sample_baseline[2, 300]-sample_baseline[2, 299]).mean()
|
|
||||||
print('Heat flow slope from initial heating: {} per minute'.format(slope_baseline*60))
|
|
||||||
|
|
||||||
drift_corrected = sample_baseline[1] - mean_isotherms[0] - (sample_baseline[2]-empty_data[2, 0])*slope
|
|
||||||
drift_from_slope = sample_baseline[1] - mean_isotherms[0] - (sample_baseline[2]-empty_data[2, 0])*slope_baseline
|
|
||||||
|
|
||||||
# plot
|
|
||||||
ax1[0, 2].axhline(mean_isotherms[0], linestyle=':')
|
|
||||||
ax1[0, 2].axhline(mean_isotherms[1], linestyle=':')
|
|
||||||
|
|
||||||
ax1[0, 2].plot(sample_baseline[2], sample_baseline[1], 'k-', label='Baseline corrected')
|
|
||||||
ax1[0, 2].plot(sample_baseline[2], drift_corrected, 'g-', label='Corrected (isotherm)')
|
|
||||||
ax1[0, 2].plot(sample_baseline[2], drift_from_slope, 'b-', label='Corrected (heating)')
|
|
||||||
|
|
||||||
ax1[0, 2].plot(sample_baseline[2], mean_isotherms[0] + (sample_baseline[2]-empty_data[2, 0])*slope, 'g--')
|
|
||||||
ax1[0, 2].plot(sample_baseline[2], mean_isotherms[0] + slope_baseline*(sample_baseline[2]-empty_data[2, 0]),
|
|
||||||
'b--')
|
|
||||||
|
|
||||||
ax1[0, 2].plot(sample_baseline[2, int(4000/rate):int(9000/rate)],
|
|
||||||
sample_baseline[1, int(4000/rate):int(9000/rate)], 'r--')
|
|
||||||
|
|
||||||
ax1[0, 2].set_title('time dependence')
|
|
||||||
ax1[0, 2].set_xlabel('t / s')
|
|
||||||
ax1[0, 2].legend()
|
|
||||||
|
|
||||||
melts = []
|
|
||||||
for i, (trans_temp, enthalpy) in enumerate(ref_points.transitions):
|
|
||||||
print(trans_temp, enthalpy)
|
|
||||||
|
|
||||||
# region around reference peaks
|
|
||||||
# NOTE: limits are hard coded for cyclohexane, other references need other limits
|
|
||||||
low_border = np.argmin(abs(reference_data[0]-(trans_temp-15)))
|
|
||||||
high_border = np.argmin(abs(reference_data[0]-(trans_temp+15)))
|
|
||||||
ref_zoom = reference_data[:, low_border:high_border]
|
|
||||||
x_val = np.array([[ref_zoom[0, 0], 1],
|
|
||||||
[ref_zoom[0, -1], 1]])
|
|
||||||
y_val = np.array([ref_zoom[1, 0],
|
|
||||||
ref_zoom[1, -1]])
|
|
||||||
print('Baseline correct reference of {} transition'.format(trans_temp))
|
|
||||||
sol = np.linalg.solve(x_val, y_val)
|
|
||||||
ref_zoom[1] -= (ref_zoom[0] * sol[0] + sol[1])
|
|
||||||
peak_max = ref_zoom[:, np.argmax(ref_zoom[1])]
|
|
||||||
integration_limit = np.argmin(abs(ref_zoom[0]-peak_max[0]+3)), np.argmin(abs(ref_zoom[0]-peak_max[0]-3))
|
|
||||||
|
|
||||||
# substract baseline around reference peaks
|
|
||||||
x_val = np.array([[ref_zoom[0, integration_limit[0]], 1],
|
|
||||||
[ref_zoom[0, integration_limit[1]], 1]])
|
|
||||||
y_val = np.array([ref_zoom[1, integration_limit[0]],
|
|
||||||
ref_zoom[1, integration_limit[1]]])
|
|
||||||
|
|
||||||
print('Baseline correct reference of {} transition'.format(trans_temp))
|
|
||||||
sol = np.linalg.solve(x_val, y_val)
|
|
||||||
ref_zoom[1] -= (ref_zoom[0] * sol[0] + sol[1])
|
|
||||||
|
|
||||||
# calculate onset slope (use points at position of maximum gradient +/- 100/rate)
|
|
||||||
ref_grad = np.gradient(ref_zoom[1])
|
|
||||||
max_grad = np.argmax(ref_grad)
|
|
||||||
|
|
||||||
x_val = np.array([[ref_zoom[0, max_grad-int(100/rate)], 1],
|
|
||||||
[ref_zoom[0, max_grad+int(100/rate)+1], 1]])
|
|
||||||
y_val = np.array([ref_zoom[1, max_grad-int(100/rate)],
|
|
||||||
ref_zoom[1, max_grad+int(100/rate)+1]])
|
|
||||||
sol = np.linalg.solve(x_val, y_val)
|
|
||||||
onset = sol[0]*ref_zoom[0] + sol[1]
|
|
||||||
|
|
||||||
melts.append(-sol[1]/sol[0])
|
|
||||||
|
|
||||||
# plot
|
|
||||||
ax1[1, i].set_title('reference: {:.2f} K'.format(trans_temp))
|
|
||||||
ax1[1, i].set_xlabel('T / K')
|
|
||||||
|
|
||||||
ax1[1, i].plot(reference_data[0], reference_data[1], 'r-')
|
|
||||||
ax1[1, i].plot(ref_zoom[0, max_grad], ref_zoom[0, max_grad], 'kx')
|
|
||||||
ax1[1, i].plot(ref_zoom[0], onset, 'k--')
|
|
||||||
ax1[1, i].axhline(0, color='k', linestyle='--')
|
|
||||||
|
|
||||||
ax1[1, i].set_xlim(ref_zoom[0, integration_limit[0]], ref_zoom[0, integration_limit[1]])
|
|
||||||
ax1[1, i].set_ylim(-max(ref_zoom[1])/10, max(ref_zoom[1])*1.1)
|
|
||||||
|
|
||||||
print('Onset of transition: {:.2f} K, should be at {:.2f}'.format(melts[-1], trans_temp))
|
|
||||||
if enthalpy is not None:
|
|
||||||
# integrate over low temperature peak to calibrate y axis
|
|
||||||
# NOTE: again, this is only valid for cyclohexane
|
|
||||||
# delta H in J/g: Integrate Peak over time and divide by weight
|
|
||||||
area = 1e-3 * simps(ref_zoom[1, integration_limit[0]:integration_limit[1]],
|
|
||||||
ref_zoom[2, integration_limit[0]:integration_limit[1]],
|
|
||||||
even='avg')
|
|
||||||
calib_y_axis = enthalpy / (area / reference.weight)
|
|
||||||
print("Calibration factor of peak: {}".format(calib_y_axis))
|
|
||||||
|
|
||||||
sample_baseline[1] *= calib_y_axis
|
|
||||||
|
|
||||||
fig1.delaxes(ax1[1, 2])
|
|
||||||
fig1.tight_layout()
|
|
||||||
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
# give a choice how to compensate for long-time drift
|
|
||||||
mode = None
|
|
||||||
while mode not in ['i', 'h']:
|
|
||||||
mode = input('\nUse [i]sotherms or initial [h]eating for long-time correction? (Default: i) ')
|
|
||||||
if mode == '':
|
|
||||||
mode = 'i'
|
|
||||||
|
|
||||||
if mode == 'h':
|
|
||||||
print('\nCorrect slope from initial heating')
|
|
||||||
sample_baseline[1] = drift_from_slope
|
|
||||||
else:
|
|
||||||
print('\nCorrect slope from isotherm')
|
|
||||||
sample_baseline[1] = drift_corrected
|
|
||||||
|
|
||||||
# calibrate T axis
|
|
||||||
print('\nCalibrate temperature')
|
|
||||||
real_trans = np.array([ref_points.transition1, ref_points.transition2])
|
|
||||||
t_vals = np.array([[melts[0], 1],
|
|
||||||
[melts[1], 1]])
|
|
||||||
calibration_temp = np.linalg.solve(t_vals, real_trans)
|
|
||||||
print('T_real = {:.4f} * T_meas {:+.4f}'.format(*calibration_temp))
|
|
||||||
|
|
||||||
sample_baseline[0] = calibration_temp[0] * sample_baseline[0] + calibration_temp[1]
|
|
||||||
|
|
||||||
print('Convert to capacity')
|
|
||||||
cp = sample_baseline[1] * 60. / rate / sample.weight / 1000.
|
|
||||||
if sample.weight is None:
|
|
||||||
raise ValueError('No sample weight given')
|
|
||||||
|
|
||||||
# plot final results in separate figure
|
|
||||||
fig2, ax2 = plt.subplots()
|
|
||||||
ax2.set_title('{} K/min: Heat flow vs. heat capacity (close to cont.)'.format(rate))
|
|
||||||
ax2.set_xlabel('Temperature / K')
|
|
||||||
|
|
||||||
ax2.plot(sample_baseline[0], sample_baseline[1], label='heat flow')
|
|
||||||
ax2.plot(sample_baseline[0], cp, label='heat capacity')
|
|
||||||
|
|
||||||
plt.legend()
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
outname = os.path.splitext(sample.name)[0] + '_' + str(rate) + 'K-min.dat'
|
|
||||||
header = 'Made with version: {}\n'.format(__version__)
|
|
||||||
header += 'T/K\tCp/J/(gK)\theat flow/mW'
|
|
||||||
|
|
||||||
print()
|
|
||||||
print('Save to', outname)
|
|
||||||
np.savetxt(outname, np.c_[sample_baseline[0], cp, sample_baseline[1]], header=header)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
args = parser.parse_args()
|
|
||||||
evaluate(args.sample, args.empty, args.reference, show_cooling=args.cooling)
|
|
27
src/nmreval/dsc/hodge.py
Normal file
27
src/nmreval/dsc/hodge.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import numpy as np
|
||||||
|
from scipy.stats import linregress
|
||||||
|
|
||||||
|
from nmreval.data import Points
|
||||||
|
from nmreval.fit.minimizer import FitRoutine
|
||||||
|
from nmreval.fit.result import FitResult
|
||||||
|
from nmreval.lib.utils import ArrayLike
|
||||||
|
from nmreval.models import Arrhenius
|
||||||
|
from nmreval.utils import kB
|
||||||
|
|
||||||
|
|
||||||
|
def tau_hodge(tg: ArrayLike, rate: ArrayLike) -> (Points, FitResult):
|
||||||
|
rate = np.asanyarray(rate)
|
||||||
|
tg = np.asanyarray(tg)
|
||||||
|
fitter = FitRoutine()
|
||||||
|
fitter.set_model(Arrhenius)
|
||||||
|
d = fitter.add_data(1000/tg, rate, we='y')
|
||||||
|
|
||||||
|
init = linregress(1000/tg, np.log(rate))
|
||||||
|
|
||||||
|
d.set_parameter([np.exp(init.intercept), 1000*init.slope*kB], fun_kwargs={'invt': 'invt1000'})
|
||||||
|
|
||||||
|
res = fitter.run()[0]
|
||||||
|
de = res.parameter['E_{A}']
|
||||||
|
tau = kB*tg**2/np.abs(de.value)/rate*60
|
||||||
|
|
||||||
|
return Points(x=1000/tg, y=tau, y_err=tau*de.error/np.abs(de.value), name='Hodge'), res
|
46
src/nmreval/dsc/tnmh_model.py
Normal file
46
src/nmreval/dsc/tnmh_model.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from nmreval.utils.constants import R_joule as R
|
||||||
|
|
||||||
|
|
||||||
|
class TNMH:
|
||||||
|
type = 'DSC'
|
||||||
|
name = 'TNMH model'
|
||||||
|
equation = r''
|
||||||
|
params = [r'\tau_{g}', 'x', r'\beta', r'\Delta H', 'T_{g}', 'rate']
|
||||||
|
bounds = [(0, None), (0, 1), (0, 1), (0, None), (0, None), (0, None)]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def func(x: np.ndarray, tau_g: float, xx: float, beta: float, energy: float, tg: float, rate: float) -> np.ndarray:
|
||||||
|
model_temp = np.r_[x[::-1], x[1:]]
|
||||||
|
|
||||||
|
curve = TNMH.tnm_function(model_temp, tau_g, xx, beta, energy, tg, rate)[x.size - 1:]
|
||||||
|
res = np.gradient(curve, x)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def tnm_function(x: np.ndarray, tau_g: float, xx: float, beta: float, energy: float, tg: float, rate: float) -> np.ndarray:
|
||||||
|
step = x.size
|
||||||
|
|
||||||
|
Tf = np.empty(step)
|
||||||
|
Tf[0] = x[0]
|
||||||
|
|
||||||
|
delta_temp = np.diff(x)
|
||||||
|
delta_time = np.abs(delta_temp) * 60 / rate
|
||||||
|
|
||||||
|
tau = np.empty(step)
|
||||||
|
dt_by_tau = np.zeros(step)
|
||||||
|
temp_0 = x[0]
|
||||||
|
|
||||||
|
for i in range(0, step - 1):
|
||||||
|
tau[i] = TNMH.relax(x[i+1], Tf[i], tau_g, tg, energy, xx)
|
||||||
|
dt_by_tau[:i] += delta_time[i] / tau[i]
|
||||||
|
Tf[i + 1] = np.sum(delta_temp[:i] * (1 - np.exp(-dt_by_tau[:i] ** beta))) + temp_0
|
||||||
|
|
||||||
|
return Tf
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def relax(t, tf, tau_g, t_glass, ea, x):
|
||||||
|
h = ea/R
|
||||||
|
return tau_g * np.exp((x*h / t) + ((1 - x) * h / tf) - h / t_glass)
|
@ -24,12 +24,13 @@ class ModelFactory:
|
|||||||
param_len.append(len(func['func'].params))
|
param_len.append(len(func['func'].params))
|
||||||
|
|
||||||
if func['children']:
|
if func['children']:
|
||||||
right, _, _ = ModelFactory.create_from_list(func['children'], left=func['func'], left_cnt=func['pos'],
|
right, _, _ = ModelFactory.create_from_list(func['children'], left_cnt=func['pos'],
|
||||||
func_order=func_order, param_len=param_len)
|
func_order=func_order, param_len=param_len)
|
||||||
right_cnt = None
|
right_cnt = None
|
||||||
|
right = MultiModel(func['func'], right, func['children'][0]['op'], left_idx=func['cnt'], right_idx=None)
|
||||||
else:
|
else:
|
||||||
right = func['func']
|
right = func['func']
|
||||||
right_cnt = func['pos']
|
right_cnt = func['cnt']
|
||||||
|
|
||||||
if left is None:
|
if left is None:
|
||||||
left = right
|
left = right
|
||||||
@ -46,7 +47,13 @@ class MultiModel:
|
|||||||
str_op = {'+': operator.add, '*': operator.mul, '-': operator.sub, '/': operator.truediv}
|
str_op = {'+': operator.add, '*': operator.mul, '-': operator.sub, '/': operator.truediv}
|
||||||
int_op = {0: operator.add, 1: operator.mul, 2: operator.sub, 3: operator.truediv}
|
int_op = {0: operator.add, 1: operator.mul, 2: operator.sub, 3: operator.truediv}
|
||||||
|
|
||||||
def __init__(self, left: Any, right: Any, op: str | Callable | int = '+', left_idx=0, right_idx=1):
|
def __init__(self,
|
||||||
|
left: Any,
|
||||||
|
right: Any,
|
||||||
|
op: str | Callable | int = '+',
|
||||||
|
left_idx: int | None = 0,
|
||||||
|
right_idx: int | None = 1,
|
||||||
|
):
|
||||||
self._left = left
|
self._left = left
|
||||||
self._right = right
|
self._right = right
|
||||||
|
|
||||||
@ -68,6 +75,7 @@ class MultiModel:
|
|||||||
self._kwargs_right = {}
|
self._kwargs_right = {}
|
||||||
self._kwargs_left = {}
|
self._kwargs_left = {}
|
||||||
self.fun_kwargs = {}
|
self.fun_kwargs = {}
|
||||||
|
self.idx = (left_idx, right_idx)
|
||||||
|
|
||||||
# mapping kwargs to kwargs of underlying functions
|
# mapping kwargs to kwargs of underlying functions
|
||||||
self._ext_int_kw = {}
|
self._ext_int_kw = {}
|
||||||
@ -178,13 +186,13 @@ class MultiModel:
|
|||||||
if isinstance(self._left, MultiModel):
|
if isinstance(self._left, MultiModel):
|
||||||
yield from self._left.sub_name()
|
yield from self._left.sub_name()
|
||||||
elif hasattr(self._left, 'name'):
|
elif hasattr(self._left, 'name'):
|
||||||
yield self._left.name
|
yield f'{self._left.name}({self.idx[0]})'
|
||||||
else:
|
else:
|
||||||
yield self.name + '(lhs)'
|
yield self.name + '(lhs)'
|
||||||
|
|
||||||
if isinstance(self._right, MultiModel):
|
if isinstance(self._right, MultiModel):
|
||||||
yield from self._right.sub_name()
|
yield from self._right.sub_name()
|
||||||
elif hasattr(self._right, 'name'):
|
elif hasattr(self._right, 'name'):
|
||||||
yield self._right.name
|
yield f'{self._right.name}({self.idx[1]})'
|
||||||
else:
|
else:
|
||||||
yield self.name + '(rhs)'
|
yield self.name + '(rhs)'
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from .model import Model
|
from .model import Model
|
||||||
from .parameter import Parameters
|
from .parameter import Parameters, Parameter
|
||||||
|
|
||||||
|
|
||||||
class Data(object):
|
class Data(object):
|
||||||
@ -11,27 +13,27 @@ class Data(object):
|
|||||||
if self.y.shape[0] != self.x.shape[0]:
|
if self.y.shape[0] != self.x.shape[0]:
|
||||||
raise ValueError(f'x and y have different lengths {self.x.shape[0]} and {self.y.shape[0]}')
|
raise ValueError(f'x and y have different lengths {self.x.shape[0]} and {self.y.shape[0]}')
|
||||||
|
|
||||||
self.we = self._calc_weights(we)
|
self.we, self.we_string = self._calc_weights(we)
|
||||||
self.idx = idx
|
self.idx = idx
|
||||||
self.model = None
|
self.model = None
|
||||||
self.minimizer = None
|
self.minimizer = None
|
||||||
self.parameter = Parameters()
|
self.parameter = Parameters()
|
||||||
self.para_keys = None
|
self.para_keys: list = []
|
||||||
self.fun_kwargs = {}
|
self.fun_kwargs = {}
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return self.y.shape[0]
|
return self.y.shape[0]
|
||||||
|
|
||||||
def _calc_weights(self, we):
|
def _calc_weights(self, we: str | np.ndarray | None) -> tuple[np.ndarray, str]:
|
||||||
if we is None:
|
if isinstance(we, str) or we is None:
|
||||||
return 1.
|
if we is None or we.lower() == 'none':
|
||||||
|
we_string = 'None'
|
||||||
if isinstance(we, str):
|
|
||||||
if we == 'y2':
|
|
||||||
we_func = lambda yy: 1. / yy**2
|
|
||||||
elif we.lower() == 'none':
|
|
||||||
we_func = lambda yy: np.ones_like(len(yy))
|
we_func = lambda yy: np.ones_like(len(yy))
|
||||||
|
elif we == 'y2':
|
||||||
|
we_string = we
|
||||||
|
we_func = lambda yy: 1. / yy**2
|
||||||
else:
|
else:
|
||||||
|
we_string = we
|
||||||
we_func = lambda yy: 1. / np.abs(yy)
|
we_func = lambda yy: 1. / np.abs(yy)
|
||||||
|
|
||||||
if np.iscomplexobj(self.y):
|
if np.iscomplexobj(self.y):
|
||||||
@ -40,6 +42,7 @@ class Data(object):
|
|||||||
weights = we_func(self.y)
|
weights = we_func(self.y)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
we_string = 'yerr' # This is pure speculation that array equals error
|
||||||
we = 1. / np.asarray(we)
|
we = 1. / np.asarray(we)
|
||||||
if np.iscomplexobj(self.y):
|
if np.iscomplexobj(self.y):
|
||||||
if np.iscomplexobj(we):
|
if np.iscomplexobj(we):
|
||||||
@ -51,7 +54,7 @@ class Data(object):
|
|||||||
|
|
||||||
weights[weights == np.inf] = np.finfo(float).max
|
weights[weights == np.inf] = np.finfo(float).max
|
||||||
|
|
||||||
return weights
|
return weights, we_string
|
||||||
|
|
||||||
def set_model(self, func, *args, **kwargs):
|
def set_model(self, func, *args, **kwargs):
|
||||||
if isinstance(func, Model):
|
if isinstance(func, Model):
|
||||||
@ -68,12 +71,19 @@ class Data(object):
|
|||||||
def get_model(self):
|
def get_model(self):
|
||||||
return self.model
|
return self.model
|
||||||
|
|
||||||
def set_parameter(self, parameter, var=None, ub=None, lb=None,
|
def set_parameter(self,
|
||||||
default_bounds=False, fun_kwargs=None):
|
values: list[float | Parameter],
|
||||||
|
*,
|
||||||
|
var: list[bool] = None,
|
||||||
|
ub: list[float] = None,
|
||||||
|
lb: list[float] = None,
|
||||||
|
default_bounds: bool = False,
|
||||||
|
fun_kwargs: dict = None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Creates parameter for this data.
|
Creates parameter for this data.
|
||||||
If no Model is available, it falls back to the model
|
If no Model is available, it falls back to the model
|
||||||
:param parameter: list of parameters
|
:param values: list of parameters
|
||||||
:param var: list of boolean or boolean; False fixes parameter at given list index.
|
:param var: list of boolean or boolean; False fixes parameter at given list index.
|
||||||
Single value is broadcast to all parameter
|
Single value is broadcast to all parameter
|
||||||
:param ub: list of upper boundaries or float; Single value is broadcast to all parameter.
|
:param ub: list of upper boundaries or float; Single value is broadcast to all parameter.
|
||||||
@ -87,23 +97,46 @@ class Data(object):
|
|||||||
model = self.model
|
model = self.model
|
||||||
if model is None:
|
if model is None:
|
||||||
# Data has no unique
|
# Data has no unique
|
||||||
if self.minimizer is None:
|
if self.minimizer is not None:
|
||||||
model = None
|
|
||||||
else:
|
|
||||||
model = self.minimizer.fit_model
|
model = self.minimizer.fit_model
|
||||||
self.fun_kwargs.update(model.fun_kwargs)
|
|
||||||
|
|
||||||
if model is None:
|
if model is None:
|
||||||
raise ValueError('No model found, please set model before parameters')
|
raise ValueError('No model found, please set model before parameters')
|
||||||
|
|
||||||
if default_bounds:
|
if len(values) != len(model.params):
|
||||||
|
raise ValueError('Number of given parameter does not match number of model parameters')
|
||||||
|
|
||||||
|
is_parameter = [isinstance(v, Parameter) for v in values]
|
||||||
|
if all(is_parameter):
|
||||||
|
for p_i in values:
|
||||||
|
key = f"p{next(Parameters.parameter_counter)}"
|
||||||
|
self.parameter.add_parameter(key, p_i)
|
||||||
|
elif any(is_parameter):
|
||||||
|
raise ValueError('list of parameter are not all float of Parameter')
|
||||||
|
|
||||||
|
else:
|
||||||
|
if var is None:
|
||||||
|
var = [True] * len(values)
|
||||||
|
|
||||||
if lb is None:
|
if lb is None:
|
||||||
|
if default_bounds:
|
||||||
lb = model.lb
|
lb = model.lb
|
||||||
|
else:
|
||||||
|
lb = [None] * len(values)
|
||||||
|
|
||||||
if ub is None:
|
if ub is None:
|
||||||
|
if default_bounds:
|
||||||
ub = model.ub
|
ub = model.ub
|
||||||
|
else:
|
||||||
|
ub = [None] * len(values)
|
||||||
|
|
||||||
self.para_keys = self.parameter.add_parameter(parameter, var=var, lb=lb, ub=ub)
|
arg_names = ['name', 'value', 'var', 'lb', 'ub']
|
||||||
|
for parameter_arg in zip(model.params, values, var, lb, ub):
|
||||||
|
self.parameter.add(**{arg_name: arg_value for arg_name, arg_value in zip(arg_names, parameter_arg)})
|
||||||
|
|
||||||
|
self.para_keys = list(self.parameter.keys())
|
||||||
|
|
||||||
|
self.fun_kwargs.update(model.fun_kwargs)
|
||||||
if fun_kwargs is not None:
|
if fun_kwargs is not None:
|
||||||
self.fun_kwargs.update(fun_kwargs)
|
self.fun_kwargs.update(fun_kwargs)
|
||||||
|
|
||||||
@ -123,6 +156,18 @@ class Data(object):
|
|||||||
else:
|
else:
|
||||||
return [p.value for p in self.minimizer.parameters[self.parameter]]
|
return [p.value for p in self.minimizer.parameters[self.parameter]]
|
||||||
|
|
||||||
|
def replace_parameter(self, key: str, parameter: Parameter) -> None:
|
||||||
|
tobereplaced = None
|
||||||
|
for k, v in self.parameter.items():
|
||||||
|
if v.name == parameter.name:
|
||||||
|
tobereplaced = k
|
||||||
|
break
|
||||||
|
|
||||||
|
if tobereplaced is None:
|
||||||
|
raise KeyError(f'Global parameter {key} not found in list of parameters')
|
||||||
|
self.para_keys[self.para_keys.index(tobereplaced)] = key
|
||||||
|
self.parameter.replace_parameter(tobereplaced, key, parameter)
|
||||||
|
|
||||||
def cost(self, p):
|
def cost(self, p):
|
||||||
"""
|
"""
|
||||||
Cost function :math:`y-f(p, x)`
|
Cost function :math:`y-f(p, x)`
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
|
||||||
@ -14,21 +16,81 @@ from .result import FitResultCreator
|
|||||||
|
|
||||||
__all__ = ['FitRoutine', 'FitAbortException']
|
__all__ = ['FitRoutine', 'FitAbortException']
|
||||||
|
|
||||||
|
from ..lib.logger import logger
|
||||||
|
|
||||||
|
|
||||||
class FitAbortException(Exception):
|
class FitAbortException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# COST FUNCTIONS: f(x) - y (least_square, minimize), and f(x) (ODR)
|
||||||
|
def _cost_scipy_glob(p: list[float], data: list[Data], varpars: list[str], used_pars: list[list[str]]):
|
||||||
|
# replace values
|
||||||
|
for keys, values in zip(varpars, p):
|
||||||
|
for data_i in data:
|
||||||
|
if keys in data_i.parameter.keys():
|
||||||
|
# TODO move this to scaled_value setter
|
||||||
|
data_i.parameter[keys].scaled_value = values
|
||||||
|
data_i.parameter[keys].namespace[keys] = data_i.parameter[keys].value
|
||||||
|
r = []
|
||||||
|
# unpack parameter and calculate y values and concatenate all
|
||||||
|
for values, p_idx in zip(data, used_pars):
|
||||||
|
actual_parameters = [values.parameter[keys].value for keys in p_idx]
|
||||||
|
r = np.r_[r, values.cost(actual_parameters)]
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def _cost_scipy(p, data, varpars, used_pars):
|
||||||
|
for keys, values in zip(varpars, p):
|
||||||
|
data.parameter[keys].scaled_value = values
|
||||||
|
data.parameter[keys].namespace[keys] = data.parameter[keys].value
|
||||||
|
|
||||||
|
actual_parameters = [data.parameter[keys].value for keys in used_pars]
|
||||||
|
return data.cost(actual_parameters)
|
||||||
|
|
||||||
|
|
||||||
|
def _cost_odr(p: list[float], data: Data, varpars: list[str], used_pars: list[str], fitmode: int=0):
|
||||||
|
for keys, values in zip(varpars, p):
|
||||||
|
data.parameter[keys].scaled_value = values
|
||||||
|
data.parameter[keys].namespace[keys] = data.parameter[keys].value
|
||||||
|
|
||||||
|
actual_parameters = [data.parameter[keys].value for keys in used_pars]
|
||||||
|
|
||||||
|
return data.func(actual_parameters, data.x)
|
||||||
|
|
||||||
|
|
||||||
|
def _cost_odr_glob(p: list[float], data: list[Data], var_pars: list[str], used_pars: list[str]):
|
||||||
|
# replace values
|
||||||
|
for data_i in data:
|
||||||
|
_update_parameter(data_i, var_pars, p)
|
||||||
|
|
||||||
|
r = []
|
||||||
|
# unpack parameter and calculate y values and concatenate all
|
||||||
|
for values, p_idx in zip(data, used_pars):
|
||||||
|
actual_parameters = [values.parameter[keys].value for keys in p_idx]
|
||||||
|
r = np.r_[r, values.func(actual_parameters, values.x)]
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def _update_parameter(data: Data, varied_keys: list[str], parameter: list[float]):
|
||||||
|
for keys, values in zip(varied_keys, parameter):
|
||||||
|
if keys in data.parameter.keys():
|
||||||
|
data.parameter[keys].scaled_value = values
|
||||||
|
data.parameter[keys].namespace[keys] = data.parameter[keys].value
|
||||||
|
|
||||||
|
|
||||||
class FitRoutine(object):
|
class FitRoutine(object):
|
||||||
def __init__(self, mode='lsq'):
|
def __init__(self, mode='lsq'):
|
||||||
self._fitmethod = mode
|
self.fitmethod = mode
|
||||||
self.data = []
|
self.data = []
|
||||||
self.fit_model = None
|
self.fit_model = None
|
||||||
self._no_own_model = []
|
self._no_own_model = []
|
||||||
self.parameter = Parameters()
|
|
||||||
self.result = []
|
self.result = []
|
||||||
self.linked = []
|
self.linked = []
|
||||||
self._abort = False
|
self._abort = False
|
||||||
|
self.step = 0
|
||||||
|
|
||||||
def add_data(self, x, y=None, we=None, idx=None):
|
def add_data(self, x, y=None, we=None, idx=None):
|
||||||
if isinstance(x, Data):
|
if isinstance(x, Data):
|
||||||
@ -78,29 +140,27 @@ class FitRoutine(object):
|
|||||||
|
|
||||||
return self.fit_model
|
return self.fit_model
|
||||||
|
|
||||||
def set_link_parameter(self, parameter: tuple, replacement: tuple):
|
def set_link_parameter(self, dismissed_param: tuple[Model | Data, str], replacement: tuple[Model, str]):
|
||||||
if isinstance(replacement[0], Model):
|
if isinstance(replacement[0], Model):
|
||||||
if replacement[1] not in replacement[0].global_parameter:
|
if replacement[1] not in replacement[0].parameter:
|
||||||
raise KeyError(f'Parameter at pos {replacement[1]} of '
|
raise KeyError(f'Parameter {replacement[1]} of '
|
||||||
f'model {str(replacement[0])} is not global')
|
f'model {replacement[0]} is not global')
|
||||||
|
|
||||||
if isinstance(parameter[0], Model):
|
if isinstance(dismissed_param[0], Model):
|
||||||
warnings.warn(f'Replaced parameter at pos {parameter[1]} in {str(parameter[0])} '
|
warnings.warn(f'Replaced parameter {dismissed_param[1]} in {dismissed_param[0]} '
|
||||||
f'becomes global with linkage.')
|
f'becomes global with linkage.')
|
||||||
|
|
||||||
self.linked.append((*parameter, *replacement))
|
self.linked.append((*dismissed_param, *replacement))
|
||||||
|
|
||||||
def prepare_links(self):
|
def prepare_links(self):
|
||||||
self._no_own_model = []
|
self._no_own_model = []
|
||||||
self.parameter = Parameters()
|
|
||||||
_found_models = {}
|
_found_models = {}
|
||||||
linked_sender = {}
|
linked_sender = {}
|
||||||
|
|
||||||
for v in self.data:
|
for v in self.data:
|
||||||
linked_sender[v] = set()
|
linked_sender[v] = set()
|
||||||
self.parameter.update(v.parameter.copy())
|
|
||||||
|
|
||||||
# set temporaray model
|
# set temporary model
|
||||||
if v.model is None:
|
if v.model is None:
|
||||||
v.model = self.fit_model
|
v.model = self.fit_model
|
||||||
self._no_own_model.append(v)
|
self._no_own_model.append(v)
|
||||||
@ -108,8 +168,6 @@ class FitRoutine(object):
|
|||||||
# register model
|
# register model
|
||||||
if v.model not in _found_models:
|
if v.model not in _found_models:
|
||||||
_found_models[v.model] = []
|
_found_models[v.model] = []
|
||||||
m_param = v.model.parameter.copy()
|
|
||||||
self.parameter.update(m_param)
|
|
||||||
|
|
||||||
_found_models[v.model].append(v)
|
_found_models[v.model].append(v)
|
||||||
|
|
||||||
@ -117,24 +175,21 @@ class FitRoutine(object):
|
|||||||
linked_sender[v.model] = set()
|
linked_sender[v.model] = set()
|
||||||
|
|
||||||
linked_parameter = {}
|
linked_parameter = {}
|
||||||
for par, par_parm, repl, repl_par in self.linked:
|
for dismiss_model, dismiss_param, replace_model, replace_param in self.linked:
|
||||||
if isinstance(par, Data):
|
linked_sender[replace_model].add(dismiss_model)
|
||||||
if isinstance(repl, Data):
|
linked_sender[replace_model].add(replace_model)
|
||||||
linked_parameter[par.para_keys[par_parm]] = repl.para_keys[repl_par]
|
|
||||||
else:
|
|
||||||
linked_parameter[par.para_keys[par_parm]] = repl.global_parameter[repl_par]
|
|
||||||
|
|
||||||
else:
|
replace_key = replace_model.parameter.get_key(replace_param)
|
||||||
if isinstance(repl, Data):
|
dismiss_key = dismiss_model.parameter.get_key(dismiss_param)
|
||||||
par.global_parameter[par_parm] = repl.para_keys[repl_par]
|
|
||||||
else:
|
|
||||||
par.global_parameter[par_parm] = repl.global_parameter[repl_par]
|
|
||||||
|
|
||||||
linked_sender[repl].add(par)
|
if isinstance(replace_model, Data):
|
||||||
linked_sender[par].add(repl)
|
linked_parameter[dismiss_key] = replace_key
|
||||||
|
else:
|
||||||
|
p = dismiss_model.set_global_parameter(dismiss_param, replace_key)
|
||||||
|
p._expr_disp = replace_param
|
||||||
|
|
||||||
for mm, m_data in _found_models.items():
|
for mm, m_data in _found_models.items():
|
||||||
if mm.global_parameter:
|
if mm.parameter:
|
||||||
for dd in m_data:
|
for dd in m_data:
|
||||||
linked_sender[mm].add(dd)
|
linked_sender[mm].add(dd)
|
||||||
linked_sender[dd].add(mm)
|
linked_sender[dd].add(mm)
|
||||||
@ -163,15 +218,16 @@ class FitRoutine(object):
|
|||||||
self.find_paths(neighbor, graph, coupled_nodes, visited_nodes)
|
self.find_paths(neighbor, graph, coupled_nodes, visited_nodes)
|
||||||
|
|
||||||
def abort(self):
|
def abort(self):
|
||||||
print('ABORT ???')
|
logger.info('Fit aborted by user')
|
||||||
self._abort = True
|
self._abort = True
|
||||||
|
|
||||||
def run(self, mode='lsq'):
|
def run(self, mode: str = None):
|
||||||
self._abort = False
|
self._abort = False
|
||||||
self.parameter = Parameters()
|
|
||||||
|
if mode is None:
|
||||||
|
mode = self.fitmethod
|
||||||
|
|
||||||
fit_groups, linked_parameter = self.prepare_links()
|
fit_groups, linked_parameter = self.prepare_links()
|
||||||
|
|
||||||
for data_groups in fit_groups:
|
for data_groups in fit_groups:
|
||||||
if len(data_groups) == 1 and not self.linked:
|
if len(data_groups) == 1 and not self.linked:
|
||||||
data = data_groups[0]
|
data = data_groups[0]
|
||||||
@ -202,17 +258,32 @@ class FitRoutine(object):
|
|||||||
|
|
||||||
self.unprep_run()
|
self.unprep_run()
|
||||||
|
|
||||||
|
for r in self.result:
|
||||||
|
r.pprint()
|
||||||
|
|
||||||
return self.result
|
return self.result
|
||||||
|
|
||||||
|
def make_preview(self, x: np.ndarray) -> list[np.ndarray]:
|
||||||
|
y_pred = []
|
||||||
|
fit_groups, linked_parameter = self.prepare_links()
|
||||||
|
for data_groups in fit_groups:
|
||||||
|
data = data_groups[0]
|
||||||
|
actual_parameters = [p.value for p in data.parameter.values()]
|
||||||
|
y_pred.append(data.func(actual_parameters, x))
|
||||||
|
|
||||||
|
return y_pred
|
||||||
|
|
||||||
def _prep_data(self, data):
|
def _prep_data(self, data):
|
||||||
if data.get_model() is None:
|
if data.get_model() is None:
|
||||||
data._model = self.fit_model
|
data._model = self.fit_model
|
||||||
self._no_own_model.append(data)
|
self._no_own_model.append(data)
|
||||||
|
|
||||||
|
# data.parameter.prepare_bounds()
|
||||||
|
|
||||||
return self._prep_parameter(data.parameter)
|
return self._prep_parameter(data.parameter)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _prep_parameter(parameter):
|
def _prep_parameter(parameter: Parameters):
|
||||||
vals = []
|
vals = []
|
||||||
var_pars = []
|
var_pars = []
|
||||||
for p_k, v_k in parameter.items():
|
for p_k, v_k in parameter.items():
|
||||||
@ -224,29 +295,26 @@ class FitRoutine(object):
|
|||||||
|
|
||||||
return pp, lb, ub, var_pars
|
return pp, lb, ub, var_pars
|
||||||
|
|
||||||
def _prep_global(self, data_group, linked):
|
@staticmethod
|
||||||
|
def _prep_global(data_group: list[Data], linked):
|
||||||
p0 = []
|
p0 = []
|
||||||
lb = []
|
lb = []
|
||||||
ub = []
|
ub = []
|
||||||
var = []
|
var = []
|
||||||
data_pars = []
|
data_pars = []
|
||||||
|
|
||||||
# loopyloop over data that belong to one fit (linked or global)
|
# loopy-loop over data that belong to one fit (linked or global)
|
||||||
for data in data_group:
|
for data in data_group:
|
||||||
actual_pars = []
|
|
||||||
for i, (p_k, v_k) in enumerate(data.parameter.items()):
|
|
||||||
p_k_used = p_k
|
|
||||||
v_k_used = v_k
|
|
||||||
|
|
||||||
# is parameter replaced by global parameter?
|
# is parameter replaced by global parameter?
|
||||||
if i in data.model.global_parameter:
|
for k, v in data.model.parameter.items():
|
||||||
p_k_used = data.model.global_parameter[i]
|
data.replace_parameter(k, v)
|
||||||
v_k_used = self.parameter[p_k_used]
|
|
||||||
|
|
||||||
# links trump global parameter
|
# data.parameter.prepare_bounds()
|
||||||
if p_k_used in linked:
|
|
||||||
p_k_used = linked[p_k_used]
|
actual_pars = []
|
||||||
v_k_used = self.parameter[p_k_used]
|
for i, p_k in enumerate(data.para_keys):
|
||||||
|
p_k_used = p_k
|
||||||
|
v_k_used = data.parameter[p_k]
|
||||||
|
|
||||||
actual_pars.append(p_k_used)
|
actual_pars.append(p_k_used)
|
||||||
# parameter is variable and was not found before as shared parameter
|
# parameter is variable and was not found before as shared parameter
|
||||||
@ -265,58 +333,20 @@ class FitRoutine(object):
|
|||||||
d._model = None
|
d._model = None
|
||||||
|
|
||||||
self._no_own_model = []
|
self._no_own_model = []
|
||||||
|
Parameters.reset()
|
||||||
# COST FUNCTIONS: f(x) - y (least_square, minimize), and f(x) (ODR)
|
|
||||||
def __cost_scipy(self, p, data, varpars, used_pars):
|
|
||||||
for keys, values in zip(varpars, p):
|
|
||||||
self.parameter[keys].scaled_value = values
|
|
||||||
|
|
||||||
actual_parameters = [self.parameter[keys].value for keys in used_pars]
|
|
||||||
return data.cost(actual_parameters)
|
|
||||||
|
|
||||||
def __cost_odr(self, p, data, varpars, used_pars):
|
|
||||||
for keys, values in zip(varpars, p):
|
|
||||||
self.parameter[keys].scaled_value = values
|
|
||||||
|
|
||||||
actual_parameters = [self.parameter[keys].value for keys in used_pars]
|
|
||||||
|
|
||||||
return data.func(actual_parameters, data.x)
|
|
||||||
|
|
||||||
def __cost_scipy_glob(self, p, data, varpars, used_pars):
|
|
||||||
# replace values
|
|
||||||
for keys, values in zip(varpars, p):
|
|
||||||
self.parameter[keys].scaled_value = values
|
|
||||||
|
|
||||||
r = []
|
|
||||||
# unpack parameter and calculate y values and concatenate all
|
|
||||||
for values, p_idx in zip(data, used_pars):
|
|
||||||
actual_parameters = [self.parameter[keys].value for keys in p_idx]
|
|
||||||
r = np.r_[r, values.cost(actual_parameters)]
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
def __cost_odr_glob(self, p, data, varpars, used_pars):
|
|
||||||
# replace values
|
|
||||||
for keys, values in zip(varpars, p):
|
|
||||||
self.parameter[keys].scaled_value = values
|
|
||||||
|
|
||||||
r = []
|
|
||||||
# unpack parameter and calculate y values and concatenate all
|
|
||||||
for values, p_idx in zip(data, used_pars):
|
|
||||||
actual_parameters = [self.parameter[keys].value for keys in p_idx]
|
|
||||||
r = np.r_[r, values.func(actual_parameters, values.x)]
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
def _least_squares_single(self, data, p0, lb, ub, var):
|
def _least_squares_single(self, data, p0, lb, ub, var):
|
||||||
|
self.step = 0
|
||||||
|
|
||||||
def cost(p):
|
def cost(p):
|
||||||
|
self.step += 1
|
||||||
if self._abort:
|
if self._abort:
|
||||||
raise FitAbortException(f'Fit aborted by user')
|
raise FitAbortException(f'Fit aborted by user')
|
||||||
|
|
||||||
return self.__cost_scipy(p, data, var, data.para_keys)
|
return _cost_scipy(p, data, var, data.para_keys)
|
||||||
|
|
||||||
with np.errstate(all='ignore'):
|
with np.errstate(all='ignore'):
|
||||||
res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=1000 * len(p0))
|
res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=500 * len(p0))
|
||||||
|
|
||||||
err, corr, partial_corr = self._calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape)
|
err, corr, partial_corr = self._calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape)
|
||||||
self.make_results(data, res.x, var, data.para_keys, res.jac.shape,
|
self.make_results(data, res.x, var, data.para_keys, res.jac.shape,
|
||||||
@ -324,12 +354,13 @@ class FitRoutine(object):
|
|||||||
|
|
||||||
def _least_squares_global(self, data, p0, lb, ub, var, data_pars):
|
def _least_squares_global(self, data, p0, lb, ub, var, data_pars):
|
||||||
def cost(p):
|
def cost(p):
|
||||||
|
self.step += 1
|
||||||
if self._abort:
|
if self._abort:
|
||||||
raise FitAbortException(f'Fit aborted by user')
|
raise FitAbortException(f'Fit aborted by user')
|
||||||
return self.__cost_scipy_glob(p, data, var, data_pars)
|
return _cost_scipy_glob(p, data, var, data_pars)
|
||||||
|
|
||||||
with np.errstate(all='ignore'):
|
with np.errstate(all='ignore'):
|
||||||
res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=1000 * len(p0))
|
res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=500 * len(p0))
|
||||||
|
|
||||||
err, corr, partial_corr = self._calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape)
|
err, corr, partial_corr = self._calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape)
|
||||||
for v, var_pars_k in zip(data, data_pars):
|
for v, var_pars_k in zip(data, data_pars):
|
||||||
@ -338,25 +369,27 @@ class FitRoutine(object):
|
|||||||
|
|
||||||
def _nm_single(self, data, p0, lb, ub, var):
|
def _nm_single(self, data, p0, lb, ub, var):
|
||||||
def cost(p):
|
def cost(p):
|
||||||
|
self.step += 1
|
||||||
if self._abort:
|
if self._abort:
|
||||||
raise FitAbortException(f'Fit aborted by user')
|
raise FitAbortException(f'Fit aborted by user')
|
||||||
return (self.__cost_scipy(p, data, var, data.para_keys)**2).sum()
|
return (_cost_scipy(p, data, var, data.para_keys) ** 2).sum()
|
||||||
|
|
||||||
with np.errstate(all='ignore'):
|
with np.errstate(all='ignore'):
|
||||||
res = optimize.minimize(cost, p0, bounds=[(b1, b2) for (b1, b2) in zip(lb, ub)],
|
res = optimize.minimize(cost, p0, bounds=[(b1, b2) for (b1, b2) in zip(lb, ub)],
|
||||||
method='Nelder-Mead', options={'maxiter': 1000 * len(p0)})
|
method='Nelder-Mead', options={'maxiter': 500 * len(p0)})
|
||||||
|
|
||||||
self.make_results(data, res.x, var, data.para_keys, (len(data), len(p0)))
|
self.make_results(data, res.x, var, data.para_keys, (len(data), len(p0)))
|
||||||
|
|
||||||
def _nm_global(self, data, p0, lb, ub, var, data_pars):
|
def _nm_global(self, data, p0, lb, ub, var, data_pars):
|
||||||
def cost(p):
|
def cost(p):
|
||||||
|
self.step += 1
|
||||||
if self._abort:
|
if self._abort:
|
||||||
raise FitAbortException(f'Fit aborted by user')
|
raise FitAbortException(f'Fit aborted by user')
|
||||||
return (self.__cost_scipy_glob(p, data, var, data_pars)**2).sum()
|
return (_cost_scipy_glob(p, data, var, data_pars) ** 2).sum()
|
||||||
|
|
||||||
with np.errstate(all='ignore'):
|
with np.errstate(all='ignore'):
|
||||||
res = optimize.minimize(cost, p0, bounds=[(b1, b2) for (b1, b2) in zip(lb, ub)],
|
res = optimize.minimize(cost, p0, bounds=[(b1, b2) for (b1, b2) in zip(lb, ub)],
|
||||||
method='Nelder-Mead', options={'maxiter': 1000 * len(p0)})
|
method='Nelder-Mead', options={'maxiter': 500 * len(p0)})
|
||||||
|
|
||||||
for v, var_pars_k in zip(data, data_pars):
|
for v, var_pars_k in zip(data, data_pars):
|
||||||
self.make_results(v, res.x, var, var_pars_k, (sum(len(d) for d in data), len(p0)))
|
self.make_results(v, res.x, var, var_pars_k, (sum(len(d) for d in data), len(p0)))
|
||||||
@ -365,15 +398,21 @@ class FitRoutine(object):
|
|||||||
odr_data = odr.Data(data.x, data.y)
|
odr_data = odr.Data(data.x, data.y)
|
||||||
|
|
||||||
def func(p, _):
|
def func(p, _):
|
||||||
|
self.step += 1
|
||||||
if self._abort:
|
if self._abort:
|
||||||
raise FitAbortException(f'Fit aborted by user')
|
raise FitAbortException(f'Fit aborted by user')
|
||||||
return self.__cost_odr(p, data, var_pars, data.para_keys)
|
return _cost_odr(p, data, var_pars, data.para_keys)
|
||||||
|
|
||||||
odr_model = odr.Model(func)
|
odr_model = odr.Model(func)
|
||||||
|
|
||||||
|
corr, partial_corr, res = self._odr_fit(odr_data, odr_model, p0)
|
||||||
|
|
||||||
|
self.make_results(data, res.beta, var_pars, data.para_keys, (len(data), len(p0)),
|
||||||
|
err=res.sd_beta, corr=corr, partial_corr=partial_corr)
|
||||||
|
|
||||||
|
def _odr_fit(self, odr_data, odr_model, p0):
|
||||||
o = odr.ODR(odr_data, odr_model, beta0=p0)
|
o = odr.ODR(odr_data, odr_model, beta0=p0)
|
||||||
res = o.run()
|
res = o.run()
|
||||||
|
|
||||||
corr = res.cov_beta / (res.sd_beta[:, None] * res.sd_beta[None, :]) * res.res_var
|
corr = res.cov_beta / (res.sd_beta[:, None] * res.sd_beta[None, :]) * res.res_var
|
||||||
try:
|
try:
|
||||||
corr_inv = np.linalg.inv(corr)
|
corr_inv = np.linalg.inv(corr)
|
||||||
@ -382,15 +421,14 @@ class FitRoutine(object):
|
|||||||
partial_corr[np.diag_indices_from(partial_corr)] = 1.
|
partial_corr[np.diag_indices_from(partial_corr)] = 1.
|
||||||
except np.linalg.LinAlgError:
|
except np.linalg.LinAlgError:
|
||||||
partial_corr = corr
|
partial_corr = corr
|
||||||
|
return corr, partial_corr, res
|
||||||
self.make_results(data, res.beta, var_pars, data.para_keys, (len(data), len(p0)),
|
|
||||||
err=res.sd_beta, corr=corr, partial_corr=partial_corr)
|
|
||||||
|
|
||||||
def _odr_global(self, data, p0, var, data_pars):
|
def _odr_global(self, data, p0, var, data_pars):
|
||||||
def func(p, _):
|
def func(p, _):
|
||||||
|
self.step += 1
|
||||||
if self._abort:
|
if self._abort:
|
||||||
raise FitAbortException(f'Fit aborted by user')
|
raise FitAbortException(f'Fit aborted by user')
|
||||||
return self.__cost_odr_glob(p, data, var, data_pars)
|
return _cost_odr_glob(p, data, var, data_pars)
|
||||||
|
|
||||||
x = []
|
x = []
|
||||||
y = []
|
y = []
|
||||||
@ -401,17 +439,7 @@ class FitRoutine(object):
|
|||||||
odr_data = odr.Data(x, y)
|
odr_data = odr.Data(x, y)
|
||||||
odr_model = odr.Model(func)
|
odr_model = odr.Model(func)
|
||||||
|
|
||||||
o = odr.ODR(odr_data, odr_model, beta0=p0, ifixb=var)
|
corr, partial_corr, res = self._odr_fit(odr_data, odr_model, p0)
|
||||||
res = o.run()
|
|
||||||
|
|
||||||
corr = res.cov_beta / (res.sd_beta[:, None] * res.sd_beta[None, :]) * res.res_var
|
|
||||||
try:
|
|
||||||
corr_inv = np.linalg.inv(corr)
|
|
||||||
corr_inv_diag = np.diag(np.sqrt(1 / np.diag(corr_inv)))
|
|
||||||
partial_corr = -1. * np.dot(np.dot(corr_inv_diag, corr_inv), corr_inv_diag) # Partial correlation matrix
|
|
||||||
partial_corr[np.diag_indices_from(partial_corr)] = 1.
|
|
||||||
except np.linalg.LinAlgError:
|
|
||||||
partial_corr = corr
|
|
||||||
|
|
||||||
for v, var_pars_k in zip(data, data_pars):
|
for v, var_pars_k in zip(data, data_pars):
|
||||||
self.make_results(v, res.beta, var, var_pars_k, (sum(len(d) for d in data), len(p0)),
|
self.make_results(v, res.beta, var, var_pars_k, (sum(len(d) for d in data), len(p0)),
|
||||||
@ -425,15 +453,17 @@ class FitRoutine(object):
|
|||||||
|
|
||||||
# update parameter values
|
# update parameter values
|
||||||
for keys, p_value, err_value in zip(var_pars, p, err):
|
for keys, p_value, err_value in zip(var_pars, p, err):
|
||||||
self.parameter[keys].scaled_value = p_value
|
if keys in data.parameter.keys():
|
||||||
self.parameter[keys].scaled_error = err_value
|
data.parameter[keys].scaled_value = p_value
|
||||||
|
data.parameter[keys].scaled_error = err_value
|
||||||
|
data.parameter[keys].namespace[keys] = data.parameter[keys].value
|
||||||
|
|
||||||
combinations = list(product(var_pars, var_pars))
|
combinations = list(product(var_pars, var_pars))
|
||||||
actual_parameters = []
|
actual_parameters = []
|
||||||
corr_idx = []
|
corr_idx = []
|
||||||
|
|
||||||
for i, p_i in enumerate(used_pars):
|
for i, p_i in enumerate(used_pars):
|
||||||
actual_parameters.append(self.parameter[p_i])
|
actual_parameters.append(data.parameter[p_i])
|
||||||
for j, p_j in enumerate(used_pars):
|
for j, p_j in enumerate(used_pars):
|
||||||
try:
|
try:
|
||||||
# find the position of the parameter combinations
|
# find the position of the parameter combinations
|
||||||
@ -454,9 +484,18 @@ class FitRoutine(object):
|
|||||||
idx = self.data.index(data)
|
idx = self.data.index(data)
|
||||||
model = data.get_model()
|
model = data.get_model()
|
||||||
|
|
||||||
self.result[idx] = FitResultCreator.make_with_model(model, data.x, data.y,
|
self.result[idx] = FitResultCreator.make_with_model(
|
||||||
actual_parameters, data.fun_kwargs, data.idx,
|
model,
|
||||||
*shape, corr=actual_corr, pcorr=actual_pcorr)
|
data.x,
|
||||||
|
data.y,
|
||||||
|
actual_parameters,
|
||||||
|
data.fun_kwargs,
|
||||||
|
data.we_string,
|
||||||
|
data.idx,
|
||||||
|
*shape,
|
||||||
|
corr=actual_corr,
|
||||||
|
pcorr=actual_pcorr,
|
||||||
|
)
|
||||||
|
|
||||||
return self.result
|
return self.result
|
||||||
|
|
||||||
@ -464,7 +503,14 @@ class FitRoutine(object):
|
|||||||
def _calc_error(jac, chi, nobs, nvars):
|
def _calc_error(jac, chi, nobs, nvars):
|
||||||
# copy of scipy.curve_fit to calculate covariance
|
# copy of scipy.curve_fit to calculate covariance
|
||||||
# noinspection PyTupleAssignmentBalance
|
# noinspection PyTupleAssignmentBalance
|
||||||
|
try:
|
||||||
_, s, vt = la.svd(jac, full_matrices=False)
|
_, s, vt = la.svd(jac, full_matrices=False)
|
||||||
|
except ValueError as e:
|
||||||
|
# this may be issue #39: On entry to DGESSD parameter had an illegal value
|
||||||
|
# catch this exception and ignore error calculation
|
||||||
|
logger.error(f'Error calculation failed with {e.args}')
|
||||||
|
pcov = None
|
||||||
|
else:
|
||||||
threshold = EPS * max(jac.shape) * s[0]
|
threshold = EPS * max(jac.shape) * s[0]
|
||||||
s = s[s > threshold]
|
s = s[s > threshold]
|
||||||
vt = vt[:s.size]
|
vt = vt[:s.size]
|
||||||
@ -487,3 +533,4 @@ class FitRoutine(object):
|
|||||||
partial_corr = corr
|
partial_corr = corr
|
||||||
|
|
||||||
return _err, corr, partial_corr
|
return _err, corr, partial_corr
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ from typing import Sized
|
|||||||
from numpy import inf
|
from numpy import inf
|
||||||
|
|
||||||
from ._meta import MultiModel
|
from ._meta import MultiModel
|
||||||
from .parameter import Parameters
|
from .parameter import Parameters, Parameter
|
||||||
|
|
||||||
|
|
||||||
class Model(object):
|
class Model(object):
|
||||||
@ -25,7 +25,6 @@ class Model(object):
|
|||||||
self.ub = [i if i is not None else inf for i in self.ub]
|
self.ub = [i if i is not None else inf for i in self.ub]
|
||||||
|
|
||||||
self.parameter = Parameters()
|
self.parameter = Parameters()
|
||||||
self.global_parameter = {}
|
|
||||||
self.is_complex = None
|
self.is_complex = None
|
||||||
self._complex_part = False
|
self._complex_part = False
|
||||||
|
|
||||||
@ -80,23 +79,33 @@ class Model(object):
|
|||||||
self.fun_kwargs = {k: v.default for k, v in inspect.signature(model.func).parameters.items()
|
self.fun_kwargs = {k: v.default for k, v in inspect.signature(model.func).parameters.items()
|
||||||
if v.default is not inspect.Parameter.empty}
|
if v.default is not inspect.Parameter.empty}
|
||||||
|
|
||||||
def set_global_parameter(self, idx, p, var=None, lb=None, ub=None, default_bounds=False):
|
def set_global_parameter(self,
|
||||||
if idx is None:
|
key: str | Parameter,
|
||||||
self.parameter = Parameters()
|
value: float | str = None,
|
||||||
self.global_parameter = {}
|
*,
|
||||||
return
|
var: bool = None,
|
||||||
|
lb: float = None,
|
||||||
|
ub: float = None,
|
||||||
|
default_bounds: bool = False,
|
||||||
|
) -> Parameter:
|
||||||
|
|
||||||
|
if isinstance(key, Parameter):
|
||||||
|
p = key
|
||||||
|
key = f'p{next(Parameters.parameter_counter)}'
|
||||||
|
self.parameter.add_parameter(key, p)
|
||||||
|
|
||||||
|
else:
|
||||||
|
idx = [self.params.index(key)]
|
||||||
if default_bounds:
|
if default_bounds:
|
||||||
if lb is None:
|
if lb is None:
|
||||||
lb = [self.lb[i] for i in idx]
|
lb = [self.lb[i] for i in idx]
|
||||||
if ub is None:
|
if ub is None:
|
||||||
ub = [self.lb[i] for i in idx]
|
ub = [self.lb[i] for i in idx]
|
||||||
|
|
||||||
gp = self.parameter.add_parameter(p, var=var, lb=lb, ub=ub)
|
p = self.parameter.add(key, value, var=var, lb=lb, ub=ub)
|
||||||
for k, v in zip(idx, gp):
|
p.is_global = True
|
||||||
self.global_parameter[k] = v
|
|
||||||
|
|
||||||
return gp
|
return p
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _prep(param_len, val):
|
def _prep(param_len, val):
|
||||||
@ -126,12 +135,12 @@ class Model(object):
|
|||||||
kwargs = self.fun_kwargs
|
kwargs = self.fun_kwargs
|
||||||
|
|
||||||
if not self.is_multi:
|
if not self.is_multi:
|
||||||
return [self.func(p, x, **kwargs)]
|
return []
|
||||||
else:
|
else:
|
||||||
return list(self._int_iter(x, *p, *self.fun_args, **kwargs))
|
return list(self._int_iter(x, *p, *self.fun_args, **kwargs))
|
||||||
|
|
||||||
def sub_name(self):
|
def sub_name(self):
|
||||||
if not self.is_multi:
|
if not self.is_multi:
|
||||||
return [self.name]
|
return []
|
||||||
else:
|
else:
|
||||||
return list(self._iter_name())
|
return list(self._iter_name())
|
||||||
|
@ -1,94 +1,233 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from numbers import Number
|
import ast
|
||||||
|
import re
|
||||||
from itertools import count
|
from itertools import count
|
||||||
|
from io import StringIO
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
class Parameters(dict):
|
class Parameters(dict):
|
||||||
count = count()
|
parameter_counter = count()
|
||||||
|
# is one global namespace a good idea?
|
||||||
|
namespace: dict = {}
|
||||||
|
|
||||||
def __str__(self):
|
def __init__(self):
|
||||||
return 'Parameters:\n' + '\n'.join([str(k)+': '+str(v) for k, v in self.items()])
|
super().__init__()
|
||||||
|
self._mapping: dict = {}
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __str__(self) -> str:
|
||||||
if isinstance(item, (list, tuple, np.ndarray)):
|
return 'Parameters:\n' + '\n'.join([f'{k}: {v}' for k, v in self.items()])
|
||||||
values = []
|
|
||||||
for item_i in item:
|
def __getitem__(self, item) -> Parameter:
|
||||||
values.append(super().__getitem__(item_i))
|
if item in self._mapping:
|
||||||
return values
|
return super().__getitem__(self._mapping[item])
|
||||||
else:
|
else:
|
||||||
return super().__getitem__(item)
|
return super().__getitem__(item)
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
self.add_parameter(key, value)
|
||||||
|
|
||||||
|
def __contains__(self, item):
|
||||||
|
for v in self.values():
|
||||||
|
if item == v.name:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def add(self,
|
||||||
|
name: str,
|
||||||
|
value: str | float | int = None,
|
||||||
|
*,
|
||||||
|
var: bool = True,
|
||||||
|
lb: str | float = -np.inf,
|
||||||
|
ub: str | float = np.inf) -> Parameter:
|
||||||
|
|
||||||
|
par = Parameter(name=name, value=value, var=var, lb=lb, ub=ub)
|
||||||
|
key = f'_p{next(Parameters.parameter_counter)}'
|
||||||
|
|
||||||
|
self.add_parameter(key, par)
|
||||||
|
|
||||||
|
return par
|
||||||
|
|
||||||
|
def add_parameter(self, key: str, parameter: Parameter):
|
||||||
|
self._mapping[parameter.name] = key
|
||||||
|
super().__setitem__(key, parameter)
|
||||||
|
|
||||||
|
parameter.eval_allowed = False
|
||||||
|
self.namespace[key] = parameter.value
|
||||||
|
parameter.namespace = self.namespace
|
||||||
|
parameter.eval_allowed = True
|
||||||
|
|
||||||
|
self.update_namespace()
|
||||||
|
|
||||||
|
def replace_parameter(self, key_out: str, key_in: str, parameter: Parameter):
|
||||||
|
self.add_parameter(key_in, parameter)
|
||||||
|
for k, v in self._mapping.items():
|
||||||
|
if v == key_out:
|
||||||
|
self._mapping[k] = key_in
|
||||||
|
break
|
||||||
|
|
||||||
|
if key_out in self.namespace:
|
||||||
|
del self.namespace[key_out]
|
||||||
|
|
||||||
|
def fix(self):
|
||||||
|
for v in self.keys():
|
||||||
|
v._value = v.value
|
||||||
|
v.namespace = {}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _prep_bounds(val, p_len: int) -> list:
|
def reset():
|
||||||
# helper function to ensure that bounds and variable are of parameter shape
|
Parameters.namespace = {}
|
||||||
if isinstance(val, (Number, bool)) or val is None:
|
|
||||||
return [val] * p_len
|
|
||||||
|
|
||||||
elif len(val) == p_len:
|
def get_key(self, name: str) -> str | None:
|
||||||
return val
|
|
||||||
|
|
||||||
elif len(val) == 1:
|
|
||||||
return [val[0]] * p_len
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise ValueError('Input {} has wrong dimensions'.format(val))
|
|
||||||
|
|
||||||
def add_parameter(self, param, var=None, lb=None, ub=None):
|
|
||||||
if isinstance(param, Number):
|
|
||||||
param = [param]
|
|
||||||
|
|
||||||
p_len = len(param)
|
|
||||||
|
|
||||||
# make list if only single value is given
|
|
||||||
var = self._prep_bounds(var, p_len)
|
|
||||||
lb = self._prep_bounds(lb, p_len)
|
|
||||||
ub = self._prep_bounds(ub, p_len)
|
|
||||||
|
|
||||||
new_keys = []
|
|
||||||
for i in range(p_len):
|
|
||||||
new_idx = next(self.count)
|
|
||||||
new_keys.append(new_idx)
|
|
||||||
|
|
||||||
self[new_idx] = Parameter(param[i], var=var[i], lb=lb[i], ub=ub[i])
|
|
||||||
|
|
||||||
return new_keys
|
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
p = Parameters()
|
|
||||||
for k, v in self.items():
|
for k, v in self.items():
|
||||||
p[k] = Parameter(v.value, var=v.var, lb=v.lb, ub=v.ub)
|
if name == v.name:
|
||||||
|
return k
|
||||||
|
|
||||||
if len(p) == 0:
|
return
|
||||||
return p
|
|
||||||
|
|
||||||
max_k = max(p.keys())
|
|
||||||
c = next(p.count)
|
|
||||||
while c < max_k:
|
|
||||||
c = next(p.count)
|
|
||||||
|
|
||||||
return p
|
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
return {k: v.get_state() for k, v in self.items()}
|
return {k: v.get_state() for k, v in self.items()}
|
||||||
|
|
||||||
|
def update_namespace(self):
|
||||||
|
for p in self.values():
|
||||||
|
try:
|
||||||
|
p.value
|
||||||
|
except NameError:
|
||||||
|
expression = p._expr_disp
|
||||||
|
for n, k in self._mapping.items():
|
||||||
|
expression, num_replaced = re.subn(re.escape(n), k, expression)
|
||||||
|
|
||||||
|
p._expr = expression
|
||||||
|
|
||||||
|
def prepare_bounds(self):
|
||||||
|
print('prepare_bounds')
|
||||||
|
original_values = list(self.values())
|
||||||
|
for param in original_values:
|
||||||
|
already_with_expression = False
|
||||||
|
for mode, value in (('lower', param.lb), ('upper', param.ub)):
|
||||||
|
print(mode, value)
|
||||||
|
if already_with_expression:
|
||||||
|
raise ValueError('Only one boundary can be an expression')
|
||||||
|
already_with_expression = self.parse(param, value, bnd=mode)
|
||||||
|
|
||||||
|
def parse(self, parameter, expression: str, bnd: str = 'lower') -> bool:
|
||||||
|
try:
|
||||||
|
float(expression)
|
||||||
|
return False
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
pp = ast.parse(expression)
|
||||||
|
expr_syms = pp.body[0].value
|
||||||
|
|
||||||
|
if isinstance(expr_syms, ast.BinOp):
|
||||||
|
left, op, right = expr_syms.left, expr_syms.op, expr_syms.right
|
||||||
|
|
||||||
|
# check for sign in numerator
|
||||||
|
sign = 1
|
||||||
|
if isinstance(left, ast.UnaryOp):
|
||||||
|
if isinstance(left.op, (ast.Not, ast.Invert)):
|
||||||
|
raise ValueError('Only `+` and `-` are supported for signs')
|
||||||
|
if isinstance(left.op, ast.USub):
|
||||||
|
sign = -1
|
||||||
|
left = left.operand
|
||||||
|
|
||||||
|
# is expression number / parameter?
|
||||||
|
if not (isinstance(left, ast.Constant) and isinstance(op, ast.Div) and isinstance(right, ast.Name)):
|
||||||
|
raise ValueError('Only simple division `const/parameter` are possible')
|
||||||
|
|
||||||
|
result = self.make_proxy_div(parameter, left.value*sign, right.id, bnd=bnd)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError('I cannot work under these conditions')
|
||||||
|
|
||||||
|
def make_proxy_div(self, param, num: str | float, denom: str | float, bnd: str = 'upper') -> bool:
|
||||||
|
other_param = self[denom]
|
||||||
|
other_lb, other_ub = other_param.lb, other_param.ub
|
||||||
|
|
||||||
|
proxy = {'name': f'{param.name}*{other_param.name}', 'value': other_param.value*param.value, 'lb': None, 'ub': None}
|
||||||
|
|
||||||
|
# switching signs is bad for inequalities
|
||||||
|
if other_lb < 0 < other_ub:
|
||||||
|
raise ValueError('Parameter in expression changes sign')
|
||||||
|
|
||||||
|
if bnd == 'upper':
|
||||||
|
# this whole schlamassel is only working for some values as lower bound
|
||||||
|
if param.lb not in [-np.inf, 0]:
|
||||||
|
raise ValueError('Invalid lower bounds')
|
||||||
|
|
||||||
|
if other_ub < 0 or num < 0:
|
||||||
|
# upper bound is always negative, switch boundaries
|
||||||
|
proxy['lb'] = num
|
||||||
|
proxy['ub'] = param.lb if param.lb == 0 else np.inf
|
||||||
|
else:
|
||||||
|
# upper bound is always positive
|
||||||
|
proxy['lb'] = param.lb
|
||||||
|
proxy['ub'] = num
|
||||||
|
|
||||||
|
elif bnd == 'lower':
|
||||||
|
if param.ub not in [np.inf, 0]:
|
||||||
|
raise ValueError('Invalid upper bound')
|
||||||
|
|
||||||
|
if other_ub <= 0 or num < 0:
|
||||||
|
proxy['lb'] = param.lb if param.lb == 0 else np.inf
|
||||||
|
proxy['ub'] = num
|
||||||
|
else:
|
||||||
|
proxy['lb'] = num
|
||||||
|
proxy['ub'] = param.ub
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError(f'unknown bound {bnd!r}, use `upper`or `lower`')
|
||||||
|
|
||||||
|
param.set_expression(f'{num}/{denom}')
|
||||||
|
self.add(**proxy)
|
||||||
|
self.update_namespace()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class Parameter:
|
class Parameter:
|
||||||
"""
|
"""
|
||||||
Container for one parameter
|
Container for one parameter
|
||||||
"""
|
"""
|
||||||
__slots__ = ['name', 'value', 'error', 'init_val', 'var', 'lb', 'ub', 'scale', 'function']
|
|
||||||
|
|
||||||
def __init__(self, value: float, var: bool = True, lb: float = -np.inf, ub: float = np.inf):
|
# TODO Parameter should know its own key
|
||||||
self.lb = lb if lb is not None else -np.inf
|
def __init__(self,
|
||||||
self.ub = ub if ub is not None else np.inf
|
name: str,
|
||||||
|
value: float | str,
|
||||||
|
var: bool = True,
|
||||||
|
lb: str | float = -np.inf,
|
||||||
|
ub: str | float = np.inf,
|
||||||
|
):
|
||||||
|
self._value: float | None = None
|
||||||
|
self.var: bool = bool(var) if var is not None else True
|
||||||
|
self.error: None | float = None if self.var is False else 0.0
|
||||||
|
self.name: str = name
|
||||||
|
self.function: str = ""
|
||||||
|
|
||||||
if self.lb <= value <= self.ub:
|
self.lb: str | None | float = lb if lb is not None else -np.inf
|
||||||
self.value = value
|
self.ub: str | float | None = ub if ub is not None else np.inf
|
||||||
|
self.namespace: dict = {}
|
||||||
|
self.eval_allowed: bool = True
|
||||||
|
self._expr: None | str = None
|
||||||
|
self._expr_disp: None | str = None
|
||||||
|
self.is_global = False
|
||||||
|
|
||||||
|
if isinstance(value, str):
|
||||||
|
self._expr_disp = value
|
||||||
|
self._expr = value
|
||||||
|
self.var = False
|
||||||
|
else:
|
||||||
|
if isinstance(self.lb, (int, float)) and isinstance(self.ub, (int, float)):
|
||||||
|
if (self.lb <= value <= self.ub) or (not self.var):
|
||||||
|
self._value = value
|
||||||
else:
|
else:
|
||||||
raise ValueError('Value of parameter is outside bounds')
|
raise ValueError('Value of parameter is outside bounds')
|
||||||
|
else:
|
||||||
|
self._value = value
|
||||||
|
|
||||||
self.init_val = value
|
self.init_val = value
|
||||||
|
|
||||||
@ -99,25 +238,31 @@ class Parameter:
|
|||||||
if self.scale == 0:
|
if self.scale == 0:
|
||||||
self.scale = 1.
|
self.scale = 1.
|
||||||
|
|
||||||
self.var = bool(var) if var is not None else True
|
def __str__(self) -> str:
|
||||||
self.error = None if self.var is False else 0.0
|
start = StringIO()
|
||||||
self.name = ''
|
|
||||||
self.function = ''
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
start = ''
|
|
||||||
if self.name:
|
if self.name:
|
||||||
if self.function:
|
if self.function:
|
||||||
start = f'{self.name} ({self.function}): '
|
start.write(f"{self.name} ({self.function})")
|
||||||
else:
|
else:
|
||||||
start = self.name + ': '
|
start.write(self.name)
|
||||||
|
|
||||||
|
if self.is_global:
|
||||||
|
start.write("*")
|
||||||
|
|
||||||
|
start.write(": ")
|
||||||
|
|
||||||
if self.var:
|
if self.var:
|
||||||
return start + f'{self.value:.4g} +/- {self.error:.4g}, init={self.init_val}'
|
start.write(f"{self.value:.4g} +/- {self.error:.4g}, init={self.init_val}")
|
||||||
else:
|
else:
|
||||||
return start + f'{self.value:} (fixed)'
|
start.write(f"{self.value:.4g}")
|
||||||
|
if self._expr is None:
|
||||||
|
start.write(" (fixed)")
|
||||||
|
else:
|
||||||
|
start.write(f" (calc: {self._expr_disp})")
|
||||||
|
|
||||||
def __add__(self, other: Parameter | float) -> float:
|
return start.getvalue()
|
||||||
|
|
||||||
|
def __add__(self, other: Parameter | float | int) -> float:
|
||||||
if isinstance(other, (float, int)):
|
if isinstance(other, (float, int)):
|
||||||
return self.value + other
|
return self.value + other
|
||||||
elif isinstance(other, Parameter):
|
elif isinstance(other, Parameter):
|
||||||
@ -127,40 +272,88 @@ class Parameter:
|
|||||||
return self.__add__(other)
|
return self.__add__(other)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def scaled_value(self):
|
def scaled_value(self) -> float:
|
||||||
return self.value / self.scale
|
return self.value / self.scale
|
||||||
|
|
||||||
@scaled_value.setter
|
@scaled_value.setter
|
||||||
def scaled_value(self, value):
|
def scaled_value(self, value: float) -> None:
|
||||||
self.value = value * self.scale
|
self._value = value * self.scale
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def scaled_error(self):
|
def value(self) -> float | None:
|
||||||
if self.error is None:
|
if self._value is not None:
|
||||||
return self.error
|
return self._value
|
||||||
else:
|
|
||||||
|
if self._expr is not None and self.eval_allowed:
|
||||||
|
return eval(self._expr, {}, self.namespace)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def set_expression(self, expr: str):
|
||||||
|
self._value = None
|
||||||
|
self._expr_disp = expr
|
||||||
|
self._expr = expr
|
||||||
|
self.var = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scaled_error(self) -> None | float:
|
||||||
|
if self.error is not None:
|
||||||
return self.error / self.scale
|
return self.error / self.scale
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
@scaled_error.setter
|
@scaled_error.setter
|
||||||
def scaled_error(self, value):
|
def scaled_error(self, value) -> None:
|
||||||
self.error = value * self.scale
|
self.error = value * self.scale
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self) -> dict:
|
||||||
|
state_dict = {
|
||||||
return {slot: getattr(self, slot) for slot in self.__slots__}
|
'name': self.name,
|
||||||
|
'value': self._value,
|
||||||
|
'error': self.error,
|
||||||
|
'init_val': self.init_val,
|
||||||
|
'var': self.var,
|
||||||
|
'lb': self.lb,
|
||||||
|
'ub': self.ub,
|
||||||
|
'scale': self.scale,
|
||||||
|
'function': self.function,
|
||||||
|
'_expr': self._expr,
|
||||||
|
'_expr_disp': self._expr_disp,
|
||||||
|
'is_global': self.is_global,
|
||||||
|
}
|
||||||
|
return state_dict
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_state(state: dict):
|
def set_state(state: dict) -> Parameter:
|
||||||
par = Parameter(state.pop('value'))
|
par = Parameter(state.get('name'), state.pop('value'))
|
||||||
for k, v in state.items():
|
for k, v in state.items():
|
||||||
setattr(par, k, v)
|
setattr(par, k, v)
|
||||||
|
|
||||||
return par
|
return par
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_name(self):
|
def full_name(self) -> str:
|
||||||
name = self.name
|
name = self.name
|
||||||
if self.function:
|
if self.function:
|
||||||
name += ' (' + self.function + ')'
|
name += f" ({self.function})"
|
||||||
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
def copy(self) -> Parameter:
|
||||||
|
if self._expr:
|
||||||
|
val = self._expr_disp
|
||||||
|
else:
|
||||||
|
val = self._value
|
||||||
|
para_copy = Parameter(name=self.name, value=val, var=self.var, lb=self.lb, ub=self.ub)
|
||||||
|
para_copy._expr = self._expr
|
||||||
|
para_copy.namespace = self.namespace
|
||||||
|
para_copy.is_global = self.is_global
|
||||||
|
para_copy.error = self.error
|
||||||
|
para_copy.function = self.function
|
||||||
|
|
||||||
|
return para_copy
|
||||||
|
|
||||||
|
def fix(self):
|
||||||
|
self._value = self.value
|
||||||
|
self.namespace = {}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from io import StringIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@ -9,6 +10,7 @@ import numpy as np
|
|||||||
from scipy.stats import f as fdist
|
from scipy.stats import f as fdist
|
||||||
from scipy.interpolate import interp1d
|
from scipy.interpolate import interp1d
|
||||||
|
|
||||||
|
from ._meta import MultiModel
|
||||||
from .parameter import Parameter
|
from .parameter import Parameter
|
||||||
from ..data.points import Points
|
from ..data.points import Points
|
||||||
from ..data.signals import Signal
|
from ..data.signals import Signal
|
||||||
@ -17,7 +19,7 @@ from ..utils.text import convert
|
|||||||
|
|
||||||
class FitResultCreator:
|
class FitResultCreator:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def make_from_session(x_orig: np.ndarray, y_orig: np.ndarray, idx: int, kwargs: dict[Any]) -> (dict, list):
|
def make_from_session(x_orig: np.ndarray, y_orig: np.ndarray, idx: int, kwargs: dict[Any]) -> FitResult:
|
||||||
params = OrderedDict()
|
params = OrderedDict()
|
||||||
|
|
||||||
for key, pbest, err in zip(kwargs['pnames'], kwargs['parameter'], kwargs['error']):
|
for key, pbest, err in zip(kwargs['pnames'], kwargs['parameter'], kwargs['error']):
|
||||||
@ -37,10 +39,22 @@ class FitResultCreator:
|
|||||||
stats = FitResultCreator.calc_statistics(resid, _y)
|
stats = FitResultCreator.calc_statistics(resid, _y)
|
||||||
|
|
||||||
return FitResult(kwargs['x'], kwargs['y'], x_orig, y_orig, params, dict(kwargs['choice']), resid, 0, 0,
|
return FitResult(kwargs['x'], kwargs['y'], x_orig, y_orig, params, dict(kwargs['choice']), resid, 0, 0,
|
||||||
kwargs['name'], stats, idx), []
|
kwargs['name'], stats, idx)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def make_with_model(model, x_orig, y_orig, p, fun_kwargs, idx, nobs, nvar, corr, pcorr) -> (dict, list):
|
def make_with_model(
|
||||||
|
model: 'Model',
|
||||||
|
x_orig: np.ndarray,
|
||||||
|
y_orig: np.ndarray,
|
||||||
|
p: 'Parameters',
|
||||||
|
fun_kwargs: dict,
|
||||||
|
we: str,
|
||||||
|
idx: str | None,
|
||||||
|
nobs: int,
|
||||||
|
nvar: int,
|
||||||
|
corr: np.ndarray,
|
||||||
|
pcorr: np.ndarray,
|
||||||
|
) -> FitResult:
|
||||||
if np.all(x_orig > 0) and (np.max(x_orig) > 100 * np.min(x_orig)):
|
if np.all(x_orig > 0) and (np.max(x_orig) > 100 * np.min(x_orig)):
|
||||||
islog = True
|
islog = True
|
||||||
else:
|
else:
|
||||||
@ -48,7 +62,7 @@ class FitResultCreator:
|
|||||||
|
|
||||||
if len(x_orig) < 51:
|
if len(x_orig) < 51:
|
||||||
if islog:
|
if islog:
|
||||||
_x = np.logspace(np.log10(np.min(x_orig)), np.log10(np.max(x_orig)), num=10*x_orig.size-9)
|
_x = np.geomspace(np.min(x_orig), np.max(x_orig), num=10*x_orig.size-9)
|
||||||
else:
|
else:
|
||||||
_x = np.linspace(np.min(x_orig), np.max(x_orig), num=10*x_orig.size-9)
|
_x = np.linspace(np.min(x_orig), np.max(x_orig), num=10*x_orig.size-9)
|
||||||
else:
|
else:
|
||||||
@ -62,17 +76,22 @@ class FitResultCreator:
|
|||||||
parameters = OrderedDict([(k, v) for k, v in zip(pnames, p)])
|
parameters = OrderedDict([(k, v) for k, v in zip(pnames, p)])
|
||||||
p_final = [p.value for p in parameters.values()]
|
p_final = [p.value for p in parameters.values()]
|
||||||
|
|
||||||
part_functions = []
|
resid = model.func(p_final, x_orig, **fun_kwargs) - y_orig
|
||||||
|
|
||||||
if model.is_multi:
|
actual_mode = -1
|
||||||
for sub_name, sub_y in zip(model.sub_name(), model.sub(p_final, _x, **fun_kwargs)):
|
if 'complex_mode' in fun_kwargs:
|
||||||
if np.iscomplexobj(sub_y):
|
actual_mode = fun_kwargs['complex_mode']
|
||||||
part_functions.append(Signal(_x, sub_y, name=sub_name))
|
fun_kwargs['complex_mode'] = 0
|
||||||
else:
|
|
||||||
part_functions.append(Points(_x, sub_y, name=sub_name))
|
|
||||||
|
|
||||||
_y = model.func(p_final, _x, **fun_kwargs)
|
_y = model.func(p_final, _x, **fun_kwargs)
|
||||||
resid = model.func(p_final, x_orig, **fun_kwargs) - y_orig
|
|
||||||
|
if not actual_mode < 0:
|
||||||
|
if actual_mode == 1:
|
||||||
|
_y.imag = 0
|
||||||
|
elif actual_mode == 2:
|
||||||
|
_y.real = 0
|
||||||
|
|
||||||
|
fun_kwargs['complex_mode'] = actual_mode
|
||||||
|
|
||||||
stats = FitResultCreator.calc_statistics(_y, resid, nobs, nvar)
|
stats = FitResultCreator.calc_statistics(_y, resid, nobs, nvar)
|
||||||
varied = [p.var for p in parameters.values()]
|
varied = [p.var for p in parameters.values()]
|
||||||
@ -97,12 +116,24 @@ class FitResultCreator:
|
|||||||
correlation = corr
|
correlation = corr
|
||||||
partial_correlation = pcorr
|
partial_correlation = pcorr
|
||||||
|
|
||||||
return (
|
return FitResult(
|
||||||
FitResult(_x, _y, x_orig, y_orig, parameters, fun_kwargs, resid,
|
x=_x,
|
||||||
nobs, nvar, model.name, stats,
|
y=_y,
|
||||||
idx=idx, corr=correlation, pcorr=partial_correlation,
|
x_data=x_orig,
|
||||||
islog=islog),
|
y_data=y_orig,
|
||||||
part_functions,
|
params=parameters,
|
||||||
|
fun_kwargs=fun_kwargs,
|
||||||
|
resid=resid,
|
||||||
|
nobs=nobs,
|
||||||
|
nvar=nvar,
|
||||||
|
we=we,
|
||||||
|
name=model.name,
|
||||||
|
stats=stats,
|
||||||
|
idx=idx,
|
||||||
|
corr=correlation,
|
||||||
|
pcorr=partial_correlation,
|
||||||
|
islog=islog,
|
||||||
|
func=model,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -141,19 +172,38 @@ class FitResultCreator:
|
|||||||
|
|
||||||
class FitResult(Points):
|
class FitResult(Points):
|
||||||
|
|
||||||
def __init__(self, x, y, x_data, y_data, params, fun_kwargs, resid, nobs, nvar, name, stats,
|
def __init__(
|
||||||
idx=None, corr=None, pcorr=None, islog=False,
|
self: FitResult,
|
||||||
**kwargs):
|
x: np.ndarray,
|
||||||
|
y: np.ndarray,
|
||||||
|
x_data: np.ndarray,
|
||||||
|
y_data: np.ndarray,
|
||||||
|
params: dict,
|
||||||
|
fun_kwargs: dict,
|
||||||
|
resid: np.ndarray,
|
||||||
|
nobs: int,
|
||||||
|
nvar: int,
|
||||||
|
we: str,
|
||||||
|
name: str,
|
||||||
|
stats: dict,
|
||||||
|
idx: int = None,
|
||||||
|
corr: np.ndarray = None,
|
||||||
|
pcorr: np.ndarray = None,
|
||||||
|
islog: bool = False,
|
||||||
|
func=None,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
|
|
||||||
self.parameter, name = self._prepare_names(params, name)
|
self.parameter, name = self._prepare_names(params, name)
|
||||||
|
label = kwargs.get('label', name)
|
||||||
super().__init__(x=x, y=y, name=name, **kwargs)
|
super().__init__(x=x, y=y, name=label, **kwargs)
|
||||||
|
|
||||||
self.residual = resid
|
self.residual = resid
|
||||||
self.idx = idx
|
self.idx = idx
|
||||||
self.statistics = stats
|
self.statistics = stats
|
||||||
self.nobs = nobs
|
self.nobs = nobs
|
||||||
self.nvar = nvar
|
self.nvar = nvar
|
||||||
|
self.we = we
|
||||||
self.fun_kwargs = fun_kwargs
|
self.fun_kwargs = fun_kwargs
|
||||||
self.correlation = corr
|
self.correlation = corr
|
||||||
self.partial_correlation = pcorr
|
self.partial_correlation = pcorr
|
||||||
@ -162,6 +212,7 @@ class FitResult(Points):
|
|||||||
self.x_data = x_data
|
self.x_data = x_data
|
||||||
self.y_data = y_data
|
self.y_data = y_data
|
||||||
self._model_name = name
|
self._model_name = name
|
||||||
|
self._func = func
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _prepare_names(parameter: dict, modelname: str):
|
def _prepare_names(parameter: dict, modelname: str):
|
||||||
@ -179,7 +230,7 @@ class FitResult(Points):
|
|||||||
nice_name = m.group(1)
|
nice_name = m.group(1)
|
||||||
if func_number in split_funcs:
|
if func_number in split_funcs:
|
||||||
nice_func = split_funcs[func_number]
|
nice_func = split_funcs[func_number]
|
||||||
|
pvalue.fix()
|
||||||
pvalue.name = nice_name
|
pvalue.name = nice_name
|
||||||
pvalue.function = nice_func
|
pvalue.function = nice_func
|
||||||
parameter_dic[pname] = pvalue
|
parameter_dic[pname] = pvalue
|
||||||
@ -200,6 +251,13 @@ class FitResult(Points):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
return 'FitObject'
|
return 'FitObject'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def func(self):
|
||||||
|
if isinstance(self._func, MultiModel):
|
||||||
|
return self._func.func
|
||||||
|
else:
|
||||||
|
return self._func
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def p_final(self):
|
def p_final(self):
|
||||||
return [pp.value for pp in self.parameter.values()]
|
return [pp.value for pp in self.parameter.values()]
|
||||||
@ -209,26 +267,31 @@ class FitResult(Points):
|
|||||||
return self.nobs-self.nvar
|
return self.nobs-self.nvar
|
||||||
|
|
||||||
def pprint(self, statistics=True, correlations=True):
|
def pprint(self, statistics=True, correlations=True):
|
||||||
print('Fit result:')
|
s = StringIO()
|
||||||
print(' model :', self.name)
|
s.write('Fit result:\n')
|
||||||
print(' #data :', self.nobs)
|
s.write(f' model : {self.name}\n')
|
||||||
print(' #var :', self.nvar)
|
s.write(f' #data : {self.nobs}\n')
|
||||||
print('\nParameter')
|
s.write(f' #var : {self.nvar}\n')
|
||||||
print(self._parameter_string())
|
s.write(f' #weight: {self.we}\n')
|
||||||
|
s.write('\nParameter\n')
|
||||||
|
s.write(self.parameter_string())
|
||||||
|
|
||||||
if statistics:
|
if statistics:
|
||||||
print('Statistics')
|
s.write('\nStatistics\n')
|
||||||
for k, v in self.statistics.items():
|
for k, v in self.statistics.items():
|
||||||
print(f' {k} : {v:.4f}')
|
s.write(f' {k} : {v:.4f}\n')
|
||||||
|
|
||||||
if correlations and self.correlation is not None:
|
if correlations and self.correlation is not None:
|
||||||
print('\nCorrelation (partial corr.)')
|
s.write('\nCorrelation (partial corr.)\n')
|
||||||
print(self._correlation_string())
|
s.write(self._correlation_string())
|
||||||
print()
|
s.write('\n')
|
||||||
|
|
||||||
def _parameter_string(self):
|
print(s.getvalue())
|
||||||
|
|
||||||
|
def parameter_string(self):
|
||||||
ret_val = ''
|
ret_val = ''
|
||||||
|
|
||||||
for pval in self.parameter.values():
|
for pkey, pval in self.parameter.items():
|
||||||
ret_val += convert(str(pval), old='tex', new='str') + '\n'
|
ret_val += convert(str(pval), old='tex', new='str') + '\n'
|
||||||
|
|
||||||
if self.fun_kwargs:
|
if self.fun_kwargs:
|
||||||
@ -240,9 +303,7 @@ class FitResult(Points):
|
|||||||
def _correlation_string(self):
|
def _correlation_string(self):
|
||||||
ret_val = ''
|
ret_val = ''
|
||||||
for p_i, p_j, corr_ij, pcorr_ij in self.correlation_list():
|
for p_i, p_j, corr_ij, pcorr_ij in self.correlation_list():
|
||||||
ret_val += ' {} / {} : {:.4f} ({:.4f})\n'.format(convert(p_i, old='tex', new='str'),
|
ret_val += f" {convert(p_i, old='tex', new='str')} / {convert(p_j, old='tex', new='str')} : {corr_ij:.4f} ({pcorr_ij:.4f})\n"
|
||||||
convert(p_j, old='tex', new='str'),
|
|
||||||
corr_ij, pcorr_ij)
|
|
||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
def correlation_list(self, limit=0.1):
|
def correlation_list(self, limit=0.1):
|
||||||
@ -291,7 +352,7 @@ class FitResult(Points):
|
|||||||
|
|
||||||
with path.open(writemode) as f:
|
with path.open(writemode) as f:
|
||||||
if overwrite or not path.exists():
|
if overwrite or not path.exists():
|
||||||
f.write('# label(1)\t')
|
f.write('# xvalue(1)\t')
|
||||||
for i, pname in enumerate(self.parameter.keys()):
|
for i, pname in enumerate(self.parameter.keys()):
|
||||||
raw_name = convert(pname, old='tex', new='str')
|
raw_name = convert(pname, old='tex', new='str')
|
||||||
f.write(f'{raw_name}({2*i+2})\t{raw_name}_err({2*i+3})\t')
|
f.write(f'{raw_name}({2*i+2})\t{raw_name}_err({2*i+3})\t')
|
||||||
@ -305,10 +366,15 @@ class FitResult(Points):
|
|||||||
err = 0.
|
err = 0.
|
||||||
f.write(f'{p.value:.8e}\t{err:.8e}\t')
|
f.write(f'{p.value:.8e}\t{err:.8e}\t')
|
||||||
|
|
||||||
if self.fun_kwargs:
|
|
||||||
f.write('# ')
|
f.write('# ')
|
||||||
|
if self.fun_kwargs:
|
||||||
for k, v in self.fun_kwargs.items():
|
for k, v in self.fun_kwargs.items():
|
||||||
f.write(f"{convert(k, old='tex', new='str')}: {convert(str(v), old='tex', new='str')}\t")
|
f.write(f"{convert(k, old='tex', new='str')}: {convert(str(v), old='tex', new='str')}\t")
|
||||||
|
|
||||||
|
f.write(f'weight: {self.we}\t')
|
||||||
|
|
||||||
|
f.write('\n')
|
||||||
|
f.write(f'# line above from: {self.name} (model: {self.model_name})')
|
||||||
f.write('\n')
|
f.write('\n')
|
||||||
|
|
||||||
def f_test(self, chi2: float, dof: float):
|
def f_test(self, chi2: float, dof: float):
|
||||||
@ -316,7 +382,13 @@ class FitResult(Points):
|
|||||||
f_value = 1e318
|
f_value = 1e318
|
||||||
else:
|
else:
|
||||||
f_value = (chi2-self.statistics['chi^2']) / (dof-self.dof) / self.statistics['red. chi^2']
|
f_value = (chi2-self.statistics['chi^2']) / (dof-self.dof) / self.statistics['red. chi^2']
|
||||||
return f_value, 1-fdist.cdf(f_value, dof-self.dof, self.dof)
|
|
||||||
|
try:
|
||||||
|
prob_f = 1-fdist.cdf(f_value, dof-self.dof, self.dof)
|
||||||
|
except:
|
||||||
|
prob_f = 0
|
||||||
|
|
||||||
|
return f_value, prob_f
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
state = super().get_state()
|
state = super().get_state()
|
||||||
@ -326,6 +398,7 @@ class FitResult(Points):
|
|||||||
state[attr] = getattr(self, attr)
|
state[attr] = getattr(self, attr)
|
||||||
|
|
||||||
state['name'] = self._model_name
|
state['name'] = self._model_name
|
||||||
|
state['label'] = self.name
|
||||||
state['corr'] = self.correlation
|
state['corr'] = self.correlation
|
||||||
state['pcorr'] = self.partial_correlation
|
state['pcorr'] = self.partial_correlation
|
||||||
state['stats'] = self.statistics
|
state['stats'] = self.statistics
|
||||||
@ -342,3 +415,50 @@ class FitResult(Points):
|
|||||||
data = FitResult(**state)
|
data = FitResult(**state)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def with_new_x(self, x_values):
|
||||||
|
if self.func is None:
|
||||||
|
raise ValueError('no fit function available to calculate new y values')
|
||||||
|
|
||||||
|
actual_mode = -1
|
||||||
|
if 'complex_mode' in self.fun_kwargs:
|
||||||
|
actual_mode = self.fun_kwargs['complex_mode']
|
||||||
|
self.fun_kwargs['complex_mode'] = 0
|
||||||
|
|
||||||
|
new_fit = self.copy()
|
||||||
|
y_values = self.func.func(self.p_final, x_values, **self.fun_kwargs)
|
||||||
|
if not actual_mode < 0:
|
||||||
|
if actual_mode == 1:
|
||||||
|
y_values.imag = 0
|
||||||
|
elif actual_mode == 2:
|
||||||
|
y_values.real = 0
|
||||||
|
|
||||||
|
self.fun_kwargs['complex_mode'] = actual_mode
|
||||||
|
|
||||||
|
new_fit.set_data(x_values, y_values, y_err=0.0)
|
||||||
|
|
||||||
|
return new_fit
|
||||||
|
|
||||||
|
def sub(self, x_values):
|
||||||
|
part_functions = []
|
||||||
|
actual_mode = -1
|
||||||
|
if 'complex_mode' in self.fun_kwargs:
|
||||||
|
actual_mode = self.fun_kwargs['complex_mode']
|
||||||
|
self.fun_kwargs['complex_mode'] = 0
|
||||||
|
|
||||||
|
for sub_name, sub_y in zip(self.func.sub_name(), self.func.sub(self.p_final, x_values, **self.fun_kwargs)):
|
||||||
|
if not actual_mode < 0:
|
||||||
|
if actual_mode == 1:
|
||||||
|
sub_y.imag = 0
|
||||||
|
elif actual_mode == 2:
|
||||||
|
sub_y.real = 0
|
||||||
|
|
||||||
|
part_functions.append(Signal(x_values, sub_y, name=sub_name))
|
||||||
|
|
||||||
|
else:
|
||||||
|
part_functions.append(Points(x_values, sub_y, name=sub_name))
|
||||||
|
|
||||||
|
if actual_mode > 0:
|
||||||
|
self.fun_kwargs['complex_mode'] = actual_mode
|
||||||
|
|
||||||
|
return part_functions
|
||||||
|
@ -1,23 +1,26 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
from io import BytesIO
|
||||||
from itertools import islice
|
from itertools import islice
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from ..data.points import Points
|
from ..data.points import Points
|
||||||
from ..data.nmr import FID, Spectrum
|
from ..data.nmr import FID, Spectrum
|
||||||
|
from ..data.bds import BDS
|
||||||
|
from ..data.dsc import DSC
|
||||||
from ..utils.utils import staggered_range
|
from ..utils.utils import staggered_range
|
||||||
|
|
||||||
NUMBERRE = re.compile(r'[0-9]\.*[0-9]*[Ee]*[+-]*[0-9]*')
|
|
||||||
|
|
||||||
|
|
||||||
class AsciiReader:
|
class AsciiReader:
|
||||||
delimiters = ['\t', ' ', ',']
|
# delimiters = ['\t', ' ', ',']
|
||||||
|
|
||||||
def __init__(self, fname):
|
def __init__(self, fname):
|
||||||
self.fname = None
|
self.fname = None
|
||||||
self.header = []
|
self.header = []
|
||||||
self.data = []
|
self.lines = []
|
||||||
self.delays = None
|
self.delays = None
|
||||||
self.width = []
|
self.width = []
|
||||||
self.line_comment = []
|
self.line_comment = []
|
||||||
@ -39,15 +42,16 @@ class AsciiReader:
|
|||||||
break
|
break
|
||||||
|
|
||||||
def make_preview(self, num_lines: int):
|
def make_preview(self, num_lines: int):
|
||||||
if num_lines <= len(self.data):
|
if num_lines <= len(self.lines):
|
||||||
return self.data[:num_lines], max(self.width[:num_lines])
|
return self.lines[:num_lines], max(self.width[:num_lines]), self.line_comment
|
||||||
|
|
||||||
num_lines += len(self.header)
|
num_lines += len(self.header)
|
||||||
with self.fname.open('r') as f:
|
with self.fname.open('r') as f:
|
||||||
for i, line in enumerate(islice(f, len(self.header)+len(self.data), num_lines)):
|
for i, line in enumerate(islice(f, len(self.header)+len(self.lines), num_lines)):
|
||||||
line = line.rstrip('\n\t\r, ')
|
line = line.strip('\n\t\r, ')
|
||||||
|
line = re.sub(r'[\t ;,] *', ';', line)
|
||||||
|
line = line.split(';')
|
||||||
|
|
||||||
line = re.split('[\s,;]', line)
|
|
||||||
try:
|
try:
|
||||||
comment_start = line.index('#')
|
comment_start = line.index('#')
|
||||||
self.line_comment.append(' '.join(line[comment_start:]))
|
self.line_comment.append(' '.join(line[comment_start:]))
|
||||||
@ -55,10 +59,15 @@ class AsciiReader:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
self.line_comment.append('')
|
self.line_comment.append('')
|
||||||
|
|
||||||
self.width.append(len(line))
|
is_empty = len(line) == 0
|
||||||
self.data.append(line)
|
|
||||||
|
|
||||||
return self.data, max(self.width)
|
if not is_empty:
|
||||||
|
self.lines.append(line)
|
||||||
|
self.width.append(len(line))
|
||||||
|
else:
|
||||||
|
self.lines.append('')
|
||||||
|
|
||||||
|
return self.lines, max(self.width), self.line_comment
|
||||||
|
|
||||||
def look_for_delay(self, fname=None):
|
def look_for_delay(self, fname=None):
|
||||||
if fname is None:
|
if fname is None:
|
||||||
@ -78,31 +87,49 @@ class AsciiReader:
|
|||||||
if stagg:
|
if stagg:
|
||||||
self.delays = staggered_range(self.delays, stepsize=stag_size)
|
self.delays = staggered_range(self.delays, stepsize=stag_size)
|
||||||
|
|
||||||
def export(self, x: int = None, y: list = None, yerr: list = None,
|
def export(
|
||||||
mode: str = 'Points', col_names=None) -> list:
|
self: 'AsciiReader',
|
||||||
|
x: int | str = None,
|
||||||
|
y: list = None,
|
||||||
|
yerr: list = None,
|
||||||
|
mode: str = 'points',
|
||||||
|
col_names=None,
|
||||||
|
num_value: float = None,
|
||||||
|
) -> list:
|
||||||
|
|
||||||
|
mode = mode.lower()
|
||||||
|
if mode not in ['points', 'fid', 'spectrum', 'dsc', 'bds']:
|
||||||
|
raise ValueError(f'Unknown mode {mode!r} as selected class')
|
||||||
|
|
||||||
if yerr is None:
|
if yerr is None:
|
||||||
yerr = []
|
yerr = []
|
||||||
elif y is None:
|
elif y is None:
|
||||||
raise ValueError('y is None and yerr is not None')
|
raise ValueError('y is None and yerr is not None')
|
||||||
|
|
||||||
if (x is None) ^ (y is None):
|
# if (x is None) ^ (y is None):
|
||||||
raise ValueError(f'x is {type(x)} and y is {type(y)}, should be both None or both defined')
|
# raise ValueError(f'x is {type(x)} and y is {type(y)}, should be both None or both defined')
|
||||||
|
|
||||||
if x is None:
|
if x is None:
|
||||||
x = [0]
|
x = [0]
|
||||||
elif isinstance(x, int):
|
elif isinstance(x, int):
|
||||||
x = [x]
|
x = [x]
|
||||||
|
elif isinstance(x, str) and x == 'index':
|
||||||
|
x = []
|
||||||
else:
|
else:
|
||||||
raise ValueError(f'x is {type(x)} not int')
|
raise ValueError(f'type of x is {type(x)} not `int` or `str`')
|
||||||
|
|
||||||
if y is None:
|
if y is None:
|
||||||
y = list(range(1, max(self.width)))
|
y = list(range(int(len(x) != 0), max(self.width)))
|
||||||
|
|
||||||
cols = x + y + yerr
|
cols = x + y + yerr
|
||||||
raw_data = np.genfromtxt(self.fname, usecols=cols, missing_values='--')
|
with self.fname.open('rb') as fh:
|
||||||
|
tmp_ = re.sub(b'[;,]', b' ', fh.read())
|
||||||
|
raw_data = np.genfromtxt(BytesIO(tmp_), usecols=cols, missing_values='--')
|
||||||
|
del tmp_
|
||||||
|
|
||||||
if raw_data.ndim == 1:
|
if raw_data.ndim == 1:
|
||||||
# only one row or column
|
# only one row or column
|
||||||
if len(self.data) == 1:
|
if len(self.lines) == 1:
|
||||||
# one row
|
# one row
|
||||||
raw_data = raw_data.reshape(1, -1)
|
raw_data = raw_data.reshape(1, -1)
|
||||||
else:
|
else:
|
||||||
@ -115,33 +142,45 @@ class AsciiReader:
|
|||||||
else:
|
else:
|
||||||
raw_data = raw_data.reshape((1, *raw_data.shape))
|
raw_data = raw_data.reshape((1, *raw_data.shape))
|
||||||
|
|
||||||
|
if len(x) == 0 or raw_data.shape[2] == 1:
|
||||||
|
_temp = np.zeros((raw_data.shape[0], raw_data.shape[1], raw_data.shape[2]+1))
|
||||||
|
_temp[:, :, 0] = np.arange(raw_data.shape[1])
|
||||||
|
_temp[:, :, 1:] = raw_data
|
||||||
|
raw_data = _temp
|
||||||
|
|
||||||
|
if y:
|
||||||
|
y = [i+1 for i in y]
|
||||||
|
else:
|
||||||
|
y = [1]
|
||||||
|
|
||||||
filename = self.fname.stem
|
filename = self.fname.stem
|
||||||
|
|
||||||
if self.delays:
|
if self.delays:
|
||||||
delay_names = self.delays
|
delay_names = self.delays
|
||||||
else:
|
else:
|
||||||
delay_names = [filename]
|
delay_names = [num_value]
|
||||||
|
|
||||||
imported_sets = []
|
imported_sets = []
|
||||||
|
|
||||||
for i, value in enumerate(delay_names):
|
for i, value in enumerate(delay_names):
|
||||||
kwargs = {'value': value, 'filename': self.fname, 'name': filename, 'group': filename, 'y_err': None}
|
kwargs = {
|
||||||
|
'value': value,
|
||||||
|
'filename': self.fname,
|
||||||
|
'name': filename,
|
||||||
|
'group': filename,
|
||||||
|
'y_err': None
|
||||||
|
}
|
||||||
|
|
||||||
num_y = len(y)
|
num_y = len(y)
|
||||||
if mode == 'Points':
|
|
||||||
single_len = 1
|
single_len = 1
|
||||||
cls = Points
|
|
||||||
stepsize = 1
|
stepsize = 1
|
||||||
elif mode == 'FID':
|
|
||||||
cls = FID
|
if mode in ['spectrum', 'fid', 'bds']:
|
||||||
|
# complex data types
|
||||||
single_len = 2
|
single_len = 2
|
||||||
stepsize = 2
|
stepsize = 2
|
||||||
elif mode == 'Spectrum':
|
|
||||||
cls = Spectrum
|
cls = {'points': Points, 'fid': FID, 'spectrum': Spectrum, 'bds': BDS, 'dsc': DSC}[mode]
|
||||||
single_len = 2
|
|
||||||
stepsize = 2
|
|
||||||
else:
|
|
||||||
raise ValueError(f'Unknown mode {mode}, mot ´Points´, ´FID´ or ´Spectrum´.')
|
|
||||||
|
|
||||||
for j in range(1, num_y+1, stepsize):
|
for j in range(1, num_y+1, stepsize):
|
||||||
if col_names is not None:
|
if col_names is not None:
|
||||||
@ -151,8 +190,8 @@ class AsciiReader:
|
|||||||
# more than one axis, append column number
|
# more than one axis, append column number
|
||||||
kwargs['name'] = filename + '_' + str(y[j-1])
|
kwargs['name'] = filename + '_' + str(y[j-1])
|
||||||
|
|
||||||
if j+num_y+1 < raw_data.shape[2]:
|
if j+num_y < raw_data.shape[2]:
|
||||||
kwargs['y_err'] = raw_data[i, j+num_y+1]
|
kwargs['y_err'] = raw_data[i, :, j+num_y]
|
||||||
|
|
||||||
imported_sets.append(cls(x=raw_data[i, :, 0], y=raw_data[i, :, j:j+single_len].T, **kwargs))
|
imported_sets.append(cls(x=raw_data[i, :, 0], y=raw_data[i, :, j:j+single_len].T, **kwargs))
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user