commit a222072b2838a685236c37196cfe3c3d7e4b5ea0
Author: dominik
Date: Tue Mar 8 10:27:40 2022 +0100
First commit
diff --git a/Makefile b/Makefile
new file mode 100755
index 0000000..8c9b2d5
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,42 @@
+.PHONY: clean
+
+#binaries
+PYUIC = pyuic5
+PYRCC = pyrcc5
+
+#Directory with ui files
+RESOURCE_DIR = resources/_ui
+
+#Directory for compiled resources
+COMPILED_DIR = nmreval/gui_qt/_py
+
+#UI files to compile, uses every *.ui found in RESOURCE_DIR
+UI_FILES = $(foreach dir, $(RESOURCE_DIR), $(notdir $(wildcard $(dir)/*.ui)))
+COMPILED_UI = $(UI_FILES:%.ui=$(COMPILED_DIR)/%.py)
+
+SVG_FILES = $(foreach dir, $(RCC_DIR), $(notdir $(wildcard $(dir)/*.svg)))
+PNG_FILES = $(SVG_FILES:%.svg=$(RCC_DIR)/%.png)
+
+all : ui
+
+ui : $(COMPILED_UI)
+
+rcc: $(PNG_FILES)
+
+
+$(COMPILED_DIR)/%.py : $(RESOURCE_DIR)/%.ui
+ $(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
+ convert -background none $< $@
+ $(PYRCC) $(RCC_DIR)/images.qrc -o $(COMPILED_DIR)/images_rc.py
+
+clean:
+ find . -name '*.pyc' -exec rm -f {} +
+ find . -name '*.pyo' -exec rm -f {} +
+ find . -name '*~' -exec rm -f {} +
+ find . -name '__pycache__' -exec rm -fr {} +
+
diff --git a/__init__.py b/__init__.py
new file mode 100755
index 0000000..6860c71
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,2 @@
+
+from .version import __version__, __releasename__
diff --git a/bin/evaluate.py b/bin/evaluate.py
new file mode 100755
index 0000000..7fbd533
--- /dev/null
+++ b/bin/evaluate.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python3
+
+import sys
+# 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 nmreval.gui_qt import App
+from nmreval.gui_qt.main.mainwindow import NMRMainWindow
+
+app = App([])
+
+mplQt = NMRMainWindow()
+mplQt.show()
+
+sys.exit(app.exec())
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..6cfe3d2
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= /autohome/dominik/miniconda3/bin/sphinx-build
+SOURCEDIR = /autohome/dominik/nmreval/docs/source
+BUILDDIR = /autohome/dominik/nmreval/docs/build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..9534b01
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=source
+set BUILDDIR=build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/docs/source/_static/fit_dialog.png b/docs/source/_static/fit_dialog.png
new file mode 100644
index 0000000..559aa14
Binary files /dev/null and b/docs/source/_static/fit_dialog.png differ
diff --git a/docs/source/_static/logo.png b/docs/source/_static/logo.png
new file mode 100644
index 0000000..9cab63d
Binary files /dev/null and b/docs/source/_static/logo.png differ
diff --git a/docs/source/_templates/autosummary.rst b/docs/source/_templates/autosummary.rst
new file mode 100644
index 0000000..0f54333
--- /dev/null
+++ b/docs/source/_templates/autosummary.rst
@@ -0,0 +1,25 @@
+{{ fullname | escape | underline }}
+
+
+.. currentmodule:: {{ module }}
+
+
+{% if objtype in ['class'] %}
+
+Inheritance diagram
+
+.. inheritance-diagram:: {{ objname }}
+ :parts: 1
+
+{{ objtype }}
+
+.. auto{{ objtype }}:: {{ objname }}
+ :special-members: __call__
+ :members:
+ :undoc-members:
+
+{% else %}
+.. auto{{ objtype }}:: {{ objname }}
+
+{% endif %}
+
diff --git a/docs/source/api/data.rst b/docs/source/api/data.rst
new file mode 100644
index 0000000..616a808
--- /dev/null
+++ b/docs/source/api/data.rst
@@ -0,0 +1,23 @@
+**************
+Data container
+**************
+
+.. automodule:: nmreval.data
+ :no-members:
+ :no-undoc-members:
+
+
+.. contents:: Table of Contents
+ :depth: 3
+ :local:
+ :backlinks: entry
+
+
+.. autosummary::
+ :toctree: generated/
+ :template: autosummary.rst
+ :nosignatures:
+
+ nmreval.data.Points
+ nmreval.data.Signal
+ nmreval.data.BDS
diff --git a/docs/source/api/distributions/index.rst b/docs/source/api/distributions/index.rst
new file mode 100644
index 0000000..cf1fadd
--- /dev/null
+++ b/docs/source/api/distributions/index.rst
@@ -0,0 +1,29 @@
+*********************************
+Distribution of correlation times
+*********************************
+
+List of all implemented distributions and the associated correlation functions, spectral densities, susceptibilies
+
+.. contents:: Table of Contents
+ :depth: 3
+ :local:
+ :backlinks: entry
+
+Cole-Cole
+---------
+
+.. automodule:: nmreval.distributions.colecole
+ :members:
+
+
+Cole-Davidson
+-------------
+
+.. automodule:: nmreval.distributions.coledavidson
+ :members:
+
+Havriliak-Negami
+----------------
+
+.. automodule:: nmreval.distributions.havriliaknegami
+ :members:
diff --git a/docs/source/api/generated/nmreval.data.BDS.rst b/docs/source/api/generated/nmreval.data.BDS.rst
new file mode 100644
index 0000000..0818b3b
--- /dev/null
+++ b/docs/source/api/generated/nmreval.data.BDS.rst
@@ -0,0 +1,23 @@
+nmreval.data.BDS
+================
+
+
+.. currentmodule:: nmreval.data
+
+
+
+
+Inheritance diagram
+
+.. inheritance-diagram:: BDS
+ :parts: 1
+
+class
+
+.. autoclass:: BDS
+ :special-members: __call__
+ :members:
+ :undoc-members:
+
+
+
\ No newline at end of file
diff --git a/docs/source/api/generated/nmreval.data.Points.rst b/docs/source/api/generated/nmreval.data.Points.rst
new file mode 100644
index 0000000..16d7d60
--- /dev/null
+++ b/docs/source/api/generated/nmreval.data.Points.rst
@@ -0,0 +1,23 @@
+nmreval.data.Points
+===================
+
+
+.. currentmodule:: nmreval.data
+
+
+
+
+Inheritance diagram
+
+.. inheritance-diagram:: Points
+ :parts: 1
+
+class
+
+.. autoclass:: Points
+ :special-members: __call__
+ :members:
+ :undoc-members:
+
+
+
\ No newline at end of file
diff --git a/docs/source/api/generated/nmreval.data.Signal.rst b/docs/source/api/generated/nmreval.data.Signal.rst
new file mode 100644
index 0000000..c913f1d
--- /dev/null
+++ b/docs/source/api/generated/nmreval.data.Signal.rst
@@ -0,0 +1,23 @@
+nmreval.data.Signal
+===================
+
+
+.. currentmodule:: nmreval.data
+
+
+
+
+Inheritance diagram
+
+.. inheritance-diagram:: Signal
+ :parts: 1
+
+class
+
+.. autoclass:: Signal
+ :special-members: __call__
+ :members:
+ :undoc-members:
+
+
+
\ No newline at end of file
diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst
new file mode 100644
index 0000000..acbad32
--- /dev/null
+++ b/docs/source/api/index.rst
@@ -0,0 +1,12 @@
+==========
+References
+==========
+
+.. toctree::
+ :caption: Table of contents
+ :maxdepth: 2
+ :glob:
+
+ data.rst
+ models/index.rst
+ distributions/index.rst
diff --git a/docs/source/api/models/basic.rst b/docs/source/api/models/basic.rst
new file mode 100644
index 0000000..e87e570
--- /dev/null
+++ b/docs/source/api/models/basic.rst
@@ -0,0 +1,6 @@
+************************
+``nmreval.models.basic``
+************************
+
+.. automodule:: nmreval.models.basic
+ :members:
\ No newline at end of file
diff --git a/docs/source/api/models/index.rst b/docs/source/api/models/index.rst
new file mode 100644
index 0000000..006cb95
--- /dev/null
+++ b/docs/source/api/models/index.rst
@@ -0,0 +1,31 @@
+**************
+Model function
+**************
+
+List of all implemented functions
+
+.. contents:: Table of Contents
+ :depth: 3
+ :local:
+ :backlinks: entry
+
+Basic functions
+---------------
+
+.. currentmodule:: nmreval
+
+.. autosummary::
+ :toctree:
+
+ nmreval.models.basic
+
+
+
+
+NMR relaxation functions
+------------------------
+
+.. automodule:: nmreval.models.relaxation
+ :members:
+
+
diff --git a/docs/source/api/models/nmreval.models.basic.rst b/docs/source/api/models/nmreval.models.basic.rst
new file mode 100644
index 0000000..17cfa50
--- /dev/null
+++ b/docs/source/api/models/nmreval.models.basic.rst
@@ -0,0 +1,37 @@
+nmreval.models.basic
+====================
+
+.. automodule:: nmreval.models.basic
+
+
+
+
+
+
+
+
+
+
+
+ .. rubric:: Classes
+
+ .. autosummary::
+
+ Constant
+ ExpFunc
+ Linear
+ Log
+ MittagLeffler
+ Parabola
+ PowerLaw
+ PowerLawCross
+ Sine
+
+
+
+
+
+
+
+
+
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 0000000..3d36539
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,424 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import sys
+import sphinx_bootstrap_theme
+sys.path.append('/autohome/dominik/nmreval')
+import nmreval
+
+
+# -- Project information -----------------------------------------------------
+project = 'NMREval'
+author = 'Dominik Demuth'
+copyright = '2022, Dominik Demuth'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version
+release = nmreval.__version__
+# The full version, including alpha/beta/rc tags.
+version = release
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.autosummary',
+ 'sphinx.ext.inheritance_diagram',
+ 'sphinx.ext.napoleon',
+ 'sphinx.ext.viewcode',
+ 'sphinx.ext.intersphinx',
+]
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#
+# today = ''
+#
+# Else, today_fmt is used as the format for a strftime call.
+#
+# today_fmt = '%B %d, %Y'
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#
+add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#
+add_module_names = False
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#
+# show_authors = False
+
+# A list of ignored prefixes for module index sorting.
+# modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+# html_theme = 'bootstrap'
+html_theme = 'pydata_sphinx_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {
+# # 'source_link_position': "footer",
+# 'bootswatch_theme': "cosmo",
+# 'navbar_title': "Welcome to hell",
+# 'navbar_sidebarrel': True,
+# 'nosidebar': False,
+# 'body_max_width': '100%',
+# 'navbar_links': [
+# ('User guide', 'user_guide/index'),
+# ('References', 'api/index'),
+# ],
+# }
+html_theme_options = {
+ 'collapse_navigation': False,
+ 'show_prev_next': False,
+ 'navbar_end': ['navbar-icon-links.html', 'search-field.html'],
+ 'show_toc_level': 3
+}
+
+# Add any paths that contain custom themes here, relative to this directory.
+# html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
+
+# The name for this set of Sphinx documents.
+# " v documentation" by default.
+#
+# html_title = u'NMREval v0.0.1'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+# html_short_title = None
+
+# (Optional) Logo. Should be small enough to fit the navbar (ideally 24x24).
+# Path should be relative to the ``_static`` files directory.
+html_logo = '_static/logo.png'
+
+# Custom sidebar templates, maps document names to template names.
+html_sidebars = {'**': ['sidebar-nav-bs.html']}
+# html_sidebars = {
+# '**': ['localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'],
+# }
+
+# If true, links to the reST sources are added to the pages.
+html_show_sourcelink = False
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+# html_favicon = None
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = ''
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+html_additional_pages = {}
+
+# If false, no module index is generated.
+html_domain_indices = False
+
+# If false, no index is generated.
+html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+html_split_index = False
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+# html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+# html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+# html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+html_file_suffix = None
+
+# Output file base name for HTML help builder.
+# htmlhelp_basename = 'pydoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'NMREval.tex', u'NMREval Documentation',
+ u'Dominik Demuth', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#
+# latex_use_parts = False
+
+# If true, show page references after internal links.
+#
+# latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#
+# latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#
+# latex_appendices = []
+
+# It false, will not define \strong, \code, itleref, \crossref ... but only
+# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
+# packages.
+#
+# latex_keep_old_macro_names = True
+
+# If false, no module index is generated.
+#
+# latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'nmreval', u'NMREval Documentation',
+ [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#
+# man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'NMREval', u'NMREval Documentation',
+ author, 'NMREval', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+#
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#
+# texinfo_no_detailmenu = False
+
+
+# -- Options for Epub output ----------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = project
+epub_author = author
+epub_publisher = author
+epub_copyright = copyright
+
+# The basename for the epub file. It defaults to the project name.
+# epub_basename = project
+
+# The HTML theme for the epub output. Since the default themes are not
+# optimized for small screen space, using the same theme for HTML and epub
+# output is usually not wise. This defaults to 'epub', a theme designed to save
+# visual space.
+#
+# epub_theme = 'epub'
+
+# The language of the text. It defaults to the language option
+# or 'en' if the language is not set.
+#
+# epub_language = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+# epub_scheme = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#
+# epub_identifier = ''
+
+# A unique identification for the text.
+#
+# epub_uid = ''
+
+# A tuple containing the cover image and cover page html template filenames.
+#
+# epub_cover = ()
+
+# A sequence of (type, uri, title) tuples for the guide element of content.opf.
+#
+# epub_guide = ()
+
+# HTML files that should be inserted before the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#
+# epub_pre_files = []
+
+# HTML files that should be inserted after the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#
+# epub_post_files = []
+
+# A list of files that should not be packed into the epub file.
+epub_exclude_files = ['search.html']
+
+# The depth of the table of contents in toc.ncx.
+#
+# epub_tocdepth = 3
+
+# Allow duplicate toc entries.
+#
+# epub_tocdup = True
+
+# Choose between 'default' and 'includehidden'.
+#
+# epub_tocscope = 'default'
+
+# Fix unsupported image types using the Pillow.
+#
+# epub_fix_images = False
+
+# Scale large images.
+#
+# epub_max_image_width = 0
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
+# epub_show_urls = 'inline'
+
+# If false, no index is generated.
+#
+# epub_use_index = True
+
+
+# configuration for intersphinx
+intersphinx_mapping = {
+ 'Pillow': ('https://pillow.readthedocs.io/en/stable/', None),
+ 'cycler': ('https://matplotlib.org/cycler/', None),
+ 'dateutil': ('https://dateutil.readthedocs.io/en/stable/', None),
+ 'ipykernel': ('https://ipykernel.readthedocs.io/en/latest/', None),
+ 'numpy': ('https://numpy.org/doc/stable/', None),
+ 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None),
+ 'pytest': ('https://pytest.org/en/stable/', None),
+ 'python': ('https://docs.python.org/3/', None),
+ 'scipy': ('https://docs.scipy.org/doc/scipy/', None),
+ 'tornado': ('https://www.tornadoweb.org/en/stable/', None),
+ 'xarray': ('https://xarray.pydata.org/en/stable/', None),
+ 'PyQt': ('https://www.riverbankcomputing.com/static/Docs/PyQt5/', None),
+}
+
+# autodoc options
+autodoc_typehints = 'none'
+autodoc_class_signature = 'separated'
+autoclass_content = 'class'
+
+# autosummay options
+autosummary_generate = True
+
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 0000000..98a6339
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,26 @@
+.. NMREval documentation master file, created by
+ sphinx-quickstart on Sun Feb 20 17:26:03 2022.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+###################################
+NMREval documentation
+###################################
+
+.. toctree::
+ :maxdepth: 1
+
+ user_guide/index
+
+ nmr/index
+
+ api/index
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/source/nmr/depake.png b/docs/source/nmr/depake.png
new file mode 100644
index 0000000..feafb28
Binary files /dev/null and b/docs/source/nmr/depake.png differ
diff --git a/docs/source/nmr/index.rst b/docs/source/nmr/index.rst
new file mode 100644
index 0000000..acbe480
--- /dev/null
+++ b/docs/source/nmr/index.rst
@@ -0,0 +1,8 @@
+=============
+NMR specifics
+=============
+
+.. toctree::
+ :maxdepth: 2
+
+ pake
\ No newline at end of file
diff --git a/docs/source/nmr/pake.rst b/docs/source/nmr/pake.rst
new file mode 100644
index 0000000..1e66f6d
--- /dev/null
+++ b/docs/source/nmr/pake.rst
@@ -0,0 +1,43 @@
+.. _nmr.pake:
+
+Wideline spectra
+^^^^^^^^^^^^^^^^
+
+Calculation of spectra
+----------------------
+
+In general, time signals are calculated by integration of all orientations (see also :ref:`list.orienations`):
+
+.. math::
+ g(t) = \int f[\omega_\text{int}(\theta, \phi)t]\sin\theta\,\mathrm{d}\theta\,\mathrm{d}\phi
+
+with :math:`f(\theta, \phi, t) = \cos[\omega_\text{int}(\theta, \phi) t]` or :math:`\exp[i\omega_\text{int}(\theta) t]` and fourier transform for a spectrum.
+However, summation over :math:`\theta`, :math:`\phi`, and calculating :math:`f(\theta, \phi, t)` for each orientation is time consuming.
+
+Alternatively, if the orientations are equidistant in :math:`\cos\theta`, one can get to the spectrum directly by creating a histogram of :math:`\omega_\text{int}(\theta, \phi)`, thus circumventing a lot of calculations.
+
+
+De-Paked spectra
+----------------
+
+A superposition of different Pake spectra complicates the evaluation of relaxation times or similar.
+The idea is to deconvolute these broad spectra into one line corresponding to relative orientation :math:`\theta = 0` [mccabe97]_.
+
+For :math:`\omega_\text{int}(\theta) \propto (3\cos^2\theta -1)/2 = P_2(\cos\theta)`, the property :math:`\omega_\text{int}(\theta) = \omega_\text{int}(0) \omega_\text{int}(\theta)` is used to write
+
+.. math::
+ g(t) = \int_0^{1} f[0, \omega_\text{int}(\theta)t]\,\mathrm{d}\cos\theta.
+
+This way, the integration is not over orientations at one time :math:`t`, but over times at one orientation 0.
+After some integrations, rearrangenments, and substitutions, a spectrum can be calculated by
+
+.. math::
+ F(-2\omega) = \sqrt{\frac{3|\omega |}{2\pi}}(1\pm i) \text{FT}[g(t)\sqrt{t}]
+
+with :math:`1+i` for :math:`\omega > 0` and :math:`1-i` for :math:`\omega > 0`.
+
+.. figure:: depake.png
+ :scale: 50 %
+
+
+.. [mccabe97] M.A. McCabe, S.R. Wassail: Rapid deconvolution of NMR powder spectra by weighted fast Fourier transformation, Solid State Nuclear Magnetic Resonance (1997). https://doi.org/10.1016/S0926-2040(97)00024-6
\ No newline at end of file
diff --git a/docs/source/user_guide/fit.rst b/docs/source/user_guide/fit.rst
new file mode 100644
index 0000000..959e836
--- /dev/null
+++ b/docs/source/user_guide/fit.rst
@@ -0,0 +1,43 @@
+.. _user_guide.fit:
+
+============
+Fitting data
+============
+
+.. image:: ../_static/fit_dialog.png
+ :scale: 80%
+ :align: center
+
+The picture gives an example of dialog to setup and start fits.
+First, there is the possibiity to fit different functions, called models to differentiate from the functions inside each
+model, to different data simultaneously.
+In the given example, two models were would be used during fit:
+test1 and test3 use the default model, in this case model a,
+while test2 uses model b.
+
+In the middle column, the functions of model a are currently given in a tree structure:
+It is comprised of three functions: a constant, a sine curve, which is a child of the constant, and free diffusion.
+Functions can be dragged by mouse to each position, including a child position, and it is possible to switch between the
+four basic arithmetic operations (+, -, \*, /) by striking the corresponding key.
+Function at the same level, e.g., constant and free diffusion, are evaluated in the given order.
+Children of a function take precedence over the following functions.
+This means for the given example, that model a is something like
+
+.. math::
+ f(x) = [\text{constant} / \text{sine}] + \text{diffusion}
+
+It is possible to skip functions by de-selecting it.
+This choice alse affects children, even if they are selected.
+
+The right column gives access to the parameter of selected function, in this case of the free diffusion function.
+Here, the parameter :math:`M_0` is linked to the parameter :math:`C` of another constant function of model b
+(identifiable by the number behind the name).
+Linkage links not only parameter between function but all settings, e.g., if :math:`C` is fixed, :math:`M_0` is fixed.
+Some parameters like gradient :math:`g` are predefined as fixed parameters and are lacking any options besides their value.
+Upper and lower bounds may be given.
+If one or two of the bounds are not given, this parameter is unbounded on the respective side.
+
+If a parameter has one entry, this value is initial parameter for all data sets.
+If the number of entries is greater than the number of data sets, the first :math:`n` values are used.
+Here, test1 uses 1 as parameter for :math:`t_{ev}` and test3 uses 3.
+If the number of entries is less than the number of data sets, the last value will be used for each additional set.
\ No newline at end of file
diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst
new file mode 100644
index 0000000..2ec252f
--- /dev/null
+++ b/docs/source/user_guide/index.rst
@@ -0,0 +1,11 @@
+==========
+User Guide
+==========
+
+
+.. toctree::
+ :maxdepth: 2
+
+ read
+ fit
+ shift_scale
diff --git a/docs/source/user_guide/read.rst b/docs/source/user_guide/read.rst
new file mode 100644
index 0000000..a423962
--- /dev/null
+++ b/docs/source/user_guide/read.rst
@@ -0,0 +1,19 @@
+.. _user_guide.read:
+
+*************
+Reading files
+*************
+
+Supported filetypes are
+
+ * Text files,
+ * DAMARIS HDF5 files,
+ * Grace images.
+ * HP alpha-analyzer EPS files
+ * NTNMR .tnt files
+
+DAMARIS HDF files
+=================
+
+After scanning the selected file the program shows a list of the available data.
+
diff --git a/docs/source/user_guide/shift_scale.rst b/docs/source/user_guide/shift_scale.rst
new file mode 100644
index 0000000..45e4b4e
--- /dev/null
+++ b/docs/source/user_guide/shift_scale.rst
@@ -0,0 +1,16 @@
+.. _usage.shift_scale:
+
+Moving and scaling
+^^^^^^^^^^^^^^^^^^
+
+Values are always used and displayed in scientific notation *x.yyyyez* where x is a signed single digit larger than 0.
+The relative stepsize (by default 0.0001) increases or decreases is always of the same magnitude as the current value, e.g.,
+1.2345e0 changes by 0.0001, but 1.2345e20 changes by 0.0001e20.
+If the step results in a new order of magnitude the value will be adjusted accordingly, i.e., a step from 9.9999e1 to 10.0000e1 will become 1.0000e2.
+
+.. note::
+ This behaviour leads almost always to values different from zero when using the arrow buttons. Please enter it directly to get a value of zero.
+
+Boxes :guilabel:`log x` and :guilabel:`log y` change the respective axis from a linear to logarithmic scaling and vice versa.
+
+Every selected set will be recalculated according to :math:`new = scale \cdot old + offset`.
\ No newline at end of file
diff --git a/nmreval/__init__.py b/nmreval/__init__.py
new file mode 100644
index 0000000..d18f409
--- /dev/null
+++ b/nmreval/__init__.py
@@ -0,0 +1 @@
+__version__ = '0.0.2'
diff --git a/nmreval/bds/__init__.py b/nmreval/bds/__init__.py
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/nmreval/bds/__init__.py
@@ -0,0 +1 @@
+
diff --git a/nmreval/configs.py b/nmreval/configs.py
new file mode 100644
index 0000000..84f1a83
--- /dev/null
+++ b/nmreval/configs.py
@@ -0,0 +1,69 @@
+import configparser
+import pathlib
+import pickle
+import logging.handlers
+
+
+__all__ = ['config_paths', 'read_configuration', 'write_configuration', 'allowed_values', 'write_state', 'read_state']
+
+
+def config_paths() -> pathlib.Path:
+ # TODO adjust for different OS
+ searchpaths = ['~/.local/share/auswerten', '~/.auswerten', '/usr/share/nmreval']
+ path = None
+ for p in searchpaths:
+ path = pathlib.Path(p).expanduser()
+ if path.exists():
+ break
+
+ if path is None:
+ raise FileNotFoundError('No valid configuration path found')
+
+ return path
+
+
+def read_configuration() -> configparser.ConfigParser:
+ try:
+ config_file = config_paths() / 'nmreval.cfg'
+ if not config_file.exists():
+ write_configuration({'GUI': {'theme': 'normal', 'color': 'light'}})
+ # raise FileNotFoundError('Configuration file not found')
+ #
+ except FileNotFoundError as e:
+ raise e
+
+ parser = configparser.ConfigParser()
+ parser.read(config_file)
+
+ return parser
+
+
+def write_configuration(opts: dict):
+ config_file = config_paths() / 'nmreval.cfg'
+
+ parser = configparser.ConfigParser()
+ parser.read_dict(opts)
+
+ with config_file.open('w') as f:
+ parser.write(f)
+
+
+allowed_values = {
+ ('GUI', 'theme'): ['normal', 'pokemon'],
+ ('GUI', 'color'): ['light', 'dark'],
+}
+
+
+def write_state(opts: dict):
+ config_file = config_paths() / 'guistate.ini'
+ with config_file.open('wb') as f:
+ pickle.dump(opts, f)
+
+
+def read_state() -> dict:
+ config_file = config_paths() / 'guistate.ini'
+ if not config_file.exists():
+ return {}
+
+ with config_file.open('rb') as f:
+ return pickle.load(f)
diff --git a/nmreval/data/__init__.py b/nmreval/data/__init__.py
new file mode 100755
index 0000000..7b73355
--- /dev/null
+++ b/nmreval/data/__init__.py
@@ -0,0 +1,5 @@
+from .points import Points
+from .signals import Signal
+from .bds import BDS
+from .dsc import DSC
+from .nmr import FID, Spectrum
diff --git a/nmreval/data/bds.py b/nmreval/data/bds.py
new file mode 100644
index 0000000..086313a
--- /dev/null
+++ b/nmreval/data/bds.py
@@ -0,0 +1,47 @@
+from typing import Any
+
+import numpy as np
+from scipy.signal import savgol_filter
+
+from . import Points
+from .signals import Signal
+
+
+class BDS(Signal):
+ """
+ Extension of Signal for dielectric spectroscopy purposes.
+ """
+
+ def __init__(self, x, y, **kwargs: Any):
+ super().__init__(x, y, **kwargs)
+
+ if np.all(self._x > 0) and not np.allclose(np.diff(self._x), self._x[1]-self._x[0]):
+ self.dx = self.x[1] / self.x[0]
+ else:
+ self.dx = self._x[1] - self._x[0]
+
+ def __repr__(self) -> str:
+ return f"{self.meta['mode']}: {self.name}"
+
+ def derivative(self, window_length=9) -> Points:
+ """
+ Calculates the derivative :math:`-\\frac{\\pi}{2}\\frac{d\\epsilon'}{d\\ln\\omega}`.
+ To reduce :func:`scipy.signal.savgol_filter`
+
+ Args:
+ window_length (int)
+
+ Returns:
+ Points
+ New Points instance with
+
+ References:
+ Wübbenhorst, M.; van Turnhout, J.: Analysis of complex dielectric spectra.
+ I. One-dimensional derivative techniques and three-dimensional modelling.
+ J. Non-Cryst. Solid. 305 (2002) https://doi.org/10.1016/s0022-3093(02)01086-4
+
+ """
+ y = -savgol_filter(self.y[self.mask].real, window_length, 2, deriv=1) * np.pi / 2
+ data = Points(x=self.x[self.mask], y=y)
+ data.update(self.meta)
+ return data
diff --git a/nmreval/data/dsc.py b/nmreval/data/dsc.py
new file mode 100644
index 0000000..429ddcd
--- /dev/null
+++ b/nmreval/data/dsc.py
@@ -0,0 +1,6 @@
+from .points import Points
+
+
+class DSC(Points):
+ def __init__(self, x, y, **kwargs):
+ super().__init__(x, y, **kwargs)
diff --git a/nmreval/data/nmr.py b/nmreval/data/nmr.py
new file mode 100644
index 0000000..79dc663
--- /dev/null
+++ b/nmreval/data/nmr.py
@@ -0,0 +1,179 @@
+import warnings
+from typing import Union
+
+import numpy as np
+
+from .signals import Signal
+
+
+class FID(Signal):
+ def __init__(self, x, y, **kwargs):
+ super().__init__(x, y, **kwargs)
+
+ self.meta['apod'] = []
+ self.meta['zf'] = 0
+
+ def __repr__(self):
+ return f"FID: {self.name}"
+
+ def baseline(self, percentage=0.12):
+ self._y -= self._y[int(-percentage * self.length):].mean()
+
+ return self
+
+ def apod(self, p, method=None):
+ try:
+ self._y *= method.apod(self._x, *p)
+ self.meta['apod'].append((str(method), p))
+ except KeyError:
+ print(f'Unknown apodization {method}')
+
+ def zerofill(self, pad: int = 1):
+ _max_x = self._x.max()
+
+ factor = 2**pad
+ if factor < 1:
+ self._x = self._x[:int(self.length*factor)]
+ self._y = self._y[:int(self.length*factor)]
+ self._y_err = self._y_err[:int(self.length*factor)]
+ self.mask = self.mask[:int(self.length*factor)]
+
+ elif factor > 1:
+ add_length = int((factor-1) * self.length)
+ self._y = np.concatenate((self._y, np.zeros(add_length)))
+ self._y_err = np.concatenate((self._y_err, np.zeros(add_length)))
+ self.mask = np.concatenate((self.mask, np.ones(add_length, dtype=bool)))
+
+ _temp_x = np.arange(1, add_length+1) * self.dx + np.max(self._x)
+ self._x = np.concatenate((self._x, _temp_x))
+
+ self.meta['zf'] += pad
+
+ return self
+
+ def fourier(self):
+ ft = np.fft.fftshift(np.fft.fft(self._y)) / self.dx
+ freq = np.fft.fftshift(np.fft.fftfreq(self.length, self.dx))
+
+ spec = Spectrum(freq, ft, dx=self.dx)
+ spec.update(self.meta)
+
+ return spec
+
+ def fft_depake(self, scale: bool = False):
+ """
+ Calculate deconvoluted Pake spectra
+ M.A. McCabe, S.R. Wassail:
+ Rapid deconvolution of NMR powder spectra by weighted fast Fourier transformation,
+ SSNMR (1997), Vol.10, Nr.1-2, pp. 53-61,
+ """
+ _y = self._y + 0
+ # Perform FFT depaking
+ _y *= np.sqrt(self._x)
+
+ # built halves
+ right = np.fft.fftshift(np.fft.fft(_y)) * (1 + 1j)
+ left = np.fft.fftshift(np.fft.fft(_y)) * (1 - 1j)
+
+ freq = np.fft.fftshift(np.fft.fftfreq(self.length, self.dx))
+
+ if scale:
+ # fourier transform should be multiplied with \sqrt(|omega|)
+ # but lines at omega=0 are suppressed, so sometimes not a good idea
+ right *= np.sqrt(np.abs(freq))
+ left *= np.sqrt(np.abs(freq))
+
+ # built depaked spectrum
+ new_spc = np.concatenate([left[:(self.length//2)], right[(self.length//2):]])
+
+ spec = Spectrum(2*freq, new_spc, dx=self.dx)
+ spec.update(self.meta)
+
+ return spec
+
+ def convert(self):
+ spec = Spectrum(self._x, self._y, dx=self.dx)
+ self.update(self.meta)
+
+ return spec
+
+ def _calc_noise(self):
+ # noise per channel, so divide by sqrt(2)
+ self._y_err = np.std(self._y[-int(0.1*self.length):])/1.41
+ return self._y_err * np.ones(self.length)
+
+ def shift(self, points: Union[str, float], mode: str = 'pts'):
+ """
+
+ Args:
+ points : shift value,
+ mode (str):
+
+ Returns:
+
+ """
+ if mode == 'pts':
+ super().shift(points)
+ else:
+ pts = int(points//self.dx)
+ super().shift(pts)
+
+
+class Spectrum(Signal):
+ def __init__(self, x, y, dx=None, **kwargs):
+ super().__init__(x, y, **kwargs)
+ if dx is None:
+ if 2 * abs(self._x[0])/len(self._x) - abs(self._x[1]-self._x[0]) < 1e-9:
+ self.dx = 1/(2 * abs(self._x[0]))
+ else:
+ self.dx = 1
+ else:
+ self.dx = dx
+
+ def __repr__(self):
+ return f"Spectrum: {self.name}"
+
+ def baseline(self, percentage: float = 0.12):
+ corr = np.mean([self._y[int(-percentage*self.length):], self._y[:int(percentage*self.length)]])
+ self._y -= corr
+
+ return self
+
+ def fourier(self):
+ ft = np.fft.ifft(np.fft.ifftshift(self._y * self.dx))
+ t = np.arange(len(ft))*self.dx
+ shifted = np.fft.ifftshift(self._x)[0]
+ if shifted != 0:
+ warnings.warn(f'Shift of {shifted}: Spectrum is not zero-centered and FFT may give wrong results!')
+ ft *= np.exp(1j * shifted * 2*np.pi*t)
+
+ fid = FID(t, ft)
+ fid.update(self.meta)
+
+ return fid
+
+ def convert(self):
+ fid = FID(self._x, self._y)
+ fid.update(self.meta)
+
+ return fid
+
+ def _calc_noise(self):
+ step = int(0.05 * self.length)
+ start = np.argmin(abs(self._x - max(self._x.min(), -2e5)))
+ stop = np.argmin(abs(self._x - min(self._x.max(), 2e5)))
+ if start > stop:
+ stop, start = start, stop
+ stop -= step
+ self._y_err = np.inf
+ for i in range(start, stop):
+ # scan regions to find minimum noise (undisturbed by signals)
+ self._y_err = min(np.std(self._y[i:i + step])/np.sqrt(2), self._y_err)
+
+ return self._y_err * np.ones(self.length)
+
+ def shift(self, points, mode='pts'):
+ if mode == 'pts':
+ super().shift(points)
+ else:
+ return self
diff --git a/nmreval/data/points.py b/nmreval/data/points.py
new file mode 100644
index 0000000..c143151
--- /dev/null
+++ b/nmreval/data/points.py
@@ -0,0 +1,665 @@
+import copy
+from numbers import Number, Real
+from pathlib import Path
+from typing import Any, List, Optional, Tuple, TypeVar, Union
+
+import numpy as np
+try:
+ from scipy.integrate import simpson, cumulative_trapezoid
+except ImportError:
+ from scipy.integrate import simps as simpson, cumtrapz as cumulative_trapezoid
+
+from ..lib.utils import ArrayLike
+from ..utils import NUMBER_RE
+
+PointLike = TypeVar('PointLike', bound='Points')
+
+"""
+This is a test for all and everything
+"""
+
+
+class Points:
+ """
+ Base class for all datatypes
+
+ Args:
+ x (array_like): x values
+ y (array_like): y values
+ y_err (array_like, optional): errors
+
+ """
+ def __init__(self, x: ArrayLike, y: ArrayLike, y_err: Optional[ArrayLike] = None, **kwargs: Any):
+
+ self._x, self._y, self._y_err, self.mask = self._prepare_xy(x, y, y_err=y_err)
+
+ name = str(kwargs.pop('name', ''))
+ value = kwargs.pop('value', None)
+ self.meta = {
+ 'group': '',
+ 'name': name.split('/')[-1]
+ }
+ self.meta.update(kwargs)
+
+ if isinstance(value, str):
+ for m in NUMBER_RE.finditer(value):
+ value = float(m.group().replace('p', '.'))
+
+ try:
+ value = float(value)
+ except ValueError:
+ value = None
+
+ if value is None:
+ for m in NUMBER_RE.finditer(name):
+ value = float(m.group().replace('p', '.'))
+ if not value:
+ value = 0.0
+
+ self.meta['value'] = value
+ if self.name == '':
+ self.name = str(value)
+
+ @staticmethod
+ def _prepare_xy(x: ArrayLike, y: ArrayLike, y_err: Optional[ArrayLike] = None):
+ x = np.atleast_1d(x).astype(float)
+ if x.ndim > 1:
+ raise TypeError('x axis cannot be multidimensional')
+
+ y = np.atleast_1d(y)
+ if not np.iscomplexobj(y):
+ y = y.astype(float)
+
+ if x.shape != y.shape:
+ # remove ugly 1-length dims
+ x = np.squeeze(x)
+ y = np.squeeze(y)
+ if x.shape != y.shape:
+ raise ValueError(f'x ({x.shape}) and y ({y.shape}) have different shapes')
+
+ mask = np.ones_like(x, dtype=bool)
+
+ if y_err is None:
+ y_err = np.zeros(y.shape, dtype=float)
+
+ elif isinstance(y_err, Real):
+ y_err = np.ones(y.shape, dtype=float) * y_err
+ else:
+ y_err = np.atleast_1d(y_err)
+ if y_err.shape != y.shape:
+ raise ValueError(f'y_err ({y_err.shape}) and y ({y.shape}) have different shapes')
+
+ return x, y, y_err, mask
+
+ @property
+ def x(self) -> np.ndarray:
+ return self._x
+
+ @x.setter
+ def x(self, value: ArrayLike):
+ value = np.asarray(value, dtype=float)
+ if value.shape == self._x.shape:
+ self._x = value
+ elif value.shape == self._x[self.mask].shape:
+ self._x = value
+ self._y = self._y[self.mask]
+ self._y_err = self._y_err[self.mask]
+ else:
+ raise ValueError('Shape mismatch in setting x')
+
+ @property
+ def y(self) -> np.ndarray:
+ return self._y
+
+ @y.setter
+ def y(self, value: ArrayLike):
+ """
+ Set new y values. If the length of the new array equals that of original y including masked points
+ this is equivalent to a simple assignment. If the length equals the masked points then masked points are
+ discarded.
+
+ Args:
+ value: array of the same length as full or masked y
+
+ Returns:
+
+ """
+ value = np.asarray(value, dtype=self._y.dtype)
+ if value.shape == self._y.shape:
+ self._y = value
+ elif value.shape == self._y[self.mask].shape:
+ self._y[self.mask] = value
+ self._x = self._x[self.mask]
+ self._y_err = self._y_err[self.mask]
+ else:
+ raise ValueError('Shape mismatch in setting y')
+
+ @property
+ def y_err(self) -> np.ndarray:
+ return self._y_err
+
+ @y_err.setter
+ def y_err(self, value: ArrayLike):
+ value = np.asarray(value, dtype=float)
+ if value.shape == self._y_err.shape:
+ self._y_err = value
+ elif value.shape == self._y_err[self.mask].shape:
+ self._y[self.mask] = value
+ else:
+ raise ValueError('Shape mismatch in setting y_err')
+
+ def __len__(self) -> int:
+ return len(self._x[self.mask])
+
+ def __repr__(self) -> str:
+ return f'Point: {self.name}'
+
+ def __getitem__(self, item: str) -> Any:
+ try:
+ return self.meta[item]
+ except KeyError:
+ KeyError(f'{item} not found')
+
+ def __setitem__(self, key: str, value: Any):
+ self.meta[key] = value
+
+ def __delitem__(self, key: str):
+ del self.meta[key]
+
+ def update(self, opts: dict) -> None:
+ """
+ Update metadata
+
+ Args:
+ opts:
+
+ Returns:
+
+ """
+ self.meta.update(opts)
+
+ def __lshift__(self, other):
+ if isinstance(other, Real):
+ _tempx = self._x - other
+ ret = type(self)(_tempx, self._y)
+ self.update(self.meta)
+
+ return ret
+ else:
+ raise TypeError(f'{other} is not a number')
+
+ def __rshift__(self, other):
+ return self.__lshift__(-other)
+
+ def __ilshift__(self, other):
+ if isinstance(other, Real):
+ self._x -= other
+ return self
+ else:
+ raise TypeError(f'{other} is not a number')
+
+ def __irshift__(self, other):
+ return self.__ilshift__(-other)
+
+ def __add__(self, other):
+ """
+ Implements self + other
+ """
+ if isinstance(other, Number):
+ _tempy = self._y + other
+ ret = type(self)(self._x, _tempy)
+ ret.update(self.meta)
+ return ret
+ elif isinstance(other, self.__class__):
+ if np.equal(self._x, other.x).all():
+ _tempy = self._y + other.y
+ ret = type(self)(self._x, _tempy)
+ ret.update(self.meta)
+
+ return ret
+ else:
+ raise ValueError('Objects have different x values')
+
+ def __radd__(self, other):
+ """
+ Implements other + self
+ """
+ return self.__add__(other)
+
+ def __iadd__(self, other):
+ """
+ Implements self += other
+ """
+ if isinstance(other, Number):
+ self._y += other
+ return self
+ elif isinstance(other, self.__class__):
+ # restrict to same x values
+ if np.equal(self._x, other.x).all():
+ self._y += other.y
+ else:
+ raise ValueError('Objects have different x values')
+ return self
+
+ def __neg__(self):
+ """
+ Implements -self
+ """
+ ret = type(self)(self._x, self._y * (-1))
+ ret.update(self.meta)
+
+ return ret
+
+ @property
+ def value(self):
+ return self.meta['value']
+
+ @property
+ def group(self):
+ return self.meta['group']
+
+ @group.setter
+ def group(self, value: str):
+ self.meta['group'] = str(value)
+
+ @value.setter
+ def value(self, value: float):
+ self.meta['value'] = float(value)
+
+ @property
+ def name(self):
+ return self.meta['name']
+
+ @name.setter
+ def name(self, value):
+ self.meta['name'] = str(value)
+
+ @property
+ def length(self):
+ return len(self._x)
+
+ def points(self, idx: Optional[list] = None, special: Optional[str] = None,
+ avg_range: Tuple[int, int] = (0, 0), avg_mode: str = 'mean',
+ pts: Optional[list] = None) -> List[tuple]:
+ """
+ Return (x, y) values at specific positions.
+
+ Args:
+ idx (list, optional) : List of indexes to evaluate.
+ special (str, {'max', 'min', 'absmax', 'absmin'}, optional) :
+ Special positions to extract.
+
+ `max` : Selects position of the maximum of y, or real part if y is complex.
+
+ 'min' : Selects position of the minimum of y, or real part if y is complex.
+
+ 'absmax' : Selects position at the maximum of the absolute value of y.
+
+ 'absmin' : Selects position at the minimum of the absolute value of y.
+
+ avg_range (tuple of int) :
+ Region for average of y values. Tuple (a, b) uses ``y[i-a:i+b+1]`` around index `i`. Default is (0, 0).
+ avg_mode (str {'mean', 'sum', 'integral'} , optional) :
+ Averaging type
+
+ `mean` : Arithmetic average
+
+ `sum`: Sum over y values
+
+ 'integral`: Integration over range using Simpson's rule
+
+ pts (list, optional) :
+ If given, points will be appended.
+
+ Returns :
+ list of tuples (x, y, y_err)
+ """
+
+ if (idx is None) and (special is None):
+ raise ValueError('Either `idx` or `special` must be given')
+
+ if avg_mode not in ['mean', 'sum', 'integral']:
+ raise ValueError(f'Parameter `avg_mode` is `mean`, `sum`, `integral`, not `{avg_mode}`' )
+
+ if pts is None:
+ pts = []
+
+ for x in idx:
+ if isinstance(x, tuple):
+ x_idx = np.argmin(np.abs(self._x[self.mask] - (x[0]+x[1])/2))
+ left_b = np.argmin(np.abs(self._x[self.mask] - x[0]))
+ right_b = np.argmin(np.abs(self._x[self.mask] - x[1]))
+ else:
+ x_idx = np.argmin(np.abs(self._x[self.mask]-x))
+ left_b = int(max(0, x_idx - avg_range[0]))
+ right_b = int(min(len(self), x_idx + avg_range[1] + 1))
+
+ if left_b < right_b:
+ pts.append([self._x[x_idx], *self._average(avg_mode, x_idx, left_b, right_b)])
+ else:
+ pts.append([self._x[x_idx], self._y[x_idx], self._y_err[x_idx]])
+
+ if special is not None:
+ if special not in ['max', 'min', 'absmax', 'absmin']:
+ raise ValueError('Parameter "special" must be "max", "min", "absmax", "absmin".')
+
+ if special == 'max':
+ x_idx = np.argmax(self._y.real[self.mask])
+ elif special == 'min':
+ x_idx = np.argmax(self._y.real[self.mask])
+ elif special == 'absmax':
+ x_idx = np.argmax(np.abs(self._y[self.mask]))
+ else:
+ x_idx = np.argmin(np.abs(self._y[self.mask]))
+
+ left_b = int(max(0, x_idx - avg_range[0]))
+ right_b = int(min(len(self), x_idx + avg_range[1] + 1))
+
+ pts.append([self._x[self.mask][x_idx], *self._average(avg_mode, x_idx, left_b, right_b)])
+
+ return pts
+
+ def _average(self, mode: str, idx, left: int, right: int) -> Tuple[float, float]:
+ if mode == 'mean':
+ y_mean = np.mean(self._y[self.mask][left:right].real)
+ y_err = np.linalg.norm(self._y_err[self.mask][left:right]) / (right - left)
+
+ elif mode == 'sum':
+ y_mean = np.sum(self._y[self.mask][left:right].real)
+ y_err = np.linalg.norm(self._y_err[self.mask][left:right])
+
+ elif mode == 'integral':
+ y_mean = simpson(self._y[self.mask][left:right].real, x=self._x[left:right])
+ y_err = np.linalg.norm(cumulative_trapezoid(self._y_err[self.mask][left:right].real, x=self._x[left:right]))
+
+ else:
+ y_mean = self._y[self.mask][idx].real
+ y_err = self._y_err[self.mask][idx]
+
+ return y_mean, y_err
+
+ def concatenate(self, other):
+ """
+ Add the values of an object of the same
+
+ Args:
+ other: Dataset of same type
+
+ """
+ if isinstance(other, self.__class__):
+ self._x = np.r_[self._x, other.x]
+ self._y = np.r_[self._y, other.y]
+ self.mask = np.r_[self.mask, other.mask]
+ self._y_err = np.r_[self._y_err, other.y_err]
+ else:
+ raise TypeError(f'{other} is of type {type(other)}')
+
+ return self
+
+ def integrate(self, log: bool = False, limits: Tuple[float, float] = None) -> PointLike:
+ new_data = self.copy()
+
+ if limits is not None:
+ new_data.cut(*limits)
+
+ if log:
+ new_data.y = cumulative_trapezoid(y=new_data.y, x=np.log(new_data.x), initial=0)
+ else:
+ new_data.y = cumulative_trapezoid(y=new_data.y, x=new_data.x, initial=0)
+
+ return new_data
+
+ def diff(self, log: bool = False, limits: Optional[Tuple[float, float]] = None) -> PointLike:
+ """
+
+ Args:
+ log:
+ limits:
+
+ Returns:
+
+ """
+
+ new_data = self.copy()
+
+ if limits is not None:
+ new_data.cut(*limits)
+
+ if log:
+ new_data.y = np.gradient(new_data.y, np.log(new_data.x))
+ else:
+ new_data.y = np.gradient(new_data.y, new_data.x)
+
+ return new_data
+
+ def magnitude(self) -> PointLike:
+ new_data = self.copy()
+ new_data.y.real = np.abs(self.y)
+ if np.iscomplexobj(new_data.y):
+ new_data.y.imag = 0.
+
+ return new_data
+
+ def copy(self) -> PointLike:
+ """
+ Copy the object
+
+ Returns:
+ A copy of the object
+ """
+ return copy.deepcopy(self)
+
+ def get_values(self, idx: int) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
+ """
+ Return values at a given index
+
+ Args:
+ idx (int) : Index value
+
+ Returns:
+ Tuple of (`x[idx]`, `y[idx]` , `y_err[idx]`)
+ """
+ return self._x[idx], self._y[idx], self._y_err[idx]
+
+ def set_values(self, idx: int, value: Union[list, tuple, np.ndarray]) -> PointLike:
+ if not (isinstance(value, (list, tuple, np.ndarray)) and (len(value) in [2, 3])):
+ raise TypeError('Values should be list type of length 2 or 3')
+
+ self._x[idx] = value[0]
+ self._y[idx] = value[1]
+
+ if len(value) == 3:
+ self._y_err[idx] = value[2]
+ else:
+ self._y_err[idx] = 0
+
+ return self
+
+ def set_data(self,
+ x: Optional[np.ndarray] = None,
+ y: Optional[np.ndarray] = None,
+ y_err: Optional[np.ndarray] = None) -> PointLike:
+ if x is None:
+ x = self._x
+ if y is None:
+ y = self._y
+ if y_err is not None:
+ y_err = self._y_err
+
+ self._x, self._y, self._y_err, self.mask = self._prepare_xy(x, y, y_err)
+
+ return self
+
+ def append(self, x: ArrayLike, y: ArrayLike, y_err: Optional[ArrayLike] = None):
+ x, y, y_err, mask = self._prepare_xy(x, y, y_err)
+
+ self._x = np.r_[self._x, x]
+ self._y = np.r_[self._y, y]
+ self._y_err = np.r_[self._y_err, y_err]
+ self.mask = np.r_[self.mask, mask]
+
+ return self
+
+ def remove(self, idx):
+ self._x = np.delete(self._x, idx)
+ self._y = np.delete(self._y, idx)
+ self.mask = np.delete(self.mask, idx)
+ self._y_err = np.delete(self._y_err, idx)
+
+ return self
+
+ def cut(self, *limits):
+ if len(limits) != 2:
+ raise ValueError('Two arguments are needed')
+
+ low_lim, high_lim = limits
+ if low_lim is None:
+ low_lim = np.min(self._x)
+
+ if high_lim is None:
+ high_lim = np.max(self._x)
+
+ _mask = np.ma.masked_inside(self._x, low_lim, high_lim).mask
+
+ self._x = self._x[_mask]
+ self._y = self._y[_mask]
+ self._y_err = self._y_err[_mask]
+ self.mask = self.mask[_mask]
+
+ return self
+
+ def shift(self, points: int) -> PointLike:
+ """
+ Move y values `points` indexes, remove invalid indexes and fill remaining with zeros
+ Negative `points` shift to the left, positive `points` shift to the right
+
+ Args:
+ points (int) : shift value
+
+ Example:
+ >>> pts = Points(x=[0., 1., 2., 3.], y=[1., 2., 3., 4.])
+ >>> pts.shift(2)
+ >>> pts.x
+ array([0., 1., 2., 3.])
+ >>> pts.y
+ array([3., 4., 0., 0.])
+
+ Returns:
+ The original object with shifted values.
+ """
+ if points > 0:
+ self._y = np.roll(self._y, -int(points))
+ self._y[-int(points)-1:] = 0
+ else:
+ self._y = np.roll(self._y, int(points))
+ self._y[:-int(points)] = 0
+
+ self.meta['shift'] += points
+
+ return self
+
+ def sort(self, axis=0):
+ if axis == 0:
+ sort_order = np.argsort(self._x)
+ else:
+ sort_order = np.argsort(self._y)
+ self._x = self._x[sort_order]
+ self._y = self._y[sort_order]
+ self._y_err = self._y_err[sort_order]
+ self.mask = self.mask[sort_order]
+
+ return self
+
+ def normalize(self, mode):
+ if mode == 'first':
+ scale = self._y[0].real
+ elif mode == 'last':
+ scale = self._y[-1].real
+ elif mode == 'max':
+ scale = np.max(self._y.real)
+ elif mode == 'maxabs':
+ scale = np.max(np.abs(self._y))
+ elif mode == 'area':
+ try:
+ from scipy.integrate import simpson
+ except ImportError:
+ from scipy.integrate import simps as simpson
+ scale = simpson(self._y.real, x=self._x)
+ else:
+ raise ValueError(f'Unknown normalize mode {mode}')
+
+ self._y /= scale
+ self._y_err /= scale
+
+ return scale
+
+ def toarray(self, err=True):
+ if np.count_nonzero(self._y_err) == 0 or not err:
+ return np.c_[self._x, self._y]
+ else:
+ return np.c_[self._x, self._y, self._y_err]
+
+ def tohdf(self, hdffile):
+ grp = hdffile
+ grp2_name = self.name
+
+ try:
+ dset = grp.create_dataset(grp2_name, shape=(len(self._x),),
+ dtype=np.dtype([('x', 'f'), ('y', 'f')]),
+ compression='gzip')
+ except RuntimeError:
+ dset = grp[grp2_name]
+
+ dset['x'] = self._x
+ dset['y'] = self._y
+ dset.attrs['TITLE'] = self.group + '/' + self.name
+ dset.attrs['damaris_type'] = 'MeasurementResult'
+
+ def savetxt(self, fname, err=False):
+ path = Path(fname)
+
+ if not path.parent.exists():
+ path.parent.mkdir(parents=True)
+
+ header = []
+ for k, v in self.meta.items():
+ header.append('%s: %s' % (k, str(v)))
+ header = '\n'.join(header)
+
+ if np.all(self.mask):
+ np.savetxt(path, self.toarray(err=err), header=header, fmt='%.10f')
+ else:
+ with path.open('w') as f:
+ f.write(header)
+ for i, l in enumerate(self.toarray(err=err)):
+ if self.mask[i]:
+ f.write('\t'.join(map(str, l.tolist())) + '\n')
+ else:
+ f.write('#' + '\t'.join(map(str, l.tolist())) + '\n')
+
+ def get_state(self) -> dict:
+ ret_dic = {'x': self._x.tolist(),
+ 'y': self._y.tolist(),
+ 'mask': (np.where(~self.mask)[0]).tolist()}
+
+ if np.all(self._y_err == 0):
+ ret_dic['y_err'] = 0.0
+ else:
+ ret_dic['y_err'] = self._y_err.tolist()
+
+ ret_dic.update(self.meta)
+
+ return ret_dic
+
+ @classmethod
+ def set_state(cls, state: dict):
+ _x = state.pop('x')
+ _y = state.pop('y')
+ _yerr = state.pop('y_err')
+ data = cls(x=_x, y=_y, y_err=_yerr)
+ _m = state.pop('mask')
+ data.mask[_m] = False
+ data.meta.update(state)
+
+ return data
+
diff --git a/nmreval/data/signals.py b/nmreval/data/signals.py
new file mode 100644
index 0000000..222d03e
--- /dev/null
+++ b/nmreval/data/signals.py
@@ -0,0 +1,100 @@
+import numpy as np
+
+
+from .points import Points
+
+
+class Signal(Points):
+ """
+ Extension of Points for complex y values.
+ """
+
+ def __init__(self, x, y, **kwargs):
+ y = np.atleast_1d(y)
+ if (y.ndim == 1) or y.shape[0] == 1:
+ y = y.astype(complex)
+ else:
+ y = y[0, :] + 1j * y[1, :]
+
+ super().__init__(x, y, **kwargs)
+
+ self.dx = kwargs.get('dx', abs(self._x[1] - self._x[0]))
+ self.meta.update({'phase': [], 'shift': 0})
+
+ def __repr__(self):
+ return 'Signal: ' + self.name
+
+ def swap(self):
+ """
+
+ Swaps real and imaginary part of y
+
+ """
+ self._y.real, self._y.imag = self._y.imag, self._y.real
+
+ return self
+
+ def phase(self, method: str = 'manual', **kwargs):
+ if method == 'manual':
+ self.manual_phase(**kwargs)
+ elif method == 'simple':
+ self.simple_phase(**kwargs)
+ else:
+ raise KeyError(f'Not implemented method {method}')
+
+ return self
+
+ def manual_phase(self, ph0: float = 0., ph1: float = 0., pvt: float = 0):
+ self._y *= np.exp(1j * ph0 * np.pi / 180.)
+ if ph1 != 0:
+ x = (self._x - pvt) / max(self._x)
+ self._y *= np.exp(1j * x * ph1 * np.pi / 180.)
+ self.meta['phase'].append((ph0, ph1, pvt))
+
+ return self
+
+ def simple_phase(self, pos: int = 0, avg: int = 0):
+ ph = np.mean(np.angle(self._y)[pos-avg:pos+avg+1])
+ self._y *= np.exp(-1j*ph)
+
+ self.meta['phase'].append((ph, 0, 0))
+
+ return self
+
+ def baseline_spline(self, line):
+ self._y -= line
+
+ return self
+
+ def divide(self, part):
+ if part:
+ self.cut(0.01*self._x[0], self._x[int(self.length//2)+1])
+ else:
+ self.cut(self._x[int(self.length//2)], 10*self._x[-1])
+
+ return self
+
+ def toarray(self, err=False):
+ if not err or np.all(self._y_err == 0.):
+ return np.c_[self._x, self._y.real, self._y.imag]
+ else:
+ return np.c_[self._x, self._y.real, self._y.imag, self._y_err]
+
+ def tohdf(self, hdffile):
+ grp = hdffile
+ try:
+ grp2 = grp.create_group(self.name)
+ is_empty = True
+ except ValueError:
+ is_empty = False
+ grp2 = grp[self.name]
+ grp2.attrs['damaris_type'] = 'Accumulation'
+ grp2.attrs['TITLE'] = self.group + '/' + self.name
+ if is_empty:
+ grp2.create_dataset('accu_data', data=np.c_[self._y.real, self._y.imag],
+ compression='gzip')
+ idx = grp2.create_dataset('indices', (1,), dtype=np.dtype([('dwelltime', 'f')]))
+ idx['dwelltime'] = self.dx
+ else:
+ grp2['accu_data'][...] = np.c_[self._y.real, self._y.imag]
+ grp2['indices']['dwelltime'] = self.dx
diff --git a/nmreval/distributions/__init__.py b/nmreval/distributions/__init__.py
new file mode 100644
index 0000000..55d58aa
--- /dev/null
+++ b/nmreval/distributions/__init__.py
@@ -0,0 +1,7 @@
+
+from .havriliaknegami import HavriliakNegami
+from .colecole import ColeCole
+from .coledavidson import ColeDavidson
+from .debye import Debye
+from .kww import KWW
+from .loggaussian import LogGaussian
diff --git a/nmreval/distributions/base.py b/nmreval/distributions/base.py
new file mode 100644
index 0000000..d89a326
--- /dev/null
+++ b/nmreval/distributions/base.py
@@ -0,0 +1,85 @@
+import abc
+from warnings import warn
+
+import numpy as np
+
+
+class Distribution(abc.ABC):
+ name = 'Abstract distribution'
+ parameter = None
+ bounds = None
+
+ @classmethod
+ def __repr__(cls):
+ return cls.name
+
+ @staticmethod
+ @abc.abstractmethod
+ def distribution(taus, tau0, *args):
+ pass
+
+ @staticmethod
+ @abc.abstractmethod
+ def susceptibility(omega, tau, *args):
+ pass
+
+ @classmethod
+ def specdens(cls, omega, tau, *args):
+ return -cls.susceptibility(omega, tau, *args).imag / omega
+
+ @staticmethod
+ @abc.abstractmethod
+ def correlation(t, tau0, *args):
+ pass
+
+ @classmethod
+ def mean_value(cls, *args, mode='mean'):
+ if mode == 'mean':
+ return cls.mean(*args)
+ if mode == 'logmean':
+ return np.exp(cls.logmean(*args))
+ if mode in ['max', 'peak']:
+ return cls.max(*args)
+
+ return args[0]
+
+ @classmethod
+ def convert(cls, *args, from_='raw', to_='mean'):
+ if from_ == to_:
+ return args[0]
+
+ if from_ == 'raw':
+ return cls.mean_value(*args, mode=to_)
+ else:
+ # makes only sense as long as mean/peak is given by = factor * x
+ factor = cls.mean_value(1, *args[1:], mode=from_)
+ try:
+ raw = args[0] / factor
+ except ZeroDivisionError:
+ raise ZeroDivisionError('Conversion between mean values impossible')
+
+ if to_ == 'raw':
+ return raw
+ else:
+ return cls.mean_value(raw, *args[1:], mode=to_)
+
+ @staticmethod
+ def logmean(*args):
+ """
+ Return mean logarithmic tau
+
+ Args:
+ *args:
+
+ Returns:
+
+ """
+ return np.log(args[0])
+
+ @staticmethod
+ def mean(*args):
+ return args[0]
+
+ @staticmethod
+ def max(*args):
+ return args[0]
diff --git a/nmreval/distributions/colecole.py b/nmreval/distributions/colecole.py
new file mode 100644
index 0000000..e85c58a
--- /dev/null
+++ b/nmreval/distributions/colecole.py
@@ -0,0 +1,105 @@
+import numpy as np
+
+from .base import Distribution
+from ..math.mittagleffler import mlf
+
+
+class ColeCole(Distribution):
+ """
+ Functions based on Cole-Cole distribution
+ """
+ name = 'Cole-Cole'
+ parameter = [r'\alpha']
+ bounds = [(0, 1)]
+
+ @staticmethod
+ def distribution(taus, tau0, alpha):
+ """
+ Distribution of correlation times
+
+ .. math::
+ G(\ln\\tau) = \\frac{1}{2\pi} \\frac{\\sin(\\pi\\alpha)}{\cosh[\\alpha\ln(\\tau/\\tau_0)] + \cos(\\alpha \pi))}
+
+ Args:
+ taus:
+ tau0:
+ alpha:
+ """
+ z = np.log(taus/tau0)
+ return np.sin(np.pi*alpha)/(np.cosh(z*alpha) + np.cos(alpha*np.pi))/(2*np.pi)
+
+ @staticmethod
+ def susceptibility(omega, tau, alpha):
+ """
+ Susceptibility
+
+ .. math::
+ \chi(\omega, \\tau, \\alpha) = \\frac{1}{1-(i\omega\\tau_0)^\\alpha}
+
+ Args:
+ omega:
+ tau:
+ alpha:
+ """
+ _tau = np.atleast_1d(tau)
+ _omega = np.atleast_1d(omega)
+ val = 1/(1+(1j*_omega[:, None]*_tau[None, :])**alpha)
+ return np.conjugate(val).squeeze()
+
+ @staticmethod
+ def specdens(omega, tau, alpha):
+ """
+ Spectral density
+
+ .. math::
+ J(\omega, \\tau, \\alpha) = \\frac{\sin(\\alpha\pi/2)(\omega\\tau)^\\alpha}{\omega[1+(\omega\\tau)^{2\\alpha} + 2\\pi\cos(\\alpha\pi/2)(\omega\\tau)^\\alpha]}
+
+ Args:
+ omega:
+ tau:
+ alpha:
+ """
+ _tau = np.atleast_1d(tau)
+ _omega = np.atleast_1d(omega)
+ omtau = (_omega*_tau)**alpha
+ return np.sin(alpha*np.pi/2) * omtau / (1 + omtau**2 + 2*np.cos(alpha*np.pi/2)*omtau) / omega
+
+ @staticmethod
+ def correlation(t, tau0, alpha):
+ """
+ Correlation function :math:`C(t)`
+
+ .. math::
+ C(t) = E_\\alpha[-(t/\\tau_0)^\\alpha]
+
+ with Mittag-Leffler function :math:`E_a(x)`
+
+ Args:
+ t:
+ tau0:
+ alpha:
+ """
+ return mlf(-(t/tau0)**alpha, alpha)
+
+
+if __name__ == '__main__':
+ import matplotlib.pyplot as plt
+
+ x = np.logspace(-3, 3, num=61)
+
+ fig, ax = plt.subplots(2, 2)
+ ax[0, 0].set_title('Distribution')
+ ax[0, 1].set_title('Correlation func.')
+ ax[1, 0].set_title('Spectral density')
+ ax[1, 1].set_title('Susceptibility')
+
+ for a in [0.4, 0.6, 0.8]:
+ ax[0, 0].loglog(x, ColeCole.distribution(x, 1, a))
+ ax[0, 1].semilogx(x, ColeCole.correlation(x, 1, a))
+ ax[1, 0].loglog(x, ColeCole.specdens(x, 1, a))
+ g = ax[1, 1].loglog(x, ColeCole.susceptibility(x, 1, a).imag)
+ ax[1, 1].loglog(x, ColeCole.susceptibility(x, 1, a).real, '--', color=g[0].get_color())
+
+ fig.tight_layout()
+
+ plt.show()
diff --git a/nmreval/distributions/coledavidson.py b/nmreval/distributions/coledavidson.py
new file mode 100644
index 0000000..8d2fab6
--- /dev/null
+++ b/nmreval/distributions/coledavidson.py
@@ -0,0 +1,179 @@
+import numbers
+
+import numpy as np
+from scipy.special import psi, gammaincc
+
+from .base import Distribution
+from ..utils.constants import Eu
+
+
+class ColeDavidson(Distribution):
+ """
+ Functions based on Cole-Davidson distribution
+ """
+ name = 'Cole-Davidson'
+ parameter = [r'\gamma']
+ bounds = [(0, 1)]
+
+ @staticmethod
+ def susceptibility(omega, tau, gamma):
+ """
+ Susceptibility
+
+ .. math::
+ \chi(\omega, \\tau, \\gamma) = \\frac{1}{(1-i\omega\\tau_0)^\\gamma}
+
+ Args:
+ omega:
+ tau:
+ gamma:
+ """
+ return (1/(1+1j*omega*tau)**gamma).conjugate()
+
+ @staticmethod
+ def specdens(omega, tau,gamma):
+ """
+ Spectral density
+
+ .. math::
+ J(\omega, \\tau, \\gamma) = \\frac{\sin[\\gamma\\arctan(\\omega\\tau)]}{\omega[1+(\omega\\tau)^2]^{\\gamma/2}}
+
+ Args:
+ omega:
+ tau:
+ gamma:
+ """
+ gam = gamma
+ _w = np.atleast_1d(omega)
+ _t = np.atleast_1d(tau)
+ omtau = _w[:, None] * _t[None, :]
+
+ ret_val = np.sin(gam*np.arctan2(omtau, 1)) / (1 + omtau**2)**(gam/2.) / _w[:, None]
+ ret_val[_w == 0, :] = gam*_t
+
+ return np.squeeze(ret_val)
+
+ @staticmethod
+ def mean(tau, gamma):
+ """
+ Mean tau
+
+ .. math::
+ \\langle \\tau \\rangle = \\gamma \\tau
+
+ Args:
+ tau:
+ gamma:
+
+ """
+
+ return tau*gamma
+
+ @staticmethod
+ def logmean(tau, gamma):
+ """
+ Mean logarithm of tau
+
+ .. math::
+ \\langle \ln \\tau \\rangle = \ln\\tau + \psi(\gamma) + \mathrm{Eu}
+
+ with the Euler's constant :math:`\mathrm{Eu} \\approx 0.57721567\ldots` and the digamma function
+
+ .. math::
+ \psi(x) = \\frac{d}{dx} \ln(\Gamma(x))
+
+ Args:
+ tau:
+ gamma:
+
+ References:
+ R. Zorn: Logartihmic moments of relaxation time distribution. J. Chem. Phys. (2002). http://dx.doi.org/10.1063/1.1446035
+
+ """
+ return np.log(tau) + psi(gamma) + Eu
+
+ @staticmethod
+ def max(tau, gamma):
+ """
+ Calculate :math:`\\tau_\\text{peak}`, i.e. the inverse of the maximum frequency of the imaginary part of the susceptibility
+
+ .. math::
+ \\tau_\\text{peak} = \\frac{\\tau}{\\tan[\\pi/(2\gamma+2)]}
+
+ Args:
+ tau:
+ gamma:
+
+ References:
+ R. Dı́az-Calleja: Comment on the Maximum in the Loss Permittivity for the Havriliak-Negami Equation.
+ Macromolecules (2000). https://doi.org/10.1021/ma991082i
+
+ """
+
+ return tau/np.tan(np.pi/(2*gamma+2))
+
+ @staticmethod
+ def distribution(tau, tau0, gamma):
+ """
+ Distribution of correlation times
+
+ .. math::
+ G(\ln\\tau) =
+ \\begin{cases}
+ \\frac{\sin(\pi\gamma)}{\pi} \\Big(\\frac{\\tau}{\\tau_0 - \\tau}\\Big)^\gamma & \\tau < \\tau_0 \\\\
+ 0 & \\text{else}
+ \end{cases}
+
+ Args:
+ tau:
+ tau0:
+ gamma:
+ """
+
+ if isinstance(tau, numbers.Number):
+ return np.sin(np.pi*gamma) / np.pi * (1 / (tau0/tau - 1))**gamma if tau < tau0 else 0
+
+ ret_val = np.zeros_like(tau)
+ ret_val[tau < tau0] = np.sin(np.pi*gamma) / np.pi * (1 / (tau0/tau[tau < tau0] - 1))**gamma
+
+ return ret_val
+
+ @staticmethod
+ def correlation(t, tau0, gamma):
+ r"""
+ Correlation function
+
+ .. math::
+ C(t, \tau_0, \gamma) = \frac{\Gamma(\gamma, t/\tau_0)}{\Gamma(\gamma)}
+
+ with incomplete Gamma function
+
+ .. math::
+ \Gamma(\gamma, t/\tau_0) = \frac{1}{\Gamma(\gamma)}\int_{t/\tau_0}^\infty x^{\gamma-1}\text{e}^{-x}\,\mathrm{d}x
+
+ Args:
+ t:
+ tau0:
+ gamma:
+
+ References:
+ R. Hilfer: H-function representations for stretched exponential relaxation and non-Debye susceptibilities in glassy systems. Phys. Rev. E (2002) https://doi.org/10.1103/PhysRevE.65.061510
+
+ """
+ return gammaincc(gamma, t / tau0)
+
+
+if __name__ == '__main__':
+ import matplotlib.pyplot as plt
+
+ x = np.logspace(-3, 3, num=61)
+
+ fig, ax = plt.subplots(2, 2)
+
+ for g in [0.4, 0.6, 0.8]:
+ ax[0, 0].loglog(x, ColeDavidson.distribution(x, 1, g))
+ ax[0, 1].semilogx(x, ColeDavidson.correlation(x, 1, g))
+ ax[1, 0].loglog(x, ColeDavidson.specdens(x, 1, g))
+ gr = ax[1, 1].loglog(x, ColeDavidson.susceptibility(x, 1, g).imag)
+ ax[1, 1].loglog(x, ColeDavidson.susceptibility(x, 1, g).real, '--', color=gr[0].get_color())
+ plt.show()
diff --git a/nmreval/distributions/debye.py b/nmreval/distributions/debye.py
new file mode 100644
index 0000000..dc76bd4
--- /dev/null
+++ b/nmreval/distributions/debye.py
@@ -0,0 +1,30 @@
+import numbers
+
+import numpy as np
+
+from .base import Distribution
+
+
+class Debye(Distribution):
+ name = 'Debye'
+
+ @staticmethod
+ def correlation(t, tau0, *args):
+ return np.exp(-t/tau0)
+
+ @staticmethod
+ def susceptibility(omega, tau0, *args):
+ return 1/(1 + 1j*omega*tau0)
+
+ @staticmethod
+ def specdens(omega, tau0, *args):
+ return tau0 / (1 + (omega*tau0)**2)
+
+ @staticmethod
+ def distribution(taus, tau0, *args):
+ if isinstance(taus, numbers.Number):
+ return 1 if taus == tau0 else 0
+
+ ret_val = np.zeros_like(taus)
+ ret_val[np.argmin(abs(taus - tau0))] = 1
+ return ret_val
diff --git a/nmreval/distributions/energy.py b/nmreval/distributions/energy.py
new file mode 100644
index 0000000..41e9d53
--- /dev/null
+++ b/nmreval/distributions/energy.py
@@ -0,0 +1,124 @@
+from itertools import product
+
+import numpy as np
+from scipy.integrate import simps as simpson
+
+from .base import Distribution
+from ..utils.constants import kB
+
+
+class EnergyBarriers(Distribution):
+ name = 'Energy barriers'
+ parameter = [r'\tau_{0}', r'E_{m}', r'\Delta E']
+
+ @staticmethod
+ def distribution(tau, temperature, *args):
+ t0, em, sigma = args
+ m = np.exp(em / (kB * temperature)) * t0
+ return temperature * np.exp(-0.5 * (np.log(tau/m)*kB*temperature/sigma)**2) / np.sqrt(2*np.pi)/sigma
+
+ @staticmethod
+ def rate(t0, e_a, te):
+ return np.exp(-e_a / (kB * te)) / t0
+
+ @staticmethod
+ def energydistribution(e_a, mu, sigma):
+ return np.exp(-0.5 * ((mu-e_a) / sigma) ** 2) / (np.sqrt(2 * np.pi) * sigma)
+
+ @staticmethod
+ def correlation(t, temperature, *args):
+ 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)
+ 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 t for tt in temperature])
+
+ return ret_val
+
+ @staticmethod
+ def susceptibility(omega, temperature, *args):
+ # in contrast to other spectral densities, it's omega and temperature
+ tau0, e_m, e_b = args
+
+ def integrand_real(e_a, w, t0, mu, sigma, t):
+ r = EnergyBarriers.rate(t0, e_a, t)
+ return 1/(r**2 + w**2) * EnergyBarriers.energydistribution(e_a, mu, sigma)
+
+ def integrand_imag(e_a, w, t0, mu, sigma, t):
+ r = EnergyBarriers.rate(t0, e_a, t)
+ return w*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 = []
+ for o, tt in product(omega, temperature):
+ ret_val.append(simpson(integrand_real(e_axis, o, tau0, e_m, e_b, tt), e_axis) -
+ 1j * simpson(integrand_imag(e_axis, o, tau0, e_m, e_b, tt), e_axis))
+
+ 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
+
+ @staticmethod
+ def mean(*args):
+ return args[1]*np.exp(args[2]/(kB*args[0]))
+
+ @staticmethod
+ def logmean(*args):
+ return args[1] + args[2] / (kB * args[0])
+
+ @staticmethod
+ def max(*args):
+ return args[1] * np.exp(args[2] / (kB * args[0]))
+
+
+if __name__ == '__main__':
+ import matplotlib.pyplot as plt
+
+ x = np.logspace(-12, 12, num=601)
+
+ fig, ax = plt.subplots(2, 2)
+ ax[0, 0].set_title('Distribution')
+ ax[0, 1].set_title('Correlation func.')
+ ax[1, 0].set_title('Spectral density')
+ ax[1, 1].set_title('Susceptibility')
+
+ p = [1e-12, 0.3, 0.02]
+
+ for a in [100, 150, 300]:
+ ax[0, 0].semilogx(x, EnergyBarriers.distribution(x, a, *p))
+ ax[0, 1].semilogx(x, EnergyBarriers.correlation(x, a, *p))
+ ax[1, 0].loglog(x, EnergyBarriers.specdens(x, a, *p))
+ g = ax[1, 1].loglog(x, -EnergyBarriers.susceptibility(x, a, *p).imag)
+ ax[1, 1].loglog(x, EnergyBarriers.susceptibility(x, a, *p).real, '--', color=g[0].get_color())
+
+ fig.tight_layout()
+
+ plt.show()
diff --git a/nmreval/distributions/gengamma.py b/nmreval/distributions/gengamma.py
new file mode 100644
index 0000000..3c96063
--- /dev/null
+++ b/nmreval/distributions/gengamma.py
@@ -0,0 +1,111 @@
+from typing import Union
+
+import numpy as np
+from scipy.integrate import simps
+from scipy.special import gammaln
+
+from .base import Distribution
+
+
+class GGAlpha(Distribution):
+ name = r'General \Gamma (\alpha-process)'
+ parameter = [r'\alpha', r'\beta']
+
+ @staticmethod
+ def correlation(t, tau0, *args):
+ logtau0 = np.log(tau0)
+ logtau = np.linspace(logtau0-20, logtau0+20, num=4001)
+ taus = np.exp(logtau)
+ gtau = GGAlpha.distribution(taus, tau0, *args)
+ ret_val = np.array([simps(np.exp(-xvals/taus)*gtau, logtau) for xvals in t])
+ return ret_val
+
+ @staticmethod
+ def susceptibility(omega, tau0, *args):
+ logtau0 = np.log(tau0)
+ logtau = np.linspace(logtau0 - 20, logtau0 + 20, num=4001)
+ taus = np.exp(logtau)
+ gtau = GGAlpha.distribution(taus, tau0, *args)
+ ret_val = np.array([simps(1/(1+1j*xvals*taus) * gtau, logtau) for xvals in omega])
+ return ret_val
+
+ @staticmethod
+ def distribution(taus: Union[float, np.ndarray], tau: float, *args) -> Union[float, np.ndarray]:
+ alpha, beta = args
+ b_to_a = beta / alpha
+ norm = np.exp(gammaln(b_to_a) - b_to_a * np.log(b_to_a)) / alpha
+ t_to_t0 = taus / tau
+ ret_val = np.exp(-b_to_a * t_to_t0 ** alpha) * t_to_t0 ** beta
+
+ return ret_val / norm
+
+
+class GGAlphaEW(Distribution):
+ name = r'General \Gamma (\alpha-process + EW)'
+ parameter = [r'\alpha', r'\beta']
+
+ @staticmethod
+ def correlation(t, tau0, *args):
+ logtau0 = np.log(tau0)
+ logtau = np.linspace(logtau0-20, logtau0+20, num=4001)
+ taus = np.exp(logtau)
+ gtau = GGAlphaEW.distribution(taus, tau0, *args)
+ # wir integrieren ueber lntau, nicht tau
+ ret_val = np.array([simps(np.exp(-xvals/taus)*gtau, logtau) for xvals in t])
+ return ret_val
+
+ @staticmethod
+ def susceptibility(omega, tau0, *args):
+ logtau0 = np.log(tau0)
+ logtau = np.linspace(logtau0 - 20, logtau0 + 20, num=4001)
+ taus = np.exp(logtau)
+ gtau = GGAlphaEW.distribution(taus, tau0, *args)
+ ret_val = np.array([simps(1/(1+1j*xvals*taus) * gtau, logtau) for xvals in omega])
+ return ret_val
+
+ @staticmethod
+ def distribution(tau: Union[float, np.ndarray], tau0: float, *args) -> Union[float, np.ndarray]:
+ alpha, beta, sigma, gamma = args
+ if gamma == beta:
+ return GGAlpha.distribution(tau, tau0, alpha, beta)
+ b_to_a = beta / alpha
+ g_to_a = gamma / alpha
+ t_to_t0 = tau / tau0
+ norm = (np.exp(gammaln(b_to_a)) + sigma**(gamma-beta) *
+ np.exp(gammaln(g_to_a) + (b_to_a-g_to_a)*np.log(b_to_a))) / np.exp(b_to_a*np.log(b_to_a)) / alpha
+
+ ret_val = np.exp(-b_to_a * t_to_t0**alpha) * t_to_t0**beta * (1 + (t_to_t0*sigma)**(gamma-beta))
+
+ return ret_val / norm
+
+
+class GGBeta(Distribution):
+ name = r'General \Gamma (\beta-process)'
+ parameter = [r'\alpha', r'\beta']
+
+ @staticmethod
+ def correlation(t, tau0, *args):
+ logtau0 = np.log(tau0)
+ logtau = np.linspace(logtau0-20, logtau0+20, num=4001)
+ taus = np.exp(logtau)
+ gtau = GGBeta.distribution(taus, tau0, *args)
+ # wir integrieren ueber lntau, nicht tau
+ ret_val = np.array([simps(np.exp(-xvals/taus)*gtau, logtau) for xvals in t])
+ return ret_val
+
+ @staticmethod
+ def susceptibility(omega, tau0, *args):
+ logtau0 = np.log(tau0)
+ logtau = np.linspace(logtau0 - 20, logtau0 + 20, num=4001)
+ taus = np.exp(logtau)
+ gtau = GGBeta.distribution(taus, tau0, *args)
+ ret_val = np.array([simps(1/(1+1j*xvals*taus) * gtau, logtau) for xvals in omega])
+ return ret_val
+
+ @staticmethod
+ def distribution(tau: Union[float, np.ndarray], tau0: float, *args) -> Union[float, np.ndarray]:
+ a, b = args
+ norm = a * (1+b) * np.sin(np.pi*b/(1+b)) * b**(b/(1+b)) / np.pi
+ ret_val = b * (tau/tau0)**a + (tau/tau0)**(-a*b)
+
+ return norm / ret_val
diff --git a/nmreval/distributions/havriliaknegami.py b/nmreval/distributions/havriliaknegami.py
new file mode 100644
index 0000000..5e771a3
--- /dev/null
+++ b/nmreval/distributions/havriliaknegami.py
@@ -0,0 +1,106 @@
+import numpy as np
+from scipy.special import psi
+
+from .base import Distribution
+from ..math.mittagleffler import mlf
+from ..utils import Eu
+
+
+class HavriliakNegami(Distribution):
+ """
+ Functions based on Cole-Davidson distribution
+ """
+
+ name = 'Havriliak-Negami'
+ parameter = [r'\alpha', r'\gamma']
+ bounds = [(0, 1), (0, 1)]
+
+ @staticmethod
+ def correlation(t, tau0, alpha, gamma):
+ r"""
+ Correlation function
+
+ .. math::
+ C(t, \tau_0, \alpha, \gamma) = 1 - \left(\frac{t}{\tau_0}\right)^{\alpha\gamma} \text{E}_{\alpha,\alpha\gamma+1}^\gamma\left[-\left(\frac{t}{\tau_0}\right)^\alpha\right]
+
+ with incomplete Gamma function
+
+ .. math::
+ \Gamma(\gamma, t/\tau_0) = \frac{1}{\Gamma(\gamma)}\int_{t/\tau_0}^\infty x^{\gamma-1}\text{e}^{-x}\,\mathrm{d}x
+
+ Args:
+ t:
+ tau0:
+ alpha:
+ gamma:
+
+ References:
+ R. Hilfer: H-function representations for stretched exponential relaxation and non-Debye susceptibilities in glassy systems. Phys. Rev. E (2002) https://doi.org/10.1103/PhysRevE.65.061510
+
+ """
+ return 1 - (t/tau0)**(alpha*gamma) * mlf(-(t/tau0)**alpha, alpha, alpha*gamma+1, gamma)
+
+ @staticmethod
+ def susceptibility(omega, tau, alpha, gamma):
+ return np.conjugate(1/(1 + (1j*omega[:, None]*tau[None, :])**alpha)**gamma).squeeze()
+
+ @staticmethod
+ def specdens(omega, tau, alpha, gamma):
+ omtau = (omega[:, None]*tau[None, :])**alpha
+
+ zaehler = np.sin(gamma * np.arctan2(omtau*np.sin(0.5*alpha*np.pi), 1 + omtau*np.cos(0.5*alpha*np.pi)))
+ nenner = (1 + 2*omtau * np.cos(0.5*alpha*np.pi) + omtau**2)**(0.5*gamma)
+
+ return ((1 / omega) * (zaehler/nenner)).squeeze()
+
+ @staticmethod
+ def distribution(tau, tau0, alpha, gamma):
+ if alpha == 1:
+ from .coledavidson import ColeDavidson
+ return ColeDavidson.distribution(tau, tau0, gamma)
+ elif gamma == 1:
+ from .colecole import ColeCole
+ return ColeCole.distribution(tau, tau0, alpha)
+ else:
+ _y = tau/tau0
+ om_y = (1 + 2*np.cos(np.pi*alpha)*(_y**alpha) + _y**(2*alpha))**(-gamma/2)
+ theta_y = np.arctan2(np.sin(np.pi * alpha), _y**alpha + np.cos(np.pi*alpha))
+
+ return np.sin(gamma*theta_y) * om_y * (_y**(alpha*gamma)) / np.pi
+
+ @staticmethod
+ def max(tau, alpha, gamma):
+ return tau*(np.sin(0.5*np.pi*alpha*gamma/(gamma+1)) / np.sin(0.5*np.pi*alpha/(gamma+1)))**(1/alpha)
+
+ @staticmethod
+ def logmean(tau, alpha, gamma):
+ r"""
+ Calculate mean logarithm of tau
+
+ .. math::
+ \langle \ln \tau \rangle = \ln \tau + \frac{\Psi(\gamma) + \text{Eu}}{\alpha}
+
+ Args:
+ tau:
+ alpha:
+ gamma:
+
+ Returns:
+
+ """
+ return np.log(tau) + (psi(gamma)+Eu)/alpha
+
+ @staticmethod
+ def mean(tau, alpha, gamma):
+ # approximation according to Th. Bauer et al., J. Chem. Phys. 133, 144509 (2010).
+ return tau * alpha*gamma
+
+
+if __name__ == '__main__':
+ import matplotlib.pyplot as plt
+
+ x = np.logspace(-7, 3)
+ y = HavriliakNegami.correlation(x, 1, 0.8, 0.26)
+
+ plt.semilogx(x, y)
+ plt.show()
diff --git a/nmreval/distributions/intermolecular.py b/nmreval/distributions/intermolecular.py
new file mode 100644
index 0000000..8a20ce4
--- /dev/null
+++ b/nmreval/distributions/intermolecular.py
@@ -0,0 +1,99 @@
+import numpy as np
+from scipy.integrate import quad
+
+from .base import Distribution
+
+
+class FFHS(Distribution):
+ name = 'Intermolecular (FFHS)'
+ parameter = None
+
+ @staticmethod
+ def distribution(taus, tau0, *args):
+ # Distribution over tau, not log(tau), like in other cases
+ z = taus / tau0
+ return 54 * np.sqrt(z) / (162*z**3 + 18*z**2 - 4*z + 2) / np.pi / tau0
+
+ @staticmethod
+ def correlation(t, tau0, *args):
+ def integrand(u, tt, tau0):
+ 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])
+
+ return ret_val
+
+ @staticmethod
+ def specdens(omega, tau0, *args):
+ def integrand(u, o, tau0):
+ 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])
+
+ return ret_val
+
+ @staticmethod
+ def susceptibility(omega, tau0, *args):
+ def integrand_real(u, o, tau0):
+ return FFHS.distribution(u, tau0) / (1+o**2 * u**2)
+
+ def integrand_imag(u, o, tau0):
+ 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),
+ 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),
+ epsabs=1e-12, epsrel=1e-12)[0] for o in omega])
+
+ return ret_val
+
+
+# class Bessel(Distribution):
+# name = 'Intermolecular (Bessel)'
+# parameter = None
+#
+# @staticmethod
+# def distribution(taus, tau0, *args):
+# x = np.sqrt(tau0 / taus)
+# return special.jv(1.5, x)**2 / tau0 / 2
+#
+# @staticmethod
+# def correlation(t, tau0, *args):
+# def integrand(lx, tt):
+# x = np.exp(lx)
+# return Bessel.distribution(x, tau0)*np.exp(-tt/lx)
+#
+# logaxis = np.linspace(-20+np.log(tau0), 20+np.log(tau0), num=5001)
+#
+# ret_val = np.array([simps(integrand(logaxis, tt), logaxis) for tt in t])
+#
+# return ret_val
+#
+# @staticmethod
+# def susceptibility(omega, tau0, *args):
+# def integrand(lx, o):
+# x = np.exp(lx)
+# return Bessel.distribution(x, tau0) * 1/(1+1j*omega*x)
+#
+# logaxis = np.linspace(-20 + np.log(tau0), 20 + np.log(tau0), num=5001)
+#
+# ret_val = np.array([simps(integrand(logaxis, o), logaxis) for o in omega])
+#
+# return ret_val
+
+
+if __name__ == '__main__':
+ import matplotlib.pyplot as plt
+
+ x = np.logspace(-3, 3, num=61)
+
+ fig, ax = plt.subplots(2, 2)
+
+ ax[0, 0].loglog(x, FFHS.distribution(x, 1))
+ ax[0, 1].semilogx(x, FFHS.correlation(x, 1))
+ ax[1, 0].loglog(x, FFHS.specdens(x, 1))
+ ax[1, 1].loglog(x, FFHS.susceptibility(x, 1).real,
+ x, -FFHS.susceptibility(x, 1).imag)
+ fig.tight_layout()
+ plt.show()
diff --git a/nmreval/distributions/kww.py b/nmreval/distributions/kww.py
new file mode 100644
index 0000000..87ee238
--- /dev/null
+++ b/nmreval/distributions/kww.py
@@ -0,0 +1,84 @@
+import numpy as np
+from scipy.interpolate import interp1d
+from scipy.special import gamma
+
+from .base import Distribution
+from ..math.kww import kww_cos, kww_sin
+from ..utils.constants import Eu
+
+
+class KWW(Distribution):
+ name = 'KWW'
+ parameter = [r'\beta']
+ boounds = [(0, 1)]
+
+ @staticmethod
+ def distribution(taus, tau, *args):
+ b = args[0]
+ assert 0.1 <= b <= 0.9
+ b_supp = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
+ B_supp = [0.145, 0.197, 0.243, 0.285, 0.382, 0.306, 0.360, 0.435, 0.700]
+ C_supp = [0.89, 0.50, 0.35, 0.25, 0, 0.13, 0.22, 0.4015, 0.32]
+
+ B = interp1d(b_supp, B_supp)(b)
+ C = interp1d(b_supp, C_supp)(b)
+
+ tt = tau/taus
+
+ delta = b * abs(b-0.5) / (1-b)
+ if b > 0.5:
+ f = 1 + C * tt**delta
+ else:
+ f = 1 / (1 + C * tt**delta)
+ ret_val = tau * B * np.exp(-(1-b) * b**(b/(1-b)) / tt**(b/(1-b))) / tt**((1-0.5*b)/(1-b))
+
+ return ret_val * f / taus
+
+ @staticmethod
+ def correlation(t, tau, *args):
+ return np.exp(-(t/tau)**args[0])
+
+ @staticmethod
+ def susceptibility(omega, tau, *args):
+ return 1-omega*kww_sin(omega, tau, args[0]) + 1j*omega*kww_cos(omega, tau, args[0])
+
+ @staticmethod
+ def specdens(omega, tau, *args):
+ return kww_cos(omega, tau, args[0])
+
+ @staticmethod
+ def mean(*args):
+ tau, beta = args
+ return tau/beta * gamma(1 / beta)
+
+ @staticmethod
+ def logmean(*args):
+ tau, beta = args
+ return (1-1/beta) * Eu + np.log(tau)
+
+ @staticmethod
+ def max(*args):
+ tau, beta = args
+ return (1.7851 - 0.87052*beta - 0.028836*beta**2 + 0.11391*beta**3) * tau
+
+
+if __name__ == '__main__':
+ import matplotlib.pyplot as plt
+
+ x = np.logspace(-3, 3, num=61)
+
+ fig, ax = plt.subplots(2, 2)
+ ax[0, 0].set_title('Distribution')
+ ax[0, 1].set_title('Correlation func.')
+ ax[1, 0].set_title('Spectral density')
+ ax[1, 1].set_title('Susceptibility')
+
+ for g in [0.3, 0.5, 0.7, 0.9]:
+ ax[0, 0].semilogx(x, KWW.distribution(x, 1, g))
+ ax[0, 1].semilogx(x, KWW.correlation(x, 1, g))
+ ax[1, 0].loglog(x, KWW.specdens(x, 1, g))
+ a = ax[1, 1].loglog(x, KWW.susceptibility(x, 1, g).imag)
+ ax[1, 1].loglog(x, KWW.susceptibility(x, 1, g).real, '--', color=a[0].get_color())
+
+ fig.tight_layout()
+ plt.show()
diff --git a/nmreval/distributions/loggaussian.py b/nmreval/distributions/loggaussian.py
new file mode 100644
index 0000000..065eacc
--- /dev/null
+++ b/nmreval/distributions/loggaussian.py
@@ -0,0 +1,138 @@
+from multiprocessing import Pool, cpu_count
+from itertools import product
+
+import numpy as np
+try:
+ from scipy.integrate import simpson
+except ImportError:
+ from scipy.integrate import simps as simpson
+from scipy.integrate import quad
+
+from .base import Distribution
+
+
+__all__ = ['LogGaussian']
+
+
+class LogGaussian(Distribution):
+ name = 'Log-Gaussian'
+ parameter = [r'\sigma']
+ bounds = [(0, 10)]
+
+ @staticmethod
+ def distribution(tau, tau0, *args):
+ sigma = args[0]
+ return np.exp(-0.5*(np.log(tau/tau0)/sigma)**2)/np.sqrt(2*np.pi)/sigma
+
+ @staticmethod
+ def correlation(t, tau0, *args):
+ sigma = args[0]
+ _t = np.atleast_1d(t)
+ _tau = np.atleast_1d(tau0)
+
+ pool = Pool(processes=min(cpu_count(), 4))
+ integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_t, _tau)]
+
+ with np.errstate(divide='ignore'):
+ res = np.array(pool.map(_integrate_process_3, integration_ranges))
+ ret_val = res.reshape((_t.shape[0], _tau.shape[0]))
+
+ return ret_val.squeeze()
+
+ @staticmethod
+ def susceptibility(omega, tau0, *args):
+ sigma = args[0]
+ _omega = np.atleast_1d(omega)
+ _tau = np.atleast_1d(tau0)
+
+ pool = Pool(processes=min(cpu_count(), 4))
+ integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_omega, _tau)]
+
+ with np.errstate(divide='ignore'):
+ res_real = np.array(pool.map(_integrate_process_1, integration_ranges))
+ res_imag = np.array(pool.map(_integrate_process_2, integration_ranges))
+ ret_val = (res_real+1j*res_imag).reshape((_omega.shape[0], _tau.shape[0]))
+
+ return ret_val.squeeze()
+
+ @staticmethod
+ def specdens(omega, tau0, *args):
+ sigma = args[0]
+ _omega = np.atleast_1d(omega)
+ _tau = np.atleast_1d(tau0)
+
+ pool = Pool(processes=min(cpu_count(), 4))
+ integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_omega, _tau)]
+
+ with np.errstate(divide='ignore'):
+ res = np.array(pool.map(_integrate_process_1, integration_ranges))
+ ret_val = res.reshape((_omega.shape[0], _tau.shape[0]))
+
+ ret_val /= _omega[:, None]
+
+ return ret_val.squeeze()
+
+ def mean(*args):
+ return args[0]*np.exp(args[1]**2 / 2)
+
+
+def _integrate_process_1(args):
+ omega_i, tau_j, sigma = args
+ area = quad(_integrand_freq_imag_high, 0, 50, args=(omega_i, tau_j, sigma))[0]
+ area += quad(_integrand_freq_imag_low, -50, 0, args=(omega_i, tau_j, sigma))[0]
+
+ return area
+
+
+def _integrate_process_2(args):
+ omega_i, tau_j, sigma = args
+ 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_i, tau_j, sigma))[0]
+
+ return area
+
+
+def _integrate_process_3(args):
+ omega_i, tau_j, sigma = args
+ return quad(_integrand_time, -50, 50, args=(omega_i, tau_j, sigma))[0]
+
+
+def _integrand_time(u, t, tau, sigma):
+ uu = np.exp(u)
+ return LogGaussian.distribution(uu, tau, sigma) * np.exp(-t/uu)
+
+
+def _integrand_freq_imag_low(u, omega, tau, sigma):
+ uu = np.exp(u)
+ return LogGaussian.distribution(uu, tau, sigma) * omega * uu / (1 + (omega*uu)**2)
+
+
+def _integrand_freq_imag_high(u, omega, tau, sigma):
+ uu = np.exp(-u)
+ return LogGaussian.distribution(1/uu, tau, sigma) * omega * uu / (uu**2 + omega**2)
+
+
+def _integrand_freq_real_low(u, omega, tau, sigma):
+ uu = np.exp(u)
+ return LogGaussian.distribution(uu, tau, sigma) / (1 + (omega*uu)**2)
+
+
+def _integrand_freq_real_high(u, omega, tau, sigma):
+ uu = np.exp(-2*u)
+ return LogGaussian.distribution(np.exp(u), tau, sigma) * uu / (uu + omega**2)
+
+
+if __name__ == '__main__':
+ import matplotlib.pyplot as plt
+
+ x = np.logspace(-3, 3, num=61)
+
+ fig, ax = plt.subplots(2, 2)
+
+ for g in [1, 2, 5]:
+ ax[0, 0].loglog(x, LogGaussian.distribution(x, 1, g))
+ ax[0, 1].semilogx(x, LogGaussian.correlation(x, 1, g))
+ ax[1, 0].loglog(x, LogGaussian.specdens(1, x, g))
+ gr = ax[1, 1].loglog(x, -LogGaussian.susceptibility(x, 1, g).imag)
+ ax[1, 1].loglog(x, LogGaussian.susceptibility(x, 1, g).real, '--', color=gr[0].get_color())
+ plt.show()
diff --git a/nmreval/dsc/calibration.py b/nmreval/dsc/calibration.py
new file mode 100644
index 0000000..965605f
--- /dev/null
+++ b/nmreval/dsc/calibration.py
@@ -0,0 +1,290 @@
+__version__ = '0.1.3'
+
+import os
+import argparse
+import sys
+
+import numpy as np
+import matplotlib.pyplot as plt
+import scipy.interpolate
+from scipy.integrate import simps
+
+from nmreval.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.heating.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)
diff --git a/nmreval/dsc/dsc_calibration_fast_neu.py b/nmreval/dsc/dsc_calibration_fast_neu.py
new file mode 100644
index 0000000..0575213
--- /dev/null
+++ b/nmreval/dsc/dsc_calibration_fast_neu.py
@@ -0,0 +1,292 @@
+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)
diff --git a/nmreval/fit/__init__.py b/nmreval/fit/__init__.py
new file mode 100644
index 0000000..d311aa7
--- /dev/null
+++ b/nmreval/fit/__init__.py
@@ -0,0 +1,7 @@
+import numpy as np
+
+EPS = np.finfo(float).eps
+
+
+class FitException(BaseException):
+ pass
diff --git a/nmreval/fit/_meta.py b/nmreval/fit/_meta.py
new file mode 100644
index 0000000..42e1d3f
--- /dev/null
+++ b/nmreval/fit/_meta.py
@@ -0,0 +1,161 @@
+from typing import Union, Callable, Any
+import operator
+
+from inspect import signature, Parameter
+
+
+class ModelFactory:
+
+ @staticmethod
+ def create_from_list(funcs: list, left=None, func_order=None, param_len=None, left_cnt=None):
+ if func_order is None:
+ func_order = []
+
+ if param_len is None:
+ param_len = []
+
+ for func in funcs:
+ if not func['active']:
+ continue
+
+ func_order.append(func['cnt'])
+ param_len.append(len(func['func'].params))
+
+ if func['children']:
+ right, _, _ = ModelFactory.create_from_list(func['children'], left=func['func'], left_cnt=func['pos'],
+ func_order=func_order, param_len=param_len)
+ right_cnt = None
+ else:
+ right = func['func']
+ right_cnt = func['pos']
+
+ if left is None:
+ left = right
+ left_cnt = right_cnt
+ else:
+ left = MultiModel(left, right, func['op'],
+ left_idx=left_cnt, right_idx=right_cnt)
+
+ return left, func_order, param_len
+
+
+class MultiModel:
+ op_repr = {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}
+
+ def __init__(self, left: Any, right: Any, op: Union[str, Callable, int] = '+', left_idx=0, right_idx=1):
+ self._left = left
+ self._right = right
+
+ self._op = None
+
+ if isinstance(op, str):
+ self._op = MultiModel.str_op.get(op, None)
+ elif isinstance(op, int):
+ self._op = MultiModel.int_op.get(op, None)
+ elif isinstance(op, Callable):
+ self._op = op
+
+ if self._op is None:
+ raise ValueError('Invalid binary operator.')
+
+ self.name = '('
+ self.params = []
+ self.bounds = []
+ self._kwargs_right = {}
+ self._kwargs_left = {}
+ self._fun_kwargs = {}
+
+ # mapping kwargs to kwargs of underlying functions
+ self._ext_int_kw = {}
+
+ self._get_parameter(left, 'l', left_idx)
+ self._param_left = len(left.params)
+
+ try:
+ self.name += MultiModel.op_repr[self._op]
+ except KeyError:
+ self.name += str(op)
+
+ self._get_parameter(right, 'r', right_idx)
+ self.name += ')'
+
+ self._param_len = len(self.params)
+
+ def __str__(self):
+ return self.name
+
+ def _get_parameter(self, func, pos, idx):
+ kw_dict = {'l': self._kwargs_left, 'r': self._kwargs_right}[pos]
+
+ if isinstance(func, MultiModel):
+ strcnt = ''
+ kw_dict.update(func.fun_kwargs)
+ self._fun_kwargs.update({k: v for k, v in kw_dict.items()})
+ self._ext_int_kw.update({k: k for k in kw_dict.keys()})
+
+ else:
+ temp_dic = {k: v.default for k, v in signature(func.func).parameters.items()
+ if v.default is not Parameter.empty}
+
+ for k, v in temp_dic.items():
+ key_ = '%s_%d' % (k, idx)
+ kw_dict[key_] = v
+ self._fun_kwargs[key_] = v
+ self._ext_int_kw[key_] = k
+
+ strcnt = '(%d)' % idx
+
+ self.params += [pp+strcnt for pp in func.params]
+ self.name += func.name + strcnt
+
+ try:
+ self.bounds.extend(func.bounds)
+ except AttributeError:
+ self.bounds.extend([(None, None)]*len(func.params))
+
+ def _left_arguments(self, *args, **kwargs):
+ kw_left = {k_int: kwargs[k_ext] for k_ext, k_int in self._ext_int_kw.items() if k_ext in self._kwargs_left}
+ pl = args[:self._param_left]
+
+ return pl, kw_left
+
+ def _right_arguments(self, *args, **kwargs):
+ kw_right = {k_int: kwargs[k_ext] for k_ext, k_int in self._ext_int_kw.items() if k_ext in self._kwargs_right}
+ pr = args[self._param_left:self._param_len]
+
+ return pr, kw_right
+
+ def func(self, x, *args, **kwargs):
+ pl, kw_left = self._left_arguments(*args, **kwargs)
+ l_func = self._left.func(x, *pl, **kw_left)
+
+ pr, kw_right = self._right_arguments(*args, **kwargs)
+ r_func = self._right.func(x, *pr, **kw_right)
+
+ return self._op(l_func, r_func)
+
+ def left_func(self, x, *args, **kwargs):
+ return self._left.func(x, *args, **kwargs)
+
+ def right_func(self, x, *args, **kwargs):
+ return self._right.func(x, *args, **kwargs)
+
+ @property
+ def fun_kwargs(self):
+ return self._fun_kwargs
+
+ def subs(self, x, *args, **kwargs):
+ """ Iterator over all sub-functions (depth-first and left-to-right) """
+ pl, kw_left = self._left_arguments(*args, **kwargs)
+ if isinstance(self._left, MultiModel):
+ yield from self._left.subs(x, *pl, **kw_left)
+ else:
+ yield self._left.func(x, *pl, **kw_left)
+
+ pr, kw_right = self._right_arguments(*args, **kwargs)
+ if isinstance(self._right, MultiModel):
+ yield from self._right.subs(x, *pr, **kw_right)
+ else:
+ yield self._right.func(x, *pr, **kw_right)
diff --git a/nmreval/fit/data.py b/nmreval/fit/data.py
new file mode 100644
index 0000000..42b95bb
--- /dev/null
+++ b/nmreval/fit/data.py
@@ -0,0 +1,149 @@
+import numpy as np
+
+from .model import Model
+from .parameter import Parameters
+
+
+class Data(object):
+ def __init__(self, x, y, we=None, idx=None):
+ self.x = np.asarray(x)
+ self.y = np.asarray(y)
+ 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]}')
+
+ self.we = self._calc_weights(we)
+ self.idx = idx
+ self.model = None
+ self.minimizer = None
+ self.parameter = Parameters()
+ self.para_keys = None
+ self.fun_kwargs = {}
+
+ def __len__(self):
+ return self.y.shape[0]
+
+ def _calc_weights(self, we):
+ if we is None:
+ return 1.
+
+ 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))
+ else:
+ we_func = lambda yy: 1. / np.abs(yy)
+
+ if np.iscomplexobj(self.y):
+ weights = we_func(np.r_[self.y.real, self.y.imag])
+ else:
+ weights = we_func(self.y)
+
+ else:
+ we = 1. / np.asarray(we)
+ if np.iscomplexobj(self.y):
+ if np.iscomplexobj(we):
+ weights = np.r_[we.real, we.imag]
+ else:
+ weights = np.tile(we)
+ else:
+ weights = we
+
+ weights[weights == np.inf] = np.finfo(float).max
+
+ return weights
+
+ def set_model(self, func, *args, **kwargs):
+ if isinstance(func, Model):
+ self.model = func
+ else:
+ self.model = Model(func, *args)
+
+ self.fun_kwargs.update(self.model.fun_kwargs)
+ self.fun_kwargs.update(kwargs)
+ self.parameter = Parameters()
+
+ return self.model
+
+ def get_model(self):
+ return self.model
+
+ def set_parameter(self, parameter, var=None, ub=None, lb=None,
+ default_bounds=False, fun_kwargs=None):
+ """
+ Creates parameter for this data.
+ If no Model is available, it falls back to the model
+ :param parameter: list of parameters
+ :param var: list of boolean or boolean; False fixes parameter at given list index.
+ Single value is broadcast to all parameter
+ :param ub: list of upper boundaries or float; Single value is broadcast to all parameter.
+ None means no bound.
+ :param lb: list of lower bounds or float; Single value is broadcast to all parameter.
+ None means no bound.
+ :param default_bounds: bool; If True, uses default_bounds of a Model if lb or ub are None
+ :param fun_kwargs: dict; key-word arguments for model,
+ :return: Parameters
+ """
+ model = self.model
+ if model is None:
+ # Data has no unique
+ if self.minimizer is None:
+ model = None
+ else:
+ model = self.minimizer.fit_model
+ self.fun_kwargs.update(model.fun_kwargs)
+
+ if model is None:
+ raise ValueError('No model found, please set model before parameters')
+
+ if default_bounds:
+ if lb is None:
+ lb = model.lb
+ if ub is None:
+ ub = model.ub
+
+ self.para_keys = self.parameter.add_parameter(parameter, var=var, lb=lb, ub=ub)
+
+ if fun_kwargs is not None:
+ self.fun_kwargs.update(fun_kwargs)
+
+ return self.para_keys
+
+ def get_parameter(self, scaled: bool = False):
+ """
+ Returns list of parameters if set.
+ :param scaled: If True, returns scaled values, otherwise unscaled
+ :return: parameter array
+ """
+ if self.parameter is None:
+ return
+
+ if scaled:
+ return [p.scaled_value for p in self.minimizer.parameters[self.parameter]]
+ else:
+ return [p.value for p in self.minimizer.parameters[self.parameter]]
+
+ def cost(self, p):
+ """
+ Cost function :math:`y-f(p, x)`
+ :param p: list of parameters
+ :return:
+ """
+ y_pred = self.model.func(p, self.x, **self.fun_kwargs)
+ resid = y_pred - self.y
+
+ if np.iscomplexobj(resid):
+ resid = np.r_[resid.real, resid.imag]
+
+ resid *= self.we
+
+ return resid
+
+ def func(self, p, x):
+ """
+ Function :math:`f(p, x)`
+ :param x:
+ :param p: list of parameters
+ :return:
+ """
+ return self.model.func(p, x, **self.fun_kwargs)
diff --git a/nmreval/fit/minimizer.py b/nmreval/fit/minimizer.py
new file mode 100644
index 0000000..4d5dc05
--- /dev/null
+++ b/nmreval/fit/minimizer.py
@@ -0,0 +1,489 @@
+import warnings
+from itertools import product
+
+import numpy as np
+import scipy.linalg as la
+from scipy import optimize
+import scipy.odr as odr
+
+from .data import Data
+from .model import Model
+from . import EPS
+from .parameter import Parameters
+from .result import FitResultCreator
+
+__all__ = ['FitRoutine', 'FitAbortException']
+
+
+class FitAbortException(Exception):
+ pass
+
+
+class FitRoutine(object):
+ def __init__(self, mode='lsq'):
+ self._fitmethod = mode
+ self.data = []
+ self.fit_model = None
+ self._no_own_model = []
+ self.parameter = Parameters()
+ self.result = []
+ self.linked = []
+ self._abort = False
+
+ def add_data(self, x, y=None, we=None, idx=None):
+ if isinstance(x, Data):
+ d = x
+
+ else:
+ _x = np.asarray(x)
+ if _x.ndim == 2 and _x.shape[1] == 2:
+ d = Data(*x, we=we)
+
+ elif y is None:
+ raise ValueError('First argument must be of type Data, 2d-array, '
+ 'or second argument must be given')
+
+ else:
+ d = Data(x, y, we=we, idx=idx)
+
+ if idx is not None:
+ d.idx = idx
+ d.minimizer = self
+ self.data.append(d)
+ self.result.append(None)
+
+ return d
+
+ def remove_data(self, data):
+ try:
+ idx = self.data.index(data)
+ _ = self.data.pop(idx)
+ self.result.pop(idx)
+
+ except ValueError:
+ raise IndexError('Data {} not found'.format(data))
+
+ def set_model(self, func, *args, idx=None, **kwargs):
+ if isinstance(func, Model):
+ model = func
+ else:
+ model = Model(func, *args, **kwargs)
+
+ if idx is not None:
+ for i, data in enumerate(self.data):
+ if data.idx == idx:
+ data.set_model(model)
+ else:
+ self.fit_model = model
+
+ return self.fit_model
+
+ def set_link_parameter(self, parameter: tuple, replacement: tuple):
+ if isinstance(replacement[0], Model):
+ if replacement[1] not in replacement[0].global_parameter:
+ raise KeyError(f'Parameter at pos {replacement[1]} of '
+ f'model {str(replacement[0])} is not global')
+
+ if isinstance(parameter[0], Model):
+ warnings.warn(f'Replaced parameter at pos {parameter[1]} in {str(parameter[0])} '
+ f'becomes global with linkage.')
+
+ self.linked.append((*parameter, *replacement))
+
+ def prepare_links(self):
+ self._no_own_model = []
+ self.parameter = Parameters()
+ _found_models = {}
+ linked_sender = {}
+
+ for v in self.data:
+ linked_sender[v] = set()
+ self.parameter.update(v.parameter.copy())
+
+ # set temporaray model
+ if v.model is None:
+ v.model = self.fit_model
+ self._no_own_model.append(v)
+
+ # register model
+ if v.model not in _found_models:
+ _found_models[v.model] = []
+ m_param = v.model.parameter.copy()
+ self.parameter.update(m_param)
+
+ _found_models[v.model].append(v)
+
+ if v.model not in linked_sender:
+ linked_sender[v.model] = set()
+
+ linked_parameter = {}
+ for par, par_parm, repl, repl_par in self.linked:
+ if isinstance(par, Data):
+ if isinstance(repl, Data):
+ 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:
+ if isinstance(repl, Data):
+ 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)
+ linked_sender[par].add(repl)
+
+ for mm, m_data in _found_models.items():
+ if mm.global_parameter:
+ for dd in m_data:
+ linked_sender[mm].add(dd)
+ linked_sender[dd].add(mm)
+
+ coupled_data = []
+ visited_data = []
+ for s in linked_sender.keys():
+ if s in visited_data:
+ continue
+ sub_graph = []
+ self.find_paths(s, linked_sender, sub_graph, visited_data)
+ if sub_graph:
+ coupled_data.append(sub_graph)
+
+ return coupled_data, linked_parameter
+
+ def find_paths(self, start, graph, coupled_nodes=None, visited_nodes=None):
+ visited_nodes.append(start)
+ if isinstance(start, Data):
+ coupled_nodes.append(start)
+
+ for neighbor in graph[start]:
+ if neighbor in visited_nodes:
+ continue
+
+ self.find_paths(neighbor, graph, coupled_nodes, visited_nodes)
+
+ def abort(self):
+ print('ABORT ???')
+ self._abort = True
+
+ def run(self, mode='lsq'):
+ self._abort = False
+ self.parameter = Parameters()
+
+ fit_groups, linked_parameter = self.prepare_links()
+
+ for data_groups in fit_groups:
+ if len(data_groups) == 1 and not self.linked:
+ data = data_groups[0]
+ # get variable parameter for fitter
+ p0_k, lb_k, ub_k, var_pars_k = self._prep_data(data)
+
+ if mode == 'lsq':
+ self._least_squares_single(data, p0_k, lb_k, ub_k, var_pars_k)
+
+ elif mode == 'nm':
+ self._nm_single(data, p0_k, lb_k, ub_k, var_pars_k)
+
+ elif mode == 'odr':
+ # ODR takes no bounds
+ self._odr_single(data, p0_k, var_pars_k)
+
+ else:
+ data_pars, p0, lb, ub, var_pars = self._prep_global(data_groups, linked_parameter)
+
+ if mode == 'lsq':
+ self._least_squares_global(data_groups, p0, lb, ub, var_pars, data_pars)
+
+ elif mode == 'nm':
+ self._nm_global(data_groups, p0, lb, ub, var_pars, data_pars)
+
+ elif mode == 'odr':
+ self._odr_global(data_groups, p0, var_pars, data_pars)
+
+ self.unprep_run()
+
+ return self.result
+
+ def _prep_data(self, data):
+ if data.get_model() is None:
+ data._model = self.fit_model
+ self._no_own_model.append(data)
+
+ return self._prep_parameter(data.parameter)
+
+ @staticmethod
+ def _prep_parameter(parameter):
+ vals = []
+ var_pars = []
+ for p_k, v_k in parameter.items():
+ if v_k.var:
+ vals.append([v_k.scaled_value, v_k.lb / v_k.scale, v_k.ub / v_k.scale])
+ var_pars.append(p_k)
+
+ pp, lb, ub = zip(*vals)
+
+ return pp, lb, ub, var_pars
+
+ def _prep_global(self, data_group, linked):
+ p0 = []
+ lb = []
+ ub = []
+ var = []
+ data_pars = []
+
+ # loopyloop over data that belong to one fit (linked or global)
+ 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?
+ if i in data.model.global_parameter:
+ p_k_used = data.model.global_parameter[i]
+ v_k_used = self.parameter[p_k_used]
+
+ # links trump global parameter
+ if p_k_used in linked:
+ p_k_used = linked[p_k_used]
+ v_k_used = self.parameter[p_k_used]
+
+ actual_pars.append(p_k_used)
+ # parameter is variable and was not found before as shared parameter
+ if v_k_used.var and p_k_used not in var:
+ p0.append(v_k_used.scaled_value)
+ lb.append(v_k_used.lb / v_k_used.scale)
+ ub.append(v_k_used.ub / v_k_used.scale)
+ var.append(p_k_used)
+
+ data_pars.append(actual_pars)
+
+ return data_pars, p0, lb, ub, var
+
+ def unprep_run(self):
+ for d in self._no_own_model:
+ d._model = None
+
+ self._no_own_model = []
+
+ # 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 cost(p):
+ if self._abort:
+ raise FitAbortException(f'Fit aborted by user')
+
+ return self.__cost_scipy(p, data, var, data.para_keys)
+
+ with np.errstate(all='ignore'):
+ res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=1000 * len(p0))
+
+ 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,
+ err=err, corr=corr, partial_corr=partial_corr)
+
+ def _least_squares_global(self, data, p0, lb, ub, var, data_pars):
+ def cost(p):
+ if self._abort:
+ raise FitAbortException(f'Fit aborted by user')
+ return self.__cost_scipy_glob(p, data, var, data_pars)
+
+ with np.errstate(all='ignore'):
+ res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=1000 * len(p0))
+
+ 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):
+ self.make_results(v, res.x, var, var_pars_k, res.jac.shape,
+ err=err, corr=corr, partial_corr=partial_corr)
+
+ def _nm_single(self, data, p0, lb, ub, var):
+ def cost(p):
+ if self._abort:
+ raise FitAbortException(f'Fit aborted by user')
+ return (self.__cost_scipy(p, data, var, data.para_keys)**2).sum()
+
+ with np.errstate(all='ignore'):
+ res = optimize.minimize(cost, p0, bounds=[(b1, b2) for (b1, b2) in zip(lb, ub)],
+ method='Nelder-Mead', options={'maxiter': 1000 * 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 cost(p):
+ if self._abort:
+ raise FitAbortException(f'Fit aborted by user')
+ return (self.__cost_scipy_glob(p, data, var, data_pars)**2).sum()
+
+ with np.errstate(all='ignore'):
+ res = optimize.minimize(cost, p0, bounds=[(b1, b2) for (b1, b2) in zip(lb, ub)],
+ method='Nelder-Mead', options={'maxiter': 1000 * len(p0)})
+
+ 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)))
+
+ def _odr_single(self, data, p0, var_pars):
+ odr_data = odr.Data(data.x, data.y)
+
+ def func(p, _):
+ if self._abort:
+ raise FitAbortException(f'Fit aborted by user')
+ return self.__cost_odr(p, data, var_pars, data.para_keys)
+
+ odr_model = odr.Model(func)
+
+ o = odr.ODR(odr_data, odr_model, beta0=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
+
+ 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 func(p, _):
+ if self._abort:
+ raise FitAbortException(f'Fit aborted by user')
+ return self.__cost_odr_glob(p, data, var, data_pars)
+
+ x = []
+ y = []
+ for d in data:
+ x = np.r_[x, d.x]
+ y = np.r_[y, d.y]
+
+ odr_data = odr.Data(x, y)
+ odr_model = odr.Model(func)
+
+ o = odr.ODR(odr_data, odr_model, beta0=p0, ifixb=var)
+ 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):
+ self.make_results(v, res.beta, var, var_pars_k, (sum(len(d) for d in data), len(p0)),
+ err=res.sd_beta, corr=corr, partial_corr=partial_corr)
+
+ def make_results(self, data, p, var_pars, used_pars, shape,
+ err=None, corr=None, partial_corr=None):
+
+ if err is None:
+ err = [0] * len(p)
+
+ # update parameter values
+ for keys, p_value, err_value in zip(var_pars, p, err):
+ self.parameter[keys].scaled_value = p_value
+ self.parameter[keys].scaled_error = err_value
+
+ combinations = list(product(var_pars, var_pars))
+ actual_parameters = []
+ corr_idx = []
+
+ for i, p_i in enumerate(used_pars):
+ actual_parameters.append(self.parameter[p_i])
+ for j, p_j in enumerate(used_pars):
+ try:
+ # find the position of the parameter combinations
+ corr_idx.append(combinations.index((p_i, p_j)))
+ except ValueError:
+ pass
+
+ # reshape the correlation matrices
+ if corr is None:
+ actual_corr = None
+ actual_pcorr = None
+ else:
+ indexes = np.unravel_index(corr_idx, corr.shape)
+ dim_one = int(np.sqrt(len(corr_idx)))
+ actual_corr = corr[indexes].reshape((dim_one, -1))
+ actual_pcorr = partial_corr[indexes].reshape((dim_one, -1))
+
+ idx = self.data.index(data)
+ model = data.get_model()
+
+ self.result[idx] = FitResultCreator.make_with_model(model, data.x, data.y,
+ actual_parameters, data.fun_kwargs, data.idx,
+ *shape, corr=actual_corr, pcorr=actual_pcorr)
+
+ return self.result
+
+ @staticmethod
+ def _calc_error(jac, chi, nobs, nvars):
+ # copy of scipy.curve_fit to calculate covariance
+ # noinspection PyTupleAssignmentBalance
+ _, s, vt = la.svd(jac, full_matrices=False)
+ threshold = EPS * max(jac.shape) * s[0]
+ s = s[s > threshold]
+ vt = vt[:s.size]
+ pcov = np.dot(vt.T / s**2, vt) * chi / (nobs - nvars)
+
+ if pcov is None:
+ _err = np.zeros(nvars)
+ corr = np.zeros((nvars, nvars))
+ else:
+ _err = np.sqrt(np.diag(pcov))
+ corr = pcov / (_err[:, None] * _err[None, :])
+
+ corr = corr.astype(np.float64)
+ 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
+
+ return _err, corr, partial_corr
diff --git a/nmreval/fit/model.py b/nmreval/fit/model.py
new file mode 100644
index 0000000..1df8fbf
--- /dev/null
+++ b/nmreval/fit/model.py
@@ -0,0 +1,153 @@
+import inspect
+from typing import Sized
+
+from numpy import inf
+
+from ._meta import MultiModel
+from .parameter import Parameters
+
+
+class Model(object):
+ def __init__(self, model, *args, **kwargs):
+ self.idx = kwargs.pop('idx', None)
+
+ self.is_multi = False
+ if inspect.isclass(model) or isinstance(model, MultiModel):
+ self._init_from_class(model)
+ elif inspect.isfunction(model):
+ self._init_from_function(model)
+ else:
+ raise ValueError(f'No idea how to use datatype {model}.')
+
+ self.lb = [i if i is not None else -inf for i in self.lb]
+ self.ub = [i if i is not None else inf for i in self.ub]
+
+ self.parameter = Parameters()
+ self.global_parameter = {}
+ self.is_complex = None
+ self._complex_part = False
+
+ if 'complex' in kwargs:
+ self.set_complex(kwargs.pop('complex'))
+
+ if args:
+ self.fun_args = args
+ else:
+ self.fun_args = []
+
+ self.fun_kwargs.update(kwargs)
+
+ def _init_from_function(self, func):
+ self.name = str(func)
+ self._int_func = func
+ self._int_iter = func
+
+ self.params = []
+ self.fun_kwargs = {}
+ for k, v in inspect.signature(func).parameters.items():
+ if v.default != inspect.Parameter.empty:
+ self.fun_kwargs[k] = v.default
+ else:
+ self.params.append(k)
+ # first parameter is x
+ self.params.pop(0)
+
+ self.lb = [None] * len(self.params)
+ self.ub = [None] * len(self.params)
+
+ def _init_from_class(self, model):
+ self.name = model.name
+ self.params = model.params
+ self._int_func = model.func
+ if hasattr(model, 'subs'):
+ self._int_iter = model.subs
+ self.is_multi = True
+ else:
+ self._int_iter = model.func
+
+
+ try:
+ self.lb, self.ub = list(zip(*model.bounds))
+ except AttributeError:
+ self.lb = [None] * len(self.params)
+ self.ub = [None] * len(self.params)
+
+ if isinstance(model, MultiModel):
+ self.fun_kwargs = model.fun_kwargs
+ else:
+ self.fun_kwargs = {k: v.default for k, v in inspect.signature(model.func).parameters.items()
+ if v.default is not inspect.Parameter.empty}
+
+ def set_complex(self, state):
+ if state not in [None, 'complex', 'real', 'imag']:
+ raise ValueError('"complex" argument is not None, "complex", "real", "imag"')
+
+ self.is_complex = state
+ if state in ['real', 'imag']:
+ self._complex_part = state
+ else:
+ self._complex_part = False
+
+ def set_global_parameter(self, idx, p, var=None, lb=None, ub=None, default_bounds=False):
+ if idx is None:
+ self.parameter = Parameters()
+ self.global_parameter = {}
+ return
+
+ if default_bounds:
+ if lb is None:
+ lb = [self.lb[i] for i in idx]
+ if ub is None:
+ ub = [self.lb[i] for i in idx]
+
+ gp = self.parameter.add_parameter(p, var=var, lb=lb, ub=ub)
+ for k, v in zip(idx, gp):
+ self.global_parameter[k] = v
+
+ return gp
+
+ @staticmethod
+ def _prep(param_len, val):
+ if isinstance(val, Sized):
+ if len(val) != param_len:
+ raise ValueError('Length mismatch for global parameter')
+ else:
+ return [val] * param_len
+
+ def __str__(self):
+ msg = 'Model: ' + self.name
+ return msg
+
+ def __len__(self):
+ return len(self.params)
+
+ def func(self, p, x, **kwargs):
+ if not kwargs:
+ kwargs = self.fun_kwargs
+
+ f = self._int_func(x, *p, *self.fun_args, **kwargs)
+
+ if self._complex_part:
+ if self._complex_part == 'real':
+ return f.real
+ else:
+ return f.imag
+
+ return f
+
+ def sub(self, p, x, **kwargs):
+ if not self.is_multi:
+ return [self.func(p, x, **kwargs)]
+
+ else:
+ print('multi model')
+ if not kwargs:
+ kwargs = self.fun_kwargs
+
+ if self._complex_part:
+ if self._complex_part == 'real':
+ return [f.real for f in self._int_iter(x, *p, *self.fun_args, **kwargs)]
+ else:
+ return [f.imag for f in self._int_iter(x, *p, *self.fun_args, **kwargs)]
+
+ return list(self._int_iter(x, *p, *self.fun_args, **kwargs))
diff --git a/nmreval/fit/parameter.py b/nmreval/fit/parameter.py
new file mode 100644
index 0000000..c23cff1
--- /dev/null
+++ b/nmreval/fit/parameter.py
@@ -0,0 +1,155 @@
+from numbers import Number
+from itertools import count
+
+import numpy as np
+
+
+class Parameters(dict):
+ count = count()
+
+ def __str__(self):
+ return 'Parameters:\n' + '\n'.join([str(k)+': '+str(v) for k, v in self.items()])
+
+ def __getitem__(self, item):
+ if isinstance(item, (list, tuple, np.ndarray)):
+ values = []
+ for item_i in item:
+ values.append(super().__getitem__(item_i))
+ return values
+ else:
+ return super().__getitem__(item)
+
+ @staticmethod
+ def _prep_bounds(val, p_len: int) -> list:
+ # helper function to ensure that bounds and variable are of parameter shape
+ if isinstance(val, (Number, bool)) or val is None:
+ return [val] * p_len
+
+ elif len(val) == p_len:
+ 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():
+ p[k] = Parameter(v.value, var=v.var, lb=v.lb, ub=v.ub)
+
+ if len(p) == 0:
+ 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):
+ return {k: v.get_state() for k, v in self.items()}
+
+
+class 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):
+ self.lb = lb if lb is not None else -np.inf
+ self.ub = ub if ub is not None else np.inf
+
+ if self.lb <= value <= self.ub:
+ self.value = value
+ else:
+ raise ValueError('Value of parameter is outside bounds')
+
+ self.init_val = value
+
+ with np.errstate(divide='ignore'):
+ # throws RuntimeWarning for zeros
+ self.scale = 10**(np.floor(np.log10(np.abs(self.value))))
+
+ if self.scale == 0:
+ self.scale = 1.
+
+ self.var = bool(var) if var is not None else True
+ self.error = None if self.var is False else 0.0
+ self.name = ''
+ self.function = ''
+
+ def __str__(self):
+ start = ''
+ if self.name:
+ if self.function:
+ start = f'{self.name} ({self.function}): '
+ else:
+ start = self.name + ': '
+
+ if self.var:
+ return start + f'{self.value:.4g} +/- {self.error:.4g}, init={self.init_val}'
+ else:
+ return start + f'{self.value:} (fixed)'
+
+ @property
+ def scaled_value(self):
+ return self.value / self.scale
+
+ @scaled_value.setter
+ def scaled_value(self, value):
+ self.value = value * self.scale
+
+ @property
+ def scaled_error(self):
+ if self.error is None:
+ return self.error
+ else:
+ return self.error / self.scale
+
+ @scaled_error.setter
+ def scaled_error(self, value):
+ self.error = value * self.scale
+
+ def get_state(self):
+
+ return {slot: getattr(self, slot) for slot in self.__slots__}
+
+ @staticmethod
+ def set_state(state: dict):
+ par = Parameter(state.pop('value'))
+ for k, v in state.items():
+ setattr(par, k, v)
+
+ return par
+
+ @property
+ def full_name(self):
+ name = self.name
+ if self.function:
+ name += ' (' + self.function + ')'
+
+ return name
diff --git a/nmreval/fit/result.py b/nmreval/fit/result.py
new file mode 100644
index 0000000..97a8a5f
--- /dev/null
+++ b/nmreval/fit/result.py
@@ -0,0 +1,339 @@
+import pathlib
+import re
+from collections import OrderedDict
+
+import numpy as np
+from scipy.stats import f as fdist
+from scipy.interpolate import interp1d
+
+from ..data.points import Points
+from .parameter import Parameter
+from ..data.signals import Signal
+from ..utils.text import convert
+
+
+class FitResultCreator:
+ @staticmethod
+ def make_from_session(x_orig, y_orig, idx, kwargs) -> (dict, list):
+ params = OrderedDict()
+
+ for key, pbest, err in zip(kwargs['pnames'], kwargs['parameter'], kwargs['error']):
+ params[key] = Parameter(pbest)
+ params[key].error = err
+
+ _x = kwargs['x']
+ _y = kwargs['y']
+
+ if len(_x) != len(x_orig):
+ f = interp1d(_x, _y)
+ rng = (x_orig >= np.min(_x)) * (x_orig <= np.max(_x))
+ resid = f(x_orig[rng]) - y_orig[rng]
+ else:
+ resid = kwargs['y'] - y_orig
+
+ stats = FitResultCreator.calc_statistics(resid, _y)
+
+ return FitResult(kwargs['x'], kwargs['y'], x_orig, y_orig, params, dict(kwargs['choice']), resid, 0, 0,
+ kwargs['name'], stats, idx), []
+
+ @staticmethod
+ def make_with_model(model, x_orig, y_orig, p, fun_kwargs, idx, nobs, nvar, corr, pcorr) -> (dict, list):
+ if np.all(x_orig > 0) and (np.max(x_orig) > 100 * np.min(x_orig)):
+ islog = True
+ else:
+ islog = False
+
+ if len(x_orig) < 51:
+ if islog:
+ _x = np.logspace(np.log10(np.min(x_orig)), np.log10(np.max(x_orig)), num=10*x_orig.size-9)
+ else:
+ _x = np.linspace(np.min(x_orig), np.max(x_orig), num=10*x_orig.size-9)
+ else:
+ _x = x_orig
+
+ try:
+ pnames = model.pnames
+ except AttributeError:
+ pnames = model.params
+
+ parameters = OrderedDict([(k, v) for k, v in zip(pnames, p)])
+ p_final = [p.value for p in parameters.values()]
+
+ part_functions = []
+
+ if model.is_multi:
+ for sub_y in model.sub(p_final, _x, **fun_kwargs):
+ if np.iscomplexobj(sub_y):
+ part_functions.append(Signal(_x, sub_y))
+ else:
+ part_functions.append(Points(_x, sub_y))
+
+ _y = model.func(p_final, _x, **fun_kwargs)
+ resid = model.func(p_final, x_orig, **fun_kwargs) - y_orig
+
+ stats = FitResultCreator.calc_statistics(_y, resid, nobs, nvar)
+ varied = [p.var for p in parameters.values()]
+
+ if corr is None:
+ correlation = np.eye(len(p_final), len(p_final))
+ partial_correlation = np.eye(len(p_final), len(p_final))
+
+ else:
+ if len(corr) < len(p_final):
+ correlation = np.eye(len(p_final), len(p_final))
+ partial_correlation = np.eye(len(p_final), len(p_final))
+ # probably some fixed variables
+ j = 0
+ for i in range(len(corr)):
+ while not varied[j]:
+ j += 1
+ correlation[j, varied] = corr[i]
+ partial_correlation[j, varied] = pcorr[i]
+ j += 1
+ else:
+ correlation = corr
+ partial_correlation = pcorr
+
+ return FitResult(_x, _y, x_orig, y_orig, parameters, fun_kwargs, resid, nobs, nvar, model.name, stats,
+ idx=idx, corr=correlation, pcorr=partial_correlation, islog=islog, iscomplex=model.is_complex), part_functions
+
+ @staticmethod
+ def calc_statistics(y, residual, nobs=None, nvar=None):
+ chi = (residual**2).sum()
+ try:
+ r = 1 - chi/((y-np.mean(y))**2).sum()
+ except RuntimeWarning:
+ r = -9999
+
+ if nobs is None:
+ nobs = 1
+
+ if nvar is None:
+ nvar = 0
+
+ dof = nobs - nvar
+ loglikehood = nobs * np.log(chi / nobs)
+
+ stats = {
+ 'chi^2': chi,
+ 'R^2': r,
+ 'AIC': loglikehood + 2 * nvar,
+ 'BIC': loglikehood + np.log(nobs) * nvar
+ }
+
+ if dof != 0:
+ stats['adj. R^2'] = 1 - (nobs-1)/dof * (1-r)
+ stats['red. chi^2'] = chi / dof if dof != 0 else 0
+
+ if dof != 1:
+ stats['AICc'] = stats['AIC'] + 2*(nvar+1)*nvar / (dof-1)
+
+ return stats
+
+
+class FitResult(Points):
+
+ def __init__(self, x, y, x_data, y_data, params, fun_kwargs, resid, nobs, nvar, name, stats,
+ idx=None, corr=None, pcorr=None, islog=False, iscomplex=None,
+ **kwargs):
+
+ self.parameter, name = self._prepare_names(params, name)
+
+ super().__init__(x=x, y=y, name=name, **kwargs)
+
+ self.residual = resid
+ self.idx = idx
+ self.statistics = stats
+ self.nobs = nobs
+ self.nvar = nvar
+ self.fun_kwargs = fun_kwargs
+ self.correlation = corr
+ self.partial_correlation = pcorr
+ self.islog = islog
+ self.iscomplex = iscomplex
+ self.x_data = x_data
+ self.y_data = y_data
+ self._model_name = name
+
+ @staticmethod
+ def _prepare_names(parameter: dict, modelname: str):
+ pattern = re.compile(r'(\\?\w[\\\w .-]*(?:_{[\w\\ .-]})?)(\(\d+\))')
+
+ split_funcs = {g2: g1 for (g1, g2) in pattern.findall(modelname)}
+
+ parameter_dic = {}
+ for pname, pvalue in parameter.items():
+ nice_name = pname
+ nice_func = ''
+ m = pattern.match(pname)
+ if m:
+ func_number = m.group(2)
+ nice_name = m.group(1)
+ if func_number in split_funcs:
+ nice_func = split_funcs[func_number]
+
+ pvalue.name = nice_name
+ pvalue.function = nice_func
+ parameter_dic[pname] = pvalue
+
+ modelname = re.sub(r'\(\d+\)', '', modelname)
+ if modelname[0] == '(' and modelname[-1] == ')':
+ modelname = modelname[1:-1]
+
+ return parameter_dic, modelname
+
+ @property
+ def model_name(self):
+ return self._model_name
+
+ def __len__(self):
+ return len(self.parameter)
+
+ def __repr__(self):
+ try:
+ return 'Fit: ' + self.name
+ except AttributeError:
+ return 'FitObject'
+
+ @property
+ def p_final(self):
+ return [pp.value for pp in self.parameter.values()]
+
+ @property
+ def dof(self):
+ return self.nobs-self.nvar
+
+ def pprint(self, statistics=True, correlations=True):
+ print('Fit result:')
+ print(' model :', self.name)
+ print(' #data :', self.nobs)
+ print(' #var :', self.nvar)
+ print('\nParameter')
+ print(self._parameter_string())
+ if statistics:
+ print('Statistics')
+ for k, v in self.statistics.items():
+ print(f' {k} : {v:.4f}')
+
+ if correlations and self.correlation is not None:
+ print('\nCorrelation (partial corr.)')
+ print(self._correlation_string())
+ print()
+
+ def _parameter_string(self):
+ ret_val = ''
+
+ for pval in self.parameter.values():
+ ret_val += convert(str(pval), old='tex', new='str') + '\n'
+
+ if self.fun_kwargs:
+ for k, v in self.fun_kwargs.items():
+ ret_val += f' {k}: {v}\n'
+
+ return ret_val
+
+ def _correlation_string(self):
+ ret_val = ''
+ 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'),
+ convert(p_j, old='tex', new='str'),
+ corr_ij, pcorr_ij)
+ return ret_val
+
+ def correlation_list(self, limit=0.1):
+ correlations = []
+
+ if self.correlation is not None:
+ pnames = list(self.parameter.keys())
+
+ corr = np.triu(self.correlation, k=1)
+
+ for i, j in zip(*np.unravel_index(np.argsort(np.abs(corr), axis=None)[::-1],
+ self.correlation.shape)):
+ if i == j:
+ continue
+
+ if abs(corr[i, j]) < limit:
+ break
+
+ correlations.append((pnames[i], pnames[j], self.correlation[i, j], self.partial_correlation[i, j]))
+
+ return correlations
+
+ def savetxt(self, fname, err=True):
+ header = self.name + '\n'
+ for pval in self.parameter.values():
+ header += '{}\t{}\t{}\n'.format(convert(pval.name, old='tex', new='str'), pval.value, pval.error)
+ if self.fun_kwargs:
+ for k, v in self.fun_kwargs.items():
+ header += '{}\t{}\n'.format(convert(k, old='tex', new='str'),
+ convert(str(v), old='tex', new='str'))
+
+ if self.iscomplex == 'complex':
+ np.savetxt(fname, np.c_[self.x, self.y.real, self.y.imag], header=header)
+ else:
+ np.savetxt(fname, np.c_[self.x, self.y], header=header)
+
+ def save_parameter(self, fname: str, label: str = None, overwrite: bool = False):
+ path = pathlib.Path(fname)
+ if not path.is_file():
+ overwrite = True
+
+ writemode = 'w' if overwrite else 'a'
+
+ if label is None:
+ label = self.value
+
+ with path.open(writemode) as f:
+ if overwrite or not path.exists():
+ f.write('# label(1)\t')
+ for i, pname in enumerate(self.parameter.keys()):
+ 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('\n')
+
+ f.write(str(label).replace(' ', '') + '\t')
+ for p in self.parameter.values():
+ if p.error is not None:
+ err = p.error
+ else:
+ err = 0.
+ f.write(f'{p.value:.8e}\t{err:.8e}\t')
+
+ if self.fun_kwargs:
+ f.write('# ')
+ 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('\n')
+
+ def f_test(self, chi2: float, dof: float):
+ if 'red. chi^2' not in self.statistics or dof == self.dof:
+ f_value = 1e318
+ else:
+ 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)
+
+ def get_state(self):
+ state = super().get_state()
+
+ for attr in ['idx', 'fun_kwargs', 'nobs', 'nvar',
+ 'islog', 'iscomplex', 'x_data', 'y_data']:
+ state[attr] = getattr(self, attr)
+
+ state['name'] = self._model_name
+ state['corr'] = self.correlation
+ state['pcorr'] = self.partial_correlation
+ state['stats'] = self.statistics
+ state['resid'] = self.residual
+ state['params'] = {k: v.get_state() for k, v in self.parameter.items()}
+
+ state['mode'] = 'fit'
+
+ return state
+
+ @staticmethod
+ def set_state(state, **kwargs):
+ state['params'] = {k: Parameter.set_state(v) for k, v in state.pop('params').items()}
+ data = FitResult(**state)
+
+ return data
diff --git a/nmreval/gui_qt/Qt.py b/nmreval/gui_qt/Qt.py
new file mode 100644
index 0000000..1a85709
--- /dev/null
+++ b/nmreval/gui_qt/Qt.py
@@ -0,0 +1,12 @@
+
+from PyQt5 import QtCore, QtGui, QtWidgets, QtPrintSupport
+
+# from PySide2 import QtCore, QtGui, QtWidgets
+# QtCore.pyqtSignal = QtCore.Signal
+# QtCore.pyqtProperty = QtCore.Property
+# QtCore.pyqtSlot = QtCore.Slot
+
+# import pyqtgraph as pg
+#
+# pg.setConfigOption('background', 'w')
+# pg.setConfigOption('foreground', 'k')
diff --git a/nmreval/gui_qt/__init__.py b/nmreval/gui_qt/__init__.py
new file mode 100644
index 0000000..cd017b5
--- /dev/null
+++ b/nmreval/gui_qt/__init__.py
@@ -0,0 +1,17 @@
+from .Qt import QtWidgets
+from .lib.styles import MyProxyStyle
+from ..configs import read_configuration
+
+
+class App(QtWidgets.QApplication):
+ color = 'light'
+ theme = 'normal'
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ configs = read_configuration()
+ App.theme = configs.get('GUI', 'Theme')
+ App.color = configs.get('GUI', 'Color')
+
+ self.setStyle(MyProxyStyle(App.color))
diff --git a/nmreval/gui_qt/_py/__init__.py b/nmreval/gui_qt/_py/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nmreval/gui_qt/_py/agroptiondialog.py b/nmreval/gui_qt/_py/agroptiondialog.py
new file mode 100644
index 0000000..7adeaf4
--- /dev/null
+++ b/nmreval/gui_qt/_py/agroptiondialog.py
@@ -0,0 +1,323 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file '_ui/agroptiondialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(513, 466)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.tabWidget = QtWidgets.QTabWidget(Dialog)
+ self.tabWidget.setTabShape(QtWidgets.QTabWidget.Rounded)
+ self.tabWidget.setObjectName("tabWidget")
+ self.tabWidgetPage1 = QtWidgets.QWidget()
+ self.tabWidgetPage1.setObjectName("tabWidgetPage1")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.tabWidgetPage1)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.frame_2 = QtWidgets.QFrame(self.tabWidgetPage1)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth())
+ self.frame_2.setSizePolicy(sizePolicy)
+ self.frame_2.setFrameShape(QtWidgets.QFrame.Panel)
+ self.frame_2.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.frame_2.setLineWidth(3)
+ self.frame_2.setMidLineWidth(5)
+ self.frame_2.setObjectName("frame_2")
+ self.gridLayout_4 = QtWidgets.QGridLayout(self.frame_2)
+ self.gridLayout_4.setSpacing(3)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ self.bottomMarginDoubleSpinBox = QtWidgets.QDoubleSpinBox(self.frame_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.bottomMarginDoubleSpinBox.sizePolicy().hasHeightForWidth())
+ self.bottomMarginDoubleSpinBox.setSizePolicy(sizePolicy)
+ self.bottomMarginDoubleSpinBox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
+ self.bottomMarginDoubleSpinBox.setObjectName("bottomMarginDoubleSpinBox")
+ self.gridLayout_4.addWidget(self.bottomMarginDoubleSpinBox, 5, 2, 1, 1)
+ self.bottomMarginLabel = QtWidgets.QLabel(self.frame_2)
+ self.bottomMarginLabel.setObjectName("bottomMarginLabel")
+ self.gridLayout_4.addWidget(self.bottomMarginLabel, 5, 1, 1, 1)
+ self.rightMarginLabel = QtWidgets.QLabel(self.frame_2)
+ self.rightMarginLabel.setAlignment(QtCore.Qt.AlignCenter)
+ self.rightMarginLabel.setObjectName("rightMarginLabel")
+ self.gridLayout_4.addWidget(self.rightMarginLabel, 2, 3, 1, 1)
+ self.rightMarginDoubleSpinBox = QtWidgets.QDoubleSpinBox(self.frame_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.rightMarginDoubleSpinBox.sizePolicy().hasHeightForWidth())
+ self.rightMarginDoubleSpinBox.setSizePolicy(sizePolicy)
+ self.rightMarginDoubleSpinBox.setObjectName("rightMarginDoubleSpinBox")
+ self.gridLayout_4.addWidget(self.rightMarginDoubleSpinBox, 3, 3, 1, 1)
+ self.leftMarginDoubleSpinBox = QtWidgets.QDoubleSpinBox(self.frame_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.leftMarginDoubleSpinBox.sizePolicy().hasHeightForWidth())
+ self.leftMarginDoubleSpinBox.setSizePolicy(sizePolicy)
+ self.leftMarginDoubleSpinBox.setObjectName("leftMarginDoubleSpinBox")
+ self.gridLayout_4.addWidget(self.leftMarginDoubleSpinBox, 3, 0, 1, 1)
+ self.leftMarginLabel = QtWidgets.QLabel(self.frame_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.leftMarginLabel.sizePolicy().hasHeightForWidth())
+ self.leftMarginLabel.setSizePolicy(sizePolicy)
+ self.leftMarginLabel.setAlignment(QtCore.Qt.AlignCenter)
+ self.leftMarginLabel.setObjectName("leftMarginLabel")
+ self.gridLayout_4.addWidget(self.leftMarginLabel, 2, 0, 1, 1)
+ self.topMarginLabel = QtWidgets.QLabel(self.frame_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.topMarginLabel.sizePolicy().hasHeightForWidth())
+ self.topMarginLabel.setSizePolicy(sizePolicy)
+ self.topMarginLabel.setObjectName("topMarginLabel")
+ self.gridLayout_4.addWidget(self.topMarginLabel, 0, 1, 1, 1)
+ self.topMarginDoubleSpinBox = QtWidgets.QDoubleSpinBox(self.frame_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.topMarginDoubleSpinBox.sizePolicy().hasHeightForWidth())
+ self.topMarginDoubleSpinBox.setSizePolicy(sizePolicy)
+ self.topMarginDoubleSpinBox.setObjectName("topMarginDoubleSpinBox")
+ self.gridLayout_4.addWidget(self.topMarginDoubleSpinBox, 0, 2, 1, 1)
+ self.frame = QtWidgets.QFrame(self.frame_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth())
+ self.frame.setSizePolicy(sizePolicy)
+ self.frame.setFrameShape(QtWidgets.QFrame.WinPanel)
+ self.frame.setMidLineWidth(0)
+ self.frame.setObjectName("frame")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame)
+ self.verticalLayout_2.setSpacing(0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.gridLayout_4.addWidget(self.frame, 1, 1, 4, 2)
+ self.gridLayout_2.addWidget(self.frame_2, 1, 0, 2, 2)
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem)
+ self.heightLabel = QtWidgets.QLabel(self.tabWidgetPage1)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.heightLabel.sizePolicy().hasHeightForWidth())
+ self.heightLabel.setSizePolicy(sizePolicy)
+ self.heightLabel.setObjectName("heightLabel")
+ self.verticalLayout_3.addWidget(self.heightLabel)
+ self.heightDoubleSpinBox = QtWidgets.QDoubleSpinBox(self.tabWidgetPage1)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.heightDoubleSpinBox.sizePolicy().hasHeightForWidth())
+ self.heightDoubleSpinBox.setSizePolicy(sizePolicy)
+ self.heightDoubleSpinBox.setObjectName("heightDoubleSpinBox")
+ self.verticalLayout_3.addWidget(self.heightDoubleSpinBox)
+ spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem1)
+ self.gridLayout_2.addLayout(self.verticalLayout_3, 1, 2, 2, 1)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem2)
+ self.widthLabel = QtWidgets.QLabel(self.tabWidgetPage1)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.widthLabel.sizePolicy().hasHeightForWidth())
+ self.widthLabel.setSizePolicy(sizePolicy)
+ self.widthLabel.setObjectName("widthLabel")
+ self.horizontalLayout.addWidget(self.widthLabel)
+ self.widthDoubleSpinBox = QtWidgets.QDoubleSpinBox(self.tabWidgetPage1)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.widthDoubleSpinBox.sizePolicy().hasHeightForWidth())
+ self.widthDoubleSpinBox.setSizePolicy(sizePolicy)
+ self.widthDoubleSpinBox.setObjectName("widthDoubleSpinBox")
+ self.horizontalLayout.addWidget(self.widthDoubleSpinBox)
+ spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem3)
+ self.gridLayout_2.addLayout(self.horizontalLayout, 0, 0, 1, 2)
+ self.tabWidget.addTab(self.tabWidgetPage1, "")
+ self.tab = QtWidgets.QWidget()
+ self.tab.setObjectName("tab")
+ self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.tab)
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
+ self.label_11 = QtWidgets.QLabel(self.tab)
+ self.label_11.setObjectName("label_11")
+ self.verticalLayout_4.addWidget(self.label_11)
+ self.spinBox_2 = QtWidgets.QSpinBox(self.tab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.spinBox_2.sizePolicy().hasHeightForWidth())
+ self.spinBox_2.setSizePolicy(sizePolicy)
+ self.spinBox_2.setMaximum(1000)
+ self.spinBox_2.setProperty("value", 100)
+ self.spinBox_2.setObjectName("spinBox_2")
+ self.verticalLayout_4.addWidget(self.spinBox_2)
+ self.legendLabel = QtWidgets.QLabel(self.tab)
+ self.legendLabel.setObjectName("legendLabel")
+ self.verticalLayout_4.addWidget(self.legendLabel)
+ self.spinBox = QtWidgets.QSpinBox(self.tab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.spinBox.sizePolicy().hasHeightForWidth())
+ self.spinBox.setSizePolicy(sizePolicy)
+ self.spinBox.setMaximum(1000)
+ self.spinBox.setProperty("value", 100)
+ self.spinBox.setObjectName("spinBox")
+ self.verticalLayout_4.addWidget(self.spinBox)
+ self.groupBox_4 = QtWidgets.QGroupBox(self.tab)
+ self.groupBox_4.setObjectName("groupBox_4")
+ self.formLayout_2 = QtWidgets.QFormLayout(self.groupBox_4)
+ self.formLayout_2.setObjectName("formLayout_2")
+ self.verticalAxisTickLabel = QtWidgets.QLabel(self.groupBox_4)
+ self.verticalAxisTickLabel.setObjectName("verticalAxisTickLabel")
+ self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.verticalAxisTickLabel)
+ self.spinBox_5 = QtWidgets.QSpinBox(self.groupBox_4)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.spinBox_5.sizePolicy().hasHeightForWidth())
+ self.spinBox_5.setSizePolicy(sizePolicy)
+ self.spinBox_5.setMaximum(1000)
+ self.spinBox_5.setProperty("value", 100)
+ self.spinBox_5.setObjectName("spinBox_5")
+ self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.spinBox_5)
+ self.verticalAxisLabelSpinBox = QtWidgets.QSpinBox(self.groupBox_4)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.verticalAxisLabelSpinBox.sizePolicy().hasHeightForWidth())
+ self.verticalAxisLabelSpinBox.setSizePolicy(sizePolicy)
+ self.verticalAxisLabelSpinBox.setMaximum(1000)
+ self.verticalAxisLabelSpinBox.setProperty("value", 100)
+ self.verticalAxisLabelSpinBox.setObjectName("verticalAxisLabelSpinBox")
+ self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.verticalAxisLabelSpinBox)
+ self.verticalAxisLabelLabel = QtWidgets.QLabel(self.groupBox_4)
+ self.verticalAxisLabelLabel.setObjectName("verticalAxisLabelLabel")
+ self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.verticalAxisLabelLabel)
+ self.verticalLayout_4.addWidget(self.groupBox_4)
+ self.groupBox_3 = QtWidgets.QGroupBox(self.tab)
+ self.groupBox_3.setObjectName("groupBox_3")
+ self.formLayout = QtWidgets.QFormLayout(self.groupBox_3)
+ self.formLayout.setObjectName("formLayout")
+ self.spinBox_4 = QtWidgets.QSpinBox(self.groupBox_3)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.spinBox_4.sizePolicy().hasHeightForWidth())
+ self.spinBox_4.setSizePolicy(sizePolicy)
+ self.spinBox_4.setMaximum(1000)
+ self.spinBox_4.setProperty("value", 100)
+ self.spinBox_4.setObjectName("spinBox_4")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.spinBox_4)
+ self.spinBox_3 = QtWidgets.QSpinBox(self.groupBox_3)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.spinBox_3.sizePolicy().hasHeightForWidth())
+ self.spinBox_3.setSizePolicy(sizePolicy)
+ self.spinBox_3.setMaximum(1000)
+ self.spinBox_3.setProperty("value", 100)
+ self.spinBox_3.setObjectName("spinBox_3")
+ self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.spinBox_3)
+ self.label_10 = QtWidgets.QLabel(self.groupBox_3)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_10.sizePolicy().hasHeightForWidth())
+ self.label_10.setSizePolicy(sizePolicy)
+ self.label_10.setObjectName("label_10")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_10)
+ self.label_12 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_12.setObjectName("label_12")
+ self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_12)
+ self.verticalLayout_4.addWidget(self.groupBox_3)
+ spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_4.addItem(spacerItem4)
+ self.tabWidget.addTab(self.tab, "")
+ self.tab_2 = QtWidgets.QWidget()
+ self.tab_2.setObjectName("tab_2")
+ self.gridLayout_3 = QtWidgets.QGridLayout(self.tab_2)
+ self.gridLayout_3.setObjectName("gridLayout_3")
+ self.spinBox_6 = QtWidgets.QSpinBox(self.tab_2)
+ self.spinBox_6.setMaximum(1000)
+ self.spinBox_6.setProperty("value", 100)
+ self.spinBox_6.setObjectName("spinBox_6")
+ self.gridLayout_3.addWidget(self.spinBox_6, 0, 1, 1, 1)
+ self.label_3 = QtWidgets.QLabel(self.tab_2)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout_3.addWidget(self.label_3, 0, 0, 1, 1)
+ spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout_3.addItem(spacerItem5, 2, 1, 1, 1)
+ self.doubleSpinBox_7 = QtWidgets.QDoubleSpinBox(self.tab_2)
+ self.doubleSpinBox_7.setDecimals(1)
+ self.doubleSpinBox_7.setMaximum(20.0)
+ self.doubleSpinBox_7.setSingleStep(0.5)
+ self.doubleSpinBox_7.setProperty("value", 1.0)
+ self.doubleSpinBox_7.setObjectName("doubleSpinBox_7")
+ self.gridLayout_3.addWidget(self.doubleSpinBox_7, 1, 1, 1, 1)
+ self.label_4 = QtWidgets.QLabel(self.tab_2)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout_3.addWidget(self.label_4, 1, 0, 1, 1)
+ self.tabWidget.addTab(self.tab_2, "")
+ self.verticalLayout.addWidget(self.tabWidget)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout.addWidget(self.buttonBox)
+
+ self.retranslateUi(Dialog)
+ self.tabWidget.setCurrentIndex(0)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Grace settings"))
+ self.bottomMarginDoubleSpinBox.setSuffix(_translate("Dialog", " cm"))
+ self.bottomMarginLabel.setText(_translate("Dialog", "Bottom margin"))
+ self.rightMarginLabel.setText(_translate("Dialog", "Right margin"))
+ self.rightMarginDoubleSpinBox.setSuffix(_translate("Dialog", " cm"))
+ self.leftMarginDoubleSpinBox.setSuffix(_translate("Dialog", " cm"))
+ self.leftMarginLabel.setText(_translate("Dialog", "Left margin"))
+ self.topMarginLabel.setText(_translate("Dialog", "Top margin"))
+ self.topMarginDoubleSpinBox.setSuffix(_translate("Dialog", " cm"))
+ self.heightLabel.setText(_translate("Dialog", "Paper height"))
+ self.heightDoubleSpinBox.setSuffix(_translate("Dialog", " cm"))
+ self.widthLabel.setText(_translate("Dialog", "Paper width"))
+ self.widthDoubleSpinBox.setSuffix(_translate("Dialog", " cm"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabWidgetPage1), _translate("Dialog", "Dimensions"))
+ self.label_11.setText(_translate("Dialog", "Title"))
+ self.legendLabel.setText(_translate("Dialog", "Legend"))
+ self.groupBox_4.setTitle(_translate("Dialog", "Vertical axis"))
+ self.verticalAxisTickLabel.setText(_translate("Dialog", "tick"))
+ self.verticalAxisLabelLabel.setText(_translate("Dialog", "label"))
+ self.groupBox_3.setTitle(_translate("Dialog", "Horizontal axis"))
+ self.label_10.setText(_translate("Dialog", "tick"))
+ self.label_12.setText(_translate("Dialog", "label"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Dialog", "Fonts"))
+ self.label_3.setText(_translate("Dialog", "Default symbol size"))
+ self.label_4.setText(_translate("Dialog", "Default line size"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Dialog", "Symbol/Line"))
diff --git a/nmreval/gui_qt/_py/apod_dialog.py b/nmreval/gui_qt/_py/apod_dialog.py
new file mode 100644
index 0000000..3c985d9
--- /dev/null
+++ b/nmreval/gui_qt/_py/apod_dialog.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file '_ui/apod_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_ApodEdit(object):
+ def setupUi(self, ApodEdit):
+ ApodEdit.setObjectName("ApodEdit")
+ ApodEdit.resize(784, 484)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(ApodEdit.sizePolicy().hasHeightForWidth())
+ ApodEdit.setSizePolicy(sizePolicy)
+ self.gridLayout = QtWidgets.QGridLayout(ApodEdit)
+ self.gridLayout.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout.setSpacing(3)
+ self.gridLayout.setObjectName("gridLayout")
+ self.graphicsView = PlotWidget(ApodEdit)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
+ self.graphicsView.setSizePolicy(sizePolicy)
+ self.graphicsView.setObjectName("graphicsView")
+ self.gridLayout.addWidget(self.graphicsView, 2, 0, 1, 1)
+ self.graphicsView_2 = PlotWidget(ApodEdit)
+ self.graphicsView_2.setObjectName("graphicsView_2")
+ 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.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.apodcombobox.sizePolicy().hasHeightForWidth())
+ self.apodcombobox.setSizePolicy(sizePolicy)
+ self.apodcombobox.setObjectName("apodcombobox")
+ self.gridLayout.addWidget(self.apodcombobox, 0, 0, 1, 1)
+ self.widget_layout = QtWidgets.QHBoxLayout()
+ self.widget_layout.setContentsMargins(-1, 6, -1, -1)
+ self.widget_layout.setSpacing(20)
+ self.widget_layout.setObjectName("widget_layout")
+ self.gridLayout.addLayout(self.widget_layout, 1, 0, 1, 2)
+ self.buttonBox = QtWidgets.QDialogButtonBox(ApodEdit)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 4, 0, 1, 2)
+ self.eqn_label = QtWidgets.QLabel(ApodEdit)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.eqn_label.sizePolicy().hasHeightForWidth())
+ self.eqn_label.setSizePolicy(sizePolicy)
+ self.eqn_label.setIndent(3)
+ self.eqn_label.setObjectName("eqn_label")
+ self.gridLayout.addWidget(self.eqn_label, 0, 1, 1, 1)
+
+ self.retranslateUi(ApodEdit)
+ self.buttonBox.accepted.connect(ApodEdit.accept)
+ self.buttonBox.rejected.connect(ApodEdit.close)
+ QtCore.QMetaObject.connectSlotsByName(ApodEdit)
+
+ def retranslateUi(self, ApodEdit):
+ _translate = QtCore.QCoreApplication.translate
+ ApodEdit.setWindowTitle(_translate("ApodEdit", "Apodization"))
+ self.eqn_label.setText(_translate("ApodEdit", "TextLabel"))
+from pyqtgraph import PlotWidget
diff --git a/nmreval/gui_qt/_py/asciidialog.py b/nmreval/gui_qt/_py/asciidialog.py
new file mode 100644
index 0000000..523ad06
--- /dev/null
+++ b/nmreval/gui_qt/_py/asciidialog.py
@@ -0,0 +1,203 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file '_ui/asciidialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_ascii_reader(object):
+ def setupUi(self, ascii_reader):
+ ascii_reader.setObjectName("ascii_reader")
+ ascii_reader.resize(667, 509)
+ self.verticalLayout = QtWidgets.QVBoxLayout(ascii_reader)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.tabWidget = QtWidgets.QTabWidget(ascii_reader)
+ self.tabWidget.setObjectName("tabWidget")
+ self.tabWidgetPage1 = QtWidgets.QWidget()
+ self.tabWidgetPage1.setObjectName("tabWidgetPage1")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tabWidgetPage1)
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.plainTextEdit_2 = QtWidgets.QPlainTextEdit(self.tabWidgetPage1)
+ self.plainTextEdit_2.setEnabled(False)
+ self.plainTextEdit_2.setMaximumSize(QtCore.QSize(16777215, 180))
+ self.plainTextEdit_2.setObjectName("plainTextEdit_2")
+ self.verticalLayout_3.addWidget(self.plainTextEdit_2)
+ self.ascii_table = QtWidgets.QTableWidget(self.tabWidgetPage1)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.ascii_table.sizePolicy().hasHeightForWidth())
+ self.ascii_table.setSizePolicy(sizePolicy)
+ self.ascii_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
+ self.ascii_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.ascii_table.setAlternatingRowColors(True)
+ self.ascii_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+ self.ascii_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectColumns)
+ self.ascii_table.setObjectName("ascii_table")
+ self.ascii_table.setColumnCount(0)
+ self.ascii_table.setRowCount(0)
+ self.ascii_table.horizontalHeader().setMinimumSectionSize(1)
+ self.verticalLayout_3.addWidget(self.ascii_table)
+ self.tabWidget.addTab(self.tabWidgetPage1, "")
+ self.tabWidgetPage2 = QtWidgets.QWidget()
+ self.tabWidgetPage2.setObjectName("tabWidgetPage2")
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.tabWidgetPage2)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.plainTextEdit = QtWidgets.QPlainTextEdit(self.tabWidgetPage2)
+ self.plainTextEdit.setObjectName("plainTextEdit")
+ self.horizontalLayout_3.addWidget(self.plainTextEdit)
+ self.formLayout = QtWidgets.QFormLayout()
+ self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow)
+ self.formLayout.setContentsMargins(0, -1, -1, -1)
+ self.formLayout.setObjectName("formLayout")
+ self.label_2 = QtWidgets.QLabel(self.tabWidgetPage2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
+ self.label_2.setSizePolicy(sizePolicy)
+ self.label_2.setObjectName("label_2")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_2)
+ self.delay_lineedit = QtWidgets.QLineEdit(self.tabWidgetPage2)
+ self.delay_lineedit.setObjectName("delay_lineedit")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.delay_lineedit)
+ self.label_3 = QtWidgets.QLabel(self.tabWidgetPage2)
+ self.label_3.setObjectName("label_3")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_3)
+ self.start_lineedit = QtWidgets.QLineEdit(self.tabWidgetPage2)
+ self.start_lineedit.setObjectName("start_lineedit")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.start_lineedit)
+ self.label_4 = QtWidgets.QLabel(self.tabWidgetPage2)
+ self.label_4.setObjectName("label_4")
+ self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_4)
+ self.end_lineedit = QtWidgets.QLineEdit(self.tabWidgetPage2)
+ self.end_lineedit.setObjectName("end_lineedit")
+ self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.end_lineedit)
+ self.checkBox = QtWidgets.QCheckBox(self.tabWidgetPage2)
+ self.checkBox.setLayoutDirection(QtCore.Qt.LeftToRight)
+ self.checkBox.setObjectName("checkBox")
+ self.formLayout.setWidget(3, QtWidgets.QFormLayout.SpanningRole, self.checkBox)
+ self.checkBox_2 = QtWidgets.QCheckBox(self.tabWidgetPage2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.checkBox_2.sizePolicy().hasHeightForWidth())
+ self.checkBox_2.setSizePolicy(sizePolicy)
+ self.checkBox_2.setObjectName("checkBox_2")
+ self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.checkBox_2)
+ self.stag_lineEdit = QtWidgets.QLineEdit(self.tabWidgetPage2)
+ self.stag_lineEdit.setEnabled(True)
+ self.stag_lineEdit.setObjectName("stag_lineEdit")
+ self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.stag_lineEdit)
+ self.pushButton = QtWidgets.QPushButton(self.tabWidgetPage2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.pushButton.sizePolicy().hasHeightForWidth())
+ self.pushButton.setSizePolicy(sizePolicy)
+ icon = QtGui.QIcon.fromTheme("accessories-calculator")
+ self.pushButton.setIcon(icon)
+ self.pushButton.setObjectName("pushButton")
+ self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.pushButton)
+ self.horizontalLayout_3.addLayout(self.formLayout)
+ self.tabWidget.addTab(self.tabWidgetPage2, "")
+ self.verticalLayout.addWidget(self.tabWidget)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.x_label = QtWidgets.QLabel(ascii_reader)
+ self.x_label.setObjectName("x_label")
+ self.horizontalLayout.addWidget(self.x_label)
+ self.x_lineedit = QtWidgets.QLineEdit(ascii_reader)
+ self.x_lineedit.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhPreferNumbers)
+ self.x_lineedit.setObjectName("x_lineedit")
+ self.horizontalLayout.addWidget(self.x_lineedit)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem1)
+ self.y_label = QtWidgets.QLabel(ascii_reader)
+ self.y_label.setObjectName("y_label")
+ self.horizontalLayout.addWidget(self.y_label)
+ self.y_lineedit = QtWidgets.QLineEdit(ascii_reader)
+ self.y_lineedit.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhPreferNumbers)
+ self.y_lineedit.setObjectName("y_lineedit")
+ self.horizontalLayout.addWidget(self.y_lineedit)
+ self.widget = QtWidgets.QWidget(ascii_reader)
+ self.widget.setObjectName("widget")
+ self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.widget)
+ self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_4.setSpacing(0)
+ self.horizontalLayout_4.setObjectName("horizontalLayout_4")
+ spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_4.addItem(spacerItem2)
+ self.label_5 = QtWidgets.QLabel(self.widget)
+ self.label_5.setObjectName("label_5")
+ self.horizontalLayout_4.addWidget(self.label_5)
+ self.lineEdit = QtWidgets.QLineEdit(self.widget)
+ self.lineEdit.setObjectName("lineEdit")
+ self.horizontalLayout_4.addWidget(self.lineEdit)
+ self.horizontalLayout.addWidget(self.widget)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.skippy_checkbox = QtWidgets.QCheckBox(ascii_reader)
+ self.skippy_checkbox.setObjectName("skippy_checkbox")
+ self.horizontalLayout_2.addWidget(self.skippy_checkbox)
+ spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem3)
+ self.radioButton = QtWidgets.QRadioButton(ascii_reader)
+ self.radioButton.setChecked(True)
+ self.radioButton.setAutoExclusive(True)
+ self.radioButton.setObjectName("radioButton")
+ self.buttonGroup = QtWidgets.QButtonGroup(ascii_reader)
+ self.buttonGroup.setObjectName("buttonGroup")
+ self.buttonGroup.addButton(self.radioButton)
+ self.horizontalLayout_2.addWidget(self.radioButton)
+ self.radioButton_2 = QtWidgets.QRadioButton(ascii_reader)
+ self.radioButton_2.setAutoExclusive(True)
+ self.radioButton_2.setObjectName("radioButton_2")
+ self.buttonGroup.addButton(self.radioButton_2)
+ self.horizontalLayout_2.addWidget(self.radioButton_2)
+ self.radioButton_3 = QtWidgets.QRadioButton(ascii_reader)
+ self.radioButton_3.setObjectName("radioButton_3")
+ self.buttonGroup.addButton(self.radioButton_3)
+ self.horizontalLayout_2.addWidget(self.radioButton_3)
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
+ self.buttonbox = QtWidgets.QDialogButtonBox(ascii_reader)
+ self.buttonbox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonbox.setObjectName("buttonbox")
+ self.verticalLayout.addWidget(self.buttonbox)
+
+ self.retranslateUi(ascii_reader)
+ self.tabWidget.setCurrentIndex(0)
+ self.buttonbox.rejected.connect(ascii_reader.close)
+ QtCore.QMetaObject.connectSlotsByName(ascii_reader)
+
+ def retranslateUi(self, ascii_reader):
+ _translate = QtCore.QCoreApplication.translate
+ ascii_reader.setWindowTitle(_translate("ascii_reader", "Read text file"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabWidgetPage1), _translate("ascii_reader", "Data"))
+ self.label_2.setText(_translate("ascii_reader", "Number of delays"))
+ self.label_3.setText(_translate("ascii_reader", "Start value"))
+ self.label_4.setText(_translate("ascii_reader", "End value"))
+ self.checkBox.setText(_translate("ascii_reader", "Logarithmic scale"))
+ self.checkBox_2.setText(_translate("ascii_reader", "Staggered range"))
+ self.pushButton.setText(_translate("ascii_reader", "Calculate delays"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabWidgetPage2), _translate("ascii_reader", "Delays"))
+ self.x_label.setText(_translate("ascii_reader", "x"))
+ self.x_lineedit.setToolTip(_translate("ascii_reader", " Specify which column is used as x-value.
"))
+ self.y_label.setText(_translate("ascii_reader", "y"))
+ self.y_lineedit.setToolTip(_translate("ascii_reader", " 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.)
"))
+ self.label_5.setText(_translate("ascii_reader", " Δy
"))
+ self.skippy_checkbox.setToolTip(_translate("ascii_reader", "Use selection for next files. Deletes possible delay values."))
+ self.skippy_checkbox.setText(_translate("ascii_reader", "Skip next dialogues?"))
+ self.radioButton.setText(_translate("ascii_reader", "Points"))
+ self.radioButton_2.setText(_translate("ascii_reader", "FID"))
+ self.radioButton_3.setText(_translate("ascii_reader", "Spectrum"))
diff --git a/nmreval/gui_qt/_py/axisConfigTemplate.py b/nmreval/gui_qt/_py/axisConfigTemplate.py
new file mode 100644
index 0000000..04e360e
--- /dev/null
+++ b/nmreval/gui_qt/_py/axisConfigTemplate.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file '_ui/axisConfigTemplate.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(186, 154)
+ Form.setMaximumSize(QtCore.QSize(200, 16777215))
+ self.gridLayout = QtWidgets.QGridLayout(Form)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setSpacing(0)
+ self.gridLayout.setObjectName("gridLayout")
+ self.label = QtWidgets.QLabel(Form)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 7, 0, 1, 2)
+ self.linkCombo = QtWidgets.QComboBox(Form)
+ self.linkCombo.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
+ self.linkCombo.setObjectName("linkCombo")
+ self.gridLayout.addWidget(self.linkCombo, 7, 2, 1, 2)
+ self.autoPercentSpin = QtWidgets.QSpinBox(Form)
+ self.autoPercentSpin.setEnabled(True)
+ self.autoPercentSpin.setMinimum(1)
+ self.autoPercentSpin.setMaximum(100)
+ self.autoPercentSpin.setSingleStep(1)
+ self.autoPercentSpin.setProperty("value", 100)
+ self.autoPercentSpin.setObjectName("autoPercentSpin")
+ self.gridLayout.addWidget(self.autoPercentSpin, 2, 2, 1, 2)
+ self.autoRadio = QtWidgets.QRadioButton(Form)
+ self.autoRadio.setChecked(True)
+ self.autoRadio.setObjectName("autoRadio")
+ self.gridLayout.addWidget(self.autoRadio, 2, 0, 1, 2)
+ self.manualRadio = QtWidgets.QRadioButton(Form)
+ self.manualRadio.setObjectName("manualRadio")
+ self.gridLayout.addWidget(self.manualRadio, 1, 0, 1, 2)
+ self.minText = QtWidgets.QLineEdit(Form)
+ self.minText.setObjectName("minText")
+ self.gridLayout.addWidget(self.minText, 1, 2, 1, 1)
+ self.maxText = QtWidgets.QLineEdit(Form)
+ self.maxText.setObjectName("maxText")
+ self.gridLayout.addWidget(self.maxText, 1, 3, 1, 1)
+ self.invertCheck = QtWidgets.QCheckBox(Form)
+ self.invertCheck.setObjectName("invertCheck")
+ self.gridLayout.addWidget(self.invertCheck, 5, 0, 1, 4)
+ self.mouseCheck = QtWidgets.QCheckBox(Form)
+ self.mouseCheck.setChecked(True)
+ self.mouseCheck.setObjectName("mouseCheck")
+ self.gridLayout.addWidget(self.mouseCheck, 6, 0, 1, 4)
+ self.visibleOnlyCheck = QtWidgets.QCheckBox(Form)
+ self.visibleOnlyCheck.setObjectName("visibleOnlyCheck")
+ self.gridLayout.addWidget(self.visibleOnlyCheck, 3, 2, 1, 2)
+ self.autoPanCheck = QtWidgets.QCheckBox(Form)
+ self.autoPanCheck.setObjectName("autoPanCheck")
+ self.gridLayout.addWidget(self.autoPanCheck, 4, 2, 1, 2)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "PyQtGraph"))
+ self.label.setText(_translate("Form", "Link Axis:"))
+ self.linkCombo.setToolTip(_translate("Form", "
Links this axis with another view. When linked, both views will display the same data range.
"))
+ self.autoPercentSpin.setToolTip(_translate("Form", "Percent of data to be visible when auto-scaling. It may be useful to decrease this value for data with spiky noise.
"))
+ self.autoPercentSpin.setSuffix(_translate("Form", "%"))
+ self.autoRadio.setToolTip(_translate("Form", "Automatically resize this axis whenever the displayed data is changed.
"))
+ self.autoRadio.setText(_translate("Form", "Auto"))
+ self.manualRadio.setToolTip(_translate("Form", "Set the range for this axis manually. This disables automatic scaling.
"))
+ self.manualRadio.setText(_translate("Form", "Manual"))
+ self.minText.setToolTip(_translate("Form", "Minimum value to display for this axis.
"))
+ self.minText.setText(_translate("Form", "0"))
+ self.maxText.setToolTip(_translate("Form", "Maximum value to display for this axis.
"))
+ self.maxText.setText(_translate("Form", "0"))
+ self.invertCheck.setToolTip(_translate("Form", "Inverts the display of this axis. (+y points downward instead of upward)
"))
+ self.invertCheck.setText(_translate("Form", "Invert Axis"))
+ self.mouseCheck.setToolTip(_translate("Form", "Enables mouse interaction (panning, scaling) for this axis.
"))
+ self.mouseCheck.setText(_translate("Form", "Mouse Enabled"))
+ self.visibleOnlyCheck.setToolTip(_translate("Form", "When checked, the axis will only auto-scale to data that is visible along the orthogonal axis.
"))
+ self.visibleOnlyCheck.setText(_translate("Form", "Visible Data Only"))
+ self.autoPanCheck.setToolTip(_translate("Form", "When checked, the axis will automatically pan to center on the current data, but the scale along this axis will not change.
"))
+ self.autoPanCheck.setText(_translate("Form", "Auto Pan Only"))
diff --git a/nmreval/gui_qt/_py/baseline_dialog.py b/nmreval/gui_qt/_py/baseline_dialog.py
new file mode 100644
index 0000000..ca67fe7
--- /dev/null
+++ b/nmreval/gui_qt/_py/baseline_dialog.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file '_ui/baseline_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_SignalEdit(object):
+ def setupUi(self, SignalEdit):
+ SignalEdit.setObjectName("SignalEdit")
+ SignalEdit.resize(919, 595)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(SignalEdit.sizePolicy().hasHeightForWidth())
+ SignalEdit.setSizePolicy(sizePolicy)
+ self.gridLayout = QtWidgets.QGridLayout(SignalEdit)
+ self.gridLayout.setObjectName("gridLayout")
+ self.groupBox = QtWidgets.QWidget(SignalEdit)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth())
+ self.groupBox.setSizePolicy(sizePolicy)
+ self.groupBox.setObjectName("groupBox")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.listWidget = QtWidgets.QListWidget(self.groupBox)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
+ self.listWidget.setSizePolicy(sizePolicy)
+ self.listWidget.setObjectName("listWidget")
+ self.verticalLayout.addWidget(self.listWidget)
+ self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(SignalEdit)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 3)
+ self.graphicsView = PlotWidget(SignalEdit)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
+ self.graphicsView.setSizePolicy(sizePolicy)
+ self.graphicsView.setObjectName("graphicsView")
+ self.gridLayout.addWidget(self.graphicsView, 0, 2, 1, 1)
+
+ self.retranslateUi(SignalEdit)
+ self.buttonBox.accepted.connect(SignalEdit.accept)
+ self.buttonBox.rejected.connect(SignalEdit.close)
+ QtCore.QMetaObject.connectSlotsByName(SignalEdit)
+
+ def retranslateUi(self, SignalEdit):
+ _translate = QtCore.QCoreApplication.translate
+ SignalEdit.setWindowTitle(_translate("SignalEdit", "Dialog"))
+from pyqtgraph import PlotWidget
diff --git a/nmreval/gui_qt/_py/basewindow.py b/nmreval/gui_qt/_py/basewindow.py
new file mode 100644
index 0000000..4e8a29b
--- /dev/null
+++ b/nmreval/gui_qt/_py/basewindow.py
@@ -0,0 +1,606 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/basewindow.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_BaseWindow(object):
+ def setupUi(self, BaseWindow):
+ BaseWindow.setObjectName("BaseWindow")
+ BaseWindow.resize(1388, 735)
+ icon = QtGui.QIcon()
+ icon.addPixmap(QtGui.QPixmap(":/logo.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ BaseWindow.setWindowIcon(icon)
+ BaseWindow.setDockOptions(QtWidgets.QMainWindow.AllowTabbedDocks|QtWidgets.QMainWindow.AnimatedDocks|QtWidgets.QMainWindow.ForceTabbedDocks|QtWidgets.QMainWindow.VerticalTabs)
+ self.centralwidget = QtWidgets.QWidget(BaseWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.splitter = QtWidgets.QSplitter(self.centralwidget)
+ self.splitter.setOrientation(QtCore.Qt.Horizontal)
+ self.splitter.setObjectName("splitter")
+ self.tabWidget = QtWidgets.QTabWidget(self.splitter)
+ self.tabWidget.setTabPosition(QtWidgets.QTabWidget.West)
+ self.tabWidget.setTabShape(QtWidgets.QTabWidget.Rounded)
+ self.tabWidget.setElideMode(QtCore.Qt.ElideRight)
+ self.tabWidget.setTabsClosable(True)
+ self.tabWidget.setMovable(False)
+ self.tabWidget.setTabBarAutoHide(True)
+ self.tabWidget.setObjectName("tabWidget")
+ self.datawidget = DataWidget()
+ self.datawidget.setObjectName("datawidget")
+ self.tabWidget.addTab(self.datawidget, "")
+ self.valuewidget = ValueEditWidget()
+ self.valuewidget.setObjectName("valuewidget")
+ icon1 = QtGui.QIcon()
+ icon1.addPixmap(QtGui.QPixmap(":/value_dock"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.tabWidget.addTab(self.valuewidget, icon1, "")
+ self.fit_dialog = QFitDialog()
+ self.fit_dialog.setObjectName("fit_dialog")
+ icon2 = QtGui.QIcon()
+ icon2.addPixmap(QtGui.QPixmap(":/fit_dock"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.tabWidget.addTab(self.fit_dialog, icon2, "")
+ self.editsignalwidget = EditSignalWidget()
+ self.editsignalwidget.setObjectName("editsignalwidget")
+ icon3 = QtGui.QIcon()
+ icon3.addPixmap(QtGui.QPixmap(":/signal_dock"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.tabWidget.addTab(self.editsignalwidget, icon3, "")
+ self.ptsselectwidget = PointSelectWidget()
+ self.ptsselectwidget.setObjectName("ptsselectwidget")
+ icon4 = QtGui.QIcon()
+ icon4.addPixmap(QtGui.QPixmap(":/peakpick_dock"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.tabWidget.addTab(self.ptsselectwidget, icon4, "")
+ self.t1tauwidget = QT1Widget()
+ self.t1tauwidget.setObjectName("t1tauwidget")
+ icon5 = QtGui.QIcon()
+ icon5.addPixmap(QtGui.QPixmap(":/eval_t1_dock"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.tabWidget.addTab(self.t1tauwidget, icon5, "")
+ self.area = QtWidgets.QMdiArea(self.splitter)
+ self.area.setObjectName("area")
+ self.verticalLayout.addWidget(self.splitter)
+ BaseWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(BaseWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 1388, 30))
+ self.menubar.setObjectName("menubar")
+ self.menuFile = QtWidgets.QMenu(self.menubar)
+ self.menuFile.setObjectName("menuFile")
+ self.menuSave = QtWidgets.QMenu(self.menuFile)
+ icon6 = QtGui.QIcon()
+ icon6.addPixmap(QtGui.QPixmap(":/Daleks.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.menuSave.setIcon(icon6)
+ self.menuSave.setSeparatorsCollapsible(True)
+ self.menuSave.setObjectName("menuSave")
+ self.menuData = QtWidgets.QMenu(self.menubar)
+ self.menuData.setObjectName("menuData")
+ self.menuHelp = QtWidgets.QMenu(self.menubar)
+ self.menuHelp.setObjectName("menuHelp")
+ self.menuExtra = QtWidgets.QMenu(self.menubar)
+ self.menuExtra.setObjectName("menuExtra")
+ self.menuNormalize = QtWidgets.QMenu(self.menuExtra)
+ icon7 = QtGui.QIcon()
+ icon7.addPixmap(QtGui.QPixmap(":/normal.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.menuNormalize.setIcon(icon7)
+ self.menuNormalize.setObjectName("menuNormalize")
+ self.menuFit = QtWidgets.QMenu(self.menubar)
+ self.menuFit.setObjectName("menuFit")
+ self.menuMethod = QtWidgets.QMenu(self.menuFit)
+ self.menuMethod.setObjectName("menuMethod")
+ self.menuLimits = QtWidgets.QMenu(self.menuFit)
+ self.menuLimits.setObjectName("menuLimits")
+ self.menuOptions = QtWidgets.QMenu(self.menubar)
+ self.menuOptions.setObjectName("menuOptions")
+ self.menuWindow = QtWidgets.QMenu(self.menubar)
+ self.menuWindow.setObjectName("menuWindow")
+ self.menuView = QtWidgets.QMenu(self.menuWindow)
+ self.menuView.setObjectName("menuView")
+ self.menuNMR = QtWidgets.QMenu(self.menubar)
+ self.menuNMR.setObjectName("menuNMR")
+ self.menuBDS = QtWidgets.QMenu(self.menubar)
+ self.menuBDS.setObjectName("menuBDS")
+ self.menuSpectrum = QtWidgets.QMenu(self.menubar)
+ self.menuSpectrum.setObjectName("menuSpectrum")
+ self.menuStuff = QtWidgets.QMenu(self.menubar)
+ self.menuStuff.setTitle("")
+ self.menuStuff.setObjectName("menuStuff")
+ BaseWindow.setMenuBar(self.menubar)
+ self.toolBar = QtWidgets.QToolBar(BaseWindow)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.toolBar.sizePolicy().hasHeightForWidth())
+ self.toolBar.setSizePolicy(sizePolicy)
+ self.toolBar.setAllowedAreas(QtCore.Qt.AllToolBarAreas)
+ self.toolBar.setIconSize(QtCore.QSize(24, 24))
+ self.toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
+ self.toolBar.setObjectName("toolBar")
+ BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
+ self.toolbar_edit = QtWidgets.QToolBar(BaseWindow)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.toolbar_edit.sizePolicy().hasHeightForWidth())
+ self.toolbar_edit.setSizePolicy(sizePolicy)
+ self.toolbar_edit.setIconSize(QtCore.QSize(24, 24))
+ self.toolbar_edit.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
+ self.toolbar_edit.setObjectName("toolbar_edit")
+ BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolbar_edit)
+ self.statusBar = QtWidgets.QStatusBar(BaseWindow)
+ self.statusBar.setObjectName("statusBar")
+ BaseWindow.setStatusBar(self.statusBar)
+ self.toolBar_nmr = QtWidgets.QToolBar(BaseWindow)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.toolBar_nmr.sizePolicy().hasHeightForWidth())
+ self.toolBar_nmr.setSizePolicy(sizePolicy)
+ self.toolBar_nmr.setIconSize(QtCore.QSize(24, 24))
+ self.toolBar_nmr.setObjectName("toolBar_nmr")
+ BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar_nmr)
+ self.toolBar_fit = QtWidgets.QToolBar(BaseWindow)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.toolBar_fit.sizePolicy().hasHeightForWidth())
+ self.toolBar_fit.setSizePolicy(sizePolicy)
+ self.toolBar_fit.setIconSize(QtCore.QSize(24, 24))
+ self.toolBar_fit.setObjectName("toolBar_fit")
+ BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar_fit)
+ self.toolBar_spectrum = QtWidgets.QToolBar(BaseWindow)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.toolBar_spectrum.sizePolicy().hasHeightForWidth())
+ self.toolBar_spectrum.setSizePolicy(sizePolicy)
+ self.toolBar_spectrum.setIconSize(QtCore.QSize(24, 24))
+ self.toolBar_spectrum.setObjectName("toolBar_spectrum")
+ BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar_spectrum)
+ self.toolBar_data = QtWidgets.QToolBar(BaseWindow)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.toolBar_data.sizePolicy().hasHeightForWidth())
+ self.toolBar_data.setSizePolicy(sizePolicy)
+ self.toolBar_data.setIconSize(QtCore.QSize(24, 24))
+ self.toolBar_data.setObjectName("toolBar_data")
+ BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar_data)
+ self.action_close = QtWidgets.QAction(BaseWindow)
+ icon = QtGui.QIcon.fromTheme("window-close")
+ self.action_close.setIcon(icon)
+ self.action_close.setObjectName("action_close")
+ self.actionExportGraphic = QtWidgets.QAction(BaseWindow)
+ self.actionExportGraphic.setObjectName("actionExportGraphic")
+ self.action_open = QtWidgets.QAction(BaseWindow)
+ self.action_open.setObjectName("action_open")
+ self.actionExportData = QtWidgets.QAction(BaseWindow)
+ self.actionExportData.setObjectName("actionExportData")
+ self.action_calc = QtWidgets.QAction(BaseWindow)
+ self.action_calc.setObjectName("action_calc")
+ self.action_delete_sets = QtWidgets.QAction(BaseWindow)
+ icon = QtGui.QIcon.fromTheme("edit-delete")
+ self.action_delete_sets.setIcon(icon)
+ self.action_delete_sets.setObjectName("action_delete_sets")
+ self.action_save_fit_parameter = QtWidgets.QAction(BaseWindow)
+ self.action_save_fit_parameter.setObjectName("action_save_fit_parameter")
+ self.action_sort_pts = QtWidgets.QAction(BaseWindow)
+ self.action_sort_pts.setObjectName("action_sort_pts")
+ self.action_reset = QtWidgets.QAction(BaseWindow)
+ icon = QtGui.QIcon.fromTheme("edit-clear")
+ self.action_reset.setIcon(icon)
+ self.action_reset.setObjectName("action_reset")
+ self.actionDocumentation = QtWidgets.QAction(BaseWindow)
+ icon = QtGui.QIcon.fromTheme("help-about")
+ self.actionDocumentation.setIcon(icon)
+ self.actionDocumentation.setObjectName("actionDocumentation")
+ self.action_FitWidget = QtWidgets.QAction(BaseWindow)
+ self.action_FitWidget.setObjectName("action_FitWidget")
+ self.action_norm_max = QtWidgets.QAction(BaseWindow)
+ self.action_norm_max.setObjectName("action_norm_max")
+ self.action_norm_first = QtWidgets.QAction(BaseWindow)
+ self.action_norm_first.setObjectName("action_norm_first")
+ self.action_norm_area = QtWidgets.QAction(BaseWindow)
+ self.action_norm_area.setObjectName("action_norm_area")
+ self.action_norm_max_abs = QtWidgets.QAction(BaseWindow)
+ self.action_norm_max_abs.setObjectName("action_norm_max_abs")
+ self.action_norm_last = QtWidgets.QAction(BaseWindow)
+ self.action_norm_last.setObjectName("action_norm_last")
+ self.actionSave = QtWidgets.QAction(BaseWindow)
+ self.actionSave.setObjectName("actionSave")
+ self.actiontoolbar_display = QtWidgets.QAction(BaseWindow)
+ self.actiontoolbar_display.setCheckable(True)
+ self.actiontoolbar_display.setObjectName("actiontoolbar_display")
+ self.actionEdit_toolbars = QtWidgets.QAction(BaseWindow)
+ self.actionEdit_toolbars.setCheckable(True)
+ self.actionEdit_toolbars.setObjectName("actionEdit_toolbars")
+ self.actionAddlines = QtWidgets.QAction(BaseWindow)
+ self.actionAddlines.setObjectName("actionAddlines")
+ self.actionColors = QtWidgets.QAction(BaseWindow)
+ self.actionColors.setObjectName("actionColors")
+ self.actionConcatenate_sets = QtWidgets.QAction(BaseWindow)
+ self.actionConcatenate_sets.setObjectName("actionConcatenate_sets")
+ self.actionShift = QtWidgets.QAction(BaseWindow)
+ self.actionShift.setObjectName("actionShift")
+ self.actionShow_log = QtWidgets.QAction(BaseWindow)
+ icon = QtGui.QIcon.fromTheme("dialog-information")
+ self.actionShow_log.setIcon(icon)
+ self.actionShow_log.setObjectName("actionShow_log")
+ self.action_create_fit_function = QtWidgets.QAction(BaseWindow)
+ self.action_create_fit_function.setObjectName("action_create_fit_function")
+ self.actionGrace_preferences = QtWidgets.QAction(BaseWindow)
+ self.actionGrace_preferences.setObjectName("actionGrace_preferences")
+ self.actionSave_session = QtWidgets.QAction(BaseWindow)
+ self.actionSave_session.setObjectName("actionSave_session")
+ self.actionMouse_behaviour = QtWidgets.QAction(BaseWindow)
+ self.actionMouse_behaviour.setCheckable(True)
+ self.actionMouse_behaviour.setObjectName("actionMouse_behaviour")
+ self.actionConfiguration = QtWidgets.QAction(BaseWindow)
+ self.actionConfiguration.setObjectName("actionConfiguration")
+ self.actionRefresh = QtWidgets.QAction(BaseWindow)
+ icon = QtGui.QIcon.fromTheme("view-refresh")
+ self.actionRefresh.setIcon(icon)
+ self.actionRefresh.setObjectName("actionRefresh")
+ self.actionInterpolation = QtWidgets.QAction(BaseWindow)
+ self.actionInterpolation.setObjectName("actionInterpolation")
+ self.actionRunning_values = QtWidgets.QAction(BaseWindow)
+ self.actionRunning_values.setObjectName("actionRunning_values")
+ self.actionFit_parameter_saving = QtWidgets.QAction(BaseWindow)
+ self.actionFit_parameter_saving.setObjectName("actionFit_parameter_saving")
+ self.actionShow_fit_parameter = QtWidgets.QAction(BaseWindow)
+ self.actionShow_fit_parameter.setObjectName("actionShow_fit_parameter")
+ self.actionSkip_points = QtWidgets.QAction(BaseWindow)
+ self.actionSkip_points.setObjectName("actionSkip_points")
+ self.actionGuide_lines = QtWidgets.QAction(BaseWindow)
+ self.actionGuide_lines.setObjectName("actionGuide_lines")
+ self.actionMaximize = QtWidgets.QAction(BaseWindow)
+ self.actionMaximize.setCheckable(True)
+ self.actionMaximize.setVisible(False)
+ self.actionMaximize.setObjectName("actionMaximize")
+ self.actionTile = QtWidgets.QAction(BaseWindow)
+ self.actionTile.setObjectName("actionTile")
+ self.actionMinimize = QtWidgets.QAction(BaseWindow)
+ self.actionMinimize.setCheckable(True)
+ self.actionMinimize.setVisible(False)
+ self.actionMinimize.setObjectName("actionMinimize")
+ self.actionNew_window = QtWidgets.QAction(BaseWindow)
+ self.actionNew_window.setObjectName("actionNew_window")
+ self.actionDelete_window = QtWidgets.QAction(BaseWindow)
+ icon = QtGui.QIcon.fromTheme("edit-delete")
+ self.actionDelete_window.setIcon(icon)
+ self.actionDelete_window.setObjectName("actionDelete_window")
+ self.actionCascade_windows = QtWidgets.QAction(BaseWindow)
+ self.actionCascade_windows.setObjectName("actionCascade_windows")
+ self.actionNext_window = QtWidgets.QAction(BaseWindow)
+ self.actionNext_window.setObjectName("actionNext_window")
+ self.actionPrevious = QtWidgets.QAction(BaseWindow)
+ self.actionPrevious.setObjectName("actionPrevious")
+ self.t1action = QtWidgets.QAction(BaseWindow)
+ self.t1action.setObjectName("t1action")
+ self.t1tau = QtWidgets.QAction(BaseWindow)
+ self.t1tau.setObjectName("t1tau")
+ self.action_coup_calc = QtWidgets.QAction(BaseWindow)
+ self.action_coup_calc.setObjectName("action_coup_calc")
+ self.action_calc_eps_derivative = QtWidgets.QAction(BaseWindow)
+ self.action_calc_eps_derivative.setObjectName("action_calc_eps_derivative")
+ self.actionOpen_FC = QtWidgets.QAction(BaseWindow)
+ self.actionOpen_FC.setObjectName("actionOpen_FC")
+ self.action_mean_t1 = QtWidgets.QAction(BaseWindow)
+ self.action_mean_t1.setObjectName("action_mean_t1")
+ self.actionFilon = QtWidgets.QAction(BaseWindow)
+ self.actionFilon.setObjectName("actionFilon")
+ self.action_new_set = QtWidgets.QAction(BaseWindow)
+ self.action_new_set.setObjectName("action_new_set")
+ self.action_magnitude = QtWidgets.QAction(BaseWindow)
+ self.action_magnitude.setObjectName("action_magnitude")
+ self.actionCenterMax = QtWidgets.QAction(BaseWindow)
+ self.actionCenterMax.setObjectName("actionCenterMax")
+ self.action_depake = QtWidgets.QAction(BaseWindow)
+ self.action_depake.setObjectName("action_depake")
+ self.action_edit = QtWidgets.QAction(BaseWindow)
+ self.action_edit.setObjectName("action_edit")
+ self.actionPick_position = QtWidgets.QAction(BaseWindow)
+ self.actionPick_position.setObjectName("actionPick_position")
+ self.actionIntegrate = QtWidgets.QAction(BaseWindow)
+ self.actionIntegrate.setObjectName("actionIntegrate")
+ self.actionDerivation = QtWidgets.QAction(BaseWindow)
+ self.actionDerivation.setObjectName("actionDerivation")
+ self.actionIntegration = QtWidgets.QAction(BaseWindow)
+ self.actionIntegration.setObjectName("actionIntegration")
+ self.action_cut = QtWidgets.QAction(BaseWindow)
+ self.action_cut.setObjectName("action_cut")
+ self.actionMove_between_plots = QtWidgets.QAction(BaseWindow)
+ self.actionMove_between_plots.setObjectName("actionMove_between_plots")
+ self.actionBaseline = QtWidgets.QAction(BaseWindow)
+ self.actionBaseline.setObjectName("actionBaseline")
+ self.actionCalculateT1 = QtWidgets.QAction(BaseWindow)
+ self.actionCalculateT1.setObjectName("actionCalculateT1")
+ self.actionChange_datatypes = QtWidgets.QAction(BaseWindow)
+ self.actionChange_datatypes.setObjectName("actionChange_datatypes")
+ self.actionPrint = QtWidgets.QAction(BaseWindow)
+ icon = QtGui.QIcon.fromTheme("document-print")
+ self.actionPrint.setIcon(icon)
+ self.actionPrint.setObjectName("actionPrint")
+ self.action_lm_fit = QtWidgets.QAction(BaseWindow)
+ self.action_lm_fit.setCheckable(True)
+ self.action_lm_fit.setChecked(True)
+ self.action_lm_fit.setObjectName("action_lm_fit")
+ self.action_nm_fit = QtWidgets.QAction(BaseWindow)
+ self.action_nm_fit.setCheckable(True)
+ self.action_nm_fit.setObjectName("action_nm_fit")
+ self.action_odr_fit = QtWidgets.QAction(BaseWindow)
+ self.action_odr_fit.setCheckable(True)
+ self.action_odr_fit.setObjectName("action_odr_fit")
+ self.action_no_range = QtWidgets.QAction(BaseWindow)
+ self.action_no_range.setCheckable(True)
+ self.action_no_range.setChecked(False)
+ self.action_no_range.setObjectName("action_no_range")
+ self.action_x_range = QtWidgets.QAction(BaseWindow)
+ self.action_x_range.setCheckable(True)
+ self.action_x_range.setChecked(True)
+ self.action_x_range.setObjectName("action_x_range")
+ self.action_custom_range = QtWidgets.QAction(BaseWindow)
+ self.action_custom_range.setCheckable(True)
+ self.action_custom_range.setObjectName("action_custom_range")
+ self.actionSnake = QtWidgets.QAction(BaseWindow)
+ self.actionSnake.setObjectName("actionSnake")
+ self.actionFunction_editor = QtWidgets.QAction(BaseWindow)
+ self.actionFunction_editor.setObjectName("actionFunction_editor")
+ self.actionLife = QtWidgets.QAction(BaseWindow)
+ self.actionLife.setObjectName("actionLife")
+ self.actionTetris = QtWidgets.QAction(BaseWindow)
+ self.actionTetris.setObjectName("actionTetris")
+ self.actionUpdate = QtWidgets.QAction(BaseWindow)
+ self.actionUpdate.setObjectName("actionUpdate")
+ self.menuSave.addAction(self.actionSave)
+ self.menuSave.addAction(self.actionExportGraphic)
+ self.menuSave.addAction(self.action_save_fit_parameter)
+ self.menuFile.addAction(self.action_open)
+ self.menuFile.addAction(self.actionOpen_FC)
+ self.menuFile.addAction(self.menuSave.menuAction())
+ self.menuFile.addSeparator()
+ self.menuFile.addAction(self.actionPrint)
+ self.menuFile.addAction(self.action_reset)
+ self.menuFile.addSeparator()
+ self.menuFile.addAction(self.action_close)
+ self.menuFile.addSeparator()
+ self.menuData.addAction(self.action_new_set)
+ self.menuData.addAction(self.action_delete_sets)
+ self.menuData.addAction(self.actionMove_between_plots)
+ self.menuData.addAction(self.actionConcatenate_sets)
+ self.menuData.addAction(self.actionAddlines)
+ self.menuData.addSeparator()
+ self.menuData.addAction(self.action_sort_pts)
+ self.menuData.addAction(self.actionSkip_points)
+ self.menuData.addSeparator()
+ self.menuData.addAction(self.action_cut)
+ self.menuData.addSeparator()
+ self.menuData.addAction(self.actionChange_datatypes)
+ self.menuHelp.addAction(self.actionDocumentation)
+ self.menuHelp.addAction(self.actionUpdate)
+ self.menuNormalize.addAction(self.action_norm_max)
+ self.menuNormalize.addAction(self.action_norm_max_abs)
+ self.menuNormalize.addSeparator()
+ self.menuNormalize.addAction(self.action_norm_first)
+ self.menuNormalize.addAction(self.action_norm_last)
+ self.menuNormalize.addSeparator()
+ self.menuNormalize.addAction(self.action_norm_area)
+ self.menuExtra.addAction(self.action_mean_t1)
+ self.menuExtra.addSeparator()
+ self.menuExtra.addAction(self.actionFilon)
+ self.menuExtra.addAction(self.actionDerivation)
+ self.menuExtra.addAction(self.actionIntegration)
+ self.menuExtra.addSeparator()
+ self.menuExtra.addAction(self.menuNormalize.menuAction())
+ self.menuExtra.addAction(self.actionInterpolation)
+ self.menuExtra.addAction(self.actionRunning_values)
+ self.menuExtra.addAction(self.actionShift)
+ self.menuExtra.addSeparator()
+ self.menuExtra.addAction(self.action_calc)
+ self.menuMethod.addAction(self.action_lm_fit)
+ self.menuMethod.addAction(self.action_nm_fit)
+ self.menuMethod.addAction(self.action_odr_fit)
+ self.menuLimits.addAction(self.action_no_range)
+ self.menuLimits.addAction(self.action_x_range)
+ self.menuLimits.addAction(self.action_custom_range)
+ self.menuFit.addAction(self.action_FitWidget)
+ self.menuFit.addSeparator()
+ self.menuFit.addAction(self.action_create_fit_function)
+ self.menuFit.addAction(self.actionFunction_editor)
+ self.menuFit.addSeparator()
+ self.menuFit.addAction(self.actionShow_fit_parameter)
+ self.menuFit.addAction(self.menuMethod.menuAction())
+ self.menuFit.addAction(self.menuLimits.menuAction())
+ self.menuOptions.addAction(self.actionMouse_behaviour)
+ self.menuOptions.addSeparator()
+ self.menuOptions.addAction(self.actionGrace_preferences)
+ self.menuOptions.addAction(self.actionConfiguration)
+ self.menuView.addAction(self.actionTile)
+ self.menuView.addAction(self.actionCascade_windows)
+ self.menuWindow.addAction(self.actionNew_window)
+ self.menuWindow.addAction(self.actionDelete_window)
+ self.menuWindow.addSeparator()
+ self.menuWindow.addAction(self.actionNext_window)
+ self.menuWindow.addAction(self.actionPrevious)
+ self.menuWindow.addAction(self.actionMaximize)
+ self.menuWindow.addAction(self.actionMinimize)
+ self.menuWindow.addAction(self.menuView.menuAction())
+ self.menuWindow.addSeparator()
+ self.menuWindow.addAction(self.actionRefresh)
+ self.menuNMR.addAction(self.t1action)
+ self.menuNMR.addAction(self.actionCalculateT1)
+ self.menuNMR.addAction(self.action_coup_calc)
+ self.menuBDS.addAction(self.action_calc_eps_derivative)
+ self.menuSpectrum.addAction(self.action_magnitude)
+ self.menuSpectrum.addAction(self.actionCenterMax)
+ self.menuSpectrum.addAction(self.action_depake)
+ self.menuSpectrum.addSeparator()
+ self.menuSpectrum.addAction(self.action_edit)
+ self.menuSpectrum.addAction(self.actionBaseline)
+ self.menuSpectrum.addAction(self.actionPick_position)
+ self.menuStuff.addAction(self.actionSnake)
+ self.menuStuff.addAction(self.actionLife)
+ self.menuStuff.addAction(self.actionTetris)
+ self.menubar.addAction(self.menuFile.menuAction())
+ self.menubar.addAction(self.menuWindow.menuAction())
+ self.menubar.addAction(self.menuData.menuAction())
+ self.menubar.addAction(self.menuExtra.menuAction())
+ self.menubar.addAction(self.menuSpectrum.menuAction())
+ self.menubar.addAction(self.menuFit.menuAction())
+ self.menubar.addAction(self.menuNMR.menuAction())
+ self.menubar.addAction(self.menuBDS.menuAction())
+ self.menubar.addAction(self.menuOptions.menuAction())
+ self.menubar.addAction(self.menuHelp.menuAction())
+ self.menubar.addAction(self.menuStuff.menuAction())
+ self.toolBar.addAction(self.action_open)
+ self.toolBar.addAction(self.actionSave)
+ self.toolBar.addSeparator()
+ self.toolBar.addAction(self.actionMouse_behaviour)
+ self.toolbar_edit.addAction(self.action_calc)
+ self.toolbar_edit.addAction(self.action_mean_t1)
+ self.toolbar_edit.addAction(self.actionShift)
+ self.toolBar_nmr.addAction(self.t1action)
+ self.toolBar_nmr.addAction(self.actionCalculateT1)
+ self.toolBar_fit.addAction(self.action_FitWidget)
+ self.toolBar_spectrum.addAction(self.action_edit)
+ self.toolBar_spectrum.addAction(self.actionPick_position)
+ self.toolBar_data.addAction(self.actionConcatenate_sets)
+ self.toolBar_data.addAction(self.action_sort_pts)
+
+ self.retranslateUi(BaseWindow)
+ self.tabWidget.setCurrentIndex(0)
+ self.action_close.triggered.connect(BaseWindow.close)
+ QtCore.QMetaObject.connectSlotsByName(BaseWindow)
+
+ def retranslateUi(self, BaseWindow):
+ _translate = QtCore.QCoreApplication.translate
+ BaseWindow.setWindowTitle(_translate("BaseWindow", "Mr. Godot told me to tell you he won\'t come this evening but surely tomorrow."))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.datawidget), _translate("BaseWindow", "Data"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.valuewidget), _translate("BaseWindow", "Values"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.fit_dialog), _translate("BaseWindow", "Fit"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.editsignalwidget), _translate("BaseWindow", "Signals"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.ptsselectwidget), _translate("BaseWindow", "Pick points"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.t1tauwidget), _translate("BaseWindow", "SLR"))
+ self.menuFile.setTitle(_translate("BaseWindow", "&File"))
+ self.menuSave.setTitle(_translate("BaseWindow", "&Save..."))
+ self.menuData.setTitle(_translate("BaseWindow", "&Data"))
+ self.menuHelp.setTitle(_translate("BaseWindow", "&Help"))
+ self.menuExtra.setTitle(_translate("BaseWindow", "Math"))
+ self.menuNormalize.setTitle(_translate("BaseWindow", "&Normalize"))
+ self.menuFit.setTitle(_translate("BaseWindow", "F&it"))
+ self.menuMethod.setTitle(_translate("BaseWindow", "Method"))
+ self.menuLimits.setTitle(_translate("BaseWindow", "Limits"))
+ self.menuOptions.setTitle(_translate("BaseWindow", "Options"))
+ self.menuWindow.setTitle(_translate("BaseWindow", "Plots"))
+ self.menuView.setTitle(_translate("BaseWindow", "View"))
+ self.menuNMR.setTitle(_translate("BaseWindow", "NMR"))
+ self.menuBDS.setTitle(_translate("BaseWindow", "BDS"))
+ self.menuSpectrum.setTitle(_translate("BaseWindow", "Spectrum"))
+ self.toolBar.setWindowTitle(_translate("BaseWindow", "Main"))
+ self.toolbar_edit.setWindowTitle(_translate("BaseWindow", "Edit"))
+ self.toolBar_nmr.setWindowTitle(_translate("BaseWindow", "NMR"))
+ self.toolBar_fit.setWindowTitle(_translate("BaseWindow", "Fit"))
+ self.toolBar_spectrum.setWindowTitle(_translate("BaseWindow", "Spectrum"))
+ self.toolBar_data.setWindowTitle(_translate("BaseWindow", "toolBar_2"))
+ self.action_close.setText(_translate("BaseWindow", "&Quit"))
+ self.action_close.setShortcut(_translate("BaseWindow", "Ctrl+Q"))
+ self.actionExportGraphic.setText(_translate("BaseWindow", "Export graphic..."))
+ self.actionExportGraphic.setShortcut(_translate("BaseWindow", "Ctrl+Shift+S"))
+ self.action_open.setText(_translate("BaseWindow", "&Open..."))
+ self.action_open.setShortcut(_translate("BaseWindow", "Ctrl+O"))
+ self.actionExportData.setText(_translate("BaseWindow", "Export data..."))
+ self.actionExportData.setShortcut(_translate("BaseWindow", "Ctrl+Shift+S"))
+ self.action_calc.setText(_translate("BaseWindow", "&Evaluate expression..."))
+ self.action_delete_sets.setText(_translate("BaseWindow", "&Delete Set"))
+ self.action_delete_sets.setShortcut(_translate("BaseWindow", "Ctrl+Del"))
+ self.action_save_fit_parameter.setText(_translate("BaseWindow", "Save fit ¶meter..."))
+ self.action_sort_pts.setText(_translate("BaseWindow", "Sort &points"))
+ self.action_reset.setText(_translate("BaseWindow", "&Reset"))
+ self.action_reset.setShortcut(_translate("BaseWindow", "Ctrl+R"))
+ self.actionDocumentation.setText(_translate("BaseWindow", "&Documentation"))
+ self.actionDocumentation.setShortcut(_translate("BaseWindow", "F1"))
+ self.action_FitWidget.setText(_translate("BaseWindow", "Open &Fit"))
+ self.action_FitWidget.setShortcut(_translate("BaseWindow", "Ctrl+F"))
+ self.action_norm_max.setText(_translate("BaseWindow", "&Max"))
+ self.action_norm_first.setText(_translate("BaseWindow", "&First point"))
+ self.action_norm_area.setText(_translate("BaseWindow", "&Area"))
+ self.action_norm_max_abs.setText(_translate("BaseWindow", "Ma&x(Abs)"))
+ self.action_norm_last.setText(_translate("BaseWindow", "&Last point"))
+ self.actionSave.setText(_translate("BaseWindow", "S&ave..."))
+ self.actionSave.setShortcut(_translate("BaseWindow", "Ctrl+S"))
+ self.actiontoolbar_display.setText(_translate("BaseWindow", "&Views"))
+ self.actionEdit_toolbars.setText(_translate("BaseWindow", "&Edit"))
+ self.actionAddlines.setText(_translate("BaseWindow", "Set by function..."))
+ self.actionColors.setText(_translate("BaseWindow", "&Reset color"))
+ self.actionConcatenate_sets.setText(_translate("BaseWindow", "Join sets"))
+ self.actionShift.setText(_translate("BaseWindow", "&Shift/scale..."))
+ self.actionShow_log.setText(_translate("BaseWindow", "&Show log..."))
+ self.action_create_fit_function.setText(_translate("BaseWindow", "&Create fit function..."))
+ self.actionGrace_preferences.setText(_translate("BaseWindow", "&Grace preferences..."))
+ self.actionSave_session.setText(_translate("BaseWindow", "Update session"))
+ self.actionMouse_behaviour.setText(_translate("BaseWindow", "Mouse behaviour"))
+ self.actionMouse_behaviour.setToolTip(_translate("BaseWindow", "Switch between zoom and pan in graph."))
+ self.actionConfiguration.setText(_translate("BaseWindow", "Configuration..."))
+ self.actionRefresh.setText(_translate("BaseWindow", "Refresh"))
+ self.actionRefresh.setShortcut(_translate("BaseWindow", "F5"))
+ self.actionInterpolation.setText(_translate("BaseWindow", "Interpolation..."))
+ self.actionRunning_values.setText(_translate("BaseWindow", "Smoothing..."))
+ self.actionFit_parameter_saving.setText(_translate("BaseWindow", "Fit parameter saving..."))
+ self.actionShow_fit_parameter.setText(_translate("BaseWindow", "Parameter..."))
+ self.actionSkip_points.setText(_translate("BaseWindow", "Skip points..."))
+ self.actionGuide_lines.setText(_translate("BaseWindow", "Draw lines..."))
+ self.actionMaximize.setText(_translate("BaseWindow", "Maximize"))
+ self.actionTile.setText(_translate("BaseWindow", "Tile windows"))
+ self.actionMinimize.setText(_translate("BaseWindow", "Minimize"))
+ self.actionNew_window.setText(_translate("BaseWindow", "New plot"))
+ self.actionDelete_window.setText(_translate("BaseWindow", "Delete plot"))
+ self.actionCascade_windows.setText(_translate("BaseWindow", "Cascade windows"))
+ self.actionNext_window.setText(_translate("BaseWindow", "Next"))
+ self.actionNext_window.setShortcut(_translate("BaseWindow", "Alt+Right"))
+ self.actionPrevious.setText(_translate("BaseWindow", "Previous"))
+ self.actionPrevious.setShortcut(_translate("BaseWindow", "Alt+Left"))
+ self.t1action.setText(_translate("BaseWindow", "Evaluate T1 minimum..."))
+ self.t1tau.setText(_translate("BaseWindow", "Calculate T1..."))
+ self.action_coup_calc.setText(_translate("BaseWindow", "Coupling values..."))
+ self.action_calc_eps_derivative.setText(_translate("BaseWindow", "Calculate derivative loss"))
+ self.actionOpen_FC.setText(_translate("BaseWindow", "Read FC data..."))
+ self.action_mean_t1.setText(_translate("BaseWindow", "Convert mean values..."))
+ self.actionFilon.setText(_translate("BaseWindow", "Log FT..."))
+ self.action_new_set.setText(_translate("BaseWindow", "New set"))
+ self.action_magnitude.setText(_translate("BaseWindow", "Calculate magnitude"))
+ self.actionCenterMax.setText(_translate("BaseWindow", "Center on max"))
+ self.action_depake.setText(_translate("BaseWindow", "De-paked spectrum"))
+ self.action_edit.setText(_translate("BaseWindow", "Edit signals..."))
+ self.actionPick_position.setText(_translate("BaseWindow", "Pick points..."))
+ self.actionIntegrate.setText(_translate("BaseWindow", "Integrate"))
+ self.actionDerivation.setText(_translate("BaseWindow", "Differentiation..."))
+ self.actionIntegration.setText(_translate("BaseWindow", "Integration..."))
+ self.action_cut.setText(_translate("BaseWindow", "Cut to visible range"))
+ self.actionMove_between_plots.setText(_translate("BaseWindow", "Move sets..."))
+ self.actionBaseline.setText(_translate("BaseWindow", "Baseline..."))
+ self.actionCalculateT1.setText(_translate("BaseWindow", "Calculate relaxation..."))
+ self.actionChange_datatypes.setText(_translate("BaseWindow", "Change datatypes..."))
+ self.actionPrint.setText(_translate("BaseWindow", "Print..."))
+ self.actionPrint.setShortcut(_translate("BaseWindow", "Ctrl+P"))
+ self.action_lm_fit.setText(_translate("BaseWindow", "Default stuff"))
+ self.action_nm_fit.setText(_translate("BaseWindow", "Nelder-Mead"))
+ self.action_odr_fit.setText(_translate("BaseWindow", "ODR"))
+ self.action_no_range.setText(_translate("BaseWindow", "None"))
+ self.action_x_range.setText(_translate("BaseWindow", "Visible x range"))
+ self.action_custom_range.setText(_translate("BaseWindow", "Custom"))
+ self.actionSnake.setText(_translate("BaseWindow", "Worms"))
+ self.actionFunction_editor.setText(_translate("BaseWindow", "Function editor..."))
+ self.actionLife.setText(_translate("BaseWindow", "Life..."))
+ self.actionTetris.setText(_translate("BaseWindow", "Not Tetris"))
+ self.actionUpdate.setText(_translate("BaseWindow", "Look for updates"))
+from ..data.datawidget.datawidget import DataWidget
+from ..data.point_select import PointSelectWidget
+from ..data.signaledit.editsignalwidget import EditSignalWidget
+from ..data.valueeditwidget import ValueEditWidget
+from ..fit.fitwindow import QFitDialog
+from ..nmr.t1widget import QT1Widget
diff --git a/nmreval/gui_qt/_py/bdsdialog.py b/nmreval/gui_qt/_py/bdsdialog.py
new file mode 100644
index 0000000..909eb76
--- /dev/null
+++ b/nmreval/gui_qt/_py/bdsdialog.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/bdsdialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(400, 319)
+ self.gridLayout = QtWidgets.QGridLayout(Dialog)
+ self.gridLayout.setObjectName("gridLayout")
+ self.listWidget = QtWidgets.QListWidget(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
+ self.listWidget.setSizePolicy(sizePolicy)
+ self.listWidget.setObjectName("listWidget")
+ self.gridLayout.addWidget(self.listWidget, 1, 0, 1, 1)
+ self.label = QtWidgets.QLabel(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ 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, 0, 1, 1)
+ 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.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1)
+ self.verticalLayout = QtWidgets.QVBoxLayout()
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.eps_checkBox = QtWidgets.QCheckBox(Dialog)
+ self.eps_checkBox.setChecked(True)
+ self.eps_checkBox.setObjectName("eps_checkBox")
+ self.verticalLayout.addWidget(self.eps_checkBox)
+ self.modul_checkBox = QtWidgets.QCheckBox(Dialog)
+ self.modul_checkBox.setObjectName("modul_checkBox")
+ self.verticalLayout.addWidget(self.modul_checkBox)
+ self.cond_checkBox = QtWidgets.QCheckBox(Dialog)
+ self.cond_checkBox.setObjectName("cond_checkBox")
+ self.verticalLayout.addWidget(self.cond_checkBox)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem)
+ self.gridLayout.addLayout(self.verticalLayout, 1, 1, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 2, 0, 1, 2)
+
+ self.retranslateUi(Dialog)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Read BDS data"))
+ self.label.setText(_translate("Dialog", "Found temperatures"))
+ self.label_2.setText(_translate("Dialog", "Read as:"))
+ self.eps_checkBox.setText(_translate("Dialog", "Permittivity ε"))
+ self.modul_checkBox.setText(_translate("Dialog", "Modulus 1/ε"))
+ self.cond_checkBox.setText(_translate("Dialog", "Conductivity iεω"))
diff --git a/nmreval/gui_qt/_py/color_palette.py b/nmreval/gui_qt/_py/color_palette.py
new file mode 100644
index 0000000..7d7e186
--- /dev/null
+++ b/nmreval/gui_qt/_py/color_palette.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/color_palette.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(390, 409)
+ self.gridLayout = QtWidgets.QGridLayout(Dialog)
+ self.gridLayout.setObjectName("gridLayout")
+ self.append_palette_button = QtWidgets.QPushButton(Dialog)
+ self.append_palette_button.setObjectName("append_palette_button")
+ self.gridLayout.addWidget(self.append_palette_button, 2, 0, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout.addItem(spacerItem, 4, 0, 1, 1)
+ self.palette_combobox = QtWidgets.QComboBox(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.palette_combobox.sizePolicy().hasHeightForWidth())
+ self.palette_combobox.setSizePolicy(sizePolicy)
+ self.palette_combobox.setObjectName("palette_combobox")
+ self.gridLayout.addWidget(self.palette_combobox, 0, 0, 1, 1)
+ self.add_color_button = QtWidgets.QPushButton(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.add_color_button.sizePolicy().hasHeightForWidth())
+ self.add_color_button.setSizePolicy(sizePolicy)
+ self.add_color_button.setObjectName("add_color_button")
+ self.gridLayout.addWidget(self.add_color_button, 6, 0, 1, 1)
+ self.color_combobox = ColorListEditor(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.color_combobox.sizePolicy().hasHeightForWidth())
+ self.color_combobox.setSizePolicy(sizePolicy)
+ self.color_combobox.setObjectName("color_combobox")
+ self.gridLayout.addWidget(self.color_combobox, 5, 0, 1, 1)
+ self.save_button = QtWidgets.QPushButton(Dialog)
+ self.save_button.setObjectName("save_button")
+ self.gridLayout.addWidget(self.save_button, 9, 1, 1, 1)
+ self.add_palette_button = QtWidgets.QPushButton(Dialog)
+ self.add_palette_button.setObjectName("add_palette_button")
+ self.gridLayout.addWidget(self.add_palette_button, 1, 0, 1, 1)
+ self.new_name_edit = QtWidgets.QLineEdit(Dialog)
+ self.new_name_edit.setObjectName("new_name_edit")
+ self.gridLayout.addWidget(self.new_name_edit, 9, 2, 1, 1)
+ self.colorlist = QtWidgets.QListWidget(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.colorlist.sizePolicy().hasHeightForWidth())
+ self.colorlist.setSizePolicy(sizePolicy)
+ self.colorlist.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
+ self.colorlist.setObjectName("colorlist")
+ self.gridLayout.addWidget(self.colorlist, 0, 1, 9, 2)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 10, 0, 1, 3)
+
+ self.retranslateUi(Dialog)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Color Palette"))
+ self.append_palette_button.setText(_translate("Dialog", "Append"))
+ self.add_color_button.setText(_translate("Dialog", "Add color"))
+ self.save_button.setText(_translate("Dialog", "Save as new list"))
+ self.add_palette_button.setText(_translate("Dialog", "Load"))
+from ..lib.delegates import ColorListEditor
diff --git a/nmreval/gui_qt/_py/coupling_calculator.py b/nmreval/gui_qt/_py/coupling_calculator.py
new file mode 100644
index 0000000..72ac914
--- /dev/null
+++ b/nmreval/gui_qt/_py/coupling_calculator.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/coupling_calculator.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_coupling_calc_dialog(object):
+ def setupUi(self, coupling_calc_dialog):
+ coupling_calc_dialog.setObjectName("coupling_calc_dialog")
+ coupling_calc_dialog.resize(400, 280)
+ self.verticalLayout = QtWidgets.QVBoxLayout(coupling_calc_dialog)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.comboBox = QtWidgets.QComboBox(coupling_calc_dialog)
+ self.comboBox.setObjectName("comboBox")
+ self.verticalLayout.addWidget(self.comboBox)
+ self.label_2 = QtWidgets.QLabel(coupling_calc_dialog)
+ self.label_2.setObjectName("label_2")
+ self.verticalLayout.addWidget(self.label_2)
+ self.verticalFrame = QtWidgets.QFrame(coupling_calc_dialog)
+ self.verticalFrame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.verticalFrame.setObjectName("verticalFrame")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalFrame)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.verticalLayout.addWidget(self.verticalFrame)
+ self.label = QtWidgets.QLabel(coupling_calc_dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
+ self.label.setSizePolicy(sizePolicy)
+ self.label.setStyleSheet("font-weight: bold")
+ self.label.setObjectName("label")
+ self.verticalLayout.addWidget(self.label)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem)
+ self.buttonBox = QtWidgets.QDialogButtonBox(coupling_calc_dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout.addWidget(self.buttonBox)
+
+ self.retranslateUi(coupling_calc_dialog)
+ self.buttonBox.accepted.connect(coupling_calc_dialog.accept)
+ self.buttonBox.rejected.connect(coupling_calc_dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(coupling_calc_dialog)
+
+ def retranslateUi(self, coupling_calc_dialog):
+ _translate = QtCore.QCoreApplication.translate
+ coupling_calc_dialog.setWindowTitle(_translate("coupling_calc_dialog", "Calculate BPP coupling constants"))
+ self.label_2.setText(_translate("coupling_calc_dialog", "TextLabel"))
+ self.label.setText(_translate("coupling_calc_dialog", "TextLabel"))
diff --git a/nmreval/gui_qt/_py/coupling_t1_from_tau.py b/nmreval/gui_qt/_py/coupling_t1_from_tau.py
new file mode 100644
index 0000000..4a18eea
--- /dev/null
+++ b/nmreval/gui_qt/_py/coupling_t1_from_tau.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/coupling_t1_from_tau.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_couplingForm(object):
+ def setupUi(self, couplingForm):
+ couplingForm.setObjectName("couplingForm")
+ couplingForm.resize(400, 300)
+ self.horizontalLayout = QtWidgets.QHBoxLayout(couplingForm)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(couplingForm)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.lineEdit = LineEdit(couplingForm)
+ self.lineEdit.setObjectName("lineEdit")
+ self.horizontalLayout.addWidget(self.lineEdit)
+ self.radioButton_2 = QtWidgets.QRadioButton(couplingForm)
+ self.radioButton_2.setChecked(True)
+ self.radioButton_2.setObjectName("radioButton_2")
+ self.buttonGroup = QtWidgets.QButtonGroup(couplingForm)
+ self.buttonGroup.setObjectName("buttonGroup")
+ self.buttonGroup.addButton(self.radioButton_2)
+ self.horizontalLayout.addWidget(self.radioButton_2)
+ self.radioButton = QtWidgets.QRadioButton(couplingForm)
+ self.radioButton.setObjectName("radioButton")
+ self.buttonGroup.addButton(self.radioButton)
+ self.horizontalLayout.addWidget(self.radioButton)
+
+ self.retranslateUi(couplingForm)
+ QtCore.QMetaObject.connectSlotsByName(couplingForm)
+
+ def retranslateUi(self, couplingForm):
+ _translate = QtCore.QCoreApplication.translate
+ couplingForm.setWindowTitle(_translate("couplingForm", "Form"))
+ self.label.setText(_translate("couplingForm", "parameter_name"))
+ self.radioButton_2.setText(_translate("couplingForm", "Value"))
+ self.radioButton.setText(_translate("couplingForm", "Index"))
+from nmrevalgui.lib.forms import LineEdit
diff --git a/nmreval/gui_qt/_py/datawidget.py b/nmreval/gui_qt/_py/datawidget.py
new file mode 100644
index 0000000..498dbfe
--- /dev/null
+++ b/nmreval/gui_qt/_py/datawidget.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/datawidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_DataWidget(object):
+ def setupUi(self, DataWidget):
+ DataWidget.setObjectName("DataWidget")
+ DataWidget.resize(307, 847)
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(DataWidget)
+ self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_2.setSpacing(3)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.verticalLayout = QtWidgets.QVBoxLayout()
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.verticalLayout_2.addLayout(self.verticalLayout)
+ self.propwidget = ExpandableWidget(DataWidget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.propwidget.sizePolicy().hasHeightForWidth())
+ self.propwidget.setSizePolicy(sizePolicy)
+ self.propwidget.setObjectName("propwidget")
+ self.verticalLayout_2.addWidget(self.propwidget)
+ self.frame = QtWidgets.QFrame(DataWidget)
+ self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.frame.setObjectName("frame")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setSpacing(0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.graph_toolButton = QtWidgets.QToolButton(self.frame)
+ self.graph_toolButton.setObjectName("graph_toolButton")
+ self.horizontalLayout.addWidget(self.graph_toolButton)
+ self.empty_toolButton = QtWidgets.QToolButton(self.frame)
+ self.empty_toolButton.setObjectName("empty_toolButton")
+ self.horizontalLayout.addWidget(self.empty_toolButton)
+ self.func_toolButton = QtWidgets.QToolButton(self.frame)
+ self.func_toolButton.setText("")
+ self.func_toolButton.setObjectName("func_toolButton")
+ self.horizontalLayout.addWidget(self.func_toolButton)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.verticalLayout_2.addWidget(self.frame)
+
+ self.retranslateUi(DataWidget)
+ QtCore.QMetaObject.connectSlotsByName(DataWidget)
+
+ def retranslateUi(self, DataWidget):
+ _translate = QtCore.QCoreApplication.translate
+ DataWidget.setWindowTitle(_translate("DataWidget", "Data"))
+ self.graph_toolButton.setToolTip(_translate("DataWidget", "New graph"))
+ self.graph_toolButton.setText(_translate("DataWidget", "New graph"))
+ self.empty_toolButton.setToolTip(_translate("DataWidget", "New set"))
+ self.empty_toolButton.setText(_translate("DataWidget", "Empty set"))
+from ..lib.expandablewidget import ExpandableWidget
diff --git a/nmreval/gui_qt/_py/dscfile_dialog.py b/nmreval/gui_qt/_py/dscfile_dialog.py
new file mode 100644
index 0000000..12cb6d5
--- /dev/null
+++ b/nmreval/gui_qt/_py/dscfile_dialog.py
@@ -0,0 +1,213 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/dscfile_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.9.2
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(962, 662)
+ self.gridLayout_2 = QtWidgets.QGridLayout(Dialog)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Save)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout_2.addWidget(self.buttonBox, 1, 1, 1, 1)
+ self.gridLayout_4 = QtWidgets.QGridLayout()
+ self.gridLayout_4.setContentsMargins(-1, 0, 0, -1)
+ self.gridLayout_4.setSpacing(3)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ self.cp_checkBox = QtWidgets.QCheckBox(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.cp_checkBox.sizePolicy().hasHeightForWidth())
+ self.cp_checkBox.setSizePolicy(sizePolicy)
+ self.cp_checkBox.setChecked(True)
+ self.cp_checkBox.setObjectName("cp_checkBox")
+ self.gridLayout_4.addWidget(self.cp_checkBox, 11, 0, 1, 4)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ 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.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.reference_tableWidget.sizePolicy().hasHeightForWidth())
+ self.reference_tableWidget.setSizePolicy(sizePolicy)
+ self.reference_tableWidget.setMinimumSize(QtCore.QSize(0, 0))
+ self.reference_tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
+ self.reference_tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.reference_tableWidget.setColumnCount(2)
+ self.reference_tableWidget.setObjectName("reference_tableWidget")
+ self.reference_tableWidget.setRowCount(0)
+ self.reference_tableWidget.horizontalHeader().setVisible(False)
+ self.reference_tableWidget.horizontalHeader().setStretchLastSection(True)
+ self.reference_tableWidget.verticalHeader().setVisible(False)
+ self.gridLayout_4.addWidget(self.reference_tableWidget, 9, 0, 1, 4)
+ 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.setSpacing(3)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.ref_add_pushButton = QtWidgets.QPushButton(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.ref_add_pushButton.sizePolicy().hasHeightForWidth())
+ self.ref_add_pushButton.setSizePolicy(sizePolicy)
+ self.ref_add_pushButton.setObjectName("ref_add_pushButton")
+ self.horizontalLayout.addWidget(self.ref_add_pushButton)
+ self.ref_remove_pushButton = QtWidgets.QPushButton(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.ref_remove_pushButton.sizePolicy().hasHeightForWidth())
+ self.ref_remove_pushButton.setSizePolicy(sizePolicy)
+ self.ref_remove_pushButton.setObjectName("ref_remove_pushButton")
+ self.horizontalLayout.addWidget(self.ref_remove_pushButton)
+ self.gridLayout_4.addLayout(self.horizontalLayout, 10, 0, 1, 4)
+ self.line_2 = QtWidgets.QFrame(Dialog)
+ self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.gridLayout_4.addWidget(self.line_2, 2, 0, 1, 4)
+ self.gridLayout_2.addLayout(self.gridLayout_4, 0, 0, 1, 1)
+ self.gridLayout = QtWidgets.QGridLayout()
+ self.gridLayout.setObjectName("gridLayout")
+ self.raw_graph = PlotWidget(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.raw_graph.sizePolicy().hasHeightForWidth())
+ self.raw_graph.setSizePolicy(sizePolicy)
+ self.raw_graph.setMinimumSize(QtCore.QSize(300, 200))
+ self.raw_graph.setObjectName("raw_graph")
+ self.gridLayout.addWidget(self.raw_graph, 0, 0, 1, 1)
+ self.calib_graph = PlotWidget(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.calib_graph.sizePolicy().hasHeightForWidth())
+ self.calib_graph.setSizePolicy(sizePolicy)
+ self.calib_graph.setMinimumSize(QtCore.QSize(300, 200))
+ self.calib_graph.setObjectName("calib_graph")
+ self.gridLayout.addWidget(self.calib_graph, 1, 0, 1, 1)
+ self.baseline_graph = PlotWidget(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.baseline_graph.sizePolicy().hasHeightForWidth())
+ self.baseline_graph.setSizePolicy(sizePolicy)
+ self.baseline_graph.setMinimumSize(QtCore.QSize(300, 200))
+ self.baseline_graph.setObjectName("baseline_graph")
+ self.gridLayout.addWidget(self.baseline_graph, 0, 1, 1, 1)
+ self.end_graph = PlotWidget(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.end_graph.sizePolicy().hasHeightForWidth())
+ self.end_graph.setSizePolicy(sizePolicy)
+ self.end_graph.setMinimumSize(QtCore.QSize(0, 0))
+ self.end_graph.setObjectName("end_graph")
+ self.gridLayout.addWidget(self.end_graph, 1, 1, 1, 1)
+ self.gridLayout_2.addLayout(self.gridLayout, 0, 1, 1, 1)
+
+ self.retranslateUi(Dialog)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Read DSC file"))
+ self.cp_checkBox.setText(_translate("Dialog", "Convert to heat capacity"))
+ self.loadempty_button.setText(_translate("Dialog", "Load empty"))
+ self.delempty_button.setText(_translate("Dialog", "Remove empty"))
+ self.isotherm_radioButton.setText(_translate("Dialog", "Isotherms"))
+ 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.ref_add_pushButton.setText(_translate("Dialog", "Add reference"))
+ self.ref_remove_pushButton.setText(_translate("Dialog", "Remove reference"))
+
+from pyqtgraph import PlotWidget
diff --git a/nmreval/gui_qt/_py/editsignalwidget.py b/nmreval/gui_qt/_py/editsignalwidget.py
new file mode 100644
index 0000000..1372ab5
--- /dev/null
+++ b/nmreval/gui_qt/_py/editsignalwidget.py
@@ -0,0 +1,182 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/editsignalwidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(328, 732)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Form)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.groupBox_4 = QtWidgets.QGroupBox(Form)
+ self.groupBox_4.setObjectName("groupBox_4")
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.groupBox_4)
+ self.horizontalLayout_3.setContentsMargins(3, 3, 3, 3)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.baselinebutton = QtWidgets.QPushButton(self.groupBox_4)
+ self.baselinebutton.setObjectName("baselinebutton")
+ self.horizontalLayout_3.addWidget(self.baselinebutton)
+ self.verticalLayout.addWidget(self.groupBox_4)
+ self.groupBox = QtWidgets.QGroupBox(Form)
+ self.groupBox.setObjectName("groupBox")
+ self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox)
+ self.gridLayout_3.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_3.setObjectName("gridLayout_3")
+ self.leftshiftbutton = QtWidgets.QPushButton(self.groupBox)
+ self.leftshiftbutton.setObjectName("leftshiftbutton")
+ self.gridLayout_3.addWidget(self.leftshiftbutton, 1, 0, 1, 2)
+ self.comboBox = QtWidgets.QComboBox(self.groupBox)
+ self.comboBox.setObjectName("comboBox")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.gridLayout_3.addWidget(self.comboBox, 0, 0, 1, 1)
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_2.setContentsMargins(-1, 0, -1, -1)
+ self.verticalLayout_2.setSpacing(0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.lsspinBox = QtWidgets.QSpinBox(self.groupBox)
+ self.lsspinBox.setMaximum(9999)
+ self.lsspinBox.setObjectName("lsspinBox")
+ self.verticalLayout_2.addWidget(self.lsspinBox)
+ self.lineEdit = QtWidgets.QLineEdit(self.groupBox)
+ self.lineEdit.setObjectName("lineEdit")
+ self.verticalLayout_2.addWidget(self.lineEdit)
+ self.gridLayout_3.addLayout(self.verticalLayout_2, 0, 1, 1, 1)
+ self.verticalLayout.addWidget(self.groupBox)
+ self.groupBox_6 = QtWidgets.QGroupBox(Form)
+ self.groupBox_6.setFlat(False)
+ self.groupBox_6.setObjectName("groupBox_6")
+ self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.groupBox_6)
+ self.horizontalLayout_4.setContentsMargins(3, 3, 3, 3)
+ self.horizontalLayout_4.setObjectName("horizontalLayout_4")
+ self.zfbutton = QtWidgets.QPushButton(self.groupBox_6)
+ self.zfbutton.setObjectName("zfbutton")
+ self.horizontalLayout_4.addWidget(self.zfbutton)
+ self.verticalLayout.addWidget(self.groupBox_6)
+ self.groupBox_2 = QtWidgets.QGroupBox(Form)
+ self.groupBox_2.setFlat(False)
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox_2)
+ self.gridLayout_4.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_4.setVerticalSpacing(0)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ self.ph1slider = QtWidgets.QDoubleSpinBox(self.groupBox_2)
+ self.ph1slider.setMinimum(-360.0)
+ self.ph1slider.setMaximum(360.0)
+ self.ph1slider.setObjectName("ph1slider")
+ self.gridLayout_4.addWidget(self.ph1slider, 5, 1, 1, 1)
+ self.label_6 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_6.setObjectName("label_6")
+ self.gridLayout_4.addWidget(self.label_6, 4, 0, 3, 1)
+ self.pushButton_2 = QtWidgets.QPushButton(self.groupBox_2)
+ self.pushButton_2.setObjectName("pushButton_2")
+ self.gridLayout_4.addWidget(self.pushButton_2, 8, 0, 1, 1)
+ self.label = QtWidgets.QLabel(self.groupBox_2)
+ self.label.setObjectName("label")
+ self.gridLayout_4.addWidget(self.label, 1, 0, 3, 1)
+ self.pivot_lineedit = QtWidgets.QLineEdit(self.groupBox_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.pivot_lineedit.sizePolicy().hasHeightForWidth())
+ self.pivot_lineedit.setSizePolicy(sizePolicy)
+ self.pivot_lineedit.setInputMethodHints(QtCore.Qt.ImhDigitsOnly)
+ self.pivot_lineedit.setObjectName("pivot_lineedit")
+ self.gridLayout_4.addWidget(self.pivot_lineedit, 7, 1, 1, 1)
+ self.ph0slider = QtWidgets.QDoubleSpinBox(self.groupBox_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.ph0slider.sizePolicy().hasHeightForWidth())
+ self.ph0slider.setSizePolicy(sizePolicy)
+ self.ph0slider.setMinimum(-180.0)
+ self.ph0slider.setMaximum(180.0)
+ self.ph0slider.setObjectName("ph0slider")
+ self.gridLayout_4.addWidget(self.ph0slider, 2, 1, 1, 1)
+ self.label_8 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_8.setObjectName("label_8")
+ self.gridLayout_4.addWidget(self.label_8, 7, 0, 1, 1)
+ self.phasebutton = QtWidgets.QPushButton(self.groupBox_2)
+ self.phasebutton.setObjectName("phasebutton")
+ self.gridLayout_4.addWidget(self.phasebutton, 8, 1, 1, 1)
+ self.verticalLayout.addWidget(self.groupBox_2)
+ self.groupBox_3 = QtWidgets.QGroupBox(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.groupBox_3.sizePolicy().hasHeightForWidth())
+ self.groupBox_3.setSizePolicy(sizePolicy)
+ self.groupBox_3.setObjectName("groupBox_3")
+ self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.groupBox_3)
+ self.verticalLayout_7.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_7.setObjectName("verticalLayout_7")
+ self.apodcombobox = QtWidgets.QComboBox(self.groupBox_3)
+ self.apodcombobox.setObjectName("apodcombobox")
+ self.verticalLayout_7.addWidget(self.apodcombobox)
+ self.label_2 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_2.setIndent(3)
+ self.label_2.setObjectName("label_2")
+ self.verticalLayout_7.addWidget(self.label_2)
+ self.verticalLayout_8 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_8.setSpacing(0)
+ self.verticalLayout_8.setObjectName("verticalLayout_8")
+ self.verticalLayout_7.addLayout(self.verticalLayout_8)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.pushButton = QtWidgets.QPushButton(self.groupBox_3)
+ self.pushButton.setObjectName("pushButton")
+ self.horizontalLayout.addWidget(self.pushButton)
+ self.apodbutton = QtWidgets.QPushButton(self.groupBox_3)
+ self.apodbutton.setObjectName("apodbutton")
+ self.horizontalLayout.addWidget(self.apodbutton)
+ self.verticalLayout_7.addLayout(self.horizontalLayout)
+ self.verticalLayout.addWidget(self.groupBox_3)
+ self.groupBox_5 = QtWidgets.QGroupBox(Form)
+ self.groupBox_5.setObjectName("groupBox_5")
+ self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.groupBox_5)
+ self.horizontalLayout_5.setContentsMargins(3, 3, 3, 3)
+ self.horizontalLayout_5.setObjectName("horizontalLayout_5")
+ self.fourierutton = QtWidgets.QPushButton(self.groupBox_5)
+ self.fourierutton.setObjectName("fourierutton")
+ self.horizontalLayout_5.addWidget(self.fourierutton)
+ self.verticalLayout.addWidget(self.groupBox_5)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.groupBox_4.setTitle(_translate("Form", "Baseline correction"))
+ self.baselinebutton.setText(_translate("Form", "Apply"))
+ self.groupBox.setTitle(_translate("Form", "Left shift"))
+ self.leftshiftbutton.setText(_translate("Form", "Apply"))
+ self.comboBox.setItemText(0, _translate("Form", "Points"))
+ self.comboBox.setItemText(1, _translate("Form", "Seconds"))
+ self.groupBox_6.setTitle(_translate("Form", "Zerofilling"))
+ self.zfbutton.setText(_translate("Form", "Apply"))
+ self.groupBox_2.setTitle(_translate("Form", "Phase correction"))
+ self.label_6.setText(_translate("Form", "Phase 1"))
+ self.pushButton_2.setText(_translate("Form", "Preview"))
+ self.label.setText(_translate("Form", "Phase 0"))
+ self.pivot_lineedit.setText(_translate("Form", "0"))
+ self.label_8.setText(_translate("Form", "Pivot"))
+ self.phasebutton.setText(_translate("Form", "Apply"))
+ self.groupBox_3.setTitle(_translate("Form", "Apodization"))
+ self.label_2.setText(_translate("Form", "TextLabel"))
+ self.pushButton.setText(_translate("Form", "Preview"))
+ self.apodbutton.setText(_translate("Form", "Apply"))
+ self.groupBox_5.setTitle(_translate("Form", "FFT"))
+ self.fourierutton.setText(_translate("Form", "Apply"))
diff --git a/nmreval/gui_qt/_py/eval_expr_dialog.py b/nmreval/gui_qt/_py/eval_expr_dialog.py
new file mode 100644
index 0000000..d70a5ed
--- /dev/null
+++ b/nmreval/gui_qt/_py/eval_expr_dialog.py
@@ -0,0 +1,240 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/eval_expr_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_CalcDialog(object):
+ def setupUi(self, CalcDialog):
+ CalcDialog.setObjectName("CalcDialog")
+ CalcDialog.setWindowModality(QtCore.Qt.WindowModal)
+ CalcDialog.resize(804, 627)
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout(CalcDialog)
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.splitter_2 = QtWidgets.QSplitter(CalcDialog)
+ self.splitter_2.setOrientation(QtCore.Qt.Horizontal)
+ self.splitter_2.setObjectName("splitter_2")
+ self.verticalLayoutWidget = QtWidgets.QWidget(self.splitter_2)
+ self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.stackedWidget = QtWidgets.QStackedWidget(self.verticalLayoutWidget)
+ self.stackedWidget.setObjectName("stackedWidget")
+ self.page = QtWidgets.QWidget()
+ self.page.setObjectName("page")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.page)
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_2.setSpacing(0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.label_2 = QtWidgets.QLabel(self.page)
+ self.label_2.setObjectName("label_2")
+ self.verticalLayout_2.addWidget(self.label_2)
+ self.listWidget = QtWidgets.QListWidget(self.page)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
+ self.listWidget.setSizePolicy(sizePolicy)
+ self.listWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
+ self.listWidget.setObjectName("listWidget")
+ self.verticalLayout_2.addWidget(self.listWidget)
+ self.overwrite_checkbox = QtWidgets.QCheckBox(self.page)
+ self.overwrite_checkbox.setLayoutDirection(QtCore.Qt.LeftToRight)
+ self.overwrite_checkbox.setObjectName("overwrite_checkbox")
+ self.verticalLayout_2.addWidget(self.overwrite_checkbox)
+ self.stackedWidget.addWidget(self.page)
+ self.page_2 = QtWidgets.QWidget()
+ self.page_2.setObjectName("page_2")
+ self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.page_2)
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
+ self.groupBox_2 = QtWidgets.QGroupBox(self.page_2)
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.formLayout_3 = QtWidgets.QFormLayout(self.groupBox_2)
+ self.formLayout_3.setContentsMargins(3, 3, 3, 3)
+ self.formLayout_3.setHorizontalSpacing(3)
+ self.formLayout_3.setObjectName("formLayout_3")
+ self.label_3 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_3.setObjectName("label_3")
+ self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_3)
+ self.label_lineEdit = QtWidgets.QLineEdit(self.groupBox_2)
+ self.label_lineEdit.setObjectName("label_lineEdit")
+ self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.label_lineEdit)
+ self.label_9 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_9.setObjectName("label_9")
+ self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_9)
+ self.value_lineEdit = QtWidgets.QLineEdit(self.groupBox_2)
+ self.value_lineEdit.setObjectName("value_lineEdit")
+ self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.value_lineEdit)
+ self.label_6 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_6.setObjectName("label_6")
+ self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_6)
+ self.dtype_comboBox = QtWidgets.QComboBox(self.groupBox_2)
+ self.dtype_comboBox.setObjectName("dtype_comboBox")
+ self.dtype_comboBox.addItem("")
+ self.dtype_comboBox.addItem("")
+ self.dtype_comboBox.addItem("")
+ self.dtype_comboBox.addItem("")
+ self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.dtype_comboBox)
+ self.verticalLayout_4.addWidget(self.groupBox_2)
+ self.groupBox = QtWidgets.QGroupBox(self.page_2)
+ self.groupBox.setObjectName("groupBox")
+ self.formLayout_2 = QtWidgets.QFormLayout(self.groupBox)
+ self.formLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.formLayout_2.setSpacing(3)
+ self.formLayout_2.setObjectName("formLayout_2")
+ self.label_4 = QtWidgets.QLabel(self.groupBox)
+ self.label_4.setObjectName("label_4")
+ self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_4)
+ self.symcolor_comboBox = ColorListEditor(self.groupBox)
+ self.symcolor_comboBox.setObjectName("symcolor_comboBox")
+ self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.symcolor_comboBox)
+ self.label_5 = QtWidgets.QLabel(self.groupBox)
+ self.label_5.setObjectName("label_5")
+ self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_5)
+ self.symbol_spinBox = QtWidgets.QSpinBox(self.groupBox)
+ self.symbol_spinBox.setProperty("value", 10)
+ self.symbol_spinBox.setObjectName("symbol_spinBox")
+ self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.symbol_spinBox)
+ self.symbol_comboBox = SymbolStyleEditor(self.groupBox)
+ self.symbol_comboBox.setObjectName("symbol_comboBox")
+ self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.symbol_comboBox)
+ self.label_10 = QtWidgets.QLabel(self.groupBox)
+ self.label_10.setObjectName("label_10")
+ self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_10)
+ self.verticalLayout_4.addWidget(self.groupBox)
+ self.groupBox_3 = QtWidgets.QGroupBox(self.page_2)
+ self.groupBox_3.setObjectName("groupBox_3")
+ self.formLayout = QtWidgets.QFormLayout(self.groupBox_3)
+ self.formLayout.setContentsMargins(3, 3, 3, 3)
+ self.formLayout.setSpacing(3)
+ self.formLayout.setObjectName("formLayout")
+ self.label_7 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_7.setObjectName("label_7")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_7)
+ self.linecolor_comboBox = ColorListEditor(self.groupBox_3)
+ self.linecolor_comboBox.setObjectName("linecolor_comboBox")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.linecolor_comboBox)
+ self.label_8 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_8.setObjectName("label_8")
+ self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_8)
+ self.line_doubleSpinBox = QtWidgets.QDoubleSpinBox(self.groupBox_3)
+ self.line_doubleSpinBox.setProperty("value", 1.0)
+ self.line_doubleSpinBox.setObjectName("line_doubleSpinBox")
+ self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.line_doubleSpinBox)
+ self.linestyle_comboBox = LineStyleEditor(self.groupBox_3)
+ self.linestyle_comboBox.setObjectName("linestyle_comboBox")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.linestyle_comboBox)
+ self.label_11 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_11.setObjectName("label_11")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_11)
+ self.verticalLayout_4.addWidget(self.groupBox_3)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_4.addItem(spacerItem)
+ self.graph_comboBox = QtWidgets.QComboBox(self.page_2)
+ self.graph_comboBox.setObjectName("graph_comboBox")
+ self.verticalLayout_4.addWidget(self.graph_comboBox)
+ self.groupBox.raise_()
+ self.groupBox_2.raise_()
+ self.groupBox_3.raise_()
+ self.graph_comboBox.raise_()
+ self.stackedWidget.addWidget(self.page_2)
+ self.page_3 = QtWidgets.QWidget()
+ self.page_3.setObjectName("page_3")
+ self.stackedWidget.addWidget(self.page_3)
+ self.verticalLayout.addWidget(self.stackedWidget)
+ self.splitter = QtWidgets.QSplitter(self.splitter_2)
+ self.splitter.setOrientation(QtCore.Qt.Vertical)
+ self.splitter.setChildrenCollapsible(False)
+ self.splitter.setObjectName("splitter")
+ self.verticalLayoutWidget_2 = QtWidgets.QWidget(self.splitter)
+ self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2")
+ self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2)
+ self.verticalLayout_6.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_6.setObjectName("verticalLayout_6")
+ self.namespace_widget = QNamespaceWidget(self.verticalLayoutWidget_2)
+ self.namespace_widget.setObjectName("namespace_widget")
+ self.verticalLayout_6.addWidget(self.namespace_widget)
+ self.verticalLayoutWidget_3 = QtWidgets.QWidget(self.splitter)
+ self.verticalLayoutWidget_3.setObjectName("verticalLayoutWidget_3")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_3)
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.line_2 = QtWidgets.QFrame(self.verticalLayoutWidget_3)
+ self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.verticalLayout_3.addWidget(self.line_2)
+ self.label = QtWidgets.QLabel(self.verticalLayoutWidget_3)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.label.setFont(font)
+ self.label.setObjectName("label")
+ self.verticalLayout_3.addWidget(self.label)
+ self.calc_edit = QtWidgets.QPlainTextEdit(self.verticalLayoutWidget_3)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.calc_edit.sizePolicy().hasHeightForWidth())
+ self.calc_edit.setSizePolicy(sizePolicy)
+ self.calc_edit.setObjectName("calc_edit")
+ self.verticalLayout_3.addWidget(self.calc_edit)
+ self.verticalLayout_5.addWidget(self.splitter_2)
+ self.buttonBox = QtWidgets.QDialogButtonBox(CalcDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout_5.addWidget(self.buttonBox)
+ self.label_3.setBuddy(self.label_lineEdit)
+ self.label_9.setBuddy(self.value_lineEdit)
+ self.label_6.setBuddy(self.dtype_comboBox)
+ self.label_4.setBuddy(self.symcolor_comboBox)
+ self.label_5.setBuddy(self.symbol_spinBox)
+ self.label_7.setBuddy(self.linecolor_comboBox)
+ self.label_8.setBuddy(self.line_doubleSpinBox)
+
+ self.retranslateUi(CalcDialog)
+ self.stackedWidget.setCurrentIndex(2)
+ QtCore.QMetaObject.connectSlotsByName(CalcDialog)
+ CalcDialog.setTabOrder(self.calc_edit, self.listWidget)
+ CalcDialog.setTabOrder(self.listWidget, self.overwrite_checkbox)
+ CalcDialog.setTabOrder(self.overwrite_checkbox, self.label_lineEdit)
+ CalcDialog.setTabOrder(self.label_lineEdit, self.value_lineEdit)
+ CalcDialog.setTabOrder(self.value_lineEdit, self.dtype_comboBox)
+ CalcDialog.setTabOrder(self.dtype_comboBox, self.symcolor_comboBox)
+ CalcDialog.setTabOrder(self.symcolor_comboBox, self.symbol_spinBox)
+ CalcDialog.setTabOrder(self.symbol_spinBox, self.linecolor_comboBox)
+ CalcDialog.setTabOrder(self.linecolor_comboBox, self.line_doubleSpinBox)
+
+ def retranslateUi(self, CalcDialog):
+ _translate = QtCore.QCoreApplication.translate
+ CalcDialog.setWindowTitle(_translate("CalcDialog", "Evaluate stuff"))
+ self.label_2.setText(_translate("CalcDialog", "Select sets for evaluation"))
+ self.overwrite_checkbox.setText(_translate("CalcDialog", "Overwrite values"))
+ self.groupBox_2.setTitle(_translate("CalcDialog", "GroupBox"))
+ self.label_3.setText(_translate("CalcDialog", "Label"))
+ self.label_9.setText(_translate("CalcDialog", "Value"))
+ self.label_6.setText(_translate("CalcDialog", "Datatype"))
+ self.dtype_comboBox.setItemText(0, _translate("CalcDialog", "Points"))
+ self.dtype_comboBox.setItemText(1, _translate("CalcDialog", "Timesignal"))
+ self.dtype_comboBox.setItemText(2, _translate("CalcDialog", "Spectrum"))
+ self.dtype_comboBox.setItemText(3, _translate("CalcDialog", "BDS"))
+ self.groupBox.setTitle(_translate("CalcDialog", "Symbol"))
+ self.label_4.setText(_translate("CalcDialog", "Color"))
+ self.label_5.setText(_translate("CalcDialog", "Size"))
+ self.label_10.setText(_translate("CalcDialog", "Style"))
+ self.groupBox_3.setTitle(_translate("CalcDialog", "Line"))
+ self.label_7.setText(_translate("CalcDialog", "Color"))
+ self.label_8.setText(_translate("CalcDialog", "Width"))
+ self.label_11.setText(_translate("CalcDialog", "Style"))
+ self.label.setText(_translate("CalcDialog", "Expressions are evaluated line by line and change previous values"))
+from ..lib.delegates import ColorListEditor, LineStyleEditor, SymbolStyleEditor
+from ..lib.namespace import QNamespaceWidget
diff --git a/nmreval/gui_qt/_py/evalexpression.py b/nmreval/gui_qt/_py/evalexpression.py
new file mode 100644
index 0000000..43e0f9c
--- /dev/null
+++ b/nmreval/gui_qt/_py/evalexpression.py
@@ -0,0 +1,217 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/evalexpression.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_CalcDialog(object):
+ def setupUi(self, CalcDialog):
+ CalcDialog.setObjectName("CalcDialog")
+ CalcDialog.resize(895, 547)
+ self.gridLayout = QtWidgets.QGridLayout(CalcDialog)
+ self.gridLayout.setObjectName("gridLayout")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_2.setContentsMargins(-1, 0, -1, -1)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.tabWidget = QtWidgets.QTabWidget(CalcDialog)
+ self.tabWidget.setObjectName("tabWidget")
+ self.tab = QtWidgets.QWidget()
+ self.tab.setObjectName("tab")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.tab)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
+ self.horizontalLayout_2.setSpacing(0)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.textEdit = QtWidgets.QTextEdit(self.tab)
+ self.textEdit.setEnabled(True)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.textEdit.sizePolicy().hasHeightForWidth())
+ self.textEdit.setSizePolicy(sizePolicy)
+ self.textEdit.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.textEdit.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.textEdit.setAutoFormatting(QtWidgets.QTextEdit.AutoNone)
+ self.textEdit.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
+ self.textEdit.setObjectName("textEdit")
+ self.horizontalLayout_2.addWidget(self.textEdit)
+ self.textEdit_3 = QtWidgets.QTextEdit(self.tab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.textEdit_3.sizePolicy().hasHeightForWidth())
+ self.textEdit_3.setSizePolicy(sizePolicy)
+ self.textEdit_3.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.textEdit_3.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.textEdit_3.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
+ self.textEdit_3.setObjectName("textEdit_3")
+ self.horizontalLayout_2.addWidget(self.textEdit_3)
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
+ self.tabWidget.addTab(self.tab, "")
+ self.tab_2 = QtWidgets.QWidget()
+ self.tab_2.setObjectName("tab_2")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tab_2)
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_3.setSpacing(0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_3.setContentsMargins(-1, 0, -1, -1)
+ self.horizontalLayout_3.setSpacing(0)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.textEdit_2 = QtWidgets.QTextEdit(self.tab_2)
+ self.textEdit_2.setEnabled(True)
+ self.textEdit_2.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.textEdit_2.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.textEdit_2.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
+ self.textEdit_2.setObjectName("textEdit_2")
+ self.horizontalLayout_3.addWidget(self.textEdit_2)
+ self.textEdit_4 = QtWidgets.QTextEdit(self.tab_2)
+ self.textEdit_4.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.textEdit_4.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.textEdit_4.setReadOnly(True)
+ self.textEdit_4.setObjectName("textEdit_4")
+ self.horizontalLayout_3.addWidget(self.textEdit_4)
+ self.verticalLayout_3.addLayout(self.horizontalLayout_3)
+ self.tabWidget.addTab(self.tab_2, "")
+ self.tab_3 = QtWidgets.QWidget()
+ self.tab_3.setObjectName("tab_3")
+ self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.tab_3)
+ self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_4.setSpacing(0)
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
+ self.treeWidget = QtWidgets.QTreeWidget(self.tab_3)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.treeWidget.sizePolicy().hasHeightForWidth())
+ self.treeWidget.setSizePolicy(sizePolicy)
+ self.treeWidget.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.treeWidget.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.treeWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.treeWidget.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
+ self.treeWidget.setObjectName("treeWidget")
+ self.treeWidget.header().setVisible(False)
+ self.verticalLayout_4.addWidget(self.treeWidget)
+ self.tabWidget.addTab(self.tab_3, "")
+ self.verticalLayout_2.addWidget(self.tabWidget)
+ self.label = QtWidgets.QLabel(CalcDialog)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.label.setFont(font)
+ self.label.setObjectName("label")
+ self.verticalLayout_2.addWidget(self.label)
+ self.calc_edit = QtWidgets.QPlainTextEdit(CalcDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.calc_edit.sizePolicy().hasHeightForWidth())
+ self.calc_edit.setSizePolicy(sizePolicy)
+ self.calc_edit.setObjectName("calc_edit")
+ self.verticalLayout_2.addWidget(self.calc_edit)
+ self.gridLayout.addLayout(self.verticalLayout_2, 0, 3, 1, 1)
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_5.setSpacing(0)
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.label_2 = QtWidgets.QLabel(CalcDialog)
+ self.label_2.setObjectName("label_2")
+ self.verticalLayout_5.addWidget(self.label_2)
+ self.listWidget = QtWidgets.QListWidget(CalcDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
+ self.listWidget.setSizePolicy(sizePolicy)
+ self.listWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
+ self.listWidget.setObjectName("listWidget")
+ self.verticalLayout_5.addWidget(self.listWidget)
+ self.overwrite_checkbox = QtWidgets.QCheckBox(CalcDialog)
+ self.overwrite_checkbox.setLayoutDirection(QtCore.Qt.LeftToRight)
+ self.overwrite_checkbox.setObjectName("overwrite_checkbox")
+ self.verticalLayout_5.addWidget(self.overwrite_checkbox)
+ self.gridLayout.addLayout(self.verticalLayout_5, 0, 1, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(CalcDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 1, 3, 1, 1)
+ self.line = QtWidgets.QFrame(CalcDialog)
+ self.line.setFrameShape(QtWidgets.QFrame.VLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.gridLayout.addWidget(self.line, 0, 2, 1, 1)
+
+ self.retranslateUi(CalcDialog)
+ self.tabWidget.setCurrentIndex(0)
+ self.buttonBox.accepted.connect(CalcDialog.accept)
+ self.buttonBox.rejected.connect(CalcDialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(CalcDialog)
+
+ def retranslateUi(self, CalcDialog):
+ _translate = QtCore.QCoreApplication.translate
+ CalcDialog.setWindowTitle(_translate("CalcDialog", "Evaluate stuff"))
+ self.textEdit.setHtml(_translate("CalcDialog", "\n"
+" \n"
+"- X, y, and Δy values
\n"
+"- Values of dataset on position i in list
\n"
+" (s[i].x and x return the same values)
\n"
+"- Numpy functions
\n"
+"- If available, fit parameters
\n"
+" (see namespace for available parameters)
\n"
+"- Fit functions:
\n"
+" (meaning of p and extra arguments
\n"
+" depend on function)
\n"
+"- Constants:
\n"
+" (nuclei are accessed by const[\'gamma\'][\'1H\'] )
"))
+ self.textEdit_3.setHtml(_translate("CalcDialog", "\n"
+" \n"
+"x y y_err
\n"
+"s[i].x s[i+2].y s[i-1].y_err
\n"
+"
\n"
+"np.function
\n"
+"fit[\'NAME\']
\n"
+"
\n"
+"
\n"
+"Name.func(p, x, *args)
\n"
+"
\n"
+"
\n"
+"const[\'NAME\']
"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("CalcDialog", "Parameter"))
+ self.textEdit_2.setHtml(_translate("CalcDialog", "\n"
+" \n"
+"Substract neighbouring datasets:
\n"
+"Normalize on fit value M∞ :
\n"
+"Logscale x:
\n"
+"Division by exponential decay:
\n"
+"
"))
+ self.textEdit_4.setHtml(_translate("CalcDialog", "\n"
+" \n"
+"y = y-s[i+1].y
\n"
+"y = y/fit[\'M_infty\']
\n"
+"x = np.log10(x)
\n"
+"y = y/np.exp(-x/10)
\n"
+"y = y/Exponential_Decay.func([0, 1, 10, 1], x)
"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("CalcDialog", "Example"))
+ self.treeWidget.headerItem().setText(0, _translate("CalcDialog", "Namespace"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("CalcDialog", "Namespace"))
+ self.label.setText(_translate("CalcDialog", "Expressions are evaluated line by line and change previous values"))
+ self.label_2.setText(_translate("CalcDialog", "Select sets for evaluation (no selection = all visible):
"))
+ self.overwrite_checkbox.setText(_translate("CalcDialog", "Overwrite values?"))
diff --git a/nmreval/gui_qt/_py/expandablewidget.py b/nmreval/gui_qt/_py/expandablewidget.py
new file mode 100644
index 0000000..3fc84db
--- /dev/null
+++ b/nmreval/gui_qt/_py/expandablewidget.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/expandablewidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_ExpandableForm(object):
+ def setupUi(self, ExpandableForm):
+ ExpandableForm.setObjectName("ExpandableForm")
+ ExpandableForm.resize(400, 300)
+ self.verticalLayout = QtWidgets.QVBoxLayout(ExpandableForm)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setSpacing(0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.toolButton = QtWidgets.QToolButton(ExpandableForm)
+ self.toolButton.setStyleSheet("border: 0")
+ self.toolButton.setText("")
+ self.toolButton.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.toolButton.setArrowType(QtCore.Qt.RightArrow)
+ self.toolButton.setObjectName("toolButton")
+ self.horizontalLayout.addWidget(self.toolButton)
+ self.label = QtWidgets.QLabel(ExpandableForm)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
+ self.label.setSizePolicy(sizePolicy)
+ self.label.setText("")
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.line = QtWidgets.QFrame(ExpandableForm)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.horizontalLayout.addWidget(self.line)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+
+ self.retranslateUi(ExpandableForm)
+ QtCore.QMetaObject.connectSlotsByName(ExpandableForm)
+
+ def retranslateUi(self, ExpandableForm):
+ _translate = QtCore.QCoreApplication.translate
+ ExpandableForm.setWindowTitle(_translate("ExpandableForm", "Form"))
diff --git a/nmreval/gui_qt/_py/exportConfigTemplate.py b/nmreval/gui_qt/_py/exportConfigTemplate.py
new file mode 100644
index 0000000..9f64f56
--- /dev/null
+++ b/nmreval/gui_qt/_py/exportConfigTemplate.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/exportConfigTemplate.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(241, 367)
+ self.gridLayout = QtWidgets.QGridLayout(Form)
+ self.gridLayout.setSpacing(0)
+ self.gridLayout.setObjectName("gridLayout")
+ self.label = QtWidgets.QLabel(Form)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 0, 0, 1, 3)
+ self.itemTree = QtWidgets.QTreeWidget(Form)
+ self.itemTree.setObjectName("itemTree")
+ self.itemTree.headerItem().setText(0, "1")
+ self.itemTree.header().setVisible(False)
+ self.gridLayout.addWidget(self.itemTree, 1, 0, 1, 3)
+ self.label_2 = QtWidgets.QLabel(Form)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 2, 0, 1, 3)
+ self.formatList = QtWidgets.QListWidget(Form)
+ self.formatList.setObjectName("formatList")
+ self.gridLayout.addWidget(self.formatList, 3, 0, 1, 3)
+ self.exportBtn = QtWidgets.QPushButton(Form)
+ self.exportBtn.setObjectName("exportBtn")
+ self.gridLayout.addWidget(self.exportBtn, 6, 1, 1, 1)
+ self.closeBtn = QtWidgets.QPushButton(Form)
+ self.closeBtn.setObjectName("closeBtn")
+ self.gridLayout.addWidget(self.closeBtn, 6, 2, 1, 1)
+ self.paramTree = ParameterTree(Form)
+ self.paramTree.setColumnCount(2)
+ self.paramTree.setObjectName("paramTree")
+ self.paramTree.headerItem().setText(0, "1")
+ self.paramTree.header().setVisible(False)
+ self.gridLayout.addWidget(self.paramTree, 5, 0, 1, 3)
+ self.label_3 = QtWidgets.QLabel(Form)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout.addWidget(self.label_3, 4, 0, 1, 3)
+ self.copyBtn = QtWidgets.QPushButton(Form)
+ self.copyBtn.setObjectName("copyBtn")
+ self.gridLayout.addWidget(self.copyBtn, 6, 0, 1, 1)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Export"))
+ self.label.setText(_translate("Form", "Item to export:"))
+ self.label_2.setText(_translate("Form", "Export format"))
+ self.exportBtn.setText(_translate("Form", "Export"))
+ self.closeBtn.setText(_translate("Form", "Close"))
+ self.label_3.setText(_translate("Form", "Export options"))
+ self.copyBtn.setText(_translate("Form", "Copy"))
+from ..parametertree import ParameterTree
diff --git a/nmreval/gui_qt/_py/fcreader.py b/nmreval/gui_qt/_py/fcreader.py
new file mode 100644
index 0000000..766c641
--- /dev/null
+++ b/nmreval/gui_qt/_py/fcreader.py
@@ -0,0 +1,196 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fcreader.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_FCEval_dialog(object):
+ def setupUi(self, FCEval_dialog):
+ FCEval_dialog.setObjectName("FCEval_dialog")
+ FCEval_dialog.resize(457, 697)
+ self.verticalLayout = QtWidgets.QVBoxLayout(FCEval_dialog)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.input_box = QtWidgets.QGroupBox(FCEval_dialog)
+ self.input_box.setObjectName("input_box")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.input_box)
+ self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_2.setSpacing(3)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.file_pushbutton = QtWidgets.QPushButton(self.input_box)
+ self.file_pushbutton.setChecked(False)
+ self.file_pushbutton.setObjectName("file_pushbutton")
+ self.gridLayout_2.addWidget(self.file_pushbutton, 0, 0, 1, 1)
+ self.dir_pushbutton = QtWidgets.QPushButton(self.input_box)
+ self.dir_pushbutton.setObjectName("dir_pushbutton")
+ self.gridLayout_2.addWidget(self.dir_pushbutton, 0, 1, 1, 1)
+ self.listWidget = QtWidgets.QListWidget(self.input_box)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
+ self.listWidget.setSizePolicy(sizePolicy)
+ self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+ self.listWidget.setTextElideMode(QtCore.Qt.ElideLeft)
+ self.listWidget.setObjectName("listWidget")
+ self.gridLayout_2.addWidget(self.listWidget, 1, 0, 1, 3)
+ self.overwrite_cb = QtWidgets.QCheckBox(self.input_box)
+ self.overwrite_cb.setObjectName("overwrite_cb")
+ self.gridLayout_2.addWidget(self.overwrite_cb, 0, 2, 1, 1)
+ self.verticalLayout.addWidget(self.input_box)
+ self.region_box = QtWidgets.QGroupBox(FCEval_dialog)
+ self.region_box.setCheckable(True)
+ self.region_box.setObjectName("region_box")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.region_box)
+ self.horizontalLayout.setContentsMargins(3, 3, 3, 3)
+ self.horizontalLayout.setSpacing(3)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.start_lineedit = QtWidgets.QLineEdit(self.region_box)
+ self.start_lineedit.setObjectName("start_lineedit")
+ self.horizontalLayout.addWidget(self.start_lineedit)
+ self.stop_lineedit = QtWidgets.QLineEdit(self.region_box)
+ self.stop_lineedit.setObjectName("stop_lineedit")
+ self.horizontalLayout.addWidget(self.stop_lineedit)
+ self.verticalLayout.addWidget(self.region_box)
+ self.fit_box = QtWidgets.QGroupBox(FCEval_dialog)
+ self.fit_box.setObjectName("fit_box")
+ self.gridLayout_3 = QtWidgets.QGridLayout(self.fit_box)
+ self.gridLayout_3.setObjectName("gridLayout_3")
+ self.label_12 = QtWidgets.QLabel(self.fit_box)
+ self.label_12.setObjectName("label_12")
+ self.gridLayout_3.addWidget(self.label_12, 0, 0, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout_3.addItem(spacerItem, 0, 1, 1, 1)
+ self.kww_checkbox = QtWidgets.QCheckBox(self.fit_box)
+ self.kww_checkbox.setChecked(True)
+ self.kww_checkbox.setObjectName("kww_checkbox")
+ self.gridLayout_3.addWidget(self.kww_checkbox, 0, 2, 1, 1)
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_3.setSpacing(2)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.label_3 = QtWidgets.QLabel(self.fit_box)
+ self.label_3.setObjectName("label_3")
+ self.horizontalLayout_3.addWidget(self.label_3)
+ self.t1_cb = QtWidgets.QCheckBox(self.fit_box)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.t1_cb.sizePolicy().hasHeightForWidth())
+ self.t1_cb.setSizePolicy(sizePolicy)
+ self.t1_cb.setText("")
+ self.t1_cb.setChecked(True)
+ self.t1_cb.setObjectName("t1_cb")
+ self.horizontalLayout_3.addWidget(self.t1_cb)
+ self.label_4 = QtWidgets.QLabel(self.fit_box)
+ self.label_4.setObjectName("label_4")
+ self.horizontalLayout_3.addWidget(self.label_4)
+ self.beta_cb = QtWidgets.QCheckBox(self.fit_box)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.beta_cb.sizePolicy().hasHeightForWidth())
+ self.beta_cb.setSizePolicy(sizePolicy)
+ self.beta_cb.setChecked(True)
+ self.beta_cb.setObjectName("beta_cb")
+ self.horizontalLayout_3.addWidget(self.beta_cb)
+ self.label_5 = QtWidgets.QLabel(self.fit_box)
+ self.label_5.setObjectName("label_5")
+ self.horizontalLayout_3.addWidget(self.label_5)
+ self.m0_cb = QtWidgets.QCheckBox(self.fit_box)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.m0_cb.sizePolicy().hasHeightForWidth())
+ self.m0_cb.setSizePolicy(sizePolicy)
+ self.m0_cb.setText("")
+ self.m0_cb.setObjectName("m0_cb")
+ self.horizontalLayout_3.addWidget(self.m0_cb)
+ self.label_6 = QtWidgets.QLabel(self.fit_box)
+ self.label_6.setObjectName("label_6")
+ self.horizontalLayout_3.addWidget(self.label_6)
+ self.off_cb = QtWidgets.QCheckBox(self.fit_box)
+ self.off_cb.setObjectName("off_cb")
+ self.horizontalLayout_3.addWidget(self.off_cb)
+ self.gridLayout_3.addLayout(self.horizontalLayout_3, 1, 0, 1, 3)
+ self.verticalLayout.addWidget(self.fit_box)
+ self.out_box = QtWidgets.QGroupBox(FCEval_dialog)
+ self.out_box.setObjectName("out_box")
+ self.gridLayout = QtWidgets.QGridLayout(self.out_box)
+ self.gridLayout.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout.setObjectName("gridLayout")
+ self.savebutton = QtWidgets.QPushButton(self.out_box)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.savebutton.sizePolicy().hasHeightForWidth())
+ self.savebutton.setSizePolicy(sizePolicy)
+ self.savebutton.setObjectName("savebutton")
+ self.gridLayout.addWidget(self.savebutton, 0, 1, 1, 1)
+ self.line = QtWidgets.QFrame(self.out_box)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.gridLayout.addWidget(self.line, 2, 0, 1, 2)
+ self.graph_comboBox = QtWidgets.QComboBox(self.out_box)
+ self.graph_comboBox.setObjectName("graph_comboBox")
+ self.gridLayout.addWidget(self.graph_comboBox, 3, 1, 1, 1)
+ self.graph_checkbox = QtWidgets.QCheckBox(self.out_box)
+ self.graph_checkbox.setChecked(True)
+ self.graph_checkbox.setObjectName("graph_checkbox")
+ self.gridLayout.addWidget(self.graph_checkbox, 3, 0, 1, 1)
+ self.label = QtWidgets.QLabel(self.out_box)
+ self.label.setMaximumSize(QtCore.QSize(16777215, 16777215))
+ self.label.setText("")
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 1, 0, 1, 2)
+ self.label_2 = QtWidgets.QLabel(self.out_box)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 0, 0, 1, 1)
+ self.verticalLayout.addWidget(self.out_box)
+ spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(FCEval_dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout.addWidget(self.buttonBox)
+ self.label_12.setBuddy(self.kww_checkbox)
+ self.label_4.setBuddy(self.t1_cb)
+ self.label_5.setBuddy(self.beta_cb)
+ self.label_6.setBuddy(self.m0_cb)
+
+ self.retranslateUi(FCEval_dialog)
+ self.buttonBox.accepted.connect(FCEval_dialog.accept)
+ self.buttonBox.rejected.connect(FCEval_dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(FCEval_dialog)
+
+ def retranslateUi(self, FCEval_dialog):
+ _translate = QtCore.QCoreApplication.translate
+ FCEval_dialog.setWindowTitle(_translate("FCEval_dialog", "FC evaluation"))
+ self.input_box.setTitle(_translate("FCEval_dialog", "Input"))
+ self.file_pushbutton.setText(_translate("FCEval_dialog", "Add HDF files..."))
+ self.dir_pushbutton.setText(_translate("FCEval_dialog", "Add directory..."))
+ self.overwrite_cb.setText(_translate("FCEval_dialog", "Overwrite prev. data"))
+ self.region_box.setTitle(_translate("FCEval_dialog", "Evaluate region (empty values default to start/end)"))
+ self.start_lineedit.setPlaceholderText(_translate("FCEval_dialog", "start pos in µs"))
+ self.stop_lineedit.setPlaceholderText(_translate("FCEval_dialog", "end pos in µs"))
+ self.fit_box.setTitle(_translate("FCEval_dialog", "Fit equation"))
+ self.label_12.setText(_translate("FCEval_dialog", "y = M0 exp[-(x/T1 )β ] + Off"))
+ self.kww_checkbox.setToolTip(_translate("FCEval_dialog", "Check to fit a stretched exponential instead of exponential function."))
+ self.kww_checkbox.setText(_translate("FCEval_dialog", "Stretched exponential"))
+ self.label_3.setText(_translate("FCEval_dialog", "Plot:"))
+ self.label_4.setText(_translate("FCEval_dialog", "T1 "))
+ self.label_5.setText(_translate("FCEval_dialog", "β"))
+ self.label_6.setText(_translate("FCEval_dialog", "M0 "))
+ self.off_cb.setText(_translate("FCEval_dialog", "Offset"))
+ self.out_box.setTitle(_translate("FCEval_dialog", "Output"))
+ self.savebutton.setText(_translate("FCEval_dialog", "Change directory..."))
+ self.graph_checkbox.setText(_translate("FCEval_dialog", "New graph"))
+ self.label_2.setText(_translate("FCEval_dialog", "Save location"))
diff --git a/nmreval/gui_qt/_py/filedialog.py b/nmreval/gui_qt/_py/filedialog.py
new file mode 100644
index 0000000..ade0cd7
--- /dev/null
+++ b/nmreval/gui_qt/_py/filedialog.py
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/filedialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_QFileDialog(object):
+ def setupUi(self, QFileDialog):
+ QFileDialog.setObjectName("QFileDialog")
+ QFileDialog.resize(521, 316)
+ QFileDialog.setSizeGripEnabled(True)
+ self.gridlayout = QtWidgets.QGridLayout(QFileDialog)
+ self.gridlayout.setObjectName("gridlayout")
+ self.lookInLabel = QtWidgets.QLabel(QFileDialog)
+ self.lookInLabel.setObjectName("lookInLabel")
+ self.gridlayout.addWidget(self.lookInLabel, 0, 0, 1, 1)
+ self.hboxlayout = QtWidgets.QHBoxLayout()
+ self.hboxlayout.setObjectName("hboxlayout")
+ self.lookInCombo = QFileDialogComboBox(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(1)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.lookInCombo.sizePolicy().hasHeightForWidth())
+ self.lookInCombo.setSizePolicy(sizePolicy)
+ self.lookInCombo.setMinimumSize(QtCore.QSize(50, 0))
+ self.lookInCombo.setObjectName("lookInCombo")
+ self.hboxlayout.addWidget(self.lookInCombo)
+ self.backButton = QtWidgets.QToolButton(QFileDialog)
+ self.backButton.setObjectName("backButton")
+ self.hboxlayout.addWidget(self.backButton)
+ self.forwardButton = QtWidgets.QToolButton(QFileDialog)
+ self.forwardButton.setObjectName("forwardButton")
+ self.hboxlayout.addWidget(self.forwardButton)
+ self.toParentButton = QtWidgets.QToolButton(QFileDialog)
+ self.toParentButton.setObjectName("toParentButton")
+ self.hboxlayout.addWidget(self.toParentButton)
+ self.newFolderButton = QtWidgets.QToolButton(QFileDialog)
+ self.newFolderButton.setObjectName("newFolderButton")
+ self.hboxlayout.addWidget(self.newFolderButton)
+ self.listModeButton = QtWidgets.QToolButton(QFileDialog)
+ self.listModeButton.setObjectName("listModeButton")
+ self.hboxlayout.addWidget(self.listModeButton)
+ self.detailModeButton = QtWidgets.QToolButton(QFileDialog)
+ self.detailModeButton.setObjectName("detailModeButton")
+ self.hboxlayout.addWidget(self.detailModeButton)
+ self.gridlayout.addLayout(self.hboxlayout, 0, 1, 1, 2)
+ self.splitter = QtWidgets.QSplitter(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.splitter.sizePolicy().hasHeightForWidth())
+ self.splitter.setSizePolicy(sizePolicy)
+ self.splitter.setOrientation(QtCore.Qt.Horizontal)
+ self.splitter.setChildrenCollapsible(False)
+ self.splitter.setObjectName("splitter")
+ self.sidebar = QSidebar(self.splitter)
+ self.sidebar.setObjectName("sidebar")
+ self.frame = QtWidgets.QFrame(self.splitter)
+ self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
+ self.frame.setObjectName("frame")
+ self.vboxlayout = QtWidgets.QVBoxLayout(self.frame)
+ self.vboxlayout.setContentsMargins(0, 0, 0, 0)
+ self.vboxlayout.setSpacing(0)
+ self.vboxlayout.setObjectName("vboxlayout")
+ self.stackedWidget = QtWidgets.QStackedWidget(self.frame)
+ self.stackedWidget.setObjectName("stackedWidget")
+ self.page = QtWidgets.QWidget()
+ self.page.setObjectName("page")
+ self.vboxlayout1 = QtWidgets.QVBoxLayout(self.page)
+ self.vboxlayout1.setContentsMargins(0, 0, 0, 0)
+ self.vboxlayout1.setSpacing(0)
+ self.vboxlayout1.setObjectName("vboxlayout1")
+ self.listView = QFileDialogListView(self.page)
+ self.listView.setObjectName("listView")
+ self.vboxlayout1.addWidget(self.listView)
+ self.stackedWidget.addWidget(self.page)
+ self.page_2 = QtWidgets.QWidget()
+ self.page_2.setObjectName("page_2")
+ self.vboxlayout2 = QtWidgets.QVBoxLayout(self.page_2)
+ self.vboxlayout2.setContentsMargins(0, 0, 0, 0)
+ self.vboxlayout2.setSpacing(0)
+ self.vboxlayout2.setObjectName("vboxlayout2")
+ self.treeView = QFileDialogTreeView(self.page_2)
+ self.treeView.setObjectName("treeView")
+ self.vboxlayout2.addWidget(self.treeView)
+ self.stackedWidget.addWidget(self.page_2)
+ self.vboxlayout.addWidget(self.stackedWidget)
+ self.gridlayout.addWidget(self.splitter, 1, 0, 1, 3)
+ self.fileNameLabel = QtWidgets.QLabel(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fileNameLabel.sizePolicy().hasHeightForWidth())
+ self.fileNameLabel.setSizePolicy(sizePolicy)
+ self.fileNameLabel.setMinimumSize(QtCore.QSize(0, 0))
+ self.fileNameLabel.setObjectName("fileNameLabel")
+ self.gridlayout.addWidget(self.fileNameLabel, 2, 0, 1, 1)
+ self.fileNameEdit = QFileDialogLineEdit(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(1)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fileNameEdit.sizePolicy().hasHeightForWidth())
+ self.fileNameEdit.setSizePolicy(sizePolicy)
+ self.fileNameEdit.setObjectName("fileNameEdit")
+ self.gridlayout.addWidget(self.fileNameEdit, 2, 1, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(QFileDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Vertical)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridlayout.addWidget(self.buttonBox, 2, 2, 2, 1)
+ self.fileTypeLabel = QtWidgets.QLabel(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fileTypeLabel.sizePolicy().hasHeightForWidth())
+ self.fileTypeLabel.setSizePolicy(sizePolicy)
+ self.fileTypeLabel.setObjectName("fileTypeLabel")
+ self.gridlayout.addWidget(self.fileTypeLabel, 3, 0, 1, 1)
+ self.fileTypeCombo = QtWidgets.QComboBox(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fileTypeCombo.sizePolicy().hasHeightForWidth())
+ self.fileTypeCombo.setSizePolicy(sizePolicy)
+ self.fileTypeCombo.setObjectName("fileTypeCombo")
+ self.gridlayout.addWidget(self.fileTypeCombo, 3, 1, 1, 1)
+
+ self.retranslateUi(QFileDialog)
+ self.stackedWidget.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(QFileDialog)
+ QFileDialog.setTabOrder(self.lookInCombo, self.backButton)
+ QFileDialog.setTabOrder(self.backButton, self.forwardButton)
+ QFileDialog.setTabOrder(self.forwardButton, self.toParentButton)
+ QFileDialog.setTabOrder(self.toParentButton, self.newFolderButton)
+ QFileDialog.setTabOrder(self.newFolderButton, self.listModeButton)
+ QFileDialog.setTabOrder(self.listModeButton, self.detailModeButton)
+ QFileDialog.setTabOrder(self.detailModeButton, self.sidebar)
+ QFileDialog.setTabOrder(self.sidebar, self.treeView)
+ QFileDialog.setTabOrder(self.treeView, self.listView)
+ QFileDialog.setTabOrder(self.listView, self.fileNameEdit)
+ QFileDialog.setTabOrder(self.fileNameEdit, self.buttonBox)
+ QFileDialog.setTabOrder(self.buttonBox, self.fileTypeCombo)
+
+ def retranslateUi(self, QFileDialog):
+ _translate = QtCore.QCoreApplication.translate
+ self.lookInLabel.setText(_translate("QFileDialog", "Look in:"))
+ self.backButton.setToolTip(_translate("QFileDialog", "Back"))
+ self.backButton.setAccessibleName(_translate("QFileDialog", "Back"))
+ self.backButton.setAccessibleDescription(_translate("QFileDialog", "Go back"))
+ self.backButton.setShortcut(_translate("QFileDialog", "Alt+Left"))
+ self.forwardButton.setToolTip(_translate("QFileDialog", "Forward"))
+ self.forwardButton.setAccessibleName(_translate("QFileDialog", "Forward"))
+ self.forwardButton.setAccessibleDescription(_translate("QFileDialog", "Go forward"))
+ self.forwardButton.setShortcut(_translate("QFileDialog", "Alt+Right"))
+ self.toParentButton.setToolTip(_translate("QFileDialog", "Parent Directory"))
+ self.toParentButton.setAccessibleName(_translate("QFileDialog", "Parent Directory"))
+ self.toParentButton.setAccessibleDescription(_translate("QFileDialog", "Go to the parent directory"))
+ self.toParentButton.setShortcut(_translate("QFileDialog", "Alt+Up"))
+ self.newFolderButton.setToolTip(_translate("QFileDialog", "Create New Folder"))
+ self.newFolderButton.setAccessibleName(_translate("QFileDialog", "Create New Folder"))
+ self.newFolderButton.setAccessibleDescription(_translate("QFileDialog", "Create a New Folder"))
+ self.listModeButton.setToolTip(_translate("QFileDialog", "List View"))
+ self.listModeButton.setAccessibleName(_translate("QFileDialog", "List View"))
+ self.listModeButton.setAccessibleDescription(_translate("QFileDialog", "Change to list view mode"))
+ self.detailModeButton.setToolTip(_translate("QFileDialog", "Detail View"))
+ self.detailModeButton.setAccessibleName(_translate("QFileDialog", "Detail View"))
+ self.detailModeButton.setAccessibleDescription(_translate("QFileDialog", "Change to detail view mode"))
+ self.sidebar.setAccessibleName(_translate("QFileDialog", "Sidebar"))
+ self.sidebar.setAccessibleDescription(_translate("QFileDialog", "List of places and bookmarks"))
+ self.listView.setAccessibleName(_translate("QFileDialog", "Files"))
+ self.treeView.setAccessibleName(_translate("QFileDialog", "Files"))
+ self.fileTypeLabel.setText(_translate("QFileDialog", "Files of type:"))
+from private.qfiledialog_p import QFileDialogComboBox, QFileDialogLineEdit, QFileDialogListView, QFileDialogTreeView
+from private.qsidebar_p import QSidebar
diff --git a/nmreval/gui_qt/_py/fitcreationdialog.py b/nmreval/gui_qt/_py/fitcreationdialog.py
new file mode 100644
index 0000000..f2abd98
--- /dev/null
+++ b/nmreval/gui_qt/_py/fitcreationdialog.py
@@ -0,0 +1,189 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fitcreationdialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(614, 776)
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout(Dialog)
+ self.verticalLayout_5.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_5.setSpacing(3)
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.groupBox = QtWidgets.QGroupBox(Dialog)
+ self.groupBox.setCheckable(True)
+ self.groupBox.setChecked(False)
+ self.groupBox.setObjectName("groupBox")
+ self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.groupBox)
+ self.verticalLayout_7.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_7.setSpacing(3)
+ self.verticalLayout_7.setObjectName("verticalLayout_7")
+ self.widget_2 = QtWidgets.QWidget(self.groupBox)
+ self.widget_2.setObjectName("widget_2")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.widget_2)
+ self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout_2.setSpacing(3)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.name_lineedit = QtWidgets.QLineEdit(self.widget_2)
+ self.name_lineedit.setObjectName("name_lineedit")
+ self.gridLayout_2.addWidget(self.name_lineedit, 0, 1, 1, 1)
+ self.group_lineedit = QtWidgets.QLineEdit(self.widget_2)
+ self.group_lineedit.setObjectName("group_lineedit")
+ self.gridLayout_2.addWidget(self.group_lineedit, 1, 1, 1, 1)
+ self.group_label = QtWidgets.QLabel(self.widget_2)
+ self.group_label.setObjectName("group_label")
+ self.gridLayout_2.addWidget(self.group_label, 1, 0, 1, 1)
+ self.name_label = QtWidgets.QLabel(self.widget_2)
+ self.name_label.setObjectName("name_label")
+ self.gridLayout_2.addWidget(self.name_label, 0, 0, 1, 1)
+ self.lineEdit = QtWidgets.QLineEdit(self.widget_2)
+ self.lineEdit.setObjectName("lineEdit")
+ self.gridLayout_2.addWidget(self.lineEdit, 2, 1, 1, 1)
+ self.label_2 = QtWidgets.QLabel(self.widget_2)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout_2.addWidget(self.label_2, 2, 0, 1, 1)
+ self.verticalLayout_7.addWidget(self.widget_2)
+ self.verticalLayout_5.addWidget(self.groupBox)
+ self.groupBox_2 = QtWidgets.QGroupBox(Dialog)
+ self.groupBox_2.setCheckable(True)
+ self.groupBox_2.setChecked(False)
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_2)
+ self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_2.setSpacing(3)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.widget_3 = QtWidgets.QWidget(self.groupBox_2)
+ self.widget_3.setObjectName("widget_3")
+ self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.widget_3)
+ self.verticalLayout_8.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_8.setSpacing(3)
+ self.verticalLayout_8.setObjectName("verticalLayout_8")
+ self.tableWidget = QtWidgets.QTableWidget(self.widget_3)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tableWidget.sizePolicy().hasHeightForWidth())
+ self.tableWidget.setSizePolicy(sizePolicy)
+ self.tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
+ self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.tableWidget.setColumnCount(4)
+ self.tableWidget.setObjectName("tableWidget")
+ self.tableWidget.setRowCount(0)
+ item = QtWidgets.QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(0, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(1, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(2, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(3, item)
+ self.verticalLayout_8.addWidget(self.tableWidget)
+ self.parameter_button = QtWidgets.QToolButton(self.widget_3)
+ self.parameter_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.parameter_button.setAutoRaise(False)
+ self.parameter_button.setArrowType(QtCore.Qt.RightArrow)
+ self.parameter_button.setObjectName("parameter_button")
+ self.verticalLayout_8.addWidget(self.parameter_button)
+ self.verticalLayout_2.addWidget(self.widget_3)
+ self.verticalLayout_5.addWidget(self.groupBox_2)
+ self.groupBox_3 = QtWidgets.QGroupBox(Dialog)
+ self.groupBox_3.setCheckable(True)
+ self.groupBox_3.setChecked(False)
+ self.groupBox_3.setObjectName("groupBox_3")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_3)
+ self.verticalLayout_3.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_3.setSpacing(3)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.widget = QtWidgets.QWidget(self.groupBox_3)
+ self.widget.setObjectName("widget")
+ self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.widget)
+ self.verticalLayout_6.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_6.setSpacing(3)
+ self.verticalLayout_6.setObjectName("verticalLayout_6")
+ self.use_nuclei = QtWidgets.QCheckBox(self.widget)
+ self.use_nuclei.setObjectName("use_nuclei")
+ self.verticalLayout_6.addWidget(self.use_nuclei)
+ self.tabWidget = QtWidgets.QTabWidget(self.widget)
+ self.tabWidget.setTabPosition(QtWidgets.QTabWidget.West)
+ self.tabWidget.setTabsClosable(True)
+ self.tabWidget.setObjectName("tabWidget")
+ self.verticalLayout_6.addWidget(self.tabWidget)
+ self.selection_button = QtWidgets.QToolButton(self.widget)
+ self.selection_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.selection_button.setArrowType(QtCore.Qt.RightArrow)
+ self.selection_button.setObjectName("selection_button")
+ self.verticalLayout_6.addWidget(self.selection_button)
+ self.verticalLayout_3.addWidget(self.widget)
+ self.verticalLayout_5.addWidget(self.groupBox_3)
+ self.groupBox_4 = QtWidgets.QGroupBox(Dialog)
+ self.groupBox_4.setCheckable(True)
+ self.groupBox_4.setChecked(False)
+ self.groupBox_4.setObjectName("groupBox_4")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox_4)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.namespace_widget = QNamespaceWidget(self.groupBox_4)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.namespace_widget.sizePolicy().hasHeightForWidth())
+ self.namespace_widget.setSizePolicy(sizePolicy)
+ self.namespace_widget.setObjectName("namespace_widget")
+ self.verticalLayout.addWidget(self.namespace_widget)
+ self.verticalLayout_5.addWidget(self.groupBox_4)
+ self.frame_4 = QtWidgets.QFrame(Dialog)
+ self.frame_4.setObjectName("frame_4")
+ self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.frame_4)
+ self.verticalLayout_4.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_4.setSpacing(3)
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
+ self.label = QtWidgets.QLabel(self.frame_4)
+ self.label.setObjectName("label")
+ self.verticalLayout_4.addWidget(self.label)
+ self.plainTextEdit = CodeEditor(self.frame_4)
+ self.plainTextEdit.setObjectName("plainTextEdit")
+ self.verticalLayout_4.addWidget(self.plainTextEdit)
+ self.verticalLayout_5.addWidget(self.frame_4)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout_5.addWidget(self.buttonBox)
+ self.name_label.setBuddy(self.name_lineedit)
+
+ self.retranslateUi(Dialog)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
+ self.groupBox.setTitle(_translate("Dialog", "Description"))
+ self.group_label.setText(_translate("Dialog", "Group"))
+ self.name_label.setText(_translate("Dialog", "Name"))
+ self.label_2.setText(_translate("Dialog", "Equation"))
+ self.groupBox_2.setTitle(_translate("Dialog", "Variables"))
+ item = self.tableWidget.horizontalHeaderItem(0)
+ item.setText(_translate("Dialog", "Variable"))
+ item = self.tableWidget.horizontalHeaderItem(1)
+ item.setText(_translate("Dialog", "Name"))
+ item = self.tableWidget.horizontalHeaderItem(2)
+ item.setText(_translate("Dialog", "Lower bound"))
+ item = self.tableWidget.horizontalHeaderItem(3)
+ item.setText(_translate("Dialog", "Upper bound"))
+ self.parameter_button.setText(_translate("Dialog", "Add parameter"))
+ self.groupBox_3.setTitle(_translate("Dialog", "Multiple choice part"))
+ self.use_nuclei.setText(_translate("Dialog", "Add gyromagnetic ratios"))
+ self.selection_button.setText(_translate("Dialog", "Add selection"))
+ self.groupBox_4.setTitle(_translate("Dialog", "Available namespace"))
+ self.label.setText(_translate("Dialog", "Function y = func(x)"))
+from ..lib.codeeditor import CodeEditor
+from ..lib.namespace import QNamespaceWidget
diff --git a/nmreval/gui_qt/_py/fitdialog.py b/nmreval/gui_qt/_py/fitdialog.py
new file mode 100644
index 0000000..8ed9207
--- /dev/null
+++ b/nmreval/gui_qt/_py/fitdialog.py
@@ -0,0 +1,159 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fitdialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_FitDialog(object):
+ def setupUi(self, FitDialog):
+ FitDialog.setObjectName("FitDialog")
+ FitDialog.resize(347, 710)
+ self.verticalLayout = QtWidgets.QVBoxLayout(FitDialog)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.scrollArea = QtWidgets.QScrollArea(FitDialog)
+ self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.scrollArea.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+ self.scrollArea.setWidgetResizable(True)
+ self.scrollArea.setObjectName("scrollArea")
+ self.scrollAreaWidgetContents_2 = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 341, 665))
+ self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_2)
+ self.gridLayout_2.setContentsMargins(0, 0, 0, -1)
+ self.gridLayout_2.setSpacing(3)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.weight_combobox = QtWidgets.QComboBox(self.scrollAreaWidgetContents_2)
+ self.weight_combobox.setObjectName("weight_combobox")
+ 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.newmodel_button = QtWidgets.QPushButton(self.scrollAreaWidgetContents_2)
+ self.newmodel_button.setEnabled(False)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.newmodel_button.sizePolicy().hasHeightForWidth())
+ self.newmodel_button.setSizePolicy(sizePolicy)
+ self.newmodel_button.setObjectName("newmodel_button")
+ self.gridLayout_2.addWidget(self.newmodel_button, 2, 0, 1, 1)
+ self.deletemodel_button = QtWidgets.QPushButton(self.scrollAreaWidgetContents_2)
+ self.deletemodel_button.setEnabled(False)
+ self.deletemodel_button.setObjectName("deletemodel_button")
+ self.gridLayout_2.addWidget(self.deletemodel_button, 2, 1, 1, 1)
+ self.label_3 = QtWidgets.QLabel(self.scrollAreaWidgetContents_2)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout_2.addWidget(self.label_3, 6, 0, 1, 1)
+ self.stackedWidget = QtWidgets.QStackedWidget(self.scrollAreaWidgetContents_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth())
+ self.stackedWidget.setSizePolicy(sizePolicy)
+ self.stackedWidget.setObjectName("stackedWidget")
+ self.page = QtWidgets.QWidget()
+ self.page.setObjectName("page")
+ self.stackedWidget.addWidget(self.page)
+ self.gridLayout_2.addWidget(self.stackedWidget, 1, 0, 1, 2)
+ self.functionwidget = QFunctionWidget(self.scrollAreaWidgetContents_2)
+ self.functionwidget.setObjectName("functionwidget")
+ self.gridLayout_2.addWidget(self.functionwidget, 0, 0, 1, 2)
+ self.model_frame = QtWidgets.QFrame(self.scrollAreaWidgetContents_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.model_frame.sizePolicy().hasHeightForWidth())
+ self.model_frame.setSizePolicy(sizePolicy)
+ self.model_frame.setObjectName("model_frame")
+ self.gridLayout = QtWidgets.QGridLayout(self.model_frame)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setHorizontalSpacing(0)
+ self.gridLayout.setVerticalSpacing(1)
+ self.gridLayout.setObjectName("gridLayout")
+ self.show_combobox = QtWidgets.QComboBox(self.model_frame)
+ self.show_combobox.setObjectName("show_combobox")
+ self.show_combobox.addItem("")
+ self.gridLayout.addWidget(self.show_combobox, 1, 1, 1, 1)
+ self.default_combobox = QtWidgets.QComboBox(self.model_frame)
+ self.default_combobox.setObjectName("default_combobox")
+ self.default_combobox.addItem("")
+ self.gridLayout.addWidget(self.default_combobox, 0, 1, 1, 1)
+ self.label_2 = QtWidgets.QLabel(self.model_frame)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
+ self.label = QtWidgets.QLabel(self.model_frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, 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, 0, 1, 1)
+ self.gridLayout_2.addWidget(self.model_frame, 3, 0, 1, 2)
+ self.data_widget = ExpandableWidget(self.scrollAreaWidgetContents_2)
+ self.data_widget.setMinimumSize(QtCore.QSize(0, 24))
+ self.data_widget.setObjectName("data_widget")
+ self.gridLayout_2.addWidget(self.data_widget, 5, 0, 1, 2)
+ self.scrollArea.setWidget(self.scrollAreaWidgetContents_2)
+ self.verticalLayout.addWidget(self.scrollArea)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setContentsMargins(-1, 0, -1, -1)
+ self.horizontalLayout.setSpacing(3)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.fit_button = QtWidgets.QPushButton(FitDialog)
+ self.fit_button.setStyleSheet("font-weight: bold")
+ self.fit_button.setObjectName("fit_button")
+ self.horizontalLayout.addWidget(self.fit_button)
+ self.abort_button = QtWidgets.QPushButton(FitDialog)
+ self.abort_button.setStyleSheet("font-weight: bold")
+ self.abort_button.setObjectName("abort_button")
+ self.horizontalLayout.addWidget(self.abort_button)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.preview_checkbox = QtWidgets.QCheckBox(FitDialog)
+ self.preview_checkbox.setObjectName("preview_checkbox")
+ self.horizontalLayout.addWidget(self.preview_checkbox)
+ self.preview_button = QtWidgets.QPushButton(FitDialog)
+ self.preview_button.setCheckable(False)
+ self.preview_button.setChecked(False)
+ self.preview_button.setFlat(False)
+ self.preview_button.setObjectName("preview_button")
+ self.horizontalLayout.addWidget(self.preview_button)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+
+ self.retranslateUi(FitDialog)
+ self.stackedWidget.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(FitDialog)
+
+ def retranslateUi(self, FitDialog):
+ _translate = QtCore.QCoreApplication.translate
+ FitDialog.setWindowTitle(_translate("FitDialog", "Form"))
+ self.weight_combobox.setItemText(0, _translate("FitDialog", "None"))
+ self.weight_combobox.setItemText(1, _translate("FitDialog", "y"))
+ self.weight_combobox.setItemText(2, _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.deletemodel_button.setText(_translate("FitDialog", "Delete model"))
+ self.label_3.setText(_translate("FitDialog", "Weight"))
+ self.show_combobox.setItemText(0, _translate("FitDialog", "Model a"))
+ self.default_combobox.setItemText(0, _translate("FitDialog", "Model a"))
+ self.label_2.setText(_translate("FitDialog", "Show model"))
+ self.label.setText(_translate("FitDialog", "Default"))
+ self.fit_button.setText(_translate("FitDialog", "Run fit!!!"))
+ self.abort_button.setText(_translate("FitDialog", "Abort"))
+ self.preview_checkbox.setText(_translate("FitDialog", "Preview"))
+ self.preview_button.setText(_translate("FitDialog", "Update"))
+from ..fit.fitfunction import QFunctionWidget
+from ..lib.expandablewidget import ExpandableWidget
diff --git a/nmreval/gui_qt/_py/fitdialog_window.py b/nmreval/gui_qt/_py/fitdialog_window.py
new file mode 100644
index 0000000..ad342de
--- /dev/null
+++ b/nmreval/gui_qt/_py/fitdialog_window.py
@@ -0,0 +1,285 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fitdialog_window.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_FitDialog(object):
+ def setupUi(self, FitDialog):
+ FitDialog.setObjectName("FitDialog")
+ FitDialog.setWindowModality(QtCore.Qt.ApplicationModal)
+ FitDialog.resize(828, 827)
+ icon = QtGui.QIcon()
+ icon.addPixmap(QtGui.QPixmap(":/logo.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ FitDialog.setWindowIcon(icon)
+ self.centralwidget = QtWidgets.QWidget(FitDialog)
+ self.centralwidget.setObjectName("centralwidget")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.splitter = QtWidgets.QSplitter(self.centralwidget)
+ self.splitter.setOrientation(QtCore.Qt.Horizontal)
+ self.splitter.setObjectName("splitter")
+ self.widget_2 = QtWidgets.QWidget(self.splitter)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.widget_2.sizePolicy().hasHeightForWidth())
+ self.widget_2.setSizePolicy(sizePolicy)
+ self.widget_2.setObjectName("widget_2")
+ self.gridLayout_3 = QtWidgets.QGridLayout(self.widget_2)
+ self.gridLayout_3.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_3.setSpacing(3)
+ self.gridLayout_3.setObjectName("gridLayout_3")
+ self.label_3 = QtWidgets.QLabel(self.widget_2)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout_3.addWidget(self.label_3, 3, 0, 1, 1)
+ self.weight_combobox = QtWidgets.QComboBox(self.widget_2)
+ self.weight_combobox.setObjectName("weight_combobox")
+ self.weight_combobox.addItem("")
+ self.weight_combobox.addItem("")
+ self.weight_combobox.addItem("")
+ self.weight_combobox.addItem("")
+ self.weight_combobox.addItem("")
+ self.gridLayout_3.addWidget(self.weight_combobox, 3, 1, 1, 1)
+ self.line = QtWidgets.QFrame(self.widget_2)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.gridLayout_3.addWidget(self.line, 2, 0, 1, 2)
+ self.tableWidget = QtWidgets.QTableWidget(self.widget_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tableWidget.sizePolicy().hasHeightForWidth())
+ self.tableWidget.setSizePolicy(sizePolicy)
+ self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+ self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.tableWidget.setShowGrid(False)
+ self.tableWidget.setGridStyle(QtCore.Qt.NoPen)
+ self.tableWidget.setColumnCount(2)
+ self.tableWidget.setObjectName("tableWidget")
+ self.tableWidget.setRowCount(0)
+ self.tableWidget.horizontalHeader().setVisible(False)
+ self.tableWidget.horizontalHeader().setStretchLastSection(True)
+ self.tableWidget.verticalHeader().setVisible(False)
+ self.gridLayout_3.addWidget(self.tableWidget, 0, 0, 1, 2)
+ self.horizontalFrame = QtWidgets.QFrame(self.widget_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.horizontalFrame.sizePolicy().hasHeightForWidth())
+ self.horizontalFrame.setSizePolicy(sizePolicy)
+ self.horizontalFrame.setObjectName("horizontalFrame")
+ self.gridLayout = QtWidgets.QGridLayout(self.horizontalFrame)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setHorizontalSpacing(0)
+ self.gridLayout.setVerticalSpacing(1)
+ self.gridLayout.setObjectName("gridLayout")
+ self.show_combobox = QtWidgets.QComboBox(self.horizontalFrame)
+ self.show_combobox.setObjectName("show_combobox")
+ self.show_combobox.addItem("")
+ self.gridLayout.addWidget(self.show_combobox, 1, 1, 1, 1)
+ self.label_2 = QtWidgets.QLabel(self.horizontalFrame)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
+ self.default_combobox = QtWidgets.QComboBox(self.horizontalFrame)
+ self.default_combobox.setObjectName("default_combobox")
+ self.default_combobox.addItem("")
+ self.gridLayout.addWidget(self.default_combobox, 0, 1, 1, 1)
+ self.label = QtWidgets.QLabel(self.horizontalFrame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, 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, 0, 1, 1)
+ self.gridLayout_3.addWidget(self.horizontalFrame, 1, 0, 1, 2)
+ self.middle_widget = QtWidgets.QWidget(self.splitter)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.middle_widget.sizePolicy().hasHeightForWidth())
+ self.middle_widget.setSizePolicy(sizePolicy)
+ self.middle_widget.setObjectName("middle_widget")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.middle_widget)
+ self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_2.setSpacing(3)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.newmodel_button = QtWidgets.QPushButton(self.middle_widget)
+ self.newmodel_button.setEnabled(False)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.newmodel_button.sizePolicy().hasHeightForWidth())
+ self.newmodel_button.setSizePolicy(sizePolicy)
+ self.newmodel_button.setObjectName("newmodel_button")
+ self.gridLayout_2.addWidget(self.newmodel_button, 2, 0, 1, 1)
+ self.functionwidget = FunctionSelectionWidget(self.middle_widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.functionwidget.sizePolicy().hasHeightForWidth())
+ self.functionwidget.setSizePolicy(sizePolicy)
+ self.functionwidget.setObjectName("functionwidget")
+ self.gridLayout_2.addWidget(self.functionwidget, 0, 0, 1, 2)
+ self.deletemodel_button = QtWidgets.QPushButton(self.middle_widget)
+ self.deletemodel_button.setEnabled(False)
+ self.deletemodel_button.setObjectName("deletemodel_button")
+ self.gridLayout_2.addWidget(self.deletemodel_button, 2, 1, 1, 1)
+ self.stackedWidget = QtWidgets.QStackedWidget(self.middle_widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth())
+ self.stackedWidget.setSizePolicy(sizePolicy)
+ self.stackedWidget.setObjectName("stackedWidget")
+ self.page = QtWidgets.QWidget()
+ self.page.setObjectName("page")
+ self.stackedWidget.addWidget(self.page)
+ self.page_2 = QtWidgets.QWidget()
+ self.page_2.setObjectName("page_2")
+ self.stackedWidget.addWidget(self.page_2)
+ self.gridLayout_2.addWidget(self.stackedWidget, 1, 0, 1, 2)
+ self.verticalLayout_2.addWidget(self.splitter)
+ self.frame_4 = QtWidgets.QFrame(self.centralwidget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.frame_4.sizePolicy().hasHeightForWidth())
+ self.frame_4.setSizePolicy(sizePolicy)
+ self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel)
+ self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised)
+ self.frame_4.setLineWidth(2)
+ self.frame_4.setMidLineWidth(0)
+ self.frame_4.setObjectName("frame_4")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame_4)
+ self.horizontalLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.horizontalLayout_2.setSpacing(3)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem)
+ self.fit_button = QtWidgets.QPushButton(self.frame_4)
+ self.fit_button.setObjectName("fit_button")
+ self.horizontalLayout_2.addWidget(self.fit_button)
+ self.abort_button = QtWidgets.QPushButton(self.frame_4)
+ self.abort_button.setObjectName("abort_button")
+ self.horizontalLayout_2.addWidget(self.abort_button)
+ self.preview_button = QtWidgets.QPushButton(self.frame_4)
+ icon1 = QtGui.QIcon()
+ icon1.addPixmap(QtGui.QPixmap(":/fit_preview.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.preview_button.setIcon(icon1)
+ self.preview_button.setCheckable(True)
+ self.preview_button.setObjectName("preview_button")
+ self.horizontalLayout_2.addWidget(self.preview_button)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem1)
+ self.verticalLayout_2.addWidget(self.frame_4)
+ FitDialog.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(FitDialog)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 828, 30))
+ self.menubar.setObjectName("menubar")
+ self.menuOptions = QtWidgets.QMenu(self.menubar)
+ self.menuOptions.setObjectName("menuOptions")
+ self.menuMethod = QtWidgets.QMenu(self.menuOptions)
+ self.menuMethod.setObjectName("menuMethod")
+ self.menuLimits = QtWidgets.QMenu(self.menuOptions)
+ self.menuLimits.setObjectName("menuLimits")
+ self.menuHelp = QtWidgets.QMenu(self.menubar)
+ self.menuHelp.setObjectName("menuHelp")
+ self.menuUser = QtWidgets.QMenu(self.menubar)
+ self.menuUser.setObjectName("menuUser")
+ FitDialog.setMenuBar(self.menubar)
+ self.statusBar = QtWidgets.QStatusBar(FitDialog)
+ self.statusBar.setObjectName("statusBar")
+ FitDialog.setStatusBar(self.statusBar)
+ self.action_nm = QtWidgets.QAction(FitDialog)
+ self.action_nm.setCheckable(True)
+ self.action_nm.setObjectName("action_nm")
+ self.action_odr = QtWidgets.QAction(FitDialog)
+ self.action_odr.setCheckable(True)
+ self.action_odr.setObjectName("action_odr")
+ self.action_lm = QtWidgets.QAction(FitDialog)
+ self.action_lm.setCheckable(True)
+ self.action_lm.setChecked(True)
+ self.action_lm.setObjectName("action_lm")
+ self.actionHelp = QtWidgets.QAction(FitDialog)
+ self.actionHelp.setObjectName("actionHelp")
+ self.actionOpen_editor = QtWidgets.QAction(FitDialog)
+ self.actionOpen_editor.setObjectName("actionOpen_editor")
+ self.actionPreview_points = QtWidgets.QAction(FitDialog)
+ self.actionPreview_points.setObjectName("actionPreview_points")
+ self.actionSave_current_model = QtWidgets.QAction(FitDialog)
+ self.actionSave_current_model.setObjectName("actionSave_current_model")
+ self.action_no_range = QtWidgets.QAction(FitDialog)
+ self.action_no_range.setCheckable(True)
+ self.action_no_range.setObjectName("action_no_range")
+ self.action_x_range = QtWidgets.QAction(FitDialog)
+ self.action_x_range.setCheckable(True)
+ self.action_x_range.setChecked(True)
+ self.action_x_range.setObjectName("action_x_range")
+ self.action_custom_range = QtWidgets.QAction(FitDialog)
+ self.action_custom_range.setCheckable(True)
+ self.action_custom_range.setObjectName("action_custom_range")
+ self.menuMethod.addAction(self.action_lm)
+ self.menuMethod.addAction(self.action_nm)
+ self.menuMethod.addAction(self.action_odr)
+ self.menuLimits.addAction(self.action_no_range)
+ self.menuLimits.addAction(self.action_x_range)
+ self.menuLimits.addAction(self.action_custom_range)
+ self.menuOptions.addAction(self.menuMethod.menuAction())
+ self.menuOptions.addAction(self.menuLimits.menuAction())
+ self.menuOptions.addSeparator()
+ self.menuOptions.addAction(self.actionPreview_points)
+ self.menuHelp.addAction(self.actionHelp)
+ self.menuUser.addAction(self.actionOpen_editor)
+ self.menuUser.addAction(self.actionSave_current_model)
+ self.menubar.addAction(self.menuOptions.menuAction())
+ self.menubar.addAction(self.menuUser.menuAction())
+ self.menubar.addAction(self.menuHelp.menuAction())
+
+ self.retranslateUi(FitDialog)
+ QtCore.QMetaObject.connectSlotsByName(FitDialog)
+
+ def retranslateUi(self, FitDialog):
+ _translate = QtCore.QCoreApplication.translate
+ FitDialog.setWindowTitle(_translate("FitDialog", "One must imagine Sisyphus happy."))
+ self.label_3.setText(_translate("FitDialog", "Weight"))
+ self.weight_combobox.setItemText(0, _translate("FitDialog", "None"))
+ self.weight_combobox.setItemText(1, _translate("FitDialog", "y"))
+ self.weight_combobox.setItemText(2, _translate("FitDialog", "y²"))
+ self.weight_combobox.setItemText(3, _translate("FitDialog", "Δy"))
+ self.weight_combobox.setItemText(4, _translate("FitDialog", "log(y)"))
+ self.show_combobox.setItemText(0, _translate("FitDialog", "Model a"))
+ self.label_2.setText(_translate("FitDialog", "Show model"))
+ self.default_combobox.setItemText(0, _translate("FitDialog", "Model a"))
+ self.label.setText(_translate("FitDialog", "Default"))
+ self.newmodel_button.setText(_translate("FitDialog", "New model"))
+ self.deletemodel_button.setText(_translate("FitDialog", "Delete model"))
+ self.fit_button.setText(_translate("FitDialog", "Run fit!!!"))
+ self.abort_button.setText(_translate("FitDialog", "Abort"))
+ self.preview_button.setText(_translate("FitDialog", "Preview"))
+ self.menuOptions.setTitle(_translate("FitDialog", "Options"))
+ self.menuMethod.setTitle(_translate("FitDialog", "Method"))
+ self.menuLimits.setTitle(_translate("FitDialog", "Limits"))
+ self.menuHelp.setTitle(_translate("FitDialog", "Help"))
+ self.menuUser.setTitle(_translate("FitDialog", "User"))
+ self.action_nm.setText(_translate("FitDialog", "Nelder-Mead"))
+ self.action_odr.setText(_translate("FitDialog", "ODR"))
+ self.action_lm.setText(_translate("FitDialog", "Default stuff"))
+ self.actionHelp.setText(_translate("FitDialog", "Help!"))
+ self.actionOpen_editor.setText(_translate("FitDialog", "Open editor..."))
+ self.actionPreview_points.setText(_translate("FitDialog", "Preview points..."))
+ self.actionSave_current_model.setText(_translate("FitDialog", "Save current model..."))
+ self.action_no_range.setText(_translate("FitDialog", "None"))
+ self.action_x_range.setText(_translate("FitDialog", "Visible x range"))
+ self.action_custom_range.setText(_translate("FitDialog", "Custom..."))
+from ..fit.function_selection import FunctionSelectionWidget
+import images_rc
diff --git a/nmreval/gui_qt/_py/fitfunctionwidget.py b/nmreval/gui_qt/_py/fitfunctionwidget.py
new file mode 100644
index 0000000..3035f2f
--- /dev/null
+++ b/nmreval/gui_qt/_py/fitfunctionwidget.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fitfunctionwidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(314, 232)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth())
+ Form.setSizePolicy(sizePolicy)
+ self.gridLayout = QtWidgets.QGridLayout(Form)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setSpacing(3)
+ self.gridLayout.setObjectName("gridLayout")
+ self.widget = ExpandableWidget(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
+ self.widget.setSizePolicy(sizePolicy)
+ self.widget.setObjectName("widget")
+ self.gridLayout.addWidget(self.widget, 4, 0, 1, 2)
+ self.complex_widget = QtWidgets.QWidget(Form)
+ self.complex_widget.setObjectName("complex_widget")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.complex_widget)
+ self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.label_2 = QtWidgets.QLabel(self.complex_widget)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout_2.addWidget(self.label_2, 1, 0, 1, 1)
+ self.complex_comboBox = QtWidgets.QComboBox(self.complex_widget)
+ self.complex_comboBox.setObjectName("complex_comboBox")
+ self.complex_comboBox.addItem("")
+ self.complex_comboBox.addItem("")
+ self.complex_comboBox.addItem("")
+ self.gridLayout_2.addWidget(self.complex_comboBox, 1, 1, 1, 1)
+ self.label = QtWidgets.QLabel(self.complex_widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
+ self.label.setSizePolicy(sizePolicy)
+ self.label.setObjectName("label")
+ self.gridLayout_2.addWidget(self.label, 0, 0, 1, 2)
+ self.gridLayout.addWidget(self.complex_widget, 5, 0, 1, 2)
+ self.use_function_button = QtWidgets.QToolButton(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.use_function_button.sizePolicy().hasHeightForWidth())
+ self.use_function_button.setSizePolicy(sizePolicy)
+ self.use_function_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.use_function_button.setAutoRaise(False)
+ self.use_function_button.setArrowType(QtCore.Qt.RightArrow)
+ self.use_function_button.setObjectName("use_function_button")
+ self.gridLayout.addWidget(self.use_function_button, 3, 1, 1, 1)
+ self.fitcomboBox = QtWidgets.QComboBox(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fitcomboBox.sizePolicy().hasHeightForWidth())
+ self.fitcomboBox.setSizePolicy(sizePolicy)
+ self.fitcomboBox.setObjectName("fitcomboBox")
+ self.gridLayout.addWidget(self.fitcomboBox, 1, 0, 1, 2)
+ self.typecomboBox = QtWidgets.QComboBox(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.typecomboBox.sizePolicy().hasHeightForWidth())
+ self.typecomboBox.setSizePolicy(sizePolicy)
+ self.typecomboBox.setObjectName("typecomboBox")
+ self.gridLayout.addWidget(self.typecomboBox, 0, 0, 1, 2)
+ self.fitequation = QtWidgets.QLabel(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fitequation.sizePolicy().hasHeightForWidth())
+ self.fitequation.setSizePolicy(sizePolicy)
+ self.fitequation.setWordWrap(True)
+ self.fitequation.setObjectName("fitequation")
+ self.gridLayout.addWidget(self.fitequation, 2, 0, 1, 2)
+ self.operator_combobox = QtWidgets.QComboBox(Form)
+ self.operator_combobox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
+ self.operator_combobox.setFrame(True)
+ self.operator_combobox.setObjectName("operator_combobox")
+ self.operator_combobox.addItem("")
+ self.operator_combobox.addItem("")
+ self.operator_combobox.addItem("")
+ self.operator_combobox.addItem("")
+ self.gridLayout.addWidget(self.operator_combobox, 3, 0, 1, 1)
+ self.use_combobox = QtWidgets.QComboBox(Form)
+ self.use_combobox.setObjectName("use_combobox")
+ self.gridLayout.addWidget(self.use_combobox, 6, 0, 1, 2)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.label_2.setText(_translate("Form", "Select part to fit"))
+ self.complex_comboBox.setItemText(0, _translate("Form", "Complex"))
+ self.complex_comboBox.setItemText(1, _translate("Form", "Real"))
+ self.complex_comboBox.setItemText(2, _translate("Form", "Imaginary"))
+ self.label.setText(_translate("Form", "Complex function found"))
+ self.use_function_button.setText(_translate("Form", "Use"))
+ self.fitequation.setText(_translate("Form", "Equation"))
+ self.operator_combobox.setItemText(0, _translate("Form", "Add"))
+ self.operator_combobox.setItemText(1, _translate("Form", "Multiply"))
+ self.operator_combobox.setItemText(2, _translate("Form", "Subtract"))
+ self.operator_combobox.setItemText(3, _translate("Form", "Divide by"))
+from ..lib.expandablewidget import ExpandableWidget
diff --git a/nmreval/gui_qt/_py/fitfuncwidget.py b/nmreval/gui_qt/_py/fitfuncwidget.py
new file mode 100644
index 0000000..38fa341
--- /dev/null
+++ b/nmreval/gui_qt/_py/fitfuncwidget.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fitfuncwidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_FormFit(object):
+ def setupUi(self, FormFit):
+ FormFit.setObjectName("FormFit")
+ FormFit.resize(292, 477)
+ self.verticalLayout = QtWidgets.QVBoxLayout(FormFit)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.tabWidget = QtWidgets.QTabWidget(FormFit)
+ self.tabWidget.setObjectName("tabWidget")
+ self.general_tab = QtWidgets.QWidget()
+ self.general_tab.setObjectName("general_tab")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.general_tab)
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_2.setSpacing(0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.scrollArea = QtWidgets.QScrollArea(self.general_tab)
+ self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.scrollArea.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.scrollArea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
+ self.scrollArea.setWidgetResizable(True)
+ self.scrollArea.setObjectName("scrollArea")
+ self.scrollwidget = QtWidgets.QWidget()
+ self.scrollwidget.setGeometry(QtCore.QRect(0, 0, 284, 442))
+ self.scrollwidget.setObjectName("scrollwidget")
+ self.scrollArea.setWidget(self.scrollwidget)
+ self.verticalLayout_2.addWidget(self.scrollArea)
+ self.tabWidget.addTab(self.general_tab, "")
+ self.data_tab = QtWidgets.QWidget()
+ self.data_tab.setObjectName("data_tab")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.data_tab)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.comboBox = QtWidgets.QComboBox(self.data_tab)
+ self.comboBox.setObjectName("comboBox")
+ self.verticalLayout_3.addWidget(self.comboBox)
+ self.scrollArea2 = QtWidgets.QScrollArea(self.data_tab)
+ self.scrollArea2.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.scrollArea2.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.scrollArea2.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
+ self.scrollArea2.setWidgetResizable(True)
+ self.scrollArea2.setObjectName("scrollArea2")
+ self.scrollwidget2 = QtWidgets.QWidget()
+ self.scrollwidget2.setGeometry(QtCore.QRect(0, 0, 272, 357))
+ self.scrollwidget2.setObjectName("scrollwidget2")
+ self.scrollArea2.setWidget(self.scrollwidget2)
+ self.verticalLayout_3.addWidget(self.scrollArea2)
+ self.tabWidget.addTab(self.data_tab, "")
+ self.verticalLayout.addWidget(self.tabWidget)
+
+ self.retranslateUi(FormFit)
+ self.tabWidget.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(FormFit)
+
+ def retranslateUi(self, FormFit):
+ _translate = QtCore.QCoreApplication.translate
+ FormFit.setWindowTitle(_translate("FormFit", "Form"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.general_tab), _translate("FormFit", "General settings"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.data_tab), _translate("FormFit", "Data parameter"))
diff --git a/nmreval/gui_qt/_py/fitfuncwidget_old.py b/nmreval/gui_qt/_py/fitfuncwidget_old.py
new file mode 100644
index 0000000..e949870
--- /dev/null
+++ b/nmreval/gui_qt/_py/fitfuncwidget_old.py
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fitfuncwidget_old.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_FormFit(object):
+ def setupUi(self, FormFit):
+ FormFit.setObjectName("FormFit")
+ FormFit.resize(402, 523)
+ self.verticalLayout = QtWidgets.QVBoxLayout(FormFit)
+ self.verticalLayout.setContentsMargins(0, 6, 0, 0)
+ self.verticalLayout.setSpacing(6)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.tabWidget = QtWidgets.QTabWidget(FormFit)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tabWidget.sizePolicy().hasHeightForWidth())
+ self.tabWidget.setSizePolicy(sizePolicy)
+ self.tabWidget.setObjectName("tabWidget")
+ self.general_tab = QtWidgets.QWidget()
+ self.general_tab.setObjectName("general_tab")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.general_tab)
+ self.verticalLayout_3.setContentsMargins(2, 2, 2, 2)
+ self.verticalLayout_3.setSpacing(2)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.scrollArea = QtWidgets.QScrollArea(self.general_tab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.scrollArea.sizePolicy().hasHeightForWidth())
+ self.scrollArea.setSizePolicy(sizePolicy)
+ self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.scrollArea.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
+ self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+ self.scrollArea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
+ self.scrollArea.setWidgetResizable(False)
+ self.scrollArea.setObjectName("scrollArea")
+ self.scrollwidget = QtWidgets.QWidget()
+ self.scrollwidget.setGeometry(QtCore.QRect(0, 0, 390, 478))
+ self.scrollwidget.setObjectName("scrollwidget")
+ self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.scrollwidget)
+ self.verticalLayout_6.setObjectName("verticalLayout_6")
+ self.scrollArea.setWidget(self.scrollwidget)
+ self.verticalLayout_3.addWidget(self.scrollArea)
+ self.tabWidget.addTab(self.general_tab, "")
+ self.data_tab = QtWidgets.QWidget()
+ self.data_tab.setObjectName("data_tab")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.data_tab)
+ self.verticalLayout_2.setContentsMargins(2, 2, 2, 2)
+ self.verticalLayout_2.setSpacing(2)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.comboBox = QtWidgets.QComboBox(self.data_tab)
+ self.comboBox.setObjectName("comboBox")
+ self.verticalLayout_2.addWidget(self.comboBox)
+ self.scrollArea2 = QtWidgets.QScrollArea(self.data_tab)
+ self.scrollArea2.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.scrollArea2.setWidgetResizable(True)
+ self.scrollArea2.setObjectName("scrollArea2")
+ self.scrollwidget2 = QtWidgets.QWidget()
+ self.scrollwidget2.setGeometry(QtCore.QRect(0, 0, 390, 444))
+ self.scrollwidget2.setObjectName("scrollwidget2")
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.scrollwidget2)
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.scrollArea2.setWidget(self.scrollwidget2)
+ self.verticalLayout_2.addWidget(self.scrollArea2)
+ self.tabWidget.addTab(self.data_tab, "")
+ self.verticalLayout.addWidget(self.tabWidget)
+
+ self.retranslateUi(FormFit)
+ self.tabWidget.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(FormFit)
+
+ def retranslateUi(self, FormFit):
+ _translate = QtCore.QCoreApplication.translate
+ FormFit.setWindowTitle(_translate("FormFit", "Form"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.general_tab), _translate("FormFit", "General settings"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.data_tab), _translate("FormFit", "Data parameter"))
diff --git a/nmreval/gui_qt/_py/fitmodelfixwidget.py b/nmreval/gui_qt/_py/fitmodelfixwidget.py
new file mode 100644
index 0000000..ef46181
--- /dev/null
+++ b/nmreval/gui_qt/_py/fitmodelfixwidget.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fitmodelfixwidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_FitFixParameter(object):
+ def setupUi(self, FitFixParameter):
+ FitFixParameter.setObjectName("FitFixParameter")
+ FitFixParameter.setWindowModality(QtCore.Qt.WindowModal)
+ FitFixParameter.resize(480, 267)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(FitFixParameter.sizePolicy().hasHeightForWidth())
+ FitFixParameter.setSizePolicy(sizePolicy)
+ FitFixParameter.setAutoFillBackground(True)
+ self.gridLayout = QtWidgets.QGridLayout(FitFixParameter)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setSpacing(0)
+ self.gridLayout.setObjectName("gridLayout")
+ self.frame = QtWidgets.QFrame(FitFixParameter)
+ self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.frame.setObjectName("frame")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame)
+ self.horizontalLayout.setContentsMargins(0, 0, 3, 0)
+ self.horizontalLayout.setSpacing(3)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.parametername = QtWidgets.QLabel(self.frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(1)
+ sizePolicy.setVerticalStretch(10)
+ sizePolicy.setHeightForWidth(self.parametername.sizePolicy().hasHeightForWidth())
+ self.parametername.setSizePolicy(sizePolicy)
+ self.parametername.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.parametername.setIndent(6)
+ self.parametername.setObjectName("parametername")
+ self.horizontalLayout.addWidget(self.parametername)
+ self.label = QtWidgets.QLabel(self.frame)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.gridLayout.addWidget(self.frame, 1, 0, 1, 1)
+ self.parameter_line = QtWidgets.QLineEdit(FitFixParameter)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth())
+ self.parameter_line.setSizePolicy(sizePolicy)
+ self.parameter_line.setObjectName("parameter_line")
+ self.gridLayout.addWidget(self.parameter_line, 1, 1, 1, 1)
+
+ self.retranslateUi(FitFixParameter)
+ QtCore.QMetaObject.connectSlotsByName(FitFixParameter)
+
+ def retranslateUi(self, FitFixParameter):
+ _translate = QtCore.QCoreApplication.translate
+ FitFixParameter.setWindowTitle(_translate("FitFixParameter", "Form"))
+ self.parametername.setText(_translate("FitFixParameter", "Parameter"))
+ self.label.setText(_translate("FitFixParameter", "Unit"))
+ self.parameter_line.setText(_translate("FitFixParameter", "1"))
diff --git a/nmreval/gui_qt/_py/fitmodelwidget.py b/nmreval/gui_qt/_py/fitmodelwidget.py
new file mode 100644
index 0000000..a41f14b
--- /dev/null
+++ b/nmreval/gui_qt/_py/fitmodelwidget.py
@@ -0,0 +1,135 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fitmodelwidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_FitParameter(object):
+ def setupUi(self, FitParameter):
+ FitParameter.setObjectName("FitParameter")
+ FitParameter.resize(365, 78)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(FitParameter.sizePolicy().hasHeightForWidth())
+ FitParameter.setSizePolicy(sizePolicy)
+ self.verticalLayout = QtWidgets.QVBoxLayout(FitParameter)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(1)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setSpacing(3)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.parametername = QtWidgets.QLabel(FitParameter)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.parametername.sizePolicy().hasHeightForWidth())
+ self.parametername.setSizePolicy(sizePolicy)
+ self.parametername.setMinimumSize(QtCore.QSize(28, 0))
+ self.parametername.setObjectName("parametername")
+ self.horizontalLayout_2.addWidget(self.parametername)
+ self.parameter_line = LineEdit(FitParameter)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth())
+ self.parameter_line.setSizePolicy(sizePolicy)
+ self.parameter_line.setText("")
+ self.parameter_line.setObjectName("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.setObjectName("fixed_check")
+ self.horizontalLayout_2.addWidget(self.fixed_check)
+ self.global_checkbox = QtWidgets.QCheckBox(FitParameter)
+ self.global_checkbox.setObjectName("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.frame = QtWidgets.QFrame(FitParameter)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth())
+ self.frame.setSizePolicy(sizePolicy)
+ self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.frame.setObjectName("frame")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setSpacing(3)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.checkBox = QtWidgets.QCheckBox(self.frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.checkBox.sizePolicy().hasHeightForWidth())
+ self.checkBox.setSizePolicy(sizePolicy)
+ self.checkBox.setLayoutDirection(QtCore.Qt.RightToLeft)
+ self.checkBox.setText("")
+ self.checkBox.setObjectName("checkBox")
+ self.horizontalLayout.addWidget(self.checkBox)
+ self.lineEdit = QtWidgets.QLineEdit(self.frame)
+ self.lineEdit.setEnabled(False)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.lineEdit.sizePolicy().hasHeightForWidth())
+ self.lineEdit.setSizePolicy(sizePolicy)
+ self.lineEdit.setText("")
+ self.lineEdit.setFrame(True)
+ self.lineEdit.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
+ self.lineEdit.setObjectName("lineEdit")
+ self.horizontalLayout.addWidget(self.lineEdit)
+ self.label_3 = QtWidgets.QLabel(self.frame)
+ self.label_3.setEnabled(True)
+ self.label_3.setTextFormat(QtCore.Qt.RichText)
+ self.label_3.setAlignment(QtCore.Qt.AlignCenter)
+ self.label_3.setObjectName("label_3")
+ self.horizontalLayout.addWidget(self.label_3)
+ self.lineEdit_2 = QtWidgets.QLineEdit(self.frame)
+ self.lineEdit_2.setEnabled(False)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.lineEdit_2.sizePolicy().hasHeightForWidth())
+ self.lineEdit_2.setSizePolicy(sizePolicy)
+ self.lineEdit_2.setText("")
+ self.lineEdit_2.setFrame(True)
+ self.lineEdit_2.setObjectName("lineEdit_2")
+ self.horizontalLayout.addWidget(self.lineEdit_2)
+ self.verticalLayout.addWidget(self.frame)
+ self.line = QtWidgets.QFrame(FitParameter)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.verticalLayout.addWidget(self.line)
+
+ self.retranslateUi(FitParameter)
+ QtCore.QMetaObject.connectSlotsByName(FitParameter)
+
+ def retranslateUi(self, FitParameter):
+ _translate = QtCore.QCoreApplication.translate
+ FitParameter.setWindowTitle(_translate("FitParameter", "Form"))
+ self.parametername.setText(_translate("FitParameter", "A"))
+ self.parameter_line.setToolTip(_translate("FitParameter", "Initial values"))
+ self.parameter_line.setPlaceholderText(_translate("FitParameter", "0"))
+ self.fixed_check.setText(_translate("FitParameter", "Fix"))
+ self.global_checkbox.setText(_translate("FitParameter", "Global"))
+ self.lineEdit.setToolTip(_translate("FitParameter", "Lower bound. Same bound is used for all data. Leave empty for no boundary condition.
"))
+ self.label_3.setText(_translate("FitParameter", "Textlabel"))
+ self.lineEdit_2.setToolTip(_translate("FitParameter", "Upper bound. Same bound is used for all data. Leave empty for no boundary condition.
"))
+from ..lib.forms import LineEdit
diff --git a/nmreval/gui_qt/_py/fitparametertable.py b/nmreval/gui_qt/_py/fitparametertable.py
new file mode 100644
index 0000000..5ad7241
--- /dev/null
+++ b/nmreval/gui_qt/_py/fitparametertable.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fitparametertable.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_FitParameterDialog(object):
+ def setupUi(self, FitParameterDialog):
+ FitParameterDialog.setObjectName("FitParameterDialog")
+ FitParameterDialog.resize(898, 583)
+ self.verticalLayout = QtWidgets.QVBoxLayout(FitParameterDialog)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.verticalLayout.addLayout(self.verticalLayout_2)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.pushButton = QtWidgets.QPushButton(FitParameterDialog)
+ self.pushButton.setObjectName("pushButton")
+ self.horizontalLayout.addWidget(self.pushButton)
+ self.buttonBox = QtWidgets.QDialogButtonBox(FitParameterDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.buttonBox.sizePolicy().hasHeightForWidth())
+ self.buttonBox.setSizePolicy(sizePolicy)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close)
+ self.buttonBox.setCenterButtons(False)
+ self.buttonBox.setObjectName("buttonBox")
+ self.horizontalLayout.addWidget(self.buttonBox)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+
+ self.retranslateUi(FitParameterDialog)
+ self.buttonBox.accepted.connect(FitParameterDialog.accept)
+ self.buttonBox.rejected.connect(FitParameterDialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(FitParameterDialog)
+
+ def retranslateUi(self, FitParameterDialog):
+ _translate = QtCore.QCoreApplication.translate
+ FitParameterDialog.setWindowTitle(_translate("FitParameterDialog", "Fitparameter"))
+ self.pushButton.setText(_translate("FitParameterDialog", "Copy"))
diff --git a/nmreval/gui_qt/_py/fitparameterwidget.py b/nmreval/gui_qt/_py/fitparameterwidget.py
new file mode 100644
index 0000000..6d282bd
--- /dev/null
+++ b/nmreval/gui_qt/_py/fitparameterwidget.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fitparameterwidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_FormFit(object):
+ def setupUi(self, FormFit):
+ FormFit.setObjectName("FormFit")
+ FormFit.resize(292, 477)
+ self.verticalLayout = QtWidgets.QVBoxLayout(FormFit)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.tabWidget = QtWidgets.QTabWidget(FormFit)
+ self.tabWidget.setObjectName("tabWidget")
+ self.general_tab = QtWidgets.QWidget()
+ self.general_tab.setObjectName("general_tab")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.general_tab)
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_2.setSpacing(0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.scrollArea = QtWidgets.QScrollArea(self.general_tab)
+ self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.scrollArea.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.scrollArea.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
+ self.scrollArea.setWidgetResizable(True)
+ self.scrollArea.setObjectName("scrollArea")
+ self.scrollwidget = QtWidgets.QWidget()
+ self.scrollwidget.setGeometry(QtCore.QRect(0, 0, 284, 442))
+ self.scrollwidget.setObjectName("scrollwidget")
+ self.scrollArea.setWidget(self.scrollwidget)
+ self.verticalLayout_2.addWidget(self.scrollArea)
+ self.tabWidget.addTab(self.general_tab, "")
+ self.data_tab = QtWidgets.QWidget()
+ self.data_tab.setObjectName("data_tab")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.data_tab)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.comboBox = QtWidgets.QComboBox(self.data_tab)
+ self.comboBox.setObjectName("comboBox")
+ self.verticalLayout_3.addWidget(self.comboBox)
+ self.scrollArea2 = QtWidgets.QScrollArea(self.data_tab)
+ self.scrollArea2.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.scrollArea2.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.scrollArea2.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
+ self.scrollArea2.setWidgetResizable(True)
+ self.scrollArea2.setObjectName("scrollArea2")
+ self.scrollwidget2 = QtWidgets.QWidget()
+ self.scrollwidget2.setGeometry(QtCore.QRect(0, 0, 272, 392))
+ self.scrollwidget2.setObjectName("scrollwidget2")
+ self.scrollArea2.setWidget(self.scrollwidget2)
+ self.verticalLayout_3.addWidget(self.scrollArea2)
+ self.tabWidget.addTab(self.data_tab, "")
+ self.verticalLayout.addWidget(self.tabWidget)
+
+ self.retranslateUi(FormFit)
+ self.tabWidget.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(FormFit)
+
+ def retranslateUi(self, FormFit):
+ _translate = QtCore.QCoreApplication.translate
+ FormFit.setWindowTitle(_translate("FormFit", "Form"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.general_tab), _translate("FormFit", "General settings"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.data_tab), _translate("FormFit", "Data parameter"))
diff --git a/nmreval/gui_qt/_py/fitresult.py b/nmreval/gui_qt/_py/fitresult.py
new file mode 100644
index 0000000..386e538
--- /dev/null
+++ b/nmreval/gui_qt/_py/fitresult.py
@@ -0,0 +1,180 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/fitresult.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(817, 584)
+ self.gridLayout = QtWidgets.QGridLayout(Dialog)
+ self.gridLayout.setObjectName("gridLayout")
+ 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.stack = QtWidgets.QToolBox(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.stack.sizePolicy().hasHeightForWidth())
+ self.stack.setSizePolicy(sizePolicy)
+ self.stack.setObjectName("stack")
+ self.page = QtWidgets.QWidget()
+ self.page.setGeometry(QtCore.QRect(0, 0, 399, 414))
+ self.page.setObjectName("page")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.page)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.graphicsView = GraphicsLayoutWidget(self.page)
+ self.graphicsView.setObjectName("graphicsView")
+ self.verticalLayout.addWidget(self.graphicsView)
+ self.logy_box = QtWidgets.QCheckBox(self.page)
+ self.logy_box.setLayoutDirection(QtCore.Qt.RightToLeft)
+ self.logy_box.setObjectName("logy_box")
+ self.verticalLayout.addWidget(self.logy_box)
+ self.stack.addItem(self.page, "")
+ self.page_2 = QtWidgets.QWidget()
+ self.page_2.setGeometry(QtCore.QRect(0, 0, 399, 414))
+ self.page_2.setObjectName("page_2")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.page_2)
+ self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_2.setSpacing(3)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.stats_tableWidget = QtWidgets.QTableWidget(self.page_2)
+ self.stats_tableWidget.setFrameShape(QtWidgets.QFrame.Box)
+ self.stats_tableWidget.setGridStyle(QtCore.Qt.NoPen)
+ self.stats_tableWidget.setColumnCount(1)
+ self.stats_tableWidget.setObjectName("stats_tableWidget")
+ self.stats_tableWidget.setRowCount(0)
+ self.stats_tableWidget.horizontalHeader().setVisible(False)
+ self.stats_tableWidget.horizontalHeader().setSortIndicatorShown(True)
+ self.verticalLayout_2.addWidget(self.stats_tableWidget)
+ self.stack.addItem(self.page_2, "")
+ self.page_3 = QtWidgets.QWidget()
+ self.page_3.setGeometry(QtCore.QRect(0, 0, 399, 414))
+ self.page_3.setObjectName("page_3")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.page_3)
+ self.verticalLayout_3.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_3.setSpacing(3)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.corr_tableWidget = QtWidgets.QTableWidget(self.page_3)
+ self.corr_tableWidget.setFrameShape(QtWidgets.QFrame.Box)
+ self.corr_tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.corr_tableWidget.setGridStyle(QtCore.Qt.NoPen)
+ self.corr_tableWidget.setObjectName("corr_tableWidget")
+ self.corr_tableWidget.setColumnCount(4)
+ self.corr_tableWidget.setRowCount(0)
+ item = QtWidgets.QTableWidgetItem()
+ self.corr_tableWidget.setHorizontalHeaderItem(0, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.corr_tableWidget.setHorizontalHeaderItem(1, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.corr_tableWidget.setHorizontalHeaderItem(2, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.corr_tableWidget.setHorizontalHeaderItem(3, item)
+ self.corr_tableWidget.horizontalHeader().setStretchLastSection(True)
+ self.corr_tableWidget.verticalHeader().setVisible(False)
+ self.verticalLayout_3.addWidget(self.corr_tableWidget)
+ self.stack.addItem(self.page_3, "")
+ self.gridLayout.addWidget(self.stack, 0, 1, 4, 1)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setSpacing(3)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.partial_checkBox = QtWidgets.QCheckBox(Dialog)
+ self.partial_checkBox.setObjectName("partial_checkBox")
+ self.horizontalLayout.addWidget(self.partial_checkBox)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.label_2 = QtWidgets.QLabel(Dialog)
+ self.label_2.setObjectName("label_2")
+ self.horizontalLayout.addWidget(self.label_2)
+ self.graph_checkBox = QtWidgets.QCheckBox(Dialog)
+ 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.horizontalLayout.addWidget(self.graph_checkBox)
+ self.graph_comboBox = QtWidgets.QComboBox(Dialog)
+ self.graph_comboBox.setEnabled(False)
+ self.graph_comboBox.setObjectName("graph_comboBox")
+ self.horizontalLayout.addWidget(self.graph_comboBox)
+ self.gridLayout.addLayout(self.horizontalLayout, 5, 0, 1, 2)
+ self.line_2 = QtWidgets.QFrame(Dialog)
+ self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.gridLayout.addWidget(self.line_2, 3, 0, 1, 1)
+ 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.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.retranslateUi(Dialog)
+ self.stack.setCurrentIndex(0)
+ self.stack.layout().setSpacing(0)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Fit results"))
+ self.logy_box.setText(_translate("Dialog", "logarithmic y axis"))
+ self.stack.setItemText(self.stack.indexOf(self.page), _translate("Dialog", "Plot"))
+ self.stack.setItemText(self.stack.indexOf(self.page_2), _translate("Dialog", "Statistics"))
+ item = self.corr_tableWidget.horizontalHeaderItem(0)
+ item.setText(_translate("Dialog", "Parameter 1"))
+ item = self.corr_tableWidget.horizontalHeaderItem(1)
+ item.setText(_translate("Dialog", "Parameter 2"))
+ item = self.corr_tableWidget.horizontalHeaderItem(2)
+ item.setText(_translate("Dialog", "Corr."))
+ item = self.corr_tableWidget.horizontalHeaderItem(3)
+ item.setText(_translate("Dialog", "Partial Corr."))
+ self.stack.setItemText(self.stack.indexOf(self.page_3), _translate("Dialog", "Correlations"))
+ self.partial_checkBox.setText(_translate("Dialog", "Plot partial functions"))
+ self.label_2.setText(_translate("Dialog", "Location of parameters:"))
+ self.graph_checkBox.setText(_translate("Dialog", "New graph"))
+ self.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit"))
+ self.del_prev_checkBox.setText(_translate("Dialog", "Delete previous fits"))
+from ..lib.forms import ElideComboBox
+from pyqtgraph import GraphicsLayoutWidget
diff --git a/nmreval/gui_qt/_py/ftdialog.py b/nmreval/gui_qt/_py/ftdialog.py
new file mode 100644
index 0000000..1fd694f
--- /dev/null
+++ b/nmreval/gui_qt/_py/ftdialog.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/ftdialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(400, 300)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.listWidget = QtWidgets.QListWidget(Dialog)
+ self.listWidget.setObjectName("listWidget")
+ self.verticalLayout.addWidget(self.listWidget)
+ self.mode_comboBox = QtWidgets.QComboBox(Dialog)
+ self.mode_comboBox.setObjectName("mode_comboBox")
+ self.mode_comboBox.addItem("")
+ self.mode_comboBox.addItem("")
+ self.mode_comboBox.addItem("")
+ self.verticalLayout.addWidget(self.mode_comboBox)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setContentsMargins(-1, 0, -1, -1)
+ self.horizontalLayout.setSpacing(3)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.graph_checkBox = QtWidgets.QCheckBox(Dialog)
+ self.graph_checkBox.setObjectName("graph_checkBox")
+ self.horizontalLayout.addWidget(self.graph_checkBox)
+ self.graph_comboBox = QtWidgets.QComboBox(Dialog)
+ self.graph_comboBox.setObjectName("graph_comboBox")
+ self.horizontalLayout.addWidget(self.graph_comboBox)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout.addWidget(self.buttonBox)
+
+ self.retranslateUi(Dialog)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Logarithmic Fourier"))
+ self.mode_comboBox.setItemText(0, _translate("Dialog", "Real"))
+ self.mode_comboBox.setItemText(1, _translate("Dialog", "Imag"))
+ self.mode_comboBox.setItemText(2, _translate("Dialog", "Complex"))
+ self.graph_checkBox.setText(_translate("Dialog", "New graph"))
diff --git a/nmreval/gui_qt/_py/function_tree_widget.py b/nmreval/gui_qt/_py/function_tree_widget.py
new file mode 100644
index 0000000..5235d8d
--- /dev/null
+++ b/nmreval/gui_qt/_py/function_tree_widget.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/function_tree_widget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(314, 232)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth())
+ Form.setSizePolicy(sizePolicy)
+ self.gridLayout = QtWidgets.QGridLayout(Form)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setSpacing(3)
+ self.gridLayout.setObjectName("gridLayout")
+ self.widget_2 = ExpandableWidget(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.widget_2.sizePolicy().hasHeightForWidth())
+ self.widget_2.setSizePolicy(sizePolicy)
+ self.widget_2.setObjectName("widget_2")
+ self.gridLayout.addWidget(self.widget_2, 4, 0, 1, 2)
+ self.widget = QtWidgets.QWidget(Form)
+ self.widget.setObjectName("widget")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.widget)
+ self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.label_2 = QtWidgets.QLabel(self.widget)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout_2.addWidget(self.label_2, 1, 0, 1, 1)
+ self.complex_comboBox = QtWidgets.QComboBox(self.widget)
+ self.complex_comboBox.setObjectName("complex_comboBox")
+ self.complex_comboBox.addItem("")
+ self.complex_comboBox.addItem("")
+ self.complex_comboBox.addItem("")
+ self.gridLayout_2.addWidget(self.complex_comboBox, 1, 1, 1, 1)
+ self.label = QtWidgets.QLabel(self.widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
+ self.label.setSizePolicy(sizePolicy)
+ self.label.setObjectName("label")
+ self.gridLayout_2.addWidget(self.label, 0, 0, 1, 2)
+ self.gridLayout.addWidget(self.widget, 5, 0, 1, 2)
+ self.use_function_button = QtWidgets.QToolButton(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.use_function_button.sizePolicy().hasHeightForWidth())
+ self.use_function_button.setSizePolicy(sizePolicy)
+ self.use_function_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.use_function_button.setAutoRaise(False)
+ self.use_function_button.setArrowType(QtCore.Qt.RightArrow)
+ self.use_function_button.setObjectName("use_function_button")
+ self.gridLayout.addWidget(self.use_function_button, 3, 1, 1, 1)
+ self.fitcomboBox = QtWidgets.QComboBox(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fitcomboBox.sizePolicy().hasHeightForWidth())
+ self.fitcomboBox.setSizePolicy(sizePolicy)
+ self.fitcomboBox.setObjectName("fitcomboBox")
+ self.gridLayout.addWidget(self.fitcomboBox, 1, 0, 1, 2)
+ self.typecomboBox = QtWidgets.QComboBox(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.typecomboBox.sizePolicy().hasHeightForWidth())
+ self.typecomboBox.setSizePolicy(sizePolicy)
+ self.typecomboBox.setObjectName("typecomboBox")
+ self.gridLayout.addWidget(self.typecomboBox, 0, 0, 1, 2)
+ self.fitequation = QtWidgets.QLabel(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fitequation.sizePolicy().hasHeightForWidth())
+ self.fitequation.setSizePolicy(sizePolicy)
+ self.fitequation.setWordWrap(True)
+ self.fitequation.setObjectName("fitequation")
+ self.gridLayout.addWidget(self.fitequation, 2, 0, 1, 2)
+ self.operator_combobox = QtWidgets.QComboBox(Form)
+ self.operator_combobox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
+ self.operator_combobox.setFrame(True)
+ self.operator_combobox.setObjectName("operator_combobox")
+ self.operator_combobox.addItem("")
+ self.operator_combobox.addItem("")
+ self.operator_combobox.addItem("")
+ self.operator_combobox.addItem("")
+ self.gridLayout.addWidget(self.operator_combobox, 3, 0, 1, 1)
+ self.comboBox_2 = QtWidgets.QComboBox(Form)
+ self.comboBox_2.setObjectName("comboBox_2")
+ self.gridLayout.addWidget(self.comboBox_2, 6, 0, 1, 2)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.label_2.setText(_translate("Form", "Select part to fit"))
+ self.complex_comboBox.setItemText(0, _translate("Form", "Complex"))
+ self.complex_comboBox.setItemText(1, _translate("Form", "Real"))
+ self.complex_comboBox.setItemText(2, _translate("Form", "Imaginary"))
+ self.label.setText(_translate("Form", "Complex function found"))
+ self.use_function_button.setText(_translate("Form", "Use"))
+ self.fitequation.setText(_translate("Form", "Equation"))
+ self.operator_combobox.setItemText(0, _translate("Form", "Add"))
+ self.operator_combobox.setItemText(1, _translate("Form", "Multiply"))
+ self.operator_combobox.setItemText(2, _translate("Form", "Subtract"))
+ self.operator_combobox.setItemText(3, _translate("Form", "Divide by"))
+from ..lib.expandablewidget import ExpandableWidget
diff --git a/nmreval/gui_qt/_py/gol.py b/nmreval/gui_qt/_py/gol.py
new file mode 100644
index 0000000..2d00e06
--- /dev/null
+++ b/nmreval/gui_qt/_py/gol.py
@@ -0,0 +1,276 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/gol.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(883, 732)
+ self.gridLayout_2 = QtWidgets.QGridLayout(Form)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.widget = QtWidgets.QWidget(Form)
+ self.widget.setObjectName("widget")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.widget)
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.vanish_shadow = QtWidgets.QRadioButton(self.widget)
+ self.vanish_shadow.setChecked(True)
+ self.vanish_shadow.setObjectName("vanish_shadow")
+ self.buttonGroup = QtWidgets.QButtonGroup(Form)
+ self.buttonGroup.setObjectName("buttonGroup")
+ self.buttonGroup.addButton(self.vanish_shadow)
+ self.verticalLayout_3.addWidget(self.vanish_shadow)
+ self.full_shadow = QtWidgets.QRadioButton(self.widget)
+ self.full_shadow.setObjectName("full_shadow")
+ self.buttonGroup.addButton(self.full_shadow)
+ self.verticalLayout_3.addWidget(self.full_shadow)
+ self.radioButton = QtWidgets.QRadioButton(self.widget)
+ self.radioButton.setObjectName("radioButton")
+ self.buttonGroup.addButton(self.radioButton)
+ self.verticalLayout_3.addWidget(self.radioButton)
+ self.line = QtWidgets.QFrame(self.widget)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.verticalLayout_3.addWidget(self.line)
+ self.faster_button = QtWidgets.QToolButton(self.widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.faster_button.sizePolicy().hasHeightForWidth())
+ self.faster_button.setSizePolicy(sizePolicy)
+ self.faster_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.faster_button.setArrowType(QtCore.Qt.RightArrow)
+ self.faster_button.setObjectName("faster_button")
+ self.verticalLayout_3.addWidget(self.faster_button)
+ self.velocity_label = QtWidgets.QLabel(self.widget)
+ self.velocity_label.setObjectName("velocity_label")
+ self.verticalLayout_3.addWidget(self.velocity_label)
+ self.slower_button = QtWidgets.QToolButton(self.widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.slower_button.sizePolicy().hasHeightForWidth())
+ self.slower_button.setSizePolicy(sizePolicy)
+ self.slower_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.slower_button.setArrowType(QtCore.Qt.LeftArrow)
+ self.slower_button.setObjectName("slower_button")
+ self.verticalLayout_3.addWidget(self.slower_button)
+ self.current_step = QtWidgets.QLabel(self.widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.current_step.sizePolicy().hasHeightForWidth())
+ self.current_step.setSizePolicy(sizePolicy)
+ self.current_step.setObjectName("current_step")
+ self.verticalLayout_3.addWidget(self.current_step)
+ self.pause_button = QtWidgets.QPushButton(self.widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.pause_button.sizePolicy().hasHeightForWidth())
+ self.pause_button.setSizePolicy(sizePolicy)
+ self.pause_button.setCheckable(True)
+ self.pause_button.setObjectName("pause_button")
+ self.verticalLayout_3.addWidget(self.pause_button)
+ self.line_3 = QtWidgets.QFrame(self.widget)
+ self.line_3.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_3.setObjectName("line_3")
+ self.verticalLayout_3.addWidget(self.line_3)
+ self.label_6 = QtWidgets.QLabel(self.widget)
+ self.label_6.setObjectName("label_6")
+ self.verticalLayout_3.addWidget(self.label_6)
+ self.cover_label = QtWidgets.QLabel(self.widget)
+ self.cover_label.setText("")
+ self.cover_label.setObjectName("cover_label")
+ self.verticalLayout_3.addWidget(self.cover_label)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem)
+ self.gridLayout_2.addWidget(self.widget, 0, 0, 1, 1)
+ self.view = QtWidgets.QGraphicsView(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.view.sizePolicy().hasHeightForWidth())
+ self.view.setSizePolicy(sizePolicy)
+ self.view.setStyleSheet("background-color: transparent")
+ self.view.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.view.setObjectName("view")
+ self.gridLayout_2.addWidget(self.view, 0, 1, 1, 1)
+ self.option_frame = QtWidgets.QFrame(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.option_frame.sizePolicy().hasHeightForWidth())
+ self.option_frame.setSizePolicy(sizePolicy)
+ self.option_frame.setFrameShape(QtWidgets.QFrame.Box)
+ self.option_frame.setObjectName("option_frame")
+ self.gridLayout = QtWidgets.QGridLayout(self.option_frame)
+ self.gridLayout.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout.setHorizontalSpacing(6)
+ self.gridLayout.setVerticalSpacing(3)
+ self.gridLayout.setObjectName("gridLayout")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.rule_label = QtWidgets.QLabel(self.option_frame)
+ self.rule_label.setObjectName("rule_label")
+ self.horizontalLayout_2.addWidget(self.rule_label)
+ self.rule_cb = QtWidgets.QComboBox(self.option_frame)
+ self.rule_cb.setObjectName("rule_cb")
+ self.horizontalLayout_2.addWidget(self.rule_cb)
+ self.gridLayout.addLayout(self.horizontalLayout_2, 3, 0, 1, 1)
+ self.line_2 = QtWidgets.QFrame(self.option_frame)
+ self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.gridLayout.addWidget(self.line_2, 2, 0, 1, 1)
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_2.setContentsMargins(-1, 0, -1, 0)
+ self.verticalLayout_2.setSpacing(3)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.object_widget = QtWidgets.QWidget(self.option_frame)
+ self.object_widget.setObjectName("object_widget")
+ self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.object_widget)
+ self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_6.setSpacing(3)
+ self.horizontalLayout_6.setObjectName("horizontalLayout_6")
+ self.label_5 = QtWidgets.QLabel(self.object_widget)
+ self.label_5.setObjectName("label_5")
+ self.horizontalLayout_6.addWidget(self.label_5)
+ self.object_size = QtWidgets.QSpinBox(self.object_widget)
+ self.object_size.setMinimum(1)
+ self.object_size.setMaximum(600)
+ self.object_size.setObjectName("object_size")
+ self.horizontalLayout_6.addWidget(self.object_size)
+ self.verticalLayout_2.addWidget(self.object_widget)
+ self.rand_button_wdgt = QtWidgets.QWidget(self.option_frame)
+ self.rand_button_wdgt.setObjectName("rand_button_wdgt")
+ self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.rand_button_wdgt)
+ self.horizontalLayout_7.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_7.setSpacing(3)
+ self.horizontalLayout_7.setObjectName("horizontalLayout_7")
+ self.add_random_button = QtWidgets.QPushButton(self.rand_button_wdgt)
+ self.add_random_button.setObjectName("add_random_button")
+ self.horizontalLayout_7.addWidget(self.add_random_button)
+ self.remove_random_button = QtWidgets.QPushButton(self.rand_button_wdgt)
+ self.remove_random_button.setObjectName("remove_random_button")
+ self.horizontalLayout_7.addWidget(self.remove_random_button)
+ self.verticalLayout_2.addWidget(self.rand_button_wdgt)
+ self.verticalLayout = QtWidgets.QVBoxLayout()
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.verticalLayout_2.addLayout(self.verticalLayout)
+ self.gridLayout.addLayout(self.verticalLayout_2, 2, 1, 4, 1)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label_3 = QtWidgets.QLabel(self.option_frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
+ self.label_3.setSizePolicy(sizePolicy)
+ self.label_3.setObjectName("label_3")
+ self.horizontalLayout.addWidget(self.label_3)
+ self.survival_line = QtWidgets.QLineEdit(self.option_frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.survival_line.sizePolicy().hasHeightForWidth())
+ self.survival_line.setSizePolicy(sizePolicy)
+ self.survival_line.setObjectName("survival_line")
+ self.horizontalLayout.addWidget(self.survival_line)
+ self.label_4 = QtWidgets.QLabel(self.option_frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
+ self.label_4.setSizePolicy(sizePolicy)
+ self.label_4.setObjectName("label_4")
+ self.horizontalLayout.addWidget(self.label_4)
+ self.birth_line = QtWidgets.QLineEdit(self.option_frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.birth_line.sizePolicy().hasHeightForWidth())
+ self.birth_line.setSizePolicy(sizePolicy)
+ self.birth_line.setObjectName("birth_line")
+ self.horizontalLayout.addWidget(self.birth_line)
+ self.gridLayout.addLayout(self.horizontalLayout, 4, 0, 1, 1)
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.label = QtWidgets.QLabel(self.option_frame)
+ self.label.setObjectName("label")
+ self.horizontalLayout_3.addWidget(self.label)
+ self.width_box = QtWidgets.QSpinBox(self.option_frame)
+ self.width_box.setMaximum(600)
+ self.width_box.setProperty("value", 100)
+ self.width_box.setObjectName("width_box")
+ self.horizontalLayout_3.addWidget(self.width_box)
+ self.label_2 = QtWidgets.QLabel(self.option_frame)
+ self.label_2.setObjectName("label_2")
+ self.horizontalLayout_3.addWidget(self.label_2)
+ self.height_box = QtWidgets.QSpinBox(self.option_frame)
+ self.height_box.setMaximum(600)
+ self.height_box.setProperty("value", 100)
+ self.height_box.setObjectName("height_box")
+ self.horizontalLayout_3.addWidget(self.height_box)
+ self.gridLayout.addLayout(self.horizontalLayout_3, 1, 0, 1, 1)
+ self.object_combobox = QtWidgets.QComboBox(self.option_frame)
+ self.object_combobox.setObjectName("object_combobox")
+ self.object_combobox.addItem("")
+ self.object_combobox.addItem("")
+ self.object_combobox.addItem("")
+ self.object_combobox.addItem("")
+ self.object_combobox.addItem("")
+ self.gridLayout.addWidget(self.object_combobox, 1, 1, 1, 1)
+ self.start_button = QtWidgets.QPushButton(self.option_frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.start_button.sizePolicy().hasHeightForWidth())
+ self.start_button.setSizePolicy(sizePolicy)
+ self.start_button.setObjectName("start_button")
+ self.gridLayout.addWidget(self.start_button, 0, 0, 1, 2)
+ self.gridLayout_2.addWidget(self.option_frame, 1, 1, 1, 1)
+ self.hide_button = QtWidgets.QCheckBox(Form)
+ self.hide_button.setObjectName("hide_button")
+ self.gridLayout_2.addWidget(self.hide_button, 1, 0, 1, 1)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Game Of Life"))
+ self.vanish_shadow.setText(_translate("Form", "Motion blur"))
+ self.full_shadow.setText(_translate("Form", "Scorched earth"))
+ self.radioButton.setText(_translate("Form", "Nothing"))
+ self.faster_button.setText(_translate("Form", "Faster"))
+ self.velocity_label.setText(_translate("Form", "10 steps / s"))
+ self.slower_button.setText(_translate("Form", "Slower"))
+ self.current_step.setText(_translate("Form", "0 step"))
+ self.pause_button.setText(_translate("Form", "Pause"))
+ self.label_6.setText(_translate("Form", "Coverage:"))
+ self.rule_label.setText(_translate("Form", "Rule"))
+ self.label_5.setText(_translate("Form", "Size"))
+ self.add_random_button.setText(_translate("Form", "Add Random"))
+ self.remove_random_button.setText(_translate("Form", "Remove Random"))
+ self.label_3.setText(_translate("Form", "Survival"))
+ self.label_4.setText(_translate("Form", " Birth"))
+ self.label.setText(_translate("Form", "Width"))
+ self.label_2.setText(_translate("Form", "Height"))
+ self.object_combobox.setItemText(0, _translate("Form", "Random"))
+ self.object_combobox.setItemText(1, _translate("Form", "Circle"))
+ self.object_combobox.setItemText(2, _translate("Form", "Square"))
+ self.object_combobox.setItemText(3, _translate("Form", "Diamond"))
+ self.object_combobox.setItemText(4, _translate("Form", "Plus"))
+ self.start_button.setText(_translate("Form", "Start"))
+ self.hide_button.setText(_translate("Form", "Hide options"))
diff --git a/nmreval/gui_qt/_py/gracemsgdialog.py b/nmreval/gui_qt/_py/gracemsgdialog.py
new file mode 100644
index 0000000..9c3c25f
--- /dev/null
+++ b/nmreval/gui_qt/_py/gracemsgdialog.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/gracemsgdialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_GraceMsgDialog(object):
+ def setupUi(self, GraceMsgDialog):
+ GraceMsgDialog.setObjectName("GraceMsgDialog")
+ GraceMsgDialog.resize(400, 300)
+ self.gridLayout = QtWidgets.QGridLayout(GraceMsgDialog)
+ self.gridLayout.setObjectName("gridLayout")
+ self.graph_combo = QtWidgets.QComboBox(GraceMsgDialog)
+ self.graph_combo.setObjectName("graph_combo")
+ self.gridLayout.addWidget(self.graph_combo, 1, 1, 1, 1)
+ self.graph_button = QtWidgets.QRadioButton(GraceMsgDialog)
+ self.graph_button.setObjectName("graph_button")
+ self.buttonGroup = QtWidgets.QButtonGroup(GraceMsgDialog)
+ self.buttonGroup.setObjectName("buttonGroup")
+ self.buttonGroup.addButton(self.graph_button)
+ self.gridLayout.addWidget(self.graph_button, 1, 0, 1, 1)
+ self.overwrite_button = QtWidgets.QRadioButton(GraceMsgDialog)
+ self.overwrite_button.setChecked(True)
+ self.overwrite_button.setObjectName("overwrite_button")
+ self.buttonGroup.addButton(self.overwrite_button)
+ self.gridLayout.addWidget(self.overwrite_button, 0, 0, 1, 1)
+ self.radioButton = QtWidgets.QRadioButton(GraceMsgDialog)
+ self.radioButton.setObjectName("radioButton")
+ self.buttonGroup.addButton(self.radioButton)
+ self.gridLayout.addWidget(self.radioButton, 2, 0, 1, 1)
+ self.tableWidget = QtWidgets.QTableWidget(GraceMsgDialog)
+ self.tableWidget.setColumnCount(2)
+ self.tableWidget.setObjectName("tableWidget")
+ self.tableWidget.setRowCount(0)
+ self.tableWidget.horizontalHeader().setVisible(False)
+ self.gridLayout.addWidget(self.tableWidget, 3, 0, 2, 2)
+ self.buttonBox = QtWidgets.QDialogButtonBox(GraceMsgDialog)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 5, 0, 1, 2)
+
+ self.retranslateUi(GraceMsgDialog)
+ QtCore.QMetaObject.connectSlotsByName(GraceMsgDialog)
+
+ def retranslateUi(self, GraceMsgDialog):
+ _translate = QtCore.QCoreApplication.translate
+ GraceMsgDialog.setWindowTitle(_translate("GraceMsgDialog", "Goodness gracious, file already exists."))
+ self.graph_button.setText(_translate("GraceMsgDialog", "Add to graph"))
+ self.overwrite_button.setText(_translate("GraceMsgDialog", "Overwrite file"))
+ self.radioButton.setText(_translate("GraceMsgDialog", "Replace sets"))
diff --git a/nmreval/gui_qt/_py/gracereader.py b/nmreval/gui_qt/_py/gracereader.py
new file mode 100644
index 0000000..b769d0d
--- /dev/null
+++ b/nmreval/gui_qt/_py/gracereader.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/gracereader.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(400, 613)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.label = QtWidgets.QLabel(Dialog)
+ self.label.setObjectName("label")
+ self.verticalLayout.addWidget(self.label)
+ self.treeWidget = QtWidgets.QTreeWidget(Dialog)
+ self.treeWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+ self.treeWidget.setObjectName("treeWidget")
+ self.treeWidget.headerItem().setText(0, "1")
+ self.treeWidget.header().setVisible(False)
+ self.verticalLayout.addWidget(self.treeWidget)
+ self.tableWidget = QtWidgets.QTableWidget(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tableWidget.sizePolicy().hasHeightForWidth())
+ self.tableWidget.setSizePolicy(sizePolicy)
+ self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.tableWidget.setGridStyle(QtCore.Qt.NoPen)
+ self.tableWidget.setRowCount(4)
+ self.tableWidget.setColumnCount(2)
+ self.tableWidget.setObjectName("tableWidget")
+ item = QtWidgets.QTableWidgetItem()
+ item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled)
+ self.tableWidget.setItem(0, 0, item)
+ item = QtWidgets.QTableWidgetItem()
+ item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled)
+ self.tableWidget.setItem(0, 1, item)
+ item = QtWidgets.QTableWidgetItem()
+ item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled)
+ self.tableWidget.setItem(1, 0, item)
+ item = QtWidgets.QTableWidgetItem()
+ item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled)
+ self.tableWidget.setItem(1, 1, item)
+ item = QtWidgets.QTableWidgetItem()
+ item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled)
+ self.tableWidget.setItem(2, 0, item)
+ item = QtWidgets.QTableWidgetItem()
+ item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled)
+ self.tableWidget.setItem(2, 1, item)
+ item = QtWidgets.QTableWidgetItem()
+ item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled)
+ self.tableWidget.setItem(3, 0, item)
+ item = QtWidgets.QTableWidgetItem()
+ item.setFlags(QtCore.Qt.ItemIsEnabled)
+ self.tableWidget.setItem(3, 1, item)
+ self.tableWidget.horizontalHeader().setVisible(False)
+ self.tableWidget.horizontalHeader().setStretchLastSection(True)
+ self.tableWidget.verticalHeader().setVisible(False)
+ self.verticalLayout.addWidget(self.tableWidget)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout.addWidget(self.buttonBox)
+
+ self.retranslateUi(Dialog)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Load data from agr"))
+ self.label.setText(_translate("Dialog", "Only data will be loaded, no line and symbol properties!"))
+ __sortingEnabled = self.tableWidget.isSortingEnabled()
+ self.tableWidget.setSortingEnabled(False)
+ item = self.tableWidget.item(0, 0)
+ item.setText(_translate("Dialog", "Symbol"))
+ item = self.tableWidget.item(1, 0)
+ item.setText(_translate("Dialog", "Symbol color"))
+ item = self.tableWidget.item(2, 0)
+ item.setText(_translate("Dialog", "Linestyle"))
+ item = self.tableWidget.item(3, 0)
+ item.setText(_translate("Dialog", "Line color"))
+ self.tableWidget.setSortingEnabled(__sortingEnabled)
diff --git a/nmreval/gui_qt/_py/graph.py b/nmreval/gui_qt/_py/graph.py
new file mode 100644
index 0000000..32e7c0e
--- /dev/null
+++ b/nmreval/gui_qt/_py/graph.py
@@ -0,0 +1,275 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/graph.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_GraphWindow(object):
+ def setupUi(self, GraphWindow):
+ GraphWindow.setObjectName("GraphWindow")
+ GraphWindow.resize(680, 520)
+ GraphWindow.setBaseSize(QtCore.QSize(300, 10))
+ self.verticalLayout = QtWidgets.QVBoxLayout(GraphWindow)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.widget = QtWidgets.QWidget(GraphWindow)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
+ self.widget.setSizePolicy(sizePolicy)
+ self.widget.setObjectName("widget")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setSpacing(1)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.logx_button = QtWidgets.QToolButton(self.widget)
+ self.logx_button.setIconSize(QtCore.QSize(16, 16))
+ self.logx_button.setCheckable(True)
+ self.logx_button.setAutoRaise(True)
+ self.logx_button.setObjectName("logx_button")
+ self.horizontalLayout.addWidget(self.logx_button)
+ self.logy_button = QtWidgets.QToolButton(self.widget)
+ self.logy_button.setIconSize(QtCore.QSize(16, 16))
+ self.logy_button.setCheckable(True)
+ self.logy_button.setAutoRaise(True)
+ self.logy_button.setObjectName("logy_button")
+ self.horizontalLayout.addWidget(self.logy_button)
+ self.line = QtWidgets.QFrame(self.widget)
+ self.line.setFrameShape(QtWidgets.QFrame.VLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.horizontalLayout.addWidget(self.line)
+ self.gridbutton = QtWidgets.QToolButton(self.widget)
+ self.gridbutton.setCheckable(True)
+ self.gridbutton.setAutoRaise(True)
+ self.gridbutton.setObjectName("gridbutton")
+ self.horizontalLayout.addWidget(self.gridbutton)
+ self.bwbutton = QtWidgets.QToolButton(self.widget)
+ self.bwbutton.setCheckable(True)
+ self.bwbutton.setAutoRaise(True)
+ self.bwbutton.setObjectName("bwbutton")
+ self.horizontalLayout.addWidget(self.bwbutton)
+ self.line_2 = QtWidgets.QFrame(self.widget)
+ self.line_2.setFrameShape(QtWidgets.QFrame.VLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.horizontalLayout.addWidget(self.line_2)
+ self.legend_button = QtWidgets.QToolButton(self.widget)
+ self.legend_button.setIconSize(QtCore.QSize(16, 16))
+ self.legend_button.setCheckable(True)
+ self.legend_button.setAutoRaise(True)
+ self.legend_button.setObjectName("legend_button")
+ self.horizontalLayout.addWidget(self.legend_button)
+ self.imag_button = QtWidgets.QToolButton(self.widget)
+ self.imag_button.setIconSize(QtCore.QSize(16, 16))
+ self.imag_button.setCheckable(True)
+ self.imag_button.setChecked(True)
+ self.imag_button.setAutoRaise(True)
+ self.imag_button.setObjectName("imag_button")
+ self.horizontalLayout.addWidget(self.imag_button)
+ self.real_button = QtWidgets.QToolButton(self.widget)
+ self.real_button.setIconSize(QtCore.QSize(16, 16))
+ self.real_button.setCheckable(True)
+ self.real_button.setChecked(True)
+ self.real_button.setAutoRaise(True)
+ self.real_button.setObjectName("real_button")
+ self.horizontalLayout.addWidget(self.real_button)
+ self.error_button = QtWidgets.QToolButton(self.widget)
+ self.error_button.setIconSize(QtCore.QSize(16, 16))
+ self.error_button.setCheckable(True)
+ self.error_button.setAutoRaise(True)
+ self.error_button.setObjectName("error_button")
+ self.horizontalLayout.addWidget(self.error_button)
+ self.line_3 = QtWidgets.QFrame(self.widget)
+ self.line_3.setFrameShape(QtWidgets.QFrame.VLine)
+ self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_3.setObjectName("line_3")
+ self.horizontalLayout.addWidget(self.line_3)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.limit_button = QtWidgets.QToolButton(self.widget)
+ self.limit_button.setCheckable(True)
+ self.limit_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.limit_button.setAutoRaise(True)
+ self.limit_button.setArrowType(QtCore.Qt.RightArrow)
+ self.limit_button.setObjectName("limit_button")
+ self.horizontalLayout.addWidget(self.limit_button)
+ self.label_button = QtWidgets.QToolButton(self.widget)
+ self.label_button.setCheckable(True)
+ self.label_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.label_button.setAutoRaise(True)
+ self.label_button.setArrowType(QtCore.Qt.RightArrow)
+ self.label_button.setObjectName("label_button")
+ self.horizontalLayout.addWidget(self.label_button)
+ self.verticalLayout.addWidget(self.widget)
+ self.line_4 = QtWidgets.QFrame(GraphWindow)
+ self.line_4.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_4.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_4.setObjectName("line_4")
+ self.verticalLayout.addWidget(self.line_4)
+ self.limit_widget = QtWidgets.QWidget(GraphWindow)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.limit_widget.sizePolicy().hasHeightForWidth())
+ self.limit_widget.setSizePolicy(sizePolicy)
+ self.limit_widget.setObjectName("limit_widget")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.limit_widget)
+ self.horizontalLayout_2.setContentsMargins(1, 1, 1, 1)
+ self.horizontalLayout_2.setSpacing(2)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.label = QtWidgets.QLabel(self.limit_widget)
+ self.label.setObjectName("label")
+ self.horizontalLayout_2.addWidget(self.label)
+ self.xmin_lineedit = QtWidgets.QLineEdit(self.limit_widget)
+ self.xmin_lineedit.setObjectName("xmin_lineedit")
+ self.horizontalLayout_2.addWidget(self.xmin_lineedit)
+ self.label_2 = QtWidgets.QLabel(self.limit_widget)
+ self.label_2.setObjectName("label_2")
+ self.horizontalLayout_2.addWidget(self.label_2)
+ self.xmax_lineedit = QtWidgets.QLineEdit(self.limit_widget)
+ self.xmax_lineedit.setObjectName("xmax_lineedit")
+ self.horizontalLayout_2.addWidget(self.xmax_lineedit)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem1)
+ self.label_3 = QtWidgets.QLabel(self.limit_widget)
+ self.label_3.setObjectName("label_3")
+ self.horizontalLayout_2.addWidget(self.label_3)
+ self.ymin_lineedit = QtWidgets.QLineEdit(self.limit_widget)
+ self.ymin_lineedit.setObjectName("ymin_lineedit")
+ self.horizontalLayout_2.addWidget(self.ymin_lineedit)
+ self.label_4 = QtWidgets.QLabel(self.limit_widget)
+ self.label_4.setObjectName("label_4")
+ self.horizontalLayout_2.addWidget(self.label_4)
+ self.ymax_lineedit = QtWidgets.QLineEdit(self.limit_widget)
+ self.ymax_lineedit.setObjectName("ymax_lineedit")
+ self.horizontalLayout_2.addWidget(self.ymax_lineedit)
+ self.apply_button = QtWidgets.QPushButton(self.limit_widget)
+ icon = QtGui.QIcon.fromTheme("dialog-ok")
+ self.apply_button.setIcon(icon)
+ self.apply_button.setObjectName("apply_button")
+ self.horizontalLayout_2.addWidget(self.apply_button)
+ self.verticalLayout.addWidget(self.limit_widget)
+ self.label_widget = QtWidgets.QWidget(GraphWindow)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_widget.sizePolicy().hasHeightForWidth())
+ self.label_widget.setSizePolicy(sizePolicy)
+ self.label_widget.setObjectName("label_widget")
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.label_widget)
+ self.horizontalLayout_3.setContentsMargins(1, 1, 1, 1)
+ self.horizontalLayout_3.setSpacing(2)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.label_5 = QtWidgets.QLabel(self.label_widget)
+ self.label_5.setObjectName("label_5")
+ self.horizontalLayout_3.addWidget(self.label_5)
+ self.title_lineedit = QtWidgets.QLineEdit(self.label_widget)
+ self.title_lineedit.setObjectName("title_lineedit")
+ self.horizontalLayout_3.addWidget(self.title_lineedit)
+ spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_3.addItem(spacerItem2)
+ self.label_6 = QtWidgets.QLabel(self.label_widget)
+ self.label_6.setObjectName("label_6")
+ self.horizontalLayout_3.addWidget(self.label_6)
+ self.xaxis_linedit = QtWidgets.QLineEdit(self.label_widget)
+ self.xaxis_linedit.setObjectName("xaxis_linedit")
+ self.horizontalLayout_3.addWidget(self.xaxis_linedit)
+ spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_3.addItem(spacerItem3)
+ self.label_7 = QtWidgets.QLabel(self.label_widget)
+ self.label_7.setObjectName("label_7")
+ self.horizontalLayout_3.addWidget(self.label_7)
+ self.yaxis_linedit = QtWidgets.QLineEdit(self.label_widget)
+ self.yaxis_linedit.setObjectName("yaxis_linedit")
+ self.horizontalLayout_3.addWidget(self.yaxis_linedit)
+ self.verticalLayout.addWidget(self.label_widget)
+ self.gridLayout = QtWidgets.QGridLayout()
+ self.gridLayout.setHorizontalSpacing(3)
+ self.gridLayout.setVerticalSpacing(0)
+ self.gridLayout.setObjectName("gridLayout")
+ self.listWidget = QtWidgets.QListWidget(GraphWindow)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
+ self.listWidget.setSizePolicy(sizePolicy)
+ self.listWidget.setObjectName("listWidget")
+ self.gridLayout.addWidget(self.listWidget, 1, 1, 1, 1)
+ self.checkBox = QtWidgets.QCheckBox(GraphWindow)
+ self.checkBox.setChecked(True)
+ self.checkBox.setObjectName("checkBox")
+ self.gridLayout.addWidget(self.checkBox, 0, 1, 1, 1)
+ self.graphic = PlotWidget(GraphWindow)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.graphic.sizePolicy().hasHeightForWidth())
+ self.graphic.setSizePolicy(sizePolicy)
+ self.graphic.setObjectName("graphic")
+ self.gridLayout.addWidget(self.graphic, 0, 0, 2, 1)
+ self.verticalLayout.addLayout(self.gridLayout)
+ self.label.setBuddy(self.xmin_lineedit)
+ self.label_2.setBuddy(self.xmax_lineedit)
+ self.label_3.setBuddy(self.ymin_lineedit)
+ self.label_4.setBuddy(self.ymax_lineedit)
+ self.label_5.setBuddy(self.title_lineedit)
+ self.label_6.setBuddy(self.xaxis_linedit)
+ self.label_7.setBuddy(self.yaxis_linedit)
+
+ self.retranslateUi(GraphWindow)
+ QtCore.QMetaObject.connectSlotsByName(GraphWindow)
+ GraphWindow.setTabOrder(self.logx_button, self.logy_button)
+ GraphWindow.setTabOrder(self.logy_button, self.gridbutton)
+ GraphWindow.setTabOrder(self.gridbutton, self.legend_button)
+ GraphWindow.setTabOrder(self.legend_button, self.imag_button)
+ GraphWindow.setTabOrder(self.imag_button, self.error_button)
+ GraphWindow.setTabOrder(self.error_button, self.limit_button)
+ GraphWindow.setTabOrder(self.limit_button, self.label_button)
+ GraphWindow.setTabOrder(self.label_button, self.xmin_lineedit)
+ GraphWindow.setTabOrder(self.xmin_lineedit, self.xmax_lineedit)
+ GraphWindow.setTabOrder(self.xmax_lineedit, self.ymin_lineedit)
+ GraphWindow.setTabOrder(self.ymin_lineedit, self.ymax_lineedit)
+ GraphWindow.setTabOrder(self.ymax_lineedit, self.title_lineedit)
+ GraphWindow.setTabOrder(self.title_lineedit, self.xaxis_linedit)
+ GraphWindow.setTabOrder(self.xaxis_linedit, self.yaxis_linedit)
+
+ def retranslateUi(self, GraphWindow):
+ _translate = QtCore.QCoreApplication.translate
+ GraphWindow.setWindowTitle(_translate("GraphWindow", "Form"))
+ self.logx_button.setToolTip(_translate("GraphWindow", "Change x axis linear <-> logarithmic"))
+ self.logx_button.setText(_translate("GraphWindow", "Log X"))
+ self.logy_button.setToolTip(_translate("GraphWindow", "Change y axis linear <-> logarithmic"))
+ self.logy_button.setText(_translate("GraphWindow", "Log Y"))
+ self.gridbutton.setToolTip(_translate("GraphWindow", "Show/hide grid"))
+ self.gridbutton.setText(_translate("GraphWindow", "Grid"))
+ self.bwbutton.setToolTip(_translate("GraphWindow", "Change background"))
+ self.bwbutton.setText(_translate("GraphWindow", "Black/white"))
+ self.legend_button.setToolTip(_translate("GraphWindow", "Change legend"))
+ self.legend_button.setText(_translate("GraphWindow", "Legend"))
+ self.imag_button.setToolTip(_translate("GraphWindow", "Show/hide imaginary part"))
+ self.imag_button.setText(_translate("GraphWindow", "Imaginary"))
+ self.real_button.setToolTip(_translate("GraphWindow", "Show/hide real part"))
+ self.real_button.setText(_translate("GraphWindow", "Real"))
+ self.error_button.setToolTip(_translate("GraphWindow", "Show/hide errorbars"))
+ self.error_button.setText(_translate("GraphWindow", "Errorbars"))
+ self.limit_button.setText(_translate("GraphWindow", "Limits"))
+ self.label_button.setText(_translate("GraphWindow", "Labels"))
+ self.label.setText(_translate("GraphWindow", "X: "))
+ self.label_2.setText(_translate("GraphWindow", "---"))
+ self.label_3.setText(_translate("GraphWindow", "Y: "))
+ self.label_4.setText(_translate("GraphWindow", "---"))
+ self.apply_button.setText(_translate("GraphWindow", "Apply"))
+ self.label_5.setText(_translate("GraphWindow", "Title"))
+ self.label_6.setText(_translate("GraphWindow", "X Axis"))
+ self.label_7.setText(_translate("GraphWindow", "Y Axis"))
+ self.checkBox.setText(_translate("GraphWindow", "Show legend"))
+from pyqtgraph import PlotWidget
diff --git a/nmreval/gui_qt/_py/guidelinewidget.py b/nmreval/gui_qt/_py/guidelinewidget.py
new file mode 100644
index 0000000..a69a325
--- /dev/null
+++ b/nmreval/gui_qt/_py/guidelinewidget.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/guidelinewidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(431, 799)
+ self.gridLayout_2 = QtWidgets.QGridLayout(Form)
+ self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_2.setSpacing(3)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.mode_comboBox = QtWidgets.QComboBox(Form)
+ self.mode_comboBox.setObjectName("mode_comboBox")
+ self.mode_comboBox.addItem("")
+ self.mode_comboBox.addItem("")
+ self.gridLayout_2.addWidget(self.mode_comboBox, 2, 0, 1, 2)
+ self.graph_comboBox = QtWidgets.QComboBox(Form)
+ self.graph_comboBox.setObjectName("graph_comboBox")
+ self.gridLayout_2.addWidget(self.graph_comboBox, 0, 0, 1, 2)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.diagonal_widget = QtWidgets.QWidget(Form)
+ self.diagonal_widget.setObjectName("diagonal_widget")
+ self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.diagonal_widget)
+ self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_4.setObjectName("horizontalLayout_4")
+ self.horizontalLayout_2.addWidget(self.diagonal_widget)
+ self.vh_widget = QtWidgets.QWidget(Form)
+ self.vh_widget.setObjectName("vh_widget")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.vh_widget)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(self.vh_widget)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.vh_pos_lineEdit = QtWidgets.QLineEdit(self.vh_widget)
+ self.vh_pos_lineEdit.setObjectName("vh_pos_lineEdit")
+ self.horizontalLayout.addWidget(self.vh_pos_lineEdit)
+ self.horizontalLayout_2.addWidget(self.vh_widget)
+ self.drag_checkBox = QtWidgets.QCheckBox(Form)
+ self.drag_checkBox.setChecked(True)
+ self.drag_checkBox.setObjectName("drag_checkBox")
+ self.horizontalLayout_2.addWidget(self.drag_checkBox)
+ self.gridLayout_2.addLayout(self.horizontalLayout_2, 3, 0, 1, 2)
+ self.color_comboBox = ColorListEditor(Form)
+ self.color_comboBox.setObjectName("color_comboBox")
+ self.gridLayout_2.addWidget(self.color_comboBox, 6, 1, 1, 1)
+ self.pushButton = QtWidgets.QPushButton(Form)
+ self.pushButton.setObjectName("pushButton")
+ self.gridLayout_2.addWidget(self.pushButton, 7, 0, 1, 2)
+ self.label_6 = QtWidgets.QLabel(Form)
+ self.label_6.setObjectName("label_6")
+ self.gridLayout_2.addWidget(self.label_6, 5, 0, 1, 1)
+ self.comment_lineEdit = QtWidgets.QLineEdit(Form)
+ self.comment_lineEdit.setObjectName("comment_lineEdit")
+ self.gridLayout_2.addWidget(self.comment_lineEdit, 5, 1, 1, 1)
+ self.label_2 = QtWidgets.QLabel(Form)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout_2.addWidget(self.label_2, 6, 0, 1, 1)
+ self.tableWidget = QtWidgets.QTableWidget(Form)
+ self.tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
+ self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.tableWidget.setColumnCount(2)
+ self.tableWidget.setObjectName("tableWidget")
+ self.tableWidget.setRowCount(0)
+ item = QtWidgets.QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(0, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(1, item)
+ self.tableWidget.horizontalHeader().setVisible(True)
+ self.tableWidget.horizontalHeader().setStretchLastSection(True)
+ self.gridLayout_2.addWidget(self.tableWidget, 8, 0, 1, 2)
+ self.line = QtWidgets.QFrame(Form)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.gridLayout_2.addWidget(self.line, 1, 0, 1, 2)
+ self.line_2 = QtWidgets.QFrame(Form)
+ self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.gridLayout_2.addWidget(self.line_2, 4, 0, 1, 2)
+ self.label.setBuddy(self.vh_pos_lineEdit)
+ self.label_6.setBuddy(self.comment_lineEdit)
+ self.label_2.setBuddy(self.color_comboBox)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+ Form.setTabOrder(self.graph_comboBox, self.mode_comboBox)
+ Form.setTabOrder(self.mode_comboBox, self.vh_pos_lineEdit)
+ Form.setTabOrder(self.vh_pos_lineEdit, self.drag_checkBox)
+ Form.setTabOrder(self.drag_checkBox, self.comment_lineEdit)
+ Form.setTabOrder(self.comment_lineEdit, self.color_comboBox)
+ Form.setTabOrder(self.color_comboBox, self.pushButton)
+ Form.setTabOrder(self.pushButton, self.tableWidget)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.mode_comboBox.setItemText(0, _translate("Form", "Vertical"))
+ self.mode_comboBox.setItemText(1, _translate("Form", "Horizontal"))
+ self.label.setText(_translate("Form", "Position"))
+ self.vh_pos_lineEdit.setText(_translate("Form", "0"))
+ self.drag_checkBox.setText(_translate("Form", "Drag enabled"))
+ self.pushButton.setText(_translate("Form", "Create line"))
+ self.label_6.setText(_translate("Form", "Comment"))
+ self.label_2.setText(_translate("Form", "Color"))
+ item = self.tableWidget.horizontalHeaderItem(0)
+ item.setText(_translate("Form", "Pos."))
+ item = self.tableWidget.horizontalHeaderItem(1)
+ item.setText(_translate("Form", "Comment"))
+from ..lib.delegates import ColorListEditor
diff --git a/nmreval/gui_qt/_py/hdftree.py b/nmreval/gui_qt/_py/hdftree.py
new file mode 100644
index 0000000..b0e89f1
--- /dev/null
+++ b/nmreval/gui_qt/_py/hdftree.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/hdftree.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Hdf_Dialog(object):
+ def setupUi(self, Hdf_Dialog):
+ Hdf_Dialog.setObjectName("Hdf_Dialog")
+ Hdf_Dialog.resize(460, 772)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Hdf_Dialog)
+ self.verticalLayout.setContentsMargins(4, 4, 4, 4)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.verticalLayout.addLayout(self.verticalLayout_3)
+ self.widget = QtWidgets.QWidget(Hdf_Dialog)
+ self.widget.setObjectName("widget")
+ self.gridLayout = QtWidgets.QGridLayout(self.widget)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setSpacing(2)
+ self.gridLayout.setObjectName("gridLayout")
+ self.comboBox_2 = QtWidgets.QComboBox(self.widget)
+ self.comboBox_2.setObjectName("comboBox_2")
+ self.gridLayout.addWidget(self.comboBox_2, 1, 1, 1, 1)
+ self.label = QtWidgets.QLabel(self.widget)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
+ self.label_2 = QtWidgets.QLabel(self.widget)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
+ self.comboBox = QtWidgets.QComboBox(self.widget)
+ self.comboBox.setObjectName("comboBox")
+ self.gridLayout.addWidget(self.comboBox, 0, 1, 1, 1)
+ self.verticalLayout.addWidget(self.widget)
+ self.widget_2 = ExpandableWidget(Hdf_Dialog)
+ self.widget_2.setObjectName("widget_2")
+ self.verticalLayout.addWidget(self.widget_2)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Hdf_Dialog)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout.addWidget(self.buttonBox)
+
+ self.retranslateUi(Hdf_Dialog)
+ self.buttonBox.rejected.connect(Hdf_Dialog.close)
+ self.buttonBox.accepted.connect(Hdf_Dialog.accept)
+ QtCore.QMetaObject.connectSlotsByName(Hdf_Dialog)
+
+ def retranslateUi(self, Hdf_Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Hdf_Dialog.setWindowTitle(_translate("Hdf_Dialog", "View HDF file"))
+ self.label.setText(_translate("Hdf_Dialog", "Label"))
+ self.label_2.setText(_translate("Hdf_Dialog", "Group"))
+from ..lib.expandablewidget import ExpandableWidget
diff --git a/nmreval/gui_qt/_py/integral_widget.py b/nmreval/gui_qt/_py/integral_widget.py
new file mode 100644
index 0000000..4f16d93
--- /dev/null
+++ b/nmreval/gui_qt/_py/integral_widget.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/integral_widget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(397, 681)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Form)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.label_2 = QtWidgets.QLabel(Form)
+ self.label_2.setObjectName("label_2")
+ self.verticalLayout.addWidget(self.label_2)
+ self.set_combobox = QtWidgets.QComboBox(Form)
+ self.set_combobox.setObjectName("set_combobox")
+ self.verticalLayout.addWidget(self.set_combobox)
+ self.treeWidget = QtWidgets.QTreeWidget(Form)
+ self.treeWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.treeWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.treeWidget.setHeaderHidden(True)
+ self.treeWidget.setObjectName("treeWidget")
+ self.treeWidget.headerItem().setText(0, "1")
+ self.verticalLayout.addWidget(self.treeWidget)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setSpacing(3)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(Form)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.pushButton = QtWidgets.QPushButton(Form)
+ self.pushButton.setObjectName("pushButton")
+ self.horizontalLayout.addWidget(self.pushButton)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.label_2.setText(_translate("Form", "TextLabel"))
+ self.label.setText(_translate("Form", "Save integrals as dataset"))
+ self.pushButton.setText(_translate("Form", "Apply"))
diff --git a/nmreval/gui_qt/_py/integratederive_dialog.py b/nmreval/gui_qt/_py/integratederive_dialog.py
new file mode 100644
index 0000000..0ae75f4
--- /dev/null
+++ b/nmreval/gui_qt/_py/integratederive_dialog.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/integratederive_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(400, 308)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.listWidget = QtWidgets.QListWidget(Dialog)
+ self.listWidget.setObjectName("listWidget")
+ self.verticalLayout.addWidget(self.listWidget)
+ self.widget = QtWidgets.QWidget(Dialog)
+ self.widget.setObjectName("widget")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(self.widget)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.start_lineedit = QtWidgets.QLineEdit(self.widget)
+ self.start_lineedit.setEnabled(False)
+ self.start_lineedit.setObjectName("start_lineedit")
+ self.horizontalLayout.addWidget(self.start_lineedit)
+ self.stop_lineedit = QtWidgets.QLineEdit(self.widget)
+ self.stop_lineedit.setEnabled(False)
+ self.stop_lineedit.setObjectName("stop_lineedit")
+ self.horizontalLayout.addWidget(self.stop_lineedit)
+ self.range_checkbox = QtWidgets.QCheckBox(self.widget)
+ self.range_checkbox.setChecked(True)
+ self.range_checkbox.setObjectName("range_checkbox")
+ self.horizontalLayout.addWidget(self.range_checkbox)
+ self.verticalLayout.addWidget(self.widget)
+ self.ft_comboBox = QtWidgets.QComboBox(Dialog)
+ self.ft_comboBox.setObjectName("ft_comboBox")
+ self.ft_comboBox.addItem("")
+ self.ft_comboBox.addItem("")
+ self.ft_comboBox.addItem("")
+ self.verticalLayout.addWidget(self.ft_comboBox)
+ self.log_checkbox = QtWidgets.QCheckBox(Dialog)
+ self.log_checkbox.setObjectName("log_checkbox")
+ self.verticalLayout.addWidget(self.log_checkbox)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
+ self.horizontalLayout_2.setSpacing(3)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.newgraph_checkbox = QtWidgets.QCheckBox(Dialog)
+ self.newgraph_checkbox.setObjectName("newgraph_checkbox")
+ self.horizontalLayout_2.addWidget(self.newgraph_checkbox)
+ self.graph_combobox = QtWidgets.QComboBox(Dialog)
+ self.graph_combobox.setObjectName("graph_combobox")
+ self.horizontalLayout_2.addWidget(self.graph_combobox)
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout.addWidget(self.buttonBox)
+
+ self.retranslateUi(Dialog)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
+ self.label.setText(_translate("Dialog", "Limits"))
+ self.range_checkbox.setText(_translate("Dialog", "Full"))
+ self.ft_comboBox.setItemText(0, _translate("Dialog", "Real"))
+ self.ft_comboBox.setItemText(1, _translate("Dialog", "Imag"))
+ self.ft_comboBox.setItemText(2, _translate("Dialog", "Complex"))
+ self.log_checkbox.setText(_translate("Dialog", "use logarithmic x axis"))
+ self.newgraph_checkbox.setText(_translate("Dialog", "New graph"))
diff --git a/nmreval/gui_qt/_py/interpol_dialog.py b/nmreval/gui_qt/_py/interpol_dialog.py
new file mode 100644
index 0000000..c819070
--- /dev/null
+++ b/nmreval/gui_qt/_py/interpol_dialog.py
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/interpol_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(416, 494)
+ self.gridLayout = QtWidgets.QGridLayout(Dialog)
+ self.gridLayout.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout.setSpacing(3)
+ self.gridLayout.setObjectName("gridLayout")
+ self.src_widget = QtWidgets.QWidget(Dialog)
+ self.src_widget.setObjectName("src_widget")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.src_widget)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setSpacing(3)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.graph_combobox = QtWidgets.QComboBox(self.src_widget)
+ self.graph_combobox.setObjectName("graph_combobox")
+ self.horizontalLayout.addWidget(self.graph_combobox)
+ self.set_combobox = QtWidgets.QComboBox(self.src_widget)
+ self.set_combobox.setObjectName("set_combobox")
+ self.horizontalLayout.addWidget(self.set_combobox)
+ self.gridLayout.addWidget(self.src_widget, 8, 0, 1, 2)
+ self.label_3 = QtWidgets.QLabel(Dialog)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout.addWidget(self.label_3, 0, 0, 1, 1)
+ self.label = QtWidgets.QLabel(Dialog)
+ self.label.setToolTip("")
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 4, 0, 1, 1)
+ self.line_2 = QtWidgets.QFrame(Dialog)
+ self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.gridLayout.addWidget(self.line_2, 5, 0, 1, 2)
+ self.ylog_checkBox = QtWidgets.QCheckBox(Dialog)
+ self.ylog_checkBox.setObjectName("ylog_checkBox")
+ self.gridLayout.addWidget(self.ylog_checkBox, 2, 1, 1, 1)
+ self.interp_comboBox = QtWidgets.QComboBox(Dialog)
+ self.interp_comboBox.setToolTip("")
+ self.interp_comboBox.setObjectName("interp_comboBox")
+ self.interp_comboBox.addItem("")
+ self.interp_comboBox.addItem("")
+ self.gridLayout.addWidget(self.interp_comboBox, 4, 1, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 12, 0, 1, 2)
+ 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.label_2 = QtWidgets.QLabel(Dialog)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 6, 0, 1, 1)
+ self.listWidget = QtWidgets.QListWidget(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
+ self.listWidget.setSizePolicy(sizePolicy)
+ self.listWidget.setObjectName("listWidget")
+ self.gridLayout.addWidget(self.listWidget, 1, 0, 1, 2)
+ self.sampling_widget = QtWidgets.QWidget(Dialog)
+ self.sampling_widget.setObjectName("sampling_widget")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.sampling_widget)
+ self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_2.setSpacing(3)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.label_4 = QtWidgets.QLabel(self.sampling_widget)
+ self.label_4.setObjectName("label_4")
+ self.horizontalLayout_2.addWidget(self.label_4)
+ self.start_lineEdit = QtWidgets.QLineEdit(self.sampling_widget)
+ self.start_lineEdit.setObjectName("start_lineEdit")
+ self.horizontalLayout_2.addWidget(self.start_lineEdit)
+ self.label_5 = QtWidgets.QLabel(self.sampling_widget)
+ self.label_5.setObjectName("label_5")
+ self.horizontalLayout_2.addWidget(self.label_5)
+ self.stop_lineEdit = QtWidgets.QLineEdit(self.sampling_widget)
+ self.stop_lineEdit.setObjectName("stop_lineEdit")
+ self.horizontalLayout_2.addWidget(self.stop_lineEdit)
+ self.label_6 = QtWidgets.QLabel(self.sampling_widget)
+ self.label_6.setObjectName("label_6")
+ self.horizontalLayout_2.addWidget(self.label_6)
+ self.step_lineEdit = QtWidgets.QLineEdit(self.sampling_widget)
+ self.step_lineEdit.setObjectName("step_lineEdit")
+ self.horizontalLayout_2.addWidget(self.step_lineEdit)
+ self.logspace_checkBox = QtWidgets.QCheckBox(self.sampling_widget)
+ self.logspace_checkBox.setObjectName("logspace_checkBox")
+ self.horizontalLayout_2.addWidget(self.logspace_checkBox)
+ self.gridLayout.addWidget(self.sampling_widget, 7, 0, 1, 2)
+ self.xaxis_comboBox = QtWidgets.QComboBox(Dialog)
+ self.xaxis_comboBox.setObjectName("xaxis_comboBox")
+ self.xaxis_comboBox.addItem("")
+ self.xaxis_comboBox.addItem("")
+ self.gridLayout.addWidget(self.xaxis_comboBox, 6, 1, 1, 1)
+ self.line_3 = QtWidgets.QFrame(Dialog)
+ self.line_3.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_3.setObjectName("line_3")
+ self.gridLayout.addWidget(self.line_3, 9, 0, 1, 2)
+ self.dest_combobox = QtWidgets.QComboBox(Dialog)
+ self.dest_combobox.setObjectName("dest_combobox")
+ self.gridLayout.addWidget(self.dest_combobox, 10, 1, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout.addItem(spacerItem, 11, 0, 1, 1)
+ self.label_8 = QtWidgets.QLabel(Dialog)
+ self.label_8.setObjectName("label_8")
+ self.gridLayout.addWidget(self.label_8, 10, 0, 1, 1)
+ self.xlog_checkBox = QtWidgets.QCheckBox(Dialog)
+ self.xlog_checkBox.setObjectName("xlog_checkBox")
+ self.gridLayout.addWidget(self.xlog_checkBox, 2, 0, 1, 1)
+ self.label.setBuddy(self.interp_comboBox)
+ self.label_2.setBuddy(self.xaxis_comboBox)
+ self.label_4.setBuddy(self.start_lineEdit)
+ self.label_5.setBuddy(self.stop_lineEdit)
+ self.label_6.setBuddy(self.step_lineEdit)
+ self.label_8.setBuddy(self.dest_combobox)
+
+ self.retranslateUi(Dialog)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+ Dialog.setTabOrder(self.listWidget, self.ylog_checkBox)
+ Dialog.setTabOrder(self.ylog_checkBox, self.interp_comboBox)
+ Dialog.setTabOrder(self.interp_comboBox, self.xaxis_comboBox)
+ Dialog.setTabOrder(self.xaxis_comboBox, self.start_lineEdit)
+ Dialog.setTabOrder(self.start_lineEdit, self.stop_lineEdit)
+ Dialog.setTabOrder(self.stop_lineEdit, self.step_lineEdit)
+ Dialog.setTabOrder(self.step_lineEdit, self.logspace_checkBox)
+ Dialog.setTabOrder(self.logspace_checkBox, self.graph_combobox)
+ Dialog.setTabOrder(self.graph_combobox, self.set_combobox)
+ Dialog.setTabOrder(self.set_combobox, self.dest_combobox)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Data interpolation"))
+ self.label_3.setText(_translate("Dialog", "Source data"))
+ self.label.setText(_translate("Dialog", "Spline"))
+ self.ylog_checkBox.setToolTip(_translate("Dialog", "If your data is on a logarithmic scale in y, check this box"))
+ self.ylog_checkBox.setText(_translate("Dialog", "use log(y)"))
+ self.interp_comboBox.setItemText(0, _translate("Dialog", "Cubic"))
+ self.interp_comboBox.setItemText(1, _translate("Dialog", "Linear"))
+ self.buttonBox.setToolTip(_translate("Dialog", "Accept to create new data sets."))
+ self.label_2.setText(_translate("Dialog", "New x axis"))
+ self.listWidget.setToolTip(_translate("Dialog", "Select sets that shall be interpolated. No selection will create interpolations of all visible sets."))
+ self.label_4.setText(_translate("Dialog", "Start"))
+ self.label_5.setText(_translate("Dialog", "Stop"))
+ self.label_6.setText(_translate("Dialog", "Steps"))
+ self.logspace_checkBox.setText(_translate("Dialog", "log-spaced?"))
+ self.xaxis_comboBox.setItemText(0, _translate("Dialog", "new values"))
+ self.xaxis_comboBox.setItemText(1, _translate("Dialog", "from data"))
+ self.label_8.setText(_translate("Dialog", "Add interpolated data to"))
+ self.xlog_checkBox.setText(_translate("Dialog", "use log(x)"))
diff --git a/nmreval/gui_qt/_py/lineedit_dialog.py b/nmreval/gui_qt/_py/lineedit_dialog.py
new file mode 100644
index 0000000..5a1bf2c
--- /dev/null
+++ b/nmreval/gui_qt/_py/lineedit_dialog.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/lineedit_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_LineEdit_Dialog(object):
+ def setupUi(self, LineEdit_Dialog):
+ LineEdit_Dialog.setObjectName("LineEdit_Dialog")
+ LineEdit_Dialog.resize(400, 84)
+ self.formLayout = QtWidgets.QFormLayout(LineEdit_Dialog)
+ self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow)
+ self.formLayout.setObjectName("formLayout")
+ self.label = QtWidgets.QLabel(LineEdit_Dialog)
+ self.label.setObjectName("label")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)
+ self.new_string = QtWidgets.QLineEdit(LineEdit_Dialog)
+ self.new_string.setObjectName("new_string")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.new_string)
+ self.buttonBox = QtWidgets.QDialogButtonBox(LineEdit_Dialog)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.buttonBox)
+
+ self.retranslateUi(LineEdit_Dialog)
+ self.buttonBox.accepted.connect(LineEdit_Dialog.accept)
+ self.buttonBox.rejected.connect(LineEdit_Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(LineEdit_Dialog)
+
+ def retranslateUi(self, LineEdit_Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ LineEdit_Dialog.setWindowTitle(_translate("LineEdit_Dialog", "Dialog"))
+ self.label.setText(_translate("LineEdit_Dialog", "Label"))
diff --git a/nmreval/gui_qt/_py/mean_form.py b/nmreval/gui_qt/_py/mean_form.py
new file mode 100644
index 0000000..f120794
--- /dev/null
+++ b/nmreval/gui_qt/_py/mean_form.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/mean_form.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_mean_form(object):
+ def setupUi(self, mean_form):
+ mean_form.setObjectName("mean_form")
+ mean_form.resize(712, 34)
+ self.horizontalLayout = QtWidgets.QHBoxLayout(mean_form)
+ self.horizontalLayout.setContentsMargins(1, 1, 1, 1)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(mean_form)
+ self.label.setAlignment(QtCore.Qt.AlignCenter)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.digit_checkbox = QtWidgets.QCheckBox(mean_form)
+ self.digit_checkbox.setChecked(True)
+ self.digit_checkbox.setObjectName("digit_checkbox")
+ self.horizontalLayout.addWidget(self.digit_checkbox)
+ self.lineEdit = QtWidgets.QLineEdit(mean_form)
+ self.lineEdit.setObjectName("lineEdit")
+ self.horizontalLayout.addWidget(self.lineEdit)
+ self.data_checkbox = QtWidgets.QCheckBox(mean_form)
+ self.data_checkbox.setObjectName("data_checkbox")
+ self.horizontalLayout.addWidget(self.data_checkbox)
+ self.frame = QtWidgets.QFrame(mean_form)
+ self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
+ self.frame.setObjectName("frame")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame)
+ self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_2.setSpacing(2)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.graph_combobox = QtWidgets.QComboBox(self.frame)
+ self.graph_combobox.setObjectName("graph_combobox")
+ self.horizontalLayout_2.addWidget(self.graph_combobox)
+ self.set_combobox = QtWidgets.QComboBox(self.frame)
+ self.set_combobox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
+ self.set_combobox.setObjectName("set_combobox")
+ self.horizontalLayout_2.addWidget(self.set_combobox)
+ self.horizontalLayout.addWidget(self.frame)
+
+ self.retranslateUi(mean_form)
+ QtCore.QMetaObject.connectSlotsByName(mean_form)
+
+ def retranslateUi(self, mean_form):
+ _translate = QtCore.QCoreApplication.translate
+ mean_form.setWindowTitle(_translate("mean_form", "Form"))
+ self.label.setText(_translate("mean_form", "TextLabel"))
+ self.digit_checkbox.setText(_translate("mean_form", "Digit"))
+ self.lineEdit.setText(_translate("mean_form", "1"))
+ self.data_checkbox.setText(_translate("mean_form", "Data"))
diff --git a/nmreval/gui_qt/_py/meandialog.py b/nmreval/gui_qt/_py/meandialog.py
new file mode 100644
index 0000000..9a59fcf
--- /dev/null
+++ b/nmreval/gui_qt/_py/meandialog.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/meandialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_calc_means_dialog(object):
+ def setupUi(self, calc_means_dialog):
+ calc_means_dialog.setObjectName("calc_means_dialog")
+ calc_means_dialog.resize(481, 322)
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(calc_means_dialog)
+ self.verticalLayout_2.setSpacing(3)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.dist_combobox = QtWidgets.QComboBox(calc_means_dialog)
+ self.dist_combobox.setObjectName("dist_combobox")
+ self.verticalLayout_2.addWidget(self.dist_combobox)
+ self.verticalLayout = QtWidgets.QVBoxLayout()
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.verticalLayout_2.addLayout(self.verticalLayout)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.from_combobox = QtWidgets.QComboBox(calc_means_dialog)
+ self.from_combobox.setObjectName("from_combobox")
+ self.from_combobox.addItem("")
+ self.from_combobox.addItem("")
+ self.from_combobox.addItem("")
+ self.from_combobox.addItem("")
+ self.horizontalLayout.addWidget(self.from_combobox)
+ self.label_4 = QtWidgets.QLabel(calc_means_dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
+ self.label_4.setSizePolicy(sizePolicy)
+ font = QtGui.QFont()
+ font.setPointSize(20)
+ self.label_4.setFont(font)
+ self.label_4.setObjectName("label_4")
+ self.horizontalLayout.addWidget(self.label_4)
+ self.to_combobox = QtWidgets.QComboBox(calc_means_dialog)
+ self.to_combobox.setObjectName("to_combobox")
+ self.to_combobox.addItem("")
+ self.to_combobox.addItem("")
+ self.to_combobox.addItem("")
+ self.to_combobox.addItem("")
+ self.horizontalLayout.addWidget(self.to_combobox)
+ self.verticalLayout_2.addLayout(self.horizontalLayout)
+ self.line = QtWidgets.QFrame(calc_means_dialog)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.verticalLayout_2.addWidget(self.line)
+ self.label = QtWidgets.QLabel(calc_means_dialog)
+ self.label.setObjectName("label")
+ self.verticalLayout_2.addWidget(self.label)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_2.addItem(spacerItem)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.checkBox = QtWidgets.QCheckBox(calc_means_dialog)
+ self.checkBox.setObjectName("checkBox")
+ self.horizontalLayout_2.addWidget(self.checkBox)
+ self.graph_combobox = QtWidgets.QComboBox(calc_means_dialog)
+ self.graph_combobox.setObjectName("graph_combobox")
+ self.horizontalLayout_2.addWidget(self.graph_combobox)
+ self.verticalLayout_2.addLayout(self.horizontalLayout_2)
+ self.buttonBox = QtWidgets.QDialogButtonBox(calc_means_dialog)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout_2.addWidget(self.buttonBox)
+
+ self.retranslateUi(calc_means_dialog)
+ self.to_combobox.setCurrentIndex(1)
+ QtCore.QMetaObject.connectSlotsByName(calc_means_dialog)
+
+ def retranslateUi(self, calc_means_dialog):
+ _translate = QtCore.QCoreApplication.translate
+ calc_means_dialog.setWindowTitle(_translate("calc_means_dialog", "Mean times"))
+ self.from_combobox.setItemText(0, _translate("calc_means_dialog", "Function value: τ"))
+ self.from_combobox.setItemText(1, _translate("calc_means_dialog", "Peak time: τₚ"))
+ self.from_combobox.setItemText(2, _translate("calc_means_dialog", "Arithmetic mean: ⟨τ⟩"))
+ self.from_combobox.setItemText(3, _translate("calc_means_dialog", "Geometric mean: exp( ⟨ln(τ)⟩ )"))
+ self.label_4.setText(_translate("calc_means_dialog", " ➝ "))
+ self.to_combobox.setItemText(0, _translate("calc_means_dialog", "Function value: τ"))
+ self.to_combobox.setItemText(1, _translate("calc_means_dialog", "Peak time: τₚ"))
+ self.to_combobox.setItemText(2, _translate("calc_means_dialog", "Arithmetic mean: ⟨τ⟩"))
+ self.to_combobox.setItemText(3, _translate("calc_means_dialog", "Geometric mean: exp( ⟨ln(τ)⟩ )"))
+ self.label.setText(_translate("calc_means_dialog", "TextLabel"))
+ self.checkBox.setText(_translate("calc_means_dialog", "New graph"))
diff --git a/nmreval/gui_qt/_py/modelwidget.py b/nmreval/gui_qt/_py/modelwidget.py
new file mode 100644
index 0000000..df2c5ac
--- /dev/null
+++ b/nmreval/gui_qt/_py/modelwidget.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/modelwidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(188, 44)
+ self.horizontalLayout = QtWidgets.QHBoxLayout(Form)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(Form)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.lineEdit = QtWidgets.QLineEdit(Form)
+ self.lineEdit.setObjectName("lineEdit")
+ self.horizontalLayout.addWidget(self.lineEdit)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.label.setText(_translate("Form", "TextLabel"))
diff --git a/nmreval/gui_qt/_py/move_dialog.py b/nmreval/gui_qt/_py/move_dialog.py
new file mode 100644
index 0000000..4234ddb
--- /dev/null
+++ b/nmreval/gui_qt/_py/move_dialog.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/move_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_MoveDialog(object):
+ def setupUi(self, MoveDialog):
+ MoveDialog.setObjectName("MoveDialog")
+ MoveDialog.resize(395, 345)
+ self.gridLayout = QtWidgets.QGridLayout(MoveDialog)
+ self.gridLayout.setVerticalSpacing(1)
+ self.gridLayout.setObjectName("gridLayout")
+ self.tocomboBox = QtWidgets.QComboBox(MoveDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tocomboBox.sizePolicy().hasHeightForWidth())
+ self.tocomboBox.setSizePolicy(sizePolicy)
+ self.tocomboBox.setObjectName("tocomboBox")
+ self.gridLayout.addWidget(self.tocomboBox, 3, 1, 1, 1)
+ self.fromcomboBox = QtWidgets.QComboBox(MoveDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fromcomboBox.sizePolicy().hasHeightForWidth())
+ self.fromcomboBox.setSizePolicy(sizePolicy)
+ self.fromcomboBox.setObjectName("fromcomboBox")
+ self.gridLayout.addWidget(self.fromcomboBox, 0, 1, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(MoveDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 6, 0, 1, 2)
+ self.label_2 = QtWidgets.QLabel(MoveDialog)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 3, 0, 1, 1)
+ self.line = QtWidgets.QFrame(MoveDialog)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.gridLayout.addWidget(self.line, 4, 0, 1, 2)
+ self.label = QtWidgets.QLabel(MoveDialog)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setContentsMargins(-1, 0, -1, -1)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.copy_button = QtWidgets.QRadioButton(MoveDialog)
+ self.copy_button.setObjectName("copy_button")
+ self.buttonGroup = QtWidgets.QButtonGroup(MoveDialog)
+ self.buttonGroup.setObjectName("buttonGroup")
+ self.buttonGroup.addButton(self.copy_button)
+ self.horizontalLayout.addWidget(self.copy_button)
+ self.move_button = QtWidgets.QRadioButton(MoveDialog)
+ self.move_button.setChecked(True)
+ self.move_button.setObjectName("move_button")
+ self.buttonGroup.addButton(self.move_button)
+ self.horizontalLayout.addWidget(self.move_button)
+ self.gridLayout.addLayout(self.horizontalLayout, 5, 0, 1, 2)
+ self.listWidget = QtWidgets.QListWidget(MoveDialog)
+ self.listWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
+ self.listWidget.setObjectName("listWidget")
+ self.gridLayout.addWidget(self.listWidget, 1, 1, 1, 1)
+ self.label_2.setBuddy(self.tocomboBox)
+ self.label.setBuddy(self.fromcomboBox)
+
+ self.retranslateUi(MoveDialog)
+ QtCore.QMetaObject.connectSlotsByName(MoveDialog)
+ MoveDialog.setTabOrder(self.fromcomboBox, self.listWidget)
+ MoveDialog.setTabOrder(self.listWidget, self.tocomboBox)
+ MoveDialog.setTabOrder(self.tocomboBox, self.buttonBox)
+
+ def retranslateUi(self, MoveDialog):
+ _translate = QtCore.QCoreApplication.translate
+ MoveDialog.setWindowTitle(_translate("MoveDialog", "Insert Reel 2 Real song."))
+ self.label_2.setText(_translate("MoveDialog", "To"))
+ self.label.setText(_translate("MoveDialog", "From"))
+ self.copy_button.setText(_translate("MoveDialog", "Copy"))
+ self.move_button.setText(_translate("MoveDialog", "Move"))
diff --git a/nmreval/gui_qt/_py/namespace_widget.py b/nmreval/gui_qt/_py/namespace_widget.py
new file mode 100644
index 0000000..e0f508b
--- /dev/null
+++ b/nmreval/gui_qt/_py/namespace_widget.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/namespace_widget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(400, 300)
+ self.gridLayout_2 = QtWidgets.QGridLayout(Form)
+ self.gridLayout_2.setContentsMargins(1, 1, 1, 1)
+ self.gridLayout_2.setSpacing(3)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.groups_comboBox = QtWidgets.QComboBox(Form)
+ self.groups_comboBox.setObjectName("groups_comboBox")
+ self.gridLayout_2.addWidget(self.groups_comboBox, 0, 0, 1, 1)
+ self.subgroups_comboBox = QtWidgets.QComboBox(Form)
+ self.subgroups_comboBox.setObjectName("subgroups_comboBox")
+ self.gridLayout_2.addWidget(self.subgroups_comboBox, 0, 1, 1, 1)
+ self.namespace_table = QtWidgets.QTableWidget(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.namespace_table.sizePolicy().hasHeightForWidth())
+ self.namespace_table.setSizePolicy(sizePolicy)
+ self.namespace_table.setMinimumSize(QtCore.QSize(0, 0))
+ self.namespace_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.namespace_table.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
+ self.namespace_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.namespace_table.setTextElideMode(QtCore.Qt.ElideNone)
+ self.namespace_table.setColumnCount(2)
+ self.namespace_table.setObjectName("namespace_table")
+ self.namespace_table.setRowCount(5)
+ item = QtWidgets.QTableWidgetItem()
+ self.namespace_table.setVerticalHeaderItem(0, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.namespace_table.setVerticalHeaderItem(1, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.namespace_table.setVerticalHeaderItem(2, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.namespace_table.setVerticalHeaderItem(3, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.namespace_table.setVerticalHeaderItem(4, item)
+ self.namespace_table.horizontalHeader().setVisible(False)
+ self.namespace_table.horizontalHeader().setStretchLastSection(True)
+ self.namespace_table.verticalHeader().setVisible(False)
+ self.gridLayout_2.addWidget(self.namespace_table, 1, 0, 1, 2)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ item = self.namespace_table.verticalHeaderItem(0)
+ item.setText(_translate("Form", "Neue Zeile"))
+ item = self.namespace_table.verticalHeaderItem(1)
+ item.setText(_translate("Form", "Neue Zeile"))
+ item = self.namespace_table.verticalHeaderItem(2)
+ item.setText(_translate("Form", "Neue Zeile"))
+ item = self.namespace_table.verticalHeaderItem(3)
+ item.setText(_translate("Form", "Neue Zeile"))
+ item = self.namespace_table.verticalHeaderItem(4)
+ item.setText(_translate("Form", "Neue Zeile"))
diff --git a/nmreval/gui_qt/_py/option_selection.py b/nmreval/gui_qt/_py/option_selection.py
new file mode 100644
index 0000000..456ec65
--- /dev/null
+++ b/nmreval/gui_qt/_py/option_selection.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/option_selection.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(400, 182)
+ self.gridLayout = QtWidgets.QGridLayout(Form)
+ self.gridLayout.setObjectName("gridLayout")
+ self.tableWidget = QtWidgets.QTableWidget(Form)
+ self.tableWidget.setObjectName("tableWidget")
+ self.tableWidget.setColumnCount(2)
+ self.tableWidget.setRowCount(0)
+ item = QtWidgets.QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(0, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.tableWidget.setHorizontalHeaderItem(1, item)
+ self.gridLayout.addWidget(self.tableWidget, 1, 1, 1, 1)
+ self.verticalLayout = QtWidgets.QVBoxLayout()
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.label_2 = QtWidgets.QLabel(Form)
+ 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.setObjectName("label_2")
+ self.verticalLayout.addWidget(self.label_2)
+ self.lineEdit = QtWidgets.QLineEdit(Form)
+ self.lineEdit.setObjectName("lineEdit")
+ self.verticalLayout.addWidget(self.lineEdit)
+ self.label = QtWidgets.QLabel(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
+ self.label.setSizePolicy(sizePolicy)
+ self.label.setObjectName("label")
+ self.verticalLayout.addWidget(self.label)
+ self.comboBox = QtWidgets.QComboBox(Form)
+ self.comboBox.setObjectName("comboBox")
+ self.verticalLayout.addWidget(self.comboBox)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem)
+ self.gridLayout.addLayout(self.verticalLayout, 1, 0, 1, 1)
+ self.pushButton = QtWidgets.QPushButton(Form)
+ self.pushButton.setObjectName("pushButton")
+ self.gridLayout.addWidget(self.pushButton, 2, 1, 1, 1)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ item = self.tableWidget.horizontalHeaderItem(0)
+ item.setText(_translate("Form", "Name"))
+ item = self.tableWidget.horizontalHeaderItem(1)
+ item.setText(_translate("Form", "Value"))
+ self.label_2.setText(_translate("Form", "Nice name"))
+ self.label.setText(_translate("Form", "TextLabel"))
+ self.pushButton.setText(_translate("Form", "Add option"))
diff --git a/nmreval/gui_qt/_py/parameterform.py b/nmreval/gui_qt/_py/parameterform.py
new file mode 100644
index 0000000..5bf7ba2
--- /dev/null
+++ b/nmreval/gui_qt/_py/parameterform.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/parameterform.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_parameterform(object):
+ def setupUi(self, parameterform):
+ parameterform.setObjectName("parameterform")
+ parameterform.setWindowModality(QtCore.Qt.WindowModal)
+ parameterform.resize(290, 37)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(parameterform.sizePolicy().hasHeightForWidth())
+ parameterform.setSizePolicy(sizePolicy)
+ parameterform.setAutoFillBackground(True)
+ self.horizontalLayout = QtWidgets.QHBoxLayout(parameterform)
+ self.horizontalLayout.setContentsMargins(1, 1, 1, 1)
+ self.horizontalLayout.setSpacing(2)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.line_2 = QtWidgets.QFrame(parameterform)
+ self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.horizontalLayout.addWidget(self.line_2)
+ self.label = QtWidgets.QLabel(parameterform)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(1)
+ sizePolicy.setVerticalStretch(10)
+ sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
+ self.label.setSizePolicy(sizePolicy)
+ self.label.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ spacerItem = QtWidgets.QSpacerItem(65, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.vals = QtWidgets.QLineEdit(parameterform)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.vals.sizePolicy().hasHeightForWidth())
+ self.vals.setSizePolicy(sizePolicy)
+ self.vals.setObjectName("vals")
+ self.horizontalLayout.addWidget(self.vals)
+ self.checkBox = QtWidgets.QCheckBox(parameterform)
+ self.checkBox.setObjectName("checkBox")
+ self.horizontalLayout.addWidget(self.checkBox)
+
+ self.retranslateUi(parameterform)
+ QtCore.QMetaObject.connectSlotsByName(parameterform)
+
+ def retranslateUi(self, parameterform):
+ _translate = QtCore.QCoreApplication.translate
+ parameterform.setWindowTitle(_translate("parameterform", "Form"))
+ self.label.setText(_translate("parameterform", "Name"))
+ self.vals.setText(_translate("parameterform", "1"))
+ self.checkBox.setText(_translate("parameterform", "Fix?"))
diff --git a/nmreval/gui_qt/_py/phase_corr_dialog.py b/nmreval/gui_qt/_py/phase_corr_dialog.py
new file mode 100644
index 0000000..02c5898
--- /dev/null
+++ b/nmreval/gui_qt/_py/phase_corr_dialog.py
@@ -0,0 +1,94 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/phase_corr_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_SignalEdit(object):
+ def setupUi(self, SignalEdit):
+ SignalEdit.setObjectName("SignalEdit")
+ SignalEdit.resize(919, 595)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(SignalEdit.sizePolicy().hasHeightForWidth())
+ SignalEdit.setSizePolicy(sizePolicy)
+ self.gridLayout = QtWidgets.QGridLayout(SignalEdit)
+ self.gridLayout.setContentsMargins(6, 6, 6, 6)
+ self.gridLayout.setSpacing(3)
+ self.gridLayout.setObjectName("gridLayout")
+ self.graphicsView = PlotWidget(SignalEdit)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
+ self.graphicsView.setSizePolicy(sizePolicy)
+ self.graphicsView.setObjectName("graphicsView")
+ self.gridLayout.addWidget(self.graphicsView, 0, 0, 1, 8)
+ self.pivot_lineedit = QtWidgets.QLineEdit(SignalEdit)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.pivot_lineedit.sizePolicy().hasHeightForWidth())
+ self.pivot_lineedit.setSizePolicy(sizePolicy)
+ self.pivot_lineedit.setInputMethodHints(QtCore.Qt.ImhDigitsOnly)
+ self.pivot_lineedit.setObjectName("pivot_lineedit")
+ self.gridLayout.addWidget(self.pivot_lineedit, 1, 7, 1, 1)
+ self.ph0slider = QtWidgets.QDoubleSpinBox(SignalEdit)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.ph0slider.sizePolicy().hasHeightForWidth())
+ self.ph0slider.setSizePolicy(sizePolicy)
+ self.ph0slider.setMinimum(-180.0)
+ self.ph0slider.setMaximum(180.0)
+ self.ph0slider.setObjectName("ph0slider")
+ self.gridLayout.addWidget(self.ph0slider, 1, 1, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(SignalEdit)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 2, 1, 1, 7)
+ self.ph1slider = QtWidgets.QDoubleSpinBox(SignalEdit)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.ph1slider.sizePolicy().hasHeightForWidth())
+ self.ph1slider.setSizePolicy(sizePolicy)
+ self.ph1slider.setMinimum(-360.0)
+ self.ph1slider.setMaximum(360.0)
+ self.ph1slider.setObjectName("ph1slider")
+ self.gridLayout.addWidget(self.ph1slider, 1, 4, 1, 1)
+ self.label_8 = QtWidgets.QLabel(SignalEdit)
+ self.label_8.setObjectName("label_8")
+ self.gridLayout.addWidget(self.label_8, 1, 6, 1, 1)
+ self.label_6 = QtWidgets.QLabel(SignalEdit)
+ self.label_6.setObjectName("label_6")
+ self.gridLayout.addWidget(self.label_6, 1, 3, 1, 1)
+ self.label = QtWidgets.QLabel(SignalEdit)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout.addItem(spacerItem, 1, 2, 1, 1)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout.addItem(spacerItem1, 1, 5, 1, 1)
+
+ self.retranslateUi(SignalEdit)
+ self.buttonBox.accepted.connect(SignalEdit.accept)
+ self.buttonBox.rejected.connect(SignalEdit.close)
+ QtCore.QMetaObject.connectSlotsByName(SignalEdit)
+
+ def retranslateUi(self, SignalEdit):
+ _translate = QtCore.QCoreApplication.translate
+ SignalEdit.setWindowTitle(_translate("SignalEdit", "Phase correction"))
+ self.pivot_lineedit.setText(_translate("SignalEdit", "0"))
+ self.label_8.setText(_translate("SignalEdit", "Pivot"))
+ self.label_6.setText(_translate("SignalEdit", "Phase 1"))
+ self.label.setText(_translate("SignalEdit", "Phase 0"))
+from pyqtgraph import PlotWidget
diff --git a/nmreval/gui_qt/_py/plotConfigTemplate.py b/nmreval/gui_qt/_py/plotConfigTemplate.py
new file mode 100644
index 0000000..a88d8db
--- /dev/null
+++ b/nmreval/gui_qt/_py/plotConfigTemplate.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/plotConfigTemplate.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(481, 840)
+ self.averageGroup = QtWidgets.QGroupBox(Form)
+ self.averageGroup.setGeometry(QtCore.QRect(0, 640, 242, 182))
+ self.averageGroup.setCheckable(True)
+ self.averageGroup.setChecked(False)
+ self.averageGroup.setObjectName("averageGroup")
+ self.gridLayout_5 = QtWidgets.QGridLayout(self.averageGroup)
+ self.gridLayout_5.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout_5.setSpacing(0)
+ self.gridLayout_5.setObjectName("gridLayout_5")
+ self.avgParamList = QtWidgets.QListWidget(self.averageGroup)
+ self.avgParamList.setObjectName("avgParamList")
+ self.gridLayout_5.addWidget(self.avgParamList, 0, 0, 1, 1)
+ self.decimateGroup = QtWidgets.QFrame(Form)
+ self.decimateGroup.setGeometry(QtCore.QRect(10, 140, 191, 171))
+ self.decimateGroup.setObjectName("decimateGroup")
+ self.gridLayout_4 = QtWidgets.QGridLayout(self.decimateGroup)
+ self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout_4.setSpacing(0)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ self.clipToViewCheck = QtWidgets.QCheckBox(self.decimateGroup)
+ self.clipToViewCheck.setObjectName("clipToViewCheck")
+ self.gridLayout_4.addWidget(self.clipToViewCheck, 7, 0, 1, 3)
+ self.maxTracesCheck = QtWidgets.QCheckBox(self.decimateGroup)
+ self.maxTracesCheck.setObjectName("maxTracesCheck")
+ self.gridLayout_4.addWidget(self.maxTracesCheck, 8, 0, 1, 2)
+ self.downsampleCheck = QtWidgets.QCheckBox(self.decimateGroup)
+ self.downsampleCheck.setObjectName("downsampleCheck")
+ self.gridLayout_4.addWidget(self.downsampleCheck, 0, 0, 1, 3)
+ self.peakRadio = QtWidgets.QRadioButton(self.decimateGroup)
+ self.peakRadio.setChecked(True)
+ self.peakRadio.setObjectName("peakRadio")
+ self.gridLayout_4.addWidget(self.peakRadio, 6, 1, 1, 2)
+ self.maxTracesSpin = QtWidgets.QSpinBox(self.decimateGroup)
+ self.maxTracesSpin.setObjectName("maxTracesSpin")
+ self.gridLayout_4.addWidget(self.maxTracesSpin, 8, 2, 1, 1)
+ self.forgetTracesCheck = QtWidgets.QCheckBox(self.decimateGroup)
+ self.forgetTracesCheck.setObjectName("forgetTracesCheck")
+ self.gridLayout_4.addWidget(self.forgetTracesCheck, 9, 0, 1, 3)
+ self.meanRadio = QtWidgets.QRadioButton(self.decimateGroup)
+ self.meanRadio.setObjectName("meanRadio")
+ self.gridLayout_4.addWidget(self.meanRadio, 3, 1, 1, 2)
+ self.subsampleRadio = QtWidgets.QRadioButton(self.decimateGroup)
+ self.subsampleRadio.setObjectName("subsampleRadio")
+ self.gridLayout_4.addWidget(self.subsampleRadio, 2, 1, 1, 2)
+ self.autoDownsampleCheck = QtWidgets.QCheckBox(self.decimateGroup)
+ self.autoDownsampleCheck.setChecked(True)
+ self.autoDownsampleCheck.setObjectName("autoDownsampleCheck")
+ self.gridLayout_4.addWidget(self.autoDownsampleCheck, 1, 2, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(30, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout_4.addItem(spacerItem, 2, 0, 1, 1)
+ self.downsampleSpin = QtWidgets.QSpinBox(self.decimateGroup)
+ self.downsampleSpin.setMinimum(1)
+ self.downsampleSpin.setMaximum(100000)
+ self.downsampleSpin.setProperty("value", 1)
+ self.downsampleSpin.setObjectName("downsampleSpin")
+ self.gridLayout_4.addWidget(self.downsampleSpin, 1, 1, 1, 1)
+ self.transformGroup = QtWidgets.QFrame(Form)
+ self.transformGroup.setGeometry(QtCore.QRect(10, 10, 171, 101))
+ self.transformGroup.setObjectName("transformGroup")
+ self.gridLayout = QtWidgets.QGridLayout(self.transformGroup)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setSpacing(0)
+ self.gridLayout.setObjectName("gridLayout")
+ self.logYCheck = QtWidgets.QCheckBox(self.transformGroup)
+ self.logYCheck.setObjectName("logYCheck")
+ self.gridLayout.addWidget(self.logYCheck, 2, 0, 1, 1)
+ self.logXCheck = QtWidgets.QCheckBox(self.transformGroup)
+ self.logXCheck.setObjectName("logXCheck")
+ self.gridLayout.addWidget(self.logXCheck, 1, 0, 1, 1)
+ self.fftCheck = QtWidgets.QCheckBox(self.transformGroup)
+ self.fftCheck.setObjectName("fftCheck")
+ self.gridLayout.addWidget(self.fftCheck, 0, 0, 1, 1)
+ self.derivativeCheck = QtWidgets.QCheckBox(self.transformGroup)
+ self.derivativeCheck.setObjectName("derivativeCheck")
+ self.gridLayout.addWidget(self.derivativeCheck, 3, 0, 1, 1)
+ self.phasemapCheck = QtWidgets.QCheckBox(self.transformGroup)
+ self.phasemapCheck.setObjectName("phasemapCheck")
+ self.gridLayout.addWidget(self.phasemapCheck, 4, 0, 1, 1)
+ self.pointsGroup = QtWidgets.QGroupBox(Form)
+ self.pointsGroup.setGeometry(QtCore.QRect(10, 550, 234, 58))
+ self.pointsGroup.setCheckable(True)
+ self.pointsGroup.setObjectName("pointsGroup")
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.pointsGroup)
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.autoPointsCheck = QtWidgets.QCheckBox(self.pointsGroup)
+ self.autoPointsCheck.setChecked(True)
+ self.autoPointsCheck.setObjectName("autoPointsCheck")
+ self.verticalLayout_5.addWidget(self.autoPointsCheck)
+ self.gridGroup = QtWidgets.QFrame(Form)
+ self.gridGroup.setGeometry(QtCore.QRect(10, 460, 221, 81))
+ self.gridGroup.setObjectName("gridGroup")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.gridGroup)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.xGridCheck = QtWidgets.QCheckBox(self.gridGroup)
+ self.xGridCheck.setObjectName("xGridCheck")
+ self.gridLayout_2.addWidget(self.xGridCheck, 0, 0, 1, 2)
+ self.yGridCheck = QtWidgets.QCheckBox(self.gridGroup)
+ self.yGridCheck.setObjectName("yGridCheck")
+ self.gridLayout_2.addWidget(self.yGridCheck, 1, 0, 1, 2)
+ self.gridAlphaSlider = QtWidgets.QSlider(self.gridGroup)
+ self.gridAlphaSlider.setMaximum(255)
+ self.gridAlphaSlider.setProperty("value", 128)
+ self.gridAlphaSlider.setOrientation(QtCore.Qt.Horizontal)
+ self.gridAlphaSlider.setObjectName("gridAlphaSlider")
+ self.gridLayout_2.addWidget(self.gridAlphaSlider, 2, 1, 1, 1)
+ self.label = QtWidgets.QLabel(self.gridGroup)
+ self.label.setObjectName("label")
+ self.gridLayout_2.addWidget(self.label, 2, 0, 1, 1)
+ self.alphaGroup = QtWidgets.QGroupBox(Form)
+ self.alphaGroup.setGeometry(QtCore.QRect(10, 390, 234, 60))
+ self.alphaGroup.setCheckable(True)
+ self.alphaGroup.setObjectName("alphaGroup")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.alphaGroup)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.autoAlphaCheck = QtWidgets.QCheckBox(self.alphaGroup)
+ self.autoAlphaCheck.setChecked(False)
+ self.autoAlphaCheck.setObjectName("autoAlphaCheck")
+ self.horizontalLayout.addWidget(self.autoAlphaCheck)
+ self.alphaSlider = QtWidgets.QSlider(self.alphaGroup)
+ self.alphaSlider.setMaximum(1000)
+ self.alphaSlider.setProperty("value", 1000)
+ self.alphaSlider.setOrientation(QtCore.Qt.Horizontal)
+ self.alphaSlider.setObjectName("alphaSlider")
+ self.horizontalLayout.addWidget(self.alphaSlider)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "PyQtGraph"))
+ self.averageGroup.setToolTip(_translate("Form", "Display averages of the curves displayed in this plot. The parameter list allows you to choose parameters to average over (if any are available)."))
+ self.averageGroup.setTitle(_translate("Form", "Average"))
+ self.clipToViewCheck.setToolTip(_translate("Form", "Plot only the portion of each curve that is visible. This assumes X values are uniformly spaced."))
+ self.clipToViewCheck.setText(_translate("Form", "Clip to View"))
+ self.maxTracesCheck.setToolTip(_translate("Form", "If multiple curves are displayed in this plot, check this box to limit the number of traces that are displayed."))
+ self.maxTracesCheck.setText(_translate("Form", "Max Traces:"))
+ self.downsampleCheck.setText(_translate("Form", "Downsample"))
+ self.peakRadio.setToolTip(_translate("Form", "Downsample by drawing a saw wave that follows the min and max of the original data. This method produces the best visual representation of the data but is slower."))
+ self.peakRadio.setText(_translate("Form", "Peak"))
+ self.maxTracesSpin.setToolTip(_translate("Form", "If multiple curves are displayed in this plot, check \"Max Traces\" and set this value to limit the number of traces that are displayed."))
+ self.forgetTracesCheck.setToolTip(_translate("Form", "If MaxTraces is checked, remove curves from memory after they are hidden (saves memory, but traces can not be un-hidden)."))
+ self.forgetTracesCheck.setText(_translate("Form", "Forget hidden traces"))
+ self.meanRadio.setToolTip(_translate("Form", "Downsample by taking the mean of N samples."))
+ self.meanRadio.setText(_translate("Form", "Mean"))
+ self.subsampleRadio.setToolTip(_translate("Form", "Downsample by taking the first of N samples. This method is fastest and least accurate."))
+ self.subsampleRadio.setText(_translate("Form", "Subsample"))
+ self.autoDownsampleCheck.setToolTip(_translate("Form", "Automatically downsample data based on the visible range. This assumes X values are uniformly spaced."))
+ self.autoDownsampleCheck.setText(_translate("Form", "Auto"))
+ self.downsampleSpin.setToolTip(_translate("Form", "Downsample data before plotting. (plot every Nth sample)"))
+ self.downsampleSpin.setSuffix(_translate("Form", "x"))
+ self.logYCheck.setText(_translate("Form", "Log Y"))
+ self.logXCheck.setText(_translate("Form", "Log X"))
+ self.fftCheck.setText(_translate("Form", "Power Spectrum (FFT)"))
+ self.derivativeCheck.setText(_translate("Form", "dy/dx"))
+ self.phasemapCheck.setText(_translate("Form", "Y vs. Y\'"))
+ self.pointsGroup.setTitle(_translate("Form", "Points"))
+ self.autoPointsCheck.setText(_translate("Form", "Auto"))
+ self.xGridCheck.setText(_translate("Form", "Show X Grid"))
+ self.yGridCheck.setText(_translate("Form", "Show Y Grid"))
+ self.label.setText(_translate("Form", "Opacity"))
+ self.alphaGroup.setTitle(_translate("Form", "Alpha"))
+ self.autoAlphaCheck.setText(_translate("Form", "Auto"))
diff --git a/nmreval/gui_qt/_py/pokemon.py b/nmreval/gui_qt/_py/pokemon.py
new file mode 100644
index 0000000..e601c7e
--- /dev/null
+++ b/nmreval/gui_qt/_py/pokemon.py
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/pokemon.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(400, 359)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth())
+ Dialog.setSizePolicy(sizePolicy)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.tabWidget = QtWidgets.QTabWidget(Dialog)
+ self.tabWidget.setObjectName("tabWidget")
+ self.verticalLayout.addWidget(self.tabWidget)
+ self.formLayout = QtWidgets.QFormLayout()
+ self.formLayout.setObjectName("formLayout")
+ self.label = QtWidgets.QLabel(Dialog)
+ self.label.setObjectName("label")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)
+ self.pokedex_nr = QtWidgets.QLabel(Dialog)
+ self.pokedex_nr.setObjectName("pokedex_nr")
+ self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.pokedex_nr)
+ self.label_2 = QtWidgets.QLabel(Dialog)
+ self.label_2.setObjectName("label_2")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_2)
+ self.name = QtWidgets.QComboBox(Dialog)
+ self.name.setObjectName("name")
+ self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.name)
+ self.label_3 = QtWidgets.QLabel(Dialog)
+ self.label_3.setObjectName("label_3")
+ self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_3)
+ self.category = QtWidgets.QLabel(Dialog)
+ self.category.setObjectName("category")
+ self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.category)
+ self.label_4 = QtWidgets.QLabel(Dialog)
+ self.label_4.setObjectName("label_4")
+ self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_4)
+ self.poketype = QtWidgets.QLabel(Dialog)
+ self.poketype.setObjectName("poketype")
+ self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.poketype)
+ self.label_5 = QtWidgets.QLabel(Dialog)
+ self.label_5.setObjectName("label_5")
+ self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_5)
+ self.height = QtWidgets.QLabel(Dialog)
+ self.height.setObjectName("height")
+ self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.height)
+ self.label_6 = QtWidgets.QLabel(Dialog)
+ self.label_6.setObjectName("label_6")
+ self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_6)
+ self.weight = QtWidgets.QLabel(Dialog)
+ self.weight.setObjectName("weight")
+ self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.weight)
+ self.label_7 = QtWidgets.QLabel(Dialog)
+ self.label_7.setObjectName("label_7")
+ self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_7)
+ self.color = QtWidgets.QLabel(Dialog)
+ self.color.setObjectName("color")
+ self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.color)
+ self.label_8 = QtWidgets.QLabel(Dialog)
+ self.label_8.setObjectName("label_8")
+ self.formLayout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_8)
+ self.info = QtWidgets.QLabel(Dialog)
+ self.info.setObjectName("info")
+ self.formLayout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.info)
+ self.verticalLayout.addLayout(self.formLayout)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.prev_button = QtWidgets.QToolButton(Dialog)
+ self.prev_button.setObjectName("prev_button")
+ self.horizontalLayout_2.addWidget(self.prev_button)
+ self.next_button = QtWidgets.QToolButton(Dialog)
+ self.next_button.setObjectName("next_button")
+ self.horizontalLayout_2.addWidget(self.next_button)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Retry)
+ self.buttonBox.setCenterButtons(False)
+ self.buttonBox.setObjectName("buttonBox")
+ self.horizontalLayout_2.addWidget(self.buttonBox)
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
+ self.label_2.setBuddy(self.name)
+
+ self.retranslateUi(Dialog)
+ self.tabWidget.setCurrentIndex(-1)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Random Pokémon"))
+ self.label.setText(_translate("Dialog", "National-Dex"))
+ self.pokedex_nr.setText(_translate("Dialog", "TextLabel"))
+ self.label_2.setText(_translate("Dialog", "Name"))
+ self.label_3.setText(_translate("Dialog", "Kategorie"))
+ self.category.setText(_translate("Dialog", "TextLabel"))
+ self.label_4.setText(_translate("Dialog", "Typ"))
+ self.poketype.setText(_translate("Dialog", "TextLabel"))
+ self.label_5.setText(_translate("Dialog", "Größe"))
+ self.height.setText(_translate("Dialog", "TextLabel"))
+ self.label_6.setText(_translate("Dialog", "Gewicht"))
+ self.weight.setText(_translate("Dialog", "TextLabel"))
+ self.label_7.setText(_translate("Dialog", "Farbe"))
+ self.color.setText(_translate("Dialog", "TextLabel"))
+ self.label_8.setText(_translate("Dialog", "Mehr..."))
+ self.info.setText(_translate("Dialog", "TextLabel"))
+ self.prev_button.setText(_translate("Dialog", "Prev."))
+ self.next_button.setText(_translate("Dialog", "Next"))
diff --git a/nmreval/gui_qt/_py/propwidget.py b/nmreval/gui_qt/_py/propwidget.py
new file mode 100644
index 0000000..a3c4c3b
--- /dev/null
+++ b/nmreval/gui_qt/_py/propwidget.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/propwidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(400, 300)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Form)
+ self.verticalLayout.setContentsMargins(2, 2, 2, 2)
+ self.verticalLayout.setSpacing(2)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.tabWidget = QtWidgets.QTabWidget(Form)
+ self.tabWidget.setObjectName("tabWidget")
+ self.tabWidgetPage2 = QtWidgets.QWidget()
+ self.tabWidgetPage2.setObjectName("tabWidgetPage2")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tabWidgetPage2)
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_3.setSpacing(0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.tableWidget_2 = QtWidgets.QTableWidget(self.tabWidgetPage2)
+ self.tableWidget_2.setObjectName("tableWidget_2")
+ self.tableWidget_2.setColumnCount(0)
+ self.tableWidget_2.setRowCount(0)
+ self.verticalLayout_3.addWidget(self.tableWidget_2)
+ self.tabWidget.addTab(self.tabWidgetPage2, "")
+ self.tabWidgetPage1 = QtWidgets.QWidget()
+ self.tabWidgetPage1.setObjectName("tabWidgetPage1")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tabWidgetPage1)
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_2.setSpacing(0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.tableWidget = QtWidgets.QTableWidget(self.tabWidgetPage1)
+ self.tableWidget.setObjectName("tableWidget")
+ self.tableWidget.setColumnCount(0)
+ self.tableWidget.setRowCount(0)
+ self.verticalLayout_2.addWidget(self.tableWidget)
+ self.tabWidget.addTab(self.tabWidgetPage1, "")
+ self.verticalLayout.addWidget(self.tabWidget)
+
+ self.retranslateUi(Form)
+ self.tabWidget.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabWidgetPage2), _translate("Form", "General"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabWidgetPage1), _translate("Form", "Symbol"))
diff --git a/nmreval/gui_qt/_py/ptstab.py b/nmreval/gui_qt/_py/ptstab.py
new file mode 100644
index 0000000..677b38b
--- /dev/null
+++ b/nmreval/gui_qt/_py/ptstab.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/ptstab.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(316, 747)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Form)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.peaktable = QtWidgets.QListWidget(Form)
+ self.peaktable.setEditTriggers(QtWidgets.QAbstractItemView.DoubleClicked|QtWidgets.QAbstractItemView.EditKeyPressed)
+ self.peaktable.setObjectName("peaktable")
+ self.verticalLayout.addWidget(self.peaktable)
+ self.groupBox = QtWidgets.QGroupBox(Form)
+ self.groupBox.setObjectName("groupBox")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.groupBox)
+ self.horizontalLayout.setContentsMargins(3, 3, 3, 3)
+ self.horizontalLayout.setSpacing(3)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.left_pt = QtWidgets.QSpinBox(self.groupBox)
+ self.left_pt.setMaximum(999)
+ self.left_pt.setObjectName("left_pt")
+ self.horizontalLayout.addWidget(self.left_pt)
+ self.right_pt = QtWidgets.QSpinBox(self.groupBox)
+ self.right_pt.setMaximum(999)
+ self.right_pt.setObjectName("right_pt")
+ self.horizontalLayout.addWidget(self.right_pt)
+ self.average_combobox = QtWidgets.QComboBox(self.groupBox)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.average_combobox.sizePolicy().hasHeightForWidth())
+ self.average_combobox.setSizePolicy(sizePolicy)
+ self.average_combobox.setObjectName("average_combobox")
+ self.average_combobox.addItem("")
+ self.average_combobox.addItem("")
+ self.average_combobox.addItem("")
+ self.horizontalLayout.addWidget(self.average_combobox)
+ self.verticalLayout.addWidget(self.groupBox)
+ self.groupBox_2 = QtWidgets.QGroupBox(Form)
+ self.groupBox_2.setCheckable(True)
+ self.groupBox_2.setChecked(False)
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.groupBox_2)
+ self.horizontalLayout_5.setContentsMargins(3, 3, 3, 3)
+ self.horizontalLayout_5.setSpacing(2)
+ self.horizontalLayout_5.setObjectName("horizontalLayout_5")
+ self.special_comboBox = QtWidgets.QComboBox(self.groupBox_2)
+ self.special_comboBox.setObjectName("special_comboBox")
+ self.special_comboBox.addItem("")
+ self.special_comboBox.addItem("")
+ self.special_comboBox.addItem("")
+ self.special_comboBox.addItem("")
+ self.horizontalLayout_5.addWidget(self.special_comboBox)
+ self.verticalLayout.addWidget(self.groupBox_2)
+ self.groupBox_3 = QtWidgets.QGroupBox(Form)
+ self.groupBox_3.setObjectName("groupBox_3")
+ self.gridLayout = QtWidgets.QGridLayout(self.groupBox_3)
+ self.gridLayout.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout.setSpacing(3)
+ self.gridLayout.setObjectName("gridLayout")
+ self.xbutton = QtWidgets.QCheckBox(self.groupBox_3)
+ self.xbutton.setObjectName("xbutton")
+ self.gridLayout.addWidget(self.xbutton, 0, 0, 1, 1)
+ self.ybutton = QtWidgets.QCheckBox(self.groupBox_3)
+ self.ybutton.setChecked(True)
+ self.ybutton.setObjectName("ybutton")
+ self.gridLayout.addWidget(self.ybutton, 0, 1, 1, 1)
+ self.graph_checkbox = QtWidgets.QCheckBox(self.groupBox_3)
+ self.graph_checkbox.setChecked(True)
+ self.graph_checkbox.setObjectName("graph_checkbox")
+ self.gridLayout.addWidget(self.graph_checkbox, 1, 0, 1, 1)
+ self.graph_combobox = QtWidgets.QComboBox(self.groupBox_3)
+ self.graph_combobox.setEnabled(False)
+ self.graph_combobox.setObjectName("graph_combobox")
+ self.gridLayout.addWidget(self.graph_combobox, 1, 1, 1, 1)
+ self.verticalLayout.addWidget(self.groupBox_3)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
+ self.horizontalLayout_2.setSpacing(2)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.okButton = QtWidgets.QPushButton(Form)
+ icon = QtGui.QIcon.fromTheme("dialog-ok")
+ self.okButton.setIcon(icon)
+ self.okButton.setObjectName("okButton")
+ self.horizontalLayout_2.addWidget(self.okButton)
+ self.deleteButton = QtWidgets.QPushButton(Form)
+ icon = QtGui.QIcon.fromTheme("dialog-cancel")
+ self.deleteButton.setIcon(icon)
+ self.deleteButton.setObjectName("deleteButton")
+ self.horizontalLayout_2.addWidget(self.deleteButton)
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.peaktable.setToolTip(_translate("Form", "Edit by entering new value: \n"
+"Single number for points (e.g. 1e-6); \n"
+"two numbers separated by space for regions (e.g. 1e-6 5e-6). \n"
+"Changing between regions and points is NOT possible"))
+ self.groupBox.setTitle(_translate("Form", "Average"))
+ self.left_pt.setSuffix(_translate("Form", " pts"))
+ self.left_pt.setPrefix(_translate("Form", "- "))
+ self.right_pt.setSuffix(_translate("Form", " pts"))
+ self.right_pt.setPrefix(_translate("Form", "+ "))
+ self.average_combobox.setItemText(0, _translate("Form", "Mean"))
+ self.average_combobox.setItemText(1, _translate("Form", "Sum"))
+ self.average_combobox.setItemText(2, _translate("Form", "Integral"))
+ self.groupBox_2.setTitle(_translate("Form", "Special value"))
+ self.special_comboBox.setToolTip(_translate("Form", "Automatic selection of respective points"))
+ self.special_comboBox.setItemText(0, _translate("Form", "max(y)"))
+ self.special_comboBox.setItemText(1, _translate("Form", "max(abs(y))"))
+ self.special_comboBox.setItemText(2, _translate("Form", "min(y)"))
+ self.special_comboBox.setItemText(3, _translate("Form", "min(abs(y))"))
+ self.groupBox_3.setTitle(_translate("Form", "Result"))
+ self.xbutton.setText(_translate("Form", "x"))
+ self.ybutton.setText(_translate("Form", "y"))
+ self.graph_checkbox.setText(_translate("Form", "New graph?"))
+ self.okButton.setText(_translate("Form", "Apply"))
+ self.deleteButton.setText(_translate("Form", "Delete selected"))
diff --git a/nmreval/gui_qt/_py/qfiledialog.py b/nmreval/gui_qt/_py/qfiledialog.py
new file mode 100644
index 0000000..52829e2
--- /dev/null
+++ b/nmreval/gui_qt/_py/qfiledialog.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/qfiledialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_QFileDialog(object):
+ def setupUi(self, QFileDialog):
+ QFileDialog.setObjectName("QFileDialog")
+ QFileDialog.resize(521, 316)
+ QFileDialog.setSizeGripEnabled(True)
+ self.gridlayout = QtWidgets.QGridLayout(QFileDialog)
+ self.gridlayout.setObjectName("gridlayout")
+ self.lookInLabel = QtWidgets.QLabel(QFileDialog)
+ self.lookInLabel.setObjectName("lookInLabel")
+ self.gridlayout.addWidget(self.lookInLabel, 0, 0, 1, 1)
+ self.hboxlayout = QtWidgets.QHBoxLayout()
+ self.hboxlayout.setObjectName("hboxlayout")
+ self.lookInCombo = QtWidgets.QComboBox(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(1)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.lookInCombo.sizePolicy().hasHeightForWidth())
+ self.lookInCombo.setSizePolicy(sizePolicy)
+ self.lookInCombo.setMinimumSize(QtCore.QSize(50, 0))
+ self.lookInCombo.setObjectName("lookInCombo")
+ self.hboxlayout.addWidget(self.lookInCombo)
+ self.backButton = QtWidgets.QToolButton(QFileDialog)
+ self.backButton.setObjectName("backButton")
+ self.hboxlayout.addWidget(self.backButton)
+ self.forwardButton = QtWidgets.QToolButton(QFileDialog)
+ self.forwardButton.setObjectName("forwardButton")
+ self.hboxlayout.addWidget(self.forwardButton)
+ self.toParentButton = QtWidgets.QToolButton(QFileDialog)
+ self.toParentButton.setObjectName("toParentButton")
+ self.hboxlayout.addWidget(self.toParentButton)
+ self.newFolderButton = QtWidgets.QToolButton(QFileDialog)
+ self.newFolderButton.setObjectName("newFolderButton")
+ self.hboxlayout.addWidget(self.newFolderButton)
+ self.listModeButton = QtWidgets.QToolButton(QFileDialog)
+ self.listModeButton.setObjectName("listModeButton")
+ self.hboxlayout.addWidget(self.listModeButton)
+ self.detailModeButton = QtWidgets.QToolButton(QFileDialog)
+ self.detailModeButton.setObjectName("detailModeButton")
+ self.hboxlayout.addWidget(self.detailModeButton)
+ self.gridlayout.addLayout(self.hboxlayout, 0, 1, 1, 2)
+ self.splitter = QtWidgets.QSplitter(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.splitter.sizePolicy().hasHeightForWidth())
+ self.splitter.setSizePolicy(sizePolicy)
+ self.splitter.setOrientation(QtCore.Qt.Horizontal)
+ self.splitter.setChildrenCollapsible(False)
+ self.splitter.setObjectName("splitter")
+ self.sidebar = QtWidgets.QListWidget(self.splitter)
+ self.sidebar.setObjectName("sidebar")
+ self.frame = QtWidgets.QFrame(self.splitter)
+ self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
+ self.frame.setObjectName("frame")
+ self.vboxlayout = QtWidgets.QVBoxLayout(self.frame)
+ self.vboxlayout.setContentsMargins(0, 0, 0, 0)
+ self.vboxlayout.setSpacing(0)
+ self.vboxlayout.setObjectName("vboxlayout")
+ self.stackedWidget = QtWidgets.QStackedWidget(self.frame)
+ self.stackedWidget.setObjectName("stackedWidget")
+ self.page = QtWidgets.QWidget()
+ self.page.setObjectName("page")
+ self.vboxlayout1 = QtWidgets.QVBoxLayout(self.page)
+ self.vboxlayout1.setContentsMargins(0, 0, 0, 0)
+ self.vboxlayout1.setSpacing(0)
+ self.vboxlayout1.setObjectName("vboxlayout1")
+ self.listView = QtWidgets.QListView(self.page)
+ self.listView.setObjectName("listView")
+ self.vboxlayout1.addWidget(self.listView)
+ self.stackedWidget.addWidget(self.page)
+ self.page_2 = QtWidgets.QWidget()
+ self.page_2.setObjectName("page_2")
+ self.vboxlayout2 = QtWidgets.QVBoxLayout(self.page_2)
+ self.vboxlayout2.setContentsMargins(0, 0, 0, 0)
+ self.vboxlayout2.setSpacing(0)
+ self.vboxlayout2.setObjectName("vboxlayout2")
+ self.treeView = QtWidgets.QTreeView(self.page_2)
+ self.treeView.setObjectName("treeView")
+ self.vboxlayout2.addWidget(self.treeView)
+ self.stackedWidget.addWidget(self.page_2)
+ self.vboxlayout.addWidget(self.stackedWidget)
+ self.gridlayout.addWidget(self.splitter, 1, 0, 1, 3)
+ self.fileNameLabel = QtWidgets.QLabel(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fileNameLabel.sizePolicy().hasHeightForWidth())
+ self.fileNameLabel.setSizePolicy(sizePolicy)
+ self.fileNameLabel.setMinimumSize(QtCore.QSize(0, 0))
+ self.fileNameLabel.setObjectName("fileNameLabel")
+ self.gridlayout.addWidget(self.fileNameLabel, 2, 0, 1, 1)
+ self.fileNameEdit = QtWidgets.QLineEdit(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(1)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fileNameEdit.sizePolicy().hasHeightForWidth())
+ self.fileNameEdit.setSizePolicy(sizePolicy)
+ self.fileNameEdit.setObjectName("fileNameEdit")
+ self.gridlayout.addWidget(self.fileNameEdit, 2, 1, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(QFileDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Vertical)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridlayout.addWidget(self.buttonBox, 2, 2, 2, 1)
+ self.fileTypeLabel = QtWidgets.QLabel(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fileTypeLabel.sizePolicy().hasHeightForWidth())
+ self.fileTypeLabel.setSizePolicy(sizePolicy)
+ self.fileTypeLabel.setObjectName("fileTypeLabel")
+ self.gridlayout.addWidget(self.fileTypeLabel, 3, 0, 1, 1)
+ self.fileTypeCombo = QtWidgets.QComboBox(QFileDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.fileTypeCombo.sizePolicy().hasHeightForWidth())
+ self.fileTypeCombo.setSizePolicy(sizePolicy)
+ self.fileTypeCombo.setObjectName("fileTypeCombo")
+ self.gridlayout.addWidget(self.fileTypeCombo, 3, 1, 1, 1)
+
+ self.retranslateUi(QFileDialog)
+ self.stackedWidget.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(QFileDialog)
+ QFileDialog.setTabOrder(self.lookInCombo, self.backButton)
+ QFileDialog.setTabOrder(self.backButton, self.forwardButton)
+ QFileDialog.setTabOrder(self.forwardButton, self.toParentButton)
+ QFileDialog.setTabOrder(self.toParentButton, self.newFolderButton)
+ QFileDialog.setTabOrder(self.newFolderButton, self.listModeButton)
+ QFileDialog.setTabOrder(self.listModeButton, self.detailModeButton)
+ QFileDialog.setTabOrder(self.detailModeButton, self.sidebar)
+ QFileDialog.setTabOrder(self.sidebar, self.treeView)
+ QFileDialog.setTabOrder(self.treeView, self.listView)
+ QFileDialog.setTabOrder(self.listView, self.fileNameEdit)
+ QFileDialog.setTabOrder(self.fileNameEdit, self.buttonBox)
+ QFileDialog.setTabOrder(self.buttonBox, self.fileTypeCombo)
+
+ def retranslateUi(self, QFileDialog):
+ _translate = QtCore.QCoreApplication.translate
+ self.lookInLabel.setText(_translate("QFileDialog", "Look in:"))
+ self.backButton.setToolTip(_translate("QFileDialog", "Back"))
+ self.backButton.setAccessibleName(_translate("QFileDialog", "Back"))
+ self.backButton.setAccessibleDescription(_translate("QFileDialog", "Go back"))
+ self.backButton.setShortcut(_translate("QFileDialog", "Alt+Left"))
+ self.forwardButton.setToolTip(_translate("QFileDialog", "Forward"))
+ self.forwardButton.setAccessibleName(_translate("QFileDialog", "Forward"))
+ self.forwardButton.setAccessibleDescription(_translate("QFileDialog", "Go forward"))
+ self.forwardButton.setShortcut(_translate("QFileDialog", "Alt+Right"))
+ self.toParentButton.setToolTip(_translate("QFileDialog", "Parent Directory"))
+ self.toParentButton.setAccessibleName(_translate("QFileDialog", "Parent Directory"))
+ self.toParentButton.setAccessibleDescription(_translate("QFileDialog", "Go to the parent directory"))
+ self.toParentButton.setShortcut(_translate("QFileDialog", "Alt+Up"))
+ self.newFolderButton.setToolTip(_translate("QFileDialog", "Create New Folder"))
+ self.newFolderButton.setAccessibleName(_translate("QFileDialog", "Create New Folder"))
+ self.newFolderButton.setAccessibleDescription(_translate("QFileDialog", "Create a New Folder"))
+ self.listModeButton.setToolTip(_translate("QFileDialog", "List View"))
+ self.listModeButton.setAccessibleName(_translate("QFileDialog", "List View"))
+ self.listModeButton.setAccessibleDescription(_translate("QFileDialog", "Change to list view mode"))
+ self.detailModeButton.setToolTip(_translate("QFileDialog", "Detail View"))
+ self.detailModeButton.setAccessibleName(_translate("QFileDialog", "Detail View"))
+ self.detailModeButton.setAccessibleDescription(_translate("QFileDialog", "Change to detail view mode"))
+ self.sidebar.setAccessibleName(_translate("QFileDialog", "Sidebar"))
+ self.sidebar.setAccessibleDescription(_translate("QFileDialog", "List of places and bookmarks"))
+ self.listView.setAccessibleName(_translate("QFileDialog", "Files"))
+ self.treeView.setAccessibleName(_translate("QFileDialog", "Files"))
+ self.fileTypeLabel.setText(_translate("QFileDialog", "Files of type:"))
diff --git a/nmreval/gui_qt/_py/save_fit_parameter.py b/nmreval/gui_qt/_py/save_fit_parameter.py
new file mode 100644
index 0000000..4db6c00
--- /dev/null
+++ b/nmreval/gui_qt/_py/save_fit_parameter.py
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/save_fit_parameter.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_fitparameter_save_dialog(object):
+ def setupUi(self, fitparameter_save_dialog):
+ fitparameter_save_dialog.setObjectName("fitparameter_save_dialog")
+ fitparameter_save_dialog.resize(578, 537)
+ self.gridLayout = QtWidgets.QGridLayout(fitparameter_save_dialog)
+ self.gridLayout.setObjectName("gridLayout")
+ self.save_path_line = QtWidgets.QLineEdit(fitparameter_save_dialog)
+ self.save_path_line.setObjectName("save_path_line")
+ self.gridLayout.addWidget(self.save_path_line, 0, 1, 1, 6)
+ self.save_path_button = QtWidgets.QToolButton(fitparameter_save_dialog)
+ self.save_path_button.setObjectName("save_path_button")
+ self.gridLayout.addWidget(self.save_path_button, 0, 0, 1, 1)
+ self.comment_line = QtWidgets.QLineEdit(fitparameter_save_dialog)
+ self.comment_line.setObjectName("comment_line")
+ self.gridLayout.addWidget(self.comment_line, 6, 6, 1, 1)
+ self.prec_spinbox = QtWidgets.QSpinBox(fitparameter_save_dialog)
+ self.prec_spinbox.setProperty("value", 8)
+ self.prec_spinbox.setObjectName("prec_spinbox")
+ self.gridLayout.addWidget(self.prec_spinbox, 6, 3, 1, 1)
+ self.line_2 = QtWidgets.QFrame(fitparameter_save_dialog)
+ self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.gridLayout.addWidget(self.line_2, 4, 0, 1, 7)
+ self.label_5 = QtWidgets.QLabel(fitparameter_save_dialog)
+ self.label_5.setObjectName("label_5")
+ self.gridLayout.addWidget(self.label_5, 3, 0, 1, 1)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.col_line = QtWidgets.QLineEdit(fitparameter_save_dialog)
+ self.col_line.setObjectName("col_line")
+ self.horizontalLayout.addWidget(self.col_line)
+ self.usage_button = QtWidgets.QToolButton(fitparameter_save_dialog)
+ self.usage_button.setObjectName("usage_button")
+ self.horizontalLayout.addWidget(self.usage_button)
+ self.gridLayout.addLayout(self.horizontalLayout, 3, 1, 1, 6)
+ self.header_edit = QtWidgets.QTextEdit(fitparameter_save_dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.header_edit.sizePolicy().hasHeightForWidth())
+ self.header_edit.setSizePolicy(sizePolicy)
+ self.header_edit.setObjectName("header_edit")
+ self.gridLayout.addWidget(self.header_edit, 5, 1, 1, 6)
+ self.label_4 = QtWidgets.QLabel(fitparameter_save_dialog)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout.addWidget(self.label_4, 6, 2, 1, 1)
+ self.line = QtWidgets.QFrame(fitparameter_save_dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.line.sizePolicy().hasHeightForWidth())
+ self.line.setSizePolicy(sizePolicy)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.gridLayout.addWidget(self.line, 1, 0, 1, 7)
+ self.missing_value_line = QtWidgets.QLineEdit(fitparameter_save_dialog)
+ self.missing_value_line.setObjectName("missing_value_line")
+ self.gridLayout.addWidget(self.missing_value_line, 6, 1, 1, 1)
+ self.label_2 = QtWidgets.QLabel(fitparameter_save_dialog)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 6, 4, 1, 1)
+ self.tableWidget = QtWidgets.QTableWidget(fitparameter_save_dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tableWidget.sizePolicy().hasHeightForWidth())
+ self.tableWidget.setSizePolicy(sizePolicy)
+ self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.tableWidget.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)
+ self.tableWidget.setAlternatingRowColors(True)
+ self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.tableWidget.setColumnCount(2)
+ self.tableWidget.setObjectName("tableWidget")
+ self.tableWidget.setRowCount(0)
+ self.tableWidget.horizontalHeader().setVisible(False)
+ self.tableWidget.horizontalHeader().setStretchLastSection(True)
+ self.gridLayout.addWidget(self.tableWidget, 2, 0, 1, 7)
+ self.buttonBox = QtWidgets.QDialogButtonBox(fitparameter_save_dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 7, 0, 1, 7)
+ self.label = QtWidgets.QLabel(fitparameter_save_dialog)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 6, 0, 1, 1)
+ self.header_checkBox = QtWidgets.QCheckBox(fitparameter_save_dialog)
+ self.header_checkBox.setObjectName("header_checkBox")
+ self.gridLayout.addWidget(self.header_checkBox, 5, 0, 1, 1)
+ self.label_5.setBuddy(self.col_line)
+ self.label_2.setBuddy(self.comment_line)
+ self.label.setBuddy(self.missing_value_line)
+
+ self.retranslateUi(fitparameter_save_dialog)
+ self.buttonBox.accepted.connect(fitparameter_save_dialog.accept)
+ self.buttonBox.rejected.connect(fitparameter_save_dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(fitparameter_save_dialog)
+ fitparameter_save_dialog.setTabOrder(self.save_path_button, self.save_path_line)
+ fitparameter_save_dialog.setTabOrder(self.save_path_line, self.tableWidget)
+ fitparameter_save_dialog.setTabOrder(self.tableWidget, self.header_edit)
+ fitparameter_save_dialog.setTabOrder(self.header_edit, self.missing_value_line)
+ fitparameter_save_dialog.setTabOrder(self.missing_value_line, self.comment_line)
+
+ def retranslateUi(self, fitparameter_save_dialog):
+ _translate = QtCore.QCoreApplication.translate
+ fitparameter_save_dialog.setWindowTitle(_translate("fitparameter_save_dialog", "Save parameter"))
+ self.save_path_button.setText(_translate("fitparameter_save_dialog", "Save path..."))
+ self.comment_line.setText(_translate("fitparameter_save_dialog", "#"))
+ self.label_5.setText(_translate("fitparameter_save_dialog", "Columns"))
+ self.col_line.setPlaceholderText(_translate("fitparameter_save_dialog", "e.g. x; 1000/x; mean(T_1,beta); {beta/beta_1}; M_0*x"))
+ self.usage_button.setText(_translate("fitparameter_save_dialog", "Usage?"))
+ self.header_edit.setPlaceholderText(_translate("fitparameter_save_dialog", "Header ends always with column description. Additional header lines are commented automatically"))
+ self.label_4.setText(_translate("fitparameter_save_dialog", "Precision"))
+ self.missing_value_line.setText(_translate("fitparameter_save_dialog", "1e308"))
+ self.label_2.setText(_translate("fitparameter_save_dialog", "Comment"))
+ self.buttonBox.setToolTip(_translate("fitparameter_save_dialog", "Number of significant digits."))
+ self.label.setText(_translate("fitparameter_save_dialog", "Missing values"))
+ self.header_checkBox.setText(_translate("fitparameter_save_dialog", "Header"))
diff --git a/nmreval/gui_qt/_py/save_fitmodel_dialog.py b/nmreval/gui_qt/_py/save_fitmodel_dialog.py
new file mode 100644
index 0000000..25f058a
--- /dev/null
+++ b/nmreval/gui_qt/_py/save_fitmodel_dialog.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/save_fitmodel_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_SaveDialog(object):
+ def setupUi(self, SaveDialog):
+ SaveDialog.setObjectName("SaveDialog")
+ SaveDialog.resize(400, 166)
+ self.gridLayout = QtWidgets.QGridLayout(SaveDialog)
+ self.gridLayout.setObjectName("gridLayout")
+ self.label_2 = QtWidgets.QLabel(SaveDialog)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(SaveDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 3, 1, 1, 1)
+ self.comboBox = QtWidgets.QComboBox(SaveDialog)
+ self.comboBox.setObjectName("comboBox")
+ self.gridLayout.addWidget(self.comboBox, 1, 1, 1, 1)
+ self.label = QtWidgets.QLabel(SaveDialog)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
+ self.lineEdit = QtWidgets.QLineEdit(SaveDialog)
+ self.lineEdit.setObjectName("lineEdit")
+ self.gridLayout.addWidget(self.lineEdit, 0, 1, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout.addItem(spacerItem, 4, 1, 1, 1)
+ self.frame = QtWidgets.QFrame(SaveDialog)
+ self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.frame.setObjectName("frame")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setSpacing(0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.lineEdit_2 = QtWidgets.QLineEdit(self.frame)
+ self.lineEdit_2.setObjectName("lineEdit_2")
+ self.horizontalLayout.addWidget(self.lineEdit_2)
+ self.toolButton = QtWidgets.QToolButton(self.frame)
+ self.toolButton.setObjectName("toolButton")
+ self.horizontalLayout.addWidget(self.toolButton)
+ self.gridLayout.addWidget(self.frame, 2, 1, 1, 1)
+
+ self.retranslateUi(SaveDialog)
+ self.buttonBox.accepted.connect(SaveDialog.accept)
+ self.buttonBox.rejected.connect(SaveDialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(SaveDialog)
+
+ def retranslateUi(self, SaveDialog):
+ _translate = QtCore.QCoreApplication.translate
+ SaveDialog.setWindowTitle(_translate("SaveDialog", "Dialog"))
+ self.label_2.setText(_translate("SaveDialog", "Group"))
+ self.label.setText(_translate("SaveDialog", "Name"))
+ self.toolButton.setText(_translate("SaveDialog", "OK"))
diff --git a/nmreval/gui_qt/_py/save_options.py b/nmreval/gui_qt/_py/save_options.py
new file mode 100644
index 0000000..602a2c8
--- /dev/null
+++ b/nmreval/gui_qt/_py/save_options.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/save_options.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(400, 58)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Form)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.label = QtWidgets.QLabel(Form)
+ self.label.setObjectName("label")
+ self.verticalLayout.addWidget(self.label)
+ self.checkBox = QtWidgets.QCheckBox(Form)
+ self.checkBox.setObjectName("checkBox")
+ self.verticalLayout.addWidget(self.checkBox)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.label.setText(_translate("Form", "Use <label> as placeholder in filename for data label.
"))
+ self.checkBox.setText(_translate("Form", "Replace spaces with underscore"))
diff --git a/nmreval/gui_qt/_py/saveoptions.py b/nmreval/gui_qt/_py/saveoptions.py
new file mode 100644
index 0000000..c210a7e
--- /dev/null
+++ b/nmreval/gui_qt/_py/saveoptions.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/saveoptions.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Frame(object):
+ def setupUi(self, Frame):
+ Frame.setObjectName("Frame")
+ Frame.resize(464, 62)
+ Frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
+ Frame.setFrameShadow(QtWidgets.QFrame.Raised)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Frame)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.label = QtWidgets.QLabel(Frame)
+ self.label.setObjectName("label")
+ self.verticalLayout.addWidget(self.label)
+ self.line = QtWidgets.QFrame(Frame)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.verticalLayout.addWidget(self.line)
+ self.checkBox = QtWidgets.QCheckBox(Frame)
+ self.checkBox.setChecked(True)
+ self.checkBox.setObjectName("checkBox")
+ self.verticalLayout.addWidget(self.checkBox)
+
+ self.retranslateUi(Frame)
+ QtCore.QMetaObject.connectSlotsByName(Frame)
+
+ def retranslateUi(self, Frame):
+ _translate = QtCore.QCoreApplication.translate
+ Frame.setWindowTitle(_translate("Frame", "Frame"))
+ self.label.setText(_translate("Frame", "Use <label> as placeholder in filename. (e.g. t1_<label>.dat )
"))
+ self.checkBox.setText(_translate("Frame", "Replace spaces with underscore"))
diff --git a/nmreval/gui_qt/_py/sdmodelwidget.py b/nmreval/gui_qt/_py/sdmodelwidget.py
new file mode 100644
index 0000000..4f3a9a0
--- /dev/null
+++ b/nmreval/gui_qt/_py/sdmodelwidget.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/sdmodelwidget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_SDParameter(object):
+ def setupUi(self, SDParameter):
+ SDParameter.setObjectName("SDParameter")
+ SDParameter.setWindowModality(QtCore.Qt.WindowModal)
+ SDParameter.resize(290, 37)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(SDParameter.sizePolicy().hasHeightForWidth())
+ SDParameter.setSizePolicy(sizePolicy)
+ SDParameter.setAutoFillBackground(True)
+ self.verticalLayout = QtWidgets.QVBoxLayout(SDParameter)
+ self.verticalLayout.setContentsMargins(1, 0, 0, 0)
+ self.verticalLayout.setSpacing(0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.gridLayout = QtWidgets.QGridLayout()
+ self.gridLayout.setContentsMargins(6, -1, 0, -1)
+ self.gridLayout.setSpacing(0)
+ self.gridLayout.setObjectName("gridLayout")
+ self.parameter_line = LineEdit(SDParameter)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth())
+ self.parameter_line.setSizePolicy(sizePolicy)
+ self.parameter_line.setObjectName("parameter_line")
+ self.gridLayout.addWidget(self.parameter_line, 0, 2, 1, 1)
+ self.parametername = QtWidgets.QLabel(SDParameter)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(1)
+ sizePolicy.setVerticalStretch(10)
+ sizePolicy.setHeightForWidth(self.parametername.sizePolicy().hasHeightForWidth())
+ self.parametername.setSizePolicy(sizePolicy)
+ self.parametername.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.parametername.setObjectName("parametername")
+ self.gridLayout.addWidget(self.parametername, 0, 0, 1, 1)
+ self.checkBox = QtWidgets.QCheckBox(SDParameter)
+ self.checkBox.setObjectName("checkBox")
+ self.gridLayout.addWidget(self.checkBox, 0, 3, 1, 1)
+ self.line_2 = QtWidgets.QFrame(SDParameter)
+ self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.gridLayout.addWidget(self.line_2, 1, 0, 1, 4)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout.addItem(spacerItem, 0, 1, 1, 1)
+ self.verticalLayout.addLayout(self.gridLayout)
+
+ self.retranslateUi(SDParameter)
+ QtCore.QMetaObject.connectSlotsByName(SDParameter)
+
+ def retranslateUi(self, SDParameter):
+ _translate = QtCore.QCoreApplication.translate
+ SDParameter.setWindowTitle(_translate("SDParameter", "Form"))
+ self.parameter_line.setText(_translate("SDParameter", "1"))
+ self.parametername.setText(_translate("SDParameter", "Fitparameter"))
+ self.checkBox.setText(_translate("SDParameter", "Fix?"))
+from nmrevalgui.lib.forms import LineEdit
diff --git a/nmreval/gui_qt/_py/selection_widget.py b/nmreval/gui_qt/_py/selection_widget.py
new file mode 100644
index 0000000..45614d4
--- /dev/null
+++ b/nmreval/gui_qt/_py/selection_widget.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/selection_widget.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_SelectionWidget(object):
+ def setupUi(self, SelectionWidget):
+ SelectionWidget.setObjectName("SelectionWidget")
+ SelectionWidget.resize(367, 43)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(SelectionWidget.sizePolicy().hasHeightForWidth())
+ SelectionWidget.setSizePolicy(sizePolicy)
+ self.horizontalLayout = QtWidgets.QHBoxLayout(SelectionWidget)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setSpacing(0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(SelectionWidget)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.comboBox = QtWidgets.QComboBox(SelectionWidget)
+ self.comboBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
+ self.comboBox.setFrame(True)
+ self.comboBox.setObjectName("comboBox")
+ self.horizontalLayout.addWidget(self.comboBox)
+
+ self.retranslateUi(SelectionWidget)
+ QtCore.QMetaObject.connectSlotsByName(SelectionWidget)
+
+ def retranslateUi(self, SelectionWidget):
+ _translate = QtCore.QCoreApplication.translate
+ SelectionWidget.setWindowTitle(_translate("SelectionWidget", "Form"))
+ self.label.setText(_translate("SelectionWidget", "TextLabel"))
diff --git a/nmreval/gui_qt/_py/setbyfunction_dialog.py b/nmreval/gui_qt/_py/setbyfunction_dialog.py
new file mode 100644
index 0000000..ecd065d
--- /dev/null
+++ b/nmreval/gui_qt/_py/setbyfunction_dialog.py
@@ -0,0 +1,225 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/setbyfunction_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_NewCurveDialog(object):
+ def setupUi(self, NewCurveDialog):
+ NewCurveDialog.setObjectName("NewCurveDialog")
+ NewCurveDialog.resize(648, 578)
+ self.gridLayout_4 = QtWidgets.QGridLayout(NewCurveDialog)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ self.groupBox_2 = QtWidgets.QGroupBox(NewCurveDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ 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.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox_2)
+ self.gridLayout_2.setContentsMargins(2, 2, 2, 2)
+ self.gridLayout_2.setSpacing(2)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.lineEdit_3 = QtWidgets.QLineEdit(self.groupBox_2)
+ self.lineEdit_3.setPlaceholderText("")
+ self.lineEdit_3.setObjectName("lineEdit_3")
+ self.gridLayout_2.addWidget(self.lineEdit_3, 0, 1, 1, 1)
+ self.lineEdit_5 = QtWidgets.QLineEdit(self.groupBox_2)
+ self.lineEdit_5.setObjectName("lineEdit_5")
+ self.gridLayout_2.addWidget(self.lineEdit_5, 0, 6, 1, 1)
+ self.label_5 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_5.setObjectName("label_5")
+ self.gridLayout_2.addWidget(self.label_5, 0, 3, 1, 1)
+ self.lineEdit_4 = QtWidgets.QLineEdit(self.groupBox_2)
+ self.lineEdit_4.setObjectName("lineEdit_4")
+ self.gridLayout_2.addWidget(self.lineEdit_4, 0, 4, 1, 1)
+ self.label_4 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout_2.addWidget(self.label_4, 0, 0, 1, 1)
+ self.label_6 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_6.setObjectName("label_6")
+ self.gridLayout_2.addWidget(self.label_6, 0, 5, 1, 1)
+ self.checkBox = QtWidgets.QCheckBox(self.groupBox_2)
+ self.checkBox.setLayoutDirection(QtCore.Qt.RightToLeft)
+ self.checkBox.setObjectName("checkBox")
+ self.gridLayout_2.addWidget(self.checkBox, 0, 7, 1, 1)
+ self.gridLayout_4.addWidget(self.groupBox_2, 0, 0, 1, 1)
+ self.groupBox = QtWidgets.QGroupBox(NewCurveDialog)
+ self.groupBox.setObjectName("groupBox")
+ self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox)
+ self.gridLayout_3.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_3.setSpacing(2)
+ self.gridLayout_3.setObjectName("gridLayout_3")
+ self.lineEdit_2 = QtWidgets.QLineEdit(self.groupBox)
+ self.lineEdit_2.setObjectName("lineEdit_2")
+ self.gridLayout_3.addWidget(self.lineEdit_2, 1, 2, 1, 1)
+ self.comboBox_6 = QtWidgets.QComboBox(self.groupBox)
+ self.comboBox_6.setObjectName("comboBox_6")
+ self.gridLayout_3.addWidget(self.comboBox_6, 2, 0, 1, 1)
+ self.pushButton = QtWidgets.QPushButton(self.groupBox)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.pushButton.sizePolicy().hasHeightForWidth())
+ self.pushButton.setSizePolicy(sizePolicy)
+ self.pushButton.setObjectName("pushButton")
+ self.gridLayout_3.addWidget(self.pushButton, 4, 0, 1, 1)
+ self.verticalLayout = QtWidgets.QVBoxLayout()
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.gridLayout_3.addLayout(self.verticalLayout, 3, 0, 1, 1)
+ self.comboBox_7 = QtWidgets.QComboBox(self.groupBox)
+ self.comboBox_7.setObjectName("comboBox_7")
+ self.gridLayout_3.addWidget(self.comboBox_7, 2, 2, 1, 1)
+ self.lineEdit = QtWidgets.QLineEdit(self.groupBox)
+ self.lineEdit.setObjectName("lineEdit")
+ self.gridLayout_3.addWidget(self.lineEdit, 1, 0, 1, 1)
+ self.label = QtWidgets.QLabel(self.groupBox)
+ self.label.setObjectName("label")
+ self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1)
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.gridLayout_3.addLayout(self.verticalLayout_2, 3, 2, 1, 1)
+ self.label_2 = QtWidgets.QLabel(self.groupBox)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
+ self.label_2.setSizePolicy(sizePolicy)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout_3.addWidget(self.label_2, 0, 2, 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_3.addWidget(self.line_2, 0, 1, 4, 1)
+ self.gridLayout_4.addWidget(self.groupBox, 1, 0, 1, 1)
+ self.groupBox_3 = QtWidgets.QGroupBox(NewCurveDialog)
+ self.groupBox_3.setObjectName("groupBox_3")
+ self.gridLayout = QtWidgets.QGridLayout(self.groupBox_3)
+ self.gridLayout.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout.setSpacing(2)
+ self.gridLayout.setObjectName("gridLayout")
+ self.doubleSpinBox = QtWidgets.QDoubleSpinBox(self.groupBox_3)
+ self.doubleSpinBox.setDecimals(1)
+ self.doubleSpinBox.setMaximum(20.0)
+ self.doubleSpinBox.setSingleStep(0.5)
+ self.doubleSpinBox.setProperty("value", 1.0)
+ self.doubleSpinBox.setObjectName("doubleSpinBox")
+ self.gridLayout.addWidget(self.doubleSpinBox, 3, 4, 1, 1)
+ self.line = QtWidgets.QFrame(self.groupBox_3)
+ self.line.setFrameShape(QtWidgets.QFrame.VLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.gridLayout.addWidget(self.line, 0, 2, 4, 1)
+ self.spinBox = QtWidgets.QSpinBox(self.groupBox_3)
+ self.spinBox.setMaximum(100)
+ self.spinBox.setProperty("value", 10)
+ self.spinBox.setObjectName("spinBox")
+ self.gridLayout.addWidget(self.spinBox, 3, 1, 1, 1)
+ self.label_8 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_8.setObjectName("label_8")
+ self.gridLayout.addWidget(self.label_8, 0, 4, 1, 1)
+ self.comboBox = SymbolStyleEditor(self.groupBox_3)
+ self.comboBox.setObjectName("comboBox")
+ self.gridLayout.addWidget(self.comboBox, 1, 1, 1, 1)
+ self.comboBox_2 = LineStyleEditor(self.groupBox_3)
+ self.comboBox_2.setObjectName("comboBox_2")
+ self.gridLayout.addWidget(self.comboBox_2, 1, 4, 1, 1)
+ self.label_7 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_7.setObjectName("label_7")
+ self.gridLayout.addWidget(self.label_7, 0, 1, 1, 1)
+ self.comboBox_4 = ColorListEditor(self.groupBox_3)
+ self.comboBox_4.setObjectName("comboBox_4")
+ self.gridLayout.addWidget(self.comboBox_4, 2, 4, 1, 1)
+ self.comboBox_5 = ColorListEditor(self.groupBox_3)
+ self.comboBox_5.setObjectName("comboBox_5")
+ self.gridLayout.addWidget(self.comboBox_5, 2, 1, 1, 1)
+ self.gridLayout_4.addWidget(self.groupBox_3, 2, 0, 1, 1)
+ self.groupBox_21 = QtWidgets.QGroupBox(NewCurveDialog)
+ self.groupBox_21.setObjectName("groupBox_21")
+ self.gridLayout_5 = QtWidgets.QGridLayout(self.groupBox_21)
+ self.gridLayout_5.setObjectName("gridLayout_5")
+ self.label_3 = QtWidgets.QLabel(self.groupBox_21)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout_5.addWidget(self.label_3, 0, 0, 1, 1)
+ self.lineEdit_6 = QtWidgets.QLineEdit(self.groupBox_21)
+ self.lineEdit_6.setObjectName("lineEdit_6")
+ self.gridLayout_5.addWidget(self.lineEdit_6, 0, 1, 1, 1)
+ self.label_10 = QtWidgets.QLabel(self.groupBox_21)
+ self.label_10.setObjectName("label_10")
+ self.gridLayout_5.addWidget(self.label_10, 0, 2, 1, 1)
+ self.comboBox_3 = QtWidgets.QComboBox(self.groupBox_21)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.comboBox_3.sizePolicy().hasHeightForWidth())
+ self.comboBox_3.setSizePolicy(sizePolicy)
+ self.comboBox_3.setObjectName("comboBox_3")
+ self.gridLayout_5.addWidget(self.comboBox_3, 0, 3, 1, 1)
+ self.gridLayout_4.addWidget(self.groupBox_21, 3, 0, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(NewCurveDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout_4.addWidget(self.buttonBox, 5, 0, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout_4.addItem(spacerItem, 4, 0, 1, 1)
+ self.label_5.setBuddy(self.lineEdit_4)
+ self.label_4.setBuddy(self.lineEdit_3)
+ self.label_6.setBuddy(self.lineEdit_5)
+ self.label.setBuddy(self.lineEdit)
+ self.label_2.setBuddy(self.lineEdit_2)
+ self.label_8.setBuddy(self.comboBox_2)
+ self.label_7.setBuddy(self.comboBox)
+ self.label_3.setBuddy(self.lineEdit_6)
+ self.label_10.setBuddy(self.comboBox_3)
+
+ self.retranslateUi(NewCurveDialog)
+ self.comboBox.setCurrentIndex(-1)
+ self.comboBox_2.setCurrentIndex(-1)
+ self.buttonBox.accepted.connect(NewCurveDialog.accept)
+ self.buttonBox.rejected.connect(NewCurveDialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(NewCurveDialog)
+ NewCurveDialog.setTabOrder(self.lineEdit_3, self.lineEdit_4)
+ NewCurveDialog.setTabOrder(self.lineEdit_4, self.lineEdit_5)
+ NewCurveDialog.setTabOrder(self.lineEdit_5, self.checkBox)
+ NewCurveDialog.setTabOrder(self.checkBox, self.lineEdit)
+ NewCurveDialog.setTabOrder(self.lineEdit, self.comboBox)
+ NewCurveDialog.setTabOrder(self.comboBox, self.spinBox)
+ NewCurveDialog.setTabOrder(self.spinBox, self.comboBox_2)
+ NewCurveDialog.setTabOrder(self.comboBox_2, self.doubleSpinBox)
+ NewCurveDialog.setTabOrder(self.doubleSpinBox, self.lineEdit_6)
+ NewCurveDialog.setTabOrder(self.lineEdit_6, self.comboBox_3)
+ NewCurveDialog.setTabOrder(self.comboBox_3, self.buttonBox)
+
+ def retranslateUi(self, NewCurveDialog):
+ _translate = QtCore.QCoreApplication.translate
+ NewCurveDialog.setWindowTitle(_translate("NewCurveDialog", "Create new data by function"))
+ self.groupBox_2.setTitle(_translate("NewCurveDialog", "Control variable i"))
+ self.lineEdit_3.setText(_translate("NewCurveDialog", "0"))
+ self.lineEdit_5.setText(_translate("NewCurveDialog", "10"))
+ self.label_5.setText(_translate("NewCurveDialog", "Stop at"))
+ self.lineEdit_4.setText(_translate("NewCurveDialog", "1"))
+ self.label_4.setText(_translate("NewCurveDialog", "Start at"))
+ self.label_6.setText(_translate("NewCurveDialog", "# points"))
+ self.checkBox.setText(_translate("NewCurveDialog", "Logarithmic?"))
+ self.groupBox.setTitle(_translate("NewCurveDialog", "Expressions"))
+ self.lineEdit_2.setText(_translate("NewCurveDialog", "x**2"))
+ self.pushButton.setText(_translate("NewCurveDialog", "Check"))
+ self.lineEdit.setText(_translate("NewCurveDialog", "i"))
+ self.label.setText(_translate("NewCurveDialog", " x = "))
+ self.label_2.setText(_translate("NewCurveDialog", " y = "))
+ self.groupBox_3.setTitle(_translate("NewCurveDialog", "Look"))
+ self.label_8.setText(_translate("NewCurveDialog", "Line"))
+ self.label_7.setText(_translate("NewCurveDialog", "Symbol"))
+ self.groupBox_21.setTitle(_translate("NewCurveDialog", "Designation"))
+ self.label_3.setText(_translate("NewCurveDialog", "Name"))
+ self.label_10.setText(_translate("NewCurveDialog", "Graph"))
+from ..lib.delegates import ColorListEditor, LineStyleEditor, SymbolStyleEditor
diff --git a/nmreval/gui_qt/_py/shift_scale_dialog.py b/nmreval/gui_qt/_py/shift_scale_dialog.py
new file mode 100644
index 0000000..b8605e5
--- /dev/null
+++ b/nmreval/gui_qt/_py/shift_scale_dialog.py
@@ -0,0 +1,314 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/shift_scale_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_shift_dialog(object):
+ def setupUi(self, shift_dialog):
+ shift_dialog.setObjectName("shift_dialog")
+ shift_dialog.resize(959, 639)
+ self.verticalLayout = QtWidgets.QVBoxLayout(shift_dialog)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.splitter = QtWidgets.QSplitter(shift_dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.splitter.sizePolicy().hasHeightForWidth())
+ self.splitter.setSizePolicy(sizePolicy)
+ self.splitter.setOrientation(QtCore.Qt.Horizontal)
+ self.splitter.setObjectName("splitter")
+ self.tabWidget = QtWidgets.QTabWidget(self.splitter)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tabWidget.sizePolicy().hasHeightForWidth())
+ self.tabWidget.setSizePolicy(sizePolicy)
+ self.tabWidget.setObjectName("tabWidget")
+ self.shift_page = QtWidgets.QWidget()
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.shift_page.sizePolicy().hasHeightForWidth())
+ self.shift_page.setSizePolicy(sizePolicy)
+ self.shift_page.setObjectName("shift_page")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.shift_page)
+ self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.label_7 = QtWidgets.QLabel(self.shift_page)
+ self.label_7.setObjectName("label_7")
+ self.gridLayout_2.addWidget(self.label_7, 2, 0, 1, 1)
+ self.label = QtWidgets.QLabel(self.shift_page)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
+ self.label.setSizePolicy(sizePolicy)
+ self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label.setObjectName("label")
+ self.gridLayout_2.addWidget(self.label, 2, 1, 1, 1)
+ self.x_shift_spinbox = SciSpinBox(self.shift_page)
+ self.x_shift_spinbox.setMinimumSize(QtCore.QSize(150, 0))
+ self.x_shift_spinbox.setDecimals(3)
+ self.x_shift_spinbox.setProperty("value", 0.0)
+ self.x_shift_spinbox.setObjectName("x_shift_spinbox")
+ self.gridLayout_2.addWidget(self.x_shift_spinbox, 2, 2, 1, 1)
+ self.label_5 = QtWidgets.QLabel(self.shift_page)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth())
+ self.label_5.setSizePolicy(sizePolicy)
+ self.label_5.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label_5.setObjectName("label_5")
+ self.gridLayout_2.addWidget(self.label_5, 3, 1, 1, 1)
+ self.y_shift_spinbox = SciSpinBox(self.shift_page)
+ self.y_shift_spinbox.setMinimumSize(QtCore.QSize(150, 0))
+ self.y_shift_spinbox.setDecimals(3)
+ self.y_shift_spinbox.setProperty("value", 0.0)
+ self.y_shift_spinbox.setObjectName("y_shift_spinbox")
+ self.gridLayout_2.addWidget(self.y_shift_spinbox, 3, 2, 1, 1)
+ self.shift_table = QtWidgets.QTableWidget(self.shift_page)
+ self.shift_table.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
+ self.shift_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.shift_table.setColumnCount(3)
+ self.shift_table.setObjectName("shift_table")
+ self.shift_table.setRowCount(0)
+ item = QtWidgets.QTableWidgetItem()
+ self.shift_table.setHorizontalHeaderItem(0, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.shift_table.setHorizontalHeaderItem(1, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.shift_table.setHorizontalHeaderItem(2, item)
+ self.shift_table.verticalHeader().setVisible(False)
+ self.gridLayout_2.addWidget(self.shift_table, 0, 0, 1, 3)
+ self.tabWidget.addTab(self.shift_page, "")
+ self.scale_page = QtWidgets.QWidget()
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.scale_page.sizePolicy().hasHeightForWidth())
+ self.scale_page.setSizePolicy(sizePolicy)
+ self.scale_page.setObjectName("scale_page")
+ self.gridLayout = QtWidgets.QGridLayout(self.scale_page)
+ self.gridLayout.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout.setObjectName("gridLayout")
+ self.label_3 = QtWidgets.QLabel(self.scale_page)
+ 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.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout.addWidget(self.label_3, 1, 1, 1, 1)
+ self.x_scale_spinbox = SciSpinBox(self.scale_page)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.x_scale_spinbox.sizePolicy().hasHeightForWidth())
+ self.x_scale_spinbox.setSizePolicy(sizePolicy)
+ self.x_scale_spinbox.setMinimumSize(QtCore.QSize(150, 0))
+ self.x_scale_spinbox.setDecimals(3)
+ self.x_scale_spinbox.setProperty("value", 1.0)
+ self.x_scale_spinbox.setObjectName("x_scale_spinbox")
+ self.gridLayout.addWidget(self.x_scale_spinbox, 1, 2, 1, 1)
+ self.label_6 = QtWidgets.QLabel(self.scale_page)
+ self.label_6.setObjectName("label_6")
+ self.gridLayout.addWidget(self.label_6, 1, 0, 1, 1)
+ self.label_2 = QtWidgets.QLabel(self.scale_page)
+ 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.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 2, 1, 1, 1)
+ self.y_scale_spinbox = SciSpinBox(self.scale_page)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.y_scale_spinbox.sizePolicy().hasHeightForWidth())
+ self.y_scale_spinbox.setSizePolicy(sizePolicy)
+ self.y_scale_spinbox.setMinimumSize(QtCore.QSize(150, 0))
+ self.y_scale_spinbox.setDecimals(3)
+ self.y_scale_spinbox.setProperty("value", 1.0)
+ self.y_scale_spinbox.setObjectName("y_scale_spinbox")
+ self.gridLayout.addWidget(self.y_scale_spinbox, 2, 2, 1, 1)
+ self.scale_table = QtWidgets.QTableWidget(self.scale_page)
+ self.scale_table.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
+ self.scale_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.scale_table.setObjectName("scale_table")
+ self.scale_table.setColumnCount(3)
+ self.scale_table.setRowCount(0)
+ item = QtWidgets.QTableWidgetItem()
+ self.scale_table.setHorizontalHeaderItem(0, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.scale_table.setHorizontalHeaderItem(1, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.scale_table.setHorizontalHeaderItem(2, item)
+ self.scale_table.verticalHeader().setVisible(False)
+ self.gridLayout.addWidget(self.scale_table, 0, 0, 1, 3)
+ self.tabWidget.addTab(self.scale_page, "")
+ self.verticalFrame_2 = QtWidgets.QFrame(self.splitter)
+ self.verticalFrame_2.setObjectName("verticalFrame_2")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalFrame_2)
+ self.verticalLayout_2.setSpacing(3)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.graphicsView = PlotWidget(self.verticalFrame_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
+ self.graphicsView.setSizePolicy(sizePolicy)
+ self.graphicsView.setObjectName("graphicsView")
+ self.verticalLayout_2.addWidget(self.graphicsView)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.label_4 = QtWidgets.QLabel(self.verticalFrame_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
+ self.label_4.setSizePolicy(sizePolicy)
+ self.label_4.setObjectName("label_4")
+ self.horizontalLayout.addWidget(self.label_4)
+ self.xlog_checkbox = QtWidgets.QCheckBox(self.verticalFrame_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.xlog_checkbox.sizePolicy().hasHeightForWidth())
+ self.xlog_checkbox.setSizePolicy(sizePolicy)
+ self.xlog_checkbox.setObjectName("xlog_checkbox")
+ self.horizontalLayout.addWidget(self.xlog_checkbox)
+ self.ylog_checkbox = QtWidgets.QCheckBox(self.verticalFrame_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.ylog_checkbox.sizePolicy().hasHeightForWidth())
+ self.ylog_checkbox.setSizePolicy(sizePolicy)
+ self.ylog_checkbox.setObjectName("ylog_checkbox")
+ self.horizontalLayout.addWidget(self.ylog_checkbox)
+ self.verticalLayout_2.addLayout(self.horizontalLayout)
+ self.verticalLayout.addWidget(self.splitter)
+ self.line = QtWidgets.QFrame(shift_dialog)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.verticalLayout.addWidget(self.line)
+ self.frame = QtWidgets.QFrame(shift_dialog)
+ self.frame.setObjectName("frame")
+ self.gridLayout_4 = QtWidgets.QGridLayout(self.frame)
+ self.gridLayout_4.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_4.setSpacing(3)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ self.value_checkbox = QtWidgets.QCheckBox(self.frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.value_checkbox.sizePolicy().hasHeightForWidth())
+ self.value_checkbox.setSizePolicy(sizePolicy)
+ self.value_checkbox.setChecked(True)
+ self.value_checkbox.setObjectName("value_checkbox")
+ self.gridLayout_4.addWidget(self.value_checkbox, 0, 1, 1, 1)
+ self.overwrite_checkbox = QtWidgets.QCheckBox(self.frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.overwrite_checkbox.sizePolicy().hasHeightForWidth())
+ self.overwrite_checkbox.setSizePolicy(sizePolicy)
+ self.overwrite_checkbox.setObjectName("overwrite_checkbox")
+ self.gridLayout_4.addWidget(self.overwrite_checkbox, 0, 0, 1, 1)
+ self.data_newgraph = QtWidgets.QCheckBox(self.frame)
+ self.data_newgraph.setChecked(True)
+ self.data_newgraph.setObjectName("data_newgraph")
+ self.gridLayout_4.addWidget(self.data_newgraph, 1, 0, 1, 1)
+ self.data_combobox = QtWidgets.QComboBox(self.frame)
+ self.data_combobox.setEnabled(False)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.data_combobox.sizePolicy().hasHeightForWidth())
+ self.data_combobox.setSizePolicy(sizePolicy)
+ self.data_combobox.setObjectName("data_combobox")
+ self.gridLayout_4.addWidget(self.data_combobox, 2, 0, 1, 1)
+ self.values_newgraph = QtWidgets.QCheckBox(self.frame)
+ self.values_newgraph.setChecked(True)
+ self.values_newgraph.setObjectName("values_newgraph")
+ self.gridLayout_4.addWidget(self.values_newgraph, 1, 1, 1, 1)
+ self.values_combobox = QtWidgets.QComboBox(self.frame)
+ self.values_combobox.setEnabled(False)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.values_combobox.sizePolicy().hasHeightForWidth())
+ self.values_combobox.setSizePolicy(sizePolicy)
+ self.values_combobox.setObjectName("values_combobox")
+ self.gridLayout_4.addWidget(self.values_combobox, 2, 1, 1, 1)
+ self.verticalLayout.addWidget(self.frame)
+ self.buttonBox = QtWidgets.QDialogButtonBox(shift_dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout.addWidget(self.buttonBox)
+ self.label.setBuddy(self.x_shift_spinbox)
+ self.label_5.setBuddy(self.y_shift_spinbox)
+ self.label_3.setBuddy(self.x_scale_spinbox)
+ self.label_2.setBuddy(self.y_scale_spinbox)
+
+ self.retranslateUi(shift_dialog)
+ self.tabWidget.setCurrentIndex(0)
+ self.buttonBox.accepted.connect(shift_dialog.accept)
+ self.buttonBox.rejected.connect(shift_dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(shift_dialog)
+ shift_dialog.setTabOrder(self.tabWidget, self.shift_table)
+ shift_dialog.setTabOrder(self.shift_table, self.x_shift_spinbox)
+ shift_dialog.setTabOrder(self.x_shift_spinbox, self.y_shift_spinbox)
+ shift_dialog.setTabOrder(self.y_shift_spinbox, self.scale_table)
+ shift_dialog.setTabOrder(self.scale_table, self.x_scale_spinbox)
+ shift_dialog.setTabOrder(self.x_scale_spinbox, self.y_scale_spinbox)
+ shift_dialog.setTabOrder(self.y_scale_spinbox, self.xlog_checkbox)
+ shift_dialog.setTabOrder(self.xlog_checkbox, self.ylog_checkbox)
+ shift_dialog.setTabOrder(self.ylog_checkbox, self.graphicsView)
+
+ def retranslateUi(self, shift_dialog):
+ _translate = QtCore.QCoreApplication.translate
+ shift_dialog.setWindowTitle(_translate("shift_dialog", "Shift + Scale"))
+ self.label_7.setText(_translate("shift_dialog", "Global"))
+ self.label.setText(_translate("shift_dialog", "x
"))
+ self.label_5.setText(_translate("shift_dialog", "y
"))
+ item = self.shift_table.horizontalHeaderItem(0)
+ item.setText(_translate("shift_dialog", "Data"))
+ item = self.shift_table.horizontalHeaderItem(1)
+ item.setText(_translate("shift_dialog", "x"))
+ item = self.shift_table.horizontalHeaderItem(2)
+ item.setText(_translate("shift_dialog", "y"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.shift_page), _translate("shift_dialog", "Shift"))
+ self.label_3.setText(_translate("shift_dialog", "x
"))
+ self.label_6.setText(_translate("shift_dialog", "Global:"))
+ self.label_2.setText(_translate("shift_dialog", "y
"))
+ item = self.scale_table.horizontalHeaderItem(0)
+ item.setText(_translate("shift_dialog", "Data"))
+ item = self.scale_table.horizontalHeaderItem(1)
+ item.setText(_translate("shift_dialog", "x"))
+ item = self.scale_table.horizontalHeaderItem(2)
+ item.setText(_translate("shift_dialog", "y"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.scale_page), _translate("shift_dialog", "Scale"))
+ self.label_4.setText(_translate("shift_dialog", "Axes:"))
+ self.xlog_checkbox.setText(_translate("shift_dialog", "log x"))
+ self.ylog_checkbox.setText(_translate("shift_dialog", "log y"))
+ self.value_checkbox.setText(_translate("shift_dialog", "Export shift/scale values"))
+ self.overwrite_checkbox.setText(_translate("shift_dialog", "Overwrite data"))
+ self.data_newgraph.setText(_translate("shift_dialog", "New graph"))
+ self.values_newgraph.setText(_translate("shift_dialog", "New graph"))
+from ..lib.utils import SciSpinBox
+from pyqtgraph import PlotWidget
diff --git a/nmreval/gui_qt/_py/skipdialog.py b/nmreval/gui_qt/_py/skipdialog.py
new file mode 100644
index 0000000..f541dc2
--- /dev/null
+++ b/nmreval/gui_qt/_py/skipdialog.py
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/skipdialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_SkipDialog(object):
+ def setupUi(self, SkipDialog):
+ SkipDialog.setObjectName("SkipDialog")
+ SkipDialog.resize(448, 208)
+ SkipDialog.setWindowTitle("")
+ self.gridLayout = QtWidgets.QGridLayout(SkipDialog)
+ self.gridLayout.setObjectName("gridLayout")
+ self.label_2 = QtWidgets.QLabel(SkipDialog)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout.addItem(spacerItem, 3, 0, 1, 1)
+ self.label_4 = QtWidgets.QLabel(SkipDialog)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout.addWidget(self.label_4, 0, 0, 1, 4)
+ self.offset_spinbox = QtWidgets.QSpinBox(SkipDialog)
+ self.offset_spinbox.setMinimum(0)
+ self.offset_spinbox.setMaximum(1)
+ self.offset_spinbox.setProperty("value", 0)
+ self.offset_spinbox.setObjectName("offset_spinbox")
+ self.gridLayout.addWidget(self.offset_spinbox, 1, 3, 1, 1)
+ self.label = QtWidgets.QLabel(SkipDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, 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, 1, 2, 1, 1)
+ self.step_spinbox = QtWidgets.QSpinBox(SkipDialog)
+ self.step_spinbox.setMinimum(2)
+ self.step_spinbox.setObjectName("step_spinbox")
+ self.gridLayout.addWidget(self.step_spinbox, 1, 1, 1, 1)
+ self.invert_check = QtWidgets.QCheckBox(SkipDialog)
+ self.invert_check.setLayoutDirection(QtCore.Qt.LeftToRight)
+ self.invert_check.setObjectName("invert_check")
+ self.gridLayout.addWidget(self.invert_check, 1, 4, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(SkipDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 4, 0, 1, 5)
+ self.hide_button = QtWidgets.QRadioButton(SkipDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.hide_button.sizePolicy().hasHeightForWidth())
+ self.hide_button.setSizePolicy(sizePolicy)
+ self.hide_button.setChecked(True)
+ self.hide_button.setObjectName("hide_button")
+ self.buttonGroup = QtWidgets.QButtonGroup(SkipDialog)
+ self.buttonGroup.setObjectName("buttonGroup")
+ self.buttonGroup.addButton(self.hide_button)
+ self.gridLayout.addWidget(self.hide_button, 2, 0, 1, 2)
+ self.delete_button = QtWidgets.QRadioButton(SkipDialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.delete_button.sizePolicy().hasHeightForWidth())
+ self.delete_button.setSizePolicy(sizePolicy)
+ self.delete_button.setObjectName("delete_button")
+ self.buttonGroup.addButton(self.delete_button)
+ self.gridLayout.addWidget(self.delete_button, 2, 2, 1, 3)
+ self.label_2.setBuddy(self.step_spinbox)
+ self.label.setBuddy(self.offset_spinbox)
+
+ self.retranslateUi(SkipDialog)
+ self.buttonBox.accepted.connect(SkipDialog.accept)
+ self.buttonBox.rejected.connect(SkipDialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(SkipDialog)
+ SkipDialog.setTabOrder(self.step_spinbox, self.offset_spinbox)
+ SkipDialog.setTabOrder(self.offset_spinbox, self.invert_check)
+ SkipDialog.setTabOrder(self.invert_check, self.hide_button)
+ SkipDialog.setTabOrder(self.hide_button, self.delete_button)
+
+ def retranslateUi(self, SkipDialog):
+ _translate = QtCore.QCoreApplication.translate
+ self.label_2.setText(_translate("SkipDialog", "Step"))
+ self.label_4.setText(_translate("SkipDialog", "Show every step point, beginning with offset (<step). Use Invert to hide every step point.
"))
+ self.label.setText(_translate("SkipDialog", "Offset"))
+ self.invert_check.setText(_translate("SkipDialog", "Invert"))
+ self.hide_button.setText(_translate("SkipDialog", "Hide skipped pts."))
+ self.delete_button.setText(_translate("SkipDialog", "Copy without any hidden pts."))
diff --git a/nmreval/gui_qt/_py/smoothdialog.py b/nmreval/gui_qt/_py/smoothdialog.py
new file mode 100644
index 0000000..634fde5
--- /dev/null
+++ b/nmreval/gui_qt/_py/smoothdialog.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/smoothdialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_SmoothDialog(object):
+ def setupUi(self, SmoothDialog):
+ SmoothDialog.setObjectName("SmoothDialog")
+ SmoothDialog.resize(451, 220)
+ self.gridLayout = QtWidgets.QGridLayout(SmoothDialog)
+ self.gridLayout.setSpacing(3)
+ self.gridLayout.setObjectName("gridLayout")
+ self.frac_label = QtWidgets.QLabel(SmoothDialog)
+ self.frac_label.setObjectName("frac_label")
+ self.gridLayout.addWidget(self.frac_label, 1, 0, 1, 1)
+ self.widget_2 = QtWidgets.QWidget(SmoothDialog)
+ self.widget_2.setObjectName("widget_2")
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget_2)
+ self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_3.setSpacing(3)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.label_3 = QtWidgets.QLabel(self.widget_2)
+ self.label_3.setObjectName("label_3")
+ self.horizontalLayout_3.addWidget(self.label_3)
+ self.iter_spinBox = QtWidgets.QSpinBox(self.widget_2)
+ self.iter_spinBox.setMaximum(3)
+ self.iter_spinBox.setSingleStep(1)
+ self.iter_spinBox.setProperty("value", 1)
+ self.iter_spinBox.setObjectName("iter_spinBox")
+ self.horizontalLayout_3.addWidget(self.iter_spinBox)
+ self.gridLayout.addWidget(self.widget_2, 3, 0, 1, 2)
+ self.line = QtWidgets.QFrame(SmoothDialog)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.gridLayout.addWidget(self.line, 4, 0, 1, 2)
+ self.buttonBox = QtWidgets.QDialogButtonBox(SmoothDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 7, 0, 1, 2)
+ self.widget = QtWidgets.QWidget(SmoothDialog)
+ self.widget.setObjectName("widget")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.widget)
+ self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_2.setSpacing(3)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.label = QtWidgets.QLabel(self.widget)
+ self.label.setObjectName("label")
+ self.horizontalLayout_2.addWidget(self.label)
+ self.polynom_spinBox = QtWidgets.QSpinBox(self.widget)
+ self.polynom_spinBox.setMinimum(1)
+ self.polynom_spinBox.setMaximum(3)
+ self.polynom_spinBox.setObjectName("polynom_spinBox")
+ self.horizontalLayout_2.addWidget(self.polynom_spinBox)
+ self.gridLayout.addWidget(self.widget, 2, 0, 1, 2)
+ self.frac_spinBox = QtWidgets.QSpinBox(SmoothDialog)
+ self.frac_spinBox.setMinimum(1)
+ self.frac_spinBox.setMaximum(999)
+ self.frac_spinBox.setObjectName("frac_spinBox")
+ self.gridLayout.addWidget(self.frac_spinBox, 1, 1, 1, 1)
+ self.comboBox = QtWidgets.QComboBox(SmoothDialog)
+ self.comboBox.setObjectName("comboBox")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.gridLayout.addWidget(self.comboBox, 0, 0, 1, 2)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout.addItem(spacerItem, 6, 0, 1, 1)
+ self.y_checkBox = QtWidgets.QCheckBox(SmoothDialog)
+ self.y_checkBox.setObjectName("y_checkBox")
+ self.gridLayout.addWidget(self.y_checkBox, 5, 1, 1, 1)
+ self.x_checkBox = QtWidgets.QCheckBox(SmoothDialog)
+ self.x_checkBox.setObjectName("x_checkBox")
+ self.gridLayout.addWidget(self.x_checkBox, 5, 0, 1, 1)
+ self.frac_label.setBuddy(self.frac_spinBox)
+ self.label_3.setBuddy(self.iter_spinBox)
+ self.label.setBuddy(self.polynom_spinBox)
+
+ self.retranslateUi(SmoothDialog)
+ self.buttonBox.accepted.connect(SmoothDialog.accept)
+ self.buttonBox.rejected.connect(SmoothDialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(SmoothDialog)
+ SmoothDialog.setTabOrder(self.comboBox, self.frac_spinBox)
+ SmoothDialog.setTabOrder(self.frac_spinBox, self.polynom_spinBox)
+ SmoothDialog.setTabOrder(self.polynom_spinBox, self.iter_spinBox)
+ SmoothDialog.setTabOrder(self.iter_spinBox, self.x_checkBox)
+ SmoothDialog.setTabOrder(self.x_checkBox, self.y_checkBox)
+
+ def retranslateUi(self, SmoothDialog):
+ _translate = QtCore.QCoreApplication.translate
+ SmoothDialog.setWindowTitle(_translate("SmoothDialog", "1D smoothing filter"))
+ self.frac_label.setText(_translate("SmoothDialog", "Window length"))
+ self.label_3.setText(_translate("SmoothDialog", "Iterations"))
+ self.label.setText(_translate("SmoothDialog", "Polynomial degree"))
+ self.polynom_spinBox.setToolTip(_translate("SmoothDialog", "Deg"))
+ self.frac_spinBox.setToolTip(_translate("SmoothDialog", "Number of data points used as smoothing window.
"))
+ self.comboBox.setItemText(0, _translate("SmoothDialog", "Moving mean"))
+ self.comboBox.setItemText(1, _translate("SmoothDialog", "Savitzky-Golay"))
+ self.comboBox.setItemText(2, _translate("SmoothDialog", "Loess (slow, use for < 10000 pts)"))
+ self.comboBox.setItemText(3, _translate("SmoothDialog", "Moving median"))
+ self.comboBox.setItemText(4, _translate("SmoothDialog", "Moving standard deviation"))
+ self.comboBox.setItemText(5, _translate("SmoothDialog", "Moving variance"))
+ self.comboBox.setItemText(6, _translate("SmoothDialog", "Moving maximum"))
+ self.comboBox.setItemText(7, _translate("SmoothDialog", "Moving minimum"))
+ self.comboBox.setItemText(8, _translate("SmoothDialog", "Moving sum"))
+ self.y_checkBox.setText(_translate("SmoothDialog", "y log-spaced?"))
+ self.x_checkBox.setText(_translate("SmoothDialog", "x log-spaced?"))
diff --git a/nmreval/gui_qt/_py/t1_calc_dialog.py b/nmreval/gui_qt/_py/t1_calc_dialog.py
new file mode 100644
index 0000000..b560eba
--- /dev/null
+++ b/nmreval/gui_qt/_py/t1_calc_dialog.py
@@ -0,0 +1,318 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/t1_calc_dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(582, 698)
+ self.gridLayout_2 = QtWidgets.QGridLayout(Dialog)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.groupBox_2 = QtWidgets.QGroupBox(Dialog)
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_2)
+ self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_2.setSpacing(0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.specdens_combobox = QtWidgets.QComboBox(self.groupBox_2)
+ self.specdens_combobox.setObjectName("specdens_combobox")
+ self.verticalLayout_2.addWidget(self.specdens_combobox)
+ self.specdens_frame = QtWidgets.QFrame(self.groupBox_2)
+ self.specdens_frame.setObjectName("specdens_frame")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.specdens_frame)
+ self.verticalLayout_3.setSpacing(0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.verticalLayout_2.addWidget(self.specdens_frame)
+ self.coupling_combobox = QtWidgets.QComboBox(self.groupBox_2)
+ self.coupling_combobox.setObjectName("coupling_combobox")
+ self.verticalLayout_2.addWidget(self.coupling_combobox)
+ self.coupling_frame = QtWidgets.QFrame(self.groupBox_2)
+ self.coupling_frame.setObjectName("coupling_frame")
+ self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.coupling_frame)
+ self.verticalLayout_4.setContentsMargins(-1, -1, 1, 1)
+ self.verticalLayout_4.setSpacing(0)
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
+ self.verticalLayout_2.addWidget(self.coupling_frame)
+ self.gridLayout_2.addWidget(self.groupBox_2, 1, 0, 1, 1)
+ self.groupBox_3 = QtWidgets.QGroupBox(Dialog)
+ self.groupBox_3.setObjectName("groupBox_3")
+ self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox_3)
+ self.gridLayout_4.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_4.setSpacing(3)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ self.graph_combobox = QtWidgets.QComboBox(self.groupBox_3)
+ self.graph_combobox.setEnabled(False)
+ self.graph_combobox.setObjectName("graph_combobox")
+ self.gridLayout_4.addWidget(self.graph_combobox, 1, 1, 1, 1)
+ self.graph_checkbox = QtWidgets.QCheckBox(self.groupBox_3)
+ self.graph_checkbox.setChecked(True)
+ self.graph_checkbox.setObjectName("graph_checkbox")
+ self.gridLayout_4.addWidget(self.graph_checkbox, 1, 0, 1, 1)
+ self.relax_combox = QtWidgets.QComboBox(self.groupBox_3)
+ self.relax_combox.setObjectName("relax_combox")
+ self.relax_combox.addItem("")
+ self.relax_combox.addItem("")
+ self.gridLayout_4.addWidget(self.relax_combox, 0, 0, 1, 2)
+ self.gridLayout_2.addWidget(self.groupBox_3, 2, 0, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout_2.addItem(spacerItem, 3, 0, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout_2.addWidget(self.buttonBox, 4, 0, 1, 1)
+ self.groupBox = QtWidgets.QGroupBox(Dialog)
+ self.groupBox.setObjectName("groupBox")
+ self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox)
+ self.gridLayout_3.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_3.setSpacing(2)
+ self.gridLayout_3.setObjectName("gridLayout_3")
+ self.second_x_lineEdit = QtWidgets.QLineEdit(self.groupBox)
+ self.second_x_lineEdit.setObjectName("second_x_lineEdit")
+ self.gridLayout_3.addWidget(self.second_x_lineEdit, 10, 1, 1, 1)
+ self.x_input_combobox = QtWidgets.QComboBox(self.groupBox)
+ self.x_input_combobox.setObjectName("x_input_combobox")
+ self.x_input_combobox.addItem("")
+ self.x_input_combobox.addItem("")
+ self.gridLayout_3.addWidget(self.x_input_combobox, 1, 1, 1, 1)
+ self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_4.setSpacing(2)
+ self.horizontalLayout_4.setObjectName("horizontalLayout_4")
+ self.radioButton = QtWidgets.QRadioButton(self.groupBox)
+ self.radioButton.setChecked(True)
+ self.radioButton.setObjectName("radioButton")
+ self.buttonGroup = QtWidgets.QButtonGroup(Dialog)
+ self.buttonGroup.setObjectName("buttonGroup")
+ self.buttonGroup.addButton(self.radioButton)
+ self.horizontalLayout_4.addWidget(self.radioButton)
+ self.radioButton_2 = QtWidgets.QRadioButton(self.groupBox)
+ self.radioButton_2.setObjectName("radioButton_2")
+ self.buttonGroup.addButton(self.radioButton_2)
+ self.horizontalLayout_4.addWidget(self.radioButton_2)
+ self.radioButton_4 = QtWidgets.QRadioButton(self.groupBox)
+ self.radioButton_4.setObjectName("radioButton_4")
+ self.buttonGroup.addButton(self.radioButton_4)
+ self.horizontalLayout_4.addWidget(self.radioButton_4)
+ self.radioButton_3 = QtWidgets.QRadioButton(self.groupBox)
+ self.radioButton_3.setObjectName("radioButton_3")
+ self.buttonGroup.addButton(self.radioButton_3)
+ self.horizontalLayout_4.addWidget(self.radioButton_3)
+ self.gridLayout_3.addLayout(self.horizontalLayout_4, 0, 0, 1, 2)
+ self.label_7 = QtWidgets.QLabel(self.groupBox)
+ self.label_7.setObjectName("label_7")
+ self.gridLayout_3.addWidget(self.label_7, 10, 0, 1, 1)
+ self.line = QtWidgets.QFrame(self.groupBox)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line.setObjectName("line")
+ self.gridLayout_3.addWidget(self.line, 9, 0, 1, 2)
+ self.line_2 = QtWidgets.QFrame(self.groupBox)
+ self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.line_2.setObjectName("line_2")
+ self.gridLayout_3.addWidget(self.line_2, 7, 0, 1, 2)
+ self.range_widget = QtWidgets.QWidget(self.groupBox)
+ self.range_widget.setObjectName("range_widget")
+ self.gridLayout = QtWidgets.QGridLayout(self.range_widget)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setObjectName("gridLayout")
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout.addItem(spacerItem1, 0, 0, 1, 1)
+ self.stop_lineEdit = QtWidgets.QLineEdit(self.range_widget)
+ self.stop_lineEdit.setObjectName("stop_lineEdit")
+ self.gridLayout.addWidget(self.stop_lineEdit, 0, 3, 1, 1)
+ self.label_4 = QtWidgets.QLabel(self.range_widget)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout.addWidget(self.label_4, 0, 2, 1, 1)
+ self.spinBox = QtWidgets.QSpinBox(self.range_widget)
+ self.spinBox.setProperty("value", 50)
+ self.spinBox.setObjectName("spinBox")
+ self.gridLayout.addWidget(self.spinBox, 0, 5, 1, 1)
+ self.start_lineEdit = QtWidgets.QLineEdit(self.range_widget)
+ self.start_lineEdit.setObjectName("start_lineEdit")
+ self.gridLayout.addWidget(self.start_lineEdit, 0, 1, 1, 1)
+ self.checkBox = QtWidgets.QCheckBox(self.range_widget)
+ self.checkBox.setObjectName("checkBox")
+ self.gridLayout.addWidget(self.checkBox, 0, 6, 1, 1)
+ self.gridLayout_3.addWidget(self.range_widget, 2, 0, 1, 2)
+ self.data_widget = QtWidgets.QWidget(self.groupBox)
+ self.data_widget.setObjectName("data_widget")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.data_widget)
+ self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_2.setSpacing(2)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.tau_graph_combobox = QtWidgets.QComboBox(self.data_widget)
+ self.tau_graph_combobox.setObjectName("tau_graph_combobox")
+ self.horizontalLayout_2.addWidget(self.tau_graph_combobox)
+ self.tau_set_combobox = QtWidgets.QComboBox(self.data_widget)
+ self.tau_set_combobox.setObjectName("tau_set_combobox")
+ self.horizontalLayout_2.addWidget(self.tau_set_combobox)
+ self.label_10 = QtWidgets.QLabel(self.data_widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_10.sizePolicy().hasHeightForWidth())
+ self.label_10.setSizePolicy(sizePolicy)
+ self.label_10.setObjectName("label_10")
+ self.horizontalLayout_2.addWidget(self.label_10)
+ self.x_radioButton = QtWidgets.QRadioButton(self.data_widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.x_radioButton.sizePolicy().hasHeightForWidth())
+ self.x_radioButton.setSizePolicy(sizePolicy)
+ self.x_radioButton.setObjectName("x_radioButton")
+ self.buttonGroup_2 = QtWidgets.QButtonGroup(Dialog)
+ self.buttonGroup_2.setObjectName("buttonGroup_2")
+ self.buttonGroup_2.addButton(self.x_radioButton)
+ self.horizontalLayout_2.addWidget(self.x_radioButton)
+ self.y_radioButton = QtWidgets.QRadioButton(self.data_widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.y_radioButton.sizePolicy().hasHeightForWidth())
+ self.y_radioButton.setSizePolicy(sizePolicy)
+ self.y_radioButton.setChecked(True)
+ self.y_radioButton.setObjectName("y_radioButton")
+ self.buttonGroup_2.addButton(self.y_radioButton)
+ self.horizontalLayout_2.addWidget(self.y_radioButton)
+ self.gridLayout_3.addWidget(self.data_widget, 3, 0, 1, 2)
+ self.temp_widget = QtWidgets.QWidget(self.groupBox)
+ self.temp_widget.setObjectName("temp_widget")
+ self.gridLayout_5 = QtWidgets.QGridLayout(self.temp_widget)
+ self.gridLayout_5.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout_5.setHorizontalSpacing(6)
+ self.gridLayout_5.setVerticalSpacing(0)
+ self.gridLayout_5.setObjectName("gridLayout_5")
+ self.arr_widget = QtWidgets.QWidget(self.temp_widget)
+ self.arr_widget.setObjectName("arr_widget")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.arr_widget)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setSpacing(2)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem2)
+ self.label = QtWidgets.QLabel(self.arr_widget)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.tau0_lineEdit = QtWidgets.QLineEdit(self.arr_widget)
+ self.tau0_lineEdit.setObjectName("tau0_lineEdit")
+ self.horizontalLayout.addWidget(self.tau0_lineEdit)
+ spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem3)
+ self.label_2 = QtWidgets.QLabel(self.arr_widget)
+ self.label_2.setObjectName("label_2")
+ self.horizontalLayout.addWidget(self.label_2)
+ self.ea_lineEdit = QtWidgets.QLineEdit(self.arr_widget)
+ self.ea_lineEdit.setObjectName("ea_lineEdit")
+ self.horizontalLayout.addWidget(self.ea_lineEdit)
+ self.gridLayout_5.addWidget(self.arr_widget, 0, 1, 1, 1)
+ self.vft_widget = QtWidgets.QWidget(self.temp_widget)
+ self.vft_widget.setObjectName("vft_widget")
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.vft_widget)
+ self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_3.setSpacing(2)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.label_3 = QtWidgets.QLabel(self.vft_widget)
+ self.label_3.setObjectName("label_3")
+ self.horizontalLayout_3.addWidget(self.label_3)
+ self.tau0_vft_lineEdit = QtWidgets.QLineEdit(self.vft_widget)
+ self.tau0_vft_lineEdit.setObjectName("tau0_vft_lineEdit")
+ self.horizontalLayout_3.addWidget(self.tau0_vft_lineEdit)
+ spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_3.addItem(spacerItem4)
+ self.label_5 = QtWidgets.QLabel(self.vft_widget)
+ self.label_5.setObjectName("label_5")
+ self.horizontalLayout_3.addWidget(self.label_5)
+ self.b_vft_lineEdit = QtWidgets.QLineEdit(self.vft_widget)
+ self.b_vft_lineEdit.setObjectName("b_vft_lineEdit")
+ self.horizontalLayout_3.addWidget(self.b_vft_lineEdit)
+ spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_3.addItem(spacerItem5)
+ self.label_6 = QtWidgets.QLabel(self.vft_widget)
+ self.label_6.setObjectName("label_6")
+ self.horizontalLayout_3.addWidget(self.label_6)
+ self.t0_vft_lineEdit = QtWidgets.QLineEdit(self.vft_widget)
+ self.t0_vft_lineEdit.setObjectName("t0_vft_lineEdit")
+ self.horizontalLayout_3.addWidget(self.t0_vft_lineEdit)
+ self.gridLayout_5.addWidget(self.vft_widget, 1, 1, 1, 1)
+ self.temp_combobox = QtWidgets.QComboBox(self.temp_widget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.temp_combobox.sizePolicy().hasHeightForWidth())
+ self.temp_combobox.setSizePolicy(sizePolicy)
+ self.temp_combobox.setObjectName("temp_combobox")
+ self.temp_combobox.addItem("")
+ self.temp_combobox.addItem("")
+ self.gridLayout_5.addWidget(self.temp_combobox, 0, 0, 2, 1)
+ self.gridLayout_3.addWidget(self.temp_widget, 4, 0, 1, 2)
+ self.xtype_combobox = QtWidgets.QComboBox(self.groupBox)
+ self.xtype_combobox.setObjectName("xtype_combobox")
+ self.xtype_combobox.addItem("")
+ self.xtype_combobox.addItem("")
+ self.xtype_combobox.addItem("")
+ self.xtype_combobox.addItem("")
+ self.gridLayout_3.addWidget(self.xtype_combobox, 8, 1, 1, 1)
+ self.label_8 = QtWidgets.QLabel(self.groupBox)
+ self.label_8.setObjectName("label_8")
+ self.gridLayout_3.addWidget(self.label_8, 8, 0, 1, 1)
+ self.label_9 = QtWidgets.QLabel(self.groupBox)
+ self.label_9.setObjectName("label_9")
+ self.gridLayout_3.addWidget(self.label_9, 1, 0, 1, 1)
+ self.gridLayout_2.addWidget(self.groupBox, 0, 0, 1, 1)
+
+ self.retranslateUi(Dialog)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Calculate relaxation"))
+ self.groupBox_2.setTitle(_translate("Dialog", "Model"))
+ self.groupBox_3.setTitle(_translate("Dialog", "Result"))
+ self.graph_checkbox.setText(_translate("Dialog", "New graph?"))
+ self.relax_combox.setStatusTip(_translate("Dialog", "NOTE: Mean values are not available for all spectral densities. For more information ask someone."))
+ self.relax_combox.setItemText(0, _translate("Dialog", "Spin-Lattice Relaxation T1"))
+ self.relax_combox.setItemText(1, _translate("Dialog", "Spin-Spin Relaxation T2"))
+ self.groupBox.setTitle(_translate("Dialog", "Axis"))
+ self.x_input_combobox.setItemText(0, _translate("Dialog", "Range"))
+ self.x_input_combobox.setItemText(1, _translate("Dialog", "Data"))
+ self.radioButton.setText(_translate("Dialog", "τ / s"))
+ self.radioButton_2.setText(_translate("Dialog", "ω / Hz"))
+ self.radioButton_4.setText(_translate("Dialog", "1000 K / T"))
+ self.radioButton_3.setText(_translate("Dialog", "T / K"))
+ self.label_7.setText(_translate("Dialog", "2nd axis"))
+ self.label_4.setText(_translate("Dialog", "–"))
+ self.spinBox.setSuffix(_translate("Dialog", " pts."))
+ self.checkBox.setText(_translate("Dialog", "Log?"))
+ self.label_10.setText(_translate("Dialog", " Use"))
+ self.x_radioButton.setText(_translate("Dialog", "x"))
+ self.y_radioButton.setText(_translate("Dialog", "y"))
+ self.label.setText(_translate("Dialog", "τ0 / s "))
+ self.tau0_lineEdit.setText(_translate("Dialog", "1"))
+ self.label_2.setText(_translate("Dialog", "E_A / eV "))
+ self.ea_lineEdit.setText(_translate("Dialog", "1"))
+ self.label_3.setText(_translate("Dialog", "τ0 / s "))
+ self.tau0_vft_lineEdit.setText(_translate("Dialog", "1"))
+ self.label_5.setText(_translate("Dialog", "B / K "))
+ self.b_vft_lineEdit.setText(_translate("Dialog", "1"))
+ self.label_6.setText(_translate("Dialog", "T_0 / K "))
+ self.t0_vft_lineEdit.setText(_translate("Dialog", "1"))
+ self.temp_combobox.setItemText(0, _translate("Dialog", "Arrhenius"))
+ self.temp_combobox.setItemText(1, _translate("Dialog", "VFT"))
+ self.xtype_combobox.setItemText(0, _translate("Dialog", "Function parameter: τ"))
+ self.xtype_combobox.setItemText(1, _translate("Dialog", "Peak time: τₚ"))
+ self.xtype_combobox.setItemText(2, _translate("Dialog", "Arithmetic mean: ⟨τ⟩"))
+ self.xtype_combobox.setItemText(3, _translate("Dialog", "Geometric mean: exp(⟨ln τ⟩)"))
+ self.label_8.setText(_translate("Dialog", "Interpretation as:"))
+ self.label_9.setText(_translate("Dialog", "Input from: "))
diff --git a/nmreval/gui_qt/_py/t1_dock.py b/nmreval/gui_qt/_py/t1_dock.py
new file mode 100644
index 0000000..cc1bdf0
--- /dev/null
+++ b/nmreval/gui_qt/_py/t1_dock.py
@@ -0,0 +1,258 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/t1_dock.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_t1dialog(object):
+ def setupUi(self, t1dialog):
+ t1dialog.setObjectName("t1dialog")
+ t1dialog.resize(295, 771)
+ self.dockWidgetContents = QtWidgets.QWidget()
+ self.dockWidgetContents.setObjectName("dockWidgetContents")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.dockWidgetContents)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.scrollArea = QtWidgets.QScrollArea(self.dockWidgetContents)
+ self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.scrollArea.setWidgetResizable(True)
+ self.scrollArea.setObjectName("scrollArea")
+ self.scrollAreaWidgetContents = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 279, 727))
+ self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
+ self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
+ self.verticalLayout_7.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_7.setSpacing(0)
+ self.verticalLayout_7.setObjectName("verticalLayout_7")
+ self.groupBox_1 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
+ self.groupBox_1.setObjectName("groupBox_1")
+ self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox_1)
+ self.gridLayout_3.setVerticalSpacing(0)
+ self.gridLayout_3.setObjectName("gridLayout_3")
+ self.comboBox_3 = QtWidgets.QComboBox(self.groupBox_1)
+ self.comboBox_3.setObjectName("comboBox_3")
+ self.comboBox_3.addItem("")
+ self.comboBox_3.addItem("")
+ self.comboBox_3.addItem("")
+ self.comboBox_3.addItem("")
+ self.gridLayout_3.addWidget(self.comboBox_3, 0, 1, 1, 1)
+ self.label_6 = QtWidgets.QLabel(self.groupBox_1)
+ self.label_6.setObjectName("label_6")
+ self.gridLayout_3.addWidget(self.label_6, 0, 0, 1, 1)
+ self.comboBox_2 = QtWidgets.QComboBox(self.groupBox_1)
+ self.comboBox_2.setObjectName("comboBox_2")
+ self.comboBox_2.addItem("")
+ self.comboBox_2.addItem("")
+ self.comboBox_2.addItem("")
+ self.gridLayout_3.addWidget(self.comboBox_2, 1, 1, 1, 1)
+ self.label_5 = QtWidgets.QLabel(self.groupBox_1)
+ self.label_5.setObjectName("label_5")
+ self.gridLayout_3.addWidget(self.label_5, 1, 0, 1, 1)
+ self.verticalLayout_7.addWidget(self.groupBox_1)
+ self.groupBox_5 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
+ self.groupBox_5.setObjectName("groupBox_5")
+ self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox_5)
+ self.verticalLayout_6.setSpacing(0)
+ self.verticalLayout_6.setObjectName("verticalLayout_6")
+ self.comboBox_6 = QtWidgets.QComboBox(self.groupBox_5)
+ self.comboBox_6.setObjectName("comboBox_6")
+ self.comboBox_6.addItem("")
+ self.comboBox_6.addItem("")
+ self.comboBox_6.addItem("")
+ self.comboBox_6.addItem("")
+ self.verticalLayout_6.addWidget(self.comboBox_6)
+ self.frame = QtWidgets.QFrame(self.groupBox_5)
+ self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.frame.setObjectName("frame")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame)
+ self.horizontalLayout.setContentsMargins(0, 6, 0, 0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label_9 = QtWidgets.QLabel(self.frame)
+ self.label_9.setObjectName("label_9")
+ self.horizontalLayout.addWidget(self.label_9)
+ self.lineEdit_2 = QtWidgets.QLineEdit(self.frame)
+ self.lineEdit_2.setObjectName("lineEdit_2")
+ self.horizontalLayout.addWidget(self.lineEdit_2)
+ self.label_10 = QtWidgets.QLabel(self.frame)
+ self.label_10.setObjectName("label_10")
+ self.horizontalLayout.addWidget(self.label_10)
+ self.lineEdit_3 = QtWidgets.QLineEdit(self.frame)
+ self.lineEdit_3.setObjectName("lineEdit_3")
+ self.horizontalLayout.addWidget(self.lineEdit_3)
+ self.checkBox = QtWidgets.QCheckBox(self.frame)
+ self.checkBox.setObjectName("checkBox")
+ self.horizontalLayout.addWidget(self.checkBox)
+ self.verticalLayout_6.addWidget(self.frame)
+ self.frame_2 = QtWidgets.QFrame(self.groupBox_5)
+ self.frame_2.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame_2.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.frame_2.setObjectName("frame_2")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame_2)
+ self.horizontalLayout_2.setContentsMargins(0, 6, 0, 0)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.label_11 = QtWidgets.QLabel(self.frame_2)
+ self.label_11.setObjectName("label_11")
+ self.horizontalLayout_2.addWidget(self.label_11)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem)
+ self.label_13 = QtWidgets.QLabel(self.frame_2)
+ self.label_13.setObjectName("label_13")
+ self.horizontalLayout_2.addWidget(self.label_13)
+ self.label_12 = QtWidgets.QLabel(self.frame_2)
+ self.label_12.setObjectName("label_12")
+ self.horizontalLayout_2.addWidget(self.label_12)
+ self.pushButton_2 = QtWidgets.QPushButton(self.frame_2)
+ self.pushButton_2.setObjectName("pushButton_2")
+ self.horizontalLayout_2.addWidget(self.pushButton_2)
+ self.verticalLayout_6.addWidget(self.frame_2)
+ self.verticalLayout_7.addWidget(self.groupBox_5)
+ self.groupBox_2 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.gridLayout = QtWidgets.QGridLayout(self.groupBox_2)
+ self.gridLayout.setVerticalSpacing(0)
+ self.gridLayout.setObjectName("gridLayout")
+ self.label_3 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout.addWidget(self.label_3, 0, 0, 1, 1)
+ self.label_2 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 0, 3, 1, 1)
+ self.t1_min_edit = QtWidgets.QLineEdit(self.groupBox_2)
+ self.t1_min_edit.setObjectName("t1_min_edit")
+ self.gridLayout.addWidget(self.t1_min_edit, 0, 4, 1, 1)
+ self.label_7 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_7.setObjectName("label_7")
+ self.gridLayout.addWidget(self.label_7, 0, 5, 1, 1)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout.addItem(spacerItem1, 0, 1, 1, 1)
+ self.t1_pos_edit = QtWidgets.QLineEdit(self.groupBox_2)
+ self.t1_pos_edit.setObjectName("t1_pos_edit")
+ self.gridLayout.addWidget(self.t1_pos_edit, 0, 2, 1, 1)
+ self.label = QtWidgets.QLabel(self.groupBox_2)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
+ self.label_8 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_8.setObjectName("label_8")
+ self.gridLayout.addWidget(self.label_8, 1, 5, 1, 1)
+ self.lineEdit = QtWidgets.QLineEdit(self.groupBox_2)
+ self.lineEdit.setInputMethodHints(QtCore.Qt.ImhDigitsOnly|QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhPreferNumbers)
+ self.lineEdit.setObjectName("lineEdit")
+ self.gridLayout.addWidget(self.lineEdit, 1, 4, 1, 1)
+ self.verticalLayout_7.addWidget(self.groupBox_2)
+ self.groupBox = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
+ self.groupBox.setObjectName("groupBox")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox)
+ self.verticalLayout_2.setSpacing(0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.comboBox_4 = QtWidgets.QComboBox(self.groupBox)
+ self.comboBox_4.setObjectName("comboBox_4")
+ self.verticalLayout_2.addWidget(self.comboBox_4)
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.verticalLayout_2.addLayout(self.verticalLayout_3)
+ self.label_14 = QtWidgets.QLabel(self.groupBox)
+ self.label_14.setObjectName("label_14")
+ self.verticalLayout_2.addWidget(self.label_14)
+ self.verticalLayout_7.addWidget(self.groupBox)
+ self.groupBox_4 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
+ self.groupBox_4.setObjectName("groupBox_4")
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupBox_4)
+ self.verticalLayout_5.setSpacing(0)
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.comboBox_5 = QtWidgets.QComboBox(self.groupBox_4)
+ self.comboBox_5.setObjectName("comboBox_5")
+ self.verticalLayout_5.addWidget(self.comboBox_5)
+ self.verticalLayout_4 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_4.setContentsMargins(-1, -1, 0, 0)
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
+ self.verticalLayout_5.addLayout(self.verticalLayout_4)
+ self.verticalLayout_7.addWidget(self.groupBox_4)
+ self.groupBox_3 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
+ self.groupBox_3.setObjectName("groupBox_3")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox_3)
+ self.gridLayout_2.setContentsMargins(-1, 1, -1, -1)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.comboBox = QtWidgets.QComboBox(self.groupBox_3)
+ self.comboBox.setObjectName("comboBox")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.gridLayout_2.addWidget(self.comboBox, 0, 1, 1, 1)
+ self.label_4 = QtWidgets.QLabel(self.groupBox_3)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout_2.addWidget(self.label_4, 0, 0, 1, 1)
+ self.checkBox_interpol = QtWidgets.QCheckBox(self.groupBox_3)
+ self.checkBox_interpol.setObjectName("checkBox_interpol")
+ self.gridLayout_2.addWidget(self.checkBox_interpol, 1, 1, 1, 1)
+ self.verticalLayout_7.addWidget(self.groupBox_3)
+ self.pushButton = QtWidgets.QPushButton(self.scrollAreaWidgetContents)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.pushButton.setFont(font)
+ self.pushButton.setObjectName("pushButton")
+ self.verticalLayout_7.addWidget(self.pushButton)
+ spacerItem2 = QtWidgets.QSpacerItem(17, 423, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_7.addItem(spacerItem2)
+ self.scrollArea.setWidget(self.scrollAreaWidgetContents)
+ self.verticalLayout.addWidget(self.scrollArea)
+ t1dialog.setWidget(self.dockWidgetContents)
+
+ self.retranslateUi(t1dialog)
+ QtCore.QMetaObject.connectSlotsByName(t1dialog)
+
+ def retranslateUi(self, t1dialog):
+ _translate = QtCore.QCoreApplication.translate
+ t1dialog.setWindowTitle(_translate("t1dialog", "Evaluate T1 (temperature)"))
+ self.groupBox_1.setTitle(_translate("t1dialog", "Axes"))
+ self.comboBox_3.setItemText(0, _translate("t1dialog", "T1"))
+ self.comboBox_3.setItemText(1, _translate("t1dialog", "1/T1"))
+ self.comboBox_3.setItemText(2, _translate("t1dialog", "log10(T1)"))
+ self.comboBox_3.setItemText(3, _translate("t1dialog", "log10(1/T1)"))
+ self.label_6.setText(_translate("t1dialog", "Relaxation in"))
+ self.comboBox_2.setItemText(0, _translate("t1dialog", "T"))
+ self.comboBox_2.setItemText(1, _translate("t1dialog", "1000/T"))
+ self.comboBox_2.setItemText(2, _translate("t1dialog", "1/T"))
+ self.label_5.setText(_translate("t1dialog", "Temperature in"))
+ self.groupBox_5.setTitle(_translate("t1dialog", "T1 minimon"))
+ self.comboBox_6.setItemText(0, _translate("t1dialog", "Data minimum"))
+ self.comboBox_6.setItemText(1, _translate("t1dialog", "Parabola"))
+ self.comboBox_6.setItemText(2, _translate("t1dialog", "Cubic spline"))
+ self.comboBox_6.setItemText(3, _translate("t1dialog", "Pchip"))
+ self.label_9.setText(_translate("t1dialog", "start (K)"))
+ self.lineEdit_2.setPlaceholderText(_translate("t1dialog", "1"))
+ self.label_10.setText(_translate("t1dialog", " end (K)"))
+ self.lineEdit_3.setPlaceholderText(_translate("t1dialog", "2"))
+ self.checkBox.setText(_translate("t1dialog", "Show?"))
+ self.label_11.setText(_translate("t1dialog", "Minimon"))
+ self.label_13.setText(_translate("t1dialog", "x value"))
+ self.label_12.setText(_translate("t1dialog", "y value"))
+ self.pushButton_2.setText(_translate("t1dialog", "Use"))
+ self.groupBox_2.setTitle(_translate("t1dialog", "Parameter"))
+ self.label_3.setText(_translate("t1dialog", "T1 minimum
"))
+ self.label_2.setText(_translate("t1dialog", "K"))
+ self.t1_min_edit.setPlaceholderText(_translate("t1dialog", "1e-3"))
+ self.label_7.setText(_translate("t1dialog", "s"))
+ self.t1_pos_edit.setPlaceholderText(_translate("t1dialog", "100"))
+ self.label.setText(_translate("t1dialog", "frequency"))
+ self.label_8.setText(_translate("t1dialog", "Hz"))
+ self.lineEdit.setPlaceholderText(_translate("t1dialog", "100e6"))
+ self.groupBox.setTitle(_translate("t1dialog", "Spectral density"))
+ self.label_14.setText(_translate("t1dialog", "Calculated minimum:"))
+ self.groupBox_4.setTitle(_translate("t1dialog", "Coupling"))
+ self.groupBox_3.setTitle(_translate("t1dialog", "Result"))
+ self.comboBox.setStatusTip(_translate("t1dialog", "NOTE: Mean values are not available for all spectral densities. For more information ask someone."))
+ self.comboBox.setItemText(0, _translate("t1dialog", "Fit parameter: τ"))
+ self.comboBox.setItemText(1, _translate("t1dialog", "Peak time: τₚ"))
+ self.comboBox.setItemText(2, _translate("t1dialog", "Arithmetic mean: ⟨τ⟩"))
+ self.comboBox.setItemText(3, _translate("t1dialog", "Geometric mean: exp(⟨ln τ⟩)"))
+ self.label_4.setText(_translate("t1dialog", "Result as"))
+ self.checkBox_interpol.setText(_translate("t1dialog", "Use interpolation"))
+ self.pushButton.setText(_translate("t1dialog", "Calculate"))
diff --git a/nmreval/gui_qt/_py/t1_tau_calculation.py b/nmreval/gui_qt/_py/t1_tau_calculation.py
new file mode 100644
index 0000000..d61945a
--- /dev/null
+++ b/nmreval/gui_qt/_py/t1_tau_calculation.py
@@ -0,0 +1,215 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/t1_tau_calculation.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(400, 799)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Form)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.groupBox_2 = QtWidgets.QGroupBox(Form)
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox_2)
+ self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_2.setHorizontalSpacing(3)
+ self.gridLayout_2.setVerticalSpacing(1)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.freq_spinbox = QtWidgets.QDoubleSpinBox(self.groupBox_2)
+ self.freq_spinbox.setMaximum(999.99)
+ self.freq_spinbox.setProperty("value", 100.0)
+ self.freq_spinbox.setObjectName("freq_spinbox")
+ self.gridLayout_2.addWidget(self.freq_spinbox, 0, 0, 1, 1)
+ self.freq_combox = QtWidgets.QComboBox(self.groupBox_2)
+ self.freq_combox.setObjectName("freq_combox")
+ self.freq_combox.addItem("")
+ self.freq_combox.addItem("")
+ self.freq_combox.addItem("")
+ self.gridLayout_2.addWidget(self.freq_combox, 0, 1, 1, 1)
+ self.verticalLayout.addWidget(self.groupBox_2)
+ self.scrollArea = QtWidgets.QScrollArea(Form)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.scrollArea.sizePolicy().hasHeightForWidth())
+ self.scrollArea.setSizePolicy(sizePolicy)
+ self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.scrollArea.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+ self.scrollArea.setWidgetResizable(True)
+ self.scrollArea.setObjectName("scrollArea")
+ self.scrollAreaWidgetContents = QtWidgets.QWidget()
+ self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 388, 713))
+ self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
+ self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_5.setSpacing(3)
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.groupBox_6 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
+ self.groupBox_6.setObjectName("groupBox_6")
+ self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.groupBox_6)
+ self.verticalLayout_7.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_7.setSpacing(1)
+ self.verticalLayout_7.setObjectName("verticalLayout_7")
+ self.comboBox_7 = QtWidgets.QComboBox(self.groupBox_6)
+ self.comboBox_7.setObjectName("comboBox_7")
+ self.comboBox_7.addItem("")
+ self.comboBox_7.addItem("")
+ self.comboBox_7.addItem("")
+ self.verticalLayout_7.addWidget(self.comboBox_7)
+ self.arr_frame = QtWidgets.QFrame(self.groupBox_6)
+ self.arr_frame.setObjectName("arr_frame")
+ self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.arr_frame)
+ self.verticalLayout_6.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_6.setSpacing(1)
+ self.verticalLayout_6.setObjectName("verticalLayout_6")
+ self.verticalLayout_7.addWidget(self.arr_frame)
+ self.temp_frame = QtWidgets.QFrame(self.groupBox_6)
+ self.temp_frame.setObjectName("temp_frame")
+ self.gridLayout = QtWidgets.QGridLayout(self.temp_frame)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setObjectName("gridLayout")
+ self.lineEdit_3 = QtWidgets.QLineEdit(self.temp_frame)
+ self.lineEdit_3.setObjectName("lineEdit_3")
+ self.gridLayout.addWidget(self.lineEdit_3, 0, 1, 1, 1)
+ self.checkBox = QtWidgets.QCheckBox(self.temp_frame)
+ self.checkBox.setObjectName("checkBox")
+ self.gridLayout.addWidget(self.checkBox, 0, 7, 1, 1)
+ self.label_4 = QtWidgets.QLabel(self.temp_frame)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout.addWidget(self.label_4, 0, 2, 1, 1)
+ self.lineEdit_4 = QtWidgets.QLineEdit(self.temp_frame)
+ self.lineEdit_4.setObjectName("lineEdit_4")
+ self.gridLayout.addWidget(self.lineEdit_4, 0, 3, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout.addItem(spacerItem, 0, 4, 1, 1)
+ self.spinBox = QtWidgets.QSpinBox(self.temp_frame)
+ self.spinBox.setProperty("value", 50)
+ self.spinBox.setObjectName("spinBox")
+ self.gridLayout.addWidget(self.spinBox, 0, 5, 1, 1)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout.addItem(spacerItem1, 0, 6, 1, 1)
+ self.verticalLayout_7.addWidget(self.temp_frame)
+ self.data_frame = QtWidgets.QFrame(self.groupBox_6)
+ self.data_frame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.data_frame.setFrameShadow(QtWidgets.QFrame.Raised)
+ self.data_frame.setObjectName("data_frame")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.data_frame)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setSpacing(1)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.tau_graph_combobox = QtWidgets.QComboBox(self.data_frame)
+ self.tau_graph_combobox.setObjectName("tau_graph_combobox")
+ self.horizontalLayout.addWidget(self.tau_graph_combobox)
+ self.tau_set_combobox = QtWidgets.QComboBox(self.data_frame)
+ self.tau_set_combobox.setObjectName("tau_set_combobox")
+ self.horizontalLayout.addWidget(self.tau_set_combobox)
+ self.verticalLayout_7.addWidget(self.data_frame)
+ self.comboBox = QtWidgets.QComboBox(self.groupBox_6)
+ self.comboBox.setObjectName("comboBox")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.comboBox.addItem("")
+ self.verticalLayout_7.addWidget(self.comboBox)
+ self.verticalLayout_5.addWidget(self.groupBox_6)
+ self.groupBox = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
+ self.groupBox.setObjectName("groupBox")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox)
+ self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_2.setSpacing(0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.specdens_combobox = QtWidgets.QComboBox(self.groupBox)
+ self.specdens_combobox.setObjectName("specdens_combobox")
+ self.verticalLayout_2.addWidget(self.specdens_combobox)
+ self.specdens_frame = QtWidgets.QFrame(self.groupBox)
+ self.specdens_frame.setObjectName("specdens_frame")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.specdens_frame)
+ self.verticalLayout_3.setSpacing(0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.verticalLayout_2.addWidget(self.specdens_frame)
+ self.coupling_combobox = QtWidgets.QComboBox(self.groupBox)
+ self.coupling_combobox.setObjectName("coupling_combobox")
+ self.verticalLayout_2.addWidget(self.coupling_combobox)
+ self.coupling_frame = QtWidgets.QFrame(self.groupBox)
+ self.coupling_frame.setObjectName("coupling_frame")
+ self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.coupling_frame)
+ self.verticalLayout_4.setContentsMargins(-1, -1, 1, 1)
+ self.verticalLayout_4.setSpacing(0)
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
+ self.verticalLayout_2.addWidget(self.coupling_frame)
+ self.verticalLayout_5.addWidget(self.groupBox)
+ self.groupBox_3 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
+ self.groupBox_3.setObjectName("groupBox_3")
+ self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox_3)
+ self.gridLayout_4.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_4.setSpacing(3)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ self.tau_combox = QtWidgets.QComboBox(self.groupBox_3)
+ self.tau_combox.setObjectName("tau_combox")
+ self.tau_combox.addItem("")
+ self.tau_combox.addItem("")
+ self.tau_combox.addItem("")
+ self.tau_combox.addItem("")
+ self.gridLayout_4.addWidget(self.tau_combox, 0, 0, 1, 2)
+ self.graph_checkbox = QtWidgets.QCheckBox(self.groupBox_3)
+ self.graph_checkbox.setChecked(True)
+ self.graph_checkbox.setObjectName("graph_checkbox")
+ self.gridLayout_4.addWidget(self.graph_checkbox, 1, 0, 1, 1)
+ self.graph_combobox = QtWidgets.QComboBox(self.groupBox_3)
+ self.graph_combobox.setEnabled(True)
+ self.graph_combobox.setObjectName("graph_combobox")
+ self.gridLayout_4.addWidget(self.graph_combobox, 1, 1, 1, 1)
+ self.verticalLayout_5.addWidget(self.groupBox_3)
+ self.pushButton = QtWidgets.QPushButton(self.scrollAreaWidgetContents)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.pushButton.setFont(font)
+ self.pushButton.setObjectName("pushButton")
+ self.verticalLayout_5.addWidget(self.pushButton)
+ spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_5.addItem(spacerItem2)
+ self.scrollArea.setWidget(self.scrollAreaWidgetContents)
+ self.verticalLayout.addWidget(self.scrollArea)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.groupBox_2.setTitle(_translate("Form", "Frequency"))
+ self.freq_combox.setItemText(0, _translate("Form", "MHz"))
+ self.freq_combox.setItemText(1, _translate("Form", "kHz"))
+ self.freq_combox.setItemText(2, _translate("Form", "Hz"))
+ self.groupBox_6.setTitle(_translate("Form", "Correlation times"))
+ self.comboBox_7.setItemText(0, _translate("Form", "Arrhenius"))
+ self.comboBox_7.setItemText(1, _translate("Form", "VFT"))
+ self.comboBox_7.setItemText(2, _translate("Form", "Data"))
+ self.lineEdit_3.setPlaceholderText(_translate("Form", "1 K"))
+ self.checkBox.setText(_translate("Form", "1000/T"))
+ self.label_4.setText(_translate("Form", "–"))
+ self.lineEdit_4.setPlaceholderText(_translate("Form", "100 K"))
+ self.spinBox.setSuffix(_translate("Form", " pts."))
+ self.comboBox.setItemText(0, _translate("Form", "Fit parameter: τ"))
+ self.comboBox.setItemText(1, _translate("Form", "Peak time: τₚ"))
+ self.comboBox.setItemText(2, _translate("Form", "Arithmetic mean: ⟨τ⟩"))
+ self.comboBox.setItemText(3, _translate("Form", "Geometric mean: exp(⟨ln τ⟩)"))
+ self.groupBox.setTitle(_translate("Form", "Model"))
+ self.groupBox_3.setTitle(_translate("Form", "Result"))
+ self.tau_combox.setStatusTip(_translate("Form", "NOTE: Mean values are not available for all spectral densities. For more information ask someone."))
+ self.tau_combox.setItemText(0, _translate("Form", "Fit parameter: τ"))
+ self.tau_combox.setItemText(1, _translate("Form", "Peak time: τₚ"))
+ self.tau_combox.setItemText(2, _translate("Form", "Arithmetic mean: ⟨τ⟩"))
+ self.tau_combox.setItemText(3, _translate("Form", "Geometric mean: exp(⟨ln τ⟩)"))
+ self.graph_checkbox.setText(_translate("Form", "New graph?"))
+ self.pushButton.setText(_translate("Form", "Calculate"))
diff --git a/nmreval/gui_qt/_py/t1dialog.py b/nmreval/gui_qt/_py/t1dialog.py
new file mode 100644
index 0000000..7f6c421
--- /dev/null
+++ b/nmreval/gui_qt/_py/t1dialog.py
@@ -0,0 +1,228 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/t1dialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_t1dialog(object):
+ def setupUi(self, t1dialog):
+ t1dialog.setObjectName("t1dialog")
+ t1dialog.resize(316, 741)
+ self.gridLayout_2 = QtWidgets.QGridLayout(t1dialog)
+ self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.groupBox_1 = QtWidgets.QGroupBox(t1dialog)
+ self.groupBox_1.setObjectName("groupBox_1")
+ self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox_1)
+ self.gridLayout_3.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_3.setHorizontalSpacing(3)
+ self.gridLayout_3.setVerticalSpacing(0)
+ self.gridLayout_3.setObjectName("gridLayout_3")
+ self.t1_combobox = QtWidgets.QComboBox(self.groupBox_1)
+ self.t1_combobox.setObjectName("t1_combobox")
+ self.t1_combobox.addItem("")
+ self.t1_combobox.addItem("")
+ self.gridLayout_3.addWidget(self.t1_combobox, 1, 1, 1, 1)
+ self.temp_combobox = QtWidgets.QComboBox(self.groupBox_1)
+ self.temp_combobox.setObjectName("temp_combobox")
+ self.temp_combobox.addItem("")
+ self.temp_combobox.addItem("")
+ self.temp_combobox.addItem("")
+ self.gridLayout_3.addWidget(self.temp_combobox, 1, 0, 1, 1)
+ self.gridLayout_2.addWidget(self.groupBox_1, 0, 0, 1, 1)
+ self.groupBox_2 = QtWidgets.QGroupBox(t1dialog)
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.gridLayout = QtWidgets.QGridLayout(self.groupBox_2)
+ self.gridLayout.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout.setHorizontalSpacing(3)
+ self.gridLayout.setVerticalSpacing(1)
+ self.gridLayout.setObjectName("gridLayout")
+ self.freq_combox = QtWidgets.QComboBox(self.groupBox_2)
+ self.freq_combox.setObjectName("freq_combox")
+ self.freq_combox.addItem("")
+ self.freq_combox.addItem("")
+ self.freq_combox.addItem("")
+ self.gridLayout.addWidget(self.freq_combox, 0, 1, 1, 1)
+ self.freq_spinbox = QtWidgets.QDoubleSpinBox(self.groupBox_2)
+ self.freq_spinbox.setMaximum(999.99)
+ self.freq_spinbox.setProperty("value", 100.0)
+ self.freq_spinbox.setObjectName("freq_spinbox")
+ self.gridLayout.addWidget(self.freq_spinbox, 0, 0, 1, 1)
+ self.gridLayout_2.addWidget(self.groupBox_2, 1, 0, 1, 1)
+ self.groupBox = QtWidgets.QGroupBox(t1dialog)
+ self.groupBox.setObjectName("groupBox")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox)
+ self.verticalLayout_2.setContentsMargins(3, 3, 3, -1)
+ self.verticalLayout_2.setSpacing(2)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.specdens_combobox = QtWidgets.QComboBox(self.groupBox)
+ self.specdens_combobox.setObjectName("specdens_combobox")
+ self.verticalLayout_2.addWidget(self.specdens_combobox)
+ self.sd_frame = QtWidgets.QFrame(self.groupBox)
+ self.sd_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
+ self.sd_frame.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.sd_frame.setObjectName("sd_frame")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.sd_frame)
+ self.verticalLayout_3.setContentsMargins(1, 1, 1, 1)
+ self.verticalLayout_3.setSpacing(0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.verticalLayout_2.addWidget(self.sd_frame)
+ self.coupling_combobox = QtWidgets.QComboBox(self.groupBox)
+ self.coupling_combobox.setObjectName("coupling_combobox")
+ self.verticalLayout_2.addWidget(self.coupling_combobox)
+ self.cp_frame = QtWidgets.QFrame(self.groupBox)
+ self.cp_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
+ self.cp_frame.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.cp_frame.setObjectName("cp_frame")
+ self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.cp_frame)
+ self.verticalLayout_4.setContentsMargins(1, 1, 1, 1)
+ self.verticalLayout_4.setSpacing(0)
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
+ self.verticalLayout_2.addWidget(self.cp_frame)
+ self.gridLayout_2.addWidget(self.groupBox, 3, 0, 1, 1)
+ self.groupBox_5 = QtWidgets.QGroupBox(t1dialog)
+ self.groupBox_5.setObjectName("groupBox_5")
+ self.gridLayout_5 = QtWidgets.QGridLayout(self.groupBox_5)
+ self.gridLayout_5.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_5.setHorizontalSpacing(2)
+ self.gridLayout_5.setObjectName("gridLayout_5")
+ self.interpol_combobox = QtWidgets.QComboBox(self.groupBox_5)
+ self.interpol_combobox.setObjectName("interpol_combobox")
+ self.interpol_combobox.addItem("")
+ self.interpol_combobox.addItem("")
+ self.interpol_combobox.addItem("")
+ self.interpol_combobox.addItem("")
+ self.gridLayout_5.addWidget(self.interpol_combobox, 0, 0, 1, 1)
+ self.frame = QtWidgets.QFrame(self.groupBox_5)
+ self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.frame.setObjectName("frame")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label_9 = QtWidgets.QLabel(self.frame)
+ self.label_9.setObjectName("label_9")
+ self.horizontalLayout.addWidget(self.label_9)
+ self.lineEdit_2 = QtWidgets.QLineEdit(self.frame)
+ self.lineEdit_2.setObjectName("lineEdit_2")
+ self.horizontalLayout.addWidget(self.lineEdit_2)
+ self.label_10 = QtWidgets.QLabel(self.frame)
+ self.label_10.setObjectName("label_10")
+ self.horizontalLayout.addWidget(self.label_10)
+ self.lineEdit_3 = QtWidgets.QLineEdit(self.frame)
+ self.lineEdit_3.setObjectName("lineEdit_3")
+ self.horizontalLayout.addWidget(self.lineEdit_3)
+ self.gridLayout_5.addWidget(self.frame, 1, 0, 1, 2)
+ self.frame_2 = QtWidgets.QFrame(self.groupBox_5)
+ self.frame_2.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame_2.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.frame_2.setObjectName("frame_2")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame_2)
+ self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.label_11 = QtWidgets.QLabel(self.frame_2)
+ self.label_11.setObjectName("label_11")
+ self.horizontalLayout_2.addWidget(self.label_11)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem)
+ self.label_13 = QtWidgets.QLabel(self.frame_2)
+ self.label_13.setObjectName("label_13")
+ self.horizontalLayout_2.addWidget(self.label_13)
+ self.label_12 = QtWidgets.QLabel(self.frame_2)
+ self.label_12.setObjectName("label_12")
+ self.horizontalLayout_2.addWidget(self.label_12)
+ self.gridLayout_5.addWidget(self.frame_2, 2, 0, 1, 2)
+ self.t1min_toolButton = QtWidgets.QToolButton(self.groupBox_5)
+ self.t1min_toolButton.setObjectName("t1min_toolButton")
+ self.gridLayout_5.addWidget(self.t1min_toolButton, 0, 1, 1, 1)
+ self.gridLayout_2.addWidget(self.groupBox_5, 2, 0, 1, 1)
+ self.groupBox_3 = QtWidgets.QGroupBox(t1dialog)
+ self.groupBox_3.setObjectName("groupBox_3")
+ self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox_3)
+ self.gridLayout_4.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_4.setSpacing(3)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ self.graph_combobox = QtWidgets.QComboBox(self.groupBox_3)
+ self.graph_combobox.setEnabled(False)
+ self.graph_combobox.setObjectName("graph_combobox")
+ self.gridLayout_4.addWidget(self.graph_combobox, 3, 1, 1, 1)
+ self.tau_combox = QtWidgets.QComboBox(self.groupBox_3)
+ self.tau_combox.setObjectName("tau_combox")
+ self.tau_combox.addItem("")
+ self.tau_combox.addItem("")
+ self.tau_combox.addItem("")
+ self.tau_combox.addItem("")
+ self.gridLayout_4.addWidget(self.tau_combox, 1, 0, 1, 2)
+ self.checkBox_interpol = QtWidgets.QCheckBox(self.groupBox_3)
+ self.checkBox_interpol.setObjectName("checkBox_interpol")
+ self.gridLayout_4.addWidget(self.checkBox_interpol, 2, 0, 1, 2)
+ self.graph_checkbox = QtWidgets.QCheckBox(self.groupBox_3)
+ self.graph_checkbox.setChecked(True)
+ self.graph_checkbox.setObjectName("graph_checkbox")
+ self.gridLayout_4.addWidget(self.graph_checkbox, 3, 0, 1, 1)
+ self.label = QtWidgets.QLabel(self.groupBox_3)
+ self.label.setObjectName("label")
+ self.gridLayout_4.addWidget(self.label, 0, 0, 1, 1)
+ self.label_t1min = QtWidgets.QLabel(self.groupBox_3)
+ self.label_t1min.setText("")
+ self.label_t1min.setObjectName("label_t1min")
+ self.gridLayout_4.addWidget(self.label_t1min, 0, 1, 1, 1)
+ self.gridLayout_2.addWidget(self.groupBox_3, 4, 0, 1, 1)
+ spacerItem1 = QtWidgets.QSpacerItem(17, 19, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout_2.addItem(spacerItem1, 6, 0, 1, 1)
+ self.calc_pushButton = QtWidgets.QPushButton(t1dialog)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.calc_pushButton.setFont(font)
+ self.calc_pushButton.setObjectName("calc_pushButton")
+ self.gridLayout_2.addWidget(self.calc_pushButton, 5, 0, 1, 1)
+ self.label_9.setBuddy(self.lineEdit_2)
+ self.label_10.setBuddy(self.lineEdit_3)
+
+ self.retranslateUi(t1dialog)
+ QtCore.QMetaObject.connectSlotsByName(t1dialog)
+
+ def retranslateUi(self, t1dialog):
+ _translate = QtCore.QCoreApplication.translate
+ t1dialog.setWindowTitle(_translate("t1dialog", "Form"))
+ self.groupBox_1.setTitle(_translate("t1dialog", "Axes"))
+ self.t1_combobox.setItemText(0, _translate("t1dialog", "T1"))
+ self.t1_combobox.setItemText(1, _translate("t1dialog", "1/T1"))
+ self.temp_combobox.setItemText(0, _translate("t1dialog", "1000/T"))
+ self.temp_combobox.setItemText(1, _translate("t1dialog", "T"))
+ self.temp_combobox.setItemText(2, _translate("t1dialog", "1/T"))
+ self.groupBox_2.setTitle(_translate("t1dialog", "Frequency"))
+ self.freq_combox.setItemText(0, _translate("t1dialog", "MHz"))
+ self.freq_combox.setItemText(1, _translate("t1dialog", "kHz"))
+ self.freq_combox.setItemText(2, _translate("t1dialog", "Hz"))
+ self.groupBox.setTitle(_translate("t1dialog", "Model"))
+ self.groupBox_5.setTitle(_translate("t1dialog", "Pick T1 minimon"))
+ self.interpol_combobox.setItemText(0, _translate("t1dialog", "Data minimum"))
+ self.interpol_combobox.setItemText(1, _translate("t1dialog", "Parabola"))
+ self.interpol_combobox.setItemText(2, _translate("t1dialog", "Cubic spline"))
+ self.interpol_combobox.setItemText(3, _translate("t1dialog", "Akima spline"))
+ self.label_9.setText(_translate("t1dialog", "Interpolate"))
+ self.lineEdit_2.setPlaceholderText(_translate("t1dialog", "1 K"))
+ self.label_10.setText(_translate("t1dialog", "--"))
+ self.lineEdit_3.setPlaceholderText(_translate("t1dialog", "2000 K"))
+ self.label_11.setText(_translate("t1dialog", "Minimon"))
+ self.label_13.setText(_translate("t1dialog", "x value"))
+ self.label_12.setText(_translate("t1dialog", "y value"))
+ self.t1min_toolButton.setText(_translate("t1dialog", "Set"))
+ self.groupBox_3.setTitle(_translate("t1dialog", "Result"))
+ self.tau_combox.setStatusTip(_translate("t1dialog", "NOTE: Mean values are not available for all spectral densities. For more information ask someone."))
+ self.tau_combox.setItemText(0, _translate("t1dialog", "Fit parameter: τ"))
+ self.tau_combox.setItemText(1, _translate("t1dialog", "Peak time: τₚ"))
+ self.tau_combox.setItemText(2, _translate("t1dialog", "Arithmetic mean: ⟨τ⟩"))
+ self.tau_combox.setItemText(3, _translate("t1dialog", "Geometric mean: exp(⟨ln τ⟩)"))
+ self.checkBox_interpol.setText(_translate("t1dialog", "Use minimon interpolation"))
+ self.graph_checkbox.setText(_translate("t1dialog", "New graph?"))
+ self.label.setText(_translate("t1dialog", "Calculated minimon"))
+ self.calc_pushButton.setText(_translate("t1dialog", "Calculate"))
diff --git a/nmreval/gui_qt/_py/tntdialog.py b/nmreval/gui_qt/_py/tntdialog.py
new file mode 100644
index 0000000..d3754ec
--- /dev/null
+++ b/nmreval/gui_qt/_py/tntdialog.py
@@ -0,0 +1,138 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/tntdialog.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_tntdialog(object):
+ def setupUi(self, tntdialog):
+ tntdialog.setObjectName("tntdialog")
+ tntdialog.resize(373, 482)
+ self.gridLayout = QtWidgets.QGridLayout(tntdialog)
+ self.gridLayout.setObjectName("gridLayout")
+ self.widget_3 = QDelayWidget(tntdialog)
+ self.widget_3.setObjectName("widget_3")
+ self.gridLayout.addWidget(self.widget_3, 3, 1, 1, 2)
+ self.label_3 = QtWidgets.QLabel(tntdialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
+ self.label_3.setSizePolicy(sizePolicy)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout.addWidget(self.label_3, 0, 2, 1, 1)
+ self.widget = QDelayWidget(tntdialog)
+ self.widget.setObjectName("widget")
+ self.gridLayout.addWidget(self.widget, 1, 1, 1, 2)
+ self.label_2 = QtWidgets.QLabel(tntdialog)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout.addItem(spacerItem, 6, 1, 1, 1)
+ self.widget_2 = QDelayWidget(tntdialog)
+ self.widget_2.setObjectName("widget_2")
+ self.gridLayout.addWidget(self.widget_2, 2, 1, 1, 2)
+ self.buttonBox = QtWidgets.QDialogButtonBox(tntdialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 9, 1, 1, 2)
+ self.frame = QtWidgets.QFrame(tntdialog)
+ self.frame.setObjectName("frame")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.frame)
+ self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout_2.setSpacing(0)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.checkBox_2 = QtWidgets.QCheckBox(self.frame)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.checkBox_2.sizePolicy().hasHeightForWidth())
+ self.checkBox_2.setSizePolicy(sizePolicy)
+ self.checkBox_2.setObjectName("checkBox_2")
+ self.gridLayout_2.addWidget(self.checkBox_2, 3, 0, 1, 1)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.label_5 = QtWidgets.QLabel(self.frame)
+ self.label_5.setObjectName("label_5")
+ self.horizontalLayout_2.addWidget(self.label_5)
+ self.start_lineedit = QtWidgets.QLineEdit(self.frame)
+ self.start_lineedit.setObjectName("start_lineedit")
+ self.horizontalLayout_2.addWidget(self.start_lineedit)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem1)
+ self.label_6 = QtWidgets.QLabel(self.frame)
+ self.label_6.setObjectName("label_6")
+ self.horizontalLayout_2.addWidget(self.label_6)
+ self.end_lineedit = QtWidgets.QLineEdit(self.frame)
+ self.end_lineedit.setFrame(True)
+ self.end_lineedit.setObjectName("end_lineedit")
+ self.horizontalLayout_2.addWidget(self.end_lineedit)
+ self.gridLayout_2.addLayout(self.horizontalLayout_2, 1, 0, 1, 2)
+ self.label = QtWidgets.QLabel(self.frame)
+ self.label.setObjectName("label")
+ self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1)
+ self.lineEdit = QtWidgets.QLineEdit(self.frame)
+ self.lineEdit.setObjectName("lineEdit")
+ self.gridLayout_2.addWidget(self.lineEdit, 0, 1, 1, 1)
+ self.spinBox = QtWidgets.QSpinBox(self.frame)
+ self.spinBox.setObjectName("spinBox")
+ self.gridLayout_2.addWidget(self.spinBox, 3, 1, 1, 1)
+ self.checkBox = QtWidgets.QCheckBox(self.frame)
+ self.checkBox.setLayoutDirection(QtCore.Qt.LeftToRight)
+ self.checkBox.setObjectName("checkBox")
+ self.gridLayout_2.addWidget(self.checkBox, 4, 0, 1, 1)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setContentsMargins(25, -1, -1, -1)
+ self.horizontalLayout.setSpacing(0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.pushButton = QtWidgets.QPushButton(self.frame)
+ self.pushButton.setObjectName("pushButton")
+ self.horizontalLayout.addWidget(self.pushButton)
+ self.pushButton_2 = QtWidgets.QPushButton(self.frame)
+ self.pushButton_2.setObjectName("pushButton_2")
+ self.horizontalLayout.addWidget(self.pushButton_2)
+ self.gridLayout_2.addLayout(self.horizontalLayout, 4, 1, 1, 1)
+ self.gridLayout.addWidget(self.frame, 8, 1, 1, 2)
+ self.frame_2 = QtWidgets.QFrame(tntdialog)
+ self.frame_2.setObjectName("frame_2")
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.frame_2)
+ self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.label_4 = QtWidgets.QLabel(self.frame_2)
+ self.label_4.setObjectName("label_4")
+ self.horizontalLayout_3.addWidget(self.label_4)
+ self.unknown_delay_combobox = QtWidgets.QComboBox(self.frame_2)
+ self.unknown_delay_combobox.setObjectName("unknown_delay_combobox")
+ self.horizontalLayout_3.addWidget(self.unknown_delay_combobox)
+ self.gridLayout.addWidget(self.frame_2, 4, 1, 1, 2)
+
+ self.retranslateUi(tntdialog)
+ self.buttonBox.accepted.connect(tntdialog.accept)
+ self.buttonBox.rejected.connect(tntdialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(tntdialog)
+
+ def retranslateUi(self, tntdialog):
+ _translate = QtCore.QCoreApplication.translate
+ tntdialog.setWindowTitle(_translate("tntdialog", "Read tnt file"))
+ self.label_3.setText(_translate("tntdialog", "TextLabel"))
+ self.label_2.setText(_translate("tntdialog", "Dimensions"))
+ self.checkBox_2.setText(_translate("tntdialog", "Staggered range"))
+ self.label_5.setText(_translate("tntdialog", "Start"))
+ self.start_lineedit.setPlaceholderText(_translate("tntdialog", "0"))
+ self.label_6.setText(_translate("tntdialog", "End"))
+ self.end_lineedit.setPlaceholderText(_translate("tntdialog", "1"))
+ self.label.setText(_translate("tntdialog", "Name"))
+ self.checkBox.setToolTip(_translate("tntdialog", "NOTE: There is no inspection if start and end are valid values."))
+ self.checkBox.setText(_translate("tntdialog", "Logarithmic scale"))
+ self.pushButton.setText(_translate("tntdialog", "Apply"))
+ self.pushButton_2.setText(_translate("tntdialog", "Cancel"))
+ self.label_4.setText(_translate("tntdialog", "Unassigned lists"))
+from widgets.subwidgets import QDelayWidget
diff --git a/nmreval/gui_qt/_py/typeconversion.py b/nmreval/gui_qt/_py/typeconversion.py
new file mode 100644
index 0000000..fcfa751
--- /dev/null
+++ b/nmreval/gui_qt/_py/typeconversion.py
@@ -0,0 +1,139 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/typeconversion.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(839, 502)
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(Dialog)
+ self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_2.setSpacing(3)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.label_4 = QtWidgets.QLabel(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
+ self.label_4.setSizePolicy(sizePolicy)
+ self.label_4.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
+ self.label_4.setObjectName("label_4")
+ self.verticalLayout_2.addWidget(self.label_4)
+ self.splitter = QtWidgets.QSplitter(Dialog)
+ self.splitter.setOrientation(QtCore.Qt.Horizontal)
+ self.splitter.setObjectName("splitter")
+ self.set_list = QtWidgets.QListWidget(self.splitter)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.set_list.sizePolicy().hasHeightForWidth())
+ self.set_list.setSizePolicy(sizePolicy)
+ self.set_list.setDragEnabled(True)
+ self.set_list.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)
+ self.set_list.setObjectName("set_list")
+ self.verticalLayoutWidget = QtWidgets.QWidget(self.splitter)
+ self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
+ self.gridLayout = QtWidgets.QGridLayout(self.verticalLayoutWidget)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setHorizontalSpacing(0)
+ self.gridLayout.setVerticalSpacing(2)
+ self.gridLayout.setObjectName("gridLayout")
+ self.simple_button = QtWidgets.QRadioButton(self.verticalLayoutWidget)
+ self.simple_button.setChecked(True)
+ self.simple_button.setObjectName("simple_button")
+ self.buttonGroup = QtWidgets.QButtonGroup(Dialog)
+ self.buttonGroup.setObjectName("buttonGroup")
+ self.buttonGroup.addButton(self.simple_button)
+ self.gridLayout.addWidget(self.simple_button, 0, 0, 1, 1)
+ self.merge_button = QtWidgets.QRadioButton(self.verticalLayoutWidget)
+ self.merge_button.setObjectName("merge_button")
+ self.buttonGroup.addButton(self.merge_button)
+ self.gridLayout.addWidget(self.merge_button, 0, 1, 1, 1)
+ self.stackedWidget = QtWidgets.QStackedWidget(self.verticalLayoutWidget)
+ self.stackedWidget.setObjectName("stackedWidget")
+ self.page_1 = QtWidgets.QWidget()
+ self.page_1.setObjectName("page_1")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.page_1)
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_3.setSpacing(0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.simple_table = QtWidgets.QTableWidget(self.page_1)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.simple_table.sizePolicy().hasHeightForWidth())
+ self.simple_table.setSizePolicy(sizePolicy)
+ self.simple_table.setDragDropMode(QtWidgets.QAbstractItemView.DropOnly)
+ self.simple_table.setObjectName("simple_table")
+ self.simple_table.setColumnCount(2)
+ self.simple_table.setRowCount(0)
+ item = QtWidgets.QTableWidgetItem()
+ self.simple_table.setHorizontalHeaderItem(0, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.simple_table.setHorizontalHeaderItem(1, item)
+ self.simple_table.horizontalHeader().setStretchLastSection(True)
+ self.verticalLayout_3.addWidget(self.simple_table)
+ self.stackedWidget.addWidget(self.page_1)
+ self.page_2 = QtWidgets.QWidget()
+ self.page_2.setObjectName("page_2")
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.page_2)
+ self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_5.setSpacing(0)
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.merge_table = QtWidgets.QTableWidget(self.page_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.merge_table.sizePolicy().hasHeightForWidth())
+ self.merge_table.setSizePolicy(sizePolicy)
+ self.merge_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.merge_table.setDragDropMode(QtWidgets.QAbstractItemView.DropOnly)
+ self.merge_table.setDefaultDropAction(QtCore.Qt.CopyAction)
+ self.merge_table.setObjectName("merge_table")
+ self.merge_table.setColumnCount(3)
+ self.merge_table.setRowCount(0)
+ item = QtWidgets.QTableWidgetItem()
+ self.merge_table.setHorizontalHeaderItem(0, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.merge_table.setHorizontalHeaderItem(1, item)
+ item = QtWidgets.QTableWidgetItem()
+ self.merge_table.setHorizontalHeaderItem(2, item)
+ self.merge_table.horizontalHeader().setStretchLastSection(True)
+ self.verticalLayout_5.addWidget(self.merge_table)
+ self.stackedWidget.addWidget(self.page_2)
+ self.gridLayout.addWidget(self.stackedWidget, 1, 0, 1, 2)
+ self.verticalLayout_2.addWidget(self.splitter)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout_2.addWidget(self.buttonBox)
+
+ self.retranslateUi(Dialog)
+ self.stackedWidget.setCurrentIndex(0)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Change type"))
+ self.label_4.setText(_translate("Dialog", "Drag & drop datasets and select new type."))
+ self.simple_button.setText(_translate("Dialog", "Simple conversion"))
+ self.merge_button.setText(_translate("Dialog", "Merge to complex set"))
+ item = self.simple_table.horizontalHeaderItem(0)
+ item.setText(_translate("Dialog", "Set"))
+ item = self.simple_table.horizontalHeaderItem(1)
+ item.setText(_translate("Dialog", "Type"))
+ item = self.merge_table.horizontalHeaderItem(0)
+ item.setText(_translate("Dialog", "Real"))
+ item = self.merge_table.horizontalHeaderItem(1)
+ item.setText(_translate("Dialog", "Imag"))
+ item = self.merge_table.horizontalHeaderItem(2)
+ item.setText(_translate("Dialog", "Type"))
diff --git a/nmreval/gui_qt/_py/untitled.py b/nmreval/gui_qt/_py/untitled.py
new file mode 100644
index 0000000..4b991d4
--- /dev/null
+++ b/nmreval/gui_qt/_py/untitled.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/untitled.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_MainWindow(object):
+ def setupUi(self, MainWindow):
+ MainWindow.setObjectName("MainWindow")
+ MainWindow.resize(800, 600)
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.toolButton = QtWidgets.QToolButton(self.centralwidget)
+ self.toolButton.setGeometry(QtCore.QRect(0, 0, 31, 34))
+ self.toolButton.setObjectName("toolButton")
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 30))
+ self.menubar.setObjectName("menubar")
+ self.menuDfgdfg = QtWidgets.QMenu(self.menubar)
+ self.menuDfgdfg.setObjectName("menuDfgdfg")
+ MainWindow.setMenuBar(self.menubar)
+ self.statusbar = QtWidgets.QStatusBar(MainWindow)
+ self.statusbar.setObjectName("statusbar")
+ MainWindow.setStatusBar(self.statusbar)
+ self.toolBar = QtWidgets.QToolBar(MainWindow)
+ self.toolBar.setObjectName("toolBar")
+ MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
+ self.actionAsdasd = QtWidgets.QAction(MainWindow)
+ self.actionAsdasd.setObjectName("actionAsdasd")
+ self.menuDfgdfg.addAction(self.actionAsdasd)
+ self.menubar.addAction(self.menuDfgdfg.menuAction())
+ self.toolBar.addSeparator()
+
+ self.retranslateUi(MainWindow)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
+ self.toolButton.setText(_translate("MainWindow", "..."))
+ self.menuDfgdfg.setTitle(_translate("MainWindow", "dfgdfg"))
+ self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
+ self.actionAsdasd.setText(_translate("MainWindow", "asdasd"))
diff --git a/nmreval/gui_qt/_py/userfitassist.py b/nmreval/gui_qt/_py/userfitassist.py
new file mode 100644
index 0000000..c587927
--- /dev/null
+++ b/nmreval/gui_qt/_py/userfitassist.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/userfitassist.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(675, 682)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.label_3 = QtWidgets.QLabel(Dialog)
+ self.label_3.setIndent(4)
+ self.label_3.setObjectName("label_3")
+ self.verticalLayout.addWidget(self.label_3)
+ self.lineEdit_2 = QtWidgets.QLineEdit(Dialog)
+ self.lineEdit_2.setObjectName("lineEdit_2")
+ self.verticalLayout.addWidget(self.lineEdit_2)
+ self.label_4 = QtWidgets.QLabel(Dialog)
+ self.label_4.setIndent(4)
+ self.label_4.setObjectName("label_4")
+ self.verticalLayout.addWidget(self.label_4)
+ self.lineEdit_3 = QtWidgets.QLineEdit(Dialog)
+ self.lineEdit_3.setObjectName("lineEdit_3")
+ self.verticalLayout.addWidget(self.lineEdit_3)
+ self.label_2 = QtWidgets.QLabel(Dialog)
+ self.label_2.setIndent(4)
+ self.label_2.setObjectName("label_2")
+ self.verticalLayout.addWidget(self.label_2)
+ self.lineEdit = QtWidgets.QLineEdit(Dialog)
+ self.lineEdit.setText("")
+ self.lineEdit.setObjectName("lineEdit")
+ self.verticalLayout.addWidget(self.lineEdit)
+ self.parameterLabel = QtWidgets.QLabel(Dialog)
+ self.parameterLabel.setIndent(4)
+ self.parameterLabel.setObjectName("parameterLabel")
+ self.verticalLayout.addWidget(self.parameterLabel)
+ self.parameterLineEdit = QtWidgets.QLineEdit(Dialog)
+ self.parameterLineEdit.setObjectName("parameterLineEdit")
+ self.verticalLayout.addWidget(self.parameterLineEdit)
+ self.checkBox = QtWidgets.QCheckBox(Dialog)
+ self.checkBox.setObjectName("checkBox")
+ self.verticalLayout.addWidget(self.checkBox)
+ self.externalParametersLineEdit = QtWidgets.QLineEdit(Dialog)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.externalParametersLineEdit.sizePolicy().hasHeightForWidth())
+ self.externalParametersLineEdit.setSizePolicy(sizePolicy)
+ self.externalParametersLineEdit.setObjectName("externalParametersLineEdit")
+ self.verticalLayout.addWidget(self.externalParametersLineEdit)
+ self.checkBox_2 = QtWidgets.QCheckBox(Dialog)
+ self.checkBox_2.setObjectName("checkBox_2")
+ self.verticalLayout.addWidget(self.checkBox_2)
+ self.tableWidget = QtWidgets.QTableWidget(Dialog)
+ self.tableWidget.setObjectName("tableWidget")
+ self.tableWidget.setColumnCount(0)
+ self.tableWidget.setRowCount(0)
+ self.verticalLayout.addWidget(self.tableWidget)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem)
+ self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Save)
+ self.buttonBox.setObjectName("buttonBox")
+ self.verticalLayout.addWidget(self.buttonBox)
+ self.label_3.setBuddy(self.lineEdit_2)
+ self.label_4.setBuddy(self.lineEdit_3)
+ self.label_2.setBuddy(self.lineEdit)
+ self.parameterLabel.setBuddy(self.parameterLineEdit)
+
+ self.retranslateUi(Dialog)
+ self.buttonBox.accepted.connect(Dialog.accept)
+ self.buttonBox.rejected.connect(Dialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
+ self.label_3.setText(_translate("Dialog", "Name"))
+ self.lineEdit_2.setPlaceholderText(_translate("Dialog", "Name of function, e.g. Hopperbagger"))
+ self.label_4.setText(_translate("Dialog", "Group"))
+ self.lineEdit_3.setPlaceholderText(_translate("Dialog", "Type of function, e.g., Relaxation, Diffusion, Dredge,..."))
+ self.label_2.setText(_translate("Dialog", "Equation"))
+ self.lineEdit.setPlaceholderText(_translate("Dialog", "\\alpha + B*exp(x*C_{33}) + D"))
+ self.parameterLabel.setText(_translate("Dialog", "Parameters"))
+ self.parameterLineEdit.setPlaceholderText(_translate("Dialog", "\\alpha B C_{33}"))
+ self.checkBox.setText(_translate("Dialog", "Fixed parameter"))
+ self.externalParametersLineEdit.setPlaceholderText(_translate("Dialog", "D"))
+ self.checkBox_2.setText(_translate("Dialog", "Selection"))
+ self.buttonBox.setToolTip(_translate("Dialog", "Fit model is saved in myfitmodels.py and ready to use without restart."))
diff --git a/nmreval/gui_qt/_py/usermodeleditor.py b/nmreval/gui_qt/_py/usermodeleditor.py
new file mode 100644
index 0000000..5858c0b
--- /dev/null
+++ b/nmreval/gui_qt/_py/usermodeleditor.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/usermodeleditor.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_MainWindow(object):
+ def setupUi(self, MainWindow):
+ MainWindow.setObjectName("MainWindow")
+ MainWindow.resize(800, 600)
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.edit_field = CodeEditor(self.centralwidget)
+ font = QtGui.QFont()
+ font.setPointSize(10)
+ self.edit_field.setFont(font)
+ self.edit_field.setObjectName("edit_field")
+ self.verticalLayout.addWidget(self.edit_field)
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 30))
+ self.menubar.setObjectName("menubar")
+ self.menuFile = QtWidgets.QMenu(self.menubar)
+ self.menuFile.setObjectName("menuFile")
+ MainWindow.setMenuBar(self.menubar)
+ self.statusbar = QtWidgets.QStatusBar(MainWindow)
+ self.statusbar.setObjectName("statusbar")
+ MainWindow.setStatusBar(self.statusbar)
+ self.actionOpen = QtWidgets.QAction(MainWindow)
+ self.actionOpen.setObjectName("actionOpen")
+ self.actionSave = QtWidgets.QAction(MainWindow)
+ self.actionSave.setObjectName("actionSave")
+ self.actionSave_as = QtWidgets.QAction(MainWindow)
+ self.actionSave_as.setObjectName("actionSave_as")
+ self.actionClose = QtWidgets.QAction(MainWindow)
+ self.actionClose.setObjectName("actionClose")
+ self.menuFile.addAction(self.actionOpen)
+ self.menuFile.addAction(self.actionSave)
+ self.menuFile.addAction(self.actionSave_as)
+ self.menuFile.addAction(self.actionClose)
+ self.menubar.addAction(self.menuFile.menuAction())
+
+ self.retranslateUi(MainWindow)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "Editor"))
+ self.menuFile.setTitle(_translate("MainWindow", "File"))
+ self.actionOpen.setText(_translate("MainWindow", "Open..."))
+ self.actionSave.setText(_translate("MainWindow", "Save"))
+ self.actionSave_as.setText(_translate("MainWindow", "Save as..."))
+ self.actionClose.setText(_translate("MainWindow", "Close"))
+from ..lib.codeeditor import CodeEditor
diff --git a/nmreval/gui_qt/_py/valueeditor.py b/nmreval/gui_qt/_py/valueeditor.py
new file mode 100644
index 0000000..bfab40a
--- /dev/null
+++ b/nmreval/gui_qt/_py/valueeditor.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'resources/_ui/valueeditor.ui'
+#
+# Created by: PyQt5 UI code generator 5.12.3
+#
+# WARNING! All changes made in this file will be lost!
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_MaskDialog(object):
+ def setupUi(self, MaskDialog):
+ MaskDialog.setObjectName("MaskDialog")
+ MaskDialog.resize(383, 645)
+ self.verticalLayout = QtWidgets.QVBoxLayout(MaskDialog)
+ self.verticalLayout.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout.setSpacing(3)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.comboBox = QtWidgets.QComboBox(MaskDialog)
+ self.comboBox.setObjectName("comboBox")
+ self.verticalLayout.addWidget(self.comboBox)
+ self.comboBox_2 = QtWidgets.QComboBox(MaskDialog)
+ self.comboBox_2.setObjectName("comboBox_2")
+ self.verticalLayout.addWidget(self.comboBox_2)
+ self.tableView = QtWidgets.QTableView(MaskDialog)
+ self.tableView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.tableView.setObjectName("tableView")
+ self.verticalLayout.addWidget(self.tableView)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setContentsMargins(-1, 0, -1, -1)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(MaskDialog)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.spinBox = QtWidgets.QSpinBox(MaskDialog)
+ self.spinBox.setMinimum(1)
+ self.spinBox.setObjectName("spinBox")
+ self.horizontalLayout.addWidget(self.spinBox)
+ self.toolButton = QtWidgets.QToolButton(MaskDialog)
+ self.toolButton.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.toolButton.setAutoRaise(True)
+ self.toolButton.setArrowType(QtCore.Qt.RightArrow)
+ self.toolButton.setObjectName("toolButton")
+ self.horizontalLayout.addWidget(self.toolButton)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+ self.gridLayout = QtWidgets.QGridLayout()
+ self.gridLayout.setContentsMargins(-1, 0, -1, -1)
+ self.gridLayout.setSpacing(3)
+ self.gridLayout.setObjectName("gridLayout")
+ self.unmaskbutton = QtWidgets.QPushButton(MaskDialog)
+ self.unmaskbutton.setObjectName("unmaskbutton")
+ self.gridLayout.addWidget(self.unmaskbutton, 1, 0, 1, 1)
+ self.delete_button = QtWidgets.QPushButton(MaskDialog)
+ icon = QtGui.QIcon.fromTheme("list-remove")
+ self.delete_button.setIcon(icon)
+ self.delete_button.setObjectName("delete_button")
+ self.gridLayout.addWidget(self.delete_button, 0, 1, 1, 1)
+ self.add_button = QtWidgets.QPushButton(MaskDialog)
+ icon = QtGui.QIcon.fromTheme("list-add")
+ self.add_button.setIcon(icon)
+ self.add_button.setObjectName("add_button")
+ self.gridLayout.addWidget(self.add_button, 0, 0, 1, 1)
+ self.mask_button = QtWidgets.QPushButton(MaskDialog)
+ self.mask_button.setObjectName("mask_button")
+ self.gridLayout.addWidget(self.mask_button, 1, 1, 1, 1)
+ self.verticalLayout.addLayout(self.gridLayout)
+ self.label.setBuddy(self.spinBox)
+
+ self.retranslateUi(MaskDialog)
+ QtCore.QMetaObject.connectSlotsByName(MaskDialog)
+
+ def retranslateUi(self, MaskDialog):
+ _translate = QtCore.QCoreApplication.translate
+ MaskDialog.setWindowTitle(_translate("MaskDialog", "Form"))
+ self.label.setText(_translate("MaskDialog", "Go to line:"))
+ self.toolButton.setText(_translate("MaskDialog", "Go Go Power Rangers!"))
+ self.unmaskbutton.setText(_translate("MaskDialog", "Show all"))
+ self.delete_button.setText(_translate("MaskDialog", "Delete rows"))
+ self.add_button.setText(_translate("MaskDialog", "Add row"))
+ self.mask_button.setToolTip(_translate("MaskDialog", "Masked rows are shown in green
"))
+ self.mask_button.setText(_translate("MaskDialog", "Hide/Show selected"))
diff --git a/nmreval/gui_qt/data/__init__.py b/nmreval/gui_qt/data/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nmreval/gui_qt/data/container.py b/nmreval/gui_qt/data/container.py
new file mode 100644
index 0000000..7027698
--- /dev/null
+++ b/nmreval/gui_qt/data/container.py
@@ -0,0 +1,651 @@
+from collections import OrderedDict
+from itertools import cycle
+from typing import Any
+
+import numpy as np
+from pyqtgraph import mkPen
+
+from ...data.points import Points
+from ...data.signals import Signal
+from ...utils.text import convert
+from ...data.bds import BDS
+from ...lib.colors import Colors
+from ...lib.lines import LineStyle
+from ...lib.symbols import SymbolStyle, symbolcycle
+from ...data.nmr import Spectrum, FID
+
+from ..Qt import QtCore
+from ..io.exporters import pgitem_to_dic
+from ..lib.decorators import plot_update
+from ..lib.pg_objects import ErrorBars, PlotItem
+
+
+class ExperimentContainer(QtCore.QObject):
+ dataChanged = QtCore.pyqtSignal(str)
+ labelChanged = QtCore.pyqtSignal(str, str)
+ groupChanged = QtCore.pyqtSignal(str, str)
+ colors = cycle(Colors)
+
+ def __init__(self, identifier, data, **kwargs):
+ super().__init__()
+ self.id = str(identifier)
+
+ self._fits = []
+ self._data = data
+ self._manager = kwargs.get('manager')
+
+ self.mode = 'point'
+ self.plot_real = None
+ self.plot_imag = None
+ self.plot_error = None
+
+ self.actions = {}
+ self._update_actions()
+
+ def _init_plot(self):
+ raise NotImplementedError
+
+ def __getitem__(self, item):
+ try:
+ return self._data[item]
+ except KeyError:
+ raise KeyError('Unknown key %s' % str(item))
+
+ def __del__(self):
+ del self._data
+ del self.plot_real
+ del self.plot_imag
+ del self.plot_error
+
+ def __repr__(self):
+ return 'name:' + self.name
+
+ def __len__(self):
+ return len(self._data)
+
+ def copy(self, full: bool = False):
+ if full:
+ new_data = type(self)(str(self.id), self._data.copy(), manager=self._manager)
+ new_data.mode = self.mode
+
+ for src, dest in [(self.plot_real, new_data.plot_real), (self.plot_imag, new_data.plot_imag)]:
+ if src is not None:
+ dest.set_symbol(symbol=src.symbol, size=src.symbolsize, color=src.symbolcolor)
+ dest.set_line(style=src.linestyle, width=src.linewidth, color=src.linecolor)
+
+ return new_data
+
+ else:
+ return self._data.copy()
+
+ def change_type(self, data):
+ if isinstance(data, (FID, Spectrum, BDS)):
+ new_type = SignalContainer
+ elif isinstance(data, Points):
+ new_type = PointContainer
+ else:
+ raise TypeError('Unknown data type')
+
+ new_data = new_type(str(self.id), data, manager=self._manager)
+
+ for src, dest in [(self.plot_real, new_data.plot_real), (self.plot_imag, new_data.plot_imag)]:
+ if dest is not None:
+ if src is not None:
+ dest.set_symbol(symbol=src.symbol, size=src.symbolsize, color=src.symbolcolor)
+ dest.set_line(style=src.linestyle, width=src.linewidth, color=src.linecolor)
+ else:
+ dest.set_symbol(symbol=self.plot_real.symbol, size=self.plot_real.symbolsize, color=self.plot_real.symbolcolor)
+ dest.set_line(style=self.plot_real.linestyle, width=self.plot_real.linewidth, color=self.plot_real.linecolor)
+
+ return new_data
+
+ @property
+ def x(self):
+ return self._data.x[self._data.mask]
+
+ @x.setter
+ @plot_update
+ def x(self, value):
+ if len(self._data.x) == len(value):
+ self._data.x = value
+ elif len(self._data.x[self._data.mask]) == len(value):
+ self._data.x = value
+ self._data.y = self._data.y[self._data.mask]
+ self._data.mask = np.ma.array(np.ones_like(self._data.x, dtype=bool))
+ else:
+ raise ValueError('x and y have different dimensions!')
+
+ @property
+ def y(self):
+ return self._data.y[self._data.mask]
+
+ @y.setter
+ @plot_update
+ def y(self, value):
+ if len(self._data.y) == len(value):
+ self._data.y = value
+ elif len(self._data.y[self._data.mask]) == len(value):
+ self._data.y = value
+ self._data.x = self._data.x[self._data.mask]
+ self._data.mask = np.ma.array(np.ones_like(self._data.y, dtype=bool))
+ else:
+ raise ValueError('x and y have different dimensions!')
+
+ @property
+ def y_err(self):
+ return self._data.y_err[self._data.mask]
+
+ @y_err.setter
+ @plot_update
+ def y_err(self, value):
+ if len(self._data.y_err) == len(value):
+ self._data.y_err = value
+ elif len(self._data.y[self._data.mask]) == len(value):
+ self._data.y_err[self._data.mask] = value
+ else:
+ raise ValueError('y_err has not correct length')
+
+ @property
+ def name(self):
+ return self._data.name
+
+ @name.setter
+ @plot_update
+ def name(self, value: str):
+ self._data.name = value
+ self.plot_real.opts['name'] = value
+ try:
+ self.plot_imag.opts['name'] = value
+ except AttributeError:
+ pass
+ try:
+ num_val = float(value)
+ self._data.value = num_val
+ except ValueError:
+ pass
+
+ @property
+ def value(self):
+ return self._data.value
+
+ @value.setter
+ def value(self, val):
+ self._data.value = float(val)
+
+ @property
+ def group(self):
+ return str(self._data['group'])
+
+ @group.setter
+ def group(self, valium):
+ self._data['group'] = str(valium)
+ self.groupChanged.emit(self.id, str(valium))
+
+ @property
+ def data(self):
+ return self._data
+
+ @data.setter
+ @plot_update
+ def data(self, new_data):
+ self._data = new_data
+ self._update_actions()
+
+ @property
+ def opts(self):
+ return self._data.meta
+
+ @property
+ def plots(self):
+ return self.plot_real, self.plot_imag, self.plot_error
+
+ def get_state(self):
+ ret_dic = {
+ 'id': self.id,
+ 'data': self._data.get_state(),
+ 'mode': self.mode,
+ 'fits': self._fits,
+ 'real': ({'symbol': self.plot_real.symbol.value,
+ 'size': self.plot_real.symbolsize,
+ 'color': self.plot_real.symbolcolor.value},
+ {'style': self.plot_real.linestyle.value,
+ 'width': self.plot_real.linewidth,
+ 'color': self.plot_real.linecolor.value})
+ }
+
+ if self.plot_imag is not None:
+ ret_dic['imag'] = ({'symbol': self.plot_imag.symbol.value,
+ 'size': self.plot_imag.symbolsize,
+ 'color': self.plot_imag.symbolcolor.value},
+ {'style': self.plot_imag.linestyle.value,
+ 'width': self.plot_imag.linewidth,
+ 'color': self.plot_imag.linecolor.value})
+
+ return ret_dic
+
+ def get_fits(self):
+ return [self._manager[idx] for idx in self._fits]
+
+ def has_fits(self):
+ return len(self._fits) != 0
+
+ def set_fits(self, value: str or list, replace: bool = False):
+ if isinstance(value, str):
+ value = [value]
+
+ if replace:
+ if isinstance(value, list):
+ self._fits = value
+ else:
+ raise TypeError()
+ else:
+ self._fits.extend(value)
+
+ def _update_actions(self):
+ self.actions.update({'sort': self._data.sort,
+ 'cut': self._data.cut,
+ 'norm': self._data.normalize,
+ 'center': self.center})
+
+ @plot_update
+ def update(self, opts: dict):
+ self._data.update(opts)
+
+ def get_properties(self) -> dict:
+ props = OrderedDict()
+ props['General'] = OrderedDict([('Name', self.name), ('Value', str(self.value)), ('Group', str(self.group))])
+ props['Symbol'] = OrderedDict()
+ props['Line'] = OrderedDict()
+
+ props['Symbol']['Symbol'] = self.plot_real.symbol
+ props['Symbol']['Size'] = self.plot_real.symbolsize
+ props['Symbol']['Color'] = self.plot_real.symbolcolor
+
+ props['Line']['Style'] = self.plot_real.linestyle
+ props['Line']['Width'] = self.plot_real.linewidth
+ props['Line']['Color'] = self.plot_real.linecolor
+
+ if self.plot_imag is not None:
+ props['Symbol']['Symbol (imag)'] = self.plot_imag.symbol
+ props['Symbol']['Size (imag)'] = self.plot_imag.symbolsize
+ props['Symbol']['Color (imag)'] = self.plot_imag.symbolcolor
+
+ props['Line']['Style (imag)'] = self.plot_imag.linestyle
+ props['Line']['Width (imag)'] = self.plot_imag.linewidth
+ props['Line']['Color (imag)'] = self.plot_imag.linecolor
+
+ return props
+
+ def setColor(self, color, symbol=False, line=False, mode='real'):
+ if mode == 'real':
+ self.plot_real.set_color(color, symbol=symbol, line=line)
+ if self.plot_real.symbol != SymbolStyle.No and symbol:
+ err_pen = self.plot_error.pen
+ err_pen.setColor(self.plot_real.symbolcolor.rbg())
+ self.plot_error.setData(pen=err_pen)
+ elif line:
+ err_pen = self.plot_error.pen
+ err_pen.setColor(self.plot_real.linecolor.rbg())
+ self.plot_error.setData(pen=err_pen)
+
+ elif mode == 'imag' and self.plot_imag is not None:
+ 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'):
+ if mode == 'real':
+ self.plot_real.set_symbol(symbol=symbol, size=size, color=color)
+ elif mode == 'imag' and self.plot_imag is not None:
+ self.plot_imag.set_symbol(symbol=symbol, size=size, color=color)
+ else:
+ print('Updating symbol failed for ' + str(self.id))
+
+ def setLine(self, width=None, style=None, color=None, mode='real'):
+ if mode == 'real':
+ self.plot_real.set_line(width=width, style=style, color=color)
+ elif mode == 'imag' and self.plot_imag is not None:
+ self.plot_imag.set_line(width=width, style=style, color=color)
+ else:
+ print('Updating line failed for ' + str(self.id))
+
+ def update_property(self, key1: str, key2: str, value: Any):
+ keykey = key2.split()
+ if len(keykey) == 1:
+ if key1 == 'Symbol':
+ self.setSymbol(mode='real', **{key2.lower(): value})
+
+ elif key1 == 'Line':
+ self.setLine(mode='real', **{key2.lower(): value})
+
+ elif key1 == 'General':
+ setattr(self, key2.lower(), value)
+
+ else:
+ if key1 == 'Symbol':
+ self.setSymbol(mode='imag', **{keykey[0].lower(): value})
+
+ elif key1 == 'Line':
+ self.setLine(mode='imag', **{keykey[0].lower(): value})
+
+ def points(self, params: dict):
+ return self._data.points(**params)
+
+ @plot_update
+ def apply(self, func: str, args: tuple):
+ if func in self.actions:
+ f = self.actions[func]
+ f(*args)
+
+ return self
+
+ @plot_update
+ def unsort(self, order: np.ndarray):
+ # this exists only to update plots after an undo action
+ self._data.x = self._data.x[order]
+ self._data.y = self._data.y[order]
+ self._data.y_err = self._data.y_err[order]
+ self._data.mask = self._data.mask[order]
+
+ def save(self, fname):
+ ext = fname.suffix
+ if ext == '.agr':
+ real_dic = pgitem_to_dic(self.plot_real)
+ from ..io.exporters import GraceExporter
+
+ GraceExporter(real_dic).export(fname)
+
+ elif ext in ['.dat', '.txt']:
+ self._data.savetxt(fname, err=True)
+
+ else:
+ raise ValueError('Unknown extension ' + ext)
+
+ @plot_update
+ def setvalues(self, pos, valium):
+ xy, position = pos
+ if xy == 0:
+ self._data.x[position] = valium
+ elif xy == 1:
+ self._data.y[position] = valium
+ else:
+ self._data.y_err[position] = valium
+
+ @property
+ def mask(self):
+ return self._data.mask
+
+ @mask.setter
+ @plot_update
+ def mask(self, m):
+ self._data.mask = np.asarray(m, dtype=bool)
+
+ @plot_update
+ def add(self, m):
+ if isinstance(m, (np.ndarray, list, tuple)):
+ self._data.append(m[0], m[1], y_err=m[2])
+ elif isinstance(m, (Points, ExperimentContainer)):
+ self._data.append(m.x, m.y, y_err=m.y_err)
+ else:
+ raise TypeError('Unknown type ' + type(m))
+
+ @plot_update
+ def remove(self, m):
+ self._data.remove(m)
+
+ @plot_update
+ def center(self) -> float:
+ offset = self.x[np.argmax(self.y.real)]
+ self._data._x -= offset
+
+ return offset
+
+ def get_namespace(self, i: int = None, j: int = None) -> dict:
+ if (i is None) and (j is None):
+ prefix = ''
+ else:
+ prefix = 'g[%i].s[%i].' % (i, j)
+
+ namespace = {prefix + 'x': (self.x, 'x values'),
+ prefix + 'y': [self.y, 'y values'],
+ prefix + 'y_err': (self.y_err, 'y error values'),
+ prefix + 'value': (self.value, str(self.value))}
+
+ if len(self._fits) == 1:
+ namespace.update({
+ "%sfit['%s']" % (prefix, convert(pname, old='tex', new='str')): (pvalue.value, str(pvalue.value))
+ for (pname, pvalue) in self._manager[self._fits[0]].parameter.items()
+ })
+ else:
+ for k, f in enumerate(self._fits):
+ namespace.update({
+ "%sfit['%s_%d']" % (prefix, convert(pname, old='tex', new='str'), k): (pvalue.value, str(pvalue.value))
+ for (pname, pvalue) in self._manager[f].parameter.items()
+ })
+
+ return namespace
+
+ def eval_expression(self, cmds, namespace):
+ namespace.update({'x': self.x, 'y': self.y, 'y_err': self.y_err, 'value': self.value})
+
+ if len(self._fits) == 1:
+ namespace.update({"fit['%s']" % (convert(pname, old='tex', new='str')): pvalue.value
+ for (pname, pvalue) in self._manager[self._fits[0]].parameter.items()})
+ else:
+ for k, f in enumerate(self._fits):
+ namespace.update({"fit['%s_%i']" % (convert(pname, old='tex', new='str'), k): pvalue.value
+ for (pname, pvalue) in self._manager[f].parameter.items()})
+
+ new_data = self.copy()
+ for c in cmds:
+ if c:
+ exec(c, globals(), namespace)
+
+ new_data.set_data(x=namespace['x'], y=namespace['y'], y_err=namespace['y_err'])
+ new_data.value = namespace['value']
+
+ return new_data
+
+
+class PointContainer(ExperimentContainer):
+ symbols = symbolcycle()
+
+ def __init__(self, identifier, data, **kwargs):
+ super().__init__(identifier, data, **kwargs)
+
+ self.mode = 'pts'
+ self._init_plot(**kwargs)
+
+ def _init_plot(self, **kwargs):
+ self.plot_imag = None
+
+ color = kwargs.get('color', None)
+ symcolor = kwargs.get('symbolcolor', color)
+ linecolor = kwargs.get('linecolor', color)
+
+ if symcolor is None and linecolor is None:
+ color = next(self.colors)
+ symcolor = color
+ linecolor = color
+ elif symcolor is None:
+ symcolor = linecolor
+ elif linecolor is None:
+ linecolor = symcolor
+
+ sym_kwargs = {
+ 'symbol': kwargs.get('symbol', None),
+ 'size': kwargs.get('symbolsize', 10),
+ 'color': symcolor
+ }
+
+ line_kwargs = {
+ 'style': kwargs.get('linestyle', None),
+ 'width': kwargs.get('linewidth', 1),
+ 'color': linecolor
+ }
+
+ if sym_kwargs['symbol'] is None and line_kwargs['style'] is None:
+ if len(self._data) > 1000:
+ line_kwargs['style'] = LineStyle.Solid
+ sym_kwargs['symbol'] = SymbolStyle.No
+ else:
+ line_kwargs['style'] = LineStyle.No
+ sym_kwargs['symbol'] = next(PointContainer.symbols)
+
+ self.plot_real = PlotItem(x=self._data.x, y=self._data.y, name=self.name,
+ symbol=None, pen=None, connect='finite')
+
+ self.setSymbol(mode='real', **sym_kwargs)
+ self.setLine(mode='real', **line_kwargs)
+
+ 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,
+ pen=mkPen({'color': self.plot_real.symbolcolor.rgb()}))
+ else:
+ self.plot_error = ErrorBars(x=self._data.x, y=self._data.y, top=self._data.y_err, bottom=self._data.y_err,
+ pen=mkPen({'color': self.plot_real.linecolor.rgb()}))
+
+ def update_property(self, key1: str, key2: str, value: Any):
+ # update default color
+ if key1 == 'Symbol' and key2 == 'Color':
+ self.plot_real.set_color(value, symbol=True, line=False)
+ super().update_property(key1, key2, value)
+
+
+class FitContainer(ExperimentContainer):
+ def __init__(self, identifier, data, **kwargs):
+ super().__init__(identifier, data, **kwargs)
+ self.fitted_key = kwargs.get('src', '')
+ self.mode = 'fit'
+ self.parent_set = kwargs.get('src', '')
+
+ self._init_plot(**kwargs)
+
+ for n in ['statistics', 'nobs', 'nvar', 'parameter', 'model_name']:
+ setattr(self, n, getattr(data, n))
+
+ def _init_plot(self, **kwargs):
+ color = kwargs.get('color', (0, 0, 0))
+ if isinstance(color, Colors):
+ color = color.rgb()
+
+ self.plot_real = PlotItem(x=self._data.x, y=self._data.y.real, name=self.name,
+ pen=mkPen({'color': color}),
+ connect='finite', symbol=None)
+
+ if np.iscomplexobj(self._data.y):
+ self.plot_imag = PlotItem(x=self._data.x, y=self._data.y.imag, name=self.name,
+ pen=mkPen({'color': color}),
+ connect='finite', symbol=None)
+
+ @property
+ def fitted_key(self):
+ return self._data.idx
+
+ @fitted_key.setter
+ def fitted_key(self, val):
+ self._data.idx = val
+
+ def get_namespace(self, i: int = None, j: int = None):
+ namespace = super().get_namespace(i, j)
+
+ namespace.update({
+ "g[%i].s[%i].fit['%s']" % (i, j, convert(pname, old='latex', new='plain')): (pvalue.value, str(pvalue.value))
+ for (pname, pvalue) in self._data.parameter.items()
+ })
+
+ return namespace
+
+
+class SignalContainer(ExperimentContainer):
+ symbols = symbolcycle()
+
+ def __init__(self, identifier, data, symbol=None, **kwargs):
+ super().__init__(identifier, data, **kwargs)
+
+ self.mode = 'signal'
+ self._init_plot(symbol=symbol, **kwargs)
+
+ def _init_plot(self, **kwargs):
+ self.plot_real = PlotItem(x=self._data.x, y=self._data.y.real, name=self.name,
+ symbol=None, pen=None, connect='finite')
+ self.plot_imag = PlotItem(x=self._data.x, y=self._data.y.imag, name=self.name,
+ symbol=None, pen=None, connect='finite')
+
+ color = kwargs.get('color', None)
+ symcolor = kwargs.get('symbolcolor', color)
+ linecolor = kwargs.get('linecolor', color)
+
+ if symcolor is None and linecolor is None:
+ color = next(self.colors)
+ symcolor = color
+ linecolor = color
+ elif symcolor is None:
+ symcolor = linecolor
+ elif linecolor is None:
+ linecolor = symcolor
+
+ sym_kwargs = {
+ 'symbol': kwargs.get('symbol', None),
+ 'size': kwargs.get('symbolsize', 10),
+ 'color': symcolor
+ }
+
+ line_kwargs = {
+ 'style': kwargs.get('linestyle', None),
+ 'width': kwargs.get('linewidth', 1),
+ 'color': linecolor
+ }
+
+ if isinstance(self._data, BDS):
+ self.mode = 'bds'
+
+ if sym_kwargs['symbol'] is None and line_kwargs['style'] is None:
+ sym_kwargs['symbol'] = next(PointContainer.symbols)
+ line_kwargs['style'] = LineStyle.No
+
+ elif isinstance(self._data, Signal):
+ if line_kwargs['style'] is None and sym_kwargs['symbol'] is None:
+ line_kwargs['style'] = LineStyle.Solid
+ sym_kwargs['symbol'] = SymbolStyle.No
+
+ if isinstance(self._data, FID):
+ self.mode = 'fid'
+ else:
+ self.mode = 'spectrum'
+ else:
+ raise TypeError('Unknown class %s, should be FID, Spectrum, or BDS.' % type(self._data))
+
+ for mode in ['real', 'imag']:
+ if mode == 'imag':
+ line_kwargs['style'] = LineStyle.Dashed
+ self.setSymbol(mode=mode, **sym_kwargs)
+ self.setLine(mode=mode, **line_kwargs)
+
+ def _update_actions(self):
+ super()._update_actions()
+
+ self.actions.update({'ph': self._data.manual_phase, 'bls': self._data.baseline_spline})
+ if isinstance(self._data, Spectrum):
+ self.actions.update({'bl': self._data.baseline, 'ls': self._data.shift,
+ 'divide': self._data.divide, 'ft': self.fourier})
+ self.mode = 'spectrum'
+
+ elif isinstance(self._data, FID):
+ self.actions.update({'bl': self._data.baseline, 'ls': self._data.shift,
+ 'zf': self._data.zerofill, 'divide': self._data.divide,
+ 'ap': self._data.apod, 'ft': self.fourier})
+ self.mode = 'fid'
+
+ @plot_update
+ def fourier(self, mode='normal'):
+ if mode == 'normal':
+ self._data = self._data.fourier()
+ elif mode == 'depake':
+ try:
+ self._data = self._data.fft_depake()
+ except AttributeError:
+ return
+ self._update_actions()
+
+ return self
diff --git a/nmreval/gui_qt/data/conversion.py b/nmreval/gui_qt/data/conversion.py
new file mode 100644
index 0000000..dbcb0b8
--- /dev/null
+++ b/nmreval/gui_qt/data/conversion.py
@@ -0,0 +1,171 @@
+from nmreval.gui_qt.Qt import QtWidgets, QtCore, QtGui
+from nmreval.gui_qt._py.typeconversion import Ui_Dialog
+
+
+class ConversionDialog(QtWidgets.QDialog, Ui_Dialog):
+ convertSets = QtCore.pyqtSignal(list)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.simple_table.installEventFilter(self)
+ self.merge_table.installEventFilter(self)
+
+ self._simple_table_dropevent = self.simple_table.dropEvent
+ self.simple_table.dropEvent = self._drop_on_simple_table
+
+ self._merge_table_dropevent = self.merge_table.dropEvent
+ self.merge_table.dropEvent = self._drop_on_simple_table
+
+ self.buttonGroup.buttonClicked.connect(self.change_table)
+
+ def set_graphs(self, graphs: dict):
+ self.set_list.clear()
+ self.simple_table.clear()
+ self.simple_table.setHorizontalHeaderLabels(['Simple', 'Type'])
+ self.merge_table.clear()
+ self.merge_table.setHorizontalHeaderLabels(['Real', 'Imag', 'Type'])
+
+ for graph, datasets in graphs.items():
+ for set_id, set_name in datasets:
+ item_name = set_name + ' (' + graph[1] + ')'
+
+ item1 = QtWidgets.QListWidgetItem(item_name)
+ item1.setData(QtCore.Qt.UserRole, set_id)
+ item1.setData(QtCore.Qt.UserRole+1, graph[0])
+ self.set_list.addItem(item1)
+
+ def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent):
+ if evt.type() in [QtCore.QEvent.DragEnter, QtCore.QEvent.DragMove]:
+ evt.accept()
+ return True
+
+ if evt.type() == QtCore.QEvent.KeyPress and evt.key() == QtCore.Qt.Key_Delete:
+ if src == self.simple_table:
+ type_idx = 1
+ else:
+ type_idx = 2
+
+ if len(src.selectedIndexes()) > 0:
+ idx = src.selectedIndexes()[0]
+ row, col = idx.row(), idx.column()
+
+ if col != type_idx:
+ src.takeItem(row, col)
+ is_empty = all(src.item(row, i) is None for i in range(type_idx))
+ if is_empty:
+ src.removeRow(row)
+
+ return True
+
+ return super().eventFilter(src, evt)
+
+ def _drop_on_simple_table(self, evt: QtGui.QDropEvent):
+ """
+ event filter does not receive dropevents?
+ """
+ if self.stackedWidget.currentIndex() == 0:
+ table = self.simple_table
+ type_column = 1
+ default_drop = self._simple_table_dropevent
+ type_name = ['Points', 'FID', 'Spectrum', 'BDS']
+ else:
+ table = self.merge_table
+ type_column = 2
+ default_drop = self._merge_table_dropevent
+ type_name = ['FID', 'Spectrum', 'BDS']
+
+ pos = evt.pos()
+ drop_col = table.columnAt(pos.x())
+ if drop_col == type_column:
+ evt.ignore()
+ else:
+ drop_row = table.rowAt(pos.y())
+ default_drop(evt)
+
+ if drop_row == -1:
+ w = QtWidgets.QComboBox()
+ w.addItems(type_name)
+ item = QtWidgets.QTableWidgetItem('')
+ drop_row = table.rowAt(pos.y())
+ table.setItem(drop_row, type_column, item)
+ table.setCellWidget(drop_row, type_column, w)
+
+ item = table.item(drop_row, drop_col)
+ idx = table.indexFromItem(item)
+
+ if idx.row() == -1 and idx.column() == -1:
+ item = table.takeItem(drop_row, 0)
+ table.setItem(drop_row, drop_col, item)
+
+ if item is not None:
+ item.setToolTip(item.text())
+
+ table.resizeColumnsToContents()
+
+ @QtCore.pyqtSlot(QtWidgets.QAbstractButton)
+ def change_table(self, button: QtWidgets.QAbstractButton):
+ idx = [self.simple_button, self.merge_button].index(button)
+ self.stackedWidget.setCurrentIndex(idx)
+
+ def collect_args(self) -> list:
+ src_sets = []
+ for row in range(self.simple_table.rowCount()):
+ item = self.simple_table.item(row, 0)
+ set_id = item.data(QtCore.Qt.UserRole)
+ graph_id = item.data(QtCore.Qt.UserRole+1)
+ type_idx = self.simple_table.cellWidget(row, 1).currentIndex()
+
+ src_sets.append((set_id, graph_id, type_idx))
+
+ for row in range(self.merge_table.rowCount()):
+ item = self.merge_table.item(row, 0)
+ graph_id = ''
+ if item is not None:
+ set_id_real = item.data(QtCore.Qt.UserRole)
+ graph_id = item.data(QtCore.Qt.UserRole+1)
+ else:
+ set_id_real = ''
+
+ item = self.merge_table.item(row, 1)
+ if item is not None:
+ set_id_imag = item.data(QtCore.Qt.UserRole)
+ graph_id = item.data(QtCore.Qt.UserRole+1) if graph_id == '' else graph_id
+ else:
+ set_id_imag = ''
+ type_idx = self.merge_table.cellWidget(row, 2).currentIndex() + 1
+
+ src_sets.append((set_id_real, set_id_imag, graph_id, type_idx))
+
+ print(src_sets)
+
+ self.convertSets.emit(src_sets)
+
+ return src_sets
+
+ @QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonBox_clicked')
+ def button_clicked(self, button):
+ role = self.buttonBox.buttonRole(button)
+ if role == self.buttonBox.RejectRole:
+ self.close()
+ else:
+ self.collect_args()
+ self.accept()
+
+
+if __name__ == '__main__':
+ import sys
+ from collections import OrderedDict
+
+ app = QtWidgets.QApplication(sys.argv)
+ d = ConversionDialog()
+
+ data = OrderedDict([
+ (('1', 'Graph 0'), [('a', 'Das Sein und das Nichts'), ('b', 'b'), ('c', 'c'), ('d', 'd')]),
+ (('2', 'Graph 2'), [('e', 'e'), ('f', 'f'), ('g', 'g'), ('h', 'h')])])
+
+ d.set_graphs(data)
+ d.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/data/datawidget/__init__.py b/nmreval/gui_qt/data/datawidget/__init__.py
new file mode 100644
index 0000000..009a5fb
--- /dev/null
+++ b/nmreval/gui_qt/data/datawidget/__init__.py
@@ -0,0 +1 @@
+from .datawidget import *
diff --git a/nmreval/gui_qt/data/datawidget/datawidget.py b/nmreval/gui_qt/data/datawidget/datawidget.py
new file mode 100644
index 0000000..61311bf
--- /dev/null
+++ b/nmreval/gui_qt/data/datawidget/datawidget.py
@@ -0,0 +1,483 @@
+from typing import List, Union
+
+from .properties import PropWidget
+from ...Qt import QtWidgets, QtGui, QtCore
+from ..._py.datawidget import Ui_DataWidget
+from ...lib import make_action_icons
+from ...lib.delegates import HeaderDelegate
+
+
+class DataTree(QtWidgets.QTreeWidget):
+ stateChanged = QtCore.pyqtSignal(list, list) # selected, deselected
+ keyChanged = QtCore.pyqtSignal(str, str) # id, text
+ positionChanged = QtCore.pyqtSignal(QtWidgets.QTreeWidgetItem)
+ deleteItem = QtCore.pyqtSignal(list)
+ moveItem = QtCore.pyqtSignal(list, str, str, int) # items, from, to, new row
+ copyItem = QtCore.pyqtSignal(list, str)
+ saveFits = QtCore.pyqtSignal(list)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setColumnCount(1)
+ self.invisibleRootItem().setFlags(self.invisibleRootItem().flags() ^ QtCore.Qt.ItemIsDropEnabled)
+
+ self.itemChanged.connect(self.data_change)
+
+ self.setColumnCount(2)
+
+ self.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
+ self.setDragDropMode(QtWidgets.QTreeView.InternalMove)
+ self.setDefaultDropAction(QtCore.Qt.IgnoreAction)
+ self.setSelectionMode(QtWidgets.QTreeView.ExtendedSelection)
+ self.setSelectionBehavior(QtWidgets.QTreeView.SelectRows)
+
+ self._checked_graphs = set()
+ self._checked_sets = set()
+ self.management = None
+
+ header = QtWidgets.QHeaderView(QtCore.Qt.Horizontal, self)
+ self.setHeader(header)
+ header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
+ header.setVisible(False)
+ header.moveSection(1, 0)
+ self.setColumnWidth(1, 16)
+ self.setItemDelegateForColumn(1, HeaderDelegate())
+
+ def add_graph(self, idd: str, name: str):
+ item = QtWidgets.QTreeWidgetItem()
+ item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDropEnabled | QtCore.Qt.ItemIsEditable |
+ QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable)
+ item.setText(0, name)
+ item.setData(0, QtCore.Qt.UserRole, idd)
+ item.setCheckState(0, QtCore.Qt.Checked)
+
+ self.addTopLevelItem(item)
+ self._checked_graphs.add(idd)
+ item.setExpanded(True)
+
+ def add_item(self, items: Union[tuple, List[tuple]], gid: str):
+ if isinstance(items, tuple):
+ items = [items]
+
+ for row in range(self.invisibleRootItem().childCount()):
+ graph = self.invisibleRootItem().child(row)
+ if graph.data(0, QtCore.Qt.UserRole) == gid:
+ for (idd, name) in items:
+ item = QtWidgets.QTreeWidgetItem([name])
+ item.setData(0, QtCore.Qt.UserRole, idd)
+ item.setCheckState(0, QtCore.Qt.Checked)
+ item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsEditable |
+ QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable)
+ graph.addChild(item)
+ self._checked_sets.add(idd)
+
+ self.resizeColumnToContents(0)
+ break
+
+ @QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem)
+ def data_change(self, item: QtWidgets.QTreeWidgetItem) -> (list, list):
+ idd = item.data(0, QtCore.Qt.UserRole)
+ is_selected = item.checkState(0) == QtCore.Qt.Checked
+ to_be_hidden = set()
+ to_be_shown = set()
+
+ # item is top-level item / graph
+ if item.parent() is None:
+ was_selected = idd in self._checked_graphs
+
+ # check state changed to selected
+ if is_selected != was_selected:
+ # check state changed to checked
+ if is_selected:
+ self._checked_graphs.add(idd)
+ iterator = QtWidgets.QTreeWidgetItemIterator(item)
+ iterator += 1
+ self.blockSignals(True)
+ for i in range(item.childCount()):
+ child = item.child(i)
+ child.setCheckState(0, QtCore.Qt.Checked)
+ to_be_shown.add(child.data(0, QtCore.Qt.UserRole))
+ self.blockSignals(False)
+
+ # check state change to unchecked
+ else:
+ self._checked_graphs.remove(idd)
+ self.blockSignals(True)
+ for i in range(item.childCount()):
+ child = item.child(i)
+ child.setCheckState(0, QtCore.Qt.Unchecked)
+ to_be_hidden.add(child.data(0, QtCore.Qt.UserRole))
+ try:
+ self._checked_sets.remove(child.data(0, QtCore.Qt.UserRole))
+ except KeyError:
+ pass
+ self.blockSignals(False)
+
+ else:
+ self.keyChanged.emit(idd, item.text(0))
+
+ # item is a set
+ else:
+ was_selected = idd in self._checked_sets
+ if is_selected != was_selected:
+ if is_selected:
+ to_be_shown.add(idd)
+ self._checked_sets.add(idd)
+
+ else:
+ to_be_hidden.add(idd)
+ try:
+ self._checked_sets.remove(idd)
+ except KeyError:
+ pass
+
+ else:
+ self.keyChanged.emit(idd, item.text(0))
+
+ if to_be_shown or to_be_hidden:
+ self.stateChanged.emit(list(to_be_shown), list(to_be_hidden))
+
+ return to_be_shown, to_be_hidden
+
+ def dropEvent(self, evt: QtGui.QDropEvent):
+ dropped_index = self.indexAt(evt.pos())
+ if not dropped_index.isValid():
+ return
+
+ to_parent = self.itemFromIndex(dropped_index).parent()
+ append = False
+ if not to_parent:
+ # dropped on graph item -> append items
+ to_parent = self.itemFromIndex(dropped_index)
+ append = True
+
+ # index may change
+ persistent_drop = QtCore.QPersistentModelIndex(dropped_index)
+
+ tobemoved = []
+ take_from = []
+ for it in self.selectedItems():
+ from_parent = it.parent()
+ if from_parent is None:
+ continue
+
+ from_parent.removeChild(it)
+ tobemoved.append(it)
+ take_from.append(from_parent.data(0, QtCore.Qt.UserRole))
+
+ pos = QtCore.QModelIndex(persistent_drop)
+ if self.dropIndicatorPosition() == QtWidgets.QAbstractItemView.BelowItem:
+ pos = pos.sibling(pos.row()+1, 0)
+
+ row = pos.row()
+ if (row == -1) or append:
+ to_parent.addChildren(tobemoved)
+ else:
+ to_parent.insertChildren(row, tobemoved)
+
+ self.management.move_sets([it.data(0, QtCore.Qt.UserRole) for it in tobemoved],
+ to_parent.data(0, QtCore.Qt.UserRole), take_from,
+ pos=-1 if append else row)
+
+ def move_sets(self, sid: str, gid_in: str, gid_out: str):
+ self.blockSignals(True)
+ to_parent = None
+ from_parent = None
+ it = None
+
+ iterator = QtWidgets.QTreeWidgetItemIterator(self)
+ while iterator.value():
+ item = iterator.value()
+ if item is not None:
+ data = item.data(0, QtCore.Qt.UserRole)
+ if data == gid_out:
+ from_parent = item
+
+ elif data == gid_in:
+ to_parent = item
+
+ elif data == sid:
+ it = item
+
+ iterator += 1
+
+ if (from_parent is None) or (to_parent is None) or (it is None):
+ print('Komisch')
+ return
+
+ from_parent.removeChild(it)
+ to_parent.addChild(it)
+
+ self.blockSignals(False)
+
+ def set_name(self, sid, name):
+ iterator = QtWidgets.QTreeWidgetItemIterator(self)
+ while iterator.value():
+ item = iterator.value()
+ if item is not None:
+ data = item.data(0, QtCore.Qt.UserRole)
+ if data == sid:
+ if name != item.text(0):
+ item.setText(0, name)
+ break
+
+ iterator += 1
+
+ def keyPressEvent(self, evt: QtGui.QKeyEvent):
+ if evt.key() == QtCore.Qt.Key_Delete:
+ rm_sets = []
+ rm_graphs = []
+ for idx in self.selectedIndexes():
+ if idx.column() == 1:
+ continue
+ item = self.itemFromIndex(idx)
+ if item.parent() is None:
+ for c_i in range(item.childCount()):
+ rm_sets.append(item.child(c_i).data(0, QtCore.Qt.UserRole))
+ rm_graphs.append(item.data(0, QtCore.Qt.UserRole))
+ else:
+ rm_sets.append(item.data(0, QtCore.Qt.UserRole))
+
+ # self.deleteItem.emit(rm_sets+rm_graphs)
+ self.management.delete_sets(rm_sets+rm_graphs)
+
+ elif evt.key() == QtCore.Qt.Key_Space:
+ sets = []
+ from_parent = []
+
+ for idx in self.selectedIndexes():
+ if idx.column() != 0:
+ continue
+ item = self.itemFromIndex(idx)
+
+ if item.parent() is None:
+ is_selected = item.checkState(0)
+ self.blockSignals(True)
+ for i in range(item.childCount()):
+ child = item.child(i)
+ 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)
+
+ for it in sets:
+ if it in from_parent:
+ continue
+ it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked)
+ else:
+ super().keyPressEvent(evt)
+
+ def mousePressEvent(self, evt: QtGui.QMouseEvent):
+ # disable drag-and-drop when column 1 ('header') is clicked
+ idx = self.indexAt(evt.pos())
+ self.setDragEnabled(idx.column() == 0)
+ super().mousePressEvent(evt)
+
+ def remove_item(self, ids: list):
+ iterator = QtWidgets.QTreeWidgetItemIterator(self)
+ while iterator.value():
+ item = iterator.value()
+ _id = item.data(0, QtCore.Qt.UserRole)
+ if _id in ids:
+ try:
+ idx = item.parent().indexOfChild(item)
+ item.parent().takeChild(idx)
+ if _id in self._checked_sets:
+ self._checked_sets.remove(_id)
+ except AttributeError:
+ idx = self.invisibleRootItem().indexOfChild(item)
+ self.invisibleRootItem().takeChild(idx)
+ self._checked_graphs.remove(_id)
+
+ iterator += 1
+
+ def contextMenuEvent(self, evt):
+ menu = QtWidgets.QMenu()
+ d = menu.addAction('Hello')
+ d.setEnabled(False)
+ menu.addSeparator()
+
+ idx = self.selectedIndexes()
+
+ if self.invisibleRootItem().childCount() == 0 and len(idx) == 0:
+ rdn_action = menu.addAction('Randomness')
+
+ action = menu.exec(evt.globalPos())
+
+ if action == rdn_action:
+ import webbrowser
+ webbrowser.open('https://en.wikipedia.org/wiki/Special:Random')
+
+ else:
+ del_action = menu.addAction('Exterminate')
+ cp_action = menu.addAction('Replicate')
+ cat_action = menu.addAction('Join us!')
+ plt_action = None
+ save_action = None
+
+ idx = {}
+ has_fits = False
+ for i in self.selectedIndexes():
+ item = self.itemFromIndex(i)
+ parent = item.parent()
+ if parent is None:
+ continue
+
+ else:
+ graph_id = parent.data(0, QtCore.Qt.UserRole)
+ if graph_id not in idx:
+ idx[graph_id] = []
+ # collect sets in their graph
+ idx[graph_id].append(item.data(0, QtCore.Qt.UserRole))
+ data = self.management[item.data(0, QtCore.Qt.UserRole)]
+ if data.mode == 'fit':
+ has_fits = True
+
+ if has_fits:
+ menu.addSeparator()
+ plt_action = menu.addAction('Plot fit parameter')
+ save_action = menu.addAction('Save fit parameter')
+
+ action = menu.exec(evt.globalPos())
+
+ if action == del_action:
+ s = []
+ for gid, sets in idx.items():
+ s.extend(sets)
+ self.management.delete_sets(s)
+
+ elif action == cp_action:
+ for gid, sets in idx.items():
+ self.management.copy_sets(sets, gid)
+
+ elif action == cat_action:
+ s = []
+ for gid, sets in idx.items():
+ s.extend(sets)
+ self.management.cat(s)
+
+ elif action == plt_action:
+ s = []
+ for gid, sets in idx.items():
+ s.extend(sets)
+ self.management.make_fit_parameter(s)
+
+ elif action == save_action:
+ s = []
+ for gid, sets in idx.items():
+ s.extend(sets)
+ self.saveFits.emit(s)
+
+ evt.accept()
+
+ def highlight(self, gid: str):
+ iterator = QtWidgets.QTreeWidgetItemIterator(self)
+ while iterator.value():
+ item = iterator.value()
+ if item is not None:
+ if item.data(0, QtCore.Qt.UserRole) == gid:
+ item.setBackground(0, QtGui.QBrush(QtGui.QColor('gray')))
+ else:
+ item.setBackground(0, QtGui.QBrush())
+ iterator += 1
+
+ def uncheck_sets(self, sets: List[str]):
+ self.blockSignals(True)
+ iterator = QtWidgets.QTreeWidgetItemIterator(self)
+ while iterator.value():
+ item = iterator.value()
+ if item is not None:
+ if item.data(0, QtCore.Qt.UserRole) in sets:
+ item.setCheckState(0, QtCore.Qt.Unchecked)
+ iterator += 1
+ self.blockSignals(False)
+
+
+class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
+ keyChanged = QtCore.pyqtSignal(str, str)
+ deleteItem = QtCore.pyqtSignal(list)
+ startShowProperty = QtCore.pyqtSignal(list)
+ propertyChanged = QtCore.pyqtSignal(list, str, str, object)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setupUi(self)
+ self.tree = DataTree(self)
+ self.verticalLayout.addWidget(self.tree)
+ 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.proptable = PropWidget(self)
+ self.propwidget.addWidget(self.proptable)
+ self.propwidget.setText('Properties')
+ self.propwidget.expansionChanged.connect(self.show_property)
+ self.proptable.propertyChanged.connect(self.change_property)
+
+ make_action_icons(self)
+
+ def add_graph(self, idd: str, name: str):
+ self.tree.blockSignals(True)
+ self.tree.add_graph(idd, name)
+ self.tree.blockSignals(False)
+
+ def add_item(self, idd: str, name: str, gid: str):
+ self.tree.blockSignals(True)
+ self.tree.add_item((idd, name), gid)
+ self.tree.blockSignals(False)
+
+ def add_item_list(self, loi: list, gid: str):
+ self.tree.blockSignals(True)
+ self.tree.add_item(loi, gid)
+ self.tree.blockSignals(False)
+
+ def remove_item(self, key):
+ self.tree.remove_item(key)
+
+ def show_property(self, _: QtCore.QModelIndex = None):
+ if not self.propwidget.isExpanded():
+ return
+
+ sid = []
+ for i in self.tree.selectedIndexes():
+ if i.column() == 0:
+ sid.append(i.data(role=QtCore.Qt.UserRole))
+
+ self.startShowProperty.emit(sid)
+
+ @QtCore.pyqtSlot(dict)
+ def set_properties(self, props: dict):
+ self.proptable.populate(props)
+
+ def change_property(self, key1, key2, value):
+ ids = [item.data(0, QtCore.Qt.UserRole) for item in self.tree.selectedItems()]
+ if key2 == 'Value':
+ try:
+ value = float(value)
+ except ValueError:
+ QtWidgets.QMessageBox.warning(self, 'Invalid entry',
+ 'Value %r is not a valid number for `value`.' % value)
+ return
+
+ self.propertyChanged.emit(ids, key1, key2, value)
+
+ def uncheck_sets(self, sets: List[str]):
+ self.tree.uncheck_sets(sets)
+
+ def set_name(self, sid, value):
+ self.tree.set_name(sid, value)
+
+ @property
+ def management(self):
+ return self.tree.management
+
+ @management.setter
+ def management(self, value):
+ self.tree.management = value
diff --git a/nmreval/gui_qt/data/datawidget/properties.py b/nmreval/gui_qt/data/datawidget/properties.py
new file mode 100644
index 0000000..dd07087
--- /dev/null
+++ b/nmreval/gui_qt/data/datawidget/properties.py
@@ -0,0 +1,78 @@
+from ...Qt import QtWidgets, QtCore, QtGui
+from ...lib.delegates import PropertyDelegate
+
+
+class PropWidget(QtWidgets.QWidget):
+ propertyChanged = QtCore.pyqtSignal(str, str, object)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.layout = QtWidgets.QVBoxLayout(self)
+ self.layout.setContentsMargins(2, 2, 2, 2)
+ self.tab = QtWidgets.QTabWidget(self)
+ self.layout.addWidget(self.tab)
+ self.pages = []
+
+ self.tab.currentChanged.connect(self.tab_change)
+
+ self._tab_idx = 0
+
+ def populate(self, props: dict):
+ self.pages = []
+ self.tab.blockSignals(True)
+ while self.tab.count():
+ self.tab.removeTab(0)
+
+ for k, v in props.items():
+ table = PropTable(self)
+ table.populate(v)
+ table.itemChanged.connect(self.property_change)
+ self.tab.addTab(table, k)
+ self.pages.append(table)
+ self.tab.blockSignals(False)
+
+ self.tab.setCurrentIndex(self._tab_idx)
+
+ @QtCore.pyqtSlot(QtWidgets.QTableWidgetItem)
+ def property_change(self, item: QtWidgets.QTableWidgetItem):
+ tab_idx = self.tab.currentIndex()
+ table = self.pages[tab_idx]
+ idx = table.indexFromItem(item)
+ self.propertyChanged.emit(self.tab.tabText(tab_idx),
+ table.item(idx.row(), idx.column()-1).text(),
+ item.data(QtCore.Qt.DisplayRole))
+
+ @QtCore.pyqtSlot(int)
+ def tab_change(self, idx: int):
+ self._tab_idx = idx
+
+
+class PropTable(QtWidgets.QTableWidget):
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setColumnCount(2)
+ self.setItemDelegateForColumn(1, PropertyDelegate())
+ self.horizontalHeader().setStretchLastSection(True)
+ self.verticalHeader().setVisible(False)
+ self.horizontalHeader().setVisible(False)
+ self.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.setFrameShadow(QtWidgets.QFrame.Plain)
+
+ def populate(self, prop: dict):
+ self.clear()
+ self.setRowCount(0)
+ self.blockSignals(True)
+ for k, v in prop.items():
+ value_item = QtWidgets.QTableWidgetItem('')
+ value_item.setData(QtCore.Qt.DisplayRole, v)
+
+ key_item = QtWidgets.QTableWidgetItem(k)
+ key_item.setFlags(QtCore.Qt.NoItemFlags)
+ key_item.setForeground(QtGui.QBrush(QtGui.QColor(0, 0, 0)))
+
+ self.setRowCount(self.rowCount()+1)
+ self.setItem(self.rowCount()-1, 0, key_item)
+ self.setItem(self.rowCount()-1, 1, value_item)
+ self.blockSignals(False)
diff --git a/nmreval/gui_qt/data/integral_widget.py b/nmreval/gui_qt/data/integral_widget.py
new file mode 100644
index 0000000..19272bb
--- /dev/null
+++ b/nmreval/gui_qt/data/integral_widget.py
@@ -0,0 +1,213 @@
+from itertools import cycle
+
+import pyqtgraph as pg
+from numpy import nanmax, nanmin, inf, argsort, where
+try:
+ # numpy > 1.19 renamed some integration functions
+ from scipy.integrate import cumulative_trapezoid
+except ImportError:
+ from scipy.integrate import cumtrapz as cumulative_trapezoid
+
+from ..Qt import QtWidgets, QtCore, QtGui
+from .._py.integral_widget import Ui_Form
+
+
+class IntegralWidget(QtWidgets.QWidget, Ui_Form):
+ colors = cycle(['red', 'green', 'blue', 'cyan', 'magenta',
+ 'darkRed', 'darkGreen', 'darkBlue', 'darkCyan', 'darkMagenta'])
+
+ requestData = QtCore.pyqtSignal(str)
+ item_deleted = QtCore.pyqtSignal(pg.GraphicsObject)
+ newData = QtCore.pyqtSignal(str, list)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.connected_figure = ''
+ self.graph_shown = None
+ self.shown_set = None
+
+ self._data = None
+ self.ranges = []
+ self.lines = []
+
+ self.max_area = 0
+ self.max_y = inf
+ self.min_y = -inf
+
+ def __call__(self, graph_name, items):
+ self.label_2.setText(f'Connected to {graph_name} ')
+
+ self.clear()
+
+ self.set_combobox.blockSignals(True)
+ self.set_combobox.clear()
+
+ for sid, name in items:
+ self.set_combobox.addItem(name, userData=sid)
+
+ self.set_combobox.blockSignals(False)
+
+ self.set_combobox.setCurrentIndex(0)
+ self.set_combobox.currentIndexChanged.emit(0)
+
+ return self
+
+ def keyPressEvent(self, e):
+ if e.key() == QtCore.Qt.Key_Delete:
+ self.remove_integral()
+ else:
+ super().keyPressEvent(e)
+
+ @QtCore.pyqtSlot(int, name='on_set_combobox_currentIndexChanged')
+ def change_set(self, idx: int):
+ key = self.set_combobox.itemData(idx)
+ self.requestData.emit(key)
+
+ def set_data(self, ptr):
+ self._data = ptr
+ self.max_y = nanmax(self._data.y.real)
+ self.min_y = nanmin(self._data.y.real)
+ for idx, rnge in enumerate(self.ranges):
+ self._update_values(idx, rnge)
+
+ def add(self, pos):
+ x = pos[0]
+ self.ranges.append((x, x * 1.1))
+
+ c = next(IntegralWidget.colors)
+ qc = QtGui.QColor(c)
+ qc.setAlpha(40)
+ region = pg.LinearRegionItem(values=[x, x*1.1], brush=QtGui.QBrush(qc), pen=pg.mkPen(QtGui.QColor(c)))
+ integral_plot = pg.PlotDataItem(x=[], y=[])
+ region.sigRegionChanged.connect(self._update_integral)
+
+ self.lines.append((region, integral_plot))
+ self.areas.append(0)
+ self._make_entry(c)
+
+ return region, integral_plot
+
+ def _make_entry(self, c):
+ item = QtWidgets.QTreeWidgetItem()
+ item.setText(0, f'Integral {len(self.ranges)}')
+
+ item.setForeground(0, QtGui.QBrush(QtGui.QColor(c)))
+
+ pts_i = self.ranges[-1]
+ item_list = []
+ for text, val in [('Start', pts_i[0]), ('Stop', pts_i[1]), ('Areas', 0), ('Ratio', 1.)]:
+ child = QtWidgets.QTreeWidgetItem()
+ child.setFlags(QtCore.Qt.NoItemFlags)
+ child.setText(0, f'{text}: {val:.5g}')
+ child.setForeground(0, QtGui.QBrush(QtGui.QColor('black')))
+
+ item_list.append(child)
+
+ item.addChildren(item_list)
+
+ self.treeWidget.addTopLevelItem(item)
+ self.treeWidget.expandToDepth(1)
+
+ self._update_values(len(self.ranges) - 1, pts_i)
+
+ def _update_integral(self):
+ idx = None
+ sender = self.sender()
+ for i, (reg, _) in enumerate(self.lines):
+ if sender == reg:
+ idx = i
+ break
+
+ if idx is None:
+ return
+
+ self._update_values(idx, sender.getRegion())
+
+ def _update_values(self, idx, new_range):
+ self.ranges[idx] = new_range
+ area = self.calc_integral(idx, *new_range)
+
+ item = self.treeWidget.topLevelItem(idx)
+ item.child(0).setText(0, f'Start: {new_range[0]:.5g}')
+ item.child(1).setText(0, f'Stop: {new_range[1]:.5g}')
+
+ if area is not None:
+ self.areas[idx] = area
+ item.child(2).setText(0, f'Area: {area:.5g}')
+ if self.max_area > 0:
+ self._set_ratios(idx, self.max_area)
+
+ curr_max = max(self.areas)
+ if curr_max != self.max_area:
+ if curr_max > 0:
+ root = self.treeWidget.invisibleRootItem()
+ for i in range(root.childCount()):
+ self._set_ratios(i, curr_max)
+ self.max_area = curr_max
+
+ def _set_ratios(self, idx, max_value):
+ item = self.treeWidget.invisibleRootItem().child(idx)
+ area_i = self.areas[idx]
+ item.child(3).setText(0, f'Ratio: {area_i / max_value:.3g}')
+
+ integral_line = self.lines[idx][1]
+ x_i, y_i = integral_line.getData()
+ scale = (self.max_y - self.min_y) / y_i[-1] * (area_i / max_value)
+ integral_line.setData(x=x_i, y=y_i * scale)
+
+ def calc_integral(self, idx, x_min, x_max):
+ int_range = where((self._data.x >= x_min) & (self._data.x <= x_max))[0]
+ if len(int_range) > 1:
+ x_int = self._data.x[int_range]
+ y_int = self._data.y[int_range].real
+ order = argsort(x_int)
+
+ integral = cumulative_trapezoid(y=y_int[order], x=x_int[order], initial=0)
+ scale = (self.max_y-self.min_y) / integral[-1]
+ self.lines[idx][1].setData(x=x_int[order], y=integral*scale + self.min_y)
+
+ return integral[-1]
+
+ else:
+ self.lines[idx][1].setData(x=[], y=[])
+ return None
+
+ def remove_integral(self):
+ root = self.treeWidget.invisibleRootItem()
+ for item in self.treeWidget.selectedItems():
+ idx = root.indexOfChild(item)
+
+ self.ranges.pop(idx)
+ self.item_deleted.emit(self.lines[idx][0])
+ self.item_deleted.emit(self.lines[idx][1])
+ self.lines.pop(idx)
+ self.areas.pop(idx)
+ self.treeWidget.takeTopLevelItem(idx)
+
+ @QtCore.pyqtSlot(name='on_pushButton_clicked')
+ def convert_to_datasets(self):
+ set_id = self.set_combobox.currentData()
+ values = []
+ for i in range(len(self.ranges)):
+ x_i, y_i = self.lines[i][1].getData()
+ start_i, stop_i = self.ranges[i]
+ area_i = self.areas[i]
+ values.append((x_i, y_i, start_i, stop_i, area_i))
+
+ self.newData.emit(set_id, values)
+
+ def clear(self):
+ self.connected_figure = ''
+ self.graph_shown = None
+ self.shown_set = None
+
+ self._data = None
+ self.ranges = []
+ self.lines = []
+
+ self.max_area = 0
+ self.max_y = inf
+ self.min_y = -inf
+
diff --git a/nmreval/gui_qt/data/interpolate_dialog.py b/nmreval/gui_qt/data/interpolate_dialog.py
new file mode 100644
index 0000000..e70fc4b
--- /dev/null
+++ b/nmreval/gui_qt/data/interpolate_dialog.py
@@ -0,0 +1,41 @@
+from numpy import linspace, logspace, log10
+
+from ..Qt import QtWidgets, QtCore
+from .._py.interpol_dialog import Ui_Dialog
+
+
+class QInterpol(QtWidgets.QDialog, Ui_Dialog):
+ ready = QtCore.pyqtSignal(dict)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setupUi(self)
+
+ self.on_comboBox_2_currentIndexChanged(0)
+
+ @QtCore.pyqtSlot(int)
+ def on_comboBox_2_currentIndexChanged(self, idx: int):
+ if idx == 0:
+ self.frame.hide()
+ self.frame_2.show()
+ else:
+ self.frame.show()
+ self.frame_2.hide()
+
+ def accept(self):
+ ret_dic = {'kind': self.comboBox.currentIndex(), 'ylog': self.checkBox_2.isChecked()}
+ if self.comboBox_2.currentIndex() == 0:
+ ret_dic['xaxis'] = int(self.lineEdit.text())
+ else:
+ if self.checkBox.isChecked():
+ ret_dic['xaxis'] = logspace(log10(float(self.lineEdit_2.text())),
+ log10(float(self.lineEdit_3.text())),
+ num=int(self.lineEdit_4.text()))
+ else:
+ ret_dic['xaxis'] = linspace(float(self.lineEdit_2.text()),
+ float(self.lineEdit_3.text()),
+ num=int(self.lineEdit_4.text()))
+ ret_dic['idx'] = [i.row() for i in self.listWidget.selectedIndexes()]
+ self.ready.emit(ret_dic)
+ self.close()
diff --git a/nmreval/gui_qt/data/plot_dialog.py b/nmreval/gui_qt/data/plot_dialog.py
new file mode 100644
index 0000000..a49102a
--- /dev/null
+++ b/nmreval/gui_qt/data/plot_dialog.py
@@ -0,0 +1,123 @@
+from random import randint
+
+import numpy as np
+
+from ... import models
+from ...lib.importer import find_models
+from ...lib.utils import valid_function
+
+from ..Qt import QtGui, QtCore, QtWidgets
+from .._py.setbyfunction_dialog import Ui_NewCurveDialog
+
+
+class QPlotDialog(QtWidgets.QDialog, Ui_NewCurveDialog):
+ line_created = QtCore.pyqtSignal(object)
+
+ def __init__(self):
+ super().__init__()
+ self.setupUi(self)
+
+ self._function = find_models(models)
+
+ self.lineEdit_3.setValidator(QtGui.QDoubleValidator())
+ self.lineEdit_4.setValidator(QtGui.QDoubleValidator())
+ self.lineEdit_5.setValidator(QtGui.QIntValidator().setBottom(0))
+
+ self.buttonBox.accepted.connect(self.make_line)
+
+ for cb in [self.comboBox, self.comboBox_2, self.comboBox_4, self.comboBox_5]:
+ cb.setCurrentIndex(randint(0, cb.count()))
+
+ for cb in [self.comboBox_6, self.comboBox_7]:
+ self.load_models(cb)
+
+ def load_models(self, cb: QtWidgets.QComboBox):
+ for f in self._function:
+ cb.addItem(f'{f.name} ({f.type})', userData=f)
+
+ @QtCore.pyqtSlot(name='on_pushButton_clicked')
+ def check_input(self):
+ err = []
+ try:
+ start = float(self.lineEdit_3.text())
+ except ValueError:
+ err.append(0)
+ start = 1
+ try:
+ stop = float(self.lineEdit_4.text())
+ except ValueError:
+ err.append(1)
+ stop = 10
+
+ try:
+ nums = int(self.lineEdit_5.text())
+ except ValueError:
+ err.append(2)
+ nums = 10
+
+ if self.checkBox.isChecked():
+ if start <= 0 or stop <= 0:
+ err.append(3)
+ start, stop = abs(start)+1e-12, abs(stop)
+ grid = np.geomspace(start, stop, num=nums)
+ else:
+ grid = np.linspace(start, stop, num=nums)
+
+ x_func = self.lineEdit.text()
+ x, isok = valid_function(x_func, extra_namespace={'i': grid})
+ if not isok:
+ err.append(4)
+ x = grid
+
+ y_func = self.lineEdit_2.text()
+ y, isok = valid_function(y_func, extra_namespace={'i': grid, 'x':x})
+ if not isok:
+ err.append(5)
+
+ msg_err = {0: 'Invalid value for grid start',
+ 1: 'Invalid value for grid end',
+ 2: 'Invalid number of grid steps',
+ 3: 'Negative numbers in logarithmic grid',
+ 4: 'Invalid expression for x',
+ 5: 'Invalid expression for y'
+ }
+
+ if err:
+ m = '\n'.join([msg_err[e] for e in err])
+ QtWidgets.QMessageBox().information(self, 'Error detected', m)
+ return False
+
+ return True
+
+ def make_line(self):
+ if not self.check_input():
+ return
+
+ start = float(self.lineEdit_3.text())
+ stop = float(self.lineEdit_4.text())
+ nums = int(self.lineEdit_5.text())
+ if self.checkBox.isChecked():
+ x = np.geomspace(start, stop, num=nums)
+ else:
+ x = np.linspace(start, stop, num=nums)
+ x_func = self.lineEdit.text()
+ y_func = self.lineEdit_2.text()
+
+ sym = self.comboBox.currentText()
+ lin = self.comboBox_2.currentText()
+
+ name = self.lineEdit_6.text()
+ if not name:
+ name = 'self done'
+
+ lw = self.doubleSpinBox.value()
+ sw = self.spinBox.value()
+
+
+if __name__ == '__main__':
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ win = QPlotDialog()
+ win.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/data/point_select.py b/nmreval/gui_qt/data/point_select.py
new file mode 100644
index 0000000..c400249
--- /dev/null
+++ b/nmreval/gui_qt/data/point_select.py
@@ -0,0 +1,196 @@
+import re
+
+from ..Qt import QtCore, QtWidgets
+from .._py.ptstab import Ui_Form
+
+__all__ = ['PointSelectWidget']
+
+from ..lib.pg_objects import LogInfiniteLine, RegionItem
+
+REGION_RE = re.compile(r'(?P[+-]*\d+(?:\.\d*)*(?:[eE][+-]*\d+)*)'
+ r'(?: ?- ?(?P[+-]*\d+(?:\.\d*)*(?:[eE][+-]*\d+)*))*')
+
+
+class PointSelectWidget(QtWidgets.QWidget, Ui_Form):
+ widget_closed = QtCore.pyqtSignal()
+ points_selected = QtCore.pyqtSignal(dict, str)
+ point_removed = QtCore.pyqtSignal(LogInfiniteLine)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.pts = []
+ self.pts_lines = []
+ self.nop = 0
+ self._prev_pos = ''
+ self._last_item = None
+ self.connected_figure = ''
+
+ self.okButton.clicked.connect(self.apply)
+ self.deleteButton.clicked.connect(self.remove_points)
+ self.peaktable.itemChanged.connect(self.editing_finished)
+ self.peaktable.itemDoubleClicked.connect(self.editing_started)
+
+ def keyPressEvent(self, e):
+ if e.key() == QtCore.Qt.Key_Delete:
+ self.remove_points()
+ elif e.key() == QtCore.Qt.Key_F2:
+ self.editing_started()
+ else:
+ super().keyPressEvent(e)
+
+ def clear(self):
+ self.pts = []
+ self.nop = 0
+ self.peaktable.clear()
+ self.pts_lines = []
+
+ @QtCore.pyqtSlot(tuple, bool)
+ def add(self, pos: tuple, double: bool):
+ x = pos[0]
+ if double:
+ self.removepoint(-1)
+
+ self.pts.append((x, x*1.1))
+ item = RegionItem(values=[x, x*1.1], mode='mid')
+ item.sigRegionChanged.connect(self._update_region)
+ else:
+ self.pts.append(x)
+ item = LogInfiniteLine(pos=x, movable=True)
+ item.sigPositionChanged.connect(self._update_line)
+
+ self.pts_lines.append(item)
+ self.nop += 1
+ self._makerow()
+
+ return item
+
+ def remove_points(self):
+ for i in sorted(self.peaktable.selectedIndexes(), key=lambda x: x.row(), reverse=True):
+ self.removepoint(pos=i.row())
+
+ def removepoint(self, pos=0):
+ if pos == -1:
+ pos = len(self.pts) - 1
+
+ try:
+ self.pts.pop(pos)
+ self.nop -= 1
+ item = self.peaktable.takeItem(pos)
+ del item
+
+ del_line = self.pts_lines.pop(pos)
+ self.point_removed.emit(del_line)
+ del del_line
+ except IndexError:
+ pass
+
+ def _makerow(self):
+ if isinstance(self.pts[-1], tuple):
+ item = QtWidgets.QListWidgetItem(f'{self.pts[-1][0]:.5g} - {self.pts[-1][1]:.5g}')
+ else:
+ item = QtWidgets.QListWidgetItem(f'{self.pts[-1]:.5g}')
+ item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
+ self.peaktable.blockSignals(True)
+ self.peaktable.addItem(item)
+ self.peaktable.blockSignals(False)
+
+ def closeEvent(self, evt):
+ self.widget_closed.emit()
+ super().closeEvent(evt)
+
+ @QtCore.pyqtSlot()
+ def apply(self) -> dict:
+ ret_dic = {'avg_range': [self.left_pt.value(), self.right_pt.value()],
+ 'avg_mode': {0: 'mean', 1: 'sum', 2: 'integral'}[self.average_combobox.currentIndex()],
+ 'special': None, 'idx': None,
+ 'xy': (self.xbutton.isChecked(), self.ybutton.isChecked())}
+
+ if self.groupBox_2.isChecked():
+ ret_dic['special'] = {0: 'max', 1: 'absmax', 2: 'min', 3: 'absmin'}[self.special_comboBox.currentIndex()]
+
+ if len(self.pts) != 0:
+ ret_dic['idx'] = self.pts
+
+ if self.graph_checkbox.isChecked():
+ gid = ''
+ else:
+ gid = self.graph_combobox.currentData()
+
+ self.points_selected.emit(ret_dic, gid)
+
+ return ret_dic
+
+ def _update_region(self):
+ try:
+ idx = self.pts_lines.index(self.sender())
+ except ValueError:
+ return
+
+ self.pts[idx] = self.sender().getRegion()
+ self.peaktable.blockSignals(True)
+ self.peaktable.item(idx).setText('{:.5g} - {:.5g}'.format(*self.pts[idx]))
+ self.peaktable.blockSignals(False)
+
+ def _update_line(self):
+ try:
+ idx = self.pts_lines.index(self.sender())
+ except ValueError:
+ return
+
+ self.pts[idx] = self.sender().value()
+ self.peaktable.blockSignals(True)
+ self.peaktable.item(idx).setText(f'{self.pts[idx]:.5g}')
+ self.peaktable.blockSignals(False)
+
+ @QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
+ def editing_started(self, item=None):
+ if item is None:
+ item = self.peaktable.selectedItems()[0]
+ self._prev_pos = item.text()
+ self.peaktable.editItem(item)
+
+ @QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
+ def editing_finished(self, it: QtWidgets.QListWidgetItem):
+ m = re.match(REGION_RE, it.text()).groupdict()
+ undo = True
+ if m:
+ start, stop = m['first'], m['second']
+ row = self.peaktable.row(it)
+ it_pts = self.pts_lines[row]
+ if ((stop is None) and isinstance(it_pts, RegionItem)) or \
+ ((stop is not None) and isinstance(it_pts, LogInfiniteLine)):
+ QtWidgets.QMessageBox().information(self, 'Invalid type',
+ 'Conversion between point and region is not possible.')
+ else:
+ if stop is None:
+ it_pts.blockSignals(True)
+ it_pts.setValue(float(start))
+ it_pts.blockSignals(False)
+ self.pts[row] = float(start)
+ else:
+ start, stop = float(start), float(stop)
+ pos = (min(start, stop), max(start, stop))
+ self.pts[row] = pos
+ self.peaktable.blockSignals(True)
+ it.setText(f'{pos[0]:.5g} - {pos[1]:.5g}')
+ self.peaktable.blockSignals(False)
+ it_pts.blockSignals(True)
+ it_pts.setRegion(pos)
+ it_pts.blockSignals(False)
+ undo = False
+
+ if undo:
+ self.peaktable.blockSignals(True)
+ it.setText(self._prev_pos)
+ self.peaktable.blockSignals(False)
+
+ def set_graphs(self, graphs: list):
+ self.graph_combobox.clear()
+ for g in graphs:
+ self.graph_combobox.addItem(g[1], userData=g[0])
+
+ @QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged')
+ def changed_state(self, checked):
+ self.graph_combobox.setEnabled(checked!=QtCore.Qt.Checked)
diff --git a/nmreval/gui_qt/data/shift_graphs.py b/nmreval/gui_qt/data/shift_graphs.py
new file mode 100644
index 0000000..0fa7f32
--- /dev/null
+++ b/nmreval/gui_qt/data/shift_graphs.py
@@ -0,0 +1,190 @@
+import numpy as np
+from itertools import cycle
+
+from pyqtgraph import mkColor, mkPen
+
+from ...lib.colors import Tab10
+from ..Qt import QtGui, QtCore, QtWidgets
+from .._py.shift_scale_dialog import Ui_shift_dialog
+from ..lib.pg_objects import PlotItem
+from ..lib.utils import SciSpinBox
+
+
+class QShift(QtWidgets.QDialog, Ui_shift_dialog):
+ valuesChanged = QtCore.pyqtSignal(dict, tuple)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.graphicsView.setMenuEnabled(False)
+ self.splitter.setSizes([int(self.width()/4), int(self.width()*2/3)])
+
+ self.movements = {}
+ self.data = {}
+ self._colors = cycle(Tab10)
+
+ delegate = SpinBoxDelegate()
+ delegate.valueChanged.connect(self.shift)
+ self.shift_table.setItemDelegate(delegate)
+ self.x_shift_spinbox.valueChanged.connect(lambda: self.glob_shift('h'))
+ self.y_shift_spinbox.valueChanged.connect(lambda: self.glob_shift('v'))
+
+ delegate = SpinBoxDelegate()
+ delegate.valueChanged.connect(self.scale)
+ self.scale_table.setItemDelegate(delegate)
+ self.x_scale_spinbox.valueChanged.connect(lambda: self.glob_scale('h'))
+ self.y_scale_spinbox.valueChanged.connect(lambda: self.glob_scale('v'))
+
+ def add_item(self, idx, name, x, y):
+ color = mkColor(next(self._colors).rgb())
+ if np.iscomplexobj(y):
+ pl = [PlotItem(x, y.real, name=name, pen=mkPen(color=color)),
+ PlotItem(x, y.imag, name=name, pen=mkPen(color=color))]
+ else:
+ pl = [PlotItem(x, y, name=name, pen=mkPen(color=color))]
+
+ self.data[idx] = (pl, x, y)
+
+ # [[horizontal shift, vertical shift], [horizontal scale, vertical scale]]
+ self.movements[idx] = [[0, 0], [1, 1]]
+
+ for i, tw in enumerate([self.shift_table, self.scale_table]):
+ tw.blockSignals(True)
+ row = tw.rowCount()
+ tw.insertRow(row)
+
+ item = QtWidgets.QTableWidgetItem(name)
+ item.setForeground(QtGui.QBrush(color))
+ item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
+ item.setCheckState(QtCore.Qt.Checked)
+ item.setData(QtCore.Qt.UserRole, idx)
+ tw.setItem(row, 0, item)
+
+ tw.setItem(row, 1, QtWidgets.QTableWidgetItem(str(i)))
+ tw.setItem(row, 2, QtWidgets.QTableWidgetItem(str(i)))
+
+ tw.blockSignals(False)
+ for i in pl:
+ self.graphicsView.addItem(i)
+
+ def set_graphs(self, graphs: list):
+ for key, name in graphs:
+ self.data_combobox.addItem(name, userData=key)
+ self.values_combobox.addItem(name, userData=key)
+
+ def glob_shift_scale(self, widget: QtWidgets.QTableWidget, mode: int, col: int, value: float):
+ for row in range(widget.rowCount()):
+ if widget.item(row, 0).checkState() == QtCore.Qt.Checked:
+ item = widget.item(row, col)
+ item.setText(str(value))
+ self.shift_scale(widget, mode, row, col-1, value)
+
+ def glob_shift(self, direction: str):
+ if direction == 'h':
+ val = self.x_shift_spinbox.value()
+ self.glob_shift_scale(self.shift_table, 0, 1, val)
+ else:
+ val = self.y_shift_spinbox.value()
+ self.glob_shift_scale(self.shift_table, 0, 2, val)
+
+ def glob_scale(self, direction: str):
+ if direction == 'h':
+ val = self.x_scale_spinbox.value()
+ self.glob_shift_scale(self.scale_table, 1, 1, val)
+ else:
+ val = self.y_scale_spinbox.value()
+ self.glob_shift_scale(self.scale_table, 1, 2, val)
+
+ def shift_scale(self, widget: QtWidgets.QTableWidget, mode: int,
+ row: int, col: int, value: float):
+ item = widget.item(row, 0)
+ key = item.data(QtCore.Qt.UserRole)
+ self.movements[key][mode][col] = value
+
+ (x_off, y_off), (x_scale, y_scale) = self.movements[key]
+
+ pl, x, y = self.data[key]
+ y_part = [np.real, np.imag]
+ for i, item in enumerate(pl):
+ item.setData(x=x*x_scale+x_off, y=y_part[i](y) * y_scale + y_off)
+
+ @QtCore.pyqtSlot(int, int, float)
+ def shift(self, row: int, column: int, value: float):
+ self.shift_scale(self.shift_table, 0, row, column-1, value)
+
+ @QtCore.pyqtSlot(int, int, float)
+ def scale(self, row: int, column: int, value: float):
+ self.shift_scale(self.scale_table, 1, row, column-1, value)
+
+ @QtCore.pyqtSlot(int, name='on_xlog_checkbox_stateChanged')
+ @QtCore.pyqtSlot(int, name='on_ylog_checkbox_stateChanged')
+ def set_log(self, state: int):
+ if self.sender() == self.xlog_checkbox:
+ log_state = self.graphicsView.plotItem.ctrl.logXCheck
+ else:
+ log_state = self.graphicsView.plotItem.ctrl.logYCheck
+ log_state.setCheckState(state)
+ self.graphicsView.plotItem.updateLogMode()
+
+ def on_overwrite_checkbox_stateChanged(self, state: int):
+ self.data_newgraph.setVisible(state != QtCore.Qt.Checked)
+ self.data_combobox.setVisible(state != QtCore.Qt.Checked)
+
+ def on_value_checkbox_stateChanged(self, state: int):
+ self.values_newgraph.setVisible(state == QtCore.Qt.Checked)
+ self.values_combobox.setVisible(state == QtCore.Qt.Checked)
+
+ def on_data_newgraph_stateChanged(self, state: int):
+ self.data_combobox.setEnabled(state != QtCore.Qt.Checked)
+
+ def on_values_newgraph_stateChanged(self, state: int):
+ self.values_combobox.setEnabled(state != QtCore.Qt.Checked)
+
+ def accept(self):
+ data_saving = None
+ if not self.overwrite_checkbox.isChecked():
+ if self.data_newgraph.isChecked():
+ data_saving = ''
+ else:
+ data_saving = self.data_combobox.currentData()
+
+ value_saving = None
+ if self.value_checkbox.isChecked():
+ if self.values_newgraph.isChecked():
+ value_saving = ''
+ else:
+ value_saving = self.values_combobox.currentData()
+
+ self.valuesChanged.emit(self.movements, (data_saving, value_saving))
+ self.close()
+
+
+class SpinBoxDelegate(QtWidgets.QStyledItemDelegate):
+ valueChanged = QtCore.pyqtSignal(int, int, float)
+
+ def createEditor(self, parent: QtWidgets.QWidget, option: QtWidgets.QStyleOptionViewItem,
+ idx: QtCore.QModelIndex) -> QtWidgets.QWidget:
+ editor = SciSpinBox(parent)
+ editor.valueChanged.connect(self.new_value)
+
+ return editor
+
+ def new_value(self, val):
+ # geht bestimmt besser...
+ table = self.sender().parent().parent()
+ self.valueChanged.emit(table.currentRow(), table.currentColumn(), val)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QtWidgets.QApplication(sys.argv)
+ mplQt = QShift()
+ xx = np.geomspace(1, 100)
+ mplQt.add_item('aa', 'a', xx, xx/(1+xx**2)*10)
+ mplQt.add_item('bb', 'b', xx, xx/(1+(0.1*xx)**2))
+ mplQt.add_item('cc', 'c', xx, xx/(1+(0.01*xx)**2)/10)
+ mplQt.set_graphs([('123', 'zyx'), ('456', 'wvu')])
+ mplQt.show()
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/data/signaledit/__init__.py b/nmreval/gui_qt/data/signaledit/__init__.py
new file mode 100644
index 0000000..b92851f
--- /dev/null
+++ b/nmreval/gui_qt/data/signaledit/__init__.py
@@ -0,0 +1,2 @@
+from .phase_dialog import QApodDialog, QPhasedialog
+from .baseline_dialog import QBaselineDialog
diff --git a/nmreval/gui_qt/data/signaledit/baseline_dialog.py b/nmreval/gui_qt/data/signaledit/baseline_dialog.py
new file mode 100644
index 0000000..c231cd7
--- /dev/null
+++ b/nmreval/gui_qt/data/signaledit/baseline_dialog.py
@@ -0,0 +1,92 @@
+import numpy as np
+import pyqtgraph as pg
+
+from scipy.interpolate import splrep, splev
+
+from ...Qt import QtCore, QtWidgets
+from ..._py.baseline_dialog import Ui_SignalEdit
+
+
+class QBaselineDialog(QtWidgets.QDialog, Ui_SignalEdit):
+ finished = QtCore.pyqtSignal(str, tuple)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.data = None
+
+ self.graph = pg.PlotDataItem(x=[], y=[], pen=pg.mkPen({'color': 'b'}))
+ self.graph_corr = pg.PlotDataItem(x=[], y=[], pen=pg.mkPen({'color': 'r'}))
+ self.baseline = pg.PlotDataItem(x=[], y=[])
+
+ self.anchors = []
+ self.anchor_lines = []
+ self.spline = None
+
+ self.graphicsView.scene().sigMouseClicked.connect(self.add_node)
+ self.graphicsView.addItem(self.graph_corr)
+ self.graphicsView.addItem(self.graph)
+ self.graphicsView.addItem(self.baseline)
+
+ def add_data(self, x, y):
+ if self.data is not None:
+ QtWidgets.QMessageBox().information(self, 'Invalid number of datasets',
+ 'Baseline correction is only working on one set at a time.')
+ self.close()
+ self.anchors.extend([np.min(x), np.max(x)])
+ self.data = (x, y)
+ self.graph.setData(x=x, y=y.real)
+ self.graph_corr.setData(x=x, y=y.real)
+
+ def accept(self):
+ self.finished.emit('bls', (splev(self.data[0], self.spline),))
+ self.close()
+
+ def add_node(self, evt):
+ vb = self.graphicsView.plotItem.vb
+
+ if self.graphicsView.plotItem.sceneBoundingRect().contains(evt.scenePos()) and evt.button() == 1:
+ pos = vb.mapSceneToView(evt.scenePos())
+ x = pos.x()
+
+ self.anchors.append(x)
+ self.anchors.sort()
+ row = self.anchors.index(x)
+ self.listWidget.insertItem(row-1, QtWidgets.QListWidgetItem(str(x)))
+
+ inf_line = pg.InfiniteLine(pos=x)
+ self.anchor_lines.insert(row-1, inf_line)
+ self.graphicsView.addItem(inf_line)
+
+ self.change_baseline()
+
+ def change_baseline(self):
+ if self.data:
+ x, y = self.data
+
+ def mean(xx):
+ return np.mean(y[max(0, np.argmin(abs(x-xx))-5):min(len(x), np.argmin(abs(x-xx))+6)].real)
+
+ y_node = [mean(x_node) for x_node in self.anchors]
+ try:
+ self.spline = splrep(self.anchors, y_node, per=False)
+ except TypeError:
+ self.spline = splrep(self.anchors, y_node, per=False, k=1)
+
+ bl = splev(x, self.spline)
+
+ self.baseline.setData(x=x, y=bl)
+ self.graph_corr.setData(x=x, y=y.real-bl)
+
+ def keyPressEvent(self, evt):
+ if self.listWidget.hasFocus() and evt.key() == QtCore.Qt.Key_Delete:
+ r = self.listWidget.currentRow()
+ self.anchors.pop(r+1)
+ listitem = self.listWidget.takeItem(r)
+ del listitem
+ self.graphicsView.removeItem(self.anchor_lines.pop(r))
+ self.change_baseline()
+
+ else:
+ super().keyPressEvent(evt)
diff --git a/nmreval/gui_qt/data/signaledit/editsignalwidget.py b/nmreval/gui_qt/data/signaledit/editsignalwidget.py
new file mode 100644
index 0000000..23bb9cd
--- /dev/null
+++ b/nmreval/gui_qt/data/signaledit/editsignalwidget.py
@@ -0,0 +1,92 @@
+from ....math import apodization
+from ....lib.importer import find_models
+from ....utils.text import convert
+
+from ...Qt import QtCore, QtWidgets, QtGui
+from ...lib.forms import FormWidget
+from ..._py.editsignalwidget import Ui_Form
+
+
+class EditSignalWidget(QtWidgets.QWidget, Ui_Form):
+ do_something = QtCore.pyqtSignal(str, tuple)
+ get_values = QtCore.pyqtSignal()
+ preview_triggered = QtCore.pyqtSignal(str)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.apodlist = find_models(apodization)
+
+ self.lineEdit.hide()
+ self.lineEdit.setValidator(QtGui.QDoubleValidator())
+
+ for ap in self.apodlist:
+ self.apodcombobox.addItem(str(ap().name))
+ self.change_apodization(0)
+
+ self.baselinebutton.clicked.connect(lambda: self.apply_changes('bl'))
+ self.zfbutton.clicked.connect(lambda: self.apply_changes('zf'))
+ self.phasebutton.clicked.connect(lambda: self.apply_changes('ph'))
+ self.apodbutton.clicked.connect(lambda: self.apply_changes('ap'))
+ self.leftshiftbutton.clicked.connect(lambda: self.apply_changes('ls'))
+ self.fourierutton.clicked.connect(lambda: self.apply_changes('ft'))
+
+ self.pushButton.clicked.connect(lambda: self.preview_triggered.emit('ap'))
+ self.pushButton_2.clicked.connect(lambda: self.preview_triggered.emit('ph'))
+
+ @QtCore.pyqtSlot(str)
+ def apply_changes(self, sender):
+ if sender in ['bl', 'zf', 'ft']:
+ self.do_something.emit(sender, tuple())
+
+ elif sender == 'ls':
+ if self.comboBox.currentIndex() == 0:
+ _nop = int(self.lsspinBox.text())
+ stype = 'pts'
+ else:
+ try:
+ _nop = float(self.lineEdit.text())
+ except ValueError:
+ _nop = 0.0
+ stype = 'time'
+ self.do_something.emit(sender, (_nop, stype))
+
+ elif sender == 'ap':
+ apodmodel = self.apodlist[self.apodcombobox.currentIndex()]
+ p = [float(x.text()) for x in self.groupBox_3.findChildren(QtWidgets.QLineEdit)]
+ self.do_something.emit(sender, (p, apodmodel))
+
+ elif sender == 'ph':
+ ph0 = float(self.ph0slider.value())
+ ph1 = float(self.ph1slider.value())
+ pvt = float(self.pivot_lineedit.text())
+ self.do_something.emit(sender, (ph0, ph1, pvt))
+
+ else:
+ print('You should never reach this by accident.')
+
+ @QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged')
+ def change_apodization(self, index):
+ apod_func = self.apodlist[index]
+ self.label_2.setText(convert(apod_func.equation))
+
+ while self.verticalLayout_8.count():
+ item = self.verticalLayout_8.takeAt(0)
+ try:
+ item.widget().deleteLater()
+ except AttributeError:
+ pass
+
+ for k, v in enumerate(apod_func.params):
+ widgt = FormWidget(name=v)
+ self.verticalLayout_8.addWidget(widgt)
+
+ @QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged')
+ def change_ls(self, idx):
+ if idx:
+ self.lineEdit.show()
+ self.lsspinBox.hide()
+ else:
+ self.lineEdit.hide()
+ self.lsspinBox.show()
diff --git a/nmreval/gui_qt/data/signaledit/phase_dialog.py b/nmreval/gui_qt/data/signaledit/phase_dialog.py
new file mode 100644
index 0000000..0f89208
--- /dev/null
+++ b/nmreval/gui_qt/data/signaledit/phase_dialog.py
@@ -0,0 +1,196 @@
+import numpy as np
+import pyqtgraph as pg
+from numpy import inf, linspace
+from numpy.fft import fft, fftfreq, fftshift
+
+from ....lib.importer import find_models
+from ....math import apodization as apodization
+from ....utils.text import convert
+
+from ...Qt import QtCore, QtWidgets
+from ..._py.apod_dialog import Ui_ApodEdit
+from ..._py.phase_corr_dialog import Ui_SignalEdit
+from ...lib.forms import FormWidget
+
+
+class QPreviewDialogs(QtWidgets.QDialog):
+ finished = QtCore.pyqtSignal(str, tuple)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.data = []
+ self.graphs = []
+
+ self.mode = ''
+
+ def add_data(self, x, y):
+ self.data.append((x, y))
+ real_plt = pg.PlotDataItem(x=x, y=y.real, pen=pg.mkColor('b'))
+ imag_plt = pg.PlotDataItem(x=x, y=y.imag, pen=pg.mkColor('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.mode = 'ph'
+
+ self.pvt_line = pg.InfiniteLine(pos=0, movable=True)
+ self.graphicsView.addItem(self.pvt_line)
+ self.pvt_line.sigPositionChanged.connect(self.move_line)
+
+ @QtCore.pyqtSlot(float, name='on_ph1slider_valueChanged')
+ @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(str(int(evt.value())))
+
+
+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.apodcombobox.blockSignals(True)
+ for ap in self.apods:
+ self.apodcombobox.addItem(ap().name)
+ self.apodcombobox.blockSignals(False)
+
+ self.apod_graph = pg.PlotDataItem(x=[], y=[])
+ self.graphicsView.addItem(self.apod_graph)
+
+ self.mode = 'ap'
+
+ self.change_apodization(0)
+
+ def add_data(self, x, y):
+ real_plt = pg.PlotDataItem(x=x, y=y.real, pen=pg.mkPen('b'))
+ # imag_plt = pg.PlotDataItem(x=x, y=y.imag, pen=pg.mkPen('r'))
+ self.graphicsView.addItem(real_plt)
+ # self.graphicsView.addItem(imag_plt)
+
+ y_fft = fftshift(fft(y))
+ x_fft = fftshift(fftfreq(len(x), d=x[1]-x[0]))
+ real_plt_fft = pg.PlotDataItem(x=x_fft, y=y_fft.real, pen=pg.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))
+ self.data.append((x, y, x_fft))
+
+ xlimits = (max(x.min(), self._limits[0][0]), min(x.max(), self._limits[0][1]))
+ ylimit = max(self._limits[1], y.real.max())
+ self._limits = xlimits, ylimit
+
+ @QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged')
+ def change_apodization(self, index):
+ # delete old widgets
+ self.eqn_label.setText(convert(self.apods[index].equation))
+ while self.widget_layout.count():
+ item = self.widget_layout.takeAt(0)
+ try:
+ item.widget().deleteLater()
+ except AttributeError:
+ pass
+
+ # set up parameter widgets for new model
+ for k, v in enumerate(self.apods[index]().params):
+ widgt = FormWidget(name=v)
+ widgt.valueChanged.connect(self._temp_apod)
+ self.widget_layout.addWidget(widgt)
+
+ self.widget_layout.addStretch()
+ self._temp_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):
+ p = []
+ for i in range(self.widget_layout.count()):
+ item = self.widget_layout.itemAt(i)
+ w = item.widget()
+ try:
+ p.append(w.value)
+ except AttributeError:
+ continue
+
+ return p
+
+ def get_value(self):
+ apodmodel = self.apods[self.apodcombobox.currentIndex()]
+ p = self._get_parameter()
+
+ return p, apodmodel
diff --git a/nmreval/gui_qt/data/valueeditwidget.py b/nmreval/gui_qt/data/valueeditwidget.py
new file mode 100644
index 0000000..f79e32c
--- /dev/null
+++ b/nmreval/gui_qt/data/valueeditwidget.py
@@ -0,0 +1,336 @@
+from typing import Union, List
+
+import numpy as np
+
+from ..Qt import QtGui, QtCore, QtWidgets
+from .._py.valueeditor import Ui_MaskDialog
+
+
+class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog):
+ requestData = QtCore.pyqtSignal(str)
+ maskSignal = QtCore.pyqtSignal(str, list)
+ itemChanged = QtCore.pyqtSignal(str, tuple, object)
+ itemDeleted = QtCore.pyqtSignal(str, list)
+ itemAdded = QtCore.pyqtSignal(str)
+ values_selected = QtCore.pyqtSignal(str, list, list)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setupUi(self)
+
+ self.graph_shown = None
+ self.shown_set = None
+ self.items = {}
+
+ self.model = ValueModel()
+ self.model.itemChanged.connect(self.update_items)
+
+ self.selection_model = QtCore.QItemSelectionModel(self.model)
+ self.selection_model.selectionChanged.connect(self.show_position)
+
+ self.tableView.setModel(self.model)
+ self.tableView.setSelectionModel(self.selection_model)
+ self.tableView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
+ self.tableView.customContextMenuRequested.connect(self.ctx)
+
+ self.comboBox.currentIndexChanged.connect(self._populate_sets)
+ self.comboBox_2.currentIndexChanged.connect(self._populate_table)
+
+ def __call__(self, items: dict):
+ self.items = items
+
+ self.comboBox.blockSignals(True)
+ self.comboBox.clear()
+ for k, v in items.items():
+ self.comboBox.addItem(k[1], userData=k[0])
+ self.comboBox.blockSignals(False)
+
+ idx = self.comboBox.findData(self.graph_shown)
+ if idx == -1:
+ idx = 0
+
+ self.comboBox.setCurrentIndex(idx)
+ self.comboBox.currentIndexChanged.emit(idx)
+
+ return self
+
+ @QtCore.pyqtSlot(int)
+ def _populate_sets(self, idx: int):
+ if idx == -1:
+ self.comboBox.setCurrentIndex(0)
+ return
+
+ self.comboBox_2.blockSignals(True)
+ self.comboBox_2.clear()
+ self.graph_shown = self.comboBox.currentData()
+
+ if self.items:
+ for sid, name in self.items[(self.comboBox.currentData(), self.comboBox.currentText())]:
+ self.comboBox_2.addItem(name, userData=sid)
+ self.comboBox_2.blockSignals(False)
+
+ sidx = self.comboBox_2.findData(self.shown_set)
+ if sidx == -1:
+ sidx = 0
+
+ self.comboBox_2.setCurrentIndex(sidx)
+ self.comboBox_2.currentIndexChanged.emit(sidx)
+
+ @QtCore.pyqtSlot(int)
+ def _populate_table(self, idx):
+ self.selection_model.clearSelection()
+ self.shown_set = self.comboBox_2.itemData(idx)
+ self.requestData.emit(self.comboBox_2.itemData(idx))
+
+ def set_data(self, data: list, mask: np.ndarray):
+ self.selection_model.clearSelection()
+ self.model.loadData(data, mask)
+ self.spinBox.setMaximum(self.model.rowCount())
+
+ def ctx(self, pos: QtCore.QPoint):
+ idx = self.tableView.indexAt(pos)
+ if not idx.isValid():
+ return
+
+ menu = QtWidgets.QMenu()
+ menu.addSeparator()
+
+ hide_action = menu.addAction('Hide/Show')
+ menu.addSeparator()
+ del_action = menu.addAction('Delete')
+ menu.addSeparator()
+ cpc_action = menu.addAction('Copy to clipboard')
+ action = menu.exec(self.tableView.viewport().mapToGlobal(pos))
+
+ if action == hide_action:
+ self.mask_row()
+ elif action == del_action:
+ self.delete_item()
+ elif action == cpc_action:
+ self.copy_selection()
+
+ def keyPressEvent(self, evt):
+ if evt.matches(QtGui.QKeySequence.Copy):
+ self.copy_selection()
+ elif evt.key() == QtCore.Qt.Key_Delete:
+ self.delete_item()
+ else:
+ super().keyPressEvent(evt)
+
+ def copy_selection(self) -> str:
+ table = ''
+ for r in self.selection_model.selectedRows():
+ table += self.model.data(r) + '\t'
+ table += '\t'.join([self.model.data(r.sibling(r.row(), i)) for i in [1, 2]]) + '\n'
+
+ QtWidgets.QApplication.clipboard().setText(table)
+
+ return table
+
+ @QtCore.pyqtSlot(name='on_mask_button_clicked')
+ def mask_row(self):
+ for r in self.selection_model.selectedRows():
+ self.model.setData(r, not self.model.data(r, ValueModel.maskRole), ValueModel.maskRole)
+ self.maskSignal.emit(self.comboBox_2.currentData(), self.model.mask)
+
+ @QtCore.pyqtSlot(name='on_unmaskbutton_clicked')
+ def unmask(self):
+ self.model.unmask()
+ self.maskSignal.emit(self.comboBox_2.currentData(), self.model.mask)
+
+ @QtCore.pyqtSlot(name='on_delete_button_clicked')
+ def delete_item(self):
+ idx = [r.row() for r in self.selection_model.selectedRows()]
+ success = False
+ for i in sorted(idx, reverse=True):
+ success = self.model.removeRow(i)
+
+ if success:
+ self.itemDeleted.emit(self.comboBox_2.currentData(), idx)
+ self.spinBox.setMaximum(self.spinBox.maximum()-len(idx))
+
+ @QtCore.pyqtSlot(name='on_add_button_clicked')
+ def new_value(self):
+ success = self.model.addRows()
+ if success:
+ self.spinBox.setMaximum(self.spinBox.maximum()+1)
+ self.itemAdded.emit(self.comboBox_2.currentData())
+
+ @QtCore.pyqtSlot(int, int, str)
+ def update_items(self, col, row, val):
+ sid = self.comboBox_2.currentData()
+ new_value = complex(val)
+ new_value = new_value.real if new_value.imag == 0 else new_value
+
+ self.itemChanged.emit(sid, (col, row), new_value)
+
+ @QtCore.pyqtSlot(QtCore.QItemSelection, QtCore.QItemSelection)
+ def show_position(self, items_selected, items_deselected):
+ xvals = []
+ yvals = []
+ for idx in self.selection_model.selectedRows():
+ xvals.append(float(self.model.data(idx)))
+ try:
+ yvals.append(float(self.model.data(idx.sibling(idx.row(), 1))))
+ except ValueError:
+ yvals.append(complex(self.model.data(idx.sibling(idx.row(), 1))).real)
+
+ self.values_selected.emit(self.graph_shown, xvals, yvals)
+
+ @QtCore.pyqtSlot(name='on_toolButton_clicked')
+ def goto(self):
+ self.tableView.scrollTo(self.model.index(self.spinBox.value()-1, 0))
+
+
+class ValueModel(QtCore.QAbstractTableModel):
+ """
+ TableModel with lazy loading
+ """
+ itemChanged = QtCore.pyqtSignal(int, int, str)
+ load_number = 20
+ maskRole = QtCore.Qt.UserRole+321
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self._data = None
+ self.total_rows = 0
+ self.rows_loaded = 0
+ self.mask = None
+ self.headers = ['x', 'y', '\u0394y']
+ for i, hd in enumerate(self.headers):
+ self.setHeaderData(i, QtCore.Qt.Horizontal, hd)
+
+ def rowCount(self, *args, **kwargs) -> int:
+ return self.total_rows
+
+ def columnCount(self, *args, **kwargs) -> int:
+ return len(self.headers)
+
+ def loadData(self, data: List[np.ndarray], mask: np.ndarray):
+ self.beginResetModel()
+ self._data = []
+ for x, y, y_err in zip(*data):
+ self._data.append([x, y, y_err])
+ self.total_rows = len(self._data)
+
+ self.mask = mask.tolist()
+
+ self.endResetModel()
+ self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [QtCore.Qt.DisplayRole])
+
+ def data(self, idx: QtCore.QModelIndex, role=QtCore.Qt.DisplayRole) -> object:
+ if not idx.isValid():
+ return
+
+ row = idx.row()
+ if role in [QtCore.Qt.DisplayRole, QtCore.Qt.EditRole]:
+ val = self._data[row][idx.column()]
+ if isinstance(val, complex):
+ return f'{val.real:.8g}{val.imag:+.8g}j'
+ else:
+ return f'{val:.8g}'
+
+ elif role == QtCore.Qt.BackgroundRole:
+ pal = QtGui.QGuiApplication.palette()
+ if not self.mask[row]:
+ return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Base)
+ else:
+ return pal.color(QtGui.QPalette.Base)
+
+ elif role == QtCore.Qt.ForegroundRole:
+ pal = QtGui.QGuiApplication.palette()
+ if not self.mask[row]:
+ return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Text)
+ else:
+ return pal.color(QtGui.QPalette.Text)
+
+ elif role == ValueModel.maskRole:
+ return self.mask[row]
+
+ else:
+ return
+
+ def setData(self, idx: QtCore.QModelIndex, value: Union[str, bool], role=QtCore.Qt.DisplayRole) -> object:
+ col, row = idx.column(), idx.row()
+
+ if role == ValueModel.maskRole:
+ self.mask[row] = bool(value)
+ self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [role])
+
+ return True
+
+ if value:
+ if role == QtCore.Qt.EditRole:
+ try:
+ value = complex(value)
+ except ValueError:
+ # not a number
+ return False
+ self._data[row][col] = value.real if value.imag == 0 else value
+ self.itemChanged.emit(col, row, str(value))
+ self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [role])
+
+ else:
+ return super().setData(idx, value, role=role)
+
+ return True
+
+ else:
+ return False
+
+ def headerData(self, section: int, orientation, role=QtCore.Qt.DisplayRole) -> object:
+ if role == QtCore.Qt.DisplayRole:
+ if orientation == QtCore.Qt.Horizontal:
+ return self.headers[section]
+ else:
+ return str(section+1)
+
+ return
+
+ def canFetchMore(self, idx: QtCore.QModelIndex) -> bool:
+ if not idx.isValid():
+ return False
+
+ return self.total_rows > self.rows_loaded
+
+ def fetchMore(self, idx: QtCore.QModelIndex):
+ remaining = self.total_rows - self.rows_loaded
+ to_be_loaded = min(remaining, ValueModel.load_number)
+
+ self.beginInsertRows(QtCore.QModelIndex(), self.rows_loaded, self.rows_loaded + to_be_loaded - 1)
+ self.rows_loaded += to_be_loaded
+ self.endInsertRows()
+
+ def flags(self, idx: QtCore.QModelIndex) -> QtCore.Qt.ItemFlags:
+ return QtCore.QAbstractTableModel.flags(self, idx) | QtCore.Qt.ItemIsEditable
+
+ def removeRows(self, pos: int, rows: int, parent=None, *args, **kwargs) -> bool:
+ self.beginRemoveRows(parent, pos, pos+rows-1)
+
+ for _ in range(rows):
+ self._data.pop(pos)
+ self.mask.pop(pos)
+
+ self.endRemoveRows()
+ return True
+
+ def addRows(self, num=1):
+ return self.insertRows(self.rowCount(), num)
+
+ def insertRows(self, pos: int, rows: int, parent=QtCore.QModelIndex(), *args, **kwargs):
+ self.beginInsertRows(parent, pos, pos+rows-1)
+
+ for _ in range(rows):
+ self._data.insert(pos, [0.0] * self.columnCount())
+ self.mask.insert(pos, True)
+ self.total_rows += rows
+
+ self.endInsertRows()
+
+ return True
+
+ def unmask(self):
+ self.mask = [True] * self.total_rows
+ self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [ValueModel.maskRole])
diff --git a/nmreval/gui_qt/fit/__init__.py b/nmreval/gui_qt/fit/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nmreval/gui_qt/fit/fit_forms.py b/nmreval/gui_qt/fit/fit_forms.py
new file mode 100644
index 0000000..958e883
--- /dev/null
+++ b/nmreval/gui_qt/fit/fit_forms.py
@@ -0,0 +1,453 @@
+from typing import Tuple, Union
+
+from ...utils.text import convert
+from ..Qt import QtCore, QtWidgets, QtGui
+from .._py.fitmodelwidget import Ui_FitParameter
+from .._py.save_fitmodel_dialog import Ui_SaveDialog
+from ..lib import get_icon
+
+
+class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
+ value_requested = QtCore.pyqtSignal(object)
+ value_changed = QtCore.pyqtSignal(str)
+ state_changed = QtCore.pyqtSignal()
+
+ def __init__(self, label: str = 'Fitparameter', parent=None, fixed: bool = False):
+ super().__init__(parent)
+ self.setupUi(self)
+
+ self.parametername.setText(label + ' ')
+
+ validator = QtGui.QDoubleValidator()
+ validator.setDecimals(9)
+ self.parameter_line.setValidator(validator)
+ self.parameter_line.setText('1')
+ self.parameter_line.setMaximumWidth(60)
+ self.lineEdit.setMaximumWidth(60)
+ self.lineEdit_2.setMaximumWidth(60)
+
+ self.label_3.setText(f'< {label} <')
+
+ self.checkBox.stateChanged.connect(self.enableBounds)
+ self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit())
+ self.parameter_line.values_requested.connect(lambda: self.value_requested.emit(self))
+ self.parameter_line.editingFinished.connect(lambda: self.value_changed.emit(self.parameter_line.text()))
+ self.fixed_check.toggled.connect(self.set_fixed)
+
+ if fixed:
+ self.fixed_check.hide()
+
+ self.menu = QtWidgets.QMenu(self)
+ self.add_links()
+
+ self.is_linked = None
+ self.parameter_pos = None
+ self.func_idx = None
+
+ self._linetext = '1'
+
+ @property
+ def name(self):
+ return convert(self.parametername.text().strip(), old='html', new='str')
+
+ def set_parameter_string(self, p: str):
+ self.parameter_line.setText(str(p))
+ self.parameter_line.setToolTip(str(p))
+
+ def set_bounds(self, lb: float, ub: float, cbox: bool = True):
+ self.checkBox.setCheckState(QtCore.Qt.Checked if cbox else QtCore.Qt.Unchecked)
+ for val, bds_line in [(lb, self.lineEdit), (ub, self.lineEdit_2)]:
+ if val is not None:
+ bds_line.setText(str(val))
+ else:
+ bds_line.setText('')
+
+ def enableBounds(self, value: int):
+ self.lineEdit.setEnabled(value == 2)
+ self.lineEdit_2.setEnabled(value == 2)
+
+ def set_parameter(self, p: list, bds: Tuple[float, float, bool] = (None, None, False),
+ fixed: bool = False, glob: bool = False):
+ 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 = ' '.join([f'{pp:.4g}' for pp in p])
+
+ self.set_parameter_string(ptext)
+
+ self.set_bounds(*bds)
+
+ self.fixed_check.setCheckState(QtCore.Qt.Unchecked if fixed else QtCore.Qt.Checked)
+ self.global_checkbox.setCheckState(QtCore.Qt.Checked if glob else QtCore.Qt.Unchecked)
+
+ def get_parameter(self):
+ if self.is_linked:
+ try:
+ p = float(self._linetext)
+ except ValueError:
+ p = 1.0
+ else:
+ try:
+ p = float(self.parameter_line.text().replace(',', '.'))
+ except ValueError:
+ _ = QtWidgets.QMessageBox().warning(self, 'Invalid value',
+ f'{self.parametername.text()} contains invalid values',
+ QtWidgets.QMessageBox.Cancel)
+ return None
+
+ if self.checkBox.isChecked():
+ try:
+ lb = float(self.lineEdit.text().replace(',', '.'))
+ except ValueError:
+ lb = None
+
+ try:
+ rb = float(self.lineEdit_2.text().replace(',', '.'))
+ except ValueError:
+ rb = None
+ else:
+ lb = rb = None
+
+ bounds = (lb, rb)
+
+ return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked(), self.is_linked
+
+ @QtCore.pyqtSlot(bool)
+ def set_fixed(self, state: bool):
+ # self.global_checkbox.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()
+ def link_parameter(self, linkto=None):
+ if linkto is None:
+ action = self.sender()
+ else:
+ 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:
+ new_text = f'Linked to {action.parentWidget().title()}.{action.text()}'
+ self._linetext = self.parameter_line.text()
+ self.parameter_line.setText(new_text)
+ self.parameter_line.setEnabled(False)
+ self.global_checkbox.hide()
+ 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.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):
+ def __init__(self, types=None, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ if types is None:
+ types = []
+
+ self.comboBox.blockSignals(True)
+ self.comboBox.addItems(types)
+ self.comboBox.addItem('New group...')
+ self.comboBox.blockSignals(False)
+
+ self.frame.hide()
+
+ @QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged')
+ def new_group(self, idx: int):
+ if idx == self.comboBox.count() - 1:
+ self.frame.show()
+ else:
+ self.lineEdit_2.clear()
+ self.frame.hide()
+
+ @QtCore.pyqtSlot(name='on_toolButton_clicked')
+ def accept_group(self):
+ self.comboBox.insertItem(self.comboBox.count() - 1, self.lineEdit_2.text())
+ self.comboBox.setCurrentIndex(self.comboBox.count() - 2)
+
+ def accept(self):
+ if self.lineEdit.text():
+ self.close()
+
+
+class FitModelTree(QtWidgets.QTreeWidget):
+ icons = ['plus', 'mal_icon', 'minus_icon', 'geteilt_icon']
+
+ treeChanged = QtCore.pyqtSignal()
+ itemRemoved = QtCore.pyqtSignal(int)
+
+ counterRole = QtCore.Qt.UserRole + 1
+ operatorRole = QtCore.Qt.UserRole + 2
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setHeaderHidden(True)
+ self.setDragEnabled(True)
+ self.setDragDropMode(QtWidgets.QTreeWidget.InternalMove)
+ self.setDefaultDropAction(QtCore.Qt.MoveAction)
+
+ self.itemSelectionChanged.connect(lambda: self.treeChanged.emit())
+
+ def keyPressEvent(self, evt):
+ operators = [QtCore.Qt.Key_Plus, QtCore.Qt.Key_Asterisk,
+ QtCore.Qt.Key_Minus, QtCore.Qt.Key_Slash]
+
+ if evt.key() == QtCore.Qt.Key_Delete:
+ for item in self.selectedItems():
+ self.remove_function(item)
+
+ elif evt.key() == QtCore.Qt.Key_Space:
+ for item in self.treeWidget.selectedItems():
+ item.setCheckState(0, QtCore.Qt.Checked) if item.checkState(
+ 0) == QtCore.Qt.Unchecked else item.setCheckState(0, QtCore.Qt.Unchecked)
+
+ elif evt.key() in operators:
+ idx = operators.index(evt.key())
+ for item in self.selectedItems():
+ item.setData(0, self.operatorRole, idx)
+ item.setIcon(0, get_icon(self.icons[idx]))
+
+ else:
+ super().keyPressEvent(evt)
+
+ def dropEvent(self, evt: QtGui.QDropEvent):
+ super().dropEvent(evt)
+ self.treeChanged.emit()
+
+ def remove_function(self, item: QtWidgets.QTreeWidgetItem):
+ """
+ Remove function and children from tree and dictionary
+ """
+ while item.childCount():
+ self.remove_function(item.child(0))
+
+ if item.parent():
+ item.parent().removeChild(item)
+ else:
+ self.invisibleRootItem().removeChild(item)
+
+ idx = item.data(0, self.counterRole)
+ self.itemRemoved.emit(idx)
+
+ def add_function(self, idx: int, cnt: int, op: int, name: str, color: Union[QtGui.QColor, str, tuple],
+ parent: QtWidgets.QTreeWidgetItem = None, children: list = None, active: bool = True, **kwargs):
+ """
+ Add function to tree and dictionary of functions.
+ """
+ if not isinstance(color, QtGui.QColor):
+ if isinstance(color, tuple):
+ color = QtGui.QColor.fromRgbF(*color)
+ else:
+ color = QtGui.QColor(color)
+
+ it = QtWidgets.QTreeWidgetItem()
+ it.setData(0, QtCore.Qt.UserRole, idx)
+ it.setData(0, self.counterRole, cnt)
+ it.setData(0, self.operatorRole, op)
+ it.setText(0, name)
+ it.setForeground(0, QtGui.QBrush(color))
+
+ it.setIcon(0, get_icon(self.icons[op]))
+ it.setCheckState(0, QtCore.Qt.Checked if active else QtCore.Qt.Unchecked)
+
+ if parent is None:
+ self.addTopLevelItem(it)
+ else:
+ parent.addChild(it)
+
+ if children is not None:
+ for c in children:
+ self.add_function(**c, parent=it)
+
+ self.setCurrentIndex(self.indexFromItem(it, 0))
+
+ def sizeHint(self):
+ w = super().sizeHint().width()
+ return QtCore.QSize(w, 100)
+
+ def get_selected(self):
+ try:
+ it = self.selectedItems()[0]
+ function_nr = it.data(0, QtCore.Qt.UserRole)
+ idx = it.data(0, self.counterRole)
+
+ except IndexError:
+ function_nr = None
+ idx = None
+
+ return function_nr, idx
+
+ def get_functions(self, full=True, parent=None, pos=-1, return_pos=False):
+ """
+ Create nested list of functions in tree. Parameters saved are idx (Index of function in list of all functions),
+ cnt (counter of number to associate with functione values), ops (+, -, *, /), and maybe children.
+ """
+ if parent is None:
+ parent = self.invisibleRootItem()
+
+ funcs = []
+ for i in range(parent.childCount()):
+ pos += 1
+ it = parent.child(i)
+
+ child = {
+ 'idx': it.data(0, QtCore.Qt.UserRole),
+ 'op': it.data(0, self.operatorRole),
+ 'pos': pos,
+ 'active': (it.checkState(0) == QtCore.Qt.Checked),
+ 'children': []
+ }
+
+ if full:
+ child['name'] = it.text(0)
+ child['cnt'] = it.data(0, self.counterRole)
+ child['color'] = it.foreground(0).color().getRgbF()
+
+ if it.childCount():
+ child['children'], pos = self.get_functions(full=full, parent=it, pos=pos, return_pos=True)
+
+ funcs.append(child)
+
+ if return_pos:
+ return funcs, pos
+ else:
+ return funcs
+
+
+class FitTableWidget(QtWidgets.QTableWidget):
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.horizontalHeader().hide()
+ self.verticalHeader().hide()
+ self.setColumnCount(2)
+ self.setSelectionBehavior(QtWidgets.QTableWidget.SelectRows)
+ self.horizontalHeader().setStretchLastSection(True)
+ self.hideColumn(1)
+
+ def add_model(self, idx: str):
+ model_count = 0
+ for r in range(self.rowCount()):
+ cb = self.cellWidget(r, 1)
+ cb.addItem('Model ' + str(idx), userData=idx)
+ model_count = cb.count()
+
+ if model_count > 2:
+ if self.isColumnHidden(1):
+ self.showColumn(1)
+ self.resizeColumnToContents(0)
+ self.setColumnWidth(1, self.columnWidth(0) - self.columnWidth(1))
+
+ def remove_model(self, idx: str):
+ model_count = 0
+ for r in range(self.rowCount()):
+ cb = self.cellWidget(r, 1)
+ if cb.currentData() == idx:
+ cb.setCurrentIndex(0)
+ cb.removeItem(cb.findData(idx))
+ model_count = cb.count()
+
+ if model_count == 2:
+ self.hideColumn(1)
+ self.resizeColumnToContents(0)
+
+ def load(self, set_ids: list):
+ self.blockSignals(True)
+
+ while self.rowCount():
+ self.removeRow(0)
+
+ for (sid, name) in set_ids:
+ item = QtWidgets.QTableWidgetItem(name)
+ item.setCheckState(QtCore.Qt.Checked)
+ item.setData(QtCore.Qt.UserRole+1, sid)
+ row = self.rowCount()
+ self.setRowCount(row+1)
+ self.setItem(row, 0, item)
+
+ item2 = QtWidgets.QTableWidgetItem('')
+ self.setItem(row, 1, item2)
+ cb = QtWidgets.QComboBox()
+ cb.addItem('Default')
+ self.setCellWidget(row, 1, cb)
+
+ self.blockSignals(False)
+
+ def collect_data(self, default=None, include_name=False):
+
+ data = {}
+
+ for i in range(self.rowCount()):
+ item = self.item(i, 0)
+ if item.checkState() == QtCore.Qt.Checked:
+ mod = self.cellWidget(i, 1).currentData()
+ if mod is None:
+ mod = default
+
+ if include_name:
+ arg = (item.data(QtCore.Qt.UserRole+1), item.text())
+ else:
+ arg = item.data(QtCore.Qt.UserRole+1)
+
+ if mod not in data:
+ data[mod] = []
+ data[mod].append(arg)
+
+ return data
+
+ def data_list(self, include_name: bool = True) -> list:
+ ret_val = []
+ for i in range(self.rowCount()):
+ item = self.item(i, 0)
+ if include_name:
+ ret_val.append((item.data(QtCore.Qt.UserRole+1), item.text()))
+ else:
+ ret_val.append(item.data(QtCore.Qt.UserRole+1))
+
+ return ret_val
diff --git a/nmreval/gui_qt/fit/fit_parameter.py b/nmreval/gui_qt/fit/fit_parameter.py
new file mode 100644
index 0000000..96ff18b
--- /dev/null
+++ b/nmreval/gui_qt/fit/fit_parameter.py
@@ -0,0 +1,250 @@
+from ...utils.text import convert
+from ..Qt import QtWidgets, QtCore, QtGui
+from .._py.fitfuncwidget import Ui_FormFit
+from ..lib.forms import FormWidget, SelectionWidget
+from .fit_forms import FitModelWidget
+
+
+class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
+ value_requested = QtCore.pyqtSignal(int)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.func = None
+ self.func_idx = None
+ self.max_width = QtCore.QSize(0, 0)
+ self.global_parameter = []
+ self.data_parameter = []
+ self.glob_values = None
+ self.data_values = {}
+
+ self.scrollwidget.setLayout(QtWidgets.QVBoxLayout())
+ self.scrollwidget2.setLayout(QtWidgets.QVBoxLayout())
+
+ def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent):
+ if isinstance(evt, QtGui.QKeyEvent):
+ if (evt.key() == QtCore.Qt.Key_Right) and \
+ (evt.modifiers() == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier):
+ self.change_single_parameter(src.value, sender=src)
+ self.select_next_preview(1)
+
+ return True
+
+ elif (evt.key() == QtCore.Qt.Key_Left) and \
+ (evt.modifiers() == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier):
+ self.change_single_parameter(src.value, sender=src)
+ self.select_next_preview(-1)
+
+ return True
+
+ return super().eventFilter(src, evt)
+
+ def load(self, data):
+ self.comboBox.blockSignals(True)
+ while self.comboBox.count():
+ self.comboBox.removeItem(0)
+
+ for sid, name in data:
+ self.comboBox.addItem(name, userData=sid)
+ self._make_parameter(sid)
+ self.comboBox.blockSignals(False)
+
+ def set_function(self, func, idx):
+ self.func = func
+ self.func_idx = idx
+
+ self.glob_values = [1] * len(func.params)
+
+ for k, v in enumerate(func.params):
+ name = convert(v)
+ widgt = FitModelWidget(label=name, parent=self.scrollwidget)
+ widgt.parameter_pos = k
+ widgt.func_idx = idx
+ try:
+ widgt.set_bounds(*func.bounds[k], False)
+ except (AttributeError, IndexError):
+ pass
+
+ size = widgt.parametername.sizeHint()
+ if self.max_width.width() < size.width():
+ self.max_width = size
+
+ widgt.state_changed.connect(self.set_global)
+ widgt.value_requested.connect(self.look_for_value)
+ widgt.value_changed.connect(self.change_global_parameter)
+
+ self.global_parameter.append(widgt)
+ self.scrollwidget.layout().addWidget(widgt)
+
+ widgt2 = FormWidget(name=name, fixable=False, parent=self.scrollwidget2)
+ widgt2.valueChanged.connect(self.change_single_parameter)
+ widgt2.installEventFilter(self)
+ self.scrollwidget2.layout().addWidget(widgt2)
+ self.data_parameter.append(widgt2)
+
+ for w1, w2 in zip(self.global_parameter, self.data_parameter):
+ w1.parametername.setFixedSize(self.max_width)
+ w1.checkBox.setFixedSize(self.max_width)
+ w2.label.setFixedSize(self.max_width)
+
+ if hasattr(func, 'choices') and func.choices is not None:
+ cbox = func.choices
+ for c in cbox:
+ widgt = SelectionWidget(*c)
+ widgt.selectionChanged.connect(self.change_global_choice)
+ self.global_parameter.append(widgt)
+ self.glob_values.append(widgt.value)
+ self.scrollwidget.layout().addWidget(widgt)
+
+ widgt2 = SelectionWidget(*c)
+ widgt2.selectionChanged.connect(self.change_single_choice)
+ self.data_parameter.append(widgt2)
+ self.scrollwidget2.layout().addWidget(widgt2)
+
+ for i in range(self.comboBox.count()):
+ self._make_parameter(self.comboBox.itemData(i))
+
+ self.scrollwidget.layout().addStretch(1)
+ self.scrollwidget2.layout().addStretch(1)
+
+ def set_links(self, parameter):
+ for w in self.global_parameter:
+ if isinstance(w, FitModelWidget):
+ w.add_links(parameter)
+
+ @QtCore.pyqtSlot(str)
+ def change_global_parameter(self, value: str):
+ idx = self.global_parameter.index(self.sender())
+ self.glob_values[idx] = float(value)
+ if self.data_values[self.comboBox.currentData()][idx] is None:
+ self.data_parameter[idx].blockSignals(True)
+ self.data_parameter[idx].value = value
+ self.data_parameter[idx].blockSignals(False)
+
+ @QtCore.pyqtSlot(str, object)
+ def change_global_choice(self, argname, value):
+ idx = self.global_parameter.index(self.sender())
+ self.glob_values[idx] = value
+ if self.data_values[self.comboBox.currentData()][idx] is None:
+ self.data_parameter[idx].blockSignals(True)
+ self.data_parameter[idx].value = value
+ self.data_parameter[idx].blockSignals(False)
+
+ def change_single_parameter(self, value, sender=None):
+ if sender is None:
+ sender = self.sender()
+ idx = self.data_parameter.index(sender)
+ self.data_values[self.comboBox.currentData()][idx] = value
+
+ def change_single_choice(self, argname, value, sender=None):
+ if sender is None:
+ sender = self.sender()
+ idx = self.data_parameter.index(sender)
+ self.data_values[self.comboBox.currentData()][idx] = value
+
+ @QtCore.pyqtSlot(object)
+ def look_for_value(self, sender):
+ self.value_requested.emit(self.global_parameter.index(sender))
+
+ @QtCore.pyqtSlot()
+ def set_global(self):
+ # disable single parameter if it is set global, enable if global is unset
+ widget = self.sender()
+ idx = self.global_parameter.index(widget)
+ enable = (widget.global_checkbox.checkState() == QtCore.Qt.Unchecked) and (widget.is_linked is None)
+ self.data_parameter[idx].setEnabled(enable)
+
+ def select_next_preview(self, direction):
+ curr_idx = self.comboBox.currentIndex()
+ next_idx = (curr_idx + direction) % self.comboBox.count()
+ self.comboBox.setCurrentIndex(next_idx)
+
+ @QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged')
+ def change_data(self, idx: int):
+ # new dataset is selected, look for locally set parameter else use global values
+ sid = self.comboBox.itemData(idx)
+ if sid not in self.data_values:
+ self._make_parameter(sid)
+
+ for i, value in enumerate(self.data_values[sid]):
+ w = self.data_parameter[i]
+ w.blockSignals(True)
+ if value is None:
+ w.value = self.glob_values[i]
+ else:
+ w.value = value
+ w.blockSignals(False)
+
+ def _make_parameter(self, sid):
+ if sid not in self.data_values:
+ self.data_values[sid] = [None] * len(self.data_parameter)
+
+ def get_parameter(self, use_func=None):
+ bds = []
+ is_global = []
+ is_fixed = []
+ globs = []
+ is_linked = []
+
+ for g in self.global_parameter:
+ if isinstance(g, FitModelWidget):
+ p_i, bds_i, fixed_i, global_i, link_i = g.get_parameter()
+
+ globs.append(p_i)
+ bds.append(bds_i)
+ is_fixed.append(fixed_i)
+ is_global.append(global_i)
+ is_linked.append(link_i)
+
+ lb, ub = list(zip(*bds))
+
+ data_parameter = {}
+ if use_func is None:
+ use_func = list(self.data_values.keys())
+
+ global_p = None
+ for sid, parameter in self.data_values.items():
+ if sid not in use_func:
+ continue
+
+ kw_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)):
+ if isinstance(g, FitModelWidget):
+ if (p_i is None) or is_global[i]:
+ p.append(globs[i])
+ if is_global[i]:
+ 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:
+ p.append(p_i)
+
+ try:
+ if p[i] > ub[i]:
+ raise ValueError(f'Parameter {g.name} is outside bounds ({lb[i]}, {ub[i]})')
+ except TypeError:
+ pass
+ try:
+ if p[i] < lb[i]:
+ raise ValueError(f'Parameter {g.name} is outside bounds ({lb[i]}, {ub[i]})')
+ except TypeError:
+ pass
+
+ else:
+ if p_i is None:
+ kw_p[g.argname] = g.value
+ else:
+ kw_p[g.argname] = p_i
+
+ data_parameter[sid] = (p, kw_p)
+
+ return data_parameter, lb, ub, is_fixed, global_p, is_linked
diff --git a/nmreval/gui_qt/fit/fitfunction.py b/nmreval/gui_qt/fit/fitfunction.py
new file mode 100644
index 0000000..bff72d2
--- /dev/null
+++ b/nmreval/gui_qt/fit/fitfunction.py
@@ -0,0 +1,240 @@
+from itertools import cycle, count
+from typing import List, Tuple, Union
+
+from nmreval.configs import config_paths
+from ... import models
+from ...lib.importer import find_models
+from ...lib.colors import BaseColor, Tab10
+from ...utils.text import convert
+from ..Qt import QtWidgets, QtCore, QtGui
+from .._py.fitfunctionwidget import Ui_Form
+from .fit_forms import FitModelTree
+from ..lib import get_icon
+
+
+class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
+ func_cnt = count()
+ func_colors = cycle(Tab10)
+ op_names = ['plus_icon', 'mal_icon', 'minus_icon', 'geteilt_icon']
+
+ newFunction = QtCore.pyqtSignal(int, int)
+ treeChanged = QtCore.pyqtSignal()
+ itemRemoved = QtCore.pyqtSignal(int)
+ showFunction = QtCore.pyqtSignal(int)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setupUi(self)
+ self.functree = FitModelTree()
+ self.widget.setText('Model structure')
+ self.widget.addWidget(self.functree)
+
+ self._types = []
+ self.functions = find_models(models)
+ try:
+ self.functions += find_models(config_paths() / 'usermodels.py')
+ except FileNotFoundError:
+ pass
+
+ for m in self.functions:
+ try:
+ m.type
+ except AttributeError:
+ m.type = 'Other'
+
+ if m.type not in self._types:
+ self._types.append(m.type)
+
+ self.typecomboBox.addItems(sorted(self._types))
+
+ self.functree.treeChanged.connect(lambda: self.treeChanged.emit())
+ self.functree.itemRemoved.connect(self.remove_function)
+
+ self.iscomplex = False
+ self.complex_widget.hide()
+
+ for i, op_icon in enumerate(self.op_names):
+ self.operator_combobox.setItemIcon(i, get_icon(op_icon))
+
+ def __len__(self):
+ return self.use_combobox.count()
+
+ @QtCore.pyqtSlot(int, name='on_typecomboBox_currentIndexChanged')
+ def change_group(self, idx: int):
+ """
+ Change items in fitcombobox to new entries
+ """
+ self.fitcomboBox.blockSignals(True)
+ while self.fitcomboBox.count():
+ self.fitcomboBox.removeItem(0)
+
+ selected_type = self.typecomboBox.itemText(idx)
+ for m in self.functions:
+ if m.type == selected_type:
+ self.fitcomboBox.addItem(m.name, userData=self.functions.index(m))
+ self.fitcomboBox.blockSignals(False)
+ self.on_fitcomboBox_currentIndexChanged(0)
+
+ @QtCore.pyqtSlot(int, name='on_fitcomboBox_currentIndexChanged')
+ def change_function(self, idx: int):
+ """
+ Display new equation on changing function
+ """
+ index = self.fitcomboBox.itemData(idx)
+ if self.functions:
+ fitfunc = self.functions[index]
+ try:
+ self.fitequation.setText(convert(fitfunc.equation))
+ except AttributeError:
+ self.fitequation.setText('')
+
+ @QtCore.pyqtSlot(name='on_use_function_button_clicked')
+ def new_function(self):
+ idx = self.fitcomboBox.itemData(self.fitcomboBox.currentIndex())
+ cnt = next(self.func_cnt)
+ op = self.operator_combobox.currentIndex()
+ name = self.functions[idx].name
+ col = next(self.func_colors)
+
+ self.newFunction.emit(idx, cnt)
+
+ self.add_function(idx, cnt, op, name, col)
+
+ def add_function(self, idx: int, cnt: int, op: int,
+ name: str, color: Union[str, Tuple[float, float, float], BaseColor], **kwargs):
+ """
+ Add function to tree and dictionary of functions.
+ """
+ if isinstance(color, BaseColor):
+ qcolor = QtGui.QColor.fromRgbF(*color.rgb(normed=True))
+ elif isinstance(color, tuple):
+ qcolor = QtGui.QColor.fromRgbF(*color)
+ else:
+ qcolor = QtGui.QColor(color)
+ self.functree.add_function(idx, cnt, op, name, qcolor, **kwargs)
+
+ self.use_combobox.addItem(name, userData=cnt)
+
+ self.use_combobox.setItemData(self.use_combobox.count()-1, color, QtCore.Qt.DecorationRole)
+ self.use_combobox.setCurrentIndex(self.use_combobox.count() - 1)
+
+ f = self.functions[idx]
+ if hasattr(f, 'iscomplex') and f.iscomplex:
+ self.iscomplex = True
+ self.complex_widget.show()
+
+ @QtCore.pyqtSlot(int)
+ def remove_function(self, idx: int):
+ iterator = QtWidgets.QTreeWidgetItemIterator(self.functree)
+ self.iscomplex = False
+ while iterator.value():
+ item = iterator.value()
+ f = self.functions[item.data(0, QtCore.Qt.UserRole)]
+ if hasattr(f, 'iscomplex') and f.iscomplex:
+ self.iscomplex = True
+ break
+
+ iterator += 1
+ self.complex_widget.setVisible(self.iscomplex)
+
+ for i in range(self.use_combobox.count()):
+ if idx == self.use_combobox.itemData(i):
+ self.use_combobox.removeItem(i)
+ break
+
+ self.itemRemoved.emit(idx)
+
+ @QtCore.pyqtSlot(int, name='on_use_combobox_currentIndexChanged')
+ def show_parameter(self, idx: int):
+ if self.use_combobox.count():
+ self.showFunction.emit(self.use_combobox.itemData(idx, QtCore.Qt.UserRole))
+
+ def get_selected(self):
+ function_nr, idx = self.functree.get_selected()
+
+ if function_nr is not None:
+ return self.functions[function_nr], idx
+ else:
+ return None, None
+
+ def get_functions(self, full: bool = True, clsname=False, include_all: bool = True):
+ """
+ Create nested list of functions in tree. Parameters saved are idx (Index of function in list of all functions),
+ cnt (counter of number to associate with functione values), ops (+, -, *, /), and maybe children.
+ """
+
+ used_functions = self.functree.get_functions(full=full)
+ self._prepare_function_for_model(used_functions, full=full, clsname=clsname, include_all=include_all)
+
+ return used_functions
+
+ def _prepare_function_for_model(self, func_list: List[dict],
+ full: bool = True, clsname: bool = False, include_all: bool = True):
+
+ for func_args in func_list:
+ is_active = func_args.get('active')
+ if (not is_active) and (not include_all):
+ continue
+
+ if not clsname:
+ func_args['func'] = self.functions[func_args['idx']]
+ else:
+ func_args['func'] = self.functions[func_args['idx']].name
+
+ if not full:
+ func_args.pop('active')
+ func_args.pop('idx')
+
+ if func_args['children']:
+ self._prepare_function_for_model(func_args['children'],
+ full=full, clsname=clsname,
+ include_all=include_all)
+
+ def get_parameter_list(self):
+ all_parameters = {}
+
+ iterator = QtWidgets.QTreeWidgetItemIterator(self.functree)
+ while iterator.value():
+ item = iterator.value()
+ f = self.functions[item.data(0, QtCore.Qt.UserRole)]
+ cnt = item.data(0, self.functree.counterRole)
+ all_parameters[f'{f.name}_{cnt}'] = [(convert(pp, new='str'), (cnt, i)) for i, pp in enumerate(f.params)]
+
+ iterator += 1
+
+ return all_parameters
+
+ def get_complex_state(self):
+ return self.complex_comboBox.currentIndex() if self.iscomplex else None
+
+ def set_complex_state(self, state):
+ if state is not None:
+ self.complex_comboBox.setCurrentIndex(state)
+
+ def clear(self):
+ self.functree.blockSignals(True)
+ self.functree.clear()
+ self.functree.blockSignals(False)
+
+ self.use_combobox.blockSignals(True)
+ self.use_combobox.clear()
+ self.use_combobox.blockSignals(False)
+
+ self.complex_comboBox.setCurrentIndex(0)
+ self.complex_widget.hide()
+
+
+if __name__ == '__main__':
+ import sys
+ from numpy.random import choice
+
+ app = QtWidgets.QApplication(sys.argv)
+ qw = choice(QtWidgets.QStyleFactory.keys())
+
+ app.setStyle(QtWidgets.QStyleFactory.create(qw))
+
+ fd = QFunctionWidget()
+ fd.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/fit/fitwindow.py b/nmreval/gui_qt/fit/fitwindow.py
new file mode 100644
index 0000000..55099a9
--- /dev/null
+++ b/nmreval/gui_qt/fit/fitwindow.py
@@ -0,0 +1,455 @@
+from itertools import count, cycle
+from string import ascii_letters
+
+from pyqtgraph import PlotDataItem, mkPen
+
+from nmreval.gui_qt.lib.pg_objects import PlotItem
+from ...fit._meta import MultiModel, ModelFactory
+from ..Qt import QtGui, QtCore, QtWidgets
+from .._py.fitdialog import Ui_FitDialog
+from .fit_forms import FitTableWidget
+from .fit_parameter import QFitParameterWidget
+
+
+class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
+ func_cnt = count()
+ model_cnt = cycle(ascii_letters)
+ preview_num = 201
+
+ preview_emit = QtCore.pyqtSignal(dict, int, bool)
+ fitStartSig = QtCore.pyqtSignal(dict, list, dict)
+ abortFit = QtCore.pyqtSignal()
+
+ def __init__(self, mgmt=None, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.parameters = {}
+ self.preview_lines = []
+ self._current_function = None
+ self.function_widgets = {}
+ self._management = mgmt
+
+ self._current_model = next(QFitDialog.model_cnt)
+ self.show_combobox.setItemData(0, self._current_model, QtCore.Qt.UserRole)
+ self.default_combobox.setItemData(0, self._current_model, QtCore.Qt.UserRole)
+
+ self.data_table = FitTableWidget(self.data_widget)
+ self.data_widget.addWidget(self.data_table)
+ self.data_widget.setText('Data')
+
+ self.models = {}
+ self._func_list = {}
+ self._complex = {}
+
+ self.connected_figure = ''
+
+ self.model_frame.hide()
+ self.preview_button.hide()
+
+ self.abort_button.clicked.connect(lambda: self.abortFit.emit())
+
+ self.functionwidget.newFunction.connect(self.add_function)
+ self.functionwidget.showFunction.connect(self.show_function_parameter)
+ self.functionwidget.itemRemoved.connect(self.remove_function)
+
+ @QtCore.pyqtSlot(int, int)
+ def add_function(self, function_idx: int, function_id: int):
+ self.show_function_parameter(function_id, function_idx)
+ self.newmodel_button.setEnabled(True)
+
+ @QtCore.pyqtSlot(int)
+ def remove_function(self, idx: int):
+ """
+ Remove function and children from tree and dictionary
+ """
+ w = self.function_widgets[idx]
+ self.stackedWidget.removeWidget(w)
+ w.deleteLater()
+ del self.function_widgets[idx]
+
+ if len(self.functionwidget) == 0:
+ # empty model
+ self.newmodel_button.setEnabled(False)
+ self.deletemodel_button.setEnabled(False)
+ self._current_function = None
+
+ else:
+ self._current_function = self.functionwidget.use_combobox.currentData()
+
+ @QtCore.pyqtSlot(int)
+ def show_function_parameter(self, function_id: int, function_idx: int = None):
+ """
+ Display parameter associated with selected function.
+ """
+ if function_id in self.function_widgets:
+ dialog = self.function_widgets[function_id]
+
+ else:
+ # create new widget for function
+ if function_idx is not None:
+ function = self.functionwidget.functions[function_idx]
+ else:
+ raise ValueError('No function index given')
+
+ if function is None:
+ return
+
+ dialog = QFitParameterWidget()
+ data_names = self.data_table.data_list(include_name=True)
+
+ dialog.set_function(function, function_idx)
+ dialog.load(data_names)
+ dialog.value_requested.connect(self.look_value)
+
+ self.stackedWidget.addWidget(dialog)
+ self.function_widgets[function_id] = dialog
+
+ self.stackedWidget.setCurrentWidget(dialog)
+
+ # collect parameter names etc. to allow linkage
+ self._func_list[self._current_model] = self.functionwidget.get_parameter_list()
+ dialog.set_links(self._func_list)
+
+ # show same tab (general parameter/Data parameter)
+ tab_idx = 0
+ if self._current_function is not None:
+ tab_idx = self.function_widgets[self._current_function].tabWidget.currentIndex()
+ dialog.tabWidget.setCurrentIndex(tab_idx)
+
+ self._current_function = function_id
+
+ def look_value(self, idx):
+ func_widget = self.function_widgets[self._current_function]
+ set_ids = [func_widget.comboBox.itemData(i) for i in range(func_widget.comboBox.count())]
+ for s in set_ids:
+ func_widget.data_values[s][idx] = self._management[s].value
+ func_widget.change_data(func_widget.comboBox.currentIndex())
+
+ def get_functions(self):
+ """ update functions, parameters"""
+ self.models[self._current_model] = self.functionwidget.get_functions()
+ self._complex[self._current_model] = self.functionwidget.get_complex_state()
+ self._func_list[self._current_model] = self.functionwidget.get_parameter_list()
+
+ def load(self, ids: list):
+ """
+ Add name and id of dataset to list.
+ """
+ self.data_table.load(ids)
+ if self.models:
+ for m in self.models.keys():
+ self.data_table.add_model(m)
+ else:
+ self.data_table.add_model(self._current_model)
+
+ for dialog in self.function_widgets.values():
+ dialog.load(ids)
+
+ @QtCore.pyqtSlot(name='on_newmodel_button_clicked')
+ def make_new_model(self):
+ """
+ Save model with all its functions in dictionary and adjust gui.
+ """
+ self.deletemodel_button.setEnabled(True)
+ self.model_frame.show()
+ idx = next(QFitDialog.model_cnt)
+
+ self.data_table.add_model(idx)
+
+ self.default_combobox.addItem('Model '+idx, userData=idx)
+ self.show_combobox.addItem('Model '+idx, userData=idx)
+ self.show_combobox.setItemData(self.show_combobox.count()-1, idx, QtCore.Qt.UserRole)
+ self.show_combobox.setCurrentIndex(self.show_combobox.count()-1)
+
+ self._current_model = idx
+ self.stackedWidget.setCurrentIndex(0)
+
+ @QtCore.pyqtSlot(int, name='on_show_combobox_currentIndexChanged')
+ def change_model(self, idx: int):
+ """
+ Save old model and display new model.
+ """
+ self.get_functions()
+ self.functionwidget.clear()
+
+ self._current_model = self.show_combobox.itemData(idx, QtCore.Qt.UserRole)
+ if self._current_model in self.models and len(self.models[self._current_model]):
+ for el in self.models[self._current_model]:
+ self.functionwidget.add_function(**el)
+ self.functionwidget.set_complex_state(self._complex[self._current_model])
+ else:
+ self.stackedWidget.setCurrentIndex(0)
+
+ @QtCore.pyqtSlot(name='on_deletemodel_button_clicked')
+ def remove_model(self):
+ model_id = self._current_model
+
+ self.show_combobox.removeItem(self.show_combobox.findData(model_id))
+ self.default_combobox.removeItem(self.default_combobox.findData(model_id))
+
+ for m in self.models[model_id]:
+ func_id = m['cnt']
+ self.stackedWidget.removeWidget(self.function_widgets[func_id])
+
+ self.function_widgets.pop(func_id)
+
+ self._complex.pop(model_id)
+ self._func_list.pop(model_id)
+ self.models.pop(model_id)
+
+ self.data_table.remove_model(model_id)
+
+ if len(self.models) == 1:
+ self.model_frame.hide()
+
+ def _prepare(self, model: list, function_use=None, parameter=None, add_idx=False, cnt=0):
+ if parameter is None:
+ parameter = {'parameter': {}, 'lb': (), 'ub': (), 'var': [],
+ 'glob': {'idx': [], 'p': [], 'var': [], 'lb': [], 'ub': []},
+ 'links': [], 'color': []}
+
+ for i, f in enumerate(model):
+ if not f['active']:
+ continue
+ try:
+ p, lb, ub, var, glob, links = self.function_widgets[f['cnt']].get_parameter(function_use)
+ except ValueError as e:
+ _ = QtWidgets.QMessageBox().warning(self, 'Invalid value', str(e),
+ QtWidgets.QMessageBox.Ok)
+ return None, -1
+
+ p_len = len(parameter['lb'])
+
+ parameter['lb'] += lb
+ parameter['ub'] += ub
+ parameter['var'] += var
+ parameter['links'] += links
+ parameter['color'] += [f['color']]
+
+ for p_k, v_k in p.items():
+ if add_idx:
+ kw_k = {f'{k}_{cnt}': v for k, v in v_k[1].items()}
+ else:
+ kw_k = v_k[1]
+
+ if p_k in parameter['parameter']:
+ params, kw = parameter['parameter'][p_k]
+ params += v_k[0]
+ kw.update(kw_k)
+ else:
+ parameter['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:
+ cnt += 1
+
+ if f['children']:
+ # recurse for children
+ child_parameter, cnt = self._prepare(f['children'], parameter=parameter, add_idx=add_idx, cnt=cnt)
+
+ return parameter, cnt
+
+ @QtCore.pyqtSlot(name='on_fit_button_clicked')
+ def start_fit(self):
+ self.get_functions()
+
+ data = self.data_table.collect_data(default=self.default_combobox.currentData())
+
+ func_dict = {}
+ for k, mod in self.models.items():
+ func, order, param_len = ModelFactory.create_from_list(mod)
+
+ if func is None:
+ continue
+
+ if k in data:
+ parameter, _ = self._prepare(mod, function_use=data[k], add_idx=isinstance(func, MultiModel))
+ if parameter is None:
+ return
+
+ parameter['func'] = func
+ parameter['order'] = order
+ parameter['len'] = param_len
+ if self._complex[k] is None:
+ parameter['complex'] = self._complex[k]
+ else:
+ parameter['complex'] = ['complex', 'real', 'imag'][self._complex[k]]
+
+ func_dict[k] = parameter
+
+ replaceable = []
+ for k, v in func_dict.items():
+ for i, link_i in enumerate(v['links']):
+ if link_i is None:
+ continue
+
+ rep_model, rep_func, rep_pos = link_i
+ try:
+ f = func_dict[rep_model]
+ except KeyError:
+ QtWidgets.QMessageBox().warning(self, 'Invalid value',
+ 'Parameter cannot be linked: Model is unused',
+ QtWidgets.QMessageBox.Ok)
+ return
+
+ try:
+ f_idx = f['order'].index(rep_func)
+ except ValueError:
+ QtWidgets.QMessageBox().warning(self, 'Invalid value',
+ 'Parameter cannot be linked: '
+ 'Function is probably not checked or deleted',
+ QtWidgets.QMessageBox.Ok)
+ return
+
+ repl_idx = sum(f['len'][:f_idx])+rep_pos
+ if repl_idx not in f['glob']['idx']:
+ _ = QtWidgets.QMessageBox().warning(self, 'Invalid value',
+ 'Parameter cannot be linked: '
+ 'Destination is not a global parameter.',
+ QtWidgets.QMessageBox.Ok)
+ return
+
+ replaceable.append((k, i, rep_model, repl_idx))
+
+ replace_value = None
+ for p_k in f['parameter'].values():
+ replace_value = p_k[0][repl_idx]
+ break
+
+ if replace_value is not None:
+ for p_k in v['parameter'].values():
+ p_k[0][i] = replace_value
+
+ weight = ['None', 'y', 'y2', 'Deltay'][self.weight_combobox.currentIndex()]
+
+ fit_args = {'we': weight}
+
+ if func_dict:
+ self.fitStartSig.emit(func_dict, replaceable, fit_args)
+
+ return func_dict
+
+ @QtCore.pyqtSlot(int, name='on_preview_checkbox_stateChanged')
+ def show_preview(self, state: int):
+ print('state', state)
+ if state:
+ self.preview_button.show()
+ self.preview_checkbox.setText('')
+
+ self._prepare_preview()
+
+ else:
+ self.preview_emit.emit({}, -1, False)
+ self.preview_lines = []
+ self.preview_button.hide()
+ self.preview_checkbox.setText('Preview')
+
+ @QtCore.pyqtSlot(name='on_preview_button_clicked')
+ def _prepare_preview(self):
+ self.get_functions()
+
+ default_model = self.default_combobox.currentData()
+ data = self.data_table.collect_data(default=default_model)
+
+ func_dict = {}
+ for k, mod in self.models.items():
+ func, order, param_len = ModelFactory.create_from_list(mod)
+ multiple_funcs = isinstance(func, MultiModel)
+
+ if k in data:
+ parameter, _ = self._prepare(mod, function_use=data[k], add_idx=multiple_funcs)
+ parameter['func'] = func
+ parameter['order'] = order
+ parameter['len'] = param_len
+
+ func_dict[k] = parameter
+
+ for v in func_dict.values():
+ for i, link_i in enumerate(v['links']):
+ if link_i is None:
+ continue
+
+ rep_model, rep_func, rep_pos = link_i
+ f = func_dict[rep_model]
+ f_idx = f['order'].index(rep_func)
+ repl_idx = sum(f['len'][:f_idx]) + rep_pos
+
+ replace_value = None
+ for p_k in f['parameter'].values():
+ replace_value = p_k[0][repl_idx]
+ break
+
+ if replace_value is not None:
+ for p_k in v['parameter'].values():
+ p_k[0][i] = replace_value
+
+ self.preview_emit.emit(func_dict, QFitDialog.preview_num, True)
+
+ def make_previews(self, x, models_parameters: dict):
+ self.preview_lines = []
+
+ for k, model in models_parameters.items():
+ f = model['func']
+ is_complex = self._complex[k]
+
+ parameters = model['parameter']
+ color = model['color']
+
+ for p, kwargs in parameters.values():
+ y = f.func(x, *p, **kwargs)
+ if is_complex is None:
+ self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3)))
+
+ elif is_complex == 0:
+ 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)))
+ elif is_complex == 1:
+ self.preview_lines.append(PlotItem(x=x, y=y.real, pen=mkPen(width=3)))
+ else:
+ self.preview_lines.append(PlotItem(x=x, y=y.imag, pen=mkPen(width=3)))
+
+ if isinstance(f, MultiModel):
+ for i, s in enumerate(f.subs(x, *p, **kwargs)):
+ pen_i = mkPen(QtGui.QColor.fromRgbF(*color[i]))
+ if is_complex is None:
+ self.preview_lines.append(PlotItem(x=x, y=s, pen=pen_i))
+ elif is_complex == 0:
+ self.preview_lines.append(PlotItem(x=x, y=s.real, pen=pen_i))
+ self.preview_lines.append(PlotItem(x=x, y=s.imag, pen=pen_i))
+ elif is_complex == 1:
+ self.preview_lines.append(PlotItem(x=x, y=s.real, pen=pen_i))
+ else:
+ self.preview_lines.append(PlotItem(x=x, y=s.imag, pen=pen_i))
+
+ return self.preview_lines
+
+ def closeEvent(self, evt: QtGui.QCloseEvent):
+ self.preview_emit.emit({}, -1, False)
+ self.preview_lines = []
+
+ super().closeEvent(evt)
+
+
+if __name__ == '__main__':
+ import sys
+ from numpy.random import choice
+
+ app = QtWidgets.QApplication(sys.argv)
+ # while qw == 'QtCurve':
+ qw = choice(QtWidgets.QStyleFactory.keys())
+ app.setStyle(QtWidgets.QStyleFactory.create(qw))
+
+ fd = QFitDialog()
+
+ fd.load([('fff', 'testtesttesttest'),
+ ('ggg', 'testtesttesttesttest'),
+ ('hhh', 'testtesttesttesttesttest')])
+ fd.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/fit/function_creation_dialog.py b/nmreval/gui_qt/fit/function_creation_dialog.py
new file mode 100644
index 0000000..0a9f07b
--- /dev/null
+++ b/nmreval/gui_qt/fit/function_creation_dialog.py
@@ -0,0 +1,247 @@
+import re
+
+import numexpr as ne
+import numpy as np
+
+from nmreval.gui_qt.Qt import QtCore, QtWidgets
+from nmreval.gui_qt._py.fitcreationdialog import Ui_Dialog
+
+
+_numexpr_funcs = []
+for k, _ in ne.expressions.functions.items():
+ pat = k + r'\('
+ _numexpr_funcs.append((re.compile(pat), 'np.' + k + '('))
+
+
+class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog):
+ classCreated = QtCore.pyqtSignal(object)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.namespace_widget.make_namespace()
+
+ self.tableWidget.itemChanged.connect(self.update_function)
+
+ self.groupBox.toggled.connect(self.change_visibility)
+ self.groupBox_2.toggled.connect(self.change_visibility)
+ self.groupBox_3.toggled.connect(self.change_visibility)
+ self.groupBox_4.toggled.connect(self.change_visibility)
+
+ self.groupBox.setChecked(True)
+
+ def __call__(self, *args, **kwargs):
+ for w in [self.lineEdit_4, self.lineEdit, self.lineEdit_3, self.lineEdit_2,
+ self.parameterLineEdit, self.externalParametersLineEdit]:
+ w.clear()
+
+ def check(self):
+ self.name = self.name_lineedit.text()
+ self.group = self.group_lineedit.text()
+ self.eq = str(self.lineEdit.text())
+ self.p = str(self.parameterLineEdit.text()).split()
+ self.func = str(self.lineEdit_4.text())
+ self._func_string = ''
+
+ error = []
+ for k, v in [('Name', self.name), ('Group', self.group), ('Parameters', self.p), ('Function', self.func)]:
+ if not v:
+ error.append('Empty ' + str(k))
+ if self.name:
+ if self.name[0].isdigit():
+ error.append('Name starts with digit')
+ if self.p:
+ if set(self.p) & set(self.ext_p):
+ error.append('Duplicate entries: {}'.format(list(set(self.p) & set(self.ext_p))))
+ if self.p and self.func:
+ p_test = np.ones((len(self.p)+len(self.ext_p)))
+ _x = np.arange(2)
+ namespace = {'x': _x}
+ for i, pp in enumerate(p_test):
+ namespace[f'p_{i}'] = pp
+ self._func_string = self.func + ''
+ self._func_string = self._func_string.replace('[', '_').replace(']', '')
+ try:
+ ne.evaluate(self._func_string, local_dict=namespace)
+ except KeyError:
+ error.append(f'Incorrect evaluation {self.func}')
+
+ if error:
+ QtWidgets.QMessageBox().warning(self, 'Invalid entries', '\n'.join(error))
+ else:
+ return True
+
+ def accept(self):
+ self.confirm()
+ super().accept()
+
+ def confirm(self):
+ print(f' name = {self.name_lineedit.text()}')
+ group_type = self.group_lineedit.text()
+ if group_type:
+ print(f' group = "{group_type}"')
+ else:
+ print(' group = "User-defined"')
+ var = []
+ for row in range(self.tableWidget.rowCount()):
+ var.append(self.tableWidget.item(row, 1).text())
+ if var:
+ print(' params = [r"', end='')
+ print('", r"'.join(var) + '"]')
+ else:
+ print(' params = []')
+
+ print('\n@staticmethod')
+ print(self.label.text())
+ import inspect
+ for k, v in self.namespace_widget.namespace.flatten().items():
+ if inspect.isfunction(v):
+ print(k, inspect.getmodule(v))
+ print(k, [cc[1] for cc in inspect.getmembers(v) if cc[0] == '__qualname__'])
+ else:
+ print(k, v)
+ print(self.plainTextEdit.toPlainText())
+
+ @QtCore.pyqtSlot(name='on_parameter_button_clicked')
+ def add_variable(self):
+ self.tableWidget.blockSignals(True)
+ row = self.tableWidget.rowCount()
+ self.tableWidget.setRowCount(row+1)
+
+ self.tableWidget.setItem(row, 0, QtWidgets.QTableWidgetItem('p'+str(row)))
+ self.tableWidget.setItem(row, 1, QtWidgets.QTableWidgetItem('p_{'+str(row)+'}'))
+ self.tableWidget.setItem(row, 2, QtWidgets.QTableWidgetItem('--'))
+ self.tableWidget.setItem(row, 3, QtWidgets.QTableWidgetItem('--'))
+ self.tableWidget.blockSignals(False)
+ self.update_function(None)
+
+ @QtCore.pyqtSlot(name='on_selection_button_clicked')
+ def add_choice(self):
+ cnt = self.tabWidget.count()
+ self.tabWidget.addTab(ChoiceWidget(self), 'choice' + str(cnt))
+ self.tabWidget.setCurrentIndex(cnt)
+
+ def register(self):
+ i = 0
+ basename = self.name.replace(' ', '')
+ classname = basename
+ # while classname in _userfits:
+ # classname = basename + '_' + str(i)
+ # i += 1
+ c = register_class(classname, self.name, self.group, self.p, self.eq, self._func_string)
+ self.classCreated.emit(c)
+ return classname, c
+
+ def save(self, cname):
+ t = '\n# Created automatically\n' \
+ 'class {cname:}(object):\n'\
+ ' name = "{name:}"\n' \
+ ' type = "{group:}"\n'\
+ ' equation = "{eq:}"\n'\
+ ' params = {p:}\n' \
+ ' ext_params = {ep:}\n\n' \
+ ' @staticmethod\n' \
+ ' def func(p, x):\n' \
+ ' return {func:}\n'
+ f_string = self.func
+ for pat, repl in _numexpr_funcs:
+ f_string = re.sub(pat, repl, f_string)
+
+ @QtCore.pyqtSlot(QtWidgets.QTableWidgetItem)
+ @QtCore.pyqtSlot(str)
+ def update_function(self, _):
+ var = []
+ for row in range(self.tableWidget.rowCount()):
+ var.append(self.tableWidget.item(row, 0).text())
+
+ if self.use_nuclei.isChecked():
+ var.append('nucleus=2.67522128e8')
+
+ # for row in range(self.selection_combobox.count()):
+ # var.append(self.selection_combobox.itemText(row) + '=')
+
+ self.label.setText('def func(x, ' + ', '.join(var) + '):')
+
+ def change_visibility(self):
+ sender = self.sender()
+
+ for gb in [self.groupBox, self.groupBox_2, self.groupBox_3, self.groupBox_4]:
+ gb.blockSignals(True)
+ gb.setChecked(sender == gb)
+ gb.blockSignals(False)
+
+ self.widget_2.setVisible(sender == self.groupBox)
+ self.widget_3.setVisible(sender == self.groupBox_2)
+ self.widget.setVisible(sender == self.groupBox_3)
+ self.namespace_widget.setVisible(sender == self.groupBox_4)
+
+
+class ChoiceWidget(QtWidgets.QWidget):
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self._init_ui()
+
+ def _init_ui(self):
+ layout = QtWidgets.QGridLayout()
+ layout.setContentsMargins(3, 3, 3, 3)
+ layout.setHorizontalSpacing(6)
+
+ self.label = QtWidgets.QLabel('Name', parent=self)
+ layout.addWidget(self.label, 0, 0)
+ self.name_line = QtWidgets.QLineEdit(self)
+ layout.addWidget(self.name_line, 0, 1)
+
+ self.label_2 = QtWidgets.QLabel('Displayed name', parent=self)
+ layout.addWidget(self.label_2, 0, 2)
+ self.display_line = QtWidgets.QLineEdit(self)
+ layout.addWidget(self.display_line, 0, 3)
+
+ self.label_3 = QtWidgets.QLabel('Type', parent=self)
+ layout.addWidget(self.label_3)
+ self.types = QtWidgets.QComboBox(self)
+ self.types.addItems(['str', 'int', 'float'])
+ layout.addWidget(self.types)
+
+ self.add_button = QtWidgets.QPushButton('Add option')
+ layout.addWidget(self.add_button)
+
+ self.table = QtWidgets.QTableWidget(self)
+ self.table.setColumnCount(2)
+ self.table.setHorizontalHeaderLabels(['Name', 'Value'])
+ layout.addWidget(self.table, 2, 0, 1, 4)
+
+ self.setLayout(layout)
+
+
+def register_class(cname, name, group, p, eq, func):
+ c = type(cname, (), {})
+ c.name = name
+ c.type = group
+ c.params = p
+ c.equation = eq
+ c.func = func_decorator(func)
+
+ return c
+
+
+def func_decorator(f_string):
+ # we need this decorator because the result is used in a class
+
+ def wrapped_f(*args):
+ namespace = {'x': args[1]}
+ for i, pp in enumerate(args[0]):
+ namespace['p_{}'.format(i)] = pp
+ return ne.evaluate(f_string, local_dict=namespace)
+
+ return wrapped_f
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QtWidgets.QApplication([])
+ w = QUserFitCreator()
+ w.show()
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/fit/result.py b/nmreval/gui_qt/fit/result.py
new file mode 100644
index 0000000..3e9ede7
--- /dev/null
+++ b/nmreval/gui_qt/fit/result.py
@@ -0,0 +1,245 @@
+from math import isnan
+
+from numpy import r_
+from pyqtgraph import mkBrush
+
+from ..lib.utils import RdBuCMap
+from ...utils.text import convert
+from ..Qt import QtWidgets, QtGui, QtCore
+from .._py.fitresult import Ui_Dialog
+from ..lib.pg_objects import PlotItem
+
+
+class QFitResult(QtWidgets.QDialog, Ui_Dialog):
+ closed = QtCore.pyqtSignal(dict, list)
+ redoFit = QtCore.pyqtSignal(dict)
+
+ def __init__(self, results: list, management, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self._management = management
+
+ self._prevs = {}
+ self._models = {}
+
+ for (res, parts) in results:
+ idx = res.idx
+ print(parts)
+ data_k = management.data[idx]
+
+ if res.name not in self._models:
+ self._models[res.name] = []
+
+ self._models[res.name].append(idx)
+
+ self._prevs[idx] = []
+ for fit in data_k.get_fits():
+ 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=[], symbol='o', symbolPen=None, symbolBrush=mkBrush(color='r'), pen=None)
+ self.residplot.addItem(self.resid_graph)
+ self.residplot.setLabel('left', 'Residual')
+
+ self.fitplot = self.graphicsView.addPlot(row=1, col=0)
+ self.data_graph = PlotItem(x=[], y=[], symbol='o', symbolPen=None, symbolBrush=mkBrush(color='r'), pen=None)
+ self.fitplot.addItem(self.data_graph)
+ self.fitplot.setLabel('left', 'Function')
+
+ self.fit_graph = PlotItem(x=[], y=[])
+ self.fitplot.addItem(self.fit_graph)
+
+ 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.horizontalHeader().sectionClicked.connect(self.show_results)
+ self.logy_box.stateChanged.connect(lambda x: self.fitplot.setLogMode(y=bool(x)))
+
+ def add_graphs(self, graphs: list):
+ self.graph_comboBox.clear()
+ for (graph_id, graph_name) in graphs:
+ self.graph_comboBox.addItem(graph_name, userData=graph_id)
+
+ @QtCore.pyqtSlot(int, name='on_graph_checkBox_stateChanged')
+ def change_graph(self, state: int):
+ self.graph_comboBox.setEnabled(state == QtCore.Qt.Unchecked)
+
+ @QtCore.pyqtSlot(int, name='on_sets_comboBox_currentIndexChanged')
+ def set_parameter(self, idx: int):
+ model_name = self.sets_comboBox.itemText(idx)
+ 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]
+ for j, pvalue in enumerate(res.parameter.values()):
+ item_text = f'{pvalue.value:.4g}'
+ if pvalue.error is not None:
+ item_text += f' \u00b1 {pvalue.error:.4g}'
+ self.param_tableWidget.setItem(2*j+1, i, QtWidgets.QTableWidgetItem('-'))
+ else:
+ self.param_tableWidget.setItem(2*j+1, i, QtWidgets.QTableWidgetItem())
+ item = QtWidgets.QTableWidgetItem(item_text)
+ self.param_tableWidget.setItem(j, i, item)
+
+ self.param_tableWidget.resizeColumnsToContents()
+ self.param_tableWidget.selectColumn(0)
+ self.show_results(0)
+
+ @QtCore.pyqtSlot(int, name='on_reject_fit_checkBox_stateChanged')
+ @QtCore.pyqtSlot(int, name='on_del_prev_checkBox_stateChanged')
+ def change_opts(self, _):
+ idx = self.sets_comboBox.currentIndex()
+
+ self._opts[idx] = (self.reject_fit_checkBox.checkState() == QtCore.Qt.Checked,
+ self.del_prev_checkBox.checkState() == QtCore.Qt.Checked)
+
+ def show_results(self, idx: int):
+ set_id = self.param_tableWidget.horizontalHeaderItem(idx).data(QtCore.Qt.UserRole)
+ self.set_plot(set_id)
+ self.set_correlation(set_id)
+ self.set_statistics(set_id)
+
+ def set_plot(self, idx: str):
+ res = self._results[idx]
+ iscomplex = res.iscomplex
+
+ self.resid_graph.setData(x=res.x_data, y=res.residual)
+ if iscomplex == 'complex':
+ self.data_graph.setData(x=r_[res.x_data, res.x_data],
+ y=r_[res.y_data.real, res.y_data.imag])
+ self.fit_graph.setData(x=r_[res.x, res.x],
+ y=r_[res.y.real, res.y.imag])
+ else:
+ self.data_graph.setData(x=res.x_data, y=res.y_data)
+ self.fit_graph.setData(x=res.x, y=res.y)
+
+ self.fitplot.setLogMode(x=res.islog)
+ self.residplot.setLogMode(x=res.islog)
+
+ def set_correlation(self, idx: str):
+ while self.corr_tableWidget.rowCount():
+ self.corr_tableWidget.removeRow(0)
+
+ res = self._results[idx]
+ c = res.correlation_list()
+ for pi, pj, corr, pcorr in c:
+ cnt = self.corr_tableWidget.rowCount()
+ self.corr_tableWidget.insertRow(cnt)
+ self.corr_tableWidget.setItem(cnt, 0, QtWidgets.QTableWidgetItem(convert(pi, old='tex', new='html')))
+ self.corr_tableWidget.setItem(cnt, 1, QtWidgets.QTableWidgetItem(convert(pj, old='tex', new='html')))
+
+ for i, val in enumerate([corr, pcorr]):
+ if isnan(val):
+ val = 1000.
+ val_item = QtWidgets.QTableWidgetItem(f'{val:.4g}')
+ val_item.setBackground(self.cmap.color(val))
+ if abs(val) > 0.75:
+ val_item.setForeground(QtGui.QColor('white'))
+ self.corr_tableWidget.setItem(cnt, i+2, val_item)
+
+ self.corr_tableWidget.resizeColumnsToContents()
+
+ def set_statistics(self, idx: str):
+ while self.stats_tableWidget.rowCount():
+ self.stats_tableWidget.removeRow(0)
+
+ res = self._results[idx]
+
+ self.stats_tableWidget.setColumnCount(1 + len(self._prevs[idx]))
+ self.stats_tableWidget.setRowCount(len(res.statistics)+3)
+
+ it = QtWidgets.QTableWidgetItem(f'{res.dof}')
+ it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
+ self.stats_tableWidget.setVerticalHeaderItem(0, QtWidgets.QTableWidgetItem('DoF'))
+ self.stats_tableWidget.setItem(0, 0, it)
+
+ for col, (name, _, dof) in enumerate(self._prevs[idx], start=1):
+ self.stats_tableWidget.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem(name))
+ it = QtWidgets.QTableWidgetItem(f'{dof}')
+ it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
+ self.stats_tableWidget.setItem(0, col, it)
+
+ for row, (k, v) in enumerate(res.statistics.items(), start=1):
+ self.stats_tableWidget.setVerticalHeaderItem(row, QtWidgets.QTableWidgetItem(k))
+ it = QtWidgets.QTableWidgetItem(f'{v:.4f}')
+ it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
+ self.stats_tableWidget.setItem(row, 0, it)
+
+ best_idx = -1
+ best_val = v
+ for col, (_, stats, _) in enumerate(self._prevs[idx], start=1):
+ if k in ['adj. R^2', 'R^2']:
+ best_idx = col if best_val < stats[k] else max(0, best_idx)
+ else:
+ best_idx = col if best_val > stats[k] else max(0, best_idx)
+ it = QtWidgets.QTableWidgetItem(f'{stats[k]:.4f}')
+ it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
+ self.stats_tableWidget.setItem(row, col, it)
+
+ if best_idx > -1:
+ self.stats_tableWidget.item(row, best_idx).setBackground(QtGui.QColor('green'))
+ self.stats_tableWidget.item(row, best_idx).setForeground(QtGui.QColor('white'))
+
+ row = self.stats_tableWidget.rowCount() - 2
+ self.stats_tableWidget.setVerticalHeaderItem(row, QtWidgets.QTableWidgetItem('F'))
+ self.stats_tableWidget.setItem(row, 0, QtWidgets.QTableWidgetItem('-'))
+
+ self.stats_tableWidget.setVerticalHeaderItem(row+1, QtWidgets.QTableWidgetItem('Pr(>F)'))
+ self.stats_tableWidget.setItem(row+1, 0, QtWidgets.QTableWidgetItem('-'))
+
+ for col, (_, stats, dof) in enumerate(self._prevs[idx], start=1):
+ f_value, prob_f = res.f_test(stats['chi^2'], dof)
+ it = QtWidgets.QTableWidgetItem(f'{f_value:.4g}')
+ it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
+ self.corr_tableWidget.setItem(row, col, it)
+
+ it = QtWidgets.QTableWidgetItem(f'{prob_f:.4g}')
+ it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
+ if prob_f < 0.05:
+ it.setBackground(QtGui.QColor('green'))
+ it.setForeground(QtGui.QColor('white'))
+ self.stats_tableWidget.setItem(row+1, col, it)
+
+ @QtCore.pyqtSlot(QtWidgets.QAbstractButton)
+ def on_buttonBox_clicked(self, button: QtWidgets.QAbstractButton):
+ button_type = self.buttonBox.standardButton(button)
+
+ if button_type == self.buttonBox.Retry:
+ self.redoFit.emit(self._results)
+
+ elif button_type == self.buttonBox.Ok:
+ graph = '' if self.graph_checkBox.checkState() == QtCore.Qt.Checked else self.graph_comboBox.currentData()
+ subplots = self.partial_checkBox.checkState() == QtCore.Qt.Checked
+ self._opts.extend([graph, subplots])
+ self.closed.emit(self._results, self._opts)
+
+ self.accept()
+
+ else:
+ self.reject()
diff --git a/nmreval/gui_qt/graphs/__init__.py b/nmreval/gui_qt/graphs/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nmreval/gui_qt/graphs/graphwindow.py b/nmreval/gui_qt/graphs/graphwindow.py
new file mode 100644
index 0000000..fab3989
--- /dev/null
+++ b/nmreval/gui_qt/graphs/graphwindow.py
@@ -0,0 +1,705 @@
+import itertools
+import os
+import uuid
+
+from math import isnan
+from typing import List, Union
+
+from numpy import errstate, floor, log10
+from pyqtgraph import GraphicsObject, getConfigOption, mkColor
+
+from ..lib.pg_objects import RegionItem
+from ...utils.text import convert
+from ..Qt import QtCore, QtWidgets, QtGui
+from .._py.graph import Ui_GraphWindow
+from ..lib import make_action_icons
+from ..lib.configurations import GraceMsgBox
+
+
+class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
+ mousePositionChanged = QtCore.pyqtSignal(float, float)
+ mouseDoubleClicked = QtCore.pyqtSignal()
+ positionClicked = QtCore.pyqtSignal(tuple, bool)
+ aboutToClose = QtCore.pyqtSignal(str)
+
+ counter = itertools.count()
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self._bgcolor = mkColor(getConfigOption('background'))
+ self._fgcolor = mkColor(getConfigOption('foreground'))
+ self._prev_colors = mkColor('k'), mkColor('w')
+
+ self._init_gui()
+
+ make_action_icons(self)
+
+ self.id = str(uuid.uuid4())
+
+ self.sets = []
+ self.active = []
+
+ self.real_plots = {}
+ self.imag_plots = {}
+ self.error_plots = {}
+
+ self._special_needs = []
+ self.closable = True
+
+ self.log = [False, False]
+
+ self.scene = self.plotItem.scene()
+ self.scene.sigMouseMoved.connect(self.move_mouse)
+
+ self.checkBox.stateChanged.connect(lambda x: self.legend.setVisible(x == QtCore.Qt.Checked))
+ self.label_button.toggled.connect(lambda x: self.label_widget.setVisible(x))
+ self.limit_button.toggled.connect(lambda x: self.limit_widget.setVisible(x))
+ self.gridbutton.toggled.connect(lambda x: self.graphic.showGrid(x=x, y=x))
+ self.logx_button.toggled.connect(lambda x: self.set_logmode(xmode=x))
+ self.logy_button.toggled.connect(lambda x: self.set_logmode(ymode=x))
+ self.graphic.plotItem.vb.sigRangeChanged.connect(self.update_limits)
+ self.listWidget.itemChanged.connect(self.show_legend)
+
+ # reconnect "Export..." in context menu to our function
+ self.scene.contextMenu[0].disconnect()
+ self.scene.contextMenu[0].triggered.connect(self.export)
+
+ def _init_gui(self):
+ self.setWindowTitle('Graph ' + str(next(QGraphWindow.counter)))
+
+ self.label_widget.hide()
+ self.limit_widget.hide()
+ self.listWidget.hide()
+ self.checkBox.hide()
+
+ self.plotItem = self.graphic.plotItem
+ for orient in ['top', 'bottom', 'left', 'right']:
+ self.plotItem.showAxis(orient)
+ ax = self.plotItem.getAxis(orient)
+ ax.enableAutoSIPrefix(False)
+ if orient == 'top':
+ ax.setStyle(showValues=False)
+ ax.setHeight(10)
+ elif orient == 'right':
+ ax.setStyle(showValues=False)
+ ax.setWidth(10)
+
+ self.legend = self.plotItem.addLegend()
+ self.legend.setVisible(True)
+ # self.legend.setBrush(color=self._bgcolor)
+ self.legend.layout.setContentsMargins(1, 1, 1, 1)
+
+ self.plotItem.setMenuEnabled(False, True)
+ self.plotItem.ctrl.logXCheck.blockSignals(True)
+ self.plotItem.ctrl.logYCheck.blockSignals(True)
+
+ for lineedit in [self.xmin_lineedit, self.xmax_lineedit, self.ymin_lineedit, self.ymax_lineedit]:
+ lineedit.setValidator(QtGui.QDoubleValidator())
+
+ def __contains__(self, item: str):
+ return item in self.sets
+
+ def __iter__(self):
+ return iter(self.active)
+
+ def __len__(self):
+ return len(self.active)
+
+ def curves(self):
+ for a in self.active:
+ if self.real_button.isChecked():
+ if self.error_plots[a] is not None:
+ yield self.real_plots[a], self.error_plots[a]
+ else:
+ yield self.real_plots[a],
+
+ if self.imag_button.isChecked() and self.imag_plots[a] is not None:
+ yield self.imag_plots[a],
+
+ @property
+ def title(self):
+ return self.windowTitle()
+
+ @title.setter
+ def title(self, value):
+ self.setWindowTitle(str(value))
+
+ @property
+ def ranges(self):
+ r = self.plotItem.getViewBox().viewRange()
+ for i in [0, 1]:
+ if self.log[i]:
+ r[i] = tuple([10**x for x in r[i]])
+ else:
+ r[i] = tuple(r[i])
+
+ return tuple(r)
+
+ def add(self, name: Union[str, List], plots: List):
+ if isinstance(name, str):
+ name = [name]
+ plots = [plots]
+
+ for (real_plot, imag_plot, err_plot), n in zip(plots, name):
+ toplevel = len(self.sets)
+ self.sets.append(n)
+
+ real_plot.setZValue(2*toplevel+1)
+ if imag_plot:
+ imag_plot.setZValue(2*toplevel+1)
+ if err_plot:
+ err_plot.setZValue(2*toplevel)
+
+ self.real_plots[n] = real_plot
+ self.imag_plots[n] = imag_plot
+ self.error_plots[n] = err_plot
+
+ list_item = QtWidgets.QListWidgetItem(real_plot.opts.get('name', ''))
+ list_item.setData(QtCore.Qt.UserRole, n)
+ list_item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable)
+ list_item.setCheckState(QtCore.Qt.Checked)
+ self.listWidget.addItem(list_item)
+
+ self.show_item(name)
+
+ def remove(self, name: Union[str, List]):
+ if isinstance(name, str):
+ name = [name]
+
+ for n in name:
+ self.sets.remove(n)
+
+ for plot in [self.real_plots, self.imag_plots, self.error_plots]:
+ self.graphic.removeItem(plot[n])
+
+ if n in self.active:
+ self.active.remove(n)
+
+ # remove from label list
+ self.listWidget.blockSignals(True)
+
+ for i in range(self.listWidget.count()-1, 0, -1):
+ item = self.listWidget.item(i)
+ if item.data(QtCore.Qt.UserRole) in name:
+ self.listWidget.takeItem(i)
+
+ self.listWidget.blockSignals(False)
+
+ self._update_zorder()
+ self.show_legend()
+
+ def move_sets(self, sets, position):
+ move_plots = []
+ move_items = []
+
+ self.listWidget.blockSignals(True)
+
+ for s in sets:
+ idx = self.sets.index(s)
+ move_plots.append(self.sets.pop(idx))
+ move_items.append(self.listWidget.takeItem(idx))
+
+ if position == -1:
+ self.sets.extend(move_plots)
+ for it in move_items:
+ self.listWidget.addItem(it)
+ else:
+ self.sets = self.sets[:position] + move_plots + self.sets[position:]
+ for it in move_items[::-1]:
+ self.listWidget.insertItem(position, it)
+
+ self.listWidget.blockSignals(False)
+ self._update_zorder()
+
+ def show_item(self, idlist: list):
+ if len(self.sets) == 0:
+ return
+
+ for a in idlist:
+ if a not in self.active:
+ self.active.append(a)
+
+ if self.imag_button.isChecked():
+ item = self.imag_plots[a]
+ if (item is not None) and (item not in self.graphic.items()):
+ self.graphic.addItem(item)
+
+ if self.real_button.isChecked():
+ item = self.real_plots[a]
+ if item not in self.graphic.items():
+ self.graphic.addItem(item)
+
+ if self.error_button.isChecked():
+ item = self.error_plots[a]
+ if (item is not None) and (item not in self.graphic.items()):
+ self.graphic.addItem(item)
+
+ self.show_legend()
+
+ def hide_item(self, idlist: list):
+ if len(self.sets) == 0:
+ return
+
+ for r in idlist:
+ if r in self.active:
+ self.active.remove(r)
+
+ for plt in [self.real_plots, self.imag_plots, self.error_plots]:
+ item = plt[r]
+ if item in self.graphic.items():
+ self.graphic.removeItem(item)
+
+ @QtCore.pyqtSlot(bool, name='on_imag_button_toggled')
+ @QtCore.pyqtSlot(bool, name='on_real_button_toggled')
+ def set_imag_visible(self, visible: bool):
+ if self.sender() == self.real_button:
+ plots = self.real_plots
+ other = self.imag_plots
+ if self.error_button.isChecked() and not visible:
+ self.error_button.setChecked(False)
+ else:
+ plots = self.imag_plots
+ other = self.real_plots
+
+ if visible:
+ func = self.graphic.addItem
+ else:
+ func = self.graphic.removeItem
+
+ for a in self.active:
+ item = plots[a]
+ other_item = other[a]
+ if (item is not None) and (other_item is not None):
+ func(item)
+
+ self.show_legend()
+
+ @QtCore.pyqtSlot(bool, name='on_error_button_toggled')
+ def show_errorbar(self, visible: bool):
+ if visible and not self.real_button.isChecked():
+ # no errorbars without points
+ self.error_button.blockSignals(True)
+ self.error_button.setChecked(False)
+ self.error_button.blockSignals(False)
+ return
+
+ if visible:
+ for a in self.active:
+ item = self.error_plots[a]
+ if (item is not None) and (item not in self.graphic.items()):
+ self.graphic.addItem(item)
+ else:
+ for a in self.active:
+ item = self.error_plots[a]
+ if (item is not None) and (item in self.graphic.items()):
+ self.graphic.removeItem(item)
+
+ def add_external(self, item):
+ if isinstance(item, RegionItem) and item.first:
+ # Give regions nice values on first addition to a graph
+ x, _ = self.ranges
+
+ if item.mode == 'mid':
+ onset = item.getRegion()[0]
+ if self.log[0]:
+ delta = log10(x[1]/x[0])/20
+ span = (onset / 10**delta , onset * 10**delta)
+ else:
+ delta = x[1]-x[0]
+ span = (onset-delta/20, onset + delta/20)
+ elif item.mode == 'half':
+ span = (0.75*x[0]+0.25*x[1], 0.25*x[0]+0.75*x[1])
+ else:
+ span = item.getRegion()
+
+ item.setRegion(span)
+ item.first = False
+
+ if item in self.graphic.items():
+ return False
+
+ if not hasattr(item, 'setLogMode'):
+ self._special_needs.append(item)
+
+ self.graphic.addItem(item)
+ item.setZValue(1000)
+
+ return True
+
+ @QtCore.pyqtSlot(GraphicsObject)
+ def remove_external(self, item):
+ if item not in self.graphic.items():
+ return False
+
+ if item in self._special_needs:
+ self._special_needs.remove(item)
+
+ self.graphic.removeItem(item)
+
+ return True
+
+ def closeEvent(self, evt: QtGui.QCloseEvent):
+ if not self.closable:
+ evt.ignore()
+ return
+
+ res = QtWidgets.QMessageBox.Yes
+ if len(self.sets) != 0:
+ res = QtWidgets.QMessageBox.question(self, 'Plot not empty', 'Graph is not empty. Deleting with all data?',
+ QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
+
+ if res == QtWidgets.QMessageBox.Yes:
+ self.aboutToClose.emit(self.id)
+ evt.accept()
+ else:
+ evt.ignore()
+
+ def move_mouse(self, evt):
+ vb = self.plotItem.getViewBox()
+ if self.plotItem.sceneBoundingRect().contains(evt):
+ pos = vb.mapSceneToView(evt)
+ if self.log[0]:
+ try:
+ _x = 10**(pos.x())
+ except OverflowError:
+ _x = pos.x()
+ else:
+ _x = pos.x()
+
+ if self.log[1]:
+ try:
+ _y = 10**(pos.y())
+ except OverflowError:
+ _y = pos.y()
+ else:
+ _y = pos.y()
+ self.mousePositionChanged.emit(_x, _y)
+
+ @QtCore.pyqtSlot(name='on_title_lineedit_returnPressed')
+ @QtCore.pyqtSlot(name='on_xaxis_linedit_returnPressed')
+ @QtCore.pyqtSlot(name='on_yaxis_linedit_returnPressed')
+ def labels_changed(self):
+ label = {self.title_lineedit: 'title', self.xaxis_linedit: 'x', self.yaxis_linedit: 'y'}[self.sender()]
+ self.set_label(**{label: self.sender().text()})
+
+ def set_label(self, x=None, y=None, title=None):
+ if title is not None:
+ self.plotItem.setTitle(convert(title, old='tex', new='html'), **{'size': '10pt', 'color': self._fgcolor})
+
+ if x is not None:
+ self.plotItem.setLabel('bottom', convert(x, old='tex', new='html'),
+ **{'font-size': '10pt', 'color': self._fgcolor.name()})
+
+ if y is not None:
+ self.plotItem.setLabel('left', convert(y, old='tex', new='html'),
+ **{'font-size': '10pt', 'color': self._fgcolor.name()})
+
+ def set_logmode(self, xmode: bool = None, ymode: bool = None):
+ r = self.ranges
+
+ if xmode is None:
+ xmode = self.plotItem.ctrl.logXCheck.isChecked()
+ else:
+ self.plotItem.ctrl.logXCheck.setCheckState(xmode)
+
+ if ymode is None:
+ ymode = self.plotItem.ctrl.logYCheck.isChecked()
+ else:
+ self.plotItem.ctrl.logYCheck.setCheckState(ymode)
+
+ self.log = [xmode, ymode]
+
+ for item in self._special_needs:
+ item.logmode[0] = self.log[:]
+
+ self.plotItem.updateLogMode()
+
+ self.set_range(x=r[0], y=r[1])
+
+ def enable_picking(self, enabled: bool):
+ if enabled:
+ self.scene.sigMouseClicked.connect(self.position_picked)
+ else:
+ try:
+ self.scene.sigMouseClicked.disconnect()
+ except TypeError:
+ pass
+
+ def position_picked(self, evt):
+ vb = self.graphic.plotItem.vb
+
+ if self.graphic.plotItem.sceneBoundingRect().contains(evt.scenePos()) and evt.button() == 1:
+ pos = vb.mapSceneToView(evt.scenePos())
+ _x, _y = pos.x(), pos.y()
+
+ if self.log[0]:
+ _x = 10**_x
+
+ if self.log[1]:
+ _y = 10**_y
+
+ self.positionClicked.emit((_x, _y), evt.double())
+
+ @QtCore.pyqtSlot(name='on_apply_button_clicked')
+ def set_range(self, x: tuple = None, y: tuple = None):
+ if x is None:
+ x = float(self.xmin_lineedit.text()), float(self.xmax_lineedit.text())
+ x = min(x), max(x)
+
+ if y is None:
+ y = float(self.ymin_lineedit.text()), float(self.ymax_lineedit.text())
+ y = min(y), max(y)
+
+ for log, xy, func in zip(self.log, (x, y), (self.graphic.setXRange, self.graphic.setYRange)):
+ if log:
+ with errstate(all='ignore'):
+ xy = [log10(val) for val in xy]
+
+ if isnan(xy[1]):
+ xy = [-1, 1]
+ elif isnan(xy[0]):
+ xy[0] = xy[1]-4
+
+ func(xy[0], xy[1], padding=0)
+
+ @QtCore.pyqtSlot(object)
+ def update_limits(self, _):
+ r = self.ranges
+ self.xmin_lineedit.setText('%.5g' % r[0][0])
+ self.xmax_lineedit.setText('%.5g' % r[0][1])
+
+ self.ymin_lineedit.setText('%.5g' % r[1][0])
+ self.ymax_lineedit.setText('%.5g' % r[1][1])
+
+ def _update_zorder(self):
+ for i, sid in enumerate(self.sets):
+ plt = self.real_plots[sid]
+ if plt.zValue() != 2*i+1:
+ plt.setZValue(2*i+1)
+ if self.imag_plots[sid] is not None:
+ self.imag_plots[sid].setZValue(2*i+1)
+ if self.error_plots[sid] is not None:
+ self.error_plots[sid].setZValue(2*i)
+
+ self.show_legend()
+
+ @QtCore.pyqtSlot(bool, name='on_legend_button_toggled')
+ def show_legend_item_list(self, visible: bool):
+ self.listWidget.setVisible(visible)
+ self.checkBox.setVisible(visible)
+
+ def update_legend(self, sid, name):
+ self.listWidget.blockSignals(True)
+
+ for i in range(self.listWidget.count()):
+ item = self.listWidget.item(i)
+ if item.data(QtCore.Qt.UserRole) == sid:
+ item.setText(convert(name, old='tex', new='html'))
+
+ self.listWidget.blockSignals(False)
+ self.show_legend()
+
+ def show_legend(self):
+ if not self.legend.isVisible():
+ return
+
+ self.legend.clear()
+
+ for i, sid in enumerate(self.sets):
+ item = self.real_plots[sid]
+ other_item = self.imag_plots[sid]
+ # should legend be visible? is either real part or imaginary part shown?
+ if self.listWidget.item(i).checkState() and \
+ (item in self.graphic.items() or other_item in self.graphic.items()):
+ self.legend.addItem(item, convert(item.opts.get('name', ''), old='tex', new='html'))
+
+ def export(self):
+ filters = 'All files (*.*);;AGR (*.agr);;SVG (*.svg);;PDF (*.pdf)'
+ for imgformat in QtGui.QImageWriter.supportedImageFormats():
+ str_format = imgformat.data().decode('utf-8')
+ filters += ';;' + str_format.upper() + ' (*.' + str_format + ')'
+
+ outfile, _ = QtWidgets.QFileDialog.getSaveFileName(self, caption='Export graphic', filter=filters,
+ options=QtWidgets.QFileDialog.DontConfirmOverwrite)
+ if outfile:
+ _, suffix = os.path.splitext(outfile)
+ if suffix == '':
+ QtWidgets.QMessageBox.warning(self, 'No file extension',
+ 'No file extension found, graphic was not saved.')
+ return
+
+ if suffix == '.agr':
+ res = 0
+ if os.path.exists(outfile):
+ res = GraceMsgBox(outfile, parent=self).exec()
+ if res == -1:
+ return
+
+ opts = self.export_graphics()
+
+ from ..io.exporters import GraceExporter
+ if res == 0:
+ mode = 'w'
+ elif res == 1:
+ mode = 'a'
+ else:
+ mode = res-2
+
+ GraceExporter(opts).export(outfile, mode=mode)
+
+ else:
+ if os.path.exists(outfile):
+ if QtWidgets.QMessageBox.warning(self, 'Export graphic',
+ f'{os.path.split(outfile)[1]} already exists.\n'
+ f'Do you REALLY want to replace it?',
+ QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
+ QtWidgets.QMessageBox.No) == QtWidgets.QMessageBox.No:
+ return
+
+ bg_color = self._bgcolor
+ fg_color = self._fgcolor
+ self.set_color(foreground='k', background='w')
+
+ if suffix == '.pdf':
+ from ..io.exporters import PDFPrintExporter
+ PDFPrintExporter(self.graphic).export(outfile)
+
+
+ elif suffix == '.svg':
+ from pyqtgraph.exporters import SVGExporter
+ SVGExporter(self.scene).export(outfile)
+
+ else:
+ from pyqtgraph.exporters import ImageExporter
+
+ ImageExporter(self.scene).export(outfile)
+
+ self.set_color(foreground=fg_color, background=bg_color)
+
+ def export_graphics(self):
+ dic = self.get_state()
+ dic['items'] = []
+
+ for item in self.curves():
+ item_dic = item[0].get_data_opts()
+ if len(item) == 2:
+ # plot can show errorbars
+ item_dic['yerr'] = item[1].opts['topData']
+
+ if item_dic:
+ dic['items'].append(item_dic)
+
+ return dic
+
+ def get_state(self) -> dict:
+ dic = {
+ 'id': self.id,
+ 'limits': (self.ranges[0], self.ranges[1]),
+ 'ticks': (),
+ 'labels': (self.plotItem.getAxis('bottom').labelText,
+ self.plotItem.getAxis('left').labelText,
+ self.plotItem.titleLabel.text,
+ self.title),
+ 'log': self.log,
+ 'grid': self.gridbutton.isChecked(),
+ 'legend': self.legend.isVisible(),
+ 'plots': (self.real_button.isChecked(), self.imag_button.isChecked(), self.error_button.isChecked()),
+ 'children': self.sets,
+ 'active': self.active,
+ }
+
+ in_legend = []
+ for i in range(self.listWidget.count()):
+ in_legend.append(bool(self.listWidget.item(i).checkState()))
+ dic['in_legend'] = in_legend
+
+ # bottomLeft gives top left corner
+ l_topleft = self.plotItem.vb.itemBoundingRect(self.legend).bottomLeft()
+ legend_origin = [l_topleft.x(), l_topleft.y()]
+ for i in [0, 1]:
+ if self.log[i]:
+ legend_origin[i] = 10**legend_origin[i]
+ dic['legend_pos'] = legend_origin
+
+ for i, ax in enumerate(['bottom', 'left']):
+ if self.log[i]:
+ major = 10
+ minor = 9
+ else:
+ vmin, vmax = dic['limits'][i][0], dic['limits'][i][1]
+ dist = vmax - vmin
+ scale = 10**floor(log10(abs(dist)))
+ steps = [0.1, 0.2, 0.25, 0.5, 1., 2., 2.5, 5., 10., 20., 50., 100.]
+ for step_i in steps:
+ if dist / step_i / scale <= 10:
+ break
+ major = step_i * scale
+ minor = 1
+
+ dic['ticks'] += (major, minor),
+
+ return dic
+
+ @staticmethod
+ def set_state(state):
+ graph = QGraphWindow()
+ graph.id = state.get('id', graph.id)
+
+ graph.plotItem.setLabel('bottom', state['labels'][0], **{'font-size': '10pt', 'color': graph._fgcolor.name()})
+ graph.plotItem.setLabel('left', state['labels'][1], **{'font-size': '10pt', 'color': graph._fgcolor.name()})
+ graph.plotItem.setTitle(state['labels'][2], **{'size': '10pt', 'color': graph._fgcolor.name()})
+ graph.setWindowTitle(state['labels'][3])
+
+ graph.graphic.showGrid(x=state['grid'], y=state['grid'])
+
+ graph.checkBox.setCheckState(QtCore.Qt.Checked if state['legend'] else QtCore.Qt.Unchecked)
+
+ graph.real_button.setChecked(state['plots'][0])
+ graph.imag_button.setChecked(state['plots'][1])
+ graph.error_button.setChecked(state['plots'][2])
+
+ graph.set_range(x=state['limits'][0], y=state['limits'][1])
+ graph.logx_button.setChecked(state['log'][0])
+ graph.logy_button.setChecked(state['log'][1])
+
+ return graph
+
+ def set_color(self, foreground=None, background=None):
+ if background is not None:
+ self._bgcolor = mkColor(background)
+ self.graphic.setBackground(self._bgcolor)
+ self.legend.setBrush(self._bgcolor)
+
+ if foreground is not None:
+ self._fgcolor = mkColor(foreground)
+
+ for ax in ['left', 'bottom']:
+ pen = self.plotItem.getAxis(ax).pen()
+ pen.setColor(self._fgcolor)
+
+ self.plotItem.getAxis(ax).setPen(pen)
+ self.plotItem.getAxis(ax).setTextPen(pen)
+
+ self.legend.setLabelTextColor(self._fgcolor)
+ if self.legend.isVisible():
+ self.show_legend()
+
+ title = self.plotItem.titleLabel.text
+ if title is not None:
+ self.plotItem.setTitle(title, **{'size': '10pt', 'color': self._fgcolor})
+
+ x = self.plotItem.getAxis('bottom').labelText
+ if x is not None:
+ self.plotItem.setLabel('bottom', x, **{'font-size': '10pt', 'color': self._fgcolor.name()})
+
+ y = self.plotItem.getAxis('left').labelText
+ if y is not None:
+ self.plotItem.setLabel('left', y, **{'font-size': '10pt', 'color': self._fgcolor.name()})
+
+ @QtCore.pyqtSlot(bool, name='on_bwbutton_toggled')
+ def change_background(self, _):
+ temp = self._fgcolor, self._bgcolor
+ self.set_color(foreground=self._prev_colors[0], background=self._prev_colors[1])
+ self._prev_colors = temp
diff --git a/nmreval/gui_qt/graphs/guide_lines.py b/nmreval/gui_qt/graphs/guide_lines.py
new file mode 100644
index 0000000..e821f55
--- /dev/null
+++ b/nmreval/gui_qt/graphs/guide_lines.py
@@ -0,0 +1,166 @@
+from pyqtgraph import InfiniteLine
+
+from ..Qt import QtWidgets, QtCore, QtGui
+from .._py.guidelinewidget import Ui_Form
+from ..lib.pg_objects import LogInfiniteLine
+
+
+class LineWidget(QtWidgets.QWidget, Ui_Form):
+ line_created = QtCore.pyqtSignal(object, str)
+ line_deleted = QtCore.pyqtSignal(object, str)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.lines = {}
+ self.comments = {}
+
+ self.vh_pos_lineEdit.setValidator(QtGui.QDoubleValidator())
+
+ self.tableWidget.installEventFilter(self)
+
+ @QtCore.pyqtSlot(name='on_pushButton_clicked')
+ def make_line(self):
+ invalid = True
+
+ idx = self.mode_comboBox.currentIndex()
+ try:
+ pos = float(self.vh_pos_lineEdit.text())
+ # Vertical: idx=0; horizontal: idx = 1
+ angle = 90*abs(1-idx)
+ invalid = False
+ except ValueError:
+ pos = None
+ angle = None
+ pass
+
+ if invalid:
+ QtWidgets.QMessageBox().information(self, 'Invalid input', 'Input is not a valid number')
+ return
+
+ qcolor = QtGui.QColor.fromRgb(*self.color_comboBox.value.rgb())
+ comment = self.comment_lineEdit.text()
+ line = LogInfiniteLine(pos=pos, angle=angle, movable=self.drag_checkBox.isChecked(), pen=qcolor)
+ line.sigPositionChanged.connect(self.move_line)
+
+ self.make_table_row(pos, angle, qcolor, comment)
+
+ graph_id = self.graph_comboBox.currentData()
+ try:
+ self.lines[graph_id].append(line)
+ self.comments[graph_id].append(comment)
+ except KeyError:
+ self.lines[graph_id] = [line]
+ self.comments[graph_id] = [comment]
+
+ self.line_created.emit(line, graph_id)
+
+ def set_graphs(self, graphs: list):
+ for graph_id, name in graphs:
+ self.graph_comboBox.addItem(name, userData=graph_id)
+
+ def remove_graph(self, graph_id: str):
+ idx = self.graph_comboBox.findData(graph_id)
+ if idx != -1:
+ self.graph_comboBox.removeItem(idx)
+
+ if graph_id in self.lines:
+ del self.lines[graph_id]
+
+ @QtCore.pyqtSlot(int, name='on_graph_comboBox_currentIndexChanged')
+ def change_graph(self, idx: int):
+ self.tableWidget.clear()
+ self.tableWidget.setRowCount(0)
+
+ graph_id = self.graph_comboBox.itemData(idx)
+ if graph_id in self.lines:
+ lines = self.lines[graph_id]
+ comments = self.comments[graph_id]
+ for i, line in enumerate(lines):
+ self.make_table_row(line.pos(), line.angle, line.pen.color(), comments[i])
+
+ def make_table_row(self, position, angle, color, comment):
+ if angle == 0:
+ try:
+ pos_label = 'x = ' + str(position.y())
+ except AttributeError:
+ pos_label = 'x = {position}'
+
+ elif angle == 90:
+ try:
+ pos_label = f'y = {position.x()}'
+ except AttributeError:
+ pos_label = f'y = {position}'
+
+ else:
+ raise ValueError('Only horizontal or vertical lines are supported')
+
+ item = QtWidgets.QTableWidgetItem(pos_label)
+ item.setFlags(QtCore.Qt.ItemIsSelectable)
+ item.setForeground(QtGui.QBrush(QtGui.QColor('black')))
+
+ row_count = self.tableWidget.rowCount()
+ self.tableWidget.setRowCount(row_count+1)
+ self.tableWidget.setItem(row_count, 0, item)
+
+ item2 = QtWidgets.QTableWidgetItem(comment)
+ self.tableWidget.setItem(row_count, 1, item2)
+
+ colitem = QtWidgets.QTableWidgetItem(' ')
+ colitem.setBackground(QtGui.QBrush(color))
+ colitem.setFlags(QtCore.Qt.ItemIsSelectable)
+ self.tableWidget.setVerticalHeaderItem(row_count, colitem)
+
+ def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent) -> bool:
+ if evt.type() == QtCore.QEvent.KeyPress:
+ if evt.key() == QtCore.Qt.Key_Delete:
+ self.delete_line()
+ return True
+
+ return super().eventFilter(src, evt)
+
+ def delete_line(self):
+ remove_rows = sorted([item.row() for item in self.tableWidget.selectedItems()])
+ graph_id = self.graph_comboBox.currentData()
+ current_lines = self.lines[graph_id]
+
+ print(remove_rows)
+ for i in reversed(remove_rows):
+ print(i)
+ self.tableWidget.removeRow(i)
+ self.line_deleted.emit(current_lines[i], graph_id)
+
+ current_lines.pop(i)
+ self.comments[graph_id].pop(i)
+
+ @QtCore.pyqtSlot(object)
+ def move_line(self, line: InfiniteLine):
+ current_idx = self.graph_comboBox.currentData()
+ graphs = self.lines[current_idx]
+ i = -1
+ for i, line_i in enumerate(graphs):
+ if line == line_i:
+ break
+ pos = line.value()
+ text_item = self.tableWidget.item(i, 0)
+ text_item.setText(text_item.text()[:4]+f'{pos:.4g}')
+
+
+if __name__ == '__main__':
+ import sys
+ from numpy.random import choice
+
+ app = QtWidgets.QApplication([])
+
+ qw = 'QtCurve'
+ while qw == 'QtCurve':
+ qw = choice(QtWidgets.QStyleFactory.keys())
+ print(qw)
+ app.setStyle(QtWidgets.QStyleFactory.create(qw))
+
+ mplQt = LineWidget()
+ mplQt.show()
+ mplQt.set_graphs([('a', 'a'), ('b', 'b')])
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/graphs/movedialog.py b/nmreval/gui_qt/graphs/movedialog.py
new file mode 100644
index 0000000..5b7378b
--- /dev/null
+++ b/nmreval/gui_qt/graphs/movedialog.py
@@ -0,0 +1,89 @@
+from ..Qt import QtCore, QtWidgets
+from .._py.move_dialog import Ui_MoveDialog
+
+
+class QMover(QtWidgets.QDialog, Ui_MoveDialog):
+ moveData = QtCore.pyqtSignal(list, str, str)
+ copyData = QtCore.pyqtSignal(list, str)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.entries = {}
+
+ self.fromcomboBox.currentIndexChanged.connect(self.change_graph)
+ self.buttonBox.clicked.connect(self.button_clicked)
+
+ def setup(self, entries: dict):
+ self.fromcomboBox.blockSignals(True)
+ self.tocomboBox.blockSignals(True)
+ for k, v in entries.items():
+ self.entries[k[0]] = v
+ self.fromcomboBox.addItem(k[1], userData=k[0])
+ self.tocomboBox.addItem(k[1], userData=k[0])
+ self.fromcomboBox.blockSignals(False)
+ self.tocomboBox.blockSignals(False)
+ self.change_graph(0)
+
+ @QtCore.pyqtSlot(int)
+ def change_graph(self, idx: int):
+ self.listWidget.clear()
+ idd = self.fromcomboBox.itemData(idx)
+ if idd is not None:
+ for i, j in self.entries[idd]:
+ it = QtWidgets.QListWidgetItem(j)
+ it.setData(QtCore.Qt.UserRole, i)
+ self.listWidget.addItem(it)
+
+ @QtCore.pyqtSlot(QtWidgets.QAbstractButton)
+ def button_clicked(self, btn: QtWidgets.QAbstractButton):
+ if self.buttonBox.buttonRole(btn) in [QtWidgets.QDialogButtonBox.ApplyRole,
+ QtWidgets.QDialogButtonBox.AcceptRole]:
+ from_graph = self.fromcomboBox.itemData(self.fromcomboBox.currentIndex())
+ to_graph = self.tocomboBox.itemData(self.tocomboBox.currentIndex())
+ if from_graph != to_graph:
+ moving = []
+ for idx in self.listWidget.selectedIndexes():
+ it = self.listWidget.itemFromIndex(idx)
+ moving.append(it.data(QtCore.Qt.UserRole))
+ it_data = (it.data(QtCore.Qt.UserRole), it.text())
+ if self.move_button.isChecked():
+ self.entries[from_graph].remove(it_data)
+ self.entries[to_graph].append(it_data)
+ self.change_graph(self.fromcomboBox.currentIndex())
+
+ if self.move_button.isChecked():
+ self.moveData.emit(moving, to_graph, from_graph)
+ else:
+ self.copyData.emit(moving, to_graph)
+
+ if self.buttonBox.buttonRole(btn) == QtWidgets.QDialogButtonBox.AcceptRole:
+ self.close()
+
+ elif self.buttonBox.buttonRole(btn) == QtWidgets.QDialogButtonBox.RejectRole:
+ self.close()
+ else:
+ return
+
+ def close(self):
+ self.listWidget.clear()
+ self.tocomboBox.clear()
+ self.fromcomboBox.clear()
+ self.entries = {}
+
+ super().close()
+
+
+if __name__ == '__main__':
+ import sys
+ global propData
+
+ app = QtWidgets.QApplication([])
+
+ m = QMover()
+ m.setup({('a', '1'): ('z', '100'), ('b', '2'): ('y', '99'), ('c', '3'): ('x', '98')})
+
+ m.show()
+
+ sys.exit(app.exec())
\ No newline at end of file
diff --git a/nmreval/gui_qt/io/__init__.py b/nmreval/gui_qt/io/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nmreval/gui_qt/io/asciireader.py b/nmreval/gui_qt/io/asciireader.py
new file mode 100644
index 0000000..fd6472a
--- /dev/null
+++ b/nmreval/gui_qt/io/asciireader.py
@@ -0,0 +1,182 @@
+from ...io.asciireader import AsciiReader
+
+from ..Qt import QtGui, QtCore, QtWidgets
+from .._py.asciidialog import Ui_ascii_reader
+
+
+class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
+ data_read = QtCore.pyqtSignal(list)
+ file_ext = ['.dat', '.txt']
+ skip = False
+
+ def __init__(self, fname=None, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.reader = AsciiReader(fname)
+
+ self.ascii_table.horizontalHeader().setStretchLastSection(True)
+ self.buttonbox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.apply)
+ self.buttonbox.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.accept)
+
+ self.changestaggeredrange(0)
+
+ self.ascii_table.contextMenuEvent = self.ctx_table
+ self.ascii_table.horizontalHeader().setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
+ self.ascii_table.horizontalHeader().customContextMenuRequested.connect(self.ctx_table)
+
+ def __call__(self, fname, *args, **kwargs):
+
+ for i in [self.stag_lineEdit, self.start_lineedit, self.end_lineedit,
+ self.plainTextEdit_2, self.ascii_table, self.delay_lineedit]:
+ i.clear()
+ self.checkBox_2.setChecked(False)
+ self.checkBox.setChecked(False)
+ self.plainTextEdit_2.show()
+
+ self.reader = AsciiReader(fname)
+ self.set_gui()
+
+ if QAsciiReader.skip:
+ self.accept()
+ else:
+ self.skippy_checkbox.blockSignals(True)
+ self.skippy_checkbox.setCheckState(QtCore.Qt.Unchecked)
+ self.skippy_checkbox.blockSignals(False)
+
+ return self
+
+ def set_gui(self):
+ for text in self.reader.header:
+ self.plainTextEdit_2.appendPlainText(text)
+
+ self.ascii_table.setRowCount(self.reader.shape[0])
+ self.ascii_table.setColumnCount(self.reader.shape[1])
+ try:
+ last_header_line = self.reader.header[-1].split()
+ if len(last_header_line) == self.reader.shape[1]:
+ self.ascii_table.setHorizontalHeaderLabels(last_header_line)
+ except IndexError:
+ self.plainTextEdit_2.hide()
+
+ for i, line in enumerate(self.reader.data):
+ for j, field in enumerate(line):
+ it = QtWidgets.QTableWidgetItem(field)
+ self.ascii_table.setItem(i, j, it)
+
+ self.ascii_table.resizeColumnsToContents()
+
+ if self.reader.delays is not None:
+ set_string = ''.join(str(d) + '\n' for d in self.reader.delays)
+ self.plainTextEdit.setPlainText(set_string)
+ self.delay_lineedit.setText(str(len(self.reader.delays)))
+
+ def ctx_table(self, _):
+ menu = QtWidgets.QMenu(self)
+ x_action = QtWidgets.QAction('Set as x', self)
+ x_action.triggered.connect(lambda: self.set_columns('x'))
+ y_action = QtWidgets.QAction('Set as y', self)
+ y_action.triggered.connect(lambda: self.set_columns('y'))
+ menu.addActions([x_action, y_action])
+ if self.label_5.isVisible():
+ yerr_action = QtWidgets.QAction('Set as \u0394y', self)
+ yerr_action.triggered.connect(lambda: self.set_columns('yerr'))
+ menu.addAction(yerr_action)
+ menu.popup(QtGui.QCursor.pos())
+
+ def set_columns(self, mode):
+ cols = ' '.join([str(s.column()+1) for s in self.ascii_table.selectionModel().selectedColumns()])
+ try:
+ lineedit = {'x': self.x_lineedit, 'y': self.y_lineedit, 'yerr': self.lineEdit}[mode]
+ lineedit.setText(cols)
+ except KeyError:
+ pass
+
+ @QtCore.pyqtSlot(int, name='on_checkBox_2_stateChanged')
+ def changestaggeredrange(self, evt):
+ if evt == 2:
+ self.stag_lineEdit.show()
+ self.stag_lineEdit.setEnabled(True)
+ else:
+ self.stag_lineEdit.hide()
+ self.stag_lineEdit.setDisabled(True)
+
+ @QtCore.pyqtSlot(name='on_pushButton_clicked')
+ def calc_delays(self):
+ self.reader.calc_delays(float(self.start_lineedit.text()), float(self.end_lineedit.text()),
+ int(self.delay_lineedit.text()), log=self.checkBox.isChecked(),
+ stagg=self.checkBox_2.isChecked(), stag_size=int(self.stag_lineEdit.text()))
+
+ set_string = ''.join(str(d) + '\n' for d in self.reader.delays)
+ self.plainTextEdit.setPlainText(set_string)
+
+ def update_header(self, text, comment='#'):
+ text = text.replace(comment, '').lstrip().rstrip()
+ self.plainTextEdit_2.appendPlainText(text)
+
+ @QtCore.pyqtSlot()
+ def on_plainTextEdit_textChanged(self):
+ new_delays = str(self.plainTextEdit.toPlainText()).rstrip('\n').split('\n')
+ self.delay_lineedit.setText(str(len(new_delays)))
+ if new_delays[0] == '':
+ new_delays = None
+ else:
+ for k, v in enumerate(new_delays):
+ try:
+ new_delays[k] = float(v)
+ except ValueError:
+ new_delays[k] = -1
+ self.reader.delays = new_delays
+
+ @QtCore.pyqtSlot()
+ def accept(self):
+ if self.apply():
+ self.close()
+
+ def apply(self):
+ # default row for x is the first row, it will be superseded if an integer number is given.
+
+ try:
+ x = [int(self.x_lineedit.text())-1]
+ except ValueError:
+ x = None
+ try:
+ y = [int(t)-1 for t in str(self.y_lineedit.text()).split(' ')]
+ except ValueError:
+ y = None
+ try:
+ y_err = [int(t)-1 for t in str(self.lineEdit.text()).split(' ')]
+ except ValueError:
+ y_err = None
+
+ ret_dic = self.reader.export(xidx=x, yidx=y, yerridx=y_err,
+ mode=self.buttonGroup.checkedButton().text())
+
+ self.data_read.emit(ret_dic)
+
+ return True
+
+ @staticmethod
+ def _update_dic(key, value, old_dic):
+ old_dic_keys = list(old_dic.keys())
+ if key in old_dic_keys:
+ counter = 0
+ replace_key = key + str(counter)
+ while replace_key in old_dic_keys:
+ counter += 1
+ replace_key = key + str(counter)
+ else:
+ replace_key = key
+ old_dic[replace_key] = value
+
+ @QtCore.pyqtSlot(int, name='on_buttonGroup_buttonClicked')
+ def show_error(self, val):
+ if val == -2:
+ self.widget.show()
+ else:
+ self.lineEdit.setText('')
+ self.widget.hide()
+
+ @QtCore.pyqtSlot(int, name='on_skippy_checkbox_stateChanged')
+ def skip_next_dial(self, _):
+ QAsciiReader.skip = self.skippy_checkbox.isChecked()
diff --git a/nmreval/gui_qt/io/bdsreader.py b/nmreval/gui_qt/io/bdsreader.py
new file mode 100644
index 0000000..8ecc837
--- /dev/null
+++ b/nmreval/gui_qt/io/bdsreader.py
@@ -0,0 +1,66 @@
+from ...io.bds_reader import BDSReader
+
+from ..Qt import QtCore, QtWidgets
+from .._py.bdsdialog import Ui_Dialog
+
+
+class QBDSReader(QtWidgets.QDialog, Ui_Dialog):
+ data_read = QtCore.pyqtSignal(list)
+ file_ext = ['.eps']
+
+ def __init__(self, fname: str = None, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ if fname is not None:
+ self.reader = BDSReader(fname)
+ self.setup_gui()
+
+ def __call__(self, fname: str):
+ self.reader = BDSReader(fname)
+ self.setup_gui()
+
+ return self
+
+ def setup_gui(self):
+ self.listWidget.clear()
+
+ for temp in self.reader.set_temp:
+ item = QtWidgets.QListWidgetItem(str(temp))
+ item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsSelectable)
+ item.setCheckState(QtCore.Qt.Checked)
+ self.listWidget.addItem(item)
+
+ def accept(self):
+ data = []
+
+ temps = []
+ for i in range(self.listWidget.count()):
+ item = self.listWidget.item(i)
+ if item.checkState() == QtCore.Qt.Checked:
+ temps.append(i)
+
+ for (mode, cb) in [('epsilon', self.eps_checkBox),
+ ('sigma', self.cond_checkBox),
+ ('modulus', self.modul_checkBox)]:
+
+ if cb.checkState() == QtCore.Qt.Checked:
+ values = self.reader.export(mode=mode)
+ for t in temps:
+ data.append(values[t])
+
+ self.data_read.emit(data)
+
+ super().accept()
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QtWidgets.QApplication(sys.argv)
+
+ reader = QBDSReader()
+ reader('/autohome/dominik/nmreval/testdata/ZWEITECHANCE_EGD4H2O.EPS')
+ reader.show()
+ sys.exit(app.exec())
+
diff --git a/nmreval/gui_qt/io/dscreader.py b/nmreval/gui_qt/io/dscreader.py
new file mode 100644
index 0000000..e11eae5
--- /dev/null
+++ b/nmreval/gui_qt/io/dscreader.py
@@ -0,0 +1,273 @@
+from pathlib import Path
+from typing import Union
+
+import numpy as np
+from pyqtgraph import PlotDataItem
+
+from nmreval.data.points import Points
+from nmreval.io.dsc import Cyclohexane, DSCCalibrator, DSCSample
+
+from nmreval.gui_qt.Qt import QtWidgets, QtCore
+from nmreval.gui_qt._py.dscfile_dialog import Ui_Dialog
+
+
+class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
+ data_read = QtCore.pyqtSignal(list)
+ file_ext = ['.txt', '.dsc']
+
+ def __init__(self, sample= None, empty=None, reference=None, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.calibrator = DSCCalibrator()
+ self.current_run = (-1, 'h')
+ self.sample_idx = None
+ self.fname = None
+
+ self.raw_graph.setLabel('bottom', text='T', units='K')
+ self.raw_graph.setTitle('Raw data')
+ self.raw_graph.addLegend()
+ self.raw_sample = PlotDataItem(x=[], y=[], name='Sample')
+ self.empty_sample = PlotDataItem(x=[], y=[], pen={'dash': (5, 5)}, name='Empty')
+ self.raw_graph.addItem(self.raw_sample)
+ self.raw_graph.addItem(self.empty_sample)
+
+ self.end_graph.setTitle('End result')
+ self.end_graph.setLabel('bottom', text='T', units='K')
+ self.baseline_sample = PlotDataItem(x=[], y=[])
+ self.end_graph.addItem(self.baseline_sample)
+
+ self.baseline_graph.setTitle('Time dependence')
+ self.baseline_graph.setLabel('bottom', text='t', units='min')
+ self.drift_sample = PlotDataItem(x=[], y=[])
+ self.slope_graph = PlotDataItem(x=[], y=[], pen={'color': 'r', 'dash': (5, 5)})
+ self.baseline_graph.addItem(self.drift_sample)
+ self.baseline_graph.addItem(self.slope_graph)
+
+ self.calib_graph.setTitle('Calibration')
+ self.calib_graph.setLabel('bottom', text='T', units='K')
+ self.ref_plotitems = []
+
+ self.sample = None
+ if sample is not None:
+ self.add_sample(sample)
+
+ self.empty = None
+ if empty is not None:
+ self.add_empty(empty=empty)
+
+ self.references = []
+ if reference is not None:
+ if isinstance(reference, (str, Path, DSCSample)):
+ reference = [reference]
+ for r in reference:
+ self.add_reference(ref=r)
+
+ def add_sample(self, fname: Union[Path, str]):
+ self.sample = self.calibrator.set_measurement(fname, mode='sample')
+ self.fname = self.sample.fname
+
+ self.step_listWidget.clear()
+
+ for opts in self.sample.steps:
+ item = QtWidgets.QListWidgetItem()
+ item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable)
+ item.setCheckState(QtCore.Qt.Unchecked)
+
+ if opts[0] == 'i':
+ item.setFlags(QtCore.Qt.NoItemFlags)
+ item.setText(f'{opts[1]:.2f} K for {opts[1] / 60:.0f} min')
+ else:
+ 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')
+
+ self.step_listWidget.addItem(item)
+
+ @QtCore.pyqtSlot(name='on_loadempty_button_clicked')
+ def add_empty(self, empty=None):
+ if empty is None:
+ empty, _ = QtWidgets.QFileDialog.getOpenFileName(directory=str(self.fname.parent))
+
+ if empty:
+ self.empty = self.calibrator.set_measurement(empty, mode='empty')
+ self.empty_label.setText(str(self.empty.fname.name))
+
+ self.update_plots()
+
+ @QtCore.pyqtSlot(name='on_delempty_button_clicked')
+ def remove_empty(self):
+ self.empty_label.setText('Empty measurement')
+ self.calibrator.empty = None
+
+ self.update_plots()
+
+ @QtCore.pyqtSlot(name='on_ref_add_pushButton_clicked')
+ def add_reference(self, ref=None):
+ if ref is None:
+ ref, _ = QtWidgets.QFileDialog.getOpenFileName(directory=str(self.fname.parent))
+
+ if ref:
+ ref = self.calibrator.set_measurement(ref, mode='reference')
+
+ self.references.append(ref)
+ item = QtWidgets.QTableWidgetItem(str(ref.fname.name))
+ item.setData(QtCore.Qt.UserRole, ref.fname)
+ item.setFlags(QtCore.Qt.ItemIsEnabled)
+
+ rowcnt = self.reference_tableWidget.rowCount()
+ self.reference_tableWidget.setRowCount(rowcnt+1)
+ self.reference_tableWidget.setItem(rowcnt, 0, item)
+ self.reference_tableWidget.setCellWidget(rowcnt, 1, ReferenceComboBox())
+ self.reference_tableWidget.resizeColumnsToContents()
+
+ self.update_plots()
+
+ @QtCore.pyqtSlot(name='on_ref_remove_pushButton_clicked')
+ def remove_reference(self):
+ idx = self.reference_tableWidget.currentRow()
+ self.calibrator.remove_reference(self.reference_tableWidget.item(idx, 0).data(QtCore.Qt.UserRole))
+
+ self.reference_tableWidget.removeRow(idx)
+
+ print(self.calibrator.reference)
+
+ self.update_plots()
+
+ @QtCore.pyqtSlot(QtWidgets.QListWidgetItem, name='on_step_listWidget_itemChanged')
+ def select_run(self, item: QtWidgets.QListWidgetItem):
+
+ idx = self.step_listWidget.indexFromItem(item).row()
+ self.step_listWidget.blockSignals(True)
+ for row in range(self.step_listWidget.count()):
+ if idx == row:
+ continue
+ self.step_listWidget.item(row).setCheckState(QtCore.Qt.Unchecked)
+ self.step_listWidget.blockSignals(False)
+
+ if item.checkState() == QtCore.Qt.Checked:
+ mode, rate, _, _ = self.sample.steps[idx]
+ self.current_run = (rate, mode)
+ self.sample_idx = idx
+ self.update_plots()
+ else:
+ self.current_run = (-1, 'h')
+ self.sample_idx = None
+ self.clear_plots()
+
+ @QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonGroup_buttonClicked')
+ def update_plots(self, _=None):
+ if self.sample_idx is None:
+ return
+
+ slope_type = {self.none_radioButton: None,
+ self.isotherm_radioButton: 'iso',
+ self.slope_radioButton: 'curve'}[self.buttonGroup.checkedButton()]
+
+ rate = self.current_run[0]
+ try:
+ raw_sample, drift_value, sample_data, empty_data, slope = self.calibrator.get_data(self.sample_idx,
+ slope=slope_type)
+ except ValueError as e:
+ _msg = QtWidgets.QMessageBox.warning(self, 'No rate found', e.args[0])
+ return
+
+ self.raw_sample.setData(x=raw_sample[0], y=raw_sample[1])
+ self.drift_sample.setData(x=drift_value[0]/60., y=drift_value[1])
+ self.slope_graph.setData(x=slope[0]/60., y=slope[1])
+
+ if empty_data is not None:
+ self.empty_sample.setData(x=empty_data[0], y=empty_data[1])
+
+ self.calibrator.ref_list = []
+ for row in range(self.reference_tableWidget.rowCount()):
+ self.calibrator.ref_list.append(self.reference_tableWidget.cellWidget(row, 1).get_reference())
+
+ self.calib_graph.clear()
+
+ calib_x, calib_y, regions = self.calibrator.get_calibration(rate)
+
+ if calib_x is not None:
+ for ref_zoom, onset, grad_points in regions:
+ ref_plot = PlotDataItem(x=ref_zoom[0], y=ref_zoom[1])
+ self.calib_graph.addItem(ref_plot)
+
+ self.calib_graph.addItem(PlotDataItem(x=grad_points[0], y=grad_points[1],
+ symbol='x'))
+
+ view_limits = -np.max(ref_zoom[1])/10, np.max(ref_zoom[1]) * 1.1
+ cut_idx, = np.where((onset < view_limits[1]) & (onset > view_limits[0]))
+ self.calib_graph.addItem(PlotDataItem(x=ref_zoom[0, cut_idx], y=onset[cut_idx],
+ pen={'color': 'r', 'dash': (2, 5)}))
+ self.calib_graph.addItem(PlotDataItem(x=[ref_zoom[0, 0], ref_zoom[0, -1]], y=[0, 0],
+ pen={'color': 'r', 'dash': (2, 5)}))
+
+ else:
+ calib_x = [1, 0]
+ calib_y = 1
+
+ if self.cp_checkBox.isChecked():
+ self.baseline_sample.setData(x=calib_x[0]*sample_data[0]+calib_x[1], y=calib_y*sample_data[1])
+ else:
+ self.baseline_sample.setData(x=calib_x[0]*sample_data[0]+calib_x[1], y=sample_data[1])
+
+ def clear_plots(self):
+ for plot in [self.raw_sample, self.baseline_sample, self.empty_sample, self.drift_sample, self.slope_graph]:
+ plot.setData(x=[], y=[])
+
+ self.calib_graph.clear()
+
+ def accept(self):
+ run_list = []
+ for i in range(self.step_listWidget.count()):
+ if self.step_listWidget.item(i).checkState() == QtCore.Qt.Checked:
+ run_list.append(i)
+
+ if self.baseline_groupBox.isChecked():
+ empty = self.empty
+ slope = {self.none_radioButton: None,
+ self.isotherm_radioButton: 'iso',
+ self.slope_radioButton: 'heat'}[self.buttonGroup.checkedButton()]
+ else:
+ empty = None
+ slope = None
+
+ if self.reference_groupBox.isChecked():
+ calibrations = []
+ for j, ref in enumerate(self.references):
+ calibrations.append((ref, self.reference_tableWidget.cellWidget(j, 1).get_reference()))
+ else:
+ calibrations = None
+
+ new_vals = []
+
+ for run in run_list:
+ mode, rate, _, _ = self.sample.steps[run]
+ xy = self.sample.curve(rate, mode, empty=empty, slope=slope, calibration=calibrations)
+ new_vals.append(Points(xy[0], xy[1], value=rate, name=f'{self.fname.stem} {rate} ({mode})'))
+
+ self.data_read.emit(new_vals)
+
+ super().accept()
+
+
+class ReferenceComboBox(QtWidgets.QComboBox):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.references = [Cyclohexane]
+ for ref in self.references:
+ self.addItem(ref.name)
+
+ def get_reference(self):
+ return self.references[self.currentIndex()]
+
+
+if __name__ == '__main__':
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+
+ reader = QDSCReader(sample='/autohome/dominik/nmreval/testdata/dsc/20m_LiTFSI_H2O.txt',
+ empty='/autohome/dominik/nmreval/testdata/dsc/leer.txt',
+ reference='/autohome/dominik/nmreval/testdata/dsc/cyclohexan_10K-min.txt')
+ reader.show()
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/io/exporters.py b/nmreval/gui_qt/io/exporters.py
new file mode 100644
index 0000000..bbca4a6
--- /dev/null
+++ b/nmreval/gui_qt/io/exporters.py
@@ -0,0 +1,143 @@
+from numpy import c_
+
+from ...io.graceeditor import GraceEditor
+from ...lib.colors import Colors
+from ...lib.lines import LineStyle
+from ...lib.symbols import SymbolStyle
+from ...utils.text import convert
+
+from ..Qt import QtGui, QtCore, QtPrintSupport
+
+
+class GraceExporter:
+ def __init__(self, kwargs: dict):
+ self.__agr = None
+ self.__opts = kwargs
+
+ def export(self, outfile: str, mode: (int, str) = 'w'):
+ if mode == 'w':
+ self.__agr = GraceEditor()
+ else:
+ self.__agr = GraceEditor(outfile)
+
+ if isinstance(mode, str):
+ new_g = self.__agr.new_graph()
+
+ new_g.set_limits(x=self.__opts['limits'][0], y=self.__opts['limits'][1])
+ new_g.set_log(x=self.__opts['log'][0], y=self.__opts['log'][1])
+
+ new_g.set_onoff('legend', self.__opts['legend'])
+ new_g.set_property(**{'title': '"' + convert(self.__opts['labels'][2], old="html", new="agr") + '"',
+ 'legend loctype': 'view',
+ 'legend': ', '.join(str(i) for i in new_g.world_to_view(self.__opts['legend_pos']))})
+
+ for i, ax in enumerate('xy'):
+ new_g.set_axis_property(ax, **{'label': f'"{convert(self.__opts["labels"][i], old="html", new="agr")}"',
+ 'tick major': self.__opts['ticks'][i][0],
+ 'tick minor ticks': self.__opts['ticks'][i][1],})
+ new_g.set_axis_onoff(ax, 'tick major grid', self.__opts['grid'])
+ g_idx = new_g.idx
+ else:
+ g_idx = mode
+
+ colors = self.__agr.colors
+
+ new_colors = []
+ for item in self.__opts['items']:
+ new_s = self.__agr.new_set(g_idx)
+
+ sc = item['symbolcolor']
+ c_num = -1
+ for i, (_, rgb) in colors.items():
+ if rgb == sc:
+ c_num = i
+ break
+
+ if c_num == -1:
+ c_num = max(colors.keys())
+ colors[c_num + 1] = (f'color{c_num + 1}', sc)
+ new_colors.append((c_num + 1, f'color{c_num + 1}', sc))
+
+ new_s.set_symbol(**{'symbol': item['symbol'].value, 'size': item['symbolsize'] / 10., 'color': c_num,
+ 'fill color': c_num, 'fill pattern': 1})
+ new_s.set_onoff('errorbar', self.__opts['plots'][2])
+
+ lc = item['linecolor']
+ c_num = -1
+ for c_num, (_, rgb) in colors.items():
+ if rgb == lc:
+ break
+
+ if c_num == -1:
+ c_num = max(colors.keys())
+ colors[c_num + 1] = ()
+ new_colors.append((c_num, f'color{c_num + 1}', sc))
+
+ new_s.set_line(**{'color': c_num, 'linewidth': item['linewidth'],
+ 'linestyle': item['linestyle'].to_agr()})
+ new_s.set_property(comment='"' + item['name'] + '"', legend='"' + item['name'] + '"')
+
+ data = self.__agr.dataset(g_idx, new_s.idx)
+ if 'yerr' in item:
+ data.type = 'xydy'
+ data.data = c_[item['x'], item['y'], item['yerr']]
+ new_s.set_property(**{'errorbar color': c_num})
+ else:
+ data.data = c_[item['x'], item['y']]
+
+ for c in new_colors:
+ self.__agr.set_color(c[1], c[2], idx=c[0])
+
+ self.__agr.write(outfile)
+
+
+class PDFPrintExporter:
+ def __init__(self, graphview):
+ self.graphic = graphview
+
+ def export(self, outfile):
+ printer = QtPrintSupport.QPrinter()
+
+ printer.setOutputFormat(printer.PdfFormat)
+ printer.setOutputFileName(outfile)
+
+ printer.setPaperSize(QtCore.QSizeF(self.graphic.width(), self.graphic.height()),
+ printer.DevicePixel)
+ printer.setPageMargins(0, 0, 0, 0, printer.DevicePixel)
+
+ painter = QtGui.QPainter(printer)
+ self.graphic.render(painter)
+ painter.end()
+
+
+def pgitem_to_dic(item):
+ x, y = item.xData, item.yData
+ if (x is None) or (len(x) == 0):
+ return
+
+ opts = item.opts
+ item_dic = {
+ 'x': x, 'y': y,
+ 'name': opts['name'],
+ 'symbolsize': opts['symbolSize']
+ }
+
+ if opts['symbol'] is None:
+ item_dic['symbol'] = SymbolStyle.No
+ item_dic['symbolcolor'] = Colors.Black
+ else:
+ item_dic['symbol'] = SymbolStyle.from_str(opts['symbol'])
+ item_dic['symbolcolor'] = Colors.from_rgb(*opts['symbolBrush'].color().getRgbF()[:3])
+
+ pen = opts['pen']
+
+ if pen is not None:
+ item_dic['linestyle'] = LineStyle(pen.style())
+ item_dic['linecolor'] = Colors.from_rgb(*pen.color().getRgbF()[:3])
+ item_dic['linewidth'] = pen.widthF()
+ else:
+ item_dic['linestyle'] = LineStyle.No
+ item_dic['linecolor'] = item_dic['symbolcolor']
+ item_dic['linewidth'] = 0.0
+
+ return item_dic
diff --git a/nmreval/gui_qt/io/fcbatchreader.py b/nmreval/gui_qt/io/fcbatchreader.py
new file mode 100644
index 0000000..e94a750
--- /dev/null
+++ b/nmreval/gui_qt/io/fcbatchreader.py
@@ -0,0 +1,116 @@
+import pathlib
+
+from ...io.fcbatchreader import FCReader
+from ..lib.utils import busy_cursor
+from ..Qt import QtCore, QtWidgets, QtGui
+from .._py.fcreader import Ui_FCEval_dialog
+
+
+class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
+ data_read = QtCore.pyqtSignal(list, str)
+
+ def __init__(self, path=None, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+ if path is None:
+ path = pathlib.Path().home()
+ self.path = path
+
+ self.start_lineedit.setValidator(QtGui.QDoubleValidator())
+ self.stop_lineedit.setValidator(QtGui.QDoubleValidator())
+
+ self.listWidget.installEventFilter(self)
+
+ def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent) -> bool:
+ # intercept key press in listwidget to allow deletion with Del
+ if evt.type() == QtCore.QEvent.KeyPress:
+ if evt.key() == QtCore.Qt.Key_Delete:
+ self.listWidget.takeItem(self.listWidget.currentRow())
+ return True
+
+ return super().eventFilter(src, evt)
+
+ @QtCore.pyqtSlot(int, name='on_region_checkBox_stateChanged')
+ def use_region(self, state: int):
+ self.start_lineedit.setEnabled(state == QtCore.Qt.Checked)
+ self.stop_lineedit.setEnabled(state == QtCore.Qt.Checked)
+
+ @QtCore.pyqtSlot(name='on_file_pushbutton_clicked')
+ @QtCore.pyqtSlot(name='on_dir_pushbutton_clicked')
+ def get_input(self):
+ if self.sender() == self.file_pushbutton:
+ infiles, _ = QtWidgets.QFileDialog.getOpenFileNames(caption='Select HDF files',
+ directory=str(self.path),
+ filter='HDF files (*.h5)')
+ if infiles:
+ self.listWidget.addItems(infiles)
+ self.label.setText(str(pathlib.Path(infiles[-1]).parent))
+ else:
+ infiles = QtWidgets.QFileDialog.getExistingDirectory(caption='Select input directory',
+ directory=str(self.path),
+ options=QtWidgets.QFileDialog.ShowDirsOnly)
+
+ if infiles:
+ self.listWidget.addItem(infiles)
+ self.label.setText(str(pathlib.Path(infiles).parent))
+
+ @QtCore.pyqtSlot(name='on_savebutton_clicked')
+ def save_path(self):
+ outfile = QtWidgets.QFileDialog.getExistingDirectory(self, caption='Select directory',
+ directory=self.label.text(),
+ options=QtWidgets.QFileDialog.ShowDirsOnly)
+ if outfile:
+ self.label.setText(outfile)
+
+ def accept(self):
+ items = [self.listWidget.item(i).text() for i in range(self.listWidget.count())]
+ if items:
+ with busy_cursor():
+ self.read(items)
+
+ self.close()
+
+ def read(self, items):
+ region = (None, None)
+ if self.region_box.isChecked():
+ start = None
+ if self.start_lineedit.text():
+ start = float(self.start_lineedit.text())
+
+ stop = None
+ if self.stop_lineedit.text():
+ stop = float(self.stop_lineedit.text())
+ region = (start, stop)
+
+ fc_eval = FCReader(items)
+ try:
+ fc_eval.load_magnetization(region=region, overwrite=self.overwrite_cb.isChecked())
+ except OSError as e:
+ QtWidgets.QMessageBox.warning(self, 'Mssing data', e.strerror)
+ self.statelabel.setText('')
+ return
+
+ fc_eval.fit(kww=self.kww_checkbox.isChecked(), save_fits=True, save_fig=True)
+
+ save_variables = []
+ for name, cb in [('t1', self.t1_cb), ('beta', self.beta_cb), ('m0', self.m0_cb), ('off', self.off_cb)]:
+ if cb.isChecked():
+ save_variables.append(name)
+
+ ret_vals = []
+ ret_vals.extend(fc_eval.get_parameter(path=self.label.text(), kind='temp', parameter=save_variables))
+
+ grp = ''
+ if self.graph_checkbox.isChecked():
+ grp = self.graph_comboBox.currentData(QtCore.Qt.UserRole)
+
+ self.data_read.emit(ret_vals, grp)
+
+
+if __name__ == '__main__':
+ import sys
+ app = QtWidgets.QApplication([])
+ qfc = QFCReader()
+ qfc.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/io/filedialog.py b/nmreval/gui_qt/io/filedialog.py
new file mode 100644
index 0000000..30306b6
--- /dev/null
+++ b/nmreval/gui_qt/io/filedialog.py
@@ -0,0 +1,88 @@
+from ..Qt import QtWidgets, QtCore
+
+
+class _FileDialog(QtWidgets.QFileDialog):
+ def __init__(self, directory=None, caption=None, filters='', parent=None):
+ super().__init__(parent=parent)
+
+ self.setOption(QtWidgets.QFileDialog.DontUseNativeDialog, True)
+
+ self.setWindowTitle(caption)
+ self.setDirectory(str(directory))
+ self.setNameFilters(filters.split(';;'))
+
+ file_tree = self.findChild(QtWidgets.QTreeView, 'treeView')
+ file_tree.setSortingEnabled(True)
+ for i in range(file_tree.header().count()):
+ file_tree.header().setSectionResizeMode(i, 0)
+ file_tree.model().sort(0)
+
+ file_list = self.findChild(QtWidgets.QListView, 'listView')
+ file_list.model().sort(0)
+
+ line = QtWidgets.QFrame(self)
+ line.setFrameShape(line.HLine)
+ line.setFrameShadow(line.Sunken)
+ self.layout().addWidget(line, self.layout().rowCount(), 0, 1, self.layout().columnCount())
+
+
+class OpenFileDialog(_FileDialog):
+ def __init__(self, directory=None, caption=None, filters='', parent=None):
+ super().__init__(directory=directory, caption=caption, filters=filters, parent=parent)
+
+ self.setFileMode(QtWidgets.QFileDialog.ExistingFiles)
+
+ self.checkBox = QtWidgets.QCheckBox(self)
+ self.checkBox.setChecked(False)
+ self.checkBox.setText('Use existing graph')
+ self.checkBox.stateChanged.connect(self.checkbox_state)
+ self.layout().addWidget(self.checkBox)
+
+ self.comboBox = QtWidgets.QComboBox(self)
+ self.comboBox.setEnabled(False)
+ self.layout().addWidget(self.comboBox)
+
+ self.add_to_graph = None
+
+ def set_graphs(self, graphs):
+ self.comboBox.blockSignals(True)
+ for gid, name in graphs:
+ self.comboBox.addItem(name, userData=gid)
+ self.comboBox.blockSignals(False)
+
+ if not self.comboBox.count():
+ self.comboBox.hide()
+ self.checkBox.hide()
+
+ def checkbox_state(self, checked: QtCore.Qt.CheckState):
+ if checked == QtCore.Qt.Checked:
+ self.comboBox.setEnabled(True)
+ self.add_to_graph = self.comboBox.currentData()
+ else:
+ self.comboBox.setEnabled(False)
+ self.add_to_graph = None
+
+ @QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged')
+ def graph_changed(self, idx: int):
+ self.add_to_graph = self.comboBox.itemData(idx)
+
+
+class SaveDirectoryDialog(_FileDialog):
+ def __init__(self, directory=None, filters='', parent=None):
+ super().__init__(directory=directory, filters=filters, parent=parent)
+
+ self.setOption(QtWidgets.QFileDialog.DontConfirmOverwrite, False)
+ self.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
+
+ self.label = QtWidgets.QLabel(self)
+ self.label.setTextFormat(QtCore.Qt.RichText)
+ self.label.setText('Use <label> as placeholder in filename. (e.g. t1_<label>.dat )')
+ self.layout().addWidget(self.label, self.layout().rowCount(), 0, 1, self.layout().columnCount())
+
+ self.checkBox = QtWidgets.QCheckBox(self)
+ self.checkBox.setChecked(True)
+ self.checkBox.setText('Replace spaces with underscore')
+ self.layout().addWidget(self.checkBox, self.layout().rowCount(), 0, 1, self.layout().columnCount())
+
+ self.setWindowTitle('Save')
+ self.setNameFilters(['All files (*.*)', 'Session file (*.nmr)', 'Text file (*.dat)', 'HDF file (*.h5)', 'Grace files (*.agr)'])
diff --git a/nmreval/gui_qt/io/filereaders.py b/nmreval/gui_qt/io/filereaders.py
new file mode 100755
index 0000000..27d54ec
--- /dev/null
+++ b/nmreval/gui_qt/io/filereaders.py
@@ -0,0 +1,133 @@
+from pathlib import Path
+import struct
+from typing import List, Union
+
+from ..Qt import QtCore
+
+from .asciireader import QAsciiReader
+from .hdfreader import QHdfViewer
+from .bdsreader import QBDSReader
+from .gracereader import QGraceReader
+from .dscreader import QDSCReader
+from .nmrreader import QNMRReader
+
+
+class QFileReader(QtCore.QObject):
+ data_read = QtCore.pyqtSignal([list], [dict])
+
+ def __init__(self, manager=None):
+ QtCore.QObject.__init__(self)
+
+ self.select = 'all'
+ self.data = []
+ self.filenames = None
+ self.extensions = set()
+
+ self.reader = {}
+
+ for ext, reader in [
+ ('txt', QAsciiReader), ('dsc', QDSCReader), ('agr', QGraceReader),
+ ('bds', QBDSReader), ('hdf', QHdfViewer), ('nmr', QNMRReader)
+ ]:
+ self.register(ext, reader)
+
+ def __call__(self, files: Union[List[str], str]) -> List:
+ self.data = []
+ if isinstance(files, str):
+ self.filenames = [files]
+ else:
+ self.filenames = files
+
+ return self.readfiles(files)
+
+ def readfiles(self, fname: Union[List[str], str]) -> List:
+ if not isinstance(fname, list):
+ fname = [fname]
+
+ for f in fname:
+ f = Path(f)
+ dtype = self.guess_type(f)
+ if dtype in self.reader:
+ r = self.reader[dtype]
+ else:
+ raise ValueError(f'Unknown type for file {f}')
+
+ if r(f) is not None:
+ # If QAsciiReader.skip = True it accepts automatically and returns None
+ r(f).exec()
+
+ self.data_read.emit(self.data)
+
+ try:
+ self.reader['txt'].skip = False
+ except KeyError:
+ pass
+
+ return self.data
+
+ def readtnt(self, fname):
+ """ Special treatment for tnt. If data is a single measurement, skip dialog """
+ raise NotImplementedError
+ # tntreader = self.reader[2]
+ # tntreader(fname)
+ # if not tntreader.reader.onedimensional:
+ # tntreader.exec()
+
+ @QtCore.pyqtSlot(list)
+ def _update_dic(self, other: list):
+ self.data.extend(other)
+
+ def register(self, key, new_reader):
+ if key in self.reader:
+ return self.reader[key]
+ r = new_reader()
+ self.reader[key] = r
+ r.data_read.connect(self._update_dic)
+ self.extensions.update(r.file_ext)
+
+ return r
+
+ def guess_type(self, fname: Path) -> str:
+ ext = fname.suffix.lower()
+
+ if ext in self.extensions:
+ if ext in ('.dat', '.txt'):
+ with fname.open('r', encoding='iso-8859-15') as fp:
+ line = fp.readline()
+ if line.strip().startswith('Filename: C:\\'):
+ return 'dsc'
+
+ return 'txt'
+
+ if ext in ('.h5', '.hdf', '.hdf5'):
+ return 'hdf'
+
+ if ext == '.eps':
+ return 'bds'
+
+ return ext[1:] # remove dot
+
+ else:
+ with fname.open('rb') as fp:
+ s16 = fp.read(16)
+
+ # copied from whichdb (Python 2.7) to look for magic value of dbhash
+ (magic,) = struct.unpack("=l", s16[-4:])
+ if magic in (0x00061561, 0x61150600):
+ return 'nmr'
+
+ else:
+ ret_types = ('nmr', 'bds', 'tnt', 'agr', 'dsc')
+ strings = (b'NMREVAL', b'NOVOCONTROL', b'TNT1.005', b'# Grace project', b'Filename:\tC:')
+
+ (magic,) = struct.unpack('<16s', s16)
+ for dtype, startstring in zip(ret_types, strings):
+ if magic.startswith(startstring):
+ return dtype
+
+ # nothing matched, text file is best guess
+ return 'txt'
+
+
+if __name__ == '__main__':
+ pass
diff --git a/nmreval/gui_qt/io/gracereader.py b/nmreval/gui_qt/io/gracereader.py
new file mode 100644
index 0000000..7261571
--- /dev/null
+++ b/nmreval/gui_qt/io/gracereader.py
@@ -0,0 +1,110 @@
+from ...lib.lines import LineStyle
+from ...lib.symbols import SymbolStyle
+from ...data.points import Points
+from ...io.graceeditor import GraceEditor
+
+from ..Qt import QtCore, QtWidgets, QtGui
+from .._py.gracereader import Ui_Dialog
+
+
+class QGraceReader(QtWidgets.QDialog, Ui_Dialog):
+ data_read = QtCore.pyqtSignal(list)
+ file_ext = ['.agr']
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+ self._reader = GraceEditor()
+ self.treeWidget.itemClicked.connect(self.show_property)
+
+ self.treeWidget.installEventFilter(self)
+
+ def __call__(self, fname, *args, **kwargs):
+ self.read(fname)
+
+ return self
+
+ def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent):
+ if evt.type() == QtCore.QEvent.KeyPress:
+ if evt.key() == QtCore.Qt.Key_Space:
+ iterator = QtWidgets.QTreeWidgetItemIterator(self.treeWidget)
+ while iterator.value():
+ item = iterator.value()
+ if item.parent() is not None and item.isSelected():
+ item.setCheckState(0,
+ QtCore.Qt.Checked if item.checkState(0) == QtCore.Qt.Unchecked else
+ QtCore.Qt.Unchecked)
+
+ iterator += 1
+
+ return True
+
+ return super().eventFilter(src, evt)
+
+ def read(self, fname):
+ self._reader.parse(fname)
+
+ for graphs in self._reader.graphs:
+ item = QtWidgets.QTreeWidgetItem([f'Graph {graphs.idx} (Title "{graphs.get_property("title")}")'])
+ for gset in graphs.set:
+ item_2 = QtWidgets.QTreeWidgetItem([f'Set {gset.idx} (Label: {gset.get_property("legend")}, '
+ f'shape: {self._reader.dataset(graphs.idx, gset.idx).shape})'])
+ item_2.setCheckState(0, QtCore.Qt.Checked)
+ item_2.setData(0, QtCore.Qt.UserRole, (graphs.idx, gset.idx))
+ item.addChild(item_2)
+
+ self.treeWidget.addTopLevelItem(item)
+ self.treeWidget.expandAll()
+
+ @QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem, int)
+ def show_property(self, item: QtWidgets.QTreeWidgetItem, col: int):
+ keys = item.data(col, QtCore.Qt.UserRole)
+ if keys is not None:
+ cols = self._reader.colors
+
+ self.tableWidget.item(0, 1).setText(SymbolStyle(self._reader.get_property(*keys, 'symbol')).name)
+ sym_col = cols[self._reader.get_property(*keys, 'symbol fill color')][1]
+ self.tableWidget.item(1, 1).setBackground(QtGui.QBrush(QtGui.QColor(*sym_col)))
+
+ self.tableWidget.item(2, 1).setText(LineStyle(self._reader.get_property(*keys, 'line linestyle')).name)
+
+ line_col = cols[self._reader.get_property(*keys, 'line color')][1]
+ self.tableWidget.item(3, 1).setBackground(QtGui.QBrush(QtGui.QColor(*line_col)))
+
+ def accept(self):
+ data = []
+ iterator = QtWidgets.QTreeWidgetItemIterator(self.treeWidget,
+ QtWidgets.QTreeWidgetItemIterator.NoChildren |
+ QtWidgets.QTreeWidgetItemIterator.Checked)
+
+ while iterator.value():
+ item = iterator.value()
+ key = (item.data(0, QtCore.Qt.UserRole))
+ s = self._reader.dataset(*key)
+ label = self._reader.get_property(*key, 'legend').replace('"', '')
+ # label = self._reader.graphs[key[0]].sets[key[1]]['legend'].replace('"', '')
+ sd = s.data
+ if s.type == 'xydy':
+ data.append(Points(x=sd[:, 0], y=sd[:, 1], y_err=sd[:, 2], name=label))
+ else:
+ data.append(Points(x=sd[:, 0], y=sd[:, 1], name=label))
+
+ iterator += 1
+
+ self.data_read.emit(data)
+
+ self.close()
+
+ def close(self):
+ self._reader.clear()
+ super().close()
+
+
+if __name__ == '__main__':
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+
+ reader = QGraceReader()
+ reader.read('/autohome/dominik/nmreval/testdata/02_relax_2.agr')
+ reader.show()
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/io/hdfreader.py b/nmreval/gui_qt/io/hdfreader.py
new file mode 100644
index 0000000..e0c673c
--- /dev/null
+++ b/nmreval/gui_qt/io/hdfreader.py
@@ -0,0 +1,196 @@
+from ...io.hdfreader import HdfReader
+
+from ..Qt import QtGui, QtCore, QtWidgets
+from .._py.hdftree import Ui_Hdf_Dialog
+
+
+class QHdfViewer(QtWidgets.QDialog, Ui_Hdf_Dialog):
+ data_read = QtCore.pyqtSignal(list)
+ file_ext = ['.h5', '.hdf', '.hdf5']
+
+ def __init__(self, fname: str = None, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.treewidget = HDFTreeWidget(self)
+ self.verticalLayout_3.addWidget(self.treewidget)
+
+ self.variables = VarTable(self)
+ self.widget_2.addWidget(self.variables)
+ self.widget_2.setText('Variables')
+
+ self._reader = HdfReader()
+
+ if fname is not None:
+ self.__call__(fname)
+
+ def __call__(self, fname, *args, **kwargs):
+ self.treewidget.clear()
+ for cb in [self.comboBox, self.comboBox_2]:
+ cb.clear()
+ cb.addItem('Automatic for the people')
+ cb.show()
+
+ self._reader(filename=fname)
+ self._fill_boxes()
+ self._populate_tree(self._reader, self.treewidget.invisibleRootItem())
+
+ if self.comboBox.count() == 1:
+ self.comboBox.hide()
+ self.comboBox_2.hide()
+
+ self.treewidget.expandToDepth(0)
+ self.treewidget.resizeColumnToContents(1)
+
+ return self
+
+ def _populate_tree(self, node, item):
+ self.treewidget.blockSignals(True)
+
+ # add varied parameter to comboboxes
+ for key, value in node.title_parameter[1].items():
+ if isinstance(value, list):
+ idx = self.comboBox.findText(key)
+ if idx == -1:
+ self.comboBox.addItem(key)
+ self.comboBox_2.addItem(key)
+
+ if node.children is not None:
+ for child in node.children.values():
+ label_item = QtWidgets.QTreeWidgetItem([child.name])
+ label_item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsSelectable)
+ label_item.setCheckState(0, QtCore.Qt.Unchecked)
+ if child.type == 'signal':
+ label_item.setBackground(0, QtGui.QBrush(QtGui.QColor('cyan')))
+ label_item.setCheckState(1, QtCore.Qt.Unchecked)
+ elif child.type == 'points':
+ label_item.setBackground(0, QtGui.QBrush(QtGui.QColor('red')))
+ item.addChild(label_item)
+ self._populate_tree(child, label_item)
+
+ self.treewidget.blockSignals(False)
+
+ def _fill_boxes(self):
+ for k, v in self._reader.parameter.items():
+ if isinstance(v, list):
+ idx = self.comboBox.findText(k)
+ if idx == -1:
+ self.comboBox.addItem(k)
+ self.comboBox_2.addItem(k)
+
+ self.variables.populate(self._reader.parameter)
+
+ @QtCore.pyqtSlot()
+ def get_selected(self):
+
+ if self.comboBox.currentIndex() == 0:
+ value = None
+ else:
+ value = self.comboBox.currentText()
+
+ if self.comboBox_2.currentIndex() == 0:
+ group = None
+ else:
+ group = self.comboBox_2.currentText()
+
+ iterator = QtWidgets.QTreeWidgetItemIterator(self.treewidget, QtWidgets.QTreeWidgetItemIterator.NoChildren)
+ selected = []
+ while iterator.value():
+ item = iterator.value()
+ iterator += 1
+ if item.checkState(0) == QtCore.Qt.Checked:
+ path = item.text(0)
+ parent = item.parent()
+ while parent is not None:
+ path = parent.text(0) + '/' + path
+ parent = parent.parent()
+
+ if item.checkState(1) == QtCore.Qt.Checked:
+ selected.extend(self._reader.get_selected(path, flag='spectrum', value=value, group=group))
+ else:
+ selected.extend(self._reader.get_selected(path, value=value, group=group))
+
+ self.data_read.emit(selected)
+
+ def accept(self):
+ self.get_selected()
+ self.close()
+
+
+class VarTable(QtWidgets.QTableWidget):
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setColumnCount(2)
+ self.setHorizontalHeaderLabels(['Key', 'Value'])
+ self.horizontalHeader().setStretchLastSection(True)
+ self.verticalHeader().setVisible(False)
+ self.horizontalHeader().setVisible(True)
+
+ def populate(self, vars: dict):
+ self.setHorizontalHeaderLabels(['Key', 'Value'])
+ self.setRowCount(len(vars))
+ for i, (k, v) in enumerate(vars.items()):
+ key_item = QtWidgets.QTableWidgetItem(k)
+ key_item.setFlags(QtCore.Qt.NoItemFlags)
+ key_item.setForeground(QtGui.QBrush(QtGui.QColor(0, 0, 0)))
+ if isinstance(v, list):
+ val = f'<{len(v)} values>'
+ if len(v) < 40:
+ tip = '\n'.join(str(p) for p in v)
+ else:
+ tip = '\n'.join(str(p) for p in v[:40])
+ tip += '\n...'
+ else:
+ val = str(v)
+ tip = val
+ value_item = QtWidgets.QTableWidgetItem(val)
+ value_item.setFlags(QtCore.Qt.NoItemFlags)
+ value_item.setForeground(QtGui.QBrush(QtGui.QColor(0, 0, 0)))
+ value_item.setToolTip(tip)
+ self.setItem(i, 0, key_item)
+ self.setItem(i, 1, value_item)
+
+
+class HDFTreeWidget(QtWidgets.QTreeWidget):
+ def __init__(self, parent):
+ super().__init__(parent=parent)
+
+ self.setHeaderLabels(['Data', 'Spectrum?'])
+ self.header().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
+ self.header().setSectionResizeMode(1, QtWidgets.QHeaderView.Fixed)
+ self.header().setStretchLastSection(False)
+ self.header().setCascadingSectionResizes(True)
+ self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+ self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectItems)
+
+ self.itemChanged.connect(self.change_item)
+
+ def keyPressEvent(self, evt: QtGui.QKeyEvent):
+ if evt.key() == QtCore.Qt.Key_Space:
+ for idx in self.selectedIndexes():
+ item = self.itemFromIndex(idx)
+ col = idx.column()
+ cs = item.checkState(col)
+ item.setCheckState(col, QtCore.Qt.Unchecked if cs == QtCore.Qt.Checked else QtCore.Qt.Checked)
+ else:
+ super().keyPressEvent(evt)
+
+ @staticmethod
+ def change_item(item: QtWidgets.QTreeWidgetItem, column: int):
+ state = item.checkState(column)
+ for i in range(item.childCount()):
+ child = item.child(i)
+ child.setCheckState(column, state)
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QtWidgets.QApplication([])
+ # w = QHdfViewer('/autohome/dominik/nmreval/testdata/fc_test/abc.h5')
+ w = QHdfViewer('/autohome/dominik/nmreval/testdata/L8_313K_17O_GG_KG_2020-12-02_1535.h5')
+
+ w.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/io/nmrreader.py b/nmreval/gui_qt/io/nmrreader.py
new file mode 100644
index 0000000..6e2a374
--- /dev/null
+++ b/nmreval/gui_qt/io/nmrreader.py
@@ -0,0 +1,34 @@
+from struct import unpack
+
+from ...io.nmrreader import NMRReader
+
+from ..Qt import QtCore
+
+
+class QNMRReader(QtCore.QObject):
+ data_read = QtCore.pyqtSignal(list)
+ file_ext = ['.nmr']
+
+ def __init__(self):
+ super().__init__()
+ self._reader = NMRReader()
+
+ def __call__(self, fname):
+ with fname.open('rb') as fp:
+ s16 = fp.read(16)
+
+ # copied from whichdb (Python 2.7) to look for magic value of dbhash
+ (magic,) = unpack("=l", s16[-4:])
+ if magic in (0x00061561, 0x61150600):
+ self._reader(fname, '-1')
+
+ else:
+ (magic,) = unpack('<16s', s16)
+ if magic.startswith(b'NMREVAL'):
+ self._reader(fname, str(magic[7:].rstrip(b'\x00'), 'utf8'))
+
+ return self
+
+ def exec(self):
+ data = self._reader.make_data()
+ self.data_read.emit([data])
diff --git a/nmreval/gui_qt/io/save_fitparameter.py b/nmreval/gui_qt/io/save_fitparameter.py
new file mode 100644
index 0000000..4c7b63a
--- /dev/null
+++ b/nmreval/gui_qt/io/save_fitparameter.py
@@ -0,0 +1,78 @@
+from ..Qt import QtWidgets, QtCore, QtGui
+from .._py.save_fit_parameter import Ui_fitparameter_save_dialog
+
+
+class QSaveFit(QtWidgets.QDialog, Ui_fitparameter_save_dialog):
+ def __init__(self, path: str = None, parent=None):
+ super().__init__(parent=parent)
+
+ self.pnames = None
+ self.outpath = path
+
+ self.setupUi(self)
+
+ self.tableWidget.cellDoubleClicked.connect(self.clicketyclick)
+ self.tableWidget.setColumnCount(2)
+
+ self.save_path_line.textEdited.connect(lambda: self.change_path(self.save_path_line.text()))
+ self.save_path_button.clicked.connect(self.set_save_path)
+
+ self.header_edit.hide()
+ self.header_checkBox.stateChanged.connect(lambda state: self.header_edit.setVisible(state))
+
+ self.missing_value_line.setValidator(QtGui.QDoubleValidator())
+
+ def set_parameter(self, fits: dict):
+ for model_name, parameter in fits.items():
+ for p in parameter:
+ item = QtWidgets.QTableWidgetItem(p)
+ item2 = QtWidgets.QTableWidgetItem(model_name)
+ item2.setToolTip(model_name)
+ row = self.tableWidget.rowCount()
+ self.tableWidget.setRowCount(row+1)
+ self.tableWidget.setItem(row, 0, item)
+ self.tableWidget.setItem(row, 1, item2)
+
+ self.tableWidget.resizeColumnsToContents()
+
+ @QtCore.pyqtSlot(int, int)
+ def clicketyclick(self, row: int, _):
+ if self.col_line.text():
+ self.col_line.setText(self.col_line.text() + '; ' + self.tableWidget.item(row, 0).text())
+ else:
+ self.col_line.setText(self.tableWidget.item(row, 0).text())
+
+ def set_save_path(self):
+ fname, _ = QtWidgets.QFileDialog.getSaveFileName(options=QtWidgets.QFileDialog.DontConfirmOverwrite,
+ directory=self.outpath)
+
+ if fname:
+ self.outpath = fname
+ self.save_path_line.setText(fname)
+
+ def add_header(self, idx: int):
+ self.textEdit.setVisible(idx != 0)
+
+ @QtCore.pyqtSlot(name='on_usage_button_clicked')
+ def show_usage(self):
+ _ = QtWidgets.QMessageBox.information(self, 'Usage',
+ 'Separate each column by semicolon.\n'
+ 'Use p_err to save error of parameter p.\n'
+ 'If a column shall contain different parameters use {p1/p2}.\n'
+ 'KWW mean is calculated by mean(τ,β), KWW peak by peak(τ,β).')
+
+ def accept(self):
+ super().accept()
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QtWidgets.QApplication([])
+
+ dialog = QSaveFit()
+ dialog.set_parameter({'fit1': ['p1', 'p2'], 'fit2': ['p2', 'p3']})
+
+ dialog.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/io/tntreader.py b/nmreval/gui_qt/io/tntreader.py
new file mode 100644
index 0000000..cd97c76
--- /dev/null
+++ b/nmreval/gui_qt/io/tntreader.py
@@ -0,0 +1,109 @@
+from ...io.tntreader import TNTReader
+
+from ..Qt import QtCore, QtWidgets
+from .._py.tntdialog import Ui_tntdialog
+
+
+class QTNTReader(QtWidgets.QDialog, Ui_tntdialog):
+ data_read = QtCore.pyqtSignal(dict)
+ file_ext = ['.tnt']
+
+ def __init__(self, fname=None, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.reader = TNTReader(fname)
+
+ self.frame.hide()
+
+ if fname is not None:
+ if self.reader.onedimensional:
+ self.export()
+ else:
+ self.set_gui()
+
+ def __call__(self, fname, *args, **kwargs):
+ self.reader(fname)
+ for s in [self.widget, self.widget_2, self.widget_3]:
+ s.__call__()
+
+ self.frame.hide()
+ self.frame_2.show()
+ self.unknown_delay_combobox.clear()
+
+ if self.reader.onedimensional:
+ self.export()
+ else:
+ self.set_gui()
+
+ return self
+
+ def set_gui(self):
+ self.label_3.setText(' x '.join(map(str, self.reader.shape)))
+ for i, w in enumerate([self.widget, self.widget_2, self.widget_3], start=1):
+ if self.reader.shape[i] == 1:
+ w.hide()
+ else:
+ w.label.setText('Dimension {}'.format(i+1))
+ w.dim = i
+ w.add_items([x[0] for x in self.reader.delays.items() if len(x[1]) == self.reader.shape[i]])
+ w.get_data.connect(self.show_delays)
+ w.newDelay.connect(self.calc_delays)
+ for x in self.reader.delays.items():
+ if len(x[1]) in self.reader.shape[1:]:
+ continue
+ else:
+ self.unknown_delay_combobox.addItem(x[0])
+ if self.unknown_delay_combobox.count() == 0:
+ self.frame_2.hide()
+
+ @QtCore.pyqtSlot(str)
+ def show_delays(self, label):
+ vals = self.reader.delays[str(label)]
+ self.sender().vals = '\n'.join(map(str, vals))
+
+ def calc_delays(self):
+ self.frame.show()
+ self._caller = self.sender()
+
+ @QtCore.pyqtSlot(name='on_pushButton_2_clicked')
+ def cancel_delay(self):
+ self.frame.hide()
+
+ @QtCore.pyqtSlot(name='on_pushButton_clicked')
+ def add_delay(self):
+ try:
+ s = float(self.start_lineedit.text())
+ except ValueError:
+ return
+ try:
+ e = float(self.end_lineedit.text())
+ except ValueError:
+ return
+ d = []
+ for w in [self.widget, self.widget_2, self.widget_3]:
+ if w.isVisible():
+ d.append(w.comboBox.currentText())
+ self.reader.add_delay(s, e, self._caller.dim, self.lineEdit.text(),
+ staggered=self.checkBox_2.isChecked(), stag_steps=int(self.spinBox.value()),
+ log=self.checkBox.isChecked())
+ self._caller.add_items(self.lineEdit.text())
+ self.frame.hide()
+
+ def export(self):
+ d = []
+ for w in [self.widget, self.widget_2, self.widget_3]:
+ if w.isVisible():
+ d.append(w.comboBox.currentText())
+ else:
+ d.append(None)
+
+ ret_dic = self.reader.export(d)
+
+ self.data_read.emit(ret_dic)
+ self.close()
+
+ return ret_dic
+
+ def accept(self):
+ self.export()
diff --git a/nmreval/gui_qt/lib/__init__.py b/nmreval/gui_qt/lib/__init__.py
new file mode 100644
index 0000000..f273183
--- /dev/null
+++ b/nmreval/gui_qt/lib/__init__.py
@@ -0,0 +1,76 @@
+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 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
+
+ dirname = 'resources.icons.%s_light' % icon_type
+ 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
diff --git a/nmreval/gui_qt/lib/codeeditor.py b/nmreval/gui_qt/lib/codeeditor.py
new file mode 100644
index 0000000..245a9e1
--- /dev/null
+++ b/nmreval/gui_qt/lib/codeeditor.py
@@ -0,0 +1,262 @@
+# CodeEditor based on QT example, Python syntax highlighter found on Python site
+
+
+from ..Qt import QtGui, QtCore, QtWidgets
+
+
+def _make_textformats(color, style=''):
+ """Return a QTextCharFormat with the given attributes.
+ """
+ _color = QtGui.QColor()
+ _color.setNamedColor(color)
+
+ _format = QtGui.QTextCharFormat()
+ _format.setForeground(_color)
+ if 'bold' in style:
+ _format.setFontWeight(QtGui.QFont.Bold)
+ if 'italic' in style:
+ _format.setFontItalic(True)
+
+ return _format
+
+
+# Syntax styles that can be shared by all languages
+STYLES = {
+ 'keyword': _make_textformats('blue'),
+ 'operator': _make_textformats('black'),
+ 'brace': _make_textformats('black'),
+ 'defclass': _make_textformats('black', 'bold'),
+ 'string': _make_textformats('darkGreen'),
+ 'comment': _make_textformats('gray', 'italic'),
+ 'self': _make_textformats('brown', 'italic'),
+ 'property': _make_textformats('brown'),
+ 'numbers': _make_textformats('darkRed'),
+}
+
+
+class PythonHighlighter(QtGui.QSyntaxHighlighter):
+ """
+ Syntax highlighter for the Python language.
+ """
+ # Python keywords
+ keywords = [
+ 'and', 'assert', 'break', 'class', 'continue', 'def',
+ 'del', 'elif', 'else', 'except', 'exec', 'finally',
+ 'for', 'from', 'global', 'if', 'import', 'in',
+ 'is', 'lambda', 'not', 'or', 'pass', 'print',
+ 'raise', 'return', 'try', 'while', 'yield',
+ 'None', 'True', 'False', 'object'
+ ]
+
+ def __init__(self, document):
+ super().__init__(document)
+
+ # Multi-line strings (expression, flag, style)
+ # FIXME: The triple-quotes in these two lines will mess up the
+ # syntax highlighting from this point onward
+ self.tri_single = (QtCore.QRegExp("r'{3}"), 1, STYLES['string'])
+ self.tri_double = (QtCore.QRegExp('r?"{3}'), 2, STYLES['string'])
+
+ rules = []
+
+ # Keyword, operator, and brace rules
+ rules += [(rf'\b{w}\b', 0, STYLES['keyword']) for w in PythonHighlighter.keywords]
+
+ # Other rules
+ rules += [
+ # 'self'
+ (r'\bself\b', 0, STYLES['self']),
+
+ # 'def' followed by an identifier
+ (r'\bdef\b\s*(\w+)', 1, STYLES['defclass']),
+ # 'class' followed by an identifier
+ (r'\bclass\b\s*(\w+)', 1, STYLES['defclass']),
+ # @ followed by a word
+ (r'\s*@(\w+)\s*', 0, STYLES['property']),
+
+ # Numeric literals
+ (r'\b[+-]?\d+[lL]?\b', 0, STYLES['numbers']),
+ (r'\b[+-]?0[xX][\dA-Fa-f]+[lL]?\b', 0, STYLES['numbers']),
+ (r'\b[+-]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b', 0, STYLES['numbers']),
+
+
+ # Double-quoted string, possibly containing escape sequences
+ (r'[rf]?"[^"\\]*(\\.[^"\\]*)*"', 0, STYLES['string']),
+ # Single-quoted string, possibly containing escape sequences
+ (r"[rf]?'[^'\\]*(\\.[^'\\]*)*'", 0, STYLES['string']),
+
+ # From '#' until a newline
+ (r'#[^\n]*', 0, STYLES['comment']),
+ ]
+
+ # Build a QRegExp for each pattern
+ self.rules = [(QtCore.QRegExp(pat), index, fmt) for (pat, index, fmt) in rules]
+
+ def highlightBlock(self, text):
+ """
+ Apply syntax highlighting to the given block of text.
+ """
+ # Do other syntax formatting
+ for expression, nth, rule in self.rules:
+ index = expression.indexIn(text, 0)
+
+ while index >= 0:
+ # We actually want the index of the nth match
+ index = expression.pos(nth)
+ length = len(expression.cap(nth))
+ self.setFormat(index, length, rule)
+ index = expression.indexIn(text, index + length)
+
+ self.setCurrentBlockState(0)
+
+ # Do multi-line strings
+ in_multiline = self.match_multiline(text, *self.tri_single)
+ if not in_multiline:
+ in_multiline = self.match_multiline(text, *self.tri_double)
+
+ def match_multiline(self, text, delimiter, in_state, style):
+ """
+ Highlighting of multi-line strings. ``delimiter`` should be a
+ ``QRegExp`` for triple-single-quotes or triple-double-quotes, and
+ ``in_state`` should be a unique integer to represent the corresponding
+ state changes when inside those strings. Returns True if we're still
+ inside a multi-line string when this function is finished.
+ """
+ # If inside triple-single quotes, start at 0
+ if self.previousBlockState() == in_state:
+ start = 0
+ add = 0
+ # Otherwise, look for the delimiter on this line
+ else:
+ start = delimiter.indexIn(text)
+ # Move past this match
+ add = delimiter.matchedLength()
+
+ # As long as there's a delimiter match on this line...
+ while start >= 0:
+ # Look for the ending delimiter
+ end = delimiter.indexIn(text, start + add)
+ # Ending delimiter on this line?
+ if end >= add:
+ length = end - start + add + delimiter.matchedLength()
+ self.setCurrentBlockState(0)
+ # No; multi-line string
+ else:
+ self.setCurrentBlockState(in_state)
+ try:
+ length = text.length() - start + add
+ except AttributeError:
+ length = len(text) - start + add
+ # Apply formatting
+ self.setFormat(start, length, style)
+ # Look for the next match
+ start = delimiter.indexIn(text, start + length)
+
+ # Return True if still inside a multi-line string, False otherwise
+ if self.currentBlockState() == in_state:
+ return True
+ else:
+ return False
+
+
+class LineNumbers(QtWidgets.QWidget):
+ def __init__(self, editor):
+ super().__init__(editor)
+ self.editor = editor
+
+ def sizeHint(self):
+ return QtCore.QSize(self.editor.width_linenumber, 0)
+
+ def paintEvent(self, event):
+ self.editor.paintevent_linenumber(event)
+
+
+class CodeEditor(QtWidgets.QPlainTextEdit):
+ # more or less a direct translation of the Qt example
+ def __init__(self, parent):
+ super().__init__(parent)
+
+ self.current_linenumber = LineNumbers(self)
+
+ self.blockCountChanged.connect(self.update_width_linenumber)
+ self.updateRequest.connect(self.update_current_area)
+ self.cursorPositionChanged.connect(self.highlight_current_line)
+ self.update_width_linenumber(0)
+
+ self.highlight = PythonHighlighter(self.document())
+
+ def keyPressEvent(self, evt):
+ if evt.key() == QtCore.Qt.Key_Tab:
+ # use spaces instead of tab
+ self.insertPlainText(' '*4)
+ elif evt.key() == QtCore.Qt.Key_Insert:
+ self.setOverwriteMode(not self.overwriteMode())
+ else:
+ super().keyPressEvent(evt)
+
+ @property
+ def width_linenumber(self):
+ digits = 1
+ count = max(1, self.blockCount())
+ while count >= 10:
+ count /= 10
+ digits += 1
+ space = 6 + self.fontMetrics().width('9') * digits
+
+ return space
+
+ def update_width_linenumber(self, _):
+ self.setViewportMargins(self.width_linenumber, 0, 0, 0)
+
+ def update_current_area(self, rect, dy):
+ if dy:
+ self.current_linenumber.scroll(0, dy)
+ else:
+ self.current_linenumber.update(0, rect.y(), self.current_linenumber.width(), rect.height())
+ if rect.contains(self.viewport().rect()):
+ self.update_width_linenumber(0)
+
+ def resizeEvent(self, evt):
+ super().resizeEvent(evt)
+
+ cr = self.contentsRect()
+ self.current_linenumber.setGeometry(QtCore.QRect(cr.left(), cr.top(), self.width_linenumber, cr.height()))
+
+ def paintevent_linenumber(self, evt):
+ painter = QtGui.QPainter(self.current_linenumber)
+ painter.fillRect(evt.rect(), QtCore.Qt.lightGray)
+
+ block = self.firstVisibleBlock()
+ block_number = block.blockNumber()
+ top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top()
+ bottom = top + self.blockBoundingRect(block).height()
+
+ # Just to make sure I use the right font
+ height = self.fontMetrics().height()
+ while block.isValid() and (top <= evt.rect().bottom()):
+ if block.isVisible() and (bottom >= evt.rect().top()):
+ number = str(block_number + 1)
+ painter.setPen(QtCore.Qt.black)
+ painter.drawText(0, top, self.current_linenumber.width() - 3, height,
+ QtCore.Qt.AlignRight, number)
+
+ block = block.next()
+ top = bottom
+ bottom = top + self.blockBoundingRect(block).height()
+ block_number += 1
+
+ def highlight_current_line(self):
+ extra_selections = []
+
+ if not self.isReadOnly():
+ selection = QtWidgets.QTextEdit.ExtraSelection()
+
+ line_color = QtGui.QColor(QtCore.Qt.yellow).lighter(180)
+
+ selection.format.setBackground(line_color)
+ selection.format.setProperty(QtGui.QTextFormat.FullWidthSelection, True)
+ selection.cursor = self.textCursor()
+ selection.cursor.clearSelection()
+ extra_selections.append(selection)
+
+ self.setExtraSelections(extra_selections)
diff --git a/nmreval/gui_qt/lib/color_dialog.py b/nmreval/gui_qt/lib/color_dialog.py
new file mode 100644
index 0000000..0c51f64
--- /dev/null
+++ b/nmreval/gui_qt/lib/color_dialog.py
@@ -0,0 +1,89 @@
+from nmreval.configs import config_paths
+from nmreval.gui_qt.Qt import QtWidgets, QtCore, QtGui
+from nmreval.gui_qt._py.color_palette import Ui_Dialog
+from nmreval.lib.colors import Colors, get_palettes
+
+
+class PaletteDialog(QtWidgets.QDialog, Ui_Dialog):
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self._palettes = get_palettes()
+ self.palette_combobox.addItems(self._palettes.keys())
+
+ self.colorlist.installEventFilter(self)
+
+ def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent) -> bool:
+ if evt.type() == evt.KeyPress:
+ if evt.key() == QtCore.Qt.Key_Delete:
+ self.colorlist.takeItem(self.colorlist.currentRow())
+
+ return True
+
+ return super().eventFilter(src, evt)
+
+ @QtCore.pyqtSlot(name='on_add_palette_button_pressed')
+ @QtCore.pyqtSlot(name='on_append_palette_button_pressed')
+ def set_palette(self, clear=True):
+ if self.sender() == self.add_palette_button or clear:
+ self.colorlist.clear()
+
+ for color in self._palettes[self.palette_combobox.currentText()]:
+ item = QtWidgets.QListWidgetItem(color.name)
+ item.setData(QtCore.Qt.DecorationRole, QtGui.QColor.fromRgbF(*color.rgb(normed=True)))
+ self.colorlist.addItem(item)
+
+ @QtCore.pyqtSlot(name='on_add_color_button_pressed')
+ def add_color(self):
+ color = self.color_combobox.value
+ item = QtWidgets.QListWidgetItem(color.name)
+ item.setData(QtCore.Qt.DecorationRole, QtGui.QColor.fromRgbF(*color.rgb(normed=True)))
+ self.colorlist.addItem(item)
+
+ @QtCore.pyqtSlot(name='on_save_button_pressed')
+ def save_new_palette(self):
+ if not self.colorlist.count():
+ _ = QtWidgets.QMessageBox.warning(self, 'Error', 'No colors to save.')
+ return
+
+ if not self.new_name_edit.text():
+ _ = QtWidgets.QMessageBox.warning(self, 'Error', 'New colors have no name.')
+ return
+
+ color_name = self.new_name_edit.text()
+ if color_name in self._palettes:
+ _ = QtWidgets.QMessageBox.warning(self, 'Error', 'Name already used.')
+ return
+
+ color_file = config_paths() / 'colorschemes.cfg'
+
+ new_palette = []
+ with color_file.open('a') as f:
+ f.write('#-- %s\n' % color_name)
+ for i in range(self.colorlist.count()):
+ item = self.colorlist.item(i)
+ r, g, b = item.data(QtCore.Qt.DecorationRole).getRgbF()[:3]
+ new_palette.append(Colors.from_rgb(r, g, b, normed=True))
+ f.write('{r*255:.1f}, {g*255:.1f}, {b*255:.1f}\n')
+ f.write('%.1f, %.1f}, %.1f}\n' % (r*255, g*255, b*255))
+ f.write('\n')
+
+ self._palettes[color_name] = new_palette
+ self.palette_combobox.addItem(color_name)
+
+ def load_palette(self, name: str):
+ idx = self.palette_combobox.findText(name)
+ if idx != -1:
+ self.palette_combobox.setCurrentIndex(idx)
+ self.set_palette(clear=True)
+
+
+if __name__ == '__main__':
+ import sys
+ app = QtWidgets.QApplication([])
+ d = PaletteDialog()
+ d.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/lib/configurations.py b/nmreval/gui_qt/lib/configurations.py
new file mode 100644
index 0000000..0bc114f
--- /dev/null
+++ b/nmreval/gui_qt/lib/configurations.py
@@ -0,0 +1,156 @@
+import os
+
+from ...configs import *
+from ...io.graceeditor import GraceEditor
+
+from ..Qt import QtWidgets, QtGui
+from .._py.agroptiondialog import Ui_Dialog
+
+
+class QGraceSettings(QtWidgets.QDialog, Ui_Dialog):
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setupUi(self)
+ self._default_path = config_paths().joinpath('Default.agr')
+ self._default = GraceEditor(self._default_path)
+
+ self.setup_ui()
+
+ def setup_ui(self):
+ page = self._default.size
+ self.widthDoubleSpinBox.setValue(page[0])
+ self.heightDoubleSpinBox.setValue(page[1])
+
+ view = self._default.get_property(0, 'view')
+ self.leftMarginDoubleSpinBox.setValue(self._default.convert(view[0], direction='abs'))
+ self.bottomMarginDoubleSpinBox.setValue(self._default.convert(view[1], direction='abs'))
+ self.rightMarginDoubleSpinBox.setValue(page[0]-self._default.convert(view[2], direction='abs'))
+ self.topMarginDoubleSpinBox.setValue(page[1]-self._default.convert(view[3], direction='abs'))
+
+
+class GraceMsgBox(QtWidgets.QDialog):
+ def __init__(self, fname, parent=None):
+ super(GraceMsgBox, self).__init__(parent=parent)
+
+ agr = GraceEditor()
+ agr.parse(fname)
+
+ layout = QtWidgets.QGridLayout()
+ layout.setContentsMargins(13, 13, 13, 13)
+ self.setLayout(layout)
+
+ label = QtWidgets.QLabel('%s already exists. Select one of the options or cancel:' % os.path.split(fname)[1])
+ layout.addWidget(label, 1, 1, 1, 2)
+
+ self.button_grp = QtWidgets.QButtonGroup(self)
+
+ self.overwrite_radiobutton = QtWidgets.QRadioButton('Overwrite file', parent=self)
+ self.overwrite_radiobutton.setChecked(True)
+ self.button_grp.addButton(self.overwrite_radiobutton, id=0)
+ layout.addWidget(self.overwrite_radiobutton, 2, 1, 1, 2)
+
+ self.new_graph_button = QtWidgets.QRadioButton('Create new graph', parent=self)
+ self.button_grp.addButton(self.new_graph_button, id=1)
+ layout.addWidget(self.new_graph_button, 3, 1, 1, 2)
+
+ self.addgraph_button = QtWidgets.QRadioButton('Add sets to graph', parent=self)
+ self.button_grp.addButton(self.addgraph_button, id=3)
+ layout.addWidget(self.addgraph_button, 4, 1, 1, 1)
+
+ self.graph_combobox = QtWidgets.QComboBox(self)
+ self.graph_combobox.addItems(['G' + str(g.idx) for g in agr.graphs])
+ layout.addWidget(self.graph_combobox, 4, 2, 1, 1)
+
+ self.buttonbox = QtWidgets.QDialogButtonBox(self)
+ self.buttonbox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok)
+ layout.addWidget(self.buttonbox, 5, 1, 1, 2)
+
+ self.buttonbox.rejected.connect(self.reject)
+ self.buttonbox.accepted.connect(self.accept)
+
+ def accept(self) -> None:
+ super().accept()
+ self.setResult(self.button_grp.checkedId())
+ if self.button_grp.checkedId() == 3:
+ self.setResult(int(self.graph_combobox.currentText()[1:]) + 2)
+
+ def reject(self) -> None:
+ super().reject()
+ self.setResult(-1)
+
+
+class GeneralConfiguration(QtWidgets.QDialog):
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ layout = QtWidgets.QVBoxLayout()
+ layout.setContentsMargins(3, 3, 3, 3)
+
+ intro = QtWidgets.QLabel('Changes become active after restart.')
+ layout.addWidget(intro)
+
+ parser = read_configuration()
+ for sec in parser.sections():
+ group = QtWidgets.QGroupBox(sec, self)
+
+ layout2 = QtWidgets.QGridLayout()
+ layout2.setContentsMargins(3, 3, 3, 3)
+ row = 0
+ for key, value in parser.items(sec):
+ label = QtWidgets.QLabel(key.capitalize(), self)
+ layout2.addWidget(label, row, 0)
+ if (sec, key) in allowed_values:
+ edit = QtWidgets.QComboBox(self)
+ edit.addItems(allowed_values[(sec, key)])
+ edit.setCurrentIndex(edit.findText(value))
+ else:
+ edit = QtWidgets.QLineEdit(self)
+ edit.setText(value)
+ try:
+ _ = float(value)
+ edit.setValidator(QtGui.QDoubleValidator())
+ except ValueError:
+ pass
+
+ layout2.addWidget(edit, row, 1)
+ row += 1
+
+ group.setLayout(layout2)
+
+ layout.addWidget(group)
+
+ self.buttonbox = QtWidgets.QDialogButtonBox(self)
+ self.buttonbox.setStandardButtons(self.buttonbox.Ok|self.buttonbox.Cancel)
+ self.buttonbox.rejected.connect(self.close)
+ self.buttonbox.accepted.connect(self.accept)
+ layout.addWidget(self.buttonbox)
+
+ self.setLayout(layout)
+
+ def accept(self):
+ options = {}
+ for section in self.findChildren(QtWidgets.QGroupBox):
+ args = {}
+
+ layout = section.layout()
+ for row in range(layout.rowCount()):
+ key = layout.itemAtPosition(row, 0).widget().text()
+
+ value_widget = layout.itemAtPosition(row, 1).widget()
+ if isinstance(value_widget, QtWidgets.QComboBox):
+ value = value_widget.currentText()
+ elif isinstance(value_widget, QtWidgets.QLineEdit):
+ value = value_widget.text()
+ elif isinstance(value_widget, QtWidgets.QDoubleSpinBox):
+ value = value_widget.text()
+ else:
+ raise TypeError('Config key %s has unknown type %s' % (key, repr(value_widget)))
+
+ args[key] = value
+
+ options[section.title()] = args
+
+ write_configuration(options)
+
+ super().accept()
diff --git a/nmreval/gui_qt/lib/decorators.py b/nmreval/gui_qt/lib/decorators.py
new file mode 100644
index 0000000..9f40149
--- /dev/null
+++ b/nmreval/gui_qt/lib/decorators.py
@@ -0,0 +1,55 @@
+from functools import wraps
+
+from ..Qt import QtWidgets
+
+
+def update_indexes(func):
+
+ @wraps(func)
+ def wrapped(self, *args, **kwargs):
+ ret_val = func(self, *args, **kwargs)
+ self.blockSignals(True)
+
+ iterator = QtWidgets.QTreeWidgetItemIterator(self)
+ i = j = 0
+ while iterator.value():
+ item = iterator.value()
+ if item is not None:
+ if item.parent() is None:
+ item.setText(1, 'g[{}]'.format(i))
+ i += 1
+ j = 0
+ else:
+ item.setText(1, '.s[{}]'.format(j))
+ j += 1
+ iterator += 1
+
+ self.blockSignals(False)
+
+ return ret_val
+
+ return wrapped
+
+
+def plot_update(func):
+
+ @wraps(func)
+ def wrapped(self, *args, **kwargs):
+ ret_val = func(self, *args, **kwargs)
+ m = self._data.mask
+ _x = self._data.x
+ _y = self._data.y
+
+ self.plot_real.setData(x=_x[m], y=_y.real[m], name=self._data.name)
+ if self.plot_imag is not None:
+ self.plot_imag.setData(x=_x[m], y=_y.imag[m], name=self._data.name)
+
+ if self.plot_error is not None:
+ _y_err = self._data.y_err
+ self.plot_error.setData(x=_x[m], y=_y.real[m], top=_y_err[m], bottom=_y_err[m])
+
+ self.dataChanged.emit(self.id)
+
+ return ret_val
+
+ return wrapped
diff --git a/nmreval/gui_qt/lib/delegates.py b/nmreval/gui_qt/lib/delegates.py
new file mode 100644
index 0000000..50f79eb
--- /dev/null
+++ b/nmreval/gui_qt/lib/delegates.py
@@ -0,0 +1,235 @@
+import re
+
+from ..Qt import QtWidgets, QtGui, QtCore
+
+from ...lib.colors import BaseColor, Colors
+from ...lib.lines import LineStyle
+from ...lib.symbols import SymbolStyle, make_symbol_pixmap
+
+
+class PropertyDelegate(QtWidgets.QStyledItemDelegate):
+ def paint(self, painter: QtGui.QPainter, options: QtWidgets.QStyleOptionViewItem, idx: QtCore.QModelIndex):
+ r = idx.data(QtCore.Qt.DisplayRole)
+ if r is not None:
+ if isinstance(r, BaseColor):
+ painter.save()
+ c = QtGui.QColor(*r.value)
+ rect = options.rect
+ painter.fillRect(rect, QtGui.QBrush(c))
+ painter.restore()
+
+ elif isinstance(r, LineStyle):
+ painter.save()
+ pen = QtGui.QPen()
+ pal = QtGui.QGuiApplication.palette()
+ pen.setColor(pal.color(QtGui.QPalette.Text))
+ pen.setWidth(2)
+ pen.setStyle(r.value)
+ pen.setCapStyle(QtCore.Qt.RoundCap)
+ painter.setPen(pen)
+
+ rect = options.rect
+ rect.adjust(5, 0, -5, 0)
+ mid = (rect.bottom()+rect.top()) / 2
+ painter.drawLine(rect.left(), mid, rect.right(), mid)
+ painter.restore()
+
+ elif isinstance(r, SymbolStyle):
+ painter.save()
+ pen = QtGui.QPen()
+ pal = QtGui.QGuiApplication.palette()
+ pen.setColor(pal.color(QtGui.QPalette.Text))
+ painter.setPen(pen)
+
+ pm = make_symbol_pixmap(r)
+ painter.drawPixmap(options.rect.topLeft()+QtCore.QPoint(3, (options.rect.height()-pm.height())/2), pm)
+
+ style = QtWidgets.QApplication.style()
+ text_rect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, options, None)
+ text_rect.adjust(5+pm.width(), 0, 0, 0)
+ painter.drawText(text_rect, options.displayAlignment, r.name)
+
+ painter.restore()
+
+ else:
+ super().paint(painter, options, idx)
+
+ def createEditor(self, parent: QtWidgets.QWidget,
+ options: QtWidgets.QStyleOptionViewItem, idx: QtCore.QModelIndex) -> QtWidgets.QWidget:
+ data = idx.data()
+ if isinstance(data, BaseColor):
+ editor = ColorListEditor(parent)
+
+ elif isinstance(data, LineStyle):
+ editor = LineStyleEditor(parent)
+
+ elif isinstance(data, SymbolStyle):
+ editor = SymbolStyleEditor(parent)
+
+ elif isinstance(data, float):
+ editor = SpinBoxEditor(parent)
+
+ else:
+ editor = super().createEditor(parent, options, idx)
+
+ return editor
+
+ def setEditorData(self, editor: QtWidgets.QWidget, idx: QtCore.QModelIndex):
+ data = idx.data()
+ if isinstance(data, (BaseColor, LineStyle, SymbolStyle)):
+ editor.value = data
+ else:
+ super().setEditorData(editor, idx)
+
+ def setModelData(self, editor: QtWidgets.QWidget, model: QtCore.QAbstractItemModel, idx: QtCore.QModelIndex):
+ data = idx.data()
+ if isinstance(data, (BaseColor, LineStyle, SymbolStyle)):
+ model.setData(idx, editor.value)
+ else:
+ super().setModelData(editor, model, idx)
+
+
+class ColorListEditor(QtWidgets.QComboBox):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.populateList()
+
+ @QtCore.pyqtProperty(BaseColor, user=True)
+ def value(self):
+ return self.itemData(self.currentIndex())
+
+ @value.setter
+ def value(self, val):
+ for i in range(self.count()):
+ if val == self.itemData(i):
+ self.setCurrentIndex(i)
+ break
+
+ def populateList(self):
+ for i, colorName in enumerate(Colors):
+ color = QtGui.QColor(*colorName.value)
+ self.insertItem(i, colorName.name)
+ self.setItemData(i, colorName)
+ px = QtGui.QPixmap(self.iconSize())
+ px.fill(color)
+ self.setItemData(i, QtGui.QIcon(px), QtCore.Qt.DecorationRole)
+
+
+class SpinBoxEditor(QtWidgets.QDoubleSpinBox):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.setValue(1.0)
+ self.setSingleStep(0.1)
+ self.setDecimals(1)
+
+ @QtCore.pyqtProperty(float, user=True)
+ def value(self):
+ return super().value()
+
+ @value.setter
+ def value(self, val):
+ super().setValue(val)
+
+
+class LineStyleEditor(QtWidgets.QComboBox):
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setItemDelegate(LineStyleDelegate())
+ self.populate()
+
+ @QtCore.pyqtProperty(int, user=True)
+ def value(self):
+ return self.itemData(self.currentIndex())
+
+ @value.setter
+ def value(self, val):
+ for i in range(self.count()):
+ if val == self.itemData(i):
+ self.setCurrentIndex(i)
+ break
+
+ def populate(self):
+ for i, style in enumerate(LineStyle):
+ self.insertItem(i, re.sub(r'([A-Z])', r' \g<1>', style.name))
+ self.setItemData(i, style)
+
+ def paintEvent(self, evt: QtGui.QPaintEvent):
+ if self.currentData() is not None:
+ painter = QtWidgets.QStylePainter(self)
+ opt = QtWidgets.QStyleOptionComboBox()
+ self.initStyleOption(opt)
+ painter.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, opt)
+ pen = QtGui.QPen()
+ pal = QtGui.QGuiApplication.palette()
+ pen.setColor(pal.color(QtGui.QPalette.Text))
+ pen.setWidth(2)
+ pen.setStyle(self.currentData().value)
+ pen.setCapStyle(QtCore.Qt.RoundCap)
+ painter.setPen(pen)
+
+ rect = painter.style().subControlRect(QtWidgets.QStyle.CC_ComboBox,
+ opt, QtWidgets.QStyle.SC_ComboBoxEditField, None)
+ rect.adjust(+10, 0, -10, 0)
+ mid = (rect.bottom() + rect.top()) / 2
+ painter.drawLine(rect.left(), mid, rect.right(), mid)
+ painter.end()
+ else:
+ super().paintEvent(evt)
+
+
+class LineStyleDelegate(QtWidgets.QStyledItemDelegate):
+ def paint(self, painter, option, index):
+ data = index.data(QtCore.Qt.UserRole)
+
+ if data is not None:
+ pen = QtGui.QPen()
+ pal = QtGui.QGuiApplication.palette()
+ pen.setColor(pal.color(QtGui.QPalette.Text))
+ pen.setWidth(2)
+ pen.setStyle(data.value)
+ pen.setCapStyle(QtCore.Qt.RoundCap)
+ painter.setPen(pen)
+
+ rect = option.rect
+ rect.adjust(+10, 0, -10, 0)
+ mid = (rect.bottom()+rect.top()) / 2
+ painter.drawLine(rect.left(), mid, rect.right(), mid)
+ else:
+ QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)
+
+
+class SymbolStyleEditor(QtWidgets.QComboBox):
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.populate()
+
+ @QtCore.pyqtProperty(SymbolStyle, user=True)
+ def value(self):
+ return self.itemData(self.currentIndex())
+
+ @value.setter
+ def value(self, val):
+ for i in range(self.count()):
+ if val == self.itemData(i):
+ self.setCurrentIndex(i)
+ break
+
+ def populate(self):
+ for i, s in enumerate(SymbolStyle):
+ self.insertItem(i, re.sub(r'([A-Z])', r' \g<1>', s.name))
+ self.setItemData(i, s)
+ self.setItemData(i, make_symbol_pixmap(s), QtCore.Qt.DecorationRole)
+
+
+class HeaderDelegate(QtWidgets.QStyledItemDelegate):
+ def paint(self, painter: QtGui.QPainter, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex):
+
+ header_option = QtWidgets.QStyleOptionHeader()
+ header_option.rect = option.rect
+
+ style = QtWidgets.QApplication.style()
+ style.drawControl(QtWidgets.QStyle.CE_HeaderSection, header_option, painter)
+ if option.state & QtWidgets.QStyle.State_Selected:
+ painter.fillRect(option.rect, option.palette.highlight())
\ No newline at end of file
diff --git a/nmreval/gui_qt/lib/expandablewidget.py b/nmreval/gui_qt/lib/expandablewidget.py
new file mode 100644
index 0000000..29bfd82
--- /dev/null
+++ b/nmreval/gui_qt/lib/expandablewidget.py
@@ -0,0 +1,65 @@
+from ..Qt import QtWidgets, QtCore
+
+
+class ExpandableWidget(QtWidgets.QWidget):
+ expansionChanged = QtCore.pyqtSignal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self._init_ui()
+
+ self._widget = None
+ self._expanded = False
+
+ self.toolButton.clicked.connect(self.changeVisibility)
+
+ def _init_ui(self):
+ self.verticalLayout = QtWidgets.QVBoxLayout(self)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(0)
+
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setSpacing(0)
+
+ self.toolButton = QtWidgets.QToolButton(self)
+ self.toolButton.setArrowType(QtCore.Qt.RightArrow)
+ self.toolButton.setStyleSheet('border: 0')
+ self.toolButton.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.horizontalLayout.addWidget(self.toolButton)
+
+ self.line = QtWidgets.QFrame(self)
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
+ self.horizontalLayout.addWidget(self.line)
+
+ self.verticalLayout.addLayout(self.horizontalLayout)
+
+ def addWidget(self, widget: QtWidgets.QWidget):
+ self._widget = widget
+ self._widget.setVisible(self._expanded)
+ self.layout().addWidget(widget)
+
+ def setText(self, text: str):
+ self.toolButton.setText(text)
+
+ def isExpanded(self):
+ return self._expanded
+
+ def changeVisibility(self):
+ if not self._widget:
+ return
+
+ self.setExpansion(not self._expanded)
+
+ self.expansionChanged.emit()
+
+ def setExpansion(self, state: bool):
+ self.blockSignals(True)
+ if state:
+ self.toolButton.setArrowType(QtCore.Qt.DownArrow)
+ else:
+ self.toolButton.setArrowType(QtCore.Qt.RightArrow)
+
+ self._expanded = state
+ self._widget.setVisible(state)
+ self.blockSignals(False)
diff --git a/nmreval/gui_qt/lib/forms.py b/nmreval/gui_qt/lib/forms.py
new file mode 100644
index 0000000..fb3058d
--- /dev/null
+++ b/nmreval/gui_qt/lib/forms.py
@@ -0,0 +1,402 @@
+from numpy import inf
+
+from ...utils.text import convert
+
+from ..Qt import QtGui, QtCore, QtWidgets
+from .._py.mean_form import Ui_mean_form
+
+
+class QDelayWidget(QtWidgets.QWidget):
+ get_data = QtCore.pyqtSignal(str)
+ newDelay = QtCore.pyqtSignal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.vals = ''
+ self.dim = 0
+ self.plainTextEdit.setVisible(False)
+
+ def __call__(self):
+ self.vals = ''
+ self.dim = 0
+ self.comboBox.clear()
+ self.comboBox.blockSignals(True)
+ self.comboBox.addItem('New list...')
+ self.comboBox.blockSignals(True)
+ self.plainTextEdit.setVisible(False)
+
+ def add_items(self, item):
+ self.comboBox.blockSignals(True)
+ if isinstance(item, list):
+ self.comboBox.insertItems(0, item)
+ else:
+ self.comboBox.insertItem(0, item)
+ self.comboBox.setCurrentIndex(0)
+ self.comboBox.blockSignals(False)
+
+ @QtCore.pyqtSlot(name='on_toolButton_clicked')
+ def show_values(self):
+ if self.plainTextEdit.isVisible():
+ self.plainTextEdit.clear()
+ self.plainTextEdit.setVisible(False)
+ elif self.comboBox.currentText() == 'New list...':
+ self.newDelay.emit()
+ else:
+ self.get_data.emit(self.comboBox.currentText())
+ self.plainTextEdit.setVisible(True)
+ self.plainTextEdit.setPlainText(self.vals)
+
+
+class LineEdit(QtWidgets.QLineEdit):
+ values_requested = QtCore.pyqtSignal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ def contextMenuEvent(self, evt):
+ menu = self.createStandardContextMenu()
+ request_action = menu.addAction('Use value of set(s)')
+
+ action = menu.exec(evt.globalPos())
+
+ if action == request_action:
+ self.values_requested.emit()
+
+
+class LineEditPost(QtWidgets.QLineEdit):
+ values_requested = QtCore.pyqtSignal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.suffix = ''
+ self.prefix = ''
+
+ self.editingFinished.connect(self.add_fixes)
+
+ def add_fixes(self):
+ self.setText(self.prefix+super().text()+self.suffix)
+
+ def text(self):
+ text = super().text()
+ if text.startswith(self.prefix):
+ text = text[len(self.prefix):]
+ if text.endswith(self.suffix):
+ text = text[:-len(self.suffix)]
+
+ return text
+
+
+class FormWidget(QtWidgets.QWidget):
+ types = {'float': (float, QtGui.QDoubleValidator),
+ 'int': (int, QtGui.QIntValidator),
+ 'str': (str, lambda: 0)}
+
+ valueChanged = QtCore.pyqtSignal(object)
+ stateChanged = QtCore.pyqtSignal(bool)
+
+ def __init__(self, name: str, validator: str = 'float', fixable: bool = False, parent=None):
+ super().__init__(parent=parent)
+
+ self._init_ui()
+
+ self._name = name
+
+ self._type = FormWidget.types[validator][0]
+ self.vals.setValidator(FormWidget.types[validator][1]())
+ self.vals.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0)
+
+ self.label.setText(convert(name))
+
+ self._checkable = fixable
+ self.checkBox.setVisible(fixable)
+ self.checkBox.stateChanged.connect(lambda x: self.stateChanged.emit(True if x else False))
+
+ def _init_ui(self):
+ layout = QtWidgets.QHBoxLayout(self)
+ layout.setContentsMargins(2, 2, 2, 2)
+ layout.setSpacing(2)
+
+ self.label = QtWidgets.QLabel(self)
+ layout.addWidget(self.label)
+
+ layout.addSpacerItem(QtWidgets.QSpacerItem(20, 20,
+ QtWidgets.QSizePolicy.Expanding,
+ QtWidgets.QSizePolicy.Minimum))
+
+ self.vals = QtWidgets.QLineEdit(self)
+ layout.addWidget(self.vals)
+
+ self.checkBox = QtWidgets.QCheckBox('Fix?', self)
+ layout.addWidget(self.checkBox)
+
+ @property
+ def value(self):
+ try:
+ return self._type(self.vals.text().replace(',', '.'))
+ except ValueError:
+ return {float: 0.0, int: 0, str: ''}[self._type]
+
+ @value.setter
+ def value(self, val):
+ self.vals.setText(str(val))
+
+ def setChecked(self, enable):
+ if self._checkable:
+ self.checkBox.setCheckState(QtCore.Qt.Checked if enable else QtCore.Qt.Unchecked)
+ else:
+ print(f'Parameter {self._name} is not variable')
+
+ def isChecked(self):
+ return self.checkBox.isChecked()
+
+
+class SelectionWidget(QtWidgets.QWidget):
+ selectionChanged = QtCore.pyqtSignal(str, object)
+
+ def __init__(self, label: str, argname: str, opts: dict, parent=None):
+ super().__init__(parent=parent)
+
+ self._init_ui()
+ self.label.setText(convert(label))
+ for k in opts.keys():
+ self.comboBox.addItem(k)
+
+ self.argname = argname
+ self.options = opts
+
+ self.comboBox.currentIndexChanged.connect(lambda idx: self.selectionChanged.emit(self.argname, self.value))
+
+ def _init_ui(self):
+ layout = QtWidgets.QHBoxLayout(self)
+ layout.setContentsMargins(1, 1, 1, 1)
+ layout.setSpacing(2)
+
+ self.label = QtWidgets.QLabel(self)
+ layout.addWidget(self.label)
+
+ layout.addSpacerItem(QtWidgets.QSpacerItem(65, 20,
+ QtWidgets.QSizePolicy.Expanding,
+ QtWidgets.QSizePolicy.Minimum))
+
+ self.comboBox = QtWidgets.QComboBox(self)
+ layout.addWidget(self.comboBox)
+ self.setLayout(layout)
+
+ @property
+ def value(self):
+ try:
+ return float(self.options[str(self.comboBox.currentText())])
+ except ValueError:
+ return str(self.options[str(self.comboBox.currentText())])
+
+ def get_parameter(self):
+ return str(self.comboBox.currentText())
+
+ @value.setter
+ def value(self, val):
+ key = [k for k, v in self.options.items() if v == val][0]
+ self.comboBox.setCurrentIndex(self.comboBox.findText(key))
+
+
+class Widget(QtWidgets.QWidget, Ui_mean_form):
+ valueChanged = QtCore.pyqtSignal()
+
+ def __init__(self, name: str, tree: dict, collapsing: bool = False, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self._tree = {}
+
+ self._collapse = collapsing
+
+ self.label.setText(convert(name))
+ self.lineEdit.setValidator(QtGui.QDoubleValidator())
+
+ self.set_graphs(tree)
+ self.change_graph(0)
+
+ if self._collapse:
+ self.digit_checkbox.hide()
+ self.frame.hide()
+ self.data_checkbox.stateChanged.connect(self.collapse_widgets)
+ else:
+ self.data_checkbox.stateChanged.connect(self.change_mode)
+ self.digit_checkbox.stateChanged.connect(self.change_mode)
+
+ def set_graphs(self, graph: dict):
+ self.graph_combobox.blockSignals(True)
+ self._tree.clear()
+ for key, (name, _) in graph.items():
+ self.graph_combobox.addItem(name, userData=key)
+ self.graph_combobox.blockSignals(False)
+ self._tree.update(graph)
+ self.change_graph(0)
+
+ @QtCore.pyqtSlot(int, name='on_graph_combobox_currentIndexChanged')
+ def change_graph(self, idx: int):
+ self.set_combobox.clear()
+
+ key = self.graph_combobox.itemData(idx, QtCore.Qt.UserRole)
+ if key is not None:
+ for set_key, set_name in self._tree[key][1]:
+ self.set_combobox.addItem(set_name, userData=set_key)
+
+ def on_lineEdit_textChanged(self, text=''):
+ if text:
+ self.valueChanged.emit()
+
+ @property
+ def value(self):
+ if self.data_checkbox.isChecked():
+ return self.set_combobox.currentData()
+ else:
+ try:
+ return float(self.lineEdit.text())
+ except ValueError:
+ return
+
+ @QtCore.pyqtSlot(int)
+ def change_mode(self, state: int):
+ box = self.sender()
+ other_box = self.data_checkbox if box == self.digit_checkbox else self.digit_checkbox
+
+ if (state == QtCore.Qt.Unchecked) and (other_box.checkState() == QtCore.Qt.Unchecked):
+ box.blockSignals(True)
+ box.setCheckState(QtCore.Qt.Checked)
+ box.blockSignals(False)
+ return
+
+ other_box.blockSignals(True)
+ other_box.setChecked(False)
+ other_box.blockSignals(False)
+
+ self.valueChanged.emit()
+
+ @QtCore.pyqtSlot(int)
+ def collapse_widgets(self, state: int):
+ data_is_checked = state == QtCore.Qt.Checked
+ self.frame.setVisible(data_is_checked)
+ self.lineEdit.setVisible(not data_is_checked)
+
+
+class CheckBoxHeader(QtWidgets.QHeaderView):
+ clicked = QtCore.pyqtSignal(int, bool)
+
+ _x_offset = 3
+ _y_offset = 0 # This value is calculated later, based on the height of the paint rect
+ _width = 20
+ _height = 20
+
+ def __init__(self, column_indices, orientation=QtCore.Qt.Horizontal, parent=None):
+ super().__init__(orientation, parent)
+ self.setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
+ # self.setClickable(True)
+
+ if isinstance(column_indices, list) or isinstance(column_indices, tuple):
+ self.column_indices = column_indices
+ elif isinstance(column_indices, int):
+ self.column_indices = [column_indices]
+ else:
+ raise RuntimeError('column_indices must be a list, tuple or integer')
+
+ self.isChecked = {}
+ for column in self.column_indices:
+ self.isChecked[column] = 0
+
+ def paintSection(self, painter, rect, logicalIndex):
+ painter.save()
+ super().paintSection(painter, rect, logicalIndex)
+ painter.restore()
+
+ self._y_offset = int((rect.height()-self._width)/2.)
+
+ if logicalIndex in self.column_indices:
+ option = QtWidgets.QStyleOptionButton()
+ option.rect = QtCore.QRect(rect.x() + self._x_offset, rect.y() + self._y_offset, self._width, self._height)
+ option.state = QtWidgets.QStyle.State_Enabled | QtWidgets.QStyle.State_Active
+ if self.isChecked[logicalIndex] == 2:
+ option.state |= QtWidgets.QStyle.State_NoChange
+ elif self.isChecked[logicalIndex]:
+ option.state |= QtWidgets.QStyle.State_On
+ else:
+ option.state |= QtWidgets.QStyle.State_Off
+
+ self.style().drawControl(QtWidgets.QStyle.CE_CheckBox,option,painter)
+
+ def updateCheckState(self, index, state):
+ self.isChecked[index] = state
+ self.viewport().update()
+
+ def mousePressEvent(self, event):
+ index = self.logicalIndexAt(event.pos())
+ if 0 <= index < self.count():
+ x = self.sectionPosition(index)
+ if x + self._x_offset < event.pos().x() < x + self._x_offset + self._width and self._y_offset < event.pos().y() < self._y_offset + self._height:
+ if self.isChecked[index] == 1:
+ self.isChecked[index] = 0
+ else:
+ self.isChecked[index] = 1
+
+ self.clicked.emit(index, self.isChecked[index])
+ self.viewport().update()
+ else:
+ super().mousePressEvent(event)
+ else:
+ super().mousePressEvent(event)
+
+
+class CustomRangeDialog(QtWidgets.QDialog):
+ """ Simple dialog to enter x and y limits to fit regions"""
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.gl = QtWidgets.QGridLayout()
+ self.gl.addWidget(QtWidgets.QLabel('Leave empty for complete range'), 0, 0, 1, 4)
+
+ self._limits = [[], []]
+ for i, orient in enumerate(['x', 'y'], start=1):
+ self.gl.addWidget(QtWidgets.QLabel(orient + ' from'), i, 0)
+ self.gl.addWidget(QtWidgets.QLabel('to'), i, 2)
+ for j in [0, 1]:
+ lim = QtWidgets.QLineEdit()
+ lim.setValidator(QtGui.QDoubleValidator())
+ self._limits[i-1].append(lim)
+ self.gl.addWidget(lim, i, 2*j+1)
+
+ self.buttonbox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok)
+ self.buttonbox.accepted.connect(self.accept)
+
+ self.gl.addWidget(self.buttonbox, 3, 0, 1, 4)
+ self.setLayout(self.gl)
+
+ @property
+ def limits(self):
+ ret_val = []
+ for orient in self._limits:
+ for i, w in enumerate(orient):
+ val = w.text()
+ if val == '':
+ ret_val.append(-inf if i == 0 else inf)
+ else:
+ ret_val.append(float(val))
+ return ret_val
+
+
+class ElideComboBox(QtWidgets.QComboBox):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.view().setTextElideMode(QtCore.Qt.ElideRight)
+
+ def paintEvent(self, evt: QtGui.QPaintEvent):
+ opt = QtWidgets.QStyleOptionComboBox()
+ self.initStyleOption(opt)
+
+ painter = QtWidgets.QStylePainter(self)
+ painter.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, opt)
+
+ rect = self.style().subControlRect(QtWidgets.QStyle.CC_ComboBox, opt, QtWidgets.QStyle.SC_ComboBoxEditField, self)
+
+ opt.currentText = painter.fontMetrics().elidedText(opt.currentText, QtCore.Qt.ElideRight, rect.width())
+ painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, opt)
diff --git a/nmreval/gui_qt/lib/gol.py b/nmreval/gui_qt/lib/gol.py
new file mode 100644
index 0000000..4212e71
--- /dev/null
+++ b/nmreval/gui_qt/lib/gol.py
@@ -0,0 +1,390 @@
+from __future__ import annotations
+
+import numbers
+import numpy as np
+import sys
+from itertools import accumulate
+
+from ..Qt import QtWidgets, QtGui, QtCore
+from .._py.gol import Ui_Form
+
+
+def circle(radius):
+ pxl = []
+ for x in range(int(np.ceil(radius/np.sqrt(2)))):
+ y = round(np.sqrt(radius**2-x**2))
+ pxl.extend([[x, y], [y, x], [x, -y], [y, -x],
+ [-x, -y], [-y, -x], [-x, y], [-y, x]])
+
+ return np.array(pxl)
+
+
+def square(a):
+ pxl = []
+ pxl.extend(list(zip(range(-a, a+1), [a]*(2*a+1))))
+ pxl.extend(list(zip(range(-a, a+1), [-a]*(2*a+1))))
+ pxl.extend(list(zip([a]*(2*a+1), range(-a, a+1))))
+ pxl.extend(list(zip([-a]*(2*a+1), range(-a, a+1))))
+
+ return np.array(pxl)
+
+
+def diamond(a):
+ pxl = []
+ for x in range(int(a+1)):
+ y = a-x
+ pxl.extend([[x, y], [-x, y], [x, -y], [-x, -y]])
+
+ print(np.array(pxl).shape)
+
+ return np.array(pxl)
+
+
+def plus(a):
+ pxl = np.zeros((4*int(a)+2, 2), dtype=int)
+ pxl[:2*int(a)+1, 0] = np.arange(-a, a+1)
+ pxl[2*int(a)+1:, 1] = np.arange(-a, a+1)
+
+ return pxl
+
+# birth, survival
+predefined_rules = {
+ 'Conway': ('23', '3'),
+ 'Life34': ('34', '34'),
+ 'Coagulation': ('235678', '378'),
+ 'Corrosion': ('12345', '45'),
+ 'Long life': ('5', '345'),
+ 'Maze': ('12345', '3'),
+ 'Coral': ('45678', '3'),
+ 'Pseudo life': ('238', '357'),
+ 'Flakes': ('012345678', '3'),
+ 'Gnarl': ('1', '1'),
+ 'Fabric': ('12', '1'),
+ 'Assimilation': ('4567', '345'),
+ 'Diamoeba': ('5678', '35678'),
+ 'High life': ('23', '36'),
+ 'More Maze': ('1235', '3'),
+ 'Replicator': ('1357', '1357'),
+ 'Seed': ('', '2'),
+ 'Serviette': ('', '234'),
+ 'More coagulation': ('235678', '3678'),
+ 'Domino': ('125', '36'),
+ 'Anneal': ('35678', '4678'),
+}
+
+
+class GameOfLife:
+ colors = [
+ [31, 119, 180],
+ [255, 127, 14],
+ [44, 160, 44],
+ [214, 39, 40],
+ [148, 103, 189],
+ [140, 86, 75]
+ ]
+
+ def __init__(self,
+ size: tuple = (400, 400),
+ pattern: np.ndarray = None,
+ fill: float | list[float] = 0.05,
+ num_pop: int = 1,
+ rules: str | tuple = ('23', '3')
+ ):
+ self.populations = num_pop if pattern is None else 1
+ self._size = size
+
+ self._world = np.zeros(shape=(*self._size, self.populations), dtype=np.uint8)
+ self._neighbors = np.zeros(shape=self._world.shape, dtype=np.uint8)
+ self._drawing = 255 * np.zeros(shape=(*self._size, 3), dtype=np.uint8)
+
+ self.fill = np.zeros(self.populations)
+ self._populate(fill, pattern)
+
+ print(rules)
+ if isinstance(rules, str):
+ try:
+ b_rule, s_rule = predefined_rules[rules]
+ except KeyError:
+ raise ValueError('Rule is not predefined')
+ else:
+ b_rule, s_rule = rules
+
+ self.survival_condition = np.array([int(c) for c in s_rule])
+ self.birth_condition = np.array([int(c) for c in b_rule])
+
+ # indexes for neighbors
+ self._neighbor_idx = [
+ [[slice(None), slice(1, None)], [slice(None), slice(0, -1)]], # N (:, 1:), S (:, _-1)
+ [[slice(1, None), slice(1, None)], [slice(0, -1), slice(0, -1)]], # NE (1:, 1:), SW (:-1, :-1)
+ [[slice(1, None), slice(None)], [slice(0, -1), slice(None)]], # E (1:, :), W (:-1, :)
+ [[slice(1, None), slice(0, -1)], [slice(0, -1), slice(1, None)]] # SE (1:, :-1), NW (:-1:, 1:)
+ ]
+
+ def _populate(self, fill, pattern):
+ if pattern is None:
+ if isinstance(fill, numbers.Number):
+ fill = [fill]*self.populations
+
+ prob = (np.random.default_rng().random(size=self._size))
+ lower_lim = 0
+ for i, upper_lim in enumerate(accumulate(fill)):
+ self._world[:, :, i] = (lower_lim <= prob) & (prob < upper_lim)
+ lower_lim = upper_lim
+
+ else:
+ pattern = np.asarray(pattern)
+
+ x_step = self._size[0]//(self.populations+1)
+ y_step = self._size[1]//(self.populations+1)
+
+ for i in range(self.populations):
+ self._world[-pattern[:, 0]+(i+1)*x_step, pattern[:, 1]+(i+1)*y_step, i] = 1
+
+ for i in range(self.populations):
+ self.fill[i] = self._world[:, :, i].sum() / (self._size[0]*self._size[1])
+
+ def tick(self):
+ n = self._neighbors
+ w = self._world
+
+ n[...] = 0
+ for idx_1, idx_2 in self._neighbor_idx:
+ n[tuple(idx_1)] += w[tuple(idx_2)]
+ n[tuple(idx_2)] += w[tuple(idx_1)]
+
+ birth = ((np.in1d(n, self.birth_condition).reshape(n.shape)) & (w.sum(axis=-1) == 0)[:, :, None])
+ survive = ((np.in1d(n, self.survival_condition).reshape(n.shape)) & (w == 1))
+
+ w[...] = 0
+ w[birth | survive] = 1
+
+ def draw(self, shade: int):
+ if shade == 0:
+ self._drawing[...] = 0
+ elif shade == 1:
+ self._drawing -= (self._drawing/4).astype(np.uint8)
+ self._drawing = self._drawing.clip(0, 127)
+
+ for i in range(self.populations):
+ self._drawing[(self._world[:, :, i] == 1)] = self.colors[i]
+ self.fill[i] = self._world[:, :, i].sum() / (self._size[0]*self._size[1])
+
+ return self._drawing
+
+
+class QGameOfLife(QtWidgets.QDialog, Ui_Form):
+ SPEEDS = [0.5, 1, 2, 5, 7.5, 10, 12.5, 15, 20, 25, 30, 40, 50]
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self._size = (100, 100)
+ self.game = None
+ self._step = 0
+ self._shading = 1
+ self._speed = 5
+
+ self.timer = QtCore.QTimer()
+ self.timer.setInterval(100)
+ self.timer.timeout.connect(self.tick)
+
+ self._init_ui()
+
+ def _init_ui(self):
+ self.item = None
+ self.scene = QtWidgets.QGraphicsScene()
+ self.item = self.scene.addPixmap(QtGui.QPixmap())
+ self.view.setScene(self.scene)
+
+ self.rule_cb.addItems(list(predefined_rules.keys()))
+
+ self.random_widgets = []
+ for _ in range(6):
+ w = QSliderText(15, parent=self)
+ w.slider.valueChanged.connect(self.set_max_population)
+ self.verticalLayout.addWidget(w)
+ self.random_widgets.append(w)
+
+ self.birth_line.setValidator(QtGui.QIntValidator())
+ self.survival_line.setValidator(QtGui.QIntValidator())
+
+ for w in self.random_widgets[1:] + [self.object_widget]:
+ w.hide()
+
+ self.setGeometry(QtWidgets.QStyle.alignedRect(QtCore.Qt.LeftToRight, QtCore.Qt.AlignCenter,
+ self.size(), QtWidgets.qApp.desktop().availableGeometry()))
+
+ self.view.resizeEvent = self.resizeEvent
+
+ @QtCore.pyqtSlot(int)
+ def on_object_combobox_currentIndexChanged(self, idx: int):
+ for w in self.random_widgets + [self.object_widget, self.rand_button_wdgt]:
+ w.hide()
+
+ if idx == 0:
+ self.random_widgets[0].show()
+ self.rand_button_wdgt.show()
+ else:
+ self.object_widget.show()
+
+ @QtCore.pyqtSlot()
+ def on_add_random_button_clicked(self):
+ if self.object_combobox.currentIndex() != 0:
+ return
+
+ for w in self.random_widgets[1:]:
+ if not w.isVisible():
+ w.show()
+ break
+
+ @QtCore.pyqtSlot()
+ def on_remove_random_button_clicked(self):
+ if self.object_combobox.currentIndex() != 0:
+ return
+
+ for w in reversed(self.random_widgets[1:]):
+ if w.isVisible():
+ w.hide()
+ break
+
+ @QtCore.pyqtSlot(str)
+ def on_rule_cb_currentIndexChanged(self, entry: str):
+ rule = predefined_rules[entry]
+ self.birth_line.setText(rule[1])
+ self.survival_line.setText(rule[0])
+
+ @QtCore.pyqtSlot(int)
+ def set_max_population(self, _: int):
+ over_population = -100
+ num_tribes = -1
+ for w in self.random_widgets:
+ if w.isVisible():
+ over_population += w.slider.value()
+ num_tribes += 1
+
+ if over_population > 0:
+ for w in self.random_widgets:
+ if w == self.sender() or w.isHidden():
+ continue
+ w.setValue(max(0, int(w.slider.value()-over_population/num_tribes)))
+
+ @QtCore.pyqtSlot()
+ def on_start_button_clicked(self):
+ self.pause_button.setChecked(False)
+ self._step = 0
+ self.current_step.setText(f'{self._step} steps')
+ self._size = (int(self.height_box.value()), int(self.width_box.value()))
+
+ pattern = None
+ num_pop = 0
+ fill = []
+ pattern_size = self.object_size.value()
+ if 2*pattern_size >= max(self._size):
+ pattern_size = int(np.floor(max(self._size[0]-1, self._size[1]-1) / 2))
+
+ idx = self.object_combobox.currentIndex()
+ if idx == 0:
+ for w in self.random_widgets:
+ if w.isVisible():
+ num_pop += 1
+ fill.append(w.slider.value()/100)
+ else:
+ pattern = [None, circle, square, diamond, plus][idx](pattern_size)
+
+ self.game = GameOfLife(self._size, pattern=pattern, fill=fill, num_pop=num_pop,
+ rules=(self.birth_line.text(), self.survival_line.text()))
+ self.draw()
+ self.view.fitInView(self.item, QtCore.Qt.KeepAspectRatio)
+
+ self.timer.start()
+
+ def tick(self):
+ self.game.tick()
+ self.draw()
+
+ self._step += 1
+ self.current_step.setText(f'{self._step} steps')
+ self.cover_label.setText('\n'.join([f'Color {i+1}: {f*100:.2f} %' for i, f in enumerate(self.game.fill)]))
+
+ @QtCore.pyqtSlot()
+ def on_faster_button_clicked(self):
+ self._speed = min(self._speed+1, len(QGameOfLife.SPEEDS)-1)
+ new_speed = QGameOfLife.SPEEDS[self._speed]
+ self.timer.setInterval(int(1000/new_speed))
+ self.velocity_label.setText(f'{new_speed:.1f} steps/s')
+
+ @QtCore.pyqtSlot()
+ def on_slower_button_clicked(self):
+ self._speed = max(self._speed-1, 0)
+ new_speed = QGameOfLife.SPEEDS[self._speed]
+ self.timer.setInterval(int(1000/new_speed))
+ self.velocity_label.setText(f'{new_speed:.1f} steps/s')
+
+ @QtCore.pyqtSlot()
+ def on_pause_button_clicked(self):
+ if self.pause_button.isChecked():
+ self.timer.stop()
+ else:
+ self.timer.start()
+
+ @QtCore.pyqtSlot(QtWidgets.QAbstractButton)
+ def on_buttonGroup_buttonClicked(self, button):
+ self._shading = [self.radioButton, self.vanish_shadow, self.full_shadow].index(button)
+
+ def draw(self):
+ bitmap = self.game.draw(shade=self._shading)
+ h, w, c = bitmap.shape
+ image = QtGui.QImage(bitmap.tobytes(), w, h, w*c, QtGui.QImage.Format_RGB888)
+ self.scene.removeItem(self.item)
+ pixmap = QtGui.QPixmap.fromImage(image)
+ self.item = self.scene.addPixmap(pixmap)
+
+ @QtCore.pyqtSlot(int)
+ def on_hide_button_stateChanged(self, state: int):
+ self.option_frame.setVisible(not state)
+ self.view.fitInView(self.scene.sceneRect(), QtCore.Qt.KeepAspectRatio)
+
+ def resizeEvent(self, evt):
+ super().resizeEvent(evt)
+ self.view.fitInView(self.item, QtCore.Qt.KeepAspectRatio)
+
+ def showEvent(self, evt):
+ super().showEvent(evt)
+ self.view.fitInView(self.scene.sceneRect(), QtCore.Qt.KeepAspectRatio)
+
+
+class QSliderText(QtWidgets.QWidget):
+ def __init__(self, value: int, parent=None):
+ super().__init__(parent=parent)
+ layout = QtWidgets.QHBoxLayout()
+ layout.setContentsMargins(0, 0, 0, 0)
+ layout.setSpacing(3)
+
+ self.slider = QtWidgets.QSlider(self)
+ self.slider.setOrientation(QtCore.Qt.Horizontal)
+ self.slider.setMaximum(100)
+ self.slider.setTickPosition(self.slider.TicksBothSides)
+ self.slider.setTickInterval(5)
+
+ self.value = QtWidgets.QLabel(self)
+
+ self.slider.valueChanged.connect(lambda x: self.value.setText(f'{x} %'))
+
+ layout.addWidget(self.slider)
+ layout.addWidget(self.value)
+
+ self.setLayout(layout)
+
+ self.setValue(value)
+
+ def setValue(self, value: int):
+ self.slider.setValue(value)
+ self.value.setText(f'{value} %')
+
+
+if __name__ == "__main__":
+ application = QtWidgets.QApplication(sys.argv)
+ qGameOfLife = QGameOfLife()
+ qGameOfLife.show()
+ sys.exit(application.exec())
diff --git a/nmreval/gui_qt/lib/namespace.py b/nmreval/gui_qt/lib/namespace.py
new file mode 100644
index 0000000..9229176
--- /dev/null
+++ b/nmreval/gui_qt/lib/namespace.py
@@ -0,0 +1,211 @@
+import inspect
+import re
+from collections import namedtuple
+
+import numpy as np
+
+from ... import models
+from ...configs import config_paths
+from ...lib.importer import find_models, import_
+from ...utils import constants as constants
+from ...utils.text import convert
+from ..Qt import QtWidgets, QtCore
+from .._py.namespace_widget import Ui_Form
+
+
+class Namespace:
+ def __init__(self, fitfuncs=False, const=False, basic=False):
+ self.namespace = {}
+ self.groupings = {}
+ self.top_levels = {}
+
+ if basic:
+ self.add_namespace({'x': (None, 'x values'), '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)'),
+ 'tan': (np.tan, 'Tangens', 'tan(PIKA)'), 'ln': (np.log, 'Natural Logarithm', 'ln(PIKA)'),
+ 'log': (np.log10, 'Logarithm (base 10)', 'log(PIKA)'),
+ 'exp': (np.exp, 'Exponential', 'exp(PIKA)'), 'sqrt': (np.sqrt, 'Root', 'sqrt(PIKA)'),
+ '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]',
+ 'lin_range(start, stop, N)')},
+ parents=('Basic', 'Functions'))
+
+ self.add_namespace({'max': (np.max, 'Maximum value', 'max(PIKA)'),
+ 'min': (np.min, 'Minimum value', 'min(PIKA)'),
+ 'argmax': (np.argmax, 'Index of maximum value', 'argmax(PIKA)'),
+ 'argmin': (np.argmax, 'Index of minimum value', 'argmin(PIKA)')},
+ parents=('Basic', 'Values'))
+
+ if const:
+ self.add_namespace({'e': (constants.e, 'e / As'), 'eps0': (constants.epsilon0, 'epsilon0 / As/Vm'),
+ 'Eu': (constants.Eu,), 'h': (constants.h, 'h / eVs'),
+ 'hbar': (constants.hbar, 'hbar / eVs'), 'kB': (constants.kB, 'kB / eV/K'),
+ 'mu0': (constants.mu0, 'mu0 / Vs/Am'), 'NA': (constants.NA, 'NA / 1/mol'),
+ 'pi': (constants.pi,), 'R': (constants.R, 'R / eV')},
+ parents=('Constants', 'Maybe useful'))
+
+ self.add_namespace({f'gamma["{k}"]': (v, k, f'gamma["{k}"]') for k, v in constants.gamma.items()},
+ parents=('Constants', 'Magnetogyric ratios (in 1/(sT))'))
+
+ if fitfuncs:
+ self.make_dict_from_fitmodule(models)
+ try:
+ usermodels = import_(config_paths() / 'usermodels.py')
+ self.make_dict_from_fitmodule(usermodels, parent='User-defined')
+ except FileNotFoundError:
+ pass
+
+ def __str__(self):
+ ret = '\n'.join([f'{k}: {v}' for k, v in self.groupings.items()])
+ return ret
+
+ def make_dict_from_fitmodule(self, module, parent='Fit function'):
+ fitfunc_dict = {}
+ for funcs in find_models(module):
+ name = funcs.name
+ if funcs.type not in fitfunc_dict:
+ fitfunc_dict[funcs.type] = {}
+
+ func_list = fitfunc_dict[funcs.type]
+
+ func_args = 'x, ' + ', '.join(convert(p, old='latex', new='str', brackets=False) for p in funcs.params)
+
+ # keywords arguments need names in function
+ sig = inspect.signature(funcs.func)
+ for p in sig.parameters.values():
+ if p.default != p.empty:
+ func_args += ', ' + str(p)
+
+ func_list[f"{funcs.__name__}"] = (funcs.func, name, f"{funcs.__name__}({func_args})")
+
+ for k, v in fitfunc_dict.items():
+ self.add_namespace(v, parents=(parent, k))
+
+ def add_namespace(self, namespace, parents):
+ if parents[0] in self.top_levels:
+ if parents[1] not in self.top_levels[parents[0]]:
+ self.top_levels[parents[0]].append(parents[1])
+ else:
+ self.top_levels[parents[0]] = [parents[1]]
+ if (parents[0], parents[1]) not in self.groupings:
+ self.groupings[(parents[0], parents[1])] = list(namespace.keys())
+ else:
+ self.groupings[(parents[0], parents[1])].extend(list(namespace.keys()))
+
+ self.namespace.update(namespace)
+
+ def flatten(self):
+ ret_dic = {}
+ graph = namedtuple('graphs', ['s'])
+ sets = namedtuple('sets', ['x', 'y', 'y_err', 'value', 'fit'], defaults=(None,))
+
+ gs = re.compile(r'g\[(\d+)].s\[(\d+)].(x|y(?:_err)*|value|fit)')
+
+ for k, v in self.namespace.items():
+ m = gs.match(k)
+ if m:
+ if 'g' not in ret_dic:
+ ret_dic['g'] = {}
+
+ g_num, s_num, pos = m.groups()
+ if int(g_num) not in ret_dic['g']:
+ ret_dic['g'][int(g_num)] = graph({})
+
+ gg = ret_dic['g'][int(g_num)]
+ if int(s_num) not in gg.s:
+ gg.s[int(s_num)] = sets('', '', '', '')
+
+ ss = gg.s[int(s_num)]
+ if pos == 'fit':
+ if ss.fit is None:
+ gg.s[int(s_num)] = ss._replace(**{pos: {}})
+ ss = gg.s[int(s_num)]
+ ss.fit[k[m.end()+2:-2]] = v[0]
+
+ else:
+ ret_dic['g'][int(g_num)].s[int(s_num)] = ss._replace(**{pos: v[0]})
+
+ else:
+ ret_dic[k] = v[0]
+
+ return ret_dic
+
+
+class QNamespaceWidget(QtWidgets.QWidget, Ui_Form):
+ selected = QtCore.pyqtSignal(str)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setupUi(self)
+ self.namespace = None
+
+ def make_namespace(self):
+ self.set_namespace(Namespace(fitfuncs=True, const=True, basic=True))
+
+ @QtCore.pyqtSlot(int, name='on_groups_comboBox_currentIndexChanged')
+ def show_subgroup(self, idx: int):
+ name = self.groups_comboBox.itemText(idx)
+
+ self.subgroups_comboBox.blockSignals(True)
+ self.subgroups_comboBox.clear()
+ for item in self.namespace.top_levels[name]:
+ self.subgroups_comboBox.addItem(item)
+ self.subgroups_comboBox.blockSignals(False)
+
+ self.show_namespace(0)
+
+ @QtCore.pyqtSlot(int, name='on_subgroups_comboBox_currentIndexChanged')
+ def show_namespace(self, idx: int):
+ subspace = self.namespace.groupings[(self.groups_comboBox.currentText(), self.subgroups_comboBox.itemText(idx))]
+
+ self.namespace_table.clear()
+ self.namespace_table.setRowCount(0)
+
+ for entry in subspace:
+ key_item = QtWidgets.QTableWidgetItem(entry)
+ key_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+
+ vals = self.namespace.namespace[entry]
+
+ if len(vals) < 3:
+ alias = entry
+ else:
+ alias = vals[2]
+
+ if len(vals) < 2:
+ display = entry
+ else:
+ display = vals[1]
+
+ value_item = QtWidgets.QTableWidgetItem(display)
+ value_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+
+ key_item.setData(QtCore.Qt.UserRole, alias)
+ value_item.setData(QtCore.Qt.UserRole, alias)
+
+ row = self.namespace_table.rowCount()
+ self.namespace_table.setRowCount(row+1)
+ self.namespace_table.setItem(row, 0, key_item)
+ self.namespace_table.setItem(row, 1, value_item)
+
+ self.namespace_table.resizeColumnsToContents()
+
+ def set_namespace(self, namespace):
+ self.namespace = namespace
+
+ self.groups_comboBox.blockSignals(True)
+ self.groups_comboBox.clear()
+ for k in self.namespace.top_levels.keys():
+ self.groups_comboBox.addItem(k)
+ self.groups_comboBox.blockSignals(False)
+
+ self.show_subgroup(0)
+
+ @QtCore.pyqtSlot(QtWidgets.QTableWidgetItem, name='on_namespace_table_itemDoubleClicked')
+ def item_selected(self, item: QtWidgets.QTableWidgetItem):
+ self.selected.emit(item.data(QtCore.Qt.UserRole))
diff --git a/nmreval/gui_qt/lib/pg_objects.py b/nmreval/gui_qt/lib/pg_objects.py
new file mode 100644
index 0000000..7fa9851
--- /dev/null
+++ b/nmreval/gui_qt/lib/pg_objects.py
@@ -0,0 +1,435 @@
+import numpy as np
+from pyqtgraph import (
+ InfiniteLine,
+ ErrorBarItem,
+ LinearRegionItem, mkBrush,
+ mkColor, mkPen,
+ PlotDataItem
+)
+
+from ...lib.colors import BaseColor, Colors
+from ...lib.lines import LineStyle
+from ...lib.symbols import SymbolStyle
+
+from ..Qt import QtCore, QtGui
+
+"""
+Subclasses of pyqtgraph items, mostly to take care of log-scaling.
+pyqtgraph looks for function "setLogMode" for logarithmic axes, so needs to be implemented.
+"""
+
+
+class LogInfiniteLine(InfiniteLine):
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+ self.logmode = [False, False]
+
+ def setLogMode(self, xmode, ymode):
+ """
+ Does only work for vertical and horizontal lines
+ """
+ if self.logmode == [xmode, ymode]:
+ return
+
+ new_p = list(self.p[:])
+ if (self.angle == 90) and (self.logmode[0] != xmode):
+ if xmode:
+ new_p[0] = np.log10(new_p[0]+np.finfo(float).eps)
+ else:
+ new_p[0] = 10**new_p[0]
+
+ if (self.angle == 0) and (self.logmode[1] != ymode):
+ if ymode:
+ new_p[1] = np.log10(new_p[1]+np.finfo(float).eps)
+ else:
+ new_p[1] = 10**new_p[1]
+
+ self.logmode = [xmode, ymode]
+
+ if np.all(np.isfinite(new_p)):
+ self.setPos(new_p)
+ else:
+ self.setPos(self.p)
+ self.sigPositionChanged.emit(self)
+
+ def setValue(self, v):
+ if isinstance(v, QtCore.QPointF):
+ v = [v.x(), v.y()]
+
+ with np.errstate(divide='ignore'):
+ if isinstance(v, (list, tuple)):
+ for i in [0, 1]:
+ if self.logmode[i]:
+ v[i] = np.log10(v[i]+np.finfo(float).eps)
+ else:
+ if self.angle == 90:
+ if self.logmode[0]:
+ v = [np.log10(v+np.finfo(float).eps), 0]
+ else:
+ v = [v, 0]
+ elif self.angle == 0:
+ if self.logmode[1]:
+ v = [0, np.log10(v+np.finfo(float).eps)]
+ else:
+ v = [0, v]
+ else:
+ raise ValueError('LogInfiniteLine: Diagonal lines need two values')
+
+ self.setPos(v)
+
+ def value(self):
+ p = self.getPos()
+ if self.angle == 0:
+ return 10**p[1] if self.logmode[1] else p[1]
+ elif self.angle == 90:
+ return 10**p[0] if self.logmode[0] else p[0]
+ else:
+ if self.logmode[0]:
+ p[0] = 10**p[0]
+ if self.logmode[1]:
+ p[1] = 10**p[1]
+ return p
+
+
+class ErrorBars(ErrorBarItem):
+ def __init__(self, **opts):
+ self.log = [False, False]
+
+ opts['xData'] = opts.get('x', None)
+ opts['yData'] = opts.get('y', None)
+ opts['topData'] = opts.get('top', None)
+ opts['bottomData'] = opts.get('bottom', None)
+
+ super().__init__(**opts)
+
+ def setLogMode(self, x_mode, y_mode):
+ if self.log == [x_mode, y_mode]:
+ return
+
+ self._make_log_scale(x_mode, y_mode)
+
+ self.log[0] = x_mode
+ self.log[1] = y_mode
+
+ super().setData()
+
+ def setData(self, **opts):
+ self.opts.update(opts)
+
+ self.opts['xData'] = opts.get('x', self.opts['xData'])
+ self.opts['yData'] = opts.get('y', self.opts['yData'])
+ self.opts['topData'] = opts.get('top', self.opts['topData'])
+ self.opts['bottomData'] = opts.get('bottom', self.opts['bottomData'])
+
+ if any(self.log):
+ self._make_log_scale(*self.log)
+
+ super().setData()
+
+ def _make_log_scale(self, x_mode, y_mode):
+ _x = self.opts['xData']
+ _xmask = np.logical_not(np.isnan(_x))
+
+ if x_mode:
+ with np.errstate(all='ignore'):
+ _x = np.log10(_x)
+ _xmask = np.logical_not(np.isnan(_x))
+
+ _y = self.opts['yData']
+ _ymask = np.ones(_y.size, dtype=bool)
+ _top = self.opts['topData']
+ _bottom = self.opts['bottomData']
+
+ if y_mode:
+ with np.errstate(all='ignore'):
+ logtop = np.log10(self.opts['topData']+_y)
+ logbottom = np.log10(_y-self.opts['bottomData'])
+
+ _y = np.log10(_y)
+ _ymask = np.logical_not(np.isnan(_y))
+
+ logbottom[logbottom == -np.inf] = _y[logbottom == -np.inf]
+ _bottom = np.nan_to_num(np.maximum(_y-logbottom, 0))
+ logtop[logtop == -np.inf] = _y[logtop == -np.inf]
+ _top = np.nan_to_num(np.maximum(logtop-_y, 0))
+
+ _mask = np.logical_and(_xmask, _ymask)
+
+ self.opts['x'] = _x[_mask]
+ self.opts['y'] = _y[_mask]
+ self.opts['top'] = _top[_mask]
+ self.opts['bottom'] = _bottom[_mask]
+
+
+class PlotItem(PlotDataItem):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.opts['linecolor'] = Colors.Black
+ self.opts['symbolcolor'] = Colors.Black
+
+ if self.opts['pen'] is not None:
+ pen = self.opts['pen']
+ if isinstance(pen, tuple):
+ self.opts['linecolor'] = pen
+ else:
+ c = pen.color()
+ self.opts['linecolor'] = c.red(), c.green(), c.blue()
+
+ if self.symbol != SymbolStyle.No:
+ c = self.opts['symbolBrush'].color()
+ self.opts['symbolcolor'] = c.red(), c.green(), c.blue()
+
+ def __getitem__(self, item):
+ return self.opts.get(item, None)
+
+ @property
+ def symbol(self):
+ return SymbolStyle.from_str(self.opts['symbol'])
+
+ @property
+ def symbolcolor(self):
+ sc = self.opts['symbolcolor']
+ if isinstance(sc, tuple):
+ return Colors(sc)
+ elif isinstance(sc, str):
+ return Colors.from_str(sc)
+ else:
+ return sc
+
+ @property
+ def symbolsize(self):
+ return self.opts['symbolSize']
+
+ @property
+ def linestyle(self) -> LineStyle:
+ pen = self.opts['pen']
+ if pen is None:
+ return LineStyle.No
+ else:
+ return LineStyle(pen.style())
+
+ @property
+ def linewidth(self) -> float:
+ pen = self.opts['pen']
+ if pen is None:
+ return 1.
+ else:
+ return pen.widthF()
+
+ @property
+ def linecolor(self) -> Colors:
+ lc = self.opts['linecolor']
+ if isinstance(lc, tuple):
+ return Colors(lc)
+ elif isinstance(lc, str):
+ return Colors.from_str(lc)
+ else:
+ return lc
+
+ def updateItems(self):
+ """
+ We override this function so that curves with nan/inf values can be displayed.
+ Newer versions close this bug differently (https://github.com/pyqtgraph/pyqtgraph/pull/1058)
+ but this works somewhat.
+ """
+
+ curveArgs = {}
+ for k, v in [('pen', 'pen'), ('shadowPen', 'shadowPen'), ('fillLevel', 'fillLevel'),
+ ('fillOutline', 'fillOutline'), ('fillBrush', 'brush'), ('antialias', 'antialias'),
+ ('connect', 'connect'), ('stepMode', 'stepMode')]:
+ curveArgs[v] = self.opts[k]
+
+ scatterArgs = {}
+ for k, v in [('symbolPen', 'pen'), ('symbolBrush', 'brush'), ('symbol', 'symbol'), ('symbolSize', 'size'),
+ ('data', 'data'), ('pxMode', 'pxMode'), ('antialias', 'antialias')]:
+ if k in self.opts:
+ scatterArgs[v] = self.opts[k]
+
+ x, y = self.getData()
+ if x is None:
+ x = []
+ if y is None:
+ y = []
+ # scatterArgs['mask'] = self.dataMask
+
+ if curveArgs['pen'] is not None or (curveArgs['brush'] is not None and curveArgs['fillLevel'] is not None):
+ is_finite = np.isfinite(x) & np.isfinite(y)
+ all_finite = np.all(is_finite)
+ if not all_finite:
+ # remove all bad values
+ x = x[is_finite]
+ y = y[is_finite]
+ self.curve.setData(x=x, y=y, **curveArgs)
+ self.curve.show()
+ else:
+ self.curve.hide()
+
+ if scatterArgs['symbol'] is not None:
+ if self.opts.get('stepMode', False) is True:
+ x = 0.5 * (x[:-1] + x[1:])
+ self.scatter.setData(x=x, y=y, **scatterArgs)
+ self.scatter.show()
+ else:
+ self.scatter.hide()
+
+ def set_symbol(self, symbol=None, size=None, color=None):
+ if symbol is not None:
+ if isinstance(symbol, int):
+ self.setSymbol(SymbolStyle(symbol).to_str())
+ elif isinstance(symbol, SymbolStyle):
+ self.setSymbol(symbol.to_str())
+ else:
+ self.setSymbol(symbol)
+
+ if color is not None:
+ self.set_color(color, symbol=True)
+
+ if size is not None:
+ self.setSymbolSize(size)
+
+ def set_color(self, color, symbol=False, line=False):
+ if isinstance(color, BaseColor):
+ color = color.rgb()
+ elif isinstance(color, QtGui.QColor):
+ color = color.getRgb()[:3]
+
+ if symbol:
+ self.setSymbolBrush(mkBrush(color))
+ self.setSymbolPen(mkPen(color=color))
+ self.opts['symbolcolor'] = color
+
+ if line:
+ pen = self.opts['pen']
+ self.opts['linecolor'] = color
+ if pen is not None:
+ pen.setColor(mkColor(color))
+ self.opts['pen'] = pen
+ self.updateItems()
+
+ def set_line(self, style=None, width=None, color=None):
+ pen = self.opts['pen']
+ if pen is None:
+ pen = mkPen(style=QtCore.Qt.NoPen)
+
+ if width is not None:
+ pen.setWidthF(width)
+
+ if style is not None:
+ if isinstance(style, LineStyle):
+ style = style.value
+
+ pen.setStyle(style)
+
+ self.opts['pen'] = pen
+ self.updateItems()
+
+ if color is not None:
+ self.set_color(color, symbol=False, line=True)
+
+ def get_data_opts(self) -> dict:
+ x, y = self.xData, self.yData
+ if (x is None) or (len(x) == 0):
+ return {}
+
+ opts = self.opts
+ item_dic = {
+ 'x': x, 'y': y,
+ 'name': opts['name'],
+ 'symbolsize': opts['symbolSize']
+ }
+
+ if opts['symbol'] is None:
+ item_dic['symbol'] = SymbolStyle.No
+ item_dic['symbolcolor'] = Colors.Black
+ else:
+ item_dic['symbol'] = SymbolStyle.from_str(opts['symbol'])
+ item_dic['symbolcolor'] = opts['symbolcolor']
+
+ pen = opts['pen']
+
+ if pen is not None:
+ item_dic['linestyle'] = LineStyle(pen.style())
+ item_dic['linecolor'] = opts['linecolor']
+ item_dic['linewidth'] = pen.widthF()
+ else:
+ item_dic['linestyle'] = LineStyle.No
+ item_dic['linecolor'] = item_dic['symbolcolor']
+ item_dic['linewidth'] = 0.0
+
+ return item_dic
+
+
+class RegionItem(LinearRegionItem):
+ def __init__(self, *args, **kwargs):
+ self.mode = kwargs.pop('mode', 'half')
+
+ super().__init__(*args, **kwargs)
+
+ self.logmode = False
+ self.first = True
+
+ def setLogMode(self, xmode, _):
+ if self.logmode == xmode:
+ return
+
+ if xmode:
+ new_region = [np.log10(self.lines[0].value()), np.log10(self.lines[1].value())]
+
+ if np.isnan(new_region[1]):
+ new_region[1] = self.lines[1].value()
+
+ if np.isnan(new_region[0]):
+ new_region[0] = new_region[1]/10.
+
+ else:
+ new_region = [10**self.lines[0].value(), 10**self.lines[1].value()]
+
+ self.logmode = xmode
+ self.setRegion(new_region)
+
+ def dataBounds(self, axis, frac=1.0, orthoRange=None):
+ if axis == self._orientation_axis[self.orientation]:
+ r = self.getRegion()
+ if self.logmode:
+ r = np.log10(r[0]), np.log10(r[1])
+ return r
+ else:
+ return None
+
+ def getRegion(self):
+ region = super().getRegion()
+ if self.logmode:
+ return 10**region[0], 10**region[1]
+ else:
+ return region
+
+ def boundingRect(self):
+ # overwrite to draw correct rect in logmode
+
+ br = self.viewRect() # bounds of containing ViewBox mapped to local coords.
+
+ rng = self.getRegion()
+ if self.logmode:
+ rng = np.log10(rng[0]), np.log10(rng[1])
+
+ if self.orientation in ('vertical', LinearRegionItem.Vertical):
+ br.setLeft(rng[0])
+ br.setRight(rng[1])
+ length = br.height()
+ br.setBottom(br.top() + length * self.span[1])
+ br.setTop(br.top() + length * self.span[0])
+ else:
+ br.setTop(rng[0])
+ br.setBottom(rng[1])
+ length = br.width()
+ br.setRight(br.left() + length * self.span[1])
+ br.setLeft(br.left() + length * self.span[0])
+
+ br = br.normalized()
+
+ if self._bounds != br:
+ self._bounds = br
+ self.prepareGeometryChange()
+
+ return br
diff --git a/nmreval/gui_qt/lib/randpok.py b/nmreval/gui_qt/lib/randpok.py
new file mode 100644
index 0000000..55de7de
--- /dev/null
+++ b/nmreval/gui_qt/lib/randpok.py
@@ -0,0 +1,119 @@
+import sys
+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 = '{} '.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}')
+
+
+if __name__ == '__main__':
+ app = QtWidgets.QApplication(sys.argv)
+ w = QPokemon(number=807)
+ w.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/lib/stuff.py b/nmreval/gui_qt/lib/stuff.py
new file mode 100644
index 0000000..59a1207
--- /dev/null
+++ b/nmreval/gui_qt/lib/stuff.py
@@ -0,0 +1,502 @@
+import random
+import sys
+import numpy as np
+
+from nmreval.gui_qt.Qt import QtWidgets, QtCore, QtGui
+
+
+__all__ = ['Game']
+
+
+class Game(QtWidgets.QDialog):
+ def __init__(self, mode, parent=None):
+ super().__init__(parent=parent)
+
+ layout = QtWidgets.QVBoxLayout()
+ layout.setContentsMargins(3, 3, 3, 3)
+
+ self.label = QtWidgets.QLabel(self)
+ layout.addWidget(self.label)
+
+ self.startbutton = QtWidgets.QPushButton('Start', self)
+ self.startbutton.clicked.connect(self.start)
+ layout.addWidget(self.startbutton)
+
+ if mode == 'tetris':
+ self._setup_tetris()
+ else:
+ self._setup_snake()
+
+ layout.addWidget(self.board)
+ self.setStyleSheet("""
+ Board {
+ border: 5px solid black;
+ }
+ QPushButton {
+ font-weight: bold;
+ }
+ """)
+
+ self.setLayout(layout)
+
+ self.board.new_status.connect(self.new_message)
+
+ def _setup_tetris(self):
+ self.board = TetrisBoard(self)
+ self.setGeometry(200, 100, 276, 546+self.startbutton.height()+self.label.height())
+ self.setWindowTitle('Totally not Tetris')
+
+ def _setup_snake(self):
+ self.board = SnakeBoard(self)
+ self.setGeometry(200, 100, 406, 406+self.startbutton.height()+self.label.height())
+ self.setWindowTitle('Snakey')
+
+ def start(self):
+
+ self.board.start()
+
+ @QtCore.pyqtSlot(str)
+ def new_message(self, msg: str):
+ self.label.setText(msg)
+
+
+class Board(QtWidgets.QFrame):
+ new_status = QtCore.pyqtSignal(str)
+
+ SPEED = 1000
+ WIDTH = 10
+ HEIGHT = 10
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.timer = QtCore.QTimer(self)
+ self.timer.timeout.connect(self.next_move)
+
+ self.score = 0
+ self._speed = self.SPEED
+ self._ispaused = False
+ self._isdead = True
+
+ self.setFrameStyle(QtWidgets.QFrame.Box | QtWidgets.QFrame.Raised)
+ self.setLineWidth(3)
+
+ self.setFocusPolicy(QtCore.Qt.StrongFocus)
+ self.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+
+ def _init_game(self):
+ raise NotImplementedError
+
+ @property
+ def cellwidth(self):
+ return int(self.contentsRect().width() // self.WIDTH)
+
+ # square height
+ @property
+ def cellheight(self):
+ return int(self.contentsRect().height() // self.HEIGHT)
+
+ def start(self):
+ if self._isdead:
+ self._init_game()
+ self.new_status.emit(f'Score: {self.score}')
+ self.timer.start(self._speed)
+ self._isdead = False
+ self.setFocus()
+
+ def stop(self, msg):
+ self.new_status.emit(f'Score {self.score} // ' + msg)
+ self._isdead = True
+ self.timer.stop()
+
+ def pause(self):
+ if self._ispaused and not self._isdead:
+ self.new_status.emit(f'Score {self.score}')
+ self.timer.start(self._speed)
+ else:
+ self.new_status.emit(f'Score {self.score} // Paused')
+ self.timer.stop()
+
+ self._ispaused = not self._ispaused
+
+ def next_move(self):
+ raise NotImplementedError
+
+ def draw_square(self, painter, x, y, color):
+ color = QtGui.QColor(color)
+ painter.fillRect(x+1, y+1, self.cellwidth-2, self.cellheight-2, color)
+
+ def draw_circle(self, painter, x, y, color):
+ painter.save()
+
+ color = QtGui.QColor(color)
+ painter.setPen(QtGui.QPen(color))
+ painter.setBrush(QtGui.QBrush(color))
+ painter.drawEllipse(x+1, y+1, self.cellwidth, self.cellheight)
+
+ painter.restore()
+
+ def keyPressEvent(self, evt):
+ if evt.key() == QtCore.Qt.Key_P:
+ self.pause()
+ else:
+ super().keyPressEvent(evt)
+
+
+class SnakeBoard(Board):
+ SPEED = 100
+ WIDTH = 30
+ HEIGHT = 30
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.snake = [[int(SnakeBoard.WIDTH//2), int(SnakeBoard.HEIGHT//2)],
+ [int(SnakeBoard.WIDTH//2)+1, int(SnakeBoard.HEIGHT//2)]]
+ self.current_x_head, self.current_y_head = self.snake[0]
+ self.direction = 'l'
+
+ self.food = None
+ self.grow_snake = False
+
+ def _init_game(self):
+ self.snake = [[int(SnakeBoard.WIDTH//2), int(SnakeBoard.HEIGHT//2)],
+ [int(SnakeBoard.WIDTH//2)+1, int(SnakeBoard.HEIGHT//2)]]
+ self.current_x_head, self.current_y_head = self.snake[0]
+ self.direction = 'l'
+
+ self.food = None
+ self.grow_snake = False
+ self.new_food()
+
+ def next_move(self):
+ self.snake_move()
+ self.got_food()
+ self.check_death()
+ self.update()
+
+ def snake_move(self):
+ if self.direction == 'l':
+ self.current_x_head -= 1
+ elif self.direction == 'r':
+ self.current_x_head += 1
+
+ # y increases top to bottom
+ elif self.direction == 'u':
+ self.current_y_head -= 1
+ elif self.direction == 'd':
+ self.current_y_head += 1
+
+ head = [self.current_x_head, self.current_y_head]
+ self.snake.insert(0, head)
+
+ if not self.grow_snake:
+ self.snake.pop()
+ else:
+ self.new_status.emit(f'Score: {self.score}')
+ self.grow_snake = False
+
+ def got_food(self):
+ head = self.snake[0]
+ if self.food == head:
+ self.new_food()
+ self.grow_snake = True
+ self.score += 1
+
+ def new_food(self):
+ x = random.randint(3, SnakeBoard.WIDTH-3)
+ y = random.randint(3, SnakeBoard.HEIGHT-3)
+
+ while [x, y] == self.snake[0]:
+ x = random.randint(3, SnakeBoard.WIDTH-3)
+ y = random.randint(3, SnakeBoard.HEIGHT-3)
+
+ self.food = [x, y]
+
+ def check_death(self):
+ rip_message = ''
+ is_dead = False
+ if (self.current_x_head < 1) or (self.current_x_head > SnakeBoard.WIDTH-2) or \
+ (self.current_y_head < 1) or (self.current_y_head > SnakeBoard.HEIGHT-2):
+ rip_message = 'Snake found wall :('
+ is_dead = True
+
+ head = self.snake[0]
+ for snake_i in self.snake[1:]:
+ if snake_i == head:
+ rip_message = 'Snake bit itself :('
+ is_dead = True
+ break
+
+ if is_dead:
+ self.stop(rip_message)
+
+ def keyPressEvent(self, event):
+ key = event.key()
+ if key in (QtCore.Qt.Key_Left, QtCore.Qt.Key_A):
+ if self.direction != 'r':
+ self.direction = 'l'
+
+ elif key in (QtCore.Qt.Key_Right, QtCore.Qt.Key_D):
+ if self.direction != 'l':
+ self.direction = 'r'
+
+ elif key in (QtCore.Qt.Key_Down, QtCore.Qt.Key_S):
+ if self.direction != 'u':
+ self.direction = 'd'
+
+ elif key in (QtCore.Qt.Key_Up, QtCore.Qt.Key_W):
+ if self.direction != 'd':
+ self.direction = 'u'
+
+ else:
+ return super().keyPressEvent(event)
+
+ def paintEvent(self, event):
+ painter = QtGui.QPainter(self)
+
+ rect = self.contentsRect()
+ boardtop = rect.bottom() - SnakeBoard.HEIGHT * self.cellheight
+ boardleft = rect.left()
+
+ for pos in self.snake:
+ self.draw_circle(painter,
+ int(boardleft+pos[0]*self.cellwidth),
+ int(boardtop+pos[1]*self.cellheight),
+ 'blue')
+ if self.food is not None:
+ self.draw_square(painter,
+ int(boardleft+self.food[0]*self.cellwidth),
+ int(boardtop+self.food[1]*self.cellheight),
+ 'orange')
+
+
+class TetrisBoard(Board):
+ WIDTH = 10
+ HEIGHT = 20
+ SPEED = 300
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self._shapes = {
+ 1: ZShape,
+ 2: SShape,
+ 3: Line,
+ 4: TShape,
+ 5: Square,
+ 6: MirrorL
+ }
+
+ self._init_game()
+
+ def _init_game(self):
+ self.curr_x = 0
+ self.curr_y = 0
+ self.curr_piece = None
+ self.board = np.zeros((TetrisBoard.WIDTH, TetrisBoard.HEIGHT + 2), dtype=int)
+
+ def next_move(self):
+ if self.curr_piece is None:
+ self.make_new_piece()
+ else:
+ self.move_down()
+
+ def try_move(self, piece, x, y):
+ if piece is None:
+ return False
+
+ if x+piece.x.min() < 0 or x+piece.x.max() >= TetrisBoard.WIDTH:
+ return False
+
+ if y-piece.y.max() < 0 or y-piece.y.min() >= TetrisBoard.HEIGHT+2:
+ return False
+
+ if np.any(self.board[piece.x+x, y-piece.y]) != 0:
+ return False
+
+ if piece != self.curr_piece:
+ self.curr_piece = piece
+
+ self.curr_x = x
+ self.curr_y = y
+
+ self.update()
+
+ return True
+
+ def make_new_piece(self):
+ new_piece = self._shapes[random.randint(1, len(self._shapes))]()
+
+ startx = TetrisBoard.WIDTH//2
+ starty = TetrisBoard.HEIGHT+2 - 1 + new_piece.y.min()
+ if not self.try_move(new_piece, startx, starty):
+ self.stop('Game over :(')
+
+ def move_down(self):
+ if not self.try_move(self.curr_piece, self.curr_x, self.curr_y-1):
+ self.final_destination_reached()
+
+ def drop_to_bottom(self):
+ new_y = self.curr_y
+
+ while new_y > 0:
+ if not self.try_move(self.curr_piece, self.curr_x, new_y-1):
+ break
+ new_y -= 1
+
+ self.final_destination_reached()
+
+ def final_destination_reached(self):
+ x = self.curr_x+self.curr_piece.x
+ y = self.curr_y-self.curr_piece.y
+ self.board[x, y] = next(k for k, v in self._shapes.items() if isinstance(self.curr_piece, v))
+
+ self.remove_lines()
+
+ self.curr_piece = None
+ self.make_new_piece()
+
+ def remove_lines(self):
+ full_rows = np.where(np.all(self.board, axis=0))[0]
+ num_rows = len(full_rows)
+
+ if num_rows:
+ temp = np.zeros_like(self.board)
+ temp[:, :temp.shape[1]-num_rows] = np.delete(self.board, full_rows, axis=1)
+ self.board = temp
+
+ self.score += num_rows
+ self.new_status.emit(f'Lines: {self.score}')
+
+ if self.score % 10 == 0:
+ self._speed += 0.9
+ self.timer.setInterval(int(self._speed))
+
+ self.update()
+
+ def keyPressEvent(self, event):
+ key = event.key()
+
+ if self.curr_piece is None:
+ return super().keyPressEvent(event)
+
+ if key == QtCore.Qt.Key_Left:
+ self.try_move(self.curr_piece, self.curr_x-1, self.curr_y)
+
+ elif key == QtCore.Qt.Key_Right:
+ self.try_move(self.curr_piece, self.curr_x+1, self.curr_y)
+
+ elif key == QtCore.Qt.Key_Down:
+ if not self.try_move(self.curr_piece.rotate(), self.curr_x, self.curr_y):
+ self.curr_piece.rotate(clockwise=False)
+
+ elif key == QtCore.Qt.Key_Up:
+ if not self.try_move(self.curr_piece.rotate(clockwise=False), self.curr_x, self.curr_y):
+ self.curr_piece.rotate()
+
+ elif key == QtCore.Qt.Key_Space:
+ self.drop_to_bottom()
+
+ else:
+ super().keyPressEvent(event)
+
+ def paintEvent(self, event):
+ painter = QtGui.QPainter(self)
+ rect = self.contentsRect()
+ board_top = rect.bottom() - TetrisBoard.HEIGHT*self.cellheight
+
+ for i in range(TetrisBoard.WIDTH):
+ for j in range(TetrisBoard.HEIGHT):
+ shape = self.board[i, j]
+
+ if shape:
+ color = self._shapes[shape].color
+ self.draw_square(painter,
+ rect.left() + i*self.cellwidth,
+ board_top + (TetrisBoard.HEIGHT-j-1)*self.cellheight, color)
+
+ if self.curr_piece is not None:
+ x = self.curr_x + self.curr_piece.x
+ y = self.curr_y - self.curr_piece.y
+
+ for i in range(4):
+ if TetrisBoard.HEIGHT < y[i]+1:
+ continue
+
+ self.draw_square(painter, rect.left() + x[i] * self.cellwidth,
+ board_top + (TetrisBoard.HEIGHT-y[i]-1) * self.cellheight,
+ self.curr_piece.color)
+
+
+class Tetromino:
+ SHAPE = np.array([[0], [0]])
+ color = None
+
+ def __init__(self):
+ self.shape = self.SHAPE
+
+ def rotate(self, clockwise: bool = True):
+ if clockwise:
+ self.shape = np.vstack((-self.shape[1], self.shape[0]))
+ else:
+ self.shape = np.vstack((self.shape[1], -self.shape[0]))
+
+ return self
+
+ @property
+ def x(self):
+ return self.shape[0]
+
+ @property
+ def y(self):
+ return self.shape[1]
+
+
+class ZShape(Tetromino):
+ SHAPE = np.array([[0, 0, -1, -1],
+ [-1, 0, 0, 1]])
+ color = 'green'
+
+
+class SShape(Tetromino):
+ SHAPE = np.array([[0, 0, 1, 1],
+ [-1, 0, 0, 1]])
+ color = 'purple'
+
+
+class Line(Tetromino):
+ SHAPE = np.array([[0, 0, 0, 0],
+ [-1, 0, 1, 2]])
+ color = 'red'
+
+
+class TShape(Tetromino):
+ SHAPE = np.array([[-1, 0, 1, 0],
+ [0, 0, 0, 1]])
+ color = 'orange'
+
+
+class Square(Tetromino):
+ SHAPE = np.array([[0, 1, 0, 1],
+ [0, 0, 1, 1]])
+ color = 'yellow'
+
+
+class LShape(Tetromino):
+ SHAPE = np.array([[-1, 0, 0, 0],
+ [1, -1, 0, 1]])
+ color = 'blue'
+
+
+class MirrorL(Tetromino):
+ SHAPE = np.array([[1, 0, 0, 0],
+ [-1, -1, 0, 1]])
+ color = 'lightGray'
+
+
+if __name__ == '__main__':
+ app = QtWidgets.QApplication([])
+ tetris = Game('snake')
+ tetris.show()
+ sys.exit(app.exec_())
diff --git a/nmreval/gui_qt/lib/styles.py b/nmreval/gui_qt/lib/styles.py
new file mode 100644
index 0000000..d453f50
--- /dev/null
+++ b/nmreval/gui_qt/lib/styles.py
@@ -0,0 +1,102 @@
+from . import HAS_IMPORTLIB_RESOURCE
+from ..Qt import QtGui, QtWidgets
+
+
+class DarkPalette(QtGui.QPalette):
+ def __init__(self):
+ super().__init__()
+
+ self.setColor(QtGui.QPalette.Base, QtGui.QColor(42, 42, 42))
+ self.setColor(QtGui.QPalette.AlternateBase, QtGui.QColor(66, 66, 66))
+ self.setColor(QtGui.QPalette.Window, QtGui.QColor(93, 93, 93))
+ self.setColor(QtGui.QPalette.ToolTipBase, QtGui.QColor(93, 93, 93))
+ self.setColor(QtGui.QPalette.Button, QtGui.QColor(93, 93, 93))
+
+ self.setColor(QtGui.QPalette.WindowText, QtGui.QColor(220, 220, 220))
+ self.setColor(QtGui.QPalette.ToolTipText, QtGui.QColor(220, 220, 220))
+ self.setColor(QtGui.QPalette.Text, QtGui.QColor(220, 220, 220))
+ self.setColor(QtGui.QPalette.BrightText, QtGui.QColor(220, 220, 220))
+ self.setColor(QtGui.QPalette.ButtonText, QtGui.QColor(220, 220, 220))
+ self.setColor(QtGui.QPalette.HighlightedText, QtGui.QColor(220, 220, 220))
+
+ self.setColor(QtGui.QPalette.Shadow, QtGui.QColor(20, 20, 20))
+ self.setColor(QtGui.QPalette.Dark, QtGui.QColor(35, 35, 35))
+
+ self.setColor(QtGui.QPalette.Highlight, QtGui.QColor(42, 130, 218))
+ self.setColor(QtGui.QPalette.Link, QtGui.QColor(220, 220, 220))
+ self.setColor(QtGui.QPalette.LinkVisited, QtGui.QColor(108, 180, 218))
+
+ # disabled
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Base, QtGui.QColor(80, 80, 80))
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Window, QtGui.QColor(80, 80, 80))
+
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Text, QtGui.QColor(127, 127, 127))
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, QtGui.QColor(127, 127, 127))
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.HighlightedText, QtGui.QColor(127, 127, 127))
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, QtGui.QColor(127, 127, 127))
+
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Highlight, QtGui.QColor(80, 80, 80))
+
+
+class LightPalette(QtGui.QPalette):
+ def __init__(self):
+ super().__init__()
+
+ self.setColor(QtGui.QPalette.Base, QtGui.QColor(237, 237, 237))
+ self.setColor(QtGui.QPalette.AlternateBase, QtGui.QColor(225, 225, 225))
+ self.setColor(QtGui.QPalette.Window, QtGui.QColor(240, 240, 240))
+ self.setColor(QtGui.QPalette.ToolTipBase, QtGui.QColor(240, 240, 240))
+ self.setColor(QtGui.QPalette.Button, QtGui.QColor(240, 240, 240))
+
+ self.setColor(QtGui.QPalette.WindowText, QtGui.QColor(0, 0, 0))
+ self.setColor(QtGui.QPalette.Text, QtGui.QColor(0, 0, 0))
+ self.setColor(QtGui.QPalette.BrightText, QtGui.QColor(0, 0, 0))
+ self.setColor(QtGui.QPalette.ButtonText, QtGui.QColor(0, 0, 0))
+ self.setColor(QtGui.QPalette.ToolTipText, QtGui.QColor(0, 0, 0))
+ self.setColor(QtGui.QPalette.HighlightedText, QtGui.QColor(0, 0, 0))
+
+ self.setColor(QtGui.QPalette.Shadow, QtGui.QColor(20, 20, 20))
+ self.setColor(QtGui.QPalette.Dark, QtGui.QColor(225, 225, 225))
+
+ self.setColor(QtGui.QPalette.Highlight, QtGui.QColor(218, 66, 42))
+ self.setColor(QtGui.QPalette.Link, QtGui.QColor(0, 162, 232))
+ self.setColor(QtGui.QPalette.LinkVisited, QtGui.QColor(222, 222, 222))
+
+ # disabled
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Base, QtGui.QColor(115, 115, 115))
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Window, QtGui.QColor(115, 115, 115))
+
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, QtGui.QColor(115, 115, 115))
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Text, QtGui.QColor(115, 115, 115))
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, QtGui.QColor(115, 115, 115))
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.HighlightedText, QtGui.QColor(115, 115, 115))
+
+ self.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Highlight, QtGui.QColor(190, 190, 190))
+
+
+class MyProxyStyle(QtWidgets.QProxyStyle):
+ def __init__(self, color):
+ super().__init__()
+
+ if color == 'dark':
+ self._palette = DarkPalette()
+ else:
+ self._palette = LightPalette()
+
+ def polish(self, obj):
+ if isinstance(obj, QtGui.QPalette):
+ return self._palette
+
+ elif isinstance(obj, QtWidgets.QApplication):
+ if HAS_IMPORTLIB_RESOURCE:
+ from importlib.resources import path
+ with path('resources.icons', 'style.qss') as fp:
+ with fp.open('r') as f:
+ obj.setStyleSheet(f.read())
+ else:
+ from pkg_resources import resource_filename
+ with open(resource_filename('resources.icons', 'style.qss'), 'r') as f:
+ obj.setStyleSheet(f.read())
+
+ else:
+ return super().polish(obj)
diff --git a/nmreval/gui_qt/lib/tables.py b/nmreval/gui_qt/lib/tables.py
new file mode 100644
index 0000000..15195c4
--- /dev/null
+++ b/nmreval/gui_qt/lib/tables.py
@@ -0,0 +1,31 @@
+from ..Qt import QtWidgets, QtGui, QtCore
+
+
+class TreeWidget(QtWidgets.QTreeWidget):
+ def keyPressEvent(self, evt: QtGui.QKeyEvent):
+ if evt.key() == QtCore.Qt.Key_Space:
+ sets = []
+ from_parent = []
+
+ for idx in self.selectedIndexes():
+ if idx.column() != 0:
+ continue
+ item = self.itemFromIndex(idx)
+
+ if item.parent() is None:
+ is_selected = item.checkState(0)
+ self.blockSignals(True)
+ for i in range(item.childCount()):
+ child = item.child(i)
+ # child.setCheckState(0, is_selected)
+ from_parent.append(child)
+ self.blockSignals(False)
+ item.setCheckState(0, QtCore.Qt.Unchecked if is_selected == QtCore.Qt.Checked else QtCore.Qt.Checked)
+ else:
+ sets.append(item)
+ for it in sets:
+ if it in from_parent:
+ continue
+ it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked)
+ else:
+ super().keyPressEvent(evt)
\ No newline at end of file
diff --git a/nmreval/gui_qt/lib/undos.py b/nmreval/gui_qt/lib/undos.py
new file mode 100644
index 0000000..d2231af
--- /dev/null
+++ b/nmreval/gui_qt/lib/undos.py
@@ -0,0 +1,245 @@
+import copy
+
+from numpy import argsort
+
+from ..Qt import QtWidgets, QtCore
+from ..data.container import FitContainer
+from ..graphs.graphwindow import QGraphWindow
+
+
+class ApodizationCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data, apod_values: list, apod_func: object):
+ super().__init__('Apodization')
+
+ self.__data = data
+ self.__y = copy.deepcopy(data.data.y)
+ self.__apod_func = apod_func
+ self.__apod_values = apod_values
+
+ def undo(self):
+ # doing a copy (again) to ensure two different objects
+ self.__data.y = copy.deepcopy(self.__y)
+
+ def redo(self):
+ self.__data.apply('ap', (self.__apod_values, self.__apod_func))
+
+
+class CutCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data, *limits):
+ super().__init__('Apodization')
+
+ self.__data = data
+ self.__data_data = copy.deepcopy(data.data)
+ self.__limits = limits
+
+ def undo(self):
+ # doing a copy (again) to ensure two different objects
+ self.__data.data = copy.deepcopy(self.__data_data)
+
+ def redo(self):
+ self.__data.apply('cut', self.__limits)
+
+
+class PhaseCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data, ph0: float, ph1: float, pvt: float):
+ super().__init__('Phase correction')
+
+ self.__phase = (ph0, ph1, pvt)
+ self.__data = data
+
+ def undo(self):
+ self.__data.apply('ph', (-self.__phase[0], -self.__phase[1], self.__phase[2]))
+
+ def redo(self):
+ self.__data.apply('ph', self.__phase)
+
+
+class ShiftCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data, value, mode):
+ super().__init__('Fourier')
+
+ self.__data = data
+ self.__original = copy.deepcopy(self.__data.data)
+ self.__args = (value, mode)
+
+ def undo(self):
+ self.__data.data = copy.deepcopy(self.__original)
+
+ def redo(self):
+ self.__data.apply('ls', self.__args)
+
+
+class NormCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data, mode):
+ super().__init__('Normalize')
+
+ self.__data = data
+ self.__mode = mode
+ self.__scale = 1.
+
+ def undo(self):
+ self.__data.y *= self.__scale
+ self.__data.y_err *= self.__scale
+
+ def redo(self):
+ max_value = self.__data.y.max()
+ self.__data.apply('norm', (self.__mode,))
+ self.__scale = max_value / self.__data.y.max()
+
+
+class CenterCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data):
+ super().__init__('Normalize')
+
+ self.__data = data
+ self.__offset = 0.
+
+ def undo(self):
+ _x = self.__data.data.x
+ _x += self.__offset
+ self.__data.x = _x
+
+ def redo(self):
+ x0 = self.__data.x[0]
+ self.__data.apply('center', ())
+ self.__offset = x0 - self.__data.x[0]
+
+
+class ZerofillCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data):
+ super().__init__('Zero filling')
+
+ self.__data = data
+
+ def undo(self):
+ self.__data.apply('zf', (-1,))
+
+ def redo(self):
+ self.__data.apply('zf', (1,))
+
+
+class BaselineCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data):
+ super().__init__('Baseline correction')
+
+ self.__baseline = None
+ self.__data = data
+
+ def undo(self):
+ self.__data.y += self.__baseline
+
+ def redo(self):
+ y_prev = self.__data.y[-1]
+ self.__data.apply('bl', tuple())
+ self.__baseline = y_prev - self.__data.y[-1]
+
+
+class BaselineSplineCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data, baseline):
+ super().__init__('Baseline correction')
+
+ self.__baseline = baseline
+ self.__data = data
+
+ def undo(self):
+ self.__data.apply('bls', (-self.__baseline,))
+
+ def redo(self):
+ self.__data.apply('bls', (self.__baseline,))
+
+
+class FourierCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data):
+ super().__init__('Fourier')
+
+ self.__data = data
+ self.__original = copy.deepcopy(self.__data.data)
+
+ def undo(self):
+ self.__data.data = copy.deepcopy(self.__original)
+
+ def redo(self):
+ self.__data.apply('ft', tuple())
+
+
+class SortCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data):
+ super().__init__('Sort')
+
+ self.__data = data
+ self.__sort = None
+
+ def undo(self):
+ self.__data.unsort(self.__sort)
+
+ def redo(self):
+ self.__sort = argsort(argsort(self.__data.data.x))
+ self.__data.apply('sort', tuple())
+
+
+class DeleteGraphCommand(QtWidgets.QUndoCommand):
+ def __init__(self, container: dict, key: str,
+ signal1: QtCore.pyqtSignal, signal2: QtCore.pyqtSignal):
+ super().__init__('Delete graph')
+ # Deletion of GraphWindow is more complicated because C++ object is destroyed
+
+ self.__container = container
+ _value = self.__container[key]
+ self.__value = self.__container[key].get_state()
+ self.__key = key
+ self.__signal_add = signal1
+ self.__signal_remove = signal2
+
+ def redo(self):
+ self.__signal_remove.emit(self.__key)
+ del self.__container[self.__key]
+
+ def undo(self):
+ q = QGraphWindow().set_state(self.__value)
+ self.__container[self.__key] = q
+ self.__signal_add.emit(self.__key)
+
+
+class DeleteCommand(QtWidgets.QUndoCommand):
+ def __init__(self, container, key, signal1, signal2):
+ super().__init__('Delete data')
+
+ self.__container = container
+ self.__value = self.__container[key]
+ self.__key = key
+ self.__signal_add = signal1
+ self.__signal_remove = signal2
+
+ def redo(self):
+ self.__signal_remove.emit(self.__key)
+ if isinstance(self.__value, FitContainer):
+ try:
+ self.__container[self.__value.fitted_key]._fits.remove(self.__key)
+ except KeyError:
+ pass
+ del self.__container[self.__key]
+
+ def undo(self):
+ self.__container[self.__key] = self.__value
+ if isinstance(self.__value, FitContainer):
+ try:
+ self.__container[self.__value.fitted_key]._fits.append(self.__key)
+ except KeyError:
+ pass
+
+ self.__signal_add.emit([self.__key], self.__value.graph)
+
+
+class EvalCommand(QtWidgets.QUndoCommand):
+ def __init__(self, container: dict, key: str, new_data, title: str):
+ super().__init__(title)
+ self.__container = container
+ self.__value = copy.deepcopy(self.__container[key].data)
+ self.__replacement = new_data
+ self.__key = key
+
+ def redo(self):
+ self.__container[self.__key].data = self.__replacement
+
+ def undo(self):
+ self.__container[self.__key].data = self.__value
diff --git a/nmreval/gui_qt/lib/usermodeleditor.py b/nmreval/gui_qt/lib/usermodeleditor.py
new file mode 100644
index 0000000..ab02c45
--- /dev/null
+++ b/nmreval/gui_qt/lib/usermodeleditor.py
@@ -0,0 +1,143 @@
+from __future__ import annotations
+
+from pathlib import Path
+
+from ..Qt import QtWidgets, QtCore, QtGui
+from ..lib.codeeditor import CodeEditor
+
+
+class QUsermodelEditor(QtWidgets.QMainWindow):
+ modelsChanged = QtCore.pyqtSignal()
+
+ def __init__(self, fname: str | Path = None, parent=None):
+ super().__init__(parent=parent)
+
+ self._init_gui()
+
+ self.fname = None
+ self._dir = None
+ if fname is not None:
+ self.read_file(fname)
+
+ def _init_gui(self):
+ self.centralwidget = QtWidgets.QWidget(self)
+
+ layout = QtWidgets.QVBoxLayout(self.centralwidget)
+ layout.setContentsMargins(3, 3, 3, 3)
+ layout.setSpacing(3)
+
+ self.edit_field = CodeEditor(self.centralwidget)
+ font = QtGui.QFont('default')
+ font.setStyleHint(font.Monospace)
+ font.setPointSize(10)
+ self.edit_field.setFont(font)
+
+ layout.addWidget(self.edit_field)
+ self.setCentralWidget(self.centralwidget)
+
+ self.statusbar = QtWidgets.QStatusBar(self)
+ self.setStatusBar(self.statusbar)
+
+ self.menubar = self.menuBar()
+
+ self.menuFile = QtWidgets.QMenu('File', self.menubar)
+ self.menubar.addMenu(self.menuFile)
+
+ self.menuFile.addAction('Open...', self.open_file, QtGui.QKeySequence.Open)
+ self.menuFile.addAction('Save', self.overwrite_file, QtGui.QKeySequence.Save)
+ self.menuFile.addAction('Save as...', self.save_file, QtGui.QKeySequence('Ctrl+Shift+S'))
+ self.menuFile.addSeparator()
+ self.menuFile.addAction('Close', self.close, QtGui.QKeySequence.Quit)
+
+ self.resize(800, 600)
+ self.setGeometry(QtWidgets.QStyle.alignedRect(
+ QtCore.Qt.LeftToRight, QtCore.Qt.AlignCenter,
+ self.size(), QtWidgets.qApp.desktop().availableGeometry()
+ ))
+
+ @property
+ def is_modified(self):
+ return self.edit_field.document().isModified()
+
+ @is_modified.setter
+ def is_modified(self, val: bool):
+ self.edit_field.document().setModified(val)
+
+ @QtCore.pyqtSlot()
+ def open_file(self):
+ overwrite = self.changes_saved
+
+ if overwrite:
+ fname, _ = QtWidgets.QFileDialog.getOpenFileName(directory=str(self._dir))
+ if fname:
+ self.read_file(fname)
+
+ def read_file(self, fname: str | Path):
+ self.set_fname_opts(fname)
+
+ with self.fname.open('r') as f:
+ self.edit_field.setPlainText(f.read())
+
+ def set_fname_opts(self, fname: str | Path):
+ self.fname = Path(fname)
+ self._dir = self.fname.parent
+ self.setWindowTitle('Edit ' + str(fname))
+
+ @property
+ def changes_saved(self) -> bool:
+ if not self.is_modified:
+ return True
+
+ ret = QtWidgets.QMessageBox.question(self, 'Time to think',
+ 'The document was modified.
\n'
+ 'Do you want to save changes?
',
+ QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No |
+ QtWidgets.QMessageBox.Cancel)
+ if ret == QtWidgets.QMessageBox.Yes:
+ self.save_file()
+
+ if ret == QtWidgets.QMessageBox.No:
+ self.is_modified = False
+
+ return not self.is_modified
+
+ @QtCore.pyqtSlot()
+ def save_file(self):
+ outfile, _ = QtWidgets.QFileDialog().getSaveFileName(parent=self, caption='Save file', directory=str(self._dir))
+
+ if outfile:
+ with open(outfile, 'w') as f:
+ f.write(self.edit_field.toPlainText())
+
+ self.set_fname_opts(outfile)
+
+ self.is_modified = False
+
+ return self.is_modified
+
+ @QtCore.pyqtSlot()
+ def overwrite_file(self):
+ if self.fname is not None:
+ with self.fname.open('w') as f:
+ f.write(self.edit_field.toPlainText())
+
+ self.modelsChanged.emit()
+
+ self.is_modified = False
+
+ def closeEvent(self, evt: QtGui.QCloseEvent):
+ if not self.changes_saved:
+ evt.ignore()
+ else:
+ super().closeEvent(evt)
+
+
+if __name__ == '__main__':
+ import sys
+ app = QtWidgets.QApplication([])
+
+ win = QUsermodelEditor('/autohome/dominik/.nmreval/myfitmodels.py')
+ win.show()
+
+ sys.exit(app.exec())
+
diff --git a/nmreval/gui_qt/lib/utils.py b/nmreval/gui_qt/lib/utils.py
new file mode 100644
index 0000000..adba102
--- /dev/null
+++ b/nmreval/gui_qt/lib/utils.py
@@ -0,0 +1,93 @@
+from math import inf
+from contextlib import contextmanager
+from numpy import linspace
+from scipy.interpolate import interp1d
+
+from ..Qt import QtGui, QtWidgets
+
+
+@contextmanager
+def busy_cursor():
+ try:
+ cursor = QtGui.QCursor(QtGui.QPixmap('/autohome/dominik/Downloads/slowbro.gif'))
+ QtWidgets.QApplication.setOverrideCursor(cursor)
+ yield
+
+ finally:
+ QtWidgets.QApplication.restoreOverrideCursor()
+
+
+class SciSpinBox(QtWidgets.QDoubleSpinBox):
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.validator = QtGui.QDoubleValidator(self)
+
+ self.setMinimum(-inf)
+ self.setMaximum(inf)
+ self.setDecimals(1000)
+ self.precision = 0.001
+
+ self._prev_value = float(self.lineEdit().text())
+
+ def valueFromText(self, text: str) -> float:
+ try:
+ self._prev_value = float(self.cleanText())
+ except ValueError:
+ pass
+ return self._prev_value
+
+ def textFromValue(self, value: float) -> str:
+ if value == 0:
+ return '0'
+ else:
+ return f'{value:.3e}'
+
+ def stepBy(self, step: int):
+ self._prev_value = self.value()
+
+ new_value = self._prev_value
+ if new_value != 0.0:
+ new_value *= 10**(step/19.)
+ else:
+ new_value = 0.001
+
+ self.setValue(new_value)
+ self.lineEdit().setText(f'{new_value:.3e}')
+
+ def validate(self, text, pos):
+ return self.validator.validate(text, pos)
+
+
+class RdBuCMap:
+ # taken from Excel sheet from colorbrewer.org
+ _rdbu = [
+ (103, 0, 31),
+ (178, 24, 43),
+ (214, 96, 77),
+ (244, 165, 130),
+ (253, 219, 199),
+ (247, 247, 247),
+ (209, 229, 240),
+ (146, 197, 222),
+ (67, 147, 195),
+ (33, 102, 172),
+ (5, 48, 97)
+ ]
+
+ def __init__(self, vmin=-1., vmax=1.):
+ self.min = vmin
+ self.max = vmax
+
+ self.spline = [interp1d(linspace(self.max, self.min, num=11), [rgb[i] for rgb in RdBuCMap._rdbu])
+ for i in range(3)]
+
+ def color(self, val: float):
+ if val > self.max:
+ col = QtGui.QColor.fromRgb(*RdBuCMap._rdbu[0])
+ elif val < self.min:
+ col = QtGui.QColor.fromRgb(*RdBuCMap._rdbu[-1])
+ else:
+ col = QtGui.QColor.fromRgb(*(float(self.spline[i](val)) for i in range(3)))
+
+ return col
diff --git a/nmreval/gui_qt/main/__init__.py b/nmreval/gui_qt/main/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nmreval/gui_qt/main/mainwindow.py b/nmreval/gui_qt/main/mainwindow.py
new file mode 100644
index 0000000..2747138
--- /dev/null
+++ b/nmreval/gui_qt/main/mainwindow.py
@@ -0,0 +1,922 @@
+import pathlib
+from pathlib import Path
+from typing import List, Tuple
+
+from numpy import geomspace, linspace
+from pyqtgraph import ViewBox, PlotDataItem
+
+from .management import UpperManagement
+from ..Qt import QtCore, QtGui, QtPrintSupport, QtWidgets
+from ..data.shift_graphs import QShift
+from ..data.signaledit import QApodDialog, QBaselineDialog, QPhasedialog
+from ..fit.result import QFitResult
+from ..graphs.graphwindow import QGraphWindow
+from ..graphs.movedialog import QMover
+from ..io.fcbatchreader import QFCReader
+from ..io.filedialog import OpenFileDialog, SaveDirectoryDialog
+from ..lib import get_icon, make_action_icons
+from ..lib.pg_objects import RegionItem
+from ..math.evaluation import QEvalDialog
+from ..math.interpol import InterpolDialog
+from ..math.mean_dialog import QMeanTimes
+from ..math.smooth import QSmooth
+from ..nmr.coupling_calc import QCoupCalcDialog
+from ..nmr.t1_from_tau import QRelaxCalc
+from .._py.basewindow import Ui_BaseWindow
+from ...configs import *
+
+
+class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
+ closeSignal = QtCore.pyqtSignal()
+ openpoints = QtCore.pyqtSignal(dict)
+ save_ses_sig = QtCore.pyqtSignal(str)
+ rest_ses_sig = QtCore.pyqtSignal(str)
+
+ def __init__(self, parents=None, path=None):
+ super().__init__(parent=parents)
+
+ if path is None:
+ self.path = Path.home()
+ else:
+ self.path = Path(path)
+
+ self.read_state()
+
+ self.management = UpperManagement(self)
+
+ self.fitlimitvalues = [None, None]
+ self.fitpreview = []
+ self._fit_plot_id = None
+ self.savefitdialog = None
+ self.eval = None
+ self.editor = None
+
+ self.movedialog = QMover(self)
+
+ self.current_graph_widget = None
+ self.current_plotitem = None
+ self._block_window_change = False
+
+ self.fname = None
+ self.tim = QtCore.QTimer()
+
+ self.settings = QtCore.QSettings('NMREVal', 'settings')
+ self._init_gui()
+ self._init_signals()
+
+ def _init_gui(self):
+ self.setupUi(self)
+ make_action_icons(self)
+ self.setWindowIcon(get_icon('logo'))
+
+ self.norm_toolbutton = QtWidgets.QToolButton(self)
+ self.norm_toolbutton.setMenu(self.menuNormalize)
+ self.norm_toolbutton.setPopupMode(self.norm_toolbutton.InstantPopup)
+ self.norm_toolbutton.setIcon(get_icon('normal'))
+ self.toolbar_edit.addWidget(self.norm_toolbutton)
+
+ self.fitlim_button = QtWidgets.QToolButton(self)
+ self.fitlim_button.setMenu(self.menuLimits)
+ self.fitlim_button.setPopupMode(self.fitlim_button.InstantPopup)
+ self.fitlim_button.setIcon(get_icon('fit_region'))
+ self.toolBar_fit.addWidget(self.fitlim_button)
+
+ self.area.dragEnterEvent = self.dragEnterEvent
+
+ while self.tabWidget.count() > 2:
+ self.tabWidget.removeTab(self.tabWidget.count()-1)
+
+ # Prevent closing "data" and "values"
+ for i in [0, 1]:
+ self.tabWidget.tabBar().tabButton(i, QtWidgets.QTabBar.ButtonPosition.RightSide).resize(0, 0)
+
+ self.setAcceptDrops(True)
+
+ self.mousepos = QtWidgets.QLabel('')
+ self.status = QtWidgets.QLabel('')
+ self.statusBar.addWidget(self.status)
+ self.statusBar.addWidget(self.mousepos)
+
+ self.fitregion = RegionItem()
+ self._values_plot = PlotDataItem(x=[], y=[], symbolSize=30, symbol='x',
+ pen=None, symbolPen='#d526b5', symbolBrush='#d526b5')
+
+ self._fit_plot_id = None
+
+ self.setGeometry(QtWidgets.QStyle.alignedRect(QtCore.Qt.LeftToRight, QtCore.Qt.AlignCenter,
+ self.size(), QtWidgets.qApp.desktop().availableGeometry()))
+
+ self.datawidget.management = self.management
+
+ self.ac_group = QtWidgets.QActionGroup(self)
+ self.ac_group.addAction(self.action_lm_fit)
+ self.ac_group.addAction(self.action_nm_fit)
+ self.ac_group.addAction(self.action_odr_fit)
+
+ self.ac_group2 = QtWidgets.QActionGroup(self)
+ self.ac_group2.addAction(self.action_no_range)
+ self.ac_group2.addAction(self.action_x_range)
+ self.ac_group2.addAction(self.action_custom_range)
+
+ def _init_signals(self):
+ self.actionRedo = self.management.undostack.createRedoAction(self)
+ icon = QtGui.QIcon.fromTheme("edit-redo")
+ self.actionRedo.setIcon(icon)
+ self.actionRedo.setShortcuts(QtGui.QKeySequence.Redo)
+ self.menuData.insertAction(self.action_new_set, self.actionRedo)
+
+ self.actionUndo = self.management.undostack.createUndoAction(self)
+ self.actionUndo.setShortcuts(QtGui.QKeySequence.Undo)
+ icon = QtGui.QIcon.fromTheme("edit-undo")
+ self.actionUndo.setIcon(icon)
+ 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.ac_group2.triggered.connect(self.change_fit_limits)
+
+ self.t1action.triggered.connect(lambda: self._show_tab('t1_temp'))
+ self.action_edit.triggered.connect(lambda: self._show_tab('signal'))
+ self.actionPick_position.triggered.connect(lambda: self._show_tab('pick'))
+ self.actionIntegrate.triggered.connect(lambda: self._show_tab('integrate'))
+ self.action_FitWidget.triggered.connect(lambda: self._show_tab('fit'))
+
+ self.action_new_set.triggered.connect(self.management.create_empty)
+
+ self.datawidget.keyChanged.connect(self.management.change_keys)
+ self.datawidget.tree.deleteItem.connect(self.management.delete_sets)
+ self.datawidget.tree.moveItem.connect(self.management.move_sets)
+ self.datawidget.tree.copyItem.connect(self.management.copy_sets)
+ self.datawidget.graph_toolButton.clicked.connect(self.new_graph)
+ self.datawidget.empty_toolButton.clicked.connect(self.management.create_empty)
+ self.datawidget.func_toolButton.clicked.connect(self.make_data_from_function)
+ self.datawidget.tree.stateChanged.connect(self.management.change_visibility)
+ self.datawidget.startShowProperty.connect(self.management.get_properties)
+ self.datawidget.propertyChanged.connect(self.management.update_property)
+ self.datawidget.tree.saveFits.connect(self.save_fit_parameter)
+
+ self.management.newData.connect(self.show_new_data)
+ self.management.newGraph.connect(self.new_graph)
+ self.management.dataChanged.connect(self.update_data)
+ self.management.deleteData.connect(self.delete_data)
+ self.management.deleteGraph.connect(self.remove_graph)
+ self.management.restoreGraph.connect(self.set_graph)
+ self.management.properties_collected.connect(self.datawidget.set_properties)
+ self.management.unset_state.connect(lambda x: self.datawidget.uncheck_sets(x))
+ self.management.fitFinished.connect(self.show_fit_results)
+
+ self.fit_dialog._management = self.management
+ self.fit_dialog.preview_emit.connect(self.show_fit_preview)
+ self.fit_dialog.fitStartSig.connect(self.start_fit)
+ self.fit_dialog.abortFit.connect(lambda : self.management.stopFit.emit())
+
+ self.movedialog.moveData.connect(self.move_sets)
+ self.movedialog.copyData.connect(self.management.copy_sets)
+
+ self.ptsselectwidget.points_selected.connect(self.management.extract_points)
+
+ self.t1tauwidget.newData.connect(self.management.add_new_data)
+
+ self.editsignalwidget.do_something.connect(self.management.apply)
+ self.editsignalwidget.preview_triggered.connect(self.do_preview)
+
+ self.action_sort_pts.triggered.connect(lambda: self.management.apply('sort', ()))
+ self.action_calc_eps_derivative.triggered.connect(self.management.bds_deriv)
+ self.action_magnitude.triggered.connect(self.management.calc_magn)
+ self.actionCenterMax.triggered.connect(lambda: self.management.apply('center', ()))
+
+ self.valuewidget.requestData.connect(self.show_data_values)
+ self.valuewidget.itemChanged.connect(self.management.set_values)
+ self.valuewidget.itemDeleted.connect(self.management.remove_values)
+ self.valuewidget.itemAdded.connect(self.management.append)
+ self.valuewidget.maskSignal.connect(self.management.mask_value)
+ self.valuewidget.values_selected.connect(self.plot_selected_values)
+
+ self.actionMaximize.triggered.connect(lambda: self.current_graph_widget.showMaximized())
+ self.actionNext_window.triggered.connect(lambda: self.area.activateNextSubWindow())
+ self.actionPrevious.triggered.connect(lambda: self.area.activatePreviousSubWindow())
+
+ self.closeSignal.connect(self.close)
+
+ self.action_norm_max.triggered.connect(lambda: self.management.apply('norm', ('max',)))
+ self.action_norm_max_abs.triggered.connect(lambda: self.management.apply('norm', ('maxabs',)))
+ self.action_norm_first.triggered.connect(lambda: self.management.apply('norm', ('first',)))
+ self.action_norm_last.triggered.connect(lambda: self.management.apply('norm', ('last',)))
+ self.action_norm_area.triggered.connect(lambda: self.management.apply('norm', ('area',)))
+ self.action_cut.triggered.connect(lambda: self.management.cut())
+
+ self.actionConcatenate_sets.triggered.connect(lambda : self.management.cat())
+
+ @QtCore.pyqtSlot(name='on_action_open_triggered')
+ def open(self):
+ filedialog = OpenFileDialog(directory=self.path, caption='Open files',
+ filters='All files (*.*);;'
+ 'Program session (*.nmr);;'
+ 'HDF files (*.h5);;'
+ 'Text files (*.txt *.dat);;'
+ 'Novocontrol Alpha (*.EPS);;'
+ 'TecMag files (*.tnt);;'
+ 'Grace files (*.agr)')
+
+ filedialog.set_graphs(self.management.graphs.list())
+
+ filedialog.exec()
+ fname = filedialog.selectedFiles()
+
+ if fname:
+ self.path = Path(fname[0]).parent
+ self.management.load_files(fname, new_plot=filedialog.add_to_graph)
+
+ @QtCore.pyqtSlot(name='on_actionOpen_FC_triggered')
+ def read_fc(self):
+ reader = QFCReader(path=self.path, parent=self)
+ reader.data_read.connect(self.management.add_new_data)
+ reader.exec()
+
+ del reader
+
+ @QtCore.pyqtSlot(name='on_actionPrint_triggered')
+ def print(self):
+ QtPrintSupport.QPrintDialog().exec()
+
+ @QtCore.pyqtSlot(name='on_actionExportData_triggered')
+ @QtCore.pyqtSlot(name='on_actionSave_triggered')
+ def save(self):
+ save_dialog = SaveDirectoryDialog(
+ directory=str(self.path), parent=self,
+ )
+
+ mode = save_dialog.exec()
+ if mode == QtWidgets.QDialog.Accepted:
+ path = save_dialog.selectedFiles()
+ selected_filter = save_dialog.selectedNameFilter()
+
+ if path:
+ self.management.save(path[0], selected_filter)
+
+ @QtCore.pyqtSlot()
+ @QtCore.pyqtSlot(list)
+ def save_fit_parameter(self, fit_sets: List[str] = None):
+ fname, _ = QtWidgets.QFileDialog.getSaveFileName(self, 'Save fit parameter', directory=str(self.path),
+ filter='All files(*, *);;Text files(*.dat *.txt)')
+
+ if fname:
+ self.management.save_fit_parameter(fname, fit_sets=fit_sets)
+
+ @QtCore.pyqtSlot(name='on_actionExportGraphic_triggered')
+ def export_graphic(self):
+ self.current_graph_widget.export()
+
+ @QtCore.pyqtSlot(name='on_actionNew_window_triggered')
+ def new_graph(self):
+ w = QGraphWindow()
+ self.management.graphs[w.id] = w
+ self.set_graph(w.id)
+
+ if self.eval is not None:
+ self.eval.set_graphs(self.management.graphs.list())
+
+ return w.id
+
+ @QtCore.pyqtSlot(list, str)
+ def show_new_data(self, sets: list, graph: str):
+ if len(sets) == 0:
+ return
+
+ if graph == '':
+ graph = self.new_graph()
+
+ self.management.plots_to_graph(sets, graph)
+
+ for idd in sets:
+ new_item = self.management[idd]
+ self.datawidget.blockSignals(True)
+ self.datawidget.add_item(new_item.id, new_item.name, graph)
+ self.datawidget.blockSignals(False)
+
+ if graph == self.fit_dialog.connected_figure:
+ self.fit_dialog.load(self.management.graphs.active(graph))
+
+ @QtCore.pyqtSlot(name='on_actionDelete_window_triggered')
+ def delete_windows(self):
+ self.management.delete_sets()
+
+ @QtCore.pyqtSlot(str)
+ def remove_graph(self, gid: str):
+ print(gid, self.current_graph_widget)
+ self.datawidget.remove_item(gid)
+ w = None
+ for w in self.area.subWindowList():
+ wdgt = w.widget()
+ if wdgt.id == gid:
+ wdgt.disconnect()
+ if wdgt == self.current_graph_widget:
+ if self.ptsselectwidget.connected_figure == gid:
+ self.ptsselectwidget.connected_figure = None
+ self.tabWidget.removeTab(self.tabWidget.indexOf(self.ptsselectwidget))
+
+ if self.t1tauwidget.connected_figure == gid:
+ self.t1tauwidget.connected_figure = None
+ self.tabWidget.removeTab(self.tabWidget.indexOf(self.t1tauwidget))
+
+ if self.fit_dialog.connected_figure == gid:
+ self.fit_dialog.connected_figure = None
+ for item in self.fit_dialog.preview_lines:
+ self.current_graph_widget.remove_external(item)
+
+ if self.valuewidget.graph_shown == gid:
+ self.tabWidget.setCurrentIndex(0)
+
+ self.current_graph_widget = None
+ self.management.current_graph = ''
+ self.current_plotitem = None
+
+ break
+
+ if w is not None:
+ w.close()
+
+ if self.current_graph_widget is None:
+ self.area.activateNextSubWindow()
+
+ @QtCore.pyqtSlot(str)
+ def set_graph(self, key: str):
+ w = self.management.graphs[key]
+
+ subwindow = self.area.addSubWindow(w)
+ subwindow.setOption(QtWidgets.QMdiSubWindow.RubberBandMove, True)
+ subwindow.setOption(QtWidgets.QMdiSubWindow.RubberBandResize, True)
+ subwindow.setMinimumHeight(400)
+ subwindow.setMinimumWidth(600)
+
+ self.datawidget.blockSignals(True)
+ self.datawidget.tree.blockSignals(True)
+ self.datawidget.add_graph(w.id, w.title)
+ self.datawidget.tree.blockSignals(False)
+ self.datawidget.blockSignals(False)
+
+ w.mousePositionChanged.connect(self.mousemoved)
+ w.aboutToClose.connect(self.delete_windows)
+ w.positionClicked.connect(self.point_selected)
+ w.show()
+
+ graph_list = self.management.graphs.list()
+ self.t1tauwidget.set_graphs(graph_list)
+ self.ptsselectwidget.set_graphs(graph_list)
+
+ @QtCore.pyqtSlot(QtWidgets.QMdiSubWindow, name='on_area_subWindowActivated')
+ 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"""
+ if wd is not None:
+ if self.current_graph_widget is not None:
+ self.current_graph_widget.closable = True
+
+ if self.ptsselectwidget.isVisible():
+ self._select_ptswidget(False, False, False)
+
+ if self.fit_dialog.isVisible():
+ self._select_fitwidget(False, False)
+
+ self.current_graph_widget = wd.widget()
+ self.management.current_graph = wd.widget().id
+ self.current_plotitem = self.current_graph_widget.graphic
+
+ pick = False
+ block = False
+ if self.ptsselectwidget.isVisible():
+ pick, block = self._select_ptswidget(True, pick, block)
+ if self.fit_dialog.isVisible():
+ block = self._select_fitwidget(True, block)
+
+ self._set_pick_block(pick, block)
+
+ self.datawidget.tree.blockSignals(True)
+ self.datawidget.tree.highlight(self.management.current_graph)
+ 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')
+ def type_change_dialog(self):
+ from ..data.conversion import ConversionDialog
+
+ dialog = ConversionDialog(self)
+ dialog.set_graphs(self.management.graphs.tree())
+ dialog.convertSets.connect(self.management.convert_sets)
+
+ _ = dialog.exec()
+
+ dialog.disconnect()
+
+ def _set_pick_block(self, pick: bool, block: bool):
+ self.current_graph_widget.enable_picking(pick)
+ self.current_graph_widget.closable = not block
+
+ @QtCore.pyqtSlot(int, name='on_tabWidget_currentChanged')
+ def toggle_tabs(self, idx: int):
+ widget = self.tabWidget.widget(idx)
+ if self.current_graph_widget is None:
+ if self.tabWidget.currentIndex() > 1:
+ self.tabWidget.removeTab(self.tabWidget.indexOf(widget))
+ self.tabWidget.setCurrentIndex(0)
+ return
+
+ if self.current_graph_widget is not None:
+ self.current_graph_widget.enable_picking(False)
+
+ pick_required, block_window = self._select_ptswidget(widget == self.ptsselectwidget, False, False)
+ self._select_valuewidget(widget == self.valuewidget)
+ 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)
+
+ self._set_pick_block(pick_required, block_window)
+
+ def _select_ptswidget(self, onoff: bool, pick_required: bool, block_window: bool) -> Tuple[bool, bool]:
+ if self.current_graph_widget is None:
+ return pick_required, block_window
+
+ if onoff: # point selection
+ for line in self.ptsselectwidget.pts_lines:
+ self.current_graph_widget.add_external(line)
+ self.ptsselectwidget.point_removed.connect(self.current_graph_widget.remove_external)
+ self.ptsselectwidget.connected_figure = self.management.current_graph
+ pick_required = True
+ else:
+ if self.ptsselectwidget.connected_figure:
+ g = self.management.graphs[self.ptsselectwidget.connected_figure]
+ for line in self.ptsselectwidget.pts_lines:
+ g.remove_external(line)
+ # self.ptsselectwidget.clear()
+
+ return pick_required, block_window
+
+ @QtCore.pyqtSlot(int, name='on_tabWidget_tabCloseRequested')
+ def close_tab(self, idx: int):
+ if idx == 0:
+ pass
+ else:
+ self.tabWidget.setCurrentIndex(0)
+ self.tabWidget.removeTab(idx)
+
+ def _show_tab(self, mode: str):
+ widget, name = {
+ 't1_temp': (self.t1tauwidget, 'T1 mininmon'),
+ 'signal': (self.editsignalwidget, 'Signals'),
+ 'pick': (self.ptsselectwidget, 'Pick points'),
+ 'fit': (self.fit_dialog, 'Fit')
+ }[mode]
+
+ for idx in range(self.tabWidget.count()):
+ if self.tabWidget.widget(idx) == widget:
+ self.tabWidget.setCurrentIndex(idx)
+ return
+
+ self.tabWidget.addTab(widget, name)
+ self.tabWidget.setCurrentIndex(self.tabWidget.count()-1)
+
+ def _select_valuewidget(self, onoff: bool):
+ if onoff: # Values
+ self.valuewidget(self.management.graphs.tree())
+ self.valuewidget.connected_figure = self.management.current_graph
+ if self.valuewidget.graph_shown is not None:
+ self.management.graphs[self.valuewidget.graph_shown].add_external(self._values_plot)
+ else:
+ if self.valuewidget.graph_shown is not None:
+ self.management.graphs[self.valuewidget.graph_shown].remove_external(self._values_plot)
+
+ def _select_integralwidget(self, onoff: bool, pick_required: bool):
+ if self.current_graph_widget is None:
+ return pick_required
+
+ if onoff:
+ self.integralwidget(self.current_graph_widget.title,
+ self.management.graphs.current_sets(self.management.current_graph))
+ self.integralwidget.item_deleted.connect(self.current_graph_widget.remove_external)
+ self.integralwidget.connected_figure = self.management.current_graph
+ pick_required = True
+ else:
+ if self.integralwidget.connected_figure:
+ g = self.management.graphs[self.integralwidget.connected_figure]
+ for line in self.integralwidget.lines:
+ g.remove_external(line[0])
+ g.remove_external(line[1])
+ self.integralwidget.clear()
+
+ return pick_required
+
+ def _select_t1tauwidget(self, onoff: bool, pick_required: bool, block_window: bool):
+ if onoff: # tau from t1
+ if self.current_graph_widget is None:
+ return
+
+ idx = self.tabWidget.indexOf(self.t1tauwidget)
+ if len(self.current_graph_widget) != 1:
+ QtWidgets.QMessageBox.information(self, 'Too many datasets',
+ 'Only one T1 curve can be evaluated at once')
+ self.tabWidget.removeTab(idx)
+ self.tabWidget.setCurrentIndex(0)
+ return
+
+ self.tabWidget.setTabText(idx, 'T1 min (%s)' % {self.current_graph_widget.title})
+
+ self.t1tauwidget.set_graphs(self.management.graphs.list())
+ self.t1tauwidget.set_data(*self.management.get_data(self.current_graph_widget.active[0], xy_only=True),
+ name=self.management[self.current_graph_widget.active[0]].name)
+ self.t1tauwidget.connected_figure = self.management.current_graph
+ self.current_graph_widget.add_external(self.t1tauwidget.min_pos)
+ self.current_graph_widget.add_external(self.t1tauwidget.parabola)
+ pick_required = True
+ block_window = True
+ else:
+ if self.t1tauwidget.connected_figure:
+ g = self.management.graphs[self.t1tauwidget.connected_figure]
+ g.remove_external(self.t1tauwidget.min_pos)
+ g.remove_external(self.t1tauwidget.parabola)
+
+ return pick_required, block_window
+
+ @QtCore.pyqtSlot(str)
+ def get_data(self, key: str):
+ self.sender().set_data(self.management[key])
+
+ @QtCore.pyqtSlot(name='on_actionCalculateT1_triggered')
+ def show_t1calc_dialog(self):
+ dialog = QRelaxCalc(self)
+ dialog.set_graphs(self.management.graphs.tree(key_only=True))
+ dialog.newData.connect(self.management.calc_relaxation)
+ dialog.show()
+
+ @QtCore.pyqtSlot(name='on_actionSkip_points_triggered')
+ def skip_pts_dialog(self):
+ from ..math.skipping import QSkipDialog
+
+ dial = QSkipDialog(self)
+ dial.exec()
+
+ self.management.skip_points(**dial.get_arguments())
+
+ @QtCore.pyqtSlot(str, name='on_action_coup_calc_triggered')
+ def coupling_dialog(self):
+ dialog = QCoupCalcDialog(self)
+ dialog.show()
+
+ @QtCore.pyqtSlot(name='on_action_mean_t1_triggered')
+ def mean_dialog(self):
+ gnames = self.management.graphs.tree(key_only=True)
+ dialog = QMeanTimes(gnames, parent=self)
+ dialog.newValues.connect(self.management.calc_mean)
+ dialog.show()
+
+ @QtCore.pyqtSlot(name='on_actionRunning_values_triggered')
+ def smooth_dialog(self):
+ dialog = QSmooth(parent=self)
+ dialog.newValues.connect(self.management.smooth_data)
+ dialog.show()
+
+ @QtCore.pyqtSlot(name='on_actionInterpolation_triggered')
+ def interpol_dialog(self):
+ if self.current_graph_widget is None:
+ return
+
+ gnames = self.management.graphs.tree()
+ dialog = InterpolDialog(parent=self)
+ dialog.set_data(gnames, self.current_graph_widget.id)
+ dialog.new_data.connect(self.management.interpolate_data)
+ dialog.show()
+
+ @QtCore.pyqtSlot(name='on_action_calc_triggered')
+ def open_eval_dialog(self):
+ if self.eval is None:
+ self.eval = QEvalDialog(parent=self)
+ self.eval.do_eval.connect(self.management.eval_expression)
+ self.eval.do_calc.connect(self.management.create_from_function)
+
+ self.eval.set_mode('e')
+ self.eval.set_namespace(self.management.get_namespace())
+ self.eval.add_data(self.management.active_sets)
+ self.eval.set_graphs(self.management.graphs.list())
+
+ self.eval.exec()
+
+ @QtCore.pyqtSlot(name='on_actionAddlines_triggered')
+ def make_data_from_function(self):
+ if self.eval is None:
+ self.eval = QEvalDialog(parent=self)
+ self.eval.do_eval.connect(self.management.eval_expression)
+ self.eval.do_calc.connect(self.management.create_from_function)
+
+ self.eval.set_mode('c')
+ self.eval.set_namespace(self.management.get_namespace())
+ self.eval.set_graphs(self.management.graphs.list())
+
+ self.eval.exec()
+
+ @QtCore.pyqtSlot(name='on_actionDerivation_triggered')
+ @QtCore.pyqtSlot(name='on_actionIntegration_triggered')
+ @QtCore.pyqtSlot(name='on_actionFilon_triggered')
+ def int_diff_ft(self):
+ sets = self.management.active_sets
+ mode = {self.actionIntegration: 'int',
+ self.actionDerivation: 'diff',
+ self.actionFilon: 'logft'}[self.sender()]
+
+ if sets:
+ from ..math.integrate_derive import QDeriveIntegrate
+
+ dialog = QDeriveIntegrate(mode)
+ dialog.add_graphs(self.management.graphs.list())
+ dialog.set_sets(sets)
+
+ res = dialog.exec()
+
+ if res:
+ options = dialog.get_options()
+ mode = options['mode']
+ if mode in ['i', 'd']:
+ self.management.integrate(**options)
+ elif mode == 'l':
+ self.management.logft(**options)
+ else:
+ raise ValueError('Unknown mode %s, not `i`, `d`, `l`.' % str(mode))
+
+ @QtCore.pyqtSlot(str)
+ def update_data(self, sid: str):
+ if self.valuewidget.shown_set == sid:
+ self.show_data_values(sid)
+
+ self.datawidget.set_name(sid, self.management[sid].name)
+
+ @QtCore.pyqtSlot(str)
+ def delete_data(self, sid):
+ print('remove', sid)
+ if self.valuewidget.shown_set == sid:
+ self.tabWidget.setCurrentIndex(0)
+
+ self.datawidget.remove_item(sid)
+
+ @QtCore.pyqtSlot(name='on_actionBaseline_triggered')
+ def baseline_dialog(self):
+ if not self.current_graph_widget:
+ return
+
+ if len(self.current_graph_widget) != 1:
+ QtWidgets.QMessageBox.information(self, 'Invalid number of sets',
+ 'Baseline correction can only applied to one set at a time.')
+ return
+
+ for sid in self.current_graph_widget.active:
+ data_mode = self.management[sid].mode
+ if data_mode in ['spectrum']:
+ editor = QBaselineDialog(self)
+ editor.add_data(*self.management.get_data(sid, xy_only=True))
+ editor.finished.connect(self.management.apply)
+ editor.exec()
+
+ @QtCore.pyqtSlot(str)
+ def do_preview(self, mode):
+ if mode == 'ap':
+ dialog = QApodDialog(parent=self)
+ elif mode == 'ph':
+ dialog = QPhasedialog(parent=self)
+ else:
+ raise ValueError('Unknown preview mode %s' % str(mode))
+
+ for sid in self.current_graph_widget.active:
+ data_mode = self.management[sid].mode
+ tobeadded = False
+ if (data_mode == 'fid') or (data_mode == 'spectrum' and mode == 'ph'):
+ tobeadded = True
+
+ if tobeadded:
+ dialog.add_data(*self.management.get_data(sid, xy_only=True))
+
+ if dialog.exec() == QtWidgets.QDialog.Accepted:
+ self.management.apply(mode, dialog.get_value())
+
+ @QtCore.pyqtSlot(name='on_actionMove_between_plots_triggered')
+ def move_sets_dialog(self):
+ gnames = self.management.graphs.tree()
+ self.movedialog.setup(gnames)
+ self.movedialog.exec()
+
+ @QtCore.pyqtSlot(list, str, str)
+ def move_sets(self, sets, dest, src):
+ self.management.move_sets(sets, dest, src)
+ for s in sets:
+ self.datawidget.tree.move_sets(s, dest, src)
+
+ @QtCore.pyqtSlot(str)
+ def show_data_values(self, sid: str):
+ if sid == '':
+ return
+
+ data, mask = self.management.get_data(sid)
+ self.valuewidget.set_data(data, mask)
+
+ def plot_selected_values(self, gid: str, x: list, y: list):
+ self._values_plot.setData(x=x, y=y)
+ if gid != self.valuewidget.connected_figure:
+ self.management.graphs[self.valuewidget.connected_figure].remove_external(self._values_plot)
+ self.management.graphs[gid].add_external(self._values_plot)
+ self.valuewidget.connected_figure = gid
+
+ @QtCore.pyqtSlot(object, str)
+ def item_to_graph(self, item, graph_id):
+ self.management.graphs[graph_id].add_external(item)
+
+ @QtCore.pyqtSlot(object, str)
+ def item_from_graph(self, item, graph_id):
+ self.management.graphs[graph_id].remove_external(item)
+
+ def closeEvent(self, evt):
+ # self._write_settings()
+ self.close()
+
+ @QtCore.pyqtSlot(int)
+ def request_data(self, idx):
+ idd = self.datawidget.get_indexes(idx=idx-1)
+ try:
+ x = self.management[idd].x
+ y = self.management[idd].y
+ ret_val = (x, y)
+ except KeyError:
+ ret_val = None
+
+ self.sender().receive_data(ret_val)
+
+ return ret_val
+
+ @QtCore.pyqtSlot(tuple, bool)
+ def point_selected(self, pos, double):
+ w = self.tabWidget.currentWidget()
+ if w == self.ptsselectwidget:
+ line = self.ptsselectwidget.add(pos, double)
+ self.current_graph_widget.add_external(line)
+
+ elif w == self.t1tauwidget:
+ self.t1tauwidget.t1min_picked(pos)
+
+ def _select_fitwidget(self, onoff: bool, block_window: bool):
+ if self.current_graph_widget is not None:
+ print('select', self.current_graph_widget.id)
+ if onoff:
+ if self.management.active_sets:
+ self.fit_dialog.connected_figure = self.management.current_graph
+ self.fit_dialog.load(self.management.active_sets)
+ for item in self.fit_dialog.preview_lines:
+ self.current_graph_widget.add_external(item)
+ if self.action_custom_range.isChecked():
+ self.current_graph_widget.add_external(self.fitregion)
+
+ block_window = True
+ else:
+ for item in self.fit_dialog.preview_lines:
+ self.current_graph_widget.remove_external(item)
+ self.current_graph_widget.remove_external(self.fitregion)
+
+ return block_window
+
+ @QtCore.pyqtSlot(QtWidgets.QAction)
+ def change_fit_limits(self, action: QtWidgets.QAction):
+ if action == self.action_custom_range and self.fit_dialog.isVisible():
+ self.current_graph_widget.add_external(self.fitregion)
+ else:
+ self.current_graph_widget.remove_external(self.fitregion)
+
+ def start_fit(self, parameter, links, fit_options):
+ fit_options['limits'] = {
+ self.action_no_range: 'none',
+ self.action_x_range: 'x',
+ self.action_custom_range: self.fitregion.getRegion()
+ }[self.ac_group2.checkedAction()]
+
+ fit_options['fit_mode'] = {
+ self.action_lm_fit: 'lsq',
+ self.action_nm_fit: 'nm',
+ self.action_odr_fit: 'odr'
+ }[self.ac_group.checkedAction()]
+
+ self.fit_dialog.fit_button.setEnabled(False)
+ self.management.start_fit(parameter, links, fit_options)
+
+ @QtCore.pyqtSlot(dict, int, bool)
+ def show_fit_preview(self, funcs: dict, num: int, show: bool):
+ if self.fit_dialog.connected_figure is None:
+ return
+
+ g = self.management.graphs[self.fit_dialog.connected_figure]
+ for item in self.fit_dialog.preview_lines:
+ g.remove_external(item)
+
+ if show:
+ x_lim, _ = g.ranges
+ space = geomspace if g.log[0] else linspace
+ x = space(1.01*x_lim[0], 0.99*x_lim[1], num=num)
+
+ self.fit_dialog.make_previews(x, funcs)
+
+ for item in self.fit_dialog.preview_lines:
+ g.add_external(item)
+
+ self.raise_()
+
+ @QtCore.pyqtSlot(list)
+ def show_fit_results(self, results: list):
+ self.fit_dialog.fit_button.setEnabled(True)
+ if results:
+ res_dialog = QFitResult(results, self.management, parent=self)
+ res_dialog.add_graphs(self.management.graphs.list())
+ res_dialog.closed.connect(self.management.make_fits)
+ res_dialog.redoFit.connect(self.management.redo_fits)
+ res_dialog.show()
+
+ @QtCore.pyqtSlot(name='on_actionFunction_editor_triggered')
+ def edit_models(self):
+ if self.editor is None:
+ from ..lib.usermodeleditor import QUsermodelEditor
+
+ self.editor = QUsermodelEditor(config_paths() / 'usermodels.py', parent=self)
+ self.editor.modelsChanged.connect(self.update_fitmodels)
+ self.editor.setWindowModality(QtCore.Qt.ApplicationModal)
+ self.editor.show()
+
+ @QtCore.pyqtSlot(name='on_actionShift_triggered')
+ def shift_dialog(self):
+ s = QShift(self)
+ s.set_graphs(self.management.graphs.list())
+ for key, name in self.management.active_sets:
+ data = self.management.data[key]
+ s.add_item(key, name, data.x, data.y)
+ s.valuesChanged.connect(self.management.shift_scale)
+ s.show()
+
+ def update_fitmodels(self):
+ pass
+
+ @staticmethod
+ @QtCore.pyqtSlot(name='on_actionDocumentation_triggered')
+ def open_doc():
+ docpath = '/autohome/dominik/auswerteprogramm3/doc/_build/html/index.html'
+ import webbrowser
+ webbrowser.open(docpath)
+
+ def dropEvent(self, evt):
+ if evt.mimeData().hasUrls():
+ files = [str(url.toLocalFile()) for url in evt.mimeData().urls()]
+ self.management.load_files(files)
+
+ def dragEnterEvent(self, evt):
+ evt.accept()
+
+ @QtCore.pyqtSlot(bool, name='on_actionMouse_behaviour_toggled')
+ def change_mouse_mode(self, is_checked):
+ if is_checked:
+ self.current_plotitem.plotItem.vb.setMouseMode(ViewBox.RectMode)
+ else:
+ self.current_plotitem.plotItem.vb.setMouseMode(ViewBox.PanMode)
+
+ def mousemoved(self, xpos, ypos):
+ self.mousepos.setText('x={:.3g}; y={:.3g}'.format(xpos, ypos))
+
+ @QtCore.pyqtSlot(name='on_actionSnake_triggered')
+ @QtCore.pyqtSlot(name='on_actionTetris_triggered')
+ @QtCore.pyqtSlot(name='on_actionLife_triggered')
+ def spannung_spiel_und_spass(self):
+
+ if self.sender() == self.actionLife:
+ from ..lib.gol import QGameOfLife
+ game = QGameOfLife(parent=self)
+ game.setWindowModality(QtCore.Qt.NonModal)
+ game.show()
+
+ else:
+ from ..lib.stuff import Game
+ if self.sender() == self.actionSnake:
+ gtype = 'snake'
+ else:
+ gtype = 'tetris'
+
+ game = Game(gtype, parent=self)
+ game.show()
+
+ @QtCore.pyqtSlot(name='on_actionConfiguration_triggered')
+ def open_configuration(self):
+ from ..lib.configurations import GeneralConfiguration
+ dialog = GeneralConfiguration(self)
+ dialog.show()
+
+ def close(self):
+ write_state({'recent_path': str(self.path)})
+
+ super().close()
+
+ def read_state(self):
+ opts = read_state()
+ self.path = pathlib.Path(opts.get('recent_path', Path.home()))
diff --git a/nmreval/gui_qt/main/management.py b/nmreval/gui_qt/main/management.py
new file mode 100644
index 0000000..5968933
--- /dev/null
+++ b/nmreval/gui_qt/main/management.py
@@ -0,0 +1,1074 @@
+import pathlib
+import re
+import uuid
+from typing import List
+
+from ...fit import data as fit_d
+from ...fit.model import Model
+from ...fit.result import FitResult
+from ...fit.minimizer import FitRoutine
+from ...math.interpol import interpolate
+from ...math.logfourier import logft
+from ...math.smooth import smooth
+from ...nmr.relaxation import Relaxation
+
+from ..lib.undos import *
+from ..data.container import *
+from ..io.filereaders import QFileReader
+from ..lib.utils import busy_cursor
+
+
+class GraphSignals(QtCore.QObject):
+ valueChanged = QtCore.pyqtSignal()
+
+
+class GraphDict(OrderedDict):
+ def __init__(self, data):
+ super().__init__()
+
+ self._data = data
+ self.signals = GraphSignals()
+ self.valueChanged = self.signals.valueChanged
+
+ def __setitem__(self, key, value):
+ super().__setitem__(key, value)
+ self.valueChanged.emit()
+
+ def __delitem__(self, key):
+ super().__delitem__(key)
+ self.valueChanged.emit()
+
+ def tree(self, key_only=False):
+ ret_val = OrderedDict()
+ for k, g in self.items():
+ if key_only:
+ ret_val[k] = (g.title, [(s, self._data[s].name) for s in g.sets])
+ else:
+ ret_val[(k, g.title)] = [(s, self._data[s].name) for s in g.sets]
+
+ return ret_val
+
+ def list(self):
+ return [(k, v.title) for k, v in self.items()]
+
+ def active(self, key: str):
+ if key:
+ return [(self._data[i].id, self._data[i].name) for i in self[key]]
+ else:
+ return []
+
+ def current_sets(self, key: str):
+ if key:
+ return [(self._data[i].id, self._data[i].name) for i in self[key].sets]
+ else:
+ return []
+
+
+class UpperManagement(QtCore.QObject):
+ newGraph = QtCore.pyqtSignal()
+ restoreGraph = QtCore.pyqtSignal(str)
+ deleteGraph = QtCore.pyqtSignal(str)
+ newData = QtCore.pyqtSignal(list, str)
+ deleteData = QtCore.pyqtSignal(str)
+ dataChanged = QtCore.pyqtSignal(str)
+ fitFinished = QtCore.pyqtSignal(list)
+ stopFit = QtCore.pyqtSignal()
+ properties_collected = QtCore.pyqtSignal(dict)
+ unset_state = QtCore.pyqtSignal(list)
+
+
+ _colors = cycle(Colors)
+
+ _actions = {
+ 'ls': (ShiftCommand, 'Left shift'),
+ 'cut': (CutCommand, 'Cut'),
+ 'ap': (ApodizationCommand, 'Apodization'),
+ 'zf': (ZerofillCommand, 'Zerofill'),
+ 'ph': (PhaseCommand, 'Phase'),
+ 'bl': (BaselineCommand, 'Baseline'),
+ 'bls': (BaselineSplineCommand, 'Baseline'),
+ 'ft': (FourierCommand, 'Fourier'),
+ 'ft_pake': 'FT (de-paked)',
+ 'sort': (SortCommand, 'Sort'),
+ 'norm': (NormCommand, 'Normalize'),
+ 'center': (CenterCommand, 'Center on max')
+ }
+
+ def __init__(self, window):
+ super().__init__()
+
+ self._fit_active = False
+ self.fit_thread = None
+ self.fit_worker = None
+
+ self.counter = 0
+ self.data = OrderedDict()
+ self.window = window
+ self.current_graph = ''
+ self.graphs = GraphDict(self.data)
+ self.namespace = None
+ self.undostack = QtWidgets.QUndoStack()
+ self.deleteData.connect(self.plot_from_graph)
+
+ def __setitem__(self, key: str, value, **kwargs):
+ if isinstance(value, ExperimentContainer):
+ item = value
+ item.id = key
+ elif isinstance(value, FitResult):
+ item = FitContainer(key, value, manager=self, **kwargs)
+ elif isinstance(value, Signal):
+ item = SignalContainer(key, value, manager=self, **kwargs)
+ else:
+ item = PointContainer(key, value, manager=self, **kwargs)
+
+ item.dataChanged.connect(lambda x: self.dataChanged.emit(x))
+
+ self.data[key] = item
+
+ def __getitem__(self, item):
+ return self.data[item]
+
+ def __contains__(self, item):
+ return item in self.data
+
+ def __iter__(self):
+ for k, v in self.data.items():
+ yield k, v
+
+ @property
+ def active_sets(self):
+ return self.graphs.active(self.current_graph)
+
+ def add(self, data, **kwargs):
+ _id = str(uuid.uuid4())
+ self.__setitem__(_id, data, **kwargs)
+
+ return _id
+
+ def load_files(self, fname: List[str], new_plot: str = None):
+ ret_dic = QFileReader(manager=self).readfiles(fname)
+ self.add_new_data(ret_dic, new_plot)
+
+ def _load_session(self, sets: dict, graphs: dict):
+ sid = self._load_sets(sets)
+
+ for g in graphs:
+ _ = g.pop('id')
+ graph = QGraphWindow.set_state(g)
+ self.graphs[graph.id] = graph
+ self.restoreGraph.emit(graph.id)
+
+ children = [sid[c] for c in g['children']]
+ active = [sid[c] for c in g['active']]
+ inactive = [k for k in children if k not in active]
+
+ self.newData.emit(children, graph.id)
+
+ graph.active = active
+ graph.listWidget.blockSignals(True)
+ for i, l in enumerate(g['in_legend']):
+ graph.listWidget.item(i).setCheckState(l)
+ graph.listWidget.blockSignals(False)
+
+ # set unchecked in tree and hide/show in plot
+ self.unset_state.emit(inactive)
+ self.change_visibility(active, inactive)
+
+ def _load_sets(self, sets: dict) -> dict:
+ sid = {}
+ for _id, (data, opts) in sets.items():
+ if isinstance(data, FitResult):
+ # for fits, _id belongs to the fitted data, not the fit
+ src_id = data.idx
+ if src_id in sid:
+ new_id = self.add(data, src=sid[src_id])
+ self.data[sid[src_id]]._fits.append(new_id)
+ else:
+ new_id = self.add(data)
+ else:
+ new_id = self.add(data)
+
+ sid[_id] = new_id
+
+ for m in ['real', 'imag']:
+ if m in opts:
+ self.data[new_id].setSymbol(**opts[m][0], mode=m)
+ self.data[new_id].setLine(**opts[m][1], mode=m)
+
+ return sid
+
+ def add_new_data(self, data: list, gid: str):
+ sid = []
+ for d in data:
+ if isinstance(d, tuple):
+ if len(d) == 2:
+ self._load_session(d[0], graphs=d[1])
+ else:
+ sid.extend(list(self._load_sets(d[0]).values()))
+ else:
+ sid.append(self.add(d))
+
+ if sid:
+ gid = '' if not gid else gid
+
+ self.newData.emit(sid, gid)
+
+ def plots_to_graph(self, plotkeys: list, gid: str):
+ self.graphs[gid].add(plotkeys, [self.data[k].plots for k in plotkeys])
+ for k in plotkeys:
+ self.data[k].graph = gid
+
+ @QtCore.pyqtSlot(str)
+ def plot_from_graph(self, key: str):
+ self.graphs[self.data[key].graph].remove(key)
+
+ @QtCore.pyqtSlot(list, str, str)
+ def move_sets(self, sets: list, dest: str, src, pos: int = -1):
+ if isinstance(src, str):
+ src = [src]*len(sets)
+
+ for graph_id, set_id in zip(src, sets):
+ # move all plots to the same graph
+ if graph_id != dest:
+ self.graphs[graph_id].remove(set_id)
+ self.plots_to_graph([set_id], dest)
+
+ # move to correct position
+ self.graphs[dest].move_sets(sets, pos)
+
+ @QtCore.pyqtSlot()
+ @QtCore.pyqtSlot(list, str)
+ def copy_sets(self, sets: list = None, src: str = None):
+ if sets is None:
+ sets = self.graphs[self.current_graph].active[:]
+
+ if src is None:
+ src = self.current_graph
+
+ new_ids = []
+ for s in sets:
+ copy_of_s = self.data[s].copy(full=True)
+ copy_of_s.id = str(uuid.uuid4())
+ new_ids.append(copy_of_s.id)
+ self.data[copy_of_s.id] = copy_of_s
+
+ self.newData.emit(new_ids, src)
+
+ return new_ids
+
+ @QtCore.pyqtSlot(list)
+ @QtCore.pyqtSlot(str)
+ def delete_sets(self, rm_sets: list = None):
+ rm_graphs = []
+ print(rm_sets)
+
+ if rm_sets is None:
+ rm_sets = self.graphs[self.current_graph].sets + [self.current_graph]
+
+ self.undostack.beginMacro('Delete')
+
+ for k in rm_sets[::-1]:
+ if k in self.data:
+ cmd = DeleteCommand(self.data, k, self.newData, self.deleteData)
+ self.undostack.push(cmd)
+ else:
+ rm_graphs.append(k)
+
+ for k in rm_graphs:
+ cmd = DeleteGraphCommand(self.graphs, k, self.restoreGraph, self.deleteGraph)
+ self.undostack.push(cmd)
+
+ self.undostack.endMacro()
+
+ @QtCore.pyqtSlot()
+ def cat(self, src_sets=None):
+ joined = None
+ group_set = set()
+ name_set = set()
+ value_set = set()
+
+ if src_sets is None:
+ src_sets = self.graphs[self.current_graph].active
+ for sid in src_sets:
+ data_i = self.data[sid]
+ if joined is None:
+ joined = data_i.copy()
+ else:
+ joined.append(data_i.x, data_i.y, data_i.y_err)
+
+ name_set.add(data_i.name)
+ group_set.add(data_i.group)
+ value_set.add(data_i.value)
+
+ if joined is not None:
+ joined.group = '/'.join(group_set)
+ joined.name = '/'.join(name_set)
+
+ if len(value_set) == 1:
+ joined.value = value_set.pop()
+ else:
+ joined.value = 0.0
+
+ self.newData.emit([self.add(joined)], self.current_graph)
+
+ def get_data(self, sid: str, xy_only=False):
+ """
+ Return data for a given id.
+ Return value is tuple of [x, y, y_err] and mask if xy_only is False, [x, y] if true.
+ """
+ d = self.data[sid]
+ if xy_only:
+ return [d.x, d.y]
+
+ return [d.data.x, d.data.y, d.data.y_err], d.data.mask.data
+
+ def change_visibility(self, selected: list, deselected: list):
+ """Change status of list of ids after status change in datawidget"""
+
+ for s in selected:
+ self.graphs[self.data[s].graph].show_item([s])
+
+ for d in deselected:
+ self.graphs[self.data[d].graph].hide_item([d])
+
+ @QtCore.pyqtSlot(str, str)
+ def change_keys(self, identifier: str, name: str):
+ if identifier in self.data:
+ d = self.data[identifier]
+ d.name = name
+ self.graphs[d.graph].update_legend(identifier, name)
+ elif identifier in self.graphs:
+ self.graphs[identifier].title = name
+ else:
+ raise KeyError('Unknown ID ' + str(identifier))
+
+ @QtCore.pyqtSlot(str, tuple)
+ def apply(self, func: str, arguments: tuple):
+ # undos, names displayed by undo action
+ cmd, cmd_text = self._actions[func]
+
+ self.undostack.beginMacro(cmd_text)
+ for sid in self.graphs[self.current_graph]:
+ single_undo = cmd(self.data[sid], *arguments)
+ self.undostack.push(single_undo)
+ self.undostack.endMacro()
+
+ def cut(self):
+ xlim, _ = self.graphs[self.current_graph].ranges
+ self.apply('cut', xlim)
+
+ @QtCore.pyqtSlot()
+ def unmask(self):
+ for d in self.data.values():
+ d.mask = np.ones_like(d.mask, dtype=bool)
+
+ def start_fit(self, parameter: dict, links: list, fit_options: dict):
+ if self._fit_active:
+ return
+
+ self.__fit_options = (parameter, links, fit_options)
+
+ fitter = FitRoutine()
+ models = {}
+ fit_limits = fit_options['limits']
+ fit_mode = fit_options['fit_mode']
+ we = fit_options['we']
+
+ for model_id, model_p in parameter.items():
+ m = Model(model_p['func'])
+ models[model_id] = m
+
+ m_complex = model_p['complex']
+ m.set_complex(m_complex)
+
+ for set_id, set_params in model_p['parameter'].items():
+ data_i = self.data[set_id]
+ if we == 'Deltay':
+ we = data_i.y_err**2
+
+ if m_complex is None or m_complex == 'real':
+ _y = data_i.y.real
+ elif m_complex == 'imag' and np.iscomplexobj(self.data[set_id].y):
+ _y = data_i.y.imag
+ else:
+ _y = data_i.y
+
+ _x = data_i.x
+
+ if fit_limits == 'none':
+ inside = slice(None)
+ elif fit_limits == 'x':
+ x_lim, _ = self.graphs[self.current_graph].ranges
+ inside = np.where((_x >= x_lim[0]) & (_x <= x_lim[1]))
+ else:
+ inside = np.where((_x >= fit_limits[0]) & (_x <= fit_limits[1]))
+
+ if isinstance(we, str):
+ d = fit_d.Data(_x[inside], _y[inside], we=we, idx=set_id)
+ else:
+ d = fit_d.Data(_x[inside], _y[inside], we=we[inside], idx=set_id)
+
+ d.set_model(m)
+ d.set_parameter(set_params[0], var=model_p['var'],
+ lb=model_p['lb'], ub=model_p['ub'],
+ fun_kwargs=set_params[1])
+
+ fitter.add_data(d)
+
+ model_globs = model_p['glob']
+ if model_globs:
+ m.set_global_parameter(**model_p['glob'])
+
+ for links_i in links:
+ fitter.set_link_parameter((models[links_i[0]], links_i[1]),
+ (models[links_i[2]], links_i[3]))
+
+ with busy_cursor():
+ self.fit_worker = FitWorker(fitter, fit_mode)
+ self.fit_thread = QtCore.QThread()
+ self.fit_worker.moveToThread(self.fit_thread)
+
+ self.fit_thread.started.connect(self.fit_worker.run)
+ self.fit_worker.finished.connect(self.end_fit)
+ self.fit_worker.finished.connect(self.fit_thread.quit)
+ self.fit_worker.finished.connect(self.fit_worker.deleteLater)
+ self.fit_thread.finished.connect(self.fit_thread.deleteLater)
+
+ self.stopFit.connect(lambda: self.fit_worker.fitter.abort())
+
+ self.fit_thread.start()
+
+ @QtCore.pyqtSlot(list, bool)
+ def end_fit(self, result: list, success: bool):
+ print('FIT FINISHED')
+ if success:
+ self.fitFinished.emit(result)
+ else:
+ QtWidgets.QMessageBox.warning(QtWidgets.QWidget(), 'Fit failed',
+ 'Fit kaput with exception: \n' + "\n".join(result[0]))
+ self.fitFinished.emit([])
+ self._fit_active = False
+
+ @QtCore.pyqtSlot(dict)
+ def redo_fits(self, res: dict):
+ models = self.__fit_options[0]
+ for single_model, model_args in models.items():
+ parameter = model_args['parameter']
+
+ for set_id, set_parameter in parameter.items():
+ new_values = [v.value for v in res[set_id].parameter.values()]
+ parameter[set_id] = (new_values, set_parameter[1])
+ self.start_fit(*self.__fit_options)
+
+ @QtCore.pyqtSlot(dict, list)
+ def make_fits(self, res: dict, opts: list):
+ f_id_list = []
+ gid = ''
+
+ subplots = opts.pop(-1)
+ param_graph = opts.pop(-1)
+ tobedeleted = []
+ for i, (k, fit) in enumerate(res.items()):
+ reject, delete_prev = opts[i]
+ if reject:
+ continue
+
+ data_k = self.data[k]
+ if delete_prev:
+ tobedeleted.extend([f.id for f in data_k.get_fits()])
+ data_k.set_fits([])
+
+ syms = data_k.plot_real.symbol
+ if syms == SymbolStyle.No:
+ color = data_k.plot_real.linecolor
+ else:
+ color = data_k.plot_real.symbolcolor
+
+ fit.value = data_k.value
+ fit.group = data_k.group
+
+ f_id = self.add(fit, color=color, src=k)
+
+ if subplots:
+ print('subplots')
+
+ f_id_list.append(f_id)
+ data_k.set_fits(f_id)
+ gid = data_k.graph
+
+ self.delete_sets(tobedeleted)
+
+ if f_id_list:
+ self.newData.emit(f_id_list, gid)
+ self.make_fit_parameter(f_id_list, graph_id=param_graph)
+
+ def make_fit_parameter(self, fit_sets: List[str], graph_id: str = None):
+ fit_dict = self._collect_fit_parameter(fit_sets)
+
+ if fit_dict:
+ p_id_list = []
+ for v in fit_dict.values():
+ xy = np.array(v[0]).T
+ p_id_list.append(self.add(Points(x=xy[0], y=xy[1], y_err=xy[2], name=v[1])))
+
+ if not graph_id:
+ graph_id = ''
+
+ self.newData.emit(p_id_list, graph_id)
+
+ def save_fit_parameter(self, fname: str, fit_sets: List[str] = None):
+ if fit_sets is None:
+ fit_sets = [s for (s, _) in self.active_sets]
+
+ for set_id in fit_sets:
+ data = self.data[set_id]
+ if data.mode != 'fit':
+ continue
+
+ data.data.save_parameter(fname)
+
+ def _collect_fit_parameter(self, fit_sets: List[str]) -> dict:
+ fit_dict = {}
+
+ for set_id in fit_sets:
+ data = self.data[set_id]
+ if data.mode != 'fit':
+ continue
+
+ for key, pvalue in data.parameter.items():
+ name = pvalue.full_name
+ fit_key = key + data.model_name
+
+ if fit_key not in fit_dict:
+ fit_dict[fit_key] = [[], name]
+
+ err = 0 if pvalue.error is None else pvalue.error
+
+ fit_dict[fit_key][0].append([data.value, pvalue.value, err])
+
+ return fit_dict
+
+ @QtCore.pyqtSlot(dict, str)
+ def extract_points(self, params: dict, gid: str):
+ xy_mode = params.pop('xy')
+ _active = self.graphs[self.current_graph].active
+
+ new_datasets = {}
+ for sid in _active:
+ data_i = self.data[sid]
+ if data_i.group not in new_datasets:
+ new_datasets[data_i.group] = [], []
+ new_x_axis, _temp = new_datasets[data_i.group]
+
+ new_x_axis.append(data_i.value)
+ _temp.append(data_i.points(params))
+
+ key_list = []
+ for label, (new_x_axis, _temp) in new_datasets.items():
+ _temp = np.array(_temp) # (number of sets, number of picks, (x, y, y_err))
+ num_pts = _temp.shape[1]
+
+ for i in range(num_pts):
+ if xy_mode[0]:
+ key = self.add(Points(x=new_x_axis, y=_temp[:, i, 0], name=label))
+ key_list.append(key)
+
+ if xy_mode[1]:
+ key = self.add(Points(x=new_x_axis, y=_temp[:, i, 1], y_err=_temp[:, i, 2], name=label))
+ key_list.append(key)
+
+ self.newData.emit(key_list, gid)
+
+ @QtCore.pyqtSlot(list)
+ def get_properties(self, sid: list) -> dict:
+ props = {}
+ for key in sid:
+ if key not in self.data:
+ continue
+
+ props = self.data[key].get_properties()
+
+ self.properties_collected.emit(props)
+
+ return props
+
+ @QtCore.pyqtSlot(list, str, str, object)
+ def update_property(self, sid: list, key1: str, key2: str, value: Any):
+ for s in sid:
+ self.data[s].update_property(key1, key2, value)
+
+ def create_empty(self):
+ import numpy.random as random
+ dat = Points(x=np.arange(10), y=np.arange(10) + random.rand(10)-0.5, y_err=random.rand(10),
+ name='Das Sein und das Nichts')
+ idd = self.add(dat)
+ self.newData.emit([idd], self.current_graph)
+
+ @QtCore.pyqtSlot(tuple, dict, str)
+ def calc_mean(self, dist_params, conversion, graph):
+ dist, args = dist_params
+ parameter = []
+
+ x = None
+ name = 'tau (%s)' % {conversion["to_"]}
+ value = 0.
+ for i, p in enumerate(args):
+ if isinstance(p, float):
+ parameter.append(p)
+ else:
+ if x is None:
+ x = self.data[p].x
+ if i == 0:
+ name = self.data[p].name
+ value = self.data[p].value
+ parameter.append(self.data[p].y)
+
+ if x is None:
+ x = 0
+
+ key = self.add(Points(x, dist.convert(*parameter, **conversion), name=name, value=value))
+ self.newData.emit([key], graph)
+ self.sender().update_graphs(self.graphs.tree(key_only=True))
+
+ @QtCore.pyqtSlot(list, str, bool, bool, tuple, str)
+ def interpolate_data(self, data_ids, mode, xlog, ylog, new_axis, dest_graph):
+ if len(new_axis) == 4:
+ start, end, steps, loggy = new_axis
+ if loggy:
+ new_x = np.logspace(np.log10(start), np.log10(end), steps)
+ else:
+ new_x = np.linspace(start, end, steps)
+ else:
+ new_x = self.data[new_axis[0]].x
+
+ new_key = []
+ for ids in data_ids:
+ k = self.add(interpolate(self.data[ids], new_x, xlog=xlog, ylog=ylog, kind=mode, extrapolate=True))
+ new_key.append(k)
+
+ self.newData.emit(new_key, dest_graph)
+
+ @QtCore.pyqtSlot(int, dict)
+ def smooth_data(self, npoints, param_kwargs):
+ _active = self.graphs[self.current_graph].active
+ new_data = []
+ for sid in _active:
+ try:
+ key = self.add(smooth(self.data[sid], npoints, **param_kwargs))
+ new_data.append(key)
+ except Exception as e:
+ QtWidgets.QMessageBox().warning(self.window,
+ 'Smoothing failed!',
+ f'Smoothing failed for {self.data[sid].name} with exception:\n{e.args}')
+ if new_data:
+ self.newData.emit(new_data, self.current_graph)
+
+ @QtCore.pyqtSlot()
+ def update_color(self):
+ UpperManagement._colors = cycle(Colors)
+ for i in self.active:
+ self.data[i].color = next(UpperManagement._colors)
+
+ @QtCore.pyqtSlot(dict, tuple)
+ def shift_scale(self, values: dict, options: tuple):
+ copy_data, value_plot = options
+
+ sid_list = []
+ shift_y = []
+ shift_x = []
+ for k, v in values.items():
+ d_k = self.data[k]
+
+ if copy_data is None:
+ d_k.x = d_k.x*v[1][0] + v[0][0]
+ d_k.y = d_k.y*v[1][1] + v[0][1]
+ else:
+ new_data = d_k.copy(full=True)
+ new_data.update({'shift': v[0], 'scale': 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_list.append(sid)
+
+ shift_x.append(d_k.value)
+ shift_y.append(v[0]+v[1])
+
+ self.newData.emit(sid_list, copy_data)
+
+ if value_plot is not None:
+ sid_list = []
+ shift_y = np.array(shift_y)
+ for i, (mode, default) in enumerate([('x shift', 0.), ('y shift', 0.),
+ ('x scale', 1.), ('y scale', 1.), ]):
+ if np.all(shift_y[:, i] == default):
+ continue
+ data = Points(shift_x, shift_y[:, i], name=mode)
+ sid_list.append(self.add(data))
+
+ self.newData.emit(sid_list, value_plot)
+
+ @QtCore.pyqtSlot(list)
+ def convert_sets(self, src: list):
+ new_graph = {}
+
+ error_list = []
+
+ for sets in src:
+ # merge: sets (real, imag, graph, type)
+ # normal: sets (source set, graph, type)
+
+ graph_id = sets[-2]
+ new_type = [Points, FID, Spectrum, BDS][sets[-1]]
+
+ if len(sets) == 4:
+ real_set, imag_set = sets[0], sets[1]
+ if real_set != '':
+ data = self.data[real_set]
+ new_data = new_type(data.x, data.y.real)
+ if imag_set != '':
+ imag_data = self.data[imag_set]
+ if len(imag_data) == len(data):
+ new_data.y.imag = imag_data.y.real
+ else:
+ error_list.append(f'Lengths mismatch of {data.name} ({len(data)}) and {imag_data.name} ({len(imag_data)})')
+ continue
+
+ else:
+ data = self.data[imag_set]
+ new_data = new_type(data.x, np.zeros(data.x.size))
+ new_data.y.imag = data.y.real
+
+ else:
+ data = self.data[sets[0]]
+ if isinstance(data.data, new_type):
+ error_list.append(f'{data.name} is alreade of type {new_type.__name__}')
+ continue
+
+ new_data = new_type(data.x, np.zeros(data.x.size))
+ new_data.y.real = data.y.real
+
+ new_data.update(data.opts)
+ new_id = self.add(data.change_type(new_data))
+ if graph_id not in new_graph:
+ new_graph[graph_id] = []
+
+ new_graph[graph_id].append(new_id)
+
+ for g, s in new_graph.items():
+ self.newData.emit(s, g)
+
+ if error_list:
+ err_string = "\n- ".join(error_list)
+ _ = QtWidgets.QMessageBox.information(QtWidgets.QWidget(), 'Something was skipped',
+ f'Some conversions were skipped:\n{err_string}')
+
+ def get_namespace(self):
+ from ..lib.namespace import Namespace
+ self.namespace = Namespace(basic=True, const=True, fitfuncs=True)
+
+ for i, g in enumerate(self.graphs.values()):
+ for j, sid in enumerate(g.sets):
+ sets = self.data[sid]
+ self.namespace.add_namespace(sets.get_namespace(i, j), parents=('Data', f'{sets.name} ({g.title})'))
+
+ return self.namespace
+
+ @QtCore.pyqtSlot(list, list, bool)
+ def eval_expression(self, cmds: list, set_ids: list, overwrite: bool):
+ ns = self.namespace.flatten()
+
+ if overwrite:
+ self.undostack.beginMacro('Evaluate expression')
+
+ failures = []
+ for sid in set_ids:
+ data_i = self.data[sid]
+ try:
+ # use a copy of original namespace
+ new_data = data_i.eval_expression(cmds, dict(ns))
+ if overwrite:
+ cmd = EvalCommand(self.data, sid, new_data, 'Evaluate expression')
+ self.undostack.push(cmd)
+ else:
+ new_id = self.copy_sets(sets=[sid])
+ self.data[new_id[0]].data = new_data
+ except Exception as e:
+ failures.append((data_i, e))
+ print(str(data_i) + ' failed with Exception: ' + ''.join(e.args))
+ continue
+
+ if overwrite:
+ self.undostack.endMacro()
+
+ if failures:
+ err_msg = QtWidgets.QMessageBox(parent=self.sender())
+ err_msg.setText('One or more errors occured during evaluation.')
+ err_msg.setDetailedText('\n'.join(f'{d.name} failed with error: {err.args}' for d, err in failures))
+ err_msg.exec()
+
+ self.sender().success = not failures
+
+ self.sender().add_data(self.active_sets)
+
+ @QtCore.pyqtSlot(list, dict)
+ def create_from_function(self, cmds: list, opts: dict):
+ ns = dict(self.namespace.flatten())
+
+ dtype = [Points, FID, Spectrum, BDS][opts.pop('dtype')]
+
+ try:
+ for c in cmds:
+ exec(c, globals(), ns)
+
+ name = opts.pop('name')
+ value = opts.pop('val')
+ graph = opts.pop('graph')
+
+ data = dtype(x=ns['x'], y=ns['y'], y_err=ns['y_err'], name=name, value=value)
+ s_id = self.add(data, **opts)
+ self.sender().success = True
+ self.newData.emit([s_id], graph)
+
+ except Exception as err:
+ print('Creation failed with error: ' + ', '.join(err.args))
+ err_msg = QtWidgets.QMessageBox(parent=self.sender())
+ err_msg.setText('One or more errors occured during evaluation.')
+ err_msg.setDetailedText('Creation failed with error: ' + ', '.join(err.args))
+ err_msg.exec()
+
+ self.sender().success = False
+
+ def show_statistics(self, mode):
+ x, y, = [], []
+
+ for i, _ in self.active_sets:
+ _temp = self.data[i]
+ try:
+ x.append(float(_temp.name))
+ except ValueError:
+ x.append(i)
+ y.append(_temp.statistic(mode))
+
+ @QtCore.pyqtSlot()
+ def calc_magn(self):
+ new_id = []
+ for k, _ in self.active_sets:
+ dataset = self.data[k]
+ if isinstance(dataset, SignalContainer):
+ new_value = dataset.copy(full=True)
+ new_value.data = dataset.data.magnitude()
+ new_id.append(self.add(new_value))
+
+ self.newData.emit(new_id, '')
+
+ @QtCore.pyqtSlot()
+ def center(self):
+ new_id = []
+ for k, _ in self.active_sets:
+ new_value = self.data[k].copy(full=True)
+ new_value.x -= new_value.x[np.argmax(new_value.y.real)]
+ new_id.append(self.add(new_value))
+
+ self.newData.emit(new_id, '')
+
+ def integrate(self, **kwargs):
+ new_sets = []
+ log = kwargs['log']
+ limits = kwargs.get('limits')
+ mode = kwargs['mode']
+
+ for set_id in kwargs['sets']:
+ data_i = self.data[set_id]
+ if mode == 'i':
+ new_data = data_i.data.integrate(log=log, limits=limits)
+ elif mode == 'd':
+ new_data = data_i.data.diff(log=log)
+ else:
+ raise ValueError(f'Unknown mode {mode}.')
+
+ new_container = data_i.copy(full=True)
+ new_container.data = new_data
+
+ new_sets.append(self.add(new_container))
+
+ self.newData.emit(new_sets, kwargs['graph'])
+
+ def bds_deriv(self):
+ new_sets = []
+
+ for (set_id, _) in self.active_sets:
+ data_i = self.data[set_id]
+ diff = data_i.data.diff(log=True)
+ new_data = Points(x=diff.x, y=-np.pi/2*diff.y.real)
+ new_data.update(data_i.data.meta)
+
+ new_sets.append(self.add(new_data, color=data_i.plot_imag.linecolor))
+
+ self.newData.emit(new_sets, '')
+
+ def logft(self, **kwargs):
+ new_sets = []
+ ft_mode = kwargs['ft_mode']
+
+ for set_id in kwargs['sets']:
+ data_i = self.data[set_id]
+ if ft_mode in ['cos', 'sin']:
+ new_data = Points(*logft(data_i.x, data_i.y, mode=ft_mode))
+ else:
+ new_data = Signal(*logft(data_i.x, data_i.y, mode=ft_mode))
+
+ 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.newData.emit(new_sets, kwargs['graph'])
+
+ def skip_points(self, offset: int, step: int, invert: bool = False, copy: bool = False):
+ for k, _ in self.active_sets:
+ src = self.data[k]
+ if invert:
+ mask = np.mod(np.arange(offset, src.x.size+offset), step) != 0
+ else:
+ mask = np.mod(np.arange(offset, src.x.size+offset), step) == 0
+
+ if copy:
+ data = src.copy()
+ temp = data.mask.copy()
+ temp[temp] = mask
+
+ data.remove(np.where(~temp))
+ data.mask = np.ones(data.x.shape)
+
+ idd = self.add(data)
+ self.newData.emit([idd], self.current_graph)
+ else:
+ src.mask[src.mask] = mask
+ src.mask = src.mask
+
+ @QtCore.pyqtSlot(dict)
+ def calc_relaxation(self, opts: dict):
+ params = opts['pts']
+ if len(params) == 4:
+ if params[3]:
+ _x = x1 = np.geomspace(params[0], params[1], num=params[2])
+ else:
+ _x = x1 = np.linspace(params[0], params[1], num=params[2])
+
+ if opts['axis1'] in ['t', 'invt1000']:
+ t_p = opts['t_param']
+ if len(t_p) == 2:
+ from ...models import Arrhenius as Func
+ else:
+ from ...models import VFT as Func
+
+ _x = Func.func(x1, *t_p, invt=opts['axis1'])
+
+ else:
+ if params[1]:
+ x1 = self.data[params[0]].x
+ _x = self.data[params[0]].y.real
+ else:
+ _x = x1 = self.data[params[0]].x
+
+ x2 = opts['val2']
+
+ 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.convert(_x, *sd_param, from_=opts['tau_type'], to_='raw')
+
+ relax = Relaxation()
+ relax.distribution(sd, parameter=sd_param, 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]]
+ relax.coupling(opts['coup'], parameter=cp_param, keywords=opts['cp_param'][1])
+
+ if opts['out'] == 't1':
+ y = relax.t1(x2, _x)
+ else:
+ y = relax.t2(x2, _x)
+
+ pts = Points(x1, y, name=sd.name)
+ pts.meta.update(opts)
+
+ # we do not want class instances
+ pts.meta['coup'] = opts['coup'].name
+ pts.meta['spec_dens'] = sd.name
+
+ self.newData.emit([self.add(pts)], opts['graph'])
+ self.sender().update_graphs(self.graphs.list())
+
+ def mask_value(self, idx: str, m: list):
+ self.data[idx].mask = m
+
+ def remove_values(self, idx: str, m: list):
+ self.data[idx].remove(m)
+
+ def set_values(self, idx: str, pos: tuple, value):
+ self.data[idx].setvalues(pos, value)
+
+ def append(self, idx: str):
+ self.data[idx].add([0.0, 0.0, 0.0])
+
+ def save(self, outpath: str, extension: str, strip_spaces=False):
+ path = pathlib.Path(outpath)
+ suffix = path.suffix
+
+ if not suffix:
+ m = re.match(r'[\w\s]*\(\*(\.\w+)\)', extension)
+ if m:
+ suffix = m.group(1)
+ path = path.with_suffix(suffix)
+ else:
+ raise ValueError('No file extension detected')
+
+ if suffix == '.nmr':
+ from ...io.sessionwriter import NMRWriter
+ NMRWriter(self.graphs, self.data).export(path)
+
+ return
+
+ real_outnames = []
+ for set_id, set_name in self.active_sets:
+ full_name = path.stem
+ if '' in outpath:
+ full_name = full_name.replace('', convert(set_name, old='tex', new='str'))
+
+ data_i = self.data[set_id]
+
+ if isinstance(data_i, FitContainer):
+ full_name += ' fit(' + self.data[data_i.parent_set].name + ')'
+
+ if strip_spaces:
+ full_name = full_name.replace(' ', '_')
+
+ if full_name in real_outnames:
+ cnt = 1
+ while (full_name + '_' + str(cnt)) in real_outnames:
+ cnt += 1
+ real_outnames.append(full_name + '_' + str(cnt))
+ else:
+ real_outnames.append(full_name)
+
+ outpath_set = path.with_name(real_outnames[-1]+path.suffix)
+ data_i.save(outpath_set)
+
+
+class FitWorker(QtCore.QObject):
+ finished = QtCore.pyqtSignal(list, bool)
+
+ def __init__(self, fitter, mode):
+ super().__init__()
+
+ self.fitter = fitter
+ self.mode = mode
+
+ @QtCore.pyqtSlot()
+ def run(self):
+ try:
+ res = self.fitter.run(mode=self.mode)
+ success = True
+ except Exception as e:
+ res = [e.args]
+ success = False
+ self.finished.emit(res, success)
+
diff --git a/nmreval/gui_qt/math/bootstrap.py b/nmreval/gui_qt/math/bootstrap.py
new file mode 100644
index 0000000..8017527
--- /dev/null
+++ b/nmreval/gui_qt/math/bootstrap.py
@@ -0,0 +1,3 @@
+from numpy import random
+
+
diff --git a/nmreval/gui_qt/math/evaluation.py b/nmreval/gui_qt/math/evaluation.py
new file mode 100644
index 0000000..3031dff
--- /dev/null
+++ b/nmreval/gui_qt/math/evaluation.py
@@ -0,0 +1,120 @@
+from ..Qt import QtCore, QtWidgets, QtGui
+from .._py.eval_expr_dialog import Ui_CalcDialog
+from ..lib.namespace import Namespace
+
+
+class QEvalDialog(QtWidgets.QDialog, Ui_CalcDialog):
+ do_eval = QtCore.pyqtSignal(list, list, bool)
+ do_calc = QtCore.pyqtSignal(list, dict)
+
+ def __init__(self, mode='c', parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+ self.namespace = None
+ self.success = True
+
+ self.set_mode(mode)
+
+ self.value_lineEdit.setValidator(QtGui.QDoubleValidator())
+
+ def set_mode(self, mode: str):
+ if mode not in ['c', 'e']:
+ raise ValueError(f'Mode should be "c" or "e", {mode} given')
+
+ if mode == 'c':
+ self.stackedWidget.setCurrentIndex(1)
+ else:
+ self.stackedWidget.setCurrentIndex(0)
+ self.mode = mode
+
+ def set_namespace(self, namespace):
+ self.namespace = namespace
+ self.namespace_widget.set_namespace(self.namespace)
+
+ def add_data(self, data):
+ self.listWidget.clear()
+ for set_id, name in data:
+ item = QtWidgets.QListWidgetItem(name)
+ item.setData(QtCore.Qt.UserRole, set_id)
+ item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
+ item.setCheckState(QtCore.Qt.Checked)
+ self.listWidget.addItem(item)
+
+ def set_graphs(self, graphs: list):
+ self.graph_comboBox.clear()
+ self.graph_comboBox.addItem('New graph', userData='')
+ for g_id, g_name in graphs:
+ self.graph_comboBox.addItem(g_name, userData=g_id)
+
+ @QtCore.pyqtSlot(str, name='on_namespace_widget_selected')
+ def add_to_equation(self, string: str):
+ self.calc_edit.insertPlainText(string + ' ')
+
+ def on_buttonBox_clicked(self, bttn):
+ bttn_role = self.buttonBox.buttonRole(bttn)
+ if bttn_role == QtWidgets.QDialogButtonBox.ApplyRole:
+ self.collect_args()
+
+ elif bttn_role == QtWidgets.QDialogButtonBox.AcceptRole:
+ self.collect_args()
+ if self.success:
+ self.accept()
+
+ else:
+ self.reject()
+
+ def collect_args(self):
+ cmds = self.calc_edit.toPlainText().strip().split('\n')
+ if cmds[-1] == '':
+ # remove last empty line to check
+ cmds.pop()
+
+ if cmds:
+ if self.mode == 'e':
+ set_id = []
+ for i in range(self.listWidget.count()):
+ item = self.listWidget.item(i)
+ if item.checkState() == QtCore.Qt.Checked:
+ set_id.append(item.data(QtCore.Qt.UserRole))
+
+ self.do_eval.emit(cmds, set_id, self.overwrite_checkbox.isChecked())
+
+ elif self.mode == 'c':
+ opts = {
+ 'symbol': self.symbol_comboBox.value,
+ 'symbolcolor': self.symcolor_comboBox.value,
+ 'symbolsize': self.symbol_spinBox.value(),
+
+ 'linestyle': self.linestyle_comboBox.value,
+ 'linecolor': self.linecolor_comboBox.value,
+ 'linewidth': self.line_doubleSpinBox.value(),
+
+ 'graph': self.graph_comboBox.currentData(),
+ 'dtype': self.dtype_comboBox.currentIndex(),
+ 'name': self.label_lineEdit.text(),
+ 'val': self.value_lineEdit.text()
+ }
+
+ self.do_calc.emit(cmds, opts)
+
+ else:
+ # keine Ahnung, warum man hier landen sollte ...
+ self.success = False
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QtWidgets.QApplication([])
+ names = Namespace(basic=True)
+ names.add_namespace({'g[0].s[0].x': (1,), 'g[0].s[0].y': (4,)}, parents=('Data', 'S0 (G0)'))
+ names.add_namespace({'g[0].s[1].x': (2,), 'g[0].s[1].y': (5,)}, parents=('Data', 'S1 (G0)'))
+ names.add_namespace({'g[1].s[2].x': (3,), 'g[1].s[2].y': (6,),
+ "g[1].s[2].fit['C']": (7,), "g[1].s[2].fit['D_0']": (8,)},
+ parents=('Data', 'S0 (G1)'))
+
+ dialog = QEvalDialog(mode='c')
+ dialog.set_namespace(names)
+ dialog.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/math/integrate_derive.py b/nmreval/gui_qt/math/integrate_derive.py
new file mode 100644
index 0000000..0980b26
--- /dev/null
+++ b/nmreval/gui_qt/math/integrate_derive.py
@@ -0,0 +1,81 @@
+from ..Qt import QtWidgets, QtCore, QtGui
+from .._py.integratederive_dialog import Ui_Dialog
+
+
+class QDeriveIntegrate(QtWidgets.QDialog, Ui_Dialog):
+ newValues = QtCore.pyqtSignal(int, dict)
+
+ def __init__(self, mode: str, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.mode = mode.lower()[0]
+
+ if self.mode == 'i':
+ self.setWindowTitle('Integration dialog')
+ self.start_lineedit.setValidator(QtGui.QDoubleValidator())
+ self.stop_lineedit.setValidator(QtGui.QDoubleValidator())
+ self.ft_comboBox.hide()
+
+ elif self.mode == 'd':
+ self.setWindowTitle('Differentiation dialog')
+ self.widget.hide()
+ self.ft_comboBox.hide()
+
+ elif self.mode == 'l':
+ self.setWindowTitle('Logarithmic FT dialog')
+ self.log_checkbox.hide()
+ self.widget.hide()
+
+ else:
+ raise ValueError(f'Unknown mode {mode}, use "d", "i", or "l".')
+
+ def add_graphs(self, graphs: list):
+ self.graph_combobox.clear()
+
+ for (graph_id, graph_name) in graphs:
+ self.graph_combobox.addItem(graph_name, userData=graph_id)
+
+ def set_sets(self, sets: list):
+ self.listWidget.clear()
+ for (set_id, set_name) in sets:
+ item = QtWidgets.QListWidgetItem(set_name)
+ item.setData(QtCore.Qt.UserRole, set_id)
+ item.setCheckState(QtCore.Qt.Checked)
+ self.listWidget.addItem(item)
+
+ @QtCore.pyqtSlot(int, name='on_checkBox_2_stateChanged')
+ def enable_graph(self, state: int):
+ self.graph_combobox.setEnabled(state != QtCore.Qt.Checked)
+
+ def get_options(self):
+ opts = {'graph': '' if self.newgraph_checkbox.isChecked() else self.graph_combobox.currentData(),
+ 'mode': self.mode, 'sets': []}
+
+ if self.mode == 'i':
+ start = None
+ stop = None
+
+ if not self.range_checkbox.isChecked():
+ start = self.start_lineedit.text()
+ stop = self.stop_lineedit.text()
+
+ if start:
+ start = float(start)
+
+ if stop:
+ stop = float(stop)
+
+ opts['limits'] = (start, stop)
+
+ if self.mode == 'l':
+ opts['ft_mode'] = ['cos', 'sin', 'complex'][self.ft_comboBox.currentIndex()]
+ else:
+ opts['log'] = self.log_checkbox.isChecked()
+
+ for i in range(self.listWidget.count()):
+ item = self.listWidget.item(i)
+ if item.checkState() == QtCore.Qt.Checked:
+ opts['sets'].append(item.data(QtCore.Qt.UserRole))
+
+ return opts
diff --git a/nmreval/gui_qt/math/interpol.py b/nmreval/gui_qt/math/interpol.py
new file mode 100644
index 0000000..ad3957f
--- /dev/null
+++ b/nmreval/gui_qt/math/interpol.py
@@ -0,0 +1,109 @@
+from ..Qt import QtCore, QtWidgets, QtGui
+from .._py.interpol_dialog import Ui_Dialog
+
+
+class InterpolDialog(QtWidgets.QDialog, Ui_Dialog):
+ new_data = QtCore.pyqtSignal(list, str, bool, bool, tuple, str)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.src_widget.hide()
+ validator = QtGui.QDoubleValidator()
+ self.start_lineEdit.setValidator(validator)
+ self.stop_lineEdit.setValidator(validator)
+ self.step_lineEdit.setValidator(QtGui.QIntValidator())
+
+ self._data = {}
+
+ @QtCore.pyqtSlot(int, name='on_xaxis_comboBox_currentIndexChanged')
+ def change_x_source(self, idx: int):
+ self.src_widget.setVisible(idx == 1)
+ self.sampling_widget.setVisible(idx != 1)
+
+ def set_data(self, data, current_gid):
+ self.graph_combobox.blockSignals(True)
+ self._data = {}
+ for (gid, graph_name), sets in data.items():
+ self.graph_combobox.addItem(graph_name, userData=gid)
+ self.dest_combobox.addItem(graph_name, userData=gid)
+ if gid == current_gid:
+ self.make_list(sets)
+ self._data[gid] = sets
+ self.graph_combobox.blockSignals(False)
+ self.change_graph(0)
+
+ def make_list(self, current_sets):
+ for sid, set_name in current_sets:
+ item = QtWidgets.QListWidgetItem(set_name)
+ item.setData(QtCore.Qt.UserRole, sid)
+ item.setCheckState(QtCore.Qt.Checked)
+ self.listWidget.addItem(item)
+
+ @QtCore.pyqtSlot(int, name='on_graph_combobox_currentIndexChanged')
+ def change_graph(self, idx: int):
+ self.set_combobox.clear()
+ gid = self.graph_combobox.itemData(idx, QtCore.Qt.UserRole)
+ if gid is not None:
+ for set_key, set_name in self._data[gid]:
+ self.set_combobox.addItem(set_name, userData=set_key)
+
+ def collect_parameter(self):
+ xlog = self.xlog_checkBox.isChecked()
+ ylog = self.ylog_checkBox.isChecked()
+
+ mode = self.interp_comboBox.currentText().lower()
+
+ if self.xaxis_comboBox.currentIndex() == 0:
+ try:
+ start = float(self.start_lineEdit.text())
+ stop = float(self.stop_lineEdit.text())
+ step = int(self.step_lineEdit.text())
+ except ValueError:
+ QtWidgets.QMessageBox.warning(self, 'Invalid axis', 'Values for axis are invalid or missing.')
+ return False
+
+ loggy = self.logspace_checkBox.isChecked()
+ if loggy and ((start <= 0) or (stop <= 0)):
+ QtWidgets.QMessageBox.warning(self, 'Invalid axis', 'Logarithmic axis cannot contain negative values.')
+ return False
+
+ x_src = (start, stop, step, loggy)
+ else:
+ x_src = (self.set_combobox.currentData(QtCore.Qt.UserRole),)
+
+ dest_graph = self.dest_combobox.currentData(QtCore.Qt.UserRole)
+
+ use_data = []
+ for i in range(self.listWidget.count()):
+ item = self.listWidget.item(i)
+ if item.checkState() == QtCore.Qt.Checked:
+ use_data.append(item.data(QtCore.Qt.UserRole))
+
+ self.new_data.emit(use_data, mode, xlog, ylog, x_src, dest_graph)
+
+ return True
+
+ def accept(self):
+ success = self.collect_parameter()
+ if success:
+ super().accept()
+
+
+if __name__ == '__main__':
+ import sys
+
+ app = QtWidgets.QApplication([])
+ from numpy.random import choice
+
+ qw = 'QtCurve'
+ while qw == 'QtCurve':
+ qw = choice(QtWidgets.QStyleFactory.keys())
+ print(qw)
+ app.setStyle(QtWidgets.QStyleFactory.create(qw))
+
+ mplQt = InterpolDialog()
+ mplQt.show()
+
+ sys.exit(app.exec())
\ No newline at end of file
diff --git a/nmreval/gui_qt/math/mean_dialog.py b/nmreval/gui_qt/math/mean_dialog.py
new file mode 100644
index 0000000..18b60fd
--- /dev/null
+++ b/nmreval/gui_qt/math/mean_dialog.py
@@ -0,0 +1,112 @@
+from ...distributions import ColeDavidson, HavriliakNegami, KWW, LogGaussian
+from ..Qt import QtWidgets, QtCore
+from .._py.meandialog import Ui_calc_means_dialog
+from ..lib.forms import Widget
+
+
+class QMeanTimes(QtWidgets.QDialog, Ui_calc_means_dialog):
+ newValues = QtCore.pyqtSignal(tuple, dict, str)
+
+ def __init__(self, tree: dict, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self._tree = tree
+
+ self.distributions = [ColeDavidson, HavriliakNegami, KWW, LogGaussian]
+ for s in self.distributions:
+ self.dist_combobox.addItem(s.name)
+
+ self.update_graphs(self._tree)
+ self.change_distribution(0)
+
+ self.dist_combobox.currentIndexChanged.connect(self.change_distribution)
+ self.from_combobox.currentIndexChanged.connect(lambda idx: self.calculate_means())
+ self.to_combobox.currentIndexChanged.connect(lambda idx: self.calculate_means())
+
+ def update_graphs(self, graph: dict):
+ graph_id = self.graph_combobox.currentData(QtCore.Qt.UserRole)
+
+ self.graph_combobox.clear()
+ for key, (name, _) in graph.items():
+ self.graph_combobox.addItem(name, userData=key)
+ if graph_id is not None:
+ self.graph_combobox.setCurrentIndex(self.graph_combobox.findData(graph_id, QtCore.Qt.UserRole))
+ else:
+ self.graph_combobox.setCurrentIndex(0)
+
+ @QtCore.pyqtSlot(int)
+ def change_distribution(self, idx):
+ dist = self.distributions[idx]
+
+ while self.verticalLayout.count():
+ item = self.verticalLayout.takeAt(0)
+ item.widget().deleteLater()
+
+ for i, p in enumerate([r'\tau'] + dist.parameter):
+ w = Widget(p, self._tree, collapsing=True, parent=self)
+ w.valueChanged.connect(lambda: self.calculate_means())
+ self.verticalLayout.addWidget(w)
+
+ self.calculate_means()
+
+ def calculate_means(self):
+ parameter = []
+ for i in range(self.verticalLayout.count()):
+ w = self.verticalLayout.itemAt(i).widget()
+ try:
+ parameter.append(float(w.lineEdit.text()))
+ except ValueError:
+ parameter = None
+ break
+
+ if parameter is not None:
+ dist, direction = self.get_conversions()
+
+ self.label.setText(f'{self.to_combobox.currentText()}: '
+ f'{dist.convert(*parameter, **direction):.8g}')
+
+ @QtCore.pyqtSlot()
+ def get_parameter(self):
+ parameter = []
+ for i in range(self.verticalLayout.count()):
+ w = self.verticalLayout.itemAt(i).widget()
+
+ p = w.value
+ if p is not None:
+ parameter.append(p)
+ else:
+ QtWidgets.QMessageBox.warning(self, 'Invalid input',
+ f'Invalid input for parameter {w.label.text()}')
+ return
+
+ dist, direction = self.get_conversions()
+
+ graph_destination = ''
+ if not self.checkBox.isChecked():
+ graph_destination = self.graph_combobox.currentData()
+
+ self.newValues.emit((dist, parameter), direction, graph_destination)
+
+ def get_conversions(self):
+ mode = ['raw', 'peak', 'mean', 'logmean']
+ mode_to = mode[self.to_combobox.currentIndex()]
+ mode_from = mode[self.from_combobox.currentIndex()]
+
+ dist = self.distributions[self.dist_combobox.currentIndex()]
+
+ return dist, {'from_': mode_from, 'to_': mode_to}
+
+ @QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonBox_clicked')
+ def button_clicked(self, bttn: QtWidgets.QAbstractButton):
+ role = self.buttonBox.buttonRole(bttn)
+
+ if role == QtWidgets.QDialogButtonBox.RejectRole:
+ self.close()
+ elif role == QtWidgets.QDialogButtonBox.AcceptRole:
+ self.get_parameter()
+ self.close()
+ elif role == QtWidgets.QDialogButtonBox.ApplyRole:
+ self.get_parameter()
+ else:
+ print('Unknown role')
diff --git a/nmreval/gui_qt/math/skipping.py b/nmreval/gui_qt/math/skipping.py
new file mode 100644
index 0000000..0b285c8
--- /dev/null
+++ b/nmreval/gui_qt/math/skipping.py
@@ -0,0 +1,19 @@
+from ..Qt import QtWidgets
+from .._py.skipdialog import Ui_SkipDialog
+
+
+class QSkipDialog(QtWidgets.QDialog, Ui_SkipDialog):
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ def on_spinBox_valueChanged(self, val: int):
+ self.offset_spinbox.setMaximum(int(val)-1)
+
+ def get_arguments(self):
+ return {
+ 'offset': int(self.offset_spinbox.value()),
+ 'step': int(self.step_spinbox.value()),
+ 'copy': self.delete_button.isChecked(),
+ 'invert': self.invert_check.isChecked()
+ }
diff --git a/nmreval/gui_qt/math/smooth.py b/nmreval/gui_qt/math/smooth.py
new file mode 100644
index 0000000..6d547a1
--- /dev/null
+++ b/nmreval/gui_qt/math/smooth.py
@@ -0,0 +1,57 @@
+from ..Qt import QtWidgets, QtCore
+from .._py.smoothdialog import Ui_SmoothDialog
+
+
+class QSmooth(QtWidgets.QDialog, Ui_SmoothDialog):
+ newValues = QtCore.pyqtSignal(int, dict)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+ self.on_comboBox_currentIndexChanged(0)
+
+ @QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged')
+ def change_mode(self, idx: int):
+ if idx == 2:
+ self.widget.show()
+ self.widget_2.hide()
+ elif idx == 3:
+ self.widget.show()
+ self.widget_2.show()
+ else:
+ self.widget.hide()
+ self.widget_2.hide()
+
+ def accept(self):
+ para = {'logx': self.x_checkBox.isChecked(),
+ 'logy': self.y_checkBox.isChecked()}
+ npoints = self.frac_spinBox.value()
+ idx = self.comboBox.currentIndex()
+
+ # this order must match the combobox
+ para['mode'] = ['mean', 'savgol', 'loess', 'median', 'std', 'var', 'max', 'min', 'sum'][idx]
+
+ if idx == 2:
+ para['deg'] = self.polynom_spinBox.value()
+
+ if idx == 3:
+ para['deg'] = self.polynom_spinBox.value()
+ para['it'] = self.iter_spinBox.value()
+
+ self.newValues.emit(npoints, para)
+
+ super().accept()
+
+ def close(self):
+ self.disconnect()
+ super().close()
+
+
+if __name__ == '__main__':
+ import sys
+ app = QtWidgets.QApplication([])
+
+ w = QSmooth()
+ w.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/nmr/coupling_calc.py b/nmreval/gui_qt/nmr/coupling_calc.py
new file mode 100644
index 0000000..e68c895
--- /dev/null
+++ b/nmreval/gui_qt/nmr/coupling_calc.py
@@ -0,0 +1,73 @@
+from ...nmr.couplings import *
+from ...utils.text import convert
+
+from ..Qt import QtWidgets, QtCore
+from .._py.coupling_calculator import Ui_coupling_calc_dialog
+from ..lib.forms import SelectionWidget, FormWidget
+
+
+class QCoupCalcDialog(QtWidgets.QDialog, Ui_coupling_calc_dialog):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.setupUi(self)
+
+ self.cp = [Quadrupolar, QuadrupolarQCC, Czjzek, HeteroDipolar, HomoDipolar]
+ for cp in self.cp:
+ self.comboBox.addItem(cp.name)
+
+ self._coupling_parameter = []
+
+ self.change_coupling(0)
+
+ @QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged')
+ def change_coupling(self, idx):
+ self.label_2.setText(convert(self.cp[idx].equation))
+ coup = self.cp[idx]
+ self._coupling_parameter = []
+
+ while self.verticalLayout_2.count():
+ item = self.verticalLayout_2.takeAt(0)
+ try:
+ item.widget().deleteLater()
+ except AttributeError:
+ pass
+
+ max_size = 0
+ if coup.parameter is not None:
+ for p, u in zip(coup.parameter, coup.unit):
+ if u:
+ _temp = FormWidget(parent=self, name=convert(p+' / '+u))
+ else:
+ _temp = FormWidget(parent=self, name=convert(p))
+ _temp.valueChanged.connect(lambda v: self.calc_coupling())
+ size = _temp.label.sizeHint()
+ max_size = max(max_size, size.width())
+ self.verticalLayout_2.addWidget(_temp)
+ self._coupling_parameter.append(_temp)
+
+ for item in self._coupling_parameter:
+ item.label.setFixedWidth(max_size)
+
+ if coup.choice is not None:
+ for c in self.cp[idx].choice:
+ _temp = SelectionWidget(*c, parent=self)
+ _temp.selectionChanged.connect(lambda name, value: self.calc_coupling())
+ self.verticalLayout_2.addWidget(_temp)
+ self._coupling_parameter.append(_temp)
+
+ self.calc_coupling()
+
+ def calc_coupling(self):
+ p = []
+ for pp in self._coupling_parameter:
+ p.append(pp.value)
+
+ self.label.setText('Coupling constant: %.8g' % self.cp[self.comboBox.currentIndex()].func(*p))
+
+
+if __name__ == '__main__':
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ w = QCoupCalcDialog()
+ w.show()
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/nmr/t1_from_tau.py b/nmreval/gui_qt/nmr/t1_from_tau.py
new file mode 100644
index 0000000..dc0642c
--- /dev/null
+++ b/nmreval/gui_qt/nmr/t1_from_tau.py
@@ -0,0 +1,211 @@
+from typing import List, Tuple
+
+from ...nmr.couplings import *
+from ...distributions import ColeCole, ColeDavidson, HavriliakNegami
+from ...utils import pi
+from ...utils.text import convert
+
+from ..Qt import QtGui, QtCore, QtWidgets
+from ..lib.forms import SelectionWidget, Widget
+from .._py.t1_calc_dialog import Ui_Dialog
+
+
+class QRelaxCalc(QtWidgets.QDialog, Ui_Dialog):
+ newData = QtCore.pyqtSignal(dict)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setupUi(self)
+
+ self.graphs = {}
+
+ self.specdens = [ColeCole, ColeDavidson, HavriliakNegami]
+ self.coupling = [Quadrupolar, QuadrupolarQCC, Czjzek, HomoDipolar]
+ self.tau_parameter = []
+
+ for line_edit in [self.ea_lineEdit, self.tau0_lineEdit, self.start_lineEdit, self.stop_lineEdit,
+ self.tau0_vft_lineEdit, self.b_vft_lineEdit, self.t0_vft_lineEdit]:
+ line_edit.setValidator(QtGui.QDoubleValidator())
+
+ for s in self.specdens:
+ self.specdens_combobox.addItem(s.name)
+
+ for c in self.coupling:
+ self.coupling_combobox.addItem(c.name)
+
+ self.specdens_combobox.currentIndexChanged.connect(self.update_specdens_model)
+ self.coupling_combobox.currentIndexChanged.connect(self.update_coupling_model)
+ self.buttonGroup.buttonClicked.connect(self.change_axis)
+
+ self.update_specdens_model(0)
+ self.update_coupling_model(0)
+ self.temp_combo_change(0)
+ self.tau_combo_changed(0)
+ self.change_axis(self.radioButton)
+
+ def set_graphs(self, graphs: dict):
+ self.graph_combobox.clear()
+ self.tau_graph_combobox.clear()
+
+ self.tau_graph_combobox.blockSignals(True)
+ for key, (name, _) in graphs.items():
+ self.graph_combobox.addItem(name, userData=key)
+ self.tau_graph_combobox.addItem(name, userData=key)
+ self.tau_graph_combobox.blockSignals(False)
+
+ self.graphs = graphs
+
+ self.update_specdens_model(self.specdens_combobox.currentIndex())
+ self.update_coupling_model(self.coupling_combobox.currentIndex())
+ self.tau_graph_changed(self.tau_graph_combobox.currentIndex())
+
+ def update_graphs(self, graphs: List[Tuple[str, str]]):
+ current_id = self.graph_combobox.currentData()
+ self.graph_combobox.clear()
+ for (gid, name) in graphs:
+ self.graph_combobox.addItem(name, userData=gid)
+
+ self.graph_combobox.setCurrentIndex(self.graph_combobox.findData(current_id, QtCore.Qt.UserRole))
+
+ @QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged')
+ def changed_state(self, checked):
+ self.graph_combobox.setEnabled(checked != QtCore.Qt.Checked)
+
+ @QtCore.pyqtSlot(int, name='on_tau_graph_combobox_currentIndexChanged')
+ def tau_graph_changed(self, idx: int):
+ key = self.tau_graph_combobox.itemData(idx)
+ self.tau_set_combobox.clear()
+ if self.graphs:
+ for (set_key, set_name) in self.graphs[key][1]:
+ self.tau_set_combobox.addItem(set_name, userData=set_key)
+
+ @QtCore.pyqtSlot(QtWidgets.QAbstractButton)
+ def change_axis(self, bttn: QtWidgets.QRadioButton):
+ self.temp_widget.setVisible(bttn in [self.radioButton_3, self.radioButton_4])
+ if bttn == self.radioButton_2:
+ self.label_7.setText('\u03c4 / s')
+ else:
+ self.label_7.setText('\u03c9 / Hz')
+
+ @QtCore.pyqtSlot(int, name='on_temp_combobox_curr#entIndexChanged')
+ def temp_combo_change(self, idx: int):
+ self.arr_widget.setVisible(idx == 0)
+ self.vft_widget.setVisible(idx == 1)
+
+ @QtCore.pyqtSlot(int, name='on_x_input_combobox_currentIndexChanged')
+ def tau_combo_changed(self, idx: int):
+ self.range_widget.setVisible(idx == 0)
+ self.data_widget.setVisible(idx == 1)
+
+ @QtCore.pyqtSlot(int)
+ def update_coupling_model(self, idx: int):
+ while self.verticalLayout_4.count():
+ item = self.verticalLayout_4.takeAt(0)
+ try:
+ item.widget().deleteLater()
+ except AttributeError:
+ pass
+
+ if self.coupling[idx].parameter is not None:
+ for p in self.coupling[idx].parameter:
+ _temp = Widget(convert(p), tree=self.graphs, collapsing=True, parent=self)
+ self.verticalLayout_4.addWidget(_temp)
+
+ if self.coupling[idx].choice is not None:
+ for c in self.coupling[idx].choice:
+ _temp = SelectionWidget(*c, parent=self)
+ self.verticalLayout_4.addWidget(_temp)
+
+ @QtCore.pyqtSlot(int)
+ def update_specdens_model(self, idx):
+ while self.verticalLayout_3.count():
+ item = self.verticalLayout_3.takeAt(0)
+ try:
+ item.widget().deleteLater()
+ except AttributeError:
+ pass
+
+ if self.specdens[idx].parameter is not None:
+ for param in self.specdens[idx].parameter:
+ _temp = Widget(convert(param), tree=self.graphs, collapsing=True, parent=self)
+ self.verticalLayout_3.addWidget(_temp)
+
+ def get_taus(self, dic: dict):
+ dic['tau_type'] = {0: 'raw', 1: 'mean', 2: 'peak', 3: 'logmean'}[self.xtype_combobox.currentIndex()]
+ dic['axis1'] = {self.radioButton: 'tau', self.radioButton_2: 'omega',
+ self.radioButton_3: 't', self.radioButton_4: 'invt1000'}[self.buttonGroup.checkedButton()]
+
+ dic['val2'] = float(self.second_x_lineEdit.text())
+ if dic['axis1'] != 'omega':
+ dic['val2'] *= 2*pi
+
+ idx = self.x_input_combobox.currentIndex()
+ if idx == 0:
+ dic['pts'] = (float(self.start_lineEdit.text()), float(self.stop_lineEdit.text()),
+ self.spinBox.value(), self.checkBox.isChecked())
+ if self.buttonGroup.checkedButton() in [self.radioButton_3, self.radioButton_4]:
+ if self.temp_combobox.currentIndex() == 0:
+ dic['t_param'] = (float(self.tau0_lineEdit.text()), float(self.ea_lineEdit.text()))
+ else:
+ dic['t_param'] = (float(self.tau0_vft_lineEdit.text()), float(self.b_vft_lineEdit.text()),
+ float(self.t0_vft_lineEdit.text()))
+ else:
+ dic['pts'] = (self.tau_set_combobox.currentData(), self.y_radioButton.isChecked())
+
+ def get_coupling(self, dic):
+ dic['coup'] = self.coupling[self.coupling_combobox.currentIndex()]
+
+ parameter = []
+ kwargs = {}
+ for i in range(self.verticalLayout_4.count()):
+ p = self.verticalLayout_4.itemAt(i).widget()
+ if isinstance(p, Widget):
+ parameter.append(p.value)
+ elif isinstance(p, SelectionWidget):
+ kwargs[p.argname] = p.value
+ else:
+ raise TypeError('WTF?: Unknown widget', p)
+
+ dic['cp_param'] = (parameter, kwargs)
+
+ def get_specdens(self, dic):
+ dic['spec_dens'] = self.specdens[self.specdens_combobox.currentIndex()]
+
+ parameter = []
+ kwargs = {}
+ for i in range(self.verticalLayout_3.count()):
+ p = self.verticalLayout_3.itemAt(i).widget()
+ if isinstance(p, Widget):
+ parameter.append(p.value)
+ elif isinstance(p, SelectionWidget):
+ kwargs[p.argname] = p.value
+ else:
+ raise TypeError('WTF?: Unknown widget', p)
+
+ dic['sd_param'] = (parameter, kwargs)
+
+ def calc_relaxation(self):
+ opts = {}
+
+ self.get_taus(opts)
+ self.get_coupling(opts)
+ self.get_specdens(opts)
+
+ opts['out'] = {0: 't1', 1: 't2'}[self.relax_combox.currentIndex()]
+ opts['graph'] = '' if self.graph_checkbox.isChecked() else self.graph_combobox.currentData()
+
+ self.newData.emit(opts)
+
+ def accept(self):
+ self.calc_relaxation()
+ super().accept()
+
+
+if __name__ == '__main__':
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+
+ w = QRelaxCalc()
+ w.show()
+
+ sys.exit(app.exec())
diff --git a/nmreval/gui_qt/nmr/t1widget.py b/nmreval/gui_qt/nmr/t1widget.py
new file mode 100644
index 0000000..eb724a7
--- /dev/null
+++ b/nmreval/gui_qt/nmr/t1widget.py
@@ -0,0 +1,326 @@
+import numpy as np
+from pyqtgraph import mkBrush, mkPen
+
+from ..lib.pg_objects import PlotItem
+from ...data.points import Points
+from ...nmr.relaxation import RelaxationEvaluation
+from ...nmr.couplings import *
+from ...distributions import *
+
+from ..Qt import QtCore, QtWidgets, QtGui
+from .._py.t1dialog import Ui_t1dialog
+from ..lib.forms import FormWidget, SelectionWidget
+from ..lib.utils import busy_cursor
+
+
+class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
+ magnitude = {0: 1e6, 1: 1e3, 2: 1}
+ temp_conversion = {0: lambda xx: 1000 / xx, 1: lambda xx: xx, 2: lambda xx: 1 / xx}
+ time_conversion = {0: lambda yy: yy, 1: lambda yy: 1/yy}
+
+ newData = QtCore.pyqtSignal(list, str)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+
+ self.setupUi(self)
+
+ self.connected_figure = ''
+ self.name = ''
+
+ self.t1calculator = RelaxationEvaluation()
+
+ self.sd_parameter = []
+ self.sdmodels = [Debye, ColeCole, ColeDavidson, KWW, HavriliakNegami, LogGaussian]
+ for i in self.sdmodels:
+ self.specdens_combobox.addItem(i.name)
+ self.specdens_combobox.currentIndexChanged.connect(self.update_specdens)
+
+ self.cp_parameter = []
+ self.coupling = [Quadrupolar, QuadrupolarQCC, Czjzek, HomoDipolar, Constant]
+ for i in self.coupling:
+ self.coupling_combobox.addItem(i.name)
+ self.coupling_combobox.currentIndexChanged.connect(self.update_coupling)
+
+ self.temp_combobox.currentIndexChanged.connect(self.update_dimensions)
+ self.t1_combobox.currentIndexChanged.connect(self.update_dimensions)
+ self.lineEdit_2.textChanged.connect(lambda: self.determine_minimum(self.interpol_combobox.currentIndex()))
+ self.lineEdit_3.textChanged.connect(lambda: self.determine_minimum(self.interpol_combobox.currentIndex()))
+
+ self.conv_x = QT1Widget.temp_conversion[self.temp_combobox.currentIndex()]
+ self.conv_y = QT1Widget.time_conversion[self.t1_combobox.currentIndex()]
+
+ self.minimum = (1, np.inf)
+ self.min_pos = PlotItem(x=np.array([]), y=np.array([]),
+ symbol='+', symbolBrush=mkBrush(color='r'), symbolPen=mkPen(color='r'), symbolSize=14)
+ self.parabola = PlotItem(x=np.array([]), y=np.array([]))
+
+ self.lineEdit_2.setValidator(QtGui.QDoubleValidator())
+ self.lineEdit_3.setValidator(QtGui.QDoubleValidator())
+
+ self.freq_combox.currentIndexChanged.connect(lambda x: self.update_model())
+ self.freq_spinbox.valueChanged.connect(lambda x: self.update_model())
+
+ self.update_specdens(0)
+ self.update_coupling(0)
+
+ def set_graphs(self, graphs: list):
+ self.graph_combobox.clear()
+ for g in graphs:
+ self.graph_combobox.addItem(g[1], userData=g[0])
+
+ def set_data(self, x, y, name=''):
+ x = self.conv_x(x)
+ y = self.conv_y(y)
+
+ sortidx = np.argsort(x)
+ x = x[sortidx]
+ y = y[sortidx]
+
+ left_b = max(np.argmin(y)-2, 0)
+ right_b = min(np.argmin(y)+3, len(x)-1)
+
+ self.lineEdit_2.blockSignals(True)
+ self.lineEdit_2.setText('{:.2f}'.format(x[left_b]))
+ self.lineEdit_2.blockSignals(False)
+ self.lineEdit_3.blockSignals(True)
+ self.lineEdit_3.setText('{:.2f}'.format(x[right_b]))
+ self.lineEdit_3.blockSignals(False)
+
+ self.t1calculator.data(x, y)
+
+ self.determine_minimum(self.interpol_combobox.currentIndex())
+ self.name = name
+
+ @property
+ def frequency(self):
+ return self.freq_spinbox.value() * QT1Widget.magnitude[self.freq_combox.currentIndex()]
+
+ @QtCore.pyqtSlot(int)
+ def update_specdens(self, idx: int):
+ self.sd_parameter = []
+ while self.verticalLayout_3.count():
+ item = self.verticalLayout_3.takeAt(0)
+ try:
+ item.widget().deleteLater()
+ except AttributeError:
+ pass
+
+ if self.sdmodels[idx].parameter is not None:
+ for name in self.sdmodels[idx].parameter:
+ _temp = FormWidget(parent=self, name=name, fixable=True)
+ _temp.value = 1
+ _temp.setChecked(True)
+ _temp.valueChanged.connect(self.update_specdens_parameter)
+ _temp.stateChanged.connect(self.update_specdens_parameter)
+ self.verticalLayout_3.addWidget(_temp)
+ self.sd_parameter.append(_temp)
+
+ self.t1calculator.distribution(self.sdmodels[idx])
+ self.update_model()
+
+ @QtCore.pyqtSlot()
+ def update_specdens_parameter(self):
+ new_p = []
+ for p in self.sd_parameter:
+ new_p.append(p.value)
+
+ self.update_model()
+
+ def update_sddisplay(self, values):
+ try:
+ for i, v, in enumerate(values):
+ self.sd_parameter[i].blockSignals(True)
+ self.sd_parameter[i].value = '{:.3g}'.format(round(v, 3))
+ self.sd_parameter[i].blockSignals(False)
+ except IndexError:
+ pass
+
+ @QtCore.pyqtSlot(int)
+ def update_coupling(self, idx: int):
+ self.cp_parameter = []
+ while self.verticalLayout_4.count():
+ item = self.verticalLayout_4.takeAt(0)
+ try:
+ item.widget().deleteLater()
+ except AttributeError:
+ pass
+
+ curr_coupling = self.coupling[idx]
+ if curr_coupling.parameter is not None:
+ for i, name in enumerate(curr_coupling.parameter):
+ if curr_coupling.unit is not None:
+ u = curr_coupling.unit[i]
+ name = name+'/'+u if u else name
+ _temp = FormWidget(parent=self, name=name, fixable=True)
+ self.verticalLayout_4.addWidget(_temp)
+ _temp.value = 1
+ _temp.setChecked(True)
+ _temp.valueChanged.connect(self.update_coupling_parameter)
+ _temp.stateChanged.connect(self.update_coupling_parameter)
+ self.cp_parameter.append(_temp)
+
+ if self.coupling[idx].choice is not None:
+ for (name, kw_name, opts) in self.coupling[idx].choice:
+ widgt = SelectionWidget(name, kw_name, opts)
+ widgt.comboBox.currentIndexChanged.connect(self.update_coupling)
+ self.verticalLayout_4.addWidget(widgt)
+ self.cp_parameter.append(widgt)
+
+ self.update_coupling_parameter()
+
+ @QtCore.pyqtSlot()
+ def update_coupling_parameter(self):
+ new_p = []
+ for pp in self.cp_parameter:
+ new_p.append(pp.value)
+ new_coupling = self.coupling[self.coupling_combobox.currentIndex()]
+ self.t1calculator.coupling(new_coupling)
+
+ self.update_model()
+
+ @QtCore.pyqtSlot(int, name='on_temp_combobox_currentIndexChanged')
+ @QtCore.pyqtSlot(int, name='on_t1_combobox_currentIndexChanged')
+ def update_dimensions(self, idx: int):
+ _x = self.t1calculator.x
+ _y = self.t1calculator.y
+
+ if self.sender() == self.temp_combobox:
+ _x = self.conv_x(self.t1calculator.x)
+ self.conv_x = self.temp_conversion[idx]
+
+ else:
+ _y = self.conv_y(self.t1calculator.y)
+ self.conv_y = self.time_conversion[idx]
+
+ self.set_data(_x, _y, name=self.name)
+
+ @QtCore.pyqtSlot(tuple)
+ def t1min_picked(self, pos: tuple):
+ self.min_pos.setData(x=[pos[0]], y=[pos[1]])
+ self.update_min(self.conv_x(pos[0]), pos[1])
+
+ def update_min(self, x, y):
+ self.label_13.setText(f'{x:.4g} K')
+ self.label_12.setText(f' {y:.3e} s')
+ if y > np.min(self.t1calculator.y):
+ self.label_12.setStyleSheet('QLabel {color: red; font-weight: bold}')
+ else:
+ self.label_12.setStyleSheet('QLabel {}')
+ self.min_pos.setData(x=[self.conv_x(x)], y=[self.conv_y(y)])
+
+ self.minimum = x, y
+ self.update_model()
+
+ @QtCore.pyqtSlot(int, name='on_interpol_combobox_currentIndexChanged')
+ def determine_minimum(self, idx):
+ if idx == 0:
+ self.checkBox_interpol.setChecked(False)
+ self.checkBox_interpol.hide()
+ self.frame.hide()
+ m, i_func = self.t1calculator.calculate_t1_min(interpolate=None)
+ else:
+ self.checkBox_interpol.show()
+ self.frame.show()
+ try:
+ m, i_func = self.t1calculator.calculate_t1_min(interpolate=idx,
+ trange=(float(self.lineEdit_2.text()),
+ float(self.lineEdit_3.text())))
+ except ValueError:
+ m, i_func = self.t1calculator.calculate_t1_min(interpolate=None)
+
+ self.update_min(*m)
+
+ if i_func is not None:
+ self.parabola.setData(x=self.conv_x(i_func[0]), y=self.conv_y(i_func[1]))
+ else:
+ self.parabola.setData(x=np.array([]), y=np.array([]))
+
+ return m
+
+ @QtCore.pyqtSlot(name='on_t1min_toolButton_clicked')
+ def set_min(self):
+ m = self.determine_minimum(self.interpol_combobox.currentIndex())
+ self.update_min(x=m[0], y=m[1])
+
+ def update_model(self):
+ sd_args, sd_fix = self.get_sd_values()
+ cp_args, cp_kwargs, cp_fix = self.get_cp_values()
+
+ if (len(sd_fix)-sum(sd_fix)) + (len(cp_fix)-sum(cp_fix)) > 1:
+ QtWidgets.QMessageBox.information(self, 'Too many free variables',
+ 'More than one parameter is variable.\n'
+ 'Only one can be determined from a minimum.')
+ return
+
+ notfix = ()
+ if not all(sd_fix):
+ notfix = ('distribution', sd_fix.index(False))
+
+ if not all(cp_fix):
+ notfix = ('coupling', cp_fix.index(False))
+
+ if not np.isfinite(self.t1calculator.t1min[1]):
+ return
+
+ mini = np.nan
+ with busy_cursor():
+ calc_stretching, mini = self.t1calculator.increase(notfix, height=self.minimum[1],
+ omega=2*np.pi*self.frequency,
+ dist_parameter=sd_args, prefactor=cp_args,
+ coupling_kwargs=cp_kwargs)
+
+ self.label_t1min.setText(f'{mini:.4g} s')
+
+ if notfix:
+ forms = self.sd_parameter if notfix[0] == 'distribution' else self.cp_parameter
+ forms[notfix[1]].blockSignals(True)
+ forms[notfix[1]].value = f'{calc_stretching:.4g}'
+ forms[notfix[1]].blockSignals(False)
+
+ @QtCore.pyqtSlot(name='on_calc_pushButton_clicked')
+ def calculate_correlations(self):
+ sd_args, _ = self.get_sd_values()
+ cp_args, cp_kwargs, _ = self.get_cp_values()
+ tau_mode = ['fit', 'peak', 'mean', 'logmean'][self.tau_combox.currentIndex()]
+ corr, opts = self.t1calculator.correlation_from_t1(omega=2*np.pi*self.frequency, dist_parameter=sd_args,
+ coupling_param=cp_args, coupling_kwargs=cp_kwargs,
+ mode=tau_mode, interpolate=self.checkBox_interpol.isChecked())
+
+ name = self.name + '-' + str(self.t1calculator) + '('
+ name += ','.join([f'{a:.3g}' for a in sd_args])
+ name += ')-' + tau_mode + ' tau'
+ new_data = Points(x=self.conv_x(corr[:, 0]), y= corr[:, 1], name=name)
+ new_data.update(opts)
+ if self.graph_checkbox.isChecked():
+ gid = ''
+ else:
+ gid = self.graph_combobox.currentData()
+ self.newData.emit([new_data], gid)
+
+ def get_sd_values(self):
+ sd_args = []
+ sd_fix = []
+ for i, p in enumerate(self.sd_parameter):
+ sd_args.append(p.value)
+ sd_fix.append(p.isChecked())
+
+ return sd_args, sd_fix
+
+ def get_cp_values(self):
+ cp_args = []
+ cp_kwargs = {}
+ cp_fix = []
+ for i, p in enumerate(self.cp_parameter):
+ try:
+ cp_fix.append(p.isChecked())
+ cp_args.append(p.value)
+ except AttributeError:
+ # SelectionWidget has no isChecked()
+ cp_kwargs[p.argname] = p.value
+
+ return cp_args, cp_kwargs, cp_fix
+
+ @QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged')
+ def changed_state(self, checked):
+ self.graph_combobox.setEnabled(checked != QtCore.Qt.Checked)
diff --git a/nmreval/io/__init__.py b/nmreval/io/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nmreval/io/asciireader.py b/nmreval/io/asciireader.py
new file mode 100644
index 0000000..77cd90c
--- /dev/null
+++ b/nmreval/io/asciireader.py
@@ -0,0 +1,175 @@
+import pathlib
+import re
+from collections import OrderedDict
+
+import numpy as np
+
+from ..data.points import Points
+from ..data.nmr import FID, Spectrum
+from ..utils.utils import staggered_range
+
+NUMBERRE = re.compile(r'[0-9]\.*[0-9]*[Ee]*[+-]*[0-9]*')
+
+
+class AsciiReader:
+ delimiters = ['\t', ' ', ',']
+ maxcount = 10
+
+ def __init__(self, fname):
+ self.fname = None
+ self.header = []
+ self.data = []
+ self.delays = None
+ self.shape = (0, 0)
+
+ if fname:
+ self.fname = pathlib.Path(fname)
+ self.load_file()
+ self.look_for_delay()
+
+ def load_file(self, comments='#'):
+ i = 0
+ self.shape = (0, 0)
+ with self.fname.open('r') as f:
+ for line in f:
+ line = line.rstrip('\n\t\r, ')
+ if line.startswith(comments):
+ self.maxcount += 1
+ self.header.append(line.replace(comments, '').lstrip().rstrip())
+ else:
+ if line == '':
+ continue
+ line = self.check_delimiters(line, AsciiReader.delimiters)
+ self.shape = (self.shape[0]+1, max(self.shape[1], len(line)))
+ self.data.append(line)
+ i += 1
+ if i == self.maxcount:
+ break
+
+ def look_for_delay(self, fname=None):
+ if fname is None:
+ fname = self.fname.with_name('%s_delay.%s' % (self.fname.name, self.fname.suffix))
+
+ try:
+ self.delays = np.loadtxt(fname)
+ except IOError:
+ pass
+
+ def calc_delays(self, start, end, nums, log=True, stagg=False, stag_size=1):
+ if log:
+ self.delays = np.geomspace(start, end, num=nums)
+ else:
+ self.delays = np.linspace(start, end, num=nums)
+
+ if stagg:
+ self.delays = staggered_range(self.delays, stepsize=stag_size)
+
+ def export(self, xidx=None, yidx=None, yerridx=None, mode='Points') -> list:
+ if xidx is None:
+ xidx = [0]
+
+ if yidx is None:
+ yidx = list(range(0, self.shape[1]))
+ yidx.remove(xidx[0])
+
+ if yerridx is None:
+ yerridx = []
+
+ cols = xidx + yidx + yerridx
+ raw_data = np.genfromtxt(self.fname, usecols=cols, missing_values='--')
+
+ try:
+ last_line = self.header[-1].split()
+ except IndexError:
+ last_line = []
+
+ if self.delays is not None:
+ if raw_data.shape[0] % len(self.delays) != 0:
+ raise ValueError('number of delays {} does not fit into length of date {}'.format(
+ len(self.delays), raw_data.shape[1]))
+ raw_data = raw_data.reshape((len(self.delays), -1, raw_data.shape[1]))
+ else:
+ raw_data = raw_data.reshape((1, *raw_data.shape))
+
+ names = True if len(last_line) == raw_data.shape[1] else False
+ only_f = self.fname.stem
+
+ if self.delays:
+ del_names = self.delays
+ else:
+ del_names = [only_f]
+
+ ret_dic = OrderedDict()
+ for j in range(raw_data.shape[0]):
+ value = del_names[j]
+
+ if mode == 'Points':
+ try:
+ for k in range(1, raw_data.shape[2]):
+ if raw_data.shape[2] < 3:
+ name = only_f
+ elif names:
+ name = last_line[k]
+ else:
+ name = only_f+'_'+str(k)
+ new_data = Points(raw_data[j, :, 0], raw_data[j, :, k], group=only_f,
+ name=name, value=value, filename=self.fname)
+
+ self._update_dic(name, new_data, ret_dic)
+ except IndexError:
+ new_data = Points(np.arange(0, raw_data.shape[1]), raw_data[0, :],
+ name=only_f, group=str(only_f), filename=self.fname)
+
+ self._update_dic(only_f, new_data, ret_dic)
+ else:
+ cls = FID if mode == 'FID' else Spectrum
+
+ try:
+ for k in range(1, raw_data.shape[2], 2):
+ if raw_data.shape[2] < 4:
+ name = only_f
+ elif names:
+ name = last_line[k]
+ else:
+ name = only_f + '_' + str(k)
+
+ new_data = cls(raw_data[j, :, 0], raw_data[j, :, k:k+2].T, value=value,
+ group=name, name=str(value), filename=self.fname)
+ self._update_dic(name, new_data, ret_dic)
+
+ except OverflowError:
+ new_data = cls(np.arange(0, raw_data.shape[1]), raw_data[0, :],
+ group=only_f, name=str(only_f), filename=self.fname)
+ self._update_dic(only_f, new_data, ret_dic)
+
+ return list(ret_dic.values())
+
+ @staticmethod
+ def _update_dic(key, value, old_dic):
+ old_dic_keys = old_dic.keys()
+ if key in old_dic_keys:
+ counter = 0
+ replace_key = key + str(counter)
+ while replace_key in old_dic_keys:
+ counter += 1
+ replace_key = key + str(counter)
+ else:
+ replace_key = key
+ old_dic[replace_key] = value
+
+ @staticmethod
+ def check_delimiters(line: str, delimiters: list):
+ line = [line]
+
+ for delim in delimiters:
+ new_l = []
+ for l in line:
+ if delim in [' ', '\t']:
+ new_l.extend(l.split())
+ else:
+ new_l.extend((l.split(delim)))
+ line = new_l[:]
+
+ ret_val = [l for l in line]
+
+ return ret_val
diff --git a/nmreval/io/bds_reader.py b/nmreval/io/bds_reader.py
new file mode 100644
index 0000000..93067d6
--- /dev/null
+++ b/nmreval/io/bds_reader.py
@@ -0,0 +1,178 @@
+import re
+import struct
+import warnings
+from pathlib import Path
+
+import numpy as np
+
+from ..data.signals import Signal
+
+MAGIC_RE = re.compile(b'NOVOCONTROL DK-RESULTS')
+
+
+class BDSReader:
+ def __init__(self, fname: str = None):
+ self.opts = {}
+ self.fname = None
+ self.x = None
+ self.y = None
+
+ if fname is not None:
+ self.fname = Path(fname)
+ self._parse_file()
+
+ def __call__(self, fname: str):
+ self.fname = Path(fname)
+ self._parse_file()
+
+ return self
+
+ def _parse_file(self):
+ self.opts = {}
+ with self.fname.open(mode='rb') as f:
+ (magic_part,) = struct.unpack('<22s', f.read(22))
+ if re.match(MAGIC_RE, magic_part) is None:
+ raise TypeError(f'{self.fname} not a valid file')
+
+ f.seek(48)
+ self.opts['voltage'] = struct.unpack('f', f.read(4))[0]
+ self.opts['parameter'] = list(struct.unpack('i', f.read(4)))
+
+ f.seek(2, 1)
+ self.opts['edge correction'] = bool(struct.unpack('b', f.read(1))[0])
+ f.seek(1, 1)
+ self.opts['electrode thickness'] = struct.unpack('f', f.read(4))[0]
+
+ self.opts['parameter'] += struct.unpack('3f', f.read(12))
+
+ f.seek(2, 1)
+ self.opts['capacity'] = struct.unpack('f', f.read(4))[0]
+ self.opts['spacer area'] = struct.unpack('f', f.read(4))[0]
+ f.seek(20, 1)
+
+ self.opts['diameter'] = struct.unpack('f', f.read(4))[0]
+ self.opts['area'] = self.opts['diameter']**2 * np.pi / 4
+ self.opts['thickness'] = struct.unpack('f', f.read(4))[0]
+ self.opts['C_0'] = (self.opts['area']-self.opts['spacer area']) / self.opts['thickness'] * 8.8541878128e-12
+
+ f.seek(158)
+ name_length = struct.unpack('b', f.read(1))[0]
+
+ f.seek(164)
+ b = f.read(name_length)
+ self.opts['name'] = str(struct.unpack(f'<{name_length}s', b)[0][:-1], encoding='utf-8')
+
+ _ = struct.unpack('h', f.read(2))
+ freq_values_length = struct.unpack('b', f.read(1))[0]
+
+ f.seek(7, 1)
+ self.x = np.empty(freq_values_length)
+ for freqs in range(freq_values_length):
+ self.x[freqs] = struct.unpack('f', f.read(4))[0]
+
+ _ = struct.unpack('h', f.read(2))
+ length_temps = struct.unpack('b', f.read(1))[0]
+
+ f.seek(7, 1)
+ self.opts['temperatures'] = np.array(struct.unpack('f' * length_temps, f.read(4*length_temps)))
+
+ f.seek(110, 1)
+ date_length = (struct.unpack('b', f.read(1)))[0]
+
+ f.seek(5, 1)
+ self.opts['date'] = str(struct.unpack(f'<{date_length}s', f.read(date_length))[0][:-1],
+ encoding='utf-8')
+
+ f.seek(47, 1)
+ # there are 9 different values per frequency and temperature
+
+ _y = []
+ actual_temps_length = 0
+ read_length = 9 * freq_values_length
+ while True:
+ try:
+ _y += struct.unpack('f' * read_length, f.read(4*read_length))
+ actual_temps_length += 1
+ except struct.error:
+ break
+
+ if actual_temps_length != length_temps:
+ warnings.warn('Number of set temperatures does not match number of data points')
+
+ _y = np.array(_y).reshape((actual_temps_length, freq_values_length, 9))
+ # last 3 entries are zero, save only 6
+ self.y = np.transpose(_y[:, :, :6], (2, 0, 1))
+
+ def export(self, mode='epsilon') -> list:
+ if mode == 'epsilon':
+ eps = self.epsilon()
+ elif mode == 'sigma':
+ eps = self.sigma()
+ elif mode == 'modulus':
+ eps = self.modulus()
+ else:
+ raise ValueError(f'Unknown mode {mode}, not "epsilon", "sigma", or "modulus".')
+
+ ret_val = []
+
+ useful_info = {
+ 'voltage': self.opts['voltage'],
+ 'diameter': self.opts['diameter'],
+ 'area': self.opts['area'],
+ 'thickness': self.opts['thickness'],
+ 'mode': mode
+ }
+
+ for i, temps in enumerate(self.temperature):
+ ret_val.append(Signal(x=self.frequencies, y=eps[0][i, :] + 1j*eps[1][i, :],
+ name=f'{temps:.2f}', value=temps, **useful_info))
+
+ return ret_val
+
+ def __getitem__(self, val):
+ return self.opts[val]
+
+ @property
+ def set_temp(self):
+ return self.opts['temperatures'][:len(self.temperature)]
+
+ @property
+ def probe_temp(self):
+ return self.y[2]
+
+ @property
+ def temperature(self):
+ return self.probe_temp.mean(axis=1)
+
+ @property
+ def frequencies(self):
+ return self.x
+
+ def capacity(self):
+ # NOTE: nobody ever used edge correction, so we ignore this option entirely
+ return self.y[0] - self.opts['capacity'], -1./self.y[1] / (self.frequencies*2*np.pi)
+
+ def voltage(self):
+ return self.y[5]
+
+ def sigma(self):
+ # sigma^* = i * omega * eps_0 * (eps^* - 1)
+ eps_r, eps_i = self.epsilon()
+ conv = 0.01 * 8.8541878128e-12 * 2*np.pi*self.x # factor 0.01: sigma in S/cm
+ return conv * eps_i, conv * (eps_r-1)
+
+ def epsilon(self):
+ # eps^* = Cp^* d/A/eps_0
+ cr, ci = self.capacity()
+ return cr / self.opts['C_0'], -ci / self.opts['C_0']
+
+ def modulus(self):
+ # M^* = 1/eps^*
+ eps_r, eps_i = self.epsilon()
+ magn = eps_r ** 2 + eps_i ** 2
+ return eps_r / magn, eps_i / magn
+
+ def loss_factor(self):
+ eps_r, eps_i = self.epsilon()
+
+ return eps_i / eps_r, None
diff --git a/nmreval/io/dsc.py b/nmreval/io/dsc.py
new file mode 100644
index 0000000..28ff16b
--- /dev/null
+++ b/nmreval/io/dsc.py
@@ -0,0 +1,323 @@
+from pathlib import Path
+import re
+from collections import namedtuple
+from typing import Union
+
+import numpy as np
+try:
+ from scipy.integrate import simpson
+except ImportError:
+ from scipy.integrate import simps as simpson
+from scipy.interpolate import interp1d
+
+ReferenceValue = namedtuple('Reference', ['name', 'transitions'])
+Cyclohexane = ReferenceValue('Cyclohexane', [(-87.06+273.15, 79.58), (6.54+273.15, None)])
+
+
+class DSCSample:
+ NUMBER_RE = re.compile(r'(-?\d+(?:\.\d*)?)')
+ DSC_RUN_RE = re.compile(r'(\d+)\) DSC')
+ DSC_END_RE = re.compile(r'DSC8[50]00 MANUAL TUNE')
+
+ def __init__(self, fname: Union[str, Path]):
+ fname = Path(fname)
+ if fname.exists():
+ self.fname = fname
+ else:
+ raise IOError(f'{fname} does not exist')
+
+ self.weight = None
+ self.steps = []
+ self.starts = []
+
+ self.read_file(fname)
+
+ def read_file(self, fname: Union[str, Path]):
+ fname = Path(fname)
+
+ # file contains weird deg C character in stupiod ISO encoding
+ with fname.open('r', encoding='iso-8859-15') as f:
+ ii = 1
+ for line in f:
+ # iterate over file and look for different steps and start/end positions
+ if 'Heat from' in line:
+ match = DSCSample.NUMBER_RE.findall(line)
+ self.steps.append(('h', float(match[3]), float(match[1])+273.15, float(match[2])+273.15))
+
+ elif 'Cool from' in line:
+ match = DSCSample.NUMBER_RE.findall(line)
+ self.steps.append(('c', float(match[3]), float(match[1])+273.15, float(match[2])+273.15))
+
+ elif 'Hold for' in line:
+ match = DSCSample.NUMBER_RE.findall(line)
+ self.steps.append(('i', float(match[2])+273.15, float(match[1])*60))
+
+ # start of measurement step
+ elif DSCSample.DSC_RUN_RE.match(line):
+ match = DSCSample.DSC_RUN_RE.match(line)
+ r = int(match.group(1))
+ if r == 1:
+ # account for table header
+ self.starts.append(ii + 2)
+ else:
+ self.starts.append(ii)
+
+ elif 'Display Weight' in line:
+ self.weight = float(DSCSample.NUMBER_RE.search(line).group()) * 1e-3
+
+ # end of measurements
+ elif DSCSample.DSC_END_RE.search(line) is not None:
+ end_line = ii - 2
+
+ ii += 1
+ # there are weird empty lines in the beginning that mess up the line count
+ if r'\r\r\n' in repr(line):
+ ii += 1
+
+ self.starts.append(end_line)
+
+ @property
+ def cooling(self) -> dict:
+ ret_val = {}
+ for i, step in enumerate(self.steps):
+ if step[0] == 'c':
+ ret_val[i] = step[1]
+
+ return ret_val
+
+ @property
+ def heating(self) -> dict:
+ ret_val = {}
+ for i, step in enumerate(self.steps):
+ if step[0] == 'h':
+ ret_val[i] = step[1]
+
+ return ret_val
+
+ def get_run(self, rate: float, mode: str = 'h') -> int:
+ r = None
+
+ for k, v in enumerate(self.steps):
+ if (v[0] == mode) and (v[1] == rate):
+ r = k
+ break
+
+ if r is None:
+ raise ValueError(f'Rate {rate} not found')
+
+ return r
+
+ def length(self, run: int) -> int:
+ return self.starts[run+1] - self.starts[run] - 2
+
+ def flow_data(self, run: int, length: int = None):
+ start = self.starts[run]
+ if length is None:
+ length = self.length(run)
+
+ with self.fname.open('r', encoding='iso-8859-15') as f:
+ raw_data = np.genfromtxt(f, skip_header=start, max_rows=length-1, usecols=(4, 1, 0))
+
+ raw_data[:, 0] += 273.15
+ raw_data[:, 2] *= 60
+
+ return raw_data.T
+
+ def isotherm_data(self, run: int, length: int = None) -> np.ndarray:
+ start = self.starts[run]
+ if length is None:
+ length = self.length(run)
+
+ with self.fname.open('r', encoding='iso-8859-15') as f:
+ raw_data = np.genfromtxt(f, skip_header=start, max_rows=length-1, usecols=(0, 1))
+
+ raw_data[:, 0] *= 60
+
+ return raw_data.T
+
+
+class DSCCalibrator:
+ def __init__(self):
+ self.sample = None
+ self.empty = None
+ self.reference =[]
+ self.ref_list = []
+
+ def set_measurement(self,
+ fname: Union[str, Path, DSCSample], mode: str = 'sample',
+ reference: ReferenceValue = Cyclohexane):
+ if mode not in ['sample', 'empty', 'reference']:
+ raise ValueError(f'Unknown mode {mode}, not "sample", "empty", "reference"')
+ if mode == 'reference' and not isinstance(reference, ReferenceValue):
+ raise ValueError(f'Reference measurement has no reference values')
+
+ if not isinstance(fname, DSCSample):
+ fname = DSCSample(fname)
+
+ if mode == 'sample':
+ self.sample = fname
+ elif mode == 'empty':
+ self.empty = fname
+ else:
+ self.reference.append(fname)
+ self.ref_list.append(reference)
+
+ return fname
+
+ def remove_reference(self, fname: Union[str, Path]):
+ fname = Path(fname)
+ i = None
+ for i, ref in enumerate(self.reference):
+ if ref.fname == fname:
+ self.reference.pop(i)
+ break
+
+ if i is not None:
+ self.ref_list.pop(i)
+
+ def get_calibration(self, rate: float):
+ real_tm = []
+ melts = []
+ calib_y = []
+
+ results = []
+
+ for data, ref_value in zip(self.reference, self.ref_list):
+ try:
+ idx = data.get_run(rate, mode='h')
+ except ValueError:
+ return None, None, []
+
+ measurement = data.flow_data(idx)
+
+ for trans_temp, enthalpy in ref_value.transitions:
+ real_tm.append(trans_temp)
+ t_low_lim, t_high_lim = trans_temp - 10, trans_temp + 20
+
+ low_border = np.argmin(np.abs(measurement[0] - t_low_lim))
+ high_border = np.argmin(np.abs(measurement[0] - t_high_lim))
+ ref_zoom = measurement[:, 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]])
+
+ 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])]
+ integ_limit = (np.argmin(np.abs(ref_zoom[0] - peak_max[0] + 3)),
+ np.argmin(np.abs(ref_zoom[0] - peak_max[0] - 3)))
+
+ # substract baseline around reference peaks
+ x_val = np.array([[ref_zoom[0, integ_limit[0]], 1], [ref_zoom[0, integ_limit[1]], 1]])
+ y_val = np.array([ref_zoom[1, integ_limit[0]], ref_zoom[1, integ_limit[1]]])
+
+ 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 (+50/rate))
+ ref_grad = np.gradient(ref_zoom[1])
+ max_grad = np.argmax(ref_grad)
+
+ grad_pos = max_grad-int(50 / rate), max_grad
+
+ x_val = np.array([[ref_zoom[0, grad_pos[0]], 1],
+ [ref_zoom[0, grad_pos[1]], 1]])
+ y_val = np.array([ref_zoom[1, grad_pos[0]],
+ ref_zoom[1,grad_pos[1]]])
+ sol = np.linalg.solve(x_val, y_val)
+ onset = sol[0] * ref_zoom[0] + sol[1]
+
+ melts.append(-sol[1] / sol[0])
+
+ if enthalpy is not None:
+ # integrate over peak to calibrate y axis
+ # delta H in J/g: Integrate Peak over time and divide by weight
+ area = simpson(ref_zoom[1, integ_limit[0]:integ_limit[1]],
+ ref_zoom[2, integ_limit[0]:integ_limit[1]],
+ even='avg') * 1e-3
+ calib_y.append(enthalpy / (area / data.weight))
+
+ results.append([ref_zoom, onset, ref_zoom[:, grad_pos]])
+
+ if len(melts) > 1:
+ A = np.vstack([melts, np.ones(len(melts))]).T
+ y = np.array(real_tm)
+
+ calib_x = np.linalg.lstsq(A, y, rcond=None)[0]
+ else:
+ calib_x = [1, 0]
+
+ if calib_y:
+ calib_y = 1e-3 * np.mean(calib_y) * 60. / self.sample.weight / rate
+ else:
+ calib_y = 1
+
+ return calib_x, calib_y, results
+
+ def get_data(self, idx, slope='iso'):
+ if self.sample.steps[idx][0] == 'i':
+ raise ValueError('baseline correction is not implemented for isotherms')
+
+ if slope not in ['iso', 'curve', None]:
+ raise ValueError(f'Unknown argument for slope {slope}, not "iso" or "curve"')
+
+ sample_data = self.sample.flow_data(idx)
+ raw_sample = sample_data.copy()
+ empty_data = None
+
+ mode, rate, _, _ = self.sample.steps[idx]
+
+ empty_y = 0
+ idx_empty = 0
+ if self.empty is not None:
+ try:
+ idx_empty = self.empty.get_run(rate, mode=mode)
+ except ValueError:
+ raise ValueError(f'Empty measurement has no heat rate {rate} K/min')
+
+ empty_data = self.empty.flow_data(idx_empty)
+
+ empty_y = empty_data[1]
+ if self.sample.length(idx) != self.empty.length(idx_empty):
+ with np.errstate(all='ignore'):
+ empty_y = interp1d(empty_data[0], empty_data[1], fill_value='extrapolate')(sample_data[0])
+
+ sample_data[1] -= empty_y
+ drift_value = sample_data.copy()[(2, 1), :]
+
+ mean_isotherms = []
+ for offset in [-1, 1]:
+ # read isotherms
+ isotherm_sample = self.sample.isotherm_data(idx+offset)
+ if self.empty is not None:
+ isotherm_empty = self.empty.isotherm_data(idx_empty+offset)
+ isotherm_sample[1] -= isotherm_empty[1, 200:-200].mean()
+
+ # get mean isotherm value
+ m = isotherm_sample[1, 200:-200].mean()
+ mean_isotherms.append(m)
+
+ if offset == -1:
+ drift_value = np.c_[isotherm_sample, drift_value]
+ else:
+ drift_value = np.c_[drift_value, isotherm_sample]
+
+ if slope is not None:
+ if slope == 'iso':
+ # calculate slope from difference between isotherms
+ m = (mean_isotherms[1] - mean_isotherms[0]) / (sample_data[2, -1] - sample_data[2, 0])
+ offset = sample_data[1, 200]
+ else:
+ # calculate mean slope of heat flow from points in the beginning
+ offset = sample_data[1, 200]
+ grad = np.gradient(sample_data[1, :], sample_data[2, :])
+ m = grad[(grad < grad.mean()+grad.std()/5)*(grad > grad.mean()-grad.std()/5)].mean()
+
+ sample_data[1] -= m * (sample_data[2] - sample_data[2, 200]) + offset
+ line = np.array([[sample_data[2, 0], sample_data[2, -1]],
+ [m * (sample_data[2, 200] - sample_data[2, 200]) + offset,
+ m * (sample_data[2, -1] - sample_data[2, 200]) + offset]])
+
+ else:
+ line = np.array([[sample_data[2, 0], sample_data[2, -1]], [0, 0]])
+
+ return raw_sample, drift_value, sample_data, empty_data, line
diff --git a/nmreval/io/fcbatchreader.py b/nmreval/io/fcbatchreader.py
new file mode 100644
index 0000000..8252821
--- /dev/null
+++ b/nmreval/io/fcbatchreader.py
@@ -0,0 +1,399 @@
+import pathlib
+from typing import Union
+
+import matplotlib.pyplot as plt
+from scipy.optimize import curve_fit
+import numpy as np
+
+from ..data.points import Points
+from .asciireader import AsciiReader
+from .hdfreader import HdfReader
+from ..utils.utils import get_temperature, roundrobin
+
+
+class FCReader:
+ def __init__(self, fname: Union[list, str]):
+ if type(fname) != list:
+ self.fnames = [fname]
+ else:
+ self.fnames = fname
+
+ self.temperatures = []
+ self.data = {}
+ self.filenames = {}
+ self.t_params = {}
+ self.f_params = {}
+
+ def __call__(self, fname: Union[list, str]):
+ if isinstance(fname, (str, pathlib.Path)):
+ self.fnames = [fname]
+ else:
+ self.fnames = fname
+
+ self.temperatures = []
+ self.data = {}
+ self.filenames = {}
+ self.t_params = {}
+ self.f_params = {}
+
+ def load_magnetization(self, region: tuple = None, overwrite: bool = True):
+ for filename in self.fnames:
+ filename = pathlib.Path(filename)
+ found_temperature = get_temperature(filename.stem)
+ if found_temperature == -1:
+ found_temperature = filename.stem
+
+ if filename.is_file():
+ if region is None:
+ _temp = self._read_from_hdf(filename)
+ else:
+ _temp = self._read_signals(filename, region)
+
+ elif filename.is_dir():
+ _temp = self._read_from_dir(filename)
+
+ else:
+ raise TypeError
+
+ if not _temp:
+ raise OSError(-666, f'No magnetization found for {filename.name}.', filename.name)
+
+ self.data[found_temperature] = _temp
+ self.filenames[found_temperature] = filename
+
+ fname_no_ext = filename.with_suffix('')
+ data_path = fname_no_ext.joinpath('data')
+ # data_path.mkdir(parents=True, exist_ok=True)
+
+ for k, v in sorted(_temp.items()):
+ save_name = data_path.joinpath(f'{filename.stem}_{k:011.2f}'.replace('.', 'p') + '.dat')
+ if save_name.exists() and (not overwrite):
+ continue
+ v.savetxt(save_name)
+
+ @staticmethod
+ def _read_from_hdf(filename: Union[str, pathlib.Path]) -> dict:
+ _temp = {}
+ reader = HdfReader(filename)
+ for mag in reader.get_selected('mag', dtype='points'):
+ _temp[mag.value] = mag
+
+ return _temp
+
+ @staticmethod
+ def _read_from_dir(filename) -> dict:
+ fname_no_ext = filename.with_suffix('')
+ data_path = fname_no_ext / 'data'
+ _temp = {}
+
+ for mag in data_path.glob('*.dat'):
+ d = AsciiReader(mag).export()
+ for v in d:
+ _temp[v.value] = v
+ break
+
+ return _temp
+
+ @staticmethod
+ def _read_signals(filename, region: tuple = None) -> dict:
+ reader = HdfReader(filename)
+ start = 0
+ stop = 30e-5
+
+ # This is one set with attributes to find default start:stop values
+ try:
+ p = reader.parameters('/ABS_ACC_FID')
+ start = p['start']
+ stop = p['stop']
+ except:
+ pass
+
+ if region is None:
+ region = (start, stop)
+
+ if region[0] is None:
+ region = (start, region[1])
+ if region[1] is None:
+ region = (region[0], stop)
+
+ sig = reader.get_selected('/data/B=*/ACC_ABS_FID*', dtype='signal')
+ _temp = {}
+ for s in sig:
+ pts = s.points([region])
+ b = s.group
+ if b not in _temp:
+ _temp[b] = []
+
+ _temp[b].append([s.value, *[pp[1] for pp in pts]])
+
+ for b, m in sorted(_temp.items()):
+ m = np.array(m)
+ _temp[b] = Points(x=m[:, 0], y=m[:, 1], value=b, name=f'B={b}').sort()
+
+ return _temp
+
+ def fit(self, kww: bool = True, save_fits: bool = True, save_fig: bool = True):
+ if kww:
+ bounds = ([-np.inf, -np.inf, 0.0, 0.0], [np.inf, np.inf, np.inf, np.inf])
+ else:
+ bounds = ([-np.inf, -np.inf, 0.0, 0.99999], [np.inf, np.inf, np.inf, 1.0])
+
+ for temperature, filename in self.filenames.items():
+ fname_no_ext = filename.with_suffix('')
+
+ if save_fits:
+ fit_path = fname_no_ext.joinpath('fit')
+ fit_path.mkdir(parents=True, exist_ok=True)
+
+ if save_fig:
+ image_path = fname_no_ext.joinpath('png')
+ image_path.mkdir(parents=True, exist_ok=True)
+
+ header = 'm0\tt1\tbeta\toff\n'
+
+ params = []
+ errors = []
+ freqs = []
+
+ for k, v in sorted(self.data[temperature].items()):
+ freqs.append(k)
+
+ # fit
+ p0 = [v.y[0], v.y[-1]-v.y[0], v.x[int(0.5*len(v.x) - 0.5)], 1]
+ try:
+ p0, pcov = curve_fit(FCReader.kww, v.x, v.y, p0=p0, bounds=bounds, max_nfev=500*len(v))
+ except RuntimeError:
+ continue
+
+ perr = np.sqrt(np.diag(pcov))
+ params.append(p0)
+ errors.append(perr)
+
+ if isinstance(temperature, float):
+ new_entry = list(roundrobin([temperature], p0, perr))
+
+ try:
+ self.f_params[k].append(new_entry)
+ except KeyError:
+ self.f_params[k] = [new_entry]
+
+ if save_fits or save_fig:
+ xplot = np.geomspace(v.x[0], v.x[-1], num=10*len(v.x))
+ yplot = FCReader.kww(xplot, *p0)
+ save_name = f'{filename.stem}_{k:011.2f}'.replace('.', 'p') + '.dat'
+
+ if save_fits:
+ np.savetxt(fit_path.joinpath(save_name), np.c_[xplot, yplot],
+ header=header+'\t'.join([f'{p}+/-{err}' for p, err in zip(p0, perr)]))
+
+ if save_fig:
+ fig, ax = plt.subplots()
+ ax.set_xlabel('t / s')
+ ax.set_ylabel('M')
+ axheader = f'T1: {p0[2]:.4g}(+/-{perr[2]:.4g}) beta: {p0[3]:.4g}(+/-{perr[3]:.4g})'
+ ax.set_title(f'f = {k:.4g} Hz\n{axheader}')
+ ax.semilogx(v.x, v.y, 'o')
+ ax.semilogx(xplot, yplot, '-')
+ fig.savefig(image_path.joinpath(save_name).with_suffix('.png'))
+ plt.close(fig)
+
+ freqs = np.array(freqs)
+ params = np.array(params)
+ errors = np.array(errors)
+
+ # das ist jetzt eher haesslich
+ self.t_params[temperature] = np.c_[freqs,
+ params[:, 0], errors[:, 0], params[:, 1], errors[:, 1],
+ params[:, 2], errors[:, 2], params[:, 3], errors[:, 3]]
+
+ for k, val in self.f_params.items():
+ val = np.array(val)
+ np.nan_to_num(val)
+ self.f_params[k] = val
+
+ def write_parameter(self, path, kind):
+ path = pathlib.Path(path)
+ path.mkdir(parents=True, exist_ok=True)
+
+ if kind == 'temp':
+ _params = self.t_params
+ fmt = '3.2f'
+ else:
+ _params = self.f_params
+ fmt = '011.2f'
+
+ save_path = path.joinpath(kind)
+ if not save_path.exists():
+ save_path.mkdir(parents=True)
+
+ for key, par in _params.items():
+ try:
+ np.savetxt(save_path.joinpath(f'fitparameter_{key:{fmt}}.dat'), par,
+ header=f'{key}\nM0\tM0_err\tOff\tOff_err\tT1\tT1_err\tbeta\tbeta_err')
+ except ValueError:
+ np.savetxt(save_path.joinpath(f'fitparameter_{key}.dat'), par,
+ header=f'{key}\nM0\tM0_err\tOff\tOff_err\tT1\tT1_err\tbeta\tbeta_err')
+
+ @staticmethod
+ def _write_parameter(key, parameter, path, kind, fmt):
+ save_path = path.joinpath(kind)
+ save_path.mkdir(parents=True, exist_ok=True)
+ try:
+ np.savetxt(save_path.joinpath(f'fitparameter_{key:{fmt}}.dat'), parameter,
+ header=f'{key}\nM0\tM0_err\tOff\tOff_err\tT1\tT1_err\tbeta\tbeta_err')
+ except ValueError:
+ np.savetxt(save_path.joinpath(f'fitparameter_{key}.dat'), parameter,
+ header=f'{key}\nM0\tM0_err\tOff\tOff_err\tT1\tT1_err\tbeta\tbeta_err')
+
+ @staticmethod
+ def _plot_parameter(key, param, fig_mag, fig_t1, fig_beta):
+ ax_mag = fig_mag.get_axes()[0]
+ ax_t1 = fig_t1.get_axes()[0]
+ ax_beta = fig_beta.get_axes()[0]
+
+ pl, = ax_mag.plot(param[:, 0], param[:, 1], 'o', label=key)
+ ax_mag.plot(param[:, 0], param[:, 3], 's', color=pl.get_color())
+ ax_t1.plot(param[:, 0], param[:, 5], 'o', label=key)
+ ax_beta.plot(param[:, 0], param[:, 7], 'o', label=key)
+
+ @staticmethod
+ def _save_parameter_plot(path, kind, fig_mag, fig_t1, fig_beta):
+ ax_mag = fig_mag.get_axes()[0]
+ ax_t1 = fig_t1.get_axes()[0]
+ ax_beta = fig_beta.get_axes()[0]
+
+ for a in [ax_mag, ax_t1, ax_beta]:
+ if kind == 'freq':
+ a.legend(loc='upper left', bbox_to_anchor=(1, 1), ncol=2)
+ a.set_xlabel('T / K')
+ else:
+ a.set_xscale('log')
+ a.legend(loc='upper left', bbox_to_anchor=(1, 1))
+ a.set_xlabel('f / Hz')
+
+ ax_t1.set_yscale('log')
+ ax_t1.set_ylabel('T1 / s')
+ ax_beta.set_ylabel('beta')
+ ax_mag.set_ylabel('M0 (squares), Offset (circles)')
+
+ fig_beta.savefig(path.joinpath(f'beta_{kind}.png'), bbox_inches="tight")
+ fig_mag.savefig(path.joinpath(f'mag_{kind}.png'), bbox_inches="tight")
+ fig_t1.savefig(path.joinpath(f't1_{kind}.png'), bbox_inches="tight")
+ plt.close(fig_mag)
+ plt.close(fig_beta)
+ plt.close(fig_t1)
+
+ def plot_parameter(self, path, kind):
+ path = pathlib.Path(path)
+ path.mkdir(parents=True, exist_ok=True)
+
+ fig_mag, ax_mag = plt.subplots()
+ fig_t1, ax_t1 = plt.subplots()
+ fig_beta, ax_beta = plt.subplots()
+
+ if kind == 'temp':
+ _params = self.t_params
+ else:
+ _params = self.f_params
+
+ save_path = path.joinpath(kind)
+ if not save_path.exists():
+ save_path.mkdir(parents=True)
+
+ for key, par in _params.items():
+ pl, = ax_mag.plot(par[:, 0], par[:, 1], 'o', label=key)
+ ax_mag.plot(par[:, 0], par[:, 3], 's', color=pl.get_color())
+ ax_t1.plot(par[:, 0], par[:, 5], 'o', label=key)
+ ax_beta.plot(par[:, 0], par[:, 7], 'o', label=key)
+
+ for a in [ax_mag, ax_t1, ax_beta]:
+ if kind == 'freq':
+ a.legend(loc='upper left', bbox_to_anchor=(1, 1), ncol=2)
+ a.set_xlabel('T / K')
+ else:
+ a.set_xscale('log')
+ a.legend(loc='upper left', bbox_to_anchor=(1, 1))
+ a.set_xlabel('f / Hz')
+
+ ax_t1.set_yscale('log')
+ ax_t1.set_ylabel('T1 / s')
+ ax_beta.set_ylabel('beta')
+ ax_mag.set_ylabel('M0 (squares), Offset (circles)')
+
+ fig_beta.savefig(path.joinpath(f'beta_{kind}.png'), bbox_inches="tight")
+ fig_mag.savefig(path.joinpath(f'mag_{kind}.png'), bbox_inches="tight")
+ fig_t1.savefig(path.joinpath(f't1_{kind}.png'), bbox_inches="tight")
+ plt.close(fig_mag)
+ plt.close(fig_beta)
+ plt.close(fig_t1)
+
+ def get_parameter(self, parameter='all', kind='freq', path=None, write=True, plot=True):
+ param_list = []
+ if isinstance(parameter, str):
+ parameter = [parameter]
+
+ for p in parameter:
+ plower = p.lower()
+ if plower == 'all':
+ param_list = [(0, 'M0'), (1, 'Off'), (2, 'T1'), (3, 'beta')]
+ break
+
+ for i, name in [(0, 'M0'), (1, 'Off'), (2, 'T1'), (3, 'beta')]:
+ if plower == name.lower():
+ param_list.append((i, name))
+
+ if write or plot:
+ if path is None:
+ raise ValueError('Please specify a path to write to.')
+ else:
+ path = pathlib.Path(path)
+ path.mkdir(parents=True, exist_ok=True)
+
+ if kind == 'temp':
+ _params = self.t_params
+ fmt = '3.2f'
+ else:
+ _params = self.f_params
+ fmt = '011.2f'
+
+ if plot:
+ fig_mag, ax_mag = plt.subplots()
+ fig_t1, ax_t1 = plt.subplots()
+ fig_beta, ax_beta = plt.subplots()
+
+ ret_val = []
+ for key, par in _params.items():
+ try:
+ value = float(key)
+ except ValueError:
+ value = None
+
+ if write:
+ self._write_parameter(key, par, path, kind, fmt)
+
+ if plot:
+ self._plot_parameter(key, par, fig_mag, fig_t1, fig_beta)
+
+ for i, name in param_list:
+ ret_val.append(Points(x=par[:, 0], y=par[:, 2*i+1], y_err=par[:, 2*i+2],
+ name=f'{key} ({name})', value=value))
+
+ if plot:
+ self._save_parameter_plot(path, kind, fig_mag, fig_t1, fig_beta)
+
+ return ret_val
+
+ @staticmethod
+ def kww(x, m0, off, t1, beta):
+ return m0 * np.exp(-(x/t1)**beta) + off
+
+
+if __name__ == '__main__':
+ test = ['/autohome/dominik/nmreval/testdata/fc_test/abc298K.h5',
+ '/autohome/dominik/nmreval/testdata/fc_test/abc.h5']
+
+ fc_reader = FCReader(test)
+ fc_reader.load_magnetization(region=(90e-6, 120e-6), overwrite=True)
+ fc_reader.fit(save_fits=True, save_fig=True)
+ fc_reader.get_parameter(path='/autohome/dominik/nmreval/testdata/fc_test', kind='temp', write=True, plot=True)
+ fc_reader.get_parameter(path='/autohome/dominik/nmreval/testdata/fc_test', kind='freq', write=True, plot=True)
diff --git a/nmreval/io/graceeditor.py b/nmreval/io/graceeditor.py
new file mode 100644
index 0000000..bd53a5e
--- /dev/null
+++ b/nmreval/io/graceeditor.py
@@ -0,0 +1,711 @@
+import pathlib
+import re
+from io import StringIO
+from typing import Optional, Tuple
+
+import numpy as np
+from numpy import log10
+
+from ..configs import config_paths
+
+
+class GraceEditor:
+ _RE_HEADER_START = re.compile(r'# Grace project file')
+ _RE_HEADER_END = re.compile(r'@timestamp def')
+ _RE_OBJECT_START = re.compile(r'@with (box|ellipse|string|)')
+ _RE_REGION_START = re.compile(r'@r(\w+) (on|off)', re.IGNORECASE)
+ _RE_GRAPH_START = re.compile(r'@g(\w+) (on|off)', re.IGNORECASE)
+ _RE_SET_START = re.compile(r'@target G(\d+).S(\d+)', re.IGNORECASE)
+ _RE_COLOR = re.compile(r'@map color (?P\d+) to '
+ r'\((?P\d+), (?P\d+), (?P\d+)\),\s+\"(?P.+)\"',
+ re.MULTILINE)
+ _RE_FONT = re.compile(r'@map font (?P\d+) to \"(?P.+?)\",\s+\"(?P.+)\"')
+ _RE_PAGE_SIZE = re.compile(r'@page size (\d+), (\d+)')
+
+ def __init__(self, filename=None):
+ self.header = GraceHeader()
+ self.drawing_objects = []
+ self.regions = []
+ self.graphs = []
+ self.sets = []
+
+ _default_file = config_paths().joinpath('Default.agr')
+
+ if filename == _default_file:
+ self.defaults = self
+ else:
+ self.defaults = GraceEditor(filename=_default_file)
+
+ self.file = filename
+ if filename is not None:
+ self.parse(filename)
+
+ def __call__(self, filename: str):
+ self.clear()
+ self.parse(filename)
+
+ return self
+
+ def __str__(self):
+ s = str(self.header)
+ s += ''.join(map(str, self.regions))
+ s += ''.join(map(str, self.drawing_objects))
+ s += ''.join(map(str, self.graphs))
+ s += ''.join(map(str, self.sets))
+
+ return s
+
+ def clear(self):
+ self.header = GraceHeader()
+ self.drawing_objects = []
+ self.regions = []
+ self.graphs = []
+ self.sets = []
+
+ def new_graph(self):
+ if self.file is None:
+ self.parse(config_paths().joinpath('Default.agr'))
+ else:
+ max_idx = 0
+ for g in self.graphs:
+ max_idx = max(g.idx, max_idx)
+ self.graphs.append(self.defaults.graphs[-1].copy())
+ self.graphs[-1].renumber(max_idx+1)
+
+ return self.graphs[-1]
+
+ def new_set(self, graph):
+ s = None
+ g_idx = -1
+ for g in self.graphs:
+ if g.idx == graph:
+ s = g.new_set()
+ g_idx = g.idx
+ break
+
+ if s is None:
+ raise ValueError(f'Graph {graph} was not found.')
+
+ self.sets.append(GraceSet(g_idx, s.idx))
+ self.sets[-1].append(f'@target G{g_idx}.S{s.idx}\n')
+ self.sets[-1].append('@type xy\n')
+ self.sets[-1].append('&\n')
+
+ return s
+
+ def parse(self, filename):
+ self.file = pathlib.Path(filename)
+
+ # we start always with the header
+ current_pos = 'header'
+ with self.file.open('r') as f:
+ for line_nr, line in enumerate(f):
+
+ # lots of states to check
+ # agr file order: header, drawing object, region, graph, set
+ if current_pos == 'header':
+ self.header.append(line)
+
+ # already at end of header
+ if self._RE_HEADER_END.match(line):
+ current_pos = 'header_end'
+ elif self._RE_GRAPH_START.match(line):
+ current_pos = 'graph'
+ self.graphs.append(GraceGraph(int(self._RE_GRAPH_START.match(line).group(1))))
+ self.graphs[-1].append(line)
+
+ elif current_pos == 'header_end':
+ # what comes after the header? region, graph or drawing object?
+ if self._RE_REGION_START.match(line):
+ current_pos = 'region'
+ self.regions.append(GraceRegion())
+ self.regions[-1].append(line)
+ elif self._RE_GRAPH_START.match(line):
+ current_pos = 'graph'
+ self.graphs.append(GraceGraph(int(self._RE_GRAPH_START.match(line).group(1))))
+ self.graphs[-1].append(line)
+ elif self._RE_OBJECT_START.match(line):
+ current_pos = 'drawing'
+ self.drawing_objects.append(GraceDrawing())
+ self.drawing_objects[-1].append(line)
+ else:
+ print('What happened with this line?', line.rstrip())
+
+ elif current_pos == 'drawing':
+ if self._RE_REGION_START.match(line):
+ current_pos = 'region'
+ self.regions.append(GraceRegion())
+ self.regions[-1].append(line)
+ else:
+ m = self._RE_GRAPH_START.match(line)
+ if m:
+ current_pos = 'graph'
+ self.graphs.append(GraceGraph(int(m.group(1))))
+ self.graphs[-1].append(line)
+ else:
+ if self._RE_OBJECT_START.match(line):
+ self.drawing_objects.append(GraceDrawing())
+ self.drawing_objects[-1].append(line)
+
+ elif current_pos == 'region':
+ # regions are followed by regions or graphs
+ m = self._RE_GRAPH_START.match(line)
+ if m:
+ current_pos = 'graph'
+ self.graphs.append(GraceGraph(int(m.group(1))))
+ self.graphs[-1].append(line)
+ else:
+ if self._RE_REGION_START.match(line):
+ self.regions.append(GraceRegion())
+ self.regions[-1].append(line)
+
+ elif current_pos == 'graph':
+ m = self._RE_SET_START.match(line)
+ if m:
+ current_pos = 'set'
+ self.sets.append(GraceSet(int(m.group(1)), int(m.group(2))))
+ self.sets[-1].append(line)
+ else:
+ m = self._RE_GRAPH_START.match(line)
+ if m:
+ self.graphs.append(GraceGraph(int(m.group(1))))
+ self.graphs[-1].append(line)
+
+ elif current_pos == 'set':
+ m = self._RE_SET_START.match(line)
+ if m:
+ current_pos = 'set'
+ self.sets.append(GraceSet(int(m.group(1)), int(m.group(2))))
+ self.sets[-1].append(line)
+
+ @property
+ def colors(self):
+ _colors = {}
+ for line in self.header:
+ m = self._RE_COLOR.match(line)
+ if m:
+ _colors[int(m['id'])] = (m['disp'], (int(m['red']), int(m['green']), int(m['blue'])))
+
+ return _colors
+
+ def get_color(self, color_num):
+ color_num = str(color_num)
+ for line in self.header:
+ m = self._RE_COLOR.match(line)
+ if m:
+ if m['id'] == color_num:
+ return m['disp'], (int(m['red']), int(m['green']), int(m['blue']))
+
+ return
+
+ def set_color(self, name: str, rgb: tuple, idx: int = None):
+ pos = 'before_colors'
+ cnt = 0
+ for i, line in enumerate(self.header):
+ m = self._RE_COLOR.match(line)
+ if m:
+ pos = 'in_colors'
+ cnt += 1
+ if (int(m['red']), int(m['green']), int(m['blue'])) == rgb:
+ print(f'color already defined as {m["disp"]}')
+ return m['id']
+ elif m['id'] == idx:
+ self.header[i] = f'@map color {idx} to {rgb}, "{name}"\n'
+
+ elif pos == 'in_colors':
+ # landing here for first mismatch after all colors
+ if cnt == 64:
+ raise ValueError('Maximum numbers of color reached, no new color created')
+ elif idx is None:
+ self.header.insert(i, f'@map color {cnt} to {rgb}, "{name}"\n')
+ return cnt
+ else:
+ self.header.insert(i, f'@map color {idx} to {rgb}, "{name}"\n')
+ return idx
+
+ @property
+ def fonts(self):
+ _fonts = {}
+ for line in self.header:
+ m = self._RE_FONT.match(line)
+ if m:
+ _fonts[m['id']] = m['disp']
+
+ return _fonts
+
+ @property
+ def size(self):
+ for line in self.header:
+ m = self._RE_PAGE_SIZE.match(line)
+ if m:
+ return int(m.group(1)) / 72 * 2.54, int(m.group(2)) / 72 * 2.54
+
+ @size.setter
+ def size(self, value: tuple):
+ width = value[0] * 72 / 2.54
+ height = value[1] * 72 / 2.54
+
+ for i, line in enumerate(self.header):
+ m = self._RE_PAGE_SIZE.match(line)
+ if m:
+ self.header[i] = f'@page size {width:.0f}, {height:.0f}\n'
+ break
+
+ def convert(self, value, direction='rel'):
+ page_size = min(self.size)
+
+ if direction.startswith('rel'):
+ return value / page_size
+ elif direction.startswith('abs'):
+ return value * page_size
+ else:
+ raise ValueError(f'Use `abs`, `absolute`, `rel`, or `relative` to convert, found {direction} instead.')
+
+ def get_property(self, g, *args):
+ if len(args) == 1:
+ return self.graphs[g].get_property(args[0])
+ elif len(args) == 2:
+ return self.graphs[g].set[args[0]].get_property(args[1])
+ else:
+ raise TypeError(f'get_property takes two or three arguments ({len(args)+1} given)')
+
+ def set_property(self, g, *args):
+ if len(args) == 2:
+ self.graphs[g][args[0]] = args[1]
+ elif len(args) == 3:
+ self.graphs[g].set[args[0]][args[1]] = args[2]
+ else:
+ raise TypeError(f'get_property takes three or four arguments ({len(args)+1} given)')
+
+ def dataset(self, g, s):
+ cmp_to = (int(g), int(s))
+ for dataset in self.sets:
+ if dataset.graph_set == cmp_to:
+ return dataset
+ return
+
+ def write(self, fname):
+ outfile = pathlib.Path(fname)
+ with outfile.open('w') as f:
+ f.write(str(self.header))
+ f.flush()
+
+ for element in [self.regions, self.drawing_objects, self.graphs, self.sets]:
+ for obj in element:
+ f.write(str(obj))
+ f.flush()
+
+
+class GraceDrawing(list):
+ def __str__(self):
+ return ''.join(self)
+
+
+class GraceHeader(list):
+ def __str__(self):
+ return ''.join(self)
+
+
+class GraceProperties(list):
+ _RE_ENTRY = re.compile(r'(?!.*(on|off)$)' # ignore lines that end with on or off
+ r'@\s*(?P[gs]\d+)?\s*' # @ maybe followed by g0 or s12
+ r'(?P[\w\s]*)\s+' # key: stops at last space unless comma-separated values
+ r'(?P(?:\s*[\d\w.+-]+\s*,)*\s*[\\{}\"\w.+\- ]+)', # one value, maybe more with commas
+ re.IGNORECASE | re.VERBOSE)
+ _RE_ONOFF = re.compile(r'@\s(?P[gs]\d+)*\s*(?P[\w\s]*)\s+(on|off)')
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ def get_property(self, key: str):
+ props = []
+ for line in self:
+ m = self._RE_ENTRY.match(line)
+ if m:
+ k = m.group('key').strip()
+ if k == key:
+ props.append(_convert_to_value(m.group('val')))
+
+ if len(props) == 0:
+ raise KeyError(f'No attribute `{key}` found')
+
+ elif len(props) == 1:
+ return props[0]
+
+ else:
+ return
+
+ def set_property(self, **kwargs):
+ raise NotImplementedError
+
+ def _set_property(self, **kwargs):
+
+ for i, line in enumerate(self):
+ m = self._RE_ENTRY.match(line)
+ if m:
+ k = m.group('key')
+ if k in kwargs:
+ # re.sub is probably better, but I could not work it out for labels with backslashes (\S)
+ self[i] = line[:m.start('val')] + str(kwargs[k]) + line[m.end('val'):]
+ kwargs.pop(k)
+
+ if not kwargs:
+ break
+
+ return kwargs
+
+ def get_all_properties(self):
+ props = {}
+ for line in self:
+ m = self._RE_ENTRY.match(line)
+ if m:
+ key = m.group('key')
+ val = _convert_to_value(m.group('val'))
+ if key not in props:
+ props[key] = val
+ elif isinstance(props[key], tuple):
+ props[key] = props[key] + (val,)
+ else:
+ props[key] = props[key], val
+
+ props.pop('with')
+
+ return props
+
+ def get_onoff(self, key: str) -> Optional[bool]:
+ onoff_pattern = re.compile(rf'@\s*{key}\s+(?P(on|off))', re.IGNORECASE)
+ for i, line in enumerate(self):
+ m = onoff_pattern.match(line)
+
+ if m:
+ return m.group('val') == 'on'
+ return
+
+ def set_onoff(self, key: str, onoff: bool):
+ raise NotImplementedError
+
+ def _set_onoff(self, key , onoff: bool):
+
+ bool_str = 'on' if onoff else 'off'
+ for i, line in enumerate(self):
+ m = key.match(line)
+ if m:
+ self[i] = line[:m.start('val')] + bool_str + '\n'
+
+ return True
+
+ return False
+
+
+class GraceGraph(GraceProperties):
+ _RE_SET_START = re.compile(r'@\s*s(\d+)\s+\w*', re.IGNORECASE)
+
+ def __init__(self, idx):
+ super().__init__()
+ self.set = []
+ self.idx = int(idx)
+ self.__current_pos = 'graph'
+ self.__current_idx = None
+
+ def __str__(self):
+ return ''.join(self) + ''.join(map(str, self.set))
+
+ def copy(self):
+ _cp = GraceGraph(self.idx)
+ for line in self:
+ _cp.append(line)
+
+ return _cp
+
+ def append(self, line: str):
+ m = self._RE_SET_START.match(line)
+ if m:
+ if self.__current_pos == 'graph' or self.__current_idx != m.group(1):
+ self.__current_pos = 'set'
+ self.__current_idx = m.group(1)
+ self.set.append(GraceSetProps(self.__current_idx))
+
+ if self.__current_pos == 'graph':
+ super().append(line)
+ elif self.__current_pos == 'set':
+ self.set[-1].append(line)
+ else:
+ raise TypeError('Unknown state')
+
+ def new_set(self):
+ max_set_idx = -1
+ for s in self.set:
+ max_set_idx = max(max_set_idx, s.idx)
+ self.set.append(GraceSetProps(max_set_idx+1))
+
+ return self.set[-1]
+
+ def renumber(self, idx: int):
+ number_re = re.compile('g' + str(self.idx), re.IGNORECASE)
+ for i, line in enumerate(self):
+ m = list(number_re.finditer(line))
+ if m:
+ self[i] = line[:m[0].start()+1] + str(idx) + line[m[0].end():]
+
+ self.idx = idx
+
+ def set_limits(self, x=None, y=None):
+ print(x, y)
+ for i, line in enumerate(self):
+ m = self._RE_ENTRY.match(line)
+ if m and m.group('key') == 'world':
+ _prev_lims = _convert_to_value(m.group('val'))
+ if x is None:
+ x = _prev_lims[0]
+ if y is None:
+ y = _prev_lims[1]
+
+ self[i] = f'@ world {x[0]}, {y[0]}, {x[1]}, {y[1]}\n'
+
+ def set_log(self, x=None, y=None):
+ kwargs = {}
+ if x is not None:
+ kwargs['xaxes scale'] = 'Logarithmic' if x else 'Normal'
+ if y is not None:
+ kwargs['yaxes scale'] = 'Logarithmic' if y else 'Normal'
+
+ for i, line in enumerate(self):
+ m = self._RE_ENTRY.match(line)
+ if m and m.group('key') in kwargs:
+ self[i] = re.sub(m.group('val'), kwargs[m.group('key')], line)
+
+ def set_property(self, **kwargs):
+ remaining_kw = self._set_property(**kwargs)
+
+ for k, v in remaining_kw.items():
+ self.append(f'@ {k} {v}\n')
+
+ def set_axis_property(self, axis: str, **kwargs):
+ ax_kwargs = {}
+ for k, v in kwargs.items():
+ if k in ['invert', 'scale']:
+ ax_kwargs[axis + 'axes ' + k] = v
+ else:
+ ax_kwargs[axis + 'axis ' + k] = v
+
+ self.set_property(**ax_kwargs)
+
+ def get_axis_property(self, axis: str, key: str):
+ if key in ['invert', 'scale']:
+ ax_key = axis + 'axes ' + key
+ else:
+ ax_key = axis + 'axis ' + key
+
+ return self.get_property(ax_key)
+
+ def set_axis_onoff(self, axis: str, key: str, onoff: bool):
+ if key in ['invert', 'scale']:
+ ax_key = axis + 'axes ' + key
+ else:
+ ax_key = axis + 'axis ' + key
+
+ return self.set_onoff(ax_key, onoff)
+
+ def set_onoff(self, key: str, onoff: bool):
+ onoff_pattern = re.compile(rf'@\s*{key}\s+(?P(on|off))', re.IGNORECASE)
+ if not self._set_onoff(onoff_pattern, onoff):
+ self.append(f'@ {key} {"on" if onoff else "off"}\n')
+
+ def view_to_world(self, pos) -> Tuple[float, float]:
+ view = self.get_property('view')
+ world = self.get_property('world')
+
+ log_x = self.get_property('xaxes scale').lower()
+ if log_x == 'logarithmic':
+ x_world = 10**(log10(world[2]/world[0]) / (view[2]-view[0]) * (view[2]-pos[0]) + log10(world[2]))
+ else:
+ x_world = (world[2]-world[0]) / (view[2]-view[0]) * (view[2]-pos[0]) + world[2]
+
+ log_y = self.get_property('yaxes scale').lower()
+ if log_y == 'logarithmic':
+ y_world = 10**(log10(world[3]/world[1]) / (view[3]-view[1]) * (view[3]-pos[1]) + log10(world[3]))
+ else:
+ y_world = (world[3] - world[1]) / (view[3]-view[1]) * (view[3]-pos[1]) + world[3]
+
+ return x_world, y_world
+
+ def world_to_view(self, pos) -> Tuple[float, float]:
+ view = self.get_property('view')
+ world = self.get_property('world')
+
+ view_pos = pos[:]
+
+ log_axes = [self.get_property('xaxes scale'), self.get_property('yaxes scale')]
+ for i in [0, 1]:
+ if log_axes[i].lower() == 'logarithmic':
+ view_pos[i] = (view[i+2]-view[i]) / log10(world[i+2]/world[i]) * log10(pos[i]/world[i]) + view[i]
+ else:
+ view_pos[i] = (view[i+2]-view[i]) / (world[i+2]-world[i]) * (pos[i]-world[i]) + view[i]
+
+ return view_pos
+
+
+class GraceSetProps(GraceProperties):
+
+ def __init__(self, idx):
+ super().__init__()
+ self.idx = int(idx)
+
+ def __str__(self):
+ return ''.join(self)
+
+ def set_property(self, **kwargs):
+ remaining_kw = self._set_property(**kwargs)
+
+ for k, v in remaining_kw.items():
+ self.append(f'@ s{self.idx} {k} {v}\n')
+
+ def set_onoff(self, key: str, onoff: bool):
+ onoff_pattern = re.compile(rf'@\s*s\d{1,2}\s+{key}\s+(?P(on|off))', re.IGNORECASE)
+ if not self._set_onoff(onoff_pattern, onoff):
+ self.append(f'@ s{self.idx} {key} {"on" if onoff else "off"}\n')
+
+ def set_line(self, **kwargs):
+ _kwargs = {'line '+k: v for k, v in kwargs.items()}
+
+ self.set_property(**_kwargs)
+
+ def set_symbol(self, **kwargs):
+
+ _kwargs = {'symbol '+k: v for k, v in kwargs.items()}
+ if 'symbol' in kwargs:
+ _kwargs['symbol'] = kwargs['symbol']
+ _kwargs.pop('symbol symbol')
+
+ self.set_property(**_kwargs)
+
+
+class GraceSet(list):
+ _RE_TYPE = re.compile(r'@type (\w+)')
+ column_types = {'xy': 2, 'xydx': 3, 'xydy': 3, 'xydxdx': 4, 'xydydy': 4, 'xydxdy': 4, 'xydxdxdydy': 6,
+ 'bar': 2, 'bardy': 3, 'bardydy': 4,
+ 'xyhilo': 5, 'xyz': 3,
+ 'xyr': 3, 'xysize': 3, 'xycolor': 3, 'xycolpat': 4,
+ 'xyvmap': 4, 'xyboxplot': 6}
+
+ def __init__(self, g, s):
+ super().__init__()
+ self._graph = int(g)
+ self._set = int(s)
+
+ def __str__(self):
+ return ''.join(self)
+
+ @property
+ def graph(self):
+ return self._graph
+
+ @graph.setter
+ def graph(self, new_graph: int):
+ self._graph = new_graph
+ self[0] = f'@target G{new_graph}.S{self._set}\n'
+
+ @property
+ def set(self):
+ return self._set
+
+ @set.setter
+ def set(self, new_set: int):
+ self._set = int(new_set)
+ self[0] = f'@target G{self._graph}.S{new_set}\n'
+
+ @property
+ def graph_set(self):
+ return self._graph, self._set
+
+ @graph_set.setter
+ def graph_set(self, new_idx: tuple):
+ self._graph, self._set = new_idx
+ self[0] = f'@target G{new_idx[0]}.S{new_idx[1]}\n'
+
+ @property
+ def type(self):
+ m = GraceSet._RE_TYPE.match(self[1])
+ if m:
+ return m.group(1)
+
+ @type.setter
+ def type(self, new_type):
+ if new_type not in GraceSet.column_types:
+ raise ValueError(f'Unknown plot type {new_type}.')
+
+ self[1] = f'@type {new_type}\n'
+
+ @property
+ def rows(self):
+ return len(self) - 3
+
+ @property
+ def columns(self):
+ if self[2] != '&\n':
+ return len(self[2].split())
+ else:
+ return 0
+
+ @property
+ def shape(self):
+ return self.rows, self.columns
+
+ @property
+ def data(self):
+ return np.genfromtxt(StringIO(str(self)), skip_header=2, skip_footer=1)
+
+ @data.setter
+ def data(self, value):
+ if self.columns != 0 and value.shape[1] != self.columns:
+ raise TypeError(f'Number of columns is not {self.columns} (given {value.shape[1]})')
+
+ dtype = self.type
+ self.clear()
+ self.append(f'@target G{self._graph}.S{self._set}\n')
+ self.append(f'@type {dtype}\n')
+ for i, line in enumerate(value):
+ self.append(' '.join(line.astype(str)) + '\n')
+ self.append('&\n')
+
+
+class GraceRegion(list):
+ """
+ Just another name for a list
+ """
+ def __str__(self):
+ return ''.join(self)
+
+
+def _convert_to_value(parse_string):
+ tuples = parse_string.split(',')
+ for i, v in enumerate(tuples):
+ v = v.strip()
+
+ if re.match(r'[+-]?\d+.\d+', v):
+ tuples[i] = float(v)
+ elif re.match(r'[+-]?\d+', v):
+ tuples[i] = int(v)
+ elif re.match(r'\".*\"', v):
+ tuples[i] = v[1:-1]
+ else:
+ tuples[i] = v
+
+ if len(tuples) == 1:
+ return tuples[0]
+
+ return tuples
+
+
+def _convert_to_str(value):
+ if isinstance(value, (tuple, list)):
+ return ', '.join(map(str, value))
+ else:
+ return str(value)
+
+
+if __name__ == '__main__':
+ agr = GraceEditor('/autohome/dominik/nmreval/testdata/02_relax_2.agr')
+ import pprint
+ pprint.pprint(agr.graphs)
+ agr.graphs[0].set_property(title='"asdasdasd"')
diff --git a/nmreval/io/hdfreader.py b/nmreval/io/hdfreader.py
new file mode 100644
index 0000000..889c9b0
--- /dev/null
+++ b/nmreval/io/hdfreader.py
@@ -0,0 +1,388 @@
+import re
+from functools import reduce
+
+import h5py
+import numpy as np
+
+from collections import OrderedDict
+
+from ..data.points import Points
+from ..data.nmr import FID, Spectrum
+
+
+__all__ = ['HdfReader']
+
+
+def unicode_(text):
+ return str(text, encoding='utf-8')
+
+
+KEY_VAL_RE = re.compile(r'(?P[\w_]+)\s*=\s*(?P[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)')
+
+
+class HdfNode:
+ __slots__ = ['name', 'reference', 'type', 'parent', 'children',
+ 'num_signals', 'num_pts', 'num_grp',
+ 'title_parameter', 'parameter']
+
+ def __init__(self, name, ref, parent):
+ self.name = name
+ self.type = 'group'
+ self.reference = ref
+ self.parent = parent
+ self.children = None
+ self.num_signals = 0
+ self.num_grp = 0
+ self.num_pts = 0
+
+ self.parameter = {}
+ self.title_parameter = [(), {}]
+
+ def __repr__(self):
+ return f'{self.name} ({self.type})'
+
+ def __getitem__(self, item):
+ return self.children[item]
+
+ def __setitem__(self, key, value):
+ try:
+ self.children[key] = value
+ except TypeError:
+ self.children = OrderedDict()
+ self.children[key] = value
+
+ def clear(self):
+ self.name = ''
+ self.type = 'group'
+ self.parent = None
+ self.children = None
+ self.num_signals = 0
+ self.num_grp = 0
+ self.num_pts = 0
+
+ self.parameter = {}
+ self.title_parameter = [(), {}]
+
+ def __iter__(self):
+ yield self
+ if self.children is not None:
+ for val in self.children.values():
+ yield from val
+
+ def data(self, dtype: str = None):
+ if dtype is None:
+ _dtype = ['signal', 'points']
+ else:
+ _dtype = [dtype]
+
+ if self.type in _dtype:
+ yield self
+
+ if self.children is not None:
+ for val in self.children.values():
+ yield from val.data(dtype=dtype)
+
+ def keys(self, prefix: str = '', dtype: str = None):
+ if dtype is None:
+ _dtype = ['signal', 'points']
+ else:
+ _dtype = [dtype]
+
+ new_prefix = f'{prefix}/{self.name}' if self.name else self.name
+
+ if self.type in _dtype:
+ yield new_prefix
+
+ if self.children is not None:
+ for val in self.children.values():
+ yield from val.keys(prefix=new_prefix, dtype=dtype)
+
+ def parameters(self, key: str):
+ node = self.get(key)
+ return node.parameter
+
+ @property
+ def path(self):
+ if self.parent is None:
+ return self.name
+ else:
+ return self.parent.path + '/' + self.name
+
+ def get(self, key: str):
+ split_keys = key.split('/')
+ if split_keys[0] == '':
+ split_keys = split_keys[1:]
+ return reduce(lambda d, k: d[k], split_keys, self)
+
+ def set_parameter(self, key, value, keep=False):
+ if keep and key in self.parameter:
+ prev_val = self.parameter[key]
+ if isinstance(prev_val, list):
+ if value not in prev_val:
+ prev_val.append(value)
+ else:
+ if value != prev_val:
+ self.parameter[key] = [prev_val, value]
+ else:
+ self.parameter[key] = value
+
+ if self.parent is not None:
+ self.parent.set_parameter(key, value, keep=True)
+
+ def set_title_parameter(self, child_node, params):
+ if params:
+ self.title_parameter[0] = params[-1]
+ else:
+ self.title_parameter[0] = ('', None)
+
+ if child_node is not None:
+ child_parameter = self.title_parameter[1]
+ key, value = child_node.title_parameter[0]
+ if key in child_parameter:
+ prev_val = child_parameter[key]
+ if isinstance(prev_val, list):
+ if value not in prev_val:
+ prev_val.append(value)
+ else:
+ if value != prev_val:
+ child_parameter[key] = [prev_val, value]
+ else:
+ child_parameter[key] = value
+
+ if (self.parent is not None) and params:
+ self.parent.set_title_parameter(self, params[:-1])
+
+
+class HdfReader(HdfNode):
+ def __init__(self, filename=None, base='data_pool'):
+ super().__init__('', None, None)
+ self.filename = filename
+ self.file = None
+ self.base = base
+ if self.filename is not None:
+ try:
+ self.file = h5py.File(filename, 'r')
+ if base in self.file.keys():
+ self.create_node(self.file[base], parent=self)
+ except OSError:
+ self.file = None
+ raise IOError('Invalid file ' + filename)
+
+ def __call__(self, filename, base='data_pool'):
+ super().clear()
+ self.filename = filename
+ self.base = base
+ try:
+ self.file = h5py.File(filename, 'r')
+ if base in self.file.keys():
+ self.create_node(self.file[base], parent=self)
+ except OSError:
+ self.file = None
+ raise IOError('Invalid file ' + filename)
+
+ return self
+
+ def __del__(self):
+ try:
+ if self.file is not None:
+ self.file.close()
+ except ImportError:
+ pass
+
+ def create_node(self, node, parent=None):
+ for k, v in node.items():
+ attr = v.attrs
+ if 'TITLE' in attr:
+ location = unicode_(attr['TITLE'])
+ else:
+ location = v.name
+
+ data = HdfNode(location.split('/')[-1], v.ref, parent)
+
+ if isinstance(v, h5py.Group):
+ if 'damaris_type' in attr:
+ # Group is DAMARIS data (ADC result, Accumulation)
+ name = location.split('/')[-1]
+ data.type = 'signal'
+ parent[name] = data
+ parent.num_signals += 1
+
+ for desc in attr.keys():
+ # looking for description_KEY in attributes
+ m = re.search(r'description_(?P\S+)', desc)
+ if m is not None:
+ var_name = m['var'].lower()
+ try:
+ var_value = float(attr[desc])
+ except ValueError:
+ var_value = unicode_(attr[desc])
+ data.set_parameter(var_name, var_value)
+
+ title_params = []
+ for lvl in location.split('/'):
+ m = KEY_VAL_RE.search(lvl)
+ if m is not None:
+ title_params.append(m.groups())
+ else:
+ title_params.append((lvl, None))
+ data.set_title_parameter(None, title_params)
+
+ else:
+ # Group is a real group
+ parent[location] = data
+ parent.num_grp += 1
+ self.create_node(v, parent=data)
+ else:
+ # dataset is MeasurementResult
+ name = location.split('/')[-1]
+ data.type = 'points'
+ parent[name] = data
+ parent.num_pts += 1
+
+ m = KEY_VAL_RE.search(unicode_(attr['quantity_name']))
+ if m:
+ data.parameter[m['key']] = float(m['val'])
+ m = KEY_VAL_RE.search(name)
+ if m:
+ data.title_parameter[0] = (m['key'], float(m['val']))
+ else:
+ data.title_parameter[0] = (None, None)
+
+ def get_points(self):
+ return self.get_selected('', dtype='points')
+
+ def get_signals(self):
+ return self.get_selected('', dtype='signal')
+
+ def get_selected(self, key: str, dtype: str = None, value: str = None,
+ group: str = None, flag: str = 'fid') -> list:
+ key_list = []
+ if '*' in key:
+ # wildcards: find all matching entries
+ for k in self.keys():
+ m = re.match(key.replace('*', '.*'), k)
+ if m:
+ key_list.append(k)
+ else:
+ key_list.append(key)
+
+ ret_val = []
+ for k in key_list:
+ val = self.get(k)
+ for child in val.data(dtype=dtype):
+ try:
+ if child.type == 'points':
+ ret_val.append(self._make_point(child))
+ elif child.type == 'signal':
+ ret_val.append(self._make_signal(child, flag=flag, value=value, group=group))
+ except IOError:
+ print('something went wrong for ' + child.name)
+ continue
+
+ return ret_val
+
+ def _make_point(self, node):
+ data = self.file[node.reference]
+
+ val = None
+ if node.parameter:
+ for v in node.parameter.values():
+ val = v
+
+ return Points(x=data['x'], y=data['y'], yerr=data['y_err'], name=node.name, value=val)
+
+ def _make_signal(self, node, flag: str = 'fid', value: str = None, group: str = None):
+ if value is None:
+ value = self._get_parameter_values(node, node.parameter)
+ else:
+ try:
+ value = node.parameter[value]
+ except KeyError:
+ try:
+ value = node.title_parameter[1][value]
+ except KeyError:
+ print(f'{value} is not a valid key for {node.name}')
+ value = None
+
+ if group is None:
+ if value is not None and node.parent is not None:
+ group = self._get_parameter_values(node.parent, node.parameter)
+ else:
+ try:
+ group = node.parameter[group]
+ except KeyError:
+ temp = node
+ while group != temp.title_parameter[0][0]:
+ if temp.parent is None:
+ break
+ temp = temp.parent
+
+ group = temp.title_parameter[0][1]
+
+ data = self.file[node.reference]
+ try:
+ y = data['accu_data']
+ except KeyError:
+ y = data['adc_data']
+
+ if y.shape[1] == 4:
+ y = y[:, 0] + 1j*y[:, 2]
+ else:
+ y = y[:, 0] + 1j*y[:, 1]
+
+ index = data['indices']
+ dw = float(index['dwelltime'])
+ if flag == 'fid':
+ x = np.arange(len(y)) * dw
+ ret = FID(x, y, name=node.name, value=value, group=group, filename=self.file.filename)
+
+ elif flag == 'spectrum':
+ x = np.linspace(-1/dw, 1/dw, num=len(y))
+ ret = Spectrum(x, y, name=node.name, value=value, group=group, filename=self.file.filename)
+ else:
+ raise ValueError(f'{flag} unknown, use `fid` or `spectrum`.')
+
+ return ret
+
+ @staticmethod
+ def _get_parameter_values(node: HdfNode, param_dic: dict) -> float:
+ (var_key, node_param_value), _ = node.title_parameter
+
+ if var_key.startswith('Accumulation_'):
+ var_key = var_key[13:]
+
+ value = None
+
+ if node_param_value:
+ # there is hope that there is a numeric value
+ value = float(node_param_value)
+ if var_key.lower() not in node.parameter:
+ # we cannot find a key that fits to the one in the title, e.g. renamed in title,
+ # so we look in the parent node what was varied and search for a key
+ parent = node.parent
+ _, parent_child_param = parent.title_parameter
+ parameter_len = len(parent_child_param[var_key])
+ var_key = None
+ multi = False
+
+ for k, v in parent.parameter.items():
+ try:
+ if len(v) == parameter_len:
+ if var_key is None:
+ var_key = k
+ else:
+ # multiple values are not useful
+ multi = True
+ break
+ except TypeError:
+ continue
+
+ if multi:
+ var_key = None
+
+ try:
+ value = param_dic[var_key.lower()]
+ except KeyError:
+ pass
+
+ return value
diff --git a/nmreval/io/merge_agr.py b/nmreval/io/merge_agr.py
new file mode 100755
index 0000000..ff07088
--- /dev/null
+++ b/nmreval/io/merge_agr.py
@@ -0,0 +1,49 @@
+#! /usr/bin/env python
+
+import sys
+import re
+import argparse
+
+
+def merger(infiles, outfile):
+ header = ''
+ graphs = ''
+ counter = 0
+ for f in infiles:
+ headerflag = True
+ for line in f.readlines():
+ if re.match('@with [Gg][0-9]', line):
+ headerflag = False
+ # one graph per file, adjust graph number
+ line = '@with g{}\n'.format(counter)
+ if headerflag:
+ if graphs == '':
+ # save fonts, colors etc. of first file
+ header += line
+ elif re.match('@[Gg][0-9]', line):
+ # save graph specifications of following files
+ line = re.sub('@[Gg][0-9]', '@g{}'.format(counter), line)
+ header += line
+ else:
+ pass
+ else:
+ # save sets
+ if re.match('@target [Gg][0-9]', line):
+ line = re.sub('@target [Gg][0-9]',
+ '@target G{}'.format(counter), line)
+ graphs += line
+ counter += 1
+ outfile.write(header+graphs)
+
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument('input', nargs='+', type=argparse.FileType('r'),
+ help='One or more input files')
+parser.add_argument('--output', '-o', nargs='?', type=argparse.FileType('w'),
+ default='merger_grace.agr',
+ help='Output file (default=merged_grace.agr)')
+
+args = parser.parse_args()
+
+merger(args.input, args.output)
diff --git a/nmreval/io/nmrreader.py b/nmreval/io/nmrreader.py
new file mode 100644
index 0000000..0205762
--- /dev/null
+++ b/nmreval/io/nmrreader.py
@@ -0,0 +1,137 @@
+import pickle
+from collections import OrderedDict
+from typing import Tuple, Union
+
+from ..data.nmr import FID, Spectrum
+from ..data.points import Points
+from ..fit.result import FitResult, FitResultCreator
+from .read_old_nmr import _read_file_v1
+from ..lib.colors import Colors
+from ..lib.lines import LineStyle
+from ..lib.symbols import SymbolStyle
+
+
+class NMRReader:
+ def __init__(self, fname: str = None, version: str = '0'):
+ self.filename = fname
+
+ self._set_version(version)
+
+ def __call__(self, fname, version: str = None):
+ self.filename = fname
+ self._set_version(version)
+
+ return self
+
+ def _set_version(self, vers: str):
+ if vers is None:
+ return
+
+ if vers == '-1':
+ self.version = 1
+ else:
+ self.version = 2
+
+ def make_data(self, fname: str = None) -> Union[Tuple[dict, dict], Tuple[dict]]:
+ if fname is None:
+ fname = self.filename
+
+ if fname:
+ if self.version == 1:
+ return self._make_old(fname),
+ else:
+ return self._make_new(fname)
+
+ @staticmethod
+ def _make_new(fname) -> Tuple[dict, dict]:
+ with open(fname, 'rb') as fp:
+ # remove magic
+ _ = fp.read(16)
+ states = pickle.load(fp)
+
+ datalist = OrderedDict()
+ _dtypes = {'pts': Points, 'fit': FitResult, 'fid': FID}
+
+ for s in states['sets']:
+ set_id = s.pop('id')
+ dtype = _dtypes[s.pop('mode')]
+ data = dtype.set_state(s.pop('data'))
+ datalist[set_id] = (data, s)
+
+ return datalist, states['graphs']
+
+ def _make_old(self, fname: str) -> dict:
+ datadic = _read_file_v1(fname)
+ datalist = OrderedDict()
+
+ for w_key, window in datadic.items():
+ order = window['data']['order']
+ for data_id in order:
+ if data_id in ['selected', 'order']:
+ continue
+
+ properties = window['data'][data_id]
+
+ data_properties = properties['_data']
+
+ meta = data_properties['meta']
+ meta['name'] = meta['value']
+ meta.pop('value')
+
+ mode = data_properties['mode'].lower()
+ if mode == 'points':
+ dtype = Points
+ elif mode == 'spectrum':
+ dtype = Spectrum
+ else:
+ dtype = FID
+
+ data = dtype(x=data_properties['x'], y=data_properties['y'],
+ y_err=data_properties['y_err'],
+ **meta)
+
+ data.mask = data_properties['mask'].data
+ datalist[data_id] = data, self.plot_opts(properties['dataplot_real'], properties['dataplot_imag'])
+
+ _fit = properties['_fit']
+ if _fit is not None:
+ ff = FitResultCreator.make_from_session(data_properties['x'], data_properties['y'], data_id, _fit)
+ datalist[data_id+'-fit'] = ff, self.plot_opts(properties['fitplot'], {})
+
+ return datalist
+
+ @staticmethod
+ def plot_opts(real_dic: dict, imag_dic: dict) -> dict:
+ opts = {}
+
+ pen = real_dic['pen']
+ symsize = real_dic['symbolSize']
+ symbol = SymbolStyle.from_str(real_dic['symbol'])
+ symcolor = None
+
+ try:
+ symcolor = Colors(real_dic['symbolBrush'][:3])
+ except ValueError:
+ pass
+
+ if pen is None:
+ ls = LineStyle.No
+ lw = 1.
+ lc = symcolor
+ else:
+ # pen is not None
+ lc = Colors(real_dic['pen'][0][:3])
+ if symcolor is None:
+ symcolor = lc
+
+ lw = real_dic['pen'][1]
+ ls = LineStyle.Solid
+
+ opts['real'] = {'symbol': symbol, 'size': symsize, 'color': symcolor}, \
+ {'style': ls, 'width': lw, 'color': lc}
+
+ if imag_dic:
+ opts['imag'] = {'symbol': symbol, 'size': symsize, 'color': symcolor}, \
+ {'style': LineStyle.Dashed, 'width': lw, 'color': lc}
+
+ return opts
diff --git a/nmreval/io/read_old_nmr.py b/nmreval/io/read_old_nmr.py
new file mode 100644
index 0000000..27dab68
--- /dev/null
+++ b/nmreval/io/read_old_nmr.py
@@ -0,0 +1,567 @@
+from copyreg import _inverted_registry, _extension_cache
+import sys
+from sys import maxsize
+from struct import unpack
+import io
+import codecs
+import _compat_pickle
+
+from pickle import *
+from pickle import _Unframer, bytes_types, _Stop, _getattribute
+import bsddb3
+
+
+"""
+Old version of the program used shelve. It pickled/serialized also the data classes, i.e. FID; Points,..., and also QColors for pens.
+This leads to several problems with Python 3.x and PyQt5:
+1.) shelve in 2.7 used (mostly) dbhash which is removed from Python 3.0 onwards, so shelve.open() does not work.
+2.) Serialized classes are imported during unpickling but PyQt4 and, due to the renaming and reordering
+in nmreval, the dataclasses are missing and lead to ImportError/ModuleNotFoundError.
+
+Problem 1 can be fixed by using bsddb3 and unpickle each item manually.
+Problem 2 is solved with a copy of the Unpickler from pickle.py that checks classes before imports and builds and
+returns the dicts instead. There are also pickled numpy arrays but they are unproblematic. Yes, someone is lazy and just
+copied the Unpickler completely.
+
+It seems that bsddb3 will be changed to berkeleydb in the future (https://www.jcea.es/programacion/pybsddb.htm)
+so this might break eventually.
+"""
+
+
+class Unpickler:
+ def __init__(self, file, *, fix_imports=True,
+ encoding="ASCII", errors="strict", buffers=None):
+ self._buffers = iter(buffers) if buffers is not None else None
+ self._file_readline = file.readline
+ self._file_read = file.read
+ self.memo = {}
+ self.encoding = encoding
+ self.errors = errors
+ self.proto = 0
+ self.fix_imports = fix_imports
+
+ def load(self):
+ if not hasattr(self, "_file_read"):
+ raise UnpicklingError("Unpickler.__init__() was not called by "
+ "%s.__init__()" % (self.__class__.__name__,))
+ self._unframer = _Unframer(self._file_read, self._file_readline)
+ self.read = self._unframer.read
+ self.readinto = self._unframer.readinto
+ self.readline = self._unframer.readline
+ self.metastack = []
+ self.stack = []
+ self.append = self.stack.append
+ self.proto = 0
+ read = self.read
+ dispatch = self.dispatch
+ try:
+ while True:
+ key = read(1)
+ if not key:
+ raise EOFError
+ assert isinstance(key, bytes_types)
+
+ dispatch[key[0]](self)
+ except _Stop as stopinst:
+ return stopinst.value
+
+ # Return a list of items pushed in the stack after last MARK instruction.
+ def pop_mark(self):
+ items = self.stack
+ self.stack = self.metastack.pop()
+ self.append = self.stack.append
+ return items
+
+ def persistent_load(self, pid):
+ raise UnpicklingError("unsupported persistent id encountered")
+
+ dispatch = {}
+
+ def load_proto(self):
+ proto = self.read(1)[0]
+ if not 0 <= proto <= 2:
+ raise ValueError("unsupported pickle protocol: %d" % proto)
+ self.proto = proto
+
+ dispatch[PROTO[0]] = load_proto
+
+ def load_persid(self):
+ try:
+ pid = self.readline()[:-1].decode("ascii")
+ except UnicodeDecodeError:
+ raise UnpicklingError(
+ "persistent IDs in protocol 0 must be ASCII strings")
+ self.append(self.persistent_load(pid))
+
+ dispatch[PERSID[0]] = load_persid
+
+ def load_binpersid(self):
+ pid = self.stack.pop()
+ self.append(self.persistent_load(pid))
+
+ dispatch[BINPERSID[0]] = load_binpersid
+
+ def load_none(self):
+ self.append(None)
+
+ dispatch[NONE[0]] = load_none
+
+ def load_false(self):
+ self.append(False)
+
+ dispatch[NEWFALSE[0]] = load_false
+
+ def load_true(self):
+ self.append(True)
+
+ dispatch[NEWTRUE[0]] = load_true
+
+ def load_int(self):
+ data = self.readline()
+ if data == FALSE[1:]:
+ val = False
+ elif data == TRUE[1:]:
+ val = True
+ else:
+ val = int(data, 0)
+ self.append(val)
+
+ dispatch[INT[0]] = load_int
+
+ def load_binint(self):
+ self.append(unpack('d', self.read(8))[0])
+
+ dispatch[BINFLOAT[0]] = load_binfloat
+
+ def _decode_string(self, value):
+ # Used to allow strings from Python 2 to be decoded either as
+ # bytes or Unicode strings. This should be used only with the
+ # STRING, BINSTRING and SHORT_BINSTRING opcodes.
+ if self.encoding == "bytes":
+ return value
+ else:
+ return value.decode(self.encoding, self.errors)
+
+ def load_string(self):
+ data = self.readline()[:-1]
+ # Strip outermost quotes
+ if len(data) >= 2 and data[0] == data[-1] and data[0] in b'"\'':
+ data = data[1:-1]
+ else:
+ raise UnpicklingError("the STRING opcode argument must be quoted")
+ self.append(self._decode_string(codecs.escape_decode(data)[0]))
+
+ dispatch[STRING[0]] = load_string
+
+ def load_binstring(self):
+ # Deprecated BINSTRING uses signed 32-bit length
+ length, = unpack(' maxsize:
+ raise UnpicklingError("BINUNICODE exceeds system's maximum size "
+ "of %d bytes" % maxsize)
+ self.append(str(self.read(length), 'utf-8', 'surrogatepass'))
+
+ dispatch[BINUNICODE[0]] = load_binunicode
+
+ def load_short_binstring(self):
+ length = self.read(1)[0]
+ data = self.read(length)
+ self.append(self._decode_string(data))
+
+ dispatch[SHORT_BINSTRING[0]] = load_short_binstring
+
+ def load_tuple(self):
+ items = self.pop_mark()
+ self.append(tuple(items))
+
+ dispatch[TUPLE[0]] = load_tuple
+
+ def load_empty_tuple(self):
+ self.append(())
+
+ dispatch[EMPTY_TUPLE[0]] = load_empty_tuple
+
+ def load_tuple1(self):
+ self.stack[-1] = (self.stack[-1],)
+
+ dispatch[TUPLE1[0]] = load_tuple1
+
+ def load_tuple2(self):
+ self.stack[-2:] = [(self.stack[-2], self.stack[-1])]
+
+ dispatch[TUPLE2[0]] = load_tuple2
+
+ def load_tuple3(self):
+ self.stack[-3:] = [(self.stack[-3], self.stack[-2], self.stack[-1])]
+
+ dispatch[TUPLE3[0]] = load_tuple3
+
+ def load_empty_list(self):
+ self.append([])
+
+ dispatch[EMPTY_LIST[0]] = load_empty_list
+
+ def load_empty_dictionary(self):
+ self.append({})
+
+ dispatch[EMPTY_DICT[0]] = load_empty_dictionary
+
+ def load_list(self):
+ items = self.pop_mark()
+ self.append(items)
+
+ dispatch[LIST[0]] = load_list
+
+ def load_dict(self):
+ items = self.pop_mark()
+ d = {items[i]: items[i + 1]
+ for i in range(0, len(items), 2)}
+ self.append(d)
+
+ dispatch[DICT[0]] = load_dict
+
+ # INST and OBJ differ only in how they get a class object. It's not
+ # only sensible to do the rest in a common routine, the two routines
+ # previously diverged and grew different bugs.
+ # klass is the class to instantiate, and k points to the topmost mark
+ # object, following which are the arguments for klass.__init__.
+ def _instantiate(self, klass, args):
+ if (args or not isinstance(klass, type) or
+ hasattr(klass, "__getinitargs__")):
+ try:
+ value = klass(*args)
+ except TypeError as err:
+ raise TypeError("in constructor for %s: %s" %
+ (klass.__name__, str(err)), sys.exc_info()[2])
+ else:
+ value = klass.__new__(klass)
+ self.append(value)
+
+ def load_inst(self):
+ module = self.readline()[:-1].decode("ascii")
+ name = self.readline()[:-1].decode("ascii")
+ klass = self.find_class(module, name)
+ self._instantiate(klass, self.pop_mark())
+
+ dispatch[INST[0]] = load_inst
+
+ def load_obj(self):
+ # Stack is ... markobject classobject arg1 arg2 ...
+ args = self.pop_mark()
+ cls = args.pop(0)
+ self._instantiate(cls, args)
+
+ dispatch[OBJ[0]] = load_obj
+
+ def load_newobj(self):
+ args = self.stack.pop()
+ cls = self.stack.pop()
+ # If the class is replaced by string by load_global, it will throw a TypeError
+ try:
+ obj = cls.__new__(cls, *args)
+ except TypeError:
+ obj = cls
+ self.append(obj)
+
+ dispatch[NEWOBJ[0]] = load_newobj
+
+ def load_global(self):
+ module = self.readline()[:-1].decode("utf-8")
+ name = self.readline()[:-1].decode("utf-8")
+ # Replace imports for PyQt and old auswerten parts by a string.
+ # Other modules are imported as intended.
+ if module == 'sip':
+ self.append('sip')
+ elif module.startswith('auswerten'):
+ self.append(name)
+ else:
+ klass = self.find_class(module, name)
+ self.append(klass)
+
+ dispatch[GLOBAL[0]] = load_global
+
+ def load_ext1(self):
+ code = self.read(1)[0]
+ self.get_extension(code)
+
+ dispatch[EXT1[0]] = load_ext1
+
+ def load_ext2(self):
+ code, = unpack('= 4:
+ return _getattribute(sys.modules[module], name)[0]
+ else:
+ return getattr(sys.modules[module], name)
+
+ def load_reduce(self):
+ stack = self.stack
+ args = stack.pop()
+ func = stack[-1]
+ # Only critical part in the file should be QColor.
+ # Originally, args is (PyQt4.QtGui, QColor, (r, g, b, a)) and func the unpickler from sip module.
+ # The rgba tuple is put on the stack, rest is garbage.
+ try:
+ stack[-1] = func(*args)
+ except TypeError:
+ if func == 'sip':
+ stack[-1] = args[-1]
+ else:
+ raise TypeError
+
+ dispatch[REDUCE[0]] = load_reduce
+
+ def load_pop(self):
+ if self.stack:
+ del self.stack[-1]
+ else:
+ self.pop_mark()
+
+ dispatch[POP[0]] = load_pop
+
+ def load_pop_mark(self):
+ self.pop_mark()
+
+ dispatch[POP_MARK[0]] = load_pop_mark
+
+ def load_dup(self):
+ self.append(self.stack[-1])
+
+ dispatch[DUP[0]] = load_dup
+
+ def load_get(self):
+ i = int(self.readline()[:-1])
+ self.append(self.memo[i])
+
+ dispatch[GET[0]] = load_get
+
+ def load_binget(self):
+ i = self.read(1)[0]
+ self.append(self.memo[i])
+
+ dispatch[BINGET[0]] = load_binget
+
+ def load_long_binget(self):
+ i, = unpack(' maxsize:
+ raise ValueError("negative LONG_BINPUT argument")
+ self.memo[i] = self.stack[-1]
+
+ dispatch[LONG_BINPUT[0]] = load_long_binput
+
+ def load_append(self):
+ stack = self.stack
+ value = stack.pop()
+ liste = stack[-1]
+ liste.append(value)
+
+ dispatch[APPEND[0]] = load_append
+
+ def load_appends(self):
+ items = self.pop_mark()
+ list_obj = self.stack[-1]
+ try:
+ extend = list_obj.extend
+ except AttributeError:
+ pass
+ else:
+ extend(items)
+ return
+ # Even if the PEP 307 requires extend() and append() methods,
+ # fall back on append() if the object has no extend() method
+ # for backward compatibility.
+ append = list_obj.append
+ for item in items:
+ append(item)
+
+ dispatch[APPENDS[0]] = load_appends
+
+ def load_setitem(self):
+ stack = self.stack
+ value = stack.pop()
+ key = stack.pop()
+ dic = stack[-1]
+ dic[key] = value
+
+ dispatch[SETITEM[0]] = load_setitem
+
+ def load_setitems(self):
+ items = self.pop_mark()
+ dic = self.stack[-1]
+ for i in range(0, len(items), 2):
+ dic[items[i]] = items[i + 1]
+
+ dispatch[SETITEMS[0]] = load_setitems
+
+ def load_build(self):
+ stack = self.stack
+ state = stack.pop()
+ inst = stack[-1]
+
+ # we replaced previous class creations by a string representation
+ # we put the original state on the stack to get all the information
+ if isinstance(inst, str):
+ state['mode'] = inst
+ stack[-1] = state
+ return
+
+ # inst is really a class that can be instantiated
+ setstate = getattr(inst, "__setstate__", None)
+ if setstate is not None:
+ setstate(state)
+ return
+ slotstate = None
+ if isinstance(state, tuple) and len(state) == 2:
+ state, slotstate = state
+ if state:
+ inst_dict = inst.__dict__
+ intern = sys.intern
+ for k, v in state.items():
+ if type(k) is str:
+ inst_dict[intern(k)] = v
+ else:
+ inst_dict[k] = v
+ if slotstate:
+ for k, v in slotstate.items():
+ setattr(inst, k, v)
+
+ dispatch[BUILD[0]] = load_build
+
+ def load_mark(self):
+ self.metastack.append(self.stack)
+ self.stack = []
+ self.append = self.stack.append
+
+ dispatch[MARK[0]] = load_mark
+
+ def load_stop(self):
+ value = self.stack.pop()
+ raise _Stop(value)
+
+ dispatch[STOP[0]] = load_stop
+
+
+def _read_file_v1(fname: str) -> dict:
+ ret_val = {}
+
+ db = bsddb3.hashopen(str(fname))
+ for k, v in db.items():
+ # numpy unpickle need the latin-1
+ ret_val[k] = Unpickler(io.BytesIO(v), encoding='latin-1').load()
+
+ return ret_val
diff --git a/nmreval/io/sessionwriter.py b/nmreval/io/sessionwriter.py
new file mode 100644
index 0000000..b65d2d8
--- /dev/null
+++ b/nmreval/io/sessionwriter.py
@@ -0,0 +1,27 @@
+from __future__ import annotations
+
+from pickle import dump
+from struct import pack
+from pathlib import Path
+
+from ..version import __version__
+
+
+class NMRWriter:
+ def __init__(self, graphs: dict, data: dict):
+ self._states = {'graphs': [], 'sets': []}
+
+ for g in graphs.values():
+ self._states['graphs'].append(g.get_state())
+
+ for s in g.sets:
+ s_state = data[s].get_state()
+ self._states['sets'].append(s_state)
+
+ def export(self, fname: (str | Path)):
+ if isinstance(fname, str):
+ fname = Path(fname)
+
+ with fname.open(mode='wb') as fh:
+ fh.write(pack('16s', b'NMREVAL' + bytes(__version__, 'utf8')))
+ dump(self._states, fh)
diff --git a/nmreval/io/tntreader.py b/nmreval/io/tntreader.py
new file mode 100644
index 0000000..2c01981
--- /dev/null
+++ b/nmreval/io/tntreader.py
@@ -0,0 +1,379 @@
+import os
+import re
+import itertools
+
+from collections import OrderedDict
+
+import numpy as np
+from numpy import dtype
+
+from ..data.nmr import FID, Spectrum
+from ..utils.utils import staggered_range
+
+__all__ = ['TNTReader']
+
+"""
+Read Tecmag files. Based on _
+"""
+
+TNTMAGIC_re = re.compile(r'^TNT1\.\d\d\d$')
+TNTMAGIC = np.dtype('a8')
+TLV = np.dtype([('tag', 'a4'), ('bool', ' 1:
+ delay = delay.split()
+ delay = np.array([self.si_conversion(d) for d in delay])
+ self.delays[delay_name] = delay
+
+ tmag = np.fromstring(tnt_sections['TMAG']['data'], TMAG, count=1)[0]
+ data = np.memmap(self.fname, np.dtype(' 0.04045 else val/12.92
+
+ x = sum(i*j for i, j in zip([0.4124, 0.3576, 0.1805], rgb))
+ y = sum(i*j for i, j in zip([0.2126, 0.7152, 0.0722], rgb))
+ z = sum(i*j for i, j in zip([0.0193, 0.1192, 0.9505], rgb))
+
+ return x, y, z
+
+ def hsv(self, normed=False):
+ r, g, b = self.rgb(normed=True)
+
+ col_max = max(r, g, b)
+ col_min = min(r, g, b)
+
+ delta = col_max - col_min
+
+ if delta == 0:
+ hue = 0
+ saturation = 0
+ else:
+ try:
+ saturation = delta / col_max
+ except ZeroDivisionError:
+ saturation = 0
+
+ if col_max == r:
+ hue = (g - b) / delta
+ elif col_max == g:
+ hue = (b - r) / delta + 2
+ elif col_max == b:
+ hue = (r - g) / delta + 4
+ else:
+ raise ValueError('convert to hsv failed')
+
+ hue *= 60.
+ if hue < 0:
+ hue += 360
+
+ if normed:
+ return hue / 360, saturation, col_max
+ else:
+ return hue, saturation * 255, col_max * 255
+
+ def lab(self):
+ x, y, z = self.xyz()
+ x /= 95.0489
+ y /= 100
+ z /= 108.884
+
+ x = x**(1 / 3) if x > 0.008856 else 7.787 * x + 16 / 116
+ y = y**(1 / 3) if y > 0.008856 else 7.787 * y + 16 / 116
+ z = z**(1 / 3) if z > 0.008856 else 7.787 * z + 16 / 116
+
+ l = 116 * y - 16
+ a = 500 * (x - y)
+ b = 200 * (x - z)
+
+ return l, a, b
+
+ def cmyk(self, normed=False):
+ r, g, b = self.rgb(normed=True)
+
+ k = 1. - max(r, g, b)
+ try:
+ c = (1. - r - k) / (1. - k)
+ m = (1. - g - k) / (1. - k)
+ y = (1. - b - k) / (1. - k)
+ except ZeroDivisionError:
+ c = m = y = 0.
+
+ if normed:
+ return c, m, y, k
+ else:
+ return c*255, m*255, y*255, k*255
+
+ @classmethod
+ def from_rgb(cls, r, g, b, normed=False):
+ if normed:
+ r *= 255
+ g *= 255
+ b *= 255
+
+ return cls((r, g, b))
+
+ @classmethod
+ def from_str(cls, hexstring):
+ r = int(hexstring[1:3], 16)
+ g = int(hexstring[3:5], 16)
+ b = int(hexstring[5:7], 16)
+
+ return cls((r, g, b))
+
+
+class TuColorsA(BaseColor):
+ TuDa1a = (93, 133, 195)
+ TuDa2a = (0, 156, 218)
+ TuDa3a = (80, 182, 149)
+ TuDa4a = (175, 204, 80)
+ TuDa5a = (221, 223, 72)
+ TuDa6a = (255, 224, 92)
+ TuDa7a = (248, 186, 60)
+ TuDa8a = (238, 122, 52)
+ TuDa9a = (233, 80, 62)
+ TuDa10a = (201, 48, 142)
+ TuDa11a = (128, 69, 151)
+
+
+class TuColorsB(BaseColor):
+ TuDa1b = (0, 90, 169)
+ TuDa2b = (0, 131, 204)
+ TuDa3b = (0, 157, 129)
+ TuDa4b = (153, 192, 0)
+ TuDa5b = (201, 212, 0)
+ TuDa6b = (253, 202, 0)
+ TuDa7b = (245, 163, 0)
+ TuDa8b = (236, 101, 0)
+ TuDa9b = (230, 0, 26)
+ TuDa10b = (166, 0, 132)
+ TuDa11b = (114, 16, 133)
+
+
+class TuColorsC(BaseColor):
+ TuDa1c = (0, 78, 138)
+ TuDa2c = (0, 104, 157)
+ TuDa3c = (0, 136, 119)
+ TuDa4c = (127, 171, 22)
+ TuDa5c = (177, 189, 0)
+ TuDa6c = (215, 172, 0)
+ TuDa7c = (210, 135, 0)
+ TuDa8c = (204, 76, 3)
+ TuDa9c = (185, 15, 34)
+ TuDa10c = (149, 17, 105)
+ TuDa11c = (97, 28, 115)
+
+
+class TUColorsD(BaseColor):
+ TuDa1d = (36, 53, 114)
+ TuDa2d = (0, 78, 115)
+ TuDa3d = (0, 113, 94)
+ TuDa4d = (106, 139, 55)
+ TuDa5d = (153, 166, 4)
+ TuDa6d = (174, 142, 0)
+ TuDa7d = (190, 111, 0)
+ TuDa8d = (169, 73, 19)
+ TuDa9d = (156, 28, 38)
+ TuDa10d = (115, 32, 84)
+ TuDa11d = (76, 34, 106)
+
+
+class TUGrays(BaseColor):
+ TuDa0a = (220, 220, 220)
+ TuDa0b = (181, 181, 181)
+ TuDa0c = (137, 137, 137)
+ TuDa0d = (83, 83, 83)
+ White = (255, 255, 255)
+ Black = (0, 0, 0)
+
+
+class RedBlue(BaseColor):
+ C1 = (103, 0, 31)
+ C2 = (178, 27, 43)
+ C3 = (178, 24, 43)
+ C4 = (244, 165, 130)
+ C5 = (253, 219, 199)
+ C6 = (220, 220, 220)
+ C7 = (253, 219, 199)
+ C8 = (146, 197, 222)
+ C9 = (67, 147, 195)
+ C10 = (33, 102, 172)
+ C11 = (5, 48, 97)
+
+
+class Tab10(BaseColor):
+ TabBlue = (31, 119, 180)
+ TabOrange = (255, 127, 14)
+ TabGreen = (44, 160, 44)
+ TabRed = (214, 39, 40)
+ TabPurple = (148, 103, 189)
+ TabBrown = (140, 86, 75)
+ TabRose = (227, 119, 194)
+ TabGrey = (220, 220, 220)
+ TabChartreuse = (188, 189, 34)
+ TabTurquoise = (23, 190, 207)
+
+
+class Tab20(BaseColor):
+ TabBlue = (31, 119, 180)
+ TabBlue2 = (174, 199, 232)
+ TabOrange = (255, 127, 14)
+ TabOrange2 = (255, 127, 14)
+ TabGreen = (44, 160, 44)
+ TabGreen2 = (44, 160, 44)
+ TabRed = (214, 39, 40)
+ TabRed2 = (214, 39, 40)
+ TabPurple = (148, 103, 189)
+ TabPurple2 = (148, 103, 189)
+ TabBrown = (140, 86, 75)
+ TabBrown2 = (140, 86, 75)
+ TabRose = (227, 119, 194)
+ TabRose2 = (227, 119, 194)
+ TabGrey = (220, 220, 220)
+ TabGrey2 = (220, 220, 220)
+ TabChartreuse = (188, 189, 34)
+ TabChartreuse2 = (188, 189, 34)
+ TabTurquoise = (23, 190, 207)
+ TabTurquoise2 = (23, 190, 207)
+
+
+class GraceColors(BaseColor):
+ White = (255, 255, 255)
+ Black = (0, 0, 0)
+ Red = (255, 0, 0)
+ Green = (0, 255, 0)
+ Blue = (0, 0, 255)
+ Yellow = (255, 255, 0)
+ Brown = (188, 143, 143)
+ Grey = (220, 220, 220)
+ Violet = (148, 0, 211)
+ Cyan = (0, 255, 255)
+ Magenta = (255, 0, 255)
+ Orange = (255, 255, 255)
+ Indigo = (114, 33, 188)
+ Maroon = (103, 7, 72)
+ Turquoise = (64, 224, 208)
+ Green4 = (0, 139, 0)
+
+
+TUColors = enum.Enum(
+ value='TUColors',
+ names={member.name: member.value for palette in [TuColorsA, TuColorsB, TuColorsC, TUColorsD, TUGrays] for member in palette},
+ type=BaseColor
+)
+
+Colors = enum.Enum(
+ value='Colors',
+ names={member.name: member.value for palette in [TUColors, GraceColors, Tab10] for member in palette},
+ type=BaseColor
+)
+
+
+def get_palettes():
+ palettes = {
+ 'Full': Colors,
+ 'TuDa:a': TuColorsA,
+ 'TuDa:b': TuColorsB,
+ 'TuDa:c': TuColorsC,
+ 'TuDa:d': TUColorsD,
+ 'Tab10': Tab10,
+ 'Grace': GraceColors
+ }
+
+ with (config_paths() / 'colorschemes.cfg').open() as f:
+ palette_open = False
+ for line in f:
+ if line.startswith('%--'):
+ color_name = line[4:-1]
+ palette_open = True
+ color_list = []
+ continue
+
+ if palette_open:
+ m = re.match('(\d{1,3}.\d*), (\d{1,3}.\d*), (\d{1,3}.\d*)', line)
+ if m:
+ color_list.append(Colors.from_rgb(*(float(c) for c in m.groups())))
+ elif line == '\n':
+ palettes[color_name] = color_list
+ palette_open = False
+
+ return palettes
+
+
diff --git a/nmreval/lib/importer.py b/nmreval/lib/importer.py
new file mode 100644
index 0000000..e1a426a
--- /dev/null
+++ b/nmreval/lib/importer.py
@@ -0,0 +1,70 @@
+import inspect
+import sys
+from importlib import reload as import_reload
+from importlib.util import spec_from_loader, module_from_spec
+from importlib.machinery import SourceFileLoader
+
+from pathlib import Path
+from typing import Union
+
+
+def import_(path: Union[str, Path], reload: bool = False):
+ """
+ Import file from random destination as module
+ """
+ path = Path(path)
+ name = path.stem
+ if not path.exists():
+ raise FileNotFoundError('File %s not found' % str(path))
+
+ loader = SourceFileLoader(name, str(path))
+
+ spec = spec_from_loader(loader.name, loader)
+ if spec is not None:
+ mod = module_from_spec(spec)
+
+ if name in sys.modules and reload:
+ if reload:
+ import_reload(mod)
+ return sys.modules[name]
+ else:
+ loader.exec_module(mod)
+
+ sys.modules[name] = mod
+
+ return mod
+
+ else:
+ raise ImportError(f'%s not loaded from %s' % (str(name), str(path)))
+
+
+def register_mixin(obj, cls):
+ _obj_cls = obj.__class__
+ _obj_cls_name = obj.__class__.__name__
+ obj.__class__ = type(_obj_cls_name, (_obj_cls, cls), {})
+
+
+def reload_(filename: Union[str, Path]):
+ return import_(filename, reload=True)
+
+
+def find_models(modules):
+ if isinstance(modules, (str, Path)):
+ modules = import_(modules)
+
+ _models = []
+ if modules is None:
+ return _models
+
+ try:
+ _classes = sys.modules[modules.__name__]
+ n = modules.__name__
+ except AttributeError:
+ return _models
+
+ for name, obj in inspect.getmembers(_classes, inspect.isclass):
+ # only import classes defined in file
+ if obj.__module__.startswith(n) and name[0] != '_':
+ _models.append(obj)
+
+ return _models
diff --git a/nmreval/lib/lines.py b/nmreval/lib/lines.py
new file mode 100644
index 0000000..93e7608
--- /dev/null
+++ b/nmreval/lib/lines.py
@@ -0,0 +1,29 @@
+import enum
+try:
+ from ..Qt import QtCore
+ HAS_QT = True
+except ImportError:
+ HAS_QT = False
+
+
+class LineStyle(enum.Enum):
+ No = 0
+ Solid = 1
+ Dashed = 2
+ Dotted = 3
+ DashDotted = 4
+ DashDotDot = 5
+
+ def to_mpl(self):
+ return ['', '-', ':', '-.', (0, (3, 5, 1, 5, 1, 5))][self.value]
+
+ def to_qt(self):
+ if HAS_QT:
+ return [QtCore.Qt.NoPen, QtCore.Qt.SolidLine, QtCore.Qt.DashLine,
+ QtCore.Qt.DotLine, QtCore.Qt.DashDotLine, QtCore.Qt.DashDotDotLine][self.value]
+ else:
+ raise ImportError('Qt library is not loaded.')
+
+ def to_agr(self):
+ # Grace uses: no, solid, dotted, dashed, dashed, dashdot, dashdot, dashdotdot, dotdashdash
+ return [0, 1, 3, 2, 5, 7][self.value]
diff --git a/nmreval/lib/logger.py b/nmreval/lib/logger.py
new file mode 100644
index 0000000..4e101ea
--- /dev/null
+++ b/nmreval/lib/logger.py
@@ -0,0 +1,29 @@
+import sys
+import logging.handlers
+
+from nmreval.configs import config_paths
+
+ERROR_LOG = config_paths() / 'errors.log'
+# initialize logger stuff
+FORMAT = '%(asctime)s - %(levelname)s - %(name)s : %(message)s'
+DATEFMT = '%d/%m/%Y %H:%M:%S'
+LEVEL = logging.ERROR
+myformatter = logging.Formatter(fmt=FORMAT, datefmt=DATEFMT)
+
+logging.basicConfig(level=LEVEL, format=FORMAT, datefmt=DATEFMT)
+flogger = logging.handlers.RotatingFileHandler(str(ERROR_LOG), maxBytes=1024*1024*10, backupCount=2)
+flogger.setFormatter(myformatter)
+flogger.setLevel(LEVEL)
+
+logger = logging.getLogger()
+logger.addHandler(flogger)
+logger.addHandler(logging.NullHandler())
+
+
+# log uncaught errors with traceback, ignore KeyboardInterrupz
+def handle_exception(exc_type, exc_value, exc_traceback):
+ if issubclass(exc_type, KeyboardInterrupt):
+ sys.__excepthook__(exc_type, exc_value, exc_traceback)
+ return
+ logger.error("Uncaught exception:", exc_info=(exc_type, exc_value, exc_traceback))
+ return
diff --git a/nmreval/lib/symbols.py b/nmreval/lib/symbols.py
new file mode 100644
index 0000000..2c02a76
--- /dev/null
+++ b/nmreval/lib/symbols.py
@@ -0,0 +1,95 @@
+import enum
+
+from ..gui_qt.Qt import QtGui, QtCore
+
+
+sym_list = ['o', 's', 'd', 't1', 't3', 't', 't2', '+', 'x', 'star']
+
+
+def symbolcycle():
+ while True:
+ for s in SymbolStyle:
+ if s == SymbolStyle.No:
+ continue
+ yield s
+
+
+class SymbolStyle(enum.Enum):
+ No = 0
+ Circle = 1
+ Square = 2
+ Diamond = 3
+ TriangleUp = 4
+ TriangleLeft = 5
+ TriangleDown = 6
+ TriangleRight = 7
+ Plus = 8
+ X = 9
+ Star = 10
+
+ def to_str(self):
+ if self.value == 0:
+ return None
+ else:
+ return sym_list[self.value-1]
+
+ @staticmethod
+ def from_str(value):
+ try:
+ return SymbolStyle(sym_list.index(value) + 1)
+ except ValueError:
+ return SymbolStyle.No
+
+
+def symbol_path(style: SymbolStyle, size: QtCore.QRectF) -> QtGui.QPainterPath:
+ paths = {3: [(0.1, 0.5), (0.5, 0), (0.9, 0.5), (0.5, 1)],
+ 4: [(0, 1), (0.5, 0), (1, 1)],
+ 5: [(1, 1), (1, 0), (0, 0.5)],
+ 6: [(0, 0), (0.5, 1), (1, 0)],
+ 7: [(0, 1), (0, 0), (1, 0.5)],
+ 8: [(0, 0.5), (1, 0.5), (0.5, 0.5), (0.5, 0), (0.5, 1), (0.5, 0.5)],
+ 9: [(0.1, 0.1), (0.9, 0.9), (0.5, 0.5), (0.1, 0.9), (0.9, 0.1), (0.5, 0.5)],
+ 10: [(0.1, 0.1), (0.9, 0.9), (0.5, 0.5), (0.1, 0.9), (0.9, 0.1), (0.5, 0.5),
+ (0, 0.5), (1, 0.5), (0.5, 0.5), (0.5, 0), (0.5, 1), (0.5, 0.5)]
+ }
+
+ p = QtGui.QPainterPath()
+ if style == SymbolStyle.No:
+ pass
+
+ elif style == SymbolStyle.Circle:
+ p.addEllipse(size)
+
+ elif style == SymbolStyle.Square:
+ p.addRect(size)
+
+ else:
+ width = size.width()
+ height = size.height()
+ pts = paths[style.value]
+ p.moveTo(pts[0][0]*width, pts[0][1]*height)
+ for (x, y) in pts[1:]:
+ p.lineTo(x*width, y*height)
+ p.closeSubpath()
+
+ return p
+
+
+def make_symbol_pixmap(style):
+ px = QtGui.QPixmap(14, 14)
+ px.fill(QtCore.Qt.transparent)
+ px_size = px.rect().adjusted(1, 1, -1, -1)
+
+ painter = QtGui.QPainter(px)
+ painter.setRenderHint(QtGui.QPainter.Antialiasing)
+
+ pal = QtGui.QGuiApplication.palette()
+ col = pal.color(QtGui.QPalette.Text)
+
+ painter.setPen(QtGui.QPen(col, 1, QtCore.Qt.SolidLine, QtCore.Qt.SquareCap, QtCore.Qt.MiterJoin))
+ painter.setBrush(col)
+ painter.drawPath(symbol_path(style, QtCore.QRectF(px_size)))
+
+ painter.end()
+
+ return px
diff --git a/nmreval/lib/utils.py b/nmreval/lib/utils.py
new file mode 100644
index 0000000..ce7e33d
--- /dev/null
+++ b/nmreval/lib/utils.py
@@ -0,0 +1,19 @@
+from typing import TypeVar
+
+from ..math.mittagleffler import mlf
+
+
+
+ArrayLike = TypeVar('ArrayLike')
+
+
+def valid_function(expr: str, extra_namespace: dict = None):
+
+ local = {'mlf': mlf}
+ if extra_namespace is not None:
+ local.update(extra_namespace)
+
+ try:
+ return ne.evaluate(expr, {}, local), True
+ except:
+ return None, False
diff --git a/nmreval/math/__init__.py b/nmreval/math/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nmreval/math/apodization.py b/nmreval/math/apodization.py
new file mode 100644
index 0000000..5bd3901
--- /dev/null
+++ b/nmreval/math/apodization.py
@@ -0,0 +1,123 @@
+import numpy as np
+from scipy.signal.windows import general_hamming
+
+
+class GaussWindow:
+ name = 'Gaussian'
+ equation = r'exp(-(\pi GB x/[2*sqrt(ln2)])^{2})'
+ params = ['GB/Hz']
+
+ @staticmethod
+ def apod(x, gb):
+ return np.exp(-(x*gb*np.pi/4./np.sqrt(np.log(2)))**2)
+
+
+class ExpWindow:
+ name = 'Exponential'
+ equation = r'exp(-\pi LB x)'
+ params = ['LB/Hz']
+
+ @staticmethod
+ def apod(x, lb):
+ return np.exp(-np.pi*x*lb)
+
+
+class GaussExpWindow:
+ name = 'Exp/Gauss'
+ equation = r'exp(\pi LB x) exp(-(\pi GB x/[2*sqrt(ln2)])^{2}))'
+ params = ['LB/Hz', 'GB/Hz']
+
+ @staticmethod
+ def apod(x, gb, lb):
+ return ExpWindow.apod(x, gb) * GaussWindow.apod(x, lb)
+
+
+class Traficante:
+ name = 'Traficante'
+ equation = r'exp(-\pi LB x) / [exp(-\pi LB x)^{2} + exp(-\pi LB (x_{max}-x))^{2}]'
+ params = ['LB/Hz']
+
+ @staticmethod
+ def apod(x, lb):
+ e = ExpWindow.apod(x, lb)
+ E = ExpWindow.apod(np.max(x)-x, lb)
+ return e / (e**2 + E**2)
+
+
+# class GaussExpShiftWindow:
+# name = 'Exp/Gauss with GB shift'
+# equation = r'exp(-\pi LB x) exp(-(\pi GB x(1-S)/[2*sqrt(ln2)])^{2})'
+# params = ['GB/Hz', 'LB/Hz', 'GB shift (0-1)']
+#
+# @staticmethod
+# def apod(x, gb, lb, shift):
+# shift *= max(x)
+# return GaussWindow.apod(x-shift, gb) * ExpWindow.apod(x, lb)
+
+
+class Trapezoid:
+ name = 'Trapezoid'
+ equation = 'x/(p_{1}x_{max}) - 1 - (x_{max}-x)/(x_{max}(1-p_{2}))'
+ params = ['Left ramp (0-1)', 'Right ramp (0-1)']
+
+ @staticmethod
+ def apod(x, lramp, rramp):
+ size = len(x)
+ lramp = min(lramp, 1)
+ rramp = min(rramp, 1)
+ window = np.r_[np.linspace(0, 1, int(lramp*size)),
+ np.ones(int((rramp-lramp)*size)),
+ np.linspace(1, 0, int((1-rramp)*size))]
+ if len(window) < size:
+ window = np.r_[window, np.zeros(size-len(window))]
+ elif len(window) > size:
+ window = window[:size]
+
+ return window
+
+
+class SineBell:
+ name = 'Sine-bell'
+ equation = r'sin(\phi + (\pi-\phi) x/x_{max})^{n}'
+ params = [r'\phi/deg.', 'n']
+
+ @staticmethod
+ def apod(x, phi, pow):
+ phi_rad = np.pi*phi/180.
+ return np.sin((np.pi - phi_rad)*x/np.max(x) + phi_rad)**pow
+
+
+class Box:
+ name = 'Box'
+ equation = 'y[p*x_{max}:x_{max}] = 0'
+ params = ['rel. position']
+
+ @staticmethod
+ def apod(x, step):
+ one = np.ones_like(x)
+ one[int(step*len(x)):] = 0.0
+ return one
+
+
+class Hamming:
+ name = 'Hamming'
+ equation = r'0.54 - 0.46cos(2\pi x/x_{max})'
+ params = []
+
+ @staticmethod
+ def apod(x):
+ return general_hamming(len(x), 0.54)
+
+
+class Hann:
+ name = 'Hann'
+ equation = r'0.5 - 0.5cos(2\pi x/x_{max})'
+ params = []
+
+ @staticmethod
+ def apod(x):
+ return general_hamming(len(x), 0.5)
+
+
+
+
diff --git a/nmreval/math/interpol.py b/nmreval/math/interpol.py
new file mode 100644
index 0000000..81a5b31
--- /dev/null
+++ b/nmreval/math/interpol.py
@@ -0,0 +1,31 @@
+import numpy as np
+from scipy.interpolate import interp1d
+
+
+def interpolate(data, new_x, xlog=False, ylog=False, kind='cubic', extrapolate=True):
+ _x = data.x
+ _y = data.y
+
+ if xlog:
+ _x = np.log10(_x)
+
+ if ylog:
+ _y = np.log10(_y)
+
+ fill = 'extrapolate' if extrapolate else np.nan
+
+ f = interp1d(_x, _y, kind=kind, fill_value=fill)
+
+ ret_val = data.copy()
+
+ if xlog:
+ new_y = f(np.log10(new_x))
+ else:
+ new_y = f(new_x)
+
+ if ylog:
+ ret_val.set_data(x=new_x, y=10**new_y, y_err=None)
+ else:
+ ret_val.set_data(x=new_x, y=new_y, y_err=None)
+
+ return ret_val
diff --git a/nmreval/math/kww.py b/nmreval/math/kww.py
new file mode 100644
index 0000000..c271a91
--- /dev/null
+++ b/nmreval/math/kww.py
@@ -0,0 +1,281 @@
+import numpy as np
+import scipy
+import scipy.special
+
+__all__ = ['kww_cos', 'kww_sin']
+
+__kww_delta = 2.2e-16
+__kww_eps = 5.5e-20
+__max_terms = 150
+__max_iter_int = 10
+
+
+def _kww_low(w, beta, kappa):
+ k = np.atleast_2d(np.arange(kappa, 2*__max_terms+kappa, step=2))
+
+ sign = np.empty(__max_terms)
+ sign[::2] = 1
+ sign[1::2] = -1
+
+ ln_w = np.log(w[:, np.newaxis])
+ ln_a_k = scipy.special.gammaln((k+1) / beta) - scipy.special.gammaln(k+1) + k*ln_w
+ a_k = np.exp(ln_a_k)
+
+ y_n = (sign * a_k).cumsum(axis=1)
+ y_n = np.nan_to_num(y_n)
+
+ z_n = a_k.cumsum(axis=1)
+ z_n = np.nan_to_num(z_n)
+
+ mask = (__kww_eps * z_n[:, :-1] + a_k[:, 1:] <= __kww_delta * y_n[:, : -1]).argmax(axis=1) - 1
+
+ y = y_n[list(range(0, len(w))), mask]
+ y[mask == -1] = np.nan
+
+ return y / beta
+
+
+def kwwc_low(w, beta):
+ return _kww_low(w, beta, 0)
+
+
+def kwws_low(w, beta):
+ return _kww_low(w, beta, 1)
+
+
+def _kww_high(w, beta, kappa):
+ if beta < 0.1 or beta > 2.0:
+ raise ValueError("invalid call to kww_hig: beta out of range")
+
+ ln_omega = np.log(w[:, np.newaxis])
+ k = np.atleast_2d(np.arange(kappa, __max_terms+kappa))
+
+ if kappa:
+ c_k = np.sin(k*beta*np.pi/2)
+ else:
+ c_k = np.cos(k*beta*np.pi/2)
+
+ ln_u_k = scipy.special.gammaln(k*beta+1) - scipy.special.gammaln(k+1) - (k*beta+1) * ln_omega
+ u_k = np.exp(ln_u_k)
+
+ a_k = c_k*u_k
+
+ if beta > 1:
+ r_k = np.sin(np.pi/2/beta)**(-1-k*beta)
+ else:
+ r_k = np.ones_like(k)
+ sign = 1-2*((k-kappa) % 2)
+
+ y_n = (sign*a_k).cumsum(axis=1)
+ y_n = np.nan_to_num(y_n)
+ z_n = np.abs(a_k).cumsum(axis=1)
+ z_n = np.nan_to_num(z_n)
+
+ mask = (__kww_eps*z_n[:, :-1] + u_k[:, 1:]*r_k[:, 1:] <= __kww_delta*np.abs(y_n[:, : -1])).argmax(axis=1)-1
+ y = y_n[list(range(0, len(w))), mask]
+ y[mask == -1] = np.nan
+
+ return y
+
+
+def kwws_high(w, beta):
+ return _kww_high(w, beta, 0)
+
+
+def kwwc_high(w, beta):
+ return _kww_high(w, beta, 1)
+
+
+def _kww_mid(w, beta, kappa):
+ if beta < 0.1 or beta > 2.0:
+ raise ValueError("invalid call to kww_mid: beta out of range")
+ elif any(w < 0):
+ raise ValueError("invalid call to kww_mid: w out of range")
+
+ if kappa and beta == 2:
+ return np.sqrt(np.pi) / 2 * np.exp(-np.sqrt(w) / 4)
+
+ if beta < 0.15:
+ p = 1.8
+ q = 0.2
+ elif beta < 0.25:
+ p = 1.6
+ q = 0.4
+ elif beta < 1:
+ p = 1.4
+ q = 0.6
+ elif beta < 1.75:
+ p = 1.0
+ q = 0.2
+ elif beta < 1.95:
+ p = 0.75
+ q = 0.2
+ else:
+ p = 0.15
+ q = 0.4
+
+ n = 10
+ y = np.zeros(w.shape, dtype=float)
+ y_min = 2e-20
+ for _ in range(__max_iter_int):
+ y_last = y
+ h = np.log(np.log((np.pi+1)*10*n/__kww_delta/y_min) / p) / n
+
+ k = np.arange(-n, n+1, dtype=float)
+ sign = 1 - 2*(k % 2)
+ hk = h*k
+
+ eta_k = 2*p*np.sinh(hk) + 2*q*hk
+ eta_k_prime = 2*p*np.cosh(hk) + 2*q
+
+ exp_eta = np.exp(-eta_k)
+ expm1_eta = -np.expm1(-eta_k)
+ expm1_eta[n] = 1.
+ a_k = np.pi * k / expm1_eta
+ a_k[n] = np.pi/h/eta_k_prime[n]
+
+ b_k_2 = sign * np.sin(np.pi*k*exp_eta / expm1_eta)
+ mask_exp = exp_eta > 1
+ if kappa:
+ b_k_2[mask_exp] = np.cos(np.pi*k[mask_exp]/expm1_eta[mask_exp])
+ else:
+ b_k_2[mask_exp] = np.sin(np.pi*k[mask_exp]/expm1_eta[mask_exp])
+
+ phi_k_prime = 1. / expm1_eta - hk*exp_eta*eta_k_prime / expm1_eta / expm1_eta
+ b_k = phi_k_prime * b_k_2
+ b_k[n] = np.sin(a_k[n]) / 2
+
+ g_k = np.exp(-(a_k / w[:, np.newaxis])**beta)
+ diff_mode = kappa and (beta > 1.75)
+ if diff_mode:
+ g_k -= np.exp(-(a_k / w[:, np.newaxis])**2)
+
+ y_k = b_k*g_k
+
+ y = y_k.sum(axis=1)
+ z = np.abs(y_k).sum(axis=1)
+
+ if diff_mode:
+ y += w/np.sqrt(np.pi)/2 * np.exp(-w**2 / 4)
+
+ if all(np.abs(y-y_last) + __kww_eps*z < __kww_delta * np.abs(y)):
+ return y * np.pi / w
+
+ n *= 2
+
+ return y * np.pi / w
+
+
+def kwwc_mid(w, beta):
+ return _kww_mid(w, beta, True)
+
+
+def kwws_mid(w, beta):
+ return _kww_mid(w, beta, False)
+
+
+def _kwwc_lim_low(b):
+ if b > 1.024:
+ return -0.8774954 * b + 3.5873 * b**2 - 2.083 * b**3 + 0.3796 * b**4
+ else:
+ return np.exp(-0.02194 / b**2 - 4.130 / b + 2.966189 + 0.030104 * b + 1.062 * b**2)
+
+
+def _kwws_lim_low(b):
+ if b > 1.024:
+ return -1.68725 * b + 4.8108 * b**2 - 2.561 * b**3 + 0.442 * b**4
+ else:
+ return np.exp(-0.03208 / b**2 - 4.314 / b + 3.516200 - 0.50287 * b + 1.240 * b**2)
+
+
+def _kwwc_lim_hig(b):
+ if b < 0.82:
+ return np.exp(0.006923209 / b ** 2 - 1.321692 / b - 1.44582 + 2.516339 * b + 0.2973773 * b ** 2)
+ else:
+ return np.exp(-0.746496154631 + 6.057558 * (b-0.82) - 3.41052 * (b-0.82)**2 + 0.7932314 * (b-0.82)**3)
+
+
+def _kwws_lim_hig(b):
+ if b < 0.82:
+ return np.exp(0.07847516 / b**2 - 2.585876 / b + 4.999414 - 8.460926 * b + 6.289183 * b**2)
+ else:
+ return np.exp(-0.962597724393 + 5.818057 * (b - 0.82) - 3.026212 * (b-0.82)**2 + 0.5485754 * (b-0.82)**3)
+
+
+def kww_cos(w, tau, beta):
+ r"""
+ Calculate \int_0^\infty dt cos(w*t) exp(-(t/tau)^beta)
+ :param w: array-like
+ :param tau: float
+ :param beta: float; 0.1 < beta <= 2
+ :return: array (w.shape)
+ """
+ # check input data
+ if beta < 0.1:
+ raise ValueError("kww: beta smaller than 0.1")
+
+ if beta > 2.0:
+ raise ValueError("kww: beta larger than 2.0")
+
+ if beta == 1:
+ return tau/(1 + (w*tau)**2)
+
+ if beta == 2:
+ return tau*np.sqrt(np.pi)/2 * np.exp(-(w*tau)**2 / 4)
+
+ _w = np.atleast_1d(np.abs(w*tau))
+
+ low_approx = _w < _kwwc_lim_low(beta)
+ high_approx = _w > _kwwc_lim_hig(beta)
+ mid_integration = ~(low_approx | high_approx)
+
+ ret_val = np.zeros_like(_w, dtype=float)
+ ret_val[high_approx] = kwwc_high(_w[high_approx], beta)
+ ret_val[low_approx] = kwwc_low(_w[low_approx], beta)
+ ret_val[mid_integration] = kwwc_mid(_w[mid_integration], beta)
+ ret_val[np.isnan(ret_val)] = kwwc_mid(_w[np.isnan(ret_val)], beta)
+
+ ret_val[np.where(_w == 0)] = scipy.special.gamma(1 / beta) / beta
+
+ if np.isscalar(_w):
+ return tau*ret_val[0]
+
+ return tau*ret_val
+
+
+# \int_0^\infty dt sin(w*t) exp(-(t/tau)^beta)
+def kww_sin(w, tau, beta):
+ # check input data
+ if beta < 0.1:
+ raise ValueError("kww: beta smaller than 0.1")
+
+ if beta > 2.0:
+ raise ValueError("kww: beta larger than 2.0")
+
+ if beta == 1:
+ return w*tau**2/(1+(w*tau)**2)
+
+ if beta == 2:
+ return tau*scipy.special.dawsn(w*tau/2.0)
+
+ _w = np.atleast_1d(np.abs(w*tau))
+
+ sign = np.sign(_w)
+
+ low_approx = _w < _kwwc_lim_low(beta)
+ high_approx = _w > _kwwc_lim_hig(beta)
+ mid_integration = ~(low_approx | high_approx)
+
+ ret_val = np.zeros_like(_w, dtype=float)
+ ret_val[high_approx] = kwws_high(_w[high_approx], beta)
+ ret_val[low_approx] = kwws_low(_w[low_approx], beta)
+ ret_val[mid_integration] = kwws_mid(_w[mid_integration], beta)
+
+ ret_val[np.isnan(ret_val)] = kwws_mid(_w[np.isnan(ret_val)], beta)
+
+ ret_val[_w == 0] = 0.0
+
+ if np.isscalar(_w):
+ return sign*tau*ret_val[0]
+
+ return sign*ret_val*tau
diff --git a/nmreval/math/logfourier.py b/nmreval/math/logfourier.py
new file mode 100644
index 0000000..8b5c1c8
--- /dev/null
+++ b/nmreval/math/logfourier.py
@@ -0,0 +1,121 @@
+import numpy as np
+
+__doc__ = r"""
+Calculates the Fourier transform of real-valued function on a logarithmic scale:
+
+.. math::
+ F(\omega) = \int_0^\infty f(t) \exp(i\omega t) \,dt
+
+For a function f(t) sampled at :math:`N` discrete timepoints, the integrals are approximated by
+
+.. math::
+ \integral_0^\infty f(t) \cos(\omega t) \approx \sum_{i=0}^{N-1}
+ \frac{f(t_{i+i}) - f(t_i)}{f(t_{i+i}) - f(t_i)}{t_{i+1} - t_i}
+ \frac{\cos(\omega t_{i+1}) - \cos(\omega t_i)}{\omega^2}
+
+and
+
+.. math::
+ \integral_0^\infty f(t) \sin(\omega t) \approx \frac{f(t_0)}{\omega}
+ \sum_{i=0}^{N-1} \frac{f(t_{i+i}) - f(t_i)}{f(t_{i+i}) - f(t_i)}{t_{i+1} - t_i}
+ \frac{\sin(\omega t_{i+1}) - \sin(\omega t_i)}{\omega^2}
+
+More details can befound in [Blo]
+
+[Blo] Blochowicz, Th.: Ph.D. Thesis, Universität Bayreuth (2003)
+
+"""
+
+
+def logft_cos(x, y, new_x=None):
+ n = x.size
+ if new_x is None:
+ new_x = 2 * np.pi * np.geomspace(1 / np.max(x), 1 / np.min(x), num=n)
+
+ dydx = np.diff(y) / np.diff(x)
+
+ # broadcasting faster for lower array sizes, calculation of cosine makes looping as fast
+ # and memory needs are more a concern
+ if n < 5000:
+ wt = new_x[:, None] * x[None, :]
+ np.cos(wt, out=wt)
+ ret_val = np.sum(dydx * np.diff(wt, axis=1), axis=1)
+ else:
+ ret_val = np.zeros(n)
+ for i, o in enumerate(new_x):
+ cos_wt = np.cos(o * x)
+ ret_val[i] = np.sum(dydx * np.diff(cos_wt))
+
+ ret_val /= new_x**2
+
+ return new_x, ret_val
+
+
+def logft_sin(x, y, new_x=None):
+ n = x.size
+ if new_x is None:
+ new_x = 2 * np.pi * np.geomspace(1 / np.max(x), 1 / np.min(x), num=n)
+
+ dydx = np.diff(y) / np.diff(x)
+
+ # broadcasting faster for lower array sizes, calculation of cosine makes looping as fast
+ # and memory needs are more a concern
+ if n < 5000:
+ wt = new_x[:, None] * x[None, :]
+ np.sin(wt, out=wt)
+ ret_val = np.sum(dydx * np.diff(wt, axis=1), axis=1)
+ else:
+ ret_val = np.zeros(n)
+ for i, o in enumerate(new_x):
+ sin_wt = np.sin(o*x)
+ ret_val[i] = np.sum(dydx * np.diff(sin_wt))
+
+ ret_val /= new_x**2
+ ret_val += y[0] / new_x
+
+ return new_x, ret_val
+
+
+def logft_cmplx(x, y, new_x=None, backward: bool = False):
+ n = x.size
+ if new_x is None:
+ new_x = 2 * np.pi * np.logspace(-np.log10(np.max(x)), -np.log10(np.min(x)), num=n)
+
+ dydx = np.diff(y) / np.diff(x)
+ sign = 2*backward - 1
+ if n < 5000:
+ wt = new_x[:, None] * x[None, :]
+ exp_wt = np.exp(sign*1j*wt)
+
+ ret_val = np.sum(dydx * np.diff(exp_wt, axis=1), axis=1)
+ else:
+ ret_val = np.zeros(n, dtype=complex)
+ for i, o in enumerate(new_x):
+ exp_wt = np.exp(sign*1j*o*x)
+ ret_val[i] = np.sum(dydx * np.diff(exp_wt))
+
+ ret_val /= new_x**2
+ ret_val += sign * 1j*y[0]/new_x
+
+ return new_x, ret_val
+
+
+def logft(x, y, new_x=None, mode='cos'):
+ if mode not in ['cos', 'sin', 'complex']:
+ raise ValueError(f'Unknown mode {mode}, use "cos", "sin", "complex".')
+
+ ft_func = {'cos': logft_cos, 'sin': logft_sin, 'complex': logft_cmplx}[mode]
+
+ return ft_func(x, y, new_x=new_x)
+
+
+def logift(x, y, new_x=None, mode='cos'):
+ if mode not in ['cos', 'sin', 'complex']:
+ raise ValueError(f'Unknown mode {mode}, use "cos", "sin", "complex".')
+
+ if mode == 'complex':
+ return logft_cmplx(x, y, new_x=new_x, backward=True)
+ elif mode == 'cos':
+ return logft_cos(x, y, new_x=new_x)
+ else:
+ return logft_sin(x, y, new_x=new_x)
diff --git a/nmreval/math/mittagleffler.py b/nmreval/math/mittagleffler.py
new file mode 100644
index 0000000..44e42f1
--- /dev/null
+++ b/nmreval/math/mittagleffler.py
@@ -0,0 +1,262 @@
+from numbers import Number
+
+import numpy as np
+from math import sqrt, log, exp, ceil
+
+from scipy.special import gamma
+
+__all__ = ['mlf']
+
+# calculate Mittag-Leffler based on MATLAB code
+# https://de.mathworks.com/matlabcentral/fileexchange/48154-the-mittag-leffler-function
+# R. Garrappa, Numerical evaluation of two and three parameter Mittag-Leffler functions,
+# SIAM Journal of Numerical Analysis, 2015, 53(3), 1350-1369
+
+
+def mlf(z, a: float, b: float = 1, g: float = 1):
+ if b == 1 and gamma == 1:
+ if a == 0:
+ return 1 / (1 - z)
+ if a == 1:
+ return np.exp(z)
+ if a == 2:
+ return np.cosh(z)
+
+ if g > 1 and a > 1:
+ raise ValueError('Three parameter Mittag-Leffler needs 0 1e-15
+ s_star = s_star[idx_zero]
+ phi_s = phi_s[idx_zero]
+
+ # add origin and fictitious phi(s_j+1)=infty
+ phi_s = np.r_[0, phi_s, np.infty]
+ s_star = np.r_[0, s_star]
+
+ # strength of the singularities
+ p = g * np.ones(phi_s.shape, dtype=np.float)
+ p[0] = max(0, -2*(a*g-b + 1))
+ q = g * np.ones(phi_s.shape, dtype=np.float)
+ q[-1] = np.inf
+
+ log_epsilon = log(1e-15) # limit for errors
+ # Looking for the admissible regions with respect to round-off errors admissible_regions = find( ...
+ admissable_regions = np.nonzero((phi_s[:-1] < (log_epsilon-log(np.finfo(np.float).eps))) &
+ (phi_s[:-1] < phi_s[1:]))[0]
+
+ # initialize vectors for optimal parameters
+ last_region = admissable_regions[-1]
+ mu_list = np.ones(last_region+1) * np.inf
+ num_intsteps_list = np.ones(last_region+1) * np.inf
+ h_list = np.ones(last_region+1) * np.inf
+
+ # Evaluation of parameters for inversion of LT in each admissible region
+ len_s_star = len(s_star)-1
+ while True:
+ for j in admissable_regions:
+ if j < len_s_star:
+ mu_j, h_j, n_j = optimum_bounded_region(phi_s[j], phi_s[j+1], p[j], q[j], log_epsilon)
+ else:
+ mu_j, h_j, n_j = optimum_unbounded_region(phi_s[j], p[j], log_epsilon)
+
+ mu_list[j] = mu_j
+ h_list[j] = h_j
+ num_intsteps_list[j] = n_j
+
+ if min(num_intsteps_list) > 200:
+ log_epsilon += log(10)
+ else:
+ break
+
+ # selection of admissible region for integration which involves the minimum number of nodes
+ num_intsteps_min_idx = np.argmin(num_intsteps_list)
+ num_intsteps = num_intsteps_list[num_intsteps_min_idx]
+ mu = mu_list[num_intsteps_min_idx]
+ h = h_list[num_intsteps_min_idx]
+
+ # Evaluation of the inverse Laplace transform (eq. (2.5)
+ u = h * np.arange(-num_intsteps, num_intsteps+1)
+ z = mu * (1j*u + 1)**2 # eq. (3.1)
+ z_deriv = -2*mu*u + 2j * mu
+ g_u = np.exp(z) * z**(a*g - b) * z_deriv / (z**a - lamb)**g # eq (2.7)
+ integration_part = h * np.sum(g_u) / 2 / pi / 1j
+
+ # evaluation of residues
+ ss_star = s_star[num_intsteps_min_idx+1:]
+ residues = np.sum(1. / a * ss_star**(1 - b) * np.exp(ss_star))
+
+ # Evaluation of the ML function
+ ret_val = residues + integration_part
+ if np.imag(lamb) == 0.:
+ ret_val = np.real(ret_val)
+
+ return ret_val
+
+
+# Finding optimal parameters in a right-bounded region
+def optimum_bounded_region(phi_s_j, phi_s_j1, pj, qj, log_epsilon):
+ log_eps = -36.043653389117154
+ fac = 1.01
+ conservative_error_analysis = False
+
+ # Maximum value of fbar as the ration between tolerance and round-off unit
+ f_max = exp(log_epsilon - log_eps)
+ f_bar = np.nan
+
+ # Evaluation of the starting values for sq_phi_star_j and sq_phi_star_j1
+ sq_phi_j = sqrt(phi_s_j)
+ threshold = 2 * sqrt(log_epsilon - log_eps)
+ sq_phi_j1 = min(sqrt(phi_s_j1), threshold - sq_phi_j)
+
+ if pj < 1.0e-14:
+ if qj < 1.0e-14:
+ # Zero or negative values of pj and qj
+ sq_phibar_j = sq_phi_j
+ sq_phibar_j1 = sq_phi_j1
+
+ else:
+ # Zero or negative values of just pj
+ sq_phibar_j = sq_phi_j
+ if sq_phi_j > 0:
+ f_min = fac * (sq_phi_j / (sq_phi_j1-sq_phi_j))**qj
+ else:
+ f_min = fac
+
+ if f_min < f_max:
+ f_bar = f_min + f_min/f_max*(f_max-f_min)
+ fq = f_bar**(-1/qj)
+ sq_phibar_j1 = (2*sq_phi_j1 - fq*sq_phi_j)/(2+fq)
+ else:
+ return 0, 0, np.inf
+
+ else:
+ if qj < 1e-14:
+ # zero or negative values of just q
+ sq_phibar_j1 = sq_phi_j1
+ f_min = fac*(sq_phi_j1/(sq_phi_j1-sq_phi_j))**pj
+ if f_min < f_max:
+ f_bar = f_min + f_min/f_max*(f_max-f_min)
+ fp = f_bar**(-1/pj)
+ sq_phibar_j = (2*sq_phi_j+fp*sq_phi_j1)/(2-fp)
+ else:
+ return 0, 0, np.inf
+
+ else:
+ # positive values of both pj and qj
+ f_min = fac*(sq_phi_j+sq_phi_j1) / (sq_phi_j1-sq_phi_j)**max(pj, qj)
+ if f_min < f_max:
+ f_min = max(f_min, 1.5)
+ f_bar = f_min + f_min/f_max*(f_max-f_min)
+ fp = f_bar**(-1/pj)
+ fq = f_bar**(-1/qj)
+ if conservative_error_analysis:
+ w = -2*phi_s_j1 / (log_epsilon - phi_s_j1)
+ else:
+ w = -phi_s_j1/log_epsilon
+ den = 2+w - (1+w)*fp + fq
+ sq_phibar_j = ((2+w+fq)*sq_phi_j + fp*sq_phi_j1)/den
+ sq_phibar_j1 = (-(1+w)*fq*sq_phi_j + (2+w-(1+w)*fp)*sq_phi_j1)/den
+ else:
+ return 0, 0, np.inf
+
+ log_epsilon = log_epsilon - log(f_bar)
+ if conservative_error_analysis:
+ w = -2*sq_phibar_j1**2 / (log_epsilon-sq_phibar_j1**2)
+ else:
+ w = -sq_phibar_j1**2 / log_epsilon
+
+ mu_j = (((1+w)*sq_phibar_j + sq_phibar_j1)/(2+w))**2
+ h_j = -2*np.pi / log_epsilon*(sq_phibar_j1-sq_phibar_j) / ((1+w)*sq_phibar_j + sq_phibar_j1)
+ n_j = ceil(sqrt(1-log_epsilon/mu_j)/h_j)
+
+ return mu_j, h_j, n_j
+
+
+# Finding optimal parameters in a right-unbounded region
+def optimum_unbounded_region(phi_s_j, pj, log_epsilon):
+ # evaluation of the starting values for sq_phi_star_j
+ sq_phi_s_j = sqrt(phi_s_j)
+ phibar_j = phi_s_j*1.01 if phi_s_j > 0 else 0.01
+
+ sq_phibar_j = sqrt(phibar_j)
+
+ # Definition of some constants
+ f_min = 1
+ f_max = 10
+ f_tar = 5
+
+ # Iterative process to look for fbar in [f_min,f_max]
+ while True:
+ phi_t = phibar_j
+ log_eps_phi_t = log_epsilon/phi_t
+ n_j = ceil(phi_t/np.pi * (1 - 3*log_eps_phi_t/2 + sqrt(1-2*log_eps_phi_t)))
+ a = np.pi*n_j/phi_t
+ sq_mu_j = sq_phibar_j * abs(4-a) / abs(7-sqrt(1+12*a))
+ fbar = ((sq_phibar_j-sq_phi_s_j)/sq_mu_j)**(-pj)
+
+ if (pj < 1.0e-14) or (f_min < fbar < f_max):
+ break
+
+ sq_phibar_j = f_tar**(-1/pj)*sq_mu_j + sq_phi_s_j
+ phibar_j = sq_phibar_j**2
+
+ mu_j = sq_mu_j**2
+ h_j = (-3*a - 2 + 2*sqrt(1+12*a))/(4-a)/n_j
+
+ # Adjusting integration parameters to keep round-off errors under control
+ log_eps = log(np.finfo(float).eps)
+ threshold = (log_epsilon - log_eps)
+ if mu_j > threshold:
+ q = 0 if abs(pj) < 1.0e-14 else f_tar**(-1/pj)*sqrt(mu_j)
+ phibar_j = (q + sqrt(phi_s_j))**2
+ if phibar_j < threshold:
+ w = sqrt(log_eps/(log_eps-log_epsilon))
+ u = sqrt(-phibar_j/log_eps)
+ mu_j = threshold
+ n_j = ceil(w*log_epsilon/2/np.pi/(u*w-1))
+ h_j = sqrt(log_eps/(log_eps - log_epsilon))/n_j
+ else:
+ n_j = np.inf
+ h_j = 0
+
+ return mu_j, h_j, n_j
+
+
+if __name__ == '__main__':
+ import matplotlib.pyplot as plt
+ xvalue = np.geomspace(1e-3, 100, num=20) # np.array([1+1j])
+ # plt.semilogx(xvalue, mlf(-xvalue, a=0.8, b=0.3, flag=False))
+ plt.semilogx(xvalue, mlf(-xvalue, a=0.8, b=0.3))
+ plt.show()
diff --git a/nmreval/math/orientations.py b/nmreval/math/orientations.py
new file mode 100755
index 0000000..4a67b04
--- /dev/null
+++ b/nmreval/math/orientations.py
@@ -0,0 +1,173 @@
+import numpy as np
+import numpy.random as random
+from math import pi, sqrt
+
+
+def random_orientation(samples=150, planar=True):
+ a = random.rand(samples, 2)
+
+ alpha = 2*pi*a[:, 0]
+ if planar:
+ beta = pi*a[:, 1]
+ wt = np.sin(beta)
+ else:
+ beta = np.arccos(1-2*a[:, 1])
+ wt = np.ones_like(beta)
+
+ return alpha, beta, wt
+
+
+def random_planar(samples=150):
+ return random_orientation(samples=samples, planar=True)
+
+
+def random_spherical(samples=150):
+ return random_orientation(samples=samples, planar=False)
+
+
+def grid(a=144, b=None, planar=True):
+ if b:
+ a_axis = range(a)
+ b_axis = range(1, b+1)
+ else:
+ a = b = int(sqrt(a))
+ a_axis = range(a)
+ b_axis = range(1, b+1)
+ alpha = 2*pi*np.array(a_axis*b)/a
+ beta = []
+ for i in b_axis:
+ beta += [i]*a
+ if planar:
+ beta = pi*np.array(beta)/(b+1)
+ wt = np.sin(beta)
+ else:
+ beta = np.arccos(1 - 2*np.array(beta)/(b+1))
+ wt = np.ones_like(beta)
+
+ return alpha, beta, wt
+
+
+def grid_planar(a=12, b=None):
+ return grid(a=a, b=b, planar=True)
+
+
+def grid_spherical(a=12, b=None):
+ return grid(a=a, b=b, planar=False)
+
+
+def zcw(n=150, planar=True):
+ fib0, fib1, fib2 = [0, 1, 1]
+ while True:
+ fib0, fib1, fib2 = fib1, fib0 + fib1, fib1+fib2
+ if fib1+fib2 >= n:
+ break
+
+ arr = np.arange(fib2)
+ alpha = 2*pi*np.mod(arr/fib2, 1.0)
+ if planar:
+ beta = pi*np.mod(arr*fib0/fib2, 1.0)
+ wt = np.sin(beta)
+ else:
+ beta = np.arccos(1 - 2*np.mod(arr*fib0/fib2, 1.0))
+ wt = np.ones_like(beta)
+
+ return alpha, beta, wt
+
+
+def zcw_planar(n=150):
+ return zcw(n=n, planar=True)
+
+
+def zcw_spherical(n=150):
+ return zcw(n=n, planar=False)
+
+
+def alderman(samples=144):
+ n = int(0.5*sqrt(samples))
+ alpha = []
+ beta = []
+ wt = []
+ for i in range(-n, n):
+ i_abs = abs(i)
+ for j in range(-n+i_abs, n-i_abs+1):
+ j_abs = abs(j)
+ k = n-i_abs-j_abs
+ inv_r_ij = 1/sqrt(i**2 + j**2 + k**2)
+ a_ij = (np.arctan2(j, i)+2*pi) % (2*pi)
+ alpha.append(a_ij)
+ alpha.append(a_ij)
+ b_ij = np.arccos(k*inv_r_ij)
+ b_ij_neg = np.arccos(-k*inv_r_ij)
+ beta.append(b_ij)
+ beta.append(b_ij_neg)
+ wt.append((n*inv_r_ij)**3)
+ wt.append((n*inv_r_ij)**3)
+
+ return np.array(alpha), np.array(beta), np.array(wt)
+
+
+def sophe(samples=146):
+ n = int(0.5*sqrt(2*samples))
+ alpha = []
+ beta = []
+ for quad in [0, 1, 2, 3]:
+ for i in range(1, n):
+ for j in range(1, i+1):
+ # 0,90 / 0,90
+ alpha.append(0.5*pi*(i-j+1)/i + 0.5*quad*pi)
+ beta.append(0.5*pi*i/(n-1))
+
+ # 0,90 / 90,180
+ if i != n-1:
+ alpha.append(0.5*pi*(i-j+1)/i + 0.5*quad*pi)
+ beta.append(0.5*pi*(n-i-1)/(n-1)+0.5*pi)
+
+ return np.array(alpha), np.array(beta), np.ones_like(alpha)
+
+
+def spherical_to_xyz(phi, theta, r=1.):
+ x = r*np.cos(phi)*np.sin(theta)
+ y = r*np.sin(phi)*np.sin(theta)
+ z = r*np.cos(theta)
+
+ return x, y, z
+
+
+def xyz_to_spherical(x, y, z):
+ r = np.sqrt(x**2 + y**2 + z**2)
+ th = np.arccos(z/r)
+ ph = np.arctan2(y, x)
+
+ return r, th, ph
+
+
+if __name__ == '__main__':
+ import matplotlib.pyplot as plt
+ from mpl_toolkits.mplot3d import Axes3D
+
+ if True:
+ fig2, (ax2, ax3, ax4) = plt.subplots(3, 1, sharex=True)
+ for i in [alderman]: # sophe, grid_planar, grid_spherical, random_planar, random_spherical, zcw_planar, zcw_spherical
+ fig = plt.figure(str(i))
+ ax = fig.add_subplot(211, projection='3d')
+
+ ax5 = fig.add_subplot(212)
+
+ a, b, wt = i(150)
+ x, y, z = spherical_to_xyz(a, b)
+ ax.scatter(xs=x, ys=y, zs=z)
+ ax2.plot(np.linspace(0, 1, num=len(a)), a)
+ ax3.plot(np.linspace(0, 1, num=len(a)), b)
+ ax4.plot(np.linspace(0, 1, num=len(a)), wt)
+
+ ax5.plot(a*180/pi, b*180/pi, 'o')
+
+ ax5.set_xticks([0., 90, 180, 270, 360])
+ ax5.set_xticklabels(['0', '90', '180', '270', '360'])
+ ax5.set_yticks([0., 90, 180])
+ ax5.set_yticklabels(['0', '90', '180'])
+
+ ax5.set_xlim([0, 360])
+ ax5.set_ylim([0, 180])
+
+ plt.show()
diff --git a/nmreval/math/pca.py b/nmreval/math/pca.py
new file mode 100644
index 0000000..d327c38
--- /dev/null
+++ b/nmreval/math/pca.py
@@ -0,0 +1,14 @@
+import numpy as np
+
+
+def pca(a: np.ndarray, center: bool = False, scale: bool = False):
+ x = np.atleast_2d(a)
+
+ if center:
+ x -= x.mean(axis=0)
+ if scale:
+ x /= x.std(axis=0)
+
+ u, d, vt = np.linalg.svd(x, full_matrices=False)
+
+ return u * d, vt.T
diff --git a/nmreval/math/smooth.py b/nmreval/math/smooth.py
new file mode 100644
index 0000000..5207720
--- /dev/null
+++ b/nmreval/math/smooth.py
@@ -0,0 +1,167 @@
+import numpy as np
+import numpy.polynomial.polynomial as poly
+from scipy import signal as signal
+
+
+__all__ = ['smooth', 'loess', 'savgol',
+ 'running_max', 'running_min',
+ 'running_var', 'running_std',
+ 'running_median', 'running_mean',
+ 'running_sum']
+
+
+def loess(x, y, window_size, it=0, deg=2):
+ # ULTRA LANGSAM !!!
+ it = max(it, 0)
+
+ if window_size >= len(x):
+ raise ValueError('Window is too large for data')
+
+ if deg not in [1, 2]:
+ raise ValueError('Loess supports only degrees 1 and 2.')
+
+ if np.iscomplexobj(y):
+ _y = np.array([y.real, y.imag])
+ else:
+ _y = np.array([y])
+
+ new_y = np.zeros_like(_y)
+ for i, x_i in enumerate(x):
+ dist = np.abs(x - x_i)
+ idx = np.argsort(dist)[:window_size]
+
+ x_w = x[idx]
+ y_w = y[idx]
+ dist = dist[idx]
+
+ weight = (1 - np.abs(dist / np.max(dist))**3)**3
+
+ p = poly.polyfit(x_w, y_w, deg, w=weight)
+
+ for _ in range(it):
+ y_fit = poly.polyval(x_w, p)
+
+ resid = np.abs(y_fit - y_w)
+ mad = np.median(resid)
+ ww = (resid / 6. / mad)**2
+ ww = np.clip(ww, 0, 1)
+ if np.all(ww > 0.97):
+ break
+
+ robust_wt = (1 - np.clip(ww, 0, 1))**2
+ p = poly.polyfit(x_w, y_w, deg, w=weight*robust_wt)
+
+ new_y[:, i] = poly.polyval(x_i, p)
+
+ if new_y.shape[0] == 2:
+ new_y = new_y[0] + 1j * new_y[1]
+
+ new_y = new_y.reshape(x.shape)
+
+ return new_y
+
+
+def savgol(x, y, window_size: int, deg: int = 2, mode: str = 'mirror'):
+ if window_size % 2 == 0:
+ print('number of points must be odd, increased by one')
+ window_size += 1
+
+ if window_size > len(x):
+ raise ValueError('Window is bigger than size of x')
+
+ if deg >= window_size:
+ raise ValueError(f'Polynomial order {deg} is larger than window size {window_size}')
+
+ new_y = np.zeros_like(y)
+ new_y += signal.savgol_filter(y.real, window_size, deg, mode=mode)
+
+ if np.iscomplexobj(y):
+ new_y += 1j * signal.savgol_filter(y.imag, window_size, deg, mode=mode)
+
+ return new_y
+
+
+def running_mean(x, y, window_size):
+ return _running_func(np.nanmean, x, y, window_size)
+
+
+def running_median(x, y, window_size):
+ return _running_func(np.nanmedian, x, y, window_size)
+
+
+def running_std(x, y, window_size):
+ return _running_func(np.nanstd, x, y, window_size)
+
+
+def running_var(x, y, window_size):
+ return _running_func(np.nanvar, x, y, window_size)
+
+
+def running_max(x, y, window_size):
+ return _running_func(np.nanmax, x, y, window_size)
+
+
+def running_min(x, y, window_size):
+ return _running_func(np.nanmin, x, y, window_size)
+
+
+def running_sum(x, y, window_size):
+ return _running_func(np.nansum, x, y, window_size)
+
+
+def _running_func(func, x, y, window_size):
+ x = np.asarray(x)
+ y = np.asarray(y)
+
+ if x.ndim != 1 or y.ndim != 1:
+ raise ValueError('Only 1-dim arrays can be moved')
+
+ if not (0 < window_size < y.shape[-1]):
+ raise ValueError('Window should be positive and smaller then arrays')
+
+ new_x = np.mean(_moving_window(x, window_size), axis=-1)
+ new_y = func(_moving_window(y, window_size), axis=-1)
+
+ return new_x, new_y
+
+
+def _moving_window(arr, nn):
+ shapes = arr.shape[:-1] + (arr.shape[-1]-nn+1, nn) # create shape (..., len(arr)-n+1, n)
+ strides = arr.strides + (arr.strides[-1], ) # extend strides
+
+ return np.lib.stride_tricks.as_strided(arr, shapes, strides)
+
+
+_funcs = {'loess': loess, 'savgol': savgol, 'mean': running_mean, 'median': running_median,
+ 'std': running_std, 'var': running_var, 'max': running_max, 'min': running_min, 'sum': running_sum}
+
+
+def smooth(data, window_size, mode='mean', logx=False, logy=False, **kwargs):
+ try:
+ func = _funcs[mode]
+ except KeyError:
+ raise ValueError(f'Unknown mode {mode}')
+
+ _x, _y = data.x, data.y
+ if logx:
+ _x = np.log10(_x)
+ if logy:
+ _y = np.log10(_y)
+
+ new_values = func(_x, _y, window_size, **kwargs)
+
+ if isinstance(new_values, tuple):
+ new_x, new_y = new_values
+ else:
+ new_x = _x
+ new_y = new_values
+
+ if logx:
+ new_x = 10**new_x
+ if logy:
+ new_y = 10**new_y
+
+ new_data = data.copy()
+ new_data.set_data(x=new_x, y=new_y, y_err=None)
+
+ return new_data
diff --git a/nmreval/math/wideline.py b/nmreval/math/wideline.py
new file mode 100644
index 0000000..de8ec95
--- /dev/null
+++ b/nmreval/math/wideline.py
@@ -0,0 +1,383 @@
+import numpy as np
+from scipy.integrate import trapz
+
+from .orientations import zcw_spherical as crystallites
+
+
+class Powder(object):
+ def __init__(self, n=100):
+ self.alpha, self.beta, self.wt = crystallites(n)
+
+
+class Pake(object):
+ parameter = {'delta': 0., 'eta': 0., 'w_iso': 0.,
+ 'x': None, 'y': None, 'half': False}
+
+ def __init__(self, samples=10000):
+ self.powder = Powder(n=samples)
+ self.dw = None
+ self.parameter = {}
+ self.GB = 1000
+ self.pw = 1e-6
+ self.legendre = 0
+ self.axial = 0
+ self.calc_prefactor()
+
+ def calc_prefactor(self):
+ """
+ Calculates angular dependencies
+ Returns
+ -------
+
+ """
+ self.legendre = 3 * np.square(np.cos(self.powder.beta)) - 1
+ self.axial = np.square(np.sin(self.powder.beta)) * np.cos(2 * self.powder.alpha)
+
+ def set_parameter(self, p, dw=None):
+ self.parameter['delta'] = p[0] * 1e3
+ self.parameter['eta'] = p[1]
+ self.GB = p[2] * 1e3
+ self.pw = p[3] * 1e-6
+ if len(p) == 5:
+ self.parameter['w_iso'] = p[4] * 1e3
+ if dw is not None:
+ self.dw = dw
+
+ @staticmethod
+ def _check_old_data(x, d, e, w, h):
+ if Pake.parameter['x'] is None:
+ return False
+ if Pake.parameter['w_iso'] == w and Pake.parameter['delta'] == d and \
+ Pake.parameter['eta'] == e and Pake.parameter['half'] == h and \
+ Pake.parameter['x'][0] == x[0] and Pake.parameter['x'][1] == x[1]:
+ return True
+ return False
+
+ def calc_timesignal(self, tt, p, half=False):
+ """
+ Calculate time signal of pake spectrum
+ Parameters
+ ----------
+ tt: time array
+ p: list of parameters [delta, eta, GB, omega_iso]
+ half: if False, Pake spectrum will be calculated, if True, chemical shift spectrum will be calculated
+
+ Returns
+ -------
+ timesignal
+ """
+ _delta, _eta, _wiso = p
+ omega = 0.5 * _delta * (self.legendre - _eta * self.axial)
+ if half:
+ omega += _wiso
+ weight = self.powder.wt
+ if half:
+ fid_real = np.zeros_like(tt, dtype=float)
+ fid_imag = np.zeros_like(tt, dtype=float)
+ for k, v in zip(omega, weight):
+ pha = 2 * np.pi * k * tt
+ fid_real += (np.cos(pha)) * v
+ fid_imag += (np.sin(pha)) * v
+ fid = 1j * fid_imag
+ fid += fid_real
+ else:
+ fid = np.zeros_like(tt, dtype=float)
+ for k, v in zip(omega, weight):
+ fid += np.cos(2 * np.pi * k * tt) * v
+ return fid
+
+ @staticmethod
+ def calc_pulse(pw, f):
+ _phi = 2 * np.pi * f * pw
+ pulse = ((0.5 * np.pi * np.sin(np.sqrt(0.25 * np.pi ** 2 + 0.5 * _phi ** 2))) /
+ np.sqrt(0.25 * np.pi ** 2 + 0.25 * _phi ** 2)) ** 1
+
+ return pulse
+
+ def pake_spectrum(self, x, p=None, half=False):
+ if p is None:
+ _gb = self.GB
+ _pw = self.pw
+ try:
+ _delta = self.parameter['delta']
+ _eta = self.parameter['eta']
+ _wiso = self.parameter['w_iso']
+ except KeyError:
+ _delta = Pake.parameter['delta']
+ _eta = Pake.parameter['eta']
+ _wiso = Pake.parameter['w_iso']
+ else:
+ _delta = p[0] * 1e3
+ _eta = p[1]
+ _gb = p[2] * 1e3
+ _pw = p[3] * 1e-6
+ _wiso = p[4] * 1e3
+
+ # x is time
+ if abs(x[1] - x[0]) < 1:
+ if self.dw is None:
+ self.dw = x[1] - x[0]
+ t = x
+ nop = len(x)
+ isfreq = False
+ # x is frequency
+ else:
+ df = (x[1] - x[0])
+ if self.dw is None:
+ self.dw = 1 / (df * len(x))
+ if len(x) != 1 / (self.dw * df):
+ nop = int(1 / (self.dw * df))
+ else:
+ nop = len(x)
+
+ # avoid calculation for to much points
+ t = np.arange(0, min(8192, nop)) * self.dw
+ isfreq = True
+
+ new_data = self._check_old_data((nop, self.dw), _delta, _eta, _wiso, half)
+
+ if new_data:
+ fid = Pake.parameter['y'] + 0
+ Pake.parameter['delta'] = _delta
+ Pake.parameter['eta'] = _eta
+ Pake.parameter['w_iso'] = _wiso
+ Pake.parameter['x'] = (nop, self.dw)
+ Pake.parameter['half'] = half
+ self.GB = _gb
+ self.pw = _pw
+ fid = self.calc_timesignal(t, [_delta, _eta, _wiso], half=half)
+ Pake.parameter['y'] = fid + 0
+
+
+ fid *= np.exp(-(_gb * t) ** 2)
+
+ # add zeros to get desired signal length
+ if len(fid) != nop:
+ fid = np.append(fid, np.zeros((nop - len(fid))), axis=0)
+
+ spec = np.fft.fftshift(np.fft.fft(fid))
+ spec -= np.mean(spec[:10])
+ _f = np.fft.fftshift(np.fft.fftfreq(nop, d=self.dw))
+
+ # take finite pulse length into account
+ if _pw != 0:
+ pulse = self.calc_pulse(_pw, _f)
+ spec *= pulse
+
+ # shift pake spectrum by isotropic shift
+ if not half:
+ spec = np.roll(spec, int(self.dw * _wiso * nop))
+
+ # cut to original frequency range
+ if isfreq:
+ # mask = np.ma.masked_inside(_f, min(x), max(x)).mask
+ # spec = spec[mask]
+ spec = np.interp(x, _f, spec.real)
+
+ spec.real -= np.mean(spec.real[:10])
+
+ return spec.real / max(spec.real)
+
+ def pake(self, x, p=None):
+ return self.pake_spectrum(x, p=p)
+
+ def halfpake(self, x, p=None):
+ return self.pake_spectrum(x, p=p, half=True)
+
+
+class SecondOrder(object):
+ parameter = {'c_q': 1e6, 'eta': 0.,
+ 'x': None, 'y': None}
+
+ def __init__(self, samples=200000):
+ self.powder = Powder(n=samples)
+ self.dw = None
+ self.parameter = {}
+ self.GB = 1000
+ self.pw = 1e-6
+ self.legendre = 0
+ self.axial = 0
+ self.coupling = 1
+ self.larmor = 2 * np.pi * 100e6
+
+ def set_parameter(self, p, dw=None, larmor=None, spin=2.5):
+ self.parameter['c_q'] = 2 * np.pi * p[0] * 1e6
+ self.parameter['eta'] = p[1]
+ self.GB = p[2] * 1e3
+ if dw is not None:
+ self.dw = dw
+ if larmor is not None:
+ self.larmor = 2 * np.pi * larmor
+ self._calc_coupling(2 * np.pi * p[0] * 1e6, spin=spin)
+
+ def _calc_coupling(self, c_q, spin=2.5):
+ omega_q = c_q / (2 * spin * (2 * spin - 1))
+ self.coupling = 1.5 * omega_q ** 2 / self.larmor * (spin * (spin + 1) - 0.75)
+
+ def calc_omega(self, eta, delta):
+ self._calc_coupling(delta)
+ cos2phi = np.cos(2 * self.powder.alpha)
+ cos_theta_square = 0.5 + 0.5 * np.cos(2 * self.powder.beta)
+ prefactor_a = -3.375 + 2.25 * eta * cos2phi - 0.375 * (eta * cos2phi) ** 2
+ prefactor_b = 3.75 - 0.5 * eta ** 2 - 2 * eta * cos2phi + 0.75 * (eta * cos2phi) ** 2
+ prefactor_c = -0.375 + (eta ** 2) / 3. - 0.25 * eta * cos2phi - 0.375 * (eta * cos2phi) ** 2
+ ret_val = np.zeros_like(cos2phi)
+ ret_val += prefactor_a * cos_theta_square ** 2
+ ret_val += prefactor_b * cos_theta_square
+ ret_val += prefactor_c
+ return self.coupling * ret_val
+
+ def calc_timesignal(self, tt, p):
+ delta, eta = p
+ omega = self.calc_omega(eta, delta)
+ weight = self.powder.wt
+ fid_real = np.zeros_like(tt, dtype=float)
+ fid_imag = np.zeros_like(tt, dtype=float)
+
+ for o, w in zip(omega, weight):
+ pha = o * tt
+ co_pha = np.cos(pha)
+ si_pha = np.sin(pha)
+ fid_real += co_pha * w
+ fid_imag += si_pha * w
+
+ fid = 1j * fid_imag
+ fid += fid_real
+ return fid
+
+ def spectrum(self, x, p=None):
+ if p is None:
+ p = [self.parameter['c_q'], self.parameter['eta'], self.GB]
+ if abs(x[1] - x[0]) < 1:
+ if self.dw is None:
+ self.dw = x[1] - x[0]
+ t = x
+ nop = len(x)
+ isfreq = False
+ else:
+ df = (x[1] - x[0])
+ if self.dw is None:
+ self.dw = 1 / (df * len(x))
+ if len(x) != 1 / (self.dw * df):
+ nop = int(1 / (self.dw * df))
+ else:
+ nop = len(x)
+ t = np.arange(0, min(8192, nop)) * self.dw
+ isfreq = True
+
+ fid = self.calc_timesignal(t, p[:-1])
+ fid *= np.exp(-(p[-1] * t) ** 2)
+
+ if len(fid) != nop:
+ fid = np.append(fid, np.zeros((nop - len(fid))), axis=0)
+
+ spec = np.fft.fftshift(np.fft.fft(fid))
+ _f = np.fft.fftshift(np.fft.fftfreq(nop, d=self.dw))
+
+ if isfreq:
+ mask = np.ma.masked_inside(_f, min(x), max(x)).mask
+ spec = spec[mask]
+
+ return spec.real - np.mean(spec.real[:10])
+
+
+def csa(x: np.ndarray, delta: float, eta: float, wiso: float, gb: float, sw: float):
+ dw = 1 / sw
+ a, b, _ = crystallites(200000)
+ bins = 0.5 * (x[1:] + x[:-1])
+ bins = np.hstack((np.array([0.5 * (-x[1] + 3 * x[0])]), bins, np.array([0.5 * (3 * x[-1] - x[-2])])))
+
+ omega = wiso + delta * 0.5 * (3 * np.cos(b) ** 2 - 1 - eta * np.square(np.sin(b)) * np.cos(2 * a))
+
+ s_left = np.histogram(omega, bins=bins)[0]
+ s = s_left
+
+ if gb != 0:
+ apd = np.exp(-4 * np.log(2) * (np.fft.fftshift(np.fft.fftfreq(len(x), d=dw)) / sw) ** 2) * \
+ 2 * np.sqrt(np.log(2) / np.pi) / gb
+ ret_val = np.convolve(s, apd, mode='same')
+ else:
+ ret_val = s
+
+ return ret_val / trapz(ret_val, x)
+
+
+def pake(p, x):
+ dw = 1/p[-1]
+ a, b, _ = crystallites(200000)
+ bins = 0.5 * (x[1:] + x[:-1])
+ bins = np.hstack((np.array([0.5 * (-x[1] + 3 * x[0])]), bins, np.array([0.5 * (3 * x[-1] - x[-2])])))
+
+ omega = p[0] * 0.5 * (3*np.cos(b)**2 - 1 - p[1] * np.square(np.sin(b)) * np.cos(2 * a))
+
+ s_left = np.histogram(omega, bins=bins)[0]
+ s_right = np.histogram(-omega, bins=bins)[0]
+ s = s_left + s_right
+
+ if p[2] != 0:
+ apd = np.exp(-4 * np.log(2) * (np.fft.fftshift(np.fft.fftfreq(len(x), d=dw)) / p[2]) ** 2) * \
+ 2 * np.sqrt(np.log(2) / np.pi) / p[2]
+ ret_val = np.convolve(s, apd, mode='same')
+ else:
+ ret_val = s
+
+ return ret_val/trapz(ret_val, x)
+
+
+def sec_order(p, x, spin=2.5):
+ dw = 1/p[-1]
+ a, b, _ = crystallites(200000)
+ bins = 0.5 * (x[1:] + x[:-1])
+ bins = np.hstack((np.array([0.5 * (-x[1] + 3 * x[0])]), bins, np.array([0.5 * (3 * x[-1] - x[-2])])))
+
+ omega_q = 2 * np.pi * p[0] / (2*spin * (2*spin - 1))
+ coupling = 1.5 * (omega_q**2 / (2 * np.pi *p[3])) * (spin*(spin + 1) - 0.75)
+
+ cos2phi = np.cos(2 * a)
+ cos_theta_square = 0.5+0.5*np.cos(2*b)
+ prefactor_a = -3.375 + 2.25*p[1] * cos2phi - 0.375 * (p[1] * cos2phi)**2
+ prefactor_b = 3.75 - 0.5 * p[1]**2 - 2*p[1]*cos2phi + 0.75 * (p[1]*cos2phi)**2
+ prefactor_c = -0.375 + (p[1]**2)/3. - 0.25*p[1]*cos2phi - 0.375 * (p[1]*cos2phi)**2
+ orient = np.zeros_like(cos2phi)
+ orient += prefactor_a * cos_theta_square ** 2
+ orient += prefactor_b * cos_theta_square
+ orient += prefactor_c
+
+ omega = coupling * orient
+
+ s = np.histogram(omega/(2*np.pi), bins=bins)[0]
+
+ if p[2] != 0:
+ apd = np.exp(-4 * np.log(2) * (np.fft.fftshift(np.fft.fftfreq(len(x), d=dw)) / p[2]) ** 2) * \
+ 2 * np.sqrt(np.log(2) / np.pi) / p[2]
+ ret_val = np.convolve(s, apd, mode='same')
+ else:
+ ret_val = s
+
+ return ret_val / trapz(ret_val, x)
+
+
+if __name__ == '__main__':
+ # pass
+ import matplotlib.pyplot as plt
+
+ fig, ax = plt.subplots()
+
+ dw0 = 4e-6
+ xxx = np.arange(0, 8192) * dw0
+
+ freq = np.fft.fftshift(np.fft.fftfreq(len(xxx), d=dw0))[500:-1000]
+
+ p0 = [6.66e6, 0.935, 2e3, 50e6, 1/dw0]
+ spec = sec_order(p0, freq)
+ ax.plot(freq, spec)
+
+ p1 = [20e3, 0.2, 2e3, 1/dw0]
+ spec1 = pake(p1, freq)
+ ax.plot(freq, spec1)
+
+ p2 = [20e3, 0.2, 20e3, 2e3, 1/dw0]
+ spec2 = csa(p2, freq)
+ ax.plot(freq, spec2)
+
+ plt.show()
diff --git a/nmreval/models/__init__.py b/nmreval/models/__init__.py
new file mode 100755
index 0000000..f5c368e
--- /dev/null
+++ b/nmreval/models/__init__.py
@@ -0,0 +1,11 @@
+
+from .basic import *
+from .relaxation import *
+from .diffusion import *
+from .fieldcycling import *
+from .stimecho import *
+from .bds import *
+from .temperature import *
+from .transitions import *
+from .correlationfuncs import *
+from .wideline import *
diff --git a/nmreval/models/basic.py b/nmreval/models/basic.py
new file mode 100644
index 0000000..64cdc5f
--- /dev/null
+++ b/nmreval/models/basic.py
@@ -0,0 +1,250 @@
+import numpy as np
+
+from ..math.mittagleffler import mlf
+
+
+class Constant:
+ """
+ Constant
+
+ .. math::
+ y = c\cdot x
+
+ Args:
+ x (array_like): Input values
+ c (float): constant
+ """
+
+ type = 'Basic'
+ name = 'Constant'
+ equation = 'C'
+ params = ['C']
+
+ @staticmethod
+ def func(x, c: float):
+ return c*np.ones(len(x))
+
+
+class Linear:
+ """
+ Straight line.
+
+ .. math::
+ y = m\cdot x + t
+
+ Args:
+ x (array_like): Input values
+ m (float): Slope
+ t (float): Intercept
+
+ """
+ type = 'Basic'
+ name = 'Straight line'
+ equation = 'm*x + t'
+ params = ['m', 't']
+
+ @staticmethod
+ def func(x, m: float, t: float):
+ return m*x + t
+
+
+class PowerLaw:
+ """
+ Power law
+
+ .. math::
+ y = A\cdot x^b
+
+ Args:
+ x (array_like): Input values
+ A (float): Prefactor
+ b (float): Exponent
+
+ """
+
+ type = 'Basic'
+ name = 'Power law'
+ equation = 'A*x^{b}'
+ params = ['A', 'b']
+
+ @staticmethod
+ def func(x, a: float, b: float):
+ return a * x**b
+
+
+class Log:
+ """
+ Logarithm
+
+ .. math::
+ y = C\cdot \ln(x-x_0)
+
+ Args:
+ x (array_like): Input values
+ C (float): Prefactor
+ x0 (float): Offset
+
+ """
+ type = 'Basic'
+ name = 'Logarithm'
+ equation = 'C*ln(x-x_{0})'
+ params = ['C', 'x_{0}']
+
+ @staticmethod
+ def func(x, c, x0):
+ return c * np.log(x-x0)
+
+
+class Parabola:
+ """
+ Parabola with vertex :math:`(x_0, y_0)`
+
+ .. math::
+ y = C\cdot (x-x_{0}) + y_0
+
+ Args:
+ x (array_like): Input values
+ c (float): Slope
+ x0 (float): x position of vertex
+ y0 (float): y position of vertex
+
+ """
+ type = 'Basic'
+ name = 'Parabola'
+ equation = 'C*(x-x_{0})^{2} + y_{0}'
+ params = ['C', 'x_{0}', 'y_{0}']
+
+ @staticmethod
+ def func(x, c, x0, y0):
+ return c * (x-x0)**2 + y0
+
+
+class PowerLawCross:
+ """
+ Crossover between to power laws at position :math:`x_0`
+
+ .. math::
+ y \\propto
+ \\begin{cases}
+ x^{b_1}, & x \le x_0 \\\\
+ x^{b_2}, & x > x_0
+ \\end{cases}
+
+ Args:
+ x (array_like): Input values
+ c (float): Prefactor
+ b1 (float): Exponent of first power law
+ b2 (float): Exponent of second power law
+ x0 (float): x position of crossover
+
+ """
+ type = 'Basic'
+ name = 'Crossing Power Laws'
+ params = ['C', 'b_{1}', 'b_{2}', 'x_{0}']
+
+ @staticmethod
+ def func(x, c, b1, b2, x0):
+ mas = np.nonzero(x > x0)
+ ret_val = c * x**b1
+ c2 = c * x0**(b1-b2)
+ ret_val[mas] = c2 * x[mas]**b2
+ return ret_val
+
+
+class Sine:
+ """
+ Sine function
+
+ .. math::
+ y = C\sin(a x - \phi)
+
+ Args:
+ x (array_like): Input values
+ c (float): Prefactor
+ a (float): frequency
+ phi (float): shift
+
+ """
+
+ type = 'Basic'
+ name = 'Sine'
+ equation = r'C*sin(a*x-\phi)'
+ params = ['C', 'a', r'\phi']
+
+ @staticmethod
+ def func(x, c: float, a: float, phi: float):
+ return c*np.sin(a*x-phi)
+
+
+class ExpFunc:
+ """
+ Stretched exponential function
+
+ .. math::
+ y = C\exp[\pm (x\cdot x_0)^\\beta] \\text{ or } C\exp[\pm (x/x_0)^\\beta]
+
+ Args:
+ x (array_like): Input values
+ C (float): Prefactor
+ x0 (float): Decay/growth constant
+ beta (float): Stretching parameter
+
+ Keyword Args:
+ pm (int): Sign of the number determines if growing or decaying function.
+ Positive values result in growing exponentials, negative in decaying functions.
+ Default: -1
+ mode (str): Interpretation of x0 as either rate (:math:`x\cdot x_0`) or time (:math:`x/x_0`).
+ Possible values are *time* or *rate*, default is *time*.
+ """
+ type = 'Basic'
+ name = 'Exponential Function'
+ equation = r'C*exp[\pm(x_{0}x)^{\beta}] or C*exp[\pm(x/x_{0})^{\beta}]'
+ params = ['C', r'x_{0}', r'\beta']
+ choices = [('Sign', 'pm', {'decaying': -1, 'growing': 1}),
+ ('x0 type', 'mode', {'Time (x/x0)': 'time', 'Rate (x*x0)': 'rate'})]
+
+ @staticmethod
+ def func(x, c, x0, beta, pm: int = -1, mode: str = 'time'):
+ if mode == 'time':
+ return c * np.exp(np.sign(pm) * (x0 * x) ** beta)
+ elif mode == 'rate':
+ return c * np.exp(np.sign(pm) * (x / x0) ** beta)
+ else:
+ raise ValueError('Unknown mode %s. Use either "rate" or "time".' % str(mode))
+
+
+class MittagLeffler:
+ """
+ Mittag-Leffler function
+
+ .. math::
+ y = C\cdot E_\\alpha[-(x/x_0)^\\alpha] \\text{ or } C\cdot E_\\alpha[-(x\cdot x_0)^\\alpha]
+
+ where
+
+ .. math::
+ E_a(z)= \sum_{k=0}^\infty \\frac{z^k}{\Gamma(\\alpha k + 1)}
+
+ Args:
+ x (array_like): Input values
+ C (float): Prefactor
+ x0 (float): Decay constant
+ alpha (float): Stretching parameter
+
+ Keyword Args:
+ mode (str): Interpretation of x0 as either rate (:math:`x\cdot x_0`) or time (:math:`x/x_0`).
+ Possible values are *time* or *rate*, default is *time*.
+ """
+ type = 'Basic'
+ name = 'Mittag-Leffler'
+ equation = r'C*E_{\alpha}(-(x/x_{0}), \alpha)'
+ params = ['C', 'x_{0}', r'\alpha']
+
+ @staticmethod
+ def func(x, c, x0, alpha, mode: str = 'time'):
+ if mode == 'time':
+ return c*mlf(-(x/x0), alpha)
+ elif mode == 'rate':
+ return c*mlf(-(x*x0), alpha)
+ else:
+ raise ValueError('Unknown mode {mode}. Use either "rate" or "time".')
diff --git a/nmreval/models/bds.py b/nmreval/models/bds.py
new file mode 100644
index 0000000..0aa2359
--- /dev/null
+++ b/nmreval/models/bds.py
@@ -0,0 +1,155 @@
+from typing import List, Optional, Tuple
+
+import numpy as np
+
+from ..distributions import Debye, ColeCole, ColeDavidson, KWW, HavriliakNegami
+from ..utils.constants import epsilon0
+
+
+class _AbstractBDS:
+ name = 'Abstract'
+ type = 'Dielectric Spectroscopy'
+ equation = ''
+ params = [r'\Delta\epsilon', r'\tau_{0}']
+ bounds = [(0, None), (0, None)]
+ susceptibility = None
+ iscomplex = True
+
+ @classmethod
+ def func(cls, x, *args, **kwargs):
+ # args[0] : Delta epsilon
+ # args[1:] : every other parameter
+ chi = args[0] * cls.susceptibility(2*np.pi*x, *args[1:])
+
+ return chi
+
+
+class DebyeBDS(_AbstractBDS):
+ name = 'Debye'
+ equation = r'\Delta\epsilon / (1-i\omega\tau)'
+ susceptibility = Debye.susceptibility
+
+
+class ColeColeBDS(_AbstractBDS):
+ name = 'Cole-Cole'
+ equation = r'\Delta\epsilon / (1-i\omega\tau)^{\alpha}'
+ params = _AbstractBDS.params + [r'\alpha']
+ bounds = _AbstractBDS.bounds + [(0, 1)]
+ susceptibility = ColeCole.susceptibility
+
+
+class ColeDavidsonBDS(_AbstractBDS):
+ name = 'Cole-Davidson'
+ equation = r'\Delta\epsilon / [1-(i\omega\tau)^{\gamma})'
+ params = _AbstractBDS.params + [r'\gamma']
+ bounds = _AbstractBDS.bounds + [(0, 1)]
+ susceptibility = ColeDavidson.susceptibility
+
+
+class HavriliakNegamiBDS(_AbstractBDS):
+ name = 'Havriliak-Negami'
+ equation = r'\Delta\epsilon / [1-(i\omega\tau)^{\gamma}]^{\alpha}'
+ params = _AbstractBDS.params + [r'\alpha', r'\gamma']
+ bounds = _AbstractBDS.bounds + [(0, 1), (0, 1)]
+ susceptibility = HavriliakNegami.susceptibility
+
+
+class KWWBDS(_AbstractBDS):
+ name = 'KWW'
+ params = _AbstractBDS.params + [r'\beta']
+ bounds = _AbstractBDS.bounds + [(0, 1)]
+ susceptibility = KWW.susceptibility
+
+
+class EpsInfty:
+ name = 'Epsilon Infinity'
+ type = 'Dielectric Spectroscopy'
+ equation = r'\epsilon_{\infty}'
+ params = [r'\epsilon_{\infty}']
+ bounds = [(None, None)]
+ iscomplex = True
+
+ @staticmethod
+ def func(x, eps):
+ ret_val = np.zeros(x.shape, dtype=complex)
+ ret_val += eps
+
+ return ret_val
+
+
+class PowerLawBDS:
+ name = 'Power Law'
+ type = 'Dielectric Spectroscopy'
+ equation = r'A / (i\omega)^{n}'
+ params = ['A', 'n']
+ bounds = [(None, None), (None, None)]
+ iscomplex = True
+
+ @staticmethod
+ def func(x, a, n):
+ return a / (1j*x)**n
+
+
+class DCCondBDS:
+ name = 'DC cond.'
+ type = 'Dielectric Spectroscopy'
+ equation = r'i\sigma_{dc}/\epsilon_{0}\omega'
+ params = [r'\sigma_{dc}']
+ bounds = [(0, None)]
+ iscomplex = True
+
+ @staticmethod
+ def func(x, sigma):
+ ret_val = np.zeros(x.shape, dtype=complex)
+ ret_val += 1j * sigma / x / epsilon0
+
+ return ret_val
+
+
+class HavriliakNegamiDerivative:
+ name = 'Derivative HN'
+ type = 'Dielectric Spectroscopy'
+ params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\gamma']
+ bounds = [(0, None), (0, None), (0, 1), (0, 1)]
+
+ @staticmethod
+ def func(x, eps, tau, a, g):
+ omtau = 2*np.pi*x * tau
+ theta = np.arctan2(np.sin(np.pi*a/2.), omtau**(-a) + np.cos(np.pi*a/2.))
+
+ numer = a*g * omtau**a * np.cos(np.pi*a/2. - (1+g)*theta)
+ denom = (1 + 2*omtau**a * np.cos(np.pi*a/2.) + omtau**(2*a))**((1+g)/2.)
+
+ return eps * np.pi * numer / denom / 2.
+
+
+class ColeColeDerivative:
+ name = 'Derivative CC'
+ type = 'Dielectric Spectroscopy'
+ params = [r'\Delta\epsilon', r'\tau', r'\alpha']
+ bounds = [(0, None), (0, None), (0, 1)]
+
+ @staticmethod
+ def func(x, eps, tau, alpha):
+ omtau = 2*np.pi*x * tau
+ theta = np.arctan2(np.sin(np.pi*alpha/2.), omtau**(-alpha) + np.cos(np.pi*alpha/2.))
+
+ numer = alpha * omtau**alpha * np.cos(np.pi*alpha/2. - 2*theta)
+ denom = 1 + 2*omtau**alpha * np.cos(np.pi*alpha/2.) + omtau**(2*alpha)
+
+ return eps * np.pi * numer / denom / 2.
+
+
+class ColeDavidsonDerivative:
+ name = 'Derivative CD'
+ type = 'Dielectric Spectroscopy'
+ params = [r'\Delta\epsilon', r'\tau', r'\gamma']
+ bounds = [(0, None), (0, None), (0, 1)]
+
+ @staticmethod
+ def func(x, eps, tau, g):
+ omtau = 2*np.pi*x * tau
+ numer = g * omtau * np.sin((1+g)*np.sin(omtau))
+ denom = (1 + omtau**2)**((1+g)/2.)
+
+ return eps * np.pi * numer / denom / 2.
diff --git a/nmreval/models/correlationfuncs.py b/nmreval/models/correlationfuncs.py
new file mode 100644
index 0000000..575e8b2
--- /dev/null
+++ b/nmreval/models/correlationfuncs.py
@@ -0,0 +1,51 @@
+import numpy as np
+from scipy.special import gammaincc
+
+from ..math.mittagleffler import mlf
+
+
+class Exponential:
+ type = 'Correlation function'
+ name = 'Stretched Exponential'
+ equation = r'C*exp(-(x/\tau)^{\beta})'
+ params = ['C', r'\tau', r'\beta']
+
+ @staticmethod
+ def func(x, c, x0, beta):
+ return c*np.exp(-(x/x0)**beta)
+
+
+class MittagLefflerCC:
+ type = 'Correlation function'
+ name = 'Cole-Cole'
+ equation = r'C * E_{\alpha}(-(x/\tau)^{\alpha})'
+ params = ['C', r'\tau', r'\alpha']
+ bounds = [(None, None), (0, None), (0, 1)]
+
+ @staticmethod
+ def func(x, c, tau, alpha):
+ return c * mlf(-(x/tau)**alpha, alpha)
+
+
+class GammaCD:
+ type = 'Correlation function'
+ name = 'Cole-Davidson'
+ equation = r'C * \Gamma(\gamma, x/\tau) / \Gamma(\gamma)'
+ params = ['C', r'\tau', r'\gamma']
+ bounds = [(None, None), (0, None), (0, 1)]
+
+ @staticmethod
+ def func(x, c, tau, gamma):
+ return c * gammaincc(gamma, x/tau)
+
+
+class MLHavNeg:
+ type = 'Correlation function'
+ name = 'Havriliak-Negami'
+ equation = r'C * E_{\alpha,\alpha\gamma+1}^{\gamma}(-(x/\tau)^{\alpha})'
+ params = ['C', r'\tau', r'\gamma']
+ bounds = [(None, None), (0, None), (0, 1)]
+
+ @staticmethod
+ def func(x, c, tau, alpha, gamma):
+ return c * (1 - (x/tau)**(alpha*gamma) * mlf(-(x/tau)**alpha, alpha, alpha*gamma+1, gamma))
diff --git a/nmreval/models/diffusion.py b/nmreval/models/diffusion.py
new file mode 100644
index 0000000..359fdd1
--- /dev/null
+++ b/nmreval/models/diffusion.py
@@ -0,0 +1,155 @@
+import numpy as np
+from scipy import special as special
+
+from ..utils import gamma
+
+
+class Diffusion:
+ type = 'Diffusion'
+ name = 'Free diffusion'
+ equation = r'M_{0}[exp[- \gamma^{2} g^{2} t_{ev}^{2} D(2/3t_{ev}+t_{mix})] * Relax.'
+ params = ['M_{0}', 'D', 'T_{Rel.}', r'\beta_{Rel.}', 'g', 't_{ev}/t_{mix}']
+ choices = [(r'\gamma', 'nucleus', gamma), ('Varied time', 't_axis', {'t_mix': 'tm', 't_ev': 'te'})]
+ bounds = [(0, None), (0, None), (0, None), (0, 1), (0, None), (0, None)]
+
+ @staticmethod
+ def func(x, m0, d, trel, brel, g, t2, nucleus=2.67522128e8, t_axis='tm'):
+ if t_axis == 'tm':
+ tm = x
+ tp = t2
+ red = np.exp(-(tm/trel)**brel)
+ else:
+ tm = t2
+ tp = x
+ # T2 decay happens twice
+ red = np.exp(-(tp/trel)**brel) * np.exp(-(tp/trel)**brel)
+ q2 = (g * nucleus * tp)**2
+ t_eff = (2*tp)/3 + tm
+
+ return m0 * np.exp(-q2 * d * t_eff) * red
+
+
+class Diffusion1D:
+ type = 'Diffusion'
+ name = '1D Diffusion'
+ equation = r'M_{0} erf(z) / z * Relax\n' \
+ r'with z = sqrt([g \gamma t_{ev}]^{2} D [2/3t_{ev} + t_{mix}])'
+ params = ['M_{0}', 'D', 'T_{Rel.}', r'\beta_{Rel.}', 'g', 't_{ev}/t_{mix}']
+ choices = [(r'\gamma', 'nucleus', gamma), ('Varied time', 't_axis', {'t_mix': 'tm', 't_ev': 'te'})]
+ bounds = [(0, None), (0, None), (0, None), (0, 1), (0, None), (0, None)]
+
+ @staticmethod
+ def func(x, m0, d, trel, brel, g, t2, nucleus=2.67522128e8, t_axis='tm'):
+ if t_axis == 'tm':
+ tm = x
+ tp = t2
+ relax = np.exp(-(tm/trel)**brel)
+ else:
+ tm = t2
+ tp = x
+ relax = np.exp(-(tp/trel)**brel) * np.exp(-(tp/t2)**brel)
+ # Callaghan eq (6.91)
+ z = (tp * nucleus * g * np.sqrt(d * (tm + (2 / 3)*tp)))
+ diffs = special.erf(z) / z
+
+ return m0 * diffs * relax
+
+
+class Diffusion2D(object):
+ type = 'Diffusion'
+ name = '2D Diffusion'
+ equation = r'M_{0} exp(-z) * erfi(z)/z * Relax\n' \
+ r'with z = sqrt([g \gamma t_{ev}]^{2} D [t_{mix} + 2/3t_{ev}])'
+ params = ['M_{0}', 'D', 'T_{Rel.}', r'\beta_{Rel.}', 'g', 't_{ev}/t_{mix}']
+ bounds = [(0, None), (0, None), (0, None), (0, 1), (0, None), (0, None)]
+ choices = [(r'\gamma', 'nucleus', gamma), ('Varied time', 't_axis', {'t_mix': 'tm', 't_ev': 'te'})]
+
+ @staticmethod
+ def func(x, m0, d, trel, brel, g, t2, nucleus=2.67522128e8, t_axis='tm'):
+ if t_axis == 'tm':
+ tm = x
+ tp = t2
+ relax = np.exp(-(tm/trel)**brel)
+ else:
+ tm = t2
+ tp = x
+ relax = np.exp(-(tp/trel)**brel) * np.exp(-(tp/trel)**brel)
+ q_squared = (g*nucleus*tp)**2
+ a = q_squared * (2*tp/3 + tm) * d
+
+ # Callaghan eq (6.90)
+ diffs = np.exp(-a) * special.erfi(np.sqrt(a)) / np.sqrt(a)
+
+ return m0*diffs*relax
+
+
+class AnisotropicDiffusion(object):
+ type = 'Diffusion'
+ name = 'Anisotropic Diffusion'
+ equation = r'M_{0} exp[-(g \gamma t_{p})^{2} D_{\perp}] * erf(z) / z * Relax\n' \
+ r'with z = sqrt([g \gamma t_{ev}]^{2} [D_{\para} - D_{\perp}] [2/3t_{ev} + t_{mix}])'
+ params = ['M_{0}', r'D_{\perp}', r'D_{\para}', r'T_{Rel.}', r'\beta_{Rel.}', 'g', 't_{ev}/t_{mix}']
+ choices = [(r'\gamma', 'nucleus', gamma), ('Varied time', 't_axis', {'t_mix': 'tm', 't_ev': 'te'})]
+ bounds = [(0, None), (0, None), (0, None), (0, None), (0, 1), (0, None), (0, None)]
+
+ @staticmethod
+ def func(x, m0, d_perp, d_par, trel, brel, g, t2, nucleus=2.67522128e8, t_axis='tm'):
+ if t_axis == 'tm':
+ tm = x
+ tp = t2
+ relax = np.exp(-(tm/trel)**brel)
+ else:
+ tm = t2
+ tp = x
+ relax = np.exp(-(tp/trel)**brel)*np.exp(-(tp/trel)**brel)
+
+ q_squared = np.power(g * nucleus * tp, 2)
+ t = 2 * tp / 3 + tm
+ z = np.sqrt(q_squared * (d_par - d_perp) * t)
+ # Callaghan eq (6.89)
+ diffs = np.exp(-q_squared*t*d_perp) * special.erf(z) / z
+
+ return m0 * diffs * relax
+
+
+class Peschier:
+ name = 'Diffusion + Cross-Relaxation'
+ type = 'Diffusion'
+ equation = r'Diffusion with cross-relax f(ast) \rightarrow s(low)'
+ params = ['M_{0}', 'D', 'T_{1,f}', 'T_{1,s}', 'k_{f}', 'k_{s}', 't_{ev}', 'g']
+ bounds = [(0, None), (0, None), (0, None), (0, None), (0, None), (0, None)]
+ choices = [(r'\gamma', 'nucleus', gamma)]
+
+ @staticmethod
+ def func(x, m0, d, t1f, t1s, kf, ks, tp, g, nucleus=2.67522128e8):
+ q = nucleus*g*tp
+
+ r1s, r1f = 1 / t1s, 1 / t1f
+ a_plus = 0.5 * (d*q*q + kf + ks + r1f + r1s + np.sqrt((d*q*q + kf + r1f - ks - r1s)**2 + 4*kf*ks))
+ a_minu = 0.5 * (d*q*q + kf + ks + r1f + r1s - np.sqrt((d*q*q + kf + r1f - ks - r1s)**2 + 4*kf*ks))
+
+ return m0 * np.exp(-(2/3)*tp*d*q**2)*((a_plus - ks - r1s) / (a_plus - a_minu) * np.exp(-a_plus * x) -
+ (a_minu - ks - r1s) / (a_plus - a_minu) * np.exp(-a_minu * x))
+
+
+class DiffusionGradients:
+ name = 'Divided Gradients'
+ type = 'Diffusion'
+ equation = r'M_{0}exp[-\gamma^{2} (g_{1}^{2} - g_{2}^{2}) t_{ev}^{2} D(2/3t_{ev}+t_{mix})]'
+ params = ['M_{0}', 'D', 'g_{1}', 'g_{2}', 't_{ev}/t_{mix}']
+ choices = [(r'\gamma', 'nucleus', gamma), ('Varied time', 't_axis', {'t_mix': 'tm', 't_ev': 'te'})]
+ bounds = [(0, None), (0, None), (0, None), (0, None), (0, None)]
+
+ @staticmethod
+ def func(x, m0, d, g1, g2, t2, nucleus=2.67522128e8, t_axis='tm'):
+ if t_axis == 'tm':
+ tm = x
+ tp = t2
+ else:
+ tm = t2
+ tp = x
+ # T2 decay happens twice
+ q2 = (g1**2 - g2**2) * (nucleus * tp)**2
+ t_eff = (2 * tp) / 3 + tm
+
+ return m0 * np.exp(-q2 * d * t_eff)
diff --git a/nmreval/models/fieldcycling.py b/nmreval/models/fieldcycling.py
new file mode 100644
index 0000000..550ea01
--- /dev/null
+++ b/nmreval/models/fieldcycling.py
@@ -0,0 +1,124 @@
+import numpy as np
+
+from ..distributions import *
+from ..distributions.energy import EnergyBarriers
+from ..distributions.intermolecular import FFHS
+from ..nmr.relaxation import Relaxation
+from ..utils.constants import gamma
+
+
+class _AbstractFC:
+ name = 'Havriliak-Negami'
+ type = 'Field Cycling'
+ equation = ''
+ params = ['C', r'\tau']
+ bounds = [(0, None), (0, None)]
+ choices = [('x axis', 'xaxis', {'Frequency': 'freq', 'Omega': 'omega'}),
+ ('y axis', 'yaxis', {'omega / T_1': 'chi', '1/ T_1': 'rate', 'T_1': 'time'})]
+ relax = Relaxation()
+
+ @classmethod
+ def func(cls, x, c, *args, xaxis='freq', yaxis='chi'):
+ return cls._calc_relax(x, c, *args, xaxis=xaxis, yaxis=yaxis)
+
+ @classmethod
+ def _calc_relax(cls, x, c, *args, xaxis='freq', yaxis='chi', is_bpp=True, **kwargs):
+ _x = x
+ if xaxis == 'freq':
+ _x *= 2*np.pi
+
+ if is_bpp:
+ r1 = cls.relax.t1(_x, *args, prefactor=c, inverse=False)
+ else:
+ r1 = cls.relax.t1_dipolar(_x, *args, prefactor=c, inverse=False, **kwargs)
+
+ if yaxis == 'rate':
+ return r1
+ elif yaxis == 'chi':
+ return _x * r1
+ elif yaxis == 'time':
+ return 1. / r1
+ else:
+ raise ValueError(f'Unknown yaxis option {yaxis}, not `chi`, `rate`, `time`.')
+
+
+class ColeColeFC(_AbstractFC):
+ name = 'Cole-Cole'
+ params = _AbstractFC.params + [r'\alpha']
+ bounds = _AbstractFC.bounds + [(0, 1)]
+ relax = Relaxation(distribution=ColeCole)
+
+
+class ColeDavidsionFC(_AbstractFC):
+ name = 'Cole-Davidson'
+ params = _AbstractFC.params + [r'\gamma']
+ bounds = _AbstractFC.bounds + [(0, 1)]
+ relax = Relaxation(distribution=ColeDavidson)
+
+
+class HavriliakNegamiFC(_AbstractFC):
+ name = 'Havriliak-Negami'
+ params = _AbstractFC.params + [r'\alpha', r'\gamma']
+ bounds = _AbstractFC.bounds + [(0, 1), (0, 1)]
+ relax = Relaxation(distribution=HavriliakNegami)
+
+
+class KWWFC(_AbstractFC):
+ name = 'KWW'
+ params = _AbstractFC.params + [r'\beta']
+ bounds = _AbstractFC.bounds + [(0, 1)]
+ relax = Relaxation(distribution=KWW)
+
+
+class LogGaussianFC(_AbstractFC):
+ name = 'Log-Gaussian'
+ params = _AbstractFC.params + [r'\sigma']
+ bounds = _AbstractFC.bounds + [(0, None)]
+ relax = Relaxation(distribution=LogGaussian)
+
+
+class FFHSFC(_AbstractFC):
+ name = 'FFHS'
+ relax = Relaxation(distribution=FFHS)
+
+
+class EnergyFC(_AbstractFC):
+ name = 'Energy distribution'
+ params = ['C', 'T'] + EnergyBarriers.parameter
+ bounds = [(0, None), (0, None), (0, None), (0, None)]
+ ralax = Relaxation(distribution=EnergyBarriers)
+
+
+class _AbstractFCDipolar(_AbstractFC):
+ name = 'AbstractFC (het. dip.)'
+ choices = _AbstractFC.choices + [(r'\gamma (obs.)', 'gamma_obs', gamma), (r'\gamma (coup.)', 'gamma_coup', gamma)]
+
+ @classmethod
+ def func(cls, x, c, *args, gamma_obs=gamma['1H'], gamma_coup=gamma['2H'],
+ xaxis='freq', yaxis='chi'):
+ return cls._calc_relax(x, c, *args, gamma_obs=gamma_obs, gamma_coup=gamma_coup,
+ xaxis='freq', yaxis='chi', is_bpp=False)
+
+
+class HavriliakNegamiDipolar(_AbstractFCDipolar):
+ name = 'Havriliak-Negami (dipolar)'
+ params = _AbstractFCDipolar.params + [r'\alpha', r'\gamma']
+ bounds = _AbstractFCDipolar.bounds + [(0, 1), (0, 1)]
+ relax = Relaxation(distribution=HavriliakNegami)
+
+
+class KWWDipolar(_AbstractFCDipolar):
+ name = 'KWW (dipolar)'
+ params = _AbstractFCDipolar.params + [r'\beta']
+ bounds = _AbstractFCDipolar.bounds + [(0, 1)]
+ relax = Relaxation(distribution=KWW)
+
+
+if __name__ == '__main__':
+ import matplotlib.pyplot as plt
+
+ w = np.logspace(-3, 3, num=31)
+
+ plt.loglog(w, FFHSFC.func(w, 10, 1, xaxis='omega'), '-')
+
+ plt.show()
\ No newline at end of file
diff --git a/nmreval/models/gengamma.py b/nmreval/models/gengamma.py
new file mode 100644
index 0000000..a21eef9
--- /dev/null
+++ b/nmreval/models/gengamma.py
@@ -0,0 +1,298 @@
+
+import numpy as np
+from scipy.special import gammaln
+from ..math.logfourier import logft
+
+stepsize = 0.2 # 0.05
+span1 = 20
+span2 = 30
+
+
+class GeneralGamma(object):
+ lower_b = 20
+ upper_b = 20
+ dx = 0.2
+
+ @staticmethod
+ def gga_dist(tau, tau0, alpha, beta):
+ b_to_a = beta / alpha
+ norm = np.exp(gammaln(b_to_a) - b_to_a * np.log(b_to_a)) / alpha
+ t_to_t0 = tau / tau0
+ ret_val = np.exp(-b_to_a * t_to_t0 ** alpha) * t_to_t0 ** beta
+
+ return ret_val / norm
+
+ @staticmethod
+ def gga_ew_dist(tau, tau0, alpha, beta, sigma, gamma):
+ if gamma == beta:
+ return GeneralGamma.gga_dist(tau, tau0, alpha, beta)
+ b_to_a = beta / alpha
+ g_to_a = gamma / alpha
+ t_to_t0 = tau / tau0
+ norm = (np.exp(gammaln(b_to_a)) + sigma**(gamma-beta) * np.exp(gammaln(g_to_a) + (b_to_a-g_to_a)*np.log(b_to_a))) / np.exp(b_to_a*np.log(b_to_a)) / alpha
+
+ ret_val = np.exp(-b_to_a * t_to_t0**alpha) * t_to_t0**beta * (1 + (t_to_t0*sigma)**(gamma-beta))
+
+ return ret_val / norm
+
+ @staticmethod
+ def ggb_dist(tau, tau0, a, b):
+ norm = a * (1+b) * np.sin(np.pi*b/(1+b)) * b**(b/(1+b)) / np.pi
+ ret_val = b * (tau/tau0)**a + (tau/tau0)**(-a*b)
+
+ return norm / ret_val
+
+ @staticmethod
+ def _gg_time(t, tau0, *args, **kwargs):
+ logtau = np.log(tau0)
+ steps = np.arange(logtau - GeneralGamma.lower_b, logtau + GeneralGamma.upper_b, GeneralGamma.dx)
+ taus = np.exp(steps)
+ ret_val = np.zeros_like(t)
+ dist = {'gga': GeneralGamma.gga_dist,
+ 'gga_ew': GeneralGamma.gga_ew_dist,
+ 'ggb': GeneralGamma.ggb_dist}[kwargs.pop('mode', 'gga')]
+ gtau = dist(taus, tau0, *args)
+ for i, xval in enumerate(t):
+ y = np.exp(- xval / taus)
+ y *= gtau
+ y = y[1:] + y[:-1]
+ ret_val[i] = np.sum(y) * GeneralGamma.dx / 2
+ return ret_val
+
+ @staticmethod
+ def ggaew_b_freq(f, tau0, alpha, beta, sigma, gamma, tau1, a, b, r, dec=5):
+ t = np.logspace(np.log10(1 / max(f)) - dec, np.log10(1 / min(f)) + dec, num=int(len(f) * (0.5 * dec)))
+ phi_a = GeneralGamma.ggaew(t, tau0, alpha, beta, sigma, gamma)
+ phi_b = GeneralGamma.ggb(t, tau1, a, b)
+ temp = phi_a * (1 - r + r * phi_b)
+
+ ret_val = logft(t, temp, new_x=2 * np.pi * f)[1].y
+
+ return ret_val * f * 2 * np.pi
+
+ @staticmethod
+ def gga_b_freq(f, tau0, alpha, beta, tau1, a, b, r, dec=5):
+ t = np.logspace(np.log10(1 / max(f)) - dec, np.log10(1 / min(f)) + dec, num=int(len(f) * (0.5 * dec)))
+ phi_a = GeneralGamma.gga(t, tau0, alpha, beta)
+ phi_b = GeneralGamma.ggb(t, tau1, a, b)
+ temp = phi_a * (1 - r + r * phi_b)
+
+ ret_val = logft(t, temp, new_x=2 * np.pi * f)[1].y
+
+ return ret_val * f * 2 * np.pi
+
+ @staticmethod
+ def gga(t, tau0, alpha, beta):
+ return GeneralGamma._gg_time(t, tau0, alpha, beta, mode='gga')
+
+ @staticmethod
+ def ggaew(t, tau0, alpha, beta, sigma, gamma):
+ return GeneralGamma._gg_time(t, tau0, alpha, beta, sigma, gamma, mode='gga_ew')
+
+ @staticmethod
+ def ggb(t, tau0, a, b):
+ return GeneralGamma._gg_time(t, tau0, a, b, mode='ggb')
+
+ @staticmethod
+ def ggaew_b(t, tau0, alpha, beta, sigma, gamma, tau1, a, b, r):
+ phi_a = GeneralGamma.ggaew(t, tau0, alpha, beta, sigma, gamma)
+ phi_b = GeneralGamma.ggb(t, tau1, a, b)
+ ret_val = phi_a * (1 - r + r * phi_b)
+ return ret_val
+
+ @staticmethod
+ def gga_b(t, tau0, alpha, beta, tau1, a, b, r):
+ phi_a = GeneralGamma.gga(t, tau0, alpha, beta)
+ phi_b = GeneralGamma.ggb(t, tau1, a, b)
+ ret_val = phi_a * (1 - r + r * phi_b)
+ return ret_val
+
+
+class FitGG(object):
+ name = 'Alpha + Beta + EW'
+ type = 'Gamma-Functions'
+ equation = r'A*\Phi_{\alpha,EW} * [1 - C + C*\Phi_{\beta}] + B'
+ params = ['A', 'B',
+ r'\tau_{\alpha}', r'\alpha', r'\beta', '\sigma', '\gamma',
+ r'\tau_{\beta}', 'a', 'b', 'C']
+
+ @staticmethod
+ def func(p, x):
+ amp, base, tau0, alpha, beta, sigma, gamma, tau1, a, b, c = p
+ phi_a = GeneralGamma.ggaew(x, tau0, alpha, beta, sigma, gamma)
+ phi_b = GeneralGamma.ggb(x, tau1, a, b)
+ ret_val = amp * phi_a * (1 - c + c * phi_b) + base
+ return ret_val
+
+
+# Definiere Funktionen #
+def glntau1_td(ttime, tau, alpha, beta):
+ lnx = np.log(tau) + span1
+ intmin = np.log(tau) - span2
+ steps = np.arange(lnx, intmin, -stepsize)
+ explnx = np.exp(steps)
+ betatoalpha = beta / alpha
+ logtau = np.log(tau)
+ if betatoalpha > 80:
+ norm = np.exp(.5 * np.log(2 * np.pi / betatoalpha) - betatoalpha) / alpha
+ else:
+ norm = np.exp(gammaln(betatoalpha)) / alpha / np.power(betatoalpha, betatoalpha)
+ y = np.exp(-np.exp((steps - logtau) * alpha) * betatoalpha) * np.power(explnx / tau, beta) * np.exp(-ttime / explnx)
+ y = y[1:] + y[:-1]
+ intval = np.sum(y) / 2
+ intval = intval * stepsize / norm
+ return intval
+
+
+def gga(t, tau0, alpha, beta):
+ logtau = np.log(tau0)
+ stepsize = 0.2
+ steps = np.arange(logtau - 20, logtau + 20, stepsize)
+ taus = np.exp(steps)/tau0
+ betatoalpha = beta / alpha
+ norm = np.exp(gammaln(betatoalpha) - betatoalpha*np.log(betatoalpha)) / alpha
+ ret_val = np.zeros_like(t)
+ gtau = np.exp(-taus ** alpha * betatoalpha) * taus ** beta
+ for i, xval in enumerate(t):
+ # exponent = -taus**alpha * betatoalpha - xval/taus/tau0
+ y = np.exp(- xval/taus/tau0)
+ y *= gtau
+ y = y[1:] + y[:-1]
+ ret_val[i] = np.sum(y) * stepsize / norm / 2
+ return ret_val
+
+
+def ggaew(t, tau0, alpha, beta, sigma, gamma):
+ logtau = np.log(tau0)
+ stepsize = 0.2
+ steps = np.arange(logtau - 20, logtau + 20, stepsize)
+ taus = np.exp(steps) / tau0
+ q1 = beta / alpha
+ q2 = gamma / alpha
+ norm = (np.exp(gammaln(q1)) + sigma**(gamma-beta) * np.exp((q1-q2)*np.log(q1)) * np.exp(gammaln(q2))) / alpha / np.exp(q1*np.log(q1))
+ ret_val = np.empty_like(t)
+ gtau = np.exp(-taus**alpha * q1) * taus**beta * (1 + (taus*sigma)**(gamma-beta))
+ for i, xval in enumerate(t):
+ y = np.exp(-xval/taus/tau0)
+ y *= gtau
+ y = y[1:] + y[:-1]
+ ret_val[i] = np.sum(y) * stepsize / norm / 2
+ return ret_val
+
+
+def ggaew_b(t, tau0, alpha, beta, sigma, gamma, tau1, a, b, r):
+ phi_a = ggaew(t, tau0, alpha, beta,sigma, gamma)
+ phi_b = ggb(t, tau1, a, b)
+ ret_val = phi_a * (1-r + r*phi_b)
+ return ret_val
+
+
+def ggb(t, tau0, a, b):
+ logtau = np.log(tau0)
+ stepsize = 0.2
+ steps = np.arange(logtau - 20, logtau + 20, stepsize)
+ taus = np.exp(steps) / tau0
+ norm = np.pi / (a*(1+b)) / b**(b/(1+b)) / np.sin(np.pi*b / (1+b))
+ ret_val = np.empty_like(t)
+ gtau = 1. / (b * taus**a + taus**(-b*a))
+ for i, xval in enumerate(t):
+ y = np.exp(-xval / taus / tau0)
+ y *= gtau
+ y = y[1:] + y[:-1]
+ ret_val[i] = np.sum(y) * stepsize / norm / 2
+ return ret_val
+
+
+def glntau2_td(ttime, tau, alpha, beta, sigma, gam):
+ intval = 0
+ lnx = np.log(tau) + span1
+ intmin = np.log(tau) - span2
+ steps = np.arange(lnx, intmin, -stepsize)
+ explnx = np.exp(steps)
+ logtau = np.log(tau)
+ q1 = beta / alpha
+ q2 = gam / alpha
+
+ if q2 > 80:
+ norm = (np.exp(.5 * np.log(2 * np.pi / q1) - q1) + np.power(sigma, alpha * (q2 - q1)) * np.power(q2 / q1,
+ -q2) * np.exp(
+ .5 * np.log(2 * np.PI / q2) - q2)) / alpha
+ elif q1 > 80:
+ norm = (np.exp(.5 * np.log(2 * np.pi / q1) - q1) + np.power(sigma, alpha * (q2 - q1)) * np.power(q2 / q1,
+ -q2) * np.exp(
+ gammaln(q2)) * np.power(q1, -q1)) / alpha
+ else:
+ norm = (np.exp(gammaln(q1)) + np.power(sigma, gam - beta) * np.power(q1, q1 - q2) * np.exp(gammaln(q2))) / (
+ alpha * np.power(q1, q1))
+ y = np.exp(-np.exp((steps - logtau) * alpha) * q1) * np.power(explnx / tau, beta) * (
+ 1 + np.power(explnx * sigma / tau, gam - beta)) * np.exp(-ttime / explnx)
+ y = y[1:] + y[:-1]
+ intval = np.sum(y) / 2
+ intval = intval * stepsize / norm
+ return intval
+
+
+def glnbeta_td(ttime, tau, a, b):
+ intval = 0
+ lnx = np.log(tau) + span1
+ intmin = np.log(tau) - span2
+ steps = np.arange(lnx, intmin, -stepsize)
+ explnx = np.exp(steps)
+ norm = np.pi / (a * (1 + b)) / np.power(b, b / (1. + b)) / np.sin(np.pi * b / (1. + b))
+ y = 1. / (b * np.power(explnx / tau, a) + np.power(explnx / tau, -(b * a))) * np.exp(-ttime / explnx)
+ y = y[1:] + y[:-1]
+ intval = np.sum(y) / 2
+
+ intval = intval * stepsize / norm
+ return intval
+
+
+def g2(p, x):
+ ret_val = np.empty(np.shape(x))
+ for i, xval in enumerate(x):
+ # fkt1 = glntau2_td(xval, p[1], p[2], p[3], p[4], p[5])
+ # fkt1 = glntau1_td(xval, p[1], p[2], p[3])
+ fkt1 = glntau2_td(xval, p[1], p[2], p[3], p[4], p[5])
+ fkt2 = glnbeta_td(xval, p[6], p[7], p[8])
+ # ret_val[i] = (fkt1 * ((1 - p[4]) + p[4] * fkt2) * p[0]) + np.abs(p[9]) * np.exp(-(xval/p[10])**p[11]) + p[8]
+ ret_val[i] = fkt1 * (1-p[0] + p[0]*fkt2)
+ return ret_val
+
+
+def g1(p, x):
+ ret_val = np.empty(np.shape(x))
+ for i, xval in enumerate(x):
+ # fkt1 = glntau1_td(xval,p[1],p[2],p[3])
+ fkt1 = glntau2_td(xval, p[1], p[2], p[3], p[11], p[12])
+ fkt2 = glnbeta_td(xval, p[5], p[6], p[7])
+ ret_val[i] = p[0] * (fkt1 * ((1 - p[4]) + p[4] * fkt2)) + np.abs(p[8]) * np.exp(-(xval / p[9]) ** p[10])
+ return ret_val
+
+
+if __name__ == '__main__':
+ import matplotlib.pyplot as plt
+ import time
+
+ x = np.logspace(-5, 5, num=260)
+ tau0 = 100
+ alpha = 2
+ beta = 0.7
+ gamma = 0.1
+ sigma = 1000
+ tau1 = 1
+ a = 0.8
+ b = 0.6
+ r = 0
+ start = time.time()
+ y = GeneralGamma.ggaew_b(x, tau0, alpha, beta, sigma, gamma, tau1, a, b, r)
+ print('gg_time', time.time()-start)
+ start = time.time()
+ yy = g2([r, tau0, alpha, beta, sigma, gamma, tau1, a, b], x)
+ print('g2', time.time()-start)
+
+ plt.semilogx(x, y)
+ plt.semilogx(x, yy, 'x')
+ plt.show()
+
+
+
diff --git a/nmreval/models/myfitmodels.py b/nmreval/models/myfitmodels.py
new file mode 100644
index 0000000..4470466
--- /dev/null
+++ b/nmreval/models/myfitmodels.py
@@ -0,0 +1,66 @@
+import numpy as np
+from ..utils.constants import *
+
+r"""
+Create user-defined fitfunctions using this template.
+Order in params and ext_params must agree with order in list p, i.e. p[0] is A_{infty}, p[1] is beta_1,
+and C_{delta} is p[2].
+Choices are passed to the function as extra arguments (a and g)
+
+Sub- and superscripts of multiple characters are surrounded by braces.
+Use Latex notation for special characters (greek letters, infinity,...).
+Put a "r" in front of strings with backslashes.
+
+
+Known constants are:
+
+* kB: Boltzmann constant (8.6173e-5 eV/K)
+* N_A: Avogadro numbaer (6.02214129e23 1/mol)
+* R: Gas constant (5.1895e19 eV/(K*mol))
+* e: elementary charge (1.602176487e-19 C)
+* mu0: magnetic constant (4e-7*pi N/A^2)
+* h: Planck constant (6.6260693e-34 Js)
+* hbar: reduced PLack constant (=h/(2*pi))
+* Eu: Euler-Mascheroni constant (=-psi(1))
+
+Gyromagnetic values of 1H, 2H, 7Li, 13C and 19F are available via gamma['nucleus'] (including factor 2pi, so in s^-1).
+
+class Template(object):
+ name = 'Template'
+ type = 'User defined'
+ equation = r'A_{\infty} x^2 - \beta_{1} exp(-x) + C_{\delta} + \gamma_{1H}'
+ params = [r'A_{\infty}', r'\beta_{1}']
+ bounds = [(0, None), (7, 8)]
+ ext_params = [r'C_{\delta}']
+ choices = [('a', {'choice 1': 'x', 'choice 2': 'inv_x'}), ('g', gamma)]
+
+ @staticmethod
+ def func(p, x, a, g):
+ if a == 'x':
+ x = x
+ elif a == 'inv_x':
+ x = 1/x
+ return p[0] * x**2 - kB * p[1] * np.exp(-x) + p[2] + g
+"""
+
+
+class _Template(object):
+ name = 'Template'
+ type = 'User defined'
+ equation = r'A_{infty} x^{2} - k_{B} \beta_{1} exp(-x) + C_{\delta} + \gamma'
+ params = [r'A_{\infty}', r'\beta_{1}']
+ bounds = [(0, None), (7, 8)]
+ choices = [('a', {'choice 1': 'x', 'choice 2': 'inv_x'}), ('g', gamma)]
+
+ @staticmethod
+ def func(p, x, a, g):
+ # p: list of fit parameters and external parameters:
+ # [A_infty, beta_1, C_delta]
+ # a: first choice (x or inv_x)
+ # g: gyromagnetic ratio of chosen nucleus (e.g. gamma['1H'])
+ if a == 'x':
+ x = x
+ elif a == 'inv_x':
+ x = 1/x
+
+ return p[0] * x**2 - kB * p[1] * np.exp(-x) + p[2] + g
diff --git a/nmreval/models/relaxation.py b/nmreval/models/relaxation.py
new file mode 100644
index 0000000..ccd8314
--- /dev/null
+++ b/nmreval/models/relaxation.py
@@ -0,0 +1,138 @@
+import numpy as np
+
+from ..math.mittagleffler import mlf
+
+
+class TOne:
+ """
+ Generic spin-lattice relaxation function
+
+ .. math::
+ y = M_\infty (1 - \\alpha\exp[-(x/T_1)^\\beta])
+
+ Args:
+ x (array_like): Input values
+ m (float): Equilibrium magnetization
+ t1 (float): T1 relaxation time
+ beta (float): Stretching parameter
+ """
+ type = 'Relaxation'
+ name = 'T1 relaxation'
+ equation = r'M_{\infty}(1-\alpha exp(-(x/T_{1})^{\beta}))'
+ params = [r'M_{\infty}', r'\alpha', 'T_{1}', r'\beta']
+ bounds = [(0, None), (None, None), (0, None), (0, 1)]
+
+ @staticmethod
+ def func(x, m, alpha, t1, beta):
+ return m * (1 - alpha * np.exp(-(x/t1)**beta))
+
+
+class TTwo:
+ type = 'Relaxation'
+ name = 'T2 decay'
+ equation = r'\DeltaM exp(-(x/T_{2})^{\beta}) + M_{0}'
+ params = [r'\DeltaM', 'T_{2}', r'\beta', 'M_{0}']
+ bounds = [(0, None), (0, None), (0, 1), (None, None)]
+
+ @staticmethod
+ def func(x, dm, t2, beta, m0):
+ return dm * np.exp(-(x/t2)**beta) + m0
+
+
+class SatRec:
+ """
+ Spin-lattice relaxation function with offset
+
+ .. math::
+ y = \Delta M (1 - \exp[-(x/T_1)^\\beta]) + M_0
+
+ Args:
+ x (array_like): Input values
+ dm (float): Equilibrium magnetization
+ m0 (float): Initial magnetization
+ t1 (float): T1 relaxation time
+ beta (float): Stretching parameter
+
+
+
+ """
+ type = 'Relaxation'
+ name = 'Recovery experiment'
+ equation = r'\DeltaM(1-exp(-(x/T_{1})^{\beta})) + M_{0}'
+ params = [r'\DeltaM', 'T_{1}', r'\beta', 'M_{0}']
+ choices = [('Type', 'is_inv', {'Saturation': False, 'Inversion': True})]
+ bounds = [(0, None), (None, None), (0, 1), (0, None)]
+
+ @staticmethod
+ def func(x, dm, t1, beta, m0, is_inv: bool = True):
+ alpha = 1
+ if is_inv:
+ alpha = 2
+ return dm * (1 - alpha * np.exp(-(x/t1)**beta)) + m0
+
+
+class TwoSatRecAbsolute:
+ type = 'Relaxation'
+ name = 'Two-step relaxation (abs. int)'
+ equation = r'M_{0} + \Sigma \DeltaM_{i}(1-exp(-(x/T_{1,i})^{\beta_{i}}))'
+ params = [r'\DeltaM_{1}', 'T_{1,1}', r'\beta_{1}', r'\DeltaM_{2}', 'T_{1,2}', r'\beta_{2}', 'M_{0}']
+ choices = [('Type', 'is_inv', {'Saturation': False, 'Inversion': True})]
+ bounds = [(None, None), (0, None), (0, 1), (None, None), (0, None), (0, 1), (None, None)]
+
+ @staticmethod
+ def func(x, dm1, t11, beta1, dm2, t12, beta2, m0, is_inv: bool = False):
+ alpha = 1
+ if is_inv:
+ alpha = 2
+ return dm1 * (1 - alpha * np.exp(-(x/t11)**beta1)) + dm2 * (1 - alpha * np.exp(-(x/t12)**beta2)) + m0
+
+
+class TwoSatRecRelative:
+ type = 'Relaxation'
+ name = 'Two-step relaxation (rel. int)'
+ equation = r'M_{0} + \DeltaM[R(1-exp(-(x/T_{1,1})^{\beta_{1}})) + \n'\
+ r'(1-R)(1-exp(-(x/T_{1,2})^{\beta_{2}}))]'
+ params = [r'\DeltaM', 'M_{0}', 'T_{1,1}', r'\beta_{1}', 'T_{1,2}', r'\beta_{2}', 'R']
+ choices = [('Type', 'kind', {'Saturation': 'sat', 'Inversion': 'inv'})]
+ bounds = [(None, None), (0, None), (0, 1), (None, None), (0, None), (0, 1), (None, None)]
+
+ @staticmethod
+ def func(x, dm, m0, t11, beta1, t12, beta2, ratio, kind='sat'):
+ alpha = 1
+ if kind == 'inv':
+ alpha = 2
+ return dm * (ratio * (1 - alpha * np.exp(-(x/t11)**beta1)) +
+ (1-ratio) * (1 - alpha * np.exp(-(x/t12)**beta2))) + m0
+
+
+class SatRecMLF:
+ type = 'Relaxation'
+ name = 'Recovery experiment (MLF)'
+ equation = r'\DeltaM(1-E_{\alpha}(-(x/T_{1})^{\alpha})) + M_{0}'
+ params = [r'\DeltaM', 'M_{0}', 'T_{1}', r'\alpha']
+ choices = [('Type','kind', {'Saturation': 'sat', 'Inversion': 'inv'})]
+ bounds = [(0, None), (None, None), (0, None), (0, 1)]
+
+ @staticmethod
+ def func(x, dm, m0, t1, alpha, kind='sat'):
+ a = 1
+ if kind == 'inv':
+ a = 2
+ return dm * (1 - a * mlf(-(x/t1)**alpha, alpha)) + m0
+
+
+class PeschierHomogeneous:
+ name = 'Cross-Relaxation (g=0)'
+ type = 'Relaxation'
+ equation = r'Cross-Relaxation f(ast) \rightarrow s(low) (m_{s}(0)=0)'
+ params = ['M_{0}', 'k_{s}', 'k_{f}', 'T_{1,s}', 'T_{1,f}']
+ bounds = [(0, None), (0, None), (0, None), (0, None), (0, None)]
+
+ @staticmethod
+ def func(x, m, ks, kf, t1s, t1f):
+ r1f, r1s = 1/t1f, 1/t1s
+ a_plus = 0.5*((kf + ks + r1f + r1s) + np.sqrt((kf + r1f - ks - r1s)**2 + 4*kf*ks))
+ a_minu = 0.5*((kf + ks + r1f + r1s) - np.sqrt((kf + r1f - ks - r1s)**2 + 4*kf*ks))
+
+ return m*((a_plus - ks - r1s) / (a_plus - a_minu) * np.exp(-a_plus * x) -
+ (a_minu - ks - r1s) / (a_plus - a_minu) * np.exp(-a_minu * x))
diff --git a/nmreval/models/stimecho.py b/nmreval/models/stimecho.py
new file mode 100644
index 0000000..6c80b7f
--- /dev/null
+++ b/nmreval/models/stimecho.py
@@ -0,0 +1,69 @@
+from itertools import product
+
+import numpy as np
+
+from ..math.mittagleffler import mlf
+from .correlationfuncs import GammaCD
+
+
+class StimEcho:
+ type = 'Stimulated Echo'
+ name = 'Stimulated Echo'
+ equation = r'M_{0} * [(1-F_{\infty})C(-(x/\tau)^{\alpha}) + F_{\infty}]' \
+ r'R[-(x/T_{1})^{\beta}]'
+ params = ['M_{0}', r'F_{\infty}', r'\tau', r'\alpha', 'T_{1}', r'\beta']
+ bounds = [(0, None), (0, 1), (0, None), (0, 1), (0, None), (0, 1)]
+ choices = [('C', 'ctype', {'KWW': 'kww', 'MLF': 'mlf', 'CD': 'cd'}),
+ ('R', 'rtype', {'KWW': 'kww', 'MLF': 'mlf', 'CD': 'cd'})]
+
+ @staticmethod
+ def func(x, m0, finf, tau, alpha, t1, beta, ctype='kww', rtype='kww'):
+ if ctype == 'kww':
+ corr_f = np.exp(-(x / tau)**alpha)
+ elif ctype == 'mlf':
+ corr_f = mlf(-(x/tau)**alpha, alpha)
+ elif ctype == 'cd':
+ corr_f = GammaCD.func(x, 1., tau, alpha)
+ else:
+ raise ValueError(f'Unknown ctype {ctype}, use "kww", "mlf" or "cd"')
+
+ if rtype == 'kww':
+ rel_f = np.exp(-(x / t1)**beta)
+ elif rtype == 'mlf':
+ rel_f = mlf(-(x/t1)**beta, beta)
+ elif rtype == 'cd':
+ rel_f = GammaCD.func(x, 1., t1, beta)
+ else:
+ raise ValueError(f'Unknown rtype {rtype}, use "kww", "mlf" or "cd"')
+
+ return m0 * ((1 - finf) * corr_f + finf) * rel_f
+
+
+class StimEchoZ0:
+ name = 'Initial magnetization'
+ type = 'Stimulated Echo'
+ params = ['A', r'\Deltat', r'\delta', r'\eta']
+ equation = r'A(1 \pm \langle cos[2\omega_{Q}(x+\Deltat)]\rangle)'
+ choices = [('Mode', 'mode', {'Zeeman': 'cos', 'Spin-Alignment': 'sin'})]
+
+ @staticmethod
+ def func(x, a, t_ev, delta, eta, mode='cos'):
+ x = np.asarray(x)
+
+ theta = np.linspace(0, np.pi/2)
+ phi = np.linspace(0, 2*np.pi)
+ angles = np.array(list(product(theta, phi)))
+ dt = theta[1] - theta[0]
+ dp = phi[1] - phi[0]
+
+ omega = np.pi*delta*(3*np.cos(angles[:, 0])**2 - 1 - eta * np.sin(angles[:, 0])**2 * np.cos(2*angles[:, 1]))
+ wt = np.sin(angles[:, 0])
+
+ ret_val = (np.cos(2 * np.outer(omega, x-t_ev)) * wt[:, None]).sum(axis=0) * dt * dp / 2 / np.pi
+
+ if mode == 'cos':
+ return a*(1 + ret_val) / 2
+ elif mode == 'sin':
+ return a*(1 - ret_val) / 2
+ else:
+ raise ValueError
diff --git a/nmreval/models/temperature.py b/nmreval/models/temperature.py
new file mode 100644
index 0000000..cc43777
--- /dev/null
+++ b/nmreval/models/temperature.py
@@ -0,0 +1,74 @@
+import numpy as np
+
+from ..utils.constants import kB
+
+
+class _MAScalibration(object):
+ type = 'Other'
+ name = 'MAS temperature'
+ equation = ''
+ params = ['Slope', 'Offset']
+
+ @staticmethod
+ def func(p, x):
+ realt = p[0] * x + p[1]
+ return 1250 - 3.81194e-6 * np.sqrt(1.47663e17 - 1.32344e14 * realt)
+
+
+class VFT(object):
+ r"""
+ Calculates Vogel-Fulcher-Tammann
+
+ .. math::
+ f(x) = \tau_0 \exp\left(\frac{B}{x-T_0}\right)
+ """
+ type = 'Temperature'
+ name = 'VFT'
+ equation = r'\tau_{0} exp(B/(x-T_{0}))'
+ params = [r'\tau_{0}', 'B', 'T_{0}']
+ choices = [('Temperature', 'invt', {'T': 't', '1/T': 'invt', '1000/T': 'invt1000'})]
+ bounds = [(0, None), (None, None), (0, None)]
+
+ @staticmethod
+ def func(x, tau0, b, t0, invt='invt1000'):
+ if invt == 'invt':
+ x = 1./x
+ elif invt == 'invt1000':
+ x = 1000. / x
+
+ return t0 * np.exp(b / (x-t0))
+
+
+class Arrhenius:
+ type = 'Temperature'
+ name = 'Arrhenius'
+ equation = r'\tau_{0} exp(E_{A}/(k_{B} x))'
+ params = [r'\tau_{0}', 'E_{A}']
+ choices = [('Temperature', 'invt', {'T': 't', '1/T': 'invt', '1000/T': 'invt1000'})]
+ bounds = [(0, None), (None, None), (0, None)]
+
+ @staticmethod
+ def func(x, tau0, ea, invt='invt1000'):
+ if invt == 'invt':
+ x = 1./x
+ elif invt == 'invt1000':
+ x = 1000. / x
+
+ return tau0 * np.exp(ea / (kB*x))
+
+
+class Ecoop(object):
+ name = 'Roessler-Ding'
+ type = 'Temperature'
+ equation = r'\tau_{\infty}exp[E_{\infty}/T(1+exp(-\mu((T-T_{A})/E_{\infty})))]'
+ params = [r'\tau_{\infty}', r'E_{\infty}', r'\mu', 'T_{A}']
+ choices = [('Temperature', 'invt', {'T': 't', '1000/T': 'invt1000'})]
+
+ @staticmethod
+ def func(x, tau0, e_infty, mu, ta, invt='invt1000'):
+ if invt == 'invt1000':
+ x = 1000. / x
+
+ ec = e_infty * np.exp(-mu * (x - ta) / e_infty)
+
+ return tau0 * np.exp((e_infty + ec) / x)
diff --git a/nmreval/models/transitions.py b/nmreval/models/transitions.py
new file mode 100644
index 0000000..217903f
--- /dev/null
+++ b/nmreval/models/transitions.py
@@ -0,0 +1,28 @@
+import numpy as np
+from scipy import special as special
+
+from ..utils import kB
+
+
+class Weight2Phase:
+ type = 'Line shape'
+ name = 'Weighting factor'
+ equation = r'A*[0.5 + 0.5 erf[(x-T_{0})/\DeltaT]] + A_{0}'
+ params = ['T_{0}', r'\DeltaT', 'A', 'A_{0}']
+ bounds = [(0, None), (0, None), (None, None), (None, None)]
+
+ @staticmethod
+ def func(x, t0, dt, amp, off):
+ return amp*(0.5 + 0.5*special.erf((x-t0)/dt)) + off
+
+
+class HendricksonBray:
+ type = 'Line shape'
+ name = 'Hendrickson-Bray'
+ equation = 'AB/(B+(A-B)exp[-E/(kT)]) + w_0'
+ params = ['A', 'B', 'E', 'w_{0}']
+ bounds = [(0, None)] * 4
+
+ @staticmethod
+ def func(x, a, b, e, w0):
+ return a*b / (b + (a-b)*np.exp(-e/kB/x)) + w0
diff --git a/nmreval/models/user_models.py b/nmreval/models/user_models.py
new file mode 100644
index 0000000..5f98df4
--- /dev/null
+++ b/nmreval/models/user_models.py
@@ -0,0 +1,3 @@
+from ..configs import config_paths
+
+print(config_paths() / 'usermodels.py')
diff --git a/nmreval/models/wideline.py b/nmreval/models/wideline.py
new file mode 100644
index 0000000..eb11c45
--- /dev/null
+++ b/nmreval/models/wideline.py
@@ -0,0 +1,127 @@
+import numpy as np
+from numpy import trapz
+
+from ..math.orientations import zcw_spherical as crystallites
+
+
+class Pake:
+ type = 'Wideline'
+ name = 'Pake'
+ equation = ''
+ params = ['A', r'\delta', r'\eta', r'\Sigma_{B}', r't_{pulse}']
+ bounds = [(0, None), (0, None), (0, 1), (0, None)]
+ choices = [('Broadening', 'broad', {'Gaussian': 'g', 'Lorentzian': 'l'})]
+
+ @staticmethod
+ def func(x, c, delta, eta, sigma, t_pulse, broad='g'):
+ a, b, _ = crystallites(100000)
+ bins = 0.5 * (x[1:] + x[:-1])
+ bins = np.r_[0.5*(3*x[0]-x[1]), bins, 0.5*(3*x[-1]-x[-2])]
+
+ omega = delta * 0.5 * (3*np.cos(b)**2 - 1 - eta * np.sin(b)**2 * np.cos(2*a))
+
+ s_left = np.histogram(omega, bins=bins)[0]
+ s_right = np.histogram(-omega, bins=bins)[0]
+ s = s_left + s_right
+
+ if sigma != 0:
+ _x = np.arange(len(x))*(x[1]-x[0])
+ _x -= 0.5*_x[-1]
+
+ if broad == 'l':
+ apd = 2 * sigma / (4 * _x**2 + sigma**2) / np.pi
+ else:
+ apd = np.exp(-4 * np.log(2) * (_x/sigma)**2) * 2 * np.sqrt(np.log(2) / np.pi) / sigma
+
+ ret_val = np.convolve(s, apd, mode='same')
+
+ else:
+ ret_val = s
+
+ omega_1 = np.pi/2/t_pulse
+ attn = omega_1 * np.sin(t_pulse*np.sqrt(omega_1**2+0.5*(2*np.pi*x)**2)) / np.sqrt(omega_1**2+(np.pi*x)**2)
+
+ ret_val *= attn
+
+ return c * ret_val / trapz(ret_val, x)
+
+
+class CSA:
+ type = 'Wideline'
+ name = 'CSA'
+ equation = ''
+ params = ['A', r'\delta', r'\eta', r'\omega_{iso}', r'\Sigma_{B}']
+ bounds = [(0, None), (0, None), (0, 1), (None, None), (0, None)]
+ choices = [('Broadening', 'broad', {'Gaussian': 'g', 'Lorentzian': 'l'})]
+
+ @staticmethod
+ def func(x, c, delta, eta, w_iso, sigma, broad='g'):
+ a, b, _ = crystallites(100000)
+ bins = 0.5 * (x[1:] + x[:-1])
+ bins = np.r_[0.5*(-x[1] + 3*x[0]), bins, 0.5*(3*x[-1] - x[-2])]
+
+ omega = w_iso + delta * 0.5 * (3*np.cos(b)**2 - 1 - eta * np.sin(b)**2 * np.cos(2*a))
+
+ s_left = np.histogram(omega, bins=bins)[0]
+ s = s_left
+
+ if sigma != 0:
+ _x = np.arange(len(x)) * (x[1] - x[0])
+ _x -= 0.5 * _x[-1]
+ if broad == 'l':
+ apd = 2 * sigma / (4*_x**2 + sigma**2) / np.pi
+ else:
+ apd = np.exp(-4 * np.log(2) * (_x / sigma) ** 2) * 2 * np.sqrt(np.log(2) / np.pi) / sigma
+ ret_val = np.convolve(s, apd, mode='same')
+ else:
+ ret_val = s
+
+ return c * ret_val / trapz(ret_val, x)
+
+
+class SecCentralLine:
+ type = 'Wideline'
+ name = 'Central Transition 2nd order'
+ equation = ''
+ params = ['A', 'C_{Q}', r'\eta', r'\omega_{iso}', 'GB', r'\omega_{L}']
+ bounds = [(0, None), (0, None), (0, 1), (None, None), (0, None), (0, None)]
+ choices = [('Spin', 'spin', {'3/2': 1.5, '5/2': 2.5}),
+ ('Broadening', 'broad', {'Gaussian': 'g', 'Lorentzian': 'l'})]
+
+ @staticmethod
+ def func(x, c, cq, eta, f_iso, gb, f_l, spin=2.5, broad='g'):
+ a, b, _ = crystallites(200000)
+ bins = 0.5 * (x[1:] + x[:-1])
+ bins = np.r_[0.5*(-x[1] + 3*x[0]), bins, 0.5*(3*x[-1] - x[-2])]
+
+ # coupling constant
+ omega_q = 2 * np.pi * cq / (2*spin*(2*spin-1))
+ coupling = 1.5 * (omega_q**2 / (2*np.pi*f_l)) * (spin*(spin+1)-0.75)
+
+ # orientation
+ cos2phi = np.cos(2*a)
+ cos_theta_square = 0.5 + 0.5 * np.cos(2*b) # cos^2(x) = 1/2 (1+cos(2x)
+ prefactor_a = -3.375 + 2.25 * eta * cos2phi - 0.375 * (eta*cos2phi)**2
+ prefactor_b = 3.75 - 0.5 * eta**2 - 2 * eta*cos2phi + 0.75 * (eta*cos2phi)**2
+ prefactor_c = -0.375 + (eta**2) / 3 - 0.25 * eta*cos2phi - 0.375 * (eta*cos2phi)**2
+
+ orient = np.zeros_like(cos2phi)
+ orient += prefactor_a * cos_theta_square ** 2
+ orient += prefactor_b * cos_theta_square
+ orient += prefactor_c
+
+ omega = 2*np.pi*f_iso + coupling * orient
+ s = np.histogram(omega / (2*np.pi), bins=bins)[0]
+
+ if gb != 0:
+ _x = np.arange(len(x)) * (x[1]-x[0])
+ _x -= 0.5*_x[-1]
+ if broad == 'l':
+ apd = 2*gb / (4*_x**2 + gb**2) / np.pi
+ else:
+ apd = np.exp(-4*np.log(2) * (_x/gb)**2) * 2 * np.sqrt(np.log(2)/np.pi) / gb
+ ret_val = np.convolve(s, apd, mode='same')
+ else:
+ ret_val = s
+
+ return c * ret_val / trapz(ret_val, x)
diff --git a/nmreval/nmr/__init__.py b/nmreval/nmr/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nmreval/nmr/couplings.py b/nmreval/nmr/couplings.py
new file mode 100644
index 0000000..b54ed2b
--- /dev/null
+++ b/nmreval/nmr/couplings.py
@@ -0,0 +1,102 @@
+from math import log
+from collections import OrderedDict
+
+from ..utils.constants import pi, gamma, mu0, hbar
+
+
+__all__ = ['Quadrupolar', 'QuadrupolarQCC', 'Czjzek', 'HeteroDipolar', 'HomoDipolar', 'Constant']
+
+
+class Coupling(object):
+ name = 'coupling'
+ parameter = None
+ choice = None
+ unit = None
+ equation = ''
+
+
+class Quadrupolar(Coupling):
+ name = 'Quadrupolar'
+ parameter = [r'\delta', r'\eta']
+ unit = ['Hz', '']
+ equation = r'24 (2I-1)(2I+3) / (25(6m+3)^{2}) (1+\eta^2/3) \pi^{2} \delta^{2}'
+ choice = [('Spin', 'spin', OrderedDict([('1', 1), ('3/2', 1.5), ('2', 2),
+ ('5/2', 2.5), ('3', 3), ('7/2', 3.5)]))]
+
+ @staticmethod
+ def func(delta, eta, spin=1):
+ m = 0 if (2*spin) % 2 == 0 else 0.5
+ return 24*(2*spin-1)*(2*spin+3) * pi**2 * delta**2 * (1+eta**2 / 3) / 25 / (6*m+3)**2
+
+
+class QuadrupolarQCC(Coupling):
+ name = 'Quadrupolar (QCC)'
+ parameter = ['C_{Q}', r'\eta']
+ unit = ['Hz', '']
+ choice = [('Spin', 'spin', {'1': 1, '3/2': 1.5, '5/2': 2.5, '7/2': 3.5})]
+ equation = r'3/50 \pi^{2} C_{Q}^{2} (1+\eta^{2}/3) (2I+3)/(I^{2}(2I-1))'
+
+ @staticmethod
+ def func(cq, eta, spin=1):
+ return 0.06 * (2*spin+3)*pi**2 * cq**2 * (1+eta**2 / 3) / spin**2 / (2*spin-1)
+
+
+class Czjzek(Coupling):
+ name = 'Czjzek'
+ parameter = [r'\Sigma']
+ unit = ['Hz']
+ choice = [('Spin', 'spin', {'1': 1, '3/2': 1.5, '5/2': 2.5, '7/2': 3.5})]
+ equation = r'3/[20log(2)] \pi^{2} \Sigma^{2} (2I+3)/(I^{2}(2I-1))'
+
+ @staticmethod
+ def func(fwhm, spin=1):
+ return 3*(2+spin+3) * pi**2 * fwhm**2 / 20 / log(2) / spin**2 / (2*spin-1)
+
+
+class HeteroDipolar(Coupling):
+ name = 'Heteronuclear Dipolar'
+ parameter = ['r']
+ unit = ['m']
+ choice = [(r'\gamma_{1}', 'nucleus1', {k: k for k in gamma.keys()}),
+ (r'\gamma_{2}', 'nucleus2', {k: k for k in gamma.keys()})]
+ equation = r'1/10 * [\mu_{0}/(4\pi) * \hbar * \gamma_{1}\gamma_{2}/r^{3}]^{2}'
+
+ @staticmethod
+ def func(r, nucleus1='1H', nucleus2='19F'):
+ if nucleus1 not in gamma:
+ raise KeyError('Unknown nucleus {}'.format(nucleus1))
+ if nucleus2 not in gamma:
+ raise KeyError('Unknown nucleus {}'.format(nucleus2))
+
+ coupling = mu0 / (4*pi) * hbar*gamma[nucleus1]*gamma[nucleus2] / (r+1e-34)**3
+ coupling **= 2
+
+ return 0.1 * coupling
+
+
+class HomoDipolar(Coupling):
+ name = 'Homonuclear Dipolar'
+ parameter = ['r']
+ unit = ['m']
+ choice = [(r'\gamma', 'nucleus', {k: k for k in gamma.keys()})]
+ equation = r'3/10 * [\mu_{0}/(4\pi) * \hbar * \gamma^{2}/r^{3}]^{2}'
+
+ @staticmethod
+ def func(r, nucleus='1H'):
+ if nucleus not in gamma:
+ raise KeyError('Unknown nucleus {}'.format(nucleus))
+
+ coupling = mu0 / (4*pi) * hbar * gamma[nucleus]**2 / (r+1e-34)**3
+ coupling **= 2
+
+ return 0.3 * coupling
+
+
+class Constant(Coupling):
+ name = 'Constant'
+ parameter = ['C']
+ equation = 'C'
+
+ @staticmethod
+ def func(c):
+ return c
diff --git a/nmreval/nmr/fc_eval.py b/nmreval/nmr/fc_eval.py
new file mode 100644
index 0000000..1b6a3c3
--- /dev/null
+++ b/nmreval/nmr/fc_eval.py
@@ -0,0 +1,84 @@
+import numpy as np
+
+from ..utils.constants import gamma
+
+
+class FCEval(object):
+ name = 'FC Relaxation'
+ params = None
+ equation = ''
+
+ @staticmethod
+ def func(p, x, *args):
+ xmode, ymode, func_args = args
+ _x = x
+ if xmode == 'f (Hz)':
+ _x = x*2*np.pi
+ if ymode == 'Susceptibility (no BPP)':
+ return _x*FCEval.func_j(p, _x, *func_args)
+ rate = FCEval.func_r1(p, _x, *func_args)
+ if ymode == 'Rate':
+ return rate
+ elif ymode == 'Susceptibility':
+ return _x*rate
+ else:
+ return 1/rate
+
+ @staticmethod
+ def func_r1(p, x, *args):
+ counter = 0
+ y = np.zeros_like(x)
+ y_singles = []
+ for arg in args:
+ func_sd, func_cp, nuclei = arg
+ try:
+ nop_sd = len(func_sd.parameter)
+ except TypeError:
+ nop_sd = 0
+ sd_params = p[counter:counter+nop_sd]
+ counter += nop_sd
+ tau = p[counter]
+ counter += 1
+ cp_params = [pp for pp in p[counter:counter+len(func_cp.parameter)]]
+ counter += len(func_cp.parameter)
+
+ cp_params.extend([gamma[n] for n in nuclei])
+ prefactor = func_cp.func(*cp_params)
+
+ if len(nuclei) == 2:
+ t1 = func_sd.calc_t1_dipolar(x, tau, sd_params, prefactor=prefactor,
+ gamma1=nuclei[0], gamma2=nuclei[1],
+ inverse=False)
+ else:
+ t1 = func_sd.calc_t1_bpp(x, tau, sd_params, prefactor=prefactor,
+ inverse=False)
+ y_singles.append(t1)
+ y += t1
+
+ return np.array(y)
+
+ @staticmethod
+ def func_j(p, x, *args):
+ counter = 0
+ y = np.zeros_like(x)
+ y_singles = []
+ for arg in args:
+ func_sd, func_cp, nuclei = arg
+ try:
+ nop_sd = len(func_sd.parameter)
+ except TypeError:
+ nop_sd = 0
+ sd_params = p[counter:counter + nop_sd]
+ counter += nop_sd
+ tau = p[counter]
+ counter += 1
+ cp_params = [pp for pp in p[counter:counter + len(func_cp.parameter)]]
+ counter += len(func_cp.parameter)
+
+ cp_params.extend([gamma[n] for n in nuclei])
+ prefactor = func_cp.func(*cp_params)
+ j = prefactor*func_sd.specdens(x, tau, *sd_params)
+ y_singles.append(j)
+ y += j
+
+ return np.array(y)
diff --git a/nmreval/nmr/relaxation.py b/nmreval/nmr/relaxation.py
new file mode 100755
index 0000000..1b8d2a6
--- /dev/null
+++ b/nmreval/nmr/relaxation.py
@@ -0,0 +1,406 @@
+from numbers import Number
+from pathlib import Path
+from typing import Tuple
+from warnings import warn
+
+import numpy as np
+from scipy.interpolate import interp1d, Akima1DInterpolator
+from scipy.optimize import minimize
+
+from nmreval.lib.utils import ArrayLike
+from ..distributions.debye import Debye as Debye
+
+
+class Relaxation:
+ def __init__(self, distribution=None):
+ self._distribution = distribution
+ self._dist_parameter = ()
+ self._dist_kw = {}
+
+ self._coupling = None
+ self._coup_parameter = ()
+ self._coup_kw = {}
+
+ self._prefactor = 1.
+
+ def __repr__(self):
+ if self._distribution is not None:
+ return str(self._distribution.name)
+ else:
+ return super().__repr__()
+
+ def coupling(self, coupling, parameter: list = None, keywords: dict = None):
+ self._coupling = coupling
+
+ if parameter is not None:
+ self._coup_parameter = parameter
+
+ if keywords is not None:
+ self._coup_kw = keywords
+
+ if isinstance(self._coupling, Number):
+ self._prefactor = self._coupling
+ else:
+ try:
+ self._prefactor = self._coupling.func(*self._coup_parameter, **self._coup_kw)
+ except TypeError:
+ pass
+
+ def distribution(self, dist, parameter=None, keywords=None):
+ self._distribution = dist
+
+ if parameter is not None:
+ self._dist_parameter = parameter
+
+ if keywords is not None:
+ self._dist_kw = keywords
+
+ def t1(self, omega, tau, *args, mode='bpp', **kwargs):
+ # defaults to BPP
+ if mode == 'bpp':
+ return self.t1_bpp(omega, tau, *args, **kwargs)
+ elif mode == 'dipolar':
+ return self.t1_dipolar(omega, tau, *args, **kwargs)
+ else:
+ raise AttributeError(f'Unknown mode {mode}. Use either "bpp" or "dipolar".')
+
+ def t1_dipolar(self, omega: ArrayLike, tau: ArrayLike, *args,
+ inverse: bool = True, prefactor: float = None, **kwargs) -> ArrayLike:
+ """
+
+ Args:
+ omega:
+ tau:
+ *args:
+ inverse:
+ prefactor:
+ **kwargs:
+
+ Returns:
+
+ """
+ try:
+ omega_2 = kwargs['omega_coup']
+ except KeyError:
+ if kwargs.get('gamma_obs', False):
+ omega_2 = kwargs['gamma_coup'] / kwargs['gamma_obs'] * omega
+ else:
+ raise AttributeError('Unknown second frequency.')
+
+ if prefactor is None:
+ prefactor = self._prefactor
+
+ if len(args) == 0:
+ args = self._dist_parameter
+
+ rate = prefactor * (3 * self._distribution.specdens(omega, tau, *args) +
+ self._distribution.specdens(np.abs(omega - omega_2), tau, *args) +
+ 6 * self._distribution.specdens(omega + omega_2, tau, *args))
+ if inverse:
+ return 1 / rate
+ else:
+ return rate
+
+ def t1_bpp(self, omega, tau, *args, inverse=True, prefactor=None, **kwargs):
+ if prefactor is None:
+ prefactor = self._prefactor
+
+ if len(args) == 0:
+ args = self._dist_parameter
+
+ rate = prefactor * (self._distribution.specdens(omega, tau, *args) +
+ 4*self._distribution.specdens(2*omega, tau, *args))
+ if inverse:
+ return 1. / rate
+ else:
+ return rate
+
+ def t1_rho(self, omega, tau, omega_rf, *args, inverse=True, prefactor=None, **kwargs):
+ if prefactor is None:
+ prefactor = self._prefactor
+
+ if len(args) == 0:
+ args = self._dist_parameter
+
+ rate = prefactor * (10 * self._distribution.specdens(omega, tau, *args) +
+ 4 * self._distribution.specdens(2 * omega, tau, *args) +
+ 6 * self._distribution.specdens(omega_rf, tau, *args))
+ if inverse:
+ return 1. / rate
+ else:
+ return rate
+
+ def t2(self, omega, tau, *args, inverse=True, prefactor=None, **kwargs):
+ if prefactor is None:
+ prefactor = self._prefactor / 2.
+
+ if len(args) == 0:
+ args = self._dist_parameter
+
+ rate = prefactor * (3 * self._distribution.specdens(0, tau, *args) +
+ 5 * self._distribution.specdens(omega, tau, *args) +
+ 2 * self._distribution.specdens(2 * omega, tau, *args))
+ if inverse:
+ return 1. / rate
+ else:
+ return rate
+
+
+class RelaxationEvaluation(Relaxation):
+ def __init__(self, distribution=None):
+ super().__init__(distribution=distribution)
+ self.t1min = (np.nan, np.nan)
+ self._interpolate = None
+ self._interpolate_range = (None, None)
+
+ self.omega = np.nan
+ self.x = None
+ self.y = None
+
+ def data(self, temp, t1):
+ temp = np.asarray(temp)
+ t1 = np.asarray(t1)
+ sortidx = temp.argsort()
+ self.x = temp[sortidx]
+ self.y = t1[sortidx]
+ self.calculate_t1_min()
+
+ def calculate_t1_min(self, interpolate: int = None, trange=None):
+ min_index = np.argmin(self.y)
+ t1_min = (self.x[min_index], self.y[min_index])
+ parabola = None
+ self._interpolate_range = (None, None)
+
+ if interpolate is not None:
+ if interpolate not in [0, 1, 2, 3]:
+ raise ValueError(f'Unknown interpolation value {interpolate}')
+
+ if interpolate != 0:
+ if trange is None:
+ left_b = max(min_index-2, 0)
+ right_b = min(len(self.x), min_index+3)
+ else:
+ left_b = np.argmin(np.abs(self.x-trange[0]))
+ right_b = np.argmin(np.abs(self.x-trange[1]))+1
+
+ temp_range = self.x[left_b:right_b]
+ t1_range = self.y[left_b:right_b]
+
+ try:
+ x_inter = np.linspace(temp_range[0], temp_range[-1], num=51)
+
+ if interpolate == 1:
+ i_func = np.poly1d(np.polyfit(temp_range, t1_range, 2))
+ elif interpolate == 2:
+ i_func = interp1d(temp_range, t1_range, kind='cubic')
+ else:
+ i_func = Akima1DInterpolator(temp_range, t1_range)
+
+ y_inter = i_func(x_inter)
+ t1_min = (x_inter[np.argmin(y_inter)], y_inter[np.argmin(y_inter)])
+ self._interpolate = i_func
+ parabola = (x_inter, y_inter)
+ self._interpolate_range = (temp_range[0], temp_range[-1])
+
+ except IndexError:
+ pass
+
+ self.t1min = t1_min
+
+ return t1_min, parabola
+
+ def increase(self, variable: Tuple[str, int], height: float = None, omega: float = None,
+ dist_parameter: list = None, prefactor=None,
+ coupling_kwargs=None):
+
+ stretching = mini = np.nan
+ if height is None:
+ height = self.t1min[1]
+
+ if omega is None:
+ if np.isnan(self.omega):
+ raise ValueError('No larmor frequency set')
+
+ omega = self.omega
+
+ if prefactor is None:
+ prefactor = self._prefactor
+
+ if dist_parameter is None:
+ dist_parameter = self._dist_parameter
+
+ tau_lims = np.log10(1/omega)-3, np.log10(1/omega)+3
+
+ if not variable:
+ if isinstance(prefactor, list):
+ if coupling_kwargs is None:
+ coupling_kwargs = self._coup_kw
+ prefactor = self._coupling.func(*prefactor, **coupling_kwargs)
+
+ return stretching, np.min(self.t1(omega, np.logspace(*tau_lims, num=1001),
+ *dist_parameter, prefactor=prefactor, inverse=True))
+
+ if variable[0] == 'distribution':
+ if isinstance(self._distribution, Debye):
+ return 1.
+
+ if isinstance(prefactor, list):
+ if coupling_kwargs is None:
+ coupling_kwargs = self._coup_kw
+ prefactor = self._coupling.func(*prefactor, **coupling_kwargs)
+
+ use_fmin = True
+ if self._distribution.name in ['KWW', 'Cole-Davidson', 'Cole-Cole', 'Log-Gaussian']:
+
+ from importlib.resources import path
+ with path('resources.nmr', 'T1_min.npz') as fp:
+ if fp.exists():
+ t1min_table = np.load(str(fp))[self._distribution.name.replace('-', '_')]
+ debye_min = omega / 1.42517571908650 / prefactor # Zahl kommt von Wolfram alpha
+ ratio = height / debye_min
+ stretching = t1min_table[np.argmin(np.abs(t1min_table[:, 1]-ratio)), 0]
+ use_fmin = False
+
+ if use_fmin:
+ def _f(_x, p, ii):
+ p[ii] = _x[0]
+ t1_calc = np.min(self.t1(omega, np.logspace(*tau_lims, num=1001), *p,
+ prefactor=prefactor, inverse=True))
+
+ return np.abs(t1_calc - height)
+
+ stretching = minimize(_f, np.array([dist_parameter[variable[1]]]),
+ args=(dist_parameter, variable[1]),
+ method='Nelder-Mead').x[0]
+
+ elif variable[0] == 'coupling':
+ t1_no_coup = np.min(self.t1(omega, np.logspace(*tau_lims, num=1001),
+ *dist_parameter, prefactor=1))
+
+ if isinstance(prefactor, list):
+ def _f(_x, p, ii):
+ p[ii] = _x
+ return np.abs(t1_no_coup/height - self._coupling.func(*p, **coupling_kwargs)[0])
+
+ stretching = minimize(_f, np.array([prefactor[variable[1]]]),
+ args=(prefactor, variable[1])).x[0]
+ else:
+ stretching = t1_no_coup / height
+
+ else:
+ raise ValueError('Use "distribution" or "coupling" to set parameter')
+
+ if stretching:
+ self._prefactor = prefactor
+ self._dist_parameter = dist_parameter
+ if isinstance(prefactor, list):
+ self._coup_parameter = prefactor
+ mini = np.min(self.t1(omega, np.logspace(*tau_lims, num=1001), *self._dist_parameter,
+ prefactor=self._prefactor))
+
+ return stretching, mini
+
+ def correlation_from_t1(self, mode: str = 'raw', interpolate=False, omega=None,
+ dist_parameter: list = None, prefactor: float = None,
+ coupling_param: list = None, coupling_kwargs: dict = None):
+
+ if self.x is None:
+ raise ValueError('Temperature is not set')
+
+ if self.y is None:
+ raise ValueError('T1 data is not set')
+
+ if omega is None:
+ if np.isnan(self.omega):
+ raise ValueError('No frequency set')
+ omega = self.omega
+
+ if prefactor is None:
+ if coupling_kwargs is None:
+ coupling_kwargs = self._coup_kw
+
+ if coupling_param is None:
+ prefactor = self._prefactor
+ else:
+ prefactor = self._coupling.func(*coupling_param, **coupling_kwargs)
+
+ if dist_parameter is None:
+ dist_parameter = self._dist_parameter
+
+ fast_idx = self.x > self.t1min[0]
+
+ slow_t1 = self.y[~fast_idx]
+ slow_temp = self.x[~fast_idx]
+
+ correlation_times = np.ones(self.x.shape)
+ offset = len(slow_t1)
+
+ base_taus = np.logspace(-10, -7, num=1001)
+ min_tau = base_taus[np.argmin(self.t1(omega, base_taus, *dist_parameter, prefactor=prefactor))]
+
+ taus = np.geomspace(min_tau, 100.*min_tau, num=501)
+ current_t1 = self.t1(omega, taus, *dist_parameter, prefactor=prefactor)
+
+ for i in range(1, len(slow_t1)+1):
+ t1_i = slow_t1[-i]
+
+ if interpolate and self._interpolate is not None:
+ if slow_temp[-i] >= self._interpolate_range[0]:
+ t1_i = self._interpolate(slow_temp[-i])
+
+ if min(current_t1) > t1_i:
+ warn('Correlation time could not be calculated')
+ correlation_times[offset-i] = taus[0]
+ continue
+
+ cross_idx = np.where(np.diff(np.sign(current_t1 - t1_i)))[0]
+ while len(cross_idx) == 0:
+ taus *= 10
+ current_t1 = self.t1(omega, taus, *dist_parameter, prefactor=prefactor)
+ cross_idx = np.where(np.diff(np.sign(current_t1 - t1_i)))[0]
+
+ lamb = (t1_i - current_t1[cross_idx]) / (current_t1[cross_idx+1]-current_t1[cross_idx])
+ correlation_times[offset-i] = (taus[cross_idx+1] * lamb + (1 - lamb) * taus[cross_idx])[0]
+
+ fast_t1 = self.y[fast_idx]
+ fast_temp = self.x[fast_idx]
+
+ taus = np.geomspace(0.01*min_tau, min_tau, num=501)
+ current_t1 = self.t1(omega, taus, *dist_parameter, prefactor=prefactor)
+
+ for i in range(len(fast_t1)):
+ t1_i = fast_t1[i]
+
+ if interpolate and self._interpolate is not None:
+ if fast_temp[i] <= self._interpolate_range[1]:
+ t1_i = self._interpolate(fast_temp[i])
+
+ if current_t1[-1] > t1_i:
+ correlation_times[offset+i] = taus[-1]
+ warn('Correlation time could not be calculated')
+ continue
+
+ cross_idx = np.where(np.diff(np.sign(current_t1 - t1_i)))[0]
+ while len(cross_idx) == 0:
+ taus /= 10
+ current_t1 = self.t1(omega, taus, *dist_parameter, prefactor=prefactor)
+ cross_idx = np.where(np.diff(np.sign(current_t1 - t1_i)))[0]
+
+ lamb = (t1_i - current_t1[cross_idx]) / (current_t1[cross_idx+1]-current_t1[cross_idx])
+ correlation_times[offset+i] = (taus[cross_idx+1] * lamb + (1-lamb) * taus[cross_idx])[0]
+
+ opts = {'distribution': (self._distribution.name, dist_parameter),
+ 'coupling': (self._coupling.name, coupling_param, coupling_kwargs),
+ 'frequency': omega/2/np.pi}
+
+ return np.c_[self.x, self._distribution.mean_value(correlation_times, *dist_parameter, mode=mode)], opts
+
+ def _create_minimum_file(self, filename):
+ x = np.geomspace(0.1, 1, num=10001)
+ with Path(filename).open('w') as f:
+ f.write('#broadening\tT1_min/min_Debye\n')
+ for i, a in enumerate(np.geomspace(0.1, 10, num=10000)):
+ alpha = a
+ t1_i = self.t1_bpp(1, x, alpha)
+ f.write(f'{alpha}\t{min(t1_i) / 0.701667864144}\n')
+ f.flush()
diff --git a/nmreval/utils/__init__.py b/nmreval/utils/__init__.py
new file mode 100755
index 0000000..cc86e30
--- /dev/null
+++ b/nmreval/utils/__init__.py
@@ -0,0 +1,6 @@
+import re
+
+from .constants import *
+
+
+NUMBER_RE = re.compile(r'[-+]?\d*[+p.]?\d+([eE][-+]?\d+)?', re.MULTILINE)
diff --git a/nmreval/utils/constants.py b/nmreval/utils/constants.py
new file mode 100755
index 0000000..75e38c3
--- /dev/null
+++ b/nmreval/utils/constants.py
@@ -0,0 +1,196 @@
+from math import pi
+
+from collections import OrderedDict, namedtuple
+from scipy.special import psi
+
+__all__ = ['NA', 'kb_joule', 'h_joule', 'hbar_joule',
+ 'e', 'h', 'mu0', 'epsilon0', 'kB', 'R', 'hbar', 'pi', 'Eu',
+ 'nuclei', 'gamma', 'gamma_full', 'energy_converter']
+
+# Boltzmann constant in Joule, elementary charge and Avogadro constant are CODATA 2018 definitions
+NA = 6.02214076e23
+kb_joule = 1.380649e-23
+e = 1.602176634e-19
+h_joule = 6.62607015e-23
+mu0 = 1.256637062124e-6
+epsilon0 = 8.8541878128e-12
+
+kB = kb_joule / e # in eV/K
+R = NA*kB
+R_joule = NA*kb_joule
+hbar_joule = h_joule / (2*pi)
+pi = pi
+hbar = hbar_joule / e
+h = h_joule / e
+
+Eu = -psi(1)
+
+
+# information from IUPAC Recommendations NMR nomenclature 2001,
+# isotope, spin, abundance in %, magnetogyric ratio in rad/s/T * 1e7, quadrupole moment in fm^-2
+spintxt = """\
+1H 1/2 99.9885 26.7522128
+2H 1 0.0115 4.10662791 0.2860
+3H 1/2 - 28.5349779
+3He 1/2 1.37e-4 -20.3801587
+6Li 1 7.59 3.9371709 -0.0808
+7Li 3/2 92.41 10.3977013 -4.01
+9Be 3/2 100 -3.759666 5.288
+10B 3 19.9 2.8746786 8.459
+11B 3/2 80.1 8.5847044 4.059
+13C 1/2 1.07 6.728 284
+14N 1 99.632 1.9337792 2.044
+15N 1/2 0.368 -2.71261804
+17O 5/2 0.038 -3.62808 -2.558
+19F 1/2 100 25.18148
+21Ne 3/2 0.27 -2.11308 10.155
+23Na 3/2 100 7.0808493 10.4
+25Mg 5/2 10.00 -1.63887 19.94
+27Al 5/2 100 6.9762715 14.66
+29Si 1/2 4.6832 -5.3190
+31P 1/2 100 10.8394
+33S 3/2 0.76 2.055685 -6.78
+35Cl 3/2 75.78 2.624198 -8.165
+37Cl 3/2 24.22 2.184368 -6.435
+39K 3/2 93.2581 1.2500608 5.85
+40K 4 0.0117 -1.5542854 -7.3
+41K 3/2 6.7302 0.68606808 7.11
+43Ca 7/2 0.135 -1.803069 -4.08
+45Sc 7/2 100 6.5087973 -22.0
+47Ti 5/2 7.44 -1.5105 30.2
+49Ti 7/2 5.41 -1.51095 24.7
+50V 6 0.250 2.6706490 21.0
+51V 7/2 99.750 7.0455117 -5.2
+53Cr 3/2 9.501 -1.5152 -15.0
+55Mn 5/2 100 6.6452546 33.0
+57Fe 1/2 2.119 0.8680624
+59Co 7/2 100 6.332 42.0
+61Ni 3/2 1.1399 -2.3948 16.2
+63Cu 3/2 69.17 7.1117890 -22.0
+65Cu 3/2 30.83 7.60435 -20.4
+67Zn 5/2 4.10 1.676688 15.0
+69Ga 3/2 60.108 6.438855 17.1
+71Ga 3/2 39.892 8.181171 10.7
+73Ge 9/2 7.73 -0.9360303 -19.6
+75As 3/2 100 4.596163 31.4
+77Se 1/2 7.63 5.1253857
+79Br 3/2 50.69 6.725616 31.3
+81Br 3/2 49.31 7.249776 26.2
+83Kr 9/2 11.49 -1.03310 25.9
+85Rb 5/2 72.17 2.5927050 27.6
+87Rb 3/2 27.83 8.786400 13.35
+87Sr 9/2 7.00 -1.1639376 33.5
+89Y 1/2 100 -1.3162791
+91Zr 5/2 11.22 -2.49743 -17.6
+93Nb 9/2 100 6.5674 -32.0
+95Mo 5/2 15.92 -1.751 -2.2
+97Mo 5/2 9.55 -1.788 25.5
+99Tc 9/2 - 6.046 -12.9
+99Ru 5/2 12.76 -1.229 7.9
+101Ru 5/2 17.06 -1.377 45.7
+103Rh 1/2 100 -0.8468
+105Pd 5/2 22.33 -1.23 66.0
+107Ag 1/2 51.839 -1.0889181
+109Ag 1/2 48.161 -1.2518634
+111Cd 1/2 12.80 -5.6983131
+113Cd 1/2 12.22 -5.9609155
+113In 9/2 4.29 5.8845 79.9
+115In 9/2 95.71 5.8972 81.0
+115Sn 1/2 0.34 -8.8013
+117Sn 1/2 7.68 -9.58879
+119Sn 1/2 8.59 -10.0317
+121Sb 5/2 57.21 6.4435 -36.0
+123Sb 7/2 42.79 3.4892 -49.0
+123Te 1/2 0.89 -7.059098
+125Te 1/2 7.07 -8.5108404
+127I 5/2 100 5.389573 -71.0
+129Xe 1/2 26.44 -7.452103
+131Xe 3/2 21.18 2.209076 -11.4
+133Cs 7/2 100 3.5332539 -0.343
+135Ba 3/2 6.592 2.67550 16.0
+137Ba 3/2 11.232 2.99295 24.5
+138La 5 0.090 3.557239 45.0
+139La 7/2 99.910 3.8083318 20.0
+177Hf 7/2 18.60 1.086 336.5
+179Hf 9/2 13.62 -0.6821 379.3
+181Ta 7/2 99.988 3.2438 317.0
+183W 1/2 14.31 1.1282403
+185Re 5/2 37.40 6.1057 218.0
+187Re 5/2 62.60 6.1682 207.0
+187Os 1/2 1.96 0.6192895
+189Os 3/2 16.15 2.10713 85.6
+191Ir 3/2 37.3 0.4812 81.6
+193Ir 3/2 62.7 0.5227 75.1
+195Pt 1/2 33.832 5.8385
+197Au 3/2 100 0.473060 54.7
+199Hg 1/2 16.87 4.8457916
+201Hg 3/2 13.18 -1.788769 38.6
+203Tl 1/2 29.524 15.5393338
+205Tl 1/2 70.476 15.6921808
+207Pb 1/2 22.1 5.58046
+209Bi 9/2 100 4.3750 -51.6"""
+
+nucleus = namedtuple('nucleus', ['isotope', 'spin', 'abundance', 'gamma', 'Q'])
+
+
+def _parse_nuclei_quad_int(d):
+ _nuclei = OrderedDict()
+ for line in d.split('\n'):
+ isotope = line[:7].rstrip()
+ spin = line[7:12].replace(' ', '').split('/')
+ try:
+ spin = float(spin[0]) / float(spin[1])
+ except IndexError:
+ spin = float(spin[0])
+ abd = float(line[12:21].replace(' ', '').replace('-', '0'))
+ ratio = float(line[21:33].replace(' ', '')+'e7')
+ try:
+ q = float(line[33:].replace(' ', ''))
+ except ValueError:
+ q = 0.0
+
+ _nuclei[isotope] = nucleus(isotope, spin, abd, ratio, q)
+
+ return _nuclei
+
+
+nuclei = _parse_nuclei_quad_int(spintxt)
+gamma_full = OrderedDict([(k, abs(v.gamma)) for k, v in nuclei.items()])
+
+__useful_nuclei = ['1H', '2H', '6Li', '7Li', '13C', '14N', '17O', '19F', '23Na', '31P', '109Ag', ]
+gamma = OrderedDict([(k, v) for k, v in gamma_full.items() if k in __useful_nuclei])
+
+
+def energy_converter(e_in, old, new, verbose=False):
+ if old == new:
+ return e_in
+
+ fromunit = old.upper()
+ tounit = new.upper()
+
+ if fromunit == 'KJ/MOL':
+ e_ev = e_in * 1000 / (NA * e)
+ elif fromunit == 'EV':
+ e_ev = e_in
+ elif fromunit == 'K':
+ e_ev = e_in * kB
+ else:
+ raise ValueError('Unknown energy unit {}'.format(old))
+
+ if tounit == 'KJ/MOL':
+ e_out = e_ev * e * NA / 1000
+ elif tounit == 'EV':
+ e_out = e_ev
+ elif tounit == 'K':
+ e_out = e_in * e / kB
+ else:
+ raise ValueError('Unknown energy unit {}'.format(old))
+
+ if verbose:
+ try:
+ print('%f %s = %.2g %s' % (e_in, old, e_out, new))
+ except ValueError:
+ for k, v in enumerate(e_out):
+ print('%f %s = %.2f %s' % (e_in[k], old, v, new))
+
+ return e_out
diff --git a/nmreval/utils/exception.py b/nmreval/utils/exception.py
new file mode 100644
index 0000000..e69de29
diff --git a/nmreval/utils/pca_demo_pcs.py b/nmreval/utils/pca_demo_pcs.py
new file mode 100644
index 0000000..52bdd8e
--- /dev/null
+++ b/nmreval/utils/pca_demo_pcs.py
@@ -0,0 +1,302 @@
+import numpy as np
+import matplotlib.pyplot as plt
+import scipy.linalg
+
+
+def model_func(p, x):
+ func = np.exp(-(x / p[1]) + 1j * 2 * np.pi * p[0] * x)
+
+ return func
+
+
+def time_evol(p, x):
+ return p[0] * np.exp(-(x / p[1]) ** p[2])
+
+
+def create_data(m, p, n, x1, x2):
+ data_matrix = np.zeros([len(x1), len(x2)], dtype=complex)
+ ideal = np.zeros_like(x2)
+ for k, v in enumerate(m):
+ _x1 = v[0](p[k][0], x1)
+ _x2 = v[1](p[k][1], x2)
+ ideal += v[1](p[k][1], x2)
+ data_matrix += np.outer(_x1, _x2)
+ data_matrix.real += noise * np.amax(data_matrix).real * np.random.randn(len(x1), len(x2))
+ data_matrix.imag += noise * np.amax(data_matrix).real * np.random.randn(len(x1), len(x2))
+ return data_matrix, ideal
+
+
+# ------------------------------------------------------------------------------------------------ #
+
+sw = 100e3 # sampling rate
+t = np.arange(1 * 1024) / sw # time scale
+tau = np.logspace(-5, 3, 27)
+no_measurements = len(tau)
+no_pts = len(t)
+
+f = np.fft.fftshift(np.fft.fftfreq(no_pts, d=1 / sw))
+
+noise = 0.01
+models = [[model_func, time_evol],
+ [model_func, time_evol],
+ # [model_func, time_evol],
+ ]
+p = [[[-10e3, 5e-4], [0.2, 0.1, 1]],
+ [[10e3, 5e-4], [1, 10, 1]],
+ # [[0, 5e-4], [1, 1, 1]],
+ ]
+
+real_data, ideal_curve = create_data(models, p, noise, t, tau)
+# real_data = np.fft.fftshift(np.fft.fft(real_data, axis=0), axes=(0,))
+
+if True:
+ f1, axarr1 = plt.subplots()
+ for i in range(no_measurements):
+ axarr1.plot(f, real_data[:, i].real / np.max(real_data.real) + i, 'b-')
+ axarr1.plot(f, real_data[:, i].imag / np.amax(real_data.real) + i, 'r-')
+
+# raw_data = np.append(real_data.real, real_data.imag, axis=0).T
+raw_data = real_data.T
+spec_sum = np.sum(raw_data.real, axis=1)
+
+# ----------------------- PCA -------------------------------
+# calculate deviation
+meanvalues = raw_data.mean(axis=0)
+data_no_mean = raw_data - meanvalues
+# data_scaled = data_no_mean/data_no_mean.std(axis=0)
+
+data = data_no_mean
+
+# SVD
+# u_full, s_full, vt_full = np.linalg.svd(data, full_matrices=False)
+s_full, u_full = np.linalg.eig(np.cov(data))
+vt_full = u_full.T
+# ss_full = np.diag(s_full)
+eigenvalues = s_full / sum(s_full)
+
+fig1, (ax1, ax2) = plt.subplots(2)
+
+sum_eigen = 0
+n = 0
+eigen_i = np.inf
+while sum_eigen < 98 and n < 7:
+ eigen_i = 100 * eigenvalues[n]
+ sum_eigen += eigen_i
+ ax1.plot(vt_full[n, :] / max(abs((vt_full[n, :]))) - 2 * n, label='{:.2f}'.format(eigen_i))
+ ax2.plot(u_full[:, n], label='{:.2f}'.format(eigen_i))
+ n += 1
+ax1.legend()
+plt.show()
+
+n = int(input('Number of components? '))
+print('Use first {} components'.format(n))
+
+# reduce dimensionality
+u_cut = u_full[:, :n]
+s_cut = np.diag(s_full[:n])
+vt_cut = vt_full[:n, :]
+
+v_cut = vt_cut.T
+transform_matrix = np.dot(u_cut, s_cut)
+
+# reconstruct data
+cut_data = np.dot(transform_matrix, vt_cut)
+cut_data += meanvalues
+
+if n == 2:
+ scores = np.dot(cut_data, v_cut[:, 0] + v_cut[:, 1])
+ # scores = np.dot(cut_data, v_cut[:, 0] / sum(v_cut[:, 0]) + v_cut[:, 1] / sum(v_cut[:, 0]))
+else:
+ scores = np.dot(cut_data, v_cut[:, 0])
+# scores = np.sum(cut_data+_meanvalues, axis=1)
+# scores = np.dot(transform_matrix, transform_matrix.T)[:, 0]
+# scores2 = np.sum(cut_data[:, :len(cut_data)], axis=1)
+scores2 = np.sum(cut_data[:, :5], axis=1)
+# scores2 = np.dot(cut_data, cut_data.T)[:, 0]
+
+
+if True:
+ fig, ax = plt.subplots(3)
+ for i in range(no_measurements):
+ # print i, sum(u_full[i, :]), u_cut[i, :]
+ ax[0].plot(raw_data[i, :])
+ ax[1].plot(cut_data[i, :])
+ ax[2].plot(cut_data[i, :] - raw_data[i, :])
+ ax[0].set_title('full')
+ ax[1].set_title('PCA')
+ ax[2].set_title('difference')
+
+# coeff, score, latent = princomp(data, numpc=2)
+# x_r = np.dot(coeff, score).T + meanvalues
+# plt.plot(np.sum(score, axis=0))
+# plt.show()
+
+if False:
+ if n == 2:
+ f7, (ax7, ax8, ax9) = plt.subplots(3)
+ ax7.plot(u_cut[:, 0], u_cut[:, 1], '-')
+ ax8.plot(v_cut[:, 0], v_cut[:, 1], '-')
+ ax9.plot(cut_data[:, 0] / sum(v_cut[:, 1]), cut_data[:, 1] / sum(v_cut[:, 0]), '-')
+ ax9.plot(cut_data[:, 0], cut_data[:, 1], '-')
+ ax7.set_xlabel('C1')
+ ax7.set_ylabel('C2')
+ ax8.set_xlabel('C1')
+ ax8.set_ylabel('C2')
+ ax9.set_xlabel('C1')
+ ax9.set_ylabel('C2')
+ elif n == 3:
+ from mpl_toolkits.mplot3d import Axes3D
+
+ f7 = plt.figure()
+ ax7 = f7.add_subplot(311, projection='3d')
+ ax8 = f7.add_subplot(312, projection='3d')
+ ax9 = f7.add_subplot(313, projection='3d')
+ ax7.plot(u_cut[:, 0], u_cut[:, 1], u_cut[:, 2])
+ ax8.plot(v_cut[:, 0], v_cut[:, 1], v_cut[:, 2])
+ # ax9.scatter(cut_data[:, 0]/sum(v_cut[:, 1]), cut_data[:, 1]/sum(v_cut[:, 0]), '-o')
+ ax9.plot(cut_data[:, 0], cut_data[:, 1], cut_data[:, 2])
+ ax7.set_xlabel('C1')
+ ax7.set_ylabel('C2')
+ ax7.set_zlabel('C3')
+ ax8.set_xlabel('C1')
+ ax8.set_ylabel('C2')
+ ax8.set_zlabel('C3')
+ ax9.set_xlabel('C1')
+ ax9.set_ylabel('C2')
+ ax9.set_zlabel('C3')
+
+f5, ax5 = plt.subplots(1)
+ax5.semilogx(tau, ideal_curve / max(ideal_curve), 'g--', label='model')
+ax5.semilogx(tau, np.sum(real_data[:5, :], axis=0).real, label='raw')
+ax5.semilogx(tau, spec_sum / max(spec_sum), 'm-', label='spectrum: Re')
+ax5.semilogx(tau, scores2 / max(abs(scores2)), 'o', label='PCA')
+ax5.semilogx(tau, scores / max(abs(scores)), '^', label='PCA_2')
+# ax5.semilogx(tau, (scores - scores[-1]) / (scores[0] - scores[-1]), 'yo', label='PCA_Oleg')
+plt.legend()
+
+plt.show()
+
+
+# #--- try MSSA ---#
+# m = 5
+# # f6, ax6 = plt.subplots()
+# # ax6.plot(real_data)
+# y = np.array([], dtype=complex)
+# n = len(real_data[:, 0])
+# for d in real_data.T:
+# y_i = np.zeros((n, m), dtype=complex)
+# for i in xrange(m):
+# y_i[:n-i, i] = d[i:n]
+# try:
+# y = np.hstack((y, y_i))
+# except ValueError:
+# y = y_i
+#
+# c = np.dot(y.T, y) / n
+# lamb, rho = eig(c)
+# pc = np.dot(y, rho)
+# for i, k in enumerate(pc.T):
+# plt.plot(k/max(k) + i)
+# plt.show()
+# rc = []
+# for k in xrange(len(real_data[0])):
+# rc_k = np.zeros((n, m), dtype=complex)
+# for i in xrange(m):
+# z = np.zeros((n, m), dtype=complex)
+# for j in xrange(m):
+# z[j:n, j] = pc[:n-j, i]
+# rc_k[:, i] = np.dot(z, rho[k*m:(k+1)*m, i])/m
+# # print rc_k[:, 0].shape
+# # plt.plot(rc_k[:, 0])
+# # plt.show()
+# rc.append(rc_k)
+# # fig, ax = plt.subplots(1)
+# # for k, v in enumerate(rc):
+# # plt.plot(np.sum(v[:, :2], axis=1))
+# # plt.plot(data_scaled[:, k].real)
+# # plt.show()
+#
+# s_orig = []
+# s_ssa = []
+# for k, v in enumerate(raw_data.T):
+# s_orig.append(max(v.real)+meanvalues[k])
+# s_ssa.append(max((np.sum(rc[k][:, :], axis=1)*data_std[k]).real)+meanvalues[k])
+# # print s_orig
+# s_orig = np.array(s_orig)
+# s_ssa = np.array(s_ssa)
+# plt.plot(s_orig/s_orig[0], label='Orig')
+# plt.plot(s_ssa/s_ssa[0], label='SSA')
+# # plt.plot(ideal_curve)
+# # plt.plot(np.array(s_orig)/np.array(s_ssa))
+# plt.legend()
+# plt.show()
+
+# n = int(raw_input('Number of components? '))
+# print 'Use first {} components'.format(n)
+#
+# # reduce dimensionality
+# u_cut = u_full[:, :n]
+# s_cut = np.diag(s_full[:n])
+# vt_cut = vt_full[:n, :]
+#
+# v_cut = vt_cut.T
+# transform_matrix = np.dot(u_cut, s_cut)
+#
+# # reconstruct data
+# cut_data = np.dot(transform_matrix, vt_cut)
+# cut_data += meanvalues
+#
+# if n == 2:
+# scores = np.dot(cut_data, v_cut[:, 0] + v_cut[:, 1])
+# scores = np.dot(cut_data, v_cut[:, 0]/sum(v_cut[:, 0]) + v_cut[:, 1]/sum(v_cut[:, 0]))
+# else:
+# scores = np.dot(cut_data, v_cut[:, 0])
+# # scores = np.sum(cut_data+_meanvalues, axis=1)
+# # scores = np.dot(transform_matrix, transform_matrix.T)[:, 0]
+# scores2 = np.sum(cut_data[:, :len(cut_data)], axis=1)
+# # scores2 = np.dot(cut_data, cut_data.T)[:, 0]
+# #
+# if True:
+# fig, ax = plt.subplots(3)
+# for i in xrange(no_measurements):
+# # print i, sum(u_full[i, :]), u_cut[i, :]
+# ax[0].plot(raw_data[i, :])
+# ax[1].plot(cut_data[i, :])
+# ax[2].plot(cut_data[i, :] - raw_data[i, :])
+# ax[0].set_title('full')
+# ax[1].set_title('PCA')
+# ax[2].set_title('difference')
+#
+# print cut_data[:, 0]
+# if True:
+# if n == 2:
+# f7, (ax7, ax8, ax9) = plt.subplots(3)
+# ax7.plot(u_cut[:, 0], u_cut[:, 1], '-')
+# ax8.plot(v_cut[:, 0], v_cut[:, 1], '-')
+# ax9.plot(cut_data[:, 0]/sum(v_cut[:, 1]), cut_data[:, 1]/sum(v_cut[:, 0]), '-')
+# ax9.plot(cut_data[:, 0], cut_data[:, 1], '-')
+# ax7.set_xlabel('C1'); ax7.set_ylabel('C2')
+# ax8.set_xlabel('C1'); ax8.set_ylabel('C2')
+# ax9.set_xlabel('C1'); ax9.set_ylabel('C2')
+# elif n == 3:
+# from mpl_toolkits.mplot3d import Axes3D
+# f7 = plt.figure()
+# ax7 = f7.add_subplot(311, projection='3d')
+# ax8 = f7.add_subplot(312, projection='3d')
+# ax9 = f7.add_subplot(313, projection='3d')
+# ax7.plot(u_cut[:, 0], u_cut[:, 1], u_cut[:, 2])
+# ax8.plot(v_cut[:, 0], v_cut[:, 1], v_cut[:, 2])
+# # ax9.scatter(cut_data[:, 0]/sum(v_cut[:, 1]), cut_data[:, 1]/sum(v_cut[:, 0]), '-o')
+# ax9.plot(cut_data[:, 0], cut_data[:, 1], cut_data[:, 2])
+# ax7.set_xlabel('C1'); ax7.set_ylabel('C2'); ax7.set_zlabel('C3')
+# ax8.set_xlabel('C1'); ax8.set_ylabel('C2'); ax8.set_zlabel('C3')
+# ax9.set_xlabel('C1'); ax9.set_ylabel('C2'); ax9.set_zlabel('C3')
+#
+# f5, ax5 = plt.subplots(1)
+# ax5.semilogx(tau, ideal_curve/max(ideal_curve), 'g--', label='model')
+# ax5.semilogx(tau, spec_sum/max(spec_sum), 'm-', label='spectrum: Re')
+# ax5.semilogx(tau, scores2/max(abs(scores2)), 'o', label='PCA')
+# ax5.semilogx(tau, (scores-scores[-1])/(scores[0]-scores[-1]), 'yo', label='PCA_Oleg')
+# plt.legend()
+#
+# plt.show()
diff --git a/nmreval/utils/pokemon.json b/nmreval/utils/pokemon.json
new file mode 100644
index 0000000..edc36b9
--- /dev/null
+++ b/nmreval/utils/pokemon.json
@@ -0,0 +1,17740 @@
+{
+ "001": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/96/Sugimori_001.png"
+ ]
+ ],
+ "Deutsch": "Bisasam",
+ "Englisch": "Bulbasaur",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Bulbizarre",
+ "Gewicht": "6,9 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30d5\\u30b7\\u30ae\\u30c0\\u30cd (Fushigidane)",
+ "Kategorie": "Samen",
+ "Link": "http://pokewiki.de/Bisasam",
+ "National-Dex": "#001",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "002": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/47/Sugimori_002.png"
+ ]
+ ],
+ "Deutsch": "Bisaknosp",
+ "Englisch": "Ivysaur",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Herbizarre",
+ "Gewicht": "13,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30d5\\u30b7\\u30ae\\u30bd\\u30a6 (Fushigisou)",
+ "Kategorie": "Samen",
+ "Link": "http://pokewiki.de/Bisaknosp",
+ "National-Dex": "#002",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "003": {
+ "Bilder": [
+ [
+ "Bisaflor",
+ "http://www.pokewiki.de/images/1/19/Sugimori_003.png"
+ ],
+ [
+ "Mega-Bisaflor",
+ "http://www.pokewiki.de/images/1/1c/Sugimori_003m1.png"
+ ]
+ ],
+ "Deutsch": "Bisaflor",
+ "Englisch": "Venusaur",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Florizarre",
+ "Gewicht": [
+ "100,0 kg",
+ "155,5 kg (Mega)"
+ ],
+ "Größe": [
+ "2,0 m",
+ "2,4 m (Mega)"
+ ],
+ "Japanisch": "\\u30d5\\u30b7\\u30ae\\u30d0\\u30ca (Fushigibana)",
+ "Kategorie": "Samen",
+ "Link": "http://pokewiki.de/Bisaflor",
+ "National-Dex": "#003",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "004": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/63/Sugimori_004.png"
+ ]
+ ],
+ "Deutsch": "Glumanda",
+ "Englisch": "Charmander",
+ "Farbe": "Rot",
+ "Französisch": "Salam\\u00e8che",
+ "Gewicht": "8,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30d2\\u30c8\\u30ab\\u30b2 (Hitokage)",
+ "Kategorie": "Echse",
+ "Link": "http://pokewiki.de/Glumanda",
+ "National-Dex": "#004",
+ "Typ": "Feuer"
+ },
+ "005": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7a/Sugimori_005.png"
+ ]
+ ],
+ "Deutsch": "Glutexo",
+ "Englisch": "Charmeleon",
+ "Farbe": "Rot",
+ "Französisch": "Reptincel",
+ "Gewicht": "19,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30ea\\u30b6\\u30fc\\u30c9 (Lizardo)",
+ "Kategorie": "Flamme",
+ "Link": "http://pokewiki.de/Glutexo",
+ "National-Dex": "#005",
+ "Typ": "Feuer"
+ },
+ "006": {
+ "Bilder": [
+ [
+ "Glurak",
+ "http://www.pokewiki.de/images/9/96/Sugimori_006.png"
+ ],
+ [
+ "Mega-Glurak X",
+ "http://www.pokewiki.de/images/b/b9/Sugimori_006m1.png"
+ ],
+ [
+ "Mega-Glurak Y",
+ "http://www.pokewiki.de/images/4/48/Sugimori_006m2.png"
+ ]
+ ],
+ "Deutsch": "Glurak",
+ "Englisch": "Charizard",
+ "Farbe": [
+ "Rot",
+ "Schwarz (Mega X)"
+ ],
+ "Französisch": "Dracaufeu",
+ "Gewicht": [
+ "90,5 kg",
+ "110,5 kg (Mega X)",
+ "100,5 kg (Mega Y)"
+ ],
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30ea\\u30b6\\u30fc\\u30c9\\u30f3 (Lizardon)",
+ "Kategorie": "Flamme",
+ "Link": "http://pokewiki.de/Glurak",
+ "National-Dex": "#006",
+ "Typ": [
+ "Feuer",
+ "Flug",
+ "Feuer",
+ "Drache (Mega X)"
+ ]
+ },
+ "007": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ac/Sugimori_007.png"
+ ]
+ ],
+ "Deutsch": "Schiggy",
+ "Englisch": "Squirtle",
+ "Farbe": "Blau",
+ "Französisch": "Carapuce",
+ "Gewicht": "9,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30bc\\u30cb\\u30ac\\u30e1 (Zenigame)",
+ "Kategorie": "Minikr\\u00f6te",
+ "Link": "http://pokewiki.de/Schiggy",
+ "National-Dex": "#007",
+ "Typ": "Wasser"
+ },
+ "008": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/09/Sugimori_008.png"
+ ]
+ ],
+ "Deutsch": "Schillok",
+ "Englisch": "Wartortle",
+ "Farbe": "Blau",
+ "Französisch": "Carabaffe",
+ "Gewicht": "22,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30ab\\u30e1\\u30fc\\u30eb (Kameil)",
+ "Kategorie": "Kr\\u00f6te",
+ "Link": "http://pokewiki.de/Schillok",
+ "National-Dex": "#008",
+ "Typ": "Wasser"
+ },
+ "009": {
+ "Bilder": [
+ [
+ "Turtok",
+ "http://www.pokewiki.de/images/9/9a/Sugimori_009.png"
+ ],
+ [
+ "Mega-Turtok",
+ "http://www.pokewiki.de/images/3/3b/Sugimori_009m1.png"
+ ]
+ ],
+ "Deutsch": "Turtok",
+ "Englisch": "Blastoise",
+ "Farbe": "Blau",
+ "Französisch": "Tortank",
+ "Gewicht": [
+ "85,5 kg",
+ "101,1 kg (Mega)"
+ ],
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30ab\\u30e1\\u30c3\\u30af\\u30b9 (Kamex)",
+ "Kategorie": "Panzertier",
+ "Link": "http://pokewiki.de/Turtok",
+ "National-Dex": "#009",
+ "Typ": "Wasser"
+ },
+ "010": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7b/Sugimori_010.png"
+ ]
+ ],
+ "Deutsch": "Raupy",
+ "Englisch": "Caterpie",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Chenipan",
+ "Gewicht": "2,9 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30ad\\u30e3\\u30bf\\u30d4\\u30fc (Caterpie)",
+ "Kategorie": "Wurm",
+ "Link": "http://pokewiki.de/Raupy",
+ "National-Dex": "#010",
+ "Typ": "K\\u00e4fer"
+ },
+ "011": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/90/Sugimori_011.png"
+ ]
+ ],
+ "Deutsch": "Safcon",
+ "Englisch": "Metapod",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Chrysacier",
+ "Gewicht": "9,9 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30c8\\u30e9\\u30f3\\u30bb\\u30eb (Trancell)",
+ "Kategorie": "Kokon",
+ "Link": "http://pokewiki.de/Safcon",
+ "National-Dex": "#011",
+ "Typ": "K\\u00e4fer"
+ },
+ "012": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7c/Sugimori_012.png"
+ ]
+ ],
+ "Deutsch": "Smettbo",
+ "Englisch": "Butterfree",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Papilusion",
+ "Gewicht": "32,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30d0\\u30bf\\u30d5\\u30ea\\u30fc (Butterfree)",
+ "Kategorie": "Falter",
+ "Link": "http://pokewiki.de/Smettbo",
+ "National-Dex": "#012",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "013": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/62/Sugimori_013.png"
+ ]
+ ],
+ "Deutsch": "Hornliu",
+ "Englisch": "Weedle",
+ "Farbe": "Braun",
+ "Französisch": "Aspicot",
+ "Gewicht": "3,2 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30d3\\u30fc\\u30c9\\u30eb (Beedle)",
+ "Kategorie": "Raupe",
+ "Link": "http://pokewiki.de/Hornliu",
+ "National-Dex": "#013",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gift"
+ ]
+ },
+ "014": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/94/Sugimori_014.png"
+ ]
+ ],
+ "Deutsch": "Kokuna",
+ "Englisch": "Kakuna",
+ "Farbe": "Gelb",
+ "Französisch": "Coconfort",
+ "Gewicht": "10,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30b3\\u30af\\u30fc\\u30f3 (Cocoon)",
+ "Kategorie": "Kokon",
+ "Link": "http://pokewiki.de/Kokuna",
+ "National-Dex": "#014",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gift"
+ ]
+ },
+ "015": {
+ "Bilder": [
+ [
+ "Bibor",
+ "http://www.pokewiki.de/images/5/5b/Sugimori_015.png"
+ ],
+ [
+ "Mega-Bibor",
+ "http://www.pokewiki.de/images/8/88/Sugimori_015m1.png"
+ ]
+ ],
+ "Deutsch": "Bibor",
+ "Englisch": "Beedrill",
+ "Farbe": "Gelb",
+ "Französisch": "Dardargnan",
+ "Gewicht": [
+ "29,5 kg",
+ "40,5 kg (Mega)"
+ ],
+ "Größe": [
+ "1,0 m",
+ "1,4 m (Mega)"
+ ],
+ "Japanisch": "\\u30b9\\u30d4\\u30a2\\u30fc (Spear)",
+ "Kategorie": "Giftbiene",
+ "Link": "http://pokewiki.de/Bibor",
+ "National-Dex": "#015",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gift"
+ ]
+ },
+ "016": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ad/Sugimori_016.png"
+ ]
+ ],
+ "Deutsch": "Taubsi",
+ "Englisch": "Pidgey",
+ "Farbe": "Braun",
+ "Französisch": "Roucool",
+ "Gewicht": "1,8 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30dd\\u30c3\\u30dd (Poppo)",
+ "Kategorie": "Kleinvogel",
+ "Link": "http://pokewiki.de/Taubsi",
+ "National-Dex": "#016",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "017": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/28/Sugimori_017.png"
+ ]
+ ],
+ "Deutsch": "Tauboga",
+ "Englisch": "Pidgeotto",
+ "Farbe": "Braun",
+ "Französisch": "Roucoups",
+ "Gewicht": "30,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30d4\\u30b8\\u30e7\\u30f3 (Pigeon)",
+ "Kategorie": "Vogel",
+ "Link": "http://pokewiki.de/Tauboga",
+ "National-Dex": "#017",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "018": {
+ "Bilder": [
+ [
+ "Tauboss",
+ "http://www.pokewiki.de/images/2/23/Sugimori_018.png"
+ ],
+ [
+ "Mega-Tauboss",
+ "http://www.pokewiki.de/images/4/44/Sugimori_018m1.png"
+ ]
+ ],
+ "Deutsch": "Tauboss",
+ "Englisch": "Pidgeot",
+ "Farbe": "Braun",
+ "Französisch": "Roucarnage",
+ "Gewicht": [
+ "39,5 kg",
+ "50,5 kg (Mega)"
+ ],
+ "Größe": [
+ "1,5 m",
+ "2,2 m (Mega)"
+ ],
+ "Japanisch": "\\u30d4\\u30b8\\u30e7\\u30c3\\u30c8 (Pigeot)",
+ "Kategorie": "Vogel",
+ "Link": "http://pokewiki.de/Tauboss",
+ "National-Dex": "#018",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "019": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/9/9e/Sugimori_019.png"
+ ],
+ [
+ "Alola-Rattfratz",
+ "http://www.pokewiki.de/images/b/b4/Sugimori_019a.png"
+ ]
+ ],
+ "Deutsch": "Rattfratz",
+ "Englisch": "Rattata",
+ "Farbe": [
+ "Violett",
+ "Schwarz (Alola)"
+ ],
+ "Französisch": "Rattata",
+ "Gewicht": [
+ "3,5 kg",
+ "3,8 kg (Alola)"
+ ],
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30b3\\u30e9\\u30c3\\u30bf (Koratta)",
+ "Kategorie": "Maus (Generation 1 noch \\u201eRatte\\u201c)",
+ "Link": "http://pokewiki.de/Rattfratz",
+ "National-Dex": "#019",
+ "Typ": [
+ "Normal",
+ "Unlicht",
+ "Normal (Alola)"
+ ]
+ },
+ "020": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/c/ce/Sugimori_020.png"
+ ],
+ [
+ "Alola-Rattikarl",
+ "http://www.pokewiki.de/images/c/ca/Sugimori_020a.png"
+ ]
+ ],
+ "Deutsch": "Rattikarl",
+ "Englisch": "Raticate",
+ "Farbe": [
+ "Braun",
+ "Schwarz (Alola)"
+ ],
+ "Französisch": "Rattatac",
+ "Gewicht": [
+ "18,5 kg",
+ "25,5 kg (Alola)"
+ ],
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30e9\\u30c3\\u30bf (Ratta)",
+ "Kategorie": "Maus (Generation 1 noch \\u201eRatte\\u201c)",
+ "Link": "http://pokewiki.de/Rattikarl",
+ "National-Dex": "#020",
+ "Typ": [
+ "Normal",
+ "Unlicht",
+ "Normal (Alola)"
+ ]
+ },
+ "021": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7d/Sugimori_021.png"
+ ]
+ ],
+ "Deutsch": "Habitak",
+ "Englisch": "Spearow",
+ "Farbe": "Braun",
+ "Französisch": "Piafabec",
+ "Gewicht": "2,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30aa\\u30cb\\u30b9\\u30ba\\u30e1 (Onisuzume)",
+ "Kategorie": "Kleinvogel",
+ "Link": "http://pokewiki.de/Habitak",
+ "National-Dex": "#021",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "022": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/48/Sugimori_022.png"
+ ]
+ ],
+ "Deutsch": "Ibitak",
+ "Englisch": "Fearow",
+ "Farbe": "Braun",
+ "Französisch": "Rapasdepic",
+ "Gewicht": "38,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30aa\\u30cb\\u30c9\\u30ea\\u30eb (Onidrill)",
+ "Kategorie": "Pickvogel",
+ "Link": "http://pokewiki.de/Ibitak",
+ "National-Dex": "#022",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "023": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/34/Sugimori_023.png"
+ ]
+ ],
+ "Deutsch": "Rettan",
+ "Englisch": "Ekans",
+ "Farbe": "Violett",
+ "Französisch": "Abo",
+ "Gewicht": "6,9 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30a2\\u30fc\\u30dc (Arbo)",
+ "Kategorie": "Schlange",
+ "Link": "http://pokewiki.de/Rettan",
+ "National-Dex": "#023",
+ "Typ": "Gift"
+ },
+ "024": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f4/Sugimori_024.png"
+ ]
+ ],
+ "Deutsch": "Arbok",
+ "Englisch": "Arbok",
+ "Farbe": "Violett",
+ "Französisch": "Arbok",
+ "Gewicht": "65,0 kg",
+ "Größe": "3,5 m",
+ "Japanisch": "\\u30a2\\u30fc\\u30dc\\u30c3\\u30af (Arbok)",
+ "Kategorie": "Kobra",
+ "Link": "http://pokewiki.de/Arbok",
+ "National-Dex": "#024",
+ "Typ": "Gift"
+ },
+ "025": {
+ "Bilder": [
+ [
+ "Pikachu",
+ "http://www.pokewiki.de/images/6/6c/Sugimori_025.png"
+ ]
+ ],
+ "Deutsch": "Pikachu",
+ "Englisch": "Pikachu",
+ "Farbe": "Gelb",
+ "Französisch": "Pikachu",
+ "Gewicht": "6,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30d4\\u30ab\\u30c1\\u30e5\\u30a6 (Pikachu)",
+ "Kategorie": "Maus",
+ "Link": "http://pokewiki.de/Pikachu",
+ "National-Dex": "#025",
+ "Typ": "Elektro"
+ },
+ "026": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/b/bc/Sugimori_026.png"
+ ],
+ [
+ "Alola-Raichu",
+ "http://www.pokewiki.de/images/a/ac/Sugimori_026a.png"
+ ]
+ ],
+ "Deutsch": "Raichu",
+ "Englisch": "Raichu",
+ "Farbe": [
+ "Gelb",
+ "Braun (Alola)"
+ ],
+ "Französisch": "Raichu",
+ "Gewicht": [
+ "30,0 kg",
+ "21,0 kg (Alola)"
+ ],
+ "Größe": [
+ "0,8 m",
+ "0,7 m (Alola)"
+ ],
+ "Japanisch": "\\u30e9\\u30a4\\u30c1\\u30e5\\u30a6 (Raichu)",
+ "Kategorie": "Maus",
+ "Link": "http://pokewiki.de/Raichu",
+ "National-Dex": "#026",
+ "Typ": [
+ "Elektro",
+ "Elektro",
+ "Psycho (Alola)"
+ ]
+ },
+ "027": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/e/e9/Sugimori_027.png"
+ ],
+ [
+ "Alola-Sandan",
+ "http://www.pokewiki.de/images/b/b7/Sugimori_027a.png"
+ ]
+ ],
+ "Deutsch": "Sandan",
+ "Englisch": "Sandshrew",
+ "Farbe": [
+ "Gelb",
+ "Wei\\u00df (Alola)"
+ ],
+ "Französisch": "Sabelette",
+ "Gewicht": [
+ "12,0 kg",
+ "40,0 kg (Alola)"
+ ],
+ "Größe": [
+ "0,6 m",
+ "0,7 m (Alola)"
+ ],
+ "Japanisch": "\\u30b5\\u30f3\\u30c9 (Sand)",
+ "Kategorie": "Maus",
+ "Link": "http://pokewiki.de/Sandan",
+ "National-Dex": "#027",
+ "Typ": [
+ "Boden",
+ "Eis",
+ "Stahl (Alola)"
+ ]
+ },
+ "028": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/e/ec/Sugimori_028.png"
+ ],
+ [
+ "Alola-Sandamer",
+ "http://www.pokewiki.de/images/2/23/Sugimori_028a.png"
+ ]
+ ],
+ "Deutsch": "Sandamer",
+ "Englisch": "Sandslash",
+ "Farbe": [
+ "Gelb",
+ "Blau (Alola)"
+ ],
+ "Französisch": "Sablaireau",
+ "Gewicht": [
+ "29,5 kg",
+ "55,0 kg (Alola)"
+ ],
+ "Größe": [
+ "1,0 m",
+ "1,2 m (Alola)"
+ ],
+ "Japanisch": "\\u30b5\\u30f3\\u30c9\\u30d1\\u30f3 (Sandpan)",
+ "Kategorie": "Maus",
+ "Link": "http://pokewiki.de/Sandamer",
+ "National-Dex": "#028",
+ "Typ": [
+ "Boden",
+ "Eis",
+ "Stahl (Alola)"
+ ]
+ },
+ "029": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/16/Sugimori_029.png"
+ ]
+ ],
+ "Deutsch": "Nidoran\\u2640",
+ "Englisch": "Nidoran\\u2640",
+ "Farbe": "Blau",
+ "Französisch": "Nidoran\\u2640",
+ "Gewicht": "7,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30cb\\u30c9\\u30e9\\u30f3\\u2640 (Nidoran\\u2640)",
+ "Kategorie": "Giftdorn",
+ "Link": "http://pokewiki.de/Nidoran%E2%99%80",
+ "National-Dex": "#029",
+ "Typ": "Gift"
+ },
+ "030": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1c/Sugimori_030.png"
+ ]
+ ],
+ "Deutsch": "Nidorina",
+ "Englisch": "Nidorina",
+ "Farbe": "Blau",
+ "Französisch": "Nidorina",
+ "Gewicht": "20,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30cb\\u30c9\\u30ea\\u30fc\\u30ca (Nidor\\u012bna)",
+ "Kategorie": "Giftdorn",
+ "Link": "http://pokewiki.de/Nidorina",
+ "National-Dex": "#030",
+ "Typ": "Gift"
+ },
+ "031": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2d/Sugimori_031.png"
+ ]
+ ],
+ "Deutsch": "Nidoqueen",
+ "Englisch": "Nidoqueen",
+ "Farbe": "Blau",
+ "Französisch": "Nidoqueen",
+ "Gewicht": "60,0 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30cb\\u30c9\\u30af\\u30a4\\u30f3 (Nidoqueen)",
+ "Kategorie": "Bohrer",
+ "Link": "http://pokewiki.de/Nidoqueen",
+ "National-Dex": "#031",
+ "Typ": [
+ "Gift",
+ "Boden"
+ ]
+ },
+ "032": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e3/Sugimori_032.png"
+ ]
+ ],
+ "Deutsch": "Nidoran\\u2642",
+ "Englisch": "Nidoran\\u2642",
+ "Farbe": "Violett",
+ "Französisch": "Nidoran\\u2642",
+ "Gewicht": "9,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30cb\\u30c9\\u30e9\\u30f3\\u2642 (Nidoran\\u2642)",
+ "Kategorie": "Giftdorn",
+ "Link": "http://pokewiki.de/Nidoran%E2%99%82",
+ "National-Dex": "#032",
+ "Typ": "Gift"
+ },
+ "033": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/11/Sugimori_033.png"
+ ]
+ ],
+ "Deutsch": "Nidorino",
+ "Englisch": "Nidorino",
+ "Farbe": "Violett",
+ "Französisch": "Nidorino",
+ "Gewicht": "19,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30cb\\u30c9\\u30ea\\u30fc\\u30ce (Nidorino)",
+ "Kategorie": "Giftdorn",
+ "Link": "http://pokewiki.de/Nidorino",
+ "National-Dex": "#033",
+ "Typ": "Gift"
+ },
+ "034": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5d/Sugimori_034.png"
+ ]
+ ],
+ "Deutsch": "Nidoking",
+ "Englisch": "Nidoking",
+ "Farbe": "Violett",
+ "Französisch": "Nidoking",
+ "Gewicht": "62,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30cb\\u30c9\\u30ad\\u30f3\\u30b0 (Nidoking)",
+ "Kategorie": "Bohrer",
+ "Link": "http://pokewiki.de/Nidoking",
+ "National-Dex": "#034",
+ "Typ": [
+ "Gift",
+ "Boden"
+ ]
+ },
+ "035": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3c/Sugimori_035.png"
+ ]
+ ],
+ "Deutsch": "Piepi",
+ "Englisch": "Clefairy",
+ "Farbe": "Rosa",
+ "Französisch": "M\\u00e9lof\\u00e9e",
+ "Gewicht": "7,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30d4\\u30c3\\u30d4 (Pippi)",
+ "Kategorie": "Fee",
+ "Link": "http://pokewiki.de/Piepi",
+ "National-Dex": "#035",
+ "Typ": "Fee"
+ },
+ "036": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/32/Sugimori_036.png"
+ ]
+ ],
+ "Deutsch": "Pixi",
+ "Englisch": "Clefable",
+ "Farbe": "Rosa",
+ "Französisch": "M\\u00e9lodelfe",
+ "Gewicht": "40,0 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30d4\\u30af\\u30b7\\u30fc (Pixy)",
+ "Kategorie": "Fee",
+ "Link": "http://pokewiki.de/Pixi",
+ "National-Dex": "#036",
+ "Typ": "Fee"
+ },
+ "037": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/2/26/Sugimori_037.png"
+ ],
+ [
+ "Alola-Vulpix",
+ "http://www.pokewiki.de/images/9/9d/Sugimori_037a.png"
+ ]
+ ],
+ "Deutsch": "Vulpix",
+ "Englisch": "Vulpix",
+ "Farbe": [
+ "Braun",
+ "Wei\\u00df (Alola)"
+ ],
+ "Französisch": "Goupix",
+ "Gewicht": "9,9 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30ed\\u30b3\\u30f3 (Rokon)",
+ "Kategorie": "Fuchs",
+ "Link": "http://pokewiki.de/Vulpix",
+ "National-Dex": "#037",
+ "Typ": [
+ "Feuer",
+ "Eis (Alola)"
+ ]
+ },
+ "038": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/a/a5/Sugimori_038.png"
+ ],
+ [
+ "Alola-Vulnona",
+ "http://www.pokewiki.de/images/c/c4/Sugimori_038a.png"
+ ]
+ ],
+ "Deutsch": "Vulnona",
+ "Englisch": "Ninetales",
+ "Farbe": [
+ "Gelb",
+ "Blau (Alola)"
+ ],
+ "Französisch": "Feunard",
+ "Gewicht": "19,9 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30ad\\u30e5\\u30a6\\u30b3\\u30f3 (Kyukon)",
+ "Kategorie": "Fuchs",
+ "Link": "http://pokewiki.de/Vulnona",
+ "National-Dex": "#038",
+ "Typ": [
+ "Feuer",
+ "Eis",
+ "Fee (Alola)"
+ ]
+ },
+ "039": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/9b/Sugimori_039.png"
+ ]
+ ],
+ "Deutsch": "Pummeluff",
+ "Englisch": "Jigglypuff",
+ "Farbe": "Rosa",
+ "Französisch": "Rondoudou",
+ "Gewicht": "5,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30d7\\u30ea\\u30f3 (Purin)",
+ "Kategorie": "Ballon",
+ "Link": "http://pokewiki.de/Pummeluff",
+ "National-Dex": "#039",
+ "Typ": [
+ "Normal",
+ "Fee"
+ ]
+ },
+ "040": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b7/Sugimori_040.png"
+ ]
+ ],
+ "Deutsch": "Knuddeluff",
+ "Englisch": "Wigglytuff",
+ "Farbe": "Rosa",
+ "Französisch": "Grodoudou",
+ "Gewicht": "12,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30d7\\u30af\\u30ea\\u30f3 (Pukurin)",
+ "Kategorie": "Ballon",
+ "Link": "http://pokewiki.de/Knuddeluff",
+ "National-Dex": "#040",
+ "Typ": [
+ "Normal",
+ "Fee"
+ ]
+ },
+ "041": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d5/Sugimori_041.png"
+ ]
+ ],
+ "Deutsch": "Zubat",
+ "Englisch": "Zubat",
+ "Farbe": "Violett",
+ "Französisch": "Nosf\\u00e9rapti",
+ "Gewicht": "7,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30ba\\u30d0\\u30c3\\u30c8 (Zubat)",
+ "Kategorie": "Fledermaus",
+ "Link": "http://pokewiki.de/Zubat",
+ "National-Dex": "#041",
+ "Typ": [
+ "Gift",
+ "Flug"
+ ]
+ },
+ "042": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/76/Sugimori_042.png"
+ ]
+ ],
+ "Deutsch": "Golbat",
+ "Englisch": "Golbat",
+ "Farbe": "Violett",
+ "Französisch": "Nosf\\u00e9ralto",
+ "Gewicht": "55,0 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30b4\\u30eb\\u30d0\\u30c3\\u30c8 (Golbat)",
+ "Kategorie": "Fledermaus",
+ "Link": "http://pokewiki.de/Golbat",
+ "National-Dex": "#042",
+ "Typ": [
+ "Gift",
+ "Flug"
+ ]
+ },
+ "043": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2e/Sugimori_043.png"
+ ]
+ ],
+ "Deutsch": "Myrapla",
+ "Englisch": "Oddish",
+ "Farbe": "Blau",
+ "Französisch": "Mystherbe",
+ "Gewicht": "5,4 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30ca\\u30be\\u30ce\\u30af\\u30b5 (Nazonokusa)",
+ "Kategorie": "Unkraut",
+ "Link": "http://pokewiki.de/Myrapla",
+ "National-Dex": "#043",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "044": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/be/Sugimori_044.png"
+ ]
+ ],
+ "Deutsch": "Duflor",
+ "Englisch": "Gloom",
+ "Farbe": "Blau",
+ "Französisch": "Ortide",
+ "Gewicht": "8,6 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30af\\u30b5\\u30a4\\u30cf\\u30ca (Kusaihana)",
+ "Kategorie": "Unkraut",
+ "Link": "http://pokewiki.de/Duflor",
+ "National-Dex": "#044",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "045": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a4/Sugimori_045.png"
+ ]
+ ],
+ "Deutsch": "Giflor",
+ "Englisch": "Vileplume",
+ "Farbe": "Rot",
+ "Französisch": "Raffl\\u00e9sia",
+ "Gewicht": "18,6 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30e9\\u30d5\\u30ec\\u30b7\\u30a2 (Ruffresia)",
+ "Kategorie": "Blume",
+ "Link": "http://pokewiki.de/Giflor",
+ "National-Dex": "#045",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "046": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8c/Sugimori_046.png"
+ ]
+ ],
+ "Deutsch": "Paras",
+ "Englisch": "Paras",
+ "Farbe": "Rot",
+ "Französisch": "Paras",
+ "Gewicht": "5,4 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30d1\\u30e9\\u30b9 (Paras)",
+ "Kategorie": "Pilz",
+ "Link": "http://pokewiki.de/Paras",
+ "National-Dex": "#046",
+ "Typ": [
+ "K\\u00e4fer",
+ "Pflanze"
+ ]
+ },
+ "047": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/da/Sugimori_047.png"
+ ]
+ ],
+ "Deutsch": "Parasek",
+ "Englisch": "Parasect",
+ "Farbe": "Rot",
+ "Französisch": "Parasect",
+ "Gewicht": "29,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30d1\\u30e9\\u30bb\\u30af\\u30c8 (Parasect)",
+ "Kategorie": "Pilz",
+ "Link": "http://pokewiki.de/Parasek",
+ "National-Dex": "#047",
+ "Typ": [
+ "K\\u00e4fer",
+ "Pflanze"
+ ]
+ },
+ "048": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1b/Sugimori_048.png"
+ ]
+ ],
+ "Deutsch": "Bluzuk",
+ "Englisch": "Venonat",
+ "Farbe": "Violett",
+ "Französisch": "Mimitoss",
+ "Gewicht": "30,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b3\\u30f3\\u30d1\\u30f3 (Kongpang)",
+ "Kategorie": "Insekt",
+ "Link": "http://pokewiki.de/Bluzuk",
+ "National-Dex": "#048",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gift"
+ ]
+ },
+ "049": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/48/Sugimori_049.png"
+ ]
+ ],
+ "Deutsch": "Omot",
+ "Englisch": "Venomoth",
+ "Farbe": "Violett",
+ "Französisch": "A\\u00e9romite",
+ "Gewicht": "12,5 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30e2\\u30eb\\u30d5\\u30a9\\u30f3 (Morphon)",
+ "Kategorie": "Giftmotte",
+ "Link": "http://pokewiki.de/Omot",
+ "National-Dex": "#049",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gift"
+ ]
+ },
+ "050": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/7/73/Sugimori_050.png"
+ ],
+ [
+ "Alola-Digda",
+ "http://www.pokewiki.de/images/0/02/Sugimori_050a.png"
+ ]
+ ],
+ "Deutsch": "Digda",
+ "Englisch": "Diglett",
+ "Farbe": "Braun",
+ "Französisch": "Taupiqueur",
+ "Gewicht": [
+ "0,8 kg",
+ "1,0 kg (Alola)"
+ ],
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30c7\\u30a3\\u30b0\\u30c0 (Digda)",
+ "Kategorie": "Maulwurf",
+ "Link": "http://pokewiki.de/Digda",
+ "National-Dex": "#050",
+ "Typ": [
+ "Boden",
+ "Boden",
+ "Stahl (Alola)"
+ ]
+ },
+ "051": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/9/93/Sugimori_051.png"
+ ],
+ [
+ "Alola-Digdri",
+ "http://www.pokewiki.de/images/0/0d/Sugimori_051a.png"
+ ]
+ ],
+ "Deutsch": "Digdri",
+ "Englisch": "Dugtrio",
+ "Farbe": "Braun",
+ "Französisch": "Triopikeur",
+ "Gewicht": [
+ "33,3 kg",
+ "66,6 kg (Alola)"
+ ],
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30c0\\u30b0\\u30c8\\u30ea\\u30aa (Dugtrio)",
+ "Kategorie": "Maulwurf",
+ "Link": "http://pokewiki.de/Digdri",
+ "National-Dex": "#051",
+ "Typ": [
+ "Boden",
+ "Boden",
+ "Stahl (Alola)"
+ ]
+ },
+ "052": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/0/04/Sugimori_052.png"
+ ],
+ [
+ "Alola-Mauzi",
+ "http://www.pokewiki.de/images/b/b6/Sugimori_052a.png"
+ ]
+ ],
+ "Deutsch": "Mauzi",
+ "Englisch": "Meowth",
+ "Farbe": [
+ "Gelb",
+ "Blau (Alola)"
+ ],
+ "Französisch": "Miaouss",
+ "Gewicht": "4,2 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30cb\\u30e3\\u30fc\\u30b9 (Nyarth)",
+ "Kategorie": "Katze",
+ "Link": "http://pokewiki.de/Mauzi",
+ "National-Dex": "#052",
+ "Typ": [
+ "Normal",
+ "Unlicht (Alola)"
+ ]
+ },
+ "053": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/8/86/Sugimori_053.png"
+ ],
+ [
+ "Alola-Snobilikat",
+ "http://www.pokewiki.de/images/b/bd/Sugimori_053a.png"
+ ]
+ ],
+ "Deutsch": "Snobilikat",
+ "Englisch": "Persian",
+ "Farbe": [
+ "Gelb",
+ "Blau (Alola)"
+ ],
+ "Französisch": "Persian",
+ "Gewicht": [
+ "32,0 kg",
+ "33,0 kg (Alola)"
+ ],
+ "Größe": [
+ "1,0 m",
+ "1,1 m (Alola)"
+ ],
+ "Japanisch": "\\u30da\\u30eb\\u30b7\\u30a2\\u30f3 (Persian)",
+ "Kategorie": "Rassekatze",
+ "Link": "http://pokewiki.de/Snobilikat",
+ "National-Dex": "#053",
+ "Typ": [
+ "Normal",
+ "Unlicht (Alola)"
+ ]
+ },
+ "054": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c3/Sugimori_054.png"
+ ]
+ ],
+ "Deutsch": "Enton",
+ "Englisch": "Psyduck",
+ "Farbe": "Gelb",
+ "Französisch": "Psykokwak",
+ "Gewicht": "19,6 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30b3\\u30c0\\u30c3\\u30af (Koduck)",
+ "Kategorie": "Ente",
+ "Link": "http://pokewiki.de/Enton",
+ "National-Dex": "#054",
+ "Typ": "Wasser"
+ },
+ "055": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5f/Sugimori_055.png"
+ ]
+ ],
+ "Deutsch": "Entoron",
+ "Englisch": "Golduck",
+ "Farbe": "Blau",
+ "Französisch": "Akwakwak",
+ "Gewicht": "76,6 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30b4\\u30eb\\u30c0\\u30c3\\u30af (Golduck)",
+ "Kategorie": "Ente",
+ "Link": "http://pokewiki.de/Entoron",
+ "National-Dex": "#055",
+ "Typ": "Wasser"
+ },
+ "056": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b4/Sugimori_056.png"
+ ]
+ ],
+ "Deutsch": "Menki",
+ "Englisch": "Mankey",
+ "Farbe": "Braun",
+ "Französisch": "F\\u00e9rosinge",
+ "Gewicht": "28,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30de\\u30f3\\u30ad\\u30fc (Mankey)",
+ "Kategorie": "Schwein / Affe",
+ "Link": "http://pokewiki.de/Menki",
+ "National-Dex": "#056",
+ "Typ": "Kampf"
+ },
+ "057": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1c/Sugimori_057.png"
+ ]
+ ],
+ "Deutsch": "Rasaff",
+ "Englisch": "Primeape",
+ "Farbe": "Braun",
+ "Französisch": "Colossinge",
+ "Gewicht": "32,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30aa\\u30b3\\u30ea\\u30b6\\u30eb (Okorizaru)",
+ "Kategorie": "Schwein / Affe",
+ "Link": "http://pokewiki.de/Rasaff",
+ "National-Dex": "#057",
+ "Typ": "Kampf"
+ },
+ "058": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fc/Sugimori_058.png"
+ ]
+ ],
+ "Deutsch": "Fukano",
+ "Englisch": "Growlithe",
+ "Farbe": "Braun",
+ "Französisch": "Caninos",
+ "Gewicht": "19,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30ac\\u30fc\\u30c7\\u30a3 (Gardie)",
+ "Kategorie": "Hund",
+ "Link": "http://pokewiki.de/Fukano",
+ "National-Dex": "#058",
+ "Typ": "Feuer"
+ },
+ "059": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f6/Sugimori_059.png"
+ ]
+ ],
+ "Deutsch": "Arkani",
+ "Englisch": "Arcanine",
+ "Farbe": "Braun",
+ "Französisch": "Arcanin",
+ "Gewicht": "155,0 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30a6\\u30a4\\u30f3\\u30c7\\u30a3 (Windie)",
+ "Kategorie": "Legend\\u00e4r",
+ "Link": "http://pokewiki.de/Arkani",
+ "National-Dex": "#059",
+ "Typ": "Feuer"
+ },
+ "060": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d2/Sugimori_060.png"
+ ]
+ ],
+ "Deutsch": "Quapsel",
+ "Englisch": "Poliwag",
+ "Farbe": "Blau",
+ "Französisch": "Ptitard",
+ "Gewicht": "12,4 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30cb\\u30e7\\u30ed\\u30e2 (Nyoromo)",
+ "Kategorie": "Kaulquappe",
+ "Link": "http://pokewiki.de/Quapsel",
+ "National-Dex": "#060",
+ "Typ": "Wasser"
+ },
+ "061": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c1/Sugimori_061.png"
+ ]
+ ],
+ "Deutsch": "Quaputzi",
+ "Englisch": "Poliwhirl",
+ "Farbe": "Blau",
+ "Französisch": "T\\u00e9tarte",
+ "Gewicht": "20,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30cb\\u30e7\\u30ed\\u30be (Nyorozo)",
+ "Kategorie": "Kaulquappe",
+ "Link": "http://pokewiki.de/Quaputzi",
+ "National-Dex": "#061",
+ "Typ": "Wasser"
+ },
+ "062": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a9/Sugimori_062.png"
+ ]
+ ],
+ "Deutsch": "Quappo",
+ "Englisch": "Poliwrath",
+ "Farbe": "Blau",
+ "Französisch": "Tartard",
+ "Gewicht": "54,0 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30cb\\u30e7\\u30ed\\u30dc\\u30f3 (Nyorobon)",
+ "Kategorie": "Kaulquappe",
+ "Link": "http://pokewiki.de/Quappo",
+ "National-Dex": "#062",
+ "Typ": [
+ "Wasser",
+ "Kampf"
+ ]
+ },
+ "063": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/44/Sugimori_063.png"
+ ]
+ ],
+ "Deutsch": "Abra",
+ "Englisch": "Abra",
+ "Farbe": "Braun",
+ "Französisch": "Abra",
+ "Gewicht": "19,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30b1\\u30fc\\u30b7\\u30a3 (Casey)",
+ "Kategorie": "Psi",
+ "Link": "http://pokewiki.de/Abra",
+ "National-Dex": "#063",
+ "Typ": "Psycho"
+ },
+ "064": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/cd/Sugimori_064.png"
+ ]
+ ],
+ "Deutsch": "Kadabra",
+ "Englisch": "Kadabra",
+ "Farbe": "Braun",
+ "Französisch": "Kadabra",
+ "Gewicht": "56,5 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30e6\\u30f3\\u30b2\\u30e9\\u30fc (Yungeraa)",
+ "Kategorie": "Psi",
+ "Link": "http://pokewiki.de/Kadabra",
+ "National-Dex": "#064",
+ "Typ": "Psycho"
+ },
+ "065": {
+ "Bilder": [
+ [
+ "Simsala",
+ "http://www.pokewiki.de/images/e/ea/Sugimori_065.png"
+ ],
+ [
+ "Mega-Simsala",
+ "http://www.pokewiki.de/images/0/0c/Sugimori_065m1.png"
+ ]
+ ],
+ "Deutsch": "Simsala",
+ "Englisch": "Alakazam",
+ "Farbe": "Braun",
+ "Französisch": "Alakazam",
+ "Gewicht": "48,0 kg",
+ "Größe": [
+ "1,5 m",
+ "1,2 m (Mega)"
+ ],
+ "Japanisch": "\\u30d5\\u30fc\\u30c7\\u30a3\\u30f3 (Foodin)",
+ "Kategorie": "Psi",
+ "Link": "http://pokewiki.de/Simsala",
+ "National-Dex": "#065",
+ "Typ": "Psycho"
+ },
+ "066": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/44/Sugimori_066.png"
+ ]
+ ],
+ "Deutsch": "Machollo",
+ "Englisch": "Machop",
+ "Farbe": "Grau",
+ "Französisch": "Machoc",
+ "Gewicht": "19,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30ef\\u30f3\\u30ea\\u30ad\\u30fc (Wanriky)",
+ "Kategorie": "Kraftprotz",
+ "Link": "http://pokewiki.de/Machollo",
+ "National-Dex": "#066",
+ "Typ": "Kampf"
+ },
+ "067": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5b/Sugimori_067.png"
+ ]
+ ],
+ "Deutsch": "Maschock",
+ "Englisch": "Machoke",
+ "Farbe": "Grau",
+ "Französisch": "Machopeur",
+ "Gewicht": "70,5 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30b4\\u30fc\\u30ea\\u30ad\\u30fc (Goriky)",
+ "Kategorie": "Kraftprotz",
+ "Link": "http://pokewiki.de/Maschock",
+ "National-Dex": "#067",
+ "Typ": "Kampf"
+ },
+ "068": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/65/Sugimori_068.png"
+ ]
+ ],
+ "Deutsch": "Machomei",
+ "Englisch": "Machamp",
+ "Farbe": "Grau",
+ "Französisch": "Mackogneur",
+ "Gewicht": "130,0 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30ab\\u30a4\\u30ea\\u30ad\\u30fc (Kairiky)",
+ "Kategorie": "Kraftprotz",
+ "Link": "http://pokewiki.de/Machomei",
+ "National-Dex": "#068",
+ "Typ": "Kampf"
+ },
+ "069": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5d/Sugimori_069.png"
+ ]
+ ],
+ "Deutsch": "Knofensa",
+ "Englisch": "Bellsprout",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Ch\\u00e9tiflor",
+ "Gewicht": "4,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30de\\u30c0\\u30c4\\u30dc\\u30df (Madatsubomi)",
+ "Kategorie": "Blume",
+ "Link": "http://pokewiki.de/Knofensa",
+ "National-Dex": "#069",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "070": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/66/Sugimori_070.png"
+ ]
+ ],
+ "Deutsch": "Ultrigaria",
+ "Englisch": "Weepinbell",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Boustiflor",
+ "Gewicht": "6,4 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30a6\\u30c4\\u30c9\\u30f3 (Utsudon)",
+ "Kategorie": "Fliegentod",
+ "Link": "http://pokewiki.de/Ultrigaria",
+ "National-Dex": "#070",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "071": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c8/Sugimori_071.png"
+ ]
+ ],
+ "Deutsch": "Sarzenia",
+ "Englisch": "Victreebel",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Empiflor",
+ "Gewicht": "15,5 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30a6\\u30c4\\u30dc\\u30c3\\u30c8 (Utsubot)",
+ "Kategorie": "Fliegentod",
+ "Link": "http://pokewiki.de/Sarzenia",
+ "National-Dex": "#071",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "072": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c4/Sugimori_072.png"
+ ]
+ ],
+ "Deutsch": "Tentacha",
+ "Englisch": "Tentacool",
+ "Farbe": "Blau",
+ "Französisch": "Tentacool",
+ "Gewicht": "45,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30e1\\u30ce\\u30af\\u30e9\\u30b2 (Menokurage)",
+ "Kategorie": "Qualle",
+ "Link": "http://pokewiki.de/Tentacha",
+ "National-Dex": "#072",
+ "Typ": [
+ "Wasser",
+ "Gift"
+ ]
+ },
+ "073": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1f/Sugimori_073.png"
+ ]
+ ],
+ "Deutsch": "Tentoxa",
+ "Englisch": "Tentacruel",
+ "Farbe": "Blau",
+ "Französisch": "Tentacruel",
+ "Gewicht": "55,0 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30c9\\u30af\\u30af\\u30e9\\u30b2 (Dokukurage)",
+ "Kategorie": "Qualle",
+ "Link": "http://pokewiki.de/Tentoxa",
+ "National-Dex": "#073",
+ "Typ": [
+ "Wasser",
+ "Gift"
+ ]
+ },
+ "074": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/3/3a/Sugimori_074.png"
+ ],
+ [
+ "Alola-Kleinstein",
+ "http://www.pokewiki.de/images/f/f6/Sugimori_074a.png"
+ ]
+ ],
+ "Deutsch": "Kleinstein",
+ "Englisch": "Geodude",
+ "Farbe": [
+ "Braun",
+ "Grau (Alola)"
+ ],
+ "Französisch": "Racaillou",
+ "Gewicht": [
+ "20,0 kg",
+ "20,3 kg (Alola)"
+ ],
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30a4\\u30b7\\u30c4\\u30d6\\u30c6 (Ishitsubute)",
+ "Kategorie": "Gestein",
+ "Link": "http://pokewiki.de/Kleinstein",
+ "National-Dex": "#074",
+ "Typ": [
+ "Gestein",
+ "Boden",
+ "Gestein",
+ "Elektro (Alola)"
+ ]
+ },
+ "075": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/4/41/Sugimori_075.png"
+ ],
+ [
+ "Alola-Georok",
+ "http://www.pokewiki.de/images/0/09/Sugimori_075a.png"
+ ]
+ ],
+ "Deutsch": "Georok",
+ "Englisch": "Graveler",
+ "Farbe": [
+ "Braun",
+ "Grau (Alola)"
+ ],
+ "Französisch": "Gravalanch",
+ "Gewicht": [
+ "105,0 kg",
+ "110,0 kg (Alola)"
+ ],
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b4\\u30ed\\u30fc\\u30f3 (Golone)",
+ "Kategorie": "Gestein",
+ "Link": "http://pokewiki.de/Georok",
+ "National-Dex": "#075",
+ "Typ": [
+ "Gestein",
+ "Boden",
+ "Gestein",
+ "Elektro (Alola)"
+ ]
+ },
+ "076": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/d/dc/Sugimori_076.png"
+ ],
+ [
+ "Alola-Geowaz",
+ "http://www.pokewiki.de/images/9/9c/Sugimori_076a.png"
+ ]
+ ],
+ "Deutsch": "Geowaz",
+ "Englisch": "Golem",
+ "Farbe": [
+ "Braun",
+ "Grau (Alola)"
+ ],
+ "Französisch": "Grolem",
+ "Gewicht": [
+ "300,0 kg",
+ "316,0 kg (Alola)"
+ ],
+ "Größe": [
+ "1,4 m",
+ "1,7 m (Alola)"
+ ],
+ "Japanisch": "\\u30b4\\u30ed\\u30fc\\u30cb\\u30e3 (Golonya)",
+ "Kategorie": "Urgestein",
+ "Link": "http://pokewiki.de/Geowaz",
+ "National-Dex": "#076",
+ "Typ": [
+ "Gestein",
+ "Boden",
+ "Gestein",
+ "Elektro (Alola)"
+ ]
+ },
+ "077": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/01/Sugimori_077.png"
+ ]
+ ],
+ "Deutsch": "Ponita",
+ "Englisch": "Ponyta",
+ "Farbe": "Gelb",
+ "Französisch": "Ponyta",
+ "Gewicht": "30,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30dd\\u30cb\\u30fc\\u30bf (Ponita)",
+ "Kategorie": "Feuerpferd",
+ "Link": "http://pokewiki.de/Ponita",
+ "National-Dex": "#077",
+ "Typ": "Feuer"
+ },
+ "078": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2e/Sugimori_078.png"
+ ]
+ ],
+ "Deutsch": "Gallopa",
+ "Englisch": "Rapidash",
+ "Farbe": "Gelb",
+ "Französisch": "Galopa",
+ "Gewicht": "95,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30ae\\u30e3\\u30ed\\u30c3\\u30d7 (Gallop)",
+ "Kategorie": "Feuerpferd",
+ "Link": "http://pokewiki.de/Gallopa",
+ "National-Dex": "#078",
+ "Typ": "Feuer"
+ },
+ "079": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5d/Sugimori_079.png"
+ ]
+ ],
+ "Deutsch": "Flegmon",
+ "Englisch": "Slowpoke",
+ "Farbe": "Rosa",
+ "Französisch": "Ramoloss",
+ "Gewicht": "36,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30e4\\u30c9\\u30f3 (Yadon)",
+ "Kategorie": "Schnarcher",
+ "Link": "http://pokewiki.de/Flegmon",
+ "National-Dex": "#079",
+ "Typ": [
+ "Wasser",
+ "Psycho"
+ ]
+ },
+ "080": {
+ "Bilder": [
+ [
+ "Lahmus",
+ "http://www.pokewiki.de/images/2/25/Sugimori_080.png"
+ ],
+ [
+ "Mega-Lahmus",
+ "http://www.pokewiki.de/images/a/ae/Sugimori_080m1.png"
+ ]
+ ],
+ "Deutsch": "Lahmus",
+ "Englisch": "Slowbro",
+ "Farbe": "Rosa",
+ "Französisch": "Flagadoss",
+ "Gewicht": [
+ "78,5 kg",
+ "120,0 kg (Mega)"
+ ],
+ "Größe": [
+ "1,6 m",
+ "2,0 m (Mega)"
+ ],
+ "Japanisch": "\\u30e4\\u30c9\\u30e9\\u30f3 (Yadoran)",
+ "Kategorie": "Symbiose",
+ "Link": "http://pokewiki.de/Lahmus",
+ "National-Dex": "#080",
+ "Typ": [
+ "Wasser",
+ "Psycho"
+ ]
+ },
+ "081": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/08/Sugimori_081.png"
+ ]
+ ],
+ "Deutsch": "Magnetilo",
+ "Englisch": "Magnemite",
+ "Farbe": "Grau",
+ "Französisch": "Magn\\u00e9ti",
+ "Gewicht": "6,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30b3\\u30a4\\u30eb (Coil)",
+ "Kategorie": "Magnet",
+ "Link": "http://pokewiki.de/Magnetilo",
+ "National-Dex": "#081",
+ "Typ": [
+ "Elektro",
+ "Stahl"
+ ]
+ },
+ "082": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/69/Sugimori_082.png"
+ ]
+ ],
+ "Deutsch": "Magneton",
+ "Englisch": "Magneton",
+ "Farbe": "Grau",
+ "Französisch": "Magn\\u00e9ton",
+ "Gewicht": "60,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30ec\\u30a2\\u30b3\\u30a4\\u30eb (Rarecoil)",
+ "Kategorie": "Magnet",
+ "Link": "http://pokewiki.de/Magneton",
+ "National-Dex": "#082",
+ "Typ": [
+ "Elektro",
+ "Stahl"
+ ]
+ },
+ "083": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ad/Sugimori_083.png"
+ ]
+ ],
+ "Deutsch": "Porenta",
+ "Englisch": "Farfetch\\u2019d",
+ "Farbe": "Braun",
+ "Französisch": "Canarticho",
+ "Gewicht": "15,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30ab\\u30e2\\u30cd\\u30ae (Kamonegi)",
+ "Kategorie": "Wildente",
+ "Link": "http://pokewiki.de/Porenta",
+ "National-Dex": "#083",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "084": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e4/Sugimori_084.png"
+ ]
+ ],
+ "Deutsch": "Dodu",
+ "Englisch": "Doduo",
+ "Farbe": "Braun",
+ "Französisch": "Doduo",
+ "Gewicht": "39,2 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30c9\\u30fc\\u30c9\\u30fc (Dodo)",
+ "Kategorie": "Duovogel",
+ "Link": "http://pokewiki.de/Dodu",
+ "National-Dex": "#084",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "085": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f4/Sugimori_085.png"
+ ]
+ ],
+ "Deutsch": "Dodri",
+ "Englisch": "Dodrio",
+ "Farbe": "Braun",
+ "Französisch": "Dodrio",
+ "Gewicht": "85,2 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30c9\\u30fc\\u30c9\\u30ea\\u30aa (Dodorio)",
+ "Kategorie": "Trivogel",
+ "Link": "http://pokewiki.de/Dodri",
+ "National-Dex": "#085",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "086": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/cc/Sugimori_086.png"
+ ]
+ ],
+ "Deutsch": "Jurob",
+ "Englisch": "Seel",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Otaria",
+ "Gewicht": "90,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30d1\\u30a6\\u30ef\\u30a6 (Pawou)",
+ "Kategorie": "Seehund",
+ "Link": "http://pokewiki.de/Jurob",
+ "National-Dex": "#086",
+ "Typ": "Wasser"
+ },
+ "087": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/07/Sugimori_087.png"
+ ]
+ ],
+ "Deutsch": "Jugong",
+ "Englisch": "Dewgong",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Lamantine",
+ "Gewicht": "120,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30b8\\u30e5\\u30b4\\u30f3 (Jugon)",
+ "Kategorie": "Seehund",
+ "Link": "http://pokewiki.de/Jugong",
+ "National-Dex": "#087",
+ "Typ": [
+ "Wasser",
+ "Eis"
+ ]
+ },
+ "088": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/b/b2/Sugimori_088.png"
+ ],
+ [
+ "Alola-Sleima",
+ "http://www.pokewiki.de/images/0/01/Sugimori_088a.png"
+ ]
+ ],
+ "Deutsch": "Sleima",
+ "Englisch": "Grimer",
+ "Farbe": [
+ "Violett",
+ "Gr\\u00fcn (Alola)"
+ ],
+ "Französisch": "Tadmorv",
+ "Gewicht": [
+ "30,0 kg",
+ "42,0 kg (Alola)"
+ ],
+ "Größe": [
+ "0,9 m",
+ "0,7 m (Alola)"
+ ],
+ "Japanisch": "\\u30d9\\u30c8\\u30d9\\u30bf\\u30fc (Betbeter)",
+ "Kategorie": "Schlamm",
+ "Link": "http://pokewiki.de/Sleima",
+ "National-Dex": "#088",
+ "Typ": [
+ "Gift",
+ "Gift",
+ "Unlicht (Alola)"
+ ]
+ },
+ "089": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/5/5c/Sugimori_089.png"
+ ],
+ [
+ "Alola-Sleimok",
+ "http://www.pokewiki.de/images/c/c1/Sugimori_089a.png"
+ ]
+ ],
+ "Deutsch": "Sleimok",
+ "Englisch": "Muk",
+ "Farbe": [
+ "Violett",
+ "Gr\\u00fcn (Alola)"
+ ],
+ "Französisch": "Grotadmorv",
+ "Gewicht": [
+ "30,0 kg",
+ "52,0 kg (Alola)"
+ ],
+ "Größe": [
+ "1,2 m",
+ "1,0 m (Alola)"
+ ],
+ "Japanisch": "\\u30d9\\u30c8\\u30d9\\u30c8\\u30f3 (Betbeton)",
+ "Kategorie": "Schlamm",
+ "Link": "http://pokewiki.de/Sleimok",
+ "National-Dex": "#089",
+ "Typ": [
+ "Gift",
+ "Gift",
+ "Unlicht (Alola)"
+ ]
+ },
+ "090": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/15/Sugimori_090.png"
+ ]
+ ],
+ "Deutsch": "Muschas",
+ "Englisch": "Shellder",
+ "Farbe": "Violett",
+ "Französisch": "Kokiyas",
+ "Gewicht": "4,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30b7\\u30a7\\u30eb\\u30c0\\u30fc (Shellder)",
+ "Kategorie": "Muschel",
+ "Link": "http://pokewiki.de/Muschas",
+ "National-Dex": "#090",
+ "Typ": "Wasser"
+ },
+ "091": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a2/Sugimori_091.png"
+ ]
+ ],
+ "Deutsch": "Austos",
+ "Englisch": "Cloyster",
+ "Farbe": "Violett",
+ "Französisch": "Crustabri",
+ "Gewicht": "132,5 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30d1\\u30eb\\u30b7\\u30a7\\u30f3 (Parshen)",
+ "Kategorie": "Muschel",
+ "Link": "http://pokewiki.de/Austos",
+ "National-Dex": "#091",
+ "Typ": [
+ "Wasser",
+ "Eis"
+ ]
+ },
+ "092": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8f/Sugimori_092.png"
+ ]
+ ],
+ "Deutsch": "Nebulak",
+ "Englisch": "Gastly",
+ "Farbe": "Violett",
+ "Französisch": "Fantominus",
+ "Gewicht": "0,1 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30b4\\u30fc\\u30b9 (Ghos)",
+ "Kategorie": "Gas",
+ "Link": "http://pokewiki.de/Nebulak",
+ "National-Dex": "#092",
+ "Typ": [
+ "Geist",
+ "Gift"
+ ]
+ },
+ "093": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/08/Sugimori_093.png"
+ ]
+ ],
+ "Deutsch": "Alpollo",
+ "Englisch": "Haunter",
+ "Farbe": "Violett",
+ "Französisch": "Spectrum",
+ "Gewicht": "0,1 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30b4\\u30fc\\u30b9\\u30c8 (Ghost)",
+ "Kategorie": "Gas",
+ "Link": "http://pokewiki.de/Alpollo",
+ "National-Dex": "#093",
+ "Typ": [
+ "Geist",
+ "Gift"
+ ]
+ },
+ "094": {
+ "Bilder": [
+ [
+ "Gengar",
+ "http://www.pokewiki.de/images/f/f3/Sugimori_094.png"
+ ],
+ [
+ "Mega-Gengar",
+ "http://www.pokewiki.de/images/2/2b/Sugimori_094m1.png"
+ ]
+ ],
+ "Deutsch": "Gengar",
+ "Englisch": "Gengar",
+ "Farbe": "Violett",
+ "Französisch": "Ectoplasma",
+ "Gewicht": "40,5 kg",
+ "Größe": [
+ "1,5 m",
+ "1,4 m (Mega)"
+ ],
+ "Japanisch": "\\u30b2\\u30f3\\u30ac\\u30fc (Gangar)",
+ "Kategorie": "Schatten",
+ "Link": "http://pokewiki.de/Gengar",
+ "National-Dex": "#094",
+ "Typ": [
+ "Geist",
+ "Gift"
+ ]
+ },
+ "095": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/78/Sugimori_095.png"
+ ]
+ ],
+ "Deutsch": "Onix",
+ "Englisch": "Onix",
+ "Farbe": "Grau",
+ "Französisch": "Onix",
+ "Gewicht": "210,0 kg",
+ "Größe": "8,8 m",
+ "Japanisch": "\\u30a4\\u30ef\\u30fc\\u30af (Iwark)",
+ "Kategorie": "Felsnatter",
+ "Link": "http://pokewiki.de/Onix",
+ "National-Dex": "#095",
+ "Typ": [
+ "Gestein",
+ "Boden"
+ ]
+ },
+ "096": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c0/Sugimori_096.png"
+ ]
+ ],
+ "Deutsch": "Traumato",
+ "Englisch": "Drowzee",
+ "Farbe": "Gelb",
+ "Französisch": "Soporifik",
+ "Gewicht": "32,4 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b9\\u30ea\\u30fc\\u30d7 (Sleepe)",
+ "Kategorie": "Hypnose",
+ "Link": "http://pokewiki.de/Traumato",
+ "National-Dex": "#096",
+ "Typ": "Psycho"
+ },
+ "097": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d4/Sugimori_097.png"
+ ]
+ ],
+ "Deutsch": "Hypno",
+ "Englisch": "Hypno",
+ "Farbe": "Gelb",
+ "Französisch": "Hypnomade",
+ "Gewicht": "75,6 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30b9\\u30ea\\u30fc\\u30d1\\u30fc (Sleeper)",
+ "Kategorie": "Hypnose",
+ "Link": "http://pokewiki.de/Hypno",
+ "National-Dex": "#097",
+ "Typ": "Psycho"
+ },
+ "098": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/cb/Sugimori_098.png"
+ ]
+ ],
+ "Deutsch": "Krabby",
+ "Englisch": "Krabby",
+ "Farbe": "Rot",
+ "Französisch": "Krabby",
+ "Gewicht": "6,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30af\\u30e9\\u30d6 (Crab)",
+ "Kategorie": "Krabbe",
+ "Link": "http://pokewiki.de/Krabby",
+ "National-Dex": "#098",
+ "Typ": "Wasser"
+ },
+ "099": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/9a/Sugimori_099.png"
+ ]
+ ],
+ "Deutsch": "Kingler",
+ "Englisch": "Kingler",
+ "Farbe": "Rot",
+ "Französisch": "Krabboss",
+ "Gewicht": "60,0 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30ad\\u30f3\\u30b0\\u30e9\\u30fc (Kingler)",
+ "Kategorie": "Kneifer",
+ "Link": "http://pokewiki.de/Kingler",
+ "National-Dex": "#099",
+ "Typ": "Wasser"
+ },
+ "100": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/21/Sugimori_100.png"
+ ]
+ ],
+ "Deutsch": "Voltobal",
+ "Englisch": "Voltorb",
+ "Farbe": "Rot",
+ "Französisch": "Voltorbe",
+ "Gewicht": "10,4 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30d3\\u30ea\\u30ea\\u30c0\\u30de (Biriridama)",
+ "Kategorie": "Ball",
+ "Link": "http://pokewiki.de/Voltobal",
+ "National-Dex": "#100",
+ "Typ": "Elektro"
+ },
+ "101": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/44/Sugimori_101.png"
+ ]
+ ],
+ "Deutsch": "Lektrobal",
+ "Englisch": "Electrode",
+ "Farbe": "Rot",
+ "Französisch": "\\u00c9lectrode",
+ "Gewicht": "66,6 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30de\\u30eb\\u30de\\u30a4\\u30f3 (Marumine)",
+ "Kategorie": "Ball",
+ "Link": "http://pokewiki.de/Lektrobal",
+ "National-Dex": "#101",
+ "Typ": "Elektro"
+ },
+ "102": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/08/Sugimori_102.png"
+ ]
+ ],
+ "Deutsch": "Owei",
+ "Englisch": "Exeggcute",
+ "Farbe": "Rosa",
+ "Französisch": "N\\u0153un\\u0153uf",
+ "Gewicht": "2,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30bf\\u30de\\u30bf\\u30de (Tamatama)",
+ "Kategorie": "Ei",
+ "Link": "http://pokewiki.de/Owei",
+ "National-Dex": "#102",
+ "Typ": [
+ "Pflanze",
+ "Psycho"
+ ]
+ },
+ "103": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/5/55/Sugimori_103.png"
+ ],
+ [
+ "Alola-Kokowei",
+ "http://www.pokewiki.de/images/c/c5/Sugimori_103a.png"
+ ]
+ ],
+ "Deutsch": "Kokowei",
+ "Englisch": "Exeggutor",
+ "Farbe": "Gelb",
+ "Französisch": "Noadkoko",
+ "Gewicht": [
+ "120,0 kg",
+ "415,6 kg (Alola)"
+ ],
+ "Größe": [
+ "2,0 m",
+ "10,9 m (Alola)"
+ ],
+ "Japanisch": "\\u30ca\\u30c3\\u30b7\\u30fc (Nassy)",
+ "Kategorie": "Palmfrucht",
+ "Link": "http://pokewiki.de/Kokowei",
+ "National-Dex": "#103",
+ "Typ": [
+ "Pflanze",
+ "Psycho",
+ "Pflanze",
+ "Drache (Alola)"
+ ]
+ },
+ "104": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/42/Sugimori_104.png"
+ ]
+ ],
+ "Deutsch": "Tragosso",
+ "Englisch": "Cubone",
+ "Farbe": "Braun",
+ "Französisch": "Osselait",
+ "Gewicht": "6,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30ab\\u30e9\\u30ab\\u30e9 (Karakara)",
+ "Kategorie": "Einsam",
+ "Link": "http://pokewiki.de/Tragosso",
+ "National-Dex": "#104",
+ "Typ": "Boden"
+ },
+ "105": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/1/12/Sugimori_105.png"
+ ],
+ [
+ "Alola-Knogga",
+ "http://www.pokewiki.de/images/7/7f/Sugimori_105a.png"
+ ]
+ ],
+ "Deutsch": "Knogga",
+ "Englisch": "Marowak",
+ "Farbe": [
+ "Braun",
+ "Violett (Alola)"
+ ],
+ "Französisch": "Ossatueur",
+ "Gewicht": [
+ "45,0 kg",
+ "34,0 kg (Alola)"
+ ],
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30ac\\u30e9\\u30ac\\u30e9 (Garagara)",
+ "Kategorie": "Knochenfan",
+ "Link": "http://pokewiki.de/Knogga",
+ "National-Dex": "#105",
+ "Typ": [
+ "Boden",
+ "Feuer",
+ "Geist (Alola)"
+ ]
+ },
+ "106": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5f/Sugimori_106.png"
+ ]
+ ],
+ "Deutsch": "Kicklee",
+ "Englisch": "Hitmonlee",
+ "Farbe": "Braun",
+ "Französisch": "Kicklee",
+ "Gewicht": "49,8 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30b5\\u30ef\\u30e0\\u30e9\\u30fc (Sawamular)",
+ "Kategorie": "Kicker",
+ "Link": "http://pokewiki.de/Kicklee",
+ "National-Dex": "#106",
+ "Typ": "Kampf"
+ },
+ "107": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a6/Sugimori_107.png"
+ ]
+ ],
+ "Deutsch": "Nockchan",
+ "Englisch": "Hitmonchan",
+ "Farbe": "Braun",
+ "Französisch": "Tygnon",
+ "Gewicht": "50,2 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30a8\\u30d3\\u30ef\\u30e9\\u30fc (Ebiwalar)",
+ "Kategorie": "Puncher",
+ "Link": "http://pokewiki.de/Nockchan",
+ "National-Dex": "#107",
+ "Typ": "Kampf"
+ },
+ "108": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0e/Sugimori_108.png"
+ ]
+ ],
+ "Deutsch": "Schlurp",
+ "Englisch": "Lickitung",
+ "Farbe": "Rosa",
+ "Französisch": "Excelangue",
+ "Gewicht": "65,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30d9\\u30ed\\u30ea\\u30f3\\u30ac (Beroringa)",
+ "Kategorie": "Schlecker",
+ "Link": "http://pokewiki.de/Schlurp",
+ "National-Dex": "#108",
+ "Typ": "Normal"
+ },
+ "109": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e2/Sugimori_109.png"
+ ]
+ ],
+ "Deutsch": "Smogon",
+ "Englisch": "Koffing",
+ "Farbe": "Violett",
+ "Französisch": "Smogo",
+ "Gewicht": "1,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c9\\u30ac\\u30fc\\u30b9 (Dogars)",
+ "Kategorie": "Giftwolke",
+ "Link": "http://pokewiki.de/Smogon",
+ "National-Dex": "#109",
+ "Typ": "Gift"
+ },
+ "110": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fd/Sugimori_110.png"
+ ]
+ ],
+ "Deutsch": "Smogmog",
+ "Englisch": "Weezing",
+ "Farbe": "Violett",
+ "Französisch": "Smogogo",
+ "Gewicht": "9,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30de\\u30bf\\u30c9\\u30ac\\u30b9 (Matadogas)",
+ "Kategorie": "Giftwolke",
+ "Link": "http://pokewiki.de/Smogmog",
+ "National-Dex": "#110",
+ "Typ": "Gift"
+ },
+ "111": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/05/Sugimori_111.png"
+ ]
+ ],
+ "Deutsch": "Rihorn",
+ "Englisch": "Rhyhorn",
+ "Farbe": "Grau",
+ "Französisch": "Rhinocorne",
+ "Gewicht": "115,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b5\\u30a4\\u30db\\u30fc\\u30f3 (Sihorn)",
+ "Kategorie": "Stachler",
+ "Link": "http://pokewiki.de/Rihorn",
+ "National-Dex": "#111",
+ "Typ": [
+ "Boden",
+ "Gestein"
+ ]
+ },
+ "112": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/95/Sugimori_112.png"
+ ]
+ ],
+ "Deutsch": "Rizeros",
+ "Englisch": "Rhydon",
+ "Farbe": "Grau",
+ "Französisch": "Rhinoferos",
+ "Gewicht": "120,0 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30b5\\u30a4\\u30c9\\u30f3 (Sidon)",
+ "Kategorie": "Bohrer",
+ "Link": "http://pokewiki.de/Rizeros",
+ "National-Dex": "#112",
+ "Typ": [
+ "Boden",
+ "Gestein"
+ ]
+ },
+ "113": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7f/Sugimori_113.png"
+ ]
+ ],
+ "Deutsch": "Chaneira",
+ "Englisch": "Chansey",
+ "Farbe": "Rosa",
+ "Französisch": "Leveinard",
+ "Gewicht": "34,6 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30e9\\u30c3\\u30ad\\u30fc (Lucky)",
+ "Kategorie": "Ei",
+ "Link": "http://pokewiki.de/Chaneira",
+ "National-Dex": "#113",
+ "Typ": "Normal"
+ },
+ "114": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4a/Sugimori_114.png"
+ ]
+ ],
+ "Deutsch": "Tangela",
+ "Englisch": "Tangela",
+ "Farbe": "Blau",
+ "Französisch": "Saquedeneu",
+ "Gewicht": "35,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30e2\\u30f3\\u30b8\\u30e3\\u30e9 (Monjara)",
+ "Kategorie": "Ranke",
+ "Link": "http://pokewiki.de/Tangela",
+ "National-Dex": "#114",
+ "Typ": "Pflanze"
+ },
+ "115": {
+ "Bilder": [
+ [
+ "Kangama",
+ "http://www.pokewiki.de/images/0/0c/Sugimori_115.png"
+ ],
+ [
+ "Mega-Kangama",
+ "http://www.pokewiki.de/images/f/ff/Sugimori_115m1.png"
+ ]
+ ],
+ "Deutsch": "Kangama",
+ "Englisch": "Kangaskhan",
+ "Farbe": "Braun",
+ "Französisch": "Kangourex",
+ "Gewicht": [
+ "80,0 kg",
+ "100,0 kg (Mega)"
+ ],
+ "Größe": "2,2 m",
+ "Japanisch": "\\u30ac\\u30eb\\u30fc\\u30e9 (Garura)",
+ "Kategorie": "Muttertier",
+ "Link": "http://pokewiki.de/Kangama",
+ "National-Dex": "#115",
+ "Typ": "Normal"
+ },
+ "116": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/65/Sugimori_116.png"
+ ]
+ ],
+ "Deutsch": "Seeper",
+ "Englisch": "Horsea",
+ "Farbe": "Blau",
+ "Französisch": "Hypotrempe",
+ "Gewicht": "8,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30bf\\u30c3\\u30c4\\u30fc (Tattu)",
+ "Kategorie": "Drache",
+ "Link": "http://pokewiki.de/Seeper",
+ "National-Dex": "#116",
+ "Typ": "Wasser"
+ },
+ "117": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/95/Sugimori_117.png"
+ ]
+ ],
+ "Deutsch": "Seemon",
+ "Englisch": "Seadra",
+ "Farbe": "Blau",
+ "Französisch": "Hypoc\\u00e9an",
+ "Gewicht": "25,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30b7\\u30fc\\u30c9\\u30e9 (Seadra)",
+ "Kategorie": "Drache",
+ "Link": "http://pokewiki.de/Seemon",
+ "National-Dex": "#117",
+ "Typ": "Wasser"
+ },
+ "118": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3d/Sugimori_118.png"
+ ]
+ ],
+ "Deutsch": "Goldini",
+ "Englisch": "Goldeen",
+ "Farbe": "Rot",
+ "Französisch": "Poissir\\u00e8ne",
+ "Gewicht": "15,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c8\\u30b5\\u30ad\\u30f3\\u30c8 (Tosakinto)",
+ "Kategorie": "Goldfisch",
+ "Link": "http://pokewiki.de/Goldini",
+ "National-Dex": "#118",
+ "Typ": "Wasser"
+ },
+ "119": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b7/Sugimori_119.png"
+ ]
+ ],
+ "Deutsch": "Golking",
+ "Englisch": "Seaking",
+ "Farbe": "Rot",
+ "Französisch": "Poissoroy",
+ "Gewicht": "39,0 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30a2\\u30ba\\u30de\\u30aa\\u30a6 (Azumao)",
+ "Kategorie": "Goldfisch",
+ "Link": "http://pokewiki.de/Golking",
+ "National-Dex": "#119",
+ "Typ": "Wasser"
+ },
+ "120": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ab/Sugimori_120.png"
+ ]
+ ],
+ "Deutsch": "Sterndu",
+ "Englisch": "Staryu",
+ "Farbe": "Braun",
+ "Französisch": "Stari",
+ "Gewicht": "34,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30d2\\u30c8\\u30c7\\u30de\\u30f3 (Hitodeman)",
+ "Kategorie": "Sternform",
+ "Link": "http://pokewiki.de/Sterndu",
+ "National-Dex": "#120",
+ "Typ": "Wasser"
+ },
+ "121": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0d/Sugimori_121.png"
+ ]
+ ],
+ "Deutsch": "Starmie",
+ "Englisch": "Starmie",
+ "Farbe": "Violett",
+ "Französisch": "Staross",
+ "Gewicht": "80,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30b9\\u30bf\\u30fc\\u30df\\u30fc (Starmie)",
+ "Kategorie": "Mysteri\\u00f6s",
+ "Link": "http://pokewiki.de/Starmie",
+ "National-Dex": "#121",
+ "Typ": [
+ "Wasser",
+ "Psycho"
+ ]
+ },
+ "122": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0e/Sugimori_122.png"
+ ]
+ ],
+ "Deutsch": "Pantimos",
+ "Englisch": "Mr. Mime",
+ "Farbe": "Rosa",
+ "Französisch": "M. Mime",
+ "Gewicht": "54,5 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30d0\\u30ea\\u30e4\\u30fc\\u30c9 (Barrierd)",
+ "Kategorie": "Sperre",
+ "Link": "http://pokewiki.de/Pantimos",
+ "National-Dex": "#122",
+ "Typ": [
+ "Psycho",
+ "Fee"
+ ]
+ },
+ "123": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ef/Sugimori_123.png"
+ ]
+ ],
+ "Deutsch": "Sichlor",
+ "Englisch": "Scyther",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Ins\\u00e9cateur",
+ "Gewicht": "56,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30b9\\u30c8\\u30e9\\u30a4\\u30af (Strike)",
+ "Kategorie": "Mantis",
+ "Link": "http://pokewiki.de/Sichlor",
+ "National-Dex": "#123",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "124": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a5/Sugimori_124.png"
+ ]
+ ],
+ "Deutsch": "Rossana",
+ "Englisch": "Jynx",
+ "Farbe": "Rot",
+ "Französisch": "Lippoutou",
+ "Gewicht": "40,6 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30eb\\u30fc\\u30b8\\u30e5\\u30e9 (Rougela)",
+ "Kategorie": "Humanotyp",
+ "Link": "http://pokewiki.de/Rossana",
+ "National-Dex": "#124",
+ "Typ": [
+ "Eis",
+ "Psycho"
+ ]
+ },
+ "125": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/53/Sugimori_125.png"
+ ]
+ ],
+ "Deutsch": "Elektek",
+ "Englisch": "Electabuzz",
+ "Farbe": "Gelb",
+ "Französisch": "\\u00c9lektek",
+ "Gewicht": "30,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30a8\\u30ec\\u30d6\\u30fc (Eleboo)",
+ "Kategorie": "Elektro",
+ "Link": "http://pokewiki.de/Elektek",
+ "National-Dex": "#125",
+ "Typ": "Elektro"
+ },
+ "126": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fc/Sugimori_126.png"
+ ]
+ ],
+ "Deutsch": "Magmar",
+ "Englisch": "Magmar",
+ "Farbe": "Rot",
+ "Französisch": "Magmar",
+ "Gewicht": "44,5 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30d6\\u30fc\\u30d0\\u30fc (Boober)",
+ "Kategorie": "Brenner",
+ "Link": "http://pokewiki.de/Magmar",
+ "National-Dex": "#126",
+ "Typ": "Feuer"
+ },
+ "127": {
+ "Bilder": [
+ [
+ "Pinsir",
+ "http://www.pokewiki.de/images/4/49/Sugimori_127.png"
+ ],
+ [
+ "Mega-Pinsir",
+ "http://www.pokewiki.de/images/5/55/Sugimori_127m1.png"
+ ]
+ ],
+ "Deutsch": "Pinsir",
+ "Englisch": "Pinsir",
+ "Farbe": "Braun",
+ "Französisch": "Scarabrute",
+ "Gewicht": [
+ "55,0 kg",
+ "59,0 kg (Mega)"
+ ],
+ "Größe": [
+ "1,5 m",
+ "1,7 m (Mega)"
+ ],
+ "Japanisch": "\\u30ab\\u30a4\\u30ed\\u30b9 (Kailios)",
+ "Kategorie": "Kneifk\\u00e4fer",
+ "Link": "http://pokewiki.de/Pinsir",
+ "National-Dex": "#127",
+ "Typ": [
+ "K\\u00e4fer",
+ "K\\u00e4fer",
+ "Flug (Mega)"
+ ]
+ },
+ "128": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e5/Sugimori_128.png"
+ ]
+ ],
+ "Deutsch": "Tauros",
+ "Englisch": "Tauros",
+ "Farbe": "Braun",
+ "Französisch": "Tauros",
+ "Gewicht": "88,4 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30b1\\u30f3\\u30bf\\u30ed\\u30b9 (Kentaros)",
+ "Kategorie": "Wildbulle",
+ "Link": "http://pokewiki.de/Tauros",
+ "National-Dex": "#128",
+ "Typ": "Normal"
+ },
+ "129": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2e/Sugimori_129.png"
+ ]
+ ],
+ "Deutsch": "Karpador",
+ "Englisch": "Magikarp",
+ "Farbe": "Rot",
+ "Französisch": "Magicarpe",
+ "Gewicht": "10,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30b3\\u30a4\\u30ad\\u30f3\\u30b0 (Koiking)",
+ "Kategorie": "Fisch",
+ "Link": "http://pokewiki.de/Karpador",
+ "National-Dex": "#129",
+ "Typ": "Wasser"
+ },
+ "130": {
+ "Bilder": [
+ [
+ "Garados",
+ "http://www.pokewiki.de/images/b/b7/Sugimori_130.png"
+ ],
+ [
+ "Mega-Garados",
+ "http://www.pokewiki.de/images/f/fe/Sugimori_130m1.png"
+ ]
+ ],
+ "Deutsch": "Garados",
+ "Englisch": "Gyarados",
+ "Farbe": "Blau",
+ "Französisch": "L\\u00e9viator",
+ "Gewicht": [
+ "235,0 kg",
+ "305,0 kg (Mega)"
+ ],
+ "Größe": "6,5 m",
+ "Japanisch": "\\u30ae\\u30e3\\u30e9\\u30c9\\u30b9 (Gyarados)",
+ "Kategorie": "Grausam",
+ "Link": "http://pokewiki.de/Garados",
+ "National-Dex": "#130",
+ "Typ": [
+ "Wasser",
+ "Flug",
+ "Wasser",
+ "Unlicht (Mega)"
+ ]
+ },
+ "131": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b7/Sugimori_131.png"
+ ]
+ ],
+ "Deutsch": "Lapras",
+ "Englisch": "Lapras",
+ "Farbe": "Blau",
+ "Französisch": "Lokhlass",
+ "Gewicht": "220,0 kg",
+ "Größe": "2,5 m",
+ "Japanisch": "\\u30e9\\u30d7\\u30e9\\u30b9 (Laplace)",
+ "Kategorie": "Transport",
+ "Link": "http://pokewiki.de/Lapras",
+ "National-Dex": "#131",
+ "Typ": [
+ "Wasser",
+ "Eis"
+ ]
+ },
+ "132": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/dd/Sugimori_132.png"
+ ]
+ ],
+ "Deutsch": "Ditto",
+ "Englisch": "Ditto",
+ "Farbe": "Violett",
+ "Französisch": "M\\u00e9tamorph",
+ "Gewicht": "4,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30e1\\u30bf\\u30e2\\u30f3 (Metamon)",
+ "Kategorie": "Transform",
+ "Link": "http://pokewiki.de/Ditto",
+ "National-Dex": "#132",
+ "Typ": "Normal"
+ },
+ "133": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/37/Sugimori_133.png"
+ ]
+ ],
+ "Deutsch": "Evoli",
+ "Englisch": "Eevee",
+ "Farbe": "Braun",
+ "Französisch": "\\u00c9voli",
+ "Gewicht": "6,5 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30a4\\u30fc\\u30d6\\u30a4 (Eievui)",
+ "Kategorie": "Evolution",
+ "Link": "http://pokewiki.de/Evoli",
+ "National-Dex": "#133",
+ "Typ": "Normal"
+ },
+ "134": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/08/Sugimori_134.png"
+ ]
+ ],
+ "Deutsch": "Aquana",
+ "Englisch": "Vaporeon",
+ "Farbe": "Blau",
+ "Französisch": "Aquali",
+ "Gewicht": "29,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b7\\u30e3\\u30ef\\u30fc\\u30ba (Showers)",
+ "Kategorie": "Blubblase",
+ "Link": "http://pokewiki.de/Aquana",
+ "National-Dex": "#134",
+ "Typ": "Wasser"
+ },
+ "135": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1e/Sugimori_135.png"
+ ]
+ ],
+ "Deutsch": "Blitza",
+ "Englisch": "Jolteon",
+ "Farbe": "Gelb",
+ "Französisch": "Voltali",
+ "Gewicht": "24,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30b5\\u30f3\\u30c0\\u30fc\\u30b9 (Thunders)",
+ "Kategorie": "Blitz",
+ "Link": "http://pokewiki.de/Blitza",
+ "National-Dex": "#135",
+ "Typ": "Elektro"
+ },
+ "136": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4d/Sugimori_136.png"
+ ]
+ ],
+ "Deutsch": "Flamara",
+ "Englisch": "Flareon",
+ "Farbe": "Rot",
+ "Französisch": "Pyroli",
+ "Gewicht": "25,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30d6\\u30fc\\u30b9\\u30bf\\u30fc (Booster)",
+ "Kategorie": "Feuer",
+ "Link": "http://pokewiki.de/Flamara",
+ "National-Dex": "#136",
+ "Typ": "Feuer"
+ },
+ "137": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f2/Sugimori_137.png"
+ ]
+ ],
+ "Deutsch": "Porygon",
+ "Englisch": "Porygon",
+ "Farbe": "Rosa",
+ "Französisch": "Porygon",
+ "Gewicht": "36,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30dd\\u30ea\\u30b4\\u30f3 (Porygon)",
+ "Kategorie": "Virtuell",
+ "Link": "http://pokewiki.de/Porygon",
+ "National-Dex": "#137",
+ "Typ": "Normal"
+ },
+ "138": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/aa/Sugimori_138.png"
+ ]
+ ],
+ "Deutsch": "Amonitas",
+ "Englisch": "Omanyte",
+ "Farbe": "Blau",
+ "Französisch": "Amonita",
+ "Gewicht": "7,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30aa\\u30e0\\u30ca\\u30a4\\u30c8 (Omnite)",
+ "Kategorie": "Spirale",
+ "Link": "http://pokewiki.de/Amonitas",
+ "National-Dex": "#138",
+ "Typ": [
+ "Gestein",
+ "Wasser"
+ ]
+ },
+ "139": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a9/Sugimori_139.png"
+ ]
+ ],
+ "Deutsch": "Amoroso",
+ "Englisch": "Omastar",
+ "Farbe": "Blau",
+ "Französisch": "Amonistar",
+ "Gewicht": "35,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30aa\\u30e0\\u30b9\\u30bf\\u30fc (Omstar)",
+ "Kategorie": "Spirale",
+ "Link": "http://pokewiki.de/Amoroso",
+ "National-Dex": "#139",
+ "Typ": [
+ "Gestein",
+ "Wasser"
+ ]
+ },
+ "140": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8d/Sugimori_140.png"
+ ]
+ ],
+ "Deutsch": "Kabuto",
+ "Englisch": "Kabuto",
+ "Farbe": "Braun",
+ "Französisch": "Kabuto",
+ "Gewicht": "11,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30ab\\u30d6\\u30c8 (Kabuto)",
+ "Kategorie": "Schaltier",
+ "Link": "http://pokewiki.de/Kabuto",
+ "National-Dex": "#140",
+ "Typ": [
+ "Gestein",
+ "Wasser"
+ ]
+ },
+ "141": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/57/Sugimori_141.png"
+ ]
+ ],
+ "Deutsch": "Kabutops",
+ "Englisch": "Kabutops",
+ "Farbe": "Braun",
+ "Französisch": "Kabutops",
+ "Gewicht": "40,5 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30ab\\u30d6\\u30c8\\u30d7\\u30b9 (Kabutops)",
+ "Kategorie": "Schaltier",
+ "Link": "http://pokewiki.de/Kabutops",
+ "National-Dex": "#141",
+ "Typ": [
+ "Gestein",
+ "Wasser"
+ ]
+ },
+ "142": {
+ "Bilder": [
+ [
+ "Aerodactyl",
+ "http://www.pokewiki.de/images/8/8a/Sugimori_142.png"
+ ],
+ [
+ "Mega-Aerodactyl",
+ "http://www.pokewiki.de/images/f/f6/Sugimori_142m1.png"
+ ]
+ ],
+ "Deutsch": "Aerodactyl",
+ "Englisch": "Aerodactyl",
+ "Farbe": "Violett",
+ "Französisch": "Pt\\u00e9ra",
+ "Gewicht": [
+ "59,0 kg",
+ "79,0 kg (Mega)"
+ ],
+ "Größe": [
+ "1,8 m",
+ "2,1 m (Mega)"
+ ],
+ "Japanisch": "\\u30d7\\u30c6\\u30e9 (Ptera)",
+ "Kategorie": "Fossil",
+ "Link": "http://pokewiki.de/Aerodactyl",
+ "National-Dex": "#142",
+ "Typ": [
+ "Gestein",
+ "Flug"
+ ]
+ },
+ "143": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/9f/Sugimori_143.png"
+ ]
+ ],
+ "Deutsch": "Relaxo",
+ "Englisch": "Snorlax",
+ "Farbe": "Schwarz",
+ "Französisch": "Ronflex",
+ "Gewicht": "460,0 kg",
+ "Größe": "2,1 m",
+ "Japanisch": "\\u30ab\\u30d3\\u30b4\\u30f3 (Kabigon)",
+ "Kategorie": "Tagtr\\u00e4umer",
+ "Link": "http://pokewiki.de/Relaxo",
+ "National-Dex": "#143",
+ "Typ": "Normal"
+ },
+ "144": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/43/Sugimori_144.png"
+ ]
+ ],
+ "Deutsch": "Arktos",
+ "Englisch": "Articuno",
+ "Farbe": "Blau",
+ "Französisch": "Artikodin",
+ "Gewicht": "55,4 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30d5\\u30ea\\u30fc\\u30b6\\u30fc (Freezer)",
+ "Kategorie": "Eis",
+ "Link": "http://pokewiki.de/Arktos",
+ "National-Dex": "#144",
+ "Typ": [
+ "Eis",
+ "Flug"
+ ]
+ },
+ "145": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/11/Sugimori_145.png"
+ ]
+ ],
+ "Deutsch": "Zapdos",
+ "Englisch": "Zapdos",
+ "Farbe": "Gelb",
+ "Französisch": "\\u00c9lecthor",
+ "Gewicht": "52,6 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30b5\\u30f3\\u30c0\\u30fc (Thunder)",
+ "Kategorie": "Elektro",
+ "Link": "http://pokewiki.de/Zapdos",
+ "National-Dex": "#145",
+ "Typ": [
+ "Elektro",
+ "Flug"
+ ]
+ },
+ "146": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/54/Sugimori_146.png"
+ ]
+ ],
+ "Deutsch": "Lavados",
+ "Englisch": "Moltres",
+ "Farbe": "Gelb",
+ "Französisch": "Sulfura",
+ "Gewicht": "60,0 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30d5\\u30a1\\u30a4\\u30e4\\u30fc (Fire)",
+ "Kategorie": "Flamme (Generation 1 und 2 noch \\u201eFeuer\\u201c)",
+ "Link": "http://pokewiki.de/Lavados",
+ "National-Dex": "#146",
+ "Typ": [
+ "Feuer",
+ "Flug"
+ ]
+ },
+ "147": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/43/Sugimori_147.png"
+ ]
+ ],
+ "Deutsch": "Dratini",
+ "Englisch": "Dratini",
+ "Farbe": "Blau",
+ "Französisch": "Minidraco",
+ "Gewicht": "3,3 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30df\\u30cb\\u30ea\\u30e5\\u30a6 (Miniryu)",
+ "Kategorie": "Drache",
+ "Link": "http://pokewiki.de/Dratini",
+ "National-Dex": "#147",
+ "Typ": "Drache"
+ },
+ "148": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/99/Sugimori_148.png"
+ ]
+ ],
+ "Deutsch": "Dragonir",
+ "Englisch": "Dragonair",
+ "Farbe": "Blau",
+ "Französisch": "Draco",
+ "Gewicht": "16,5 kg",
+ "Größe": "4,0 m",
+ "Japanisch": "\\u30cf\\u30af\\u30ea\\u30e5\\u30fc (Hakuryu)",
+ "Kategorie": "Drache",
+ "Link": "http://pokewiki.de/Dragonir",
+ "National-Dex": "#148",
+ "Typ": "Drache"
+ },
+ "149": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0e/Sugimori_149.png"
+ ]
+ ],
+ "Deutsch": "Dragoran",
+ "Englisch": "Dragonite",
+ "Farbe": "Braun",
+ "Französisch": "Dracolosse",
+ "Gewicht": "210,0 kg",
+ "Größe": "2,2 m",
+ "Japanisch": "\\u30ab\\u30a4\\u30ea\\u30e5\\u30fc (Kairyu)",
+ "Kategorie": "Drache",
+ "Link": "http://pokewiki.de/Dragoran",
+ "National-Dex": "#149",
+ "Typ": [
+ "Drache",
+ "Flug"
+ ]
+ },
+ "150": {
+ "Bilder": [
+ [
+ "Mewtu",
+ "http://www.pokewiki.de/images/4/46/Sugimori_150.png"
+ ],
+ [
+ "Mega-Mewtu X",
+ "http://www.pokewiki.de/images/c/ca/Sugimori_150m1.png"
+ ],
+ [
+ "Mega-Mewtu Y",
+ "http://www.pokewiki.de/images/9/9c/Sugimori_150m2.png"
+ ]
+ ],
+ "Deutsch": "Mewtu",
+ "Englisch": "Mewtwo",
+ "Farbe": "Violett",
+ "Französisch": "Mewtwo",
+ "Gewicht": [
+ "122,0 kg",
+ "127,0 kg (Mega X)",
+ "33,0 kg (Mega Y)"
+ ],
+ "Größe": [
+ "2,0 m",
+ "2,3 m (Mega X)",
+ "1,5 m (Mega Y)"
+ ],
+ "Japanisch": "\\u30df\\u30e5\\u30a6\\u30c4\\u30fc (Mewtwo)",
+ "Kategorie": "Genmutant",
+ "Link": "http://pokewiki.de/Mewtu",
+ "National-Dex": "#150",
+ "Typ": [
+ "Psycho",
+ "Psycho",
+ "Kampf (Mega X)"
+ ]
+ },
+ "151": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/05/Sugimori_151.png"
+ ]
+ ],
+ "Deutsch": "Mew",
+ "Englisch": "Mew",
+ "Farbe": "Rosa",
+ "Französisch": "Mew",
+ "Gewicht": "4,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30df\\u30e5\\u30a6 (Mew)",
+ "Kategorie": "Neue Art",
+ "Link": "http://pokewiki.de/Mew",
+ "National-Dex": "#151",
+ "Typ": "Psycho"
+ },
+ "152": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0d/Sugimori_152.png"
+ ]
+ ],
+ "Deutsch": "Endivie",
+ "Englisch": "Chikorita",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Germignon",
+ "Gewicht": "6,4 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30c1\\u30b3\\u30ea\\u30fc\\u30bf (Chicorita)",
+ "Kategorie": "Laub",
+ "Link": "http://pokewiki.de/Endivie",
+ "National-Dex": "#152",
+ "Typ": "Pflanze"
+ },
+ "153": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/83/Sugimori_153.png"
+ ]
+ ],
+ "Deutsch": "Lorblatt",
+ "Englisch": "Bayleef",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Macronium",
+ "Gewicht": "15,8 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30d9\\u30a4\\u30ea\\u30fc\\u30d5 (Bayleaf)",
+ "Kategorie": "Laub",
+ "Link": "http://pokewiki.de/Lorblatt",
+ "National-Dex": "#153",
+ "Typ": "Pflanze"
+ },
+ "154": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e7/Sugimori_154.png"
+ ]
+ ],
+ "Deutsch": "Meganie",
+ "Englisch": "Meganium",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "M\\u00e9ganium",
+ "Gewicht": "100,5 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30e1\\u30ac\\u30cb\\u30a6\\u30e0 (Meganium)",
+ "Kategorie": "Kr\\u00e4uter",
+ "Link": "http://pokewiki.de/Meganie",
+ "National-Dex": "#154",
+ "Typ": "Pflanze"
+ },
+ "155": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2e/Sugimori_155.png"
+ ]
+ ],
+ "Deutsch": "Feurigel",
+ "Englisch": "Cyndaquil",
+ "Farbe": "Gelb",
+ "Französisch": "H\\u00e9ricendre",
+ "Gewicht": "7,9 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30d2\\u30ce\\u30a2\\u30e9\\u30b7 (Hinoarashi)",
+ "Kategorie": "Feuermaus",
+ "Link": "http://pokewiki.de/Feurigel",
+ "National-Dex": "#155",
+ "Typ": "Feuer"
+ },
+ "156": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/88/Sugimori_156.png"
+ ]
+ ],
+ "Deutsch": "Igelavar",
+ "Englisch": "Quilava",
+ "Farbe": "Gelb",
+ "Französisch": "Feurisson",
+ "Gewicht": "19,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30de\\u30b0\\u30de\\u30e9\\u30b7 (Magmarashi)",
+ "Kategorie": "Vulkan",
+ "Link": "http://pokewiki.de/Igelavar",
+ "National-Dex": "#156",
+ "Typ": "Feuer"
+ },
+ "157": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ee/Sugimori_157.png"
+ ]
+ ],
+ "Deutsch": "Tornupto",
+ "Englisch": "Typhlosion",
+ "Farbe": "Gelb",
+ "Französisch": "Typhlosion",
+ "Gewicht": "79,5 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30d0\\u30af\\u30d5\\u30fc\\u30f3 (Bakphoon)",
+ "Kategorie": "Vulkan",
+ "Link": "http://pokewiki.de/Tornupto",
+ "National-Dex": "#157",
+ "Typ": "Feuer"
+ },
+ "158": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/34/Sugimori_158.png"
+ ]
+ ],
+ "Deutsch": "Karnimani",
+ "Englisch": "Totodile",
+ "Farbe": "Blau",
+ "Französisch": "Ka\\u00efminus",
+ "Gewicht": "9,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30ef\\u30cb\\u30ce\\u30b3 (Waninoko)",
+ "Kategorie": "Gro\\u00dfmaul",
+ "Link": "http://pokewiki.de/Karnimani",
+ "National-Dex": "#158",
+ "Typ": "Wasser"
+ },
+ "159": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a6/Sugimori_159.png"
+ ]
+ ],
+ "Deutsch": "Tyracroc",
+ "Englisch": "Croconaw",
+ "Farbe": "Blau",
+ "Französisch": "Crocrodil",
+ "Gewicht": "25,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30a2\\u30ea\\u30b2\\u30a4\\u30c4 (Alligates)",
+ "Kategorie": "Gro\\u00dfmaul",
+ "Link": "http://pokewiki.de/Tyracroc",
+ "National-Dex": "#159",
+ "Typ": "Wasser"
+ },
+ "160": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/07/Sugimori_160.png"
+ ]
+ ],
+ "Deutsch": "Impergator",
+ "Englisch": "Feraligatr",
+ "Farbe": "Blau",
+ "Französisch": "Aligatueur",
+ "Gewicht": "88,8 kg",
+ "Größe": "2,3 m",
+ "Japanisch": "\\u30aa\\u30fc\\u30c0\\u30a4\\u30eb (Ordile)",
+ "Kategorie": "Gro\\u00dfmaul",
+ "Link": "http://pokewiki.de/Impergator",
+ "National-Dex": "#160",
+ "Typ": "Wasser"
+ },
+ "161": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/ff/Sugimori_161.png"
+ ]
+ ],
+ "Deutsch": "Wiesor",
+ "Englisch": "Sentret",
+ "Farbe": "Braun",
+ "Französisch": "Fouinette",
+ "Gewicht": "6,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30aa\\u30bf\\u30c1 (Otachi)",
+ "Kategorie": "Sp\\u00e4her",
+ "Link": "http://pokewiki.de/Wiesor",
+ "National-Dex": "#161",
+ "Typ": "Normal"
+ },
+ "162": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/25/Sugimori_162.png"
+ ]
+ ],
+ "Deutsch": "Wiesenior",
+ "Englisch": "Furret",
+ "Farbe": "Braun",
+ "Französisch": "Fouinar",
+ "Gewicht": "32,5 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30aa\\u30aa\\u30bf\\u30c1 (Ootachi)",
+ "Kategorie": "Langleib",
+ "Link": "http://pokewiki.de/Wiesenior",
+ "National-Dex": "#162",
+ "Typ": "Normal"
+ },
+ "163": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0a/Sugimori_163.png"
+ ]
+ ],
+ "Deutsch": "Hoothoot",
+ "Englisch": "Hoothoot",
+ "Farbe": "Braun",
+ "Französisch": "Hoothoot",
+ "Gewicht": "21,2 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30db\\u30fc\\u30db\\u30fc (H\\u014dh\\u014d)",
+ "Kategorie": "Eule",
+ "Link": "http://pokewiki.de/Hoothoot",
+ "National-Dex": "#163",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "164": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3a/Sugimori_164.png"
+ ]
+ ],
+ "Deutsch": "Noctuh",
+ "Englisch": "Noctowl",
+ "Farbe": "Braun",
+ "Französisch": "Noarfang",
+ "Gewicht": "40,8 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30e8\\u30eb\\u30ce\\u30ba\\u30af (Yorunozuku)",
+ "Kategorie": "Eule",
+ "Link": "http://pokewiki.de/Noctuh",
+ "National-Dex": "#164",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "165": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f3/Sugimori_165.png"
+ ]
+ ],
+ "Deutsch": "Ledyba",
+ "Englisch": "Ledyba",
+ "Farbe": "Rot",
+ "Französisch": "Coxy",
+ "Gewicht": "10,8 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30ec\\u30c7\\u30a3\\u30d0 (Rediba)",
+ "Kategorie": "F\\u00fcnf-Punkt",
+ "Link": "http://pokewiki.de/Ledyba",
+ "National-Dex": "#165",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "166": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c8/Sugimori_166.png"
+ ]
+ ],
+ "Deutsch": "Ledian",
+ "Englisch": "Ledian",
+ "Farbe": "Rot",
+ "Französisch": "Coxyclaque",
+ "Gewicht": "35,6 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30ec\\u30c7\\u30a3\\u30a2\\u30f3 (Redian)",
+ "Kategorie": "F\\u00fcnf-Punkt",
+ "Link": "http://pokewiki.de/Ledian",
+ "National-Dex": "#166",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "167": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/21/Sugimori_167.png"
+ ]
+ ],
+ "Deutsch": "Webarak",
+ "Englisch": "Spinarak",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Mimigal",
+ "Gewicht": "8,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30a4\\u30c8\\u30de\\u30eb (Itomaru)",
+ "Kategorie": "Fadenwurf",
+ "Link": "http://pokewiki.de/Webarak",
+ "National-Dex": "#167",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gift"
+ ]
+ },
+ "168": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ae/Sugimori_168.png"
+ ]
+ ],
+ "Deutsch": "Ariados",
+ "Englisch": "Ariados",
+ "Farbe": "Rot",
+ "Französisch": "Migalos",
+ "Gewicht": "33,5 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30a2\\u30ea\\u30a2\\u30c9\\u30b9 (Ariados)",
+ "Kategorie": "Langbein",
+ "Link": "http://pokewiki.de/Ariados",
+ "National-Dex": "#168",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gift"
+ ]
+ },
+ "169": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/35/Sugimori_169.png"
+ ]
+ ],
+ "Deutsch": "Iksbat",
+ "Englisch": "Crobat",
+ "Farbe": "Violett",
+ "Französisch": "Nostenfer",
+ "Gewicht": "75,0 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30af\\u30ed\\u30d0\\u30c3\\u30c8 (Crobat)",
+ "Kategorie": "Fledermaus",
+ "Link": "http://pokewiki.de/Iksbat",
+ "National-Dex": "#169",
+ "Typ": [
+ "Gift",
+ "Flug"
+ ]
+ },
+ "170": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/09/Sugimori_170.png"
+ ]
+ ],
+ "Deutsch": "Lampi",
+ "Englisch": "Chinchou",
+ "Farbe": "Blau",
+ "Französisch": "Loupio",
+ "Gewicht": "12,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30c1\\u30e7\\u30f3\\u30c1\\u30fc (Chonchie)",
+ "Kategorie": "Angler",
+ "Link": "http://pokewiki.de/Lampi",
+ "National-Dex": "#170",
+ "Typ": [
+ "Wasser",
+ "Elektro"
+ ]
+ },
+ "171": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c7/Sugimori_171.png"
+ ]
+ ],
+ "Deutsch": "Lanturn",
+ "Englisch": "Lanturn",
+ "Farbe": "Blau",
+ "Französisch": "Lanturn",
+ "Gewicht": "22,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30e9\\u30f3\\u30bf\\u30fc\\u30f3 (Lantern)",
+ "Kategorie": "Leuchte",
+ "Link": "http://pokewiki.de/Lanturn",
+ "National-Dex": "#171",
+ "Typ": [
+ "Wasser",
+ "Elektro"
+ ]
+ },
+ "172": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/07/Sugimori_172.png"
+ ]
+ ],
+ "Deutsch": "Pichu",
+ "Englisch": "Pichu",
+ "Farbe": "Gelb",
+ "Französisch": "Pichu",
+ "Gewicht": "2,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30d4\\u30c1\\u30e5\\u30fc (Pichuu)",
+ "Kategorie": "Babymaus",
+ "Link": "http://pokewiki.de/Pichu",
+ "National-Dex": "#172",
+ "Typ": "Elektro"
+ },
+ "173": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/dd/Sugimori_173.png"
+ ]
+ ],
+ "Deutsch": "Pii",
+ "Englisch": "Cleffa",
+ "Farbe": "Rosa",
+ "Französisch": "M\\u00e9lo",
+ "Gewicht": "3,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30d4\\u30a3 (Py)",
+ "Kategorie": "Sternform",
+ "Link": "http://pokewiki.de/Pii",
+ "National-Dex": "#173",
+ "Typ": "Fee"
+ },
+ "174": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/02/Sugimori_174.png"
+ ]
+ ],
+ "Deutsch": "Fluffeluff",
+ "Englisch": "Igglybuff",
+ "Farbe": "Rosa",
+ "Französisch": "Toudoudou",
+ "Gewicht": "1,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30d7\\u30d7\\u30ea\\u30f3 (Pupurin)",
+ "Kategorie": "Ballon",
+ "Link": "http://pokewiki.de/Fluffeluff",
+ "National-Dex": "#174",
+ "Typ": [
+ "Normal",
+ "Fee"
+ ]
+ },
+ "175": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a6/Sugimori_175.png"
+ ]
+ ],
+ "Deutsch": "Togepi",
+ "Englisch": "Togepi",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Tog\\u00e9pi",
+ "Gewicht": "1,5 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30c8\\u30b2\\u30d4\\u30fc (Togepy)",
+ "Kategorie": "Zackenball",
+ "Link": "http://pokewiki.de/Togepi",
+ "National-Dex": "#175",
+ "Typ": "Fee"
+ },
+ "176": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2c/Sugimori_176.png"
+ ]
+ ],
+ "Deutsch": "Togetic",
+ "Englisch": "Togetic",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Tog\\u00e9tic",
+ "Gewicht": "3,2 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c8\\u30b2\\u30c1\\u30c3\\u30af (Togechick)",
+ "Kategorie": "Freude",
+ "Link": "http://pokewiki.de/Togetic",
+ "National-Dex": "#176",
+ "Typ": [
+ "Fee",
+ "Flug"
+ ]
+ },
+ "177": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/53/Sugimori_177.png"
+ ]
+ ],
+ "Deutsch": "Natu",
+ "Englisch": "Natu",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Natu",
+ "Gewicht": "2,0 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30cd\\u30a4\\u30c6\\u30a3 (Naty)",
+ "Kategorie": "Kleinvogel",
+ "Link": "http://pokewiki.de/Natu",
+ "National-Dex": "#177",
+ "Typ": [
+ "Psycho",
+ "Flug"
+ ]
+ },
+ "178": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0d/Sugimori_178.png"
+ ]
+ ],
+ "Deutsch": "Xatu",
+ "Englisch": "Xatu",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Xatu",
+ "Gewicht": "15,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30cd\\u30a4\\u30c6\\u30a3\\u30aa (Natio)",
+ "Kategorie": "Mystik",
+ "Link": "http://pokewiki.de/Xatu",
+ "National-Dex": "#178",
+ "Typ": [
+ "Psycho",
+ "Flug"
+ ]
+ },
+ "179": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/78/Sugimori_179.png"
+ ]
+ ],
+ "Deutsch": "Voltilamm",
+ "Englisch": "Mareep",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Wattouat",
+ "Gewicht": "7,8 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e1\\u30ea\\u30fc\\u30d7 (Merriep)",
+ "Kategorie": "Wolle",
+ "Link": "http://pokewiki.de/Voltilamm",
+ "National-Dex": "#179",
+ "Typ": "Elektro"
+ },
+ "180": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b2/Sugimori_180.png"
+ ]
+ ],
+ "Deutsch": "Waaty",
+ "Englisch": "Flaaffy",
+ "Farbe": "Rosa",
+ "Französisch": "Lainergie",
+ "Gewicht": "13,3 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30e2\\u30b3\\u30b3 (Mokoko)",
+ "Kategorie": "Wolle",
+ "Link": "http://pokewiki.de/Waaty",
+ "National-Dex": "#180",
+ "Typ": "Elektro"
+ },
+ "181": {
+ "Bilder": [
+ [
+ "Ampharos",
+ "http://www.pokewiki.de/images/b/b2/Sugimori_181.png"
+ ],
+ [
+ "Mega-Ampharos",
+ "http://www.pokewiki.de/images/9/9e/Sugimori_181m1.png"
+ ]
+ ],
+ "Deutsch": "Ampharos",
+ "Englisch": "Ampharos",
+ "Farbe": "Gelb",
+ "Französisch": "Pharamp",
+ "Gewicht": "61,5 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30c7\\u30f3\\u30ea\\u30e5\\u30a6 (Denryu)",
+ "Kategorie": "Leuchte",
+ "Link": "http://pokewiki.de/Ampharos",
+ "National-Dex": "#181",
+ "Typ": [
+ "Elektro",
+ "Elektro",
+ "Drache (Mega)"
+ ]
+ },
+ "182": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/27/Sugimori_182.png"
+ ]
+ ],
+ "Deutsch": "Blubella",
+ "Englisch": "Bellossom",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Joliflor",
+ "Gewicht": "5,8 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30ad\\u30ec\\u30a4\\u30cf\\u30ca (Kireihana)",
+ "Kategorie": "Blume",
+ "Link": "http://pokewiki.de/Blubella",
+ "National-Dex": "#182",
+ "Typ": "Pflanze"
+ },
+ "183": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/03/Sugimori_183.png"
+ ]
+ ],
+ "Deutsch": "Marill",
+ "Englisch": "Marill",
+ "Farbe": "Blau",
+ "Französisch": "Marill",
+ "Gewicht": "8,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30de\\u30ea\\u30eb (Maril)",
+ "Kategorie": "Aquamaus",
+ "Link": "http://pokewiki.de/Marill",
+ "National-Dex": "#183",
+ "Typ": [
+ "Wasser",
+ "Fee"
+ ]
+ },
+ "184": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f4/Sugimori_184.png"
+ ]
+ ],
+ "Deutsch": "Azumarill",
+ "Englisch": "Azumarill",
+ "Farbe": "Blau",
+ "Französisch": "Azumarill",
+ "Gewicht": "28,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30de\\u30ea\\u30eb\\u30ea (Marilli)",
+ "Kategorie": "Aquahase",
+ "Link": "http://pokewiki.de/Azumarill",
+ "National-Dex": "#184",
+ "Typ": [
+ "Wasser",
+ "Fee"
+ ]
+ },
+ "185": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/75/Sugimori_185.png"
+ ]
+ ],
+ "Deutsch": "Mogelbaum",
+ "Englisch": "Sudowoodo",
+ "Farbe": "Braun",
+ "Französisch": "Simularbre",
+ "Gewicht": "38,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30a6\\u30bd\\u30c3\\u30ad\\u30fc (Usokkie)",
+ "Kategorie": "Imitation",
+ "Link": "http://pokewiki.de/Mogelbaum",
+ "National-Dex": "#185",
+ "Typ": "Gestein"
+ },
+ "186": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c8/Sugimori_186.png"
+ ]
+ ],
+ "Deutsch": "Quaxo",
+ "Englisch": "Politoed",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Tarpaud",
+ "Gewicht": "33,9 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30cb\\u30e7\\u30ed\\u30c8\\u30ce (Nyorotono)",
+ "Kategorie": "Frosch",
+ "Link": "http://pokewiki.de/Quaxo",
+ "National-Dex": "#186",
+ "Typ": "Wasser"
+ },
+ "187": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3b/Sugimori_187.png"
+ ]
+ ],
+ "Deutsch": "Hoppspross",
+ "Englisch": "Hoppip",
+ "Farbe": "Rosa",
+ "Französisch": "Granivol",
+ "Gewicht": "0,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30cf\\u30cd\\u30c3\\u30b3 (Hanecco)",
+ "Kategorie": "L\\u00f6wenzahn",
+ "Link": "http://pokewiki.de/Hoppspross",
+ "National-Dex": "#187",
+ "Typ": [
+ "Pflanze",
+ "Flug"
+ ]
+ },
+ "188": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d1/Sugimori_188.png"
+ ]
+ ],
+ "Deutsch": "Hubelupf",
+ "Englisch": "Skiploom",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Floravol",
+ "Gewicht": "1,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30dd\\u30dd\\u30c3\\u30b3 (Popocco)",
+ "Kategorie": "L\\u00f6wenzahn",
+ "Link": "http://pokewiki.de/Hubelupf",
+ "National-Dex": "#188",
+ "Typ": [
+ "Pflanze",
+ "Flug"
+ ]
+ },
+ "189": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d0/Sugimori_189.png"
+ ]
+ ],
+ "Deutsch": "Papungha",
+ "Englisch": "Jumpluff",
+ "Farbe": "Blau",
+ "Französisch": "Cotovol",
+ "Gewicht": "3,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30ef\\u30bf\\u30c3\\u30b3 (Watacco)",
+ "Kategorie": "L\\u00f6wenzahn",
+ "Link": "http://pokewiki.de/Papungha",
+ "National-Dex": "#189",
+ "Typ": [
+ "Pflanze",
+ "Flug"
+ ]
+ },
+ "190": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/06/Sugimori_190.png"
+ ]
+ ],
+ "Deutsch": "Griffel",
+ "Englisch": "Aipom",
+ "Farbe": "Violett",
+ "Französisch": "Capumain",
+ "Gewicht": "11,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30a8\\u30a4\\u30d1\\u30e0 (Eipam)",
+ "Kategorie": "Langschweif",
+ "Link": "http://pokewiki.de/Griffel",
+ "National-Dex": "#190",
+ "Typ": "Normal"
+ },
+ "191": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/29/Sugimori_191.png"
+ ]
+ ],
+ "Deutsch": "Sonnkern",
+ "Englisch": "Sunkern",
+ "Farbe": "Gelb",
+ "Französisch": "Tournegrin",
+ "Gewicht": "1,8 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30d2\\u30de\\u30ca\\u30c3\\u30c4 (Himanuts)",
+ "Kategorie": "Samen",
+ "Link": "http://pokewiki.de/Sonnkern",
+ "National-Dex": "#191",
+ "Typ": "Pflanze"
+ },
+ "192": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d5/Sugimori_192.png"
+ ]
+ ],
+ "Deutsch": "Sonnflora",
+ "Englisch": "Sunflora",
+ "Farbe": "Gelb",
+ "Französisch": "H\\u00e9liatronc",
+ "Gewicht": "8,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30ad\\u30de\\u30ef\\u30ea (Kimawari)",
+ "Kategorie": "Sonne",
+ "Link": "http://pokewiki.de/Sonnflora",
+ "National-Dex": "#192",
+ "Typ": "Pflanze"
+ },
+ "193": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3d/Sugimori_193.png"
+ ]
+ ],
+ "Deutsch": "Yanma",
+ "Englisch": "Yanma",
+ "Farbe": "Rot",
+ "Französisch": "Yanma",
+ "Gewicht": "38,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30e4\\u30f3\\u30e4\\u30f3\\u30de (Yanyanma)",
+ "Kategorie": "Libelle",
+ "Link": "http://pokewiki.de/Yanma",
+ "National-Dex": "#193",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "194": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/63/Sugimori_194.png"
+ ]
+ ],
+ "Deutsch": "Felino",
+ "Englisch": "Wooper",
+ "Farbe": "Blau",
+ "Französisch": "Axoloto",
+ "Gewicht": "8,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30a6\\u30d1\\u30fc (Upah)",
+ "Kategorie": "Fisch",
+ "Link": "http://pokewiki.de/Felino",
+ "National-Dex": "#194",
+ "Typ": [
+ "Wasser",
+ "Boden"
+ ]
+ },
+ "195": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/17/Sugimori_195.png"
+ ]
+ ],
+ "Deutsch": "Morlord",
+ "Englisch": "Quagsire",
+ "Farbe": "Blau",
+ "Französisch": "Maraiste",
+ "Gewicht": "75,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30cc\\u30aa\\u30fc (Nuoh)",
+ "Kategorie": "Fisch",
+ "Link": "http://pokewiki.de/Morlord",
+ "National-Dex": "#195",
+ "Typ": [
+ "Wasser",
+ "Boden"
+ ]
+ },
+ "196": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3e/Sugimori_196.png"
+ ]
+ ],
+ "Deutsch": "Psiana",
+ "Englisch": "Espeon",
+ "Farbe": "Violett",
+ "Französisch": "Mentali",
+ "Gewicht": "26,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30a8\\u30fc\\u30d5\\u30a3 (Eifie)",
+ "Kategorie": "Sonne",
+ "Link": "http://pokewiki.de/Psiana",
+ "National-Dex": "#196",
+ "Typ": "Psycho"
+ },
+ "197": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/bf/Sugimori_197.png"
+ ]
+ ],
+ "Deutsch": "Nachtara",
+ "Englisch": "Umbreon",
+ "Farbe": "Schwarz",
+ "Französisch": "Noctali",
+ "Gewicht": "27,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30d6\\u30e9\\u30c3\\u30ad\\u30fc (Blacky)",
+ "Kategorie": "Mondschein",
+ "Link": "http://pokewiki.de/Nachtara",
+ "National-Dex": "#197",
+ "Typ": "Unlicht"
+ },
+ "198": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f2/Sugimori_198.png"
+ ]
+ ],
+ "Deutsch": "Kramurx",
+ "Englisch": "Murkrow",
+ "Farbe": "Schwarz",
+ "Französisch": "Corn\\u00e8bre",
+ "Gewicht": "2,1 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30e4\\u30df\\u30ab\\u30e9\\u30b9 (Yamikarasu)",
+ "Kategorie": "Finsternis",
+ "Link": "http://pokewiki.de/Kramurx",
+ "National-Dex": "#198",
+ "Typ": [
+ "Unlicht",
+ "Flug"
+ ]
+ },
+ "199": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8b/Sugimori_199.png"
+ ]
+ ],
+ "Deutsch": "Laschoking",
+ "Englisch": "Slowking",
+ "Farbe": "Rosa",
+ "Französisch": "Roigada",
+ "Gewicht": "79,5 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30e4\\u30c9\\u30ad\\u30f3\\u30b0 (Yadoking)",
+ "Kategorie": "Monarch",
+ "Link": "http://pokewiki.de/Laschoking",
+ "National-Dex": "#199",
+ "Typ": [
+ "Wasser",
+ "Psycho"
+ ]
+ },
+ "200": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/9c/Sugimori_200.png"
+ ]
+ ],
+ "Deutsch": "Traunfugil",
+ "Englisch": "Misdreavus",
+ "Farbe": "Grau",
+ "Französisch": "Feufor\\u00eave",
+ "Gewicht": "1,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30e0\\u30a6\\u30de (Muma)",
+ "Kategorie": "Kreischer",
+ "Link": "http://pokewiki.de/Traunfugil",
+ "National-Dex": "#200",
+ "Typ": "Geist"
+ },
+ "201": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/31/Sugimori_201.png"
+ ]
+ ],
+ "Deutsch": "Icognito",
+ "Englisch": "Unown",
+ "Farbe": "Schwarz",
+ "Französisch": "Zarbi",
+ "Gewicht": "5,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30a2\\u30f3\\u30ce\\u30fc\\u30f3 (Unknown)",
+ "Kategorie": "Symbol",
+ "Link": "http://pokewiki.de/Icognito",
+ "National-Dex": "#201",
+ "Typ": "Psycho"
+ },
+ "202": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/36/Sugimori_202.png"
+ ]
+ ],
+ "Deutsch": "Woingenau",
+ "Englisch": "Wobbuffet",
+ "Farbe": "Blau",
+ "Französisch": "Qulbutok\\u00e9",
+ "Gewicht": "28,5 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30bd\\u30fc\\u30ca\\u30f3\\u30b9 (Sonans)",
+ "Kategorie": "Geduld",
+ "Link": "http://pokewiki.de/Woingenau",
+ "National-Dex": "#202",
+ "Typ": "Psycho"
+ },
+ "203": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/84/Sugimori_203.png"
+ ]
+ ],
+ "Deutsch": "Girafarig",
+ "Englisch": "Girafarig",
+ "Farbe": "Gelb",
+ "Französisch": "Girafarig",
+ "Gewicht": "41,5 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30ad\\u30ea\\u30f3\\u30ea\\u30ad (Kirinriki)",
+ "Kategorie": "Langhals",
+ "Link": "http://pokewiki.de/Girafarig",
+ "National-Dex": "#203",
+ "Typ": [
+ "Normal",
+ "Psycho"
+ ]
+ },
+ "204": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1a/Sugimori_204.png"
+ ]
+ ],
+ "Deutsch": "Tannza",
+ "Englisch": "Pineco",
+ "Farbe": "Grau",
+ "Französisch": "Pomdepik",
+ "Gewicht": "7,2 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30af\\u30cc\\u30ae\\u30c0\\u30de (Kunugidama)",
+ "Kategorie": "Beutelwurm",
+ "Link": "http://pokewiki.de/Tannza",
+ "National-Dex": "#204",
+ "Typ": "K\\u00e4fer"
+ },
+ "205": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/bd/Sugimori_205.png"
+ ]
+ ],
+ "Deutsch": "Forstellka",
+ "Englisch": "Forretress",
+ "Farbe": "Violett",
+ "Französisch": "Foretress",
+ "Gewicht": "125,8 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30d5\\u30a9\\u30ec\\u30c8\\u30b9 (Foretos)",
+ "Kategorie": "Beutelwurm",
+ "Link": "http://pokewiki.de/Forstellka",
+ "National-Dex": "#205",
+ "Typ": [
+ "K\\u00e4fer",
+ "Stahl"
+ ]
+ },
+ "206": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d1/Sugimori_206.png"
+ ]
+ ],
+ "Deutsch": "Dummisel",
+ "Englisch": "Dunsparce",
+ "Farbe": "Gelb",
+ "Französisch": "Insolourdo",
+ "Gewicht": "14,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30ce\\u30b3\\u30c3\\u30c1 (Nokocchi)",
+ "Kategorie": "Schlange",
+ "Link": "http://pokewiki.de/Dummisel",
+ "National-Dex": "#206",
+ "Typ": "Normal"
+ },
+ "207": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1c/Sugimori_207.png"
+ ]
+ ],
+ "Deutsch": "Skorgla",
+ "Englisch": "Gligar",
+ "Farbe": "Violett",
+ "Französisch": "Scorplane",
+ "Gewicht": "64,8 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30b0\\u30e9\\u30a4\\u30ac\\u30fc (Gliger)",
+ "Kategorie": "Flugskorpi",
+ "Link": "http://pokewiki.de/Skorgla",
+ "National-Dex": "#207",
+ "Typ": [
+ "Boden",
+ "Flug"
+ ]
+ },
+ "208": {
+ "Bilder": [
+ [
+ "Stahlos",
+ "http://www.pokewiki.de/images/2/2e/Sugimori_208.png"
+ ],
+ [
+ "Mega-Stahlos",
+ "http://www.pokewiki.de/images/3/3d/Sugimori_208m1.png"
+ ]
+ ],
+ "Deutsch": "Stahlos",
+ "Englisch": "Steelix",
+ "Farbe": "Grau",
+ "Französisch": "Steelix",
+ "Gewicht": [
+ "400,0 kg",
+ "740,0 kg (Mega)"
+ ],
+ "Größe": [
+ "9,2 m",
+ "10,5 m (Mega)"
+ ],
+ "Japanisch": "\\u30cf\\u30ac\\u30cd\\u30fc\\u30eb (Haganeil)",
+ "Kategorie": "Stahlboa",
+ "Link": "http://pokewiki.de/Stahlos",
+ "National-Dex": "#208",
+ "Typ": [
+ "Stahl",
+ "Boden"
+ ]
+ },
+ "209": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2c/Sugimori_209.png"
+ ]
+ ],
+ "Deutsch": "Snubbull",
+ "Englisch": "Snubbull",
+ "Farbe": "Rosa",
+ "Französisch": "Snubbull",
+ "Gewicht": "7,8 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30d6\\u30eb\\u30fc (Bull)",
+ "Kategorie": "Fee",
+ "Link": "http://pokewiki.de/Snubbull",
+ "National-Dex": "#209",
+ "Typ": "Fee"
+ },
+ "210": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f5/Sugimori_210.png"
+ ]
+ ],
+ "Deutsch": "Granbull",
+ "Englisch": "Granbull",
+ "Farbe": "Violett",
+ "Französisch": "Granbull",
+ "Gewicht": "48,7 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30b0\\u30e9\\u30f3\\u30d6\\u30eb (Granbull)",
+ "Kategorie": "Fee",
+ "Link": "http://pokewiki.de/Granbull",
+ "National-Dex": "#210",
+ "Typ": "Fee"
+ },
+ "211": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ef/Sugimori_211.png"
+ ]
+ ],
+ "Deutsch": "Baldorfish",
+ "Englisch": "Qwilfish",
+ "Farbe": "Grau",
+ "Französisch": "Qwilfish",
+ "Gewicht": "3,9 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30cf\\u30ea\\u30fc\\u30bb\\u30f3 (Harysen)",
+ "Kategorie": "Ballon",
+ "Link": "http://pokewiki.de/Baldorfish",
+ "National-Dex": "#211",
+ "Typ": [
+ "Wasser",
+ "Gift"
+ ]
+ },
+ "212": {
+ "Bilder": [
+ [
+ "Scherox",
+ "http://www.pokewiki.de/images/7/7e/Sugimori_212.png"
+ ],
+ [
+ "Mega-Scherox",
+ "http://www.pokewiki.de/images/6/61/Sugimori_212m1.png"
+ ]
+ ],
+ "Deutsch": "Scherox",
+ "Englisch": "Scizor",
+ "Farbe": "Rot",
+ "Französisch": "Cizayox",
+ "Gewicht": [
+ "118,0 kg",
+ "125,0 kg (Mega)"
+ ],
+ "Größe": [
+ "1,8 m",
+ "2,0 m (Mega)"
+ ],
+ "Japanisch": "\\u30cf\\u30c3\\u30b5\\u30e0 (Hassam)",
+ "Kategorie": "Kneifer",
+ "Link": "http://pokewiki.de/Scherox",
+ "National-Dex": "#212",
+ "Typ": [
+ "K\\u00e4fer",
+ "Stahl"
+ ]
+ },
+ "213": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a4/Sugimori_213.png"
+ ]
+ ],
+ "Deutsch": "Pottrott",
+ "Englisch": "Shuckle",
+ "Farbe": "Gelb",
+ "Französisch": "Caratroc",
+ "Gewicht": "20,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c4\\u30dc\\u30c4\\u30dc (Tsubotsubo)",
+ "Kategorie": "Schimmel",
+ "Link": "http://pokewiki.de/Pottrott",
+ "National-Dex": "#213",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gestein"
+ ]
+ },
+ "214": {
+ "Bilder": [
+ [
+ "Skaraborn",
+ "http://www.pokewiki.de/images/6/63/Sugimori_214.png"
+ ],
+ [
+ "Mega-Skaraborn",
+ "http://www.pokewiki.de/images/f/f0/Sugimori_214m1.png"
+ ]
+ ],
+ "Deutsch": "Skaraborn",
+ "Englisch": "Heracross",
+ "Farbe": "Blau",
+ "Französisch": "Scarhino",
+ "Gewicht": [
+ "54,0 kg",
+ "62,5 kg (Mega)"
+ ],
+ "Größe": [
+ "1,5 m",
+ "1,7 m (Mega)"
+ ],
+ "Japanisch": "\\u30d8\\u30e9\\u30af\\u30ed\\u30b9 (Heracros)",
+ "Kategorie": "Einzelhorn",
+ "Link": "http://pokewiki.de/Skaraborn",
+ "National-Dex": "#214",
+ "Typ": [
+ "K\\u00e4fer",
+ "Kampf"
+ ]
+ },
+ "215": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/04/Sugimori_215.png"
+ ]
+ ],
+ "Deutsch": "Sniebel",
+ "Englisch": "Sneasel",
+ "Farbe": "Schwarz",
+ "Französisch": "Farfuret",
+ "Gewicht": "28,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30cb\\u30e5\\u30fc\\u30e9 (Nyula)",
+ "Kategorie": "Stichklaue",
+ "Link": "http://pokewiki.de/Sniebel",
+ "National-Dex": "#215",
+ "Typ": [
+ "Unlicht",
+ "Eis"
+ ]
+ },
+ "216": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/17/Sugimori_216.png"
+ ]
+ ],
+ "Deutsch": "Teddiursa",
+ "Englisch": "Teddiursa",
+ "Farbe": "Braun",
+ "Französisch": "Teddiursa",
+ "Gewicht": "8,8 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30d2\\u30e1\\u30b0\\u30de (Himeguma)",
+ "Kategorie": "Kleinb\\u00e4r",
+ "Link": "http://pokewiki.de/Teddiursa",
+ "National-Dex": "#216",
+ "Typ": "Normal"
+ },
+ "217": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/ce/Sugimori_217.png"
+ ]
+ ],
+ "Deutsch": "Ursaring",
+ "Englisch": "Ursaring",
+ "Farbe": "Braun",
+ "Französisch": "Ursaring",
+ "Gewicht": "125,8 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30ea\\u30f3\\u30b0\\u30de (Ringuma)",
+ "Kategorie": "Schl\\u00e4fer",
+ "Link": "http://pokewiki.de/Ursaring",
+ "National-Dex": "#217",
+ "Typ": "Normal"
+ },
+ "218": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ad/Sugimori_218.png"
+ ]
+ ],
+ "Deutsch": "Schneckmag",
+ "Englisch": "Slugma",
+ "Farbe": "Rot",
+ "Französisch": "Limagma",
+ "Gewicht": "35,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30de\\u30b0\\u30de\\u30c3\\u30b0 (Magmag)",
+ "Kategorie": "Lava",
+ "Link": "http://pokewiki.de/Schneckmag",
+ "National-Dex": "#218",
+ "Typ": "Feuer"
+ },
+ "219": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0f/Sugimori_219.png"
+ ]
+ ],
+ "Deutsch": "Magcargo",
+ "Englisch": "Magcargo",
+ "Farbe": "Rot",
+ "Französisch": "Volcaropod",
+ "Gewicht": "55,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30de\\u30b0\\u30ab\\u30eb\\u30b4 (Magcargot)",
+ "Kategorie": "Lava",
+ "Link": "http://pokewiki.de/Magcargo",
+ "National-Dex": "#219",
+ "Typ": [
+ "Feuer",
+ "Gestein"
+ ]
+ },
+ "220": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8c/Sugimori_220.png"
+ ]
+ ],
+ "Deutsch": "Quiekel",
+ "Englisch": "Swinub",
+ "Farbe": "Braun",
+ "Französisch": "Marcacrin",
+ "Gewicht": "6,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30a6\\u30ea\\u30e0\\u30fc (Urimoo)",
+ "Kategorie": "Ferkel",
+ "Link": "http://pokewiki.de/Quiekel",
+ "National-Dex": "#220",
+ "Typ": [
+ "Eis",
+ "Boden"
+ ]
+ },
+ "221": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/68/Sugimori_221.png"
+ ]
+ ],
+ "Deutsch": "Keifel",
+ "Englisch": "Piloswine",
+ "Farbe": "Braun",
+ "Französisch": "Cochignon",
+ "Gewicht": "55,8 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30a4\\u30ce\\u30e0\\u30fc (Inomoo)",
+ "Kategorie": "Schwein",
+ "Link": "http://pokewiki.de/Keifel",
+ "National-Dex": "#221",
+ "Typ": [
+ "Eis",
+ "Boden"
+ ]
+ },
+ "222": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0d/Sugimori_222.png"
+ ]
+ ],
+ "Deutsch": "Corasonn",
+ "Englisch": "Corsola",
+ "Farbe": "Rosa",
+ "Französisch": "Corayon",
+ "Gewicht": "5,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30b5\\u30cb\\u30fc\\u30b4 (Sunnygo)",
+ "Kategorie": "Koralle",
+ "Link": "http://pokewiki.de/Corasonn",
+ "National-Dex": "#222",
+ "Typ": [
+ "Wasser",
+ "Gestein"
+ ]
+ },
+ "223": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e2/Sugimori_223.png"
+ ]
+ ],
+ "Deutsch": "Remoraid",
+ "Englisch": "Remoraid",
+ "Farbe": "Grau",
+ "Französisch": "R\\u00e9moraid",
+ "Gewicht": "12,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c6\\u30c3\\u30dd\\u30a6\\u30aa (Teppouo)",
+ "Kategorie": "Hochdruck",
+ "Link": "http://pokewiki.de/Remoraid",
+ "National-Dex": "#223",
+ "Typ": "Wasser"
+ },
+ "224": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/69/Sugimori_224.png"
+ ]
+ ],
+ "Deutsch": "Octillery",
+ "Englisch": "Octillery",
+ "Farbe": "Rot",
+ "Französisch": "Octillery",
+ "Gewicht": "28,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30aa\\u30af\\u30bf\\u30f3 (Okutank)",
+ "Kategorie": "Hochdruck",
+ "Link": "http://pokewiki.de/Octillery",
+ "National-Dex": "#224",
+ "Typ": "Wasser"
+ },
+ "225": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/65/Sugimori_225.png"
+ ]
+ ],
+ "Deutsch": "Botogel",
+ "Englisch": "Delibird",
+ "Farbe": "Rot",
+ "Französisch": "Cadoizo",
+ "Gewicht": "16,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30c7\\u30ea\\u30d0\\u30fc\\u30c9 (Delibird)",
+ "Kategorie": "Lieferant",
+ "Link": "http://pokewiki.de/Botogel",
+ "National-Dex": "#225",
+ "Typ": [
+ "Eis",
+ "Flug"
+ ]
+ },
+ "226": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/af/Sugimori_226.png"
+ ]
+ ],
+ "Deutsch": "Mantax",
+ "Englisch": "Mantine",
+ "Farbe": "Violett",
+ "Französisch": "D\\u00e9manta",
+ "Gewicht": "220,0 kg",
+ "Größe": "2,1 m",
+ "Japanisch": "\\u30de\\u30f3\\u30bf\\u30a4\\u30f3 (Mantain)",
+ "Kategorie": "Flugrochen",
+ "Link": "http://pokewiki.de/Mantax",
+ "National-Dex": "#226",
+ "Typ": [
+ "Wasser",
+ "Flug"
+ ]
+ },
+ "227": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f2/Sugimori_227.png"
+ ]
+ ],
+ "Deutsch": "Panzaeron",
+ "Englisch": "Skarmory",
+ "Farbe": "Grau",
+ "Französisch": "Airmure",
+ "Gewicht": "50,5 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30a8\\u30a2\\u30fc\\u30e0\\u30c9 (Airmd)",
+ "Kategorie": "Flugstahl",
+ "Link": "http://pokewiki.de/Panzaeron",
+ "National-Dex": "#227",
+ "Typ": [
+ "Stahl",
+ "Flug"
+ ]
+ },
+ "228": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0f/Sugimori_228.png"
+ ]
+ ],
+ "Deutsch": "Hunduster",
+ "Englisch": "Houndour",
+ "Farbe": "Schwarz",
+ "Französisch": "Malosse",
+ "Gewicht": "10,8 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c7\\u30eb\\u30d3\\u30eb (Delvil)",
+ "Kategorie": "Hades",
+ "Link": "http://pokewiki.de/Hunduster",
+ "National-Dex": "#228",
+ "Typ": [
+ "Unlicht",
+ "Feuer"
+ ]
+ },
+ "229": {
+ "Bilder": [
+ [
+ "Hundemon",
+ "http://www.pokewiki.de/images/3/32/Sugimori_229.png"
+ ],
+ [
+ "Mega-Hundemon",
+ "http://www.pokewiki.de/images/f/f2/Sugimori_229m1.png"
+ ]
+ ],
+ "Deutsch": "Hundemon",
+ "Englisch": "Houndoom",
+ "Farbe": "Schwarz",
+ "Französisch": "D\\u00e9molosse",
+ "Gewicht": [
+ "35,0 kg",
+ "49,5 kg (Mega)"
+ ],
+ "Größe": [
+ "1,4 m",
+ "1,9 m (Mega)"
+ ],
+ "Japanisch": "\\u30d8\\u30eb\\u30ac\\u30fc (Hellgar)",
+ "Kategorie": "Hades",
+ "Link": "http://pokewiki.de/Hundemon",
+ "National-Dex": "#229",
+ "Typ": [
+ "Unlicht",
+ "Feuer"
+ ]
+ },
+ "230": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2d/Sugimori_230.png"
+ ]
+ ],
+ "Deutsch": "Seedraking",
+ "Englisch": "Kingdra",
+ "Farbe": "Blau",
+ "Französisch": "Hyporoi",
+ "Gewicht": "152,0 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30ad\\u30f3\\u30b0\\u30c9\\u30e9 (Kingdra)",
+ "Kategorie": "Drache",
+ "Link": "http://pokewiki.de/Seedraking",
+ "National-Dex": "#230",
+ "Typ": [
+ "Wasser",
+ "Drache"
+ ]
+ },
+ "231": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8f/Sugimori_231.png"
+ ]
+ ],
+ "Deutsch": "Phanpy",
+ "Englisch": "Phanpy",
+ "Farbe": "Blau",
+ "Französisch": "Phanpy",
+ "Gewicht": "33,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30b4\\u30de\\u30be\\u30a6 (Gomazou)",
+ "Kategorie": "Langr\\u00fcssel",
+ "Link": "http://pokewiki.de/Phanpy",
+ "National-Dex": "#231",
+ "Typ": "Boden"
+ },
+ "232": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c9/Sugimori_232.png"
+ ]
+ ],
+ "Deutsch": "Donphan",
+ "Englisch": "Donphan",
+ "Farbe": "Grau",
+ "Französisch": "Donphan",
+ "Gewicht": "120,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30c9\\u30f3\\u30d5\\u30a1\\u30f3 (Donfan)",
+ "Kategorie": "Panzer",
+ "Link": "http://pokewiki.de/Donphan",
+ "National-Dex": "#232",
+ "Typ": "Boden"
+ },
+ "233": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3d/Sugimori_233.png"
+ ]
+ ],
+ "Deutsch": "Porygon2",
+ "Englisch": "Porygon2",
+ "Farbe": "Rot",
+ "Französisch": "Porygon2",
+ "Gewicht": "32,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30dd\\u30ea\\u30b4\\u30f3\\uff12 (Porygon2)",
+ "Kategorie": "Virtuell",
+ "Link": "http://pokewiki.de/Porygon2",
+ "National-Dex": "#233",
+ "Typ": "Normal"
+ },
+ "234": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/23/Sugimori_234.png"
+ ]
+ ],
+ "Deutsch": "Damhirplex",
+ "Englisch": "Stantler",
+ "Farbe": "Braun",
+ "Französisch": "Cerfrousse",
+ "Gewicht": "71,2 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30aa\\u30c9\\u30b7\\u30b7 (Odoshishi)",
+ "Kategorie": "Vielender",
+ "Link": "http://pokewiki.de/Damhirplex",
+ "National-Dex": "#234",
+ "Typ": "Normal"
+ },
+ "235": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/33/Sugimori_235.png"
+ ]
+ ],
+ "Deutsch": "Farbeagle",
+ "Englisch": "Smeargle",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Queulorior",
+ "Gewicht": "58,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30c9\\u30fc\\u30d6\\u30eb (Doble)",
+ "Kategorie": "Maler",
+ "Link": "http://pokewiki.de/Farbeagle",
+ "National-Dex": "#235",
+ "Typ": "Normal"
+ },
+ "236": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4c/Sugimori_236.png"
+ ]
+ ],
+ "Deutsch": "Rabauz",
+ "Englisch": "Tyrogue",
+ "Farbe": "Violett",
+ "Französisch": "Debugant",
+ "Gewicht": "21,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30d0\\u30eb\\u30ad\\u30fc (Balkie)",
+ "Kategorie": "Racker",
+ "Link": "http://pokewiki.de/Rabauz",
+ "National-Dex": "#236",
+ "Typ": "Kampf"
+ },
+ "237": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3a/Sugimori_237.png"
+ ]
+ ],
+ "Deutsch": "Kapoera",
+ "Englisch": "Hitmontop",
+ "Farbe": "Braun",
+ "Französisch": "Kapoera",
+ "Gewicht": "48,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30ab\\u30dd\\u30a8\\u30e9\\u30fc (Kapoerer)",
+ "Kategorie": "Kopfstand",
+ "Link": "http://pokewiki.de/Kapoera",
+ "National-Dex": "#237",
+ "Typ": "Kampf"
+ },
+ "238": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/aa/Sugimori_238.png"
+ ]
+ ],
+ "Deutsch": "Kussilla",
+ "Englisch": "Smoochum",
+ "Farbe": "Rosa",
+ "Französisch": "Lippouti",
+ "Gewicht": "6,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30e0\\u30c1\\u30e5\\u30fc\\u30eb (Muchul)",
+ "Kategorie": "Kuss",
+ "Link": "http://pokewiki.de/Kussilla",
+ "National-Dex": "#238",
+ "Typ": [
+ "Eis",
+ "Psycho"
+ ]
+ },
+ "239": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/07/Sugimori_239.png"
+ ]
+ ],
+ "Deutsch": "Elekid",
+ "Englisch": "Elekid",
+ "Farbe": "Gelb",
+ "Französisch": "\\u00c9lekid",
+ "Gewicht": "23,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30a8\\u30ec\\u30ad\\u30c3\\u30c9 (Elekid)",
+ "Kategorie": "Elektro",
+ "Link": "http://pokewiki.de/Elekid",
+ "National-Dex": "#239",
+ "Typ": "Elektro"
+ },
+ "240": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d4/Sugimori_240.png"
+ ]
+ ],
+ "Deutsch": "Magby",
+ "Englisch": "Magby",
+ "Farbe": "Rot",
+ "Französisch": "Magby",
+ "Gewicht": "21,4 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30d6\\u30d3\\u30a3 (Buby)",
+ "Kategorie": "Glutherd",
+ "Link": "http://pokewiki.de/Magby",
+ "National-Dex": "#240",
+ "Typ": "Feuer"
+ },
+ "241": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/43/Sugimori_241.png"
+ ]
+ ],
+ "Deutsch": "Miltank",
+ "Englisch": "Miltank",
+ "Farbe": "Rosa",
+ "Französisch": "\\u00c9cr\\u00e9meuh",
+ "Gewicht": "75,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30df\\u30eb\\u30bf\\u30f3\\u30af (Miltank)",
+ "Kategorie": "Milchkuh",
+ "Link": "http://pokewiki.de/Miltank",
+ "National-Dex": "#241",
+ "Typ": "Normal"
+ },
+ "242": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a5/Sugimori_242.png"
+ ]
+ ],
+ "Deutsch": "Heiteira",
+ "Englisch": "Blissey",
+ "Farbe": "Rosa",
+ "Französisch": "Leuphorie",
+ "Gewicht": "46,8 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30cf\\u30d4\\u30ca\\u30b9 (Happinas)",
+ "Kategorie": "Freude",
+ "Link": "http://pokewiki.de/Heiteira",
+ "National-Dex": "#242",
+ "Typ": "Normal"
+ },
+ "243": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/16/Sugimori_243.png"
+ ]
+ ],
+ "Deutsch": "Raikou",
+ "Englisch": "Raikou",
+ "Farbe": "Gelb",
+ "Französisch": "Raikou",
+ "Gewicht": "178,0 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30e9\\u30a4\\u30b3\\u30a6 (Raikou)",
+ "Kategorie": "Donner",
+ "Link": "http://pokewiki.de/Raikou",
+ "National-Dex": "#243",
+ "Typ": "Elektro"
+ },
+ "244": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c6/Sugimori_244.png"
+ ]
+ ],
+ "Deutsch": "Entei",
+ "Englisch": "Entei",
+ "Farbe": "Braun",
+ "Französisch": "Entei",
+ "Gewicht": "198,0 kg",
+ "Größe": "2,1 m",
+ "Japanisch": "\\u30a8\\u30f3\\u30c6\\u30a4 (Entei)",
+ "Kategorie": "Vulkan",
+ "Link": "http://pokewiki.de/Entei",
+ "National-Dex": "#244",
+ "Typ": "Feuer"
+ },
+ "245": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/76/Sugimori_245.png"
+ ]
+ ],
+ "Deutsch": "Suicune",
+ "Englisch": "Suicune",
+ "Farbe": "Blau",
+ "Französisch": "Suicune",
+ "Gewicht": "187,0 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30b9\\u30a4\\u30af\\u30f3 (Suicune)",
+ "Kategorie": "Polarlicht",
+ "Link": "http://pokewiki.de/Suicune",
+ "National-Dex": "#245",
+ "Typ": "Wasser"
+ },
+ "246": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4b/Sugimori_246.png"
+ ]
+ ],
+ "Deutsch": "Larvitar",
+ "Englisch": "Larvitar",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Embrylex",
+ "Gewicht": "72,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e8\\u30fc\\u30ae\\u30e9\\u30b9 (Yogiras)",
+ "Kategorie": "Felshaut",
+ "Link": "http://pokewiki.de/Larvitar",
+ "National-Dex": "#246",
+ "Typ": [
+ "Gestein",
+ "Boden"
+ ]
+ },
+ "247": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/9f/Sugimori_247.png"
+ ]
+ ],
+ "Deutsch": "Pupitar",
+ "Englisch": "Pupitar",
+ "Farbe": "Grau",
+ "Französisch": "Ymphect",
+ "Gewicht": "152,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30b5\\u30ca\\u30ae\\u30e9\\u30b9 (Sanagirasu)",
+ "Kategorie": "Hartschale",
+ "Link": "http://pokewiki.de/Pupitar",
+ "National-Dex": "#247",
+ "Typ": [
+ "Gestein",
+ "Boden"
+ ]
+ },
+ "248": {
+ "Bilder": [
+ [
+ "Despotar",
+ "http://www.pokewiki.de/images/c/c7/Sugimori_248.png"
+ ],
+ [
+ "Mega-Despotar",
+ "http://www.pokewiki.de/images/8/84/Sugimori_248m1.png"
+ ]
+ ],
+ "Deutsch": "Despotar",
+ "Englisch": "Tyranitar",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Tyranocif",
+ "Gewicht": [
+ "202,0 kg",
+ "255,0 kg (Mega)"
+ ],
+ "Größe": [
+ "2,0 m",
+ "2,5 m (Mega)"
+ ],
+ "Japanisch": "\\u30d0\\u30f3\\u30ae\\u30e9\\u30b9 (Bangiras)",
+ "Kategorie": "Panzer",
+ "Link": "http://pokewiki.de/Despotar",
+ "National-Dex": "#248",
+ "Typ": [
+ "Gestein",
+ "Unlicht"
+ ]
+ },
+ "249": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b5/Sugimori_249.png"
+ ]
+ ],
+ "Deutsch": "Lugia",
+ "Englisch": "Lugia",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Lugia",
+ "Gewicht": "216,0 kg",
+ "Größe": "5,2 m",
+ "Japanisch": "\\u30eb\\u30ae\\u30a2 (Rugia)",
+ "Kategorie": "Taucher",
+ "Link": "http://pokewiki.de/Lugia",
+ "National-Dex": "#249",
+ "Typ": [
+ "Psycho",
+ "Flug"
+ ]
+ },
+ "250": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/28/Sugimori_250.png"
+ ]
+ ],
+ "Deutsch": "Ho-Oh",
+ "Englisch": "Ho-Oh",
+ "Farbe": "Rot",
+ "Französisch": "Ho-Oh",
+ "Gewicht": "199,0 kg",
+ "Größe": "3,8 m",
+ "Japanisch": "\\u30db\\u30a6\\u30aa\\u30a6 (Houou)",
+ "Kategorie": "Regenbogen",
+ "Link": "http://pokewiki.de/Ho-Oh",
+ "National-Dex": "#250",
+ "Typ": [
+ "Feuer",
+ "Flug"
+ ]
+ },
+ "251": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a9/Sugimori_251.png"
+ ]
+ ],
+ "Deutsch": "Celebi",
+ "Englisch": "Celebi",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "C\\u00e9l\\u00e9bi",
+ "Gewicht": "5,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30bb\\u30ec\\u30d3\\u30a3 (Celebi)",
+ "Kategorie": "Zeitreise",
+ "Link": "http://pokewiki.de/Celebi",
+ "National-Dex": "#251",
+ "Typ": [
+ "Psycho",
+ "Pflanze"
+ ]
+ },
+ "252": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/86/Sugimori_252.png"
+ ]
+ ],
+ "Deutsch": "Geckarbor",
+ "Englisch": "Treecko",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Arcko",
+ "Gewicht": "5,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30ad\\u30e2\\u30ea (Kimori)",
+ "Kategorie": "Waldgecko",
+ "Link": "http://pokewiki.de/Geckarbor",
+ "National-Dex": "#252",
+ "Typ": "Pflanze"
+ },
+ "253": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2c/Sugimori_253.png"
+ ]
+ ],
+ "Deutsch": "Reptain",
+ "Englisch": "Grovyle",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Massko",
+ "Gewicht": "21,6 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30b8\\u30e5\\u30d7\\u30c8\\u30eb (Juptile)",
+ "Kategorie": "Waldgecko",
+ "Link": "http://pokewiki.de/Reptain",
+ "National-Dex": "#253",
+ "Typ": "Pflanze"
+ },
+ "254": {
+ "Bilder": [
+ [
+ "Gewaldro",
+ "http://www.pokewiki.de/images/0/07/Sugimori_254.png"
+ ],
+ [
+ "Mega-Gewaldro",
+ "http://www.pokewiki.de/images/3/30/Sugimori_254m1.png"
+ ]
+ ],
+ "Deutsch": "Gewaldro",
+ "Englisch": "Sceptile",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Jungko",
+ "Gewicht": [
+ "52,2 kg",
+ "55,2 kg (Mega)"
+ ],
+ "Größe": [
+ "1,7 m",
+ "1,9 m (Mega)"
+ ],
+ "Japanisch": "\\u30b8\\u30e5\\u30ab\\u30a4\\u30f3 (Jukain)",
+ "Kategorie": "Dschungel",
+ "Link": "http://pokewiki.de/Gewaldro",
+ "National-Dex": "#254",
+ "Typ": [
+ "Pflanze",
+ "Pflanze",
+ "Drache (Mega)"
+ ]
+ },
+ "255": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ab/Sugimori_255.png"
+ ]
+ ],
+ "Deutsch": "Flemmli",
+ "Englisch": "Torchic",
+ "Farbe": "Rot",
+ "Französisch": "Poussifeu",
+ "Gewicht": "2,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30a2\\u30c1\\u30e3\\u30e2 (Achamo)",
+ "Kategorie": "K\\u00fcken",
+ "Link": "http://pokewiki.de/Flemmli",
+ "National-Dex": "#255",
+ "Typ": "Feuer"
+ },
+ "256": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/93/Sugimori_256.png"
+ ]
+ ],
+ "Deutsch": "Jungglut",
+ "Englisch": "Combusken",
+ "Farbe": "Rot",
+ "Französisch": "Galifeu",
+ "Gewicht": "19,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30ef\\u30ab\\u30b7\\u30e3\\u30e2 (Wakasyamo)",
+ "Kategorie": "Kleinhahn",
+ "Link": "http://pokewiki.de/Jungglut",
+ "National-Dex": "#256",
+ "Typ": [
+ "Feuer",
+ "Kampf"
+ ]
+ },
+ "257": {
+ "Bilder": [
+ [
+ "Lohgock",
+ "http://www.pokewiki.de/images/c/c8/Sugimori_257.png"
+ ],
+ [
+ "Mega-Lohgock",
+ "http://www.pokewiki.de/images/4/46/Sugimori_257m1.png"
+ ]
+ ],
+ "Deutsch": "Lohgock",
+ "Englisch": "Blaziken",
+ "Farbe": "Rot",
+ "Französisch": "Bras\\u00e9gali",
+ "Gewicht": "52,0 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30d0\\u30b7\\u30e3\\u30fc\\u30e2 (Bursyamo)",
+ "Kategorie": "Gro\\u00dfbrand",
+ "Link": "http://pokewiki.de/Lohgock",
+ "National-Dex": "#257",
+ "Typ": [
+ "Feuer",
+ "Kampf"
+ ]
+ },
+ "258": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1a/Sugimori_258.png"
+ ]
+ ],
+ "Deutsch": "Hydropi",
+ "Englisch": "Mudkip",
+ "Farbe": "Blau",
+ "Französisch": "Gobou",
+ "Gewicht": "7,6 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30df\\u30ba\\u30b4\\u30ed\\u30a6 (Mizugorou)",
+ "Kategorie": "Lehmh\\u00fcpfer",
+ "Link": "http://pokewiki.de/Hydropi",
+ "National-Dex": "#258",
+ "Typ": "Wasser"
+ },
+ "259": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/de/Sugimori_259.png"
+ ]
+ ],
+ "Deutsch": "Moorabbel",
+ "Englisch": "Marshtomp",
+ "Farbe": "Blau",
+ "Französisch": "Flobio",
+ "Gewicht": "28,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30cc\\u30de\\u30af\\u30ed\\u30fc (Numacraw)",
+ "Kategorie": "Lehmh\\u00fcpfer",
+ "Link": "http://pokewiki.de/Moorabbel",
+ "National-Dex": "#259",
+ "Typ": [
+ "Wasser",
+ "Boden"
+ ]
+ },
+ "260": {
+ "Bilder": [
+ [
+ "Sumpex",
+ "http://www.pokewiki.de/images/a/a7/Sugimori_260.png"
+ ],
+ [
+ "Mega-Sumpex",
+ "http://www.pokewiki.de/images/a/ab/Sugimori_260m1.png"
+ ]
+ ],
+ "Deutsch": "Sumpex",
+ "Englisch": "Swampert",
+ "Farbe": "Blau",
+ "Französisch": "Laggron",
+ "Gewicht": [
+ "81,9 kg",
+ "102,0 kg (Mega)"
+ ],
+ "Größe": [
+ "1,5 m",
+ "1,9 m (Mega)"
+ ],
+ "Japanisch": "\\u30e9\\u30b0\\u30e9\\u30fc\\u30b8 (Laglarge)",
+ "Kategorie": "Lehmh\\u00fcpfer",
+ "Link": "http://pokewiki.de/Sumpex",
+ "National-Dex": "#260",
+ "Typ": [
+ "Wasser",
+ "Boden"
+ ]
+ },
+ "261": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/af/Sugimori_261.png"
+ ]
+ ],
+ "Deutsch": "Fiffyen",
+ "Englisch": "Poochyena",
+ "Farbe": "Grau",
+ "Französisch": "Medhy\\u00e8na",
+ "Gewicht": "13,6 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30dd\\u30c1\\u30a8\\u30ca (Pochiena)",
+ "Kategorie": "Biss",
+ "Link": "http://pokewiki.de/Fiffyen",
+ "National-Dex": "#261",
+ "Typ": "Unlicht"
+ },
+ "262": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/75/Sugimori_262.png"
+ ]
+ ],
+ "Deutsch": "Magnayen",
+ "Englisch": "Mightyena",
+ "Farbe": "Grau",
+ "Französisch": "Grahy\\u00e8na",
+ "Gewicht": "37,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b0\\u30e9\\u30a8\\u30ca (Guraena)",
+ "Kategorie": "Biss",
+ "Link": "http://pokewiki.de/Magnayen",
+ "National-Dex": "#262",
+ "Typ": "Unlicht"
+ },
+ "263": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f7/Sugimori_263.png"
+ ]
+ ],
+ "Deutsch": "Zigzachs",
+ "Englisch": "Zigzagoon",
+ "Farbe": "Braun",
+ "Französisch": "Zigzaton",
+ "Gewicht": "17,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30b8\\u30b0\\u30b6\\u30b0\\u30de (Jiguzaguma)",
+ "Kategorie": "Kleindachs",
+ "Link": "http://pokewiki.de/Zigzachs",
+ "National-Dex": "#263",
+ "Typ": "Normal"
+ },
+ "264": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ec/Sugimori_264.png"
+ ]
+ ],
+ "Deutsch": "Geradaks",
+ "Englisch": "Linoone",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Lin\\u00e9on",
+ "Gewicht": "32,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30de\\u30c3\\u30b9\\u30b0\\u30de (Massuguma)",
+ "Kategorie": "Sprinter",
+ "Link": "http://pokewiki.de/Geradaks",
+ "National-Dex": "#264",
+ "Typ": "Normal"
+ },
+ "265": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ad/Sugimori_265.png"
+ ]
+ ],
+ "Deutsch": "Waumpel",
+ "Englisch": "Wurmple",
+ "Farbe": "Rot",
+ "Französisch": "Chenipotte",
+ "Gewicht": "3,6 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30b1\\u30e0\\u30c3\\u30bd (Kemusso)",
+ "Kategorie": "Wurm",
+ "Link": "http://pokewiki.de/Waumpel",
+ "National-Dex": "#265",
+ "Typ": "K\\u00e4fer"
+ },
+ "266": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5a/Sugimori_266.png"
+ ]
+ ],
+ "Deutsch": "Schaloko",
+ "Englisch": "Silcoon",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Armulys",
+ "Gewicht": "10,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30ab\\u30e9\\u30b5\\u30ea\\u30b9 (Karasarisu)",
+ "Kategorie": "Kokon",
+ "Link": "http://pokewiki.de/Schaloko",
+ "National-Dex": "#266",
+ "Typ": "K\\u00e4fer"
+ },
+ "267": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ab/Sugimori_267.png"
+ ]
+ ],
+ "Deutsch": "Papinella",
+ "Englisch": "Beautifly",
+ "Farbe": "Gelb",
+ "Französisch": "Charmillon",
+ "Gewicht": "28,4 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30a2\\u30b2\\u30cf\\u30f3\\u30c8 (Agehanto)",
+ "Kategorie": "Falter",
+ "Link": "http://pokewiki.de/Papinella",
+ "National-Dex": "#267",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "268": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/57/Sugimori_268.png"
+ ]
+ ],
+ "Deutsch": "Panekon",
+ "Englisch": "Cascoon",
+ "Farbe": "Violett",
+ "Französisch": "Blindalys",
+ "Gewicht": "11,5 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30de\\u30e6\\u30eb\\u30c9 (Mayuld)",
+ "Kategorie": "Kokon",
+ "Link": "http://pokewiki.de/Panekon",
+ "National-Dex": "#268",
+ "Typ": "K\\u00e4fer"
+ },
+ "269": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a3/Sugimori_269.png"
+ ]
+ ],
+ "Deutsch": "Pudox",
+ "Englisch": "Dustox",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Papinox",
+ "Gewicht": "31,6 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30c9\\u30af\\u30b1\\u30a4\\u30eb (Dokucale)",
+ "Kategorie": "Giftmotte",
+ "Link": "http://pokewiki.de/Pudox",
+ "National-Dex": "#269",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gift"
+ ]
+ },
+ "270": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/08/Sugimori_270.png"
+ ]
+ ],
+ "Deutsch": "Loturzel",
+ "Englisch": "Lotad",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "N\\u00e9nupiot",
+ "Gewicht": "2,6 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30cf\\u30b9\\u30dc\\u30fc (Hassboh)",
+ "Kategorie": "Wasserlinse",
+ "Link": "http://pokewiki.de/Loturzel",
+ "National-Dex": "#270",
+ "Typ": [
+ "Wasser",
+ "Pflanze"
+ ]
+ },
+ "271": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/47/Sugimori_271.png"
+ ]
+ ],
+ "Deutsch": "Lombrero",
+ "Englisch": "Lombre",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Lombre",
+ "Gewicht": "32,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30cf\\u30b9\\u30d6\\u30ec\\u30ed (Hasubrero)",
+ "Kategorie": "Frohmut",
+ "Link": "http://pokewiki.de/Lombrero",
+ "National-Dex": "#271",
+ "Typ": [
+ "Wasser",
+ "Pflanze"
+ ]
+ },
+ "272": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d4/Sugimori_272.png"
+ ]
+ ],
+ "Deutsch": "Kappalores",
+ "Englisch": "Ludicolo",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Ludicolo",
+ "Gewicht": "55,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30eb\\u30f3\\u30d1\\u30c3\\u30d1 (Runpappa)",
+ "Kategorie": "Sorglos",
+ "Link": "http://pokewiki.de/Kappalores",
+ "National-Dex": "#272",
+ "Typ": [
+ "Wasser",
+ "Pflanze"
+ ]
+ },
+ "273": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/67/Sugimori_273.png"
+ ]
+ ],
+ "Deutsch": "Samurzel",
+ "Englisch": "Seedot",
+ "Farbe": "Braun",
+ "Französisch": "Grainipiot",
+ "Gewicht": "4,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30bf\\u30cd\\u30dc\\u30fc (Taneboh)",
+ "Kategorie": "Eichelnuss",
+ "Link": "http://pokewiki.de/Samurzel",
+ "National-Dex": "#273",
+ "Typ": "Pflanze"
+ },
+ "274": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/79/Sugimori_274.png"
+ ]
+ ],
+ "Deutsch": "Blanas",
+ "Englisch": "Nuzleaf",
+ "Farbe": "Braun",
+ "Französisch": "Pifeuil",
+ "Gewicht": "28,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b3\\u30ce\\u30cf\\u30ca (Konohana)",
+ "Kategorie": "Hinterlist",
+ "Link": "http://pokewiki.de/Blanas",
+ "National-Dex": "#274",
+ "Typ": [
+ "Pflanze",
+ "Unlicht"
+ ]
+ },
+ "275": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/9d/Sugimori_275.png"
+ ]
+ ],
+ "Deutsch": "Tengulist",
+ "Englisch": "Shiftry",
+ "Farbe": "Braun",
+ "Französisch": "Tengalice",
+ "Gewicht": "59,6 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30c0\\u30fc\\u30c6\\u30f3\\u30b0 (Dirteng)",
+ "Kategorie": "Verschlagen",
+ "Link": "http://pokewiki.de/Tengulist",
+ "National-Dex": "#275",
+ "Typ": [
+ "Pflanze",
+ "Unlicht"
+ ]
+ },
+ "276": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6b/Sugimori_276.png"
+ ]
+ ],
+ "Deutsch": "Schwalbini",
+ "Englisch": "Taillow",
+ "Farbe": "Blau",
+ "Französisch": "Nirondelle",
+ "Gewicht": "2,3 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30b9\\u30d0\\u30e1 (Subame)",
+ "Kategorie": "Schw\\u00e4lblein",
+ "Link": "http://pokewiki.de/Schwalbini",
+ "National-Dex": "#276",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "277": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/37/Sugimori_277.png"
+ ]
+ ],
+ "Deutsch": "Schwalboss",
+ "Englisch": "Swellow",
+ "Farbe": "Blau",
+ "Französisch": "H\\u00e9l\\u00e9delle",
+ "Gewicht": "19,8 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30aa\\u30aa\\u30b9\\u30d0\\u30e1 (Ohsubame)",
+ "Kategorie": "Schwalbe",
+ "Link": "http://pokewiki.de/Schwalboss",
+ "National-Dex": "#277",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "278": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f8/Sugimori_278.png"
+ ]
+ ],
+ "Deutsch": "Wingull",
+ "Englisch": "Wingull",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Go\\u00e9lise",
+ "Gewicht": "9,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30ad\\u30e3\\u30e2\\u30e1 (Camome)",
+ "Kategorie": "Seem\\u00f6we",
+ "Link": "http://pokewiki.de/Wingull",
+ "National-Dex": "#278",
+ "Typ": [
+ "Wasser",
+ "Flug"
+ ]
+ },
+ "279": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/26/Sugimori_279.png"
+ ]
+ ],
+ "Deutsch": "Pelipper",
+ "Englisch": "Pelipper",
+ "Farbe": "Gelb",
+ "Französisch": "Bekipan",
+ "Gewicht": "28,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30da\\u30ea\\u30c3\\u30d1\\u30fc (Pelipper)",
+ "Kategorie": "Wasservogel",
+ "Link": "http://pokewiki.de/Pelipper",
+ "National-Dex": "#279",
+ "Typ": [
+ "Wasser",
+ "Flug"
+ ]
+ },
+ "280": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/dd/Sugimori_280.png"
+ ]
+ ],
+ "Deutsch": "Trasla",
+ "Englisch": "Ralts",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Tarsal",
+ "Gewicht": "6,6 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30e9\\u30eb\\u30c8\\u30b9 (Ralts)",
+ "Kategorie": "Gef\\u00fchl",
+ "Link": "http://pokewiki.de/Trasla",
+ "National-Dex": "#280",
+ "Typ": [
+ "Psycho",
+ "Fee"
+ ]
+ },
+ "281": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2d/Sugimori_281.png"
+ ]
+ ],
+ "Deutsch": "Kirlia",
+ "Englisch": "Kirlia",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Kirlia",
+ "Gewicht": "20,2 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30ad\\u30eb\\u30ea\\u30a2 (Kirlia)",
+ "Kategorie": "Emotion",
+ "Link": "http://pokewiki.de/Kirlia",
+ "National-Dex": "#281",
+ "Typ": [
+ "Psycho",
+ "Fee"
+ ]
+ },
+ "282": {
+ "Bilder": [
+ [
+ "Guardevoir",
+ "http://www.pokewiki.de/images/c/c5/Sugimori_282.png"
+ ],
+ [
+ "Mega-Guardevoir",
+ "http://www.pokewiki.de/images/f/fc/Sugimori_282m1.png"
+ ]
+ ],
+ "Deutsch": "Guardevoir",
+ "Englisch": "Gardevoir",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Gardevoir",
+ "Gewicht": "48,4 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30b5\\u30fc\\u30ca\\u30a4\\u30c8 (Sirknight)",
+ "Kategorie": "Umarmung",
+ "Link": "http://pokewiki.de/Guardevoir",
+ "National-Dex": "#282",
+ "Typ": [
+ "Psycho",
+ "Fee"
+ ]
+ },
+ "283": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1f/Sugimori_283.png"
+ ]
+ ],
+ "Deutsch": "Gehweiher",
+ "Englisch": "Surskit",
+ "Farbe": "Blau",
+ "Französisch": "Arakdo",
+ "Gewicht": "1,7 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30a2\\u30e1\\u30bf\\u30de (Ametama)",
+ "Kategorie": "Wassergeher",
+ "Link": "http://pokewiki.de/Gehweiher",
+ "National-Dex": "#283",
+ "Typ": [
+ "K\\u00e4fer",
+ "Wasser"
+ ]
+ },
+ "284": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d9/Sugimori_284.png"
+ ]
+ ],
+ "Deutsch": "Maskeregen",
+ "Englisch": "Masquerain",
+ "Farbe": "Blau",
+ "Französisch": "Maskadra",
+ "Gewicht": "3,6 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30a2\\u30e1\\u30e2\\u30fc\\u30b9 (Amemoth)",
+ "Kategorie": "Auge",
+ "Link": "http://pokewiki.de/Maskeregen",
+ "National-Dex": "#284",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "285": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5b/Sugimori_285.png"
+ ]
+ ],
+ "Deutsch": "Knilz",
+ "Englisch": "Shroomish",
+ "Farbe": "Braun",
+ "Französisch": "Balignon",
+ "Gewicht": "4,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30ad\\u30ce\\u30b3\\u30b3 (Kinococo)",
+ "Kategorie": "Pilz",
+ "Link": "http://pokewiki.de/Knilz",
+ "National-Dex": "#285",
+ "Typ": "Pflanze"
+ },
+ "286": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b9/Sugimori_286.png"
+ ]
+ ],
+ "Deutsch": "Kapilz",
+ "Englisch": "Breloom",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Chapignon",
+ "Gewicht": "39,2 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30ad\\u30ce\\u30ac\\u30c3\\u30b5 (Kinogassa)",
+ "Kategorie": "Pilz",
+ "Link": "http://pokewiki.de/Kapilz",
+ "National-Dex": "#286",
+ "Typ": [
+ "Pflanze",
+ "Kampf"
+ ]
+ },
+ "287": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/38/Sugimori_287.png"
+ ]
+ ],
+ "Deutsch": "Bummelz",
+ "Englisch": "Slakoth",
+ "Farbe": "Braun",
+ "Französisch": "Parecool",
+ "Gewicht": "24,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30ca\\u30de\\u30b1\\u30ed (Namakero)",
+ "Kategorie": "Faulpelz",
+ "Link": "http://pokewiki.de/Bummelz",
+ "National-Dex": "#287",
+ "Typ": "Normal"
+ },
+ "288": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f5/Sugimori_288.png"
+ ]
+ ],
+ "Deutsch": "Muntier",
+ "Englisch": "Vigoroth",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Vigoroth",
+ "Gewicht": "46,5 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30e4\\u30eb\\u30ad\\u30e2\\u30ce (Yarukimono)",
+ "Kategorie": "Wildaffe",
+ "Link": "http://pokewiki.de/Muntier",
+ "National-Dex": "#288",
+ "Typ": "Normal"
+ },
+ "289": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3f/Sugimori_289.png"
+ ]
+ ],
+ "Deutsch": "Letarking",
+ "Englisch": "Slaking",
+ "Farbe": "Braun",
+ "Französisch": "Monafl\\u00e8mit",
+ "Gewicht": "130,5 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30b1\\u30c3\\u30ad\\u30f3\\u30b0 (Kekking)",
+ "Kategorie": "M\\u00fc\\u00dfig",
+ "Link": "http://pokewiki.de/Letarking",
+ "National-Dex": "#289",
+ "Typ": "Normal"
+ },
+ "290": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/89/Sugimori_290.png"
+ ]
+ ],
+ "Deutsch": "Nincada",
+ "Englisch": "Nincada",
+ "Farbe": "Grau",
+ "Französisch": "Ningale",
+ "Gewicht": "5,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30c4\\u30c1\\u30cb\\u30f3 (Tutinin)",
+ "Kategorie": "Vorbereiter",
+ "Link": "http://pokewiki.de/Nincada",
+ "National-Dex": "#290",
+ "Typ": [
+ "K\\u00e4fer",
+ "Boden"
+ ]
+ },
+ "291": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/72/Sugimori_291.png"
+ ]
+ ],
+ "Deutsch": "Ninjask",
+ "Englisch": "Ninjask",
+ "Farbe": "Gelb",
+ "Französisch": "Ninjask",
+ "Gewicht": "12,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30c6\\u30c3\\u30ab\\u30cb\\u30f3 (Tekkanin)",
+ "Kategorie": "Ninja",
+ "Link": "http://pokewiki.de/Ninjask",
+ "National-Dex": "#291",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "292": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4c/Sugimori_292.png"
+ ]
+ ],
+ "Deutsch": "Ninjatom",
+ "Englisch": "Shedinja",
+ "Farbe": "Braun",
+ "Französisch": "Munja",
+ "Gewicht": "1,2 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30cc\\u30b1\\u30cb\\u30f3 (Nukenin)",
+ "Kategorie": "H\\u00e4utung",
+ "Link": "http://pokewiki.de/Ninjatom",
+ "National-Dex": "#292",
+ "Typ": [
+ "K\\u00e4fer",
+ "Geist"
+ ]
+ },
+ "293": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/69/Sugimori_293.png"
+ ]
+ ],
+ "Deutsch": "Flurmel",
+ "Englisch": "Whismur",
+ "Farbe": "Rosa",
+ "Französisch": "Chuchmur",
+ "Gewicht": "16,3 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30b4\\u30cb\\u30e7\\u30cb\\u30e7 (Gonyonyo)",
+ "Kategorie": "Fl\\u00fcster",
+ "Link": "http://pokewiki.de/Flurmel",
+ "National-Dex": "#293",
+ "Typ": "Normal"
+ },
+ "294": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/14/Sugimori_294.png"
+ ]
+ ],
+ "Deutsch": "Krakeelo",
+ "Englisch": "Loudred",
+ "Farbe": "Blau",
+ "Französisch": "Ramboum",
+ "Gewicht": "40,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30c9\\u30b4\\u30fc\\u30e0 (Dogohmb)",
+ "Kategorie": "Lauthals",
+ "Link": "http://pokewiki.de/Krakeelo",
+ "National-Dex": "#294",
+ "Typ": "Normal"
+ },
+ "295": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4d/Sugimori_295.png"
+ ]
+ ],
+ "Deutsch": "Krawumms",
+ "Englisch": "Exploud",
+ "Farbe": "Blau",
+ "Französisch": "Brouhabam",
+ "Gewicht": "84,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30d0\\u30af\\u30aa\\u30f3\\u30b0 (Bakuong)",
+ "Kategorie": "Krach",
+ "Link": "http://pokewiki.de/Krawumms",
+ "National-Dex": "#295",
+ "Typ": "Normal"
+ },
+ "296": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/34/Sugimori_296.png"
+ ]
+ ],
+ "Deutsch": "Makuhita",
+ "Englisch": "Makuhita",
+ "Farbe": "Gelb",
+ "Französisch": "Makuhita",
+ "Gewicht": "86,4 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30de\\u30af\\u30ce\\u30b7\\u30bf (Makunoshita)",
+ "Kategorie": "Courage",
+ "Link": "http://pokewiki.de/Makuhita",
+ "National-Dex": "#296",
+ "Typ": "Kampf"
+ },
+ "297": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/33/Sugimori_297.png"
+ ]
+ ],
+ "Deutsch": "Hariyama",
+ "Englisch": "Hariyama",
+ "Farbe": "Braun",
+ "Französisch": "Hariyama",
+ "Gewicht": "253,8 kg",
+ "Größe": "2,3 m",
+ "Japanisch": "\\u30cf\\u30ea\\u30c6\\u30e4\\u30de (Hariteyama)",
+ "Kategorie": "Armwurf",
+ "Link": "http://pokewiki.de/Hariyama",
+ "National-Dex": "#297",
+ "Typ": "Kampf"
+ },
+ "298": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/93/Sugimori_298.png"
+ ]
+ ],
+ "Deutsch": "Azurill",
+ "Englisch": "Azurill",
+ "Farbe": "Blau",
+ "Französisch": "Azurill",
+ "Gewicht": "2,0 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30eb\\u30ea\\u30ea (Ruriri)",
+ "Kategorie": "Gepunktet",
+ "Link": "http://pokewiki.de/Azurill",
+ "National-Dex": "#298",
+ "Typ": [
+ "Normal",
+ "Fee"
+ ]
+ },
+ "299": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/68/Sugimori_299.png"
+ ]
+ ],
+ "Deutsch": "Nasgnet",
+ "Englisch": "Nosepass",
+ "Farbe": "Grau",
+ "Französisch": "Tarinor",
+ "Gewicht": "97,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30ce\\u30ba\\u30d1\\u30b9 (Nosepass)",
+ "Kategorie": "Kompass",
+ "Link": "http://pokewiki.de/Nasgnet",
+ "National-Dex": "#299",
+ "Typ": "Gestein"
+ },
+ "300": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/bd/Sugimori_300.png"
+ ]
+ ],
+ "Deutsch": "Eneco",
+ "Englisch": "Skitty",
+ "Farbe": "Rosa",
+ "Französisch": "Skitty",
+ "Gewicht": "11,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30a8\\u30cd\\u30b3 (Eneco)",
+ "Kategorie": "K\\u00e4tzchen",
+ "Link": "http://pokewiki.de/Eneco",
+ "National-Dex": "#300",
+ "Typ": "Normal"
+ },
+ "301": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/bb/Sugimori_301.png"
+ ]
+ ],
+ "Deutsch": "Enekoro",
+ "Englisch": "Delcatty",
+ "Farbe": "Violett",
+ "Französisch": "Delcatty",
+ "Gewicht": "32,6 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30a8\\u30cd\\u30b3\\u30ed\\u30ed (Enekororo)",
+ "Kategorie": "Eingebildet",
+ "Link": "http://pokewiki.de/Enekoro",
+ "National-Dex": "#301",
+ "Typ": "Normal"
+ },
+ "302": {
+ "Bilder": [
+ [
+ "Zobiris",
+ "http://www.pokewiki.de/images/3/33/Sugimori_302.png"
+ ],
+ [
+ "Mega-Zobiris",
+ "http://www.pokewiki.de/images/7/7d/Sugimori_302m1.png"
+ ]
+ ],
+ "Deutsch": "Zobiris",
+ "Englisch": "Sableye",
+ "Farbe": "Violett",
+ "Französisch": "T\\u00e9n\\u00e9fix",
+ "Gewicht": [
+ "11,0 kg",
+ "161,0 kg (Mega)"
+ ],
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30e4\\u30df\\u30e9\\u30df (Yamirami)",
+ "Kategorie": "Finsternis",
+ "Link": "http://pokewiki.de/Zobiris",
+ "National-Dex": "#302",
+ "Typ": [
+ "Unlicht",
+ "Geist"
+ ]
+ },
+ "303": {
+ "Bilder": [
+ [
+ "Flunkifer",
+ "http://www.pokewiki.de/images/3/3f/Sugimori_303.png"
+ ],
+ [
+ "Mega-Flunkifer",
+ "http://www.pokewiki.de/images/6/62/Sugimori_303m1.png"
+ ]
+ ],
+ "Deutsch": "Flunkifer",
+ "Englisch": "Mawile",
+ "Farbe": "Schwarz",
+ "Französisch": "Mysdibule",
+ "Gewicht": [
+ "11,5 kg",
+ "23,5 kg (Mega)"
+ ],
+ "Größe": [
+ "0,6 m",
+ "1,0 m (Mega)"
+ ],
+ "Japanisch": "\\u30af\\u30c1\\u30fc\\u30c8 (Kucheat)",
+ "Kategorie": "Schwindler",
+ "Link": "http://pokewiki.de/Flunkifer",
+ "National-Dex": "#303",
+ "Typ": [
+ "Stahl",
+ "Fee"
+ ]
+ },
+ "304": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/14/Sugimori_304.png"
+ ]
+ ],
+ "Deutsch": "Stollunior",
+ "Englisch": "Aron",
+ "Farbe": "Grau",
+ "Französisch": "Galekid",
+ "Gewicht": "60,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30b3\\u30b3\\u30c9\\u30e9 (Cokodora)",
+ "Kategorie": "Eisenpanzer",
+ "Link": "http://pokewiki.de/Stollunior",
+ "National-Dex": "#304",
+ "Typ": [
+ "Stahl",
+ "Gestein"
+ ]
+ },
+ "305": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c3/Sugimori_305.png"
+ ]
+ ],
+ "Deutsch": "Stollrak",
+ "Englisch": "Lairon",
+ "Farbe": "Grau",
+ "Französisch": "Galegon",
+ "Gewicht": "120,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30b3\\u30c9\\u30e9 (Kodora)",
+ "Kategorie": "Eisenpanzer",
+ "Link": "http://pokewiki.de/Stollrak",
+ "National-Dex": "#305",
+ "Typ": [
+ "Stahl",
+ "Gestein"
+ ]
+ },
+ "306": {
+ "Bilder": [
+ [
+ "Stolloss",
+ "http://www.pokewiki.de/images/0/0a/Sugimori_306.png"
+ ],
+ [
+ "Mega-Stolloss",
+ "http://www.pokewiki.de/images/6/63/Sugimori_306m1.png"
+ ]
+ ],
+ "Deutsch": "Stolloss",
+ "Englisch": "Aggron",
+ "Farbe": "Grau",
+ "Französisch": "Galeking",
+ "Gewicht": [
+ "360,0 kg",
+ "395,0 kg (Mega)"
+ ],
+ "Größe": [
+ "2,1 m",
+ "2,2 m (Mega)"
+ ],
+ "Japanisch": "\\u30dc\\u30b9\\u30b4\\u30c9\\u30e9 (Bossgodora)",
+ "Kategorie": "Eisenpanzer",
+ "Link": "http://pokewiki.de/Stolloss",
+ "National-Dex": "#306",
+ "Typ": [
+ "Stahl",
+ "Gestein",
+ "Stahl (Mega)"
+ ]
+ },
+ "307": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/79/Sugimori_307.png"
+ ]
+ ],
+ "Deutsch": "Meditie",
+ "Englisch": "Meditite",
+ "Farbe": "Blau",
+ "Französisch": "M\\u00e9ditikka",
+ "Gewicht": "11,2 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30a2\\u30b5\\u30ca\\u30f3 (Asanan)",
+ "Kategorie": "Meditation",
+ "Link": "http://pokewiki.de/Meditie",
+ "National-Dex": "#307",
+ "Typ": [
+ "Kampf",
+ "Psycho"
+ ]
+ },
+ "308": {
+ "Bilder": [
+ [
+ "Meditalis",
+ "http://www.pokewiki.de/images/e/e3/Sugimori_308.png"
+ ],
+ [
+ "Mega-Meditalis",
+ "http://www.pokewiki.de/images/b/b4/Sugimori_308m1.png"
+ ]
+ ],
+ "Deutsch": "Meditalis",
+ "Englisch": "Medicham",
+ "Farbe": "Rot",
+ "Französisch": "Charmina",
+ "Gewicht": "31,5 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30c1\\u30e3\\u30fc\\u30ec\\u30e0 (Charem)",
+ "Kategorie": "Meditation",
+ "Link": "http://pokewiki.de/Meditalis",
+ "National-Dex": "#308",
+ "Typ": [
+ "Kampf",
+ "Psycho"
+ ]
+ },
+ "309": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/41/Sugimori_309.png"
+ ]
+ ],
+ "Deutsch": "Frizelbliz",
+ "Englisch": "Electrike",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Dynavolt",
+ "Gewicht": "15,2 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e9\\u30af\\u30e9\\u30a4 (Rakurai)",
+ "Kategorie": "Gewitter",
+ "Link": "http://pokewiki.de/Frizelbliz",
+ "National-Dex": "#309",
+ "Typ": "Elektro"
+ },
+ "310": {
+ "Bilder": [
+ [
+ "Voltenso",
+ "http://www.pokewiki.de/images/7/7e/Sugimori_310.png"
+ ],
+ [
+ "Mega-Voltenso",
+ "http://www.pokewiki.de/images/c/c9/Sugimori_310m1.png"
+ ]
+ ],
+ "Deutsch": "Voltenso",
+ "Englisch": "Manectric",
+ "Farbe": "Gelb",
+ "Französisch": "\\u00c9lecsprint",
+ "Gewicht": [
+ "40,2 kg",
+ "44,0 kg (Mega)"
+ ],
+ "Größe": [
+ "1,5 m",
+ "1,8 m (Mega)"
+ ],
+ "Japanisch": "\\u30e9\\u30a4\\u30dc\\u30eb\\u30c8 (Livolt)",
+ "Kategorie": "Entladung",
+ "Link": "http://pokewiki.de/Voltenso",
+ "National-Dex": "#310",
+ "Typ": "Elektro"
+ },
+ "311": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f4/Sugimori_311.png"
+ ]
+ ],
+ "Deutsch": "Plusle",
+ "Englisch": "Plusle",
+ "Farbe": "Gelb",
+ "Französisch": "Posipi",
+ "Gewicht": "4,2 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30d7\\u30e9\\u30b9\\u30eb (Prasle)",
+ "Kategorie": "Jubel",
+ "Link": "http://pokewiki.de/Plusle",
+ "National-Dex": "#311",
+ "Typ": "Elektro"
+ },
+ "312": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a0/Sugimori_312.png"
+ ]
+ ],
+ "Deutsch": "Minun",
+ "Englisch": "Minun",
+ "Farbe": "Gelb",
+ "Französisch": "N\\u00e9gapi",
+ "Gewicht": "4,2 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30de\\u30a4\\u30ca\\u30f3 (Minun)",
+ "Kategorie": "Jubel",
+ "Link": "http://pokewiki.de/Minun",
+ "National-Dex": "#312",
+ "Typ": "Elektro"
+ },
+ "313": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/16/Sugimori_313.png"
+ ]
+ ],
+ "Deutsch": "Volbeat",
+ "Englisch": "Volbeat",
+ "Farbe": "Grau",
+ "Französisch": "Muciole",
+ "Gewicht": "17,7 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30d0\\u30eb\\u30d3\\u30fc\\u30c8 (Barubeat)",
+ "Kategorie": "Gl\\u00fchw\\u00fcrmchen (Libelle vor der 7. Gen.)",
+ "Link": "http://pokewiki.de/Volbeat",
+ "National-Dex": "#313",
+ "Typ": "K\\u00e4fer"
+ },
+ "314": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/74/Sugimori_314.png"
+ ]
+ ],
+ "Deutsch": "Illumise",
+ "Englisch": "Illumise",
+ "Farbe": "Violett",
+ "Französisch": "Lumivole",
+ "Gewicht": "17,7 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30a4\\u30eb\\u30df\\u30fc\\u30bc (Illumise)",
+ "Kategorie": "Gl\\u00fchw\\u00fcrmchen (Libelle vor der 7. Gen.)",
+ "Link": "http://pokewiki.de/Illumise",
+ "National-Dex": "#314",
+ "Typ": "K\\u00e4fer"
+ },
+ "315": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6d/Sugimori_315.png"
+ ]
+ ],
+ "Deutsch": "Roselia",
+ "Englisch": "Roselia",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Ros\\u00e9lia",
+ "Gewicht": "2,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30ed\\u30bc\\u30ea\\u30a2 (Roselia)",
+ "Kategorie": "Dorn",
+ "Link": "http://pokewiki.de/Roselia",
+ "National-Dex": "#315",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "316": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6d/Sugimori_316.png"
+ ]
+ ],
+ "Deutsch": "Schluppuck",
+ "Englisch": "Gulpin",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Gloupti",
+ "Gewicht": "10,3 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30b4\\u30af\\u30ea\\u30f3 (Gokulin)",
+ "Kategorie": "Magen",
+ "Link": "http://pokewiki.de/Schluppuck",
+ "National-Dex": "#316",
+ "Typ": "Gift"
+ },
+ "317": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/db/Sugimori_317.png"
+ ]
+ ],
+ "Deutsch": "Schlukwech",
+ "Englisch": "Swalot",
+ "Farbe": "Violett",
+ "Französisch": "Avaltout",
+ "Gewicht": "80,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30de\\u30eb\\u30ce\\u30fc\\u30e0 (Marunoom)",
+ "Kategorie": "Giftbeutel",
+ "Link": "http://pokewiki.de/Schlukwech",
+ "National-Dex": "#317",
+ "Typ": "Gift"
+ },
+ "318": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c7/Sugimori_318.png"
+ ]
+ ],
+ "Deutsch": "Kanivanha",
+ "Englisch": "Carvanha",
+ "Farbe": "Rot",
+ "Französisch": "Carvanha",
+ "Gewicht": "20,8 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30ad\\u30d0\\u30cb\\u30a2 (Kibanha)",
+ "Kategorie": "Gnadenlos",
+ "Link": "http://pokewiki.de/Kanivanha",
+ "National-Dex": "#318",
+ "Typ": [
+ "Wasser",
+ "Unlicht"
+ ]
+ },
+ "319": {
+ "Bilder": [
+ [
+ "Tohaido",
+ "http://www.pokewiki.de/images/2/2e/Sugimori_319.png"
+ ],
+ [
+ "Mega-Tohaido",
+ "http://www.pokewiki.de/images/c/c5/Sugimori_319m1.png"
+ ]
+ ],
+ "Deutsch": "Tohaido",
+ "Englisch": "Sharpedo",
+ "Farbe": "Blau",
+ "Französisch": "Sharpedo",
+ "Gewicht": [
+ "88,8 kg",
+ "130,3 kg (Mega)"
+ ],
+ "Größe": [
+ "1,8 m",
+ "2,5 m (Mega)"
+ ],
+ "Japanisch": "\\u30b5\\u30e1\\u30cf\\u30c0\\u30fc (Samehader)",
+ "Kategorie": "Brutal",
+ "Link": "http://pokewiki.de/Tohaido",
+ "National-Dex": "#319",
+ "Typ": [
+ "Wasser",
+ "Unlicht"
+ ]
+ },
+ "320": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/df/Sugimori_320.png"
+ ]
+ ],
+ "Deutsch": "Wailmer",
+ "Englisch": "Wailmer",
+ "Farbe": "Blau",
+ "Französisch": "Wailmer",
+ "Gewicht": "130,0 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30db\\u30a8\\u30eb\\u30b3 (Hoeruko)",
+ "Kategorie": "Kugelwal",
+ "Link": "http://pokewiki.de/Wailmer",
+ "National-Dex": "#320",
+ "Typ": "Wasser"
+ },
+ "321": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f9/Sugimori_321.png"
+ ]
+ ],
+ "Deutsch": "Wailord",
+ "Englisch": "Wailord",
+ "Farbe": "Blau",
+ "Französisch": "Wailord",
+ "Gewicht": "398,0 kg",
+ "Größe": "14,5 m",
+ "Japanisch": "\\u30db\\u30a8\\u30eb\\u30aa\\u30fc (Whaloh)",
+ "Kategorie": "Flutwal",
+ "Link": "http://pokewiki.de/Wailord",
+ "National-Dex": "#321",
+ "Typ": "Wasser"
+ },
+ "322": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/65/Sugimori_322.png"
+ ]
+ ],
+ "Deutsch": "Camaub",
+ "Englisch": "Numel",
+ "Farbe": "Gelb",
+ "Französisch": "Chamallot",
+ "Gewicht": "24,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30c9\\u30f3\\u30e1\\u30eb (Donmel)",
+ "Kategorie": "Taubheit",
+ "Link": "http://pokewiki.de/Camaub",
+ "National-Dex": "#322",
+ "Typ": [
+ "Feuer",
+ "Boden"
+ ]
+ },
+ "323": {
+ "Bilder": [
+ [
+ "Camerupt",
+ "http://www.pokewiki.de/images/f/f2/Sugimori_323.png"
+ ],
+ [
+ "Mega-Camerupt",
+ "http://www.pokewiki.de/images/8/87/Sugimori_323m1.png"
+ ]
+ ],
+ "Deutsch": "Camerupt",
+ "Englisch": "Camerupt",
+ "Farbe": "Rot",
+ "Französisch": "Cam\\u00e9rupt",
+ "Gewicht": [
+ "220,0 kg",
+ "320,5 kg (Mega)"
+ ],
+ "Größe": [
+ "1,9 m",
+ "2,5 m (Mega)"
+ ],
+ "Japanisch": "\\u30d0\\u30af\\u30fc\\u30c0 (Bakuuda)",
+ "Kategorie": "Ausbruch",
+ "Link": "http://pokewiki.de/Camerupt",
+ "National-Dex": "#323",
+ "Typ": [
+ "Feuer",
+ "Boden"
+ ]
+ },
+ "324": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/48/Sugimori_324.png"
+ ]
+ ],
+ "Deutsch": "Qurtel",
+ "Englisch": "Torkoal",
+ "Farbe": "Braun",
+ "Französisch": "Chartor",
+ "Gewicht": "80,4 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30b3\\u30fc\\u30bf\\u30b9 (Cotoise)",
+ "Kategorie": "Kohle",
+ "Link": "http://pokewiki.de/Qurtel",
+ "National-Dex": "#324",
+ "Typ": "Feuer"
+ },
+ "325": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c0/Sugimori_325.png"
+ ]
+ ],
+ "Deutsch": "Spoink",
+ "Englisch": "Spoink",
+ "Farbe": "Schwarz",
+ "Französisch": "Spoink",
+ "Gewicht": "30,6 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30d0\\u30cd\\u30d6\\u30fc (Baneboo)",
+ "Kategorie": "Sprungfeder",
+ "Link": "http://pokewiki.de/Spoink",
+ "National-Dex": "#325",
+ "Typ": "Psycho"
+ },
+ "326": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d0/Sugimori_326.png"
+ ]
+ ],
+ "Deutsch": "Groink",
+ "Englisch": "Grumpig",
+ "Farbe": "Violett",
+ "Französisch": "Groret",
+ "Gewicht": "71,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30d6\\u30fc\\u30d4\\u30c3\\u30b0 (Boopig)",
+ "Kategorie": "Manipulator",
+ "Link": "http://pokewiki.de/Groink",
+ "National-Dex": "#326",
+ "Typ": "Psycho"
+ },
+ "327": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6a/Sugimori_327.png"
+ ]
+ ],
+ "Deutsch": "Pandir",
+ "Englisch": "Spinda",
+ "Farbe": "Braun",
+ "Französisch": "Spinda",
+ "Gewicht": "5,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30d1\\u30c3\\u30c1\\u30fc\\u30eb (Patcheel)",
+ "Kategorie": "Punkt-Panda",
+ "Link": "http://pokewiki.de/Pandir",
+ "National-Dex": "#327",
+ "Typ": "Normal"
+ },
+ "328": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e5/Sugimori_328.png"
+ ]
+ ],
+ "Deutsch": "Knacklion",
+ "Englisch": "Trapinch",
+ "Farbe": "Braun",
+ "Französisch": "Kraknoix",
+ "Gewicht": "15,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30ca\\u30c3\\u30af\\u30e9\\u30fc (Nuckrar)",
+ "Kategorie": "Ameisenl\\u00f6we",
+ "Link": "http://pokewiki.de/Knacklion",
+ "National-Dex": "#328",
+ "Typ": "Boden"
+ },
+ "329": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a4/Sugimori_329.png"
+ ]
+ ],
+ "Deutsch": "Vibrava",
+ "Englisch": "Vibrava",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Vibraninf",
+ "Gewicht": "15,3 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30d3\\u30d6\\u30e9\\u30fc\\u30d0 (Vibrava)",
+ "Kategorie": "Vibration",
+ "Link": "http://pokewiki.de/Vibrava",
+ "National-Dex": "#329",
+ "Typ": [
+ "Boden",
+ "Drache"
+ ]
+ },
+ "330": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ec/Sugimori_330.png"
+ ]
+ ],
+ "Deutsch": "Libelldra",
+ "Englisch": "Flygon",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Lib\\u00e9gon",
+ "Gewicht": "82,0 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30d5\\u30e9\\u30a4\\u30b4\\u30f3 (Flygon)",
+ "Kategorie": "Mystik",
+ "Link": "http://pokewiki.de/Libelldra",
+ "National-Dex": "#330",
+ "Typ": [
+ "Boden",
+ "Drache"
+ ]
+ },
+ "331": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/12/Sugimori_331.png"
+ ]
+ ],
+ "Deutsch": "Tuska",
+ "Englisch": "Cacnea",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Cacnea",
+ "Gewicht": "51,3 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30b5\\u30dc\\u30cd\\u30a2 (Sabonea)",
+ "Kategorie": "Kaktus",
+ "Link": "http://pokewiki.de/Tuska",
+ "National-Dex": "#331",
+ "Typ": "Pflanze"
+ },
+ "332": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/03/Sugimori_332.png"
+ ]
+ ],
+ "Deutsch": "Noktuska",
+ "Englisch": "Cacturne",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Cacturne",
+ "Gewicht": "77,4 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30ce\\u30af\\u30bf\\u30b9 (Noctus)",
+ "Kategorie": "Vogelschock",
+ "Link": "http://pokewiki.de/Noktuska",
+ "National-Dex": "#332",
+ "Typ": [
+ "Pflanze",
+ "Unlicht"
+ ]
+ },
+ "333": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/31/Sugimori_333.png"
+ ]
+ ],
+ "Deutsch": "Wablu",
+ "Englisch": "Swablu",
+ "Farbe": "Blau",
+ "Französisch": "Tylton",
+ "Gewicht": "1,2 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30c1\\u30eb\\u30c3\\u30c8 (Tyltto)",
+ "Kategorie": "Wollvogel",
+ "Link": "http://pokewiki.de/Wablu",
+ "National-Dex": "#333",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "334": {
+ "Bilder": [
+ [
+ "Altaria",
+ "http://www.pokewiki.de/images/0/00/Sugimori_334.png"
+ ],
+ [
+ "Mega-Altaria",
+ "http://www.pokewiki.de/images/b/bf/Sugimori_334m1.png"
+ ]
+ ],
+ "Deutsch": "Altaria",
+ "Englisch": "Altaria",
+ "Farbe": "Blau",
+ "Französisch": "Altaria",
+ "Gewicht": "20,6 kg",
+ "Größe": [
+ "1,1 m",
+ "1,5 m (Mega)"
+ ],
+ "Japanisch": "\\u30c1\\u30eb\\u30bf\\u30ea\\u30b9 (Tyltalis)",
+ "Kategorie": "Summsel",
+ "Link": "http://pokewiki.de/Altaria",
+ "National-Dex": "#334",
+ "Typ": [
+ "Drache",
+ "Flug",
+ "Drache",
+ "Fee (Mega)"
+ ]
+ },
+ "335": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/af/Sugimori_335.png"
+ ]
+ ],
+ "Deutsch": "Sengo",
+ "Englisch": "Zangoose",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Mangriff",
+ "Gewicht": "40,3 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30b6\\u30f3\\u30b0\\u30fc\\u30b9 (Zangoose)",
+ "Kategorie": "Frettkatz",
+ "Link": "http://pokewiki.de/Sengo",
+ "National-Dex": "#335",
+ "Typ": "Normal"
+ },
+ "336": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4b/Sugimori_336.png"
+ ]
+ ],
+ "Deutsch": "Vipitis",
+ "Englisch": "Seviper",
+ "Farbe": "Schwarz",
+ "Französisch": "S\\u00e9viper",
+ "Gewicht": "52,5 kg",
+ "Größe": "2,7 m",
+ "Japanisch": "\\u30cf\\u30d6\\u30cd\\u30fc\\u30af (Habunake)",
+ "Kategorie": "Rei\\u00dfzahn",
+ "Link": "http://pokewiki.de/Vipitis",
+ "National-Dex": "#336",
+ "Typ": "Gift"
+ },
+ "337": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/04/Sugimori_337.png"
+ ]
+ ],
+ "Deutsch": "Lunastein",
+ "Englisch": "Lunatone",
+ "Farbe": "Gelb",
+ "Französisch": "S\\u00e9l\\u00e9roc",
+ "Gewicht": "168,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30eb\\u30ca\\u30c8\\u30fc\\u30f3 (Lunatone)",
+ "Kategorie": "Meteorit",
+ "Link": "http://pokewiki.de/Lunastein",
+ "National-Dex": "#337",
+ "Typ": [
+ "Gestein",
+ "Psycho"
+ ]
+ },
+ "338": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1b/Sugimori_338.png"
+ ]
+ ],
+ "Deutsch": "Sonnfel",
+ "Englisch": "Solrock",
+ "Farbe": "Rot",
+ "Französisch": "Solaroc",
+ "Gewicht": "154,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30bd\\u30eb\\u30ed\\u30c3\\u30af (Solrock)",
+ "Kategorie": "Meteorit",
+ "Link": "http://pokewiki.de/Sonnfel",
+ "National-Dex": "#338",
+ "Typ": [
+ "Gestein",
+ "Psycho"
+ ]
+ },
+ "339": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e1/Sugimori_339.png"
+ ]
+ ],
+ "Deutsch": "Schmerbe",
+ "Englisch": "Barboach",
+ "Farbe": "Grau",
+ "Französisch": "Barloche",
+ "Gewicht": "1,9 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30c9\\u30b8\\u30e7\\u30c3\\u30c1 (Dojoach)",
+ "Kategorie": "Barthaar",
+ "Link": "http://pokewiki.de/Schmerbe",
+ "National-Dex": "#339",
+ "Typ": [
+ "Wasser",
+ "Boden"
+ ]
+ },
+ "340": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/01/Sugimori_340.png"
+ ]
+ ],
+ "Deutsch": "Welsar",
+ "Englisch": "Whiscash",
+ "Farbe": "Blau",
+ "Französisch": "Barbicha",
+ "Gewicht": "23,6 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30ca\\u30de\\u30ba\\u30f3 (Namazun)",
+ "Kategorie": "Barthaar",
+ "Link": "http://pokewiki.de/Welsar",
+ "National-Dex": "#340",
+ "Typ": [
+ "Wasser",
+ "Boden"
+ ]
+ },
+ "341": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fd/Sugimori_341.png"
+ ]
+ ],
+ "Deutsch": "Krebscorps",
+ "Englisch": "Corphish",
+ "Farbe": "Rot",
+ "Französisch": "\\u00c9crapince",
+ "Gewicht": "11,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30d8\\u30a4\\u30ac\\u30cb (Heigani)",
+ "Kategorie": "Grobian",
+ "Link": "http://pokewiki.de/Krebscorps",
+ "National-Dex": "#341",
+ "Typ": "Wasser"
+ },
+ "342": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fb/Sugimori_342.png"
+ ]
+ ],
+ "Deutsch": "Krebutack",
+ "Englisch": "Crawdaunt",
+ "Farbe": "Rot",
+ "Französisch": "Colhomard",
+ "Gewicht": "32,8 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30b7\\u30b6\\u30ea\\u30ac\\u30fc (Shizariger)",
+ "Kategorie": "Schlingel",
+ "Link": "http://pokewiki.de/Krebutack",
+ "National-Dex": "#342",
+ "Typ": [
+ "Wasser",
+ "Unlicht"
+ ]
+ },
+ "343": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/88/Sugimori_343.png"
+ ]
+ ],
+ "Deutsch": "Puppance",
+ "Englisch": "Baltoy",
+ "Farbe": "Braun",
+ "Französisch": "Balbuto",
+ "Gewicht": "21,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30e4\\u30b8\\u30ed\\u30f3 (Yajilon)",
+ "Kategorie": "Lehmpuppe",
+ "Link": "http://pokewiki.de/Puppance",
+ "National-Dex": "#343",
+ "Typ": [
+ "Boden",
+ "Psycho"
+ ]
+ },
+ "344": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d3/Sugimori_344.png"
+ ]
+ ],
+ "Deutsch": "Lepumentas",
+ "Englisch": "Claydol",
+ "Farbe": "Schwarz",
+ "Französisch": "Kaorine",
+ "Gewicht": "108,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30cd\\u30f3\\u30c9\\u30fc\\u30eb (Nendoll)",
+ "Kategorie": "Lehmpuppe",
+ "Link": "http://pokewiki.de/Lepumentas",
+ "National-Dex": "#344",
+ "Typ": [
+ "Boden",
+ "Psycho"
+ ]
+ },
+ "345": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8c/Sugimori_345.png"
+ ]
+ ],
+ "Deutsch": "Liliep",
+ "Englisch": "Lileep",
+ "Farbe": "Violett",
+ "Französisch": "Lilia",
+ "Gewicht": "23,8 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30ea\\u30ea\\u30fc\\u30e9 (Lilyu)",
+ "Kategorie": "Seeanemone",
+ "Link": "http://pokewiki.de/Liliep",
+ "National-Dex": "#345",
+ "Typ": [
+ "Gestein",
+ "Pflanze"
+ ]
+ },
+ "346": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5d/Sugimori_346.png"
+ ]
+ ],
+ "Deutsch": "Wielie",
+ "Englisch": "Cradily",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Vacilys",
+ "Gewicht": "60,4 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30e6\\u30ec\\u30a4\\u30c9\\u30eb (Yuradle)",
+ "Kategorie": "Rankf\\u00fc\\u00dfer",
+ "Link": "http://pokewiki.de/Wielie",
+ "National-Dex": "#346",
+ "Typ": [
+ "Gestein",
+ "Pflanze"
+ ]
+ },
+ "347": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fa/Sugimori_347.png"
+ ]
+ ],
+ "Deutsch": "Anorith",
+ "Englisch": "Anorith",
+ "Farbe": "Grau",
+ "Französisch": "Anorith",
+ "Gewicht": "12,5 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30a2\\u30ce\\u30d7\\u30b9 (Anopth)",
+ "Kategorie": "Urgarnele (Krebssenior vor der 7. Gen.)",
+ "Link": "http://pokewiki.de/Anorith",
+ "National-Dex": "#347",
+ "Typ": [
+ "Gestein",
+ "K\\u00e4fer"
+ ]
+ },
+ "348": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d3/Sugimori_348.png"
+ ]
+ ],
+ "Deutsch": "Armaldo",
+ "Englisch": "Armaldo",
+ "Farbe": "Grau",
+ "Französisch": "Armaldo",
+ "Gewicht": "68,2 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30a2\\u30fc\\u30de\\u30eb\\u30c9 (Armaldo)",
+ "Kategorie": "Schild",
+ "Link": "http://pokewiki.de/Armaldo",
+ "National-Dex": "#348",
+ "Typ": [
+ "Gestein",
+ "K\\u00e4fer"
+ ]
+ },
+ "349": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ed/Sugimori_349.png"
+ ]
+ ],
+ "Deutsch": "Barschwa",
+ "Englisch": "Feebas",
+ "Farbe": "Braun",
+ "Französisch": "Barpau",
+ "Gewicht": "7,4 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30d2\\u30f3\\u30d0\\u30b9 (Hinbass)",
+ "Kategorie": "Fisch",
+ "Link": "http://pokewiki.de/Barschwa",
+ "National-Dex": "#349",
+ "Typ": "Wasser"
+ },
+ "350": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2f/Sugimori_350.png"
+ ]
+ ],
+ "Deutsch": "Milotic",
+ "Englisch": "Milotic",
+ "Farbe": "Rosa",
+ "Französisch": "Milobellus",
+ "Gewicht": "162,0 kg",
+ "Größe": "6,2 m",
+ "Japanisch": "\\u30df\\u30ed\\u30ab\\u30ed\\u30b9 (Milokaross)",
+ "Kategorie": "Zartheit",
+ "Link": "http://pokewiki.de/Milotic",
+ "National-Dex": "#350",
+ "Typ": "Wasser"
+ },
+ "351": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fe/Sugimori_351.png"
+ ]
+ ],
+ "Deutsch": "Formeo",
+ "Englisch": "Castform",
+ "Farbe": [
+ "Grau",
+ "Rot (Sonnenform)",
+ "Blau (Regenform)",
+ "Wei\\u00df (Schneeform)"
+ ],
+ "Französisch": "Morph\\u00e9o",
+ "Gewicht": "0,8 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30dd\\u30ef\\u30eb\\u30f3 (Powalen)",
+ "Kategorie": "Wetter",
+ "Link": "http://pokewiki.de/Formeo",
+ "National-Dex": "#351",
+ "Typ": [
+ "Normal (Normalform)",
+ "Feuer (Sonnenform)",
+ "Wasser (Regenform)",
+ "Eis (Schneeform)"
+ ]
+ },
+ "352": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7c/Sugimori_352.png"
+ ]
+ ],
+ "Deutsch": "Kecleon",
+ "Englisch": "Kecleon",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Kecleon",
+ "Gewicht": "22,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30ab\\u30af\\u30ec\\u30aa\\u30f3 (Kakureon)",
+ "Kategorie": "Farbenspiel",
+ "Link": "http://pokewiki.de/Kecleon",
+ "National-Dex": "#352",
+ "Typ": "Normal"
+ },
+ "353": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/60/Sugimori_353.png"
+ ]
+ ],
+ "Deutsch": "Shuppet",
+ "Englisch": "Shuppet",
+ "Farbe": "Schwarz",
+ "Französisch": "Polichombr",
+ "Gewicht": "2,3 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30ab\\u30b2\\u30dc\\u30a6\\u30ba (Kagebouzu)",
+ "Kategorie": "Puppe",
+ "Link": "http://pokewiki.de/Shuppet",
+ "National-Dex": "#353",
+ "Typ": "Geist"
+ },
+ "354": {
+ "Bilder": [
+ [
+ "Banette",
+ "http://www.pokewiki.de/images/1/1f/Sugimori_354.png"
+ ],
+ [
+ "Mega-Banette",
+ "http://www.pokewiki.de/images/a/af/Sugimori_354m1.png"
+ ]
+ ],
+ "Deutsch": "Banette",
+ "Englisch": "Banette",
+ "Farbe": "Schwarz",
+ "Französisch": "Branette",
+ "Gewicht": [
+ "12,5 kg",
+ "13,0 kg (Mega)"
+ ],
+ "Größe": [
+ "1,1 m",
+ "1,2 m (Mega)"
+ ],
+ "Japanisch": "\\u30b8\\u30e5\\u30da\\u30c3\\u30bf (Juppeta)",
+ "Kategorie": "Marionette",
+ "Link": "http://pokewiki.de/Banette",
+ "National-Dex": "#354",
+ "Typ": "Geist"
+ },
+ "355": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3a/Sugimori_355.png"
+ ]
+ ],
+ "Deutsch": "Zwirrlicht",
+ "Englisch": "Duskull",
+ "Farbe": "Schwarz",
+ "Französisch": "Skel\\u00e9nox",
+ "Gewicht": "15,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30e8\\u30de\\u30ef\\u30eb (Yomawaru)",
+ "Kategorie": "Requiem",
+ "Link": "http://pokewiki.de/Zwirrlicht",
+ "National-Dex": "#355",
+ "Typ": "Geist"
+ },
+ "356": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d3/Sugimori_356.png"
+ ]
+ ],
+ "Deutsch": "Zwirrklop",
+ "Englisch": "Dusclops",
+ "Farbe": "Schwarz",
+ "Französisch": "T\\u00e9raclope",
+ "Gewicht": "30,6 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30b5\\u30de\\u30e8\\u30fc\\u30eb (Samayouru)",
+ "Kategorie": "Wink",
+ "Link": "http://pokewiki.de/Zwirrklop",
+ "National-Dex": "#356",
+ "Typ": "Geist"
+ },
+ "357": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6b/Sugimori_357.png"
+ ]
+ ],
+ "Deutsch": "Tropius",
+ "Englisch": "Tropius",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Tropius",
+ "Gewicht": "100,0 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30c8\\u30ed\\u30d4\\u30a6\\u30b9 (Tropius)",
+ "Kategorie": "Obst",
+ "Link": "http://pokewiki.de/Tropius",
+ "National-Dex": "#357",
+ "Typ": [
+ "Pflanze",
+ "Flug"
+ ]
+ },
+ "358": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8b/Sugimori_358.png"
+ ]
+ ],
+ "Deutsch": "Palimpalim",
+ "Englisch": "Chimecho",
+ "Farbe": "Blau",
+ "Französisch": "\\u00c9oko",
+ "Gewicht": "1,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c1\\u30ea\\u30fc\\u30f3 (Chirean)",
+ "Kategorie": "Windspiel",
+ "Link": "http://pokewiki.de/Palimpalim",
+ "National-Dex": "#358",
+ "Typ": "Psycho"
+ },
+ "359": {
+ "Bilder": [
+ [
+ "Absol",
+ "http://www.pokewiki.de/images/f/f2/Sugimori_359.png"
+ ],
+ [
+ "Mega-Absol",
+ "http://www.pokewiki.de/images/4/44/Sugimori_359m1.png"
+ ]
+ ],
+ "Deutsch": "Absol",
+ "Englisch": "Absol",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Absol",
+ "Gewicht": [
+ "47,0 kg",
+ "49,0 kg (Mega)"
+ ],
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30a2\\u30d6\\u30bd\\u30eb (Absol)",
+ "Kategorie": "Desaster",
+ "Link": "http://pokewiki.de/Absol",
+ "National-Dex": "#359",
+ "Typ": "Unlicht"
+ },
+ "360": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a7/Sugimori_360.png"
+ ]
+ ],
+ "Deutsch": "Isso",
+ "Englisch": "Wynaut",
+ "Farbe": "Blau",
+ "Französisch": "Ok\\u00e9ok\\u00e9",
+ "Gewicht": "14,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30bd\\u30fc\\u30ca\\u30ce (Sohnano)",
+ "Kategorie": "Strahlekind",
+ "Link": "http://pokewiki.de/Isso",
+ "National-Dex": "#360",
+ "Typ": "Psycho"
+ },
+ "361": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/22/Sugimori_361.png"
+ ]
+ ],
+ "Deutsch": "Schneppke",
+ "Englisch": "Snorunt",
+ "Farbe": "Grau",
+ "Französisch": "Stalgamin",
+ "Gewicht": "16,8 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30e6\\u30ad\\u30ef\\u30e9\\u30b7 (Yukiwarashi)",
+ "Kategorie": "Schneehut",
+ "Link": "http://pokewiki.de/Schneppke",
+ "National-Dex": "#361",
+ "Typ": "Eis"
+ },
+ "362": {
+ "Bilder": [
+ [
+ "Firnontor",
+ "http://www.pokewiki.de/images/6/68/Sugimori_362.png"
+ ],
+ [
+ "Mega-Firnontor",
+ "http://www.pokewiki.de/images/7/74/Sugimori_362m1.png"
+ ]
+ ],
+ "Deutsch": "Firnontor",
+ "Englisch": "Glalie",
+ "Farbe": "Grau",
+ "Französisch": "Oniglali",
+ "Gewicht": [
+ "256,5 kg",
+ "350,2 kg (Mega)"
+ ],
+ "Größe": [
+ "1,5 m",
+ "2,1 m (Mega)"
+ ],
+ "Japanisch": "\\u30aa\\u30cb\\u30b4\\u30fc\\u30ea (Onigohri)",
+ "Kategorie": "Antlitz",
+ "Link": "http://pokewiki.de/Firnontor",
+ "National-Dex": "#362",
+ "Typ": "Eis"
+ },
+ "363": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/df/Sugimori_363.png"
+ ]
+ ],
+ "Deutsch": "Seemops",
+ "Englisch": "Spheal",
+ "Farbe": "Blau",
+ "Französisch": "Obalie",
+ "Gewicht": "39,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30bf\\u30de\\u30b6\\u30e9\\u30b7 (Tamazarashi)",
+ "Kategorie": "Applaus",
+ "Link": "http://pokewiki.de/Seemops",
+ "National-Dex": "#363",
+ "Typ": [
+ "Eis",
+ "Wasser"
+ ]
+ },
+ "364": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/33/Sugimori_364.png"
+ ]
+ ],
+ "Deutsch": "Seejong",
+ "Englisch": "Sealeo",
+ "Farbe": "Blau",
+ "Französisch": "Phogleur",
+ "Gewicht": "87,6 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30c8\\u30c9\\u30b0\\u30e9\\u30fc (Todoggler)",
+ "Kategorie": "Spielball",
+ "Link": "http://pokewiki.de/Seejong",
+ "National-Dex": "#364",
+ "Typ": [
+ "Eis",
+ "Wasser"
+ ]
+ },
+ "365": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/62/Sugimori_365.png"
+ ]
+ ],
+ "Deutsch": "Walraisa",
+ "Englisch": "Walrein",
+ "Farbe": "Blau",
+ "Französisch": "Kaimorse",
+ "Gewicht": "150,6 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30c8\\u30c9\\u30bc\\u30eb\\u30ac (Todoseruga)",
+ "Kategorie": "Eisbrecher",
+ "Link": "http://pokewiki.de/Walraisa",
+ "National-Dex": "#365",
+ "Typ": [
+ "Eis",
+ "Wasser"
+ ]
+ },
+ "366": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c3/Sugimori_366.png"
+ ]
+ ],
+ "Deutsch": "Perlu",
+ "Englisch": "Clamperl",
+ "Farbe": "Blau",
+ "Französisch": "Coquiperl",
+ "Gewicht": "52,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30d1\\u30fc\\u30eb\\u30eb (Pearlulu)",
+ "Kategorie": "Muschel",
+ "Link": "http://pokewiki.de/Perlu",
+ "National-Dex": "#366",
+ "Typ": "Wasser"
+ },
+ "367": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b5/Sugimori_367.png"
+ ]
+ ],
+ "Deutsch": "Aalabyss",
+ "Englisch": "Huntail",
+ "Farbe": "Blau",
+ "Französisch": "Serpang",
+ "Gewicht": "27,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30cf\\u30f3\\u30c6\\u30fc\\u30eb (Huntail)",
+ "Kategorie": "Tiefsee",
+ "Link": "http://pokewiki.de/Aalabyss",
+ "National-Dex": "#367",
+ "Typ": "Wasser"
+ },
+ "368": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/30/Sugimori_368.png"
+ ]
+ ],
+ "Deutsch": "Saganabyss",
+ "Englisch": "Gorebyss",
+ "Farbe": "Rosa",
+ "Französisch": "Rosabyss",
+ "Gewicht": "22,6 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30b5\\u30af\\u30e9\\u30d3\\u30b9 (Sakurabyss)",
+ "Kategorie": "S\\u00fcdsee",
+ "Link": "http://pokewiki.de/Saganabyss",
+ "National-Dex": "#368",
+ "Typ": "Wasser"
+ },
+ "369": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/65/Sugimori_369.png"
+ ]
+ ],
+ "Deutsch": "Relicanth",
+ "Englisch": "Relicanth",
+ "Farbe": "Grau",
+ "Französisch": "Relicanth",
+ "Gewicht": "23,4 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b8\\u30fc\\u30e9\\u30f3\\u30b9 (Glanth)",
+ "Kategorie": "Bestand",
+ "Link": "http://pokewiki.de/Relicanth",
+ "National-Dex": "#369",
+ "Typ": [
+ "Wasser",
+ "Gestein"
+ ]
+ },
+ "370": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/47/Sugimori_370.png"
+ ]
+ ],
+ "Deutsch": "Liebiskus",
+ "Englisch": "Luvdisc",
+ "Farbe": "Rosa",
+ "Französisch": "Lovdisc",
+ "Gewicht": "8,7 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e9\\u30d6\\u30ab\\u30b9 (Lovecus)",
+ "Kategorie": "Rendezvous",
+ "Link": "http://pokewiki.de/Liebiskus",
+ "National-Dex": "#370",
+ "Typ": "Wasser"
+ },
+ "371": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1b/Sugimori_371.png"
+ ]
+ ],
+ "Deutsch": "Kindwurm",
+ "Englisch": "Bagon",
+ "Farbe": "Blau",
+ "Französisch": "Draby",
+ "Gewicht": "42,1 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30bf\\u30c4\\u30d9\\u30a4 (Tatsubay)",
+ "Kategorie": "Steinhaupt",
+ "Link": "http://pokewiki.de/Kindwurm",
+ "National-Dex": "#371",
+ "Typ": "Drache"
+ },
+ "372": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4a/Sugimori_372.png"
+ ]
+ ],
+ "Deutsch": "Draschel",
+ "Englisch": "Shelgon",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Drackhaus",
+ "Gewicht": "110,5 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30b3\\u30e2\\u30eb\\u30fc (Komoruu)",
+ "Kategorie": "Durchsteher",
+ "Link": "http://pokewiki.de/Draschel",
+ "National-Dex": "#372",
+ "Typ": "Drache"
+ },
+ "373": {
+ "Bilder": [
+ [
+ "Brutalanda",
+ "http://www.pokewiki.de/images/9/96/Sugimori_373.png"
+ ],
+ [
+ "Mega-Brutalanda",
+ "http://www.pokewiki.de/images/3/35/Sugimori_373m1.png"
+ ]
+ ],
+ "Deutsch": "Brutalanda",
+ "Englisch": "Salamence",
+ "Farbe": "Blau",
+ "Französisch": "Drattak",
+ "Gewicht": [
+ "102,6 kg",
+ "112,6 kg (Mega)"
+ ],
+ "Größe": [
+ "1,5 m",
+ "1,8 m (Mega)"
+ ],
+ "Japanisch": "\\u30dc\\u30fc\\u30de\\u30f3\\u30c0 (Bohmander)",
+ "Kategorie": "Drache",
+ "Link": "http://pokewiki.de/Brutalanda",
+ "National-Dex": "#373",
+ "Typ": [
+ "Drache",
+ "Flug"
+ ]
+ },
+ "374": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b3/Sugimori_374.png"
+ ]
+ ],
+ "Deutsch": "Tanhel",
+ "Englisch": "Beldum",
+ "Farbe": "Blau",
+ "Französisch": "Terhal",
+ "Gewicht": "95,2 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c0\\u30f3\\u30d0\\u30eb (Dumbber)",
+ "Kategorie": "Eisenkugel",
+ "Link": "http://pokewiki.de/Tanhel",
+ "National-Dex": "#374",
+ "Typ": [
+ "Stahl",
+ "Psycho"
+ ]
+ },
+ "375": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a3/Sugimori_375.png"
+ ]
+ ],
+ "Deutsch": "Metang",
+ "Englisch": "Metang",
+ "Farbe": "Blau",
+ "Französisch": "M\\u00e9tang",
+ "Gewicht": "202,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30e1\\u30bf\\u30f3\\u30b0 (Metang)",
+ "Kategorie": "Eisenklaue",
+ "Link": "http://pokewiki.de/Metang",
+ "National-Dex": "#375",
+ "Typ": [
+ "Stahl",
+ "Psycho"
+ ]
+ },
+ "376": {
+ "Bilder": [
+ [
+ "Metagross",
+ "http://www.pokewiki.de/images/7/7c/Sugimori_376.png"
+ ],
+ [
+ "Mega-Metagross",
+ "http://www.pokewiki.de/images/1/12/Sugimori_376m1.png"
+ ]
+ ],
+ "Deutsch": "Metagross",
+ "Englisch": "Metagross",
+ "Farbe": "Blau",
+ "Französisch": "M\\u00e9talosse",
+ "Gewicht": [
+ "550,0 kg",
+ "942,9 kg (Mega)"
+ ],
+ "Größe": [
+ "1,6 m",
+ "2,5 m (Mega)"
+ ],
+ "Japanisch": "\\u30e1\\u30bf\\u30b0\\u30ed\\u30b9 (Metagross)",
+ "Kategorie": "Eisenfu\\u00df",
+ "Link": "http://pokewiki.de/Metagross",
+ "National-Dex": "#376",
+ "Typ": [
+ "Stahl",
+ "Psycho"
+ ]
+ },
+ "377": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/9c/Sugimori_377.png"
+ ]
+ ],
+ "Deutsch": "Regirock",
+ "Englisch": "Regirock",
+ "Farbe": "Braun",
+ "Französisch": "Regirock",
+ "Gewicht": "230,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30ec\\u30b8\\u30ed\\u30c3\\u30af (Regirock)",
+ "Kategorie": "Steingipfel",
+ "Link": "http://pokewiki.de/Regirock",
+ "National-Dex": "#377",
+ "Typ": "Gestein"
+ },
+ "378": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/72/Sugimori_378.png"
+ ]
+ ],
+ "Deutsch": "Regice",
+ "Englisch": "Regice",
+ "Farbe": "Blau",
+ "Französisch": "Regice",
+ "Gewicht": "175,0 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30ec\\u30b8\\u30a2\\u30a4\\u30b9 (Regice)",
+ "Kategorie": "Eisberg",
+ "Link": "http://pokewiki.de/Regice",
+ "National-Dex": "#378",
+ "Typ": "Eis"
+ },
+ "379": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2a/Sugimori_379.png"
+ ]
+ ],
+ "Deutsch": "Registeel",
+ "Englisch": "Registeel",
+ "Farbe": "Grau",
+ "Französisch": "Registeel",
+ "Gewicht": "205,0 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30ec\\u30b8\\u30b9\\u30c1\\u30eb (Registeel)",
+ "Kategorie": "Eisen",
+ "Link": "http://pokewiki.de/Registeel",
+ "National-Dex": "#379",
+ "Typ": "Stahl"
+ },
+ "380": {
+ "Bilder": [
+ [
+ "Latias",
+ "http://www.pokewiki.de/images/0/08/Sugimori_380.png"
+ ],
+ [
+ "Mega-Latias",
+ "http://www.pokewiki.de/images/0/07/Sugimori_380m1.png"
+ ]
+ ],
+ "Deutsch": "Latias",
+ "Englisch": "Latias",
+ "Farbe": [
+ "Rot",
+ "Violett (Mega)"
+ ],
+ "Französisch": "Latias",
+ "Gewicht": [
+ "40,0 kg",
+ "52,0 kg (Mega)"
+ ],
+ "Größe": [
+ "1,4 m",
+ "1,8 m (Mega)"
+ ],
+ "Japanisch": "\\u30e9\\u30c6\\u30a3\\u30a2\\u30b9 (Latias)",
+ "Kategorie": "\\u00c4on",
+ "Link": "http://pokewiki.de/Latias",
+ "National-Dex": "#380",
+ "Typ": [
+ "Drache",
+ "Psycho"
+ ]
+ },
+ "381": {
+ "Bilder": [
+ [
+ "Latios",
+ "http://www.pokewiki.de/images/d/d4/Sugimori_381.png"
+ ],
+ [
+ "Mega-Latios",
+ "http://www.pokewiki.de/images/c/ca/Sugimori_381m1.png"
+ ]
+ ],
+ "Deutsch": "Latios",
+ "Englisch": "Latios",
+ "Farbe": [
+ "Blau",
+ "Violett (Mega)"
+ ],
+ "Französisch": "Latios",
+ "Gewicht": [
+ "60,0 kg",
+ "70,0 kg (Mega)"
+ ],
+ "Größe": [
+ "2,0 m",
+ "2,3 m (Mega)"
+ ],
+ "Japanisch": "\\u30e9\\u30c6\\u30a3\\u30aa\\u30b9 (Latios)",
+ "Kategorie": "\\u00c4on",
+ "Link": "http://pokewiki.de/Latios",
+ "National-Dex": "#381",
+ "Typ": [
+ "Drache",
+ "Psycho"
+ ]
+ },
+ "382": {
+ "Bilder": [
+ [
+ "Kyogre",
+ "http://www.pokewiki.de/images/9/9b/Sugimori_382.png"
+ ],
+ [
+ "Proto-Kyogre",
+ "http://www.pokewiki.de/images/a/a3/Sugimori_382a.png"
+ ]
+ ],
+ "Deutsch": "Kyogre",
+ "Englisch": "Kyogre",
+ "Farbe": "Blau",
+ "Französisch": "Kyogre",
+ "Gewicht": [
+ "352,0 kg",
+ "430,0 kg (Proto)"
+ ],
+ "Größe": [
+ "4,5 m",
+ "9,8 m (Proto)"
+ ],
+ "Japanisch": "\\u30ab\\u30a4\\u30aa\\u30fc\\u30ac (Kyogre)",
+ "Kategorie": "Seegr\\u00fcndler",
+ "Link": "http://pokewiki.de/Kyogre",
+ "National-Dex": "#382",
+ "Typ": "Wasser"
+ },
+ "383": {
+ "Bilder": [
+ [
+ "Groudon",
+ "http://www.pokewiki.de/images/1/1f/Sugimori_383.png"
+ ],
+ [
+ "Proto-Groudon",
+ "http://www.pokewiki.de/images/5/59/Sugimori_383a.png"
+ ]
+ ],
+ "Deutsch": "Groudon",
+ "Englisch": "Groudon",
+ "Farbe": "Rot",
+ "Französisch": "Groudon",
+ "Gewicht": [
+ "950,0 kg",
+ "999,7 kg (Proto)"
+ ],
+ "Größe": [
+ "3,5 m",
+ "5,0 m (Proto)"
+ ],
+ "Japanisch": "\\u30b0\\u30e9\\u30fc\\u30c9\\u30f3 (Groudon)",
+ "Kategorie": "Kontinent",
+ "Link": "http://pokewiki.de/Groudon",
+ "National-Dex": "#383",
+ "Typ": [
+ "Boden",
+ "Boden",
+ "Feuer (Proto)"
+ ]
+ },
+ "384": {
+ "Bilder": [
+ [
+ "Rayquaza",
+ "http://www.pokewiki.de/images/7/7b/Sugimori_384.png"
+ ],
+ [
+ "Mega-Rayquaza",
+ "http://www.pokewiki.de/images/f/f2/Sugimori_384m1.png"
+ ]
+ ],
+ "Deutsch": "Rayquaza",
+ "Englisch": "Rayquaza",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Rayquaza",
+ "Gewicht": [
+ "206,5 kg",
+ "392,0 kg (Mega)"
+ ],
+ "Größe": [
+ "7,0 m",
+ "10,8 m (Mega)"
+ ],
+ "Japanisch": "\\u30ec\\u30c3\\u30af\\u30a6\\u30b6 (Rayquaza)",
+ "Kategorie": "Himmelhoch",
+ "Link": "http://pokewiki.de/Rayquaza",
+ "National-Dex": "#384",
+ "Typ": [
+ "Drache",
+ "Flug"
+ ]
+ },
+ "385": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6d/Sugimori_385.png"
+ ]
+ ],
+ "Deutsch": "Jirachi",
+ "Englisch": "Jirachi",
+ "Farbe": "Gelb",
+ "Französisch": "Jirachi",
+ "Gewicht": "1,1 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30b8\\u30e9\\u30fc\\u30c1 (Jirachi)",
+ "Kategorie": "W\\u00fcnscher",
+ "Link": "http://pokewiki.de/Jirachi",
+ "National-Dex": "#385",
+ "Typ": [
+ "Stahl",
+ "Psycho"
+ ]
+ },
+ "386": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/7/72/Sugimori_386.png"
+ ],
+ [
+ "Angriffsform",
+ "http://www.pokewiki.de/images/8/84/Sugimori_386a.png"
+ ],
+ [
+ "Verteidigungsform",
+ "http://www.pokewiki.de/images/d/d9/Sugimori_386b.png"
+ ],
+ [
+ "Initiativeform",
+ "http://www.pokewiki.de/images/7/76/Sugimori_386c.png"
+ ]
+ ],
+ "Deutsch": "Deoxys",
+ "Englisch": "Deoxys",
+ "Farbe": "Rot",
+ "Französisch": "D\\u00e9oxys",
+ "Gewicht": "60,8 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30c7\\u30aa\\u30ad\\u30b7\\u30b9 (Deoxys)",
+ "Kategorie": "DNS",
+ "Link": "http://pokewiki.de/Deoxys",
+ "National-Dex": "#386",
+ "Typ": "Psycho"
+ },
+ "387": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/44/Sugimori_387.png"
+ ]
+ ],
+ "Deutsch": "Chelast",
+ "Englisch": "Turtwig",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Tortipouss",
+ "Gewicht": "10,2 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30ca\\u30a8\\u30c8\\u30eb (Naetle)",
+ "Kategorie": "Winziglaub",
+ "Link": "http://pokewiki.de/Chelast",
+ "National-Dex": "#387",
+ "Typ": "Pflanze"
+ },
+ "388": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/71/Sugimori_388.png"
+ ]
+ ],
+ "Deutsch": "Chelcarain",
+ "Englisch": "Grotle",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Boskara",
+ "Gewicht": "97,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30cf\\u30e4\\u30b7\\u30ac\\u30e1 (Hayashigame)",
+ "Kategorie": "Hain",
+ "Link": "http://pokewiki.de/Chelcarain",
+ "National-Dex": "#388",
+ "Typ": "Pflanze"
+ },
+ "389": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b4/Sugimori_389.png"
+ ]
+ ],
+ "Deutsch": "Chelterrar",
+ "Englisch": "Torterra",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Torterra",
+ "Gewicht": "310,0 kg",
+ "Größe": "2,2 m",
+ "Japanisch": "\\u30c9\\u30c0\\u30a4\\u30c8\\u30b9 (Dodaitose)",
+ "Kategorie": "Kontinent",
+ "Link": "http://pokewiki.de/Chelterrar",
+ "National-Dex": "#389",
+ "Typ": [
+ "Pflanze",
+ "Boden"
+ ]
+ },
+ "390": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7b/Sugimori_390.png"
+ ]
+ ],
+ "Deutsch": "Panflam",
+ "Englisch": "Chimchar",
+ "Farbe": "Braun",
+ "Französisch": "Ouisticram",
+ "Gewicht": "6,2 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30d2\\u30b3\\u30b6\\u30eb (Hikozaru)",
+ "Kategorie": "Schimpanse",
+ "Link": "http://pokewiki.de/Panflam",
+ "National-Dex": "#390",
+ "Typ": "Feuer"
+ },
+ "391": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b7/Sugimori_391.png"
+ ]
+ ],
+ "Deutsch": "Panpyro",
+ "Englisch": "Monferno",
+ "Farbe": "Braun",
+ "Französisch": "Chimpenfeu",
+ "Gewicht": "22,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30e2\\u30a6\\u30ab\\u30b6\\u30eb (Moukazaru)",
+ "Kategorie": "Verspielt",
+ "Link": "http://pokewiki.de/Panpyro",
+ "National-Dex": "#391",
+ "Typ": [
+ "Feuer",
+ "Kampf"
+ ]
+ },
+ "392": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4d/Sugimori_392.png"
+ ]
+ ],
+ "Deutsch": "Panferno",
+ "Englisch": "Infernape",
+ "Farbe": "Braun",
+ "Französisch": "Simiabraz",
+ "Gewicht": "55,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30b4\\u30a6\\u30ab\\u30b6\\u30eb (Goukazaru)",
+ "Kategorie": "Flamme",
+ "Link": "http://pokewiki.de/Panferno",
+ "National-Dex": "#392",
+ "Typ": [
+ "Feuer",
+ "Kampf"
+ ]
+ },
+ "393": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/27/Sugimori_393.png"
+ ]
+ ],
+ "Deutsch": "Plinfa",
+ "Englisch": "Piplup",
+ "Farbe": "Blau",
+ "Französisch": "Tiplouf",
+ "Gewicht": "5,2 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30dd\\u30c3\\u30c1\\u30e3\\u30de (Pochama)",
+ "Kategorie": "Pinguin",
+ "Link": "http://pokewiki.de/Plinfa",
+ "National-Dex": "#393",
+ "Typ": "Wasser"
+ },
+ "394": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7e/Sugimori_394.png"
+ ]
+ ],
+ "Deutsch": "Pliprin",
+ "Englisch": "Prinplup",
+ "Farbe": "Blau",
+ "Französisch": "Prinplouf",
+ "Gewicht": "23,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30dd\\u30c3\\u30bf\\u30a4\\u30b7 (Pottaishi)",
+ "Kategorie": "Pinguin",
+ "Link": "http://pokewiki.de/Pliprin",
+ "National-Dex": "#394",
+ "Typ": "Wasser"
+ },
+ "395": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a9/Sugimori_395.png"
+ ]
+ ],
+ "Deutsch": "Impoleon",
+ "Englisch": "Empoleon",
+ "Farbe": "Blau",
+ "Französisch": "Pingol\\u00e9on",
+ "Gewicht": "84,5 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30a8\\u30f3\\u30da\\u30eb\\u30c8 (Emperte)",
+ "Kategorie": "Kaiser",
+ "Link": "http://pokewiki.de/Impoleon",
+ "National-Dex": "#395",
+ "Typ": [
+ "Wasser",
+ "Stahl"
+ ]
+ },
+ "396": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3c/Sugimori_396.png"
+ ]
+ ],
+ "Deutsch": "Staralili",
+ "Englisch": "Starly",
+ "Farbe": "Braun",
+ "Französisch": "\\u00c9tourmi",
+ "Gewicht": "2,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30e0\\u30c3\\u30af\\u30eb (Mukkuru)",
+ "Kategorie": "Star",
+ "Link": "http://pokewiki.de/Staralili",
+ "National-Dex": "#396",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "397": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/be/Sugimori_397.png"
+ ]
+ ],
+ "Deutsch": "Staravia",
+ "Englisch": "Staravia",
+ "Farbe": "Braun",
+ "Französisch": "\\u00c9tourvol",
+ "Gewicht": "15,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e0\\u30af\\u30d0\\u30fc\\u30c9 (Mukubird)",
+ "Kategorie": "Star",
+ "Link": "http://pokewiki.de/Staravia",
+ "National-Dex": "#397",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "398": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f1/Sugimori_398.png"
+ ]
+ ],
+ "Deutsch": "Staraptor",
+ "Englisch": "Staraptor",
+ "Farbe": "Braun",
+ "Französisch": "\\u00c9touraptor",
+ "Gewicht": "24,9 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30e0\\u30af\\u30db\\u30fc\\u30af (Mukuhawk)",
+ "Kategorie": "Raubtier",
+ "Link": "http://pokewiki.de/Staraptor",
+ "National-Dex": "#398",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "399": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/70/Sugimori_399.png"
+ ]
+ ],
+ "Deutsch": "Bidiza",
+ "Englisch": "Bidoof",
+ "Farbe": "Braun",
+ "Französisch": "Keunotor",
+ "Gewicht": "20,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30d3\\u30c3\\u30d1 (Bippa)",
+ "Kategorie": "Dickmaus",
+ "Link": "http://pokewiki.de/Bidiza",
+ "National-Dex": "#399",
+ "Typ": "Normal"
+ },
+ "400": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/73/Sugimori_400.png"
+ ]
+ ],
+ "Deutsch": "Bidifas",
+ "Englisch": "Bibarel",
+ "Farbe": "Braun",
+ "Französisch": "Castorno",
+ "Gewicht": "31,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30d3\\u30fc\\u30c0\\u30eb (Beadaru)",
+ "Kategorie": "Biber",
+ "Link": "http://pokewiki.de/Bidifas",
+ "National-Dex": "#400",
+ "Typ": [
+ "Normal",
+ "Wasser"
+ ]
+ },
+ "401": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0f/Sugimori_401.png"
+ ]
+ ],
+ "Deutsch": "Zirpurze",
+ "Englisch": "Kricketot",
+ "Farbe": "Rot",
+ "Französisch": "Crikzik",
+ "Gewicht": "2,2 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30b3\\u30ed\\u30dc\\u30fc\\u30b7 (Korobohshi)",
+ "Kategorie": "Zirper",
+ "Link": "http://pokewiki.de/Zirpurze",
+ "National-Dex": "#401",
+ "Typ": "K\\u00e4fer"
+ },
+ "402": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/84/Sugimori_402.png"
+ ]
+ ],
+ "Deutsch": "Zirpeise",
+ "Englisch": "Kricketune",
+ "Farbe": "Rot",
+ "Französisch": "Melokrik",
+ "Gewicht": "25,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b3\\u30ed\\u30c8\\u30c3\\u30af (Korotock)",
+ "Kategorie": "Zirper",
+ "Link": "http://pokewiki.de/Zirpeise",
+ "National-Dex": "#402",
+ "Typ": "K\\u00e4fer"
+ },
+ "403": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0f/Sugimori_403.png"
+ ]
+ ],
+ "Deutsch": "Sheinux",
+ "Englisch": "Shinx",
+ "Farbe": "Blau",
+ "Französisch": "Lixy",
+ "Gewicht": "9,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30b3\\u30ea\\u30f3\\u30af (Korinku)",
+ "Kategorie": "Flacker",
+ "Link": "http://pokewiki.de/Sheinux",
+ "National-Dex": "#403",
+ "Typ": "Elektro"
+ },
+ "404": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/87/Sugimori_404.png"
+ ]
+ ],
+ "Deutsch": "Luxio",
+ "Englisch": "Luxio",
+ "Farbe": "Blau",
+ "Französisch": "Luxio",
+ "Gewicht": "30,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30eb\\u30af\\u30b7\\u30aa (Luxio)",
+ "Kategorie": "Funken",
+ "Link": "http://pokewiki.de/Luxio",
+ "National-Dex": "#404",
+ "Typ": "Elektro"
+ },
+ "405": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/44/Sugimori_405.png"
+ ]
+ ],
+ "Deutsch": "Luxtra",
+ "Englisch": "Luxray",
+ "Farbe": "Blau",
+ "Französisch": "Luxray",
+ "Gewicht": "42,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30ec\\u30f3\\u30c8\\u30e9\\u30fc (Rentorar)",
+ "Kategorie": "Gl\\u00fchauge",
+ "Link": "http://pokewiki.de/Luxtra",
+ "National-Dex": "#405",
+ "Typ": "Elektro"
+ },
+ "406": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/cb/Sugimori_406.png"
+ ]
+ ],
+ "Deutsch": "Knospi",
+ "Englisch": "Budew",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Rozbouton",
+ "Gewicht": "1,2 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30b9\\u30dc\\u30df\\u30fc (Subomie)",
+ "Kategorie": "Knospe",
+ "Link": "http://pokewiki.de/Knospi",
+ "National-Dex": "#406",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "407": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/04/Sugimori_407.png"
+ ]
+ ],
+ "Deutsch": "Roserade",
+ "Englisch": "Roserade",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Roserade",
+ "Gewicht": "14,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30ed\\u30ba\\u30ec\\u30a4\\u30c9 (Roserade)",
+ "Kategorie": "Blumenstrau\\u00df",
+ "Link": "http://pokewiki.de/Roserade",
+ "National-Dex": "#407",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "408": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/db/Sugimori_408.png"
+ ]
+ ],
+ "Deutsch": "Koknodon",
+ "Englisch": "Cranidos",
+ "Farbe": "Blau",
+ "Französisch": "Kranidos",
+ "Gewicht": "31,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30ba\\u30ac\\u30a4\\u30c9\\u30b9 (Zugaidos)",
+ "Kategorie": "Kopfsto\\u00df",
+ "Link": "http://pokewiki.de/Koknodon",
+ "National-Dex": "#408",
+ "Typ": "Gestein"
+ },
+ "409": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/89/Sugimori_409.png"
+ ]
+ ],
+ "Deutsch": "Rameidon",
+ "Englisch": "Rampardos",
+ "Farbe": "Blau",
+ "Französisch": "Charkos",
+ "Gewicht": "102,5 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30e9\\u30e0\\u30d1\\u30eb\\u30c9 (Rampald)",
+ "Kategorie": "Kopfsto\\u00df",
+ "Link": "http://pokewiki.de/Rameidon",
+ "National-Dex": "#409",
+ "Typ": "Gestein"
+ },
+ "410": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b9/Sugimori_410.png"
+ ]
+ ],
+ "Deutsch": "Schilterus",
+ "Englisch": "Shieldon",
+ "Farbe": "Grau",
+ "Französisch": "Dinoclier",
+ "Gewicht": "57,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30bf\\u30c6\\u30c8\\u30d7\\u30b9 (Tatetops)",
+ "Kategorie": "Schutzschild",
+ "Link": "http://pokewiki.de/Schilterus",
+ "National-Dex": "#410",
+ "Typ": [
+ "Gestein",
+ "Stahl"
+ ]
+ },
+ "411": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4b/Sugimori_411.png"
+ ]
+ ],
+ "Deutsch": "Bollterus",
+ "Englisch": "Bastiodon",
+ "Farbe": "Grau",
+ "Französisch": "Bastiodon",
+ "Gewicht": "149,5 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30c8\\u30ea\\u30c7\\u30d7\\u30b9 (Torideps)",
+ "Kategorie": "Schutzschild",
+ "Link": "http://pokewiki.de/Bollterus",
+ "National-Dex": "#411",
+ "Typ": [
+ "Gestein",
+ "Stahl"
+ ]
+ },
+ "412": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/3/39/Sugimori_412.png"
+ ],
+ [
+ "Sandumhang",
+ "http://www.pokewiki.de/images/7/72/Sugimori_412a.png"
+ ],
+ [
+ "Lumpenumhang",
+ "http://www.pokewiki.de/images/5/57/Sugimori_412b.png"
+ ]
+ ],
+ "Deutsch": "Burmy",
+ "Englisch": "Burmy",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Cheniti",
+ "Gewicht": "3,4 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30df\\u30ce\\u30e0\\u30c3\\u30c1 (Minomucchi)",
+ "Kategorie": "Beutelwurm",
+ "Link": "http://pokewiki.de/Burmy",
+ "National-Dex": "#412",
+ "Typ": "K\\u00e4fer"
+ },
+ "413": {
+ "Bilder": [
+ [
+ "Pflanzenumhang",
+ "http://www.pokewiki.de/images/c/c1/Sugimori_413.png"
+ ],
+ [
+ "Sandumhang",
+ "http://www.pokewiki.de/images/c/c0/Sugimori_413a.png"
+ ],
+ [
+ "Lumpenumhang",
+ "http://www.pokewiki.de/images/1/1a/Sugimori_413b.png"
+ ]
+ ],
+ "Deutsch": "Burmadame",
+ "Englisch": "Wormadam",
+ "Farbe": [
+ "Gr\\u00fcn",
+ "Braun (Sandumhang)",
+ "Rot (Lumpenumhang)"
+ ],
+ "Französisch": "Cheniselle",
+ "Gewicht": "6,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30df\\u30ce\\u30de\\u30c0\\u30e0 (Minomadam)",
+ "Kategorie": "Beutelwurm",
+ "Link": "http://pokewiki.de/Burmadame",
+ "National-Dex": "#413",
+ "Typ": [
+ "K\\u00e4fer",
+ "Pflanze (Pflanzenumhang)",
+ "K\\u00e4fer",
+ "Boden (Sandumhang)",
+ "K\\u00e4fer",
+ "Stahl (Lumpenumhang)"
+ ]
+ },
+ "414": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b9/Sugimori_414.png"
+ ]
+ ],
+ "Deutsch": "Moterpel",
+ "Englisch": "Mothim",
+ "Farbe": "Gelb",
+ "Französisch": "Papilord",
+ "Gewicht": "23,3 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30ac\\u30fc\\u30e1\\u30a4\\u30eb (Garmale)",
+ "Kategorie": "Motte",
+ "Link": "http://pokewiki.de/Moterpel",
+ "National-Dex": "#414",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "415": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f9/Sugimori_415.png"
+ ]
+ ],
+ "Deutsch": "Wadribie",
+ "Englisch": "Combee",
+ "Farbe": "Gelb",
+ "Französisch": "Apitrini",
+ "Gewicht": "5,5 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30df\\u30c4\\u30cf\\u30cb\\u30fc (Mitsuhoney)",
+ "Kategorie": "Kleinbiene",
+ "Link": "http://pokewiki.de/Wadribie",
+ "National-Dex": "#415",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "416": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/19/Sugimori_416.png"
+ ]
+ ],
+ "Deutsch": "Honweisel",
+ "Englisch": "Vespiquen",
+ "Farbe": "Gelb",
+ "Französisch": "Apireine",
+ "Gewicht": "38,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30d3\\u30fc\\u30af\\u30a4\\u30f3 (Beequeen)",
+ "Kategorie": "Bienenstock",
+ "Link": "http://pokewiki.de/Honweisel",
+ "National-Dex": "#416",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "417": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/35/Sugimori_417.png"
+ ]
+ ],
+ "Deutsch": "Pachirisu",
+ "Englisch": "Pachirisu",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Pachirisu",
+ "Gewicht": "3,9 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30d1\\u30c1\\u30ea\\u30b9 (Pachirisu)",
+ "Kategorie": "Elektroh\\u00f6rnchen",
+ "Link": "http://pokewiki.de/Pachirisu",
+ "National-Dex": "#417",
+ "Typ": "Elektro"
+ },
+ "418": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8a/Sugimori_418.png"
+ ]
+ ],
+ "Deutsch": "Bamelin",
+ "Englisch": "Buizel",
+ "Farbe": "Braun",
+ "Französisch": "Must\\u00e9bou\\u00e9e",
+ "Gewicht": "29,5 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30d6\\u30a4\\u30bc\\u30eb (Buoysel)",
+ "Kategorie": "Meereswiesel",
+ "Link": "http://pokewiki.de/Bamelin",
+ "National-Dex": "#418",
+ "Typ": "Wasser"
+ },
+ "419": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/95/Sugimori_419.png"
+ ]
+ ],
+ "Deutsch": "Bojelin",
+ "Englisch": "Floatzel",
+ "Farbe": "Braun",
+ "Französisch": "Must\\u00e9flott",
+ "Gewicht": "33,5 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30d5\\u30ed\\u30fc\\u30bc\\u30eb (Floazel)",
+ "Kategorie": "Meereswiesel",
+ "Link": "http://pokewiki.de/Bojelin",
+ "National-Dex": "#419",
+ "Typ": "Wasser"
+ },
+ "420": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/62/Sugimori_420.png"
+ ]
+ ],
+ "Deutsch": "Kikugi",
+ "Englisch": "Cherubi",
+ "Farbe": "Rosa",
+ "Französisch": "Ceribou",
+ "Gewicht": "3,3 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30c1\\u30a7\\u30ea\\u30f3\\u30dc (Cherinbo)",
+ "Kategorie": "Kirsche",
+ "Link": "http://pokewiki.de/Kikugi",
+ "National-Dex": "#420",
+ "Typ": "Pflanze"
+ },
+ "421": {
+ "Bilder": [
+ [
+ "Wolkenform",
+ "http://www.pokewiki.de/images/6/63/Sugimori_421.png"
+ ],
+ [
+ "Sonnenform",
+ "http://www.pokewiki.de/images/7/76/Sugimori_421a.png"
+ ]
+ ],
+ "Deutsch": "Kinoso",
+ "Englisch": "Cherrim",
+ "Farbe": [
+ "Violett",
+ "Rosa (Sonnenform)"
+ ],
+ "Französisch": "Ceriflor",
+ "Gewicht": "9,3 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30c1\\u30a7\\u30ea\\u30e0 (Cherrim)",
+ "Kategorie": "Bl\\u00fcte",
+ "Link": "http://pokewiki.de/Kinoso",
+ "National-Dex": "#421",
+ "Typ": "Pflanze"
+ },
+ "422": {
+ "Bilder": [
+ [
+ "Westliches Meer",
+ "http://www.pokewiki.de/images/0/01/Sugimori_422.png"
+ ],
+ [
+ "\\u00d6stliches Meer",
+ "http://www.pokewiki.de/images/f/f2/Sugimori_422a.png"
+ ]
+ ],
+ "Deutsch": "Schalellos",
+ "Englisch": "Shellos",
+ "Farbe": [
+ "Violett",
+ "Blau (\\u00d6stliches Meer)"
+ ],
+ "Französisch": "Sancoki",
+ "Gewicht": "6,3 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30ab\\u30e9\\u30ca\\u30af\\u30b7 (Karanakushi)",
+ "Kategorie": "Seeschnecke",
+ "Link": "http://pokewiki.de/Schalellos",
+ "National-Dex": "#422",
+ "Typ": "Wasser"
+ },
+ "423": {
+ "Bilder": [
+ [
+ "Westliches Meer",
+ "http://www.pokewiki.de/images/7/7f/Sugimori_423.png"
+ ],
+ [
+ "\\u00d6stliches Meer",
+ "http://www.pokewiki.de/images/9/99/Sugimori_423a.png"
+ ]
+ ],
+ "Deutsch": "Gastrodon",
+ "Englisch": "Gastrodon",
+ "Farbe": [
+ "Violett",
+ "Blau (\\u00d6stliches Meer)"
+ ],
+ "Französisch": "Tritosor",
+ "Gewicht": "29,9 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30c8\\u30ea\\u30c8\\u30c9\\u30f3 (Tritodon)",
+ "Kategorie": "Seeschnecke",
+ "Link": "http://pokewiki.de/Gastrodon",
+ "National-Dex": "#423",
+ "Typ": [
+ "Wasser",
+ "Boden"
+ ]
+ },
+ "424": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8d/Sugimori_424.png"
+ ]
+ ],
+ "Deutsch": "Ambidiffel",
+ "Englisch": "Ambipom",
+ "Farbe": "Violett",
+ "Französisch": "Capidextre",
+ "Gewicht": "20,3 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30a8\\u30c6\\u30dc\\u30fc\\u30b9 (Eteboth)",
+ "Kategorie": "Langschweif",
+ "Link": "http://pokewiki.de/Ambidiffel",
+ "National-Dex": "#424",
+ "Typ": "Normal"
+ },
+ "425": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/02/Sugimori_425.png"
+ ]
+ ],
+ "Deutsch": "Driftlon",
+ "Englisch": "Drifloon",
+ "Farbe": "Violett",
+ "Französisch": "Baudrive",
+ "Gewicht": "1,2 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30d5\\u30ef\\u30f3\\u30c6 (Fuwante)",
+ "Kategorie": "Ballon",
+ "Link": "http://pokewiki.de/Driftlon",
+ "National-Dex": "#425",
+ "Typ": [
+ "Geist",
+ "Flug"
+ ]
+ },
+ "426": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/27/Sugimori_426.png"
+ ]
+ ],
+ "Deutsch": "Drifzepeli",
+ "Englisch": "Drifblim",
+ "Farbe": "Violett",
+ "Französisch": "Grodrive",
+ "Gewicht": "15,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30d5\\u30ef\\u30e9\\u30a4\\u30c9 (Fuwaride)",
+ "Kategorie": "Luftschiff",
+ "Link": "http://pokewiki.de/Drifzepeli",
+ "National-Dex": "#426",
+ "Typ": [
+ "Geist",
+ "Flug"
+ ]
+ },
+ "427": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3e/Sugimori_427.png"
+ ]
+ ],
+ "Deutsch": "Haspiror",
+ "Englisch": "Buneary",
+ "Farbe": "Braun",
+ "Französisch": "Laporeille",
+ "Gewicht": "5,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30df\\u30df\\u30ed\\u30eb (Mimirol)",
+ "Kategorie": "Hase",
+ "Link": "http://pokewiki.de/Haspiror",
+ "National-Dex": "#427",
+ "Typ": "Normal"
+ },
+ "428": {
+ "Bilder": [
+ [
+ "Schlapor",
+ "http://www.pokewiki.de/images/5/5f/Sugimori_428.png"
+ ],
+ [
+ "Mega-Schlapor",
+ "http://www.pokewiki.de/images/0/02/Sugimori_428m1.png"
+ ]
+ ],
+ "Deutsch": "Schlapor",
+ "Englisch": "Lopunny",
+ "Farbe": "Braun",
+ "Französisch": "Lockpin",
+ "Gewicht": [
+ "33,3 kg",
+ "28,3 kg (Mega)"
+ ],
+ "Größe": [
+ "1,2 m",
+ "1,3 m (Mega)"
+ ],
+ "Japanisch": "\\u30df\\u30df\\u30ed\\u30c3\\u30d7 (Mimilop)",
+ "Kategorie": "Hase",
+ "Link": "http://pokewiki.de/Schlapor",
+ "National-Dex": "#428",
+ "Typ": [
+ "Normal",
+ "Normal",
+ "Kampf (Mega)"
+ ]
+ },
+ "429": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5d/Sugimori_429.png"
+ ]
+ ],
+ "Deutsch": "Traunmagil",
+ "Englisch": "Mismagius",
+ "Farbe": "Violett",
+ "Französisch": "Magir\\u00eave",
+ "Gewicht": "4,4 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30e0\\u30a6\\u30de\\u30fc\\u30b8 (Mumargi)",
+ "Kategorie": "Magisch",
+ "Link": "http://pokewiki.de/Traunmagil",
+ "National-Dex": "#429",
+ "Typ": "Geist"
+ },
+ "430": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/53/Sugimori_430.png"
+ ]
+ ],
+ "Deutsch": "Kramshef",
+ "Englisch": "Honchkrow",
+ "Farbe": "Schwarz",
+ "Französisch": "Corboss",
+ "Gewicht": "27,3 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30c9\\u30f3\\u30ab\\u30e9\\u30b9 (Donkarasu)",
+ "Kategorie": "Anf\\u00fchrer",
+ "Link": "http://pokewiki.de/Kramshef",
+ "National-Dex": "#430",
+ "Typ": [
+ "Unlicht",
+ "Flug"
+ ]
+ },
+ "431": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b2/Sugimori_431.png"
+ ]
+ ],
+ "Deutsch": "Charmian",
+ "Englisch": "Glameow",
+ "Farbe": "Grau",
+ "Französisch": "Chaglam",
+ "Gewicht": "3,9 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30cb\\u30e3\\u30eb\\u30de\\u30fc (Nyarmar)",
+ "Kategorie": "Fies",
+ "Link": "http://pokewiki.de/Charmian",
+ "National-Dex": "#431",
+ "Typ": "Normal"
+ },
+ "432": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3e/Sugimori_432.png"
+ ]
+ ],
+ "Deutsch": "Shnurgarst",
+ "Englisch": "Purugly",
+ "Farbe": "Grau",
+ "Französisch": "Chaffreux",
+ "Gewicht": "43,8 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30d6\\u30cb\\u30e3\\u30c3\\u30c8 (Bunyatto)",
+ "Kategorie": "Tigerkatze",
+ "Link": "http://pokewiki.de/Shnurgarst",
+ "National-Dex": "#432",
+ "Typ": "Normal"
+ },
+ "433": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5e/Sugimori_433.png"
+ ]
+ ],
+ "Deutsch": "Klingplim",
+ "Englisch": "Chingling",
+ "Farbe": "Gelb",
+ "Französisch": "Korillon",
+ "Gewicht": "0,6 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30ea\\u30fc\\u30b7\\u30e3\\u30f3 (Lisyan)",
+ "Kategorie": "Gl\\u00f6ckchen",
+ "Link": "http://pokewiki.de/Klingplim",
+ "National-Dex": "#433",
+ "Typ": "Psycho"
+ },
+ "434": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e9/Sugimori_434.png"
+ ]
+ ],
+ "Deutsch": "Skunkapuh",
+ "Englisch": "Stunky",
+ "Farbe": "Violett",
+ "Französisch": "Moufouette",
+ "Gewicht": "19,2 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30b9\\u30ab\\u30f3\\u30d7\\u30fc (Skunpuu)",
+ "Kategorie": "Stinktier",
+ "Link": "http://pokewiki.de/Skunkapuh",
+ "National-Dex": "#434",
+ "Typ": [
+ "Gift",
+ "Unlicht"
+ ]
+ },
+ "435": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f3/Sugimori_435.png"
+ ]
+ ],
+ "Deutsch": "Skuntank",
+ "Englisch": "Skuntank",
+ "Farbe": "Violett",
+ "Französisch": "Moufflair",
+ "Gewicht": "38,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b9\\u30ab\\u30bf\\u30f3\\u30af (Skutank)",
+ "Kategorie": "Stinktier",
+ "Link": "http://pokewiki.de/Skuntank",
+ "National-Dex": "#435",
+ "Typ": [
+ "Gift",
+ "Unlicht"
+ ]
+ },
+ "436": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ad/Sugimori_436.png"
+ ]
+ ],
+ "Deutsch": "Bronzel",
+ "Englisch": "Bronzor",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Arch\\u00e9omire",
+ "Gewicht": "60,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30c9\\u30fc\\u30df\\u30e9\\u30fc (Dohmirror)",
+ "Kategorie": "Bronze",
+ "Link": "http://pokewiki.de/Bronzel",
+ "National-Dex": "#436",
+ "Typ": [
+ "Stahl",
+ "Psycho"
+ ]
+ },
+ "437": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/60/Sugimori_437.png"
+ ]
+ ],
+ "Deutsch": "Bronzong",
+ "Englisch": "Bronzong",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Arch\\u00e9odong",
+ "Gewicht": "187,0 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30c9\\u30fc\\u30bf\\u30af\\u30f3 (Dohtakun)",
+ "Kategorie": "Bronzeglocke",
+ "Link": "http://pokewiki.de/Bronzong",
+ "National-Dex": "#437",
+ "Typ": [
+ "Stahl",
+ "Psycho"
+ ]
+ },
+ "438": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fd/Sugimori_438.png"
+ ]
+ ],
+ "Deutsch": "Mobai",
+ "Englisch": "Bonsly",
+ "Farbe": "Braun",
+ "Französisch": "Manza\\u00ef",
+ "Gewicht": "15,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30a6\\u30bd\\u30cf\\u30c1 (Usohachi)",
+ "Kategorie": "Bonsai",
+ "Link": "http://pokewiki.de/Mobai",
+ "National-Dex": "#438",
+ "Typ": "Gestein"
+ },
+ "439": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3d/Sugimori_439.png"
+ ]
+ ],
+ "Deutsch": "Pantimimi",
+ "Englisch": "Mime Jr.",
+ "Farbe": "Rosa",
+ "Französisch": "Mime Jr.",
+ "Gewicht": "13,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30de\\u30cd\\u30cd (Manene)",
+ "Kategorie": "Mime",
+ "Link": "http://pokewiki.de/Pantimimi",
+ "National-Dex": "#439",
+ "Typ": [
+ "Psycho",
+ "Fee"
+ ]
+ },
+ "440": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/02/Sugimori_440.png"
+ ]
+ ],
+ "Deutsch": "Wonneira",
+ "Englisch": "Happiny",
+ "Farbe": "Rosa",
+ "Französisch": "Ptiravi",
+ "Gewicht": "24,4 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30d4\\u30f3\\u30d7\\u30af (Pinpuku)",
+ "Kategorie": "Spielhaus",
+ "Link": "http://pokewiki.de/Wonneira",
+ "National-Dex": "#440",
+ "Typ": "Normal"
+ },
+ "441": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/57/Sugimori_441.png"
+ ]
+ ],
+ "Deutsch": "Plaudagei",
+ "Englisch": "Chatot",
+ "Farbe": "Schwarz",
+ "Französisch": "Pijako",
+ "Gewicht": "1,9 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30da\\u30e9\\u30c3\\u30d7 (Perap)",
+ "Kategorie": "Musiknote",
+ "Link": "http://pokewiki.de/Plaudagei",
+ "National-Dex": "#441",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "442": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ec/Sugimori_442.png"
+ ]
+ ],
+ "Deutsch": "Kryppuk",
+ "Englisch": "Spiritomb",
+ "Farbe": "Violett",
+ "Französisch": "Spiritomb",
+ "Gewicht": "108,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30df\\u30ab\\u30eb\\u30b2 (Mikaruge)",
+ "Kategorie": "Verboten",
+ "Link": "http://pokewiki.de/Kryppuk",
+ "National-Dex": "#442",
+ "Typ": [
+ "Geist",
+ "Unlicht"
+ ]
+ },
+ "443": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f1/Sugimori_443.png"
+ ]
+ ],
+ "Deutsch": "Kaumalat",
+ "Englisch": "Gible",
+ "Farbe": "Blau",
+ "Französisch": "Griknot",
+ "Gewicht": "20,5 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30d5\\u30ab\\u30de\\u30eb (Fukamaru)",
+ "Kategorie": "Landhai",
+ "Link": "http://pokewiki.de/Kaumalat",
+ "National-Dex": "#443",
+ "Typ": [
+ "Drache",
+ "Boden"
+ ]
+ },
+ "444": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/21/Sugimori_444.png"
+ ]
+ ],
+ "Deutsch": "Knarksel",
+ "Englisch": "Gabite",
+ "Farbe": "Blau",
+ "Französisch": "Carmache",
+ "Gewicht": "56,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30ac\\u30d0\\u30a4\\u30c8 (Gabite)",
+ "Kategorie": "H\\u00f6hle",
+ "Link": "http://pokewiki.de/Knarksel",
+ "National-Dex": "#444",
+ "Typ": [
+ "Drache",
+ "Boden"
+ ]
+ },
+ "445": {
+ "Bilder": [
+ [
+ "Knakrack",
+ "http://www.pokewiki.de/images/b/be/Sugimori_445.png"
+ ],
+ [
+ "Mega-Knakrack",
+ "http://www.pokewiki.de/images/0/00/Sugimori_445m1.png"
+ ]
+ ],
+ "Deutsch": "Knakrack",
+ "Englisch": "Garchomp",
+ "Farbe": "Blau",
+ "Französisch": "Carchacrok",
+ "Gewicht": "95,0 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30ac\\u30d6\\u30ea\\u30a2\\u30b9 (Gaburias)",
+ "Kategorie": "Rasanz",
+ "Link": "http://pokewiki.de/Knakrack",
+ "National-Dex": "#445",
+ "Typ": [
+ "Drache",
+ "Boden"
+ ]
+ },
+ "446": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/96/Sugimori_446.png"
+ ]
+ ],
+ "Deutsch": "Mampfaxo",
+ "Englisch": "Munchlax",
+ "Farbe": "Schwarz",
+ "Französisch": "Goinfrex",
+ "Gewicht": "105,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30b4\\u30f3\\u30d9 (Gonbe)",
+ "Kategorie": "Nimmersatt",
+ "Link": "http://pokewiki.de/Mampfaxo",
+ "National-Dex": "#446",
+ "Typ": "Normal"
+ },
+ "447": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a9/Sugimori_447.png"
+ ]
+ ],
+ "Deutsch": "Riolu",
+ "Englisch": "Riolu",
+ "Farbe": "Blau",
+ "Französisch": "Riolu",
+ "Gewicht": "20,2 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30ea\\u30aa\\u30eb (Riolu)",
+ "Kategorie": "Wellenspiel",
+ "Link": "http://pokewiki.de/Riolu",
+ "National-Dex": "#447",
+ "Typ": "Kampf"
+ },
+ "448": {
+ "Bilder": [
+ [
+ "Lucario",
+ "http://www.pokewiki.de/images/e/e7/Sugimori_448.png"
+ ],
+ [
+ "Mega-Lucario",
+ "http://www.pokewiki.de/images/7/76/Sugimori_448m1.png"
+ ]
+ ],
+ "Deutsch": "Lucario",
+ "Englisch": "Lucario",
+ "Farbe": "Blau",
+ "Französisch": "Lucario",
+ "Gewicht": [
+ "54,0 kg",
+ "57,5 kg (Mega)"
+ ],
+ "Größe": [
+ "1,2 m",
+ "1,3 m (Mega)"
+ ],
+ "Japanisch": "\\u30eb\\u30ab\\u30ea\\u30aa (Lucario)",
+ "Kategorie": "Aura",
+ "Link": "http://pokewiki.de/Lucario",
+ "National-Dex": "#448",
+ "Typ": [
+ "Kampf",
+ "Stahl"
+ ]
+ },
+ "449": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a6/Sugimori_449.png"
+ ]
+ ],
+ "Deutsch": "Hippopotas",
+ "Englisch": "Hippopotas",
+ "Farbe": "Braun",
+ "Französisch": "Hippopotas",
+ "Gewicht": "49,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30d2\\u30dd\\u30dd\\u30bf\\u30b9 (Hippopotas)",
+ "Kategorie": "Flusspferd",
+ "Link": "http://pokewiki.de/Hippopotas",
+ "National-Dex": "#449",
+ "Typ": "Boden"
+ },
+ "450": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/37/Sugimori_450.png"
+ ]
+ ],
+ "Deutsch": "Hippoterus",
+ "Englisch": "Hippowdon",
+ "Farbe": "Braun",
+ "Französisch": "Hippodocus",
+ "Gewicht": "300,0 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30ab\\u30d0\\u30eb\\u30c9\\u30f3 (Kabarudon)",
+ "Kategorie": "Schwergewicht",
+ "Link": "http://pokewiki.de/Hippoterus",
+ "National-Dex": "#450",
+ "Typ": "Boden"
+ },
+ "451": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6e/Sugimori_451.png"
+ ]
+ ],
+ "Deutsch": "Pionskora",
+ "Englisch": "Skorupi",
+ "Farbe": "Violett",
+ "Französisch": "Rapion",
+ "Gewicht": "12,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30b9\\u30b3\\u30eb\\u30d4 (Scorupi)",
+ "Kategorie": "Skorpion",
+ "Link": "http://pokewiki.de/Pionskora",
+ "National-Dex": "#451",
+ "Typ": [
+ "Gift",
+ "K\\u00e4fer"
+ ]
+ },
+ "452": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7e/Sugimori_452.png"
+ ]
+ ],
+ "Deutsch": "Piondragi",
+ "Englisch": "Drapion",
+ "Farbe": "Violett",
+ "Französisch": "Drascore",
+ "Gewicht": "61,5 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30c9\\u30e9\\u30d4\\u30aa\\u30f3 (Dorapion)",
+ "Kategorie": "Ogerskorpion",
+ "Link": "http://pokewiki.de/Piondragi",
+ "National-Dex": "#452",
+ "Typ": [
+ "Gift",
+ "Unlicht"
+ ]
+ },
+ "453": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/28/Sugimori_453.png"
+ ]
+ ],
+ "Deutsch": "Glibunkel",
+ "Englisch": "Croagunk",
+ "Farbe": "Blau",
+ "Französisch": "Cradopaud",
+ "Gewicht": "23,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30b0\\u30ec\\u30c3\\u30b0\\u30eb (Gureggru)",
+ "Kategorie": "Giftmund",
+ "Link": "http://pokewiki.de/Glibunkel",
+ "National-Dex": "#453",
+ "Typ": [
+ "Gift",
+ "Kampf"
+ ]
+ },
+ "454": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/cc/Sugimori_454.png"
+ ]
+ ],
+ "Deutsch": "Toxiquak",
+ "Englisch": "Toxicroak",
+ "Farbe": "Blau",
+ "Französisch": "Coatox",
+ "Gewicht": "44,4 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30c9\\u30af\\u30ed\\u30c3\\u30b0 (Dokurog)",
+ "Kategorie": "Giftmund",
+ "Link": "http://pokewiki.de/Toxiquak",
+ "National-Dex": "#454",
+ "Typ": [
+ "Gift",
+ "Kampf"
+ ]
+ },
+ "455": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f6/Sugimori_455.png"
+ ]
+ ],
+ "Deutsch": "Venuflibis",
+ "Englisch": "Carnivine",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Vortente",
+ "Gewicht": "27,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30de\\u30b9\\u30ad\\u30c3\\u30d1 (Muskippa)",
+ "Kategorie": "K\\u00e4fertod",
+ "Link": "http://pokewiki.de/Venuflibis",
+ "National-Dex": "#455",
+ "Typ": "Pflanze"
+ },
+ "456": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/40/Sugimori_456.png"
+ ]
+ ],
+ "Deutsch": "Finneon",
+ "Englisch": "Finneon",
+ "Farbe": "Blau",
+ "Französisch": "\\u00c9cayon",
+ "Gewicht": "7,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30b1\\u30a4\\u30b3\\u30a6\\u30aa (Keikouo)",
+ "Kategorie": "Fl\\u00fcgelfisch",
+ "Link": "http://pokewiki.de/Finneon",
+ "National-Dex": "#456",
+ "Typ": "Wasser"
+ },
+ "457": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d8/Sugimori_457.png"
+ ]
+ ],
+ "Deutsch": "Lumineon",
+ "Englisch": "Lumineon",
+ "Farbe": "Blau",
+ "Französisch": "Lumin\\u00e9on",
+ "Gewicht": "24,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30cd\\u30aa\\u30e9\\u30f3\\u30c8 (Neolant)",
+ "Kategorie": "Neon",
+ "Link": "http://pokewiki.de/Lumineon",
+ "National-Dex": "#457",
+ "Typ": "Wasser"
+ },
+ "458": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/44/Sugimori_458.png"
+ ]
+ ],
+ "Deutsch": "Mantirps",
+ "Englisch": "Mantyke",
+ "Farbe": "Blau",
+ "Französisch": "Babimanta",
+ "Gewicht": "65,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30bf\\u30de\\u30f3\\u30bf (Tamanta)",
+ "Kategorie": "Flugrochen",
+ "Link": "http://pokewiki.de/Mantirps",
+ "National-Dex": "#458",
+ "Typ": [
+ "Wasser",
+ "Flug"
+ ]
+ },
+ "459": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/bc/Sugimori_459.png"
+ ]
+ ],
+ "Deutsch": "Shnebedeck",
+ "Englisch": "Snover",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Blizzi",
+ "Gewicht": "50,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30e6\\u30ad\\u30ab\\u30d6\\u30ea (Yukikaburi)",
+ "Kategorie": "Frostbaum",
+ "Link": "http://pokewiki.de/Shnebedeck",
+ "National-Dex": "#459",
+ "Typ": [
+ "Pflanze",
+ "Eis"
+ ]
+ },
+ "460": {
+ "Bilder": [
+ [
+ "Rexblisar",
+ "http://www.pokewiki.de/images/7/73/Sugimori_460.png"
+ ],
+ [
+ "Mega-Rexblisar",
+ "http://www.pokewiki.de/images/d/dc/Sugimori_460m1.png"
+ ]
+ ],
+ "Deutsch": "Rexblisar",
+ "Englisch": "Abomasnow",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Blizzaroi",
+ "Gewicht": [
+ "135,5 kg",
+ "185,0 kg (Mega)"
+ ],
+ "Größe": [
+ "2,2 m",
+ "2,7 m (Mega)"
+ ],
+ "Japanisch": "\\u30e6\\u30ad\\u30ce\\u30aa\\u30fc (Yukinooh)",
+ "Kategorie": "Frostbaum",
+ "Link": "http://pokewiki.de/Rexblisar",
+ "National-Dex": "#460",
+ "Typ": [
+ "Pflanze",
+ "Eis"
+ ]
+ },
+ "461": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/9f/Sugimori_461.png"
+ ]
+ ],
+ "Deutsch": "Snibunna",
+ "Englisch": "Weavile",
+ "Farbe": "Schwarz",
+ "Französisch": "Dimoret",
+ "Gewicht": "34,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30de\\u30cb\\u30e5\\u30fc\\u30e9 (Manyula)",
+ "Kategorie": "Stichklaue",
+ "Link": "http://pokewiki.de/Snibunna",
+ "National-Dex": "#461",
+ "Typ": [
+ "Unlicht",
+ "Eis"
+ ]
+ },
+ "462": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6f/Sugimori_462.png"
+ ]
+ ],
+ "Deutsch": "Magnezone",
+ "Englisch": "Magnezone",
+ "Farbe": "Grau",
+ "Französisch": "Magn\\u00e9zone",
+ "Gewicht": "180,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30b8\\u30d0\\u30b3\\u30a4\\u30eb (Jibacoil)",
+ "Kategorie": "Magnetgebiet",
+ "Link": "http://pokewiki.de/Magnezone",
+ "National-Dex": "#462",
+ "Typ": [
+ "Elektro",
+ "Stahl"
+ ]
+ },
+ "463": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/eb/Sugimori_463.png"
+ ]
+ ],
+ "Deutsch": "Schlurplek",
+ "Englisch": "Lickilicky",
+ "Farbe": "Rosa",
+ "Französisch": "Coudlangue",
+ "Gewicht": "140,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30d9\\u30ed\\u30d9\\u30eb\\u30c8 (Berobelt)",
+ "Kategorie": "Schlecker",
+ "Link": "http://pokewiki.de/Schlurplek",
+ "National-Dex": "#463",
+ "Typ": "Normal"
+ },
+ "464": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c1/Sugimori_464.png"
+ ]
+ ],
+ "Deutsch": "Rihornior",
+ "Englisch": "Rhyperior",
+ "Farbe": "Grau",
+ "Französisch": "Rhinastoc",
+ "Gewicht": "282,8 kg",
+ "Größe": "2,4 m",
+ "Japanisch": "\\u30c9\\u30b5\\u30a4\\u30c9\\u30f3 (Doseidon)",
+ "Kategorie": "Bohrer",
+ "Link": "http://pokewiki.de/Rihornior",
+ "National-Dex": "#464",
+ "Typ": [
+ "Boden",
+ "Gestein"
+ ]
+ },
+ "465": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/15/Sugimori_465.png"
+ ]
+ ],
+ "Deutsch": "Tangoloss",
+ "Englisch": "Tangrowth",
+ "Farbe": "Blau",
+ "Französisch": "Bouldeneu",
+ "Gewicht": "128,6 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30e2\\u30b8\\u30e3\\u30f3\\u30dc (Mojumbo)",
+ "Kategorie": "Ranke",
+ "Link": "http://pokewiki.de/Tangoloss",
+ "National-Dex": "#465",
+ "Typ": "Pflanze"
+ },
+ "466": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3d/Sugimori_466.png"
+ ]
+ ],
+ "Deutsch": "Elevoltek",
+ "Englisch": "Electivire",
+ "Farbe": "Gelb",
+ "Französisch": "\\u00c9lekable",
+ "Gewicht": "138,6 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30a8\\u30ec\\u30ad\\u30d6\\u30eb (Elekible)",
+ "Kategorie": "Donnerkeil",
+ "Link": "http://pokewiki.de/Elevoltek",
+ "National-Dex": "#466",
+ "Typ": "Elektro"
+ },
+ "467": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e6/Sugimori_467.png"
+ ]
+ ],
+ "Deutsch": "Magbrant",
+ "Englisch": "Magmortar",
+ "Farbe": "Rot",
+ "Französisch": "Maganon",
+ "Gewicht": "68,0 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30d6\\u30fc\\u30d0\\u30fc\\u30f3 (Booburn)",
+ "Kategorie": "Detonation",
+ "Link": "http://pokewiki.de/Magbrant",
+ "National-Dex": "#467",
+ "Typ": "Feuer"
+ },
+ "468": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3f/Sugimori_468.png"
+ ]
+ ],
+ "Deutsch": "Togekiss",
+ "Englisch": "Togekiss",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Togekiss",
+ "Gewicht": "38,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30c8\\u30b2\\u30ad\\u30c3\\u30b9 (Togekiss)",
+ "Kategorie": "Jubilierer",
+ "Link": "http://pokewiki.de/Togekiss",
+ "National-Dex": "#468",
+ "Typ": [
+ "Fee",
+ "Flug"
+ ]
+ },
+ "469": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d5/Sugimori_469.png"
+ ]
+ ],
+ "Deutsch": "Yanmega",
+ "Englisch": "Yanmega",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Yanmega",
+ "Gewicht": "51,5 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30e1\\u30ac\\u30e4\\u30f3\\u30de (Megayanma)",
+ "Kategorie": "Agrion",
+ "Link": "http://pokewiki.de/Yanmega",
+ "National-Dex": "#469",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "470": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3a/Sugimori_470.png"
+ ]
+ ],
+ "Deutsch": "Folipurba",
+ "Englisch": "Leafeon",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Phyllali",
+ "Gewicht": "25,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30ea\\u30fc\\u30d5\\u30a3\\u30a2 (Leafia)",
+ "Kategorie": "Unreif",
+ "Link": "http://pokewiki.de/Folipurba",
+ "National-Dex": "#470",
+ "Typ": "Pflanze"
+ },
+ "471": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b5/Sugimori_471.png"
+ ]
+ ],
+ "Deutsch": "Glaziola",
+ "Englisch": "Glaceon",
+ "Farbe": "Blau",
+ "Französisch": "Givrali",
+ "Gewicht": "25,9 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30b0\\u30ec\\u30a4\\u30b7\\u30a2 (Glacia)",
+ "Kategorie": "Neuschnee",
+ "Link": "http://pokewiki.de/Glaziola",
+ "National-Dex": "#471",
+ "Typ": "Eis"
+ },
+ "472": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/19/Sugimori_472.png"
+ ]
+ ],
+ "Deutsch": "Skorgro",
+ "Englisch": "Gliscor",
+ "Farbe": "Violett",
+ "Französisch": "Scorvol",
+ "Gewicht": "42,5 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30b0\\u30e9\\u30a4\\u30aa\\u30f3 (Glion)",
+ "Kategorie": "Zahnskorpi",
+ "Link": "http://pokewiki.de/Skorgro",
+ "National-Dex": "#472",
+ "Typ": [
+ "Boden",
+ "Flug"
+ ]
+ },
+ "473": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/db/Sugimori_473.png"
+ ]
+ ],
+ "Deutsch": "Mamutel",
+ "Englisch": "Mamoswine",
+ "Farbe": "Braun",
+ "Französisch": "Mammochon",
+ "Gewicht": "291,0 kg",
+ "Größe": "2,5 m",
+ "Japanisch": "\\u30de\\u30f3\\u30e0\\u30fc (Mammoo)",
+ "Kategorie": "Doppelsto\\u00dfzahn",
+ "Link": "http://pokewiki.de/Mamutel",
+ "National-Dex": "#473",
+ "Typ": [
+ "Eis",
+ "Boden"
+ ]
+ },
+ "474": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/25/Sugimori_474.png"
+ ]
+ ],
+ "Deutsch": "Porygon-Z",
+ "Englisch": "Porygon-Z",
+ "Farbe": "Rot",
+ "Französisch": "Porygon-Z",
+ "Gewicht": "34,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30dd\\u30ea\\u30b4\\u30f3\\uff3a (Porygon-Z)",
+ "Kategorie": "Virtuell",
+ "Link": "http://pokewiki.de/Porygon-Z",
+ "National-Dex": "#474",
+ "Typ": "Normal"
+ },
+ "475": {
+ "Bilder": [
+ [
+ "Galagladi",
+ "http://www.pokewiki.de/images/3/31/Sugimori_475.png"
+ ],
+ [
+ "Mega-Galagladi",
+ "http://www.pokewiki.de/images/a/ad/Sugimori_475m1.png"
+ ]
+ ],
+ "Deutsch": "Galagladi",
+ "Englisch": "Gallade",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Gallame",
+ "Gewicht": [
+ "52,0 kg",
+ "56,4 kg (Mega)"
+ ],
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30a8\\u30eb\\u30ec\\u30a4\\u30c9 (Erureido)",
+ "Kategorie": "Klinge",
+ "Link": "http://pokewiki.de/Galagladi",
+ "National-Dex": "#475",
+ "Typ": [
+ "Psycho",
+ "Kampf"
+ ]
+ },
+ "476": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/67/Sugimori_476.png"
+ ]
+ ],
+ "Deutsch": "Voluminas",
+ "Englisch": "Probopass",
+ "Farbe": "Grau",
+ "Französisch": "Tarinorme",
+ "Gewicht": "340,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30c0\\u30a4\\u30ce\\u30fc\\u30ba (Dainose)",
+ "Kategorie": "Kompass",
+ "Link": "http://pokewiki.de/Voluminas",
+ "National-Dex": "#476",
+ "Typ": [
+ "Gestein",
+ "Stahl"
+ ]
+ },
+ "477": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f9/Sugimori_477.png"
+ ]
+ ],
+ "Deutsch": "Zwirrfinst",
+ "Englisch": "Dusknoir",
+ "Farbe": "Schwarz",
+ "Französisch": "Noctunoir",
+ "Gewicht": "106,6 kg",
+ "Größe": "2,2 m",
+ "Japanisch": "\\u30e8\\u30ce\\u30ef\\u30fc\\u30eb (Yonoir)",
+ "Kategorie": "Greifer",
+ "Link": "http://pokewiki.de/Zwirrfinst",
+ "National-Dex": "#477",
+ "Typ": "Geist"
+ },
+ "478": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/26/Sugimori_478.png"
+ ]
+ ],
+ "Deutsch": "Frosdedje",
+ "Englisch": "Froslass",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Momartik",
+ "Gewicht": "26,6 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30e6\\u30ad\\u30e1\\u30ce\\u30b3 (Yukimenoko)",
+ "Kategorie": "Schneegebiet",
+ "Link": "http://pokewiki.de/Frosdedje",
+ "National-Dex": "#478",
+ "Typ": [
+ "Eis",
+ "Geist"
+ ]
+ },
+ "479": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/7/72/Sugimori_479.png"
+ ],
+ [
+ "Hitze-Form",
+ "http://www.pokewiki.de/images/2/2e/Sugimori_479a.png"
+ ],
+ [
+ "Wasch-Form",
+ "http://www.pokewiki.de/images/c/c8/Sugimori_479b.png"
+ ],
+ [
+ "Frost-Form",
+ "http://www.pokewiki.de/images/d/d2/Sugimori_479c.png"
+ ],
+ [
+ "Wirbel-Form",
+ "http://www.pokewiki.de/images/7/72/Sugimori_479d.png"
+ ],
+ [
+ "Schneid-Form",
+ "http://www.pokewiki.de/images/4/40/Sugimori_479e.png"
+ ]
+ ],
+ "Deutsch": "Rotom",
+ "Englisch": "Rotom",
+ "Farbe": "Rot",
+ "Französisch": "Motisma",
+ "Gewicht": "0,3 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30ed\\u30c8\\u30e0 (Rotom)",
+ "Kategorie": "Plasma",
+ "Link": "http://pokewiki.de/Rotom",
+ "National-Dex": "#479",
+ "Typ": [
+ "Elektro",
+ "Geist (Normalform)",
+ "Elektro",
+ "Feuer (Hitze-Form)",
+ "Elektro",
+ "Wasser (Wasch-Form)",
+ "Elektro",
+ "Eis (Frost-Form)",
+ "Elektro",
+ "Flug (Wirbel-Form)",
+ "Elektro",
+ "Pflanze (Schneid-Form)"
+ ]
+ },
+ "480": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d6/Sugimori_480.png"
+ ]
+ ],
+ "Deutsch": "Selfe",
+ "Englisch": "Uxie",
+ "Farbe": "Gelb",
+ "Französisch": "Cr\\u00e9helf",
+ "Gewicht": "0,3 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30e6\\u30af\\u30b7\\u30fc (Yuxie)",
+ "Kategorie": "Wissen",
+ "Link": "http://pokewiki.de/Selfe",
+ "National-Dex": "#480",
+ "Typ": "Psycho"
+ },
+ "481": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f1/Sugimori_481.png"
+ ]
+ ],
+ "Deutsch": "Vesprit",
+ "Englisch": "Mesprit",
+ "Farbe": "Rosa",
+ "Französisch": "Cr\\u00e9follet",
+ "Gewicht": "0,3 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30a8\\u30e0\\u30ea\\u30c3\\u30c8 (Emrit)",
+ "Kategorie": "Emotion",
+ "Link": "http://pokewiki.de/Vesprit",
+ "National-Dex": "#481",
+ "Typ": "Psycho"
+ },
+ "482": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d9/Sugimori_482.png"
+ ]
+ ],
+ "Deutsch": "Tobutz",
+ "Englisch": "Azelf",
+ "Farbe": "Blau",
+ "Französisch": "Cr\\u00e9fadet",
+ "Gewicht": "0,3 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30a2\\u30b0\\u30ce\\u30e0 (Agnome)",
+ "Kategorie": "Willenskraft",
+ "Link": "http://pokewiki.de/Tobutz",
+ "National-Dex": "#482",
+ "Typ": "Psycho"
+ },
+ "483": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/75/Sugimori_483.png"
+ ]
+ ],
+ "Deutsch": "Dialga",
+ "Englisch": "Dialga",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Dialga",
+ "Gewicht": "683,0 kg",
+ "Größe": "5,4 m",
+ "Japanisch": "\\u30c7\\u30a3\\u30a2\\u30eb\\u30ac (Dialga)",
+ "Kategorie": "Zeitweilig",
+ "Link": "http://pokewiki.de/Dialga",
+ "National-Dex": "#483",
+ "Typ": [
+ "Stahl",
+ "Drache"
+ ]
+ },
+ "484": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/19/Sugimori_484.png"
+ ]
+ ],
+ "Deutsch": "Palkia",
+ "Englisch": "Palkia",
+ "Farbe": "Violett",
+ "Französisch": "Palkia",
+ "Gewicht": "336,0 kg",
+ "Größe": "4,2 m",
+ "Japanisch": "\\u30d1\\u30eb\\u30ad\\u30a2 (Palkia)",
+ "Kategorie": "R\\u00e4umlich",
+ "Link": "http://pokewiki.de/Palkia",
+ "National-Dex": "#484",
+ "Typ": [
+ "Wasser",
+ "Drache"
+ ]
+ },
+ "485": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/10/Sugimori_485.png"
+ ]
+ ],
+ "Deutsch": "Heatran",
+ "Englisch": "Heatran",
+ "Farbe": "Braun",
+ "Französisch": "Heatran",
+ "Gewicht": "430,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30d2\\u30fc\\u30c9\\u30e9\\u30f3 (Heatran)",
+ "Kategorie": "Lavadom",
+ "Link": "http://pokewiki.de/Heatran",
+ "National-Dex": "#485",
+ "Typ": [
+ "Feuer",
+ "Stahl"
+ ]
+ },
+ "486": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/48/Sugimori_486.png"
+ ]
+ ],
+ "Deutsch": "Regigigas",
+ "Englisch": "Regigigas",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "R\\u00e9gigigas",
+ "Gewicht": "420,0 kg",
+ "Größe": "3,7 m",
+ "Japanisch": "\\u30ec\\u30b8\\u30ae\\u30ac\\u30b9 (Regigigas)",
+ "Kategorie": "Kolossal",
+ "Link": "http://pokewiki.de/Regigigas",
+ "National-Dex": "#486",
+ "Typ": "Normal"
+ },
+ "487": {
+ "Bilder": [
+ [
+ "Wandelform",
+ "http://www.pokewiki.de/images/3/38/Sugimori_487.png"
+ ],
+ [
+ "Urform",
+ "http://www.pokewiki.de/images/4/46/Sugimori_487a.png"
+ ]
+ ],
+ "Deutsch": "Giratina",
+ "Englisch": "Giratina",
+ "Farbe": "Schwarz",
+ "Französisch": "Giratina",
+ "Gewicht": [
+ "750,0 kg (Wandelform)",
+ "650,0 kg (Urform)"
+ ],
+ "Größe": [
+ "4,5 m (Wandelform)",
+ "6,9 m (Urform)"
+ ],
+ "Japanisch": "\\u30ae\\u30e9\\u30c6\\u30a3\\u30ca (Giratina)",
+ "Kategorie": "Rebell",
+ "Link": "http://pokewiki.de/Giratina",
+ "National-Dex": "#487",
+ "Typ": [
+ "Geist",
+ "Drache"
+ ]
+ },
+ "488": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/28/Sugimori_488.png"
+ ]
+ ],
+ "Deutsch": "Cresselia",
+ "Englisch": "Cresselia",
+ "Farbe": "Gelb",
+ "Französisch": "Cresselia",
+ "Gewicht": "85,6 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30af\\u30ec\\u30bb\\u30ea\\u30a2 (Crecelia)",
+ "Kategorie": "Lunar",
+ "Link": "http://pokewiki.de/Cresselia",
+ "National-Dex": "#488",
+ "Typ": "Psycho"
+ },
+ "489": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b9/Sugimori_489.png"
+ ]
+ ],
+ "Deutsch": "Phione",
+ "Englisch": "Phione",
+ "Farbe": "Blau",
+ "Französisch": "Phione",
+ "Gewicht": "3,1 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30d5\\u30a3\\u30aa\\u30cd (Phione)",
+ "Kategorie": "Seedrift",
+ "Link": "http://pokewiki.de/Phione",
+ "National-Dex": "#489",
+ "Typ": "Wasser"
+ },
+ "490": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/90/Sugimori_490.png"
+ ]
+ ],
+ "Deutsch": "Manaphy",
+ "Englisch": "Manaphy",
+ "Farbe": "Blau",
+ "Französisch": "Manaphy",
+ "Gewicht": "1,4 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30de\\u30ca\\u30d5\\u30a3 (Manaphy)",
+ "Kategorie": "Seefahrer",
+ "Link": "http://pokewiki.de/Manaphy",
+ "National-Dex": "#490",
+ "Typ": "Wasser"
+ },
+ "491": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f2/Sugimori_491.png"
+ ]
+ ],
+ "Deutsch": "Darkrai",
+ "Englisch": "Darkrai",
+ "Farbe": "Schwarz",
+ "Französisch": "Darkrai",
+ "Gewicht": "50,5 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30c0\\u30fc\\u30af\\u30e9\\u30a4 (Darkrai)",
+ "Kategorie": "Dunkelnacht",
+ "Link": "http://pokewiki.de/Darkrai",
+ "National-Dex": "#491",
+ "Typ": "Unlicht"
+ },
+ "492": {
+ "Bilder": [
+ [
+ "Landform",
+ "http://www.pokewiki.de/images/7/71/Sugimori_492.png"
+ ],
+ [
+ "Zenitform",
+ "http://www.pokewiki.de/images/7/71/Sugimori_492a.png"
+ ]
+ ],
+ "Deutsch": "Shaymin",
+ "Englisch": "Shaymin",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Shaymin",
+ "Gewicht": [
+ "2,1 kg (Landform)",
+ "5,2 kg (Zenitform)"
+ ],
+ "Größe": [
+ "0,2 m (Landform)",
+ "0,4 m (Zenitform)"
+ ],
+ "Japanisch": "\\u30b7\\u30a7\\u30a4\\u30df (Shaymin)",
+ "Kategorie": "Dankbarkeit",
+ "Link": "http://pokewiki.de/Shaymin",
+ "National-Dex": "#492",
+ "Typ": [
+ "Pflanze (Landform)",
+ "Pflanze",
+ "Flug (Zenitform)"
+ ]
+ },
+ "493": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a9/Sugimori_493.png"
+ ]
+ ],
+ "Deutsch": "Arceus",
+ "Englisch": "Arceus",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Arceus",
+ "Gewicht": "320,0 kg",
+ "Größe": "3,2 m",
+ "Japanisch": "\\u30a2\\u30eb\\u30bb\\u30a6\\u30b9 (Arceus)",
+ "Kategorie": "Alpha",
+ "Link": "http://pokewiki.de/Arceus",
+ "National-Dex": "#493",
+ "Typ": "Normal (variabel) (variiert je nach getragener Tafel)"
+ },
+ "494": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5b/Sugimori_494.png"
+ ]
+ ],
+ "Deutsch": "Victini",
+ "Englisch": "Victini",
+ "Farbe": "Gelb",
+ "Französisch": "Victini",
+ "Gewicht": "4,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30d3\\u30af\\u30c6\\u30a3\\u30cb (Victini)",
+ "Kategorie": "Triumph",
+ "Link": "http://pokewiki.de/Victini",
+ "National-Dex": "#494",
+ "Typ": [
+ "Psycho",
+ "Feuer"
+ ]
+ },
+ "495": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d1/Sugimori_495.png"
+ ]
+ ],
+ "Deutsch": "Serpifeu",
+ "Englisch": "Snivy",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Vip\\u00e9lierre",
+ "Gewicht": "8,1 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c4\\u30bf\\u30fc\\u30b8\\u30e3 (Tsutarja)",
+ "Kategorie": "Grasschlange",
+ "Link": "http://pokewiki.de/Serpifeu",
+ "National-Dex": "#495",
+ "Typ": "Pflanze"
+ },
+ "496": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ac/Sugimori_496.png"
+ ]
+ ],
+ "Deutsch": "Efoserp",
+ "Englisch": "Servine",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Lianaja",
+ "Gewicht": "16,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30b8\\u30e3\\u30ce\\u30d3\\u30fc (Janovy)",
+ "Kategorie": "Grasschlange",
+ "Link": "http://pokewiki.de/Efoserp",
+ "National-Dex": "#496",
+ "Typ": "Pflanze"
+ },
+ "497": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e2/Sugimori_497.png"
+ ]
+ ],
+ "Deutsch": "Serpiroyal",
+ "Englisch": "Serperior",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Majaspic",
+ "Gewicht": "63,0 kg",
+ "Größe": "3,3 m",
+ "Japanisch": "\\u30b8\\u30e3\\u30ed\\u30fc\\u30c0 (Jalorda)",
+ "Kategorie": "Hoheit",
+ "Link": "http://pokewiki.de/Serpiroyal",
+ "National-Dex": "#497",
+ "Typ": "Pflanze"
+ },
+ "498": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a8/Sugimori_498.png"
+ ]
+ ],
+ "Deutsch": "Floink",
+ "Englisch": "Tepig",
+ "Farbe": "Rot",
+ "Französisch": "Gruikui",
+ "Gewicht": "9,9 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30dd\\u30ab\\u30d6 (Pokabu)",
+ "Kategorie": "Feuerferkel",
+ "Link": "http://pokewiki.de/Floink",
+ "National-Dex": "#498",
+ "Typ": "Feuer"
+ },
+ "499": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/42/Sugimori_499.png"
+ ]
+ ],
+ "Deutsch": "Ferkokel",
+ "Englisch": "Pignite",
+ "Farbe": "Rot",
+ "Französisch": "Grotichon",
+ "Gewicht": "55,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30c1\\u30e3\\u30aa\\u30d6\\u30fc (Chaoboo)",
+ "Kategorie": "Feuerferkel",
+ "Link": "http://pokewiki.de/Ferkokel",
+ "National-Dex": "#499",
+ "Typ": [
+ "Feuer",
+ "Kampf"
+ ]
+ },
+ "500": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7f/Sugimori_500.png"
+ ]
+ ],
+ "Deutsch": "Flambirex",
+ "Englisch": "Emboar",
+ "Farbe": "Rot",
+ "Französisch": "Roitiflam",
+ "Gewicht": "150,0 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30a8\\u30f3\\u30d6\\u30aa\\u30fc (Enbuoh)",
+ "Kategorie": "Feuerschwein",
+ "Link": "http://pokewiki.de/Flambirex",
+ "National-Dex": "#500",
+ "Typ": [
+ "Feuer",
+ "Kampf"
+ ]
+ },
+ "501": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c4/Sugimori_501.png"
+ ]
+ ],
+ "Deutsch": "Ottaro",
+ "Englisch": "Oshawott",
+ "Farbe": "Blau",
+ "Französisch": "Moustillon",
+ "Gewicht": "5,9 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30df\\u30b8\\u30e5\\u30de\\u30eb (Mijumaru)",
+ "Kategorie": "Otter",
+ "Link": "http://pokewiki.de/Ottaro",
+ "National-Dex": "#501",
+ "Typ": "Wasser"
+ },
+ "502": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2f/Sugimori_502.png"
+ ]
+ ],
+ "Deutsch": "Zwottronin",
+ "Englisch": "Dewott",
+ "Farbe": "Blau",
+ "Französisch": "Mateloutre",
+ "Gewicht": "24,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30d5\\u30bf\\u30c1\\u30de\\u30eb (Futachimaru)",
+ "Kategorie": "Schulung",
+ "Link": "http://pokewiki.de/Zwottronin",
+ "National-Dex": "#502",
+ "Typ": "Wasser"
+ },
+ "503": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/62/Sugimori_503.png"
+ ]
+ ],
+ "Deutsch": "Admurai",
+ "Englisch": "Samurott",
+ "Farbe": "Blau",
+ "Französisch": "Clamiral",
+ "Gewicht": "94,6 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30c0\\u30a4\\u30b1\\u30f3\\u30ad (Daikenki)",
+ "Kategorie": "W\\u00fcrde",
+ "Link": "http://pokewiki.de/Admurai",
+ "National-Dex": "#503",
+ "Typ": "Wasser"
+ },
+ "504": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6e/Sugimori_504.png"
+ ]
+ ],
+ "Deutsch": "Nagelotz",
+ "Englisch": "Patrat",
+ "Farbe": "Braun",
+ "Französisch": "Ratentif",
+ "Gewicht": "11,6 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30df\\u30cd\\u30ba\\u30df (Minezumi)",
+ "Kategorie": "Sp\\u00e4her",
+ "Link": "http://pokewiki.de/Nagelotz",
+ "National-Dex": "#504",
+ "Typ": "Normal"
+ },
+ "505": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f0/Sugimori_505.png"
+ ]
+ ],
+ "Deutsch": "Kukmarda",
+ "Englisch": "Watchog",
+ "Farbe": "Braun",
+ "Französisch": "Miradar",
+ "Gewicht": "27,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30df\\u30eb\\u30db\\u30c3\\u30b0 (Miruhog)",
+ "Kategorie": "Wachsamkeit",
+ "Link": "http://pokewiki.de/Kukmarda",
+ "National-Dex": "#505",
+ "Typ": "Normal"
+ },
+ "506": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/41/Sugimori_506.png"
+ ]
+ ],
+ "Deutsch": "Yorkleff",
+ "Englisch": "Lillipup",
+ "Farbe": "Braun",
+ "Französisch": "Ponchiot",
+ "Gewicht": "4,1 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30e8\\u30fc\\u30c6\\u30ea\\u30fc (Yorterrie)",
+ "Kategorie": "Hund",
+ "Link": "http://pokewiki.de/Yorkleff",
+ "National-Dex": "#506",
+ "Typ": "Normal"
+ },
+ "507": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a8/Sugimori_507.png"
+ ]
+ ],
+ "Deutsch": "Terribark",
+ "Englisch": "Herdier",
+ "Farbe": "Grau",
+ "Französisch": "Ponchien",
+ "Gewicht": "14,7 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30cf\\u30fc\\u30c7\\u30ea\\u30a2 (Herderrie)",
+ "Kategorie": "Treuhund",
+ "Link": "http://pokewiki.de/Terribark",
+ "National-Dex": "#507",
+ "Typ": "Normal"
+ },
+ "508": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0f/Sugimori_508.png"
+ ]
+ ],
+ "Deutsch": "Bissbark",
+ "Englisch": "Stoutland",
+ "Farbe": "Grau",
+ "Französisch": "Mastouffe",
+ "Gewicht": "61,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30e0\\u30fc\\u30e9\\u30f3\\u30c9 (Mooland)",
+ "Kategorie": "Gro\\u00dfmut",
+ "Link": "http://pokewiki.de/Bissbark",
+ "National-Dex": "#508",
+ "Typ": "Normal"
+ },
+ "509": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/ba/Sugimori_509.png"
+ ]
+ ],
+ "Deutsch": "Felilou",
+ "Englisch": "Purrloin",
+ "Farbe": "Violett",
+ "Französisch": "Chacripan",
+ "Gewicht": "10,1 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30c1\\u30e7\\u30ed\\u30cd\\u30b3 (Choroneko)",
+ "Kategorie": "Schelm",
+ "Link": "http://pokewiki.de/Felilou",
+ "National-Dex": "#509",
+ "Typ": "Unlicht"
+ },
+ "510": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ee/Sugimori_510.png"
+ ]
+ ],
+ "Deutsch": "Kleoparda",
+ "Englisch": "Liepard",
+ "Farbe": "Violett",
+ "Französisch": "L\\u00e9opardus",
+ "Gewicht": "37,5 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30ec\\u30d1\\u30eb\\u30c0\\u30b9 (Lepardas)",
+ "Kategorie": "Gef\\u00fchlsk\\u00e4lte",
+ "Link": "http://pokewiki.de/Kleoparda",
+ "National-Dex": "#510",
+ "Typ": "Unlicht"
+ },
+ "511": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e5/Sugimori_511.png"
+ ]
+ ],
+ "Deutsch": "Vegimak",
+ "Englisch": "Pansage",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Feuillajou",
+ "Gewicht": "10,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e4\\u30ca\\u30c3\\u30d7 (Yanappu)",
+ "Kategorie": "Grasaffe",
+ "Link": "http://pokewiki.de/Vegimak",
+ "National-Dex": "#511",
+ "Typ": "Pflanze"
+ },
+ "512": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d8/Sugimori_512.png"
+ ]
+ ],
+ "Deutsch": "Vegichita",
+ "Englisch": "Simisage",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Feuiloutan",
+ "Gewicht": "30,5 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30e4\\u30ca\\u30c3\\u30ad\\u30fc (Yanakkie)",
+ "Kategorie": "Stachelaffe",
+ "Link": "http://pokewiki.de/Vegichita",
+ "National-Dex": "#512",
+ "Typ": "Pflanze"
+ },
+ "513": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6d/Sugimori_513.png"
+ ]
+ ],
+ "Deutsch": "Grillmak",
+ "Englisch": "Pansear",
+ "Farbe": "Rot",
+ "Französisch": "Flamajou",
+ "Gewicht": "11,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30d0\\u30aa\\u30c3\\u30d7 (Baoppu)",
+ "Kategorie": "Hitze",
+ "Link": "http://pokewiki.de/Grillmak",
+ "National-Dex": "#513",
+ "Typ": "Feuer"
+ },
+ "514": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/de/Sugimori_514.png"
+ ]
+ ],
+ "Deutsch": "Grillchita",
+ "Englisch": "Simisear",
+ "Farbe": "Rot",
+ "Französisch": "Flamoutan",
+ "Gewicht": "28,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30d0\\u30aa\\u30c3\\u30ad\\u30fc (Baokkie)",
+ "Kategorie": "Funkenregen",
+ "Link": "http://pokewiki.de/Grillchita",
+ "National-Dex": "#514",
+ "Typ": "Feuer"
+ },
+ "515": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/05/Sugimori_515.png"
+ ]
+ ],
+ "Deutsch": "Sodamak",
+ "Englisch": "Panpour",
+ "Farbe": "Blau",
+ "Französisch": "Flotajou",
+ "Gewicht": "13,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30d2\\u30e4\\u30c3\\u30d7 (Hiyappu)",
+ "Kategorie": "Wasserstrahl",
+ "Link": "http://pokewiki.de/Sodamak",
+ "National-Dex": "#515",
+ "Typ": "Wasser"
+ },
+ "516": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d6/Sugimori_516.png"
+ ]
+ ],
+ "Deutsch": "Sodachita",
+ "Englisch": "Simipour",
+ "Farbe": "Blau",
+ "Französisch": "Flotoutan",
+ "Gewicht": "29,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30d2\\u30e4\\u30c3\\u30ad\\u30fc (Hiyakkie)",
+ "Kategorie": "Drainage",
+ "Link": "http://pokewiki.de/Sodachita",
+ "National-Dex": "#516",
+ "Typ": "Wasser"
+ },
+ "517": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8a/Sugimori_517.png"
+ ]
+ ],
+ "Deutsch": "Somniam",
+ "Englisch": "Munna",
+ "Farbe": "Rosa",
+ "Französisch": "Munna",
+ "Gewicht": "23,3 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e0\\u30f3\\u30ca (Munna)",
+ "Kategorie": "Traumfresser",
+ "Link": "http://pokewiki.de/Somniam",
+ "National-Dex": "#517",
+ "Typ": "Psycho"
+ },
+ "518": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/84/Sugimori_518.png"
+ ]
+ ],
+ "Deutsch": "Somnivora",
+ "Englisch": "Musharna",
+ "Farbe": "Rosa",
+ "Französisch": "Mushana",
+ "Gewicht": "60,5 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30e0\\u30b7\\u30e3\\u30fc\\u30ca (Musharna)",
+ "Kategorie": "Halbschlaf",
+ "Link": "http://pokewiki.de/Somnivora",
+ "National-Dex": "#518",
+ "Typ": "Psycho"
+ },
+ "519": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ad/Sugimori_519.png"
+ ]
+ ],
+ "Deutsch": "Dusselgurr",
+ "Englisch": "Pidove",
+ "Farbe": "Grau",
+ "Französisch": "Poichigeon",
+ "Gewicht": "2,1 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30de\\u30e1\\u30d1\\u30c8 (Mamepato)",
+ "Kategorie": "T\\u00e4ubchen",
+ "Link": "http://pokewiki.de/Dusselgurr",
+ "National-Dex": "#519",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "520": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d6/Sugimori_520.png"
+ ]
+ ],
+ "Deutsch": "Navitaub",
+ "Englisch": "Tranquill",
+ "Farbe": "Grau",
+ "Französisch": "Colombeau",
+ "Gewicht": "15,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30cf\\u30c8\\u30fc\\u30dc\\u30fc (Hatoboh)",
+ "Kategorie": "Wildtaube",
+ "Link": "http://pokewiki.de/Navitaub",
+ "National-Dex": "#520",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "521": {
+ "Bilder": [
+ [
+ "m\\u00e4nnlich",
+ "http://www.pokewiki.de/images/4/45/Sugimori_521.png"
+ ],
+ [
+ "weiblich",
+ "http://www.pokewiki.de/images/c/c1/Sugimori_521a.png"
+ ]
+ ],
+ "Deutsch": "Fasasnob",
+ "Englisch": "Unfezant",
+ "Farbe": "Grau",
+ "Französisch": "D\\u00e9flaisan",
+ "Gewicht": "29,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30b1\\u30f3\\u30db\\u30ed\\u30a6 (Kenhallow)",
+ "Kategorie": "Stolz",
+ "Link": "http://pokewiki.de/Fasasnob",
+ "National-Dex": "#521",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "522": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/63/Sugimori_522.png"
+ ]
+ ],
+ "Deutsch": "Elezeba",
+ "Englisch": "Blitzle",
+ "Farbe": "Schwarz",
+ "Französisch": "Z\\u00e9bibron",
+ "Gewicht": "29,8 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30b7\\u30de\\u30de (Shimama)",
+ "Kategorie": "Hochspannung",
+ "Link": "http://pokewiki.de/Elezeba",
+ "National-Dex": "#522",
+ "Typ": "Elektro"
+ },
+ "523": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/70/Sugimori_523.png"
+ ]
+ ],
+ "Deutsch": "Zebritz",
+ "Englisch": "Zebstrika",
+ "Farbe": "Schwarz",
+ "Französisch": "Z\\u00e9blitz",
+ "Gewicht": "79,5 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30bc\\u30d6\\u30e9\\u30a4\\u30ab (Zebraika)",
+ "Kategorie": "Donnerkeil",
+ "Link": "http://pokewiki.de/Zebritz",
+ "National-Dex": "#523",
+ "Typ": "Elektro"
+ },
+ "524": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/46/Sugimori_524.png"
+ ]
+ ],
+ "Deutsch": "Kiesling",
+ "Englisch": "Roggenrola",
+ "Farbe": "Blau",
+ "Französisch": "Nodulithe",
+ "Gewicht": "18,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30c0\\u30f3\\u30b4\\u30ed (Dangoro)",
+ "Kategorie": "Erdmantel",
+ "Link": "http://pokewiki.de/Kiesling",
+ "National-Dex": "#524",
+ "Typ": "Gestein"
+ },
+ "525": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/60/Sugimori_525.png"
+ ]
+ ],
+ "Deutsch": "Sedimantur",
+ "Englisch": "Boldore",
+ "Farbe": "Blau",
+ "Französisch": "G\\u00e9olithe",
+ "Gewicht": "102,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30ac\\u30f3\\u30c8\\u30eb (Gantle)",
+ "Kategorie": "Erz",
+ "Link": "http://pokewiki.de/Sedimantur",
+ "National-Dex": "#525",
+ "Typ": "Gestein"
+ },
+ "526": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c8/Sugimori_526.png"
+ ]
+ ],
+ "Deutsch": "Brockoloss",
+ "Englisch": "Gigalith",
+ "Farbe": "Blau",
+ "Französisch": "Gigalithe",
+ "Gewicht": "260,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30ae\\u30ac\\u30a4\\u30a2\\u30b9 (Gigaiath)",
+ "Kategorie": "Kompression",
+ "Link": "http://pokewiki.de/Brockoloss",
+ "National-Dex": "#526",
+ "Typ": "Gestein"
+ },
+ "527": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6a/Sugimori_527.png"
+ ]
+ ],
+ "Deutsch": "Fleknoil",
+ "Englisch": "Woobat",
+ "Farbe": "Blau",
+ "Französisch": "Chovsourir",
+ "Gewicht": "2,1 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30b3\\u30ed\\u30e2\\u30ea (Koromori)",
+ "Kategorie": "Fledermaus",
+ "Link": "http://pokewiki.de/Fleknoil",
+ "National-Dex": "#527",
+ "Typ": [
+ "Psycho",
+ "Flug"
+ ]
+ },
+ "528": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/78/Sugimori_528.png"
+ ]
+ ],
+ "Deutsch": "Fletiamo",
+ "Englisch": "Swoobat",
+ "Farbe": "Blau",
+ "Französisch": "Rhinolove",
+ "Gewicht": "10,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30b3\\u30b3\\u30ed\\u30e2\\u30ea (Kokoromori)",
+ "Kategorie": "Balz",
+ "Link": "http://pokewiki.de/Fletiamo",
+ "National-Dex": "#528",
+ "Typ": [
+ "Psycho",
+ "Flug"
+ ]
+ },
+ "529": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6d/Sugimori_529.png"
+ ]
+ ],
+ "Deutsch": "Rotomurf",
+ "Englisch": "Drilbur",
+ "Farbe": "Grau",
+ "Französisch": "Rototaupe",
+ "Gewicht": "8,5 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30e2\\u30b0\\u30ea\\u30e5\\u30fc (Mogurew)",
+ "Kategorie": "Maulwurf",
+ "Link": "http://pokewiki.de/Rotomurf",
+ "National-Dex": "#529",
+ "Typ": "Boden"
+ },
+ "530": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d6/Sugimori_530.png"
+ ]
+ ],
+ "Deutsch": "Stalobor",
+ "Englisch": "Excadrill",
+ "Farbe": "Grau",
+ "Französisch": "Minotaupe",
+ "Gewicht": "40,4 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30c9\\u30ea\\u30e5\\u30a6\\u30ba (Doryuzu)",
+ "Kategorie": "Untergrund",
+ "Link": "http://pokewiki.de/Stalobor",
+ "National-Dex": "#530",
+ "Typ": [
+ "Boden",
+ "Stahl"
+ ]
+ },
+ "531": {
+ "Bilder": [
+ [
+ "Ohrdoch",
+ "http://www.pokewiki.de/images/e/e0/Sugimori_531.png"
+ ],
+ [
+ "Mega-Ohrdoch",
+ "http://www.pokewiki.de/images/4/4f/Sugimori_531m1.png"
+ ]
+ ],
+ "Deutsch": "Ohrdoch",
+ "Englisch": "Audino",
+ "Farbe": [
+ "Rosa",
+ "Wei\\u00df (Mega)"
+ ],
+ "Französisch": "Nanm\\u00e9ou\\u00efe",
+ "Gewicht": [
+ "31,0 kg",
+ "32,0 kg (Mega)"
+ ],
+ "Größe": [
+ "1,1 m",
+ "1,5 m (Mega)"
+ ],
+ "Japanisch": "\\u30bf\\u30d6\\u30f3\\u30cd (Tabunne)",
+ "Kategorie": "Geh\\u00f6r",
+ "Link": "http://pokewiki.de/Ohrdoch",
+ "National-Dex": "#531",
+ "Typ": [
+ "Normal",
+ "Normal",
+ "Fee (Mega)"
+ ]
+ },
+ "532": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b6/Sugimori_532.png"
+ ]
+ ],
+ "Deutsch": "Praktibalk",
+ "Englisch": "Timburr",
+ "Farbe": "Grau",
+ "Französisch": "Charpenti",
+ "Gewicht": "12,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c9\\u30c3\\u30b3\\u30e9\\u30fc (Dokkorer)",
+ "Kategorie": "Muskel",
+ "Link": "http://pokewiki.de/Praktibalk",
+ "National-Dex": "#532",
+ "Typ": "Kampf"
+ },
+ "533": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a4/Sugimori_533.png"
+ ]
+ ],
+ "Deutsch": "Strepoli",
+ "Englisch": "Gurdurr",
+ "Farbe": "Grau",
+ "Französisch": "Ouvrifer",
+ "Gewicht": "40,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30c9\\u30c6\\u30c3\\u30b3\\u30c4 (Dotekkotsu)",
+ "Kategorie": "Muskel",
+ "Link": "http://pokewiki.de/Strepoli",
+ "National-Dex": "#533",
+ "Typ": "Kampf"
+ },
+ "534": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/13/Sugimori_534.png"
+ ]
+ ],
+ "Deutsch": "Meistagrif",
+ "Englisch": "Conkeldurr",
+ "Farbe": "Braun",
+ "Französisch": "B\\u00e9tochef",
+ "Gewicht": "87,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30ed\\u30fc\\u30d6\\u30b7\\u30f3 (Roubushin)",
+ "Kategorie": "Muskel",
+ "Link": "http://pokewiki.de/Meistagrif",
+ "National-Dex": "#534",
+ "Typ": "Kampf"
+ },
+ "535": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/14/Sugimori_535.png"
+ ]
+ ],
+ "Deutsch": "Schallquap",
+ "Englisch": "Tympole",
+ "Farbe": "Blau",
+ "Französisch": "Tritonde",
+ "Gewicht": "4,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30aa\\u30bf\\u30de\\u30ed (Otamaro)",
+ "Kategorie": "Kaulquappe",
+ "Link": "http://pokewiki.de/Schallquap",
+ "National-Dex": "#535",
+ "Typ": "Wasser"
+ },
+ "536": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/44/Sugimori_536.png"
+ ]
+ ],
+ "Deutsch": "Mebrana",
+ "Englisch": "Palpitoad",
+ "Farbe": "Blau",
+ "Französisch": "Batracn\\u00e9",
+ "Gewicht": "17,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30ac\\u30de\\u30ac\\u30eb (Gamagaru)",
+ "Kategorie": "Vibration",
+ "Link": "http://pokewiki.de/Mebrana",
+ "National-Dex": "#536",
+ "Typ": [
+ "Wasser",
+ "Boden"
+ ]
+ },
+ "537": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/72/Sugimori_537.png"
+ ]
+ ],
+ "Deutsch": "Branawarz",
+ "Englisch": "Seismitoad",
+ "Farbe": "Blau",
+ "Französisch": "Crapustule",
+ "Gewicht": "62,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30ac\\u30de\\u30b2\\u30ed\\u30b2 (Gamageroge)",
+ "Kategorie": "Vibration",
+ "Link": "http://pokewiki.de/Branawarz",
+ "National-Dex": "#537",
+ "Typ": [
+ "Wasser",
+ "Boden"
+ ]
+ },
+ "538": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/86/Sugimori_538.png"
+ ]
+ ],
+ "Deutsch": "Jiutesto",
+ "Englisch": "Throh",
+ "Farbe": "Rot",
+ "Französisch": "Judokrak",
+ "Gewicht": "55,5 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30ca\\u30b2\\u30ad (Nageki)",
+ "Kategorie": "Judo",
+ "Link": "http://pokewiki.de/Jiutesto",
+ "National-Dex": "#538",
+ "Typ": "Kampf"
+ },
+ "539": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5f/Sugimori_539.png"
+ ]
+ ],
+ "Deutsch": "Karadonis",
+ "Englisch": "Sawk",
+ "Farbe": "Blau",
+ "Französisch": "Karacl\\u00e9e",
+ "Gewicht": "51,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30c0\\u30b2\\u30ad (Dageki)",
+ "Kategorie": "Karate",
+ "Link": "http://pokewiki.de/Karadonis",
+ "National-Dex": "#539",
+ "Typ": "Kampf"
+ },
+ "540": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/85/Sugimori_540.png"
+ ]
+ ],
+ "Deutsch": "Strawickl",
+ "Englisch": "Sewaddle",
+ "Farbe": "Gelb",
+ "Französisch": "Larveyette",
+ "Gewicht": "2,5 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30af\\u30eb\\u30df\\u30eb (Kurumiru)",
+ "Kategorie": "Schneider",
+ "Link": "http://pokewiki.de/Strawickl",
+ "National-Dex": "#540",
+ "Typ": [
+ "K\\u00e4fer",
+ "Pflanze"
+ ]
+ },
+ "541": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/da/Sugimori_541.png"
+ ]
+ ],
+ "Deutsch": "Folikon",
+ "Englisch": "Swadloon",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Couverdure",
+ "Gewicht": "7,3 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30af\\u30eb\\u30de\\u30e6 (Kurumayu)",
+ "Kategorie": "Wickelblatt",
+ "Link": "http://pokewiki.de/Folikon",
+ "National-Dex": "#541",
+ "Typ": [
+ "K\\u00e4fer",
+ "Pflanze"
+ ]
+ },
+ "542": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/63/Sugimori_542.png"
+ ]
+ ],
+ "Deutsch": "Matrifol",
+ "Englisch": "Leavanny",
+ "Farbe": "Gelb",
+ "Französisch": "Manternel",
+ "Gewicht": "20,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30cf\\u30cf\\u30b3\\u30e2\\u30ea (Hahakomori)",
+ "Kategorie": "Kinderpflege",
+ "Link": "http://pokewiki.de/Matrifol",
+ "National-Dex": "#542",
+ "Typ": [
+ "K\\u00e4fer",
+ "Pflanze"
+ ]
+ },
+ "543": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/61/Sugimori_543.png"
+ ]
+ ],
+ "Deutsch": "Toxiped",
+ "Englisch": "Venipede",
+ "Farbe": "Rot",
+ "Französisch": "Venipatte",
+ "Gewicht": "5,3 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30d5\\u30b7\\u30c7 (Fushide)",
+ "Kategorie": "Tausendf\\u00fc\\u00dfer",
+ "Link": "http://pokewiki.de/Toxiped",
+ "National-Dex": "#543",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gift"
+ ]
+ },
+ "544": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ab/Sugimori_544.png"
+ ]
+ ],
+ "Deutsch": "Rollum",
+ "Englisch": "Whirlipede",
+ "Farbe": "Grau",
+ "Französisch": "Scobolide",
+ "Gewicht": "58,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30db\\u30a4\\u30fc\\u30ac (Wheega)",
+ "Kategorie": "Kokonf\\u00fc\\u00dfer",
+ "Link": "http://pokewiki.de/Rollum",
+ "National-Dex": "#544",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gift"
+ ]
+ },
+ "545": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d5/Sugimori_545.png"
+ ]
+ ],
+ "Deutsch": "Cerapendra",
+ "Englisch": "Scolipede",
+ "Farbe": "Rot",
+ "Französisch": "Brutapode",
+ "Gewicht": "200,5 kg",
+ "Größe": "2,5 m",
+ "Japanisch": "\\u30da\\u30f3\\u30c9\\u30e9\\u30fc (Pendror)",
+ "Kategorie": "Riesenf\\u00fc\\u00dfer",
+ "Link": "http://pokewiki.de/Cerapendra",
+ "National-Dex": "#545",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gift"
+ ]
+ },
+ "546": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/65/Sugimori_546.png"
+ ]
+ ],
+ "Deutsch": "Waumboll",
+ "Englisch": "Cottonee",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Doudouvet",
+ "Gewicht": "0,6 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30e2\\u30f3\\u30e1\\u30f3 (Monmen)",
+ "Kategorie": "Wattebausch",
+ "Link": "http://pokewiki.de/Waumboll",
+ "National-Dex": "#546",
+ "Typ": [
+ "Pflanze",
+ "Fee"
+ ]
+ },
+ "547": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a7/Sugimori_547.png"
+ ]
+ ],
+ "Deutsch": "Elfun",
+ "Englisch": "Whimsicott",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Farfaduvet",
+ "Gewicht": "6,6 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30a8\\u30eb\\u30d5\\u30fc\\u30f3 (Elfuun)",
+ "Kategorie": "Windschatten",
+ "Link": "http://pokewiki.de/Elfun",
+ "National-Dex": "#547",
+ "Typ": [
+ "Pflanze",
+ "Fee"
+ ]
+ },
+ "548": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d4/Sugimori_548.png"
+ ]
+ ],
+ "Deutsch": "Lilminip",
+ "Englisch": "Petilil",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Chlorobule",
+ "Gewicht": "6,6 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30c1\\u30e5\\u30ea\\u30cd (Churine)",
+ "Kategorie": "Wurzel",
+ "Link": "http://pokewiki.de/Lilminip",
+ "National-Dex": "#548",
+ "Typ": "Pflanze"
+ },
+ "549": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/86/Sugimori_549.png"
+ ]
+ ],
+ "Deutsch": "Dressella",
+ "Englisch": "Lilligant",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Fragilady",
+ "Gewicht": "16,3 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30c9\\u30ec\\u30c7\\u30a3\\u30a2 (Dredear)",
+ "Kategorie": "Blumenzier",
+ "Link": "http://pokewiki.de/Dressella",
+ "National-Dex": "#549",
+ "Typ": "Pflanze"
+ },
+ "550": {
+ "Bilder": [
+ [
+ "Rotlinig",
+ "http://www.pokewiki.de/images/45"
+ ],
+ [
+ "Blaulinig",
+ "http://www.pokewiki.de/images/7f"
+ ]
+ ],
+ "Deutsch": "Barschuft",
+ "Englisch": "Basculin",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Bargantua",
+ "Gewicht": "18,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30d0\\u30b9\\u30e9\\u30aa (Bassrao)",
+ "Kategorie": "Grobheit",
+ "Link": "http://pokewiki.de/Barschuft",
+ "National-Dex": "#550",
+ "Typ": "Wasser"
+ },
+ "551": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a7/Sugimori_551.png"
+ ]
+ ],
+ "Deutsch": "Ganovil",
+ "Englisch": "Sandile",
+ "Farbe": "Braun",
+ "Französisch": "Masca\\u00efman",
+ "Gewicht": "15,2 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30e1\\u30b0\\u30ed\\u30b3 (Meguroco)",
+ "Kategorie": "W\\u00fcstenkroko",
+ "Link": "http://pokewiki.de/Ganovil",
+ "National-Dex": "#551",
+ "Typ": [
+ "Boden",
+ "Unlicht"
+ ]
+ },
+ "552": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7b/Sugimori_552.png"
+ ]
+ ],
+ "Deutsch": "Rokkaiman",
+ "Englisch": "Krokorok",
+ "Farbe": "Braun",
+ "Französisch": "Escroco",
+ "Gewicht": "33,4 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30ef\\u30eb\\u30d3\\u30eb (Waruvile)",
+ "Kategorie": "W\\u00fcstenkroko",
+ "Link": "http://pokewiki.de/Rokkaiman",
+ "National-Dex": "#552",
+ "Typ": [
+ "Boden",
+ "Unlicht"
+ ]
+ },
+ "553": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/90/Sugimori_553.png"
+ ]
+ ],
+ "Deutsch": "Rabigator",
+ "Englisch": "Krookodile",
+ "Farbe": "Rot",
+ "Französisch": "Crocorible",
+ "Gewicht": "96,3 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30ef\\u30eb\\u30d3\\u30a2\\u30eb (Waruvial)",
+ "Kategorie": "Abschreckung",
+ "Link": "http://pokewiki.de/Rabigator",
+ "National-Dex": "#553",
+ "Typ": [
+ "Boden",
+ "Unlicht"
+ ]
+ },
+ "554": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/08/Sugimori_554.png"
+ ]
+ ],
+ "Deutsch": "Flampion",
+ "Englisch": "Darumaka",
+ "Farbe": "Rot",
+ "Französisch": "Darumarond",
+ "Gewicht": "37,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c0\\u30eb\\u30de\\u30c3\\u30ab (Darumakka)",
+ "Kategorie": "Lampion",
+ "Link": "http://pokewiki.de/Flampion",
+ "National-Dex": "#554",
+ "Typ": "Feuer"
+ },
+ "555": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/23/Sugimori_555.png"
+ ]
+ ],
+ "Deutsch": "Flampivian",
+ "Englisch": "Darmanitan",
+ "Farbe": [
+ "Rot",
+ "Blau (Trance-Modus)"
+ ],
+ "Französisch": "Darumacho",
+ "Gewicht": "92,9 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30d2\\u30d2\\u30c0\\u30eb\\u30de (Hihidaruma)",
+ "Kategorie": "Lichterloh",
+ "Link": "http://pokewiki.de/Flampivian",
+ "National-Dex": "#555",
+ "Typ": [
+ "Feuer (Normalform)",
+ "Feuer",
+ "Psycho",
+ "(Trance-Modus)"
+ ]
+ },
+ "556": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3a/Sugimori_556.png"
+ ]
+ ],
+ "Deutsch": "Maracamba",
+ "Englisch": "Maractus",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Maracacchi",
+ "Gewicht": "28,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30de\\u30e9\\u30ab\\u30c3\\u30c1 (Maracacchi)",
+ "Kategorie": "Kaktus",
+ "Link": "http://pokewiki.de/Maracamba",
+ "National-Dex": "#556",
+ "Typ": "Pflanze"
+ },
+ "557": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5c/Sugimori_557.png"
+ ]
+ ],
+ "Deutsch": "Lithomith",
+ "Englisch": "Dwebble",
+ "Farbe": "Rot",
+ "Französisch": "Crabicoque",
+ "Gewicht": "14,5 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30a4\\u30b7\\u30ba\\u30de\\u30a4 (Ishizumai)",
+ "Kategorie": "Steinhaus",
+ "Link": "http://pokewiki.de/Lithomith",
+ "National-Dex": "#557",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gestein"
+ ]
+ },
+ "558": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/19/Sugimori_558.png"
+ ]
+ ],
+ "Deutsch": "Castellith",
+ "Englisch": "Crustle",
+ "Farbe": "Rot",
+ "Französisch": "Crabaraque",
+ "Gewicht": "200,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30a4\\u30ef\\u30d1\\u30ec\\u30b9 (Iwapalace)",
+ "Kategorie": "Felshaus",
+ "Link": "http://pokewiki.de/Castellith",
+ "National-Dex": "#558",
+ "Typ": [
+ "K\\u00e4fer",
+ "Gestein"
+ ]
+ },
+ "559": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5a/Sugimori_559.png"
+ ]
+ ],
+ "Deutsch": "Zurrokex",
+ "Englisch": "Scraggy",
+ "Farbe": "Gelb",
+ "Französisch": "Baggiguane",
+ "Gewicht": "11,8 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30ba\\u30eb\\u30c3\\u30b0 (Zuruggu)",
+ "Kategorie": "Hautwechsel",
+ "Link": "http://pokewiki.de/Zurrokex",
+ "National-Dex": "#559",
+ "Typ": [
+ "Unlicht",
+ "Kampf"
+ ]
+ },
+ "560": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/09/Sugimori_560.png"
+ ]
+ ],
+ "Deutsch": "Irokex",
+ "Englisch": "Scrafty",
+ "Farbe": "Rot",
+ "Französisch": "Bagga\\u00efd",
+ "Gewicht": "30,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30ba\\u30eb\\u30ba\\u30ad\\u30f3 (Zuruzukin)",
+ "Kategorie": "Halunke",
+ "Link": "http://pokewiki.de/Irokex",
+ "National-Dex": "#560",
+ "Typ": [
+ "Unlicht",
+ "Kampf"
+ ]
+ },
+ "561": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/69/Sugimori_561.png"
+ ]
+ ],
+ "Deutsch": "Symvolara",
+ "Englisch": "Sigilyph",
+ "Farbe": "Schwarz",
+ "Französisch": "Crypt\\u00e9ro",
+ "Gewicht": "14,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30b7\\u30f3\\u30dc\\u30e9\\u30fc (Symboler)",
+ "Kategorie": "Vogelgleich",
+ "Link": "http://pokewiki.de/Symvolara",
+ "National-Dex": "#561",
+ "Typ": [
+ "Psycho",
+ "Flug"
+ ]
+ },
+ "562": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/cf/Sugimori_562.png"
+ ]
+ ],
+ "Deutsch": "Makabaja",
+ "Englisch": "Yamask",
+ "Farbe": "Schwarz",
+ "Französisch": "Tutafeh",
+ "Gewicht": "1,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30c7\\u30b9\\u30de\\u30b9 (Desumasu)",
+ "Kategorie": "Seele",
+ "Link": "http://pokewiki.de/Makabaja",
+ "National-Dex": "#562",
+ "Typ": "Geist"
+ },
+ "563": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6e/Sugimori_563.png"
+ ]
+ ],
+ "Deutsch": "Echnatoll",
+ "Englisch": "Cofagrigus",
+ "Farbe": "Gelb",
+ "Französisch": "Tutankafar",
+ "Gewicht": "76,5 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30c7\\u30b9\\u30ab\\u30fc\\u30f3 (Desukarn)",
+ "Kategorie": "Sarkophag",
+ "Link": "http://pokewiki.de/Echnatoll",
+ "National-Dex": "#563",
+ "Typ": "Geist"
+ },
+ "564": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a0/Sugimori_564.png"
+ ]
+ ],
+ "Deutsch": "Galapaflos",
+ "Englisch": "Tirtouga",
+ "Farbe": "Blau",
+ "Französisch": "Carapagos",
+ "Gewicht": "16,5 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30d7\\u30ed\\u30c8\\u30fc\\u30ac (Protoga)",
+ "Kategorie": "Urzeitkr\\u00f6te",
+ "Link": "http://pokewiki.de/Galapaflos",
+ "National-Dex": "#564",
+ "Typ": [
+ "Wasser",
+ "Gestein"
+ ]
+ },
+ "565": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d9/Sugimori_565.png"
+ ]
+ ],
+ "Deutsch": "Karippas",
+ "Englisch": "Carracosta",
+ "Farbe": "Blau",
+ "Französisch": "M\\u00e9gapagos",
+ "Gewicht": "82,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30a2\\u30d0\\u30b4\\u30fc\\u30e9 (Abagoura)",
+ "Kategorie": "Urzeitkr\\u00f6te",
+ "Link": "http://pokewiki.de/Karippas",
+ "National-Dex": "#565",
+ "Typ": [
+ "Wasser",
+ "Gestein"
+ ]
+ },
+ "566": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fb/Sugimori_566.png"
+ ]
+ ],
+ "Deutsch": "Flapteryx",
+ "Englisch": "Archen",
+ "Farbe": "Gelb",
+ "Französisch": "Ark\\u00e9apti",
+ "Gewicht": "9,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30a2\\u30fc\\u30b1\\u30f3 (Archen)",
+ "Kategorie": "Urzeitvogel",
+ "Link": "http://pokewiki.de/Flapteryx",
+ "National-Dex": "#566",
+ "Typ": [
+ "Gestein",
+ "Flug"
+ ]
+ },
+ "567": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d2/Sugimori_567.png"
+ ]
+ ],
+ "Deutsch": "Aeropteryx",
+ "Englisch": "Archeops",
+ "Farbe": "Gelb",
+ "Französisch": "A\\u00e9ropt\\u00e9ryx",
+ "Gewicht": "32,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30a2\\u30fc\\u30b1\\u30aa\\u30b9 (Archeos)",
+ "Kategorie": "Urzeitvogel",
+ "Link": "http://pokewiki.de/Aeropteryx",
+ "National-Dex": "#567",
+ "Typ": [
+ "Gestein",
+ "Flug"
+ ]
+ },
+ "568": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7e/Sugimori_568.png"
+ ]
+ ],
+ "Deutsch": "Unrat\\u00fctox",
+ "Englisch": "Trubbish",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Miamiasme",
+ "Gewicht": "31,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e4\\u30d6\\u30af\\u30ed\\u30f3 (Yabukuron)",
+ "Kategorie": "M\\u00fcllt\\u00fcte",
+ "Link": "http://pokewiki.de/Unrat%C3%BCtox",
+ "National-Dex": "#568",
+ "Typ": "Gift"
+ },
+ "569": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f1/Sugimori_569.png"
+ ]
+ ],
+ "Deutsch": "Deponitox",
+ "Englisch": "Garbodor",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Miasmax",
+ "Gewicht": "107,3 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30c0\\u30b9\\u30c8\\u30c0\\u30b9 (Dustdas)",
+ "Kategorie": "M\\u00fcllhalde",
+ "Link": "http://pokewiki.de/Deponitox",
+ "National-Dex": "#569",
+ "Typ": "Gift"
+ },
+ "570": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ea/Sugimori_570.png"
+ ]
+ ],
+ "Deutsch": "Zorua",
+ "Englisch": "Zorua",
+ "Farbe": "Grau",
+ "Französisch": "Zorua",
+ "Gewicht": "12,5 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30be\\u30ed\\u30a2 (Zorua)",
+ "Kategorie": "Lausefuchs",
+ "Link": "http://pokewiki.de/Zorua",
+ "National-Dex": "#570",
+ "Typ": "Unlicht"
+ },
+ "571": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1e/Sugimori_571.png"
+ ]
+ ],
+ "Deutsch": "Zoroark",
+ "Englisch": "Zoroark",
+ "Farbe": "Grau",
+ "Französisch": "Zoroark",
+ "Gewicht": "81,1 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30be\\u30ed\\u30a2\\u30fc\\u30af (Zoroark)",
+ "Kategorie": "Polymorfuchs",
+ "Link": "http://pokewiki.de/Zoroark",
+ "National-Dex": "#571",
+ "Typ": "Unlicht"
+ },
+ "572": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/43/Sugimori_572.png"
+ ]
+ ],
+ "Deutsch": "Picochilla",
+ "Englisch": "Minccino",
+ "Farbe": "Grau",
+ "Französisch": "Chinchidou",
+ "Gewicht": "5,8 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30c1\\u30e9\\u30fc\\u30df\\u30a3 (Chillarmy)",
+ "Kategorie": "Chinchilla",
+ "Link": "http://pokewiki.de/Picochilla",
+ "National-Dex": "#572",
+ "Typ": "Normal"
+ },
+ "573": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3f/Sugimori_573.png"
+ ]
+ ],
+ "Deutsch": "Chillabell",
+ "Englisch": "Cinccino",
+ "Farbe": "Grau",
+ "Französisch": "Pashmilla",
+ "Gewicht": "7,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30c1\\u30e9\\u30c1\\u30fc\\u30ce (Chillaccino)",
+ "Kategorie": "Schal",
+ "Link": "http://pokewiki.de/Chillabell",
+ "National-Dex": "#573",
+ "Typ": "Normal"
+ },
+ "574": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/64/Sugimori_574.png"
+ ]
+ ],
+ "Deutsch": "Mollimorba",
+ "Englisch": "Gothita",
+ "Farbe": "Violett",
+ "Französisch": "Scrutella",
+ "Gewicht": "5,8 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30b4\\u30c1\\u30e0 (Gothimu)",
+ "Kategorie": "Glotz",
+ "Link": "http://pokewiki.de/Mollimorba",
+ "National-Dex": "#574",
+ "Typ": "Psycho"
+ },
+ "575": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b7/Sugimori_575.png"
+ ]
+ ],
+ "Deutsch": "Hypnomorba",
+ "Englisch": "Gothorita",
+ "Farbe": "Violett",
+ "Französisch": "Mesm\\u00e9rella",
+ "Gewicht": "18,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30b4\\u30c1\\u30df\\u30eb (Gochimiru)",
+ "Kategorie": "Manipulator",
+ "Link": "http://pokewiki.de/Hypnomorba",
+ "National-Dex": "#575",
+ "Typ": "Psycho"
+ },
+ "576": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a3/Sugimori_576.png"
+ ]
+ ],
+ "Deutsch": "Morbitesse",
+ "Englisch": "Gothitelle",
+ "Farbe": "Violett",
+ "Französisch": "Sid\\u00e9rella",
+ "Gewicht": "44,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30b4\\u30c1\\u30eb\\u30bc\\u30eb (Gothiruselle)",
+ "Kategorie": "Gestirn",
+ "Link": "http://pokewiki.de/Morbitesse",
+ "National-Dex": "#576",
+ "Typ": "Psycho"
+ },
+ "577": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3a/Sugimori_577.png"
+ ]
+ ],
+ "Deutsch": "Monozyto",
+ "Englisch": "Solosis",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Nucl\\u00e9os",
+ "Gewicht": "1,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30e6\\u30cb\\u30e9\\u30f3 (Uniran)",
+ "Kategorie": "Zelle",
+ "Link": "http://pokewiki.de/Monozyto",
+ "National-Dex": "#577",
+ "Typ": "Psycho"
+ },
+ "578": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/79/Sugimori_578.png"
+ ]
+ ],
+ "Deutsch": "Mitodos",
+ "Englisch": "Duosion",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "M\\u00e9ios",
+ "Gewicht": "8,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c0\\u30d6\\u30e9\\u30f3 (Doublan)",
+ "Kategorie": "Zellteilung",
+ "Link": "http://pokewiki.de/Mitodos",
+ "National-Dex": "#578",
+ "Typ": "Psycho"
+ },
+ "579": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3d/Sugimori_579.png"
+ ]
+ ],
+ "Deutsch": "Zytomega",
+ "Englisch": "Reuniclus",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Symbios",
+ "Gewicht": "20,1 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30e9\\u30f3\\u30af\\u30eb\\u30b9 (Lanculus)",
+ "Kategorie": "Vermehrung",
+ "Link": "http://pokewiki.de/Zytomega",
+ "National-Dex": "#579",
+ "Typ": "Psycho"
+ },
+ "580": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1f/Sugimori_580.png"
+ ]
+ ],
+ "Deutsch": "Piccolente",
+ "Englisch": "Ducklett",
+ "Farbe": "Blau",
+ "Französisch": "Couaneton",
+ "Gewicht": "5,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30b3\\u30cf\\u30eb\\u30d2\\u30fc (Koaruhie)",
+ "Kategorie": "Wasservogel",
+ "Link": "http://pokewiki.de/Piccolente",
+ "National-Dex": "#580",
+ "Typ": [
+ "Wasser",
+ "Flug"
+ ]
+ },
+ "581": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ae/Sugimori_581.png"
+ ]
+ ],
+ "Deutsch": "Swaroness",
+ "Englisch": "Swanna",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Lakm\\u00e9cygne",
+ "Gewicht": "24,2 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30b9\\u30ef\\u30f3\\u30ca (Swanna)",
+ "Kategorie": "Schwan",
+ "Link": "http://pokewiki.de/Swaroness",
+ "National-Dex": "#581",
+ "Typ": [
+ "Wasser",
+ "Flug"
+ ]
+ },
+ "582": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/92/Sugimori_582.png"
+ ]
+ ],
+ "Deutsch": "Gelatini",
+ "Englisch": "Vanillite",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Sorb\\u00e9b\\u00e9",
+ "Gewicht": "5,7 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30d0\\u30cb\\u30d7\\u30c3\\u30c1 (Vanipeti)",
+ "Kategorie": "Neuschnee",
+ "Link": "http://pokewiki.de/Gelatini",
+ "National-Dex": "#582",
+ "Typ": "Eis"
+ },
+ "583": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/58/Sugimori_583.png"
+ ]
+ ],
+ "Deutsch": "Gelatroppo",
+ "Englisch": "Vanillish",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Sorboul",
+ "Gewicht": "41,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30d0\\u30cb\\u30ea\\u30c3\\u30c1 (Vanirich)",
+ "Kategorie": "Firn",
+ "Link": "http://pokewiki.de/Gelatroppo",
+ "National-Dex": "#583",
+ "Typ": "Eis"
+ },
+ "584": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ed/Sugimori_584.png"
+ ]
+ ],
+ "Deutsch": "Gelatwino",
+ "Englisch": "Vanilluxe",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Sorbouboul",
+ "Gewicht": "57,5 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30d0\\u30a4\\u30d0\\u30cb\\u30e9 (Baivanilla)",
+ "Kategorie": "Schneesturm",
+ "Link": "http://pokewiki.de/Gelatwino",
+ "National-Dex": "#584",
+ "Typ": "Eis"
+ },
+ "585": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/d/dd/Sugimori_585.png"
+ ],
+ [
+ "Alternative Formen",
+ "http://www.pokewiki.de/images/b/bb/Sugimori_585a.png"
+ ],
+ [
+ "Alternative Formen",
+ "http://www.pokewiki.de/images/a/a8/Sugimori_585b.png"
+ ],
+ [
+ "Alternative Formen",
+ "http://www.pokewiki.de/images/5/5b/Sugimori_585c.png"
+ ]
+ ],
+ "Deutsch": "Sesokitz",
+ "Englisch": "Deerling",
+ "Farbe": "Rosa",
+ "Französisch": "Vivaldaim",
+ "Gewicht": "19,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30b7\\u30ad\\u30b8\\u30ab (Shikijika)",
+ "Kategorie": "Jahreszeit",
+ "Link": "http://pokewiki.de/Sesokitz",
+ "National-Dex": "#585",
+ "Typ": [
+ "Normal",
+ "Pflanze"
+ ]
+ },
+ "586": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/3/30/Sugimori_586.png"
+ ],
+ [
+ "Alternative Formen",
+ "http://www.pokewiki.de/images/8/89/Sugimori_586a.png"
+ ],
+ [
+ "Alternative Formen",
+ "http://www.pokewiki.de/images/2/2d/Sugimori_586b.png"
+ ],
+ [
+ "Alternative Formen",
+ "http://www.pokewiki.de/images/2/2f/Sugimori_586c.png"
+ ]
+ ],
+ "Deutsch": "Kronjuwild",
+ "Englisch": "Sawsbuck",
+ "Farbe": "Braun",
+ "Französisch": "Haydaim",
+ "Gewicht": "92,5 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30e1\\u30d6\\u30ad\\u30b8\\u30ab (Mebukijika)",
+ "Kategorie": "Jahreszeit",
+ "Link": "http://pokewiki.de/Kronjuwild",
+ "National-Dex": "#586",
+ "Typ": [
+ "Normal",
+ "Pflanze"
+ ]
+ },
+ "587": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/07/Sugimori_587.png"
+ ]
+ ],
+ "Deutsch": "Emolga",
+ "Englisch": "Emolga",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Emolga",
+ "Gewicht": "5,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30a8\\u30e2\\u30f3\\u30ac (Emonga)",
+ "Kategorie": "Flugh\\u00f6rnchen",
+ "Link": "http://pokewiki.de/Emolga",
+ "National-Dex": "#587",
+ "Typ": [
+ "Elektro",
+ "Flug"
+ ]
+ },
+ "588": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/ba/Sugimori_588.png"
+ ]
+ ],
+ "Deutsch": "Laukaps",
+ "Englisch": "Karrablast",
+ "Farbe": "Blau",
+ "Französisch": "Carabing",
+ "Gewicht": "5,9 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30ab\\u30d6\\u30eb\\u30e2 (Kaburumo)",
+ "Kategorie": "Schnappbiss",
+ "Link": "http://pokewiki.de/Laukaps",
+ "National-Dex": "#588",
+ "Typ": "K\\u00e4fer"
+ },
+ "589": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fe/Sugimori_589.png"
+ ]
+ ],
+ "Deutsch": "Cavalanzas",
+ "Englisch": "Escavalier",
+ "Farbe": "Grau",
+ "Französisch": "Lan\\u00e7argot",
+ "Gewicht": "33,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b7\\u30e5\\u30d0\\u30eb\\u30b4 (Chevargo)",
+ "Kategorie": "Kavallerie",
+ "Link": "http://pokewiki.de/Cavalanzas",
+ "National-Dex": "#589",
+ "Typ": [
+ "K\\u00e4fer",
+ "Stahl"
+ ]
+ },
+ "590": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fd/Sugimori_590.png"
+ ]
+ ],
+ "Deutsch": "Tarnpignon",
+ "Englisch": "Foongus",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Trompignon",
+ "Gewicht": "1,0 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30bf\\u30de\\u30b2\\u30bf\\u30b1 (Tamagetake)",
+ "Kategorie": "Pilz",
+ "Link": "http://pokewiki.de/Tarnpignon",
+ "National-Dex": "#590",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "591": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/36/Sugimori_591.png"
+ ]
+ ],
+ "Deutsch": "Hutsassa",
+ "Englisch": "Amoonguss",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Gaulet",
+ "Gewicht": "10,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e2\\u30ed\\u30d0\\u30ec\\u30eb (Morobareru)",
+ "Kategorie": "Pilz",
+ "Link": "http://pokewiki.de/Hutsassa",
+ "National-Dex": "#591",
+ "Typ": [
+ "Pflanze",
+ "Gift"
+ ]
+ },
+ "592": {
+ "Bilder": [
+ [
+ "m\\u00e4nnlich",
+ "http://www.pokewiki.de/images/d/d7/Sugimori_592.png"
+ ],
+ [
+ "weiblich",
+ "http://www.pokewiki.de/images/b/bc/Sugimori_592a.png"
+ ]
+ ],
+ "Deutsch": "Quabbel",
+ "Englisch": "Frillish",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Viskuse",
+ "Gewicht": "33,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30d7\\u30eb\\u30ea\\u30eb (Pururill)",
+ "Kategorie": "Gleit",
+ "Link": "http://pokewiki.de/Quabbel",
+ "National-Dex": "#592",
+ "Typ": [
+ "Wasser",
+ "Geist"
+ ]
+ },
+ "593": {
+ "Bilder": [
+ [
+ "m\\u00e4nnlich",
+ "http://www.pokewiki.de/images/6/6b/Sugimori_593.png"
+ ],
+ [
+ "weiblich",
+ "http://www.pokewiki.de/images/d/dc/Sugimori_593a.png"
+ ]
+ ],
+ "Deutsch": "Apoquallyp",
+ "Englisch": "Jellicent",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Moyade",
+ "Gewicht": "135,0 kg",
+ "Größe": "2,2 m",
+ "Japanisch": "\\u30d6\\u30eb\\u30f3\\u30b2\\u30eb (Burungel)",
+ "Kategorie": "Gleit",
+ "Link": "http://pokewiki.de/Apoquallyp",
+ "National-Dex": "#593",
+ "Typ": [
+ "Wasser",
+ "Geist"
+ ]
+ },
+ "594": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4f/Sugimori_594.png"
+ ]
+ ],
+ "Deutsch": "Mamolida",
+ "Englisch": "Alomomola",
+ "Farbe": "Rosa",
+ "Französisch": "Mamanbo",
+ "Gewicht": "31,6 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30de\\u30de\\u30f3\\u30dc\\u30a6 (Mamanbou)",
+ "Kategorie": "F\\u00fcrsorge",
+ "Link": "http://pokewiki.de/Mamolida",
+ "National-Dex": "#594",
+ "Typ": "Wasser"
+ },
+ "595": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fb/Sugimori_595.png"
+ ]
+ ],
+ "Deutsch": "Wattzapf",
+ "Englisch": "Joltik",
+ "Farbe": "Gelb",
+ "Französisch": "Statitik",
+ "Gewicht": "0,6 kg",
+ "Größe": "0,1 m",
+ "Japanisch": "\\u30d0\\u30c1\\u30e5\\u30eb (Bachuru)",
+ "Kategorie": "Kleben",
+ "Link": "http://pokewiki.de/Wattzapf",
+ "National-Dex": "#595",
+ "Typ": [
+ "K\\u00e4fer",
+ "Elektro"
+ ]
+ },
+ "596": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f9/Sugimori_596.png"
+ ]
+ ],
+ "Deutsch": "Voltula",
+ "Englisch": "Galvantula",
+ "Farbe": "Gelb",
+ "Französisch": "Mygavolt",
+ "Gewicht": "14,3 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30c7\\u30f3\\u30c1\\u30e5\\u30e9 (Dentula)",
+ "Kategorie": "Stromspinne",
+ "Link": "http://pokewiki.de/Voltula",
+ "National-Dex": "#596",
+ "Typ": [
+ "K\\u00e4fer",
+ "Elektro"
+ ]
+ },
+ "597": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c4/Sugimori_597.png"
+ ]
+ ],
+ "Deutsch": "Kastadur",
+ "Englisch": "Ferroseed",
+ "Farbe": "Grau",
+ "Französisch": "Grindur",
+ "Gewicht": "18,8 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30c6\\u30c3\\u30b7\\u30fc\\u30c9 (Tesseed)",
+ "Kategorie": "Dornfrucht",
+ "Link": "http://pokewiki.de/Kastadur",
+ "National-Dex": "#597",
+ "Typ": [
+ "Pflanze",
+ "Stahl"
+ ]
+ },
+ "598": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b3/Sugimori_598.png"
+ ]
+ ],
+ "Deutsch": "Tentantel",
+ "Englisch": "Ferrothorn",
+ "Farbe": "Grau",
+ "Französisch": "Noacier",
+ "Gewicht": "110,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30ca\\u30c3\\u30c8\\u30ec\\u30a4 (Nutrey)",
+ "Kategorie": "Dornkugel",
+ "Link": "http://pokewiki.de/Tentantel",
+ "National-Dex": "#598",
+ "Typ": [
+ "Pflanze",
+ "Stahl"
+ ]
+ },
+ "599": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f4/Sugimori_599.png"
+ ]
+ ],
+ "Deutsch": "Klikk",
+ "Englisch": "Klink",
+ "Farbe": "Grau",
+ "Französisch": "Tic",
+ "Gewicht": "21,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30ae\\u30a2\\u30eb (Giaru)",
+ "Kategorie": "Getriebe",
+ "Link": "http://pokewiki.de/Klikk",
+ "National-Dex": "#599",
+ "Typ": "Stahl"
+ },
+ "600": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/16/Sugimori_600.png"
+ ]
+ ],
+ "Deutsch": "Kliklak",
+ "Englisch": "Klang",
+ "Farbe": "Grau",
+ "Französisch": "Clic",
+ "Gewicht": "51,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30ae\\u30ae\\u30a2\\u30eb (Gigiaru)",
+ "Kategorie": "Getriebe",
+ "Link": "http://pokewiki.de/Kliklak",
+ "National-Dex": "#600",
+ "Typ": "Stahl"
+ },
+ "601": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f7/Sugimori_601.png"
+ ]
+ ],
+ "Deutsch": "Klikdiklak",
+ "Englisch": "Klinklang",
+ "Farbe": "Grau",
+ "Französisch": "Cliticlic",
+ "Gewicht": "81,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30ae\\u30ae\\u30ae\\u30a2\\u30eb (Gigigiaru)",
+ "Kategorie": "Getriebe",
+ "Link": "http://pokewiki.de/Klikdiklak",
+ "National-Dex": "#601",
+ "Typ": "Stahl"
+ },
+ "602": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c2/Sugimori_602.png"
+ ]
+ ],
+ "Deutsch": "Zapplardin",
+ "Englisch": "Tynamo",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Anchwatt",
+ "Gewicht": "0,3 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30b7\\u30d3\\u30b7\\u30e9\\u30b9 (Shibishirasu)",
+ "Kategorie": "Stromfisch",
+ "Link": "http://pokewiki.de/Zapplardin",
+ "National-Dex": "#602",
+ "Typ": "Elektro"
+ },
+ "603": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8d/Sugimori_603.png"
+ ]
+ ],
+ "Deutsch": "Zapplalek",
+ "Englisch": "Eelektrik",
+ "Farbe": "Blau",
+ "Französisch": "Lamp\\u00e9roie",
+ "Gewicht": "22 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30b7\\u30d3\\u30d3\\u30fc\\u30eb (Shibibeel)",
+ "Kategorie": "Stromfisch",
+ "Link": "http://pokewiki.de/Zapplalek",
+ "National-Dex": "#603",
+ "Typ": "Elektro"
+ },
+ "604": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/22/Sugimori_604.png"
+ ]
+ ],
+ "Deutsch": "Zapplarang",
+ "Englisch": "Eelektross",
+ "Farbe": "Blau",
+ "Französisch": "Ohmassacre",
+ "Gewicht": "80,5 kg",
+ "Größe": "2,1 m",
+ "Japanisch": "\\u30b7\\u30d3\\u30eb\\u30c9\\u30f3 (Shibirudon)",
+ "Kategorie": "Stromfisch",
+ "Link": "http://pokewiki.de/Zapplarang",
+ "National-Dex": "#604",
+ "Typ": "Elektro"
+ },
+ "605": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8b/Sugimori_605.png"
+ ]
+ ],
+ "Deutsch": "Pygraulon",
+ "Englisch": "Elgyem",
+ "Farbe": "Blau",
+ "Französisch": "Lewsor",
+ "Gewicht": "9,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30ea\\u30b0\\u30ec\\u30fc (Ligray)",
+ "Kategorie": "Grips",
+ "Link": "http://pokewiki.de/Pygraulon",
+ "National-Dex": "#605",
+ "Typ": "Psycho"
+ },
+ "606": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8e/Sugimori_606.png"
+ ]
+ ],
+ "Deutsch": "Megalon",
+ "Englisch": "Beheeyem",
+ "Farbe": "Braun",
+ "Französisch": "Neitram",
+ "Gewicht": "34,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30aa\\u30fc\\u30d9\\u30e0 (Ohbem)",
+ "Kategorie": "Grips",
+ "Link": "http://pokewiki.de/Megalon",
+ "National-Dex": "#606",
+ "Typ": "Psycho"
+ },
+ "607": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2c/Sugimori_607.png"
+ ]
+ ],
+ "Deutsch": "Lichtel",
+ "Englisch": "Litwick",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Fun\\u00e9cire",
+ "Gewicht": "3,1 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30d2\\u30c8\\u30e2\\u30b7 (Hitomoshi)",
+ "Kategorie": "Kerze",
+ "Link": "http://pokewiki.de/Lichtel",
+ "National-Dex": "#607",
+ "Typ": [
+ "Geist",
+ "Feuer"
+ ]
+ },
+ "608": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6d/Sugimori_608.png"
+ ]
+ ],
+ "Deutsch": "Laternecto",
+ "Englisch": "Lampent",
+ "Farbe": "Schwarz",
+ "Französisch": "M\\u00e9lancolux",
+ "Gewicht": "13,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e9\\u30f3\\u30d7\\u30e9\\u30fc (Lampler)",
+ "Kategorie": "Lampe",
+ "Link": "http://pokewiki.de/Laternecto",
+ "National-Dex": "#608",
+ "Typ": [
+ "Geist",
+ "Feuer"
+ ]
+ },
+ "609": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fa/Sugimori_609.png"
+ ]
+ ],
+ "Deutsch": "Skelabra",
+ "Englisch": "Chandelure",
+ "Farbe": "Schwarz",
+ "Französisch": "Lugulabre",
+ "Gewicht": "34,3 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b7\\u30e3\\u30f3\\u30c7\\u30e9 (Shandela)",
+ "Kategorie": "Geleit",
+ "Link": "http://pokewiki.de/Skelabra",
+ "National-Dex": "#609",
+ "Typ": [
+ "Geist",
+ "Feuer"
+ ]
+ },
+ "610": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/45/Sugimori_610.png"
+ ]
+ ],
+ "Deutsch": "Milza",
+ "Englisch": "Axew",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Coupenotte",
+ "Gewicht": "18,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30ad\\u30d0\\u30b4 (Kibago)",
+ "Kategorie": "Sto\\u00dfzahn",
+ "Link": "http://pokewiki.de/Milza",
+ "National-Dex": "#610",
+ "Typ": "Drache"
+ },
+ "611": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/83/Sugimori_611.png"
+ ]
+ ],
+ "Deutsch": "Sharfax",
+ "Englisch": "Fraxure",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Incisache",
+ "Gewicht": "36,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30aa\\u30ce\\u30f3\\u30c9 (Onondo)",
+ "Kategorie": "Beilkiefer",
+ "Link": "http://pokewiki.de/Sharfax",
+ "National-Dex": "#611",
+ "Typ": "Drache"
+ },
+ "612": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c5/Sugimori_612.png"
+ ]
+ ],
+ "Deutsch": "Maxax",
+ "Englisch": "Haxorus",
+ "Farbe": "Gelb",
+ "Französisch": "Tranchodon",
+ "Gewicht": "105,5 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30aa\\u30ce\\u30ce\\u30af\\u30b9 (Ononokusu)",
+ "Kategorie": "Beilkiefer",
+ "Link": "http://pokewiki.de/Maxax",
+ "National-Dex": "#612",
+ "Typ": "Drache"
+ },
+ "613": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/cc/Sugimori_613.png"
+ ]
+ ],
+ "Deutsch": "Petznief",
+ "Englisch": "Cubchoo",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Polarhume",
+ "Gewicht": "8,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30af\\u30de\\u30b7\\u30e5\\u30f3 (Kumasyun)",
+ "Kategorie": "Eisscholle",
+ "Link": "http://pokewiki.de/Petznief",
+ "National-Dex": "#613",
+ "Typ": "Eis"
+ },
+ "614": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e2/Sugimori_614.png"
+ ]
+ ],
+ "Deutsch": "Siberio",
+ "Englisch": "Beartic",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Polagriffe",
+ "Gewicht": "260,0 kg",
+ "Größe": "2,6 m",
+ "Japanisch": "\\u30c4\\u30f3\\u30d9\\u30a2\\u30fc (Tunbear)",
+ "Kategorie": "Packeis",
+ "Link": "http://pokewiki.de/Siberio",
+ "National-Dex": "#614",
+ "Typ": "Eis"
+ },
+ "615": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/77/Sugimori_615.png"
+ ]
+ ],
+ "Deutsch": "Frigometri",
+ "Englisch": "Cryogonal",
+ "Farbe": "Blau",
+ "Französisch": "Hexagel",
+ "Gewicht": "148,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30d5\\u30ea\\u30fc\\u30b8\\u30aa (Freegeo)",
+ "Kategorie": "Kristall",
+ "Link": "http://pokewiki.de/Frigometri",
+ "National-Dex": "#615",
+ "Typ": "Eis"
+ },
+ "616": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4d/Sugimori_616.png"
+ ]
+ ],
+ "Deutsch": "Schnuthelm",
+ "Englisch": "Shelmet",
+ "Farbe": "Rot",
+ "Französisch": "Escargaume",
+ "Gewicht": "7,7 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30c1\\u30e7\\u30dc\\u30de\\u30ad (Chobomaki)",
+ "Kategorie": "Schnecke",
+ "Link": "http://pokewiki.de/Schnuthelm",
+ "National-Dex": "#616",
+ "Typ": "K\\u00e4fer"
+ },
+ "617": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5f/Sugimori_617.png"
+ ]
+ ],
+ "Deutsch": "Hydragil",
+ "Englisch": "Accelgor",
+ "Farbe": "Rot",
+ "Französisch": "Limaspeed",
+ "Gewicht": "25,3 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30a2\\u30ae\\u30eb\\u30c0\\u30fc (Agilder)",
+ "Kategorie": "Entschalung",
+ "Link": "http://pokewiki.de/Hydragil",
+ "National-Dex": "#617",
+ "Typ": "K\\u00e4fer"
+ },
+ "618": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a1/Sugimori_618.png"
+ ]
+ ],
+ "Deutsch": "Flunschlik",
+ "Englisch": "Stunfisk",
+ "Farbe": "Braun",
+ "Französisch": "Limonde",
+ "Gewicht": "11,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30de\\u30c3\\u30ae\\u30e7 (Maggyo)",
+ "Kategorie": "Falle",
+ "Link": "http://pokewiki.de/Flunschlik",
+ "National-Dex": "#618",
+ "Typ": [
+ "Boden",
+ "Elektro"
+ ]
+ },
+ "619": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/33/Sugimori_619.png"
+ ]
+ ],
+ "Deutsch": "Lin-Fu",
+ "Englisch": "Mienfoo",
+ "Farbe": "Gelb",
+ "Französisch": "Kungfouine",
+ "Gewicht": "20,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30b3\\u30b8\\u30e7\\u30d5\\u30fc (Kojofu)",
+ "Kategorie": "Kampfk\\u00fcnste",
+ "Link": "http://pokewiki.de/Lin-Fu",
+ "National-Dex": "#619",
+ "Typ": "Kampf"
+ },
+ "620": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f4/Sugimori_620.png"
+ ]
+ ],
+ "Deutsch": "Wie-Shu",
+ "Englisch": "Mienshao",
+ "Farbe": "Violett",
+ "Französisch": "Shaofouine",
+ "Gewicht": "35,5 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30b3\\u30b8\\u30e7\\u30f3\\u30c9 (Kojondo)",
+ "Kategorie": "Kampfk\\u00fcnste",
+ "Link": "http://pokewiki.de/Wie-Shu",
+ "National-Dex": "#620",
+ "Typ": "Kampf"
+ },
+ "621": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ee/Sugimori_621.png"
+ ]
+ ],
+ "Deutsch": "Shardrago",
+ "Englisch": "Druddigon",
+ "Farbe": "Rot",
+ "Französisch": "Drakkarmin",
+ "Gewicht": "139,0 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30af\\u30ea\\u30e0\\u30ac\\u30f3 (Crimgan)",
+ "Kategorie": "H\\u00f6hle",
+ "Link": "http://pokewiki.de/Shardrago",
+ "National-Dex": "#621",
+ "Typ": "Drache"
+ },
+ "622": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/9b/Sugimori_622.png"
+ ]
+ ],
+ "Deutsch": "Golbit",
+ "Englisch": "Golett",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Gringolem",
+ "Gewicht": "92,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30b4\\u30d3\\u30c3\\u30c8 (Gobit)",
+ "Kategorie": "Urgolem",
+ "Link": "http://pokewiki.de/Golbit",
+ "National-Dex": "#622",
+ "Typ": [
+ "Boden",
+ "Geist"
+ ]
+ },
+ "623": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/92/Sugimori_623.png"
+ ]
+ ],
+ "Deutsch": "Golgantes",
+ "Englisch": "Golurk",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Golemastoc",
+ "Gewicht": "330,0 kg",
+ "Größe": "2,8 m",
+ "Japanisch": "\\u30b4\\u30eb\\u30fc\\u30b0 (Goloog)",
+ "Kategorie": "Urgolem",
+ "Link": "http://pokewiki.de/Golgantes",
+ "National-Dex": "#623",
+ "Typ": [
+ "Boden",
+ "Geist"
+ ]
+ },
+ "624": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/01/Sugimori_624.png"
+ ]
+ ],
+ "Deutsch": "Gladiantri",
+ "Englisch": "Pawniard",
+ "Farbe": "Rot",
+ "Französisch": "Scalpion",
+ "Gewicht": "10,2 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30b3\\u30de\\u30bf\\u30ca (Komatana)",
+ "Kategorie": "Stahlklinge",
+ "Link": "http://pokewiki.de/Gladiantri",
+ "National-Dex": "#624",
+ "Typ": [
+ "Unlicht",
+ "Stahl"
+ ]
+ },
+ "625": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/eb/Sugimori_625.png"
+ ]
+ ],
+ "Deutsch": "Caesurio",
+ "Englisch": "Bisharp",
+ "Farbe": "Rot",
+ "Französisch": "Scalproie",
+ "Gewicht": "70,0 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30ad\\u30ea\\u30ad\\u30b6\\u30f3 (Kirikizan)",
+ "Kategorie": "Schwertklinge",
+ "Link": "http://pokewiki.de/Caesurio",
+ "National-Dex": "#625",
+ "Typ": [
+ "Unlicht",
+ "Stahl"
+ ]
+ },
+ "626": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d9/Sugimori_626.png"
+ ]
+ ],
+ "Deutsch": "Bisofank",
+ "Englisch": "Bouffalant",
+ "Farbe": "Braun",
+ "Französisch": "Frison",
+ "Gewicht": "94,6 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30d0\\u30c3\\u30d5\\u30ed\\u30f3 (Buffron)",
+ "Kategorie": "Kopfsto\\u00dfrind",
+ "Link": "http://pokewiki.de/Bisofank",
+ "National-Dex": "#626",
+ "Typ": "Normal"
+ },
+ "627": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fd/Sugimori_627.png"
+ ]
+ ],
+ "Deutsch": "Geronimatz",
+ "Englisch": "Rufflet",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Furaiglon",
+ "Gewicht": "10,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30ef\\u30b7\\u30dc\\u30f3 (Washibon)",
+ "Kategorie": "Adlerk\\u00fcken",
+ "Link": "http://pokewiki.de/Geronimatz",
+ "National-Dex": "#627",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "628": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/41/Sugimori_628.png"
+ ]
+ ],
+ "Deutsch": "Washakwil",
+ "Englisch": "Braviary",
+ "Farbe": "Rot",
+ "Französisch": "Gueriaigle",
+ "Gewicht": "41,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30a6\\u30a9\\u30fc\\u30b0\\u30eb (Warrgle)",
+ "Kategorie": "K\\u00fchnheit",
+ "Link": "http://pokewiki.de/Washakwil",
+ "National-Dex": "#628",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "629": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6b/Sugimori_629.png"
+ ]
+ ],
+ "Deutsch": "Skallyk",
+ "Englisch": "Vullaby",
+ "Farbe": "Braun",
+ "Französisch": "Vostourno",
+ "Gewicht": "9,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30d0\\u30eb\\u30c1\\u30e3\\u30a4 (Valchai)",
+ "Kategorie": "Windel",
+ "Link": "http://pokewiki.de/Skallyk",
+ "National-Dex": "#629",
+ "Typ": [
+ "Unlicht",
+ "Flug"
+ ]
+ },
+ "630": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/10/Sugimori_630.png"
+ ]
+ ],
+ "Deutsch": "Grypheldis",
+ "Englisch": "Mandibuzz",
+ "Farbe": "Braun",
+ "Französisch": "Vaututrice",
+ "Gewicht": "39,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30d0\\u30eb\\u30b8\\u30fc\\u30ca (Vulgina)",
+ "Kategorie": "Knochenadler",
+ "Link": "http://pokewiki.de/Grypheldis",
+ "National-Dex": "#630",
+ "Typ": [
+ "Unlicht",
+ "Flug"
+ ]
+ },
+ "631": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ae/Sugimori_631.png"
+ ]
+ ],
+ "Deutsch": "Furnifra\\u00df",
+ "Englisch": "Heatmor",
+ "Farbe": "Rot",
+ "Französisch": "Aflamanoir",
+ "Gewicht": "58,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30af\\u30a4\\u30bf\\u30e9\\u30f3 (Kuitaran)",
+ "Kategorie": "Ameisenb\\u00e4r",
+ "Link": "http://pokewiki.de/Furnifra%C3%9F",
+ "National-Dex": "#631",
+ "Typ": "Feuer"
+ },
+ "632": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b1/Sugimori_632.png"
+ ]
+ ],
+ "Deutsch": "Fermicula",
+ "Englisch": "Durant",
+ "Farbe": "Grau",
+ "Französisch": "Fermite",
+ "Gewicht": "33,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30a2\\u30a4\\u30a2\\u30f3\\u30c8 (Aiant)",
+ "Kategorie": "Eisenameise",
+ "Link": "http://pokewiki.de/Fermicula",
+ "National-Dex": "#632",
+ "Typ": [
+ "K\\u00e4fer",
+ "Stahl"
+ ]
+ },
+ "633": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7f/Sugimori_633.png"
+ ]
+ ],
+ "Deutsch": "Kapuno",
+ "Englisch": "Deino",
+ "Farbe": "Blau",
+ "Französisch": "Solochi",
+ "Gewicht": "17,3 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30e2\\u30ce\\u30ba (Monozu)",
+ "Kategorie": "Haudrauf",
+ "Link": "http://pokewiki.de/Kapuno",
+ "National-Dex": "#633",
+ "Typ": [
+ "Unlicht",
+ "Drache"
+ ]
+ },
+ "634": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8e/Sugimori_634.png"
+ ]
+ ],
+ "Deutsch": "Duodino",
+ "Englisch": "Zweilous",
+ "Farbe": "Blau",
+ "Französisch": "Diamat",
+ "Gewicht": "50,0 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30b8\\u30d8\\u30c3\\u30c9 (Dihead)",
+ "Kategorie": "Grobheit",
+ "Link": "http://pokewiki.de/Duodino",
+ "National-Dex": "#634",
+ "Typ": [
+ "Unlicht",
+ "Drache"
+ ]
+ },
+ "635": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6b/Sugimori_635.png"
+ ]
+ ],
+ "Deutsch": "Trikephalo",
+ "Englisch": "Hydreigon",
+ "Farbe": "Blau",
+ "Französisch": "Trioxydre",
+ "Gewicht": "160,0 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30b5\\u30b6\\u30f3\\u30c9\\u30e9 (Sazandora)",
+ "Kategorie": "Brutal",
+ "Link": "http://pokewiki.de/Trikephalo",
+ "National-Dex": "#635",
+ "Typ": [
+ "Unlicht",
+ "Drache"
+ ]
+ },
+ "636": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b5/Sugimori_636.png"
+ ]
+ ],
+ "Deutsch": "Ignivor",
+ "Englisch": "Larvesta",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Pyronille",
+ "Gewicht": "28,8 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30e1\\u30e9\\u30eb\\u30d0 (Merlarva)",
+ "Kategorie": "Fackel",
+ "Link": "http://pokewiki.de/Ignivor",
+ "National-Dex": "#636",
+ "Typ": [
+ "K\\u00e4fer",
+ "Feuer"
+ ]
+ },
+ "637": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/53/Sugimori_637.png"
+ ]
+ ],
+ "Deutsch": "Ramoth",
+ "Englisch": "Volcarona",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Pyrax",
+ "Gewicht": "46,0 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30a6\\u30eb\\u30ac\\u30e2\\u30b9 (Ulgamoth)",
+ "Kategorie": "Sonne",
+ "Link": "http://pokewiki.de/Ramoth",
+ "National-Dex": "#637",
+ "Typ": [
+ "K\\u00e4fer",
+ "Feuer"
+ ]
+ },
+ "638": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/85/Sugimori_638.png"
+ ]
+ ],
+ "Deutsch": "Kobalium",
+ "Englisch": "Cobalion",
+ "Farbe": "Blau",
+ "Französisch": "Cobaltium",
+ "Gewicht": "250,0 kg",
+ "Größe": "2,1 m",
+ "Japanisch": "\\u30b3\\u30d0\\u30eb\\u30aa\\u30f3 (Cobalon)",
+ "Kategorie": "Eisenkern",
+ "Link": "http://pokewiki.de/Kobalium",
+ "National-Dex": "#638",
+ "Typ": [
+ "Stahl",
+ "Kampf"
+ ]
+ },
+ "639": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e0/Sugimori_639.png"
+ ]
+ ],
+ "Deutsch": "Terrakium",
+ "Englisch": "Terrakion",
+ "Farbe": "Grau",
+ "Französisch": "Terrakium",
+ "Gewicht": "260,0 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30c6\\u30e9\\u30ad\\u30aa\\u30f3 (Terrakion)",
+ "Kategorie": "Felsenh\\u00f6hle",
+ "Link": "http://pokewiki.de/Terrakium",
+ "National-Dex": "#639",
+ "Typ": [
+ "Gestein",
+ "Kampf"
+ ]
+ },
+ "640": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/47/Sugimori_640.png"
+ ]
+ ],
+ "Deutsch": "Viridium",
+ "Englisch": "Virizion",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Viridium",
+ "Gewicht": "200,0 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30d3\\u30ea\\u30b8\\u30aa\\u30f3 (Virizion)",
+ "Kategorie": "Wiese",
+ "Link": "http://pokewiki.de/Viridium",
+ "National-Dex": "#640",
+ "Typ": [
+ "Pflanze",
+ "Kampf"
+ ]
+ },
+ "641": {
+ "Bilder": [
+ [
+ "Inkarnationsform",
+ "http://www.pokewiki.de/images/d/d8/Sugimori_641.png"
+ ],
+ [
+ "Tiergeistform",
+ "http://www.pokewiki.de/images/f/f2/Sugimori_641a.png"
+ ]
+ ],
+ "Deutsch": "Boreos",
+ "Englisch": "Tornadus",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Bor\\u00e9as",
+ "Gewicht": "63,0 kg",
+ "Größe": [
+ "1,5 m (Inkarnationsform)",
+ "1,4 m (Tiergeistform)"
+ ],
+ "Japanisch": "\\u30c8\\u30eb\\u30cd\\u30ed\\u30b9 (Tornelos)",
+ "Kategorie": "Wirbelsturm",
+ "Link": "http://pokewiki.de/Boreos",
+ "National-Dex": "#641",
+ "Typ": "Flug"
+ },
+ "642": {
+ "Bilder": [
+ [
+ "Inkarnationsform",
+ "http://www.pokewiki.de/images/b/b9/Sugimori_642.png"
+ ],
+ [
+ "Tiergeistform",
+ "http://www.pokewiki.de/images/6/6b/Sugimori_642a.png"
+ ]
+ ],
+ "Deutsch": "Voltolos",
+ "Englisch": "Thundurus",
+ "Farbe": "Blau",
+ "Französisch": "Fulguris",
+ "Gewicht": "61,0 kg",
+ "Größe": [
+ "1,5 m (Inkarnationsform)",
+ "3,0 m (Tiergeistform)"
+ ],
+ "Japanisch": "\\u30dc\\u30eb\\u30c8\\u30ed\\u30b9 (Voltolos)",
+ "Kategorie": "Torpedo",
+ "Link": "http://pokewiki.de/Voltolos",
+ "National-Dex": "#642",
+ "Typ": [
+ "Elektro",
+ "Flug"
+ ]
+ },
+ "643": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f1/Sugimori_643.png"
+ ]
+ ],
+ "Deutsch": "Reshiram",
+ "Englisch": "Reshiram",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Reshiram",
+ "Gewicht": "330,0 kg",
+ "Größe": "3,2 m",
+ "Japanisch": "\\u30ec\\u30b7\\u30e9\\u30e0 (Reshiram)",
+ "Kategorie": "Wahres Wei\\u00df",
+ "Link": "http://pokewiki.de/Reshiram",
+ "National-Dex": "#643",
+ "Typ": [
+ "Drache",
+ "Feuer"
+ ]
+ },
+ "644": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/14/Sugimori_644.png"
+ ]
+ ],
+ "Deutsch": "Zekrom",
+ "Englisch": "Zekrom",
+ "Farbe": "Schwarz",
+ "Französisch": "Zekrom",
+ "Gewicht": "345,0 kg",
+ "Größe": "2,9 m",
+ "Japanisch": "\\u30bc\\u30af\\u30ed\\u30e0 (Zekrom)",
+ "Kategorie": "Reines Schwarz",
+ "Link": "http://pokewiki.de/Zekrom",
+ "National-Dex": "#644",
+ "Typ": [
+ "Drache",
+ "Elektro"
+ ]
+ },
+ "645": {
+ "Bilder": [
+ [
+ "Inkarnationsform",
+ "http://www.pokewiki.de/images/2/2e/Sugimori_645.png"
+ ],
+ [
+ "Tiergeistform",
+ "http://www.pokewiki.de/images/a/a9/Sugimori_645a.png"
+ ]
+ ],
+ "Deutsch": "Demeteros",
+ "Englisch": "Landorus",
+ "Farbe": "Braun",
+ "Französisch": "D\\u00e9m\\u00e9t\\u00e9ros",
+ "Gewicht": "68,0 kg",
+ "Größe": [
+ "1,5 m (Inkarnationsform)",
+ "1,3 m (Tiergeistform)"
+ ],
+ "Japanisch": "\\u30e9\\u30f3\\u30c9\\u30ed\\u30b9 (Landlos)",
+ "Kategorie": "Reichtum",
+ "Link": "http://pokewiki.de/Demeteros",
+ "National-Dex": "#645",
+ "Typ": [
+ "Boden",
+ "Flug"
+ ]
+ },
+ "646": {
+ "Bilder": [
+ [
+ "Kyurem",
+ "http://www.pokewiki.de/images/e/e1/Sugimori_646.png"
+ ],
+ [
+ "Wei\\u00dfes Kyurem",
+ "http://www.pokewiki.de/images/a/a3/Sugimori_646a.png"
+ ],
+ [
+ "Schwarzes Kyurem",
+ "http://www.pokewiki.de/images/c/c1/Sugimori_646b.png"
+ ]
+ ],
+ "Deutsch": "Kyurem",
+ "Englisch": "Kyurem",
+ "Farbe": "Grau",
+ "Französisch": "Kyurem",
+ "Gewicht": "325,0 kg",
+ "Größe": [
+ "3,0 m",
+ "3,3 m (Schwarzes Kyurem)",
+ "3,6 m (Wei\\u00dfes Kyurem)"
+ ],
+ "Japanisch": "\\u30ad\\u30e5\\u30ec\\u30e0 (Kyurem)",
+ "Kategorie": "Schwelle",
+ "Link": "http://pokewiki.de/Kyurem",
+ "National-Dex": "#646",
+ "Typ": [
+ "Drache",
+ "Eis"
+ ]
+ },
+ "647": {
+ "Bilder": [
+ [
+ "Standardform",
+ "http://www.pokewiki.de/images/1/16/Sugimori_647.png"
+ ],
+ [
+ "Resolutform",
+ "http://www.pokewiki.de/images/8/8c/Sugimori_647a.png"
+ ]
+ ],
+ "Deutsch": "Keldeo",
+ "Englisch": "Keldeo",
+ "Farbe": "Gelb",
+ "Französisch": "Keldeo",
+ "Gewicht": "48,5 kg",
+ "Größe": "1,4 m",
+ "Japanisch": "\\u30b1\\u30eb\\u30c7\\u30a3\\u30aa (Keldeo)",
+ "Kategorie": "Fohlen",
+ "Link": "http://pokewiki.de/Keldeo",
+ "National-Dex": "#647",
+ "Typ": [
+ "Wasser",
+ "Kampf"
+ ]
+ },
+ "648": {
+ "Bilder": [
+ [
+ "Gesangsform",
+ "http://www.pokewiki.de/images/c/c2/Sugimori_648.png"
+ ],
+ [
+ "Tanzform",
+ "http://www.pokewiki.de/images/6/64/Sugimori_648a.png"
+ ]
+ ],
+ "Deutsch": "Meloetta",
+ "Englisch": "Meloetta",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Meloetta",
+ "Gewicht": "6,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e1\\u30ed\\u30a8\\u30c3\\u30bf (Meloetta)",
+ "Kategorie": "Melodie",
+ "Link": "http://pokewiki.de/Meloetta",
+ "National-Dex": "#648",
+ "Typ": [
+ "Normal",
+ "Psycho (Gesangsform)",
+ "Normal",
+ "Kampf (Tanzform)"
+ ]
+ },
+ "649": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ef/Sugimori_649.png"
+ ]
+ ],
+ "Deutsch": "Genesect",
+ "Englisch": "Genesect",
+ "Farbe": "Violett",
+ "Französisch": "Genesect",
+ "Gewicht": "82,5 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30b2\\u30ce\\u30bb\\u30af\\u30c8 (Genesect)",
+ "Kategorie": "Pal\\u00e4ozoikum",
+ "Link": "http://pokewiki.de/Genesect",
+ "National-Dex": "#649",
+ "Typ": [
+ "K\\u00e4fer",
+ "Stahl"
+ ]
+ },
+ "650": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ae/Sugimori_650.png"
+ ]
+ ],
+ "Deutsch": "Igamaro",
+ "Englisch": "Chespin",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Marisson",
+ "Gewicht": "9,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30cf\\u30ea\\u30de\\u30ed\\u30f3 (Harimaron)",
+ "Kategorie": "Stachelkopf",
+ "Link": "http://pokewiki.de/Igamaro",
+ "National-Dex": "#650",
+ "Typ": "Pflanze"
+ },
+ "651": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f4/Sugimori_651.png"
+ ]
+ ],
+ "Deutsch": "Igastarnish",
+ "Englisch": "Quilladin",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Bogu\\u00e9risse",
+ "Gewicht": "29,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30cf\\u30ea\\u30dc\\u30fc\\u30b0 (Hariborg)",
+ "Kategorie": "Spitzpanzer",
+ "Link": "http://pokewiki.de/Igastarnish",
+ "National-Dex": "#651",
+ "Typ": "Pflanze"
+ },
+ "652": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/9f/Sugimori_652.png"
+ ]
+ ],
+ "Deutsch": "Brigaron",
+ "Englisch": "Chesnaught",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Blind\\u00e9pique",
+ "Gewicht": "90,0 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30d6\\u30ea\\u30ac\\u30ed\\u30f3 (Brigarron)",
+ "Kategorie": "Spitzpanzer",
+ "Link": "http://pokewiki.de/Brigaron",
+ "National-Dex": "#652",
+ "Typ": [
+ "Pflanze",
+ "Kampf"
+ ]
+ },
+ "653": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5d/Sugimori_653.png"
+ ]
+ ],
+ "Deutsch": "Fynx",
+ "Englisch": "Fennekin",
+ "Farbe": "Rot",
+ "Französisch": "Feunnec",
+ "Gewicht": "9,4 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30d5\\u30a9\\u30c3\\u30b3 (Fokko)",
+ "Kategorie": "Fuchs",
+ "Link": "http://pokewiki.de/Fynx",
+ "National-Dex": "#653",
+ "Typ": "Feuer"
+ },
+ "654": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/ba/Sugimori_654.png"
+ ]
+ ],
+ "Deutsch": "Rutena",
+ "Englisch": "Braixen",
+ "Farbe": "Rot",
+ "Französisch": "Roussil",
+ "Gewicht": "14,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30c6\\u30fc\\u30eb\\u30ca\\u30fc (Tairenar)",
+ "Kategorie": "Fuchs",
+ "Link": "http://pokewiki.de/Rutena",
+ "National-Dex": "#654",
+ "Typ": "Feuer"
+ },
+ "655": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/db/Sugimori_655.png"
+ ]
+ ],
+ "Deutsch": "Fennexis",
+ "Englisch": "Delphox",
+ "Farbe": "Rot",
+ "Französisch": "Goupelin",
+ "Gewicht": "39,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30de\\u30d5\\u30a9\\u30af\\u30b7\\u30fc (Mafoxy)",
+ "Kategorie": "Fuchs",
+ "Link": "http://pokewiki.de/Fennexis",
+ "National-Dex": "#655",
+ "Typ": [
+ "Feuer",
+ "Psycho"
+ ]
+ },
+ "656": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d0/Sugimori_656.png"
+ ]
+ ],
+ "Deutsch": "Froxy",
+ "Englisch": "Froakie",
+ "Farbe": "Blau",
+ "Französisch": "Grenousse",
+ "Gewicht": "7,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30b1\\u30ed\\u30de\\u30c4 (Keromatsu)",
+ "Kategorie": "Blubbfrosch",
+ "Link": "http://pokewiki.de/Froxy",
+ "National-Dex": "#656",
+ "Typ": "Wasser"
+ },
+ "657": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/45/Sugimori_657.png"
+ ]
+ ],
+ "Deutsch": "Amphizel",
+ "Englisch": "Frogadier",
+ "Farbe": "Blau",
+ "Französisch": "Cro\\u00e2poral",
+ "Gewicht": "10,9 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30b2\\u30b3\\u30ac\\u30b7\\u30e9 (Gekogashira)",
+ "Kategorie": "Blubbfrosch",
+ "Link": "http://pokewiki.de/Amphizel",
+ "National-Dex": "#657",
+ "Typ": "Wasser"
+ },
+ "658": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/6/60/Sugimori_658.png"
+ ],
+ [
+ "Ash-Form",
+ "http://www.pokewiki.de/images/c/cc/Sugimori_658a.png"
+ ]
+ ],
+ "Deutsch": "Quajutsu",
+ "Englisch": "Greninja",
+ "Farbe": "Blau",
+ "Französisch": "Amphinobi",
+ "Gewicht": "40,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30b2\\u30c3\\u30b3\\u30a6\\u30ac (Gekkouga)",
+ "Kategorie": "Ninja",
+ "Link": "http://pokewiki.de/Quajutsu",
+ "National-Dex": "#658",
+ "Typ": [
+ "Wasser",
+ "Unlicht"
+ ]
+ },
+ "659": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e4/Sugimori_659.png"
+ ]
+ ],
+ "Deutsch": "Scoppel",
+ "Englisch": "Bunnelby",
+ "Farbe": "Braun",
+ "Französisch": "Sapereau",
+ "Gewicht": "5,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30db\\u30eb\\u30d3\\u30fc (Horubee)",
+ "Kategorie": "Baugr\\u00e4ber",
+ "Link": "http://pokewiki.de/Scoppel",
+ "National-Dex": "#659",
+ "Typ": "Normal"
+ },
+ "660": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/27/Sugimori_660.png"
+ ]
+ ],
+ "Deutsch": "Grebbit",
+ "Englisch": "Diggersby",
+ "Farbe": "Braun",
+ "Französisch": "Excavarenne",
+ "Gewicht": "42,4 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30db\\u30eb\\u30fc\\u30c9 (Horudo)",
+ "Kategorie": "Baugr\\u00e4ber",
+ "Link": "http://pokewiki.de/Grebbit",
+ "National-Dex": "#660",
+ "Typ": [
+ "Normal",
+ "Boden"
+ ]
+ },
+ "661": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/53/Sugimori_661.png"
+ ]
+ ],
+ "Deutsch": "Dartiri",
+ "Englisch": "Fletchling",
+ "Farbe": "Rot",
+ "Französisch": "Passerouge",
+ "Gewicht": "1,7 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30e4\\u30e4\\u30b3\\u30de (Yayakoma)",
+ "Kategorie": "Rotkehlchen",
+ "Link": "http://pokewiki.de/Dartiri",
+ "National-Dex": "#661",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "662": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/17/Sugimori_662.png"
+ ]
+ ],
+ "Deutsch": "Dartignis",
+ "Englisch": "Fletchinder",
+ "Farbe": "Rot",
+ "Französisch": "Braisillon",
+ "Gewicht": "16,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30d2\\u30ce\\u30e4\\u30b3\\u30de (Hinoyakoma)",
+ "Kategorie": "Funkenregen",
+ "Link": "http://pokewiki.de/Dartignis",
+ "National-Dex": "#662",
+ "Typ": [
+ "Feuer",
+ "Flug"
+ ]
+ },
+ "663": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c9/Sugimori_663.png"
+ ]
+ ],
+ "Deutsch": "Fiaro",
+ "Englisch": "Talonflame",
+ "Farbe": "Rot",
+ "Französisch": "Flambusard",
+ "Gewicht": "24,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30d5\\u30a1\\u30a4\\u30a2\\u30ed\\u30fc (Fiarrow)",
+ "Kategorie": "Stichflamme",
+ "Link": "http://pokewiki.de/Fiaro",
+ "National-Dex": "#663",
+ "Typ": [
+ "Feuer",
+ "Flug"
+ ]
+ },
+ "664": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2c/Sugimori_664.png"
+ ]
+ ],
+ "Deutsch": "Purmel",
+ "Englisch": "Scatterbug",
+ "Farbe": "Schwarz",
+ "Französisch": "L\\u00e9pidonille",
+ "Gewicht": "2,5 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30b3\\u30d5\\u30ad\\u30e0\\u30b7 (Kofukimushi)",
+ "Kategorie": "Puderinsekt",
+ "Link": "http://pokewiki.de/Purmel",
+ "National-Dex": "#664",
+ "Typ": "K\\u00e4fer"
+ },
+ "665": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/78/Sugimori_665.png"
+ ]
+ ],
+ "Deutsch": "Puponcho",
+ "Englisch": "Spewpa",
+ "Farbe": "Schwarz",
+ "Französisch": "P\\u00e9r\\u00e9grain",
+ "Gewicht": "8,4 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30b3\\u30d5\\u30fc\\u30e9\\u30a4 (Kofuurai)",
+ "Kategorie": "Puderinsekt",
+ "Link": "http://pokewiki.de/Puponcho",
+ "National-Dex": "#665",
+ "Typ": "K\\u00e4fer"
+ },
+ "666": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/91/Sugimori_666f.png"
+ ]
+ ],
+ "Deutsch": "Vivillon",
+ "Englisch": "Vivillon",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Prismillon",
+ "Gewicht": "17,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30d3\\u30d3\\u30e8\\u30f3 (Viviyon)",
+ "Kategorie": "Fl\\u00fcgelstaub",
+ "Link": "http://pokewiki.de/Vivillon",
+ "National-Dex": "#666",
+ "Typ": [
+ "K\\u00e4fer",
+ "Flug"
+ ]
+ },
+ "667": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/11/Sugimori_667.png"
+ ]
+ ],
+ "Deutsch": "Leufeo",
+ "Englisch": "Litleo",
+ "Farbe": "Braun",
+ "Französisch": "H\\u00e9lionceau",
+ "Gewicht": "13,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30b7\\u30b7\\u30b3 (Shishiko)",
+ "Kategorie": "Jungl\\u00f6we",
+ "Link": "http://pokewiki.de/Leufeo",
+ "National-Dex": "#667",
+ "Typ": [
+ "Feuer",
+ "Normal"
+ ]
+ },
+ "668": {
+ "Bilder": [
+ [
+ "m\\u00e4nnlich",
+ "http://www.pokewiki.de/images/2/24/Sugimori_668.png"
+ ],
+ [
+ "weiblich",
+ "http://www.pokewiki.de/images/7/70/Sugimori_668a.png"
+ ]
+ ],
+ "Deutsch": "Pyroleo",
+ "Englisch": "Pyroar",
+ "Farbe": "Braun",
+ "Französisch": "N\\u00e9m\\u00e9lios",
+ "Gewicht": "81,5 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30ab\\u30a8\\u30f3\\u30b8\\u30b7 (Kaenjishi)",
+ "Kategorie": "Monarch",
+ "Link": "http://pokewiki.de/Pyroleo",
+ "National-Dex": "#668",
+ "Typ": [
+ "Feuer",
+ "Normal"
+ ]
+ },
+ "669": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6b/Sugimori_669.png"
+ ]
+ ],
+ "Deutsch": "Flab\\u00e9b\\u00e9",
+ "Englisch": "Flab\\u00e9b\\u00e9",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Flab\\u00e9b\\u00e9",
+ "Gewicht": "0,1 kg",
+ "Größe": "0,1 m",
+ "Japanisch": "\\u30d5\\u30e9\\u30d9\\u30d9 (Flabebe)",
+ "Kategorie": "Einbl\\u00fctler",
+ "Link": "http://pokewiki.de/Flab%C3%A9b%C3%A9",
+ "National-Dex": "#669",
+ "Typ": "Fee"
+ },
+ "670": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b1/Sugimori_670.png"
+ ]
+ ],
+ "Deutsch": "Floette",
+ "Englisch": "Floette",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Floette",
+ "Gewicht": "0,9 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30d5\\u30e9\\u30a8\\u30c3\\u30c6 (Furaette)",
+ "Kategorie": "Einbl\\u00fctler",
+ "Link": "http://pokewiki.de/Floette",
+ "National-Dex": "#670",
+ "Typ": "Fee"
+ },
+ "671": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/59/Sugimori_671.png"
+ ]
+ ],
+ "Deutsch": "Florges",
+ "Englisch": "Florges",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Florges",
+ "Gewicht": "10,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30d5\\u30e9\\u30fc\\u30b8\\u30a7\\u30b9 (Florges)",
+ "Kategorie": "Garten",
+ "Link": "http://pokewiki.de/Florges",
+ "National-Dex": "#671",
+ "Typ": "Fee"
+ },
+ "672": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/74/Sugimori_672.png"
+ ]
+ ],
+ "Deutsch": "M\\u00e4hikel",
+ "Englisch": "Skiddo",
+ "Farbe": "Braun",
+ "Französisch": "Cabriolaine",
+ "Gewicht": "31,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30e1\\u30a7\\u30fc\\u30af\\u30eb (Meecle)",
+ "Kategorie": "Ritt",
+ "Link": "http://pokewiki.de/M%C3%A4hikel",
+ "National-Dex": "#672",
+ "Typ": "Pflanze"
+ },
+ "673": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/00/Sugimori_673.png"
+ ]
+ ],
+ "Deutsch": "Chevrumm",
+ "Englisch": "Gogoat",
+ "Farbe": "Braun",
+ "Französisch": "Chevroum",
+ "Gewicht": "91,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30b4\\u30fc\\u30b4\\u30fc\\u30c8 (Gogoat)",
+ "Kategorie": "Ritt",
+ "Link": "http://pokewiki.de/Chevrumm",
+ "National-Dex": "#673",
+ "Typ": "Pflanze"
+ },
+ "674": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fc/Sugimori_674.png"
+ ]
+ ],
+ "Deutsch": "Pam-Pam",
+ "Englisch": "Pancham",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Pandespi\\u00e8gle",
+ "Gewicht": "8,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e4\\u30f3\\u30c1\\u30e3\\u30e0 (Yancham)",
+ "Kategorie": "Verspielt",
+ "Link": "http://pokewiki.de/Pam-Pam",
+ "National-Dex": "#674",
+ "Typ": "Kampf"
+ },
+ "675": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/27/Sugimori_675.png"
+ ]
+ ],
+ "Deutsch": "Pandagro",
+ "Englisch": "Pangoro",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Pandarbare",
+ "Gewicht": "136,0 kg",
+ "Größe": "2,1 m",
+ "Japanisch": "\\u30b4\\u30ed\\u30f3\\u30c0 (Goronda)",
+ "Kategorie": "Gaunerblick",
+ "Link": "http://pokewiki.de/Pandagro",
+ "National-Dex": "#675",
+ "Typ": [
+ "Kampf",
+ "Unlicht"
+ ]
+ },
+ "676": {
+ "Bilder": [
+ [
+ "Zottelform",
+ "http://www.pokewiki.de/images/1/13/Sugimori_676.png"
+ ],
+ [
+ "Alternative Formen",
+ "http://www.pokewiki.de/images/f/f2/Sugimori_676a.png"
+ ],
+ [
+ "Alternative Formen",
+ "http://www.pokewiki.de/images/0/06/Sugimori_676b.png"
+ ],
+ [
+ "Alternative Formen",
+ "http://www.pokewiki.de/images/a/a7/Sugimori_676c.png"
+ ]
+ ],
+ "Deutsch": "Coiffwaff",
+ "Englisch": "Furfrou",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Couafarel",
+ "Gewicht": "28,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30c8\\u30ea\\u30df\\u30a2\\u30f3 (Trimmien)",
+ "Kategorie": "Pudel",
+ "Link": "http://pokewiki.de/Coiffwaff",
+ "National-Dex": "#676",
+ "Typ": "Normal"
+ },
+ "677": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/94/Sugimori_677.png"
+ ]
+ ],
+ "Deutsch": "Psiau",
+ "Englisch": "Espurr",
+ "Farbe": "Grau",
+ "Französisch": "Psystigri",
+ "Gewicht": "3,5 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30cb\\u30e3\\u30b9\\u30d1\\u30fc (Nyasper)",
+ "Kategorie": "Z\\u00fcgelung",
+ "Link": "http://pokewiki.de/Psiau",
+ "National-Dex": "#677",
+ "Typ": "Psycho"
+ },
+ "678": {
+ "Bilder": [
+ [
+ "m\\u00e4nnlich",
+ "http://www.pokewiki.de/images/c/c9/Sugimori_678.png"
+ ],
+ [
+ "weiblich",
+ "http://www.pokewiki.de/images/6/60/Sugimori_678a.png"
+ ]
+ ],
+ "Deutsch": "Psiaugon",
+ "Englisch": "Meowstic",
+ "Farbe": [
+ "Blau",
+ "Wei\\u00df (weiblich)"
+ ],
+ "Französisch": "Mistigrix",
+ "Gewicht": "8,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30cb\\u30e3\\u30aa\\u30cb\\u30af\\u30b9 (Nyaonix)",
+ "Kategorie": "Fassung",
+ "Link": "http://pokewiki.de/Psiaugon",
+ "National-Dex": "#678",
+ "Typ": "Psycho"
+ },
+ "679": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/26/Sugimori_679.png"
+ ]
+ ],
+ "Deutsch": "Gramokles",
+ "Englisch": "Honedge",
+ "Farbe": "Braun",
+ "Französisch": "Monorpale",
+ "Gewicht": "2,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30d2\\u30c8\\u30c4\\u30ad (Hitotsuki)",
+ "Kategorie": "Klingenkraft",
+ "Link": "http://pokewiki.de/Gramokles",
+ "National-Dex": "#679",
+ "Typ": [
+ "Stahl",
+ "Geist"
+ ]
+ },
+ "680": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/32/Sugimori_680.png"
+ ]
+ ],
+ "Deutsch": "Duokles",
+ "Englisch": "Doublade",
+ "Farbe": "Braun",
+ "Französisch": "Dimocl\\u00e8s",
+ "Gewicht": "4,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30cb\\u30c0\\u30f3\\u30ae\\u30eb (Nidangill)",
+ "Kategorie": "Klingenkraft",
+ "Link": "http://pokewiki.de/Duokles",
+ "National-Dex": "#680",
+ "Typ": [
+ "Stahl",
+ "Geist"
+ ]
+ },
+ "681": {
+ "Bilder": [
+ [
+ "Schildform",
+ "http://www.pokewiki.de/images/7/70/Sugimori_681.png"
+ ],
+ [
+ "Klingenform",
+ "http://www.pokewiki.de/images/b/bf/Sugimori_681a.png"
+ ]
+ ],
+ "Deutsch": "Durengard",
+ "Englisch": "Aegislash",
+ "Farbe": "Braun",
+ "Französisch": "Exagide",
+ "Gewicht": "53,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30ae\\u30eb\\u30ac\\u30eb\\u30c9 (Gillgard)",
+ "Kategorie": "K\\u00f6nigsklinge",
+ "Link": "http://pokewiki.de/Durengard",
+ "National-Dex": "#681",
+ "Typ": [
+ "Stahl",
+ "Geist"
+ ]
+ },
+ "682": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2c/Sugimori_682.png"
+ ]
+ ],
+ "Deutsch": "Parfi",
+ "Englisch": "Spritzee",
+ "Farbe": "Rosa",
+ "Französisch": "Fluvetin",
+ "Gewicht": "0,5 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30b7\\u30e5\\u30b7\\u30e5\\u30d7 (Shushupu)",
+ "Kategorie": "Parf\\u00fcm",
+ "Link": "http://pokewiki.de/Parfi",
+ "National-Dex": "#682",
+ "Typ": "Fee"
+ },
+ "683": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/07/Sugimori_683.png"
+ ]
+ ],
+ "Deutsch": "Parfinesse",
+ "Englisch": "Aromatisse",
+ "Farbe": "Rosa",
+ "Französisch": "Cocotine",
+ "Gewicht": "15,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30d5\\u30ec\\u30d5\\u30ef\\u30f3 (Furefuwan)",
+ "Kategorie": "Duft",
+ "Link": "http://pokewiki.de/Parfinesse",
+ "National-Dex": "#683",
+ "Typ": "Fee"
+ },
+ "684": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ef/Sugimori_684.png"
+ ]
+ ],
+ "Deutsch": "Flauschling",
+ "Englisch": "Swirlix",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Sucroquin",
+ "Gewicht": "3,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30da\\u30ed\\u30c3\\u30d1\\u30d5 (Peroppafu)",
+ "Kategorie": "Zuckerwatte",
+ "Link": "http://pokewiki.de/Flauschling",
+ "National-Dex": "#684",
+ "Typ": "Fee"
+ },
+ "685": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0b/Sugimori_685.png"
+ ]
+ ],
+ "Deutsch": "Sabbaione",
+ "Englisch": "Slurpuff",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Cupcanaille",
+ "Gewicht": "5,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30da\\u30ed\\u30ea\\u30fc\\u30e0 (Peror\\u012bmu)",
+ "Kategorie": "Schlagsahne",
+ "Link": "http://pokewiki.de/Sabbaione",
+ "National-Dex": "#685",
+ "Typ": "Fee"
+ },
+ "686": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c9/Sugimori_686.png"
+ ]
+ ],
+ "Deutsch": "Iscalar",
+ "Englisch": "Inkay",
+ "Farbe": "Blau",
+ "Französisch": "Sepiatop",
+ "Gewicht": "3,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30de\\u30fc\\u30a4\\u30fc\\u30ab (Maaiika)",
+ "Kategorie": "Rotation",
+ "Link": "http://pokewiki.de/Iscalar",
+ "National-Dex": "#686",
+ "Typ": [
+ "Unlicht",
+ "Psycho"
+ ]
+ },
+ "687": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5c/Sugimori_687.png"
+ ]
+ ],
+ "Deutsch": "Calamanero",
+ "Englisch": "Malamar",
+ "Farbe": "Blau",
+ "Französisch": "Sepiatroce",
+ "Gewicht": "47,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30ab\\u30e9\\u30de\\u30cd\\u30ed (Calamanero)",
+ "Kategorie": "Inversion",
+ "Link": "http://pokewiki.de/Calamanero",
+ "National-Dex": "#687",
+ "Typ": [
+ "Unlicht",
+ "Psycho"
+ ]
+ },
+ "688": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/64/Sugimori_688.png"
+ ]
+ ],
+ "Deutsch": "Bithora",
+ "Englisch": "Binacle",
+ "Farbe": "Braun",
+ "Französisch": "Opermine",
+ "Gewicht": "31,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30ab\\u30e1\\u30c6\\u30c6 (Kametete)",
+ "Kategorie": "Krallenduo",
+ "Link": "http://pokewiki.de/Bithora",
+ "National-Dex": "#688",
+ "Typ": [
+ "Gestein",
+ "Wasser"
+ ]
+ },
+ "689": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fc/Sugimori_689.png"
+ ]
+ ],
+ "Deutsch": "Thanathora",
+ "Englisch": "Barbaracle",
+ "Farbe": "Braun",
+ "Französisch": "Golgopathe",
+ "Gewicht": "96,0 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30ac\\u30e1\\u30ce\\u30c7\\u30b9 (Gamenodes)",
+ "Kategorie": "Ballung",
+ "Link": "http://pokewiki.de/Thanathora",
+ "National-Dex": "#689",
+ "Typ": [
+ "Gestein",
+ "Wasser"
+ ]
+ },
+ "690": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b3/Sugimori_690.png"
+ ]
+ ],
+ "Deutsch": "Algitt",
+ "Englisch": "Skrelp",
+ "Farbe": "Braun",
+ "Französisch": "Venalgue",
+ "Gewicht": "7,3 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30af\\u30ba\\u30e2\\u30fc (Kuzumo)",
+ "Kategorie": "Tangmimikry",
+ "Link": "http://pokewiki.de/Algitt",
+ "National-Dex": "#690",
+ "Typ": [
+ "Gift",
+ "Wasser"
+ ]
+ },
+ "691": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/89/Sugimori_691.png"
+ ]
+ ],
+ "Deutsch": "Tandrak",
+ "Englisch": "Dragalge",
+ "Farbe": "Braun",
+ "Französisch": "Kravarech",
+ "Gewicht": "81,5 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30c9\\u30e9\\u30df\\u30c9\\u30ed (Dramidoro)",
+ "Kategorie": "Tangmimikry",
+ "Link": "http://pokewiki.de/Tandrak",
+ "National-Dex": "#691",
+ "Typ": [
+ "Gift",
+ "Drache"
+ ]
+ },
+ "692": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/07/Sugimori_692.png"
+ ]
+ ],
+ "Deutsch": "Scampisto",
+ "Englisch": "Clauncher",
+ "Farbe": "Blau",
+ "Französisch": "Flingouste",
+ "Gewicht": "8,3 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30a6\\u30c7\\u30c3\\u30dd\\u30a6 (Udeppou)",
+ "Kategorie": "Aquapistole",
+ "Link": "http://pokewiki.de/Scampisto",
+ "National-Dex": "#692",
+ "Typ": "Wasser"
+ },
+ "693": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/92/Sugimori_693.png"
+ ]
+ ],
+ "Deutsch": "Wummer",
+ "Englisch": "Clawitzer",
+ "Farbe": "Blau",
+ "Französisch": "Gamblast",
+ "Gewicht": "35,3 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30d6\\u30ed\\u30b9\\u30bf\\u30fc (Bloster)",
+ "Kategorie": "Aquawumme",
+ "Link": "http://pokewiki.de/Wummer",
+ "National-Dex": "#693",
+ "Typ": "Wasser"
+ },
+ "694": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/26/Sugimori_694.png"
+ ]
+ ],
+ "Deutsch": "Eguana",
+ "Englisch": "Helioptile",
+ "Farbe": "Gelb",
+ "Französisch": "Galvaran",
+ "Gewicht": "6,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30a8\\u30ea\\u30ad\\u30c6\\u30eb (Erikiteru)",
+ "Kategorie": "Generator",
+ "Link": "http://pokewiki.de/Eguana",
+ "National-Dex": "#694",
+ "Typ": [
+ "Elektro",
+ "Normal"
+ ]
+ },
+ "695": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d1/Sugimori_695.png"
+ ]
+ ],
+ "Deutsch": "Elezard",
+ "Englisch": "Heliolisk",
+ "Farbe": "Gelb",
+ "Französisch": "Iguolta",
+ "Gewicht": "21,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30a8\\u30ec\\u30b6\\u30fc\\u30c9 (Elezard)",
+ "Kategorie": "Generator",
+ "Link": "http://pokewiki.de/Elezard",
+ "National-Dex": "#695",
+ "Typ": [
+ "Elektro",
+ "Normal"
+ ]
+ },
+ "696": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2e/Sugimori_696.png"
+ ]
+ ],
+ "Deutsch": "Balgoras",
+ "Englisch": "Tyrunt",
+ "Farbe": "Braun",
+ "Französisch": "Ptyranidur",
+ "Gewicht": "26,0 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30c1\\u30b4\\u30e9\\u30b9 (Chigoras)",
+ "Kategorie": "Kronprinz",
+ "Link": "http://pokewiki.de/Balgoras",
+ "National-Dex": "#696",
+ "Typ": [
+ "Gestein",
+ "Drache"
+ ]
+ },
+ "697": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1c/Sugimori_697.png"
+ ]
+ ],
+ "Deutsch": "Monargoras",
+ "Englisch": "Tyrantrum",
+ "Farbe": "Rot",
+ "Französisch": "Rexillius",
+ "Gewicht": "270,0 kg",
+ "Größe": "2,5 m",
+ "Japanisch": "\\u30ac\\u30c1\\u30b4\\u30e9\\u30b9 (Gachigoras)",
+ "Kategorie": "Tyrann",
+ "Link": "http://pokewiki.de/Monargoras",
+ "National-Dex": "#697",
+ "Typ": [
+ "Gestein",
+ "Drache"
+ ]
+ },
+ "698": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0f/Sugimori_698.png"
+ ]
+ ],
+ "Deutsch": "Amarino",
+ "Englisch": "Amaura",
+ "Farbe": "Blau",
+ "Französisch": "Amagara",
+ "Gewicht": "25,2 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30a2\\u30de\\u30eb\\u30b9 (Amarus)",
+ "Kategorie": "Tundra",
+ "Link": "http://pokewiki.de/Amarino",
+ "National-Dex": "#698",
+ "Typ": [
+ "Gestein",
+ "Eis"
+ ]
+ },
+ "699": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2a/Sugimori_699.png"
+ ]
+ ],
+ "Deutsch": "Amagarga",
+ "Englisch": "Aurorus",
+ "Farbe": "Blau",
+ "Französisch": "Dragmara",
+ "Gewicht": "225,0 kg",
+ "Größe": "2,7 m",
+ "Japanisch": "\\u30a2\\u30de\\u30eb\\u30eb\\u30ac (Amaruruga)",
+ "Kategorie": "Tundra",
+ "Link": "http://pokewiki.de/Amagarga",
+ "National-Dex": "#699",
+ "Typ": [
+ "Gestein",
+ "Eis"
+ ]
+ },
+ "700": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f0/Sugimori_700.png"
+ ]
+ ],
+ "Deutsch": "Feelinara",
+ "Englisch": "Sylveon",
+ "Farbe": "Rosa",
+ "Französisch": "Nymphali",
+ "Gewicht": "23,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30cb\\u30f3\\u30d5\\u30a3\\u30a2 (Nymphia)",
+ "Kategorie": "Bindung",
+ "Link": "http://pokewiki.de/Feelinara",
+ "National-Dex": "#700",
+ "Typ": "Fee"
+ },
+ "701": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/38/Sugimori_701.png"
+ ]
+ ],
+ "Deutsch": "Resladero",
+ "Englisch": "Hawlucha",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Brutalibr\\u00e9",
+ "Gewicht": "21,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30eb\\u30c1\\u30e3\\u30d6\\u30eb (Luchabull)",
+ "Kategorie": "Wrestling",
+ "Link": "http://pokewiki.de/Resladero",
+ "National-Dex": "#701",
+ "Typ": [
+ "Kampf",
+ "Flug"
+ ]
+ },
+ "702": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7d/Sugimori_702.png"
+ ]
+ ],
+ "Deutsch": "Dedenne",
+ "Englisch": "Dedenne",
+ "Farbe": "Gelb",
+ "Französisch": "Dedenne",
+ "Gewicht": "2,2 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30c7\\u30c7\\u30f3\\u30cd (Dedenne)",
+ "Kategorie": "Antennen",
+ "Link": "http://pokewiki.de/Dedenne",
+ "National-Dex": "#702",
+ "Typ": [
+ "Elektro",
+ "Fee"
+ ]
+ },
+ "703": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e7/Sugimori_703.png"
+ ]
+ ],
+ "Deutsch": "Rocara",
+ "Englisch": "Carbink",
+ "Farbe": "Grau",
+ "Französisch": "Strassie",
+ "Gewicht": "5,7 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30e1\\u30ec\\u30b7\\u30fc (Melecie)",
+ "Kategorie": "Edelstein",
+ "Link": "http://pokewiki.de/Rocara",
+ "National-Dex": "#703",
+ "Typ": [
+ "Gestein",
+ "Fee"
+ ]
+ },
+ "704": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f4/Sugimori_704.png"
+ ]
+ ],
+ "Deutsch": "Viscora",
+ "Englisch": "Goomy",
+ "Farbe": "Violett",
+ "Französisch": "Mucuscule",
+ "Gewicht": "2,8 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30cc\\u30e1\\u30e9 (Numera)",
+ "Kategorie": "Schneckedei",
+ "Link": "http://pokewiki.de/Viscora",
+ "National-Dex": "#704",
+ "Typ": "Drache"
+ },
+ "705": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/14/Sugimori_705.png"
+ ]
+ ],
+ "Deutsch": "Viscargot",
+ "Englisch": "Sliggoo",
+ "Farbe": "Violett",
+ "Französisch": "Colimucus",
+ "Gewicht": "17,5 kg",
+ "Größe": "0,8 m",
+ "Japanisch": "\\u30cc\\u30e1\\u30a4\\u30eb (Numeil)",
+ "Kategorie": "Schneckedei",
+ "Link": "http://pokewiki.de/Viscargot",
+ "National-Dex": "#705",
+ "Typ": "Drache"
+ },
+ "706": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3b/Sugimori_706.png"
+ ]
+ ],
+ "Deutsch": "Viscogon",
+ "Englisch": "Goodra",
+ "Farbe": "Violett",
+ "Französisch": "Muplodocus",
+ "Gewicht": "150,5 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30cc\\u30e1\\u30eb\\u30b4\\u30f3 (Numelgon)",
+ "Kategorie": "Drache",
+ "Link": "http://pokewiki.de/Viscogon",
+ "National-Dex": "#706",
+ "Typ": "Drache"
+ },
+ "707": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0c/Sugimori_707.png"
+ ]
+ ],
+ "Deutsch": "Clavion",
+ "Englisch": "Klefki",
+ "Farbe": "Grau",
+ "Französisch": "Trousselin",
+ "Gewicht": "3,0 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30af\\u30ec\\u30c3\\u30d5\\u30a3 (Cleffy)",
+ "Kategorie": "Schl\\u00fcssel",
+ "Link": "http://pokewiki.de/Clavion",
+ "National-Dex": "#707",
+ "Typ": [
+ "Stahl",
+ "Fee"
+ ]
+ },
+ "708": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b1/Sugimori_708.png"
+ ]
+ ],
+ "Deutsch": "Paragoni",
+ "Englisch": "Phantump",
+ "Farbe": "Braun",
+ "Französisch": "Broc\\u00e9l\\u00f4me",
+ "Gewicht": "7,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30dc\\u30af\\u30ec\\u30fc (Bokurei)",
+ "Kategorie": "Baumstumpf",
+ "Link": "http://pokewiki.de/Paragoni",
+ "National-Dex": "#708",
+ "Typ": [
+ "Geist",
+ "Pflanze"
+ ]
+ },
+ "709": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/05/Sugimori_709.png"
+ ]
+ ],
+ "Deutsch": "Trombork",
+ "Englisch": "Trevenant",
+ "Farbe": "Braun",
+ "Französisch": "Dess\\u00e9liande",
+ "Gewicht": "71,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30aa\\u30fc\\u30ed\\u30c3\\u30c8 (Ohrot)",
+ "Kategorie": "Urgeh\\u00f6lz",
+ "Link": "http://pokewiki.de/Trombork",
+ "National-Dex": "#709",
+ "Typ": [
+ "Geist",
+ "Pflanze"
+ ]
+ },
+ "710": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d5/Sugimori_710.png"
+ ]
+ ],
+ "Deutsch": "Irrbis",
+ "Englisch": "Pumpkaboo",
+ "Farbe": "Braun",
+ "Französisch": "Pitrouille",
+ "Gewicht": [
+ "S = 3,5 kg",
+ "M = 5,0 kg",
+ "L = 7,5 kg",
+ "XL = 15,0 kg"
+ ],
+ "Größe": [
+ "S = 0,3 m",
+ "M = 0,4 m",
+ "L = 0,5 m",
+ "XL = 0,8 m"
+ ],
+ "Japanisch": "\\u30d0\\u30b1\\u30c3\\u30c1\\u30e3 (Bakeccha)",
+ "Kategorie": "K\\u00fcrbis",
+ "Link": "http://pokewiki.de/Irrbis",
+ "National-Dex": "#710",
+ "Typ": [
+ "Geist",
+ "Pflanze"
+ ]
+ },
+ "711": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/ba/Sugimori_711.png"
+ ]
+ ],
+ "Deutsch": "Pumpdjinn",
+ "Englisch": "Gourgeist",
+ "Farbe": "Braun",
+ "Französisch": "Banshitrouye",
+ "Gewicht": [
+ "S = 9,5 kg",
+ "M = 12,5 kg",
+ "L = 14,0 kg",
+ "XL = 39,0 kg"
+ ],
+ "Größe": [
+ "S = 0,7 m",
+ "M = 0,9 m",
+ "L = 1,1 m",
+ "XL = 1,7 m"
+ ],
+ "Japanisch": "\\u30d1\\u30f3\\u30d7\\u30b8\\u30f3 (Pumpjin)",
+ "Kategorie": "K\\u00fcrbis",
+ "Link": "http://pokewiki.de/Pumpdjinn",
+ "National-Dex": "#711",
+ "Typ": [
+ "Geist",
+ "Pflanze"
+ ]
+ },
+ "712": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b6/Sugimori_712.png"
+ ]
+ ],
+ "Deutsch": "Arktip",
+ "Englisch": "Bergmite",
+ "Farbe": "Blau",
+ "Französisch": "Grela\\u00e7on",
+ "Gewicht": "99,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30ab\\u30c1\\u30b3\\u30fc\\u30eb (Kachikohru)",
+ "Kategorie": "Eisklumpen",
+ "Link": "http://pokewiki.de/Arktip",
+ "National-Dex": "#712",
+ "Typ": "Eis"
+ },
+ "713": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1f/Sugimori_713.png"
+ ]
+ ],
+ "Deutsch": "Arktilas",
+ "Englisch": "Avalugg",
+ "Farbe": "Blau",
+ "Französisch": "S\\u00e9racrawl",
+ "Gewicht": "505,0 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30af\\u30ec\\u30d9\\u30fc\\u30b9 (Crebase)",
+ "Kategorie": "Eisberg",
+ "Link": "http://pokewiki.de/Arktilas",
+ "National-Dex": "#713",
+ "Typ": "Eis"
+ },
+ "714": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/30/Sugimori_714.png"
+ ]
+ ],
+ "Deutsch": "eF-eM",
+ "Englisch": "Noibat",
+ "Farbe": "Violett",
+ "Französisch": "Sonistrelle",
+ "Gewicht": "8,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30aa\\u30f3\\u30d0\\u30c3\\u30c8 (Onbat)",
+ "Kategorie": "Schallwellen",
+ "Link": "http://pokewiki.de/EF-eM",
+ "National-Dex": "#714",
+ "Typ": [
+ "Flug",
+ "Drache"
+ ]
+ },
+ "715": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/94/Sugimori_715.png"
+ ]
+ ],
+ "Deutsch": "UHaFnir",
+ "Englisch": "Noivern",
+ "Farbe": "Violett",
+ "Französisch": "Bruyverne",
+ "Gewicht": "85,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30aa\\u30f3\\u30d0\\u30fc\\u30f3 (Onvern)",
+ "Kategorie": "Schallwellen",
+ "Link": "http://pokewiki.de/UHaFnir",
+ "National-Dex": "#715",
+ "Typ": [
+ "Flug",
+ "Drache"
+ ]
+ },
+ "716": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/64/Sugimori_716.png"
+ ]
+ ],
+ "Deutsch": "Xerneas",
+ "Englisch": "Xerneas",
+ "Farbe": "Blau",
+ "Französisch": "Xerneas",
+ "Gewicht": "215,0 kg",
+ "Größe": "3,0 m",
+ "Japanisch": "\\u30bc\\u30eb\\u30cd\\u30a2\\u30b9 (Xerneas)",
+ "Kategorie": "Leben",
+ "Link": "http://pokewiki.de/Xerneas",
+ "National-Dex": "#716",
+ "Typ": "Fee"
+ },
+ "717": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ec/Sugimori_717.png"
+ ]
+ ],
+ "Deutsch": "Yveltal",
+ "Englisch": "Yveltal",
+ "Farbe": "Rot",
+ "Französisch": "Yveltal",
+ "Gewicht": "203,0 kg",
+ "Größe": "5,8 m",
+ "Japanisch": "\\u30a4\\u30d9\\u30eb\\u30bf\\u30eb (Yveltal)",
+ "Kategorie": "Zerst\\u00f6rung",
+ "Link": "http://pokewiki.de/Yveltal",
+ "National-Dex": "#717",
+ "Typ": [
+ "Unlicht",
+ "Flug"
+ ]
+ },
+ "718": {
+ "Bilder": [
+ [
+ "50%-Zygarde",
+ "http://www.pokewiki.de/images/b/b6/Sugimori_718.png"
+ ],
+ [
+ "10%-Zygarde",
+ "http://www.pokewiki.de/images/6/6f/Sugimori_718a.png"
+ ],
+ [
+ "Optimum-Zygarde",
+ "http://www.pokewiki.de/images/e/e7/Sugimori_718b.png"
+ ],
+ [
+ "Kernform",
+ "http://www.pokewiki.de/images/f/ff/Sugimori_718c.png"
+ ],
+ [
+ "Zellform",
+ "http://www.pokewiki.de/images/c/c3/Sugimori_718d.png"
+ ]
+ ],
+ "Deutsch": "Zygarde",
+ "Englisch": "Zygarde",
+ "Farbe": [
+ "Gr\\u00fcn",
+ "Schwarz (10% & Optimum)"
+ ],
+ "Französisch": "Zygarde",
+ "Gewicht": [
+ "305,0 kg (50%-Zygarde)",
+ "33,5 kg (10%-Zygarde)",
+ "610,0 kg (Optimum-Zygarde)"
+ ],
+ "Größe": [
+ "5,0 m (50%-Zygarde)",
+ "1,2 m (10%-Zygarde)",
+ "4,5 m (Optimum-Zygarde)"
+ ],
+ "Japanisch": "\\u30b8\\u30ac\\u30eb\\u30c7 (Zygarde)",
+ "Kategorie": "Ordnung",
+ "Link": "http://pokewiki.de/Zygarde",
+ "National-Dex": "#718",
+ "Typ": [
+ "Drache",
+ "Boden"
+ ]
+ },
+ "719": {
+ "Bilder": [
+ [
+ "Diancie",
+ "http://www.pokewiki.de/images/7/7f/Sugimori_719.png"
+ ],
+ [
+ "Mega-Diancie",
+ "http://www.pokewiki.de/images/2/28/Sugimori_719m1.png"
+ ]
+ ],
+ "Deutsch": "Diancie",
+ "Englisch": "Diancie",
+ "Farbe": "Rosa",
+ "Französisch": "Diancie",
+ "Gewicht": [
+ "8,8 kg",
+ "27,8 kg (Mega)"
+ ],
+ "Größe": [
+ "0,7 m",
+ "1,1 m (Mega)"
+ ],
+ "Japanisch": "\\u30c7\\u30a3\\u30a2\\u30f3\\u30b7\\u30fc (Diancie)",
+ "Kategorie": "Edelstein",
+ "Link": "http://pokewiki.de/Diancie",
+ "National-Dex": "#719",
+ "Typ": [
+ "Gestein",
+ "Fee"
+ ]
+ },
+ "720": {
+ "Bilder": [
+ [
+ "Gebannte Form",
+ "http://www.pokewiki.de/images/5/50/Sugimori_720.png"
+ ],
+ [
+ "Entfesselte Form",
+ "http://www.pokewiki.de/images/f/f0/Sugimori_720a.png"
+ ]
+ ],
+ "Deutsch": "Hoopa",
+ "Englisch": "Hoopa",
+ "Farbe": "Violett",
+ "Französisch": "Hoopa",
+ "Gewicht": [
+ "9,0 kg (Gebannte Form)",
+ "490,0 kg (Entfesselte Form)"
+ ],
+ "Größe": [
+ "0,5 m (Gebannte Form)",
+ "6,5 m (Entfesselte Form)"
+ ],
+ "Japanisch": "\\u30d5\\u30fc\\u30d1 (Hoopa)",
+ "Kategorie": [
+ "Schabernack (Gebannte Form)",
+ "Plagegeist (Entfesselte Form)"
+ ],
+ "Link": "http://pokewiki.de/Hoopa",
+ "National-Dex": "#720",
+ "Typ": [
+ "Psycho",
+ "Geist",
+ "Psycho",
+ "Unlicht",
+ "(Entfesselt)"
+ ]
+ },
+ "721": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/f6/Sugimori_721.png"
+ ]
+ ],
+ "Deutsch": "Volcanion",
+ "Englisch": "Volcanion",
+ "Farbe": "Braun",
+ "Französisch": "Volcanion",
+ "Gewicht": "195,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30dc\\u30eb\\u30b1\\u30cb\\u30aa\\u30f3 (Volcanion)",
+ "Kategorie": "Dampf",
+ "Link": "http://pokewiki.de/Volcanion",
+ "National-Dex": "#721",
+ "Typ": [
+ "Feuer",
+ "Wasser"
+ ]
+ },
+ "722": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/29/Sugimori_722.png"
+ ]
+ ],
+ "Deutsch": "Bauz",
+ "Englisch": "Rowlet",
+ "Farbe": "Braun",
+ "Französisch": "Brindibou",
+ "Gewicht": "1,5 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30e2\\u30af\\u30ed\\u30fc (Mokuroh)",
+ "Kategorie": "Laubfl\\u00fcgel",
+ "Link": "http://pokewiki.de/Bauz",
+ "National-Dex": "#722",
+ "Typ": [
+ "Pflanze",
+ "Flug"
+ ]
+ },
+ "723": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/48/Sugimori_723.png"
+ ]
+ ],
+ "Deutsch": "Arboretoss",
+ "Englisch": "Dartrix",
+ "Farbe": "Braun",
+ "Französisch": "Effl\\u00e8che",
+ "Gewicht": "16,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30d5\\u30af\\u30b9\\u30ed\\u30fc (Fukuthrow)",
+ "Kategorie": "Fl\\u00fcgelklinge",
+ "Link": "http://pokewiki.de/Arboretoss",
+ "National-Dex": "#723",
+ "Typ": [
+ "Pflanze",
+ "Flug"
+ ]
+ },
+ "724": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b0/Sugimori_724.png"
+ ]
+ ],
+ "Deutsch": "Silvarro",
+ "Englisch": "Decidueye",
+ "Farbe": "Braun",
+ "Französisch": "Arch\\u00e9duc",
+ "Gewicht": "36,6 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30b8\\u30e5\\u30ca\\u30a4\\u30d1\\u30fc (Junaiper)",
+ "Kategorie": "Pfeilfl\\u00fcgel",
+ "Link": "http://pokewiki.de/Silvarro",
+ "National-Dex": "#724",
+ "Typ": [
+ "Pflanze",
+ "Geist"
+ ]
+ },
+ "725": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b5/Sugimori_725.png"
+ ]
+ ],
+ "Deutsch": "Flamiau",
+ "Englisch": "Litten",
+ "Farbe": "Rot",
+ "Französisch": "Flamiaou",
+ "Gewicht": "4,3 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30cb\\u30e3\\u30d3\\u30fc (Nyabby)",
+ "Kategorie": "Feuerkatze",
+ "Link": "http://pokewiki.de/Flamiau",
+ "National-Dex": "#725",
+ "Typ": "Feuer"
+ },
+ "726": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/ce/Sugimori_726.png"
+ ]
+ ],
+ "Deutsch": "Miezunder",
+ "Englisch": "Torracat",
+ "Farbe": "Rot",
+ "Französisch": "Matoufeu",
+ "Gewicht": "25,0 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30cb\\u30e3\\u30d2\\u30fc\\u30c8 (Nyaheat)",
+ "Kategorie": "Feuerkatze",
+ "Link": "http://pokewiki.de/Miezunder",
+ "National-Dex": "#726",
+ "Typ": "Feuer"
+ },
+ "727": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/30/Sugimori_727.png"
+ ]
+ ],
+ "Deutsch": "Fuegro",
+ "Englisch": "Incineroar",
+ "Farbe": "Rot",
+ "Französisch": "F\\u00e9linferno",
+ "Gewicht": "83,0 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30ac\\u30aa\\u30ac\\u30a8\\u30f3 (Gaogaen)",
+ "Kategorie": "Fiesling",
+ "Link": "http://pokewiki.de/Fuegro",
+ "National-Dex": "#727",
+ "Typ": [
+ "Feuer",
+ "Unlicht"
+ ]
+ },
+ "728": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/94/Sugimori_728.png"
+ ]
+ ],
+ "Deutsch": "Robball",
+ "Englisch": "Popplio",
+ "Farbe": "Blau",
+ "Französisch": "Otaquin",
+ "Gewicht": "7,5 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30a2\\u30b7\\u30de\\u30ea (Ashimari)",
+ "Kategorie": "Seehund",
+ "Link": "http://pokewiki.de/Robball",
+ "National-Dex": "#728",
+ "Typ": "Wasser"
+ },
+ "729": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/1e/Sugimori_729.png"
+ ]
+ ],
+ "Deutsch": "Marikeck",
+ "Englisch": "Brionne",
+ "Farbe": "Blau",
+ "Französisch": "Otarlette",
+ "Gewicht": "17,5 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30aa\\u30b7\\u30e3\\u30de\\u30ea (Osyamari)",
+ "Kategorie": "Popsternchen",
+ "Link": "http://pokewiki.de/Marikeck",
+ "National-Dex": "#729",
+ "Typ": "Wasser"
+ },
+ "730": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ad/Sugimori_730.png"
+ ]
+ ],
+ "Deutsch": "Primarene",
+ "Englisch": "Primarina",
+ "Farbe": "Blau",
+ "Französisch": "Oratoria",
+ "Gewicht": "44,0 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30a2\\u30b7\\u30ec\\u30fc\\u30cc (Ashirene)",
+ "Kategorie": "Solist",
+ "Link": "http://pokewiki.de/Primarene",
+ "National-Dex": "#730",
+ "Typ": [
+ "Wasser",
+ "Fee"
+ ]
+ },
+ "731": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/be/Sugimori_731.png"
+ ]
+ ],
+ "Deutsch": "Peppeck",
+ "Englisch": "Pikipek",
+ "Farbe": "Schwarz",
+ "Französisch": "Picassaut",
+ "Gewicht": "1,2 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30c4\\u30c4\\u30b1\\u30e9 (Tsutsukera)",
+ "Kategorie": "Specht",
+ "Link": "http://pokewiki.de/Peppeck",
+ "National-Dex": "#731",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "732": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/96/Sugimori_732.png"
+ ]
+ ],
+ "Deutsch": "Trompeck",
+ "Englisch": "Trumbeak",
+ "Farbe": "Schwarz",
+ "Französisch": "Piclairon",
+ "Gewicht": "14,8 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30b1\\u30e9\\u30e9\\u30c3\\u30d1 (Kerarappa)",
+ "Kategorie": "Trompete",
+ "Link": "http://pokewiki.de/Trompeck",
+ "National-Dex": "#732",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "733": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8d/Sugimori_733.png"
+ ]
+ ],
+ "Deutsch": "Tukanon",
+ "Englisch": "Toucannon",
+ "Farbe": "Schwarz",
+ "Französisch": "Bazoucan",
+ "Gewicht": "26,0 kg",
+ "Größe": "1,1 m",
+ "Japanisch": "\\u30c9\\u30c7\\u30ab\\u30d0\\u30b7 (Dodekabashi)",
+ "Kategorie": "Kanone",
+ "Link": "http://pokewiki.de/Tukanon",
+ "National-Dex": "#733",
+ "Typ": [
+ "Normal",
+ "Flug"
+ ]
+ },
+ "734": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a1/Sugimori_734.png"
+ ]
+ ],
+ "Deutsch": "Mangunior",
+ "Englisch": "Yungoos",
+ "Farbe": "Braun",
+ "Französisch": "Manglouton",
+ "Gewicht": "6,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30e4\\u30f3\\u30b0\\u30fc\\u30b9 (Yang\\u016bsu)",
+ "Kategorie": "Patrouille",
+ "Link": "http://pokewiki.de/Mangunior",
+ "National-Dex": "#734",
+ "Typ": "Normal"
+ },
+ "735": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/d4/Sugimori_735.png"
+ ]
+ ],
+ "Deutsch": "Manguspektor",
+ "Englisch": "Gumshoos",
+ "Farbe": "Braun",
+ "Französisch": "Argouste",
+ "Gewicht": "14,2 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30c7\\u30ab\\u30b0\\u30fc\\u30b9 (Dekag\\u016bsu)",
+ "Kategorie": "Beschattung",
+ "Link": "http://pokewiki.de/Manguspektor",
+ "National-Dex": "#735",
+ "Typ": "Normal"
+ },
+ "736": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a8/Sugimori_736.png"
+ ]
+ ],
+ "Deutsch": "Mabula",
+ "Englisch": "Grubbin",
+ "Farbe": "Grau",
+ "Französisch": "Larvibule",
+ "Gewicht": "4,4 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30a2\\u30b4\\u30b8\\u30e0\\u30b7 (Agojimushi)",
+ "Kategorie": "Larve",
+ "Link": "http://pokewiki.de/Mabula",
+ "National-Dex": "#736",
+ "Typ": "K\\u00e4fer"
+ },
+ "737": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/57/Sugimori_737.png"
+ ]
+ ],
+ "Deutsch": "Akkup",
+ "Englisch": "Charjabug",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Chrysapile",
+ "Gewicht": "10,5 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30c7\\u30f3\\u30c2\\u30e0\\u30b7 (Denjimushi)",
+ "Kategorie": "Batterie",
+ "Link": "http://pokewiki.de/Akkup",
+ "National-Dex": "#737",
+ "Typ": [
+ "K\\u00e4fer",
+ "Elektro"
+ ]
+ },
+ "738": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e7/Sugimori_738.png"
+ ]
+ ],
+ "Deutsch": "Donarion",
+ "Englisch": "Vikavolt",
+ "Farbe": "Blau",
+ "Französisch": "Lucanon",
+ "Gewicht": "45,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30af\\u30ef\\u30ac\\u30ce\\u30f3 (Kuwaganon)",
+ "Kategorie": "Kneifk\\u00e4fer",
+ "Link": "http://pokewiki.de/Donarion",
+ "National-Dex": "#738",
+ "Typ": [
+ "K\\u00e4fer",
+ "Elektro"
+ ]
+ },
+ "739": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/22/Sugimori_739.png"
+ ]
+ ],
+ "Deutsch": "Krabbox",
+ "Englisch": "Crabrawler",
+ "Farbe": "Violett",
+ "Französisch": "Crabagarre",
+ "Gewicht": "7,0 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30de\\u30b1\\u30f3\\u30ab\\u30cb (Makenkani)",
+ "Kategorie": "Boxkampf",
+ "Link": "http://pokewiki.de/Krabbox",
+ "National-Dex": "#739",
+ "Typ": "Kampf"
+ },
+ "740": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3c/Sugimori_740.png"
+ ]
+ ],
+ "Deutsch": "Krawell",
+ "Englisch": "Crabominable",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Crabominable",
+ "Gewicht": "180,0 kg",
+ "Größe": "1,7 m",
+ "Japanisch": "\\u30b1\\u30b1\\u30f3\\u30ab\\u30cb (Kekenkani)",
+ "Kategorie": "Fellkrabbe",
+ "Link": "http://pokewiki.de/Krawell",
+ "National-Dex": "#740",
+ "Typ": [
+ "Kampf",
+ "Eis"
+ ]
+ },
+ "741": {
+ "Bilder": [
+ [
+ "Flamenco-Stil",
+ "http://www.pokewiki.de/images/3/37/Sugimori_741.png"
+ ],
+ [
+ "Andere Formen",
+ "http://www.pokewiki.de/images/2/2b/Sugimori_741a.png"
+ ],
+ [
+ "Andere Formen",
+ "http://www.pokewiki.de/images/b/b2/Sugimori_741b.png"
+ ],
+ [
+ "Andere Formen",
+ "http://www.pokewiki.de/images/4/4e/Sugimori_741c.png"
+ ]
+ ],
+ "Deutsch": "Choreogel",
+ "Englisch": "Oricorio",
+ "Farbe": [
+ "Rot",
+ "Gelb (Cheerleading-Stil)",
+ "Rosa (Hula-Stil)",
+ "Violett (Buyo-Stil)"
+ ],
+ "Französisch": "Plumeline",
+ "Gewicht": "3,4 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30aa\\u30c9\\u30ea\\u30c9\\u30ea (Odoridori)",
+ "Kategorie": "Tanz",
+ "Link": "http://pokewiki.de/Choreogel",
+ "National-Dex": "#741",
+ "Typ": [
+ "Feuer",
+ "Flug (Flamenco-Stil)",
+ "Elektro",
+ "Flug (Cheerleading-Stil)",
+ "Psycho",
+ "Flug (Hula-Stil)",
+ "Geist",
+ "Flug (Buyo-Stil)"
+ ]
+ },
+ "742": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e5/Sugimori_742.png"
+ ]
+ ],
+ "Deutsch": "Wommel",
+ "Englisch": "Cutiefly",
+ "Farbe": "Gelb",
+ "Französisch": "Bombydou",
+ "Gewicht": "0,2 kg",
+ "Größe": "0,1 m",
+ "Japanisch": "\\u30a2\\u30d6\\u30ea\\u30fc (Abuly)",
+ "Kategorie": "Hummelfliege",
+ "Link": "http://pokewiki.de/Wommel",
+ "National-Dex": "#742",
+ "Typ": [
+ "K\\u00e4fer",
+ "Fee"
+ ]
+ },
+ "743": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3c/Sugimori_743.png"
+ ]
+ ],
+ "Deutsch": "Bandelby",
+ "Englisch": "Ribombee",
+ "Farbe": "Gelb",
+ "Französisch": "Rubombelle",
+ "Gewicht": "0,5 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30a2\\u30d6\\u30ea\\u30dc\\u30f3 (Aburibon)",
+ "Kategorie": "Hummelfliege",
+ "Link": "http://pokewiki.de/Bandelby",
+ "National-Dex": "#743",
+ "Typ": [
+ "K\\u00e4fer",
+ "Fee"
+ ]
+ },
+ "744": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/e0/Sugimori_744.png"
+ ]
+ ],
+ "Deutsch": "Wuffels",
+ "Englisch": "Rockruff",
+ "Farbe": "Braun",
+ "Französisch": "Rocabot",
+ "Gewicht": "9,2 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30a4\\u30ef\\u30f3\\u30b3 (Iwanko)",
+ "Kategorie": "Hund",
+ "Link": "http://pokewiki.de/Wuffels",
+ "National-Dex": "#744",
+ "Typ": "Gestein"
+ },
+ "745": {
+ "Bilder": [
+ [
+ "Tagform",
+ "http://www.pokewiki.de/images/c/cd/Sugimori_745.png"
+ ],
+ [
+ "Nachtform",
+ "http://www.pokewiki.de/images/b/b7/Sugimori_745a.png"
+ ],
+ [
+ "Zwielichtform",
+ "http://www.pokewiki.de/images/b/b5/Sugimori_745b.png"
+ ]
+ ],
+ "Deutsch": "Wolwerock",
+ "Englisch": "Lycanroc",
+ "Farbe": [
+ "Braun",
+ "Rot (Nachtform)"
+ ],
+ "Französisch": "Lougaroc",
+ "Gewicht": "25,0 kg",
+ "Größe": [
+ "0,8 m (Tag- und Zwielichtform)",
+ "1,1 m (Nachtform)"
+ ],
+ "Japanisch": "\\u30eb\\u30ac\\u30eb\\u30ac\\u30f3 (Lugarugan)",
+ "Kategorie": "Wolf",
+ "Link": "http://pokewiki.de/Wolwerock",
+ "National-Dex": "#745",
+ "Typ": "Gestein"
+ },
+ "746": {
+ "Bilder": [
+ [
+ "Einzelform",
+ "http://www.pokewiki.de/images/b/bd/Sugimori_746.png"
+ ],
+ [
+ "Schwarmform",
+ "http://www.pokewiki.de/images/d/de/Sugimori_746a.png"
+ ]
+ ],
+ "Deutsch": "Lusardin",
+ "Englisch": "Wishiwashi",
+ "Farbe": "Blau",
+ "Französisch": "Froussardine",
+ "Gewicht": [
+ "0,3 kg (Einzel)",
+ "78,6 kg (Schwarm)"
+ ],
+ "Größe": [
+ "0,2 m (Einzel)",
+ "8,2 m (Schwarm)"
+ ],
+ "Japanisch": "\\u30e8\\u30ef\\u30b7 (Yowashi)",
+ "Kategorie": "Kleinfisch",
+ "Link": "http://pokewiki.de/Lusardin",
+ "National-Dex": "#746",
+ "Typ": "Wasser"
+ },
+ "747": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c0/Sugimori_747.png"
+ ]
+ ],
+ "Deutsch": "Garstella",
+ "Englisch": "Mareanie",
+ "Farbe": "Blau",
+ "Französisch": "Vorast\\u00e9rie",
+ "Gewicht": "8,0 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30d2\\u30c9\\u30a4\\u30c7 (Hidoide)",
+ "Kategorie": "Qu\\u00e4l-Stern",
+ "Link": "http://pokewiki.de/Garstella",
+ "National-Dex": "#747",
+ "Typ": [
+ "Gift",
+ "Wasser"
+ ]
+ },
+ "748": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/26/Sugimori_748.png"
+ ]
+ ],
+ "Deutsch": "Aggrostella",
+ "Englisch": "Toxapex",
+ "Farbe": "Blau",
+ "Französisch": "Pr\\u00e9dast\\u00e9rie",
+ "Gewicht": "14,5 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30c9\\u30d2\\u30c9\\u30a4\\u30c7 (Dohidoide)",
+ "Kategorie": "Qu\\u00e4l-Stern",
+ "Link": "http://pokewiki.de/Aggrostella",
+ "National-Dex": "#748",
+ "Typ": [
+ "Gift",
+ "Wasser"
+ ]
+ },
+ "749": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/bd/Sugimori_749.png"
+ ]
+ ],
+ "Deutsch": "Pampuli",
+ "Englisch": "Mudbray",
+ "Farbe": "Braun",
+ "Französisch": "Tiboudet",
+ "Gewicht": "110,0 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30c9\\u30ed\\u30d0\\u30f3\\u30b3 (Dorobanko)",
+ "Kategorie": "Packesel",
+ "Link": "http://pokewiki.de/Pampuli",
+ "National-Dex": "#749",
+ "Typ": "Boden"
+ },
+ "750": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7b/Sugimori_750.png"
+ ]
+ ],
+ "Deutsch": "Pampross",
+ "Englisch": "Mudsdale",
+ "Farbe": "Braun",
+ "Französisch": "Bourrinos",
+ "Gewicht": "920,0 kg",
+ "Größe": "2,5 m",
+ "Japanisch": "\\u30d0\\u30f3\\u30d0\\u30c9\\u30ed (Banbadoro)",
+ "Kategorie": "Zugpferd",
+ "Link": "http://pokewiki.de/Pampross",
+ "National-Dex": "#750",
+ "Typ": "Boden"
+ },
+ "751": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a1/Sugimori_751.png"
+ ]
+ ],
+ "Deutsch": "Araqua",
+ "Englisch": "Dewpider",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Araqua",
+ "Gewicht": "4,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30b7\\u30ba\\u30af\\u30e2 (Shizukumo)",
+ "Kategorie": "Wasserblase",
+ "Link": "http://pokewiki.de/Araqua",
+ "National-Dex": "#751",
+ "Typ": [
+ "Wasser",
+ "K\\u00e4fer"
+ ]
+ },
+ "752": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2e/Sugimori_752.png"
+ ]
+ ],
+ "Deutsch": "Aranestro",
+ "Englisch": "Araquanid",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Tarenbulle",
+ "Gewicht": "82,0 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30aa\\u30cb\\u30b7\\u30ba\\u30af\\u30e2 (Onishikuzumo)",
+ "Kategorie": "Wasserblase",
+ "Link": "http://pokewiki.de/Aranestro",
+ "National-Dex": "#752",
+ "Typ": [
+ "Wasser",
+ "K\\u00e4fer"
+ ]
+ },
+ "753": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ee/Sugimori_753.png"
+ ]
+ ],
+ "Deutsch": "Imantis",
+ "Englisch": "Fomantis",
+ "Farbe": "Rosa",
+ "Französisch": "Mimantis",
+ "Gewicht": "1,5 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30ab\\u30ea\\u30ad\\u30ea (Karikiri)",
+ "Kategorie": "Grassichel",
+ "Link": "http://pokewiki.de/Imantis",
+ "National-Dex": "#753",
+ "Typ": "Pflanze"
+ },
+ "754": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c4/Sugimori_754.png"
+ ]
+ ],
+ "Deutsch": "Mantidea",
+ "Englisch": "Lurantis",
+ "Farbe": "Rosa",
+ "Französisch": "Floramantis",
+ "Gewicht": "18,5 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30e9\\u30e9\\u30f3\\u30c6\\u30b9 (Rarantesu)",
+ "Kategorie": "Blumensichel",
+ "Link": "http://pokewiki.de/Mantidea",
+ "National-Dex": "#754",
+ "Typ": "Pflanze"
+ },
+ "755": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b3/Sugimori_755.png"
+ ]
+ ],
+ "Deutsch": "Bubungus",
+ "Englisch": "Morelull",
+ "Farbe": "Violett",
+ "Französisch": "Spododo",
+ "Gewicht": "1,5 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30cd\\u30de\\u30b7\\u30e5 (Nemashu)",
+ "Kategorie": "Lumineszenz",
+ "Link": "http://pokewiki.de/Bubungus",
+ "National-Dex": "#755",
+ "Typ": [
+ "Pflanze",
+ "Fee"
+ ]
+ },
+ "756": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3f/Sugimori_756.png"
+ ]
+ ],
+ "Deutsch": "Lamellux",
+ "Englisch": "Shiinotic",
+ "Farbe": "Violett",
+ "Französisch": "Lampignon",
+ "Gewicht": "11,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30de\\u30b7\\u30a7\\u30fc\\u30c9 (Mash\\u0113do)",
+ "Kategorie": "Lumineszenz",
+ "Link": "http://pokewiki.de/Lamellux",
+ "National-Dex": "#756",
+ "Typ": [
+ "Pflanze",
+ "Fee"
+ ]
+ },
+ "757": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/9e/Sugimori_757.png"
+ ]
+ ],
+ "Deutsch": "Molunk",
+ "Englisch": "Salandit",
+ "Farbe": "Schwarz",
+ "Französisch": "Tritox",
+ "Gewicht": "4,8 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30e4\\u30c8\\u30a6\\u30e2\\u30ea (Yat\\u014dmori)",
+ "Kategorie": "Giftechse",
+ "Link": "http://pokewiki.de/Molunk",
+ "National-Dex": "#757",
+ "Typ": [
+ "Gift",
+ "Feuer"
+ ]
+ },
+ "758": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/51/Sugimori_758.png"
+ ]
+ ],
+ "Deutsch": "Amfira",
+ "Englisch": "Salazzle",
+ "Farbe": "Schwarz",
+ "Französisch": "Malamandre",
+ "Gewicht": "22,2 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30a8\\u30f3\\u30cb\\u30e5\\u30fc\\u30c8 (Enny\\u016bto)",
+ "Kategorie": "Giftechse",
+ "Link": "http://pokewiki.de/Amfira",
+ "National-Dex": "#758",
+ "Typ": [
+ "Gift",
+ "Feuer"
+ ]
+ },
+ "759": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6e/Sugimori_759.png"
+ ]
+ ],
+ "Deutsch": "Velursi",
+ "Englisch": "Stufful",
+ "Farbe": "Rosa",
+ "Französisch": "Nounourson",
+ "Gewicht": "6,8 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30cc\\u30a4\\u30b3\\u30b0\\u30de (Nuikoguma)",
+ "Kategorie": "Wildfang",
+ "Link": "http://pokewiki.de/Velursi",
+ "National-Dex": "#759",
+ "Typ": [
+ "Normal",
+ "Kampf"
+ ]
+ },
+ "760": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4b/Sugimori_760.png"
+ ]
+ ],
+ "Deutsch": "Kosturso",
+ "Englisch": "Bewear",
+ "Farbe": "Rosa",
+ "Französisch": "Chelours",
+ "Gewicht": "135,0 kg",
+ "Größe": "2,1 m",
+ "Japanisch": "\\u30ad\\u30c6\\u30eb\\u30b0\\u30de (Kiteruguma)",
+ "Kategorie": "Kraftarme",
+ "Link": "http://pokewiki.de/Kosturso",
+ "National-Dex": "#760",
+ "Typ": [
+ "Normal",
+ "Kampf"
+ ]
+ },
+ "761": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/c2/Sugimori_761.png"
+ ]
+ ],
+ "Deutsch": "Frubberl",
+ "Englisch": "Bounsweet",
+ "Farbe": "Violett",
+ "Französisch": "Croquine",
+ "Gewicht": "3,2 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30a2\\u30de\\u30ab\\u30b8 (Amakaji)",
+ "Kategorie": "Obst",
+ "Link": "http://pokewiki.de/Frubberl",
+ "National-Dex": "#761",
+ "Typ": "Pflanze"
+ },
+ "762": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/38/Sugimori_762.png"
+ ]
+ ],
+ "Deutsch": "Frubaila",
+ "Englisch": "Steenee",
+ "Farbe": "Violett",
+ "Französisch": "Candine",
+ "Gewicht": "8,2 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30a2\\u30de\\u30de\\u30a4\\u30b3 (Amamaiko)",
+ "Kategorie": "Obst",
+ "Link": "http://pokewiki.de/Frubaila",
+ "National-Dex": "#762",
+ "Typ": "Pflanze"
+ },
+ "763": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/49/Sugimori_763.png"
+ ]
+ ],
+ "Deutsch": "Fruyal",
+ "Englisch": "Tsareena",
+ "Farbe": "Violett",
+ "Französisch": "Sucreine",
+ "Gewicht": "21,4 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30a2\\u30de\\u30fc\\u30b8\\u30e7 (Amajo)",
+ "Kategorie": "Obst",
+ "Link": "http://pokewiki.de/Fruyal",
+ "National-Dex": "#763",
+ "Typ": "Pflanze"
+ },
+ "764": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/2a/Sugimori_764.png"
+ ]
+ ],
+ "Deutsch": "Curelei",
+ "Englisch": "Comfey",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Gu\\u00e9rilande",
+ "Gewicht": "0,3 kg",
+ "Größe": "0,1 m",
+ "Japanisch": "\\u30ad\\u30e5\\u30ef\\u30ef\\u30fc (Cuwawa)",
+ "Kategorie": "Blumenkranz",
+ "Link": "http://pokewiki.de/Curelei",
+ "National-Dex": "#764",
+ "Typ": "Fee"
+ },
+ "765": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/0c/Sugimori_765.png"
+ ]
+ ],
+ "Deutsch": "Kommandutan",
+ "Englisch": "Oranguru",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Gouroutan",
+ "Gewicht": "76,0 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30e4\\u30ec\\u30e6\\u30fc\\u30bf\\u30f3 (Yarey\\u016btan)",
+ "Kategorie": "Weisheit",
+ "Link": "http://pokewiki.de/Kommandutan",
+ "National-Dex": "#765",
+ "Typ": [
+ "Normal",
+ "Psycho"
+ ]
+ },
+ "766": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/1/11/Sugimori_766.png"
+ ]
+ ],
+ "Deutsch": "Quartermak",
+ "Englisch": "Passimian",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Quartermac",
+ "Gewicht": "82,8 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30ca\\u30b2\\u30c4\\u30b1\\u30b5\\u30eb (Nagetsukesaru)",
+ "Kategorie": "Teamwork",
+ "Link": "http://pokewiki.de/Quartermak",
+ "National-Dex": "#766",
+ "Typ": "Kampf"
+ },
+ "767": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/a3/Sugimori_767.png"
+ ]
+ ],
+ "Deutsch": "Rei\\u00dflaus",
+ "Englisch": "Wimpod",
+ "Farbe": "Grau",
+ "Französisch": "Sovkipou",
+ "Gewicht": "12,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30b3\\u30bd\\u30af\\u30e0\\u30b7 (Kosokumushi)",
+ "Kategorie": "Laufschritt",
+ "Link": "http://pokewiki.de/Rei%C3%9Flaus",
+ "National-Dex": "#767",
+ "Typ": [
+ "K\\u00e4fer",
+ "Wasser"
+ ]
+ },
+ "768": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/60/Sugimori_768.png"
+ ]
+ ],
+ "Deutsch": "Tectass",
+ "Englisch": "Golisopod",
+ "Farbe": "Grau",
+ "Französisch": "Sarmura\\u00ef",
+ "Gewicht": "108,0 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30b0\\u30bd\\u30af\\u30e0\\u30b7\\u30e3 (Gusokumusha)",
+ "Kategorie": "R\\u00fcstung",
+ "Link": "http://pokewiki.de/Tectass",
+ "National-Dex": "#768",
+ "Typ": [
+ "K\\u00e4fer",
+ "Wasser"
+ ]
+ },
+ "769": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fa/Sugimori_769.png"
+ ]
+ ],
+ "Deutsch": "Sankabuh",
+ "Englisch": "Sandygast",
+ "Farbe": "Braun",
+ "Französisch": "Bacabouh",
+ "Gewicht": "70,0 kg",
+ "Größe": "0,5 m",
+ "Japanisch": "\\u30b9\\u30ca\\u30d0\\u30a2 (Sunabaa)",
+ "Kategorie": "Sandhaufen",
+ "Link": "http://pokewiki.de/Sankabuh",
+ "National-Dex": "#769",
+ "Typ": [
+ "Geist",
+ "Boden"
+ ]
+ },
+ "770": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/cb/Sugimori_770.png"
+ ]
+ ],
+ "Deutsch": "Colossand",
+ "Englisch": "Palossand",
+ "Farbe": "Braun",
+ "Französisch": "Tr\\u00e9passable",
+ "Gewicht": "250,0 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30b7\\u30ed\\u30c7\\u30b9\\u30ca (Shirodesuna)",
+ "Kategorie": "Sandschloss",
+ "Link": "http://pokewiki.de/Colossand",
+ "National-Dex": "#770",
+ "Typ": [
+ "Geist",
+ "Boden"
+ ]
+ },
+ "771": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/96/Sugimori_771.png"
+ ]
+ ],
+ "Deutsch": "Gufa",
+ "Englisch": "Pyukumuku",
+ "Farbe": "Schwarz",
+ "Französisch": "Concombaffe",
+ "Gewicht": "1,2 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30ca\\u30de\\u30b3\\u30d6\\u30b7 (Namakobushi)",
+ "Kategorie": "Seegurke",
+ "Link": "http://pokewiki.de/Gufa",
+ "National-Dex": "#771",
+ "Typ": "Wasser"
+ },
+ "772": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/ae/Sugimori_772.png"
+ ]
+ ],
+ "Deutsch": "Typ:Null",
+ "Englisch": "Type: Null",
+ "Farbe": "Grau",
+ "Französisch": "Type:0",
+ "Gewicht": "120,5 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30bf\\u30a4\\u30d7\\uff1a\\u30cc\\u30eb (Type:Null)",
+ "Kategorie": "Modifikation",
+ "Link": "http://pokewiki.de/Typ:Null",
+ "National-Dex": "#772",
+ "Typ": "Normal"
+ },
+ "773": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/06/Sugimori_773.png"
+ ]
+ ],
+ "Deutsch": "Amigento",
+ "Englisch": "Silvally",
+ "Farbe": "Grau",
+ "Französisch": "Silvalli\\u00e9",
+ "Gewicht": "100,5 kg",
+ "Größe": "2,3 m",
+ "Japanisch": "\\u30b7\\u30eb\\u30f4\\u30a1\\u30c7\\u30a3 (Silvady)",
+ "Kategorie": "Modifikation",
+ "Link": "http://pokewiki.de/Amigento",
+ "National-Dex": "#773",
+ "Typ": "Normal (variabel) (variiert je nach Typ des getragenen Items)"
+ },
+ "774": {
+ "Bilder": [
+ [
+ "Meteorform",
+ "http://www.pokewiki.de/images/f/f9/Sugimori_774.png"
+ ],
+ [
+ "Roter Kern",
+ "http://www.pokewiki.de/images/5/58/Sugimori_774a.png"
+ ]
+ ],
+ "Deutsch": "Meteno",
+ "Englisch": "Minior",
+ "Farbe": [
+ "Braun",
+ "Rot (Roter & Oranger Kern)",
+ "Gelb (Gelber Kern)",
+ "Gr\\u00fcn (Gr\\u00fcner Kern)",
+ "Blau (Hellblauer & Blauer Kern)",
+ "Violett (Violetter Kern)"
+ ],
+ "Französisch": "M\\u00e9t\\u00e9no",
+ "Gewicht": [
+ "40,0 kg (Meteorform)",
+ "0,3 kg (Farbige Kerne)"
+ ],
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30e1\\u30c6\\u30ce (Meteno)",
+ "Kategorie": "Meteor",
+ "Link": "http://pokewiki.de/Meteno",
+ "National-Dex": "#774",
+ "Typ": [
+ "Gestein",
+ "Flug"
+ ]
+ },
+ "775": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/c/cd/Sugimori_775.png"
+ ]
+ ],
+ "Deutsch": "Koalelu",
+ "Englisch": "Komala",
+ "Farbe": "Blau",
+ "Französisch": "Dodoala",
+ "Gewicht": "19,9 kg",
+ "Größe": "0,4 m",
+ "Japanisch": "\\u30cd\\u30c3\\u30b3\\u30a2\\u30e9 (Nekkoara)",
+ "Kategorie": "Halbschlaf",
+ "Link": "http://pokewiki.de/Koalelu",
+ "National-Dex": "#775",
+ "Typ": "Normal"
+ },
+ "776": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/6/6f/Sugimori_776.png"
+ ]
+ ],
+ "Deutsch": "Tortunator",
+ "Englisch": "Turtonator",
+ "Farbe": "Rot",
+ "Französisch": "Boumata",
+ "Gewicht": "212,0 kg",
+ "Größe": "2,0 m",
+ "Japanisch": "\\u30d0\\u30af\\u30ac\\u30e1\\u30b9 (Bakugames)",
+ "Kategorie": "Knallkr\\u00f6te",
+ "Link": "http://pokewiki.de/Tortunator",
+ "National-Dex": "#776",
+ "Typ": [
+ "Feuer",
+ "Drache"
+ ]
+ },
+ "777": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/fa/Sugimori_777.png"
+ ]
+ ],
+ "Deutsch": "Togedemaru",
+ "Englisch": "Togedemaru",
+ "Farbe": "Grau",
+ "Französisch": "Togedemaru",
+ "Gewicht": "3,3 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30c8\\u30b2\\u30c7\\u30de\\u30eb (Togedemaru)",
+ "Kategorie": "Einigler",
+ "Link": "http://pokewiki.de/Togedemaru",
+ "National-Dex": "#777",
+ "Typ": [
+ "Elektro",
+ "Stahl"
+ ]
+ },
+ "778": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/31/Sugimori_778.png"
+ ]
+ ],
+ "Deutsch": "Mimigma",
+ "Englisch": "Mimikyu",
+ "Farbe": "Gelb",
+ "Französisch": "Mimiqui",
+ "Gewicht": "0,7 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30df\\u30df\\u30c3\\u30ad\\u30e5 (Mimikkyu)",
+ "Kategorie": "Kost\\u00fcmspuk",
+ "Link": "http://pokewiki.de/Mimigma",
+ "National-Dex": "#778",
+ "Typ": [
+ "Geist",
+ "Fee"
+ ]
+ },
+ "779": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/7/7c/Sugimori_779.png"
+ ]
+ ],
+ "Deutsch": "Knirfish",
+ "Englisch": "Bruxish",
+ "Farbe": "Rosa",
+ "Französisch": "Denticrisse",
+ "Gewicht": "19,0 kg",
+ "Größe": "0,9 m",
+ "Japanisch": "\\u30cf\\u30ae\\u30ae\\u30b7\\u30ea (Hagigishiri)",
+ "Kategorie": "Knirschzahn",
+ "Link": "http://pokewiki.de/Knirfish",
+ "National-Dex": "#779",
+ "Typ": [
+ "Wasser",
+ "Psycho"
+ ]
+ },
+ "780": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/52/Sugimori_780.png"
+ ]
+ ],
+ "Deutsch": "Sen-Long",
+ "Englisch": "Drampa",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Dra\\u00efeul",
+ "Gewicht": "185,0 kg",
+ "Größe": "3,0 m",
+ "Japanisch": "\\u30b8\\u30b8\\u30fc\\u30ed\\u30f3 (Jijiron)",
+ "Kategorie": "Gelassenheit",
+ "Link": "http://pokewiki.de/Sen-Long",
+ "National-Dex": "#780",
+ "Typ": [
+ "Normal",
+ "Drache"
+ ]
+ },
+ "781": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/43/Sugimori_781.png"
+ ]
+ ],
+ "Deutsch": "Moruda",
+ "Englisch": "Dhelmise",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Sinistrail",
+ "Gewicht": "210,0 kg",
+ "Größe": "3,9 m",
+ "Japanisch": "\\u30c0\\u30c0\\u30ea\\u30f3 (Dadarin)",
+ "Kategorie": "Seetang",
+ "Link": "http://pokewiki.de/Moruda",
+ "National-Dex": "#781",
+ "Typ": [
+ "Geist",
+ "Pflanze"
+ ]
+ },
+ "782": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/91/Sugimori_782.png"
+ ]
+ ],
+ "Deutsch": "Miniras",
+ "Englisch": "Jangmo-o",
+ "Farbe": "Grau",
+ "Französisch": "B\\u00e9b\\u00e9caille",
+ "Gewicht": "29,7 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30b8\\u30e3\\u30e9\\u30b3 (Jarako)",
+ "Kategorie": "Schuppentier",
+ "Link": "http://pokewiki.de/Miniras",
+ "National-Dex": "#782",
+ "Typ": "Drache"
+ },
+ "783": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/a/aa/Sugimori_783.png"
+ ]
+ ],
+ "Deutsch": "Mediras",
+ "Englisch": "Hakamo-o",
+ "Farbe": "Grau",
+ "Französisch": "\\u00c9ca\\u00efd",
+ "Gewicht": "47,0 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30b8\\u30e3\\u30e9\\u30f3\\u30b4 (Jarango)",
+ "Kategorie": "Schuppentier",
+ "Link": "http://pokewiki.de/Mediras",
+ "National-Dex": "#783",
+ "Typ": [
+ "Drache",
+ "Kampf"
+ ]
+ },
+ "784": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ec/Sugimori_784.png"
+ ]
+ ],
+ "Deutsch": "Grandiras",
+ "Englisch": "Kommo-o",
+ "Farbe": "Grau",
+ "Französisch": "\\u00c9ka\\u00efser",
+ "Gewicht": "78,2 kg",
+ "Größe": "1,6 m",
+ "Japanisch": "\\u30b8\\u30e3\\u30e9\\u30e9\\u30f3\\u30ac (Jararanga)",
+ "Kategorie": "Schuppentier",
+ "Link": "http://pokewiki.de/Grandiras",
+ "National-Dex": "#784",
+ "Typ": [
+ "Drache",
+ "Kampf"
+ ]
+ },
+ "785": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/80/Sugimori_785.png"
+ ]
+ ],
+ "Deutsch": "Kapu-Riki",
+ "Englisch": "Tapu Koko",
+ "Farbe": "Gelb",
+ "Französisch": "Tokorico",
+ "Gewicht": "20,5 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30ab\\u30d7\\u30fb\\u30b3\\u30b1\\u30b3 (Kapu Kokeko)",
+ "Kategorie": "Schutzpatron",
+ "Link": "http://pokewiki.de/Kapu-Riki",
+ "National-Dex": "#785",
+ "Typ": [
+ "Elektro",
+ "Fee"
+ ]
+ },
+ "786": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/f/ff/Sugimori_786.png"
+ ]
+ ],
+ "Deutsch": "Kapu-Fala",
+ "Englisch": "Tapu Lele",
+ "Farbe": "Rosa",
+ "Französisch": "Tokopiyon",
+ "Gewicht": "18,6 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30ab\\u30d7\\u30fb\\u30c6\\u30c6\\u30d5 (Kapu-Tetefu)",
+ "Kategorie": "Schutzpatron",
+ "Link": "http://pokewiki.de/Kapu-Fala",
+ "National-Dex": "#786",
+ "Typ": [
+ "Psycho",
+ "Fee"
+ ]
+ },
+ "787": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/88/Sugimori_787.png"
+ ]
+ ],
+ "Deutsch": "Kapu-Toro",
+ "Englisch": "Tapu Bulu",
+ "Farbe": "Rot",
+ "Französisch": "Tokotoro",
+ "Gewicht": "45,5 kg",
+ "Größe": "1,9 m",
+ "Japanisch": "\\u30ab\\u30d7\\u30fb\\u30d6\\u30eb\\u30eb (Kapu Bulul)",
+ "Kategorie": "Schutzpatron",
+ "Link": "http://pokewiki.de/Kapu-Toro",
+ "National-Dex": "#787",
+ "Typ": [
+ "Pflanze",
+ "Fee"
+ ]
+ },
+ "788": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/2/27/Sugimori_788.png"
+ ]
+ ],
+ "Deutsch": "Kapu-Kime",
+ "Englisch": "Tapu Fini",
+ "Farbe": "Violett",
+ "Französisch": "Tokopisco",
+ "Gewicht": "21,2 kg",
+ "Größe": "1,3 m",
+ "Japanisch": "\\u30ab\\u30d7\\u30fb\\u30ec\\u30d2\\u30ec (Kapu-Rehire)",
+ "Kategorie": "Schutzpatron",
+ "Link": "http://pokewiki.de/Kapu-Kime",
+ "National-Dex": "#788",
+ "Typ": [
+ "Wasser",
+ "Fee"
+ ]
+ },
+ "789": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/4f/Sugimori_789.png"
+ ]
+ ],
+ "Deutsch": "Cosmog",
+ "Englisch": "Cosmog",
+ "Farbe": "Blau",
+ "Französisch": "Cosmog",
+ "Gewicht": "0,1 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30b3\\u30b9\\u30e2\\u30c3\\u30b0 (Cosmog)",
+ "Kategorie": "Nebula",
+ "Link": "http://pokewiki.de/Cosmog",
+ "National-Dex": "#789",
+ "Typ": "Psycho"
+ },
+ "790": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b9/Sugimori_790.png"
+ ]
+ ],
+ "Deutsch": "Cosmovum",
+ "Englisch": "Cosmoem",
+ "Farbe": "Blau",
+ "Französisch": "Cosmovum",
+ "Gewicht": "999,9 kg",
+ "Größe": "0,1 m",
+ "Japanisch": "\\u30b3\\u30b9\\u30e2\\u30a6\\u30e0 (Cosmovum)",
+ "Kategorie": "Urgestirn",
+ "Link": "http://pokewiki.de/Cosmovum",
+ "National-Dex": "#790",
+ "Typ": "Psycho"
+ },
+ "791": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/dc/Sugimori_791.png"
+ ]
+ ],
+ "Deutsch": "Solgaleo",
+ "Englisch": "Solgaleo",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Solgaleo",
+ "Gewicht": "230,0 kg",
+ "Größe": "3,4 m",
+ "Japanisch": "\\u30bd\\u30eb\\u30ac\\u30ec\\u30aa (Sorugareo)",
+ "Kategorie": "Sonnenkreis",
+ "Link": "http://pokewiki.de/Solgaleo",
+ "National-Dex": "#791",
+ "Typ": [
+ "Psycho",
+ "Stahl"
+ ]
+ },
+ "792": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/3/3e/Sugimori_792.png"
+ ]
+ ],
+ "Deutsch": "Lunala",
+ "Englisch": "Lunala",
+ "Farbe": "Violett",
+ "Französisch": "Lunala",
+ "Gewicht": "120,0 kg",
+ "Größe": "4,0 m",
+ "Japanisch": "\\u30eb\\u30ca\\u30a2\\u30fc\\u30e9 (Lunala)",
+ "Kategorie": "Mondscheibe",
+ "Link": "http://pokewiki.de/Lunala",
+ "National-Dex": "#792",
+ "Typ": [
+ "Psycho",
+ "Geist"
+ ]
+ },
+ "793": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/d/dd/Sugimori_793.png"
+ ]
+ ],
+ "Deutsch": "Anego",
+ "Englisch": "Nihilego",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Z\\u00e9ro\\u00efd",
+ "Gewicht": "55,5 kg",
+ "Größe": "1,2 m",
+ "Japanisch": "\\u30a6\\u30c4\\u30ed\\u30a4\\u30c9 (Utsuroido)",
+ "Kategorie": "Parasit",
+ "Link": "http://pokewiki.de/Anego",
+ "National-Dex": "#793",
+ "Typ": [
+ "Gestein",
+ "Gift"
+ ]
+ },
+ "794": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/85/Sugimori_794.png"
+ ]
+ ],
+ "Deutsch": "Masskito",
+ "Englisch": "Buzzwole",
+ "Farbe": "Rot",
+ "Französisch": "Mouscoto",
+ "Gewicht": "333,6 kg",
+ "Größe": "2,4 m",
+ "Japanisch": "\\u30de\\u30c3\\u30b7\\u30d6\\u30fc\\u30f3 (Massivoon)",
+ "Kategorie": "Ausdehnung",
+ "Link": "http://pokewiki.de/Masskito",
+ "National-Dex": "#794",
+ "Typ": [
+ "K\\u00e4fer",
+ "Kampf"
+ ]
+ },
+ "795": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/b/b9/Sugimori_795.png"
+ ]
+ ],
+ "Deutsch": "Schabelle",
+ "Englisch": "Pheromosa",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Cancrelove",
+ "Gewicht": "25,0 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30d5\\u30a7\\u30ed\\u30fc\\u30c1\\u30a7 (Pheroache)",
+ "Kategorie": "Eleganz",
+ "Link": "http://pokewiki.de/Schabelle",
+ "National-Dex": "#795",
+ "Typ": [
+ "K\\u00e4fer",
+ "Kampf"
+ ]
+ },
+ "796": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/8f/Sugimori_796.png"
+ ]
+ ],
+ "Deutsch": "Voltriant",
+ "Englisch": "Xurkitree",
+ "Farbe": "Schwarz",
+ "Französisch": "C\\u00e2blif\\u00e8re",
+ "Gewicht": "100,0 kg",
+ "Größe": "3,8 m",
+ "Japanisch": "\\u30c7\\u30f3\\u30b8\\u30e5\\u30e2\\u30af (Denjumoku)",
+ "Kategorie": "Illumination",
+ "Link": "http://pokewiki.de/Voltriant",
+ "National-Dex": "#796",
+ "Typ": "Elektro"
+ },
+ "797": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/91/Sugimori_797.png"
+ ]
+ ],
+ "Deutsch": "Kaguron",
+ "Englisch": "Celesteela",
+ "Farbe": "Gr\\u00fcn",
+ "Französisch": "Bamboiselle",
+ "Gewicht": "999,9 kg",
+ "Größe": "9,2 m",
+ "Japanisch": "\\u30c6\\u30c3\\u30ab\\u30b0\\u30e4 (Tekkaguya)",
+ "Kategorie": "Raketenstart",
+ "Link": "http://pokewiki.de/Kaguron",
+ "National-Dex": "#797",
+ "Typ": [
+ "Stahl",
+ "Flug"
+ ]
+ },
+ "798": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/98/Sugimori_798.png"
+ ]
+ ],
+ "Deutsch": "Katagami",
+ "Englisch": "Kartana",
+ "Farbe": "Wei\\u00df",
+ "Französisch": "Katagami",
+ "Gewicht": "0,1 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30ab\\u30df\\u30c4\\u30eb\\u30ae (Kamitsurugi)",
+ "Kategorie": "Schwertkunst",
+ "Link": "http://pokewiki.de/Katagami",
+ "National-Dex": "#798",
+ "Typ": [
+ "Pflanze",
+ "Stahl"
+ ]
+ },
+ "799": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/9/91/Sugimori_799.png"
+ ]
+ ],
+ "Deutsch": "Schlingking",
+ "Englisch": "Guzzlord",
+ "Farbe": "Schwarz",
+ "Französisch": "Engloutyran",
+ "Gewicht": "888,0 kg",
+ "Größe": "5,5 m",
+ "Japanisch": "\\u30a2\\u30af\\u30b8\\u30ad\\u30f3\\u30b0 (Akujikingu)",
+ "Kategorie": "Gaumenfolter",
+ "Link": "http://pokewiki.de/Schlingking",
+ "National-Dex": "#799",
+ "Typ": [
+ "Unlicht",
+ "Drache"
+ ]
+ },
+ "800": {
+ "Bilder": [
+ [
+ "Normalform",
+ "http://www.pokewiki.de/images/7/7f/Sugimori_800.png"
+ ],
+ [
+ "Abendm\\u00e4hne",
+ "http://www.pokewiki.de/images/6/60/Sugimori_800a.png"
+ ],
+ [
+ "Morgenschwingen",
+ "http://www.pokewiki.de/images/9/97/Sugimori_800b.png"
+ ],
+ [
+ "Ultraform",
+ "http://www.pokewiki.de/images/1/14/PGL-Artwork_Necrozma_%28Ultra-Necrozma%29.png"
+ ]
+ ],
+ "Deutsch": "Necrozma",
+ "Englisch": "Necrozma",
+ "Farbe": [
+ "Schwarz",
+ "Gelb (Abendm\\u00e4hne & Ultra)",
+ "Blau (Morgenschwingen)"
+ ],
+ "Französisch": "Necrozma",
+ "Gewicht": [
+ "230,0 kg",
+ "460,0 kg (Abendm\\u00e4hne)",
+ "350,0 kg (Morgenschwingen)",
+ "230,0kg (Ultra)"
+ ],
+ "Größe": [
+ "2,4 m",
+ "3,8 m (Abendm\\u00e4hne)",
+ "4,0 m (Morgenschwingen)",
+ "7,5 m (Ultra)"
+ ],
+ "Japanisch": "\\u30cd\\u30af\\u30ed\\u30ba\\u30de (Necrozma)",
+ "Kategorie": "Prisma",
+ "Link": "http://pokewiki.de/Necrozma",
+ "National-Dex": "#800",
+ "Typ": [
+ "Psycho",
+ "Psycho",
+ "Stahl (Abendm\\u00e4hne)",
+ "Psycho",
+ "Geist (Morgenschwingen)",
+ "Psycho",
+ "Drache (Ultra)"
+ ]
+ },
+ "801": {
+ "Bilder": [
+ [
+ "Normale Form",
+ "http://www.pokewiki.de/images/4/4e/Sugimori_801.png"
+ ],
+ [
+ "Originalfarbe",
+ "http://www.pokewiki.de/images/0/04/Sugimori_801a.png"
+ ]
+ ],
+ "Deutsch": "Magearna",
+ "Englisch": "Magearna",
+ "Farbe": [
+ "Grau",
+ "Rot (Originalfarbe)"
+ ],
+ "Französisch": "Magearna",
+ "Gewicht": "80,5 kg",
+ "Größe": "1,0 m",
+ "Japanisch": "\\u30de\\u30ae\\u30a2\\u30ca (Magearna)",
+ "Kategorie": "Fabrikat",
+ "Link": "http://pokewiki.de/Magearna",
+ "National-Dex": "#801",
+ "Typ": [
+ "Stahl",
+ "Fee"
+ ]
+ },
+ "802": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5f/Sugimori_802.png"
+ ]
+ ],
+ "Deutsch": "Marshadow",
+ "Englisch": "Marshadow",
+ "Farbe": "Grau",
+ "Französisch": "Marshadow",
+ "Gewicht": "22,2 kg",
+ "Größe": "0,7 m",
+ "Japanisch": "\\u30de\\u30fc\\u30b7\\u30e3\\u30c9\\u30fc (Marshadow)",
+ "Kategorie": "Dunkelwesen",
+ "Link": "http://pokewiki.de/Marshadow",
+ "National-Dex": "#802",
+ "Typ": [
+ "Kampf",
+ "Geist"
+ ]
+ },
+ "803": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/4/46/Sugimori_803.png"
+ ]
+ ],
+ "Deutsch": "Venicro",
+ "Englisch": "Poipole",
+ "Farbe": "Violett",
+ "Französisch": "V\\u00e9mini",
+ "Gewicht": "1,8 kg",
+ "Größe": "0,6 m",
+ "Japanisch": "\\u30d9\\u30d9\\u30ce\\u30e0 (Bebenomu)",
+ "Kategorie": "Giftdorn",
+ "Link": "http://pokewiki.de/Venicro",
+ "National-Dex": "#803",
+ "Typ": "Gift"
+ },
+ "804": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/0/03/PGL-Artwork_Agoyon.png"
+ ]
+ ],
+ "Deutsch": "Agoyon",
+ "Englisch": "Naganadel",
+ "Farbe": "Violett",
+ "Französisch": "Mandrillon",
+ "Gewicht": "150 kg",
+ "Größe": "3,6 m",
+ "Japanisch": "\\u30a2\\u30fc\\u30b4\\u30e8\\u30f3 (\\u0100goyon)",
+ "Kategorie": "Giftdorn",
+ "Link": "http://pokewiki.de/Agoyon",
+ "National-Dex": "#804",
+ "Typ": [
+ "Gift",
+ "Drache"
+ ]
+ },
+ "805": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/e/ea/Sugimori_805.png"
+ ]
+ ],
+ "Deutsch": "Muramura",
+ "Englisch": "Stakataka",
+ "Farbe": "Grau",
+ "Französisch": "Ama-Ama",
+ "Gewicht": "820,0 kg",
+ "Größe": "5,5 m",
+ "Japanisch": "\\u30c4\\u30f3\\u30c7\\u30c4\\u30f3\\u30c7 (Tsundetsunde)",
+ "Kategorie": "Steinmauer",
+ "Link": "http://pokewiki.de/Muramura",
+ "National-Dex": "#805",
+ "Typ": [
+ "Gestein",
+ "Stahl"
+ ]
+ },
+ "806": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/5/5d/Sugimori_806.png"
+ ]
+ ],
+ "Deutsch": "Kopplosio",
+ "Englisch": "Blacephalon",
+ "Farbe": "Weiß",
+ "Französisch": "Pierroteknik",
+ "Gewicht": "13,0 kg",
+ "Größe": "1,8 m",
+ "Japanisch": "\\u30ba\\u30ac\\u30c9\\u30fc\\u30f3 (Zugad\\u014dn)",
+ "Kategorie": "Feuerwerk",
+ "Link": "http://pokewiki.de/Kopplosio",
+ "National-Dex": "#806",
+ "Typ": [
+ "Feuer",
+ "Geist"
+ ]
+ },
+ "807": {
+ "Bilder": [
+ [
+ "",
+ "http://www.pokewiki.de/images/8/81/Sugimori_807.png"
+ ]
+ ],
+ "Deutsch": "Zeraora",
+ "Englisch": "Zeraora",
+ "Farbe": "Gelb",
+ "Französisch": "Zeraora",
+ "Gewicht": "44,5 kg",
+ "Größe": "1,5 m",
+ "Japanisch": "\\u30bc\\u30e9\\u30aa\\u30e9 (Zeraora)",
+ "Kategorie": "Blitzsturm",
+ "Link": "http://pokewiki.de/Zeraora",
+ "National-Dex": "#807",
+ "Typ": "Elektro"
+ },
+ "808": {
+ "Bilder": [
+ [
+ "",
+ "http://pokewiki.de/images/9/9d/Sugimori_808.png"
+ ]
+ ],
+ "Deutsch": "Meltan",
+ "Englisch": "Meltan",
+ "Farbe": "Grau",
+ "Französisch": "Meltan",
+ "Gewicht": "8,0 kg",
+ "Größe": "0,2 m",
+ "Japanisch": "\\u30e1\\u30eb\\u30bf\\u30f3 (Merutan)",
+ "Kategorie": "Mutter",
+ "Link": "http://pokewiki.de/Meltan",
+ "National-Dex": "#808",
+ "Typ": "Stahl"
+ },
+ "809": {
+ "Bilder": [
+ [
+ "",
+ "https://pokewiki.de/images/d/df/Sugimori_809.png"
+ ]
+ ],
+ "Deutsch": "Melmetal",
+ "Englisch": "Melmetal",
+ "Farbe": "Grau",
+ "Französisch": "Melmetal",
+ "Gewicht": "800,0 kg",
+ "Größe": "2,5 m",
+ "Japanisch": "\\u30e1\\u30eb\\u30e1\\u30bf\\u30eb (Merumetaru)",
+ "Kategorie": "Mutter",
+ "Link": "http://pokewiki.de/Melmetal",
+ "National-Dex": "#809",
+ "Typ": "Stahl"
+ },
+ "810": {
+ "Bilder": [
+ [
+ "",
+ "https://pokewiki.de/images/8/89/Sugimori_810.png"
+ ]
+ ],
+ "Deutsch": "Chimpep",
+ "Englisch": "Grookey",
+ "Farbe": "Grün",
+ "Französisch": "Ouistempo",
+ "Gewicht": "5,0 kg",
+ "Größe": "0,3 m",
+ "Japanisch": "\\u30e1\\u30eb\\u30e1\\u30bf\\u30eb (Merumetaru)",
+ "Kategorie": "Shimpansen-Pokemon",
+ "Link": "http://pokewiki.de/Chimpep",
+ "National-Dex": "#810",
+ "Typ": "Pflanze"
+ }
+}
\ No newline at end of file
diff --git a/nmreval/utils/text.py b/nmreval/utils/text.py
new file mode 100644
index 0000000..2dcb5d9
--- /dev/null
+++ b/nmreval/utils/text.py
@@ -0,0 +1,105 @@
+import re
+from itertools import product
+
+
+__all__ = ['convert']
+
+
+small_greek = [
+ r'\alpha \beta \gamma \delta \epsilon \zeta \eta \theta \iota \kappa \lambda \mu \nu ' # tex
+ r'\xi \omicron \pi \rho \varsigma \sigma \tau \ypsilon \phi \chi \psi \omega',
+ '\u03b1 \u03b2 \u03b3 \u03b4 \u03b5 \u03b6 \u03b7 \u03b8 \u03b9 \u03ba \u03bb \u03bc \u03bd ' # unicode (for html)
+ '\u03be \u03bf \u03c0 \u03c1 \u03c2 \u03c3 \u03c4 \u03c5 \u03c6 \u03c7 \u03c8 \u03c9',
+ r'\f{Symbol}a\f{} \f{Symbol}b\f{} \f{Symbol}g\f{} \f{Symbol}d\f{} \f{Symbol}e\f{} \f{Symbol}z\f{} ' # grace
+ r'\f{Symbol}h\f{} \f{Symbol}q\f{} \f{Symbol}i\f{} \f{Symbol}k\f{} \f{Symbol}l\f{} \f{Symbol}\f{} '
+ r'\f{Symbol}n\f{} \f{Symbol}x\f{} \f{Symbol}o\f{} \f{Symbol}p\f{} \f{Symbol}r\f{} \f{Symbol}s\f{} '
+ r'\f{Symbol}s\f{} \f{Symbol}t\f{} \f{Symbol}u\f{} \f{Symbol}f\f{} \f{Symbol}c\f{} \f{Symbol}y\f{} '
+ r'\f{Symbol}w\f{}',
+ 'alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu nu ' # plain
+ 'xi omicron pi rho sigma sigma tau ypsilon phi chi psi omega'
+]
+big_greek = [
+ r'\Alpha \Beta \Gamma \Delta \Epsilon \Zeta \Eta \Theta \Iota \Kappa \Lambda \Mu ' # tex
+ r'\Nu \Xi \Omicron \Pi \Rho \Sigma \Tau \Ypsilon \Phi \Chi \Psi \Omega',
+ '\u0391 \u0392 \u0393 \u0394 \u0395 \u0396 \u0397 \u0398 \u0399 \u039a \u039b \u039c ' # unicode (for html)
+ '\u039d \u039e \u039f \u03a0 \u03a1 \u03a3 \u03a4 \u03a5 \u03a6 \u03a7 \u03a8 \u03a9',
+ r'\f{Symbol}A\f{} \f{Symbol}B\f{} \f{Symbol}G\f{} \f{Symbol}D\f{} \f{Symbol}E\f{} \f{Symbol}Z\f{} ' # grace
+ r'\f{Symbol}H\f{} \f{Symbol}Q\f{} \f{Symbol}I\f{} \f{Symbol}K\f{} \f{Symbol}L\f{} \f{Symbol}M\f{} '
+ r'\f{Symbol}N\f{} \f{Symbol}X\f{} \f{Symbol}O\f{} \f{Symbol}P\f{} \f{Symbol}R\f{} \f{Symbol}S\f{} '
+ r'\f{Symbol}T\f{} \f{Symbol}Y\f{} \f{Symbol}F\f{} \f{Symbol}C\f{} \f{Symbol}U\f{} \f{Symbol}W\f{}',
+ 'Alpha Beta Gamma Delta Epsilon Zeta Eta Theta Iota Kappa Lambda Mu ' # plain
+ 'Nu Xi Omicron Pi Rho Sigma Tau Ypsilon Phi Chi Psi Omega'
+]
+special_chars = [
+ r'\infty \int \sum \langle \rangle \pm \perp \para \leftarrow \rightarrow \leftrightarrow \cdot \hbar',
+ '\u221e \u222b \u2211 \u27e8 \u27e9 \u00b1 \u27c2 \u2225 \u21d0 \u21d2 \u21d4 \u00b7 \u0127',
+ r'\f{Symbol}¥\f{} \f{Symbol}ò\f{} \f{Symbol}å\f{} \f{Symbol}á\f{} \f{Symbol}ñ\f{} \f{Symbol}±\f{} '
+ r'\f{Symbol}^\f{} \f{Symbol}||\f{} \f{Symbol}¬\f{} \f{Symbol}®\f{} \f{Symbol}«\f{} \f{Symbol}×\f{Symbol} '
+ r'h\h{-0.6}\v{0.3}-\v{-0.3}\h{0.3}',
+ 'infty int sum < > +- perp para <- -> <-> * hbar'
+]
+funcs = [
+ r'\exp \log \ln \sin \cos \tan',
+ 'exp log ln sin cos tan',
+ 'exp log ln sin cos tan',
+ 'exp log ln sin cos tan'
+]
+delims = [
+ [(r'_{', r'}'), (r'', r' '), (r'\\s', r'\\N'), (r'_{', r'}')],
+ [(r'\^{', r'}'), (r'', r' '), (r'\\S', r'\\N'), (r'\^{', r'}')]
+]
+
+patterns = [[None]*4 for _ in range(4)]
+
+for i, j in product(range(4), range(4)):
+ if i == j:
+ continue
+
+ replace_dict = {}
+ all_chars = [small_greek, big_greek, special_chars]
+ if (i == 0) or (j == 0):
+ all_chars.append(funcs)
+
+ for char_list in all_chars:
+ replace_dict.update({re.escape(a): b for a, b in zip(char_list[i].split(), char_list[j].split())})
+
+ patterns[i][j] = (replace_dict, re.compile('|'.join(replace_dict.keys()))) # conversion table and regexp pattern
+
+
+def _replace_chars(text, src, dest):
+ replacement, pattern = patterns[src][dest]
+
+ return pattern.sub(lambda m: replacement[re.escape(m.group(0))], text)
+
+
+def _replace_delims(text, src, dest):
+ for delim in delims:
+ text = re.sub(rf'{delim[src][0]}(.*?){delim[src][1]}',
+ rf'{delim[dest][0]}\g<1>{delim[dest][1]}',
+ text)
+ return text
+
+
+def convert(text: str, old: str = 'tex', new: str = 'html', brackets: bool = True):
+ t = {'latex': 0, 'tex': 0, 'html': 1,
+ 'agr': 2, 'plain': 3, 'str': 3}
+
+ idx_in = t[old]
+ idx_out = t[new]
+
+ if idx_out == 1:
+ text = _replace_chars(text, idx_in, idx_out)
+ if brackets:
+ text = _replace_delims(text, idx_in, idx_out)
+ else:
+ text = _replace_delims(text, idx_in, idx_out)
+ text = _replace_chars(text, idx_in, idx_out)
+
+ if idx_out in [0, 3]:
+ text = text.replace(r'\^', '^')
+
+ if idx_out == 3 and not brackets:
+ text = text.replace('{', '').replace('}', '')
+
+ return text
+
diff --git a/nmreval/utils/utils.py b/nmreval/utils/utils.py
new file mode 100755
index 0000000..ba916da
--- /dev/null
+++ b/nmreval/utils/utils.py
@@ -0,0 +1,37 @@
+import itertools
+import re
+from typing import Iterable, Iterator, Any
+
+
+def get_temperature(text: str) -> float:
+ temp_re = re.compile(r'(\d+(?:[.p]\d*)?)\s*K')
+ m = temp_re.search(text)
+ if m is not None:
+ return float(m.group(1))
+
+ else:
+ m = re.search('RT', text)
+ if m is not None:
+ return 300.0
+
+ return -1.
+
+
+def staggered_range(list_in: Iterable, stepsize: int = 2) -> Iterator[Any]:
+ return itertools.chain(*[list_in[i::stepsize] for i in range(stepsize)])
+
+
+def roundrobin(*iterables):
+ """
+ Taken from the itertools recipes
+ """
+ num_active = len(iterables)
+ nexts = itertools.cycle(iter(it).__next__ for it in iterables)
+ while num_active:
+ try:
+ for next in nexts:
+ yield next()
+ except StopIteration:
+ # Remove the iterator we just exhausted from the cycle.
+ num_active -= 1
+ nexts = itertools.cycle(itertools.islice(nexts, num_active))
diff --git a/nmreval/version.py b/nmreval/version.py
new file mode 100644
index 0000000..bd87b23
--- /dev/null
+++ b/nmreval/version.py
@@ -0,0 +1,3 @@
+# coding=utf-8
+__version__ = '0.1'
+__releasename__ = 'Snobilikat'
diff --git a/resources/__init__.py b/resources/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/resources/_rc/__init__.py b/resources/_rc/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/resources/_rc/axes.png b/resources/_rc/axes.png
new file mode 100644
index 0000000..4e8ebf4
Binary files /dev/null and b/resources/_rc/axes.png differ
diff --git a/resources/_rc/blackwhite.png b/resources/_rc/blackwhite.png
new file mode 100644
index 0000000..6a70fed
Binary files /dev/null and b/resources/_rc/blackwhite.png differ
diff --git a/resources/_rc/blackwhite.svg b/resources/_rc/blackwhite.svg
new file mode 100644
index 0000000..3e93dcb
--- /dev/null
+++ b/resources/_rc/blackwhite.svg
@@ -0,0 +1,308 @@
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_rc/cat.png b/resources/_rc/cat.png
new file mode 100644
index 0000000..d006fb2
Binary files /dev/null and b/resources/_rc/cat.png differ
diff --git a/resources/_rc/colors.png b/resources/_rc/colors.png
new file mode 100644
index 0000000..580db82
Binary files /dev/null and b/resources/_rc/colors.png differ
diff --git a/resources/_rc/colors.svg b/resources/_rc/colors.svg
new file mode 100644
index 0000000..fd6cc9a
--- /dev/null
+++ b/resources/_rc/colors.svg
@@ -0,0 +1,330 @@
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_rc/cut_region.png b/resources/_rc/cut_region.png
new file mode 100644
index 0000000..b6bb766
Binary files /dev/null and b/resources/_rc/cut_region.png differ
diff --git a/resources/_rc/eval_button.png b/resources/_rc/eval_button.png
new file mode 100644
index 0000000..9109a7f
Binary files /dev/null and b/resources/_rc/eval_button.png differ
diff --git a/resources/_rc/fit_preview.png b/resources/_rc/fit_preview.png
new file mode 100644
index 0000000..fb3e845
Binary files /dev/null and b/resources/_rc/fit_preview.png differ
diff --git a/resources/_rc/fit_region.png b/resources/_rc/fit_region.png
new file mode 100644
index 0000000..651cb3d
Binary files /dev/null and b/resources/_rc/fit_region.png differ
diff --git a/resources/_rc/grid.png b/resources/_rc/grid.png
new file mode 100644
index 0000000..b904f16
Binary files /dev/null and b/resources/_rc/grid.png differ
diff --git a/resources/_rc/grid.svg b/resources/_rc/grid.svg
new file mode 100644
index 0000000..c51d47e
--- /dev/null
+++ b/resources/_rc/grid.svg
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_rc/imag.png b/resources/_rc/imag.png
new file mode 100644
index 0000000..98ca00f
Binary files /dev/null and b/resources/_rc/imag.png differ
diff --git a/resources/_rc/imag.svg b/resources/_rc/imag.svg
new file mode 100644
index 0000000..defd02c
--- /dev/null
+++ b/resources/_rc/imag.svg
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+ i
+
diff --git a/resources/_rc/images.qrc b/resources/_rc/images.qrc
new file mode 100644
index 0000000..5507828
--- /dev/null
+++ b/resources/_rc/images.qrc
@@ -0,0 +1,17 @@
+
+
+ errors.png
+ imag.png
+ real.png
+ xlog.png
+ ylog.png
+ grid.png
+ action_legend.png
+
+
+ plus_icon.png
+ minus_icon.png
+ mal_icon.png
+ geteilt_icon.png
+
+
diff --git a/resources/_rc/logo.png b/resources/_rc/logo.png
new file mode 100644
index 0000000..6b41833
Binary files /dev/null and b/resources/_rc/logo.png differ
diff --git a/resources/_rc/mean.png b/resources/_rc/mean.png
new file mode 100644
index 0000000..5756845
Binary files /dev/null and b/resources/_rc/mean.png differ
diff --git a/resources/_rc/mouse.png b/resources/_rc/mouse.png
new file mode 100644
index 0000000..8761dd0
Binary files /dev/null and b/resources/_rc/mouse.png differ
diff --git a/resources/_rc/open_session.png b/resources/_rc/open_session.png
new file mode 100644
index 0000000..594d97d
Binary files /dev/null and b/resources/_rc/open_session.png differ
diff --git a/resources/_rc/path48-3-5-6-2.png b/resources/_rc/path48-3-5-6-2.png
new file mode 100644
index 0000000..6a70fed
Binary files /dev/null and b/resources/_rc/path48-3-5-6-2.png differ
diff --git a/resources/_rc/properties.png b/resources/_rc/properties.png
new file mode 100644
index 0000000..78f6c2d
Binary files /dev/null and b/resources/_rc/properties.png differ
diff --git a/resources/_rc/punktdings.png b/resources/_rc/punktdings.png
new file mode 100644
index 0000000..023d677
Binary files /dev/null and b/resources/_rc/punktdings.png differ
diff --git a/resources/_rc/real.png b/resources/_rc/real.png
new file mode 100644
index 0000000..a458e50
Binary files /dev/null and b/resources/_rc/real.png differ
diff --git a/resources/_rc/real.svg b/resources/_rc/real.svg
new file mode 100644
index 0000000..c408f78
--- /dev/null
+++ b/resources/_rc/real.svg
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+ r
+
diff --git a/resources/_rc/sort.png b/resources/_rc/sort.png
new file mode 100644
index 0000000..e2b67de
Binary files /dev/null and b/resources/_rc/sort.png differ
diff --git a/resources/_rc/xlog.png b/resources/_rc/xlog.png
new file mode 100644
index 0000000..f4f55d4
Binary files /dev/null and b/resources/_rc/xlog.png differ
diff --git a/resources/_rc/xlog.svg b/resources/_rc/xlog.svg
new file mode 100644
index 0000000..2d3532a
--- /dev/null
+++ b/resources/_rc/xlog.svg
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log
+
+
diff --git a/resources/_rc/ylog.png b/resources/_rc/ylog.png
new file mode 100644
index 0000000..735ba91
Binary files /dev/null and b/resources/_rc/ylog.png differ
diff --git a/resources/_rc/ylog.svg b/resources/_rc/ylog.svg
new file mode 100644
index 0000000..b83b970
--- /dev/null
+++ b/resources/_rc/ylog.svg
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ log
+
+
diff --git a/resources/_ui/agroptiondialog.ui b/resources/_ui/agroptiondialog.ui
new file mode 100644
index 0000000..57dcd4a
--- /dev/null
+++ b/resources/_ui/agroptiondialog.ui
@@ -0,0 +1,587 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 513
+ 466
+
+
+
+ Grace settings
+
+
+ -
+
+
+ QTabWidget::Rounded
+
+
+ 0
+
+
+
+ Dimensions
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::Panel
+
+
+ QFrame::Plain
+
+
+ 3
+
+
+ 5
+
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+ cm
+
+
+
+ -
+
+
+ Bottom margin
+
+
+
+ -
+
+
+ Right margin
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ cm
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ cm
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Left margin
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Top margin
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ cm
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::WinPanel
+
+
+ 0
+
+
+
+ 0
+
+
+
+
+
+
+
+ -
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Paper height
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ cm
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+ -
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Expanding
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Paper width
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ cm
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+ Fonts
+
+
+ -
+
+
+ Title
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 1000
+
+
+ 100
+
+
+
+ -
+
+
+ Legend
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 1000
+
+
+ 100
+
+
+
+ -
+
+
+ Vertical axis
+
+
+ -
+
+
+ tick
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 1000
+
+
+ 100
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 1000
+
+
+ 100
+
+
+
+ -
+
+
+ label
+
+
+
+
+
+
+ -
+
+
+ Horizontal axis
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 1000
+
+
+ 100
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 1000
+
+
+ 100
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ tick
+
+
+
+ -
+
+
+ label
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ Symbol/Line
+
+
+ -
+
+
+ 1000
+
+
+ 100
+
+
+
+ -
+
+
+ Default symbol size
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ 1
+
+
+ 20.000000000000000
+
+
+ 0.500000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ Default line size
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/apod_dialog.ui b/resources/_ui/apod_dialog.ui
new file mode 100644
index 0000000..03c5c1b
--- /dev/null
+++ b/resources/_ui/apod_dialog.ui
@@ -0,0 +1,141 @@
+
+
+ ApodEdit
+
+
+
+ 0
+ 0
+ 784
+ 484
+
+
+
+
+ 0
+ 0
+
+
+
+ Apodization
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ 20
+
+
+ 6
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ TextLabel
+
+
+ 3
+
+
+
+
+
+
+
+ PlotWidget
+ QGraphicsView
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ ApodEdit
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ ApodEdit
+ close()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/asciidialog.ui b/resources/_ui/asciidialog.ui
new file mode 100644
index 0000000..88fb2d2
--- /dev/null
+++ b/resources/_ui/asciidialog.ui
@@ -0,0 +1,377 @@
+
+
+ ascii_reader
+
+
+
+ 0
+ 0
+ 667
+ 509
+
+
+
+ Read text file
+
+
+ -
+
+
+ 0
+
+
+
+ Data
+
+
+
+ 0
+
+ -
+
+
+ false
+
+
+
+ 16777215
+ 180
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::ScrollBarAsNeeded
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ true
+
+
+ QAbstractItemView::ExtendedSelection
+
+
+ QAbstractItemView::SelectColumns
+
+
+ 1
+
+
+
+
+
+
+
+ Delays
+
+
+ -
+
+
+ -
+
+
+ QFormLayout::ExpandingFieldsGrow
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Number of delays
+
+
+
+ -
+
+
+ -
+
+
+ Start value
+
+
+
+ -
+
+
+ -
+
+
+ End value
+
+
+
+ -
+
+
+ -
+
+
+ Qt::LeftToRight
+
+
+ Logarithmic scale
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Staggered range
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Calculate delays
+
+
+
+ ../../../../.designer/backup ../../../../.designer/backup
+
+
+
+
+
+
+
+
+
+ -
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ x
+
+
+
+ -
+
+
+ <html><head/><body><p>Specify which column is used as x-value.</p></body></html>
+
+
+ Qt::ImhFormattedNumbersOnly|Qt::ImhPreferNumbers
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ y
+
+
+
+ -
+
+
+ <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>
+
+
+ Qt::ImhFormattedNumbersOnly|Qt::ImhPreferNumbers
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ <html><head/><body><p>Δy</p></body></html>
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+ -
+
+
+ Use selection for next files. Deletes possible delay values.
+
+
+ Skip next dialogues?
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Points
+
+
+ true
+
+
+ true
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ FID
+
+
+ true
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Spectrum
+
+
+ buttonGroup
+
+
+
+
+
+ -
+
+
+ QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonbox
+ rejected()
+ ascii_reader
+ close()
+
+
+ 366
+ 485
+
+
+ 366
+ 254
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/axisConfigTemplate.ui b/resources/_ui/axisConfigTemplate.ui
new file mode 100644
index 0000000..01bdf93
--- /dev/null
+++ b/resources/_ui/axisConfigTemplate.ui
@@ -0,0 +1,161 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 186
+ 154
+
+
+
+
+ 200
+ 16777215
+
+
+
+ PyQtGraph
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Link Axis:
+
+
+
+ -
+
+
+ <html><head/><body><p>Links this axis with another view. When linked, both views will display the same data range.</p></body></html>
+
+
+ QComboBox::AdjustToContents
+
+
+
+ -
+
+
+ true
+
+
+ <html><head/><body><p>Percent of data to be visible when auto-scaling. It may be useful to decrease this value for data with spiky noise.</p></body></html>
+
+
+ %
+
+
+ 1
+
+
+ 100
+
+
+ 1
+
+
+ 100
+
+
+
+ -
+
+
+ <html><head/><body><p>Automatically resize this axis whenever the displayed data is changed.</p></body></html>
+
+
+ Auto
+
+
+ true
+
+
+
+ -
+
+
+ <html><head/><body><p>Set the range for this axis manually. This disables automatic scaling. </p></body></html>
+
+
+ Manual
+
+
+
+ -
+
+
+ <html><head/><body><p>Minimum value to display for this axis.</p></body></html>
+
+
+ 0
+
+
+
+ -
+
+
+ <html><head/><body><p>Maximum value to display for this axis.</p></body></html>
+
+
+ 0
+
+
+
+ -
+
+
+ <html><head/><body><p>Inverts the display of this axis. (+y points downward instead of upward)</p></body></html>
+
+
+ Invert Axis
+
+
+
+ -
+
+
+ <html><head/><body><p>Enables mouse interaction (panning, scaling) for this axis.</p></body></html>
+
+
+ Mouse Enabled
+
+
+ true
+
+
+
+ -
+
+
+ <html><head/><body><p>When checked, the axis will only auto-scale to data that is visible along the orthogonal axis.</p></body></html>
+
+
+ Visible Data Only
+
+
+
+ -
+
+
+ <html><head/><body><p>When checked, the axis will automatically pan to center on the current data, but the scale along this axis will not change.</p></body></html>
+
+
+ Auto Pan Only
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/baseline_dialog.ui b/resources/_ui/baseline_dialog.ui
new file mode 100644
index 0000000..f047d0f
--- /dev/null
+++ b/resources/_ui/baseline_dialog.ui
@@ -0,0 +1,109 @@
+
+
+ SignalEdit
+
+
+
+ 0
+ 0
+ 919
+ 595
+
+
+
+
+ 0
+ 0
+
+
+
+ Dialog
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+ PlotWidget
+ QGraphicsView
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ SignalEdit
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ SignalEdit
+ close()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/basewindow.ui b/resources/_ui/basewindow.ui
new file mode 100644
index 0000000..bc10159
--- /dev/null
+++ b/resources/_ui/basewindow.ui
@@ -0,0 +1,1051 @@
+
+
+ BaseWindow
+
+
+
+ 0
+ 0
+ 1388
+ 735
+
+
+
+ Mr. Godot told me to tell you he won't come this evening but surely tomorrow.
+
+
+
+ :/logo.png :/logo.png
+
+
+ QMainWindow::AllowTabbedDocks|QMainWindow::AnimatedDocks|QMainWindow::ForceTabbedDocks|QMainWindow::VerticalTabs
+
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ QTabWidget::West
+
+
+ QTabWidget::Rounded
+
+
+ 0
+
+
+ Qt::ElideRight
+
+
+ true
+
+
+ false
+
+
+ true
+
+
+
+ Data
+
+
+
+
+
+ :/value_dock :/value_dock
+
+
+ Values
+
+
+
+
+
+ :/fit_dock :/fit_dock
+
+
+ Fit
+
+
+
+
+
+ :/signal_dock :/signal_dock
+
+
+ Signals
+
+
+
+
+
+ :/peakpick_dock :/peakpick_dock
+
+
+ Pick points
+
+
+
+
+
+ :/eval_t1_dock :/eval_t1_dock
+
+
+ SLR
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+ Main
+
+
+ Qt::AllToolBarAreas
+
+
+
+ 24
+ 24
+
+
+
+ Qt::ToolButtonIconOnly
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+ Edit
+
+
+
+ 24
+ 24
+
+
+
+ Qt::ToolButtonIconOnly
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+ NMR
+
+
+
+ 24
+ 24
+
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+ Fit
+
+
+
+ 24
+ 24
+
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+ Spectrum
+
+
+
+ 24
+ 24
+
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+ toolBar_2
+
+
+
+ 24
+ 24
+
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+
+
+
+ ../../../../../.designer/backup ../../../../../.designer/backup
+
+
+ &Quit
+
+
+ Ctrl+Q
+
+
+
+
+ Export graphic...
+
+
+ Ctrl+Shift+S
+
+
+
+
+ &Open...
+
+
+ Ctrl+O
+
+
+
+
+ Export data...
+
+
+ Ctrl+Shift+S
+
+
+
+
+ &Evaluate expression...
+
+
+
+
+
+ ../../../../../.designer/backup ../../../../../.designer/backup
+
+
+ &Delete Set
+
+
+ Ctrl+Del
+
+
+
+
+ Save fit ¶meter...
+
+
+
+
+ Sort &points
+
+
+
+
+
+ ../../../../../../../../.designer/backup ../../../../../../../../.designer/backup
+
+
+ &Reset
+
+
+ Ctrl+R
+
+
+
+
+
+ ../../../../../../../../.designer/backup ../../../../../../../../.designer/backup
+
+
+ &Documentation
+
+
+ F1
+
+
+
+
+ Open &Fit
+
+
+ Ctrl+F
+
+
+
+
+ &Max
+
+
+
+
+ &First point
+
+
+
+
+ &Area
+
+
+
+
+ Ma&x(Abs)
+
+
+
+
+ &Last point
+
+
+
+
+ S&ave...
+
+
+ Ctrl+S
+
+
+
+
+ true
+
+
+ &Views
+
+
+
+
+ true
+
+
+ &Edit
+
+
+
+
+ Set by function...
+
+
+
+
+ &Reset color
+
+
+
+
+ Join sets
+
+
+
+
+ &Shift/scale...
+
+
+
+
+
+ ../../../../../.designer/backup ../../../../../.designer/backup
+
+
+ &Show log...
+
+
+
+
+ &Create fit function...
+
+
+
+
+ &Grace preferences...
+
+
+
+
+ Update session
+
+
+
+
+ true
+
+
+ Mouse behaviour
+
+
+ Switch between zoom and pan in graph.
+
+
+
+
+ Configuration...
+
+
+
+
+
+ ../../../../../.designer/backup ../../../../../.designer/backup
+
+
+ Refresh
+
+
+ F5
+
+
+
+
+ Interpolation...
+
+
+
+
+ Smoothing...
+
+
+
+
+ Fit parameter saving...
+
+
+
+
+ Parameter...
+
+
+
+
+ Skip points...
+
+
+
+
+ Draw lines...
+
+
+
+
+ true
+
+
+ Maximize
+
+
+ false
+
+
+
+
+ Tile windows
+
+
+
+
+ true
+
+
+ Minimize
+
+
+ false
+
+
+
+
+ New plot
+
+
+
+
+
+ ../../../../../.designer/backup ../../../../../.designer/backup
+
+
+ Delete plot
+
+
+
+
+ Cascade windows
+
+
+
+
+ Next
+
+
+ Alt+Right
+
+
+
+
+ Previous
+
+
+ Alt+Left
+
+
+
+
+ Evaluate T1 minimum...
+
+
+
+
+ Calculate T1...
+
+
+
+
+ Coupling values...
+
+
+
+
+ Calculate derivative loss
+
+
+
+
+ Read FC data...
+
+
+
+
+ Convert mean values...
+
+
+
+
+ Log FT...
+
+
+
+
+ New set
+
+
+
+
+ Calculate magnitude
+
+
+
+
+ Center on max
+
+
+
+
+ De-paked spectrum
+
+
+
+
+ Edit signals...
+
+
+
+
+ Pick points...
+
+
+
+
+ Integrate
+
+
+
+
+ Differentiation...
+
+
+
+
+ Integration...
+
+
+
+
+ Cut to visible range
+
+
+
+
+ Move sets...
+
+
+
+
+ Baseline...
+
+
+
+
+ Calculate relaxation...
+
+
+
+
+ Change datatypes...
+
+
+
+
+
+ ../../../.designer/backup ../../../.designer/backup
+
+
+ Print...
+
+
+ Ctrl+P
+
+
+
+
+ true
+
+
+ true
+
+
+ Default stuff
+
+
+
+
+ true
+
+
+ Nelder-Mead
+
+
+
+
+ true
+
+
+ ODR
+
+
+
+
+ true
+
+
+ false
+
+
+ None
+
+
+
+
+ true
+
+
+ true
+
+
+ Visible x range
+
+
+
+
+ true
+
+
+ Custom
+
+
+
+
+ Worms
+
+
+
+
+ Function editor...
+
+
+
+
+ Life...
+
+
+
+
+ Not Tetris
+
+
+
+
+ Look for updates
+
+
+
+
+
+ DataWidget
+ QWidget
+ ..data.datawidget.datawidget
+ 1
+
+
+ EditSignalWidget
+ QWidget
+ ..data.signaledit.editsignalwidget
+ 1
+
+
+ PointSelectWidget
+ QWidget
+
+ 1
+
+
+ ValueEditWidget
+ QWidget
+
+ 1
+
+
+ QT1Widget
+ QWidget
+
+ 1
+
+
+ QFitDialog
+ QWidget
+
+ 1
+
+
+
+
+
+ action_close
+ triggered()
+ BaseWindow
+ close()
+
+
+ -1
+ -1
+
+
+ 439
+ 274
+
+
+
+
+
diff --git a/resources/_ui/bdsdialog.ui b/resources/_ui/bdsdialog.ui
new file mode 100644
index 0000000..8707a18
--- /dev/null
+++ b/resources/_ui/bdsdialog.ui
@@ -0,0 +1,141 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 400
+ 319
+
+
+
+ Read BDS data
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Found temperatures
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Read as:
+
+
+
+ -
+
+ -
+
+
+ Permittivity ε
+
+
+ true
+
+
+
+ -
+
+
+ Modulus 1/ε
+
+
+
+ -
+
+
+ Conductivity iεω
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/color_palette.ui b/resources/_ui/color_palette.ui
new file mode 100644
index 0000000..041b3af
--- /dev/null
+++ b/resources/_ui/color_palette.ui
@@ -0,0 +1,154 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 390
+ 409
+
+
+
+ Color Palette
+
+
+ -
+
+
+ Append
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Add color
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Save as new list
+
+
+
+ -
+
+
+ Load
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::InternalMove
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+ ColorListEditor
+ QComboBox
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/coupling_calculator.ui b/resources/_ui/coupling_calculator.ui
new file mode 100644
index 0000000..119bc41
--- /dev/null
+++ b/resources/_ui/coupling_calculator.ui
@@ -0,0 +1,111 @@
+
+
+ coupling_calc_dialog
+
+
+
+ 0
+ 0
+ 400
+ 280
+
+
+
+ Calculate BPP coupling constants
+
+
+ -
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ font-weight: bold
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Close
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ coupling_calc_dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ coupling_calc_dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/coupling_t1_from_tau.ui b/resources/_ui/coupling_t1_from_tau.ui
new file mode 100644
index 0000000..8a58a4a
--- /dev/null
+++ b/resources/_ui/coupling_t1_from_tau.ui
@@ -0,0 +1,64 @@
+
+
+ couplingForm
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Form
+
+
+ -
+
+
+ parameter_name
+
+
+
+ -
+
+
+ -
+
+
+ Value
+
+
+ true
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Index
+
+
+ buttonGroup
+
+
+
+
+
+
+
+ LineEdit
+ QLineEdit
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/datawidget.ui b/resources/_ui/datawidget.ui
new file mode 100644
index 0000000..b620055
--- /dev/null
+++ b/resources/_ui/datawidget.ui
@@ -0,0 +1,124 @@
+
+
+ DataWidget
+
+
+
+ 0
+ 0
+ 307
+ 847
+
+
+
+ Data
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ New graph
+
+
+ New graph
+
+
+
+ -
+
+
+ New set
+
+
+ Empty set
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+
+ ExpandableWidget
+ QWidget
+
+ 1
+
+
+
+
+
diff --git a/resources/_ui/dscfile_dialog.ui b/resources/_ui/dscfile_dialog.ui
new file mode 100644
index 0000000..6b7143e
--- /dev/null
+++ b/resources/_ui/dscfile_dialog.ui
@@ -0,0 +1,399 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 962
+ 662
+
+
+
+ Read DSC file
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Save
+
+
+
+ -
+
+
+ 0
+
+
+ 0
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Convert to heat capacity
+
+
+ true
+
+
+
+ -
+
+
+ 3
+
+ -
+
+
+ Load empty
+
+
+
+ -
+
+
+ Remove empty
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Isotherms
+
+
+ true
+
+
+ buttonGroup
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ font-weight: bold
+
+
+ Detected steps
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ 2
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Slope
+
+
+
+ -
+
+
+ Initial slope
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Empty measurement
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ font-weight: bold
+
+
+ Calibration
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ font-weight: bold
+
+
+ Baseline
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ None
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Add reference
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Remove reference
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+
+ -
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 300
+ 200
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 300
+ 200
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 300
+ 200
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+ PlotWidget
+ QGraphicsView
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/editsignalwidget.ui b/resources/_ui/editsignalwidget.ui
new file mode 100644
index 0000000..8e109d1
--- /dev/null
+++ b/resources/_ui/editsignalwidget.ui
@@ -0,0 +1,367 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 328
+ 732
+
+
+
+ Form
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Baseline correction
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Apply
+
+
+
+
+
+
+ -
+
+
+ Left shift
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Apply
+
+
+
+ -
+
+ -
+
+ Points
+
+
+ -
+
+ Seconds
+
+
+
+
+ -
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 9999
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+ Zerofilling
+
+
+ false
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Apply
+
+
+
+
+
+
+ -
+
+
+ Phase correction
+
+
+ false
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 0
+
+ -
+
+
+ -360.000000000000000
+
+
+ 360.000000000000000
+
+
+
+ -
+
+
+ Phase 1
+
+
+
+ -
+
+
+ Preview
+
+
+
+ -
+
+
+ Phase 0
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::ImhDigitsOnly
+
+
+ 0
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ -180.000000000000000
+
+
+ 180.000000000000000
+
+
+
+ -
+
+
+ Pivot
+
+
+
+ -
+
+
+ Apply
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Apodization
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ -
+
+
+ TextLabel
+
+
+ 3
+
+
+
+ -
+
+
+ 0
+
+
+
+ -
+
+ -
+
+
+ Preview
+
+
+
+ -
+
+
+ Apply
+
+
+
+
+
+
+
+
+ -
+
+
+ FFT
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Apply
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/eval_expr_dialog.ui b/resources/_ui/eval_expr_dialog.ui
new file mode 100644
index 0000000..c3f7791
--- /dev/null
+++ b/resources/_ui/eval_expr_dialog.ui
@@ -0,0 +1,432 @@
+
+
+ CalcDialog
+
+
+ Qt::WindowModal
+
+
+
+ 0
+ 0
+ 804
+ 627
+
+
+
+ Evaluate stuff
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 2
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Select sets for evaluation
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ QAbstractItemView::SingleSelection
+
+
+
+ -
+
+
+ Qt::LeftToRight
+
+
+ Overwrite values
+
+
+
+
+
+
+
+ -
+
+
+ GroupBox
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Label
+
+
+ label_lineEdit
+
+
+
+ -
+
+
+ -
+
+
+ Value
+
+
+ value_lineEdit
+
+
+
+ -
+
+
+ -
+
+
+ Datatype
+
+
+ dtype_comboBox
+
+
+
+ -
+
+ -
+
+ Points
+
+
+ -
+
+ Timesignal
+
+
+ -
+
+ Spectrum
+
+
+ -
+
+ BDS
+
+
+
+
+
+
+
+ -
+
+
+ Symbol
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Color
+
+
+ symcolor_comboBox
+
+
+
+ -
+
+
+ -
+
+
+ Size
+
+
+ symbol_spinBox
+
+
+
+ -
+
+
+ 10
+
+
+
+ -
+
+
+ -
+
+
+ Style
+
+
+
+
+
+
+ -
+
+
+ Line
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Color
+
+
+ linecolor_comboBox
+
+
+
+ -
+
+
+ -
+
+
+ Width
+
+
+ line_doubleSpinBox
+
+
+
+ -
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ -
+
+
+ Style
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+
+ groupBox
+ groupBox_2
+ groupBox_3
+ verticalSpacer
+ graph_comboBox
+
+
+
+
+
+
+
+
+ Qt::Vertical
+
+
+ false
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 75
+ true
+
+
+
+ Expressions are evaluated line by line and change previous values
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+ ColorListEditor
+ QComboBox
+
+
+
+ LineStyleEditor
+ QComboBox
+
+
+
+ SymbolStyleEditor
+ QComboBox
+
+
+
+ QNamespaceWidget
+ QWidget
+
+ 1
+
+
+
+ calc_edit
+ listWidget
+ overwrite_checkbox
+ label_lineEdit
+ value_lineEdit
+ dtype_comboBox
+ symcolor_comboBox
+ symbol_spinBox
+ linecolor_comboBox
+ line_doubleSpinBox
+
+
+
+
diff --git a/resources/_ui/evalexpression.ui b/resources/_ui/evalexpression.ui
new file mode 100644
index 0000000..6630a8b
--- /dev/null
+++ b/resources/_ui/evalexpression.ui
@@ -0,0 +1,366 @@
+
+
+ CalcDialog
+
+
+
+ 0
+ 0
+ 895
+ 547
+
+
+
+ Evaluate stuff
+
+
+ -
+
+
+ 0
+
+ -
+
+
+ 0
+
+
+
+ Parameter
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ true
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ QTextEdit::AutoNone
+
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- X, y, and Δy values </p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- Values of dataset on position <span style=" font-style:italic;">i</span> in list</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> (<span style=" font-style:italic;">s[i].x</span> and <span style=" font-style:italic;">x</span> return the same values)</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- Numpy functions</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- If available, fit parameters</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> (see namespace for available parameters)</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- Fit functions:</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> (meaning of p and extra arguments </p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> depend on function)</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- Constants:</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> (nuclei are accessed by <span style=" font-style:italic;">const['gamma']['1H']</span>)</p></body></html>
+
+
+ Qt::NoTextInteraction
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">x y y_err</span></p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">s[i].x s[i+2].y</span> <span style=" font-style:italic;">s[i-1].y_err</span></p>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-style:italic;"><br /></p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">np.function</span></p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">fit['NAME']</span></p>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-style:italic;"><br /></p>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">Name.func(p, x, *args) </span></p>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-style:italic;"><br /></p>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">const['NAME']</span></p></body></html>
+
+
+ Qt::NoTextInteraction
+
+
+
+
+
+
+
+
+
+ Example
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ true
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Substract neighbouring datasets: </p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Normalize on fit value M<span style=" vertical-align:sub;">∞</span>:</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Logscale x:</p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Division by exponential decay:</p>
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html>
+
+
+ Qt::NoTextInteraction
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ true
+
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;">
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">y = y-s[i+1].y</span></p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">y = y/fit['M_infty']</span></p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">x = np.log10(x)</span></p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">y = y/np.exp(-x/10)</span></p>
+<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">y = y/Exponential_Decay.func([0, 1, 10, 1], x)</span></p></body></html>
+
+
+
+
+
+
+
+
+
+ Namespace
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ QAbstractItemView::NoSelection
+
+
+ false
+
+
+
+ Namespace
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 75
+ true
+
+
+
+ Expressions are evaluated line by line and change previous values
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ -
+
+
+ 0
+
+ -
+
+
+ <html><head/><body><p>Select sets for evaluation<br>(no selection = all visible):</p></body></html>
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ QAbstractItemView::MultiSelection
+
+
+
+ -
+
+
+ Qt::LeftToRight
+
+
+ Overwrite values?
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ CalcDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ CalcDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/expandablewidget.ui b/resources/_ui/expandablewidget.ui
new file mode 100644
index 0000000..ddb9284
--- /dev/null
+++ b/resources/_ui/expandablewidget.ui
@@ -0,0 +1,79 @@
+
+
+ ExpandableForm
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Form
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 0
+
+ -
+
+
+ border: 0
+
+
+
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ Qt::RightArrow
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/exportConfigTemplate.ui b/resources/_ui/exportConfigTemplate.ui
new file mode 100644
index 0000000..2d0308c
--- /dev/null
+++ b/resources/_ui/exportConfigTemplate.ui
@@ -0,0 +1,103 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 241
+ 367
+
+
+
+ Export
+
+
+
+ 0
+
+ -
+
+
+ Item to export:
+
+
+
+ -
+
+
+ false
+
+
+
+ 1
+
+
+
+
+ -
+
+
+ Export format
+
+
+
+ -
+
+
+ -
+
+
+ Export
+
+
+
+ -
+
+
+ Close
+
+
+
+ -
+
+
+ 2
+
+
+ false
+
+
+
+ 1
+
+
+
+
+ -
+
+
+ Export options
+
+
+
+ -
+
+
+ Copy
+
+
+
+
+
+
+
+ ParameterTree
+ QTreeWidget
+
+
+
+
+
+
diff --git a/resources/_ui/fcreader.ui b/resources/_ui/fcreader.ui
new file mode 100644
index 0000000..7c818de
--- /dev/null
+++ b/resources/_ui/fcreader.ui
@@ -0,0 +1,409 @@
+
+
+ FCEval_dialog
+
+
+
+ 0
+ 0
+ 457
+ 697
+
+
+
+ FC evaluation
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Input
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Add HDF files...
+
+
+ false
+
+
+
+ -
+
+
+ Add directory...
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ElideLeft
+
+
+
+ -
+
+
+ Overwrite prev. data
+
+
+
+
+
+
+ -
+
+
+ Evaluate region (empty values default to start/end)
+
+
+ true
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ start pos in µs
+
+
+
+ -
+
+
+ end pos in µs
+
+
+
+
+
+
+ -
+
+
+ Fit equation
+
+
+ -
+
+
+ y = M<sub>0</sub> exp[-(x/T<sub>1</sub>)<sup>β</sup>] + Off
+
+
+ kww_checkbox
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Check to fit a stretched exponential instead of exponential function.
+
+
+ Stretched exponential
+
+
+ true
+
+
+
+ -
+
+
+ 2
+
+ -
+
+
+ Plot:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+ T<sub>1</sub>
+
+
+ t1_cb
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ true
+
+
+
+ -
+
+
+ β
+
+
+ beta_cb
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+ M<sub>0</sub>
+
+
+ m0_cb
+
+
+
+ -
+
+
+ Offset
+
+
+
+
+
+
+
+
+ -
+
+
+ Output
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Change directory...
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ -
+
+
+ New graph
+
+
+ true
+
+
+
+ -
+
+
+
+ 16777215
+ 16777215
+
+
+
+
+
+
+
+ -
+
+
+ Save location
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ FCEval_dialog
+ accept()
+
+
+ 251
+ 594
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ FCEval_dialog
+ reject()
+
+
+ 319
+ 594
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/filedialog.ui b/resources/_ui/filedialog.ui
new file mode 100644
index 0000000..e4b4aad
--- /dev/null
+++ b/resources/_ui/filedialog.ui
@@ -0,0 +1,382 @@
+
+
+ *********************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+*********************************************************************
+ QFileDialog
+
+
+
+ 0
+ 0
+ 521
+ 316
+
+
+
+ true
+
+
+ -
+
+
+ Look in:
+
+
+
+ -
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ 50
+ 0
+
+
+
+
+ -
+
+
+ Back
+
+
+ Back
+
+
+ Go back
+
+
+ Alt+Left
+
+
+
+ -
+
+
+ Forward
+
+
+ Forward
+
+
+ Go forward
+
+
+ Alt+Right
+
+
+
+ -
+
+
+ Parent Directory
+
+
+ Parent Directory
+
+
+ Go to the parent directory
+
+
+ Alt+Up
+
+
+
+ -
+
+
+ Create New Folder
+
+
+ Create New Folder
+
+
+ Create a New Folder
+
+
+
+ -
+
+
+ List View
+
+
+ List View
+
+
+ Change to list view mode
+
+
+
+ -
+
+
+ Detail View
+
+
+ Detail View
+
+
+ Change to detail view mode
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Files
+
+
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Files
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Files of type:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+ QFileDialogTreeView
+ QTreeView
+
+
+
+ QFileDialogListView
+ QListView
+
+
+
+ QSidebar
+ QListWidget
+
+
+
+ QFileDialogLineEdit
+ QLineEdit
+
+
+
+ QFileDialogComboBox
+ QComboBox
+
+
+
+
+ lookInCombo
+ backButton
+ forwardButton
+ toParentButton
+ newFolderButton
+ listModeButton
+ detailModeButton
+ sidebar
+ treeView
+ listView
+ fileNameEdit
+ buttonBox
+ fileTypeCombo
+
+
+
+
diff --git a/resources/_ui/fitcreationdialog.ui b/resources/_ui/fitcreationdialog.ui
new file mode 100644
index 0000000..689327a
--- /dev/null
+++ b/resources/_ui/fitcreationdialog.ui
@@ -0,0 +1,419 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 614
+ 776
+
+
+
+ Dialog
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Description
+
+
+ true
+
+
+ false
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 3
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Group
+
+
+
+ -
+
+
+ Name
+
+
+ name_lineedit
+
+
+
+ -
+
+
+ -
+
+
+ Equation
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Variables
+
+
+ true
+
+
+ false
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ 4
+
+
+
+ Variable
+
+
+
+
+ Name
+
+
+
+
+ Lower bound
+
+
+
+
+ Upper bound
+
+
+
+
+ -
+
+
+ Add parameter
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ false
+
+
+ Qt::RightArrow
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Multiple choice part
+
+
+ true
+
+
+ false
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Add gyromagnetic ratios
+
+
+
+ -
+
+
+ QTabWidget::West
+
+
+ true
+
+
+
+ -
+
+
+ Add selection
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ Qt::RightArrow
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Available namespace
+
+
+ true
+
+
+ false
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Function y = func(x)
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+ QNamespaceWidget
+ QWidget
+
+ 1
+
+
+ CodeEditor
+ QPlainTextEdit
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/fitdialog.ui b/resources/_ui/fitdialog.ui
new file mode 100644
index 0000000..13e5882
--- /dev/null
+++ b/resources/_ui/fitdialog.ui
@@ -0,0 +1,313 @@
+
+
+ FitDialog
+
+
+
+ 0
+ 0
+ 347
+ 710
+
+
+
+ Form
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 341
+ 665
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 3
+
+ -
+
+ -
+
+ None
+
+
+ -
+
+ y
+
+
+ -
+
+ y²
+
+
+ -
+
+ Δy
+
+
+ -
+
+ log(y)
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+ New model
+
+
+
+ -
+
+
+ false
+
+
+ Delete model
+
+
+
+ -
+
+
+ Weight
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 0
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 1
+
+ -
+
+ -
+
+ Model a
+
+
+
+
+ -
+
+ -
+
+ Model a
+
+
+
+
+ -
+
+
+ Show model
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Default
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 24
+
+
+
+
+
+
+
+
+ -
+
+
+ 3
+
+
+ 0
+
+ -
+
+
+ font-weight: bold
+
+
+ Run fit!!!
+
+
+
+ -
+
+
+ font-weight: bold
+
+
+ Abort
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Preview
+
+
+
+ -
+
+
+ Update
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+
+
+
+
+
+
+ ExpandableWidget
+ QWidget
+
+ 1
+
+
+ QFunctionWidget
+ QWidget
+
+ 1
+
+
+
+
+
diff --git a/resources/_ui/fitdialog_window.ui b/resources/_ui/fitdialog_window.ui
new file mode 100644
index 0000000..b84dc75
--- /dev/null
+++ b/resources/_ui/fitdialog_window.ui
@@ -0,0 +1,514 @@
+
+
+ FitDialog
+
+
+ Qt::ApplicationModal
+
+
+
+ 0
+ 0
+ 828
+ 827
+
+
+
+ One must imagine Sisyphus happy.
+
+
+
+ :/logo.png :/logo.png
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Weight
+
+
+
+ -
+
+ -
+
+ None
+
+
+ -
+
+ y
+
+
+ -
+
+ y²
+
+
+ -
+
+ Δy
+
+
+ -
+
+ log(y)
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ QAbstractItemView::ExtendedSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ false
+
+
+ Qt::NoPen
+
+
+ 2
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 1
+
+ -
+
+ -
+
+ Model a
+
+
+
+
+ -
+
+
+ Show model
+
+
+
+ -
+
+ -
+
+ Model a
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Default
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+ New model
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ false
+
+
+ Delete model
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+ 2
+
+
+ 0
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Run fit!!!
+
+
+
+ -
+
+
+ Abort
+
+
+
+ -
+
+
+ Preview
+
+
+
+ :/fit_preview.png :/fit_preview.png
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ Nelder-Mead
+
+
+
+
+ true
+
+
+ ODR
+
+
+
+
+ true
+
+
+ true
+
+
+ Default stuff
+
+
+
+
+ Help!
+
+
+
+
+ Open editor...
+
+
+
+
+ Preview points...
+
+
+
+
+ Save current model...
+
+
+
+
+ true
+
+
+ None
+
+
+
+
+ true
+
+
+ true
+
+
+ Visible x range
+
+
+
+
+ true
+
+
+ Custom...
+
+
+
+
+
+ FunctionSelectionWidget
+ QWidget
+
+ 1
+
+
+
+
+
+
+
diff --git a/resources/_ui/fitfunctionwidget.ui b/resources/_ui/fitfunctionwidget.ui
new file mode 100644
index 0000000..345ac8c
--- /dev/null
+++ b/resources/_ui/fitfunctionwidget.ui
@@ -0,0 +1,208 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 314
+ 232
+
+
+
+
+ 0
+ 0
+
+
+
+ Form
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Select part to fit
+
+
+
+ -
+
+ -
+
+ Complex
+
+
+ -
+
+ Real
+
+
+ -
+
+ Imaginary
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Complex function found
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Use
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ false
+
+
+ Qt::RightArrow
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Equation
+
+
+ true
+
+
+
+ -
+
+
+ QComboBox::AdjustToContents
+
+
+ true
+
+ -
+
+ Add
+
+
+ -
+
+ Multiply
+
+
+ -
+
+ Subtract
+
+
+ -
+
+ Divide by
+
+
+
+
+ -
+
+
+
+
+
+
+ ExpandableWidget
+ QWidget
+
+ 1
+
+
+
+
+
diff --git a/resources/_ui/fitfuncwidget.ui b/resources/_ui/fitfuncwidget.ui
new file mode 100644
index 0000000..5c8bdcc
--- /dev/null
+++ b/resources/_ui/fitfuncwidget.ui
@@ -0,0 +1,127 @@
+
+
+ FormFit
+
+
+
+ 0
+ 0
+ 292
+ 477
+
+
+
+ Form
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 0
+
+
+
+ General settings
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ QAbstractScrollArea::AdjustToContents
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 284
+ 442
+
+
+
+
+
+
+
+
+
+ Data parameter
+
+
+ -
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ QAbstractScrollArea::AdjustToContents
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 272
+ 357
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/fitfuncwidget_old.ui b/resources/_ui/fitfuncwidget_old.ui
new file mode 100644
index 0000000..5183235
--- /dev/null
+++ b/resources/_ui/fitfuncwidget_old.ui
@@ -0,0 +1,156 @@
+
+
+ FormFit
+
+
+
+ 0
+ 0
+ 402
+ 523
+
+
+
+ Form
+
+
+
+ 6
+
+
+ 0
+
+
+ 6
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 0
+
+
+
+ General settings
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ Qt::ScrollBarAsNeeded
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ QAbstractScrollArea::AdjustToContents
+
+
+ false
+
+
+
+
+ 0
+ 0
+ 390
+ 478
+
+
+
+
+
+
+
+
+
+
+ Data parameter
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+ -
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 390
+ 444
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/fitmodelfixwidget.ui b/resources/_ui/fitmodelfixwidget.ui
new file mode 100644
index 0000000..905532e
--- /dev/null
+++ b/resources/_ui/fitmodelfixwidget.ui
@@ -0,0 +1,114 @@
+
+
+ FitFixParameter
+
+
+ Qt::WindowModal
+
+
+
+ 0
+ 0
+ 480
+ 267
+
+
+
+
+ 0
+ 0
+
+
+
+ Form
+
+
+ true
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 3
+
+
+ 0
+
+ -
+
+
+
+ 1
+ 10
+
+
+
+ QFrame::NoFrame
+
+
+ Parameter
+
+
+ 6
+
+
+
+ -
+
+
+ Unit
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 1
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/fitmodelwidget.ui b/resources/_ui/fitmodelwidget.ui
new file mode 100755
index 0000000..02069fb
--- /dev/null
+++ b/resources/_ui/fitmodelwidget.ui
@@ -0,0 +1,256 @@
+
+
+ FitParameter
+
+
+
+ 0
+ 0
+ 365
+ 78
+
+
+
+
+ 0
+ 0
+
+
+
+ Form
+
+
+
+ 1
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 28
+ 0
+
+
+
+ A
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Initial values
+
+
+
+
+
+ 0
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Fix
+
+
+
+ -
+
+
+ Global
+
+
+
+ -
+
+
+
+
+
+ QToolButton::InstantPopup
+
+
+ Qt::RightArrow
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p>Lower bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html>
+
+
+
+
+
+ true
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+ -
+
+
+ true
+
+
+ Textlabel
+
+
+ Qt::RichText
+
+
+ Qt::AlignCenter
+
+
+ 2
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p>Upper bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html>
+
+
+
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+
+ LineEdit
+ QLineEdit
+
+
+
+
+
+
diff --git a/resources/_ui/fitparametertable.ui b/resources/_ui/fitparametertable.ui
new file mode 100644
index 0000000..7c456f4
--- /dev/null
+++ b/resources/_ui/fitparametertable.ui
@@ -0,0 +1,100 @@
+
+
+ FitParameterDialog
+
+
+
+ 0
+ 0
+ 898
+ 583
+
+
+
+ Fitparameter
+
+
+ -
+
+
+ -
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Copy
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Close
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ FitParameterDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ FitParameterDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/fitparameterwidget.ui b/resources/_ui/fitparameterwidget.ui
new file mode 100644
index 0000000..41f3e81
--- /dev/null
+++ b/resources/_ui/fitparameterwidget.ui
@@ -0,0 +1,127 @@
+
+
+ FormFit
+
+
+
+ 0
+ 0
+ 292
+ 477
+
+
+
+ Form
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 0
+
+
+
+ General settings
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ QAbstractScrollArea::AdjustToContents
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 284
+ 442
+
+
+
+
+
+
+
+
+
+ Data parameter
+
+
+ -
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ QAbstractScrollArea::AdjustToContents
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 272
+ 392
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/fitresult.ui b/resources/_ui/fitresult.ui
new file mode 100644
index 0000000..06dc1d2
--- /dev/null
+++ b/resources/_ui/fitresult.ui
@@ -0,0 +1,359 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 817
+ 584
+
+
+
+ Fit results
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 400
+ 16777215
+
+
+
+
+ 200
+ 0
+
+
+
+ QComboBox::AdjustToMinimumContentsLength
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 0
+
+
+ 0
+
+
+
+
+ 0
+ 0
+ 399
+ 414
+
+
+
+ Plot
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ -
+
+
+ Qt::RightToLeft
+
+
+ logarithmic y axis
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 399
+ 414
+
+
+
+ Statistics
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ QFrame::Box
+
+
+ Qt::NoPen
+
+
+ 1
+
+
+ false
+
+
+ true
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 399
+ 414
+
+
+
+ Correlations
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ QFrame::Box
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ Qt::NoPen
+
+
+ true
+
+
+ false
+
+
+
+ Parameter 1
+
+
+
+
+ Parameter 2
+
+
+
+
+ Corr.
+
+
+
+
+ Partial Corr.
+
+
+
+
+
+
+
+
+ -
+
+
+ 3
+
+ -
+
+
+ Plot partial functions
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Location of parameters:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ New graph
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 3
+
+ -
+
+
+ Reject this fit
+
+
+
+ -
+
+
+ Delete previous fits
+
+
+
+
+
+ -
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Retry
+
+
+
+ -
+
+
+ Qt::ScrollBarAsNeeded
+
+
+ QAbstractScrollArea::AdjustIgnored
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ true
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectColumns
+
+
+ false
+
+
+ 0
+
+
+ false
+
+
+
+
+
+
+
+ GraphicsLayoutWidget
+ QGraphicsView
+
+
+
+ ElideComboBox
+ QComboBox
+
+
+
+
+
+
diff --git a/resources/_ui/ftdialog.ui b/resources/_ui/ftdialog.ui
new file mode 100644
index 0000000..c18c648
--- /dev/null
+++ b/resources/_ui/ftdialog.ui
@@ -0,0 +1,121 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Logarithmic Fourier
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ -
+
+ -
+
+ Real
+
+
+ -
+
+ Imag
+
+
+ -
+
+ Complex
+
+
+
+
+ -
+
+
+ 3
+
+
+ 0
+
+ -
+
+
+ New graph
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/function_tree_widget.ui b/resources/_ui/function_tree_widget.ui
new file mode 100644
index 0000000..005210d
--- /dev/null
+++ b/resources/_ui/function_tree_widget.ui
@@ -0,0 +1,208 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 314
+ 232
+
+
+
+
+ 0
+ 0
+
+
+
+ Form
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Select part to fit
+
+
+
+ -
+
+ -
+
+ Complex
+
+
+ -
+
+ Real
+
+
+ -
+
+ Imaginary
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Complex function found
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Use
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ false
+
+
+ Qt::RightArrow
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Equation
+
+
+ true
+
+
+
+ -
+
+
+ QComboBox::AdjustToContents
+
+
+ true
+
+ -
+
+ Add
+
+
+ -
+
+ Multiply
+
+
+ -
+
+ Subtract
+
+
+ -
+
+ Divide by
+
+
+
+
+ -
+
+
+
+
+
+
+ ExpandableWidget
+ QWidget
+
+ 1
+
+
+
+
+
diff --git a/resources/_ui/gol.ui b/resources/_ui/gol.ui
new file mode 100644
index 0000000..489806f
--- /dev/null
+++ b/resources/_ui/gol.ui
@@ -0,0 +1,486 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 883
+ 732
+
+
+
+ Game Of Life
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Motion blur
+
+
+ true
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Scorched earth
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Nothing
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Faster
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ Qt::RightArrow
+
+
+
+ -
+
+
+ 10 steps / s
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Slower
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ Qt::LeftArrow
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 0 step
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Pause
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Coverage:
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ background-color: transparent
+
+
+ QFrame::NoFrame
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::Box
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 6
+
+
+ 3
+
+ -
+
+ -
+
+
+ Rule
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Size
+
+
+
+ -
+
+
+ 1
+
+
+ 600
+
+
+
+
+
+
+ -
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Add Random
+
+
+
+ -
+
+
+ Remove Random
+
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Survival
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Birth
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ -
+
+ -
+
+
+ Width
+
+
+
+ -
+
+
+ 600
+
+
+ 100
+
+
+
+ -
+
+
+ Height
+
+
+
+ -
+
+
+ 600
+
+
+ 100
+
+
+
+
+
+ -
+
+ -
+
+ Random
+
+
+ -
+
+ Circle
+
+
+ -
+
+ Square
+
+
+ -
+
+ Diamond
+
+
+ -
+
+ Plus
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Start
+
+
+
+
+
+
+ -
+
+
+ Hide options
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/gracemsgdialog.ui b/resources/_ui/gracemsgdialog.ui
new file mode 100644
index 0000000..1354213
--- /dev/null
+++ b/resources/_ui/gracemsgdialog.ui
@@ -0,0 +1,79 @@
+
+
+ GraceMsgDialog
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Goodness gracious, file already exists.
+
+
+ -
+
+
+ -
+
+
+ Add to graph
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Overwrite file
+
+
+ true
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Replace sets
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ 2
+
+
+ false
+
+
+
+
+
+ -
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/gracereader.ui b/resources/_ui/gracereader.ui
new file mode 100644
index 0000000..50213ba
--- /dev/null
+++ b/resources/_ui/gracereader.ui
@@ -0,0 +1,187 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 400
+ 613
+
+
+
+ Load data from agr
+
+
+ -
+
+
+ Only data will be loaded, no line and symbol properties!
+
+
+
+ -
+
+
+ QAbstractItemView::ExtendedSelection
+
+
+ false
+
+
+
+ 1
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::SelectRows
+
+
+ Qt::NoPen
+
+
+ 4
+
+
+ 2
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+
+
+
+
+
+ -
+
+ Symbol
+
+
+ ItemIsSelectable|ItemIsEnabled
+
+
+ -
+
+
+
+
+ ItemIsSelectable|ItemIsEnabled
+
+
+ -
+
+ Symbol color
+
+
+ ItemIsSelectable|ItemIsEnabled
+
+
+ -
+
+
+
+
+ ItemIsSelectable|ItemIsEnabled
+
+
+ -
+
+ Linestyle
+
+
+ ItemIsSelectable|ItemIsEnabled
+
+
+ -
+
+
+
+
+ ItemIsSelectable|ItemIsEnabled
+
+
+ -
+
+ Line color
+
+
+ ItemIsSelectable|ItemIsEnabled
+
+
+ -
+
+
+
+
+ ItemIsEnabled
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/graph.ui b/resources/_ui/graph.ui
new file mode 100644
index 0000000..7c829d1
--- /dev/null
+++ b/resources/_ui/graph.ui
@@ -0,0 +1,577 @@
+
+
+ GraphWindow
+
+
+
+ 0
+ 0
+ 680
+ 520
+
+
+
+
+ 300
+ 10
+
+
+
+ Form
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 1
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Change x axis linear <-> logarithmic
+
+
+ Log X
+
+
+
+ 16
+ 16
+
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Change y axis linear <-> logarithmic
+
+
+ Log Y
+
+
+
+ 16
+ 16
+
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Show/hide grid
+
+
+ Grid
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Change background
+
+
+ Black/white
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Change legend
+
+
+ Legend
+
+
+
+ 16
+ 16
+
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Show/hide imaginary part
+
+
+ Imaginary
+
+
+
+ 16
+ 16
+
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Show/hide real part
+
+
+ Real
+
+
+
+ 16
+ 16
+
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Show/hide errorbars
+
+
+ Errorbars
+
+
+
+ 16
+ 16
+
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Limits
+
+
+ true
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+ Qt::RightArrow
+
+
+
+ -
+
+
+ Labels
+
+
+ true
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+ Qt::RightArrow
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 2
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+ -
+
+
+ X:
+
+
+ xmin_lineedit
+
+
+
+ -
+
+
+ -
+
+
+ ---
+
+
+ xmax_lineedit
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Y:
+
+
+ ymin_lineedit
+
+
+
+ -
+
+
+ -
+
+
+ ---
+
+
+ ymax_lineedit
+
+
+
+ -
+
+
+ -
+
+
+ Apply
+
+
+
+ ../../../../.designer/backup ../../../../.designer/backup
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 2
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+ -
+
+
+ Title
+
+
+ title_lineedit
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ X Axis
+
+
+ xaxis_linedit
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Y Axis
+
+
+ yaxis_linedit
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ 3
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Show legend
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+ PlotWidget
+ QGraphicsView
+
+
+
+
+ logx_button
+ logy_button
+ gridbutton
+ legend_button
+ imag_button
+ error_button
+ limit_button
+ label_button
+ xmin_lineedit
+ xmax_lineedit
+ ymin_lineedit
+ ymax_lineedit
+ title_lineedit
+ xaxis_linedit
+ yaxis_linedit
+
+
+
+
diff --git a/resources/_ui/guidelinewidget.ui b/resources/_ui/guidelinewidget.ui
new file mode 100644
index 0000000..85d5d6b
--- /dev/null
+++ b/resources/_ui/guidelinewidget.ui
@@ -0,0 +1,213 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 431
+ 799
+
+
+
+ Form
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+ -
+
+ Vertical
+
+
+ -
+
+ Horizontal
+
+
+
+
+ -
+
+
+ -
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Position
+
+
+ vh_pos_lineEdit
+
+
+
+ -
+
+
+ 0
+
+
+
+
+
+
+ -
+
+
+ Drag enabled
+
+
+ true
+
+
+
+
+
+ -
+
+
+ -
+
+
+ Create line
+
+
+
+ -
+
+
+ Comment
+
+
+ comment_lineEdit
+
+
+
+ -
+
+
+ -
+
+
+ Color
+
+
+ color_comboBox
+
+
+
+ -
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ 2
+
+
+ true
+
+
+ true
+
+
+
+ Pos.
+
+
+
+
+ Comment
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+
+ ColorListEditor
+ QComboBox
+
+
+
+
+ graph_comboBox
+ mode_comboBox
+ vh_pos_lineEdit
+ drag_checkBox
+ comment_lineEdit
+ color_comboBox
+ pushButton
+ tableWidget
+
+
+
+
diff --git a/resources/_ui/hdftree.ui b/resources/_ui/hdftree.ui
new file mode 100755
index 0000000..5ab5c3c
--- /dev/null
+++ b/resources/_ui/hdftree.ui
@@ -0,0 +1,131 @@
+
+
+ Hdf_Dialog
+
+
+
+ 0
+ 0
+ 460
+ 772
+
+
+
+ View HDF file
+
+
+
+ 3
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+ -
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 2
+
+ -
+
+
+ -
+
+
+ Label
+
+
+
+ -
+
+
+ Group
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+ ExpandableWidget
+ QWidget
+
+ 1
+
+
+
+
+
+ buttonBox
+ rejected()
+ Hdf_Dialog
+ close()
+
+
+ 181
+ 427
+
+
+ 181
+ 225
+
+
+
+
+ buttonBox
+ accepted()
+ Hdf_Dialog
+ accept()
+
+
+ 229
+ 750
+
+
+ 229
+ 385
+
+
+
+
+
diff --git a/resources/_ui/integral_widget.ui b/resources/_ui/integral_widget.ui
new file mode 100644
index 0000000..70c78ed
--- /dev/null
+++ b/resources/_ui/integral_widget.ui
@@ -0,0 +1,85 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 397
+ 681
+
+
+
+ Form
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ -
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ QAbstractItemView::SelectRows
+
+
+ true
+
+
+
+ 1
+
+
+
+
+ -
+
+
+ 3
+
+ -
+
+
+ Save integrals as dataset
+
+
+
+ -
+
+
+ Apply
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/integratederive_dialog.ui b/resources/_ui/integratederive_dialog.ui
new file mode 100644
index 0000000..c1ff520
--- /dev/null
+++ b/resources/_ui/integratederive_dialog.ui
@@ -0,0 +1,162 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 400
+ 308
+
+
+
+ Dialog
+
+
+ -
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Limits
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ Full
+
+
+ true
+
+
+
+
+
+
+ -
+
+ -
+
+ Real
+
+
+ -
+
+ Imag
+
+
+ -
+
+ Complex
+
+
+
+
+ -
+
+
+ use logarithmic x axis
+
+
+
+ -
+
+
+ 3
+
+
+ 0
+
+ -
+
+
+ New graph
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/interpol_dialog.ui b/resources/_ui/interpol_dialog.ui
new file mode 100644
index 0000000..8aabd2f
--- /dev/null
+++ b/resources/_ui/interpol_dialog.ui
@@ -0,0 +1,327 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 416
+ 494
+
+
+
+ Data interpolation
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ Source data
+
+
+
+ -
+
+
+
+
+
+ Spline
+
+
+ interp_comboBox
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ If your data is on a logarithmic scale in y, check this box
+
+
+ use log(y)
+
+
+
+ -
+
+
+
+
+ -
+
+ Cubic
+
+
+ -
+
+ Linear
+
+
+
+
+ -
+
+
+ Accept to create new data sets.
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ New x axis
+
+
+ xaxis_comboBox
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Select sets that shall be interpolated. No selection will create interpolations of all visible sets.
+
+
+
+ -
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Start
+
+
+ start_lineEdit
+
+
+
+ -
+
+
+ -
+
+
+ Stop
+
+
+ stop_lineEdit
+
+
+
+ -
+
+
+ -
+
+
+ Steps
+
+
+ step_lineEdit
+
+
+
+ -
+
+
+ -
+
+
+ log-spaced?
+
+
+
+
+
+
+ -
+
+ -
+
+ new values
+
+
+ -
+
+ from data
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Add interpolated data to
+
+
+ dest_combobox
+
+
+
+ -
+
+
+ use log(x)
+
+
+
+
+
+
+ listWidget
+ ylog_checkBox
+ interp_comboBox
+ xaxis_comboBox
+ start_lineEdit
+ stop_lineEdit
+ step_lineEdit
+ logspace_checkBox
+ graph_combobox
+ set_combobox
+ dest_combobox
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 251
+ 490
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 319
+ 490
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/lineedit_dialog.ui b/resources/_ui/lineedit_dialog.ui
new file mode 100644
index 0000000..5c87f90
--- /dev/null
+++ b/resources/_ui/lineedit_dialog.ui
@@ -0,0 +1,74 @@
+
+
+ LineEdit_Dialog
+
+
+
+ 0
+ 0
+ 400
+ 84
+
+
+
+ Dialog
+
+
+
+ QFormLayout::ExpandingFieldsGrow
+
+ -
+
+
+ Label
+
+
+
+ -
+
+
+ -
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ LineEdit_Dialog
+ accept()
+
+
+ 199
+ 60
+
+
+ 199
+ 41
+
+
+
+
+ buttonBox
+ rejected()
+ LineEdit_Dialog
+ reject()
+
+
+ 199
+ 60
+
+
+ 199
+ 41
+
+
+
+
+
diff --git a/resources/_ui/mean_form.ui b/resources/_ui/mean_form.ui
new file mode 100644
index 0000000..1bcb145
--- /dev/null
+++ b/resources/_ui/mean_form.ui
@@ -0,0 +1,117 @@
+
+
+ mean_form
+
+
+
+ 0
+ 0
+ 712
+ 34
+
+
+
+ Form
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+ -
+
+
+ TextLabel
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Digit
+
+
+ true
+
+
+
+ -
+
+
+ 1
+
+
+
+ -
+
+
+ Data
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 2
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ -
+
+
+ QComboBox::AdjustToContents
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/meandialog.ui b/resources/_ui/meandialog.ui
new file mode 100644
index 0000000..19960eb
--- /dev/null
+++ b/resources/_ui/meandialog.ui
@@ -0,0 +1,154 @@
+
+
+ calc_means_dialog
+
+
+
+ 0
+ 0
+ 481
+ 322
+
+
+
+ Mean times
+
+
+
+ 3
+
+ -
+
+
+ -
+
+
+ -
+
+ -
+
+ -
+
+ Function value: τ
+
+
+ -
+
+ Peak time: τₚ
+
+
+ -
+
+ Arithmetic mean: ⟨τ⟩
+
+
+ -
+
+ Geometric mean: exp( ⟨ln(τ)⟩ )
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 20
+
+
+
+ ➝
+
+
+
+ -
+
+
+ 1
+
+ -
+
+ Function value: τ
+
+
+ -
+
+ Peak time: τₚ
+
+
+ -
+
+ Arithmetic mean: ⟨τ⟩
+
+
+ -
+
+ Geometric mean: exp( ⟨ln(τ)⟩ )
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ 0
+
+ -
+
+
+ New graph
+
+
+
+ -
+
+
+
+
+ -
+
+
+ QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/modelwidget.ui b/resources/_ui/modelwidget.ui
new file mode 100755
index 0000000..022c32a
--- /dev/null
+++ b/resources/_ui/modelwidget.ui
@@ -0,0 +1,31 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 188
+ 44
+
+
+
+ Form
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/resources/_ui/move_dialog.ui b/resources/_ui/move_dialog.ui
new file mode 100644
index 0000000..a1a7e71
--- /dev/null
+++ b/resources/_ui/move_dialog.ui
@@ -0,0 +1,130 @@
+
+
+ MoveDialog
+
+
+
+ 0
+ 0
+ 395
+ 345
+
+
+
+ Insert Reel 2 Real song.
+
+
+
+ 1
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ To
+
+
+ tocomboBox
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ From
+
+
+ fromcomboBox
+
+
+
+ -
+
+
+ 0
+
+ -
+
+
+ Copy
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Move
+
+
+ true
+
+
+ buttonGroup
+
+
+
+
+
+ -
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ QAbstractItemView::MultiSelection
+
+
+
+
+
+
+ fromcomboBox
+ listWidget
+ tocomboBox
+ buttonBox
+
+
+
+
+
+
+
diff --git a/resources/_ui/namespace_widget.ui b/resources/_ui/namespace_widget.ui
new file mode 100644
index 0000000..717fe8c
--- /dev/null
+++ b/resources/_ui/namespace_widget.ui
@@ -0,0 +1,109 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Form
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 3
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ Qt::ElideNone
+
+
+ 2
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+
+ Neue Zeile
+
+
+
+
+ Neue Zeile
+
+
+
+
+ Neue Zeile
+
+
+
+
+ Neue Zeile
+
+
+
+
+ Neue Zeile
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/option_selection.ui b/resources/_ui/option_selection.ui
new file mode 100644
index 0000000..41c6d2c
--- /dev/null
+++ b/resources/_ui/option_selection.ui
@@ -0,0 +1,91 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 400
+ 182
+
+
+
+ Form
+
+
+ -
+
+
+
+ Name
+
+
+
+
+ Value
+
+
+
+
+ -
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Nice name
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ TextLabel
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+ -
+
+
+ Add option
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/parameterform.ui b/resources/_ui/parameterform.ui
new file mode 100644
index 0000000..5e7d6ea
--- /dev/null
+++ b/resources/_ui/parameterform.ui
@@ -0,0 +1,104 @@
+
+
+ parameterform
+
+
+ Qt::WindowModal
+
+
+
+ 0
+ 0
+ 290
+ 37
+
+
+
+
+ 0
+ 0
+
+
+
+ Form
+
+
+ true
+
+
+
+ 2
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 1
+ 10
+
+
+
+ QFrame::NoFrame
+
+
+ Name
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 65
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 1
+
+
+
+ -
+
+
+ Fix?
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/phase_corr_dialog.ui b/resources/_ui/phase_corr_dialog.ui
new file mode 100644
index 0000000..20d14da
--- /dev/null
+++ b/resources/_ui/phase_corr_dialog.ui
@@ -0,0 +1,197 @@
+
+
+ SignalEdit
+
+
+
+ 0
+ 0
+ 919
+ 595
+
+
+
+
+ 0
+ 0
+
+
+
+ Phase correction
+
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::ImhDigitsOnly
+
+
+ 0
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ -180.000000000000000
+
+
+ 180.000000000000000
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ -360.000000000000000
+
+
+ 360.000000000000000
+
+
+
+ -
+
+
+ Pivot
+
+
+
+ -
+
+
+ Phase 1
+
+
+
+ -
+
+
+ Phase 0
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+ PlotWidget
+ QGraphicsView
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ SignalEdit
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ SignalEdit
+ close()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/plotConfigTemplate.ui b/resources/_ui/plotConfigTemplate.ui
new file mode 100644
index 0000000..e718d1d
--- /dev/null
+++ b/resources/_ui/plotConfigTemplate.ui
@@ -0,0 +1,363 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 481
+ 840
+
+
+
+ PyQtGraph
+
+
+
+
+ 0
+ 640
+ 242
+ 182
+
+
+
+ Display averages of the curves displayed in this plot. The parameter list allows you to choose parameters to average over (if any are available).
+
+
+ Average
+
+
+ true
+
+
+ false
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+
+
+
+
+ 10
+ 140
+ 191
+ 171
+
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Plot only the portion of each curve that is visible. This assumes X values are uniformly spaced.
+
+
+ Clip to View
+
+
+
+ -
+
+
+ If multiple curves are displayed in this plot, check this box to limit the number of traces that are displayed.
+
+
+ Max Traces:
+
+
+
+ -
+
+
+ Downsample
+
+
+
+ -
+
+
+ Downsample by drawing a saw wave that follows the min and max of the original data. This method produces the best visual representation of the data but is slower.
+
+
+ Peak
+
+
+ true
+
+
+
+ -
+
+
+ If multiple curves are displayed in this plot, check "Max Traces" and set this value to limit the number of traces that are displayed.
+
+
+
+ -
+
+
+ If MaxTraces is checked, remove curves from memory after they are hidden (saves memory, but traces can not be un-hidden).
+
+
+ Forget hidden traces
+
+
+
+ -
+
+
+ Downsample by taking the mean of N samples.
+
+
+ Mean
+
+
+
+ -
+
+
+ Downsample by taking the first of N samples. This method is fastest and least accurate.
+
+
+ Subsample
+
+
+
+ -
+
+
+ Automatically downsample data based on the visible range. This assumes X values are uniformly spaced.
+
+
+ Auto
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Maximum
+
+
+
+ 30
+ 20
+
+
+
+
+ -
+
+
+ Downsample data before plotting. (plot every Nth sample)
+
+
+ x
+
+
+ 1
+
+
+ 100000
+
+
+ 1
+
+
+
+
+
+
+
+
+ 10
+ 10
+ 171
+ 101
+
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Log Y
+
+
+
+ -
+
+
+ Log X
+
+
+
+ -
+
+
+ Power Spectrum (FFT)
+
+
+
+ -
+
+
+ dy/dx
+
+
+
+ -
+
+
+ Y vs. Y'
+
+
+
+
+
+
+
+
+ 10
+ 550
+ 234
+ 58
+
+
+
+ Points
+
+
+ true
+
+
+ -
+
+
+ Auto
+
+
+ true
+
+
+
+
+
+
+
+
+ 10
+ 460
+ 221
+ 81
+
+
+
+ -
+
+
+ Show X Grid
+
+
+
+ -
+
+
+ Show Y Grid
+
+
+
+ -
+
+
+ 255
+
+
+ 128
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Opacity
+
+
+
+
+
+
+
+
+ 10
+ 390
+ 234
+ 60
+
+
+
+ Alpha
+
+
+ true
+
+
+ -
+
+
+ Auto
+
+
+ false
+
+
+
+ -
+
+
+ 1000
+
+
+ 1000
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/pokemon.ui b/resources/_ui/pokemon.ui
new file mode 100644
index 0000000..7bcb2c6
--- /dev/null
+++ b/resources/_ui/pokemon.ui
@@ -0,0 +1,194 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 400
+ 359
+
+
+
+
+ 0
+ 0
+
+
+
+ Random Pokémon
+
+
+ -
+
+
+ -1
+
+
+
+ -
+
+ -
+
+
+ National-Dex
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Name
+
+
+ name
+
+
+
+ -
+
+
+ -
+
+
+ Kategorie
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Typ
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Größe
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Gewicht
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Farbe
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ Mehr...
+
+
+
+ -
+
+
+ TextLabel
+
+
+
+
+
+ -
+
+ -
+
+
+ Prev.
+
+
+
+ -
+
+
+ Next
+
+
+
+ -
+
+
+ QDialogButtonBox::Close|QDialogButtonBox::Retry
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/propwidget.ui b/resources/_ui/propwidget.ui
new file mode 100644
index 0000000..d0f94cb
--- /dev/null
+++ b/resources/_ui/propwidget.ui
@@ -0,0 +1,93 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Form
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+ -
+
+
+ 0
+
+
+
+ General
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+
+
+
+ Symbol
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/ptstab.ui b/resources/_ui/ptstab.ui
new file mode 100644
index 0000000..7ae4e05
--- /dev/null
+++ b/resources/_ui/ptstab.ui
@@ -0,0 +1,268 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 316
+ 747
+
+
+
+ Form
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Edit by entering new value:
+Single number for points (e.g. 1e-6);
+two numbers separated by space for regions (e.g. 1e-6 5e-6).
+Changing between regions and points is NOT possible
+
+
+ QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed
+
+
+
+ -
+
+
+ Average
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ pts
+
+
+ -
+
+
+ 999
+
+
+
+ -
+
+
+ pts
+
+
+ +
+
+
+ 999
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+ -
+
+ Mean
+
+
+ -
+
+ Sum
+
+
+ -
+
+ Integral
+
+
+
+
+
+
+
+ -
+
+
+ Special value
+
+
+ true
+
+
+ false
+
+
+
+ 2
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Automatic selection of respective points
+
+ -
+
+ max(y)
+
+
+ -
+
+ max(abs(y))
+
+
+ -
+
+ min(y)
+
+
+ -
+
+ min(abs(y))
+
+
+
+
+
+
+
+ -
+
+
+ Result
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ x
+
+
+
+ -
+
+
+ y
+
+
+ true
+
+
+
+ -
+
+
+ New graph?
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+
+ -
+
+
+ 2
+
+
+ 0
+
+ -
+
+
+ Apply
+
+
+
+ . .
+
+
+
+ -
+
+
+ Delete selected
+
+
+
+ . .
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/qfiledialog.ui b/resources/_ui/qfiledialog.ui
new file mode 100644
index 0000000..ad58d8e
--- /dev/null
+++ b/resources/_ui/qfiledialog.ui
@@ -0,0 +1,355 @@
+
+
+ *********************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+*********************************************************************
+ QFileDialog
+
+
+
+ 0
+ 0
+ 521
+ 316
+
+
+
+ true
+
+
+ -
+
+
+ Look in:
+
+
+
+ -
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ 50
+ 0
+
+
+
+
+ -
+
+
+ Back
+
+
+ Back
+
+
+ Go back
+
+
+ Alt+Left
+
+
+
+ -
+
+
+ Forward
+
+
+ Forward
+
+
+ Go forward
+
+
+ Alt+Right
+
+
+
+ -
+
+
+ Parent Directory
+
+
+ Parent Directory
+
+
+ Go to the parent directory
+
+
+ Alt+Up
+
+
+
+ -
+
+
+ Create New Folder
+
+
+ Create New Folder
+
+
+ Create a New Folder
+
+
+
+ -
+
+
+ List View
+
+
+ List View
+
+
+ Change to list view mode
+
+
+
+ -
+
+
+ Detail View
+
+
+ Detail View
+
+
+ Change to detail view mode
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+
+ Sidebar
+
+
+ List of places and bookmarks
+
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Files
+
+
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Files
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Files of type:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ lookInCombo
+ backButton
+ forwardButton
+ toParentButton
+ newFolderButton
+ listModeButton
+ detailModeButton
+ sidebar
+ treeView
+ listView
+ fileNameEdit
+ buttonBox
+ fileTypeCombo
+
+
+
+
diff --git a/resources/_ui/save_fit_parameter.ui b/resources/_ui/save_fit_parameter.ui
new file mode 100644
index 0000000..ca02c31
--- /dev/null
+++ b/resources/_ui/save_fit_parameter.ui
@@ -0,0 +1,234 @@
+
+
+ fitparameter_save_dialog
+
+
+
+ 0
+ 0
+ 578
+ 537
+
+
+
+ Save parameter
+
+
+ -
+
+
+ -
+
+
+ Save path...
+
+
+
+ -
+
+
+ #
+
+
+
+ -
+
+
+ 8
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Columns
+
+
+ col_line
+
+
+
+ -
+
+ -
+
+
+ e.g. x; 1000/x; mean(T_1,beta); {beta/beta_1}; M_0*x
+
+
+
+ -
+
+
+ Usage?
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Header ends always with column description. Additional header lines are commented automatically
+
+
+
+ -
+
+
+ Precision
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 1e308
+
+
+
+ -
+
+
+ Comment
+
+
+ comment_line
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ QAbstractItemView::DragOnly
+
+
+ true
+
+
+ QAbstractItemView::SelectRows
+
+
+ 2
+
+
+ false
+
+
+ true
+
+
+
+
+
+ -
+
+
+ Number of significant digits.
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ Missing values
+
+
+ missing_value_line
+
+
+
+ -
+
+
+ Header
+
+
+
+
+
+
+ save_path_button
+ save_path_line
+ tableWidget
+ header_edit
+ missing_value_line
+ comment_line
+
+
+
+
+ buttonBox
+ accepted()
+ fitparameter_save_dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ fitparameter_save_dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/save_fitmodel_dialog.ui b/resources/_ui/save_fitmodel_dialog.ui
new file mode 100644
index 0000000..31187c6
--- /dev/null
+++ b/resources/_ui/save_fitmodel_dialog.ui
@@ -0,0 +1,125 @@
+
+
+ SaveDialog
+
+
+
+ 0
+ 0
+ 400
+ 166
+
+
+
+ Dialog
+
+
+ -
+
+
+ Group
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ -
+
+
+ Name
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ -
+
+
+ OK
+
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ SaveDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ SaveDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/save_options.ui b/resources/_ui/save_options.ui
new file mode 100644
index 0000000..9fecdae
--- /dev/null
+++ b/resources/_ui/save_options.ui
@@ -0,0 +1,35 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 400
+ 58
+
+
+
+ Form
+
+
+ -
+
+
+ <html><head/><body><p>Use <span style=" font-weight:600;"><label></span> as placeholder in filename for data label.</p></body></html>
+
+
+
+ -
+
+
+ Replace spaces with underscore
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/saveoptions.ui b/resources/_ui/saveoptions.ui
new file mode 100644
index 0000000..3a2af65
--- /dev/null
+++ b/resources/_ui/saveoptions.ui
@@ -0,0 +1,51 @@
+
+
+ Frame
+
+
+
+ 0
+ 0
+ 464
+ 62
+
+
+
+ Frame
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+ -
+
+
+ <html><head/><body><p>Use <span style=" font-weight:600;"><label></span> as placeholder in filename. (e.g. <span style=" font-style:italic;">t1_<label>.dat</span>)</p></body></html>
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Replace spaces with underscore
+
+
+ true
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/sdmodelwidget.ui b/resources/_ui/sdmodelwidget.ui
new file mode 100644
index 0000000..24b8d25
--- /dev/null
+++ b/resources/_ui/sdmodelwidget.ui
@@ -0,0 +1,124 @@
+
+
+ SDParameter
+
+
+ Qt::WindowModal
+
+
+
+ 0
+ 0
+ 290
+ 37
+
+
+
+
+ 0
+ 0
+
+
+
+ Form
+
+
+ true
+
+
+
+ 0
+
+
+ 1
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 6
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 1
+
+
+
+ -
+
+
+
+ 1
+ 10
+
+
+
+ QFrame::NoFrame
+
+
+ Fitparameter
+
+
+
+ -
+
+
+ Fix?
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+ LineEdit
+ QLineEdit
+
+
+
+
+
+
diff --git a/resources/_ui/selection_widget.ui b/resources/_ui/selection_widget.ui
new file mode 100644
index 0000000..768ba6a
--- /dev/null
+++ b/resources/_ui/selection_widget.ui
@@ -0,0 +1,59 @@
+
+
+ SelectionWidget
+
+
+
+ 0
+ 0
+ 367
+ 43
+
+
+
+
+ 0
+ 0
+
+
+
+ Form
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ TextLabel
+
+
+
+ -
+
+
+ QComboBox::AdjustToMinimumContentsLength
+
+
+ true
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/setbyfunction_dialog.ui b/resources/_ui/setbyfunction_dialog.ui
new file mode 100644
index 0000000..3b2e1d4
--- /dev/null
+++ b/resources/_ui/setbyfunction_dialog.ui
@@ -0,0 +1,437 @@
+
+
+ NewCurveDialog
+
+
+
+ 0
+ 0
+ 648
+ 578
+
+
+
+ Create new data by function
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Control variable i
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+ -
+
+
+ 0
+
+
+
+
+
+
+ -
+
+
+ 10
+
+
+
+ -
+
+
+ Stop at
+
+
+ lineEdit_4
+
+
+
+ -
+
+
+ 1
+
+
+
+ -
+
+
+ Start at
+
+
+ lineEdit_3
+
+
+
+ -
+
+
+ # points
+
+
+ lineEdit_5
+
+
+
+ -
+
+
+ Qt::RightToLeft
+
+
+ Logarithmic?
+
+
+
+
+
+
+ -
+
+
+ Expressions
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 2
+
+ -
+
+
+ x**2
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Check
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ i
+
+
+
+ -
+
+
+ x =
+
+
+ lineEdit
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ y =
+
+
+ lineEdit_2
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+
+
+
+ -
+
+
+ Look
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 2
+
+ -
+
+
+ 1
+
+
+ 20.000000000000000
+
+
+ 0.500000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ 100
+
+
+ 10
+
+
+
+ -
+
+
+ Line
+
+
+ comboBox_2
+
+
+
+ -
+
+
+ -1
+
+
+
+ -
+
+
+ -1
+
+
+
+ -
+
+
+ Symbol
+
+
+ comboBox
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ Designation
+
+
+ -
+
+
+ Name
+
+
+ lineEdit_6
+
+
+
+ -
+
+
+ -
+
+
+ Graph
+
+
+ comboBox_3
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ SymbolStyleEditor
+ QComboBox
+
+
+
+ LineStyleEditor
+ QComboBox
+
+
+
+ ColorListEditor
+ QComboBox
+
+
+
+
+ lineEdit_3
+ lineEdit_4
+ lineEdit_5
+ checkBox
+ lineEdit
+ comboBox
+ spinBox
+ comboBox_2
+ doubleSpinBox
+ lineEdit_6
+ comboBox_3
+ buttonBox
+
+
+
+
+ buttonBox
+ accepted()
+ NewCurveDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ NewCurveDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/shift_scale_dialog.ui b/resources/_ui/shift_scale_dialog.ui
new file mode 100644
index 0000000..a3ac3a8
--- /dev/null
+++ b/resources/_ui/shift_scale_dialog.ui
@@ -0,0 +1,563 @@
+
+
+ shift_dialog
+
+
+
+ 0
+ 0
+ 959
+ 639
+
+
+
+ Shift + Scale
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::Horizontal
+
+
+
+
+ 0
+ 0
+
+
+
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+ Shift
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Global
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p><span style=" font-weight:600;">x</span></p></body></html>
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ x_shift_spinbox
+
+
+
+ -
+
+
+
+ 150
+ 0
+
+
+
+ 3
+
+
+ 0.000000000000000
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p><span style=" font-weight:600;">y</span></p></body></html>
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ y_shift_spinbox
+
+
+
+ -
+
+
+
+ 150
+ 0
+
+
+
+ 3
+
+
+ 0.000000000000000
+
+
+
+ -
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ 3
+
+
+ false
+
+
+
+ Data
+
+
+
+
+ x
+
+
+
+
+ y
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+ Scale
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p><span style=" font-weight:600;">x</span></p></body></html>
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ x_scale_spinbox
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 150
+ 0
+
+
+
+ 3
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ Global:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p><span style=" font-weight:600;">y</span></p></body></html>
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ y_scale_spinbox
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 150
+ 0
+
+
+
+ 3
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ false
+
+
+
+ Data
+
+
+
+
+ x
+
+
+
+
+ y
+
+
+
+
+
+
+
+
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Axes:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ log x
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ log y
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Export shift/scale values
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Overwrite data
+
+
+
+ -
+
+
+ New graph
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ New graph
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+ PlotWidget
+ QGraphicsView
+
+
+
+ SciSpinBox
+ QDoubleSpinBox
+
+
+
+
+ tabWidget
+ shift_table
+ x_shift_spinbox
+ y_shift_spinbox
+ scale_table
+ x_scale_spinbox
+ y_scale_spinbox
+ xlog_checkbox
+ ylog_checkbox
+ graphicsView
+
+
+
+
+ buttonBox
+ accepted()
+ shift_dialog
+ accept()
+
+
+ 254
+ 632
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ shift_dialog
+ reject()
+
+
+ 322
+ 632
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/skipdialog.ui b/resources/_ui/skipdialog.ui
new file mode 100644
index 0000000..ff323ba
--- /dev/null
+++ b/resources/_ui/skipdialog.ui
@@ -0,0 +1,185 @@
+
+
+ SkipDialog
+
+
+
+ 0
+ 0
+ 448
+ 208
+
+
+
+
+
+
+ -
+
+
+ Step
+
+
+ step_spinbox
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ <html><head/><body><p>Show every <span style=" font-style:italic;">step</span> point, beginning with <span style=" font-style:italic;">offset (<step).<br/></span>Use <span style=" font-style:italic;">Invert </span>to hide every <span style=" font-style:italic;">step </span>point.</p></body></html>
+
+
+
+ -
+
+
+ 0
+
+
+ 1
+
+
+ 0
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Offset
+
+
+ offset_spinbox
+
+
+
+ -
+
+
+ 2
+
+
+
+ -
+
+
+ Qt::LeftToRight
+
+
+ Invert
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Hide skipped pts.
+
+
+ true
+
+
+ buttonGroup
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Copy without any hidden pts.
+
+
+ buttonGroup
+
+
+
+
+
+
+ step_spinbox
+ offset_spinbox
+ invert_check
+ hide_button
+ delete_button
+
+
+
+
+ buttonBox
+ accepted()
+ SkipDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ SkipDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/smoothdialog.ui b/resources/_ui/smoothdialog.ui
new file mode 100644
index 0000000..e26b521
--- /dev/null
+++ b/resources/_ui/smoothdialog.ui
@@ -0,0 +1,269 @@
+
+
+ SmoothDialog
+
+
+
+ 0
+ 0
+ 451
+ 220
+
+
+
+ 1D smoothing filter
+
+
+
+ 3
+
+ -
+
+
+ Window length
+
+
+ frac_spinBox
+
+
+
+ -
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Iterations
+
+
+ iter_spinBox
+
+
+
+ -
+
+
+ 3
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Polynomial degree
+
+
+ polynom_spinBox
+
+
+
+ -
+
+
+ Deg
+
+
+ 1
+
+
+ 3
+
+
+
+
+
+
+ -
+
+
+ <html><head/><body><p>Number of data points used as smoothing window.</p></body></html>
+
+
+ 1
+
+
+ 999
+
+
+
+ -
+
+ -
+
+ Moving mean
+
+
+ -
+
+ Savitzky-Golay
+
+
+ -
+
+ Loess (slow, use for < 10000 pts)
+
+
+ -
+
+ Moving median
+
+
+ -
+
+ Moving standard deviation
+
+
+ -
+
+ Moving variance
+
+
+ -
+
+ Moving maximum
+
+
+ -
+
+ Moving minimum
+
+
+ -
+
+ Moving sum
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ y log-spaced?
+
+
+
+ -
+
+
+ x log-spaced?
+
+
+
+
+
+
+ comboBox
+ frac_spinBox
+ polynom_spinBox
+ iter_spinBox
+ x_checkBox
+ y_checkBox
+
+
+
+
+ buttonBox
+ accepted()
+ SmoothDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ SmoothDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/t1_calc_dialog.ui b/resources/_ui/t1_calc_dialog.ui
new file mode 100644
index 0000000..f915b25
--- /dev/null
+++ b/resources/_ui/t1_calc_dialog.ui
@@ -0,0 +1,687 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 582
+ 698
+
+
+
+ Calculate relaxation
+
+
+ -
+
+
+ Model
+
+
+
+ 0
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ -
+
+
+
+ 0
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+
+
+ -
+
+
+ Result
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ New graph?
+
+
+ true
+
+
+
+ -
+
+
+ NOTE: Mean values are not available for all spectral densities. For more information ask someone.
+
+ -
+
+ Spin-Lattice Relaxation T1
+
+
+ -
+
+ Spin-Spin Relaxation T2
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ Axis
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 2
+
+ -
+
+
+ -
+
+ -
+
+ Range
+
+
+ -
+
+ Data
+
+
+
+
+ -
+
+
+ 2
+
+ -
+
+
+ τ / s
+
+
+ true
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ ω / Hz
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ 1000 K / T
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ T / K
+
+
+ buttonGroup
+
+
+
+
+
+ -
+
+
+ 2nd axis
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ -
+
+
+ –
+
+
+
+ -
+
+
+ pts.
+
+
+ 50
+
+
+
+ -
+
+
+ -
+
+
+ Log?
+
+
+
+
+
+
+ -
+
+
+
+ 2
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Use
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ x
+
+
+ buttonGroup_2
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ y
+
+
+ true
+
+
+ buttonGroup_2
+
+
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 6
+
+
+ 0
+
+ -
+
+
+
+ 2
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ τ0 / s
+
+
+
+ -
+
+
+ 1
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ E_A / eV
+
+
+
+ -
+
+
+ 1
+
+
+
+
+
+
+ -
+
+
+
+ 2
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ τ0 / s
+
+
+
+ -
+
+
+ 1
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ B / K
+
+
+
+ -
+
+
+ 1
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ T_0 / K
+
+
+
+ -
+
+
+ 1
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+ -
+
+ Arrhenius
+
+
+ -
+
+ VFT
+
+
+
+
+
+
+
+ -
+
+ -
+
+ Function parameter: τ
+
+
+ -
+
+ Peak time: τₚ
+
+
+ -
+
+ Arithmetic mean: ⟨τ⟩
+
+
+ -
+
+ Geometric mean: exp(⟨ln τ⟩)
+
+
+
+
+ -
+
+
+ Interpretation as:
+
+
+
+ -
+
+
+ Input from:
+
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/t1_dock.ui b/resources/_ui/t1_dock.ui
new file mode 100644
index 0000000..32c1785
--- /dev/null
+++ b/resources/_ui/t1_dock.ui
@@ -0,0 +1,502 @@
+
+
+ t1dialog
+
+
+
+ 0
+ 0
+ 295
+ 771
+
+
+
+ Evaluate T1 (temperature)
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 279
+ 727
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Axes
+
+
+
+ 0
+
+ -
+
+ -
+
+ T1
+
+
+ -
+
+ 1/T1
+
+
+ -
+
+ log10(T1)
+
+
+ -
+
+ log10(1/T1)
+
+
+
+
+ -
+
+
+ Relaxation in
+
+
+
+ -
+
+ -
+
+ T
+
+
+ -
+
+ 1000/T
+
+
+ -
+
+ 1/T
+
+
+
+
+ -
+
+
+ Temperature in
+
+
+
+
+
+
+ -
+
+
+ T1 minimon
+
+
+
+ 0
+
+ -
+
+ -
+
+ Data minimum
+
+
+ -
+
+ Parabola
+
+
+ -
+
+ Cubic spline
+
+
+ -
+
+ Pchip
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+
+ 0
+
+
+ 6
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ start (K)
+
+
+
+ -
+
+
+ 1
+
+
+
+ -
+
+
+ end (K)
+
+
+
+ -
+
+
+ 2
+
+
+
+ -
+
+
+ Show?
+
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+
+ 0
+
+
+ 6
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Minimon
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ x value
+
+
+
+ -
+
+
+ y value
+
+
+
+ -
+
+
+ Use
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Parameter
+
+
+
+ 0
+
+ -
+
+
+ <html><head/><body><p>T<span style=" vertical-align:sub;">1</span> minimum</p></body></html>
+
+
+
+ -
+
+
+ K
+
+
+
+ -
+
+
+ 1e-3
+
+
+
+ -
+
+
+ s
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ 100
+
+
+
+ -
+
+
+ frequency
+
+
+
+ -
+
+
+ Hz
+
+
+
+ -
+
+
+ Qt::ImhDigitsOnly|Qt::ImhFormattedNumbersOnly|Qt::ImhPreferNumbers
+
+
+ 100e6
+
+
+
+
+
+
+ -
+
+
+ Spectral density
+
+
+
+ 0
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Calculated minimum:
+
+
+
+
+
+
+ -
+
+
+ Coupling
+
+
+
+ 0
+
+ -
+
+
+ -
+
+
+ 0
+
+
+ 0
+
+
+
+
+
+
+ -
+
+
+ Result
+
+
+
+ 1
+
+ -
+
+
+ NOTE: Mean values are not available for all spectral densities. For more information ask someone.
+
+ -
+
+ Fit parameter: τ
+
+
+ -
+
+ Peak time: τₚ
+
+
+ -
+
+ Arithmetic mean: ⟨τ⟩
+
+
+ -
+
+ Geometric mean: exp(⟨ln τ⟩)
+
+
+
+
+ -
+
+
+ Result as
+
+
+
+ -
+
+
+ Use interpolation
+
+
+
+
+
+
+ -
+
+
+
+ 75
+ true
+
+
+
+ Calculate
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 17
+ 423
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/t1_tau_calculation.ui b/resources/_ui/t1_tau_calculation.ui
new file mode 100644
index 0000000..d657be9
--- /dev/null
+++ b/resources/_ui/t1_tau_calculation.ui
@@ -0,0 +1,477 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 400
+ 799
+
+
+
+ Form
+
+
+ -
+
+
+ Frequency
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 1
+
+ -
+
+
+ 999.990000000000009
+
+
+ 100.000000000000000
+
+
+
+ -
+
+ -
+
+ MHz
+
+
+ -
+
+ kHz
+
+
+ -
+
+ Hz
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 388
+ 713
+
+
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Correlation times
+
+
+
+ 1
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+ -
+
+ Arrhenius
+
+
+ -
+
+ VFT
+
+
+ -
+
+ Data
+
+
+
+
+ -
+
+
+
+ 1
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 1 K
+
+
+
+ -
+
+
+ 1000/T
+
+
+
+ -
+
+
+ –
+
+
+
+ -
+
+
+ 100 K
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ pts.
+
+
+ 50
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 1
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+ -
+
+ Fit parameter: τ
+
+
+ -
+
+ Peak time: τₚ
+
+
+ -
+
+ Arithmetic mean: ⟨τ⟩
+
+
+ -
+
+ Geometric mean: exp(⟨ln τ⟩)
+
+
+
+
+
+
+
+ -
+
+
+ Model
+
+
+
+ 0
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ -
+
+
+
+ 0
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+
+
+ -
+
+
+ Result
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ NOTE: Mean values are not available for all spectral densities. For more information ask someone.
+
+ -
+
+ Fit parameter: τ
+
+
+ -
+
+ Peak time: τₚ
+
+
+ -
+
+ Arithmetic mean: ⟨τ⟩
+
+
+ -
+
+ Geometric mean: exp(⟨ln τ⟩)
+
+
+
+
+ -
+
+
+ New graph?
+
+
+ true
+
+
+
+ -
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+
+ 75
+ true
+
+
+
+ Calculate
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/t1dialog.ui b/resources/_ui/t1dialog.ui
new file mode 100644
index 0000000..e0438c1
--- /dev/null
+++ b/resources/_ui/t1dialog.ui
@@ -0,0 +1,516 @@
+
+
+ t1dialog
+
+
+
+ 0
+ 0
+ 316
+ 741
+
+
+
+ Form
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ Axes
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 0
+
+ -
+
+ -
+
+ T1
+
+
+ -
+
+ 1/T1
+
+
+
+
+ -
+
+ -
+
+ 1000/T
+
+
+ -
+
+ T
+
+
+ -
+
+ 1/T
+
+
+
+
+
+
+
+ -
+
+
+ Frequency
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 1
+
+ -
+
+ -
+
+ MHz
+
+
+ -
+
+ kHz
+
+
+ -
+
+ Hz
+
+
+
+
+ -
+
+
+ 999.990000000000009
+
+
+ 100.000000000000000
+
+
+
+
+
+
+ -
+
+
+ Model
+
+
+
+ 2
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+
+ 0
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ -
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+
+ 0
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+
+
+
+ -
+
+
+ Pick T1 minimon
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 2
+
+ -
+
+ -
+
+ Data minimum
+
+
+ -
+
+ Parabola
+
+
+ -
+
+ Cubic spline
+
+
+ -
+
+ Akima spline
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Interpolate
+
+
+ lineEdit_2
+
+
+
+ -
+
+
+ 1 K
+
+
+
+ -
+
+
+ --
+
+
+ lineEdit_3
+
+
+
+ -
+
+
+ 2000 K
+
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Minimon
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ x value
+
+
+
+ -
+
+
+ y value
+
+
+
+
+
+
+ -
+
+
+ Set
+
+
+
+
+
+
+ -
+
+
+ Result
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ NOTE: Mean values are not available for all spectral densities. For more information ask someone.
+
+ -
+
+ Fit parameter: τ
+
+
+ -
+
+ Peak time: τₚ
+
+
+ -
+
+ Arithmetic mean: ⟨τ⟩
+
+
+ -
+
+ Geometric mean: exp(⟨ln τ⟩)
+
+
+
+
+ -
+
+
+ Use minimon interpolation
+
+
+
+ -
+
+
+ New graph?
+
+
+ true
+
+
+
+ -
+
+
+ Calculated minimon
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 17
+ 19
+
+
+
+
+ -
+
+
+
+ 75
+ true
+
+
+
+ Calculate
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/tntdialog.ui b/resources/_ui/tntdialog.ui
new file mode 100644
index 0000000..48c6ec5
--- /dev/null
+++ b/resources/_ui/tntdialog.ui
@@ -0,0 +1,259 @@
+
+
+ tntdialog
+
+
+
+ 0
+ 0
+ 373
+ 482
+
+
+
+ Read tnt file
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ TextLabel
+
+
+
+ -
+
+
+ -
+
+
+ Dimensions
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Staggered range
+
+
+
+ -
+
+
+ 0
+
+ -
+
+
+ Start
+
+
+
+ -
+
+
+ 0
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ End
+
+
+
+ -
+
+
+ true
+
+
+ 1
+
+
+
+
+
+ -
+
+
+ Name
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ NOTE: There is no inspection if start and end are valid values.
+
+
+ Qt::LeftToRight
+
+
+ Logarithmic scale
+
+
+
+ -
+
+
+ 0
+
+
+ 25
+
+ -
+
+
+ Apply
+
+
+
+ -
+
+
+ Cancel
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+
+ -
+
+
+ Unassigned lists
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ QDelayWidget
+ QWidget
+
+ 1
+
+
+
+
+
+ buttonBox
+ accepted()
+ tntdialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ tntdialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/typeconversion.ui b/resources/_ui/typeconversion.ui
new file mode 100644
index 0000000..7d28937
--- /dev/null
+++ b/resources/_ui/typeconversion.ui
@@ -0,0 +1,227 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 839
+ 502
+
+
+
+ Change type
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Drag & drop datasets and select new type.
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+ 0
+ 0
+
+
+
+ true
+
+
+ QAbstractItemView::DragOnly
+
+
+
+
+
+ 0
+
+
+ 2
+
+ -
+
+
+ Simple conversion
+
+
+ true
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Merge to complex set
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::DropOnly
+
+
+ true
+
+
+
+ Set
+
+
+
+
+ Type
+
+
+
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ QAbstractItemView::DropOnly
+
+
+ Qt::CopyAction
+
+
+ true
+
+
+
+ Real
+
+
+
+
+ Imag
+
+
+
+
+ Type
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Close|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/_ui/untitled.ui b/resources/_ui/untitled.ui
new file mode 100644
index 0000000..05d1ba3
--- /dev/null
+++ b/resources/_ui/untitled.ui
@@ -0,0 +1,69 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 800
+ 600
+
+
+
+ MainWindow
+
+
+
+
+
+ 0
+ 0
+ 31
+ 34
+
+
+
+ ...
+
+
+
+
+
+
+
+ toolBar
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+
+ asdasd
+
+
+
+
+
+
diff --git a/resources/_ui/userfitassist.ui b/resources/_ui/userfitassist.ui
new file mode 100644
index 0000000..6145f3f
--- /dev/null
+++ b/resources/_ui/userfitassist.ui
@@ -0,0 +1,193 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 675
+ 682
+
+
+
+ Dialog
+
+
+ -
+
+
+ Name
+
+
+ 4
+
+
+ lineEdit_2
+
+
+
+ -
+
+
+ Name of function, e.g. Hopperbagger
+
+
+
+ -
+
+
+ Group
+
+
+ 4
+
+
+ lineEdit_3
+
+
+
+ -
+
+
+ Type of function, e.g., Relaxation, Diffusion, Dredge,...
+
+
+
+ -
+
+
+ Equation
+
+
+ 4
+
+
+ lineEdit
+
+
+
+ -
+
+
+
+
+
+ \alpha + B*exp(x*C_{33}) + D
+
+
+
+ -
+
+
+ Parameters
+
+
+ 4
+
+
+ parameterLineEdit
+
+
+
+ -
+
+
+ \alpha B C_{33}
+
+
+
+ -
+
+
+ Fixed parameter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ D
+
+
+
+ -
+
+
+ Selection
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Fit model is saved in myfitmodels.py and ready to use without restart.
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Save
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/_ui/usermodeleditor.ui b/resources/_ui/usermodeleditor.ui
new file mode 100644
index 0000000..8f705a7
--- /dev/null
+++ b/resources/_ui/usermodeleditor.ui
@@ -0,0 +1,95 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 800
+ 600
+
+
+
+ Editor
+
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+ Open...
+
+
+
+
+ Save
+
+
+
+
+ Save as...
+
+
+
+
+ Close
+
+
+
+
+
+ CodeEditor
+ QPlainTextEdit
+
+
+
+
+
+
diff --git a/resources/_ui/valueeditor.ui b/resources/_ui/valueeditor.ui
new file mode 100644
index 0000000..017c2fe
--- /dev/null
+++ b/resources/_ui/valueeditor.ui
@@ -0,0 +1,151 @@
+
+
+ MaskDialog
+
+
+
+ 0
+ 0
+ 383
+ 645
+
+
+
+ Form
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+ -
+
+
+ -
+
+
+ -
+
+
+ QAbstractItemView::SelectRows
+
+
+
+ -
+
+
+ 0
+
+ -
+
+
+ Go to line:
+
+
+ spinBox
+
+
+
+ -
+
+
+ 1
+
+
+
+ -
+
+
+ Go Go Power Rangers!
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+ Qt::RightArrow
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ 3
+
+ -
+
+
+ Show all
+
+
+
+ -
+
+
+ Delete rows
+
+
+
+ ../../../auswerteprogramm/auswerten/ui ../../../auswerteprogramm/auswerten/ui
+
+
+
+ -
+
+
+ Add row
+
+
+
+ ../../../auswerteprogramm/auswerten/ui ../../../auswerteprogramm/auswerten/ui
+
+
+
+ -
+
+
+ <html><head/><body><p>Masked rows are shown in green</p></body></html>
+
+
+ Hide/Show selected
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/icons/__init__.py b/resources/icons/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/resources/icons/icons.json b/resources/icons/icons.json
new file mode 100644
index 0000000..5c176e1
--- /dev/null
+++ b/resources/icons/icons.json
@@ -0,0 +1,44 @@
+{
+ "BaseWindow": {
+ "action_open": "open",
+ "actionOpen_FC": "open",
+ "actionSave": "save",
+ "actionExportGraphic": "save",
+ "actionExportData": "save",
+ "action_save_fit_parameter": "save",
+ "actionFit_parameter_saving": "save",
+ "actionSave_session": "save_session",
+ "actionNew_window": "new",
+ "action_new_set": "values",
+ "actionAddlines": "plus",
+ "actionConcatenate_sets": "concat",
+ "action_calc": "eval_expression",
+ "action_sort_pts": "sort",
+ "actionShift": "scaling",
+ "action_edit": "messdings",
+ "actionPick_position": "points_pick",
+ "t1action": "tau_from_t1",
+ "actionCalculateT1": "t1_from_tau",
+ "t1tau": "t1_from_tau",
+ "action_FitWidget": "fit",
+ "actionMouse_behaviour": "mouse",
+ "menuNormalize": "normal",
+ "action_mean_t1": "mean",
+ "menuLimits": "fit_region"
+ },
+ "DataWidget": {
+ "graph_toolButton": "new",
+ "empty_toolButton": "values",
+ "func_toolButton": "plus"
+ },
+ "GraphWindow": {
+ "logx_button": "xlog",
+ "logy_button": "ylog",
+ "gridbutton": "grid",
+ "imag_button": "imag",
+ "real_button": "real",
+ "legend_button": "legend",
+ "error_button": "errors",
+ "bwbutton": "blackwhite"
+ }
+}
diff --git a/resources/icons/logo.png b/resources/icons/logo.png
new file mode 100644
index 0000000..98d770f
Binary files /dev/null and b/resources/icons/logo.png differ
diff --git a/resources/icons/normal_light/__init__.py b/resources/icons/normal_light/__init__.py
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/resources/icons/normal_light/__init__.py
@@ -0,0 +1 @@
+
diff --git a/resources/icons/normal_light/blackwhite.png b/resources/icons/normal_light/blackwhite.png
new file mode 100644
index 0000000..6a70fed
Binary files /dev/null and b/resources/icons/normal_light/blackwhite.png differ
diff --git a/resources/icons/normal_light/colors.png b/resources/icons/normal_light/colors.png
new file mode 100644
index 0000000..580db82
Binary files /dev/null and b/resources/icons/normal_light/colors.png differ
diff --git a/resources/icons/normal_light/concat.png b/resources/icons/normal_light/concat.png
new file mode 100644
index 0000000..9cc90b4
Binary files /dev/null and b/resources/icons/normal_light/concat.png differ
diff --git a/resources/icons/normal_light/errors.png b/resources/icons/normal_light/errors.png
new file mode 100644
index 0000000..cebaba4
Binary files /dev/null and b/resources/icons/normal_light/errors.png differ
diff --git a/resources/icons/normal_light/eval_expression.png b/resources/icons/normal_light/eval_expression.png
new file mode 100644
index 0000000..5e5b0e6
Binary files /dev/null and b/resources/icons/normal_light/eval_expression.png differ
diff --git a/resources/icons/normal_light/fit.png b/resources/icons/normal_light/fit.png
new file mode 100644
index 0000000..044f86c
Binary files /dev/null and b/resources/icons/normal_light/fit.png differ
diff --git a/resources/icons/normal_light/fit_region.png b/resources/icons/normal_light/fit_region.png
new file mode 100644
index 0000000..651cb3d
Binary files /dev/null and b/resources/icons/normal_light/fit_region.png differ
diff --git a/resources/icons/normal_light/geteilt_icon.png b/resources/icons/normal_light/geteilt_icon.png
new file mode 100644
index 0000000..9c3fed5
Binary files /dev/null and b/resources/icons/normal_light/geteilt_icon.png differ
diff --git a/resources/icons/normal_light/grid.png b/resources/icons/normal_light/grid.png
new file mode 100644
index 0000000..f81f7f6
Binary files /dev/null and b/resources/icons/normal_light/grid.png differ
diff --git a/resources/icons/normal_light/imag.png b/resources/icons/normal_light/imag.png
new file mode 100644
index 0000000..7f51017
Binary files /dev/null and b/resources/icons/normal_light/imag.png differ
diff --git a/resources/icons/normal_light/legend.png b/resources/icons/normal_light/legend.png
new file mode 100644
index 0000000..c868d57
Binary files /dev/null and b/resources/icons/normal_light/legend.png differ
diff --git a/resources/icons/normal_light/logo.png b/resources/icons/normal_light/logo.png
new file mode 120000
index 0000000..7a3d63f
--- /dev/null
+++ b/resources/icons/normal_light/logo.png
@@ -0,0 +1 @@
+/autohome/dominik/nmreval/resources/icons/logo.png
\ No newline at end of file
diff --git a/resources/icons/normal_light/mal_icon.png b/resources/icons/normal_light/mal_icon.png
new file mode 100644
index 0000000..f2ca1fd
Binary files /dev/null and b/resources/icons/normal_light/mal_icon.png differ
diff --git a/resources/icons/normal_light/mean.png b/resources/icons/normal_light/mean.png
new file mode 100644
index 0000000..5756845
Binary files /dev/null and b/resources/icons/normal_light/mean.png differ
diff --git a/resources/icons/normal_light/messdings.png b/resources/icons/normal_light/messdings.png
new file mode 100644
index 0000000..a13d090
Binary files /dev/null and b/resources/icons/normal_light/messdings.png differ
diff --git a/resources/icons/normal_light/minus_icon.png b/resources/icons/normal_light/minus_icon.png
new file mode 100644
index 0000000..05d2cd6
Binary files /dev/null and b/resources/icons/normal_light/minus_icon.png differ
diff --git a/resources/icons/normal_light/mouse.png b/resources/icons/normal_light/mouse.png
new file mode 100644
index 0000000..e83d087
Binary files /dev/null and b/resources/icons/normal_light/mouse.png differ
diff --git a/resources/icons/normal_light/new.png b/resources/icons/normal_light/new.png
new file mode 100644
index 0000000..5dc6027
Binary files /dev/null and b/resources/icons/normal_light/new.png differ
diff --git a/resources/icons/normal_light/normal.png b/resources/icons/normal_light/normal.png
new file mode 100644
index 0000000..0f785fe
Binary files /dev/null and b/resources/icons/normal_light/normal.png differ
diff --git a/resources/icons/normal_light/open.png b/resources/icons/normal_light/open.png
new file mode 100644
index 0000000..0affe1a
Binary files /dev/null and b/resources/icons/normal_light/open.png differ
diff --git a/resources/icons/normal_light/plus.png b/resources/icons/normal_light/plus.png
new file mode 100644
index 0000000..0aa7e29
Binary files /dev/null and b/resources/icons/normal_light/plus.png differ
diff --git a/resources/icons/normal_light/plus_icon.png b/resources/icons/normal_light/plus_icon.png
new file mode 100644
index 0000000..a8fbdbc
Binary files /dev/null and b/resources/icons/normal_light/plus_icon.png differ
diff --git a/resources/icons/normal_light/points_pick.png b/resources/icons/normal_light/points_pick.png
new file mode 100644
index 0000000..ef2789d
Binary files /dev/null and b/resources/icons/normal_light/points_pick.png differ
diff --git a/resources/icons/normal_light/real.png b/resources/icons/normal_light/real.png
new file mode 100644
index 0000000..73addf8
Binary files /dev/null and b/resources/icons/normal_light/real.png differ
diff --git a/resources/icons/normal_light/save.png b/resources/icons/normal_light/save.png
new file mode 100644
index 0000000..f89d9fa
Binary files /dev/null and b/resources/icons/normal_light/save.png differ
diff --git a/resources/icons/normal_light/save_session.png b/resources/icons/normal_light/save_session.png
new file mode 100644
index 0000000..21325f6
Binary files /dev/null and b/resources/icons/normal_light/save_session.png differ
diff --git a/resources/icons/normal_light/scaling.png b/resources/icons/normal_light/scaling.png
new file mode 100644
index 0000000..cfd84fd
Binary files /dev/null and b/resources/icons/normal_light/scaling.png differ
diff --git a/resources/icons/normal_light/sort.png b/resources/icons/normal_light/sort.png
new file mode 100644
index 0000000..f46ed94
Binary files /dev/null and b/resources/icons/normal_light/sort.png differ
diff --git a/resources/icons/normal_light/t1_from_tau.png b/resources/icons/normal_light/t1_from_tau.png
new file mode 100644
index 0000000..ae8b7e7
Binary files /dev/null and b/resources/icons/normal_light/t1_from_tau.png differ
diff --git a/resources/icons/normal_light/tau_from_t1.png b/resources/icons/normal_light/tau_from_t1.png
new file mode 100644
index 0000000..8f92cf8
Binary files /dev/null and b/resources/icons/normal_light/tau_from_t1.png differ
diff --git a/resources/icons/normal_light/values.png b/resources/icons/normal_light/values.png
new file mode 100644
index 0000000..06410d7
Binary files /dev/null and b/resources/icons/normal_light/values.png differ
diff --git a/resources/icons/normal_light/xlog.png b/resources/icons/normal_light/xlog.png
new file mode 100644
index 0000000..f24b9ac
Binary files /dev/null and b/resources/icons/normal_light/xlog.png differ
diff --git a/resources/icons/normal_light/ylog.png b/resources/icons/normal_light/ylog.png
new file mode 100644
index 0000000..dec848e
Binary files /dev/null and b/resources/icons/normal_light/ylog.png differ
diff --git a/resources/icons/pokemon_light/__init__.py b/resources/icons/pokemon_light/__init__.py
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/resources/icons/pokemon_light/__init__.py
@@ -0,0 +1 @@
+
diff --git a/resources/icons/pokemon_light/blackwhite.png b/resources/icons/pokemon_light/blackwhite.png
new file mode 100644
index 0000000..6a70fed
Binary files /dev/null and b/resources/icons/pokemon_light/blackwhite.png differ
diff --git a/resources/icons/pokemon_light/colors.png b/resources/icons/pokemon_light/colors.png
new file mode 100644
index 0000000..580db82
Binary files /dev/null and b/resources/icons/pokemon_light/colors.png differ
diff --git a/resources/icons/pokemon_light/concat.png b/resources/icons/pokemon_light/concat.png
new file mode 100644
index 0000000..8dd1e01
Binary files /dev/null and b/resources/icons/pokemon_light/concat.png differ
diff --git a/resources/icons/pokemon_light/errors.png b/resources/icons/pokemon_light/errors.png
new file mode 100644
index 0000000..d4b1826
Binary files /dev/null and b/resources/icons/pokemon_light/errors.png differ
diff --git a/resources/icons/pokemon_light/eval_expression.png b/resources/icons/pokemon_light/eval_expression.png
new file mode 100644
index 0000000..6a88388
Binary files /dev/null and b/resources/icons/pokemon_light/eval_expression.png differ
diff --git a/resources/icons/pokemon_light/fit.png b/resources/icons/pokemon_light/fit.png
new file mode 100644
index 0000000..e6a7073
Binary files /dev/null and b/resources/icons/pokemon_light/fit.png differ
diff --git a/resources/icons/pokemon_light/fit_region.png b/resources/icons/pokemon_light/fit_region.png
new file mode 100644
index 0000000..651cb3d
Binary files /dev/null and b/resources/icons/pokemon_light/fit_region.png differ
diff --git a/resources/icons/pokemon_light/geteilt_icon.png b/resources/icons/pokemon_light/geteilt_icon.png
new file mode 100644
index 0000000..5e78255
Binary files /dev/null and b/resources/icons/pokemon_light/geteilt_icon.png differ
diff --git a/resources/icons/pokemon_light/grid.png b/resources/icons/pokemon_light/grid.png
new file mode 100644
index 0000000..27272e1
Binary files /dev/null and b/resources/icons/pokemon_light/grid.png differ
diff --git a/resources/icons/pokemon_light/imag.png b/resources/icons/pokemon_light/imag.png
new file mode 100644
index 0000000..0a93e7e
Binary files /dev/null and b/resources/icons/pokemon_light/imag.png differ
diff --git a/resources/icons/pokemon_light/legend.png b/resources/icons/pokemon_light/legend.png
new file mode 100644
index 0000000..95b7259
Binary files /dev/null and b/resources/icons/pokemon_light/legend.png differ
diff --git a/resources/icons/pokemon_light/logo.png b/resources/icons/pokemon_light/logo.png
new file mode 120000
index 0000000..7a3d63f
--- /dev/null
+++ b/resources/icons/pokemon_light/logo.png
@@ -0,0 +1 @@
+/autohome/dominik/nmreval/resources/icons/logo.png
\ No newline at end of file
diff --git a/resources/icons/pokemon_light/mal_icon.png b/resources/icons/pokemon_light/mal_icon.png
new file mode 100644
index 0000000..054e238
Binary files /dev/null and b/resources/icons/pokemon_light/mal_icon.png differ
diff --git a/resources/icons/pokemon_light/mean.png b/resources/icons/pokemon_light/mean.png
new file mode 100644
index 0000000..5756845
Binary files /dev/null and b/resources/icons/pokemon_light/mean.png differ
diff --git a/resources/icons/pokemon_light/messdings.png b/resources/icons/pokemon_light/messdings.png
new file mode 100644
index 0000000..6befeb4
Binary files /dev/null and b/resources/icons/pokemon_light/messdings.png differ
diff --git a/resources/icons/pokemon_light/minus_icon.png b/resources/icons/pokemon_light/minus_icon.png
new file mode 100644
index 0000000..d9231dd
Binary files /dev/null and b/resources/icons/pokemon_light/minus_icon.png differ
diff --git a/resources/icons/pokemon_light/mouse.png b/resources/icons/pokemon_light/mouse.png
new file mode 100644
index 0000000..ce9bab4
Binary files /dev/null and b/resources/icons/pokemon_light/mouse.png differ
diff --git a/resources/icons/pokemon_light/new.png b/resources/icons/pokemon_light/new.png
new file mode 100644
index 0000000..659b259
Binary files /dev/null and b/resources/icons/pokemon_light/new.png differ
diff --git a/resources/icons/pokemon_light/normal.png b/resources/icons/pokemon_light/normal.png
new file mode 100644
index 0000000..fefd8f7
Binary files /dev/null and b/resources/icons/pokemon_light/normal.png differ
diff --git a/resources/icons/pokemon_light/open.png b/resources/icons/pokemon_light/open.png
new file mode 100644
index 0000000..89c8ac3
Binary files /dev/null and b/resources/icons/pokemon_light/open.png differ
diff --git a/resources/icons/pokemon_light/plus.png b/resources/icons/pokemon_light/plus.png
new file mode 100644
index 0000000..12e5e4e
Binary files /dev/null and b/resources/icons/pokemon_light/plus.png differ
diff --git a/resources/icons/pokemon_light/plus_icon.png b/resources/icons/pokemon_light/plus_icon.png
new file mode 100644
index 0000000..bf94b0a
Binary files /dev/null and b/resources/icons/pokemon_light/plus_icon.png differ
diff --git a/resources/icons/pokemon_light/points_pick.png b/resources/icons/pokemon_light/points_pick.png
new file mode 100644
index 0000000..b28a848
Binary files /dev/null and b/resources/icons/pokemon_light/points_pick.png differ
diff --git a/resources/icons/pokemon_light/real.png b/resources/icons/pokemon_light/real.png
new file mode 100644
index 0000000..4a188c7
Binary files /dev/null and b/resources/icons/pokemon_light/real.png differ
diff --git a/resources/icons/pokemon_light/save.png b/resources/icons/pokemon_light/save.png
new file mode 100644
index 0000000..02250c0
Binary files /dev/null and b/resources/icons/pokemon_light/save.png differ
diff --git a/resources/icons/pokemon_light/save_session.png b/resources/icons/pokemon_light/save_session.png
new file mode 100644
index 0000000..73c8b62
Binary files /dev/null and b/resources/icons/pokemon_light/save_session.png differ
diff --git a/resources/icons/pokemon_light/scaling.png b/resources/icons/pokemon_light/scaling.png
new file mode 100644
index 0000000..a9ec704
Binary files /dev/null and b/resources/icons/pokemon_light/scaling.png differ
diff --git a/resources/icons/pokemon_light/sort.png b/resources/icons/pokemon_light/sort.png
new file mode 100644
index 0000000..5da9a15
Binary files /dev/null and b/resources/icons/pokemon_light/sort.png differ
diff --git a/resources/icons/pokemon_light/t1_from_tau.png b/resources/icons/pokemon_light/t1_from_tau.png
new file mode 100644
index 0000000..f6c8c26
Binary files /dev/null and b/resources/icons/pokemon_light/t1_from_tau.png differ
diff --git a/resources/icons/pokemon_light/tau_from_t1.png b/resources/icons/pokemon_light/tau_from_t1.png
new file mode 100644
index 0000000..e755d0f
Binary files /dev/null and b/resources/icons/pokemon_light/tau_from_t1.png differ
diff --git a/resources/icons/pokemon_light/values.png b/resources/icons/pokemon_light/values.png
new file mode 100644
index 0000000..5b96876
Binary files /dev/null and b/resources/icons/pokemon_light/values.png differ
diff --git a/resources/icons/pokemon_light/xlog.png b/resources/icons/pokemon_light/xlog.png
new file mode 100644
index 0000000..a8f4b69
Binary files /dev/null and b/resources/icons/pokemon_light/xlog.png differ
diff --git a/resources/icons/pokemon_light/ylog.png b/resources/icons/pokemon_light/ylog.png
new file mode 100644
index 0000000..b16e6c7
Binary files /dev/null and b/resources/icons/pokemon_light/ylog.png differ
diff --git a/resources/icons/style.qss b/resources/icons/style.qss
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/resources/icons/style.qss
@@ -0,0 +1 @@
+
diff --git a/resources/logo.png b/resources/logo.png
new file mode 100644
index 0000000..9cab63d
Binary files /dev/null and b/resources/logo.png differ
diff --git a/resources/nmr/T1_min.npz b/resources/nmr/T1_min.npz
new file mode 100644
index 0000000..3462be1
Binary files /dev/null and b/resources/nmr/T1_min.npz differ
diff --git a/resources/nmr/__init__.py b/resources/nmr/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..072b099
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,218 @@
+from setuptools import setup
+
+setup(
+ name='nmreval',
+ version='0.1',
+ packages=['.nmreval']
+)
+
+"""A setuptools based setup module.
+
+See:
+https://packaging.python.org/guides/distributing-packages-using-setuptools/
+https://github.com/pypa/sampleproject
+"""
+
+# Always prefer setuptools over distutils
+from setuptools import setup, find_packages
+import pathlib
+
+here = pathlib.Path(__file__).parent.resolve()
+
+# Get the long description from the README file
+long_description = (here / 'README.md').read_text(encoding='utf-8')
+
+# Arguments marked as "Required" below must be included for upload to PyPI.
+# Fields marked as "Optional" may be commented out.
+
+setup(
+ # This is the name of your project. The first time you publish this
+ # package, this name will be registered for you. It will determine how
+ # users can install this project, e.g.:
+ #
+ # $ pip install sampleproject
+ #
+ # And where it will live on PyPI: https://pypi.org/project/sampleproject/
+ #
+ # There are some restrictions on what makes a valid project name
+ # specification here:
+ # https://packaging.python.org/specifications/core-metadata/#name
+ name='nmreval', # Required
+
+ # Versions should comply with PEP 440:
+ # https://www.python.org/dev/peps/pep-0440/
+ #
+ # For a discussion on single-sourcing the version across setup.py and the
+ # project code, see
+ # https://packaging.python.org/guides/single-sourcing-package-version/
+ version='0.1', # Required
+
+ # This is a one-line description or tagline of what your project does. This
+ # corresponds to the "Summary" metadata field:
+ # https://packaging.python.org/specifications/core-metadata/#summary
+ description='A sample Python project', # Optional
+
+ # This is an optional longer description of your project that represents
+ # the body of text which users will see when they visit PyPI.
+ #
+ # Often, this is the same as your README, so you can just read it in from
+ # that file directly (as we have already done above)
+ #
+ # This field corresponds to the "Description" metadata field:
+ # https://packaging.python.org/specifications/core-metadata/#description-optional
+ long_description=long_description, # Optional
+
+ # Denotes that our long_description is in Markdown; valid values are
+ # text/plain, text/x-rst, and text/markdown
+ #
+ # Optional if long_description is written in reStructuredText (rst) but
+ # required for plain-text or Markdown; if unspecified, "applications should
+ # attempt to render [the long_description] as text/x-rst; charset=UTF-8 and
+ # fall back to text/plain if it is not valid rst" (see link below)
+ #
+ # This field corresponds to the "Description-Content-Type" metadata field:
+ # https://packaging.python.org/specifications/core-metadata/#description-content-type-optional
+ long_description_content_type='text/x-rst', # Optional (see note above)
+
+ # This should be a valid link to your project's main homepage.
+ #
+ # This field corresponds to the "Home-Page" metadata field:
+ # https://packaging.python.org/specifications/core-metadata/#home-page-optional
+ url='https://github.com/pypa/sampleproject', # Optional
+
+ # This should be your name or the name of the organization which owns the
+ # project.
+ author='Dominik Demuth', # Optional
+
+ # This should be a valid email address corresponding to the author listed
+ # above.
+ author_email='dominik.demuth@physik.tu-darmstadt.de', # Optional
+
+ # Classifiers help users find your project by categorizing it.
+ #
+ # For a list of valid classifiers, see https://pypi.org/classifiers/
+ classifiers=[ # Optional
+ # How mature is this project? Common values are
+ # 3 - Alpha
+ # 4 - Beta
+ # 5 - Production/Stable
+ 'Development Status :: 3 - Alpha',
+
+ # Indicate who your project is intended for
+ 'Intended Audience :: Developers',
+ 'Topic :: Software Development :: Build Tools',
+
+ # Pick your license as you wish
+ 'License :: OSI Approved :: MIT License',
+
+ # Specify the Python versions you support here. In particular, ensure
+ # that you indicate you support Python 3. These classifiers are *not*
+ # checked by 'pip install'. See instead 'python_requires' below.
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3.8',
+ 'Programming Language :: Python :: 3.9',
+ "Programming Language :: Python :: 3.10",
+ 'Programming Language :: Python :: 3 :: Only',
+ ],
+
+ # This field adds keywords for your project which will appear on the
+ # project page. What does your project relate to?
+ #
+ # Note that this is a list of additional keywords, separated
+ # by commas, to be used to assist searching for the distribution in a
+ # larger catalog.
+ keywords='sample, setuptools, development', # Optional
+
+ # When your source code is in a subdirectory under the project root, e.g.
+ # `src/`, it is necessary to specify the `package_dir` argument.
+ package_dir={'': 'src'}, # Optional
+
+ # You can just specify package directories manually here if your project is
+ # simple. Or you can use find_packages().
+ #
+ # Alternatively, if you just want to distribute a single Python file, use
+ # the `py_modules` argument instead as follows, which will expect a file
+ # called `my_module.py` to exist:
+ #
+ # py_modules=["my_module"],
+ #
+ packages=find_packages(where='src'), # Required
+
+ # Specify which Python versions you support. In contrast to the
+ # 'Programming Language' classifiers above, 'pip install' will check this
+ # and refuse to install the project if the version does not match. See
+ # https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires
+ python_requires='>=3.7, <4',
+
+ # This field lists other packages that your project depends on to run.
+ # Any package you put here will be installed by pip when your project is
+ # installed, so they must be valid existing projects.
+ #
+ # For an analysis of "install_requires" vs pip's requirements files see:
+ # https://packaging.python.org/discussions/install-requires-vs-requirements/
+ install_requires=[
+ 'numpy',
+ 'scipy',
+ 'matplotlib',
+ 'bsddb3',
+ 'pyqtgraph',
+ 'PyQt4',
+ 'h5py'
+ ], # Optional
+
+ # List additional groups of dependencies here (e.g. development
+ # dependencies). Users will be able to install these using the "extras"
+ # syntax, for example:
+ #
+ # $ pip install sampleproject[dev]
+ #
+ # Similar to `install_requires` above, these must be valid existing
+ # projects.
+ # extras_require={ # Optional
+ # 'dev': ['check-manifest'],
+ # 'test': ['coverage'],
+ # },
+
+ # If there are data files included in your packages that need to be
+ # installed, specify them here.
+ package_data={ # Optional
+ 'sample': ['package_data.dat'],
+ },
+
+ # Although 'package_data' is the preferred approach, in some case you may
+ # need to place data files outside of your packages. See:
+ # http://docs.python.org/distutils/setupscript.html#installing-additional-files
+ #
+ # In this case, 'data_file' will be installed into '/my_data'
+ data_files=[('my_data', ['data/data_file'])], # Optional
+
+ # To provide executable scripts, use entry points in preference to the
+ # "scripts" keyword. Entry points provide cross-platform support and allow
+ # `pip` to create the appropriate form of executable for the target
+ # platform.
+ #
+ # For example, the following would provide a command called `sample` which
+ # executes the function `main` from this package when invoked:
+ entry_points={ # Optional
+ 'console_scripts': [
+ 'sample=bin:main',
+ ],
+ },
+
+ # List additional URLs that are relevant to your project as a dict.
+ #
+ # This field corresponds to the "Project-URL" metadata fields:
+ # https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use
+ #
+ # Examples listed include a pattern for specifying where the package tracks
+ # issues, where the source is hosted, where to say thanks to the package
+ # maintainers, and where to support the project financially. The key is
+ # what's used to render the link text on PyPI.
+ # project_urls={ # Optional
+ # 'Bug Reports': 'https://github.com/pypa/sampleproject/issues',
+ # 'Funding': 'https://donate.pypi.org',
+ # 'Say Thanks!': 'http://saythanks.io/to/example',
+ # 'Source': 'https://github.com/pypa/sampleproject/',
+ # },
+)